From ebf503b0a60c206ec4e84920e3b89471e5f581a8 Mon Sep 17 00:00:00 2001 From: Mark Price Date: Tue, 19 Apr 2022 15:01:33 +0100 Subject: [PATCH 001/567] Schemas v3.2.2 (#845) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6d260c3af0..b57c3c66f4 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.2.0 +v3.2.2 From c66e87f66e0d72c62600955a4b867b68cca79b4e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:02:19 +0100 Subject: [PATCH 002/567] Adopt semver for releases and introduce release labels (#841) --- .github/release.yml | 20 ++++++++++++++++++++ .github/workflows/pull_request.yml | 1 + 2 files changed, 21 insertions(+) create mode 100644 .github/release.yml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000000..9dc16327bb --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,20 @@ +changelog: + categories: + - title: ⚠️ Breaking Changes + labels: + - Breaking Changes + - title: New Features + labels: + - New Features + - title: Enhancements + labels: + - Enhancement + - title: Bug Fixes + labels: + - Bug Fix + - title: Documentation + labels: + - Documentation + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 9a04a02d75..c6740978a4 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,6 +5,7 @@ on: branches: - "master" - "branch-v*" + - "bug-fix-*" jobs: python-dependencies: From 8667d964e5c43fa35db1350ba0988be1405e681a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 21 Apr 2022 11:38:01 +0100 Subject: [PATCH 003/567] Utilise correct cookie banner url (#847) --- app/translations/messages.pot | 14 +++++++------- templates/layouts/_base.html | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 944d57dd47..23940b5830 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-03-29 09:47+0100\n" +"POT-Creation-Date: 2022-04-20 14:02+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -380,7 +380,7 @@ msgstr "" msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" -#: app/survey_config/survey_config.py:43 +#: app/survey_config/survey_config.py:42 msgid "Save and exit survey" msgstr "" @@ -711,11 +711,11 @@ msgstr "" msgid "View Submitted Response" msgstr "" -#: templates/calculatedsummary.html:14 +#: templates/calculatedsummary.html:13 msgid "Please review your answers and confirm these are correct" msgstr "" -#: templates/calculatedsummary.html:25 +#: templates/calculatedsummary.html:24 msgid "Yes, I confirm these are correct" msgstr "" @@ -1129,8 +1129,8 @@ msgstr "" #: templates/layouts/_base.html:72 msgid "" "We use cookies to collect information" -" about how you use census.gov.uk. We use this information to make the " -"website work as well as possible and improve our services." +" about how you use surveys.ons.gov.uk. We use this information to make " +"the website work as well as possible and improve our services." msgstr "" #: templates/layouts/_base.html:73 @@ -1180,7 +1180,7 @@ msgstr "" msgid "Continue survey" msgstr "" -#: templates/layouts/_questionnaire.html:38 +#: templates/layouts/_questionnaire.html:37 msgid "Choose another section and return to this later" msgstr "" diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 05737e4d41..6f81b35ff8 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -69,7 +69,7 @@ onsCookiesBanner({ "secondaryButtonUrl": cookie_settings_url, "statementTitle": _('Tell us whether you accept cookies'), - "statementText": _("We use cookies to collect information about how you use census.gov.uk. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url), + "statementText": _("We use cookies to collect information about how you use surveys.ons.gov.uk. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url), "confirmationText": _("You’ve accepted all cookies. You can change your cookie preferences at any time.").format(cookie_settings_url=cookie_settings_url), "primaryButtonText": _('Accept all cookies'), "secondaryButtonText": _('Set cookie preferences'), From b7eb7489cf3e97dd8de6328087843006b29261ad Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:46:33 +0100 Subject: [PATCH 004/567] Fix labels for autogenerated release notes (#849) --- .github/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 9dc16327bb..6f3e433fb5 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -2,10 +2,10 @@ changelog: categories: - title: ⚠️ Breaking Changes labels: - - Breaking Changes + - Breaking Change - title: New Features labels: - - New Features + - New Feature - title: Enhancements labels: - Enhancement From ee700f7f0725b5441970c0485aebd972716cb5b3 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 21 Apr 2022 15:12:14 +0100 Subject: [PATCH 005/567] Make calculated summary return_to conditional (#850) --- .../contexts/calculated_summary_context.py | 13 +- app/views/contexts/summary/answer.py | 2 +- app/views/contexts/summary/question.py | 2 +- .../app/views/contexts/summary/test_answer.py | 13 +- .../test_calculated_summary_context.py | 350 ++---------------- 5 files changed, 56 insertions(+), 324 deletions(-) diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index e57f0b1119..9b96070c76 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -16,6 +16,17 @@ class CalculatedSummaryContext(Context): + def get_return_to(self, section_id: str): + # :TODO: Add support for returning to the calculated summary that was used + has_section_summary = bool(self._schema.get_summary_for_section(section_id)) + if has_section_summary: + return "section-summary" + + if not self._schema.is_flow_hub: + return "final-summary" + + return None + def build_groups_for_section(self, section): routing_path = self._router.routing_path(section["id"]) @@ -32,7 +43,7 @@ def build_groups_for_section(self, section): self._schema, location, self._language, - return_to="final-summary", + return_to=self.get_return_to(section["id"]), ).serialize() for group in section["groups"] ] diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index 60a74d8114..c5fe753585 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -45,6 +45,6 @@ def _build_link(self, *, block_id, list_name, list_item_id, return_to): block_id=block_id, list_item_id=list_item_id, return_to=return_to, - return_to_answer_id=self.id, + return_to_answer_id=self.id if return_to else None, _anchor=self.id, ) diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index fe1998ae07..40c412637b 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -60,7 +60,7 @@ def _build_answers( block_id=block_id, list_item_id=self.list_item_id, return_to=return_to, - return_to_answer_id=answer_id, + return_to_answer_id=answer_id if return_to else None, _anchor=answer_id, ) diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index c8b8e8740c..16edbf3b20 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -4,14 +4,18 @@ @pytest.mark.usefixtures("app") -def test_create_answer(): +@pytest.mark.parametrize( + "return_to", + ["section-summary", None], +) +def test_create_answer(return_to): answer = Answer( answer_schema={"id": "answer-id", "label": "Answer Label", "type": "date"}, answer_value="An answer", block_id="house-type", list_name="answer-list", list_item_id="answer-item-id", - return_to="section-summary", + return_to=return_to, ) assert answer.id == "answer-id" @@ -19,9 +23,12 @@ def test_create_answer(): assert answer.value == "An answer" assert answer.type == "date" + query_string = ( + "?return_to=section-summary&return_to_answer_id=answer-id" if return_to else "" + ) assert ( answer.link - == "/questionnaire/answer-list/answer-item-id/house-type/?return_to=section-summary&return_to_answer_id=answer-id#answer-id" + == f"/questionnaire/answer-list/answer-item-id/house-type/{query_string}#answer-id" ) diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 43eec72b9c..0c3fc63fea 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -1,14 +1,7 @@ import pytest -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.progress_store import ProgressStore from app.questionnaire.location import Location -from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE -from app.questionnaire.routing_path import RoutingPath -from app.utilities.schema import load_schema_from_name from app.views.contexts.calculated_summary_context import CalculatedSummaryContext -from app.views.contexts.section_summary_context import SectionSummaryContext from tests.app.views.contexts import assert_summary_context @@ -108,322 +101,43 @@ def test_build_view_context_for_currency_calculated_summary( ) assert context_summary["calculated_question"]["answers"][0]["value"] == value + answer_change_link = context_summary["groups"][0]["blocks"][0]["question"][ + "answers" + ][0]["link"] + assert "return_to=final-summary" in answer_change_link -@pytest.mark.usefixtures("app") -def test_context_for_section_list_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, - answer_store=people_answer_store, - list_store=ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] - ), - progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, - response_metadata={}, - current_location=Location(section_id="section"), - routing_path=RoutingPath( - [ - "primary-person-list-collector", - "list-collector", - "visitor-list-collector", - ], - section_id="section", - ), - ) - context = summary_context() - expected = { - "summary": { - "answers_are_editable": True, - "collapsible": False, - "custom_summary": [ - { - "add_link": "/questionnaire/people/add-person/?return_to=section-summary", - "add_link_text": "Add someone to this household", - "empty_list_text": "There are no householders", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", - "item_title": "Toni Morrison", - "primary_person": False, - "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", - "list_item_id": "PlwgoG", - }, - { - "edit_link": "/questionnaire/people/UHPLbX/edit-person/?return_to=section-summary", - "item_title": "Barry Pheloung", - "primary_person": False, - "remove_link": "/questionnaire/people/UHPLbX/remove-person/?return_to=section-summary", - "list_item_id": "UHPLbX", - }, - ], - }, - "list_name": "people", - "title": "Household members staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", - "type": "List", - }, - { - "add_link": "/questionnaire/visitors/add-visitor/?return_to=section-summary", - "add_link_text": "Add another visitor to this household", - "empty_list_text": "There are no visitors", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/visitors/gTrlio/edit-visitor-person/?return_to=section-summary", - "item_title": "", - "primary_person": False, - "remove_link": "/questionnaire/visitors/gTrlio/remove-visitor/?return_to=section-summary", - "list_item_id": "gTrlio", - } - ], - }, - "list_name": "visitors", - "title": "Visitors staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", - "type": "List", - }, - ], - "page_title": "People who live here and overnight visitors", - "summary_type": "SectionSummary", - "title": "People who live here and overnight visitors", - } - } - - assert context == expected - - -@pytest.mark.usefixtures("app") -def test_context_for_driving_question_summary_empty_list(): - schema = load_schema_from_name("test_list_collector_driving_question") - - summary_context = SectionSummaryContext( - DEFAULT_LANGUAGE_CODE, - schema, - AnswerStore([{"answer_id": "anyone-usually-live-at-answer", "value": "No"}]), - ListStore(), - ProgressStore(), - metadata={}, - response_metadata={}, - current_location=Location(section_id="section"), - routing_path=RoutingPath(["anyone-usually-live-at"], section_id="section"), - ) - - context = summary_context() - expected = { - "summary": { - "answers_are_editable": True, - "collapsible": False, - "custom_summary": [ - { - "add_link": "/questionnaire/anyone-usually-live-at/?return_to=section-summary", - "add_link_text": "Add someone to this household", - "empty_list_text": "There are no householders", - "list": {"editable": False, "list_items": []}, - "list_name": "people", - "title": "Household members", - "type": "List", - } - ], - "page_title": "List Collector Driving Question Summary", - "summary_type": "SectionSummary", - "title": "List Collector Driving Question Summary", - } - } - - assert context == expected - - -@pytest.mark.usefixtures("app") -def test_context_for_driving_question_summary(): - schema = load_schema_from_name("test_list_collector_driving_question") - - summary_context = SectionSummaryContext( - DEFAULT_LANGUAGE_CODE, - schema, - AnswerStore( - [ - {"answer_id": "anyone-usually-live-at-answer", "value": "Yes"}, - {"answer_id": "first-name", "value": "Toni", "list_item_id": "PlwgoG"}, - { - "answer_id": "last-name", - "value": "Morrison", - "list_item_id": "PlwgoG", - }, - ] - ), - ListStore([{"items": ["PlwgoG"], "name": "people"}]), - ProgressStore(), - metadata={}, - response_metadata={}, - current_location=Location(section_id="section"), - routing_path=RoutingPath( - ["anyone-usually-live-at", "anyone-else-live-at"], section_id="section" - ), - ) - - context = summary_context() - - expected = { - "summary": { - "answers_are_editable": True, - "collapsible": False, - "custom_summary": [ - { - "add_link": "/questionnaire/people/add-person/?return_to=section-summary", - "add_link_text": "Add someone to this household", - "empty_list_text": "There are no householders", - "list": { - "editable": True, - "list_items": [ - { - "item_title": "Toni Morrison", - "primary_person": False, - "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", - "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", - "list_item_id": "PlwgoG", - } - ], - }, - "list_name": "people", - "title": "Household members", - "type": "List", - } - ], - "page_title": "List Collector Driving Question Summary", - "summary_type": "SectionSummary", - "title": "List Collector Driving Question Summary", - } - } - - assert context == expected - - -@pytest.mark.usefixtures("app") -def test_titles_for_repeating_section_summary(people_answer_store, mocker): - schema = load_schema_from_name("test_repeating_sections_with_hub_and_spoke") - - section_summary_context = SectionSummaryContext( - DEFAULT_LANGUAGE_CODE, - schema, - people_answer_store, - ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] - ), - ProgressStore(), - metadata={}, - response_metadata={}, - current_location=Location( - section_id="personal-details-section", - list_name="people", - list_item_id="PlwgoG", - ), - routing_path=mocker.MagicMock(), - ) - context = section_summary_context() - - assert context["summary"]["title"] == "Toni Morrison" +@pytest.mark.parametrize( + "has_section_summary", + ([True, False]), +) +@pytest.mark.parametrize( + "is_hub_enabled", + ([True, False]), +) +def test_get_return_to( + has_section_summary, + is_hub_enabled, + mocker, +): + schema = mocker.MagicMock() + schema.is_flow_hub = is_hub_enabled + schema.get_summary_for_section = mocker.Mock(return_value=has_section_summary) - section_summary_context = SectionSummaryContext( - DEFAULT_LANGUAGE_CODE, + calculated_summary_context = CalculatedSummaryContext( + "en", schema, - people_answer_store, - ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] - ), - ProgressStore(), + answer_store=mocker.MagicMock(), + list_store=mocker.MagicMock(), + progress_store=mocker.MagicMock(), metadata={}, response_metadata={}, - current_location=Location( - block_id="personal-summary", - section_id="personal-details-section", - list_name="people", - list_item_id="UHPLbX", - ), - routing_path=mocker.MagicMock(), ) - context = section_summary_context() - assert context["summary"]["title"] == "Barry Pheloung" - - -@pytest.mark.usefixtures("app") -def test_primary_only_links_for_section_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, - answer_store=people_answer_store, - list_store=ListStore( - [{"items": ["PlwgoG"], "name": "people", "primary_person": "PlwgoG"}] - ), - progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, - response_metadata={}, - current_location=Location(section_id="section"), - routing_path=RoutingPath( - [ - "primary-person-list-collector", - "list-collector", - "visitor-list-collector", - ], - section_id="section", - ), - ) - context = summary_context() - - list_items = context["summary"]["custom_summary"][0]["list"]["list_items"] - - assert "/add-or-edit-primary-person/" in list_items[0]["edit_link"] - - -@pytest.mark.usefixtures("app") -def test_primary_links_for_section_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, - answer_store=people_answer_store, - list_store=ListStore( - [ - { - "items": ["PlwgoG", "fg0sPd"], - "name": "people", - "primary_person": "PlwgoG", - } - ] - ), - progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, - response_metadata={}, - current_location=Location(section_id="section"), - routing_path=RoutingPath( - [ - "primary-person-list-collector", - "list-collector", - "visitor-list-collector", - ], - section_id="section", - ), - ) - context = summary_context() - - list_items = context["summary"]["custom_summary"][0]["list"]["list_items"] - - assert "/edit-person/" in list_items[0]["edit_link"] - assert "/edit-person/" in list_items[1]["edit_link"] + return_to = calculated_summary_context.get_return_to("some-section") + if has_section_summary: + assert return_to == "section-summary" + elif not is_hub_enabled: + assert return_to == "final-summary" + else: + assert return_to is None From db7827a63024970f9e0105f8d8b785191343c0c9 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 22 Apr 2022 10:31:09 +0100 Subject: [PATCH 006/567] Upgrade ds to v48 (#835) * update to 48.0.1 and fix service links breaking change Co-authored-by: Mebin Abraham --- .design-system-version | 2 +- app/helpers/template_helpers.py | 12 ++++++++++-- app/translations/messages.pot | 10 +++++++--- tests/app/helpers/test_template_helpers.py | 6 +++++- .../spec/features/calculated_summary.spec.js | 2 +- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.design-system-version b/.design-system-version index 865604333e..143775a017 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -46.1.4 +48.0.3 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index f189d61876..22bae871eb 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -73,12 +73,20 @@ def context(self) -> dict[str, Any]: } @property - def service_links_context(self) -> Optional[dict[str, list[dict]]]: + def service_links_context( + self, + ) -> Optional[dict[str, Union[dict[str, str], list[dict]]]]: if service_links := self._survey_config.get_service_links( sign_out_url=self._sign_out_url, is_authenticated=current_user.is_authenticated, ): - return {"itemsList": service_links} + return { + "toggleServicesButton": { + "text": lazy_gettext("Menu"), + "ariaLabel": "Toggle services menu", + }, + "itemsList": service_links, + } return None diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 23940b5830..34106e60a5 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-04-20 14:02+0100\n" +"POT-Creation-Date: 2022-04-21 15:17+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -203,11 +203,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:110 +#: app/helpers/template_helpers.py:85 +msgid "Menu" +msgstr "" + +#: app/helpers/template_helpers.py:118 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:140 +#: app/helpers/template_helpers.py:148 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 56bf9cfeaf..ecf06821a3 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -160,6 +160,10 @@ def test_get_page_header_context_census_nisra(app: Flask): BusinessSurveyConfig(), True, { + "toggleServicesButton": { + "text": "Menu", + "ariaLabel": "Toggle services menu", + }, "itemsList": [ { "title": "My account", @@ -171,7 +175,7 @@ def test_get_page_header_context_census_nisra(app: Flask): "url": "/sign-out", "id": "header-link-sign-out", }, - ] + ], }, ), ], diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index 24c8ebfac2..6c06028a2d 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -12,7 +12,7 @@ import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/calcul import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page.js"; import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page.js"; import NumberTotalPlaybackPage from "../../generated_pages/calculated_summary/number-total-playback.page.js"; -import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page"; +import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page.js"; import SubmitPage from "../../generated_pages/calculated_summary/submit.page"; import ThankYouPage from "../../base_pages/thank-you.page.js"; From 6865b02e695dd53b37af03513d5b7e068ac5ea4e Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 22 Apr 2022 15:11:06 +0100 Subject: [PATCH 007/567] Update Questionnaire Schemas to v3.2.3 (#851) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b57c3c66f4..5103369287 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.2.2 +v3.2.3 From 7aa17971efc6437f7dfb42a732c015b72d809a72 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 26 Apr 2022 10:55:16 +0100 Subject: [PATCH 008/567] Make test benchmark business schema single section (#852) --- schemas/test/en/test_benchmark_business.json | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 65a82081f4..1afa7378a0 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -21,8 +21,8 @@ }, "sections": [ { - "id": "sectionquestionnaire-introduction", - "title": "Introduction", + "id": "section91", + "title": "Quarterly Stocks Survey", "groups": [ { "id": "groupquestionnaire-introduction", @@ -122,13 +122,7 @@ ] } ] - } - ] - }, - { - "id": "section91", - "title": "Quarterly Stocks Survey", - "groups": [ + }, { "id": "group91", "blocks": [ From a1ce2a4fc3228168cdf843125d3284368d72d084 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 26 Apr 2022 13:48:42 +0100 Subject: [PATCH 009/567] Add survey info in the GTM data_layer (#833) --- app/helpers/template_helpers.py | 24 +++++++++++++------ app/survey_config/business_config.py | 9 +++++++ app/survey_config/survey_config.py | 2 ++ .../integration/test_application_variables.py | 21 +++++++++++++--- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 22bae871eb..35e3a04f36 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -11,6 +11,7 @@ from app.globals import get_session_store from app.helpers.language_helper import get_languages_context +from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( BusinessSurveyConfig, @@ -20,6 +21,7 @@ SurveyConfig, WelshCensusSurveyConfig, ) +from app.utilities.schema import load_schema_from_session_data class ContextHelper: @@ -153,7 +155,9 @@ def _footer_warning(self) -> Optional[str]: @lru_cache -def survey_config_mapping(*, theme: str, language: str, base_url: str) -> SurveyConfig: +def survey_config_mapping( + *, theme: str, language: str, base_url: str, schema: QuestionnaireSchema +) -> SurveyConfig: survey_type_to_config: dict[str, Type[SurveyConfig]] = { "default": BusinessSurveyConfig, "business": BusinessSurveyConfig, @@ -166,6 +170,7 @@ def survey_config_mapping(*, theme: str, language: str, base_url: str) -> Survey return survey_type_to_config[theme]( base_url=base_url, + schema=schema, ) @@ -173,6 +178,7 @@ def get_survey_config( *, theme: Optional[str] = None, language: Optional[str] = None, + schema: Optional[QuestionnaireSchema] = None, ) -> SurveyConfig: # The fallback to assigning SURVEY_TYPE to theme is only being added until # business feedback on the differentiation between theme and SURVEY_TYPE. @@ -186,13 +192,23 @@ def get_survey_config( theme=theme, language=language, base_url=base_url, + schema=schema, ) def render_template(template: str, **kwargs: Union[str, Mapping]) -> str: language = get_locale().language + schema, session_expires_at = None, None + if session_store := get_session_store(): + if session_data := session_store.session_data: + schema = load_schema_from_session_data(session_data) + + if session_expiry := session_store.expiration_time: + session_expires_at = session_expiry.isoformat() + survey_config = get_survey_config( language=language, + schema=schema, ) is_post_submission = request.blueprint == "post_submission" include_csrf_token = bool( @@ -207,12 +223,6 @@ def render_template(template: str, **kwargs: Union[str, Mapping]) -> str: template = f"{template.lower()}.html" - session_expires_at = ( - session_store.expiration_time.isoformat() - if (session_store := get_session_store()) and session_store.expiration_time - else None - ) - return flask_render_template( template, csp_nonce=request.csp_nonce, # type: ignore diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 5d73b67f95..e64057d5f8 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -20,6 +20,15 @@ def __post_init__(self): self.base_url = self._stripped_base_url super().__post_init__() + if self.schema: + self.data_layer: list[dict] = [ + { + key: self.schema.json[key] + for key in ["form_type", "survey_id", "title"] + if key in self.schema.json + } + ] + if not self.account_service_log_out_url: self.account_service_log_out_url: str = f"{self.base_url}/sign-in/logout" diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 67cf8eab01..e8fca6accf 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -4,6 +4,7 @@ from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString +from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL, ONS_URL @@ -11,6 +12,7 @@ class SurveyConfig: """Valid options for defining survey-based configuration.""" + schema: Optional[QuestionnaireSchema] = None page_header_logo: Optional[str] = "ons-logo-en" page_header_logo_alt: Optional[LazyString] = lazy_gettext( "Office for National Statistics logo" diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 429366f03c..007ebdd88c 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -16,18 +16,33 @@ def tearDown(self): settings.EQ_GOOGLE_TAG_MANAGER_AUTH = None def test_google_analytics_code_and_credentials_are_present(self): - self.launchSurvey("test_textfield") + self.launchSurvey("test_feedback") self._client.set_cookie( "localhost", key="ons_cookie_policy", value="'usage':true" ) - self.get("/questionnaire/name-block/") + self.get("/questionnaire/feedback/") self.assertStatusOK() self.assertInHead("gtm.start") - self.assertInHead("dataLayer = []") + self.assertInHead( + 'dataLayer = [{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}]' + ) self.assertInBody("https://www.googletagmanager.com") self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_AUTH) self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_ID) + def test_google_analytics_data_layer_has_no_null_fields(self): + self.launchSurvey("test_textfield") + self._client.set_cookie( + "localhost", key="ons_cookie_policy", value="'usage':true" + ) + self.get("/questionnaire/name-block/") + self.assertStatusOK() + self.assertInHead("gtm.start") + # form_type is empty so should not be present + self.assertInHead( + 'dataLayer = [{"survey_id": "001", "title": "Other input fields"}]' + ) + def test_google_analytics_data_layer_is_set_to_nisra_false(self): self.launchSurvey("test_thank_you_census_individual") self._client.set_cookie( From ac7b02023cad421d6602ec7d8bfbc28a9a1c7867 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 27 Apr 2022 12:08:37 +0100 Subject: [PATCH 010/567] Update Chromedriver to 100.0.0 (#853) --- package.json | 2 +- yarn.lock | 1047 ++++++++++++++++++++++++++------------------------ 2 files changed, 542 insertions(+), 507 deletions(-) diff --git a/package.json b/package.json index d6d16dbb67..7229f3a34d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^99.0.0", + "chromedriver": "^100.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index c151f67a99..dc4213453c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,12 @@ "@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": version "7.16.7" @@ -16,36 +17,36 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" - integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== "@babel/core@^7.17.5": - version "7.17.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" - integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.3" + "@babel/generator" "^7.17.9" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.9" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" + "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" + json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" - integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== +"@babel/generator@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== dependencies: "@babel/types" "^7.17.0" jsesc "^2.5.1" @@ -66,25 +67,25 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== dependencies: - "@babel/compat-data" "^7.16.4" + "@babel/compat-data" "^7.17.7" "@babel/helper-validator-option" "^7.16.7" browserslist "^4.17.5" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" - integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" + integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-member-expression-to-functions" "^7.17.7" "@babel/helper-optimise-call-expression" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" @@ -125,21 +126,13 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== dependencies: - "@babel/helper-get-function-arity" "^7.16.7" "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== - dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-hoist-variables@^7.16.7": version "7.16.7" @@ -148,12 +141,12 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" - integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== +"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" + integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" @@ -162,14 +155,14 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.16.7": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz#3c3b03cc6617e33d68ef5a27a67419ac5199ccd0" - integrity sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA== +"@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" "@babel/template" "^7.16.7" @@ -208,12 +201,12 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" @@ -249,28 +242,28 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" - integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== dependencies: "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" + "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" "@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.16.7", "@babel/parser@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" - integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== +"@babel/parser@^7.16.7", "@babel/parser@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" @@ -566,9 +559,9 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" - integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" + integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== dependencies: "@babel/helper-plugin-utils" "^7.16.7" @@ -635,22 +628,22 @@ babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" - integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" + integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== dependencies: - "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-module-transforms" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" - integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw== + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" + integrity sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw== dependencies: "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-module-transforms" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" @@ -700,11 +693,11 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-regenerator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" - integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" + integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== dependencies: - regenerator-transform "^0.14.2" + regenerator-transform "^0.15.0" "@babel/plugin-transform-reserved-words@^7.16.7": version "7.16.7" @@ -868,9 +861,9 @@ esutils "^2.0.2" "@babel/register@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.0.tgz#8051e0b7cb71385be4909324f072599723a1f084" - integrity sha512-UNZsMAZ7uKoGHo1HlEXfteEOYssf64n/PNLHGqOKq/bgYcu/4LrQWAHJwSCb3BRZK8Hi5gkJdRcwrGTO2wtRCg== + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" + integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -879,9 +872,9 @@ source-map-support "^0.5.16" "@babel/runtime@^7.17.2", "@babel/runtime@^7.8.4": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" - integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== dependencies: regenerator-runtime "^0.13.4" @@ -894,18 +887,18 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" + "@babel/generator" "^7.17.9" "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" + "@babel/parser" "^7.17.9" "@babel/types" "^7.17.0" debug "^4.1.0" globals "^11.1.0" @@ -918,16 +911,16 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@eslint/eslintrc@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a" - integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w== +"@eslint/eslintrc@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.2.tgz#4989b9e8c0216747ee7cca314ae73791bb281aae" + integrity sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.3.1" globals "^13.9.0" - ignore "^4.0.6" + ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.0.4" @@ -958,20 +951,33 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.0.tgz#b30fd66b5426bb7f44bb6043ab127b20ee404063" + integrity sha512-YH+BnkvuCiPR+MUOY6JIArdTIGrRtsxnLaIxPRy4CpGJ/V6OO6Gq/1J+FJEc4j5e5h6Bcy3/K7prlMrm93BJoA== + dependencies: + "@jridgewell/set-array" "1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + version "3.0.6" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz#4ac237f4dabc8dd93330386907b97591801f7352" + integrity sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw== + +"@jridgewell/set-array@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.0.0.tgz#6a8e62049ab198c5f7daf8047e71947ef46c11c8" + integrity sha512-LcqVnHCjOAj8BTCtjpwYZCMTn4yArusbdObCVRUYvBHhrR5fVLVyENG+UVWM4T4H/ufv7NiBLdprllxWs/5PaQ== "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.11" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1062,9 +1068,9 @@ integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== "@types/inquirer@^8.1.2": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.0.tgz#b9566d048f5ff65159f2ed97aff45fe0f00b35ec" - integrity sha512-BNoMetRf3gmkpAlV5we+kxyZTle7YibdOntIZbU5pyIfMdcwy784KfeZDAcuyMznkh5OLa17RVXZOGA5LTlkgQ== + version "8.2.1" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" + integrity sha512-wKW3SKIUMmltbykg4I5JzCVzUhkuD9trD6efAmYgN2MrSntY0SMRQzEnD3mkyJ/rv9NLbTC7g3hKKE86YwEDLw== dependencies: "@types/through" "*" rxjs "^7.2.0" @@ -1088,53 +1094,58 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/json-buffer@~3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" + integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/keyv@*": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" - integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== dependencies: "@types/node" "*" "@types/lodash.flattendeep@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz#2686d9161ae6c3d56d6745fa118308d88562ae53" - integrity sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng== + version "4.4.7" + resolved "https://registry.yarnpkg.com/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz#0ce3dccbe006826d58e9824b27df4b00ed3e90e6" + integrity sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg== dependencies: "@types/lodash" "*" "@types/lodash.pickby@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" - integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== + version "4.6.7" + resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz#fd089a5a7f8cbe7294ae5c90ea5ecd9f4cae4d2c" + integrity sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig== dependencies: "@types/lodash" "*" "@types/lodash.union@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.6.tgz#2f77f2088326ed147819e9e384182b99aae8d4b0" - integrity sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw== + version "4.6.7" + resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.7.tgz#ceace5ed9f3610652ba4a72e0e0afb2a0eec7a4d" + integrity sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA== dependencies: "@types/lodash" "*" "@types/lodash@*": - version "4.14.179" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5" - integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w== + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== "@types/mocha@^9.0.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" - integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== "@types/node@*", "@types/node@^17.0.4": - version "17.0.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" - integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== + version "17.0.29" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" + integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== "@types/object-inspect@^1.8.0": version "1.8.1" @@ -1142,16 +1153,16 @@ integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== "@types/puppeteer@^5.4.0": - version "5.4.5" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.5.tgz#154e3850a77bfd3967f036680de8ddc88eb3a12b" - integrity sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA== + version "5.4.6" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.6.tgz#afc438e41dcbc27ca1ba0235ea464a372db2b21c" + integrity sha512-98Kghehs7+/GD9b56qryhqdqVCXUTbetTv3PlvDnmFRTHQH0j9DIp1f7rkAW3BAj4U3yoeSEQnKgdW8bDq0Y0Q== dependencies: "@types/node" "*" "@types/recursive-readdir@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz#b39cd5474fd58ea727fe434d5c68b7a20ba9121c" - integrity sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz#330f5ec0b73e8aeaf267a6e056884e393f3543a3" + integrity sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg== dependencies: "@types/node" "*" @@ -1214,9 +1225,9 @@ "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": - version "2.9.2" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" - integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== + version "2.10.0" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" + integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== dependencies: "@types/node" "*" @@ -1226,9 +1237,9 @@ integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== "@wdio/cli@^7.16.16": - version "7.17.4" - resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-7.17.4.tgz#a978caab4029541bc9d6493f27046e262dc73e49" - integrity sha512-nG+ZCKQ8Bu+rXYuHdsJ5Al/aNlD6YrU75DWaOU4JCJJmt/ulhVZ2awIyTiIYzRsZVwohPmm8lR01wYlMwHRX9g== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-7.19.6.tgz#0b9054fba7f4161185ef80f20f259c9a45c3493a" + integrity sha512-lcHos0zyIQumdt+uzCbzVwJuzMyCI/Tv7nt6CcaJtIkpuut5WZ9H7V6IM3j1UYV0SEqz58KU+VPB82qazEP+YQ== dependencies: "@types/ejs" "^3.0.5" "@types/fs-extra" "^9.0.4" @@ -1238,54 +1249,54 @@ "@types/lodash.union" "^4.6.6" "@types/node" "^17.0.4" "@types/recursive-readdir" "^2.2.0" - "@wdio/config" "7.17.3" - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/config" "7.19.5" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" async-exit-hook "^2.0.1" chalk "^4.0.0" chokidar "^3.0.0" cli-spinners "^2.1.0" ejs "^3.0.1" fs-extra "^10.0.0" - inquirer "8.1.5" + inquirer "8.2.2" lodash.flattendeep "^4.4.0" lodash.pickby "^4.6.0" lodash.union "^4.6.0" mkdirp "^1.0.4" recursive-readdir "^2.2.2" - webdriverio "7.17.4" + webdriverio "7.19.5" yargs "^17.0.0" yarn-install "^1.0.0" -"@wdio/config@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-7.17.3.tgz#23a004e7685db98394a6e1c99f0a53968a44bd2c" - integrity sha512-MSWCsx0w1EbxbwOD8ykTxHqgx208CWoz9n4oWHx7Q1APfetqWFLM4O7K8cdZS1gV4IvH4EAV9807L91K8r0JNw== +"@wdio/config@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/config/-/config-7.19.5.tgz#aa8158d648e1ffb28a7e53474d5ce171066e82f7" + integrity sha512-GyG0SSUjw9RyDgEwculgwiWyQ0eEeFAgaKTAa4RHC6ZgHHTgfyxzkWqBmNLzHfiB6GSR2DyZDcDsPT7ZAHkiEg== dependencies: - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" deepmerge "^4.0.0" glob "^7.1.2" "@wdio/local-runner@^7.16.16": - version "7.17.4" - resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-7.17.4.tgz#2e25e94631a4565e763498a0ccc8e9d5964a9fef" - integrity sha512-Gw3zAirGhHMyhedrzF4sumLjXwHo3AuUGTKwrIsZCrxloVT2TogO7882czmVRbaeo8SuvQbwpTDrdogf5hVsJw== + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-7.19.5.tgz#1a2a519e0e8eb7fd5197bda7651ffbc813a2c1e0" + integrity sha512-VZr726za6JHY+14SqEEq/VolKIsC2QgMH9BhX3sw0yWUsRxWy4M7STl2fiQWgJbW0bc2Z74TRLrx7TYfncIPpw== dependencies: "@types/stream-buffers" "^3.0.3" - "@wdio/logger" "7.17.3" - "@wdio/repl" "7.17.3" - "@wdio/runner" "7.17.4" - "@wdio/types" "7.17.3" + "@wdio/logger" "7.19.0" + "@wdio/repl" "7.19.5" + "@wdio/runner" "7.19.5" + "@wdio/types" "7.19.5" async-exit-hook "^2.0.1" split2 "^4.0.0" stream-buffers "^3.0.2" -"@wdio/logger@7.17.3", "@wdio/logger@^7.5.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-7.17.3.tgz#fcdfcbd9892173f9df0de67f448ae6d6f2aa6c3b" - integrity sha512-hpvJDsJMX8G/8gXHOEipxkQPjojjA+BRCZqCvZRLCVpWm2JB7tBoMzu9sUJXcpSkY03b94KAd4EwNA2uNAf9aQ== +"@wdio/logger@7.19.0", "@wdio/logger@^7.5.3": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-7.19.0.tgz#23697a4b4aaea56c3bd477a0393af2a5c175fc85" + integrity sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ== dependencies: chalk "^4.0.0" loglevel "^1.6.0" @@ -1293,98 +1304,98 @@ strip-ansi "^6.0.0" "@wdio/mocha-framework@^7.16.15": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-7.17.3.tgz#ffbf118d674ca997cf523618780ddb46e84ad3fb" - integrity sha512-6vT4Pf/u+sjQTGrhUd+IrkuhAVT6ojp6jcqywCD+4v2HwdRDek8PShZq3HsvEvqRDCZhiMeyISHkqE8D2ozpgg== + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-7.19.5.tgz#33f1965a52e8974ffc45f7ad2dc7ec642e7b6191" + integrity sha512-dZ2Xhl1LX/KOz/kAQ0bYcdsfr+rpMeLimwk2t9hPxtLt+xRYQNuJ8MnUyetJ7OqWsqtV/txe+JFxnfjuDg/HXg== dependencies: "@types/mocha" "^9.0.0" - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" expect-webdriverio "^3.0.0" mocha "^9.0.0" -"@wdio/protocols@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-7.17.3.tgz#a140bba3bdcfd108bee94a99c02e34752dfa8187" - integrity sha512-DxVRil2uMDOshk0gMOrmemC9uEZuB5Dv4bJX/ozZwXPV9AHd6oJqUrsF/fs8bT9+4AWkE58yqsRBFc/pt7sFMw== +"@wdio/protocols@7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-7.19.0.tgz#cd753752c64b9c1dd7ace05398c1d11c46af41ab" + integrity sha512-ji74rQag6v+INSNd0J8eAh2rpH5vOXgeiP5Qr32K6PWU6HzYWuAFH2x4srXsH0JawHCdTK2OQAOYrLmMb44hug== -"@wdio/repl@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-7.17.3.tgz#49c82f7c2842c348c8266b864b86e36e3f018773" - integrity sha512-ZX4dYnoOb9NC3IQFhva4B7FCoVx9v7CIG7g5W4bX/un5Xfyz3Fne1vGP9Aku15nyIaXRSCzuV6vpT/5KR6q6Hg== +"@wdio/repl@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-7.19.5.tgz#e99d1e24c52ac34b7434d21c94419c46eabcfbf8" + integrity sha512-Rej9SrH2DIPQ2342o4ixF48fJYsaeU4rtz+R7wYv6J1RZaNQvINdO21HoODfJ7DIDltVWzvLhCOe4CgzGubRGA== dependencies: - "@wdio/utils" "7.17.3" + "@wdio/utils" "7.19.5" -"@wdio/reporter@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.17.3.tgz#115f5bdbafb423ce7de52a9940d057973664b446" - integrity sha512-jf+Qapw6gXLgi2IQJ2Jd7EqwUt2UFKxWYUwPeKPKpyC3LNnnO4muv/wATYnIhCllgae8VUttvomg2jqFYhqxNQ== +"@wdio/reporter@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.19.5.tgz#2099bd46df6ed4af753d9f56f4bb6c8f66131c54" + integrity sha512-1h2IM+xF1Oc483HZElhOVgKrgSxI8erMQJ8fTgPo17aeWQFU2rl5HsdY49+LRU5YEmSHNm3HbNn8fs1nkpSnIA== dependencies: "@types/diff" "^5.0.0" "@types/node" "^17.0.4" "@types/object-inspect" "^1.8.0" "@types/supports-color" "^8.1.0" "@types/tmp" "^0.2.0" - "@wdio/types" "7.17.3" + "@wdio/types" "7.19.5" diff "^5.0.0" fs-extra "^10.0.0" object-inspect "^1.10.3" supports-color "8.1.1" -"@wdio/runner@7.17.4": - version "7.17.4" - resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-7.17.4.tgz#dd3a5e0e07c24db03b23152bfd2dafd742f379bf" - integrity sha512-TV4zbRAIxcV+5Y6R8FN+1wVPqPDaiX8W7p0D6aayZo0+HpU0hFTCW73dmKbCqJ4a49JsNcJkITA83dLmRWJ8mg== +"@wdio/runner@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-7.19.5.tgz#c9ca9574264143070c27ff73c02739be7bcb19c8" + integrity sha512-9eKoTqT/Ht8RPlVfqBcDTvhluP/nwyHOvqw0+zHUoJHTtLFCsZNcAXnBgAuozIrHFgyX5e4LMQLvY0voWDmqCQ== dependencies: - "@wdio/config" "7.17.3" - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/config" "7.19.5" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" deepmerge "^4.0.0" gaze "^1.1.2" - webdriver "7.17.3" - webdriverio "7.17.4" + webdriver "7.19.5" + webdriverio "7.19.5" "@wdio/spec-reporter@^7.16.14": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-7.17.3.tgz#ccaabecf61a989fd729dcba2eaddcd9ed5824999" - integrity sha512-p5kzIuA8jljGq/8EFZV3N2SdpMPFfXKULPj8YKhvd/XZFJk3YTNT67GVcOC+Qz4SUobYf4o/L3+w60KS2NI5RQ== + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-7.19.5.tgz#55d5c1fc99c7b3b2595889abdb332909e1062c65" + integrity sha512-ovkjhOowpSJKblEIC5Si+pOmr0/fUqo+bNkxZwYDNSzIwPyxJnED17WSNYw+JAzSp2PgfNwjELoQfMNCDPQvAQ== dependencies: "@types/easy-table" "^0.0.33" - "@wdio/reporter" "7.17.3" - "@wdio/types" "7.17.3" + "@wdio/reporter" "7.19.5" + "@wdio/types" "7.19.5" chalk "^4.0.0" easy-table "^1.1.1" pretty-ms "^7.0.0" "@wdio/sync@^7.16.16": - version "7.17.4" - resolved "https://registry.yarnpkg.com/@wdio/sync/-/sync-7.17.4.tgz#4401f7e6831f68c64eef32f9c19342a1741ba46f" - integrity sha512-XJp+awbHlxiE/WP29CBM3qQ5Y4Je9e3DtRiTGL99f4UpyyqkDaX8oOkpMfzs1GKBBlmx4rEgnc3T+0AzBGAoXQ== + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/sync/-/sync-7.19.5.tgz#c1b750a350158dfcbe6e5d5aefef77eaed6e1d96" + integrity sha512-lawVJupeXmQGKT4MAAYF6vMw8Fr7e9BVRdoE9DiQMVY25ghmQqAO+gyE8LH7xJW5qBSJqXyWdoSiOdKWbS/2tg== dependencies: "@types/fibers" "^3.1.0" "@types/puppeteer" "^5.4.0" - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" fibers "^5.0.0" - webdriverio "7.17.4" + webdriverio "7.19.5" -"@wdio/types@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.17.3.tgz#79b3d3ac0d8ac6c88f51c1af1deb12630d127246" - integrity sha512-j8kYdaMl4NFRS8M1bFDuEa3GMbUZbLQY7i6XEnJSetyW0GyMDLlzwcfXI4DdX85+3JbO5624UGKxVsQcuA7T3A== +"@wdio/types@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.19.5.tgz#e05790f61dfab54ee6683ac799cb5f96615d1d0f" + integrity sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A== dependencies: "@types/node" "^17.0.4" got "^11.8.1" -"@wdio/utils@7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-7.17.3.tgz#5e7063543d8517fdc9eb05b6b4a9ccd5dbb2c38d" - integrity sha512-20bGTCmgBNVKa2BJs3B5kxbsryjhfEOoKDnFjZ/rAVZYT1t1sg0e/W+vRfamd++NqTaIHOY/IKGEFiEnCw5nXw== +"@wdio/utils@7.19.5": + version "7.19.5" + resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-7.19.5.tgz#9c2d17e1ae3d21824409806f80faec1d7c78baff" + integrity sha512-UvZK8PvY50aUEg5CYSDkkT2NsDA0+Eo7QWjreA3L5ff50jaFrYnpVOyfX4PjWhErH8gZtYSwep+DSgldCUInGQ== dependencies: - "@wdio/logger" "7.17.3" - "@wdio/types" "7.17.3" + "@wdio/logger" "7.19.0" + "@wdio/types" "7.19.5" p-iteration "^1.1.8" acorn-jsx@^5.3.1: @@ -1393,9 +1404,9 @@ acorn-jsx@^5.3.1: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.7.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== agent-base@6: version "6.0.2" @@ -1503,12 +1514,12 @@ archiver-utils@^2.1.0: readable-stream "^2.0.0" archiver@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba" - integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg== + version "5.3.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" + integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== dependencies: archiver-utils "^2.1.0" - async "^3.2.0" + async "^3.2.3" buffer-crc32 "^0.2.1" readable-stream "^3.6.0" readdir-glob "^1.0.0" @@ -1542,13 +1553,14 @@ array-union@^2.1.0: integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.0" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" asn1.js@^5.0.1: version "5.4.1" @@ -1575,12 +1587,7 @@ async-exit-hook@^2.0.1: resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== -async@0.9.x: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - -async@^3.2.0: +async@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== @@ -1677,7 +1684,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1689,15 +1696,15 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.17.5, browserslist@^4.19.1: - version "4.20.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.0.tgz#35951e3541078c125d36df76056e94738a52ebe9" - integrity sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ== +browserslist@^4.17.5, browserslist@^4.20.2: + version "4.20.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" + integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== dependencies: - caniuse-lite "^1.0.30001313" - electron-to-chromium "^1.4.76" + caniuse-lite "^1.0.30001332" + electron-to-chromium "^1.4.118" escalade "^3.1.1" - node-releases "^2.0.2" + node-releases "^2.0.3" picocolors "^1.0.0" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: @@ -1788,10 +1795,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001313: - version "1.0.30001314" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz#65c7f9fb7e4594fca0a333bec1d8939662377596" - integrity sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw== +caniuse-lite@^1.0.30001332: + version "1.0.30001332" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" + integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== chai@^4.3.6: version "4.3.6" @@ -1817,7 +1824,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1826,7 +1833,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1874,10 +1881,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^99.0.0: - version "99.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-99.0.0.tgz#fbfcc7e74991dd50962e7dd456d78eaf49f56774" - integrity sha512-pyB+5LuyZdb7EBPL3i5D5yucZUD+SlkdiUtmpjaEnLd9zAXp+SvD/hP5xF4l/ZmWvUo/1ZLxAI1YBdhazGTpgA== +chromedriver@^101.0.0: + version "100.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-100.0.0.tgz#1b4bf5c89cea12c79f53bc94d8f5bb5aa79ed7be" + integrity sha512-oLfB0IgFEGY9qYpFQO/BNSXbPw7bgfJUN5VX8Okps9W2qNT4IqKh5hDwKWtpUIQNI6K3ToWe2/J5NdpurTY02g== dependencies: "@testim/chrome-version" "^1.1.2" axios "^0.24.0" @@ -1994,6 +2001,14 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compress-brotli@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.6.tgz#64bd6f21f4f3e9841dbac392f4c29218caf5e9d9" + integrity sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ== + dependencies: + "@types/json-buffer" "~3.0.0" + json-buffer "~3.0.1" + compress-commons@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" @@ -2017,11 +2032,11 @@ convert-source-map@^1.7.0: safe-buffer "~5.1.1" core-js-compat@^3.20.2, core-js-compat@^3.21.0: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" - integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== + version "3.22.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.2.tgz#eec621eb276518efcf718d0a6d9d042c3d0cad48" + integrity sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw== dependencies: - browserslist "^4.19.1" + browserslist "^4.20.2" semver "7.0.0" core-util-is@~1.0.0: @@ -2030,12 +2045,9 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== crc-32@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.1.tgz#436d2bcaad27bcb6bd073a2587139d3024a16460" - integrity sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.3.1" + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== crc32-stream@^4.0.2: version "4.0.2" @@ -2079,10 +2091,10 @@ css-value@^0.0.1: resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" integrity sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo= -debug@4, debug@4.3.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" @@ -2093,6 +2105,13 @@ debug@4.3.1: dependencies: ms "2.1.2" +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2149,11 +2168,12 @@ defer-to-connect@^2.0.0: integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== dependencies: - object-keys "^1.0.12" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" del@^6.0.0: version "6.0.0" @@ -2174,28 +2194,28 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -devtools-protocol@0.0.969999: - version "0.0.969999" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.969999.tgz#3d6be0a126b3607bb399ae2719b471dda71f3478" - integrity sha512-6GfzuDWU0OFAuOvBokXpXPLxjOJ5DZ157Ue3sGQQM3LgAamb8m0R0ruSfN0DDu+XG5XJgT50i6zZ/0o8RglreQ== +devtools-protocol@0.0.981744: + version "0.0.981744" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.981744.tgz#9960da0370284577d46c28979a0b32651022bacf" + integrity sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg== -devtools-protocol@^0.0.979353: - version "0.0.979353" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.979353.tgz#4aa394d344b6c1a37437d5e16bb3d10330d708af" - integrity sha512-/A7o8FU5n4i2WN/RH6opBbteawPbNgyKmmyl6Ts4zpQ5FVq/cGe2K/qGr8t80BLVu8KynTckHbdpaLCwxzRyFA== +devtools-protocol@^0.0.982423: + version "0.0.982423" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.982423.tgz#39ac3791d4c5b90ebb416d4384663b7b0cc44154" + integrity sha512-FnVW2nDbjGNw1uD/JRC+9U5768W7e1TfUwqbDTcSsAu1jXFjITSX8w3rkW5FEpHRMPPGpvNSmO1pOpqByiWscA== -devtools@7.17.3: - version "7.17.3" - resolved "https://registry.yarnpkg.com/devtools/-/devtools-7.17.3.tgz#3f6b77b7e12df10080a5289658538b63f87d6c8e" - integrity sha512-y5O+z+q7cUuAKMY9ZNGexbb62MUimKAJX7OkFecix2Fl9+YFSmAQUUtHWrTt9qFkw5NJNMdiXZhQvk+JdfRygw== +devtools@7.19.5: + version "7.19.5" + resolved "https://registry.yarnpkg.com/devtools/-/devtools-7.19.5.tgz#6a310e4d52b526fc89110dee5ec174df51ee8984" + integrity sha512-O62fArKiAT7fxTgn3GagEFZPpKF2Kfx/HkzGmJVIlBktrN4bJ+DAwZh6d27U9hE6qraMbVPEw+tW6T3RWoj0Sw== dependencies: "@types/node" "^17.0.4" "@types/ua-parser-js" "^0.7.33" - "@wdio/config" "7.17.3" - "@wdio/logger" "7.17.3" - "@wdio/protocols" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/config" "7.19.5" + "@wdio/logger" "7.19.0" + "@wdio/protocols" "7.19.0" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" chrome-launcher "^0.15.0" edge-paths "^2.1.0" puppeteer-core "^13.1.3" @@ -2257,16 +2277,16 @@ edge-paths@^2.1.0: which "^2.0.2" ejs@^3.0.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== + version "3.1.7" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" + integrity sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw== dependencies: - jake "^10.6.1" + jake "^10.8.5" -electron-to-chromium@^1.4.76: - version "1.4.81" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.81.tgz#a9ce8997232fb9fb0ec53de8931a85b18c0a7383" - integrity sha512-Gs7xVpIZ7tYYSDA+WgpzwpPvfGwUk3KSIjJ0akuj5XQHFdyQnsUoM76EA4CIHXNLPiVwTwOFay9RMb0ChG3OBw== +electron-to-chromium@^1.4.118: + version "1.4.123" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.123.tgz#de88ea7fd29d7c868e63c88f129e91494bcf3266" + integrity sha512-0pHGE53WkYoFbsgwYcVKEpWa6jbzlvkohIEA2CUoZ9b5KC+w/zlMiQHvW/4IBcOh7YoEFqRNavgTk02TBoUTUw== emoji-regex@^8.0.0: version "8.0.0" @@ -2292,10 +2312,10 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== +es-abstract@^1.19.1, es-abstract@^1.19.2: + version "1.19.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.5.tgz#a2cb01eb87f724e815b278b0dd0d00f36ca9a7f1" + integrity sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" @@ -2303,21 +2323,28 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: get-intrinsic "^1.1.1" get-symbol-description "^1.0.0" has "^1.0.3" - has-symbols "^1.0.2" + has-symbols "^1.0.3" internal-slot "^1.0.3" is-callable "^1.2.4" - is-negative-zero "^2.0.1" + is-negative-zero "^2.0.2" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" + is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" + is-weakref "^1.0.2" + object-inspect "^1.12.0" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2374,7 +2401,7 @@ eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" -eslint-module-utils@^2.7.2: +eslint-module-utils@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== @@ -2396,23 +2423,23 @@ eslint-plugin-es@^3.0.0: regexpp "^3.0.0" eslint-plugin-import@^2.25.4: - version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" - integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.2" + eslint-module-utils "^2.7.3" has "^1.0.3" - is-core-module "^2.8.0" + is-core-module "^2.8.1" is-glob "^4.0.3" - minimatch "^3.0.4" + minimatch "^3.1.2" object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.12.0" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" eslint-plugin-json@^3.1.0: version "3.1.0" @@ -2482,11 +2509,11 @@ eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.10.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d" - integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw== + version "8.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.14.0.tgz#62741f159d9eb4a79695b28ec4989fcdec623239" + integrity sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw== dependencies: - "@eslint/eslintrc" "^1.2.0" + "@eslint/eslintrc" "^1.2.2" "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" @@ -2570,15 +2597,10 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - expect-webdriverio@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-3.2.0.tgz#dee513bc89e2b499bedef861739df1438a6c387c" - integrity sha512-NZplTSIrLVMs0CfBgiX936n9jRzucckvt+2Ecct2ki80s3bAS/uy253w+aPbXlginKygdeT5bNs0Tfmo8ZR88Q== + version "3.2.1" + resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-3.2.1.tgz#7db8f450784b7e9fa231798f9761bffd70586d3a" + integrity sha512-bN3FGCKvddmtlt1A8SW3A1HK9lUbNybrvwUEDu5nEjJMwC8oTnE+9kDogA/6aEN1kRX9cX3tKB8WdyqbGPYnoA== dependencies: expect "^27.0.2" jest-matcher-utils "^27.0.2" @@ -2680,11 +2702,11 @@ file-entry-cache@^6.0.1: flat-cache "^3.0.4" filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" + integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q== dependencies: - minimatch "^3.0.4" + minimatch "^5.0.1" fill-range@^7.0.1: version "7.0.1" @@ -2769,9 +2791,9 @@ fs-constants@^1.0.0: integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" - integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -2838,11 +2860,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -2907,9 +2924,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.12.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb" - integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw== + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== dependencies: type-fest "^0.20.2" @@ -2952,9 +2969,9 @@ got@^11.0.2, got@^11.8.1: responselike "^2.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== grapheme-splitter@^1.0.2: version "1.0.4" @@ -2973,10 +2990,10 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" @@ -2988,7 +3005,14 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -3030,7 +3054,7 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: +https-proxy-agent@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== @@ -3038,6 +3062,14 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -3055,11 +3087,6 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.1.1, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -3101,10 +3128,10 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.5.tgz#2dc5159203c826d654915b5fe6990fd17f54a150" - integrity sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg== +inquirer@8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" + integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -3116,7 +3143,7 @@ inquirer@8.1.5: mute-stream "0.0.8" ora "^5.4.1" run-async "^2.4.0" - rxjs "^7.2.0" + rxjs "^7.5.5" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -3167,10 +3194,10 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.8.0, is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" @@ -3213,15 +3240,15 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-negative-zero@^2.0.1: +is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" @@ -3260,10 +3287,12 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" is-stream@^2.0.0: version "2.0.1" @@ -3299,7 +3328,7 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-weakref@^1.0.1: +is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== @@ -3337,13 +3366,13 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -jake@^10.6.1: - version "10.8.2" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" - integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== dependencies: - async "0.9.x" - chalk "^2.4.2" + async "^3.2.3" + chalk "^4.0.2" filelist "^1.0.1" minimatch "^3.0.4" @@ -3409,7 +3438,7 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-buffer@3.0.1: +json-buffer@3.0.1, json-buffer@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== @@ -3438,12 +3467,10 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== jsonc-parser@^3.0.0: version "3.0.0" @@ -3460,15 +3487,16 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" jsrsasign@^10.5.10: - version "10.5.10" - resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.10.tgz#82379cabc27a5a765e1ede3c754a3ab801070a82" - integrity sha512-iwLwWdJVfMjC9ShDvkn1fco9qrjF9J8r4tzeIcgevmSq9Uy+7nSusKQtXCHaY1flffI46DhYuG8wkjn0Ie4HCQ== + version "10.5.20" + resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.20.tgz#515a854a47c309cb350f32860dd37bfad1b81800" + integrity sha512-YHL6y8o6cnRoxwUY0eGpfvwj0pm9o0NToD4KXVp2UJC19oXSR2RgnSdSMplIRRKFovLAJGrAHqdb5MMznnhQDQ== keyv@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a" - integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ== + version "4.2.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.2.2.tgz#4b6f602c0228ef4d8214c03c520bef469ed6b768" + integrity sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ== dependencies: + compress-brotli "^1.3.6" json-buffer "3.0.1" kind-of@^6.0.2: @@ -3510,9 +3538,9 @@ lilconfig@2.0.4: integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== lint-staged@^12.3.5: - version "12.3.5" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.3.5.tgz#8048ce048c3cac12f57200a06344a54dc91c8fa9" - integrity sha512-oOH36RUs1It7b9U/C7Nl/a0sLfoIBcMB8ramiB3nuJ6brBqzsWiUAFSR5DQ3yyP/OR7XKMpijtgKl2DV1lQ3lA== + version "12.4.1" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.4.1.tgz#63fa27bfc8a33515f6902f63f6670864f1fb233c" + integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== dependencies: cli-truncate "^3.1.0" colorette "^2.0.16" @@ -3524,6 +3552,7 @@ lint-staged@^12.3.5: micromatch "^4.0.4" normalize-path "^3.0.0" object-inspect "^1.12.0" + pidtree "^0.5.0" string-argv "^0.3.1" supports-color "^9.2.1" yaml "^1.10.2" @@ -3543,9 +3572,9 @@ listr2@^4.0.1: wrap-ansi "^7.0.0" livereload-js@^3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.3.3.tgz#3e4f5699f741fdf8be6dc9c46c523e4fc1abbd0d" - integrity sha512-a7Jipme3XIBIryJluWP5LQrEAvhobDPyScBe+q+MYwxBiMT2Ck7msy4tAdF8TAa33FMdJqX4guP81Yhiu6BkmQ== + version "3.4.0" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.4.0.tgz#2083158125f16fb5207141bbadb9bdc26cfdb1ef" + integrity sha512-F/pz9ZZP+R+arY94cECTZco7PXgBXyL+KVWUPZq8AQE9TOu14GV6fYeKOviv02JCvFa4Oi3Rs1hYEpfeajc+ow== livereload@^0.9.3: version "0.9.3" @@ -3745,12 +3774,12 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" mimic-fn@^2.1.0: version "2.1.0" @@ -3779,14 +3808,21 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4: +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.0: +minimatch@^5.0.0, minimatch@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== @@ -3800,10 +3836,10 @@ minimatch@~3.0.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp-classic@^0.5.2: version "0.5.3" @@ -3816,9 +3852,9 @@ mkdirp@^1.0.4: integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mocha@^9.0.0: - version "9.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.1.tgz#a1abb675aa9a8490798503af57e8782a78f1338e" - integrity sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ== + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" @@ -3833,9 +3869,9 @@ mocha@^9.0.0: he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "3.0.4" + minimatch "4.2.1" ms "2.1.3" - nanoid "3.2.0" + nanoid "3.3.1" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" @@ -3865,10 +3901,10 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nanoid@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" - integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== natural-compare@^1.4.0: version "1.4.0" @@ -3883,14 +3919,14 @@ node-fetch@2.6.7: whatwg-url "^5.0.0" node-forge@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.2.1.tgz#82794919071ef2eb5c509293325cec8afd0fd53c" - integrity sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w== + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-jose@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.1.0.tgz#a2d12a7ff2d386f23979c1bf77f939449ce073d8" - integrity sha512-Zmm8vFPJabphGBc5Wz1/LUMPS+1cynqw16RIhgVNQMEI2yEQrvl7Gx2EwN9GhP8tkm8f7SH53K2nIx8TeNTIdg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.1.1.tgz#7d82e8d9cef8d0a722d7fa385524babfa9e30614" + integrity sha512-19nyuUGShNmFmVTeqDfP6ZJCiikbcjI0Pw2kykBCH7rl8AZgSiDZK2Ww8EDaMrOSbRg6IlfIMhI5ZvCklmOhzg== dependencies: base64url "^3.0.1" buffer "^6.0.3" @@ -3902,10 +3938,10 @@ node-jose@^2.1.0: process "^0.11.10" uuid "^8.3.2" -node-releases@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +node-releases@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" + integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== normalize-package-data@^2.3.2: version "2.5.0" @@ -3934,12 +3970,12 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -object-inspect@^1.10.3, object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0: +object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -4175,11 +4211,16 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pidtree@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" + integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4227,9 +4268,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== pretty-format@^27.5.1: version "27.5.1" @@ -4247,11 +4288,6 @@ pretty-ms@^7.0.0: dependencies: parse-ms "^2.1.0" -printj@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.3.1.tgz#9af6b1d55647a1587ac44f4c1654a4b95b8e12cb" - integrity sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -4291,13 +4327,13 @@ punycode@^2.1.0: integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== puppeteer-core@^13.1.3: - version "13.5.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-13.5.1.tgz#74d902d8f4cbc003c4cb15c647bd070cc83539e7" - integrity sha512-dobVqWjV34ilyfQHR3BBnCYaekBYTi5MgegEYBRYd3s3uFy8jUpZEEWbaFjG9ETm+LGzR5Lmr0aF6LLuHtiuCg== + version "13.6.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-13.6.0.tgz#1626efbe789c19000a4c15309dbe55db5e936724" + integrity sha512-n692xT9uOTxbFKcCRkfOT2Go5LL0YBCHrSpBdbNsjLhcxO5yuhj2/4jgAIK9bT1blY17Pb4I35eBSuDzJ54ERw== dependencies: cross-fetch "3.1.5" - debug "4.3.3" - devtools-protocol "0.0.969999" + debug "4.3.4" + devtools-protocol "0.0.981744" extract-zip "2.0.1" https-proxy-agent "5.0.0" pkg-dir "4.2.0" @@ -4412,10 +4448,10 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== dependencies: "@babel/runtime" "^7.8.4" @@ -4463,7 +4499,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.3.3: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.3: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -4839,9 +4875,9 @@ supports-color@^7.1.0: has-flag "^4.0.0" supports-color@^9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.1.tgz#599dc9d45acf74c6176e0d880bab1d7d718fe891" - integrity sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ== + version "9.2.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.2.tgz#502acaf82f2b7ee78eb7c83dcac0f89694e5a7bb" + integrity sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA== supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" @@ -4911,20 +4947,20 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -tsconfig-paths@^3.12.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7" - integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw== +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" tslib@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -4954,13 +4990,13 @@ ua-parser-js@^1.0.1: integrity sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg== unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" unbzip2-stream@1.4.3: @@ -5030,9 +5066,9 @@ validate-npm-package-license@^3.0.1: spdx-expression-parse "^3.0.0" vscode-json-languageservice@^4.1.6: - version "4.2.0" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.0.tgz#df0693b69ba2fbf0a6add896087b6f1c9c38f06a" - integrity sha512-XNawv0Vdy/sUK0S+hGf7cq/qsVAbIniGJr89TvZOqMCNJmpgKTy1e8PL1aWW0uy6BfWMG7vxa5lZb3ypuFtuGQ== + version "4.2.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" + integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== dependencies: jsonc-parser "^3.0.0" vscode-languageserver-textdocument "^1.0.3" @@ -5051,9 +5087,9 @@ vscode-languageserver-types@^3.16.0: integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" @@ -5068,51 +5104,50 @@ wcwidth@^1.0.1: defaults "^1.0.3" wdio-chromedriver-service@^7.2.8: - version "7.2.8" - resolved "https://registry.yarnpkg.com/wdio-chromedriver-service/-/wdio-chromedriver-service-7.2.8.tgz#18091be0c5f56cab3e6eb32613fe916b14dbd792" - integrity sha512-7JGmcTOBN15jvQPxTeC7t/xsQmlbttcDZStv36iUjbpuIXi7/Br+4pkttd+ES+w5eSJd2A5Ioe2ALRuTd3NeQQ== + version "7.3.2" + resolved "https://registry.yarnpkg.com/wdio-chromedriver-service/-/wdio-chromedriver-service-7.3.2.tgz#569053df4ceaf6ce9e43bebcdd540197f516e313" + integrity sha512-4M3OqFzBSC4FdbqkfwOrUMeroMEuyIFCHfcUebkU6tJ1w5GenWO61YweJ8NKlhPZx9nkO8223+20MpvBjv+fTg== dependencies: "@wdio/logger" "^7.5.3" fs-extra "^9.1.0" split2 "^3.2.2" tcp-port-used "^1.0.1" -webdriver@7.17.3: - version "7.17.3" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-7.17.3.tgz#145990fbb3451c07fbdcb496e7b72c5655cf0b9d" - integrity sha512-E1V/IKYjJoVjK9zhHfSCWeqORhgNlDuYydykm0h+CchEhMSgTmtTH/LYfXSx4myXzobdlIg6xhE7Jv7XPjSkAA== +webdriver@7.19.5: + version "7.19.5" + resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-7.19.5.tgz#485db210517d44e2e572a5a53a3294afee87ca8e" + integrity sha512-kdOC6w2LSZO2zQ+sNQjHRP4CVQzo1C+g2EDz7g3czOQv2Oo8WqkVlMT9hIBiMLHQfh896gFh776GUCDnEd9emg== dependencies: "@types/node" "^17.0.4" - "@wdio/config" "7.17.3" - "@wdio/logger" "7.17.3" - "@wdio/protocols" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/config" "7.19.5" + "@wdio/logger" "7.19.0" + "@wdio/protocols" "7.19.0" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" got "^11.0.2" ky "^0.30.0" lodash.merge "^4.6.1" -webdriverio@7.17.4, webdriverio@^7.17.4: - version "7.17.4" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-7.17.4.tgz#4261cf9631822d4d391bfb6953fb5a8e89f7e377" - integrity sha512-p7u2q7NJL7Et8FdSroq/Ltoi3KkKxERE79Srh9lFr6yRNPFqb46dJf/g4nljLhburnGkbNdYN15JWgyWYnnj9g== +webdriverio@7.19.5, webdriverio@^7.17.4: + version "7.19.5" + resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-7.19.5.tgz#ba5edc75da6cea93da03bbe67b60e5d435b92269" + integrity sha512-FDq6ULiXFhAzIxQ+NI/GprYoouT5GUDhdXNJkMHYT6wBSC0shEtbpu8+ciAB7lqbsD/D9pADePtj+18+gEG0zA== dependencies: "@types/aria-query" "^5.0.0" "@types/node" "^17.0.4" - "@wdio/config" "7.17.3" - "@wdio/logger" "7.17.3" - "@wdio/protocols" "7.17.3" - "@wdio/repl" "7.17.3" - "@wdio/types" "7.17.3" - "@wdio/utils" "7.17.3" + "@wdio/config" "7.19.5" + "@wdio/logger" "7.19.0" + "@wdio/protocols" "7.19.0" + "@wdio/repl" "7.19.5" + "@wdio/types" "7.19.5" + "@wdio/utils" "7.19.5" archiver "^5.0.0" aria-query "^5.0.0" css-shorthand-properties "^1.1.1" css-value "^0.0.1" - devtools "7.17.3" - devtools-protocol "^0.0.979353" + devtools "7.19.5" + devtools-protocol "^0.0.982423" fs-extra "^10.0.0" - get-port "^5.1.1" grapheme-splitter "^1.0.2" lodash.clonedeep "^4.5.0" lodash.isobject "^3.0.2" @@ -5124,7 +5159,7 @@ webdriverio@7.17.4, webdriverio@^7.17.4: resq "^1.9.1" rgb2hex "0.2.5" serialize-error "^8.0.0" - webdriver "7.17.3" + webdriver "7.19.5" webidl-conversions@^3.0.0: version "3.0.1" @@ -5261,9 +5296,9 @@ yargs@16.2.0: yargs-parser "^20.2.2" yargs@^17.0.0: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== + version "17.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284" + integrity sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g== dependencies: cliui "^7.0.2" escalade "^3.1.1" From db78fb574a4efde029e24bc2219ad7eb147575e5 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 27 Apr 2022 13:04:30 +0100 Subject: [PATCH 011/567] Add new help link to business survey config (#844) --- README.md | 4 +- app/helpers/template_helpers.py | 4 +- app/survey_config/business_config.py | 56 ++++++++++++++------ app/survey_config/survey_config.py | 1 + app/translations/messages.pot | 34 ++++++------ tests/app/helpers/test_template_helpers.py | 5 ++ tests/integration/components/test_address.py | 4 +- tests/integration/routes/test_errors.py | 1 + tests/integration/test_header_links.py | 39 ++++++++++++++ 9 files changed, 112 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 30e043b019..22b8432007 100644 --- a/README.md +++ b/README.md @@ -382,8 +382,8 @@ The following env variables can be used | WEB_SERVER_THREADS | | The number of worker threads per worker | | WEB_SERVER_UWSGI_ASYNC_CORES | | The number of cores to initialise when using "uwsgi-async" web server worker type | | DATASTORE_USE_GRPC | False | Determines whether to use gRPC for Datastore. gRPC is currently only supported for threaded web servers | -| ACCOUNT_SERVICE_BASE_URL | `https://surveys.ons.gov.uk` | The base URL of the account service used to launch the survey | -| ONS_URL | `https://www.ons.gov.uk` | The URL of the ONS website where static content is sourced, e.g. accessibility info | +| ACCOUNT_SERVICE_BASE_URL | `https://surveys.ons.gov.uk` | The base URL of the account service used to launch the survey | +| ONS_URL | `https://www.ons.gov.uk` | The URL of the ONS website where static content is sourced, e.g. accessibility info | The following env variables can be used when running tests diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 35e3a04f36..e5b68c7228 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -9,7 +9,7 @@ from flask_babel import LazyString, get_locale, lazy_gettext from flask_login import current_user -from app.globals import get_session_store +from app.globals import get_metadata, get_session_store from app.helpers.language_helper import get_languages_context from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL @@ -78,9 +78,11 @@ def context(self) -> dict[str, Any]: def service_links_context( self, ) -> Optional[dict[str, Union[dict[str, str], list[dict]]]]: + metadata = get_metadata(current_user) if service_links := self._survey_config.get_service_links( sign_out_url=self._sign_out_url, is_authenticated=current_user.is_authenticated, + ru_ref=metadata.get("ru_ref") if metadata else None, # type: ignore ): return { "toggleServicesButton": { diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index e64057d5f8..84ac9c0ba1 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, field from typing import Iterable, Mapping, MutableMapping, Optional +from urllib.parse import urlencode from warnings import warn from flask_babel import lazy_gettext @@ -54,23 +55,48 @@ def __post_init__(self): ).__dict__, ] + def _get_account_service_help_url( + self, *, is_authenticated: bool, ru_ref: Optional[str] + ) -> str: + if self.schema and is_authenticated and ru_ref: + request_data = { + "survey_ref": self.schema.json["survey_id"], + "ru_ref": ru_ref, + } + return f"{self.base_url}/surveys/surveys-help?{urlencode(request_data)}" + + return f"{self.base_url}/help" + def get_service_links( - self, sign_out_url: str, *, is_authenticated: bool + self, sign_out_url: str, *, is_authenticated: bool, ru_ref: Optional[str] ) -> Optional[list[dict]]: - return ( - [ - HeaderLink( - lazy_gettext("My account"), - self.account_service_my_account_url, - id="header-link-my-account", - ).__dict__, - HeaderLink( - lazy_gettext("Sign out"), sign_out_url, id="header-link-sign-out" - ).__dict__, - ] - if is_authenticated - else None - ) + links = [ + HeaderLink( + lazy_gettext("Help"), + self._get_account_service_help_url( + is_authenticated=is_authenticated, ru_ref=ru_ref + ), + id="header-link-help", + ).__dict__ + ] + + if is_authenticated: + links.extend( + [ + HeaderLink( + lazy_gettext("My account"), + self.account_service_my_account_url, + id="header-link-my-account", + ).__dict__, + HeaderLink( + lazy_gettext("Sign out"), + sign_out_url, + id="header-link-sign-out", + ).__dict__, + ] + ) + + return links @property def _stripped_base_url(self) -> str: diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index e8fca6accf..7d8bf2d090 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -58,5 +58,6 @@ def get_service_links( # pylint: disable=unused-argument, no-self-use sign_out_url: str, *, is_authenticated: bool, + ru_ref: Optional[str], ) -> Optional[list[dict]]: return None diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 34106e60a5..22072b3920 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -295,41 +295,48 @@ msgstr "" msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:33 +#: app/survey_config/business_config.py:51 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:34 +#: app/survey_config/business_config.py:52 #: app/survey_config/census_config.py:30 app/survey_config/census_config.py:80 #: app/survey_config/census_config.py:141 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:36 +#: app/survey_config/business_config.py:54 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:41 +#: app/survey_config/business_config.py:59 #: app/survey_config/census_config.py:44 app/survey_config/census_config.py:95 #: app/survey_config/census_config.py:148 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:43 +#: app/survey_config/business_config.py:61 #: app/survey_config/census_config.py:50 app/survey_config/census_config.py:101 #: app/survey_config/census_config.py:154 msgid "Privacy and data protection" msgstr "" -#: app/survey_config/business_config.py:54 +#: app/survey_config/business_config.py:72 +#: app/survey_config/business_config.py:88 +#: app/survey_config/census_config.py:27 app/survey_config/census_config.py:77 +#: app/survey_config/census_config.py:138 +msgid "Help" +msgstr "" + +#: app/survey_config/business_config.py:77 msgid "My account" msgstr "" -#: app/survey_config/business_config.py:59 +#: app/survey_config/business_config.py:82 msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:78 +#: app/survey_config/business_config.py:107 msgid "Northern Ireland Department of Finance logo" msgstr "" @@ -337,11 +344,6 @@ msgstr "" msgid "Census 2021" msgstr "" -#: app/survey_config/census_config.py:27 app/survey_config/census_config.py:77 -#: app/survey_config/census_config.py:138 -msgid "Help" -msgstr "" - #: app/survey_config/census_config.py:32 app/survey_config/census_config.py:82 msgid "Languages" msgstr "" @@ -372,15 +374,15 @@ msgstr "" msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:132 app/survey_config/survey_config.py:21 +#: app/survey_config/census_config.py:132 app/survey_config/survey_config.py:23 msgid "Use of address data is subject to the terms and conditions." msgstr "" -#: app/survey_config/survey_config.py:15 +#: app/survey_config/survey_config.py:17 msgid "Office for National Statistics logo" msgstr "" -#: app/survey_config/survey_config.py:18 +#: app/survey_config/survey_config.py:20 msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index ecf06821a3..7e43cc6dc9 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -165,6 +165,11 @@ def test_get_page_header_context_census_nisra(app: Flask): "ariaLabel": "Toggle services menu", }, "itemsList": [ + { + "title": "Help", + "url": "https://surveys.ons.gov.uk/help", + "id": "header-link-help", + }, { "title": "My account", "url": "https://surveys.ons.gov.uk/my-account", diff --git a/tests/integration/components/test_address.py b/tests/integration/components/test_address.py index ba413cad18..cb896ffbb0 100644 --- a/tests/integration/components/test_address.py +++ b/tests/integration/components/test_address.py @@ -104,14 +104,14 @@ def test_uprn_field_not_displayed_on_summary(self): self.post( { "address-mandatory-line1": "first address", - "address-mandatory-uprn": "123456789", + "address-mandatory-uprn": "0123456789", } ) self.post({}) # Then - self.assertNotInBody("123456789") + self.assertNotInBody("0123456789") def test_auth_token_not_in_page(self): self.assertNotInBody("data-authorization-token") diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 49e9ad6255..e0f4491520 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -27,6 +27,7 @@ def test_errors_404(self): self.assertStatusNotFound() # Test that my account link does not show + self.assertInBody("Help") self.assertNotInBody("My account") self.assertNotInBody("Sign out") diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index c3a23c0cf3..f061dc42a8 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -1,3 +1,4 @@ +from app.settings import ACCOUNT_SERVICE_BASE_URL from tests.integration.create_token import ACCOUNT_SERVICE_URL from tests.integration.integration_test_case import IntegrationTestCase @@ -25,6 +26,38 @@ def assert_sign_out_link_does_not_exist(self): self.assertIsNone(sign_out_link) self.assertNotInBody("Sign out") + def assert_help_link_exist(self): + help_link = self.getLinkById("header-link-help") + self.assertIsNotNone(help_link) + self.assertEqual(help_link.text, "Help") + self.assertEqual( + help_link["href"], + f"{ACCOUNT_SERVICE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=123456789012A", + ) + + def assert_help_link_exist_not_authenticated(self): + help_link = self.getLinkById("header-link-help") + self.assertIsNotNone(help_link) + self.assertEqual(help_link.text, "Help") + self.assertEqual( + help_link["href"], + f"{ACCOUNT_SERVICE_BASE_URL}/help", + ) + + def assert_help_link_exist_not_authenticated_after_sign_out(self): + help_link = self.getLinkById("header-link-help") + self.assertIsNotNone(help_link) + self.assertEqual(help_link.text, "Help") + self.assertEqual( + help_link["href"], + f"{ACCOUNT_SERVICE_URL}/help", + ) + + def assert_help_link_does_not_exist(self): + help_link = self.getLinkById("header-link-help") + self.assertIsNone(help_link) + self.assertNotInBody("Help") + class TestHeaderLinksPreSubmission(TestHeaderLinks): def test_links_in_header_when_valid_session(self): @@ -37,6 +70,7 @@ def test_links_in_header_when_valid_session(self): # Then self.assert_my_account_link_exist() self.assert_sign_out_link_exist() + self.assert_help_link_exist() def test_links_not_in_header_when_no_session(self): # Given @@ -48,6 +82,7 @@ def test_links_not_in_header_when_no_session(self): # Then self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() + self.assert_help_link_exist_not_authenticated() class TestHeaderLinksPostSubmission(TestHeaderLinks): @@ -64,6 +99,7 @@ def test_links_in_header_when_valid_session(self): # Then self.assert_my_account_link_exist() self.assert_sign_out_link_exist() + self.assert_help_link_exist() def test_links_not_in_header_when_no_session(self): # Given @@ -75,6 +111,7 @@ def test_links_not_in_header_when_no_session(self): # Then self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() + self.assert_help_link_exist_not_authenticated() class TestHeaderLinksPostSignOut(TestHeaderLinks): @@ -83,6 +120,7 @@ def test_links_not_in_header_after_sign_out(self): self.launchSurvey("test_thank_you") self.assert_my_account_link_exist() self.assert_sign_out_link_exist() + self.assert_help_link_exist() # When I sign out and go back to previous url since we will be redirected current_url = self.last_url @@ -93,3 +131,4 @@ def test_links_not_in_header_after_sign_out(self): self.assertInBody("Sorry, you need to sign in again") self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() + self.assert_help_link_exist_not_authenticated_after_sign_out() From 910ed65d285620ddd58d06b6e06c14c9931af477 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 28 Apr 2022 12:44:27 +0100 Subject: [PATCH 012/567] Bug fix: Only include first 11 characters of ru_ref in help link (#855) --- app/survey_config/business_config.py | 6 +++- tests/app/helpers/test_template_helpers.py | 33 ++++++++++++++++++---- tests/integration/test_header_links.py | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 84ac9c0ba1..798ea562d8 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -61,7 +61,11 @@ def _get_account_service_help_url( if self.schema and is_authenticated and ru_ref: request_data = { "survey_ref": self.schema.json["survey_id"], - "ru_ref": ru_ref, + # This is a temporary fix to send upstream only the first 11 characters of the ru_ref. + # The ru_ref currently is concatenated with the check letter. Which upstream currently do not support. + # The first 11 characters represents the reporting unit reference. + # The 12th character is the check letter identifier. + "ru_ref": ru_ref[:11], } return f"{self.base_url}/surveys/surveys-help?{urlencode(request_data)}" diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 7e43cc6dc9..23d7f828e9 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -4,6 +4,7 @@ from flask import Flask, current_app from app.helpers.template_helpers import ContextHelper, get_survey_config +from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( BusinessSurveyConfig, @@ -158,7 +159,7 @@ def test_get_page_header_context_census_nisra(app: Flask): ), ( BusinessSurveyConfig(), - True, + False, { "toggleServicesButton": { "text": "Menu", @@ -169,6 +170,23 @@ def test_get_page_header_context_census_nisra(app: Flask): "title": "Help", "url": "https://surveys.ons.gov.uk/help", "id": "header-link-help", + } + ], + }, + ), + ( + BusinessSurveyConfig(schema=QuestionnaireSchema({"survey_id": "001"})), + True, + { + "toggleServicesButton": { + "text": "Menu", + "ariaLabel": "Toggle services menu", + }, + "itemsList": [ + { + "title": "Help", + "url": "https://surveys.ons.gov.uk/surveys/surveys-help?survey_ref=001&ru_ref=63782964754", + "id": "header-link-help", }, { "title": "My account", @@ -189,10 +207,15 @@ def test_service_links_context( app: Flask, mocker, survey_config, is_authenticated, expected ): with app.app_context(): - current_user = mocker.patch( - "flask_login.utils._get_user", return_value=mocker.MagicMock() - ) - current_user.is_authenticated = is_authenticated + mocked_current_user = mocker.Mock() + mocked_current_user.is_authenticated = is_authenticated + mocker.patch("flask_login.utils._get_user", return_value=mocked_current_user) + + if is_authenticated: + mocker.patch( + "app.helpers.template_helpers.get_metadata", + return_value={"ru_ref": "63782964754U"}, + ) result = ContextHelper( language="en", diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index f061dc42a8..4ee76c1228 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -32,7 +32,7 @@ def assert_help_link_exist(self): self.assertEqual(help_link.text, "Help") self.assertEqual( help_link["href"], - f"{ACCOUNT_SERVICE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=123456789012A", + f"{ACCOUNT_SERVICE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=12345678901", ) def assert_help_link_exist_not_authenticated(self): From b4001f86f4f731ff848db7189aa8fcf2abdd147e Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 28 Apr 2022 15:59:12 +0100 Subject: [PATCH 013/567] Rename survey-url to schema-url (#846) --- app/authentication/authenticator.py | 2 +- app/data_models/session_data.py | 4 +- app/utilities/metadata_parser.py | 2 +- app/utilities/schema.py | 18 ++++---- doc/architecture/decisions/0010-cookies.md | 18 ++++---- tests/app/conftest.py | 2 +- tests/app/utilities/test_schema.py | 12 +++--- tests/app/views/contexts/conftest.py | 2 +- tests/app/views/handlers/conftest.py | 6 +-- tests/integration/create_token.py | 10 ++--- tests/integration/session/test_login.py | 48 +++++++++++----------- 11 files changed, 62 insertions(+), 62 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index e265d29ec5..14e3c408e5 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -128,7 +128,7 @@ def _create_session_data_from_metadata(metadata: Mapping[str, Any]) -> SessionDa period_str=metadata.get("period_str"), language_code=metadata.get("language_code"), launch_language_code=metadata.get("language_code"), - survey_url=metadata.get("survey_url"), + schema_url=metadata.get("schema_url"), ru_name=metadata.get("ru_name"), ru_ref=metadata.get("ru_ref"), response_id=metadata.get("response_id"), diff --git a/app/data_models/session_data.py b/app/data_models/session_data.py index 0457b2149b..e3a26869f4 100644 --- a/app/data_models/session_data.py +++ b/app/data_models/session_data.py @@ -9,7 +9,7 @@ def __init__( period_str: Optional[str], language_code: Optional[str], launch_language_code: Optional[str], - survey_url: Optional[str], + schema_url: Optional[str], ru_name: Optional[str], ru_ref: Optional[str], response_id: Optional[str], @@ -28,7 +28,7 @@ def __init__( self.period_str = period_str self.language_code = language_code self.launch_language_code = launch_language_code - self.survey_url = survey_url + self.schema_url = schema_url self.ru_name = ru_name self.ru_ref = ru_ref self.response_id = response_id diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index a6a4f2e426..3730056b77 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -92,7 +92,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): case_id = VALIDATORS["uuid"]() # type:ignore account_service_log_out_url = VALIDATORS["url"](required=False) # type:ignore roles = fields.List(fields.String(), required=False) - survey_url = VALIDATORS["url"](required=False) # type:ignore + schema_url = VALIDATORS["url"](required=False) # type:ignore language_code = VALIDATORS["string"](required=False) # type:ignore channel = VALIDATORS["string"]( required=False, validate=validate.Length(min=1) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 4598e084e4..29923ae34b 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -81,13 +81,13 @@ def get_allowed_languages(schema_name, launch_language): def load_schema_from_metadata(metadata): - if survey_url := metadata.get("survey_url"): - # :TODO: Remove before production uses survey_url + if schema_url := metadata.get("schema_url"): + # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. # This should not be used in production. start = time.time() - schema = load_schema_from_url(survey_url, metadata.get("language_code")) + schema = load_schema_from_url(schema_url, metadata.get("language_code")) duration_in_milliseconds = (time.time() - start) * 1_000 cache_info = ( @@ -95,7 +95,7 @@ def load_schema_from_metadata(metadata): ) logger.info( f"load_schema_from_url took {duration_in_milliseconds:.6f} milliseconds", - survey_url=survey_url, + schema_url=schema_url, currsize=cache_info.currsize, hits=cache_info.hits, misses=cache_info.misses, @@ -165,19 +165,19 @@ def _load_schema_file(schema_name, language_code): @lru_cache(maxsize=None) -def load_schema_from_url(survey_url, language_code): +def load_schema_from_url(schema_url, language_code): language_code = language_code or DEFAULT_LANGUAGE_CODE pid = os.getpid() logger.info( "loading schema from URL", - survey_url=survey_url, + schema_url=schema_url, language_code=language_code, pid=pid, ) - constructed_survey_url = f"{survey_url}?language={language_code}" + constructed_schema_url = f"{schema_url}?language={language_code}" - req = requests.get(constructed_survey_url) + req = requests.get(constructed_schema_url) schema_response = req.content.decode() response_duration_in_milliseconds = req.elapsed.total_seconds() * 1000 @@ -187,7 +187,7 @@ def load_schema_from_url(survey_url, language_code): ) if req.status_code == 404: - logger.error("no schema exists", survey_url=constructed_survey_url) + logger.error("no schema exists", schema_url=constructed_schema_url) raise NotFound return QuestionnaireSchema(json_loads(schema_response), language_code) diff --git a/doc/architecture/decisions/0010-cookies.md b/doc/architecture/decisions/0010-cookies.md index c4b4998f53..276a98fa82 100644 --- a/doc/architecture/decisions/0010-cookies.md +++ b/doc/architecture/decisions/0010-cookies.md @@ -1,8 +1,8 @@ # 10. Cookies -## Context +## Context -We should be clear about what we store in the session cookie and why. +We should be clear about what we store in the session cookie and why. We store a number of properties in the `session` cookie: @@ -17,14 +17,14 @@ We store a number of properties in the `session` cookie: | account_service_url | The link back to the launch service. Used on the signed out and session expired pages. From metadata claims (`account_service_url`) | | account_service_log_out_url | The The URL to redirect to on signout. From metadata claims (`account_service_log_out_url`) | -- The CSRF token changes per request, meaning that the session cookie is set for most requests. +- The CSRF token changes per request, meaning that the session cookie is set for most requests. -- Language is accessed from the server-side session via the Babel `get_locale` method. This means it's only available if the user has a valid session; when the session has timed out, screens are not in the appropriate language. +- Language is accessed from the server-side session via the Babel `get_locale` method. This means it's only available if the user has a valid session; when the session has timed out, screens are not in the appropriate language. ## Decision -- Continue using the session cookie for properties that are set on sign-in as they are relevant to the current session. Storing them in the cookie rather than the server-side session means that they are still accessible after the server-side session times out. -- Store `schema_name` or `survey_url` in the cookie and remove schema properties i.e. `theme`, `survey_title` and `expires_in`. This simplifies runner code as we will always be able to load a schema for any request (if they have successfully launched a questionnaire), and it provides a way to use other schema properties without adding to the cookie. +- Continue using the session cookie for properties that are set on sign-in as they are relevant to the current session. Storing them in the cookie rather than the server-side session means that they are still accessible after the server-side session times out. +- Store `schema_name` or `schema_url` in the cookie and remove schema properties i.e. `theme`, `survey_title` and `expires_in`. This simplifies runner code as we will always be able to load a schema for any request (if they have successfully launched a questionnaire), and it provides a way to use other schema properties without adding to the cookie. - The session cookie will contain: | Property | Description | @@ -34,15 +34,15 @@ We store a number of properties in the `session` cookie: | csrf_token | The CSRF token (generated for each request) | | account_service_url | The link back to the launch service | | account_service_log_out_url | The The URL to redirect to on signout | - | schema_name | The schema filename | - | survey_url | A URL to a schema | + | schema_name | The schema filename | + | schema_url | A URL to a schema | - The `session` cookie should be set once on successful authentication. To enable this, we will review our CSRF token implementation - one per session or a separate cookie. - Set `language` in a cookie, so it’s not lost as a preference when there is no server-side session. This will be stored in a separate cookie as the user can change it. - Any future user preferences that need to be remembered will be stored in separate named cookies. - + ## Additional information - An `ons_cookie_policy` cookie is set in the front end and is used to enable/disable Google tracking. diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 09c3a368f3..b02db6203b 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -86,7 +86,7 @@ def session_data(): period_str="period_str", language_code=None, launch_language_code=None, - survey_url=None, + schema_url=None, ru_name="ru_name", ru_ref="ru_ref", response_id="response_id", diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 0660643168..6515c4e998 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -144,7 +144,7 @@ def test_load_schema_from_url_200(): mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) - loaded_schema = load_schema_from_url(survey_url=TEST_SCHEMA_URL, language_code="cy") + loaded_schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") assert loaded_schema.json == mock_schema.json assert loaded_schema.language_code == mock_schema.language_code @@ -163,7 +163,7 @@ def test_load_schema_from_url_404(): responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=404) with pytest.raises(NotFound): - load_schema_from_url(survey_url=TEST_SCHEMA_URL, language_code="en") + load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 0 @@ -179,7 +179,7 @@ def test_load_schema_from_url_uses_cache(): responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) # First load: Add to cache, no hits - load_schema_from_url(survey_url=TEST_SCHEMA_URL, language_code="cy") + load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 1 @@ -187,7 +187,7 @@ def test_load_schema_from_url_uses_cache(): assert cache_info.hits == 0 # Second load: Read from cache, 1 hit - load_schema_from_url(survey_url=TEST_SCHEMA_URL, language_code="cy") + load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 1 @@ -196,10 +196,10 @@ def test_load_schema_from_url_uses_cache(): @responses.activate -def test_load_schema_from_metadata_with_survey_url(): +def test_load_schema_from_metadata_with_schema_url(): load_schema_from_url.cache_clear() - metadata = {"survey_url": TEST_SCHEMA_URL, "language_code": "cy"} + metadata = {"schema_url": TEST_SCHEMA_URL, "language_code": "cy"} mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) loaded_schema = load_schema_from_metadata(metadata=metadata) diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 3ba68b9222..66955621da 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -202,7 +202,7 @@ def fake_session_data(): period_str="period_str", language_code=None, launch_language_code=None, - survey_url=None, + schema_url=None, ru_name="ru_name", ru_ref="ru_ref", response_id="response_id", diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index e51fe677de..b88cb7e6c6 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -49,7 +49,7 @@ def session_data(): period_str=None, language_code="cy", launch_language_code="en", - survey_url=None, + schema_url=None, ru_name=None, ru_ref=None, submitted_at=datetime.now(timezone.utc).isoformat(), @@ -104,7 +104,7 @@ def session_data_feedback(): period_str=period_str, language_code=language_code, launch_language_code=None, - survey_url=None, + schema_url=None, ru_name=ru_name, ru_ref=ru_ref, case_id=case_id, @@ -160,7 +160,7 @@ def submission_payload_session_data(): period_str="period_str", language_code="cy", launch_language_code="en", - survey_url=None, + schema_url=None, ru_name="ru_name", ru_ref="ru_ref", case_id="0123456789000000", diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 2afeab2dd3..c272cf363f 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -34,12 +34,12 @@ def __init__(self, key_store, upstream_kid, sr_public_kid): self._sr_public_kid = sr_public_kid @staticmethod - def _get_payload_with_params(schema_name, survey_url=None, **extra_payload): + def _get_payload_with_params(schema_name, schema_url=None, **extra_payload): payload_vars = PAYLOAD.copy() payload_vars["tx_id"] = str(uuid4()) payload_vars["schema_name"] = schema_name - if survey_url: - payload_vars["survey_url"] = survey_url + if schema_url: + payload_vars["schema_url"] = schema_url payload_vars["iat"] = time.time() payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now @@ -74,9 +74,9 @@ def create_token_without_trad_as(self, schema_name, **extra_payload): return self.generate_token(payload_vars) - def create_token_with_survey_url(self, schema_name, survey_url, **extra_payload): + def create_token_with_schema_url(self, schema_name, schema_url, **extra_payload): payload_vars = self._get_payload_with_params( - schema_name, survey_url, **extra_payload + schema_name, schema_url, **extra_payload ) return self.generate_token(payload_vars) diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index ed5d576189..30f63e230e 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -104,31 +104,31 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() - def test_login_token_with_survey_url_should_redirect_to_survey(self): - survey_url = "http://eq-survey-register.url/my-test-schema" + def test_login_token_with_schema_url_should_redirect_to_survey(self): + schema_url = "http://eq-survey-register.url/my-test-schema" # Given - token = self.token_generator.create_token_with_survey_url( - "test_textarea", survey_url + token = self.token_generator.create_token_with_schema_url( + "test_textarea", schema_url ) # When - with HTTMock(self.survey_url_mock): + with HTTMock(self.schema_url_mock): self.get(url=f"/session?token={token}") self.assertStatusOK() self.assertInUrl("/questionnaire") - def test_login_token_with_incorrect_survey_url_results_in_404(self): - survey_url = "http://eq-survey-register.url/my-test-schema-not-found" + def test_login_token_with_incorrect_schema_url_results_in_404(self): + schema_url = "http://eq-survey-register.url/my-test-schema-not-found" # Given - token = self.token_generator.create_token_with_survey_url( - "test_textarea", survey_url + token = self.token_generator.create_token_with_schema_url( + "test_textarea", schema_url ) # When - with HTTMock(self.survey_url_mock_404): + with HTTMock(self.schema_url_mock_404): self.get(url=f"/session?token={token}") # Then @@ -136,7 +136,7 @@ def test_login_token_with_incorrect_survey_url_results_in_404(self): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") - def survey_url_mock(_url, _request): + def schema_url_mock(_url, _request): schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] with open(schema_path, encoding="utf8") as json_data: @@ -144,7 +144,7 @@ def survey_url_mock(_url, _request): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def survey_url_mock_404(_url, _request): + def schema_url_mock_404(_url, _request): return response(404) @@ -233,31 +233,31 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() - def test_login_token_with_survey_url_should_redirect_to_survey(self): - survey_url = "http://eq-survey-register.url/my-test-schema" + def test_login_token_with_schema_url_should_redirect_to_survey(self): + schema_url = "http://eq-survey-register.url/my-test-schema" # Given - token = self.token_generator.create_token_with_survey_url( - "test_textarea", survey_url + token = self.token_generator.create_token_with_schema_url( + "test_textarea", schema_url ) # When - with HTTMock(self.survey_url_mock): + with HTTMock(self.schema_url_mock): self.post(url=f"/session?token={token}") self.assertStatusOK() self.assertInUrl("/questionnaire") - def test_login_token_with_incorrect_survey_url_results_in_404(self): - survey_url = "http://eq-survey-register.url/my-test-schema-not-found" + def test_login_token_with_incorrect_schema_url_results_in_404(self): + schema_url = "http://eq-survey-register.url/my-test-schema-not-found" # Given - token = self.token_generator.create_token_with_survey_url( - "test_textarea", survey_url + token = self.token_generator.create_token_with_schema_url( + "test_textarea", schema_url ) # When - with HTTMock(self.survey_url_mock_404): + with HTTMock(self.schema_url_mock_404): self.post(url=f"/session?token={token}") # Then @@ -273,7 +273,7 @@ def test_login_without_case_id_in_token_is_unauthorised(self): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") - def survey_url_mock(_url, _request): + def schema_url_mock(_url, _request): schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] with open(schema_path, encoding="utf8") as json_data: @@ -281,5 +281,5 @@ def survey_url_mock(_url, _request): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def survey_url_mock_404(_url, _request): + def schema_url_mock_404(_url, _request): return response(404) From 2815c9eb16701eb1a60012b34a1a7238783abe65 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 29 Apr 2022 08:42:08 +0100 Subject: [PATCH 014/567] Fix mobilenumber input width (#857) --- templates/partials/answers/mobilenumber.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/partials/answers/mobilenumber.html b/templates/partials/answers/mobilenumber.html index 1458a568d0..4d00ea3424 100644 --- a/templates/partials/answers/mobilenumber.html +++ b/templates/partials/answers/mobilenumber.html @@ -7,7 +7,7 @@ "name": input.name, "type": "tel", "autocomplete": "tel", - "width": "8", + "width": "15", "value": input._value() | e, "label": { "id": answer.id ~ "-label", From b62500a89939813cb9c7d20c5cf1dd6b26eb2308 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 29 Apr 2022 10:39:30 +0100 Subject: [PATCH 015/567] Update the anchor for concatenated answers on change link (#856) --- app/questionnaire/router.py | 12 ++++++++++-- app/views/contexts/summary/question.py | 3 +-- app/views/handlers/block.py | 1 + app/views/handlers/question.py | 5 ++++- .../submit_with_summary_return_to_answer.spec.js | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index a2956366cc..e3cb51e086 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -116,6 +116,7 @@ def get_next_location_url( location: Location, routing_path: RoutingPath, return_to: Optional[str] = None, + return_to_answer_id: Optional[str] = None, ) -> str: """ Get the next location in the section. If the section is complete, determine where to go next, @@ -125,7 +126,9 @@ def get_next_location_url( location.section_id, location.list_item_id ): if return_to and ( - return_to_url := self._get_return_to_location_url(location, return_to) + return_to_url := self._get_return_to_location_url( + location, return_to, return_to_answer_id + ) ): return return_to_url @@ -136,7 +139,12 @@ def get_next_location_url( if is_last_block_on_path: return self._get_first_incomplete_location_in_section(routing_path).url() - return self.get_next_block_url(location, routing_path, return_to=return_to) + return self.get_next_block_url( + location, + routing_path, + return_to=return_to, + return_to_answer_id=return_to_answer_id, + ) def _get_next_location_url_for_complete_section(self, location: Location) -> str: if self._schema.show_summary_on_completion_for_section(location.section_id): diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 40c412637b..8138da7dd4 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -51,7 +51,6 @@ def get_answer(self, answer_store, answer_id): def _build_answers( self, *, answer_store, question_schema, block_id, list_name, return_to ): - if self.summary: answer_id = f"{self.id}-concatenated-answer" link = url_for( @@ -61,7 +60,7 @@ def _build_answers( list_item_id=self.list_item_id, return_to=return_to, return_to_answer_id=answer_id if return_to else None, - _anchor=answer_id, + _anchor=question_schema["answers"][0]["id"], ) return [ diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 1133c32eed..710eb7c095 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -100,6 +100,7 @@ def get_next_location_url(self): self._current_location, self._routing_path, self._return_to, + self._return_to_answer_id, ) def handle_post(self): diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 97a574c24c..ea23905a3d 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -100,7 +100,10 @@ def get_next_location_url(self): return location_url return self.router.get_next_location_url( - self._current_location, self._routing_path, self._return_to + self._current_location, + self._routing_path, + self._return_to, + self._return_to_answer_id, ) def _get_answers_for_question(self, question_json) -> dict[str, Any]: diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index ef350192ad..fc808ed38a 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -34,7 +34,7 @@ describe("Summary Anchor Scrolling", () => { $(InsuranceAddressPage.submit()).click(); $(AddressDurationPage.submit()).click(); expect($(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( - "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#name-question-concatenated-answer" + "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name" ); }); From 3487ccaa4345daea82b9d68ce6140f51b22cf7e4 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 29 Apr 2022 10:57:05 +0100 Subject: [PATCH 016/567] Use list component for errors (#839) --- templates/partials/error-panel.html | 34 ++++++++++++----- templates/thank-you.html | 37 +++++++++---------- .../base_pages/confirm-email.page.js | 2 +- .../base_pages/confirmation-email.page.js | 2 +- tests/functional/generate_pages.py | 10 +++++ .../spec/confirmation_email.spec.js | 4 +- tests/functional/spec/error_messages.spec.js | 12 ++++++ 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/templates/partials/error-panel.html b/templates/partials/error-panel.html index 834a6c18ec..b36dc8420c 100644 --- a/templates/partials/error-panel.html +++ b/templates/partials/error-panel.html @@ -1,19 +1,35 @@ {% from "components/panel/_macro.njk" import onsPanel %} +{% from "components/lists/_macro.njk" import onsList %} + +{% set error_list = [] %} +{% for error_id, error in form.mapped_errors %} + {{ error_list.append( { + "text": error, + "url": "#" + error_id, + "variants": "inPageLink", + "attributes": { + "data-qa": "error-link-" + loop.index|string + } + } ) or "" }} +{% endfor %} + {% call onsPanel({ "type": "error", "classes": "ons-u-mb-s", - "title": error_title , + "title": error_title, "attributes": { - "data-qa": "error-body" + "data-qa": "error-body" } }) %} -
    - {% for error_id, error in form.mapped_errors %} -
  1. - {{ error }} -
  2. - {% endfor %} -
+ {{ + onsList({ + "element": "ol", + "attributes": { + "data-qa": "error-list" + }, + "itemsList": error_list + }) + }} {% endcall %} diff --git a/templates/thank-you.html b/templates/thank-you.html index f92fd97042..fead0069b0 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -27,7 +27,7 @@ "iconType": "check", "iconSize": "xl", "classes": "ons-u-mb-m" - }) %} + }) %}

{{ _("Thank you for completing the {survey_title}").format( survey_title = survey_title) }}

@@ -51,34 +51,33 @@

{{ _("Thank you for completing the {survey_title}").format( {% if content.view_submitted_response.enabled %} {% if content.view_submitted_response.expired %} {% call onsPanel({ - "id": "view-submitted-response-guidance", - "classes": "ons-u-mb-m" - }) %} + "id": "view-submitted-response-guidance", + "classes": "ons-u-mb-m" + }) %} {{ countdown_expired_text }} {% endcall %} {% else %} {% set countdown_text = _("For security, your answers will only be available to view for another ") %}

{{ _("Get a copy of your answers") }}

-

{{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }}

+

+ {{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }} +

{{ onsTimeoutPanel ({ - "id": "view-submitted-response-countdown", - "minutesTextSingular": _("minute"), - "minutesTextPlural": _("minutes"), - "secondsTextSingular": _("second"), - "secondsTextPlural": _("seconds"), - "countdownText": countdown_text, - "nojsText": _("For security, your answers will only be available to view for 45 minutes"), - "redirectUrl": url_for("post_submission.get_thank_you"), - "countdownExpiredText": countdown_expired_text, - "sessionExpiresAt": content.view_submitted_response.expires_at - }) }} + "id": "view-submitted-response-countdown", + "redirectUrl": url_for("post_submission.get_thank_you"), + "minutesTextSingular": _("minute"), + "minutesTextPlural": _("minutes"), + "secondsTextSingular": _("second"), + "secondsTextPlural": _("seconds"), + "countdownText": countdown_text, + "nojsText": _("For security, your answers will only be available to view for 45 minutes"), + "countdownExpiredText": countdown_expired_text, + "sessionExpiresAt": content.view_submitted_response.expires_at + }) }} {% endif %} {% endif %} - - {% if content.show_feedback_call_to_action %} {% include 'partials/feedback-call-to-action.html' %} {% endif %} diff --git a/tests/functional/base_pages/confirm-email.page.js b/tests/functional/base_pages/confirm-email.page.js index 8238b8c9c1..75c4b0e2aa 100644 --- a/tests/functional/base_pages/confirm-email.page.js +++ b/tests/functional/base_pages/confirm-email.page.js @@ -14,7 +14,7 @@ class ConfirmEmailPage extends BasePage { } errorPanel() { - return `[data-qa=error-body] div.ons-panel__body > ol`; + return `[data-qa=error-body] div.ons-panel__body > [data-qa=error-list]`; } } export default new ConfirmEmailPage("confirm-email"); diff --git a/tests/functional/base_pages/confirmation-email.page.js b/tests/functional/base_pages/confirmation-email.page.js index 46d09945c8..cf9ea293ac 100644 --- a/tests/functional/base_pages/confirmation-email.page.js +++ b/tests/functional/base_pages/confirmation-email.page.js @@ -10,7 +10,7 @@ class ConfirmationEmailSentPage extends BasePage { } errorPanel() { - return `[data-qa=error-body] div.ons-panel__body > ol`; + return `[data-qa=error-body] div.ons-panel__body > [data-qa=error-list]`; } feedback() { diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 073bff6d0e..8e90c57bf4 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -153,6 +153,14 @@ """ ) +ANSWER_NUMBERED_ERROR_LIST_GETTER = r""" errorList() { return `ol[data-qa="error-list"]`; } + +""" + +ANSWER_SINGLE_ERROR_LINK_GETTER = r""" singleErrorLink() { return `p[data-qa="error-list"]`; } + +""" + ANSWER_LABEL_DESCRIPTION_GETTER = Template( r""" ${answerName}LabelDescription() { return `#${answerId}-label-description-hint`; @@ -447,6 +455,8 @@ def process_question(question, page_spec, num_questions, page_name): } page_spec.write(QUESTION_ERROR_PANEL.substitute(question_context)) page_spec.write(QUESTION_TITLE.substitute(question_context)) + page_spec.write(ANSWER_NUMBERED_ERROR_LIST_GETTER) + page_spec.write(ANSWER_SINGLE_ERROR_LINK_GETTER) def process_calculated_summary(answers, page_spec): diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index a3a693232d..38ec60fa59 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -54,7 +54,7 @@ describe("Email confirmation", () => { expect($(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); }); - it("when I go to the confirmation email page and submit without providing an email address, Then I get an error message", () => { + it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", () => { $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); $(ConfirmationEmailPage.submit()).click(); expect(browser.getUrl()).to.contain("confirmation-email/send"); @@ -62,7 +62,7 @@ describe("Email confirmation", () => { expect($(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address"); }); - it("when I submit the form without providing a correctly formatted email address, Then I get an error message", () => { + it("When I submit the form without providing a correctly formatted email address, Then I get an error message", () => { $(ConfirmationEmailPage.email()).setValue("incorrect-format"); $(ConfirmationEmailPage.submit()).click(); expect(browser.getUrl()).to.contain("confirmation-email/send"); diff --git a/tests/functional/spec/error_messages.spec.js b/tests/functional/spec/error_messages.spec.js index 7d23f9d2bc..734d42f600 100644 --- a/tests/functional/spec/error_messages.spec.js +++ b/tests/functional/spec/error_messages.spec.js @@ -70,6 +70,18 @@ describe("Error Messages", () => { expect($(AboutYou.textareaErrorItem()).getText()).to.equal("Enter an answer"); }); + it("Given a question has multiple errors, When the errors are displayed, Then the error messages are in a numbered list", () => { + $(AboutYou.submit()).click(); + expect($(AboutYou.errorList()).isDisplayed()).to.be.true; + }); + + it("Given a question has 1 error, When the error is displayed, Then error message isn't in a numbered list", () => { + answerAllButOne(); + + $(AboutYou.submit()).click(); + expect($(AboutYou.singleErrorLink()).isDisplayed()).to.be.true; + }); + it("Given a question has 1 error, When the error is displayed, Then error header is correct", () => { answerAllButOne(); From 6135aa00146cf4b5046251252941afc67b544bd3 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 6 May 2022 15:02:44 +0100 Subject: [PATCH 017/567] Set page title for Calculated summary blocks (#860) --- app/views/contexts/calculated_summary_context.py | 2 +- app/views/handlers/calculated_summary.py | 5 ++++- tests/functional/spec/features/calculated_summary.spec.js | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 9b96070c76..68a8a1dabb 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -185,7 +185,7 @@ def _get_formatted_total(self, groups, current_location, calculation_operator): @staticmethod def _get_calculated_question(calculation_question, formatted_total): - calculation_title = calculation_question.get("title") + calculation_title = calculation_question["title"] return { "title": calculation_title, diff --git a/app/views/handlers/calculated_summary.py b/app/views/handlers/calculated_summary.py index 65084badc6..a05a04e0f0 100644 --- a/app/views/handlers/calculated_summary.py +++ b/app/views/handlers/calculated_summary.py @@ -13,6 +13,9 @@ def get_context(self): self._questionnaire_store.metadata, self._questionnaire_store.response_metadata, ) - return calculated_summary_context.build_view_context_for_calculated_summary( + context = calculated_summary_context.build_view_context_for_calculated_summary( self._current_location ) + self.page_title = context["summary"]["calculated_question"]["title"] + + return context diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index 6c06028a2d..78fe6a6711 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -55,6 +55,10 @@ describe("Feature: Calculated Summary", () => { expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); }); + it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { + expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + }); + it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { // Totals and titles should be shown expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( From 91f175ba1193d3e1678c4d7f66b4a888a63d9a5b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 11 May 2022 14:26:30 +0100 Subject: [PATCH 018/567] Bug fix: Support for in-flight sessions that use survey_url (#862) --- app/data_models/session_data.py | 10 ++++- tests/app/data_model/test_session_data.py | 47 ++++++++++++++++++++++ tests/app/data_model/test_session_store.py | 41 +++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 tests/app/data_model/test_session_data.py diff --git a/app/data_models/session_data.py b/app/data_models/session_data.py index e3a26869f4..d7b35ff524 100644 --- a/app/data_models/session_data.py +++ b/app/data_models/session_data.py @@ -9,7 +9,6 @@ def __init__( period_str: Optional[str], language_code: Optional[str], launch_language_code: Optional[str], - schema_url: Optional[str], ru_name: Optional[str], ru_ref: Optional[str], response_id: Optional[str], @@ -21,6 +20,8 @@ def __init__( display_address: Optional[str] = None, confirmation_email_count: int = 0, feedback_count: int = 0, + schema_url: Optional[str] = None, + survey_url: Optional[str] = None, # pylint: disable=unused-argument **_: Any, ): # pylint: disable=too-many-locals self.tx_id = tx_id @@ -28,7 +29,6 @@ def __init__( self.period_str = period_str self.language_code = language_code self.launch_language_code = launch_language_code - self.schema_url = schema_url self.ru_name = ru_name self.ru_ref = ru_ref self.response_id = response_id @@ -40,3 +40,9 @@ def __init__( self.display_address = display_address self.confirmation_email_count = confirmation_email_count self.feedback_count = feedback_count + self.schema_url = schema_url + + # :TODO: Remove once `schema_url` has been rolled out successfully. + # This is only to support a rollback in the event `schema_url` deploy is not successful. + # Survey URL will not be used to load surveys. + self.survey_url = None diff --git a/tests/app/data_model/test_session_data.py b/tests/app/data_model/test_session_data.py new file mode 100644 index 0000000000..04af32d8b7 --- /dev/null +++ b/tests/app/data_model/test_session_data.py @@ -0,0 +1,47 @@ +import pytest + +from app.data_models import SessionData + + +def test_session_data_default_properties(): + try: + session_data = SessionData( + tx_id="123", + schema_name="some_schema_name", + period_str=None, + language_code="cy", + launch_language_code="en", + ru_name=None, + ru_ref=None, + response_id="321", + case_id="789", + ) + except TypeError: + return pytest.fail("An error occurred when creating session data") + + assert session_data.case_ref is None + assert session_data.account_service_base_url is None + assert session_data.account_service_log_out_url is None + assert session_data.trad_as is None + assert session_data.display_address is None + assert session_data.confirmation_email_count == 0 + assert session_data.feedback_count == 0 + assert session_data.schema_url is None + assert session_data.survey_url is None + + +def test_session_data_survey_url_always_set_to_none(): + session_data = SessionData( + tx_id="123", + schema_name="some_schema_name", + period_str=None, + language_code="cy", + launch_language_code="en", + ru_name=None, + ru_ref=None, + response_id="321", + case_id="789", + survey_url="some-url", + ) + + assert session_data.survey_url is None diff --git a/tests/app/data_model/test_session_store.py b/tests/app/data_model/test_session_store.py index b553dd9790..10d68e70c0 100644 --- a/tests/app/data_model/test_session_store.py +++ b/tests/app/data_model/test_session_store.py @@ -1,8 +1,11 @@ +from datetime import datetime, timezone + import pytest from flask import current_app from jwcrypto import jwe from jwcrypto.common import base64url_encode +from app.data_models import SessionData from app.data_models.app_models import EQSession from app.data_models.session_store import SessionStore from app.utilities.json import json_dumps @@ -151,6 +154,44 @@ def test_session_store_stores_none_for_trading_as_if_not_present( assert session_store.session_data.trad_as is None +def test_load_existing_session_does_not_error_when_session_data_contains_survey_url( + app, app_session_store +): + session_data_with_survey_url = SessionData( + tx_id="123", + schema_name="some_schema_name", + display_address="68 Abingdon Road, Goathill", + period_str=None, + language_code="cy", + launch_language_code="en", + survey_url="some-url", + ru_name=None, + ru_ref=None, + submitted_at=datetime.now(timezone.utc).isoformat(), + response_id="321", + case_id="789", + ) + + with app.test_request_context(): + # Given a session store with session data that has a survey url + app_session_store.session_store.create( + eq_session_id="eq_session_id", + user_id="test", + session_data=session_data_with_survey_url, + expires_at=app_session_store.expires_at, + ).save() + + # When a SessionStore is loaded (Session matching 'eq_session_id' exists at this point) + loaded_session_store = SessionStore("user_ik", "pepper", "eq_session_id") + + # Then + assert ( + loaded_session_store.session_data.__dict__ + == session_data_with_survey_url.__dict__ + ) + assert loaded_session_store.session_data.survey_url is None + + @pytest.mark.usefixtures("app") def test_legacy_load(app_session_store_encoded): _save_session( From 85342a1bb323a9378b1e2c999f076e58ac56df17 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 11 May 2022 15:29:39 +0100 Subject: [PATCH 019/567] Schemas v3.3.0 (#864) Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5103369287..b299be9782 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.2.3 +v3.3.0 From 7d9043c337338b48e2a4d32670cefc7e68848b08 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 12 May 2022 09:49:48 +0100 Subject: [PATCH 020/567] Ability to return to calculated summary (#854) --- app/questionnaire/router.py | 66 ++++++--- .../contexts/calculated_summary_context.py | 20 +-- app/views/contexts/summary/answer.py | 7 +- app/views/contexts/summary/block.py | 6 + app/views/contexts/summary/group.py | 6 + app/views/contexts/summary/question.py | 14 +- app/views/handlers/block.py | 3 + app/views/handlers/question.py | 1 + tests/app/questionnaire/test_router.py | 135 ++++++++++++++++++ .../app/views/contexts/summary/test_answer.py | 24 +++- .../test_calculated_summary_context.py | 48 ++----- .../spec/features/calculated_summary.spec.js | 39 +++-- 12 files changed, 276 insertions(+), 93 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index e3cb51e086..76e2feb470 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -117,21 +117,27 @@ def get_next_location_url( routing_path: RoutingPath, return_to: Optional[str] = None, return_to_answer_id: Optional[str] = None, + return_to_block_id: Optional[str] = None, ) -> str: """ Get the next location in the section. If the section is complete, determine where to go next, whether it be a summary, the hub or the next incomplete location. """ - if self._progress_store.is_section_complete( + is_section_complete = self._progress_store.is_section_complete( location.section_id, location.list_item_id + ) + + if return_to_url := self._get_return_to_location_url( + location, + return_to, + routing_path, + is_section_complete=is_section_complete, + return_to_answer_id=return_to_answer_id, + return_to_block_id=return_to_block_id, ): - if return_to and ( - return_to_url := self._get_return_to_location_url( - location, return_to, return_to_answer_id - ) - ): - return return_to_url + return return_to_url + if is_section_complete: return self._get_next_location_url_for_complete_section(location) # Due to backwards routing you can be on the last block of the path but with an in_progress section @@ -144,6 +150,7 @@ def get_next_location_url( routing_path, return_to=return_to, return_to_answer_id=return_to_answer_id, + return_to_block_id=return_to_block_id, ) def _get_next_location_url_for_complete_section(self, location: Location) -> str: @@ -158,21 +165,18 @@ def get_previous_location_url( routing_path: RoutingPath, return_to: Optional[str] = None, return_to_answer_id: Optional[str] = None, + return_to_block_id: Optional[str] = None, ) -> Optional[str]: """ Returns the previous 'location' to visit given a set of user answers or returns to the summary if the `return_to` var is set and the section is complete. """ - - if return_to and ( - self._progress_store.is_section_complete( - location.section_id, location.list_item_id - ) - and ( - return_to_url := self._get_return_to_location_url( - location, return_to, return_to_answer_id=return_to_answer_id - ) - ) + if return_to_url := self._get_return_to_location_url( + location, + return_to, + routing_path, + return_to_answer_id=return_to_answer_id, + return_to_block_id=return_to_block_id, ): return return_to_url @@ -194,6 +198,7 @@ def get_previous_location_url( list_name=routing_path.list_name, list_item_id=routing_path.list_item_id, return_to=return_to, + return_to_block_id=return_to_block_id, _anchor=return_to_answer_id, ) @@ -205,14 +210,37 @@ def get_previous_location_url( def _get_return_to_location_url( self, location: Location, - return_to: str, + return_to: Optional[str], + routing_path: RoutingPath, + is_section_complete: Optional[bool] = None, return_to_answer_id: Optional[str] = None, + return_to_block_id: Optional[str] = None, ) -> Optional[str]: + if not return_to: + return None + + if return_to == "calculated-summary": + if return_to_block_id in routing_path: + return url_for( + "questionnaire.block", + list_item_id=location.list_item_id, + block_id=return_to_block_id, + _anchor=return_to_answer_id, + ) + return None + + if is_section_complete is None: + is_section_complete = self._progress_store.is_section_complete( + location.section_id, location.list_item_id + ) + + if not is_section_complete: + return None + if return_to == "section-summary": return self._get_section_url( location, return_to_answer_id=return_to_answer_id ) - if return_to == "final-summary" and self.is_questionnaire_complete: return url_for( "questionnaire.submit_questionnaire", _anchor=return_to_answer_id diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 68a8a1dabb..7dc2841aef 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -16,18 +16,7 @@ class CalculatedSummaryContext(Context): - def get_return_to(self, section_id: str): - # :TODO: Add support for returning to the calculated summary that was used - has_section_summary = bool(self._schema.get_summary_for_section(section_id)) - if has_section_summary: - return "section-summary" - - if not self._schema.is_flow_hub: - return "final-summary" - - return None - - def build_groups_for_section(self, section): + def build_groups_for_section(self, section, return_to_block_id): routing_path = self._router.routing_path(section["id"]) location = Location(section["id"]) @@ -43,7 +32,8 @@ def build_groups_for_section(self, section): self._schema, location, self._language, - return_to=self.get_return_to(section["id"]), + return_to="calculated-summary", + return_to_block_id=return_to_block_id, ).serialize() for group in section["groups"] ] @@ -51,12 +41,14 @@ def build_groups_for_section(self, section): def build_view_context_for_calculated_summary(self, current_location): block = self._schema.get_block(current_location.block_id) + return_to_block_id = block["id"] + calculated_section = self._build_calculated_summary_section( block, current_location ) calculation = block["calculation"] - groups = self.build_groups_for_section(calculated_section) + groups = self.build_groups_for_section(calculated_section, return_to_block_id) formatted_total = self._get_formatted_total( groups or [], diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index c5fe753585..5d6646dcc9 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -11,6 +11,7 @@ def __init__( list_name, list_item_id, return_to, + return_to_block_id, ): self.id = answer_schema["id"] self.label = answer_schema.get("label") @@ -24,6 +25,7 @@ def __init__( list_name=list_name, list_item_id=list_item_id, return_to=return_to, + return_to_block_id=return_to_block_id, ) def serialize(self): @@ -38,7 +40,9 @@ def serialize(self): "link": self.link, } - def _build_link(self, *, block_id, list_name, list_item_id, return_to): + def _build_link( + self, *, block_id, list_name, list_item_id, return_to, return_to_block_id + ): return url_for( "questionnaire.block", list_name=list_name, @@ -46,5 +50,6 @@ def _build_link(self, *, block_id, list_name, list_item_id, return_to): list_item_id=list_item_id, return_to=return_to, return_to_answer_id=self.id if return_to else None, + return_to_block_id=return_to_block_id, _anchor=self.id, ) diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index f5ce6d3ae6..54f56b6277 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -1,3 +1,5 @@ +from typing import Optional + from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_variant @@ -16,6 +18,7 @@ def __init__( schema, location, return_to, + return_to_block_id: Optional[str] = None, ): self.id = block_schema["id"] self.title = block_schema.get("title") @@ -50,6 +53,7 @@ def __init__( schema=schema, location=location, return_to=return_to, + return_to_block_id=return_to_block_id, ) def get_question( @@ -63,6 +67,7 @@ def get_question( schema, location, return_to, + return_to_block_id, ): """ Taking question variants into account, return the question which was displayed to the user """ @@ -86,6 +91,7 @@ def get_question( location=location, block_id=self.id, return_to=return_to, + return_to_block_id=return_to_block_id, ).serialize() def serialize(self): diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 58b934c3df..abb938c487 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,3 +1,5 @@ +from typing import Optional + from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.views.contexts.summary.block import Block @@ -15,6 +17,7 @@ def __init__( location, language, return_to, + return_to_block_id: Optional[str] = None, ): self.id = group_schema["id"] self.title = group_schema.get("title") @@ -29,6 +32,7 @@ def __init__( schema=schema, location=location, return_to=return_to, + return_to_block_id=return_to_block_id, ) self.placeholder_renderer = PlaceholderRenderer( language=language, @@ -51,6 +55,7 @@ def _build_blocks( schema, location, return_to, + return_to_block_id, ): blocks = [] @@ -67,6 +72,7 @@ def _build_blocks( schema=schema, location=location, return_to=return_to, + return_to_block_id=return_to_block_id, ).serialize() ] ) diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 8138da7dd4..4c51697f48 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -1,3 +1,5 @@ +from typing import Optional + from flask import url_for from markupsafe import escape @@ -18,6 +20,7 @@ def __init__( location, block_id, return_to, + return_to_block_id: Optional[str] = None, ): self.list_item_id = location.list_item_id if location else None self.id = question_schema["id"] @@ -39,6 +42,7 @@ def __init__( block_id=block_id, list_name=location.list_name if location else None, return_to=return_to, + return_to_block_id=return_to_block_id, ) def get_answer(self, answer_store, answer_id): @@ -49,7 +53,14 @@ def get_answer(self, answer_store, answer_id): return escape_answer_value(answer.value) if answer else None def _build_answers( - self, *, answer_store, question_schema, block_id, list_name, return_to + self, + *, + answer_store, + question_schema, + block_id, + list_name, + return_to, + return_to_block_id, ): if self.summary: answer_id = f"{self.id}-concatenated-answer" @@ -87,6 +98,7 @@ def _build_answers( list_name=list_name, list_item_id=self.list_item_id, return_to=return_to, + return_to_block_id=return_to_block_id, ).serialize() summary_answers.append(summary_answer) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 710eb7c095..c30e64397d 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -39,6 +39,7 @@ def __init__( self.page_title = None self._return_to = request_args.get("return_to") self._return_to_answer_id = request_args.get("return_to_answer_id") + self._return_to_block_id = request_args.get("return_to_block_id") self.resume = "resume" in request_args if not self.is_location_valid(): @@ -93,6 +94,7 @@ def get_previous_location_url(self): self._routing_path, self._return_to, self._return_to_answer_id, + self._return_to_block_id, ) def get_next_location_url(self): @@ -101,6 +103,7 @@ def get_next_location_url(self): self._routing_path, self._return_to, self._return_to_answer_id, + self._return_to_block_id, ) def handle_post(self): diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index ea23905a3d..0f381d415f 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -104,6 +104,7 @@ def get_next_location_url(self): self._routing_path, self._return_to, self._return_to_answer_id, + self._return_to_block_id, ) def _get_answers_for_question(self, question_json) -> dict[str, Any]: diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index b6a62786ce..17104e600d 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -506,6 +506,103 @@ def test_section_summary_on_completion_false(self): assert expected_location_url == next_location + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary(self): + self.schema = load_schema_from_name("test_calculated_summary") + + current_location = Location( + section_id="default-section", block_id="fifth-number-block" + ) + + routing_path = RoutingPath( + [ + "fifth-number-block", + "sixth-number-block", + "currency-total-playback-skipped-fourth", + ], + section_id="default-section", + ) + + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to_answer_id="first-number-answer", + return_to="calculated-summary", + return_to_block_id="currency-total-playback-skipped-fourth", + ) + expected_location = Location( + section_id="default-section", + block_id="currency-total-playback-skipped-fourth", + ) + + expected_location_url = url_for( + "questionnaire.block", + list_item_id=expected_location.list_item_id, + block_id=expected_location.block_id, + _anchor="first-number-answer", + ) + + assert expected_location_url == next_location_url + + @pytest.mark.usefixtures("app") + @pytest.mark.parametrize( + "return_to_block_id, expected_url", + [ + ( + "non-valid-block", + "/questionnaire/sixth-number-block/?return_to=calculated-summary&return_to_block_id=non-valid-block", + ), + (None, "/questionnaire/sixth-number-block/?return_to=calculated-summary"), + ], + ) + def test_return_to_calculated_summary_invalid_return_to_block_id( + self, return_to_block_id, expected_url + ): + self.schema = load_schema_from_name("test_calculated_summary") + + current_location = Location( + section_id="default-section", block_id="fifth-number-block" + ) + + routing_path = RoutingPath( + ["fifth-number-block", "sixth-number-block"], + section_id="default-section", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="calculated-summary", + return_to_block_id=return_to_block_id, + ) + + assert expected_url == next_location_url + + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary_return_to_block_id_not_on_path(self): + self.schema = load_schema_from_name("test_calculated_summary") + + current_location = Location( + section_id="default-section", block_id="fifth-number-block" + ) + + routing_path = RoutingPath( + ["fifth-number-block", "sixth-number-block"], + section_id="default-section", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="calculated-summary", + return_to_block_id="fourth-number-block", + ) + + # return_to_block_id is still passed here as although it is not currently on the path it may be in future once incomplete questions are + # answered so needs to be preserved + assert ( + "/questionnaire/sixth-number-block/?return_to=calculated-summary&return_to_block_id=fourth-number-block" + == next_location_url + ) + class TestRouterNextLocationLinearFlow(RouterTestCase): @pytest.mark.usefixtures("app") @@ -634,6 +731,44 @@ def test_within_section(self): assert expected_location_url == previous_location_url + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary(self): + self.schema = load_schema_from_name("test_calculated_summary") + + current_location = Location( + section_id="default-section", block_id="fifth-number-block" + ) + + routing_path = RoutingPath( + [ + "fifth-number-block", + "sixth-number-block", + "currency-total-playback-skipped-fourth", + ], + section_id="default-section", + ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_to="calculated-summary", + return_to_answer_id="first-number-answer", + return_to_block_id="currency-total-playback-skipped-fourth", + ) + + expected_location = Location( + section_id="default-section", + block_id="currency-total-playback-skipped-fourth", + ) + + expected_location_url = url_for( + "questionnaire.block", + list_item_id=expected_location.list_item_id, + block_id=expected_location.block_id, + _anchor="first-number-answer", + ) + + assert expected_location_url == previous_location_url + @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index 16edbf3b20..b7896a8f95 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -5,10 +5,14 @@ @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "return_to", - ["section-summary", None], + "return_to, return_to_block_id", + [ + ("section-summary", None), + (None, None), + ("calculated-summary", "total"), + ], ) -def test_create_answer(return_to): +def test_create_answer(return_to, return_to_block_id): answer = Answer( answer_schema={"id": "answer-id", "label": "Answer Label", "type": "date"}, answer_value="An answer", @@ -16,6 +20,7 @@ def test_create_answer(return_to): list_name="answer-list", list_item_id="answer-item-id", return_to=return_to, + return_to_block_id=return_to_block_id, ) assert answer.id == "answer-id" @@ -23,12 +28,16 @@ def test_create_answer(return_to): assert answer.value == "An answer" assert answer.type == "date" - query_string = ( - "?return_to=section-summary&return_to_answer_id=answer-id" if return_to else "" - ) + if return_to and return_to_block_id: + query_string = f"?return_to={return_to}&return_to_answer_id={answer.id}&return_to_block_id={return_to_block_id}" + elif return_to: + query_string = f"?return_to={return_to}&return_to_answer_id={answer.id}" + else: + query_string = "" + assert ( answer.link - == f"/questionnaire/answer-list/answer-item-id/house-type/{query_string}#answer-id" + == f"/questionnaire/answer-list/answer-item-id/house-type/{query_string}#{answer.id}" ) @@ -42,6 +51,7 @@ def test_date_answer_type(): list_name="answer-list", list_item_id="answer-item-id", return_to="section-summary", + return_to_block_id=None, ) # Then diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 0c3fc63fea..0a5d0597d9 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -8,7 +8,7 @@ # pylint: disable=too-many-locals @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "block_id, locale, language, title, value, total_blocks", + "block_id, locale, language, title, value, total_blocks, return_to_answer_id", ( ( "currency-total-playback-with-fourth", @@ -17,6 +17,7 @@ "We calculate the total of currency values entered to be £27.00. Is this correct? (With Fourth)", "£27.00", 5, + "first-number-answer", ), ( "currency-total-playback-skipped-fourth", @@ -25,6 +26,7 @@ "We calculate the total of currency values entered to be £12.00. Is this correct? (Skipped Fourth)", "£12.00", 3, + "first-number-answer", ), ( "unit-total-playback", @@ -33,6 +35,7 @@ "We calculate the total of unit values entered to be 9 cm. Is this correct?", "9 cm", 2, + "second-number-answer-unit-total", ), ( "percentage-total-playback", @@ -41,6 +44,7 @@ "We calculate the total of percentage values entered to be 20%. Is this correct?", "20%", 2, + "fifth-percent-answer", ), ( "number-total-playback", @@ -49,6 +53,7 @@ "We calculate the total of number values entered to be 22. Is this correct?", "22", 2, + "fifth-number-answer", ), ), ) @@ -64,6 +69,7 @@ def test_build_view_context_for_currency_calculated_summary( list_store, progress_store, mocker, + return_to_answer_id, ): mocker.patch( "app.jinja_filters.flask_babel.get_locale", @@ -104,40 +110,6 @@ def test_build_view_context_for_currency_calculated_summary( answer_change_link = context_summary["groups"][0]["blocks"][0]["question"][ "answers" ][0]["link"] - assert "return_to=final-summary" in answer_change_link - - -@pytest.mark.parametrize( - "has_section_summary", - ([True, False]), -) -@pytest.mark.parametrize( - "is_hub_enabled", - ([True, False]), -) -def test_get_return_to( - has_section_summary, - is_hub_enabled, - mocker, -): - schema = mocker.MagicMock() - schema.is_flow_hub = is_hub_enabled - schema.get_summary_for_section = mocker.Mock(return_value=has_section_summary) - - calculated_summary_context = CalculatedSummaryContext( - "en", - schema, - answer_store=mocker.MagicMock(), - list_store=mocker.MagicMock(), - progress_store=mocker.MagicMock(), - metadata={}, - response_metadata={}, - ) - - return_to = calculated_summary_context.get_return_to("some-section") - if has_section_summary: - assert return_to == "section-summary" - elif not is_hub_enabled: - assert return_to == "final-summary" - else: - assert return_to is None + assert "return_to=calculated-summary" in answer_change_link + assert f"return_to_answer_id={return_to_answer_id}" in answer_change_link + assert f"return_to_block_id={block_id}" in answer_change_link diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index 78fe6a6711..92c38b9f1f 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -92,29 +92,40 @@ describe("Feature: Calculated Summary", () => { expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; }); - it("Given change an answer, When I get to the currency summary, Then I should see the new total", () => { + it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" + ); + }); + + it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.previous()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/#third-number-answer"); + }); + + it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/#third-number-answer"); + }); + + it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); $(FourthNumberBlockPage.submit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(12.34); - $(FourthAndAHalfNumberBlockPage.submit()).click(); - - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £40.71. Is this correct?" + "We calculate the total of currency values entered to be £30.71. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£40.71"); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); $(FourthAndAHalfNumberBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( @@ -125,9 +136,11 @@ describe("Feature: Calculated Summary", () => { }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + $(SixthNumberBlockPage.previous()).click(); + $(FifthNumberBlockPage.previous()).click(); + $(FourthAndAHalfNumberBlockPage.previous()).click(); + $(FourthNumberBlockPage.previous()).click(); $(SkipFourthBlockPage.yes()).click(); $(SkipFourthBlockPage.submit()).click(); From f1a8d04c38ed8c3e3aecd94ef5c24f50d1cfca0b Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 13 May 2022 11:57:33 +0100 Subject: [PATCH 021/567] Add failed requests handling and retries for survey url (#858) --- app/utilities/schema.py | 67 +++++++++-- tests/app/utilities/test_schema.py | 128 ++++++++++++++++++++- tests/integration/integration_test_case.py | 3 + tests/integration/session/test_login.py | 18 +-- 4 files changed, 189 insertions(+), 27 deletions(-) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 29923ae34b..94357378be 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -6,8 +6,9 @@ from typing import Mapping, Optional import requests +from requests import RequestException +from requests.adapters import HTTPAdapter, Retry from structlog import get_logger -from werkzeug.exceptions import NotFound from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, @@ -22,6 +23,23 @@ LANGUAGES_MAP = {"test_language": [["en", "cy"]]} +SCHEMA_REQUEST_MAX_BACKOFF = 0.2 +SCHEMA_REQUEST_MAX_RETRIES = 2 # Totals no. of request should be 3. The initial request + SCHEMA_REQUEST_MAX_RETRIES +SCHEMA_REQUEST_TIMEOUT = 3 +SCHEMA_REQUEST_RETRY_STATUS_CODES = [ + 408, + 429, + 500, + 502, + 503, + 504, +] + + +class SchemaRequestFailed(Exception): + def __str__(self) -> str: + return str("schema request failed") + @lru_cache(maxsize=None) def get_schema_list(language_code: str = DEFAULT_LANGUAGE_CODE) -> dict[str, list]: @@ -177,20 +195,45 @@ def load_schema_from_url(schema_url, language_code): constructed_schema_url = f"{schema_url}?language={language_code}" - req = requests.get(constructed_schema_url) - schema_response = req.content.decode() - response_duration_in_milliseconds = req.elapsed.total_seconds() * 1000 + session = requests.Session() - logger.info( - f"schema request took {response_duration_in_milliseconds:.2f} milliseconds", - pid=pid, - ) + retries = Retry( + total=SCHEMA_REQUEST_MAX_RETRIES, + status_forcelist=SCHEMA_REQUEST_RETRY_STATUS_CODES, + ) # Codes to retry according to Google Docs https://cloud.google.com/storage/docs/retry-strategy#client-libraries + + retries.BACKOFF_MAX = SCHEMA_REQUEST_MAX_BACKOFF + + session.mount("http://", HTTPAdapter(max_retries=retries)) + session.mount("https://", HTTPAdapter(max_retries=retries)) - if req.status_code == 404: - logger.error("no schema exists", schema_url=constructed_schema_url) - raise NotFound + try: + req = session.get(constructed_schema_url, timeout=SCHEMA_REQUEST_TIMEOUT) + except RequestException as exc: + logger.exception( + "schema request errored", + schema_url=constructed_schema_url, + ) + raise SchemaRequestFailed from exc + + if req.status_code == 200: + schema_response = req.content.decode() + response_duration_in_milliseconds = req.elapsed.total_seconds() * 1000 + + logger.info( + f"schema request took {response_duration_in_milliseconds:.2f} milliseconds", + pid=pid, + ) + + return QuestionnaireSchema(json_loads(schema_response), language_code) + + logger.error( + "got a non-200 response for schema url request", + status_code=req.status_code, + schema_url=constructed_schema_url, + ) - return QuestionnaireSchema(json_loads(schema_response), language_code) + raise SchemaRequestFailed def cache_questionnaire_schemas(): diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 6515c4e998..a70ffee0cb 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -1,13 +1,18 @@ import os -from unittest.mock import Mock, patch +from http.client import HTTPMessage import pytest import responses -from werkzeug.exceptions import NotFound +from mock import Mock, patch +from requests import RequestException +from requests.adapters import ConnectTimeoutError, ReadTimeoutError +from urllib3.connectionpool import HTTPConnectionPool, HTTPResponse from app.questionnaire import QuestionnaireSchema from app.setup import create_app from app.utilities.schema import ( + SCHEMA_REQUEST_MAX_RETRIES, + SchemaRequestFailed, _load_schema_from_name, cache_questionnaire_schemas, get_allowed_languages, @@ -155,14 +160,19 @@ def test_load_schema_from_url_200(): assert cache_info.hits == 0 +@pytest.mark.parametrize( + "status_code", + [401, 403, 404, 501, 511], +) @responses.activate -def test_load_schema_from_url_404(): +def test_load_schema_from_url_non_200(status_code): load_schema_from_url.cache_clear() - mock_schema = QuestionnaireSchema({}) - responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=404) + responses.add( + responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=status_code + ) - with pytest.raises(NotFound): + with pytest.raises(SchemaRequestFailed) as exc: load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") cache_info = load_schema_from_url.cache_info() @@ -170,6 +180,17 @@ def test_load_schema_from_url_404(): assert cache_info.misses == 1 assert cache_info.hits == 0 + assert str(exc.value) == "schema request failed" + + +@responses.activate +def test_load_schema_from_url_request_failed(): + responses.add(responses.GET, TEST_SCHEMA_URL, body=RequestException()) + with pytest.raises(SchemaRequestFailed) as exc: + load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + + assert str(exc.value) == "schema request failed" + @responses.activate def test_load_schema_from_url_uses_cache(): @@ -206,3 +227,98 @@ def test_load_schema_from_metadata_with_schema_url(): assert loaded_schema.json == mock_schema.json assert loaded_schema.language_code == mock_schema.language_code + + +@pytest.fixture(name="mocked_response_content") +def mocked_response_content_fixture(mocker): + decodable_content = Mock() + decodable_content.decode.return_value = b"{}" + mocker.patch("requests.models.Response.content", decodable_content) + + +def get_mocked_make_request(mocker, status_codes): + mocked_responses = [] + for status_code in status_codes: + response = HTTPResponse(status=status_code, headers={}, msg=HTTPMessage()) + response.drain_conn = mocker.Mock(return_value=None) + + mocked_responses.append(response) + + patched_make_request = mocker.patch.object( + HTTPConnectionPool, + "_make_request", + side_effect=mocked_responses, + ) + mocker.patch.object( + HTTPResponse, + "from_httplib", + side_effect=mocked_responses, + ) + + return patched_make_request + + +@pytest.fixture(name="mocked_make_request_with_timeout") +def mocked_make_request_with_timeout_fixture( + mocker, mocked_response_content # pylint: disable=unused-argument +): + connect_timeout_error = ConnectTimeoutError("connect timed out") + read_timeout_error = ReadTimeoutError( + pool=None, message="read timed out", url="test-url" + ) + + response_not_timed_out = HTTPResponse(status=200, headers={}, msg=HTTPMessage()) + response_not_timed_out.drain_conn = Mock(return_value=None) + + return mocker.patch.object( + HTTPConnectionPool, + "_make_request", + side_effect=[ + connect_timeout_error, + read_timeout_error, + response_not_timed_out, + ], + ) + + +def test_load_schema_from_url_retries_timeout_error(mocked_make_request_with_timeout): + load_schema_from_url.cache_clear() + + try: + schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + except SchemaRequestFailed: + return pytest.fail("Schema request unexpectedly failed") + + assert schema.json == QuestionnaireSchema({}).json + + expected_call = SCHEMA_REQUEST_MAX_RETRIES + 1 # Max retries + the initial request + assert mocked_make_request_with_timeout.call_count == expected_call + + +@pytest.mark.usefixtures("mocked_response_content") +def test_load_schema_from_url_retries_transient_error(mocker): + mocked_make_request = get_mocked_make_request(mocker, status_codes=[500, 500, 200]) + load_schema_from_url.cache_clear() + + try: + schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + except SchemaRequestFailed: + return pytest.fail("Schema request unexpectedly failed") + + assert schema.json == QuestionnaireSchema({}).json + + expected_call = SCHEMA_REQUEST_MAX_RETRIES + 1 # Max retries + the initial request + assert mocked_make_request.call_count == expected_call + + +def test_load_schema_from_url_max_retries(mocker): + mocked_make_request = get_mocked_make_request( + mocker, status_codes=[500, 500, 500, 500] + ) + load_schema_from_url.cache_clear() + + with pytest.raises(SchemaRequestFailed) as exc: + load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + + assert str(exc.value) == "schema request failed" + assert mocked_make_request.call_count == 3 diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 1c3a92b913..43a48b5ccf 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -413,6 +413,9 @@ def assertStatusNotFound(self): self.assertStatusCode(404) self.assertInBody("Page not found") + def assertException(self): + self.assertStatusCode(500) + def assertStatusCode(self, status_code): if self.last_response is not None: self.assertEqual(status_code, self.last_response.status_code) diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index 30f63e230e..90cf41e1c2 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -119,7 +119,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): self.assertStatusOK() self.assertInUrl("/questionnaire") - def test_login_token_with_incorrect_schema_url_results_in_404(self): + def test_login_token_with_incorrect_schema_url_results_in_500(self): schema_url = "http://eq-survey-register.url/my-test-schema-not-found" # Given @@ -128,11 +128,11 @@ def test_login_token_with_incorrect_schema_url_results_in_404(self): ) # When - with HTTMock(self.schema_url_mock_404): + with HTTMock(self.schema_url_mock_500): self.get(url=f"/session?token={token}") # Then - self.assertStatusNotFound() + self.assertException() @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") @@ -144,8 +144,8 @@ def schema_url_mock(_url, _request): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def schema_url_mock_404(_url, _request): - return response(404) + def schema_url_mock_500(_url, _request): + return response(500) class TestLoginWithPostRequest(IntegrationTestCase): @@ -257,11 +257,11 @@ def test_login_token_with_incorrect_schema_url_results_in_404(self): ) # When - with HTTMock(self.schema_url_mock_404): + with HTTMock(self.schema_url_mock_500): self.post(url=f"/session?token={token}") # Then - self.assertStatusNotFound() + self.assertException() def test_login_without_case_id_in_token_is_unauthorised(self): # Given @@ -281,5 +281,5 @@ def schema_url_mock(_url, _request): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def schema_url_mock_404(_url, _request): - return response(404) + def schema_url_mock_500(_url, _request): + return response(500) From f547e27fbeeb79297381491dfa81813e11b5beed Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 13 May 2022 12:12:32 +0100 Subject: [PATCH 022/567] Add section enabled rules from previous sections in when rules (#859) --- app/questionnaire/path_finder.py | 4 +- app/questionnaire/router.py | 9 ++- ...ing_and_skipping_section_dependencies.json | 76 +++++++++++++++++++ ..._and_skipping_section_dependencies.spec.js | 62 +++++++++++++++ 4 files changed, 148 insertions(+), 3 deletions(-) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 6d8483c2d7..7076924013 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -44,7 +44,7 @@ def routing_path( ) if section: - when_rules_block_dependencies = self._get_when_rules_block_dependencies( + when_rules_block_dependencies = self.get_when_rules_block_dependencies( section["id"] ) blocks = self._get_not_skipped_blocks_in_section( @@ -61,7 +61,7 @@ def routing_path( return RoutingPath(routing_path_block_ids, section_id, list_item_id, list_name) - def _get_when_rules_block_dependencies(self, section_id: str) -> list[str]: + def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: """NB: At present when rules block dependencies does not fully support repeating sections. It is supported when the section is dependent i.e the current section is repeating and building the routing path for sections that are not, It isn't supported if it needs to build the path for repeating sections""" diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 76e2feb470..fc86455c4a 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -371,6 +371,11 @@ def _is_section_enabled(self, section: Mapping) -> bool: return True enabled = section["enabled"] + + routing_path_block_ids = self._path_finder.get_when_rules_block_dependencies( + section["id"] + ) + if isinstance(enabled, dict): when_rule_evaluator = RuleEvaluator( self._schema, @@ -379,7 +384,7 @@ def _is_section_enabled(self, section: Mapping) -> bool: self._metadata, self._response_metadata, location=None, - routing_path_block_ids=None, + routing_path_block_ids=routing_path_block_ids, ) return bool(when_rule_evaluator.evaluate(enabled["when"])) @@ -391,6 +396,8 @@ def _is_section_enabled(self, section: Mapping) -> bool: self._metadata, self._answer_store, self._list_store, + current_location=None, + routing_path_block_ids=routing_path_block_ids, ) for condition in enabled ) diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json b/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json index 95803cf823..f3e6bea266 100644 --- a/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json +++ b/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json @@ -58,6 +58,71 @@ "title": "Do you want to skip all age questions in all sections", "type": "General" } + }, + { + "type": "Question", + "id": "skip-household-section", + "question": { + "id": "skip-household-section-question", + "title": "Do you want to skip the question about skipping the household summary section?", + "type": "General", + "answers": [ + { + "id": "skip-household-section-answer", + "label": "It will remove the enable section question from the routing path", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ] + } + }, + { + "type": "Question", + "id": "enable-section", + "question": { + "id": "enable-section-question", + "title": "Do you want to enable the household summary section?", + "type": "General", + "answers": [ + { + "id": "enable-section-answer", + "label": "Depending on the answer it will enable or disable the household summary section", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ] + }, + "skip_conditions": [ + { + "when": [ + { + "id": "skip-household-section-answer", + "condition": "equals", + "value": "Yes" + } + ] + } + ] } ], "id": "skip-age-group" @@ -273,6 +338,17 @@ ] }, { + "enabled": { + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "enable-section-answer" + } + ] + } + }, "id": "household-section", "title": "Household Summary", "summary": { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 4e509545a9..67ded7eb32 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -10,6 +10,8 @@ import RepeatingAgePage from "../generated_pages/new_routing_and_skipping_sectio import RepeatingSexPage from "../generated_pages/new_routing_and_skipping_section_dependencies/repeating-sex.page"; import SecurityPage from "../generated_pages/new_routing_and_skipping_section_dependencies/security.page"; import SkipAgePage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-age.page"; +import SkipEnableSectionPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-household-section.page"; +import EnableSectionPage from "../generated_pages/new_routing_and_skipping_section_dependencies/enable-section.page"; import SkipConfirmationPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-confirmation.page"; import SkipConfirmationSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; import SkipSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-section-summary.page"; @@ -122,6 +124,28 @@ describe("Routing and skipping section dependencies", () => { }); }); + describe("Given the routing and skipping section dependencies questionnaire", () => { + it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", () => { + answerNoToSkipEnableQuestionAndYesToEnableSection(); + + expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.true; + }); + it("When I answer 'No' to skipping the section question and 'No' to enable the section question, Then the household summary will not be visible on the hub", () => { + answerNoToSkipEnableQuestionAndNoToEnableSection(); + + expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + }); + }); + + describe("Given the routing and skipping section dependencies questionnaire and I answered 'No' to skipping the section question and 'Yes' to enable the section question", () => { + it("When I change my answer to skipping the section question to 'No', Then the household summary will not be visible on the hub", () => { + answerNoToSkipEnableQuestionAndYesToEnableSection(); + changeSkipEnableQuestionToYes(); + + expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + }); + }); + describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section", () => { it("When I change my answer to skipping age to 'No', removing the 'are you sure' question from the path, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { answerYesToSkipAgeQuestion(); @@ -182,6 +206,10 @@ const answerYesToSkipAgeQuestion = () => { $(HubPage.summaryRowLink("skip-section")).click(); $(SkipAgePage.yes()).click(); $(SkipAgePage.submit()).click(); + $(SkipEnableSectionPage.no()).click(); + $(SkipEnableSectionPage.submit()).click(); + $(EnableSectionPage.yes()).click(); + $(EnableSectionPage.submit()).click(); $(SkipSectionSummaryPage.submit()).click(); }; @@ -189,6 +217,10 @@ const answerNoToSkipAgeQuestion = () => { $(HubPage.summaryRowLink("skip-section")).click(); $(SkipAgePage.no()).click(); $(SkipAgePage.submit()).click(); + $(SkipEnableSectionPage.no()).click(); + $(SkipEnableSectionPage.submit()).click(); + $(EnableSectionPage.yes()).click(); + $(EnableSectionPage.submit()).click(); $(SkipSectionSummaryPage.submit()).click(); }; @@ -204,6 +236,36 @@ const answerYesToSkipConfirmationQuestion = () => { $(SkipConfirmationSectionSummaryPage.submit()).click(); }; +const answerNoToSkipEnableQuestionAndYesToEnableSection = () => { + $(HubPage.summaryRowLink("skip-section")).click(); + $(SkipAgePage.no()).click(); + $(SkipAgePage.submit()).click(); + $(SkipEnableSectionPage.no()).click(); + $(SkipEnableSectionPage.submit()).click(); + $(EnableSectionPage.yes()).click(); + $(EnableSectionPage.submit()).click(); + $(SkipSectionSummaryPage.submit()).click(); +}; + +const answerNoToSkipEnableQuestionAndNoToEnableSection = () => { + $(HubPage.summaryRowLink("skip-section")).click(); + $(SkipAgePage.no()).click(); + $(SkipAgePage.submit()).click(); + $(SkipEnableSectionPage.no()).click(); + $(SkipEnableSectionPage.submit()).click(); + $(EnableSectionPage.no()).click(); + $(EnableSectionPage.submit()).click(); + $(SkipSectionSummaryPage.submit()).click(); +}; + +const changeSkipEnableQuestionToYes = () => { + $(HubPage.summaryRowLink("skip-section")).click(); + $(SkipSectionSummaryPage.skipHouseholdSectionAnswerEdit()).click(); + $(SkipEnableSectionPage.yes()).click(); + $(SkipEnableSectionPage.submit()).click(); + $(SkipSectionSummaryPage.submit()).click(); +}; + const answerAndSubmitNameQuestion = () => { $(NamePage.name()).setValue("John Smith"); $(NamePage.submit()).click(); From 4e2a78410f9b55b2bef0094008d2521dc9b92a42 Mon Sep 17 00:00:00 2001 From: LJBabbage Date: Wed, 18 May 2022 14:06:26 +0100 Subject: [PATCH 023/567] Rules require dependent section routing path re-evaluation (#848) --- app/questionnaire/path_finder.py | 2 +- app/questionnaire/questionnaire_schema.py | 20 +- .../questionnaire_store_updater.py | 76 +++- app/views/handlers/block.py | 1 + app/views/handlers/question.py | 3 +- tests/app/questionnaire/conftest.py | 11 + .../test_questionnaire_schema.py | 33 ++ .../test_questionnaire_store_updater.py | 347 ++++++++++++++++-- ..._and_skipping_section_dependencies.spec.js | 93 ++++- 9 files changed, 519 insertions(+), 67 deletions(-) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 7076924013..1667c35dfb 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -67,7 +67,7 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: It isn't supported if it needs to build the path for repeating sections""" return [ block_id - for dependent_section in self.schema.when_rules_section_dependencies_map.get( + for dependent_section in self.schema.when_rules_section_dependencies_by_section.get( section_id, {} ) for block_id in self.routing_path(dependent_section) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 103c870a37..c9331052e3 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -55,7 +55,10 @@ def __init__( self._answer_dependencies_map: dict[str, set[AnswerDependent]] = defaultdict( set ) - self._when_rules_section_dependencies_map: dict[str, set[str]] = {} + self._when_rules_section_dependencies_by_section: dict[str, set[str]] = {} + self._when_rules_section_dependencies_by_answer: dict[ + str, set[str] + ] = defaultdict(set) self._language_code = language_code self._questionnaire_json = questionnaire_json @@ -75,8 +78,14 @@ def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: return ImmutableDict(self._answer_dependencies_map) @cached_property - def when_rules_section_dependencies_map(self) -> ImmutableDict[str, set[str]]: - return ImmutableDict(self._when_rules_section_dependencies_map) + def when_rules_section_dependencies_by_section( + self, + ) -> ImmutableDict[str, set[str]]: + return ImmutableDict(self._when_rules_section_dependencies_by_section) + + @cached_property + def when_rules_section_dependencies_by_answer(self) -> ImmutableDict[str, set[str]]: + return ImmutableDict(self._when_rules_section_dependencies_by_answer) @cached_property def language_code(self) -> str: @@ -816,7 +825,7 @@ def _populate_when_rules_section_dependencies(self) -> None: if rules_section_dependencies := self._get_rules_section_dependencies( section["id"], rules ): - self._when_rules_section_dependencies_map[ + self._when_rules_section_dependencies_by_section[ section["id"] ] = rules_section_dependencies @@ -846,6 +855,9 @@ def _get_rules_section_dependencies( section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore if section_id != current_section_id: + self._when_rules_section_dependencies_by_answer[answer_id].add( + current_section_id + ) rules_section_dependencies.add(section_id) # type: ignore if any(operator in rule for operator in OPERATION_MAPPING): diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 7f274c4479..bd67d6fd86 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,4 +1,4 @@ -from collections import defaultdict +from collections import defaultdict, namedtuple from itertools import combinations from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, Union @@ -8,6 +8,9 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent +from app.questionnaire.router import Router + +DependentSection = namedtuple("DependentSection", "section_id list_item_id is_complete") class QuestionnaireStoreUpdater: @@ -20,6 +23,7 @@ def __init__( current_location: Location, schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, + router: Router, current_question: Mapping[str, Any], ): self._current_location = current_location @@ -29,10 +33,12 @@ def __init__( self._answer_store = self._questionnaire_store.answer_store self._list_store = self._questionnaire_store.list_store self._progress_store = self._questionnaire_store.progress_store + self._router = router self.dependent_block_id_by_section_key: Mapping[ SectionKeyType, set[str] ] = defaultdict(set) + self.dependent_sections: set[DependentSection] = set() def save(self): if self.is_dirty(): @@ -252,7 +258,7 @@ def _update_answer( ) ) - def _capture_dependencies_for_answer(self, answer_id: str) -> None: + def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: """Captures a unique list of block ids that are dependents of the provided answer id. The block_ids are mapped to the section key. Dependencies in a repeating section use the list items @@ -284,6 +290,24 @@ def _capture_dependencies_for_answer(self, answer_id: str) -> None: (dependency.section_id, list_item_id) ].add(dependency.block_id) + def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: + """Captures a unique list of section ids that are dependents of the provided answer id.""" + + answer_id_section_dependents = ( + self._schema.when_rules_section_dependencies_by_answer + ) + + for section_id in answer_id_section_dependents.get(answer_id, {}): + if repeating_list := self._schema.get_repeating_list_for_section( + section_id + ): + for list_item_id in self._list_store[repeating_list].items: + self.dependent_sections.add( + DependentSection(section_id, list_item_id, None) + ) + else: + self.dependent_sections.add(DependentSection(section_id, None, None)) + def update_answers( self, form_data: Mapping[str, Any], list_item_id: Optional[str] = None ) -> None: @@ -298,16 +322,40 @@ def update_answers( answer_updated = self._update_answer(answer_id, list_item_id, answer_value) if answer_updated: - self._capture_dependencies_for_answer(answer_id) + self._capture_section_dependencies_for_answer(answer_id) + self._capture_block_dependencies_for_answer(answer_id) - def update_progress_for_dependant_sections(self) -> None: + def update_progress_for_dependent_sections(self) -> None: """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. + Section progress is not updated for the current location as it is handled by `handle_post` on block handlers.""" - Section progress is not updated for the current location as it is handled by `handle_post` on block handlers. + self._remove_dependent_blocks_and_capture_dependent_sections() + + for section in self.dependent_sections: + + if ( + section.section_id, + section.list_item_id, + ) not in self.started_section_keys(): + continue + + is_path_complete = section.is_complete + if is_path_complete is None: + is_path_complete = self._router.is_path_complete( + self._router.routing_path( + section.section_id, list_item_id=section.list_item_id + ) + ) + + self.update_section_status( + is_complete=is_path_complete, + section_id=section.section_id, + list_item_id=section.list_item_id, + ) + + def _remove_dependent_blocks_and_capture_dependent_sections(self) -> None: + """Removes dependent blocks from the progress store.""" - When updating the progress store, the routing path is not re-evaluated because - removing previously completed blocks means the section can't be complete. - """ for ( section_key, blocks_to_remove, @@ -330,10 +378,14 @@ def update_progress_for_dependant_sections(self) -> None: section_id != self._current_location.section_id or list_item_id != self._current_location.list_item_id ): - self.update_section_status( - is_complete=False, - section_id=section_id, - list_item_id=list_item_id, + # Since this section key will be marked as incomplete, any `DependentSection` with is_complete as `None` + # can be removed as we do not need to re-evaluate progress as we already know the section would be incomplete. + dependent = DependentSection(section_id, list_item_id, None) + if dependent in self.dependent_sections: + self.dependent_sections.remove(dependent) + + self.dependent_sections.add( + DependentSection(section_id, list_item_id, False) ) def started_section_keys(self, section_ids: Optional[Iterable[str]] = None): diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index c30e64397d..91b2f49a36 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -57,6 +57,7 @@ def questionnaire_store_updater(self): self._current_location, self._schema, self._questionnaire_store, + self.router, self.block.get("question"), ) diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 0f381d415f..1b0af18438 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -52,6 +52,7 @@ def questionnaire_store_updater(self): self._current_location, self._schema, self._questionnaire_store, + self.router, self.rendered_block.get("question"), ) @@ -189,7 +190,7 @@ def handle_post(self): # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) - self.questionnaire_store_updater.update_progress_for_dependant_sections() + self.questionnaire_store_updater.update_progress_for_dependent_sections() if self.questionnaire_store_updater.is_dirty(): self._routing_path = self.router.routing_path( section_id=self._current_location.section_id, diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 83229a2326..01a250ce30 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -15,6 +15,7 @@ find_pointers_containing, ) from app.questionnaire.placeholder_transforms import PlaceholderTransforms +from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name @@ -1067,6 +1068,11 @@ def mock_empty_answer_store(mocker): return mocker.MagicMock(spec=AnswerStore) +@pytest.fixture +def mock_router(mocker): + return mocker.MagicMock(spec=Router) + + @pytest.fixture def mock_empty_progress_store(mocker): progress_store = mocker.MagicMock(spec=ProgressStore) @@ -1172,3 +1178,8 @@ def dynamic_radio_options_from_checkbox_schema(): @pytest.fixture def dynamic_answer_options_function_driven_schema(): return load_schema_from_name("test_dynamic_answer_options_function_driven") + + +@pytest.fixture +def skipping_section_dependencies_schema(): + return load_schema_from_name("test_new_routing_and_skipping_section_dependencies") diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index fdf9d20917..2ab555b38d 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -648,3 +648,36 @@ def test_answer_dependencies_for_dynamic_options_function_driven( ), } } + + +def test_when_rules_section_dependencies_by_section( + skipping_section_dependencies_schema, +): + schema = skipping_section_dependencies_schema + assert { + "household-personal-details-section": { + "skip-confirmation-section", + "skip-section", + }, + "household-section": {"skip-section"}, + "primary-person": {"skip-confirmation-section", "skip-section"}, + "skip-confirmation-section": {"skip-section"}, + } == schema.when_rules_section_dependencies_by_section + + +def test_when_rules_section_dependencies_by_answer( + skipping_section_dependencies_schema, +): + schema = skipping_section_dependencies_schema + assert { + "enable-section-answer": {"household-section"}, + "skip-age-answer": { + "household-personal-details-section", + "primary-person", + "skip-confirmation-section", + }, + "skip-confirmation-answer": { + "household-personal-details-section", + "primary-person", + }, + } == schema.when_rules_section_dependencies_by_answer diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index d42e2dbace..a67446df9a 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -8,14 +8,19 @@ from app.data_models.progress_store import CompletionStatus, ProgressStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema -from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater +from app.questionnaire.questionnaire_store_updater import ( + DependentSection, + QuestionnaireStoreUpdater, +) +# pylint: disable=too-many-locals def test_save_answers_with_form_data( mock_location, mock_empty_schema, mock_empty_answer_store, mock_questionnaire_store, + mock_router, ): answer_id = "answer" answer_value = "1000" @@ -26,7 +31,11 @@ def test_save_answers_with_form_data( current_question = mock_empty_schema.get_block(mock_location.block_id)["question"] questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, current_question + mock_location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, + current_question, ) questionnaire_store_updater.update_answers(form_data) @@ -41,9 +50,7 @@ def test_save_answers_with_form_data( def test_save_empty_answer_removes_existing_answer( - mock_empty_schema, - mock_empty_answer_store, - mock_questionnaire_store, + mock_empty_schema, mock_empty_answer_store, mock_questionnaire_store, mock_router ): answer_id = "answer" answer_value = "1000" @@ -62,7 +69,11 @@ def test_save_empty_answer_removes_existing_answer( current_question = mock_empty_schema.get_block(location.block_id)["question"] questionnaire_store_updater = QuestionnaireStoreUpdater( - location, mock_empty_schema, mock_questionnaire_store, current_question + location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, + current_question, ) questionnaire_store_updater.update_answers(form_data) @@ -91,6 +102,7 @@ def test_default_answers_are_not_saved( mock_empty_schema, mock_empty_answer_store, mock_questionnaire_store, + mock_router, ): answer_id = "answer" default_value = 0 @@ -105,7 +117,11 @@ def test_default_answers_are_not_saved( current_question = {"answers": [{"id": "answer", "default": default_value}]} questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, current_question + mock_location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, + current_question, ) questionnaire_store_updater.update_answers(form_data) @@ -117,6 +133,7 @@ def test_empty_answers( mock_empty_schema, mock_empty_answer_store, mock_questionnaire_store, + mock_router, ): string_answer_id = "string-answer" checkbox_answer_id = "checkbox-answer" @@ -142,7 +159,11 @@ def test_empty_answers( ] } questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, current_question + mock_location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, + current_question, ) questionnaire_store_updater.update_answers(form_data) @@ -152,8 +173,7 @@ def test_empty_answers( def test_remove_all_answers_with_list_item_id( mock_location, mock_empty_schema, - mock_empty_answer_store, - mock_questionnaire_store, + mock_router, mocker, ): mock_empty_answer_store = AnswerStore( @@ -173,7 +193,7 @@ def test_remove_all_answers_with_list_item_id( ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) questionnaire_store_updater.remove_list_item_and_answers("abc", "abcdef") @@ -184,8 +204,7 @@ def test_remove_all_answers_with_list_item_id( def test_remove_primary_person( mock_location, mock_empty_schema, - mock_empty_answer_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -206,7 +225,7 @@ def test_remove_primary_person( ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) questionnaire_store_updater.remove_primary_person("people") @@ -216,7 +235,7 @@ def test_add_primary_person( mock_location, mock_empty_schema, mock_empty_answer_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -230,7 +249,7 @@ def test_add_primary_person( ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) questionnaire_store_updater.add_primary_person("people") @@ -240,7 +259,7 @@ def test_remove_completed_relationship_locations_for_list_name( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -256,7 +275,7 @@ def test_remove_completed_relationship_locations_for_list_name( progress_store=mock_empty_progress_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) patch_method = "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater._get_relationship_collectors_by_list_name" @@ -278,7 +297,7 @@ def test_remove_completed_relationship_locations_for_list_name_no_locations( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -295,7 +314,7 @@ def test_remove_completed_relationship_locations_for_list_name_no_locations( progress_store=mock_empty_progress_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) questionnaire_store_updater.remove_completed_relationship_locations_for_list_name( @@ -313,7 +332,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -325,7 +344,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( progress_store=mock_empty_progress_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) assert ( @@ -339,8 +358,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( def test_update_same_name_items( mock_location, mock_empty_schema, - mock_empty_answer_store, - mock_questionnaire_store, + mock_router, populated_list_store, mocker, ): @@ -378,7 +396,7 @@ def test_update_same_name_items( ) questionnaire_store_updater = QuestionnaireStoreUpdater( - mock_location, mock_empty_schema, mock_questionnaire_store, None + mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) questionnaire_store_updater.update_same_name_items( @@ -455,6 +473,7 @@ def get_answer_dependencies(for_list=None): ) def test_update_answers_captures_answer_dependencies( mock_empty_answer_store, + mock_router, answer_id, answer_updated, answer_dependencies, @@ -486,6 +505,7 @@ def test_update_answers_captures_answer_dependencies( schema=mock_schema, answer_store=mock_empty_answer_store, list_store=list_store, + router=mock_router, current_location=location, current_question=current_question, ) @@ -532,7 +552,11 @@ def test_update_answers_captures_answer_dependencies( ], ) def test_update_answers_with_answer_dependents( - mock_schema, answer_dependent_answer_id, updated_answer_value, expected_output + mock_schema, + mock_router, + answer_dependent_answer_id, + updated_answer_value, + expected_output, ): answer_store = AnswerStore( @@ -568,6 +592,7 @@ def test_update_answers_with_answer_dependents( schema=mock_schema, answer_store=answer_store, list_store=ListStore(), + router=mock_router, current_location=location, current_question=current_question, ) @@ -576,7 +601,7 @@ def test_update_answers_with_answer_dependents( assert answer_store == expected_output -def test_update_repeating_answers_with_answer_dependents(mock_schema): +def test_update_repeating_answers_with_answer_dependents(mock_schema, mock_router): # Given repeating dependent answers answer_store = AnswerStore( [ @@ -614,6 +639,7 @@ def test_update_repeating_answers_with_answer_dependents(mock_schema): schema=mock_schema, answer_store=answer_store, list_store=list_store, + router=mock_router, current_location=location, current_question=current_question, ) @@ -627,6 +653,222 @@ def test_update_repeating_answers_with_answer_dependents(mock_schema): ) +@pytest.mark.parametrize( + "section_status, updated_answer_value, is_path_complete, expected_status", + [ + ( # When an answer is changed which causes the path of a dependent section to be incomplete, Then that sections is update to IN_PROGRESS + "COMPLETED", + "answer updated", + False, + "IN_PROGRESS", + ), + ( # When an answer is changed which causes the path of a dependent section to be complete, Then that sections is update to COMPLETED + "IN_PROGRESS", + "answer updated", + True, + "COMPLETED", + ), + ( # When an answer is not changed, Then a dependent section status should not change + "IN_PROGRESS", + "original answer", + False, + "IN_PROGRESS", + ), + ( # When an answer is not changed, Then a dependent section status should not change + "COMPLETED", + "original answer", + True, + "COMPLETED", + ), + ], +) +def test_answer_id_section_dependents( + section_status, + updated_answer_value, + is_path_complete, + expected_status, + mock_schema, + mock_router, +): + mock_schema.get_answer_ids_for_question.return_value = ["first-answer"] + mock_schema.get_repeating_list_for_section.return_value = None + mock_schema.when_rules_section_dependencies_by_answer = { + "first-answer": {"section-2"} + } + mock_router.is_path_complete.return_value = is_path_complete + + answer_store = AnswerStore( + [ + AnswerDict(answer_id="first-answer", value="original answer"), + AnswerDict(answer_id="second-answer", value="second answer"), + ] + ) + form_data = MultiDict({"first-answer": updated_answer_value}) + location = Location( + section_id="section", + block_id="first-block", + ) + progress_store = ProgressStore( + [ + { + "section_id": "section-2", + "block_ids": ["second-block"], + "status": section_status, + } + ], + ) + current_question = mock_schema.get_block(location.block_id)["question"] + questionnaire_store_updater = get_questionnaire_store_updater( + schema=mock_schema, + answer_store=answer_store, + progress_store=progress_store, + router=mock_router, + current_location=location, + current_question=current_question, + ) + questionnaire_store_updater.update_answers(form_data) + questionnaire_store_updater.update_progress_for_dependent_sections() + + assert progress_store.get_section_status(section_id="section-2") is expected_status + + +@pytest.mark.parametrize( + "list_item_1_section_status, list_item_2_section_status, updated_answer_value, " + "is_list_item_1_path_complete, is_list_item_2_path_complete, expected_list_item_1_status, expected_list_item_2_status", + [ + ( # When an answer is changed which causes repeating dependent section to be incomplete, Then those repeating sections are updated to IN_PROGRESS + "COMPLETED", + "COMPLETED", + "answer updated", + False, + False, + "IN_PROGRESS", + "IN_PROGRESS", + ), + ( # When an answer is changed which causes repeating dependent section to be complete, Then those repeating sections are updated to COMPLETED + "IN_PROGRESS", + "IN_PROGRESS", + "answer updated", + True, + True, + "COMPLETED", + "COMPLETED", + ), + ( # When an answer is changed which causes repeating section paths to change, Then those repeating sections statuses are updated correctly + "COMPLETED", + "IN_PROGRESS", + "answer updated", + False, + True, + "IN_PROGRESS", + "COMPLETED", + ), + ( # When an answer is not changed, Then a repeating dependent section status should not change + "COMPLETED", + "IN_PROGRESS", + "original answer", + True, + False, + "COMPLETED", + "IN_PROGRESS", + ), + ], +) +def test_answer_id_section_dependents_repeating( + list_item_1_section_status, + list_item_2_section_status, + updated_answer_value, + is_list_item_1_path_complete, + is_list_item_2_path_complete, + expected_list_item_1_status, + expected_list_item_2_status, + mock_schema, + mock_router, +): + + mock_schema.get_repeating_list_for_section.return_value = "list-name" + mock_schema.get_answer_ids_for_question.return_value = ["first-answer"] + mock_schema.when_rules_section_dependencies_by_answer = { + "first-answer": {"section-2"} + } + + answer_store = AnswerStore( + [ + AnswerDict(answer_id="first-answer", value="original answer"), + AnswerDict( + answer_id="second-answer", + value="second answer", + list_item_id="list-item-id-1", + ), + AnswerDict( + answer_id="second-answer", + value="second answer", + list_item_id="list-item-id-2", + ), + ] + ) + list_store = ListStore( + [{"items": ["list-item-id-1", "list-item-id-2"], "name": "list-name"}] + ) + form_data = MultiDict({"first-answer": updated_answer_value}) + location = Location( + section_id="section", + block_id="first-block", + ) + + progress_store = ProgressStore( + [ + { + "section_id": "section-2", + "block_ids": ["second-block"], + "status": list_item_1_section_status, + "list_item_id": "list-item-id-1", + }, + { + "section_id": "section-2", + "block_ids": ["second-block"], + "status": list_item_2_section_status, + "list_item_id": "list-item-id-2", + }, + ], + ) + current_question = mock_schema.get_block(location.block_id)["question"] + questionnaire_store_updater = get_questionnaire_store_updater( + schema=mock_schema, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + router=mock_router, + current_location=location, + current_question=current_question, + ) + questionnaire_store_updater.update_answers(form_data) + + # This test case is dependent on the order that the dependent_sections set is iterated over, + # however as python sets are unordered we need to check that the first item is equal to our expected + # list_item_id so that we can set the correct side effect as per the test case + first_item = next(iter(questionnaire_store_updater.dependent_sections), None) + effects = [is_list_item_1_path_complete, is_list_item_2_path_complete] + if first_item and first_item.list_item_id != "list-item-id-1": + effects = [is_list_item_2_path_complete, is_list_item_1_path_complete] + mock_router.is_path_complete.side_effect = effects + + questionnaire_store_updater.update_progress_for_dependent_sections() + + assert ( + progress_store.get_section_status( + section_id="section-2", list_item_id="list-item-id-1" + ) + is expected_list_item_1_status + ) + assert ( + progress_store.get_section_status( + section_id="section-2", list_item_id="list-item-id-2" + ) + is expected_list_item_2_status + ) + + def get_questionnaire_store_updater( *, current_location=None, @@ -634,6 +876,7 @@ def get_questionnaire_store_updater( answer_store=None, list_store=None, progress_store=None, + router=None, current_question=None, ): answer_store = AnswerStore() if answer_store is None else answer_store @@ -659,7 +902,11 @@ def get_questionnaire_store_updater( current_question = current_question or {} return QuestionnaireStoreUpdater( - current_location, mock_schema, mock_questionnaire_store, current_question + current_location, + mock_schema, + mock_questionnaire_store, + router, + current_question, ) @@ -668,7 +915,7 @@ def get_questionnaire_store_updater( [CompletionStatus.IN_PROGRESS, CompletionStatus.COMPLETED], ) def test_dependent_sections_completed_dependant_blocks_removed_and_status_updated( - dependent_section_status, + dependent_section_status, mock_router ): # Given current_location = Location( @@ -691,7 +938,9 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update ], ) questionnaire_store_updater = get_questionnaire_store_updater( - current_location=current_location, progress_store=progress_store + current_location=current_location, + progress_store=progress_store, + router=mock_router, ) dependent_section_key = ("breakdown-section", None) dependent_block_id = "turnover-breakdown-block" @@ -705,7 +954,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update ) # When - questionnaire_store_updater.update_progress_for_dependant_sections() + questionnaire_store_updater.update_progress_for_dependent_sections() # Then assert dependent_block_id not in progress_store.get_completed_block_ids( @@ -750,7 +999,7 @@ def test_dependent_sections_current_section_status_not_updated(mocker): ) # When - questionnaire_store_updater.update_progress_for_dependant_sections() + questionnaire_store_updater.update_progress_for_dependent_sections() # Then assert dependent_block_id not in progress_store.get_completed_block_ids( @@ -760,7 +1009,7 @@ def test_dependent_sections_current_section_status_not_updated(mocker): assert questionnaire_store_updater.update_section_status.call_count == 0 -def test_dependent_sections_not_started_skipped(mocker): +def test_dependent_sections_not_started_skipped(mock_router, mocker): # Given current_location = Location( section_id="company-summary-section", block_id="total-turnover-block" @@ -775,7 +1024,9 @@ def test_dependent_sections_not_started_skipped(mocker): ], ) questionnaire_store_updater = get_questionnaire_store_updater( - current_location=current_location, progress_store=progress_store + current_location=current_location, + progress_store=progress_store, + router=mock_router, ) dependent_section_key = ("breakdown-section", None) @@ -789,14 +1040,14 @@ def test_dependent_sections_not_started_skipped(mocker): questionnaire_store_updater.update_section_status = mocker.Mock() # When - questionnaire_store_updater.update_progress_for_dependant_sections() + questionnaire_store_updater.update_progress_for_dependent_sections() # Then assert questionnaire_store_updater.remove_completed_location.call_count == 0 assert questionnaire_store_updater.update_section_status.call_count == 0 -def test_dependent_sections_started_but_blocks_incomplete(mocker): +def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): # Given current_location = Location( section_id="company-summary-section", block_id="total-employees-block" @@ -818,7 +1069,9 @@ def test_dependent_sections_started_but_blocks_incomplete(mocker): ], ) questionnaire_store_updater = get_questionnaire_store_updater( - current_location=current_location, progress_store=progress_store + current_location=current_location, + progress_store=progress_store, + router=mock_router, ) dependent_section_key = ("breakdown-section", None) @@ -834,7 +1087,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mocker): ) # When - questionnaire_store_updater.update_progress_for_dependant_sections() + questionnaire_store_updater.update_progress_for_dependent_sections() # Then assert questionnaire_store_updater.update_section_status.call_count == 0 @@ -845,7 +1098,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mocker): [CompletionStatus.IN_PROGRESS, CompletionStatus.COMPLETED], ) def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_status_updated( - dependent_section_status, + dependent_section_status, mock_router ): # Given current_location = Location( @@ -888,15 +1141,21 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta current_location=current_location, progress_store=progress_store, list_store=list_store, + router=mock_router, ) questionnaire_store_updater.dependent_block_id_by_section_key = { ("breakdown-section", list_item): {"turnover-breakdown-block"} for list_item in list_store["some-list"] } + questionnaire_store_updater.dependent_sections.add( + DependentSection( + section_id="breakdown-section", list_item_id="item-1", is_complete=None + ) + ) # When - questionnaire_store_updater.update_progress_for_dependant_sections() + questionnaire_store_updater.update_progress_for_dependent_sections() # Then for list_item in list_store["some-list"]: @@ -908,3 +1167,11 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta progress_store.get_section_status(section_id, list_item_id) == CompletionStatus.IN_PROGRESS ) + assert questionnaire_store_updater.dependent_sections == { + DependentSection( + section_id=section_id, list_item_id="item-1", is_complete=False + ), + DependentSection( + section_id=section_id, list_item_id="item-2", is_complete=False + ), + } diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 67ded7eb32..5436175690 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -19,11 +19,11 @@ import SkipSectionSummaryPage from "../generated_pages/new_routing_and_skipping_ import HubPage from "../base_pages/hub.page"; describe("Routing and skipping section dependencies", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); - }); - describe("Given the routing and skipping section dependencies questionnaire", () => { + beforeEach("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); + it("When I answer 'No' to skipping the age question, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { answerNoToSkipAgeQuestion(); @@ -125,6 +125,9 @@ describe("Routing and skipping section dependencies", () => { }); describe("Given the routing and skipping section dependencies questionnaire", () => { + beforeEach("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", () => { answerNoToSkipEnableQuestionAndYesToEnableSection(); @@ -138,6 +141,9 @@ describe("Routing and skipping section dependencies", () => { }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'No' to skipping the section question and 'Yes' to enable the section question", () => { + before("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); it("When I change my answer to skipping the section question to 'No', Then the household summary will not be visible on the hub", () => { answerNoToSkipEnableQuestionAndYesToEnableSection(); changeSkipEnableQuestionToYes(); @@ -147,17 +153,17 @@ describe("Routing and skipping section dependencies", () => { }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section", () => { + before("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); + it("When I change my answer to skipping age to 'No', removing the 'are you sure' question from the path, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { answerYesToSkipAgeQuestion(); selectConfirmationSectionAndAnswerSecurityQuestion(); answerNoToSkipConfirmationQuestion(); - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); - $(SkipAgePage.no()).click(); - $(SkipAgePage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); + editNoToSkipAgeQuestion(); selectPrimaryPerson(); answerAndSubmitNameQuestion(); @@ -173,6 +179,59 @@ describe("Routing and skipping section dependencies", () => { ); }); }); + + describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and complete the Primary Person section", () => { + before("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); + + it("When I change my answer to skipping age to 'No', Then the Primary Person section status is changed to Partially completed", () => { + answerYesToSkipAgeQuestion(); + selectPrimaryPerson(); + answerAndSubmitNameQuestion(); + answerAndSubmitReasonForNoConfirmationQuestion(); + $(PrimaryPersonSummaryPage.submit()).click(); + + expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + + editNoToSkipAgeQuestion(); + + expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Partially completed"); + }); + + it("When I change my answer back to skipping age to 'Yes', Then the Primary Person section status is changed back to Completed", () => { + editYesToSkipAgeQuestion(); + + expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + }); + }); + + describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and add 2 household members but complete only one", () => { + before("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + }); + + it("When I change my answer to skipping age to 'No', Then the completed household member status is changed to Partially completed and the other stays as not started", () => { + answerYesToSkipAgeQuestion(); + addHouseholdMembers(); + $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + $(RepeatingSexPage.female()).click(); + $(RepeatingSexPage.submit()).click(); + $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + + editNoToSkipAgeQuestion(); + + expect($(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Partially completed"); + expect($(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + }); + + it("When I change my answer back to skipping age to 'Yes', Then the Partially completed household member status is changed back to Completed and the other stays as not started", () => { + editYesToSkipAgeQuestion(); + + expect($(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Completed"); + expect($(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + }); + }); }); const addHouseholdMembers = () => { @@ -213,6 +272,22 @@ const answerYesToSkipAgeQuestion = () => { $(SkipSectionSummaryPage.submit()).click(); }; +const editNoToSkipAgeQuestion = () => { + $(HubPage.summaryRowLink("skip-section")).click(); + $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); + $(SkipAgePage.no()).click(); + $(SkipAgePage.submit()).click(); + $(SkipSectionSummaryPage.submit()).click(); +}; + +const editYesToSkipAgeQuestion = () => { + $(HubPage.summaryRowLink("skip-section")).click(); + $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); + $(SkipAgePage.yes()).click(); + $(SkipAgePage.submit()).click(); + $(SkipSectionSummaryPage.submit()).click(); +}; + const answerNoToSkipAgeQuestion = () => { $(HubPage.summaryRowLink("skip-section")).click(); $(SkipAgePage.no()).click(); From 21fd6a401bdcd3d9469feb79d319edbfd23f9b2a Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 19 May 2022 09:33:08 +0100 Subject: [PATCH 024/567] Add type hinting to secrets (#866) --- app/secrets.py | 12 +++++++++--- mypy.ini | 5 +++++ tests/integration/routes/test_feedback.py | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/secrets.py b/app/secrets.py index d993171848..338e38a3a6 100644 --- a/app/secrets.py +++ b/app/secrets.py @@ -1,3 +1,7 @@ +from typing import Mapping, Optional + +SecretsType = Mapping[str, Mapping[str, str]] + REQUIRED_SECRETS = [ "EQ_SERVER_SIDE_STORAGE_USER_ID_SALT", "EQ_SERVER_SIDE_STORAGE_USER_IK_SALT", @@ -8,7 +12,9 @@ ] -def validate_required_secrets(secrets, additional_required_secrets=None): +def validate_required_secrets( + secrets: SecretsType, additional_required_secrets: Optional[list[str]] = None +) -> None: all_required_secrets = ( REQUIRED_SECRETS + additional_required_secrets if additional_required_secrets @@ -20,8 +26,8 @@ def validate_required_secrets(secrets, additional_required_secrets=None): class SecretStore: - def __init__(self, secrets): + def __init__(self, secrets: SecretsType) -> None: self.secrets = secrets.get("secrets", {}) - def get_secret_by_name(self, secret_name): + def get_secret_by_name(self, secret_name: str) -> Optional[str]: return self.secrets.get(secret_name) diff --git a/mypy.ini b/mypy.ini index 5271cedcfb..399d54a16f 100644 --- a/mypy.ini +++ b/mypy.ini @@ -145,3 +145,8 @@ no_implicit_optional = True disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True + +[mypy-app.secrets] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True diff --git a/tests/integration/routes/test_feedback.py b/tests/integration/routes/test_feedback.py index 2c8d845298..2fb4e89a08 100644 --- a/tests/integration/routes/test_feedback.py +++ b/tests/integration/routes/test_feedback.py @@ -5,7 +5,7 @@ from tests.integration.integration_test_case import IntegrationTestCase -# pylint: disable=too-many-public-methods +# pylint: disable=too-many-public-methods class TestFeedback(IntegrationTestCase): SEND_FEEDBACK_URL = "/submitted/feedback/send" SENT_FEEDBACK_URL = "/submitted/feedback/sent" From 76ee4a81e901f1c92ec7fc8cf12dd055ebb4c52b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 24 May 2022 14:36:36 +0100 Subject: [PATCH 025/567] Schemas v3.4.0 (#871) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b299be9782..c219f7237a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.3.0 +v3.4.0 From 3b97fee210cfeb109efb6187039df5154b89a453 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 27 May 2022 11:56:05 +0100 Subject: [PATCH 026/567] Update DS version to v50 (#870) --- .design-system-version | 2 +- package.json | 2 +- templates/partials/question.html | 4 ++-- yarn.lock | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.design-system-version b/.design-system-version index 143775a017..465f70b6c2 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -48.0.3 +50.0.1 diff --git a/package.json b/package.json index 7229f3a34d..e7e179c2d3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^100.0.0", + "chromedriver": "^101.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/templates/partials/question.html b/templates/partials/question.html index 51b1f033ef..a00c36c3ef 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -67,10 +67,10 @@ {%- set mutuallyExclusive = { "or": _("Or"), - "checkbox": map_select_config(form, question.answers[-1])[0], + "exclusiveOptions": map_select_config(form, question.answers[-1]), "deselectionMessage": deselectionMessage, "deselectGroupAdjective": deselectGroupAdjective, - "deselectCheckboxAdjective": _("deselected") + "deselectExclusiveOptionAdjective": _("deselected") } -%} {% include 'partials/answer.html' %} diff --git a/yarn.lock b/yarn.lock index dc4213453c..7bc8c73abe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1882,9 +1882,9 @@ chrome-launcher@^0.15.0: lighthouse-logger "^1.0.0" chromedriver@^101.0.0: - version "100.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-100.0.0.tgz#1b4bf5c89cea12c79f53bc94d8f5bb5aa79ed7be" - integrity sha512-oLfB0IgFEGY9qYpFQO/BNSXbPw7bgfJUN5VX8Okps9W2qNT4IqKh5hDwKWtpUIQNI6K3ToWe2/J5NdpurTY02g== + version "101.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-101.0.0.tgz#ad19003008dd5df1770a1ad96059a9c5fe78e365" + integrity sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w== dependencies: "@testim/chrome-version" "^1.1.2" axios "^0.24.0" From cb59bd7efd5d5bcccb1f55393de9e0dbac3b4b8b Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 30 May 2022 09:03:59 +0100 Subject: [PATCH 027/567] Calculated Summary min/max dependencies (#865) --- app/questionnaire/questionnaire_schema.py | 11 ++- schemas/test/en/test_calculated_summary.json | 63 ++++++++++++++ .../test_questionnaire_schema.py | 24 ++++++ .../spec/features/calculated_summary.spec.js | 84 ++++++++++++++++++- 4 files changed, 179 insertions(+), 3 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index c9331052e3..ed1fa93016 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -289,7 +289,8 @@ def _update_answer_dependencies_for_answer( value = answer.get(key, {}).get("value") if isinstance(value, dict): self._update_answer_dependencies_for_value_source( - value, block_id=block_id + value, + block_id=block_id, ) if dynamic_options_values := answer.get("dynamic_options", {}).get("values"): @@ -315,6 +316,14 @@ def _update_answer_dependencies_for_value_source( self._answer_dependencies_map[value_source["identifier"]] |= { self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore } + if value_source["source"] == "calculated_summary": + identifier = value_source["identifier"] + calculated_summary_block = self.get_block(identifier) + answer_ids_for_block = calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + for answer_id_for_block in answer_ids_for_block: + self._answer_dependencies_map[answer_id_for_block] |= { + self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore + } def _get_answer_dependent_for_block_id( self, *, block_id: str, answer_id: Optional[str] = None diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 0599f2ba22..b6a2ce79a3 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -460,6 +460,69 @@ } ] } + }, + { + "type": "Question", + "id": "set-min-max-block", + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" + ] + } + }, + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-skipped-fourth", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } } ] } diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 2ab555b38d..446869d696 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -451,6 +451,12 @@ def test_answer_dependencies_for_calculated_summary( for_list=None, answer_id=None, ), + AnswerDependent( + section_id="default-section", + block_id="set-min-max-block", + for_list=None, + answer_id=None, + ), }, "second-number-answer": { AnswerDependent( @@ -465,6 +471,12 @@ def test_answer_dependencies_for_calculated_summary( for_list=None, answer_id=None, ), + AnswerDependent( + section_id="default-section", + block_id="set-min-max-block", + for_list=None, + answer_id=None, + ), }, "second-number-answer-also-in-total": { AnswerDependent( @@ -479,6 +491,12 @@ def test_answer_dependencies_for_calculated_summary( for_list=None, answer_id=None, ), + AnswerDependent( + section_id="default-section", + block_id="set-min-max-block", + for_list=None, + answer_id=None, + ), }, "third-number-answer": { AnswerDependent( @@ -493,6 +511,12 @@ def test_answer_dependencies_for_calculated_summary( for_list=None, answer_id=None, ), + AnswerDependent( + section_id="default-section", + block_id="set-min-max-block", + for_list=None, + answer_id=None, + ), }, "fourth-number-answer": { AnswerDependent( diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index 92c38b9f1f..ff1ca873d1 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -8,6 +8,7 @@ import FourthAndAHalfNumberBlockPage from "../../generated_pages/calculated_summ import FifthNumberBlockPage from "../../generated_pages/calculated_summary/fifth-number-block.page.js"; import SixthNumberBlockPage from "../../generated_pages/calculated_summary/sixth-number-block.page.js"; import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page.js"; +import SetMinMaxBlockPage from "../../generated_pages/calculated_summary/set-min-max-block.page.js"; import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/calculated_summary/currency-total-playback-skipped-fourth.page.js"; import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page.js"; import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page.js"; @@ -222,10 +223,28 @@ describe("Feature: Calculated Summary", () => { textsToAssert.forEach((text) => expect(content).to.contain(text)); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the calculated summary page which is dependent on the change before I can return to the summary", () => { + it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + + it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { + $(SubmitPage.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); $(SubmitPage.thirdNumberAnswerEdit()).click(); $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); $(ThirdNumberBlockPage.submit()).click(); @@ -239,7 +258,68 @@ describe("Feature: Calculated Summary", () => { ); $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £15.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £6.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + $(SetMinMaxBlockPage.submit()).click(); expect(browser.getUrl()).to.contain(SubmitPage.pageName); }); From cbd2a013c309d0fe9703c25ecf9a9fe8246e58fd Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 13 Jun 2022 11:30:33 +0100 Subject: [PATCH 028/567] Update schemas to v3.4.1 (#875) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c219f7237a..3d67a549d7 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.4.0 +v3.4.1 From e00bf7d99e54fc1b82704156c2000e6209775010 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 14 Jun 2022 13:38:54 +0100 Subject: [PATCH 029/567] Introduce social survey config (#878) --- app/helpers/template_helpers.py | 3 +- app/survey_config/__init__.py | 2 + app/survey_config/social_survey_config.py | 48 ++++++++++++++ templates/thank-you.html | 30 +++++---- tests/app/helpers/conftest.py | 44 +++++++++++++ tests/app/helpers/test_template_helpers.py | 77 +++++++++++++++++++--- tests/integration/routes/test_thank_you.py | 9 +++ tests/integration/test_header_links.py | 49 ++++++++++++++ 8 files changed, 239 insertions(+), 23 deletions(-) create mode 100644 app/survey_config/social_survey_config.py diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index e5b68c7228..9db625e44d 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -18,6 +18,7 @@ CensusNISRASurveyConfig, CensusSurveyConfig, NorthernIrelandBusinessSurveyConfig, + SocialSurveyConfig, SurveyConfig, WelshCensusSurveyConfig, ) @@ -164,7 +165,7 @@ def survey_config_mapping( "default": BusinessSurveyConfig, "business": BusinessSurveyConfig, "health": SurveyConfig, - "social": SurveyConfig, + "social": SocialSurveyConfig, "northernireland": NorthernIrelandBusinessSurveyConfig, "census": (WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig), "census-nisra": CensusNISRASurveyConfig, diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 5b13ae4ef5..c95a79fc3d 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -5,9 +5,11 @@ WelshCensusSurveyConfig, ) from .link import Link +from .social_survey_config import SocialSurveyConfig from .survey_config import SurveyConfig __all__ = [ + "SocialSurveyConfig", "SurveyConfig", "CensusSurveyConfig", "CensusNISRASurveyConfig", diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py new file mode 100644 index 0000000000..eaa93a476b --- /dev/null +++ b/app/survey_config/social_survey_config.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass, field +from typing import Iterable, Mapping, MutableMapping + +from flask_babel import lazy_gettext + +from app.survey_config.link import Link +from app.survey_config.survey_config import SurveyConfig + + +@dataclass +class SocialSurveyConfig( + SurveyConfig, +): + base_url: str = "https://rh.ons.gov.uk" + survey_title: str = "ONS Social Surveys" + footer_links: Iterable[MutableMapping] = field(default_factory=list) + footer_legal_links: Iterable[Mapping] = field(default_factory=list) + + def __post_init__(self): + super().__post_init__() + + if self.schema: + self.data_layer: list[dict] = [ + { + key: self.schema.json[key] + for key in ["survey_id", "title"] + if key in self.schema.json + } + ] + + if not self.account_service_log_out_url: + self.account_service_log_out_url: str = f"{self.base_url}/sign-in/logout" + + self.footer_links = [ + Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__, + Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__, + Link( + lazy_gettext("Accessibility"), + self.accessibility_url, + ).__dict__, + ] + self.footer_legal_links = [ + Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + self.privacy_and_data_protection_url, + ).__dict__, + ] diff --git a/templates/thank-you.html b/templates/thank-you.html index fead0069b0..4c262a4197 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -5,21 +5,23 @@ {% set page_title = _("We’ve received your answers") %} {% set hide_sign_out_button = content.hide_sign_out_button %} -{% set breadcrumbs = { - "ariaLabel": 'Back to surveys', - "itemsList": [ - { - "url": account_service_todo_url, - "id": "back-to-surveys", - "text": _("Back to surveys"), - "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Back to surveys link click' +{% if account_service_todo_url %} + {% set breadcrumbs = { + "ariaLabel": 'Back to surveys', + "itemsList": [ + { + "url": account_service_todo_url, + "id": "back-to-surveys", + "text": _("Back to surveys"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Back to surveys link click' + } } - } - ] -} %} + ] + } %} +{% endif %} {% block main %} {% call onsPanel({ diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index 3c0c9ab8fb..dc4e405189 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -135,6 +135,50 @@ def expected_footer_business_theme(footer_context): return {**footer_context, **business} +@fixture +def expected_footer_social_theme(footer_context): + social = { + "rows": [ + { + "itemsList": [ + { + "text": "What we do", + "url": "https://www.ons.gov.uk/aboutus/whatwedo/", + "target": "_blank", + }, + { + "text": "Contact us", + "url": "https://rh.ons.gov.uk/contact-us/", + "target": "_blank", + }, + { + "text": "Accessibility", + "url": "https://www.ons.gov.uk/help/accessibility/", + "target": "_blank", + }, + ] + } + ], + "legal": [ + { + "itemsList": [ + { + "text": "Cookies", + "url": "https://rh.ons.gov.uk/cookies/", + "target": "_blank", + }, + { + "text": "Privacy and data protection", + "url": "https://rh.ons.gov.uk/privacy-and-data-protection/", + "target": "_blank", + }, + ] + } + ], + } + return {**footer_context, **social} + + @fixture def expected_footer_nisra_theme(): return { diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 23d7f828e9..ad2888a8e0 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -11,6 +11,7 @@ CensusNISRASurveyConfig, CensusSurveyConfig, NorthernIrelandBusinessSurveyConfig, + SocialSurveyConfig, SurveyConfig, WelshCensusSurveyConfig, ) @@ -43,6 +44,20 @@ def test_footer_context_business_theme(app: Flask, expected_footer_business_them assert result == expected_footer_business_theme +def test_footer_context_social_theme(app: Flask, expected_footer_social_theme): + with app.test_client(): + survey_config = SocialSurveyConfig() + + result = ContextHelper( + language="en", + is_post_submission=False, + include_csrf_token=True, + survey_config=survey_config, + ).context["footer"] + + assert result == expected_footer_social_theme + + def test_footer_warning_in_context_census_theme(app: Flask): with app.app_context(): expected = "Make sure you leave this page or close your browser if using a shared device" @@ -103,6 +118,26 @@ def test_get_page_header_context_business(app: Flask): assert result == expected +def test_get_page_header_context_social(app: Flask): + expected = { + "logo": "ons-logo-en", + "logoAlt": "Office for National Statistics logo", + "title": "ONS Social Surveys", + } + + with app.app_context(): + survey_config = SocialSurveyConfig() + + result = ContextHelper( + language="en", + is_post_submission=False, + include_csrf_token=True, + survey_config=survey_config, + ).context["page_header"] + + assert result == expected + + def test_get_page_header_context_census(app: Flask): expected = { "title": "Census 2021", @@ -201,6 +236,12 @@ def test_get_page_header_context_census_nisra(app: Flask): ], }, ), + (SocialSurveyConfig(), False, None), + ( + SocialSurveyConfig(schema=QuestionnaireSchema({"survey_id": "001"})), + True, + None, + ), ], ) def test_service_links_context( @@ -242,6 +283,10 @@ def test_service_links_context( NorthernIrelandBusinessSurveyConfig(), "https://surveys.ons.gov.uk/contact-us/", ), + ( + SocialSurveyConfig(), + "https://rh.ons.gov.uk/contact-us/", + ), ], ) def test_contact_us_url_context( @@ -291,6 +336,10 @@ def test_sign_out_button_text_context( NorthernIrelandBusinessSurveyConfig(), "https://surveys.ons.gov.uk/cookies/", ), + ( + SocialSurveyConfig(), + "https://rh.ons.gov.uk/cookies/", + ), ], ) def test_cookie_settings_url_context( @@ -315,6 +364,7 @@ def test_cookie_settings_url_context( BusinessSurveyConfig(), "https://surveys.ons.gov.uk/my-account", ), + (SocialSurveyConfig(), None), ], ) def test_account_service_my_account_url_context( @@ -335,6 +385,10 @@ def test_account_service_my_account_url_context( BusinessSurveyConfig(), "https://surveys.ons.gov.uk/surveys/todo", ), + ( + SocialSurveyConfig(), + None, + ), ], ) def test_account_service_my_todo_url_context( @@ -359,6 +413,10 @@ def test_account_service_my_todo_url_context( NorthernIrelandBusinessSurveyConfig(), "https://surveys.ons.gov.uk/sign-in/logout", ), + ( + SocialSurveyConfig(), + "https://rh.ons.gov.uk/sign-in/logout", + ), ], ) def test_account_service_log_out_url_context( @@ -379,8 +437,8 @@ def test_account_service_log_out_url_context( ("business", "cy", BusinessSurveyConfig), ("health", "en", SurveyConfig), ("health", "cy", SurveyConfig), - ("social", "en", SurveyConfig), - ("social", "cy", SurveyConfig), + ("social", "en", SocialSurveyConfig), + ("social", "cy", SocialSurveyConfig), ("northernireland", "en", NorthernIrelandBusinessSurveyConfig), ("northernireland", "cy", NorthernIrelandBusinessSurveyConfig), ("census", "en", CensusSurveyConfig), @@ -399,17 +457,20 @@ def test_get_survey_config( @pytest.mark.parametrize( - "survey_config_type", - [SurveyConfig, BusinessSurveyConfig], + "survey_config_type, base_url", + [ + (SocialSurveyConfig, "https://rh.ons.gov.uk"), + (SurveyConfig, "http://localhost"), + (BusinessSurveyConfig, "http://localhost"), + ], ) def test_survey_config_base_url_provided_used_in_links( - app: Flask, survey_config_type: Type[SurveyConfig] + app: Flask, survey_config_type: Type[SurveyConfig], base_url: str ): - base_url = "http://localhost" with app.app_context(): result = survey_config_type(base_url=base_url) - assert result.base_url == "http://localhost" + assert result.base_url == base_url urls_to_check = [ result.account_service_my_account_url, @@ -505,7 +566,7 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte ("default", "en", "ONS Business Surveys"), ("business", "en", "ONS Business Surveys"), ("health", "en", None), - ("social", "en", None), + ("social", "en", "ONS Social Surveys"), ("northernireland", "en", "ONS Business Surveys"), ("census", "en", "Census 2021"), ("census", "cy", "Census 2021"), diff --git a/tests/integration/routes/test_thank_you.py b/tests/integration/routes/test_thank_you.py index 82cfd59128..c9fa87f69b 100644 --- a/tests/integration/routes/test_thank_you.py +++ b/tests/integration/routes/test_thank_you.py @@ -104,6 +104,15 @@ def test_back_to_surveys_link_on_thank_you(self): self.assertInBody("Back to surveys") self.assertInBody(ACCOUNT_SERVICE_TODO_PATH) + def test_back_to_surveys_link_not_on_thank_you_theme_social(self): + self.launchSurvey("test_theme_social") + self.post() + self.post() + + self.assertInUrl("thank-you") + self.assertNotInBody("Back to surveys") + self.assertNotInBody(ACCOUNT_SERVICE_TODO_PATH) + def test_view_answers_after_submission_guidance(self): self.launchSurvey("test_thank_you") self.post({"answer": "Yes"}) diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index 4ee76c1228..74360b7a3d 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -53,6 +53,10 @@ def assert_help_link_exist_not_authenticated_after_sign_out(self): f"{ACCOUNT_SERVICE_URL}/help", ) + def assert_help_link_does_not_exist_not_authenticated_after_sign_out(self): + help_link = self.getLinkById("header-link-help") + self.assertIsNone(help_link) + def assert_help_link_does_not_exist(self): help_link = self.getLinkById("header-link-help") self.assertIsNone(help_link) @@ -84,6 +88,18 @@ def test_links_not_in_header_when_no_session(self): self.assert_sign_out_link_does_not_exist() self.assert_help_link_exist_not_authenticated() + def test_links_not_in_header_when_valid_session_theme_social(self): + # Given + self.launchSurvey("test_theme_social") + + # When + self.assertStatusOK() + + # Then + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_does_not_exist() + class TestHeaderLinksPostSubmission(TestHeaderLinks): def test_links_in_header_when_valid_session(self): @@ -113,6 +129,21 @@ def test_links_not_in_header_when_no_session(self): self.assert_sign_out_link_does_not_exist() self.assert_help_link_exist_not_authenticated() + def test_links_not_in_header_when_valid_session_theme_social(self): + # Given + self.launchSurvey("test_theme_social") + self.post() + self.post() + + # When + self.assertInUrl("/thank-you") + self.assertStatusOK() + + # Then + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_does_not_exist() + class TestHeaderLinksPostSignOut(TestHeaderLinks): def test_links_not_in_header_after_sign_out(self): @@ -132,3 +163,21 @@ def test_links_not_in_header_after_sign_out(self): self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() self.assert_help_link_exist_not_authenticated_after_sign_out() + + def test_links_not_in_header_after_sign_out_theme_social(self): + # Given + self.launchSurvey("test_theme_social") + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_does_not_exist() + + # When I sign out and go back to previous url since we will be redirected + current_url = self.last_url + self.signOut() + self.get(current_url) + + # Then + self.assertInBody("Sorry, you need to sign in again") + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_does_not_exist_not_authenticated_after_sign_out() From 030787c663399b2692c204c3605f3abb8cd03e24 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:22:37 +0100 Subject: [PATCH 030/567] Remove remaining unittest library imports (#879) --- tests/app/questionnaire/rules/test_rule_evaluator.py | 2 +- tests/app/questionnaire/test_placeholder_parser.py | 2 +- tests/app/questionnaire/test_router.py | 2 +- tests/app/questionnaire/test_value_source_resolver.py | 2 +- tests/app/submitter/conftest.py | 2 +- tests/app/views/contexts/test_section_summary_context.py | 3 +-- tests/app/views/contexts/test_submitted_response_context.py | 2 +- tests/app/views/handlers/conftest.py | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 5143de59b8..8c4712c04c 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -1,9 +1,9 @@ from datetime import datetime, timezone from typing import Mapping, Optional, Union -from unittest.mock import Mock import pytest from freezegun import freeze_time +from mock import Mock from app.data_models import AnswerStore, ListStore from app.data_models.answer import Answer diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 22fc2c65fc..084a8c58d6 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1,4 +1,4 @@ -from unittest.mock import Mock +from mock import Mock from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 17104e600d..bce950d3a9 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -1,8 +1,8 @@ from functools import cached_property -from unittest.mock import Mock import pytest from flask import url_for +from mock import Mock from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 79deb00fd9..e0a4d82275 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1,7 +1,7 @@ from typing import Mapping, Optional, Union -from unittest.mock import Mock import pytest +from mock import Mock from app.data_models import AnswerStore, ListStore from app.data_models.answer import Answer, AnswerDict diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 20164b6267..b012879b8f 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -1,10 +1,10 @@ # pylint: disable=redefined-outer-name import uuid -from unittest.mock import MagicMock import pytest from google.cloud.storage import Blob from google.resumable_media import InvalidResponse +from mock import MagicMock from requests import Response from app.data_models import QuestionnaireStore diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index d2c99b6cee..8fdb7a2ff4 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -1,6 +1,5 @@ -from unittest.mock import MagicMock - import pytest +from mock import MagicMock from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 1fe762e016..07f445abc5 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -1,9 +1,9 @@ from datetime import datetime, timedelta, timezone -from unittest.mock import Mock import pytest from flask import Flask from flask_babel import format_datetime +from mock import Mock from app.data_models import QuestionnaireStore from app.data_models.answer import Answer diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index b88cb7e6c6..f38dff6f97 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -1,9 +1,9 @@ import uuid from datetime import datetime, timedelta, timezone -from unittest.mock import Mock import pytest from freezegun import freeze_time +from mock import Mock from app.data_models import QuestionnaireStore from app.data_models.session_data import SessionData From 5f769ecf96fc3b2095cf9828f714c44865c6dbf0 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:38:35 +0100 Subject: [PATCH 031/567] Schemas release v3.5.0 (#880) Co-authored-by: Mebin Abraham Co-authored-by: petechd Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3d67a549d7..c0c4025dbf 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.4.1 +v3.5.0 From 3204329c57104613aec6727990efc798b9782863 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 17 Jun 2022 11:16:53 +0100 Subject: [PATCH 032/567] Add percentage and unit placeholder transforms (#867) --- app/questionnaire/placeholder_transforms.py | 20 ++++ .../test/en/test_placeholder_transform.json | 101 ++++++++++++++++++ .../test_placeholder_transforms.py | 31 ++++++ .../test_questionnaire_placeholders.py | 16 +++ 4 files changed, 168 insertions(+) diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index dc25c2360a..c2f6496cbb 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Optional, Sequence, Sized, Union from urllib.parse import quote +from babel import units from babel.dates import format_datetime from babel.numbers import format_currency, format_decimal from dateutil.relativedelta import relativedelta @@ -132,6 +133,25 @@ def format_number(self, number: Union[int, Decimal, str]) -> str: return "" + @staticmethod + def format_percentage(value: Union[int, Decimal, str]) -> str: + return f"{value}%" + + def format_unit( + self, + unit: str, + value: Union[int, Decimal, str], + unit_length: Optional[str] = None, + ) -> str: + length = unit_length or "short" + formatted_unit: str = units.format_unit( + value=value, + measurement_unit=unit, + length=length, + locale=self.locale, + ) + return formatted_unit + @staticmethod def calculate_date_difference(first_date: str, second_date: str) -> str: diff --git a/schemas/test/en/test_placeholder_transform.json b/schemas/test/en/test_placeholder_transform.json index ceba160023..6986a52fc2 100644 --- a/schemas/test/en/test_placeholder_transform.json +++ b/schemas/test/en/test_placeholder_transform.json @@ -226,6 +226,107 @@ "type": "General" }, "type": "Question" + }, + { + "type": "Question", + "id": "training-percentage-block", + "question": { + "answers": [ + { + "id": "training-percentage", + "mandatory": false, + "decimal_places": 0, + "type": "Percentage", + "label": "Percentage of company budget", + "default": 0 + } + ], + "id": "training-percentage-question", + "title": "What percentage of the company budget you spend on training ?", + "type": "General" + } + }, + { + "id": "training-percentage-interstitial", + "content": { + "title": "Percentage of budget spent on training interstitial", + "contents": [ + { + "description": { + "text": "The percentage of the company budget you spend on training is {answer_percentage}", + "placeholders": [ + { + "placeholder": "answer_percentage", + "transforms": [ + { + "transform": "format_percentage", + "arguments": { + "value": { + "source": "answers", + "identifier": "training-percentage" + } + } + } + ] + } + ] + } + } + ] + }, + "type": "Interstitial" + }, + { + "type": "Question", + "id": "average-distance-block", + "question": { + "answers": [ + { + "id": "average-distance", + "mandatory": false, + "unit": "mile", + "type": "Unit", + "unit_length": "long", + "label": "Average commuting distance", + "default": 0 + } + ], + "id": "average-distance-question", + "title": "What is the average commuting distance of an employee (in miles) ?", + "type": "General" + } + }, + { + "id": "average-distance-interstitial", + "content": { + "title": "Average commuting distance interstitial", + "contents": [ + { + "description": { + "text": "The average commuting distance of an employee is {answer_distance}", + "placeholders": [ + { + "placeholder": "answer_distance", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "answers", + "identifier": "average-distance" + }, + "unit": "mile", + "unit_length": "long" + } + } + ] + } + ] + } + } + ] + }, + "type": "Interstitial" } ] } diff --git a/tests/app/questionnaire/test_placeholder_transforms.py b/tests/app/questionnaire/test_placeholder_transforms.py index 467d36aeb5..77178bb52d 100644 --- a/tests/app/questionnaire/test_placeholder_transforms.py +++ b/tests/app/questionnaire/test_placeholder_transforms.py @@ -38,6 +38,37 @@ def test_format_number(number, expected, transformer): assert transformer().format_number(number) == expected +@pytest.mark.parametrize( + "value, expected", + ( + (123, "123%"), + ("123.4", "123.4%"), + ("123.40", "123.40%"), + ("1000", "1000%"), + (0, "0%"), + (0.00, "0.0%"), + (Decimal("0.123"), "0.123%"), + ), +) +def test_format_percentage(value, expected, transformer): + assert transformer().format_percentage(value) == expected + + +@pytest.mark.parametrize( + "unit, value, unit_length, expected", + ( + ("millimeter", Decimal(0.123), "short", "0.123 mm"), + ("centimeter", "123", "short", "123 cm"), + ("kilometer", "123", "long", "123 kilometre"), + ("mile", "123", "short", "123 mi"), + ("mile", "123", "narrow", "123mi"), + ("mile", "123", None, "123 mi"), + ), +) +def test_format_unit(unit, value, unit_length, expected, transformer): + assert transformer().format_unit(unit, value, unit_length) == expected + + def test_format_list(transformer): transform = transformer() names = ["Alice Aardvark", "Bob Berty Brown", "Dave Dixon Davies"] diff --git a/tests/integration/questionnaire/test_questionnaire_placeholders.py b/tests/integration/questionnaire/test_questionnaire_placeholders.py index 21069b0012..b0f4041b43 100644 --- a/tests/integration/questionnaire/test_questionnaire_placeholders.py +++ b/tests/integration/questionnaire/test_questionnaire_placeholders.py @@ -51,6 +51,14 @@ def test_placeholders_rendered_in_pages(self): self.post({"add-item-question": "Yes"}) + self.post({"training-percentage": 1}) + + self.post() + + self.post({"average-distance": 1}) + + self.post() + self.assertInUrl(SUBMIT_URL_PATH) self.assertInBody( "For Integration Testing (Integration Tests), please enter the total retail turnover" @@ -76,6 +84,14 @@ def test_conditional_trad_as_without_trad_as_in_token(self): self.post({"add-item-question": "No"}) + self.post({"training-percentage": 1}) + + self.post() + + self.post({"average-distance": 1}) + + self.post() + self.assertInUrl(SUBMIT_URL_PATH) self.assertInBody( "For Integration Testing, please enter the total retail turnover" From 7d0429bf739b30c611bf0c8dbe4c0732b14282a9 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 17 Jun 2022 12:23:23 +0100 Subject: [PATCH 033/567] Add submitted date to pdf filename (#877) --- app/views/handlers/view_submitted_response_pdf.py | 7 ++++++- tests/app/views/handlers/conftest.py | 7 ++++++- .../app/views/handlers/test_view_submitted_response_pdf.py | 4 +++- .../integration/routes/test_view_submitted_response_pdf.py | 5 ++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/views/handlers/view_submitted_response_pdf.py b/app/views/handlers/view_submitted_response_pdf.py index be913e4f80..ef1ba02893 100644 --- a/app/views/handlers/view_submitted_response_pdf.py +++ b/app/views/handlers/view_submitted_response_pdf.py @@ -1,4 +1,5 @@ import io +import re import pdfkit from flask import current_app @@ -54,7 +55,11 @@ def __init__( @property def filename(self) -> str: """The name to use for the PDF file""" - return f"{self._metadata['schema_name']}.pdf" + formatted_title = re.sub( + "[^0-9a-zA-Z]+", "-", self._schema.json["title"].lower() + ) + formatted_date = self._questionnaire_store.submitted_at.date().isoformat() # type: ignore + return f"{formatted_title}-{formatted_date}.pdf" def get_pdf(self) -> io.BytesIO: """ diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index f38dff6f97..be204c8ddb 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -76,7 +76,12 @@ def language(): @pytest.fixture def schema(): - return QuestionnaireSchema({"post_submission": {"view_response": True}}) + return QuestionnaireSchema( + { + "post_submission": {"view_response": True}, + "title": "Test schema - View Submitted Response", + } + ) @pytest.fixture diff --git a/tests/app/views/handlers/test_view_submitted_response_pdf.py b/tests/app/views/handlers/test_view_submitted_response_pdf.py index 5b2f9a93ac..e031c30f62 100644 --- a/tests/app/views/handlers/test_view_submitted_response_pdf.py +++ b/tests/app/views/handlers/test_view_submitted_response_pdf.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta, timezone import pytest +from freezegun import freeze_time from app.data_models import QuestionnaireStore from app.views.handlers.view_submitted_response import ViewSubmittedResponseExpired @@ -25,6 +26,7 @@ def test_pdf_not_downloadable(storage, schema, language): @pytest.mark.usefixtures("app") +@freeze_time("2022-06-01T15:34:54+00:00") def test_filename_uses_schema_name(storage, schema, language): submitted_at = datetime.now(timezone.utc) set_storage_data(storage, submitted_at=submitted_at) @@ -33,4 +35,4 @@ def test_filename_uses_schema_name(storage, schema, language): questionnaire_store.set_metadata({"schema_name": "test_view_submitted_response"}) pdf = ViewSubmittedResponsePDF(schema, questionnaire_store, language) - assert pdf.filename == "test_view_submitted_response.pdf" + assert pdf.filename == "test-schema-view-submitted-response-2022-06-01.pdf" diff --git a/tests/integration/routes/test_view_submitted_response_pdf.py b/tests/integration/routes/test_view_submitted_response_pdf.py index 55582fac82..4de03dc3b6 100644 --- a/tests/integration/routes/test_view_submitted_response_pdf.py +++ b/tests/integration/routes/test_view_submitted_response_pdf.py @@ -1,5 +1,7 @@ from time import sleep +from freezegun import freeze_time + from app import settings from tests.integration.routes.test_view_submitted_response import ( ViewSubmittedResponseBase, @@ -19,6 +21,7 @@ def test_download_when_submitted_response_not_enabled(self): # Then a 404 page is returned self.assertStatusNotFound() + @freeze_time("2022-06-01T15:34:54+00:00") def test_download_when_submitted_response_enabled_but_not_expired(self): # Given I launch and complete a questionnaire that has view-submitted-response enabled and has not expired self._launch_and_complete_questionnaire() @@ -38,7 +41,7 @@ def test_download_when_submitted_response_enabled_but_not_expired(self): # Check filename is set as expected self.assertIn( - "filename=test_view_submitted_response.pdf", + "filename=test-view-submitted-response-2022-06-01.pdf", self.last_response_headers["Content-Disposition"], ) From 54bde7813650550f486791c20266cd8618accdd7 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 20 Jun 2022 11:04:24 +0100 Subject: [PATCH 034/567] Expose tx_id in data layer (#873) --- app/helpers/template_helpers.py | 10 +++- app/survey_config/business_config.py | 21 ++++---- app/survey_config/census_config.py | 23 +++++---- app/survey_config/survey_config.py | 12 ++++- tests/app/helpers/test_template_helpers.py | 50 +++++++++++++++---- .../integration/test_application_variables.py | 21 +++++--- 6 files changed, 99 insertions(+), 38 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 9db625e44d..a842e16080 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -69,7 +69,7 @@ def context(self) -> dict[str, Any]: "survey_title": self._survey_title, "cdn_url": self._cdn_url, "address_lookup_api_url": self._address_lookup_api, - "data_layer": self._survey_config.data_layer, + "data_layer": self.data_layer_context, "include_csrf_token": self._include_csrf_token, "google_tag_manager_id": self._google_tag_manager_id, "google_tag_manager_auth": self._google_tag_manager_auth, @@ -95,6 +95,14 @@ def service_links_context( return None + @property + def data_layer_context( + self, + ) -> list[dict]: + metadata = get_metadata(current_user) + tx_id = metadata.get("tx_id") if metadata else None + return self._survey_config.get_data_layer(tx_id=tx_id) + @property def page_header_context(self) -> dict[str, Union[bool, str, LazyString]]: context: dict[str, Union[bool, str, LazyString]] = { diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 798ea562d8..49dcf46d3f 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -21,15 +21,6 @@ def __post_init__(self): self.base_url = self._stripped_base_url super().__post_init__() - if self.schema: - self.data_layer: list[dict] = [ - { - key: self.schema.json[key] - for key in ["form_type", "survey_id", "title"] - if key in self.schema.json - } - ] - if not self.account_service_log_out_url: self.account_service_log_out_url: str = f"{self.base_url}/sign-in/logout" @@ -102,6 +93,18 @@ def get_service_links( return links + def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: + data_layer = [{"tx_id": tx_id}] if tx_id else [] + if self.schema: + data_layer.append( + { + key: self.schema.json[key] + for key in ["form_type", "survey_id", "title"] + if key in self.schema.json + } + ) + return data_layer + @property def _stripped_base_url(self) -> str: warn( diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index 358196a588..2fbdd5f33c 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping +from typing import Iterable, Mapping, MutableMapping, Optional, Union from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString @@ -57,11 +57,19 @@ class CensusSurveyConfig( ], compare=False, ) - data_layer: Iterable[Mapping] = field( - default_factory=lambda: [{"nisra": False}], compare=False - ) + survey_title: LazyString = lazy_gettext("Census 2021") sign_out_button_text: str = lazy_gettext("Save and complete later") + _is_nisra: bool = False + + def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: + data_layer: list[Union[dict[str, bool], dict[str, str]]] = [ + {"nisra": self._is_nisra} + ] + if tx_id: + data_layer.append({"tx_id": tx_id}) + + return data_layer @dataclass @@ -109,9 +117,6 @@ class WelshCensusSurveyConfig( compare=False, hash=False, ) - data_layer: Iterable[Mapping] = field( - default_factory=lambda: [{"nisra": False}], compare=False - ) @dataclass @@ -164,6 +169,4 @@ class CensusNISRASurveyConfig( ) powered_by_logo: str = "nisra-logo-black-en" powered_by_logo_alt: str = "NISRA - Northern Ireland Statistics and Research Agency" - data_layer: Iterable[Mapping] = field( - default_factory=lambda: [{"nisra": True}], compare=False - ) + _is_nisra: bool = True diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 7d8bf2d090..62dda6ce12 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -40,7 +40,9 @@ class SurveyConfig: footer_legal_links: Optional[Iterable[Mapping]] = None survey_title: Optional[LazyString] = None design_system_theme: Optional[str] = None - data_layer: Iterable[Union[Mapping]] = field(default_factory=list, compare=False) + data_layer: list[dict[str, Union[str, bool]]] = field( + default_factory=list, compare=False + ) sign_out_button_text: str = lazy_gettext("Save and exit survey") contact_us_url: str = field(init=False) cookie_settings_url: str = field(init=False) @@ -61,3 +63,11 @@ def get_service_links( # pylint: disable=unused-argument, no-self-use ru_ref: Optional[str], ) -> Optional[list[dict]]: return None + + def get_data_layer( # pylint: disable=no-self-use + self, tx_id: Optional[str] = None + ) -> list[dict]: + if tx_id: + return [{"tx_id": tx_id}] + + return [] diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index ad2888a8e0..711be0c1db 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -588,23 +588,51 @@ def test_correct_survey_title_in_context( @pytest.mark.parametrize( - "theme, language, expected", + "theme, language, schema, expected", [ - ("default", "en", []), - ("business", "en", []), - ("health", "en", []), - ("social", "en", []), - ("northernireland", "en", []), - ("census", "en", [{"nisra": False}]), - ("census", "cy", [{"nisra": False}]), - ("census-nisra", "en", [{"nisra": True}]), + ( + "default", + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), + ( + "default", + "en", + QuestionnaireSchema({"survey_id": "001", "form_type": "test"}), + [{"form_type": "test", "survey_id": "001"}], + ), + ( + "business", + "en", + QuestionnaireSchema( + {"survey_id": "001", "form_type": "test", "title": "test_title"} + ), + [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + ), + ("health", "en", QuestionnaireSchema({"survey_id": "001"}), []), + ("social", "en", QuestionnaireSchema({"survey_id": "001"}), []), + ( + "northernireland", + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), + ("census", "en", QuestionnaireSchema({"survey_id": "001"}), [{"nisra": False}]), + ("census", "cy", QuestionnaireSchema({"survey_id": "001"}), [{"nisra": False}]), + ( + "census-nisra", + QuestionnaireSchema({"survey_id": "001"}), + "en", + [{"nisra": True}], + ), ], ) def test_correct_data_layer_in_context( - app: Flask, theme: str, language: str, expected: str + app: Flask, theme: str, language: str, schema: QuestionnaireSchema, expected: str ): with app.app_context(): - survey_config = get_survey_config(theme=theme, language=language) + survey_config = get_survey_config(theme=theme, language=language, schema=schema) result = ContextHelper( language="en", diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 007ebdd88c..42a2dd8b1d 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -1,4 +1,5 @@ from app import settings +from app.utilities.json import json_loads from tests.integration.integration_test_case import IntegrationTestCase @@ -16,7 +17,9 @@ def tearDown(self): settings.EQ_GOOGLE_TAG_MANAGER_AUTH = None def test_google_analytics_code_and_credentials_are_present(self): - self.launchSurvey("test_feedback") + self.launchSurvey("test_feedback", roles=["dumper"]) + self.get("/dump/debug") + actual = json_loads(self.getResponseData()) self._client.set_cookie( "localhost", key="ons_cookie_policy", value="'usage':true" ) @@ -24,14 +27,16 @@ def test_google_analytics_code_and_credentials_are_present(self): self.assertStatusOK() self.assertInHead("gtm.start") self.assertInHead( - 'dataLayer = [{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}]' + f'dataLayer = [{{"tx_id": "{actual["METADATA"]["tx_id"]}"}}, {{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}]' ) self.assertInBody("https://www.googletagmanager.com") self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_AUTH) self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_ID) def test_google_analytics_data_layer_has_no_null_fields(self): - self.launchSurvey("test_textfield") + self.launchSurvey("test_textfield", roles=["dumper"]) + self.get("/dump/debug") + actual = json_loads(self.getResponseData()) self._client.set_cookie( "localhost", key="ons_cookie_policy", value="'usage':true" ) @@ -40,18 +45,22 @@ def test_google_analytics_data_layer_has_no_null_fields(self): self.assertInHead("gtm.start") # form_type is empty so should not be present self.assertInHead( - 'dataLayer = [{"survey_id": "001", "title": "Other input fields"}]' + f'dataLayer = [{{"tx_id": "{actual["METADATA"]["tx_id"]}"}}, {{"survey_id": "001", "title": "Other input fields"}}]' ) def test_google_analytics_data_layer_is_set_to_nisra_false(self): - self.launchSurvey("test_thank_you_census_individual") + self.launchSurvey("test_thank_you_census_individual", roles=["dumper"]) + self.get("/dump/debug") + actual = json_loads(self.getResponseData()) self._client.set_cookie( "localhost", key="ons_cookie_policy", value="'usage':true" ) self.get("/questionnaire/individual-confirmation/") self.assertStatusOK() self.assertInHead("gtm.start") - self.assertInHead('dataLayer = [{"nisra": false}]') + self.assertInHead( + f'dataLayer = [{{"nisra": false}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' + ) def test_livereload_script_rendered(self): self.launchSurvey("test_textfield") From cc347627d2c2d3f941d0429dc5ba21b2e399d576 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 22 Jun 2022 11:12:38 +0100 Subject: [PATCH 035/567] Update to ChromeDriver 102 (#884) --- package.json | 2 +- yarn.lock | 67 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index e7e179c2d3..b2ad1c6de7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^101.0.0", + "chromedriver": "^102.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 7bc8c73abe..382aac813b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1592,17 +1592,23 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -axios@^0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== dependencies: - follow-redirects "^1.14.4" + follow-redirects "^1.14.9" + form-data "^4.0.0" babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" @@ -1881,13 +1887,13 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^101.0.0: - version "101.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-101.0.0.tgz#ad19003008dd5df1770a1ad96059a9c5fe78e365" - integrity sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w== +chromedriver@^102.0.0: + version "102.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-102.0.0.tgz#02844c39ee33d1e88ac8c48fbe28cb8423e970a4" + integrity sha512-xer/0g1Oarkjc2e+4nyoLgZT4kJHYhcj3PcxD1nEoGJQYEllTjprN1uDpSb4BkgMGo0ydfIS1VDkszrr/J9OOg== dependencies: "@testim/chrome-version" "^1.1.2" - axios "^0.24.0" + axios "^0.27.2" del "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.0" @@ -1991,6 +1997,13 @@ colorette@^2.0.16: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -2189,6 +2202,11 @@ del@^6.0.0: rimraf "^3.0.2" slash "^3.0.0" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -2780,10 +2798,19 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.14.4: - version "1.14.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" - integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" fs-constants@^1.0.0: version "1.0.0" @@ -3781,6 +3808,18 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" From 467ee2806f439b6bfffdd384bb7cd08b123e61a9 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 22 Jun 2022 12:11:51 +0100 Subject: [PATCH 036/567] Add calculated summary section dependencies (#868) --- app/questionnaire/questionnaire_schema.py | 18 +- ...ction_dependencies_calculated_summary.json | 282 ++++++++++++++++++ tests/app/questionnaire/conftest.py | 7 + .../test_questionnaire_schema.py | 13 + ...on_dependencies_calculated_summary.spec.js | 123 ++++++++ 5 files changed, 437 insertions(+), 6 deletions(-) create mode 100644 schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json create mode 100644 tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index ed1fa93016..b79f34f8aa 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -852,14 +852,20 @@ def _get_rules_section_dependencies( if not isinstance(rule, Mapping): continue - answer_id = None + answer_id_list: list = [] + identifier: Optional[str] = rule.get("identifier") + source: Optional[str] = rule.get("source") if "id" in rule: - answer_id = rule["id"] - elif rule.get("source") == "answers": - answer_id = rule.get("identifier") - - if answer_id: + answer_id_list.append(rule["id"]) + elif source == "answers" and identifier: + answer_id_list.append(identifier) + elif source == "calculated_summary" and identifier: + calculated_summary_block = self.get_block(identifier) + calculated_summary_answer_ids = calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + answer_id_list.extend(iter(calculated_summary_answer_ids)) + + for answer_id in answer_id_list: block = self.get_block_for_answer_id(answer_id) # type: ignore section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json b/schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json new file mode 100644 index 0000000000..8d7ba0aecb --- /dev/null +++ b/schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json @@ -0,0 +1,282 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Routing and Skipping Section Dependencies based on Calculated Summary", + "theme": "default", + "description": "A questionnaire to test routing and skipping rules, when the rule references a different section to its current section", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "title": "Calculated Summary Section", + "summary": { "show_on_completion": true }, + "id": "calculated-summary-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "first-question-block", + "question": { + "id": "first-question", + "title": "How much do you spend on the following items?", + "description": [ + "If the total is equal to £100 a new section will appear on the hub and if it is greater than or equal to £100 a dependent question will appear in the dependent question section" + ], + "type": "General", + "answers": [ + { + "id": "milk-answer", + "label": "Milk", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "eggs-answer", + "label": "Eggs", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "bread-answer", + "label": "Bread", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "cheese-answer", + "label": "Cheese", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": ["milk-answer", "eggs-answer", "bread-answer", "cheese-answer"], + "title": "Grand total of previous values" + } + } + ], + "id": "calculated-summary-group" + } + ] + }, + { + "title": "Dependent question Section", + "summary": { "show_on_completion": true }, + "id": "dependent-question-section", + "groups": [ + { + "blocks": [ + { + "skip_conditions": { + "when": { + ">=": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + }, + 10 + ] + } + }, + "type": "Question", + "id": "fruit", + "question": { + "answers": [ + { + "id": "fruit-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "fruit-question", + "title": "Do you like eating fruit", + "type": "General" + } + }, + { + "routing_rules": [ + { + "block": "second-question-block", + "when": { + ">=": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + }, + 100 + ] + } + }, + { + "section": "End" + } + ], + "type": "Question", + "id": "vegetables", + "question": { + "answers": [ + { + "id": "vegetables-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "vegetables-question", + "title": "Do you like eating vegetables", + "type": "General" + } + }, + { + "type": "Question", + "id": "second-question-block", + "question": { + "id": "second-question", + "title": "How much do you spend on the following items?", + "type": "General", + "answers": [ + { + "id": "apples-answer", + "label": "Apples", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "bananas-answer", + "label": "Bananas", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "oranges-answer", + "label": "Oranges", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "lemons-answer", + "label": "Lemons", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + } + ], + "id": "dependent-question-group" + } + ] + }, + { + "enabled": { + "when": { + "==": [ + 100, + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + } + ] + } + }, + "title": "Dependent Enabled Section", + "summary": { "show_on_completion": true }, + "id": "dependent-enabled-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "desserts", + "question": { + "answers": [ + { + "id": "desserts-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "desserts-question", + "title": "Do you like eating desserts", + "type": "General" + } + } + ], + "id": "dependent-enabled-section-group" + } + ] + } + ] +} diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 01a250ce30..095bc2da51 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1183,3 +1183,10 @@ def dynamic_answer_options_function_driven_schema(): @pytest.fixture def skipping_section_dependencies_schema(): return load_schema_from_name("test_new_routing_and_skipping_section_dependencies") + + +@pytest.fixture +def section_dependencies_calculated_summary_schema(): + return load_schema_from_name( + "test_new_routing_and_skipping_section_dependencies_calculated_summary" + ) diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 446869d696..a14b6ebfe1 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -705,3 +705,16 @@ def test_when_rules_section_dependencies_by_answer( "primary-person", }, } == schema.when_rules_section_dependencies_by_answer + + +def test_when_rules_section_dependencies_calculated_summary( + section_dependencies_calculated_summary_schema, +): + schema = section_dependencies_calculated_summary_schema + + assert { + "milk-answer": {"dependent-enabled-section", "dependent-question-section"}, + "eggs-answer": {"dependent-enabled-section", "dependent-question-section"}, + "bread-answer": {"dependent-enabled-section", "dependent-question-section"}, + "cheese-answer": {"dependent-enabled-section", "dependent-question-section"}, + } == schema.when_rules_section_dependencies_by_answer diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js new file mode 100644 index 0000000000..6c8ff0fdf4 --- /dev/null +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -0,0 +1,123 @@ +import CalculatedSummarySectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/calculated-summary-section-summary.page"; +import CurrencyTotalPlaybackPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/currency-total-playback.page"; +import DependentQuestionSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/dependent-question-section-summary.page"; +import FirstQuestionBlockPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/first-question-block.page"; +import FruitPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; +import SecondQuestionBlockPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; +import VegetablesPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; + +import HubPage from "../base_pages/hub.page"; + +describe("Routing and skipping section dependencies based on calculated summaries", () => { + describe("Given the section dependencies based on a calculated summary questionnaire", () => { + beforeEach("Load the survey", () => { + browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies_calculated_summary.json"); + }); + + it("When the calculated summary total has not been set, Then the dependent section should not be enabled", () => { + expect($(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + expect($(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + expect($(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + }); + + it("When the calculated summary total is equal to £100, Then the dependent section should be enabled", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(25); + $(FirstQuestionBlockPage.eggs()).setValue(25); + $(FirstQuestionBlockPage.bread()).setValue(25); + $(FirstQuestionBlockPage.cheese()).setValue(25); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + expect($(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + expect($(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + expect($(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + }); + + it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is not met (total less than £10), then the dependent question should be displayed", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(1); + $(FirstQuestionBlockPage.eggs()).setValue(1); + $(FirstQuestionBlockPage.bread()).setValue(1); + $(FirstQuestionBlockPage.cheese()).setValue(1); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + $(HubPage.summaryRowLink("dependent-question-section")).click(); + expect(browser.getUrl()).to.contain(FruitPage.pageName); + }); + + it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is met (total greater than £10), then the dependent question should not be displayed", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(5); + $(FirstQuestionBlockPage.eggs()).setValue(5); + $(FirstQuestionBlockPage.bread()).setValue(5); + $(FirstQuestionBlockPage.cheese()).setValue(5); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + $(HubPage.summaryRowLink("dependent-question-section")).click(); + expect(browser.getUrl()).to.contain(VegetablesPage.pageName); + }); + + it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is greater than £100, then we should be routed to the second question block", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(30); + $(FirstQuestionBlockPage.eggs()).setValue(30); + $(FirstQuestionBlockPage.bread()).setValue(30); + $(FirstQuestionBlockPage.cheese()).setValue(30); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + $(HubPage.summaryRowLink("dependent-question-section")).click(); + $(VegetablesPage.yes()).click(); + $(VegetablesPage.submit()).click(); + expect(browser.getUrl()).to.contain(SecondQuestionBlockPage.pageName); + }); + + it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is less than £100, then we should be routed to the section summary", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(20); + $(FirstQuestionBlockPage.eggs()).setValue(20); + $(FirstQuestionBlockPage.bread()).setValue(20); + $(FirstQuestionBlockPage.cheese()).setValue(20); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + $(HubPage.summaryRowLink("dependent-question-section")).click(); + $(VegetablesPage.yes()).click(); + $(VegetablesPage.submit()).click(); + expect(browser.getUrl()).to.contain(DependentQuestionSectionSummaryPage.pageName); + }); + + it("When a question in another section has a dependency on a calculated summary total, and both sections are complete, and I go back and edit the calculated summary total, then the dependent section status should be in progress", () => { + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(FirstQuestionBlockPage.milk()).setValue(20); + $(FirstQuestionBlockPage.eggs()).setValue(20); + $(FirstQuestionBlockPage.bread()).setValue(20); + $(FirstQuestionBlockPage.cheese()).setValue(20); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + + $(HubPage.summaryRowLink("dependent-question-section")).click(); + $(VegetablesPage.yes()).click(); + $(VegetablesPage.submit()).click(); + $(DependentQuestionSectionSummaryPage.submit()).click(); + expect($(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Completed"); + + $(HubPage.summaryRowLink("calculated-summary-section")).click(); + $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); + $(FirstQuestionBlockPage.milk()).setValue(100); + $(FirstQuestionBlockPage.submit()).click(); + $(CurrencyTotalPlaybackPage.submit()).click(); + $(CalculatedSummarySectionSummaryPage.submit()).click(); + expect($(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); + }); + }); +}); From d003ab08a20294a4eba4f6bc9d8ccd42d50f8796 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 24 Jun 2022 11:57:16 +0100 Subject: [PATCH 037/567] Add type hinting for jinja filters (#876) --- app/jinja_filters.py | 239 +++++++++++------- mypy.ini | 5 + .../test/en/test_date_validation_range.json | 2 +- 3 files changed, 158 insertions(+), 88 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index c857f8bcf0..60889febdb 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -1,69 +1,86 @@ # coding: utf-8 import re +from datetime import datetime +from decimal import Decimal +from typing import Any, Callable, Mapping, Optional, Union import flask import flask_babel from babel import numbers, units from flask import current_app -from jinja2 import pass_eval_context +from jinja2 import nodes, pass_eval_context from markupsafe import Markup, escape +from wtforms import SelectFieldBase from app.questionnaire.rules.utils import parse_datetime from app.settings import MAX_NUMBER blueprint = flask.Blueprint("filters", __name__) +FormType = Mapping[str, Mapping[str, Any]] +AnswerType = Mapping[str, Any] -def mark_safe(context, value): +def mark_safe(context: nodes.EvalContext, value: str) -> Union[Markup, str]: return Markup(value) if context.autoescape else value -def strip_tags(value): +def strip_tags(value: str) -> Markup: return escape(Markup(value).striptags()) @blueprint.app_template_filter() -def format_number(value): +def format_number(value: Union[int, Decimal]) -> str: + formatted_number: str if value or value == 0: - return numbers.format_decimal(value, locale=flask_babel.get_locale()) + formatted_number = numbers.format_decimal( + value, locale=flask_babel.get_locale() + ) + return formatted_number return "" -def get_formatted_address(address_fields): +def get_formatted_address(address_fields: dict[str, str]) -> str: address_fields.pop("uprn", None) return "
".join(address_field for address_field in address_fields.values()) -def get_formatted_currency(value, currency="GBP") -> str: +def get_formatted_currency(value: Union[float, Decimal], currency: str = "GBP") -> str: if value or value == 0: - return numbers.format_currency( + formatted_currency: str = numbers.format_currency( number=value, currency=currency, locale=flask_babel.get_locale() ) + return formatted_currency return "" @blueprint.app_template_filter() -def get_currency_symbol(currency="GBP"): - return numbers.get_currency_symbol(currency, locale=flask_babel.get_locale()) +def get_currency_symbol(currency: str = "GBP") -> str: + currency_symbol: str = numbers.get_currency_symbol( + currency, locale=flask_babel.get_locale() + ) + return currency_symbol @blueprint.app_template_filter() -def format_percentage(value): +def format_percentage(value: Union[int, Decimal]) -> str: return f"{value}%" -def format_unit(unit, value, length="short"): - return units.format_unit( +def format_unit( + unit: Union[int, Decimal], value: Union[int, Decimal], length: str = "short" +) -> str: + formatted_unit: str = units.format_unit( value=value, measurement_unit=unit, length=length, locale=flask_babel.get_locale(), ) + return formatted_unit -def format_unit_input_label(unit, unit_length="short"): +def format_unit_input_label(unit: str, unit_length: str = "short") -> str: """ This function is used to only get the unit of measurement text. If the unit_length is long then only the plural form of the word is returned (e.g., Hours, Years, etc). @@ -71,22 +88,26 @@ def format_unit_input_label(unit, unit_length="short"): :param (str) unit unit of measurement :param (str) unit_length length of unit text, can be one of short/long/narrow """ + unit_label: str if unit_length == "long": - return units.format_unit( + unit_label = units.format_unit( value=2, measurement_unit=unit, length=unit_length, locale=flask_babel.get_locale(), ).replace("2 ", "") - return units.format_unit( - value="", - measurement_unit=unit, - length=unit_length, - locale=flask_babel.get_locale(), - ).strip() + else: + unit_label = units.format_unit( + value="", + measurement_unit=unit, + length=unit_length, + locale=flask_babel.get_locale(), + ).strip() + return unit_label -def format_duration(value): + +def format_duration(value: Mapping[str, int]) -> str: parts = [] if "years" in value and (value["years"] > 0 or len(value) == 1): @@ -104,14 +125,14 @@ def format_duration(value): return " ".join(parts) -def get_format_multilined_string(value): +def get_format_multilined_string(value: str) -> str: escaped_value = escape(value) new_line_regex = r"(?:\r\n|\r|\n)+" value_with_line_break_tag = re.sub(new_line_regex, "
", escaped_value) return f"{value_with_line_break_tag}" -def get_format_date(value): +def get_format_date(value: Markup) -> str: """Format a datetime string. :param (jinja2.nodes.EvalContext) context: Evaluation context. @@ -134,7 +155,9 @@ def get_format_date(value): @pass_eval_context # type: ignore @blueprint.app_template_filter() -def format_datetime(context, date_time): +def format_datetime( + context: nodes.EvalContext, date_time: datetime +) -> Union[str, Markup]: # flask babel on formatting will automatically convert based on the time zone specified in setup.py formatted_date = flask_babel.format_date(date_time, format="d MMMM yyyy") formatted_time = flask_babel.format_time(date_time, format="HH:mm") @@ -148,44 +171,50 @@ def format_datetime(context, date_time): return mark_safe(context, result) -def get_format_date_range(start_date, end_date): - return flask_babel.gettext( +def get_format_date_range(start_date: Markup, end_date: Markup) -> Markup: + date_range: Markup + date_range = flask_babel.gettext( "%(from_date)s to %(to_date)s", from_date=get_format_date(start_date), to_date=get_format_date(end_date), ) + return date_range @blueprint.app_context_processor -def format_unit_processor(): +def format_unit_processor() -> dict[ + str, Callable[[Union[int, Decimal], Union[int, Decimal], str], str] +]: return dict(format_unit=format_unit) @blueprint.app_context_processor -def format_unit_input_label_processor(): +def format_unit_input_label_processor() -> dict[str, Callable]: return dict(format_unit_input_label=format_unit_input_label) @blueprint.app_context_processor -def get_currency_symbol_processor(): +def get_currency_symbol_processor() -> dict[str, Callable]: return dict(get_currency_symbol=get_currency_symbol) @blueprint.app_template_filter() # type: ignore -def setAttribute(dictionary, key, value): +def setAttribute(dictionary: dict[str, str], key: str, value: str) -> dict[str, str]: dictionary[key] = value return dictionary @blueprint.app_template_filter() # type: ignore -def setAttributes(dictionary, attributes): +def setAttributes( + dictionary: dict[str, str], attributes: dict[str, str] +) -> dict[str, str]: for key in attributes: dictionary[key] = attributes[key] return dictionary @blueprint.app_template_filter() -def should_wrap_with_fieldset(question): +def should_wrap_with_fieldset(question: dict[str, list]) -> bool: # Logic for when to wrap with a fieldset comes from # https://ons-design-system.netlify.app/patterns/question/ if question["type"] == "DateRange": @@ -212,12 +241,12 @@ def should_wrap_with_fieldset(question): @blueprint.app_context_processor -def should_wrap_with_fieldset_processor(): +def should_wrap_with_fieldset_processor() -> dict[str, Callable]: return {"should_wrap_with_fieldset": should_wrap_with_fieldset} @blueprint.app_template_filter() -def get_width_for_number(answer): +def get_width_for_number(answer: AnswerType) -> Optional[int]: allowable_widths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30, 40, 50] min_value = answer.get("minimum", {}).get("value", 0) @@ -236,19 +265,25 @@ def get_width_for_number(answer): @blueprint.app_context_processor -def get_width_for_number_processor(): +def get_width_for_number_processor() -> dict[str, Callable]: return {"get_width_for_number": get_width_for_number} class LabelConfig: - def __init__(self, _for, text, description=None): + def __init__(self, _for: str, text: str, description: Optional[str] = None) -> None: self._for = _for self.text = text self.description = description class SelectConfig: - def __init__(self, option, index, answer, form=None): + def __init__( + self, + option: SelectFieldBase._Option, + index: int, + answer: AnswerType, + form: Optional[FormType] = None, + ) -> None: self.id = option.id self.name = option.name self.value = option.data @@ -275,7 +310,12 @@ def __init__(self, option, index, answer, form=None): class RelationshipRadioConfig(SelectConfig): - def __init__(self, option, index, answer): + def __init__( + self, + option: SelectFieldBase._Option, + index: int, + answer: AnswerType, + ) -> None: super().__init__(option, index, answer) if self._answer_option: @@ -295,7 +335,11 @@ def __init__(self, option, index, answer): class OtherConfig: - def __init__(self, detail_answer_field, detail_answer_schema): + def __init__( + self, + detail_answer_field: SelectFieldBase._Option, + detail_answer_schema: Mapping[str, str], + ) -> None: self.id = detail_answer_field.id self.name = detail_answer_field.name @@ -320,7 +364,7 @@ def __init__(self, detail_answer_field, detail_answer_schema): @blueprint.app_template_filter() # type: ignore -def map_select_config(form, answer): +def map_select_config(form: FormType, answer: AnswerType) -> list[SelectConfig]: options = form["fields"][answer["id"]] return [ @@ -330,12 +374,14 @@ def map_select_config(form, answer): @blueprint.app_context_processor -def map_select_config_processor(): +def map_select_config_processor() -> dict[str, Callable]: return dict(map_select_config=map_select_config) @blueprint.app_template_filter() # type: ignore -def map_relationships_config(form, answer): +def map_relationships_config( + form: Mapping[str, str], answer: Mapping[str, Union[int, slice]] +) -> list[RelationshipRadioConfig]: options = form["fields"][answer["id"]] return [ @@ -344,29 +390,37 @@ def map_relationships_config(form, answer): @blueprint.app_context_processor -def map_relationships_config_processor(): +def map_relationships_config_processor() -> dict[str, Callable]: return dict(map_relationships_config=map_relationships_config) class DropdownConfig: - def __init__(self, option, select): + def __init__( + self, option: SelectFieldBase._Option, select: SelectFieldBase._Option + ) -> None: self.value, self.text = option.value, option.label self.selected = select.data == self.value self.disabled = self.value == "" and select.flags.required @blueprint.app_template_filter() -def map_dropdown_config(select): +def map_dropdown_config(select: SelectFieldBase._Option) -> list[DropdownConfig]: return [DropdownConfig(choice, select) for choice in select.choices] @blueprint.app_context_processor -def map_dropdown_config_processor(): +def map_dropdown_config_processor() -> dict[str, Callable]: return dict(map_dropdown_config=map_dropdown_config) class SummaryAction: - def __init__(self, answer, answer_title, edit_link_text, edit_link_aria_label): + def __init__( + self, + answer: SelectFieldBase._Option, + answer_title: str, + edit_link_text: str, + edit_link_aria_label: str, + ) -> None: self.text = edit_link_text self.ariaLabel = edit_link_aria_label + " " + answer_title self.url = answer["link"] @@ -380,7 +434,7 @@ def __init__(self, answer, answer_title, edit_link_text, edit_link_aria_label): class SummaryRowItemValue: - def __init__(self, text, other=None): + def __init__(self, text: str, other: Optional[str] = None) -> None: self.text = text if other or other == 0: @@ -390,15 +444,15 @@ def __init__(self, text, other=None): class SummaryRowItem: def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branches self, - question, - answer, - multiple_answers, - answers_are_editable, - no_answer_provided, - edit_link_text, - edit_link_aria_label, - summary_type, - ): + question: SelectFieldBase._Option, + answer: SelectFieldBase._Option, + multiple_answers: bool, + answers_are_editable: bool, + no_answer_provided: str, + edit_link_text: str, + edit_link_aria_label: str, + summary_type: str, + ) -> None: if "type" in answer: answer_type = answer["type"] @@ -480,13 +534,13 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche class SummaryRow: def __init__( self, - question, - summary_type, - answers_are_editable, - no_answer_provided, - edit_link_text, - edit_link_aria_label, - ): + question: SelectFieldBase._Option, + summary_type: SelectFieldBase._Option, + answers_are_editable: bool, + no_answer_provided: str, + edit_link_text: str, + edit_link_aria_label: str, + ) -> None: self.rowTitle = strip_tags(question["title"]) self.id = question["id"] self.rowItems = [] @@ -513,14 +567,15 @@ def __init__( @blueprint.app_template_filter() # type: ignore def map_summary_item_config( - group, - summary_type, - answers_are_editable, - no_answer_provided, - edit_link_text, - edit_link_aria_label, - calculated_question, -): + group: SelectFieldBase._Option, + summary_type: str, + answers_are_editable: bool, + no_answer_provided: str, + edit_link_text: str, + edit_link_aria_label: str, + calculated_question: SelectFieldBase._Option, +) -> list[SummaryRow]: + rows = [ SummaryRow( block["question"], @@ -534,49 +589,59 @@ def map_summary_item_config( ] if summary_type == "CalculatedSummary": - rows.append( - SummaryRow(calculated_question, summary_type, False, None, None, None) - ) + rows.append(SummaryRow(calculated_question, summary_type, False, "", "", "")) return rows @blueprint.app_context_processor -def map_summary_item_config_processor(): +def map_summary_item_config_processor() -> dict[str, Callable]: return dict(map_summary_item_config=map_summary_item_config) @blueprint.app_template_filter() # type: ignore def map_list_collector_config( - list_items, - icon, - edit_link_text=None, - edit_link_aria_label=None, - remove_link_text=None, - remove_link_aria_label=None, -): + list_items: list[dict[str, Union[str, int]]], + icon: str, + edit_link_text: Optional[str] = None, + edit_link_aria_label: Optional[str] = None, + remove_link_text: Optional[str] = None, + remove_link_aria_label: Optional[str] = None, +) -> list[dict[str, list[dict[str, Any]]]]: rows = [] for index, list_item in enumerate(list_items, start=1): item_name = list_item.get("item_title") actions = [] + edit_link_aria_label_text = None + remove_link_aria_label_text = None if edit_link_text: + if edit_link_aria_label: + edit_link_aria_label_text = edit_link_aria_label.format( + item_name=item_name + ) + actions.append( { "text": edit_link_text, - "ariaLabel": edit_link_aria_label.format(item_name=item_name), + "ariaLabel": edit_link_aria_label_text, "url": list_item.get("edit_link"), "attributes": {"data-qa": f"list-item-change-{index}-link"}, } ) if not list_item.get("primary_person") and remove_link_text: + if remove_link_aria_label: + remove_link_aria_label_text = remove_link_aria_label.format( + item_name=item_name + ) + actions.append( { "text": remove_link_text, - "ariaLabel": remove_link_aria_label.format(item_name=item_name), + "ariaLabel": remove_link_aria_label_text, "url": list_item.get("remove_link"), "attributes": {"data-qa": f"list-item-remove-{index}-link"}, } @@ -603,5 +668,5 @@ def map_list_collector_config( @blueprint.app_context_processor -def map_list_collector_config_processor(): +def map_list_collector_config_processor() -> dict[str, Callable]: return dict(map_list_collector_config=map_list_collector_config) diff --git a/mypy.ini b/mypy.ini index 399d54a16f..645d74fefc 100644 --- a/mypy.ini +++ b/mypy.ini @@ -150,3 +150,8 @@ no_implicit_optional = True disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True + +[mypy-app.jinja_filters] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True diff --git a/schemas/test/en/test_date_validation_range.json b/schemas/test/en/test_date_validation_range.json index aaabcc1c55..97658510fe 100644 --- a/schemas/test/en/test_date_validation_range.json +++ b/schemas/test/en/test_date_validation_range.json @@ -47,7 +47,7 @@ "guidance": { "contents": [ { - "list": ["Enter a date range between 23 days and 1 month and 20 days apart"] + "description": "Enter a date range between 23 days and 1 month and 20 days apart" } ] }, From 3b1d72842c028e2570d4acebb62793e68c054b30 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 24 Jun 2022 12:20:24 +0100 Subject: [PATCH 038/567] Add conditional help link (#883) --- app/helpers/template_helpers.py | 1 + app/survey_config/business_config.py | 30 ++++++++++------ app/survey_config/survey_config.py | 1 + tests/app/helpers/test_template_helpers.py | 12 +++++-- tests/integration/routes/test_errors.py | 2 +- tests/integration/test_header_links.py | 40 ++++++++++++++++++++-- 6 files changed, 69 insertions(+), 17 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index a842e16080..3b1d8751e2 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -83,6 +83,7 @@ def service_links_context( if service_links := self._survey_config.get_service_links( sign_out_url=self._sign_out_url, is_authenticated=current_user.is_authenticated, + cookie_has_theme=bool(cookie_session.get("theme")), ru_ref=metadata.get("ru_ref") if metadata else None, # type: ignore ): return { diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 49dcf46d3f..1c02fc910f 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -63,18 +63,26 @@ def _get_account_service_help_url( return f"{self.base_url}/help" def get_service_links( - self, sign_out_url: str, *, is_authenticated: bool, ru_ref: Optional[str] + self, + sign_out_url: str, + *, + is_authenticated: bool, + cookie_has_theme: bool, + ru_ref: Optional[str], ) -> Optional[list[dict]]: - links = [ - HeaderLink( - lazy_gettext("Help"), - self._get_account_service_help_url( - is_authenticated=is_authenticated, ru_ref=ru_ref - ), - id="header-link-help", - ).__dict__ - ] - + links = ( + [ + HeaderLink( + lazy_gettext("Help"), + self._get_account_service_help_url( + is_authenticated=is_authenticated, ru_ref=ru_ref + ), + id="header-link-help", + ).__dict__ + ] + if cookie_has_theme + else [] + ) if is_authenticated: links.extend( [ diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 62dda6ce12..0bea609b07 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -60,6 +60,7 @@ def get_service_links( # pylint: disable=unused-argument, no-self-use sign_out_url: str, *, is_authenticated: bool, + cookie_has_theme: bool, ru_ref: Optional[str], ) -> Optional[list[dict]]: return None diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 711be0c1db..a132a28abc 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -2,6 +2,7 @@ import pytest from flask import Flask, current_app +from flask import session as cookie_session from app.helpers.template_helpers import ContextHelper, get_survey_config from app.questionnaire import QuestionnaireSchema @@ -185,16 +186,18 @@ def test_get_page_header_context_census_nisra(app: Flask): @pytest.mark.parametrize( - "survey_config, is_authenticated, expected", + "survey_config, is_authenticated, theme, expected", [ ( SurveyConfig(), True, None, + None, ), ( BusinessSurveyConfig(), False, + "business", { "toggleServicesButton": { "text": "Menu", @@ -212,6 +215,7 @@ def test_get_page_header_context_census_nisra(app: Flask): ( BusinessSurveyConfig(schema=QuestionnaireSchema({"survey_id": "001"})), True, + "business", { "toggleServicesButton": { "text": "Menu", @@ -236,21 +240,23 @@ def test_get_page_header_context_census_nisra(app: Flask): ], }, ), - (SocialSurveyConfig(), False, None), + (SocialSurveyConfig(), False, None, None), ( SocialSurveyConfig(schema=QuestionnaireSchema({"survey_id": "001"})), True, + "social", None, ), ], ) def test_service_links_context( - app: Flask, mocker, survey_config, is_authenticated, expected + app: Flask, mocker, survey_config, is_authenticated, theme, expected ): with app.app_context(): mocked_current_user = mocker.Mock() mocked_current_user.is_authenticated = is_authenticated mocker.patch("flask_login.utils._get_user", return_value=mocked_current_user) + cookie_session["theme"] = theme if is_authenticated: mocker.patch( diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index e0f4491520..835d165e78 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -27,7 +27,7 @@ def test_errors_404(self): self.assertStatusNotFound() # Test that my account link does not show - self.assertInBody("Help") + self.assertNotInBody("Help") self.assertNotInBody("My account") self.assertNotInBody("Sign out") diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index 74360b7a3d..8e76b37cad 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -76,6 +76,42 @@ def test_links_in_header_when_valid_session(self): self.assert_sign_out_link_exist() self.assert_help_link_exist() + def test_links_in_header_when_no_session_but_cookie_exists(self): + # Given + self.launchSurvey("test_thank_you") + self.assertInUrl("questionnaire/did-you-know/") + self.saveAndSignOut() + + # When + self.assertStatusCode(302) + self.get("questionnaire/") + + # Then + self.assertInUrl("questionnaire/") + cookie = self.getCookie() + self.assertEqual(cookie.get("theme"), "default") + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_exist_not_authenticated_after_sign_out() + + def test_links_in_header_when_no_session_but_cookie_exists_theme_social(self): + # Given + self.launchSurvey("test_theme_social") + self.assertInUrl("/questionnaire/radio/") + self.saveAndSignOut() + + # When + self.assertStatusCode(302) + self.get("questionnaire/") + + # Then + self.assertInUrl("questionnaire/") + cookie = self.getCookie() + self.assertEqual(cookie.get("theme"), "social") + self.assert_my_account_link_does_not_exist() + self.assert_sign_out_link_does_not_exist() + self.assert_help_link_does_not_exist() + def test_links_not_in_header_when_no_session(self): # Given self.get("/questionnaire") @@ -86,7 +122,7 @@ def test_links_not_in_header_when_no_session(self): # Then self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() - self.assert_help_link_exist_not_authenticated() + self.assert_help_link_does_not_exist() def test_links_not_in_header_when_valid_session_theme_social(self): # Given @@ -127,7 +163,7 @@ def test_links_not_in_header_when_no_session(self): # Then self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() - self.assert_help_link_exist_not_authenticated() + self.assert_help_link_does_not_exist() def test_links_not_in_header_when_valid_session_theme_social(self): # Given From 4e73f0d40b220171cfca4fd1db68a3fffe0573e4 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 24 Jun 2022 16:41:10 +0100 Subject: [PATCH 039/567] Schemas v3.6.0 (#881) Co-authored-by: petechd Co-authored-by: Rhys Berrow --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c0c4025dbf..130165bc0a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.5.0 +v3.6.0 From c6fd94b8c10cc6384714bf33575ac6861c512040 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:19:29 +0100 Subject: [PATCH 040/567] Schemas v3.7.0 (#889) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 130165bc0a..d1e9cf3bba 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.6.0 +v3.7.0 From 2a7d2184934527d69a1b606bcd516aa19cd1c133 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 30 Jun 2022 09:18:11 +0100 Subject: [PATCH 041/567] Schemas v3.7.1 (#892) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d1e9cf3bba..1885263250 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.7.0 +v3.7.1 From da87845aec7c7cdd96e4d7cc5fdd2fc634374e6b Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:24:25 +0100 Subject: [PATCH 042/567] Fix detail answer value bug in convert_payload_0_0_1 (#894) --- app/submitter/convert_payload_0_0_1.py | 6 +++--- tests/app/submitter/test_convert_payload_0_0_1.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index c3c2cf79d5..e969ec5c8f 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -126,9 +126,9 @@ def _get_checkbox_answer_data( if option: if "detail_answer" in option: detail_answer = answer_store.get_answer(option["detail_answer"]["id"]) - # if the user has selected an option with a detail answer we need to find the detail answer value it refers to. - # the detail answer value can be empty, in this case we just use the main value (e.g. other) - user_answer = detail_answer.value or user_answer # type: ignore + if detail_answer: + # Ignore mypy type because the answer type can be any non strings, but user_answer is expected to be a string. + user_answer = detail_answer.value # type: ignore qcodes_and_values.append((option.get("q_code"), user_answer)) diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index a63b2ef553..ea4bfabb04 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -275,7 +275,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value(fake_questionnaire_st assert answer_object["data"]["4"] == "Bacon" -def test_converter_checkboxes_with_q_codes_and_empty_other_value( +def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store( fake_questionnaire_store, ): full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] @@ -283,7 +283,6 @@ def test_converter_checkboxes_with_q_codes_and_empty_other_value( fake_questionnaire_store.answer_store = AnswerStore( [ Answer("crisps-answer", ["Ready salted", "Other"]).to_dict(), - Answer("other-answer-mandatory", "").to_dict(), ] ) @@ -308,7 +307,7 @@ def test_converter_checkboxes_with_q_codes_and_empty_other_value( "description": "Choose any other flavour", "value": "Other", "detail_answer": { - "mandatory": True, + "mandatory": False, "id": "other-answer-mandatory", "label": "Please specify other", "type": "TextField", From a860b5f73cc66ce1188d3aa7b08e4d0c139c1137 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:43:23 +0100 Subject: [PATCH 043/567] Bug fix: Support radio detail answer for data version 0.0.1 (#895) --- app/forms/questionnaire_form.py | 22 ++++++----- app/questionnaire/__init__.py | 4 +- app/questionnaire/questionnaire_schema.py | 24 +++++++----- app/submitter/convert_payload_0_0_1.py | 7 +++- app/views/handlers/confirm_email.py | 4 +- .../submitter/test_convert_payload_0_0_1.py | 39 +++++++++++++++++-- 6 files changed, 71 insertions(+), 29 deletions(-) diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 596cb669e3..0457a3ad47 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -16,7 +16,7 @@ from app.forms import error_messages from app.forms.field_handlers import DateHandler, FieldHandler, get_field_handler from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck -from app.questionnaire import Location, QuestionnaireSchema, QuestionSchema +from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver @@ -37,7 +37,7 @@ class QuestionnaireForm(FlaskForm): def __init__( self, schema: QuestionnaireSchema, - question_schema: QuestionSchema, + question_schema: QuestionSchemaType, answer_store: AnswerStore, list_store: ListStore, metadata: Mapping[str, Any], @@ -92,7 +92,7 @@ def validate( and valid_mutually_exclusive_form ) - def validate_date_range_question(self, question: QuestionSchema) -> bool: + def validate_date_range_question(self, question: QuestionSchemaType) -> bool: date_from = question["answers"][0] date_to = question["answers"][1] if self._has_min_and_max_single_dates(date_from, date_to): @@ -130,7 +130,7 @@ def validate_date_range_question(self, question: QuestionSchema) -> bool: return True - def validate_calculated_question(self, question: QuestionSchema) -> bool: + def validate_calculated_question(self, question: QuestionSchemaType) -> bool: for calculation in question["calculations"]: result = self._get_target_total_and_currency(calculation, question) if result: @@ -147,7 +147,9 @@ def validate_calculated_question(self, question: QuestionSchema) -> bool: return False - def validate_mutually_exclusive_question(self, question: QuestionSchema) -> bool: + def validate_mutually_exclusive_question( + self, question: QuestionSchemaType + ) -> bool: is_mandatory: bool = question["mandatory"] messages = ( question["validation"].get("messages") if "validation" in question else None @@ -173,7 +175,7 @@ def validate_mutually_exclusive_question(self, question: QuestionSchema) -> bool def _get_target_total_and_currency( self, calculation: Calculation, - question: QuestionSchema, + question: QuestionSchemaType, ) -> Optional[tuple[Union[Calculation, AnswerValueTypes], Optional[str]]]: calculation_value: Union[Calculation, AnswerValueTypes] @@ -249,7 +251,7 @@ def _validate_date_range_question( def _validate_calculated_question( self, calculation: Calculation, - question: QuestionSchema, + question: QuestionSchemaType, target_total: Any, currency: Optional[str], ) -> bool: @@ -427,7 +429,7 @@ def _option_value_in_data( def get_answer_fields( - question: QuestionSchema, + question: QuestionSchemaType, data: Union[None, MultiDict[str, Any], Mapping[str, Any]], schema: QuestionnaireSchema, answer_store: AnswerStore, @@ -526,7 +528,7 @@ def _get_error_id(id_: str) -> str: def _clear_detail_answer_field( - form_data: MultiDict, question_schema: QuestionSchema + form_data: MultiDict, question_schema: QuestionSchemaType ) -> MultiDict[str, Any]: """ Clears the detail answer field if the parent option is not selected @@ -544,7 +546,7 @@ def _clear_detail_answer_field( def generate_form( schema: QuestionnaireSchema, - question_schema: QuestionSchema, + question_schema: QuestionSchemaType, answer_store: AnswerStore, list_store: ListStore, metadata: Mapping[str, Any], diff --git a/app/questionnaire/__init__.py b/app/questionnaire/__init__.py index 44390dfebc..2fb3f32d83 100644 --- a/app/questionnaire/__init__.py +++ b/app/questionnaire/__init__.py @@ -1,4 +1,4 @@ from .location import Location -from .questionnaire_schema import QuestionnaireSchema, QuestionSchema +from .questionnaire_schema import QuestionnaireSchema, QuestionSchemaType -__all__ = ["QuestionnaireSchema", "Location", "QuestionSchema"] +__all__ = ["QuestionnaireSchema", "Location", "QuestionSchemaType"] diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index b79f34f8aa..572a08142a 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -22,7 +22,7 @@ RELATIONSHIP_CHILDREN = ["UnrelatedQuestion"] -QuestionSchema = Mapping[str, Any] +QuestionSchemaType = Mapping[str, Any] class InvalidSchemaConfigurationException(Exception): @@ -643,16 +643,22 @@ def get_list_collector_for_list( return None @classmethod - def get_answer_ids_for_question(cls, question: Mapping) -> list[str]: - answer_ids: list[str] = [] - - for answer in question.get("answers", []): - answer_ids.append(answer["id"]) - for option in answer.get("options", []): + def get_answers_for_question_by_id( + cls, question: QuestionSchemaType + ) -> dict[str, dict[str, Any]]: + answers: dict[str, dict[str, Any]] = {} + + for answer in question.get("answers", {}): + answers[answer["id"]] = answer + for option in answer.get("options", {}): if "detail_answer" in option: - answer_ids.append(option["detail_answer"]["id"]) + answers[option["detail_answer"]["id"]] = option["detail_answer"] + + return answers - return answer_ids + @classmethod + def get_answer_ids_for_question(cls, question: QuestionSchemaType) -> list[str]: + return list(cls.get_answers_for_question_by_id(question).keys()) def get_first_answer_id_for_block(self, block_id: str) -> str: answer_ids = self.get_answer_ids_for_block(block_id) diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index e969ec5c8f..d098a6be62 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -62,9 +62,12 @@ def convert_answers_to_payload_0_0_1( list_store, current_location=current_location, ) - for answer in question["answers"]: - if answer["id"] == answer_in_block.answer_id: + for answer_id, answer in schema.get_answers_for_question_by_id( + question + ).items(): + if answer_id == answer_in_block.answer_id: answer_schema = answer + break value = answer_in_block.value diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index d1054d54e7..51bf67d5b7 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -17,7 +17,7 @@ ) from app.forms.questionnaire_form import generate_form from app.helpers import url_safe_serializer -from app.questionnaire import QuestionnaireSchema, QuestionSchema +from app.questionnaire import QuestionnaireSchema, QuestionSchemaType from app.settings import ( EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME, EQ_SUBMISSION_CONFIRMATION_QUEUE, @@ -81,7 +81,7 @@ def form(self): ) @cached_property - def question_schema(self) -> QuestionSchema: + def question_schema(self) -> QuestionSchemaType: return { "type": "General", "id": "confirm-email", diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index ea4bfabb04..9c91367ad2 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -1,5 +1,7 @@ from datetime import datetime, timezone +import pytest + from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema @@ -215,7 +217,16 @@ def test_converter_checkboxes_with_q_codes(fake_questionnaire_store): assert answer_object["data"]["2"] == "Sweet chilli" -def test_converter_checkboxes_with_q_codes_and_other_value(fake_questionnaire_store): +@pytest.mark.parametrize( + "detail_answer_q_code_field, expected_data_length", + [ + ({"q_code": "401"}, 3), + ({}, 2), + ], +) +def test_converter_checkboxes_with_q_codes_and_other_value( + detail_answer_q_code_field, expected_data_length, fake_questionnaire_store +): full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] fake_questionnaire_store.answer_store = AnswerStore( @@ -250,6 +261,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value(fake_questionnaire_st "id": "other-answer-mandatory", "label": "Please specify other", "type": "TextField", + **detail_answer_q_code_field, }, }, ], @@ -270,10 +282,14 @@ def test_converter_checkboxes_with_q_codes_and_other_value(fake_questionnaire_st ) # Then - assert len(answer_object["data"]) == 2 + assert len(answer_object["data"]) == expected_data_length assert answer_object["data"]["1"] == "Ready salted" assert answer_object["data"]["4"] == "Bacon" + # If detail answer has a q_code then that should be used in the data outputted in the payload + if detail_answer_q_code_field: + assert answer_object["data"][detail_answer_q_code_field["q_code"]] == "Bacon" + def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store( fake_questionnaire_store, @@ -440,7 +456,10 @@ def test_radio_answer(fake_questionnaire_store): RoutingPath(["radio-block"], section_id="section-1", list_item_id=None) ] fake_questionnaire_store.answer_store = AnswerStore( - [Answer("radio-answer", "Coffee").to_dict()] + [ + Answer("radio-answer", "Coffee").to_dict(), + Answer("other-answer-mandatory", "Water").to_dict(), + ], ) question = { @@ -454,6 +473,17 @@ def test_radio_answer(fake_questionnaire_store): "options": [ {"label": "Coffee", "value": "Coffee"}, {"label": "Tea", "value": "Tea"}, + { + "label": "Other", + "value": "Other", + "detail_answer": { + "mandatory": True, + "id": "other-answer-mandatory", + "label": "Please specify other", + "type": "TextField", + "q_code": "101", + }, + }, ], } ], @@ -471,8 +501,9 @@ def test_radio_answer(fake_questionnaire_store): ) # Then - assert len(answer_object["data"]) == 1 + assert len(answer_object["data"]) == 2 assert answer_object["data"]["1"] == "Coffee" + assert answer_object["data"]["101"] == "Water" def test_number_answer(fake_questionnaire_store): From bd6e2c149689758102a7ad9a7c44e9b27d32a7f0 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:10:04 +0100 Subject: [PATCH 044/567] Update to DS v52 (#891) --- .design-system-version | 2 +- package.json | 2 +- tests/functional/spec/features/units.spec.js | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.design-system-version b/.design-system-version index 465f70b6c2..26adcbbd44 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -50.0.1 +52.0.0 diff --git a/package.json b/package.json index b2ad1c6de7..b21460fc1a 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^102.0.0", + "chromedriver": "^103.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index f58f65a84d..5b645ba16a 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -35,9 +35,9 @@ describe("Units", () => { it("Given we open a questionnaire with unit labels, when the label is highlighted by the tooltip, then the long unit label should be displayed.", () => { browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - expect($(SetLengthUnitsBlockPage.centimetres()).getAttribute("title")).to.equal("centimeters"); - expect($(SetLengthUnitsBlockPage.metres()).getAttribute("title")).to.equal("meters"); - expect($(SetLengthUnitsBlockPage.kilometres()).getAttribute("title")).to.equal("kilometers"); - expect($(SetLengthUnitsBlockPage.miles()).getAttribute("title")).to.equal("miles"); + expect($(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimeters"); + expect($(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("meters"); + expect($(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometers"); + expect($(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).to.equal("miles"); }); }); diff --git a/yarn.lock b/yarn.lock index 382aac813b..40cc313d84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1887,10 +1887,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^102.0.0: - version "102.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-102.0.0.tgz#02844c39ee33d1e88ac8c48fbe28cb8423e970a4" - integrity sha512-xer/0g1Oarkjc2e+4nyoLgZT4kJHYhcj3PcxD1nEoGJQYEllTjprN1uDpSb4BkgMGo0ydfIS1VDkszrr/J9OOg== +chromedriver@^103.0.0: + version "103.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-103.0.0.tgz#2ef086d62076e3ff6df6cfb84895d11d2c18d9cf" + integrity sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w== dependencies: "@testim/chrome-version" "^1.1.2" axios "^0.27.2" From a3a1ecaa4da5765dc881a0fadfd245a7b1d7a979 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 6 Jul 2022 08:11:26 +0100 Subject: [PATCH 045/567] Update JS packages to resolve vulnerabilities (#896) --- package.json | 2 +- yarn.lock | 41 +++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index b21460fc1a..1114c090f6 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "eslint-plugin-promise": "^6.0.0", "eslint-plugin-standard": "^4.0.1", "json-web-key": "^0.4.0", - "jsrsasign": "^10.5.10", + "jsrsasign": "^10.5.25", "lint-staged": "^12.3.5", "livereload": "^0.9.3", "node-forge": "^1.2.1", diff --git a/yarn.lock b/yarn.lock index 40cc313d84..739f1e7758 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1142,7 +1142,12 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/node@*", "@types/node@^17.0.4": +"@types/node@*": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" + integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== + +"@types/node@^17.0.4": version "17.0.29" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== @@ -1959,7 +1964,7 @@ clone-deep@^4.0.1: clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== dependencies: mimic-response "^1.0.0" @@ -2014,10 +2019,10 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -compress-brotli@^1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.6.tgz#64bd6f21f4f3e9841dbac392f4c29218caf5e9d9" - integrity sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ== +compress-brotli@^1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" + integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ== dependencies: "@types/json-buffer" "~3.0.0" json-buffer "~3.0.1" @@ -2979,9 +2984,9 @@ globule@^1.0.0: minimatch "~3.0.2" got@^11.0.2, got@^11.8.1: - version "11.8.3" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" - integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== + version "11.8.5" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" + integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== dependencies: "@sindresorhus/is" "^4.0.0" "@szmarczak/http-timer" "^4.0.5" @@ -3513,17 +3518,17 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsrsasign@^10.5.10: - version "10.5.20" - resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.20.tgz#515a854a47c309cb350f32860dd37bfad1b81800" - integrity sha512-YHL6y8o6cnRoxwUY0eGpfvwj0pm9o0NToD4KXVp2UJC19oXSR2RgnSdSMplIRRKFovLAJGrAHqdb5MMznnhQDQ== +jsrsasign@^10.5.25: + version "10.5.25" + resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.25.tgz#8eb3f943718d73f2dd3d85f587f241a5316b835a" + integrity sha512-N7zxHaCwYvFlXsybq4p4RxRwn4AbEq3cEiyjbCrWmwA7g8aS4LTKDJ9AJmsXxwtYesYx0imJ+ITtkyyxLCgeIg== keyv@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.2.2.tgz#4b6f602c0228ef4d8214c03c520bef469ed6b768" - integrity sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ== + version "4.3.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2" + integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw== dependencies: - compress-brotli "^1.3.6" + compress-brotli "^1.3.8" json-buffer "3.0.1" kind-of@^6.0.2: @@ -5269,7 +5274,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@8.5.0: version "8.5.0" From ec2017ff533947017c60c0271cc08e0eb5e59bcf Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 6 Jul 2022 12:56:58 +0100 Subject: [PATCH 046/567] Schemas v3.7.2 (#897) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 1885263250..2272683225 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.7.1 +v3.7.2 From 300c68dbda5a74e12d22c2fadf1422f95abc39bf Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 12 Jul 2022 09:52:32 +0100 Subject: [PATCH 047/567] Add missing q_code and remove extra ones from 0.0.3 data version schemas (#893) --- schemas/test/en/test_benchmark_business.json | 1 + .../en/test_big_list_naughty_strings.json | 507 ------------------ schemas/test/en/test_checkbox.json | 20 +- .../test_checkbox_detail_answer_multiple.json | 17 +- .../test_checkbox_detail_answer_numeric.json | 7 +- .../en/test_conditional_combined_routing.json | 3 - .../test/en/test_confirmation_question.json | 6 +- ...ion_question_within_repeating_section.json | 1 - schemas/test/en/test_currency.json | 4 - schemas/test/en/test_date_range.json | 2 - schemas/test/en/test_dates.json | 6 - schemas/test/en/test_durations.json | 6 - schemas/test/en/test_instructions.json | 1 - schemas/test/en/test_interstitial_page.json | 2 - schemas/test/en/test_interviewer_note.json | 1 - schemas/test/en/test_markup.json | 1 - schemas/test/en/test_metadata_routing.json | 3 - .../test/en/test_new_routing_date_equals.json | 1 - .../en/test_new_skip_condition_not_set.json | 2 - .../test/en/test_new_skip_condition_set.json | 2 - schemas/test/en/test_percentage.json | 1 - ...aceholder_based_on_first_item_in_list.json | 1 - .../en/test_placeholder_playback_list.json | 19 +- .../test/en/test_question_description.json | 1 - schemas/test/en/test_question_guidance.json | 7 - .../en/test_radio_checkbox_descriptions.json | 2 - schemas/test/en/test_routing_date_equals.json | 1 - .../en/test_routing_on_multiple_select.json | 3 - ...st_submit_with_custom_submission_text.json | 1 - ...t_with_summary_custom_submission_text.json | 1 - schemas/test/en/test_textarea.json | 1 - schemas/test/en/test_textfield.json | 1 - .../test/en/test_theme_northernireland.json | 1 - schemas/test/en/test_theme_social.json | 1 - schemas/test/en/test_timeout.json | 1 - 35 files changed, 21 insertions(+), 614 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 1afa7378a0..f33ec283ad 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -170,6 +170,7 @@ "answers": [ { "id": "answer434", + "q_code": "123", "mandatory": true, "type": "Radio", "options": [ diff --git a/schemas/test/en/test_big_list_naughty_strings.json b/schemas/test/en/test_big_list_naughty_strings.json index 1443ecb7a1..fa748320e9 100644 --- a/schemas/test/en/test_big_list_naughty_strings.json +++ b/schemas/test/en/test_big_list_naughty_strings.json @@ -34,3549 +34,3042 @@ "answers": [ { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer0" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer1" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer2" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer3" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer4" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer5" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer6" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer7" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer8" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer9" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer10" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer11" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer12" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer13" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer14" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer15" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer16" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer17" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer18" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer19" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer20" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer21" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer22" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer23" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer24" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer25" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer26" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer27" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer28" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer29" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer30" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer31" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer32" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer33" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer34" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer35" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer36" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer37" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer38" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer39" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer40" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer41" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer42" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer43" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer44" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer45" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer46" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer47" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer48" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer49" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer50" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer51" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer52" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer53" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer54" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer55" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer56" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer57" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer58" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer59" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer60" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer61" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer62" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer63" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer64" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer65" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer66" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer67" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer68" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer69" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer70" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer71" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer72" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer73" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer74" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer75" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer76" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer77" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer78" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer79" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer80" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer81" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer82" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer83" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer84" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer85" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer86" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer87" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer88" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer89" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer90" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer91" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer92" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer93" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer94" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer95" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer96" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer97" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer98" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer99" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer100" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer101" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer102" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer103" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer104" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer105" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer106" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer107" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer108" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer109" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer110" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer111" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer112" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer113" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer114" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer115" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer116" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer117" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer118" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer119" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer120" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer121" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer122" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer123" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer124" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer125" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer126" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer127" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer128" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer129" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer130" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer131" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer132" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer133" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer134" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer135" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer136" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer137" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer138" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer139" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer140" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer141" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer142" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer143" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer144" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer145" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer146" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer147" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer148" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer149" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer150" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer151" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer152" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer153" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer154" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer155" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer156" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer157" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer158" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer159" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer160" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer161" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer162" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer163" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer164" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer165" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer166" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer167" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer168" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer169" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer170" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer171" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer172" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer173" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer174" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer175" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer176" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer177" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer178" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer179" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer180" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer181" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer182" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer183" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer184" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer185" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer186" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer187" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer188" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer189" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer190" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer191" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer192" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer193" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer194" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer195" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer196" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer197" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer198" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer199" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer200" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer201" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer202" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer203" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer204" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer205" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer206" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer207" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer208" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer209" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer210" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer211" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer212" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer213" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer214" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer215" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer216" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer217" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer218" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer219" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer220" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer221" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer222" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer223" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer224" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer225" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer226" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer227" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer228" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer229" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer230" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer231" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer232" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer233" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer234" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer235" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer236" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer237" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer238" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer239" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer240" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer241" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer242" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer243" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer244" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer245" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer246" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer247" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer248" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer249" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer250" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer251" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer252" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer253" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer254" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer255" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer256" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer257" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer258" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer259" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer260" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer261" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer262" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer263" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer264" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer265" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer266" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer267" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer268" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer269" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer270" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer271" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer272" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer273" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer274" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer275" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer276" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer277" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer278" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer279" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer280" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer281" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer282" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer283" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer284" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer285" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer286" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer287" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer288" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer289" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer290" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer291" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer292" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer293" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer294" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer295" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer296" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer297" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer298" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer299" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer300" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer301" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer302" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer303" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer304" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer305" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer306" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer307" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer308" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer309" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer310" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer311" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer312" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer313" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer314" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer315" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer316" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer317" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer318" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer319" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer320" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer321" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer322" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer323" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer324" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer325" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer326" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer327" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer328" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer329" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer330" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer331" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer332" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer333" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer334" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer335" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer336" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer337" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer338" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer339" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer340" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer341" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer342" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer343" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer344" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer345" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer346" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer347" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer348" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer349" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer350" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer351" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer352" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer353" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer354" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer355" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer356" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer357" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer358" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer359" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer360" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer361" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer362" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer363" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer364" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer365" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer366" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer367" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer368" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer369" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer370" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer371" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer372" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer373" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer374" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer375" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer376" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer377" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer378" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer379" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer380" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer381" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer382" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer383" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer384" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer385" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer386" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer387" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer388" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer389" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer390" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer391" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer392" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer393" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer394" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer395" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer396" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer397" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer398" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer399" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer400" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer401" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer402" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer403" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer404" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer405" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer406" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer407" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer408" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer409" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer410" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer411" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer412" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer413" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer414" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer415" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer416" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer417" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer418" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer419" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer420" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer421" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer422" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer423" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer424" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer425" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer426" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer427" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer428" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer429" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer430" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer431" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer432" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer433" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer434" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer435" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer436" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer437" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer438" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer439" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer440" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer441" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer442" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer443" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer444" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer445" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer446" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer447" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer448" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer449" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer450" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer451" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer452" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer453" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer454" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer455" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer456" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer457" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer458" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer459" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer460" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer461" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer462" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer463" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer464" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer465" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer466" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer467" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer468" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer469" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer470" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer471" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer472" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer473" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer474" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer475" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer476" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer477" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer478" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer479" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer480" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer481" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer482" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer483" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer484" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer485" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer486" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer487" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer488" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer489" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer490" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer491" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer492" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer493" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer494" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer495" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer496" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer497" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer498" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer499" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer500" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer501" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer502" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer503" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer504" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer505" }, { "type": "TextArea", - "q_code": "0", "mandatory": false, "label": "Enter your comments", "id": "answer506" diff --git a/schemas/test/en/test_checkbox.json b/schemas/test/en/test_checkbox.json index 925ce858a8..6232699d07 100644 --- a/schemas/test/en/test_checkbox.json +++ b/schemas/test/en/test_checkbox.json @@ -51,37 +51,30 @@ "options": [ { "label": "None", - "value": "None", - "q_code": "0" + "value": "None" }, { "label": "Ham & Cheese", - "value": "Ham & Cheese", - "q_code": "1" + "value": "Ham & Cheese" }, { "label": "Ham", - "value": "Ham", - "q_code": "2" + "value": "Ham" }, { "label": "Pineapple", - "value": "Pineapple", - "q_code": "3" + "value": "Pineapple" }, { "label": "Tuna", - "value": "Tuna", - "q_code": "4" + "value": "Tuna" }, { "label": "Pepperoni", - "value": "Pepperoni", - "q_code": "5" + "value": "Pepperoni" }, { "label": "Other", - "q_code": "6", "description": "Choose any other topping", "value": "Other", "detail_answer": { @@ -146,7 +139,6 @@ } } ], - "q_code": "20", "type": "Checkbox" } ], diff --git a/schemas/test/en/test_checkbox_detail_answer_multiple.json b/schemas/test/en/test_checkbox_detail_answer_multiple.json index f3221aae6d..99d965bc8b 100644 --- a/schemas/test/en/test_checkbox_detail_answer_multiple.json +++ b/schemas/test/en/test_checkbox_detail_answer_multiple.json @@ -51,13 +51,11 @@ "options": [ { "label": "None", - "value": "None", - "q_code": "0" + "value": "None" }, { "label": "Cheese", "value": "Cheese", - "q_code": "1", "detail_answer": { "mandatory": false, "id": "cheese-type-answer", @@ -67,27 +65,22 @@ }, { "label": "Ham", - "value": "Ham", - "q_code": "2" + "value": "Ham" }, { "label": "Pineapple", - "value": "Pineapple", - "q_code": "3" + "value": "Pineapple" }, { "label": "Tuna", - "value": "Tuna", - "q_code": "4" + "value": "Tuna" }, { "label": "Pepperoni", - "value": "Pepperoni", - "q_code": "5" + "value": "Pepperoni" }, { "label": "Your choice", - "q_code": "6", "description": "Choose any other topping", "value": "Your choice", "detail_answer": { diff --git a/schemas/test/en/test_checkbox_detail_answer_numeric.json b/schemas/test/en/test_checkbox_detail_answer_numeric.json index 812386aa8f..1796bcf13f 100644 --- a/schemas/test/en/test_checkbox_detail_answer_numeric.json +++ b/schemas/test/en/test_checkbox_detail_answer_numeric.json @@ -55,17 +55,14 @@ }, { "label": "1", - "value": "1", - "q_code": "1" + "value": "1" }, { "label": "2", - "value": "2", - "q_code": "2" + "value": "2" }, { "label": "Other", - "q_code": "3", "description": "Choose any number of toppings", "value": "Other", "detail_answer": { diff --git a/schemas/test/en/test_conditional_combined_routing.json b/schemas/test/en/test_conditional_combined_routing.json index d5c02c3187..85eaf1b7c8 100644 --- a/schemas/test/en/test_conditional_combined_routing.json +++ b/schemas/test/en/test_conditional_combined_routing.json @@ -65,7 +65,6 @@ "value": "No, I don’t drink any hot drinks" } ], - "q_code": "1", "id": "conditional-routing-answer", "label": "Which conditional question should we jump to?", "mandatory": true, @@ -117,7 +116,6 @@ "id": "response-any-number-of-cups", "label": "Number of cups", "mandatory": true, - "q_code": "2", "type": "Number" } ] @@ -135,7 +133,6 @@ "id": "response-not-any-number-of-cups", "label": "Number of cups", "mandatory": true, - "q_code": "2", "type": "Number" } ] diff --git a/schemas/test/en/test_confirmation_question.json b/schemas/test/en/test_confirmation_question.json index 42e8d4fa71..fd104db534 100644 --- a/schemas/test/en/test_confirmation_question.json +++ b/schemas/test/en/test_confirmation_question.json @@ -52,7 +52,6 @@ "answers": [ { "id": "number-of-employees-total", - "q_code": "50", "label": "Total number of employees", "mandatory": false, "type": "Number", @@ -119,8 +118,7 @@ "value": "No I need to change this" } ], - "mandatory": true, - "q_code": "d50" + "mandatory": true } ], "id": "confirm-zero-employees-question", @@ -179,7 +177,6 @@ "id": "number-of-employees-male-more-30-hours", "label": "Number of male employees working more than 30 hours per week", "mandatory": false, - "q_code": "51", "type": "Number", "maximum": { "value": { @@ -192,7 +189,6 @@ "id": "number-of-employees-female-more-30-hours", "label": "Number of female employees working more than 30 hours per week", "mandatory": false, - "q_code": "52", "type": "Number", "maximum": { "value": { diff --git a/schemas/test/en/test_confirmation_question_within_repeating_section.json b/schemas/test/en/test_confirmation_question_within_repeating_section.json index e6bd5e1f9f..a9026f32db 100644 --- a/schemas/test/en/test_confirmation_question_within_repeating_section.json +++ b/schemas/test/en/test_confirmation_question_within_repeating_section.json @@ -465,7 +465,6 @@ "answers": [ { "id": "carer-answer", - "q_code": "50", "label": "Carer", "mandatory": false, "type": "Radio", diff --git a/schemas/test/en/test_currency.json b/schemas/test/en/test_currency.json index c803c8da99..de84a6f6c8 100644 --- a/schemas/test/en/test_currency.json +++ b/schemas/test/en/test_currency.json @@ -55,7 +55,6 @@ "id": "answer", "label": "How much did you spend?", "mandatory": false, - "q_code": "0", "type": "Currency", "currency": "GBP", "decimal_places": 2, @@ -67,7 +66,6 @@ "id": "answer-usd", "label": "How much did you spend?", "mandatory": false, - "q_code": "0", "type": "Currency", "currency": "USD", "decimal_places": 2, @@ -79,7 +77,6 @@ "id": "answer-eur", "label": "How much did you spend?", "mandatory": false, - "q_code": "0", "type": "Currency", "currency": "EUR", "decimal_places": 2, @@ -91,7 +88,6 @@ "id": "answer-jpy", "label": "How much did you spend?", "mandatory": false, - "q_code": "0", "type": "Currency", "currency": "JPY", "maximum": { diff --git a/schemas/test/en/test_date_range.json b/schemas/test/en/test_date_range.json index 63f377a731..ccfe1d9214 100644 --- a/schemas/test/en/test_date_range.json +++ b/schemas/test/en/test_date_range.json @@ -44,14 +44,12 @@ "id": "date-range-from-answer", "label": "Period from", "mandatory": true, - "q_code": "11", "type": "Date" }, { "id": "date-range-to-answer", "label": "Period to", "mandatory": true, - "q_code": "12", "type": "Date" } ], diff --git a/schemas/test/en/test_dates.json b/schemas/test/en/test_dates.json index 743c554599..90eadcffdf 100644 --- a/schemas/test/en/test_dates.json +++ b/schemas/test/en/test_dates.json @@ -46,14 +46,12 @@ "id": "date-range-from-answer", "label": "Period from", "mandatory": true, - "q_code": "11", "type": "Date" }, { "id": "date-range-to-answer", "label": "Period to", "mandatory": true, - "q_code": "12", "type": "Date" } ], @@ -70,7 +68,6 @@ { "id": "month-year-answer", "mandatory": true, - "q_code": "11", "type": "MonthYearDate" } ], @@ -88,7 +85,6 @@ "id": "single-date-answer", "label": "Date", "mandatory": true, - "q_code": "11", "type": "Date" } ], @@ -106,7 +102,6 @@ "id": "non-mandatory-date-answer", "label": "Date", "mandatory": false, - "q_code": "17", "type": "Date" } ], @@ -124,7 +119,6 @@ "id": "year-date-answer", "label": "Date", "mandatory": false, - "q_code": "18", "type": "YearDate" } ], diff --git a/schemas/test/en/test_durations.json b/schemas/test/en/test_durations.json index a719c970d6..fb8c66752a 100644 --- a/schemas/test/en/test_durations.json +++ b/schemas/test/en/test_durations.json @@ -45,7 +45,6 @@ "label": "Years and Months", "mandatory": false, "units": ["years", "months"], - "q_code": "11", "type": "Duration" }, { @@ -53,7 +52,6 @@ "label": "Mandatory Years and Months", "mandatory": true, "units": ["years", "months"], - "q_code": "12", "type": "Duration" }, { @@ -61,7 +59,6 @@ "label": "Years", "mandatory": false, "units": ["years"], - "q_code": "13", "type": "Duration" }, { @@ -69,7 +66,6 @@ "label": "Mandatory Years", "mandatory": true, "units": ["years"], - "q_code": "14", "type": "Duration" }, { @@ -77,7 +73,6 @@ "label": "Months", "mandatory": false, "units": ["months"], - "q_code": "14", "type": "Duration" }, { @@ -85,7 +80,6 @@ "label": "Mandatory Months", "mandatory": true, "units": ["months"], - "q_code": "15", "type": "Duration" } ], diff --git a/schemas/test/en/test_instructions.json b/schemas/test/en/test_instructions.json index 73accd7825..d80841ce84 100644 --- a/schemas/test/en/test_instructions.json +++ b/schemas/test/en/test_instructions.json @@ -63,7 +63,6 @@ "id": "favourite-lunch", "label": "What is your favourite lunchtime food", "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_interstitial_page.json b/schemas/test/en/test_interstitial_page.json index fb41492557..18b59c4440 100644 --- a/schemas/test/en/test_interstitial_page.json +++ b/schemas/test/en/test_interstitial_page.json @@ -49,7 +49,6 @@ "id": "favourite-breakfast", "label": "What is your favourite breakfast food", "mandatory": false, - "q_code": "0", "type": "TextField" } ], @@ -79,7 +78,6 @@ "id": "favourite-lunch", "label": "What is your favourite lunchtime food", "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_interviewer_note.json b/schemas/test/en/test_interviewer_note.json index 5141486394..3d67b35043 100644 --- a/schemas/test/en/test_interviewer_note.json +++ b/schemas/test/en/test_interviewer_note.json @@ -54,7 +54,6 @@ "id": "favourite-team-answer", "label": "Favourite team", "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_markup.json b/schemas/test/en/test_markup.json index 59a5c2cd3e..d5256089b7 100644 --- a/schemas/test/en/test_markup.json +++ b/schemas/test/en/test_markup.json @@ -55,7 +55,6 @@ "id": "answer", "label": "What is the thing?", "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_metadata_routing.json b/schemas/test/en/test_metadata_routing.json index dccb4c2157..ae8f7ff5d2 100644 --- a/schemas/test/en/test_metadata_routing.json +++ b/schemas/test/en/test_metadata_routing.json @@ -48,7 +48,6 @@ { "id": "block1-answer", "mandatory": false, - "q_code": "20", "type": "TextField", "label": "Question 1" } @@ -85,7 +84,6 @@ { "id": "block2-answer", "mandatory": false, - "q_code": "20", "type": "TextField", "label": "Question 2" } @@ -103,7 +101,6 @@ { "id": "block3-answer", "mandatory": false, - "q_code": "20", "type": "TextField", "label": "Question 3" } diff --git a/schemas/test/en/test_new_routing_date_equals.json b/schemas/test/en/test_new_routing_date_equals.json index e9b8fae0da..111437b93c 100644 --- a/schemas/test/en/test_new_routing_date_equals.json +++ b/schemas/test/en/test_new_routing_date_equals.json @@ -43,7 +43,6 @@ { "id": "comparison-date-answer", "mandatory": true, - "q_code": "11", "type": "Date" } ], diff --git a/schemas/test/en/test_new_skip_condition_not_set.json b/schemas/test/en/test_new_skip_condition_not_set.json index 42b18fd527..80a871d6dd 100644 --- a/schemas/test/en/test_new_skip_condition_not_set.json +++ b/schemas/test/en/test_new_skip_condition_not_set.json @@ -55,7 +55,6 @@ "value": "Eggs" } ], - "q_code": "20", "type": "Radio" } ], @@ -83,7 +82,6 @@ "value": "Coffee" } ], - "q_code": "20", "type": "Radio" } ], diff --git a/schemas/test/en/test_new_skip_condition_set.json b/schemas/test/en/test_new_skip_condition_set.json index 301fa58006..993fb59e34 100644 --- a/schemas/test/en/test_new_skip_condition_set.json +++ b/schemas/test/en/test_new_skip_condition_set.json @@ -55,7 +55,6 @@ "value": "Eggs" } ], - "q_code": "20", "type": "Radio" } ], @@ -83,7 +82,6 @@ "value": "Coffee" } ], - "q_code": "20", "type": "Radio" } ], diff --git a/schemas/test/en/test_percentage.json b/schemas/test/en/test_percentage.json index 7eb17679ae..4fdda14a66 100644 --- a/schemas/test/en/test_percentage.json +++ b/schemas/test/en/test_percentage.json @@ -49,7 +49,6 @@ "id": "answer", "label": "New to the market 2012-2014", "mandatory": false, - "q_code": "0", "type": "Percentage", "maximum": { "value": 100 diff --git a/schemas/test/en/test_placeholder_based_on_first_item_in_list.json b/schemas/test/en/test_placeholder_based_on_first_item_in_list.json index 76169583a3..b6a6d08b8f 100644 --- a/schemas/test/en/test_placeholder_based_on_first_item_in_list.json +++ b/schemas/test/en/test_placeholder_based_on_first_item_in_list.json @@ -268,7 +268,6 @@ "label": "What is your favourite drink", "max_length": 20, "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_placeholder_playback_list.json b/schemas/test/en/test_placeholder_playback_list.json index 03bc2a4521..a3ecaa4f29 100644 --- a/schemas/test/en/test_placeholder_playback_list.json +++ b/schemas/test/en/test_placeholder_playback_list.json @@ -46,37 +46,30 @@ "options": [ { "label": "None", - "value": "None", - "q_code": "0" + "value": "None" }, { "label": "Cheese", - "value": "Cheese", - "q_code": "1" + "value": "Cheese" }, { "label": "Ham", - "value": "Ham", - "q_code": "2" + "value": "Ham" }, { "label": "Pineapple", - "value": "Pineapple", - "q_code": "3" + "value": "Pineapple" }, { "label": "Tuna", - "value": "Tuna", - "q_code": "4" + "value": "Tuna" }, { "label": "Pepperoni", - "value": "Pepperoni", - "q_code": "5" + "value": "Pepperoni" }, { "label": "Other", - "q_code": "6", "description": "Choose any other topping", "value": "Other", "detail_answer": { diff --git a/schemas/test/en/test_question_description.json b/schemas/test/en/test_question_description.json index b37a74cc4f..4904ec7083 100644 --- a/schemas/test/en/test_question_description.json +++ b/schemas/test/en/test_question_description.json @@ -51,7 +51,6 @@ "label": "What is your name?", "max_length": 20, "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_question_guidance.json b/schemas/test/en/test_question_guidance.json index b136959e00..5cc4695019 100644 --- a/schemas/test/en/test_question_guidance.json +++ b/schemas/test/en/test_question_guidance.json @@ -69,7 +69,6 @@ "id": "answer-test-guidance-title", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -101,7 +100,6 @@ "id": "answer-test-guidance-description", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -135,7 +133,6 @@ "id": "answer-test-guidance-lists", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -169,7 +166,6 @@ "id": "answer-test-guidance-content-description", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -203,7 +199,6 @@ "id": "answer-test-guidance-content-title", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -237,7 +232,6 @@ "id": "answer-test-guidance-content-list", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] @@ -303,7 +297,6 @@ "id": "answer-test-guidance-all", "label": "Text question", "mandatory": false, - "q_code": "0", "type": "TextField" } ] diff --git a/schemas/test/en/test_radio_checkbox_descriptions.json b/schemas/test/en/test_radio_checkbox_descriptions.json index f0a30ca1bd..9e7011aad6 100644 --- a/schemas/test/en/test_radio_checkbox_descriptions.json +++ b/schemas/test/en/test_radio_checkbox_descriptions.json @@ -66,7 +66,6 @@ "value": "Implementation of changes to marketing concepts or strategies" } ], - "q_code": "10", "type": "Checkbox", "validation": { "messages": {} @@ -108,7 +107,6 @@ "value": "Implementation of changes to marketing concepts or strategies" } ], - "q_code": "20", "type": "Radio", "validation": { "messages": {} diff --git a/schemas/test/en/test_routing_date_equals.json b/schemas/test/en/test_routing_date_equals.json index d827b6c2bb..22bf06bdf4 100644 --- a/schemas/test/en/test_routing_date_equals.json +++ b/schemas/test/en/test_routing_date_equals.json @@ -43,7 +43,6 @@ { "id": "comparison-date-answer", "mandatory": true, - "q_code": "11", "type": "Date" } ], diff --git a/schemas/test/en/test_routing_on_multiple_select.json b/schemas/test/en/test_routing_on_multiple_select.json index c8dc6baffb..ae98c6168c 100644 --- a/schemas/test/en/test_routing_on_multiple_select.json +++ b/schemas/test/en/test_routing_on_multiple_select.json @@ -58,7 +58,6 @@ "value": "None" } ], - "q_code": "20", "type": "Checkbox" } ] @@ -95,7 +94,6 @@ "id": "block2-answer", "label": "Question 2", "mandatory": false, - "q_code": "20", "type": "TextField" } ] @@ -113,7 +111,6 @@ "id": "block3-answer", "label": "Question 3", "mandatory": false, - "q_code": "20", "type": "TextField" } ] diff --git a/schemas/test/en/test_submit_with_custom_submission_text.json b/schemas/test/en/test_submit_with_custom_submission_text.json index 2820d8c857..e4401d211c 100644 --- a/schemas/test/en/test_submit_with_custom_submission_text.json +++ b/schemas/test/en/test_submit_with_custom_submission_text.json @@ -54,7 +54,6 @@ "id": "breakfast-answer", "label": "What is your favourite breakfast food", "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_submit_with_summary_custom_submission_text.json b/schemas/test/en/test_submit_with_summary_custom_submission_text.json index 7caca60a08..07943d2b69 100644 --- a/schemas/test/en/test_submit_with_summary_custom_submission_text.json +++ b/schemas/test/en/test_submit_with_summary_custom_submission_text.json @@ -54,7 +54,6 @@ "id": "dessert", "label": "What is your favourite dessert?", "mandatory": true, - "q_code": "30", "type": "TextField" } ] diff --git a/schemas/test/en/test_textarea.json b/schemas/test/en/test_textarea.json index 0eef1f36a3..5029fbace6 100644 --- a/schemas/test/en/test_textarea.json +++ b/schemas/test/en/test_textarea.json @@ -50,7 +50,6 @@ "label": "Enter your comments", "rows": 3, "mandatory": false, - "q_code": "0", "type": "TextArea", "max_length": 20, "validation": { diff --git a/schemas/test/en/test_textfield.json b/schemas/test/en/test_textfield.json index 8458c770ca..8ca7bbc3e1 100644 --- a/schemas/test/en/test_textfield.json +++ b/schemas/test/en/test_textfield.json @@ -50,7 +50,6 @@ "label": "What is your name?", "max_length": 20, "mandatory": false, - "q_code": "0", "type": "TextField" } ], diff --git a/schemas/test/en/test_theme_northernireland.json b/schemas/test/en/test_theme_northernireland.json index 8b1f02fba7..569698acb7 100644 --- a/schemas/test/en/test_theme_northernireland.json +++ b/schemas/test/en/test_theme_northernireland.json @@ -57,7 +57,6 @@ "value": "Sausage" } ], - "q_code": "20", "type": "Radio" } ], diff --git a/schemas/test/en/test_theme_social.json b/schemas/test/en/test_theme_social.json index 4f94b88b2a..3be2f8dfef 100644 --- a/schemas/test/en/test_theme_social.json +++ b/schemas/test/en/test_theme_social.json @@ -57,7 +57,6 @@ "value": "Sausage" } ], - "q_code": "20", "type": "Radio" } ], diff --git a/schemas/test/en/test_timeout.json b/schemas/test/en/test_timeout.json index 46c3fd45ed..85e23ee42c 100644 --- a/schemas/test/en/test_timeout.json +++ b/schemas/test/en/test_timeout.json @@ -47,7 +47,6 @@ "id": "timeout-answer", "label": "Does the timeout appear?", "mandatory": false, - "q_code": "0", "type": "TextField" } ], From 44b5b82736db160124f150adab2b9b7250ab3537 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 12 Jul 2022 10:51:44 +0100 Subject: [PATCH 048/567] Fix durations suffix bug (#898) --- templates/partials/answers/duration.html | 8 +++++-- tests/functional/generate_pages.py | 28 ++++++++++++++++++++++-- tests/functional/spec/durations.spec.js | 7 ++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/templates/partials/answers/duration.html b/templates/partials/answers/duration.html index b02ee699a5..b967b47815 100644 --- a/templates/partials/answers/duration.html +++ b/templates/partials/answers/duration.html @@ -16,7 +16,9 @@ "id": years.id, "name": years.name, "value": years.data if years.data is not none else '', - "suffix": _(years.label.text) + "suffix": { + "text": _(years.label.text) + } }) %} {% endif %} @@ -25,7 +27,9 @@ "id": months.id, "name": months.name, "value": months.data if months.data is not none else '', - "suffix": _(months.label.text) + "suffix": { + "text": _(months.label.text) + } }) %} {% endif %} diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 8e90c57bf4..028c123107 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -177,6 +177,14 @@ """ ) +ANSWER_SUFFIX_GETTER = Template( + r""" ${answerName}Suffix() { + return `#${answerId} + abbr`; + } + +""" +) + DYNAMIC_ANSWER_GETTER = Template( r""" answerByIndex(answerIndex) { return `#${answerId}-${answerIndex}`; @@ -409,6 +417,7 @@ def process_answer(answer, page_spec, long_names, page_name): elif answer["type"] in "Duration": page_spec.write(_write_duration_answer(answer["id"], answer["units"], prefix)) + page_spec.write(_write_duration_suffix(answer["id"], answer["units"], prefix)) elif answer["type"] == "Address": page_spec.write(_write_address_answer(answer["id"], prefix)) elif answer["type"] in { @@ -653,8 +662,23 @@ def _write_duration_answer(answer_id, units, prefix): resp.append( ANSWER_GETTER.substitute( { - "answerName": prefix + unit.title(), - "answerId": answer_id + "-" + unit, + "answerName": f"{prefix}{unit.title()}", + "answerId": f"{answer_id}-{unit}", + } + ) + ) + + return "".join(resp) + + +def _write_duration_suffix(answer_id, units, prefix): + resp = [] + for unit in units: + resp.append( + ANSWER_SUFFIX_GETTER.substitute( + { + "answerName": f"{prefix}{unit.title()}", + "answerId": f"{answer_id}-{unit}", } ) ) diff --git a/tests/functional/spec/durations.spec.js b/tests/functional/spec/durations.spec.js index 32adbdc280..af0840c319 100644 --- a/tests/functional/spec/durations.spec.js +++ b/tests/functional/spec/durations.spec.js @@ -6,6 +6,13 @@ describe("Durations", () => { browser.openQuestionnaire("test_durations.json"); }); + it("Given the test_durations survey is selected durations suffixes are visible", () => { + expect($(DurationPage.yearMonthYearsSuffix()).getText()).to.contain("Years"); + expect($(DurationPage.mandatoryYearMonthMonthsSuffix()).getText()).to.contain("Months"); + expect($(DurationPage.yearYearsSuffix()).getText()).to.contain("Years"); + expect($(DurationPage.mandatoryMonthMonthsSuffix()).getText()).to.contain("Months"); + }); + it("Given the test_durations survey is selected when durations are entered then the summary screen shows the durations entered formatted", () => { $(DurationPage.yearMonthYears()).setValue(1); $(DurationPage.yearMonthMonths()).setValue(2); From 16091f609b467aec6ad5ef578573109ebab9f2ad Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 14 Jul 2022 09:22:27 +0100 Subject: [PATCH 049/567] Upgrade ds to v53 (#899) --- .design-system-version | 2 +- app/helpers/template_helpers.py | 6 +++--- app/survey_config/census_config.py | 6 +++--- tests/app/helpers/conftest.py | 2 +- tests/app/helpers/test_template_helpers.py | 18 +++++++++--------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.design-system-version b/.design-system-version index 26adcbbd44..5ee343ab30 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -52.0.0 +53.0.4 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 3b1d8751e2..ef4f938cec 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -107,8 +107,8 @@ def data_layer_context( @property def page_header_context(self) -> dict[str, Union[bool, str, LazyString]]: context: dict[str, Union[bool, str, LazyString]] = { - "logo": f"{self._survey_config.page_header_logo}", - "logoAlt": f"{self._survey_config.page_header_logo_alt}", + "orgLogo": f"{self._survey_config.page_header_logo}", + "orgLogoAlt": f"{self._survey_config.page_header_logo_alt}", } if self._survey_title: @@ -120,7 +120,7 @@ def page_header_context(self) -> dict[str, Union[bool, str, LazyString]]: if self._survey_config.custom_header_logo: context["customHeaderLogo"] = self._survey_config.custom_header_logo if self._survey_config.mobile_logo: - context["mobileLogo"] = self._survey_config.mobile_logo + context["orgMobileLogo"] = self._survey_config.mobile_logo return context diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index 2fbdd5f33c..8a602f7131 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -125,12 +125,12 @@ class CensusNISRASurveyConfig( ): base_url: str = NIR_BASE_URL account_service_log_out_url: str = base_url - page_header_logo: str = "nisra-logo-en" + page_header_logo: str = "nisra-logo" page_header_logo_alt: str = lazy_gettext( "Northern Ireland Statistics and Research Agency logo" ) custom_header_logo: bool = True - mobile_logo: str = "nisra-logo-en-mobile" + mobile_logo: str = "nisra-logo-mobile" copyright_declaration: LazyString = lazy_gettext( "Crown copyright and database rights 2021 NIMA MOU577.501." ) @@ -167,6 +167,6 @@ class CensusNISRASurveyConfig( compare=False, hash=False, ) - powered_by_logo: str = "nisra-logo-black-en" + powered_by_logo: str = "nisra-logo" powered_by_logo_alt: str = "NISRA - Northern Ireland Statistics and Research Agency" _is_nisra: bool = True diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index dc4e405189..ade23b9efe 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -232,7 +232,7 @@ def expected_footer_nisra_theme(): } ], "poweredBy": { - "logo": "nisra-logo-black-en", + "logo": "nisra-logo", "alt": "NISRA - Northern Ireland Statistics and Research Agency", }, } diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index a132a28abc..22a2d36818 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -102,8 +102,8 @@ def test_footer_context_census_nisra_theme(app: Flask, expected_footer_nisra_the def test_get_page_header_context_business(app: Flask): expected = { - "logo": "ons-logo-en", - "logoAlt": "Office for National Statistics logo", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", } with app.app_context(): @@ -121,8 +121,8 @@ def test_get_page_header_context_business(app: Flask): def test_get_page_header_context_social(app: Flask): expected = { - "logo": "ons-logo-en", - "logoAlt": "Office for National Statistics logo", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", "title": "ONS Social Surveys", } @@ -142,8 +142,8 @@ def test_get_page_header_context_social(app: Flask): def test_get_page_header_context_census(app: Flask): expected = { "title": "Census 2021", - "logo": "ons-logo-en", - "logoAlt": "Office for National Statistics logo", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", "titleLogo": "census-logo-en", "titleLogoAlt": "Census 2021", } @@ -164,12 +164,12 @@ def test_get_page_header_context_census(app: Flask): def test_get_page_header_context_census_nisra(app: Flask): expected = { "title": "Census 2021", - "logo": "nisra-logo-en", - "logoAlt": "Northern Ireland Statistics and Research Agency logo", + "orgLogo": "nisra-logo", + "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", "titleLogo": "census-logo-en", "titleLogoAlt": "Census 2021", "customHeaderLogo": True, - "mobileLogo": "nisra-logo-en-mobile", + "orgMobileLogo": "nisra-logo-mobile", } with app.app_context(): From 213180b861c64ae28b7525bd5c0a61ec9a3332dc Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 15 Jul 2022 09:07:24 +0100 Subject: [PATCH 050/567] Add conditional links to error pages (#888) --- app/helpers/template_helpers.py | 55 ++-- app/routes/errors.py | 22 +- app/settings.py | 2 + app/survey_config/social_survey_config.py | 3 +- app/survey_config/survey_type.py | 11 + app/translations/messages.pot | 220 ++++++++----- .../contexts/submission_metadata_context.py | 5 +- app/views/contexts/thank_you_context.py | 5 +- .../view_submitted_response_context.py | 5 +- templates/errors/401.html | 15 +- templates/errors/403.html | 17 +- templates/errors/404.html | 18 +- templates/errors/500.html | 17 +- templates/errors/_base.html | 5 + templates/errors/submission-failed.html | 14 +- tests/app/helpers/conftest.py | 42 +-- tests/app/helpers/test_template_helpers.py | 147 +++++---- tests/app/submitter/conftest.py | 3 +- .../test_submission_metatdata_context.py | 5 +- .../test_submitted_response_context.py | 13 +- .../views/contexts/test_thank_you_context.py | 5 +- tests/integration/integration_test_case.py | 17 +- tests/integration/routes/test_errors.py | 295 +++++++++++++++++- 23 files changed, 705 insertions(+), 236 deletions(-) create mode 100644 app/survey_config/survey_type.py create mode 100644 templates/errors/_base.html diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index ef4f938cec..e3f027cf6c 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -22,6 +22,7 @@ SurveyConfig, WelshCensusSurveyConfig, ) +from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_session_data @@ -49,6 +50,7 @@ def __init__( self._google_tag_manager_auth = current_app.config.get( "EQ_GOOGLE_TAG_MANAGER_AUTH" ) + self._survey_type = cookie_session.get("theme") @property def context(self) -> dict[str, Any]: @@ -73,6 +75,7 @@ def context(self) -> dict[str, Any]: "include_csrf_token": self._include_csrf_token, "google_tag_manager_id": self._google_tag_manager_id, "google_tag_manager_auth": self._google_tag_manager_auth, + "survey_type": self._survey_type, } @property @@ -83,7 +86,7 @@ def service_links_context( if service_links := self._survey_config.get_service_links( sign_out_url=self._sign_out_url, is_authenticated=current_user.is_authenticated, - cookie_has_theme=bool(cookie_session.get("theme")), + cookie_has_theme=bool(self._survey_type), ru_ref=metadata.get("ru_ref") if metadata else None, # type: ignore ): return { @@ -168,16 +171,18 @@ def _footer_warning(self) -> Optional[str]: @lru_cache def survey_config_mapping( - *, theme: str, language: str, base_url: str, schema: QuestionnaireSchema + *, theme: SurveyType, language: str, base_url: str, schema: QuestionnaireSchema ) -> SurveyConfig: - survey_type_to_config: dict[str, Type[SurveyConfig]] = { - "default": BusinessSurveyConfig, - "business": BusinessSurveyConfig, - "health": SurveyConfig, - "social": SocialSurveyConfig, - "northernireland": NorthernIrelandBusinessSurveyConfig, - "census": (WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig), - "census-nisra": CensusNISRASurveyConfig, + survey_type_to_config: dict[SurveyType, Type[SurveyConfig]] = { + SurveyType.DEFAULT: BusinessSurveyConfig, + SurveyType.BUSINESS: BusinessSurveyConfig, + SurveyType.HEALTH: SurveyConfig, + SurveyType.SOCIAL: SocialSurveyConfig, + SurveyType.NORTHERN_IRELAND: NorthernIrelandBusinessSurveyConfig, + SurveyType.CENSUS: ( + WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig + ), + SurveyType.CENSUS_NISRA: CensusNISRASurveyConfig, } return survey_type_to_config[theme]( @@ -188,20 +193,26 @@ def survey_config_mapping( def get_survey_config( *, - theme: Optional[str] = None, + base_url: Optional[str] = None, + theme: Optional[SurveyType] = None, language: Optional[str] = None, schema: Optional[QuestionnaireSchema] = None, ) -> SurveyConfig: # The fallback to assigning SURVEY_TYPE to theme is only being added until # business feedback on the differentiation between theme and SURVEY_TYPE. + if session_store := get_session_store(): + if session_data := session_store.session_data: + schema = load_schema_from_session_data(session_data) + language = language or get_locale().language - theme = theme or get_survey_type() - base_url = ( + survey_theme = theme or get_survey_type() + + base_url = base_url or ( cookie_session.get("account_service_base_url") or ACCOUNT_SERVICE_BASE_URL ) return survey_config_mapping( - theme=theme, + theme=survey_theme, language=language, base_url=base_url, schema=schema, @@ -209,19 +220,14 @@ def get_survey_config( def render_template(template: str, **kwargs: Union[str, Mapping]) -> str: + session_expires_at = None language = get_locale().language - schema, session_expires_at = None, None if session_store := get_session_store(): - if session_data := session_store.session_data: - schema = load_schema_from_session_data(session_data) - if session_expiry := session_store.expiration_time: session_expires_at = session_expiry.isoformat() - survey_config = get_survey_config( - language=language, - schema=schema, - ) + survey_config = get_survey_config() + is_post_submission = request.blueprint == "post_submission" include_csrf_token = bool( request.url_rule @@ -244,5 +250,6 @@ def render_template(template: str, **kwargs: Union[str, Mapping]) -> str: ) -def get_survey_type() -> str: - return cookie_session.get("theme", current_app.config["SURVEY_TYPE"]) +def get_survey_type() -> SurveyType: + survey_type = cookie_session.get("theme", current_app.config["SURVEY_TYPE"]) + return SurveyType(survey_type) diff --git a/app/routes/errors.py b/app/routes/errors.py index 76c0f67260..0ac26bf492 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -15,9 +15,11 @@ from app.authentication.no_token_exception import NoTokenException from app.globals import get_metadata from app.helpers.language_helper import handle_language -from app.helpers.template_helpers import render_template +from app.helpers.template_helpers import get_survey_config, render_template +from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter.previously_submitted_exception import PreviouslySubmittedException from app.submitter.submission_failed import SubmissionFailedException +from app.survey_config.survey_type import SurveyType from app.views.handlers.confirm_email import ( ConfirmationEmailFulfilmentRequestPublicationFailed, ) @@ -49,10 +51,26 @@ def log_exception(exception, status_code): def _render_error_page(status_code, template=None, **kwargs): handle_language() + business_survey_config = get_survey_config(theme=SurveyType.BUSINESS) + other_survey_config = get_survey_config( + theme=SurveyType.SOCIAL, base_url=ACCOUNT_SERVICE_BASE_URL_SOCIAL + ) + + business_logout_url = business_survey_config.account_service_log_out_url + other_logout_url = other_survey_config.account_service_log_out_url + business_contact_us_url = business_survey_config.contact_us_url + other_contact_us_url = other_survey_config.contact_us_url template = template or status_code return ( - render_template(template=f"errors/{template}", **kwargs), + render_template( + template=f"errors/{template}", + business_logout_url=business_logout_url, + other_logout_url=other_logout_url, + business_contact_us_url=business_contact_us_url, + other_contact_us_url=other_contact_us_url, + **kwargs, + ), status_code, ) diff --git a/app/settings.py b/app/settings.py index 6758bfd266..b80fa0be9d 100644 --- a/app/settings.py +++ b/app/settings.py @@ -149,6 +149,8 @@ def utcoffset_or_fail(date_value, key): "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" ) +ACCOUNT_SERVICE_BASE_URL_SOCIAL = "https://rh.ons.gov.uk" + PRINT_STYLE_SHEET_FILE_PATH = os.getenv( "PRINT_STYLE_SHEET_FILEPATH", "templates/assets/styles" ) diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index eaa93a476b..0fb1820e45 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -3,6 +3,7 @@ from flask_babel import lazy_gettext +from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.survey_config.link import Link from app.survey_config.survey_config import SurveyConfig @@ -11,7 +12,7 @@ class SocialSurveyConfig( SurveyConfig, ): - base_url: str = "https://rh.ons.gov.uk" + base_url: str = ACCOUNT_SERVICE_BASE_URL_SOCIAL survey_title: str = "ONS Social Surveys" footer_links: Iterable[MutableMapping] = field(default_factory=list) footer_legal_links: Iterable[Mapping] = field(default_factory=list) diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py new file mode 100644 index 0000000000..01c5236efa --- /dev/null +++ b/app/survey_config/survey_type.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class SurveyType(Enum): + BUSINESS = "business" + SOCIAL = "social" + DEFAULT = "default" + HEALTH = "health" + NORTHERN_IRELAND = "northernireland" + CENSUS = "census" + CENSUS_NISRA = "census-nisra" diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 22072b3920..b8c272fbb3 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-04-21 15:17+0100\n" +"POT-Creation-Date: 2022-06-27 13:29+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,26 +17,26 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: app/forms/validators.py:372 app/jinja_filters.py:94 +#: app/forms/validators.py:372 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:376 app/jinja_filters.py:102 +#: app/forms/validators.py:376 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" msgstr[0] "" msgstr[1] "" -#: app/jinja_filters.py:142 +#: app/jinja_filters.py:165 #, python-format msgid "%(date)s at %(time)s" msgstr "" -#: app/jinja_filters.py:155 +#: app/jinja_filters.py:179 #, python-format msgid "%(from_date)s to %(to_date)s" msgstr "" @@ -203,87 +203,87 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:85 +#: app/helpers/template_helpers.py:97 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:118 +#: app/helpers/template_helpers.py:138 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:148 +#: app/helpers/template_helpers.py:168 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" msgstr "" -#: app/questionnaire/placeholder_transforms.py:144 +#: app/questionnaire/placeholder_transforms.py:164 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:150 +#: app/questionnaire/placeholder_transforms.py:170 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:155 +#: app/questionnaire/placeholder_transforms.py:175 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:118 +#: app/routes/errors.py:133 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:121 +#: app/routes/errors.py:136 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:137 +#: app/routes/errors.py:152 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:140 +#: app/routes/errors.py:155 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:172 +#: app/routes/errors.py:187 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:178 +#: app/routes/errors.py:193 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:181 app/routes/errors.py:204 app/routes/errors.py:226 +#: app/routes/errors.py:196 app/routes/errors.py:219 app/routes/errors.py:241 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:200 +#: app/routes/errors.py:215 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:201 +#: app/routes/errors.py:216 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:222 templates/errors/403.html:3 -#: templates/errors/403.html:6 templates/errors/submission-failed.html:5 +#: app/routes/errors.py:237 templates/errors/403.html:3 +#: templates/errors/403.html:7 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:223 +#: app/routes/errors.py:238 msgid "You can try to submit your feedback again." msgstr "" @@ -295,86 +295,90 @@ msgstr "" msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:51 +#: app/survey_config/business_config.py:34 +#: app/survey_config/social_survey_config.py:35 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:52 -#: app/survey_config/census_config.py:30 app/survey_config/census_config.py:80 -#: app/survey_config/census_config.py:141 +#: app/survey_config/business_config.py:35 +#: app/survey_config/census_config.py:30 app/survey_config/census_config.py:88 +#: app/survey_config/census_config.py:146 +#: app/survey_config/social_survey_config.py:36 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:54 +#: app/survey_config/business_config.py:37 +#: app/survey_config/social_survey_config.py:38 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:59 -#: app/survey_config/census_config.py:44 app/survey_config/census_config.py:95 -#: app/survey_config/census_config.py:148 +#: app/survey_config/business_config.py:42 +#: app/survey_config/census_config.py:44 app/survey_config/census_config.py:103 +#: app/survey_config/census_config.py:153 +#: app/survey_config/social_survey_config.py:43 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:61 -#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:101 -#: app/survey_config/census_config.py:154 +#: app/survey_config/business_config.py:44 +#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:109 +#: app/survey_config/census_config.py:159 +#: app/survey_config/social_survey_config.py:45 msgid "Privacy and data protection" msgstr "" -#: app/survey_config/business_config.py:72 -#: app/survey_config/business_config.py:88 -#: app/survey_config/census_config.py:27 app/survey_config/census_config.py:77 -#: app/survey_config/census_config.py:138 +#: app/survey_config/business_config.py:76 +#: app/survey_config/census_config.py:27 app/survey_config/census_config.py:85 +#: app/survey_config/census_config.py:143 msgid "Help" msgstr "" -#: app/survey_config/business_config.py:77 +#: app/survey_config/business_config.py:90 msgid "My account" msgstr "" -#: app/survey_config/business_config.py:82 +#: app/survey_config/business_config.py:95 msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:107 +#: app/survey_config/business_config.py:128 msgid "Northern Ireland Department of Finance logo" msgstr "" -#: app/survey_config/census_config.py:20 app/survey_config/census_config.py:63 +#: app/survey_config/census_config.py:20 app/survey_config/census_config.py:61 msgid "Census 2021" msgstr "" -#: app/survey_config/census_config.py:32 app/survey_config/census_config.py:82 +#: app/survey_config/census_config.py:32 app/survey_config/census_config.py:90 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:36 app/survey_config/census_config.py:86 +#: app/survey_config/census_config.py:36 app/survey_config/census_config.py:94 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:97 -#: app/survey_config/census_config.py:150 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:105 +#: app/survey_config/census_config.py:155 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:54 app/survey_config/census_config.py:105 -#: app/survey_config/census_config.py:158 +#: app/survey_config/census_config.py:54 app/survey_config/census_config.py:113 +#: app/survey_config/census_config.py:163 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:64 +#: app/survey_config/census_config.py:62 msgid "Save and complete later" msgstr "" -#: app/survey_config/census_config.py:124 +#: app/survey_config/census_config.py:129 msgid "Northern Ireland Statistics and Research Agency logo" msgstr "" -#: app/survey_config/census_config.py:129 +#: app/survey_config/census_config.py:134 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:132 app/survey_config/survey_config.py:23 +#: app/survey_config/census_config.py:137 app/survey_config/survey_config.py:23 msgid "Use of address data is subject to the terms and conditions." msgstr "" @@ -386,7 +390,7 @@ msgstr "" msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" -#: app/survey_config/survey_config.py:42 +#: app/survey_config/survey_config.py:46 msgid "Save and exit survey" msgstr "" @@ -528,7 +532,7 @@ msgstr "" #: app/views/handlers/confirm_email.py:128 #: app/views/handlers/confirmation_email.py:68 -#: app/views/handlers/feedback.py:79 app/views/handlers/question.py:160 +#: app/views/handlers/feedback.py:79 app/views/handlers/question.py:165 msgid "Error: {page_title}" msgstr "" @@ -900,61 +904,61 @@ msgstr "" msgid "We’ve received your answers" msgstr "" -#: templates/thank-you.html:14 +#: templates/thank-you.html:15 msgid "Back to surveys" msgstr "" -#: templates/thank-you.html:31 +#: templates/thank-you.html:33 msgid "Thank you for completing the {survey_title}" msgstr "" -#: templates/thank-you.html:43 +#: templates/thank-you.html:45 msgid "Your answers will be processed in the next few weeks." msgstr "" -#: templates/thank-you.html:44 +#: templates/thank-you.html:46 msgid "We may contact you to query your answers via phone or secure message." msgstr "" -#: templates/thank-you.html:45 +#: templates/thank-you.html:47 msgid "For more information on how we use this data." msgstr "" -#: templates/thank-you.html:49 templates/view-submitted-response.html:66 +#: templates/thank-you.html:51 templates/view-submitted-response.html:66 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" -#: templates/thank-you.html:61 +#: templates/thank-you.html:63 msgid "For security, your answers will only be available to view for another " msgstr "" -#: templates/thank-you.html:62 +#: templates/thank-you.html:64 msgid "Get a copy of your answers" msgstr "" -#: templates/thank-you.html:64 +#: templates/thank-you.html:66 msgid "" "You can save or print " "your answers for your records." msgstr "" -#: templates/layouts/_base.html:158 templates/thank-you.html:67 +#: templates/layouts/_base.html:158 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/layouts/_base.html:159 templates/thank-you.html:68 +#: templates/layouts/_base.html:159 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:160 templates/thank-you.html:69 +#: templates/layouts/_base.html:160 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/layouts/_base.html:161 templates/thank-you.html:70 +#: templates/layouts/_base.html:161 templates/thank-you.html:74 msgid "seconds" msgstr "" -#: templates/thank-you.html:72 +#: templates/thank-you.html:76 msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" @@ -992,22 +996,52 @@ msgstr "" msgid "followed a link to a survey that has already been submitted" msgstr "" -#: templates/errors/401.html:13 +#: templates/errors/401.html:14 msgid "You will need to sign back in to access your account" msgstr "" -#: templates/errors/403.html:7 -msgid "You may need to update your browser to a newer version." +#: templates/errors/401.html:17 +msgid "" +"To access this page you need to re-enter your access " +"code." +msgstr "" + +#: templates/errors/401.html:21 +msgid "" +"If you are completing a business survey, you need to sign back in to your account." +msgstr "" + +#: templates/errors/401.html:23 +msgid "" +"If you started your survey using an access code, you need to re-enter your code." msgstr "" #: templates/errors/403.html:8 -msgid "If the problem still occurs, try using a different browser or device." +msgid "You may need to update your browser to a newer version." msgstr "" #: templates/errors/403.html:9 +msgid "If the problem still occurs, try using a different browser or device." +msgstr "" + +#: templates/errors/403.html:11 msgid "For further help, please contact us." msgstr "" +#: templates/errors/403.html:14 +msgid "" +"If you are completing a business survey and you need further help, please" +" contact us." +msgstr "" + +#: templates/errors/403.html:16 +msgid "" +"If you started your survey using an access code and you need further " +"help, please contact us." +msgstr "" + #: templates/errors/404.html:3 templates/errors/404.html:6 msgid "Page not found" msgstr "" @@ -1020,12 +1054,30 @@ msgstr "" msgid "If you pasted the web address, check you copied the whole address." msgstr "" -#: templates/errors/404.html:9 +#: templates/errors/404.html:10 msgid "" "If the web address is correct or you selected a link or button, contact us for more help." msgstr "" +#: templates/errors/404.html:12 +msgid "" +"If the web address is correct or you selected a link or button, please " +"see the following help links." +msgstr "" + +#: templates/errors/404.html:14 templates/errors/submission-failed.html:15 +msgid "" +"If you are completing a business survey, please contact " +"us." +msgstr "" + +#: templates/errors/404.html:16 templates/errors/submission-failed.html:17 +msgid "" +"If you started your survey using an access code, please contact us." +msgstr "" + #: templates/errors/500.html:3 msgid "An error has occurred" msgstr "" @@ -1042,12 +1094,24 @@ msgstr "" msgid "If you have started a survey, your answers have been saved." msgstr "" -#: templates/errors/500.html:9 +#: templates/errors/500.html:10 msgid "" "Contact us if you need to speak to someone about your" " survey." msgstr "" +#: templates/errors/500.html:13 +msgid "" +"If you are completing a business survey and you need to speak to someone " +"about your survey, please contact us." +msgstr "" + +#: templates/errors/500.html:15 +msgid "" +"If you started your survey using an access code and you need to speak to " +"someone about your survey, please contact us." +msgstr "" + #: templates/errors/previously-submitted.html:3 msgid "Submission Complete" msgstr "" @@ -1065,15 +1129,19 @@ msgid "Return to previous page" msgstr "" #: templates/errors/submission-failed.html:9 -msgid "You can try to submit your census again" +msgid "You can try to submit your survey again" msgstr "" -#: templates/errors/submission-failed.html:10 +#: templates/errors/submission-failed.html:11 msgid "" "If this problem keeps happening, please contact us " "for help." msgstr "" +#: templates/errors/submission-failed.html:13 +msgid "If this problem keeps happening, please see the following help links." +msgstr "" + #: templates/individual_response/confirmation-post.html:15 msgid "A letter has been sent to Individual Resident at {display_address}" msgstr "" diff --git a/app/views/contexts/submission_metadata_context.py b/app/views/contexts/submission_metadata_context.py index 9efd264aa0..5fc4b78103 100644 --- a/app/views/contexts/submission_metadata_context.py +++ b/app/views/contexts/submission_metadata_context.py @@ -3,10 +3,11 @@ from flask_babel import format_datetime, lazy_gettext from app.libs.utils import convert_tx_id +from app.survey_config.survey_type import SurveyType def build_submission_metadata_context( - survey_type: str, submitted_at: datetime, tx_id: str + survey_type: SurveyType, submitted_at: datetime, tx_id: str ) -> dict: submitted_on = { "term": lazy_gettext("Submitted on:"), @@ -23,7 +24,7 @@ def build_submission_metadata_context( "term": lazy_gettext("Submission reference:"), "descriptions": [{"description": convert_tx_id(tx_id)}], } - if survey_type == "social": + if survey_type is SurveyType.SOCIAL: return { "data-qa": "metadata", "termCol": 3, diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index 1fe1bb98dc..627ea71c81 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -10,6 +10,7 @@ has_view_submitted_response_expired, ) from app.questionnaire import QuestionnaireSchema +from app.survey_config.survey_type import SurveyType from app.views.contexts.email_form_context import build_email_form_context from app.views.contexts.submission_metadata_context import ( build_submission_metadata_context, @@ -20,10 +21,10 @@ def build_thank_you_context( schema: QuestionnaireSchema, session_data: SessionData, submitted_at: datetime, - survey_type: str, + survey_type: SurveyType, guidance_content: Optional[dict] = None, ) -> Mapping: - if survey_type == "social": + if survey_type is SurveyType.SOCIAL: submission_text = lazy_gettext("Your answers have been submitted.") elif session_data.trad_as and session_data.ru_name: submission_text = lazy_gettext( diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index 2c6805d893..a95dfdfbfe 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -7,6 +7,7 @@ from app.data_models import QuestionnaireStore from app.globals import has_view_submitted_response_expired from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.survey_config.survey_type import SurveyType from app.views.contexts.submission_metadata_context import ( build_submission_metadata_context, ) @@ -17,14 +18,14 @@ def build_view_submitted_response_context( language: str, schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, - survey_type: str, + survey_type: SurveyType, ) -> dict[str, Union[str, datetime, dict]]: view_submitted_response_expired = has_view_submitted_response_expired( questionnaire_store.submitted_at # type: ignore ) - if survey_type == "social": + if survey_type is SurveyType.SOCIAL: submitted_text = lazy_gettext("Answers submitted.") elif trad_as := questionnaire_store.metadata.get("trad_as"): submitted_text = lazy_gettext( diff --git a/templates/errors/401.html b/templates/errors/401.html index 09ef4fb66a..f9e349005b 100644 --- a/templates/errors/401.html +++ b/templates/errors/401.html @@ -1,4 +1,4 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} {% set page_title = _("Page is not available") %} @@ -10,5 +10,16 @@

{{ _("Sorry, you need to sign in again") }}

  • {{ _("followed a link to a page you are not signed in to") }}
  • {{ _("followed a link to a survey that has already been submitted") }}
  • -

    {{ _("You will need to sign back in to access your account").format(url = account_service_log_out_url) }}

    + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} +

    {{ _("You will need to sign back in to access your account").format(url = business_logout_url) }}

    + + {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL %} +

    {{ _("To access this page you need to re-enter your access code.").format(url = other_logout_url) }}

    + + {% else %} +

    Business surveys

    +

    {{ _("If you are completing a business survey, you need to sign back in to your account.").format(url = business_logout_url) }}

    +

    All other surveys

    +

    {{ _("If you started your survey using an access code, you need to re-enter your code.").format(url = other_logout_url) }}

    + {% endif %} {% endblock %} diff --git a/templates/errors/403.html b/templates/errors/403.html index 56cc2bab2c..14c7c44f7b 100644 --- a/templates/errors/403.html +++ b/templates/errors/403.html @@ -1,10 +1,17 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} {% set page_title = _("Sorry, there is a problem") %} {% block main %} -

    {{ _("Sorry, there is a problem") }}

    -

    {{ _("You may need to update your browser to a newer version.") }}

    -

    {{ _("If the problem still occurs, try using a different browser or device.") }}

    -

    {{ _("For further help, please contact us.").format(url=contact_us_url) }}

    +

    {{ _("Sorry, there is a problem") }}

    +

    {{ _("You may need to update your browser to a newer version.") }}

    +

    {{ _("If the problem still occurs, try using a different browser or device.") }}

    + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} +

    {{ _("For further help, please contact us.").format(url=contact_us_url) }}

    + {% else %} +

    Business surveys

    +

    {{ _("If you are completing a business survey and you need further help, please contact us.").format(url=business_contact_us_url) }}

    +

    All other surveys

    +

    {{ _("If you started your survey using an access code and you need further help, please contact us.").format(url=other_contact_us_url) }}

    + {% endif %} {% endblock %} diff --git a/templates/errors/404.html b/templates/errors/404.html index a89a707565..47806411a9 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -1,10 +1,18 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} {% set page_title = _("Page not found") %} {% block main %} -

    {{ _("Page not found") }}

    -

    {{ _("If you entered a web address, check it is correct.") }}

    -

    {{ _("If you pasted the web address, check you copied the whole address.") }}

    -

    {{ _("If the web address is correct or you selected a link or button, contact us for more help.").format(url=contact_us_url) }}

    +

    {{ _("Page not found") }}

    +

    {{ _("If you entered a web address, check it is correct.") }}

    +

    {{ _("If you pasted the web address, check you copied the whole address.") }}

    + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} +

    {{ _("If the web address is correct or you selected a link or button, contact us for more help.").format(url=contact_us_url) }}

    + {% else %} +

    {{ _("If the web address is correct or you selected a link or button, please see the following help links.") }}

    +

    Business surveys

    +

    {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

    +

    All other surveys

    +

    {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

    + {% endif %} {% endblock %} diff --git a/templates/errors/500.html b/templates/errors/500.html index 20b6a3610c..6ac3b682b1 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -1,10 +1,17 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} {% set page_title = _("An error has occurred") %} {% block main %} -

    {{ _("Sorry, there is a problem with this service") }}

    -

    {{ _("Try again later.") }}

    -

    {{ _("If you have started a survey, your answers have been saved.") }}

    -

    {{ _("Contact us if you need to speak to someone about your survey.").format(url = contact_us_url) }}

    +

    {{ _("Sorry, there is a problem with this service") }}

    +

    {{ _("Try again later.") }}

    +

    {{ _("If you have started a survey, your answers have been saved.") }}

    + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} +

    {{ _("Contact us if you need to speak to someone about your survey.").format(url = contact_us_url) }}

    + {% else %} +

    Business surveys

    +

    {{ _("If you are completing a business survey and you need to speak to someone about your survey, please contact us.").format(url=business_contact_us_url) }}

    +

    All other surveys

    +

    {{ _("If you started your survey using an access code and you need to speak to someone about your survey, please contact us.").format(url=other_contact_us_url) }}

    + {% endif %} {% endblock %} diff --git a/templates/errors/_base.html b/templates/errors/_base.html new file mode 100644 index 0000000000..228a1d6aa8 --- /dev/null +++ b/templates/errors/_base.html @@ -0,0 +1,5 @@ +{% extends 'layouts/_base.html' %} + +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business"] %} +{% set SURVEY_TYPES_DEFAULT = ["default"] %} +{% set SURVEY_TYPES_SOCIAL = ["social"] %} diff --git a/templates/errors/submission-failed.html b/templates/errors/submission-failed.html index 0c72c391b6..77b1695561 100644 --- a/templates/errors/submission-failed.html +++ b/templates/errors/submission-failed.html @@ -1,4 +1,4 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} {% set hide_sign_out_button = True %} @@ -6,6 +6,14 @@ {% block main %}

    {{ _("Sorry, there is a problem") }}

    -

    {{ _("You can try to submit your census again").format(url=url_for('questionnaire.get_questionnaire')) }}

    -

    {{ _("If this problem keeps happening, please contact us for help.").format(url=contact_us_url) }}

    +

    {{ _("You can try to submit your survey again").format(url=url_for('questionnaire.get_questionnaire')) }}

    + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} +

    {{ _("If this problem keeps happening, please contact us for help.").format(url=contact_us_url) }}

    + {% else %} +

    {{ _("If this problem keeps happening, please see the following help links.") }}

    +

    Business surveys

    +

    {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

    +

    All other surveys

    +

    {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

    + {% endif %} {% endblock %} diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index ade23b9efe..f75f962eb7 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -1,6 +1,8 @@ from pytest import fixture from app.helpers.template_helpers import ContextHelper +from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL +from app.survey_config.census_config import EN_BASE_URL @fixture @@ -40,22 +42,22 @@ def expected_footer_census_theme(footer_context): "itemsList": [ { "text": "Help", - "url": "https://census.gov.uk/help/how-to-answer-questions/online-questions-help/", + "url": f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", "target": "_blank", }, { "text": "Contact us", - "url": "https://census.gov.uk/contact-us/", + "url": f"{EN_BASE_URL}/contact-us/", "target": "_blank", }, { "text": "Languages", - "url": "https://census.gov.uk/help/languages-and-accessibility/languages/", + "url": f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", "target": "_blank", }, { "text": "BSL and audio videos", - "url": "https://census.gov.uk/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", + "url": f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", "target": "_blank", }, ] @@ -66,22 +68,22 @@ def expected_footer_census_theme(footer_context): "itemsList": [ { "text": "Cookies", - "url": "https://census.gov.uk/cookies/", + "url": f"{EN_BASE_URL}/cookies/", "target": "_blank", }, { "text": "Accessibility statement", - "url": "https://census.gov.uk/accessibility-statement/", + "url": f"{EN_BASE_URL}/accessibility-statement/", "target": "_blank", }, { "text": "Privacy and data protection", - "url": "https://census.gov.uk/privacy-and-data-protection/", + "url": f"{EN_BASE_URL}/privacy-and-data-protection/", "target": "_blank", }, { "text": "Terms and conditions", - "url": "https://census.gov.uk/terms-and-conditions/", + "url": f"{EN_BASE_URL}/terms-and-conditions/", "target": "_blank", }, ] @@ -104,7 +106,7 @@ def expected_footer_business_theme(footer_context): }, { "text": "Contact us", - "url": "https://surveys.ons.gov.uk/contact-us/", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", "target": "_blank", }, { @@ -120,12 +122,12 @@ def expected_footer_business_theme(footer_context): "itemsList": [ { "text": "Cookies", - "url": "https://surveys.ons.gov.uk/cookies/", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", "target": "_blank", }, { "text": "Privacy and data protection", - "url": "https://surveys.ons.gov.uk/privacy-and-data-protection/", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/privacy-and-data-protection/", "target": "_blank", }, ] @@ -148,7 +150,7 @@ def expected_footer_social_theme(footer_context): }, { "text": "Contact us", - "url": "https://rh.ons.gov.uk/contact-us/", + "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/contact-us/", "target": "_blank", }, { @@ -164,12 +166,12 @@ def expected_footer_social_theme(footer_context): "itemsList": [ { "text": "Cookies", - "url": "https://rh.ons.gov.uk/cookies/", + "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cookies/", "target": "_blank", }, { "text": "Privacy and data protection", - "url": "https://rh.ons.gov.uk/privacy-and-data-protection/", + "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/privacy-and-data-protection/", "target": "_blank", }, ] @@ -194,12 +196,12 @@ def expected_footer_nisra_theme(): "itemsList": [ { "text": "Help", - "url": "https://census.gov.uk/ni/help/help-with-the-questions/online-questions-help/", + "url": f"{EN_BASE_URL}/ni/help/help-with-the-questions/online-questions-help/", "target": "_blank", }, { "text": "Contact us", - "url": "https://census.gov.uk/ni/contact-us/", + "url": f"{EN_BASE_URL}/ni/contact-us/", "target": "_blank", }, ] @@ -210,22 +212,22 @@ def expected_footer_nisra_theme(): "itemsList": [ { "text": "Cookies", - "url": "https://census.gov.uk/ni/cookies/", + "url": f"{EN_BASE_URL}/ni/cookies/", "target": "_blank", }, { "text": "Accessibility statement", - "url": "https://census.gov.uk/ni/accessibility-statement/", + "url": f"{EN_BASE_URL}/ni/accessibility-statement/", "target": "_blank", }, { "text": "Privacy and data protection", - "url": "https://census.gov.uk/ni/privacy-and-data-protection/", + "url": f"{EN_BASE_URL}/ni/privacy-and-data-protection/", "target": "_blank", }, { "text": "Terms and conditions", - "url": "https://census.gov.uk/ni/terms-and-conditions/", + "url": f"{EN_BASE_URL}/ni/terms-and-conditions/", "target": "_blank", }, ] diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 22a2d36818..2c2b36d1c6 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -6,7 +6,7 @@ from app.helpers.template_helpers import ContextHelper, get_survey_config from app.questionnaire import QuestionnaireSchema -from app.settings import ACCOUNT_SERVICE_BASE_URL +from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.survey_config import ( BusinessSurveyConfig, CensusNISRASurveyConfig, @@ -16,6 +16,9 @@ SurveyConfig, WelshCensusSurveyConfig, ) +from app.survey_config.survey_type import SurveyType + +DEFAULT_URL = "http://localhost" def test_footer_context_census_theme(app: Flask, expected_footer_census_theme): @@ -206,7 +209,7 @@ def test_get_page_header_context_census_nisra(app: Flask): "itemsList": [ { "title": "Help", - "url": "https://surveys.ons.gov.uk/help", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/help", "id": "header-link-help", } ], @@ -224,12 +227,12 @@ def test_get_page_header_context_census_nisra(app: Flask): "itemsList": [ { "title": "Help", - "url": "https://surveys.ons.gov.uk/surveys/surveys-help?survey_ref=001&ru_ref=63782964754", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=63782964754", "id": "header-link-help", }, { "title": "My account", - "url": "https://surveys.ons.gov.uk/my-account", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/my-account", "id": "header-link-my-account", }, { @@ -279,19 +282,19 @@ def test_service_links_context( [ ( SurveyConfig(), - "https://surveys.ons.gov.uk/contact-us/", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( BusinessSurveyConfig(), - "https://surveys.ons.gov.uk/contact-us/", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( NorthernIrelandBusinessSurveyConfig(), - "https://surveys.ons.gov.uk/contact-us/", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( SocialSurveyConfig(), - "https://rh.ons.gov.uk/contact-us/", + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/contact-us/", ), ], ) @@ -333,18 +336,18 @@ def test_sign_out_button_text_context( @pytest.mark.parametrize( "survey_config, expected", [ - (SurveyConfig(), "https://surveys.ons.gov.uk/cookies/"), + (SurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/cookies/"), ( BusinessSurveyConfig(), - "https://surveys.ons.gov.uk/cookies/", + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( NorthernIrelandBusinessSurveyConfig(), - "https://surveys.ons.gov.uk/cookies/", + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( SocialSurveyConfig(), - "https://rh.ons.gov.uk/cookies/", + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cookies/", ), ], ) @@ -368,7 +371,7 @@ def test_cookie_settings_url_context( (SurveyConfig(), None), ( BusinessSurveyConfig(), - "https://surveys.ons.gov.uk/my-account", + f"{ACCOUNT_SERVICE_BASE_URL}/my-account", ), (SocialSurveyConfig(), None), ], @@ -389,7 +392,7 @@ def test_account_service_my_account_url_context( (SurveyConfig(), None), ( BusinessSurveyConfig(), - "https://surveys.ons.gov.uk/surveys/todo", + f"{ACCOUNT_SERVICE_BASE_URL}/surveys/todo", ), ( SocialSurveyConfig(), @@ -410,18 +413,18 @@ def test_account_service_my_todo_url_context( (SurveyConfig(), None), ( BusinessSurveyConfig(), - "https://surveys.ons.gov.uk/sign-in/logout", + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), (CensusSurveyConfig(), "https://census.gov.uk/en/start"), (WelshCensusSurveyConfig(), "https://cyfrifiad.gov.uk/en/start"), (CensusNISRASurveyConfig(), "https://census.gov.uk/ni"), ( NorthernIrelandBusinessSurveyConfig(), - "https://surveys.ons.gov.uk/sign-in/logout", + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), ( SocialSurveyConfig(), - "https://rh.ons.gov.uk/sign-in/logout", + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/sign-in/logout", ), ], ) @@ -437,20 +440,20 @@ def test_account_service_log_out_url_context( @pytest.mark.parametrize( "theme, language, expected", [ - ("default", "en", SurveyConfig), - ("default", "cy", SurveyConfig), - ("business", "en", BusinessSurveyConfig), - ("business", "cy", BusinessSurveyConfig), - ("health", "en", SurveyConfig), - ("health", "cy", SurveyConfig), - ("social", "en", SocialSurveyConfig), - ("social", "cy", SocialSurveyConfig), - ("northernireland", "en", NorthernIrelandBusinessSurveyConfig), - ("northernireland", "cy", NorthernIrelandBusinessSurveyConfig), - ("census", "en", CensusSurveyConfig), - ("census", "cy", WelshCensusSurveyConfig), - ("census-nisra", "en", CensusNISRASurveyConfig), - ("census-nisra", "cy", CensusNISRASurveyConfig), + (SurveyType.DEFAULT, "en", SurveyConfig), + (SurveyType.DEFAULT, "cy", SurveyConfig), + (SurveyType.BUSINESS, "en", BusinessSurveyConfig), + (SurveyType.BUSINESS, "cy", BusinessSurveyConfig), + (SurveyType.HEALTH, "en", SurveyConfig), + (SurveyType.HEALTH, "cy", SurveyConfig), + (SurveyType.SOCIAL, "en", SocialSurveyConfig), + (SurveyType.SOCIAL, "cy", SocialSurveyConfig), + (SurveyType.NORTHERN_IRELAND, "en", NorthernIrelandBusinessSurveyConfig), + (SurveyType.NORTHERN_IRELAND, "cy", NorthernIrelandBusinessSurveyConfig), + (SurveyType.CENSUS, "en", CensusSurveyConfig), + (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), + (SurveyType.CENSUS_NISRA, "en", CensusNISRASurveyConfig), + (SurveyType.CENSUS_NISRA, "cy", CensusNISRASurveyConfig), (None, None, BusinessSurveyConfig), ], ) @@ -465,9 +468,9 @@ def test_get_survey_config( @pytest.mark.parametrize( "survey_config_type, base_url", [ - (SocialSurveyConfig, "https://rh.ons.gov.uk"), - (SurveyConfig, "http://localhost"), - (BusinessSurveyConfig, "http://localhost"), + (SocialSurveyConfig, ACCOUNT_SERVICE_BASE_URL_SOCIAL), + (SurveyConfig, DEFAULT_URL), + (BusinessSurveyConfig, DEFAULT_URL), ], ) def test_survey_config_base_url_provided_used_in_links( @@ -493,20 +496,20 @@ def test_survey_config_base_url_provided_used_in_links( def test_survey_config_base_url_duplicate_todo(app: Flask): - base_url = "http://localhost/surveys/todo" + base_url = f"{DEFAULT_URL}/surveys/todo" with app.app_context(): result = BusinessSurveyConfig(base_url=base_url) - assert result.base_url == "http://localhost" + assert result.base_url == DEFAULT_URL - assert result.account_service_log_out_url == "http://localhost/sign-in/logout" - assert result.account_service_my_account_url == "http://localhost/my-account" - assert result.account_service_todo_url == "http://localhost/surveys/todo" - assert result.contact_us_url == "http://localhost/contact-us/" - assert result.cookie_settings_url == "http://localhost/cookies/" + assert result.account_service_log_out_url == f"{DEFAULT_URL}/sign-in/logout" + assert result.account_service_my_account_url == f"{DEFAULT_URL}/my-account" + assert result.account_service_todo_url == f"{DEFAULT_URL}/surveys/todo" + assert result.contact_us_url == f"{DEFAULT_URL}/contact-us/" + assert result.cookie_settings_url == f"{DEFAULT_URL}/cookies/" assert ( result.privacy_and_data_protection_url - == "http://localhost/privacy-and-data-protection/" + == f"{DEFAULT_URL}/privacy-and-data-protection/" ) @@ -544,14 +547,14 @@ def test_context_set_from_app_config(app): @pytest.mark.parametrize( "theme, language, expected", [ - ("default", "en", None), - ("business", "en", None), - ("health", "en", None), - ("social", "en", None), - ("northernireland", "en", None), - ("census", "en", "census"), - ("census", "cy", "census"), - ("census-nisra", "en", "census"), + (SurveyType.DEFAULT, "en", None), + (SurveyType.BUSINESS, "en", None), + (SurveyType.HEALTH, "en", None), + (SurveyType.SOCIAL, "en", None), + (SurveyType.NORTHERN_IRELAND, "en", None), + (SurveyType.CENSUS, "en", "census"), + (SurveyType.CENSUS, "cy", "census"), + (SurveyType.CENSUS_NISRA, "en", "census"), ], ) def test_correct_theme_in_context(app: Flask, theme: str, language: str, expected: str): @@ -569,14 +572,14 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte @pytest.mark.parametrize( "theme, language, expected", [ - ("default", "en", "ONS Business Surveys"), - ("business", "en", "ONS Business Surveys"), - ("health", "en", None), - ("social", "en", "ONS Social Surveys"), - ("northernireland", "en", "ONS Business Surveys"), - ("census", "en", "Census 2021"), - ("census", "cy", "Census 2021"), - ("census-nisra", "en", "Census 2021"), + (SurveyType.DEFAULT, "en", "ONS Business Surveys"), + (SurveyType.BUSINESS, "en", "ONS Business Surveys"), + (SurveyType.HEALTH, "en", None), + (SurveyType.SOCIAL, "en", "ONS Social Surveys"), + (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), + (SurveyType.CENSUS, "en", "Census 2021"), + (SurveyType.CENSUS, "cy", "Census 2021"), + (SurveyType.CENSUS_NISRA, "en", "Census 2021"), ], ) def test_correct_survey_title_in_context( @@ -597,37 +600,47 @@ def test_correct_survey_title_in_context( "theme, language, schema, expected", [ ( - "default", + SurveyType.DEFAULT, "en", QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), ( - "default", + SurveyType.DEFAULT, "en", QuestionnaireSchema({"survey_id": "001", "form_type": "test"}), [{"form_type": "test", "survey_id": "001"}], ), ( - "business", + SurveyType.BUSINESS, "en", QuestionnaireSchema( {"survey_id": "001", "form_type": "test", "title": "test_title"} ), [{"form_type": "test", "survey_id": "001", "title": "test_title"}], ), - ("health", "en", QuestionnaireSchema({"survey_id": "001"}), []), - ("social", "en", QuestionnaireSchema({"survey_id": "001"}), []), + (SurveyType.HEALTH, "en", QuestionnaireSchema({"survey_id": "001"}), []), + (SurveyType.SOCIAL, "en", QuestionnaireSchema({"survey_id": "001"}), []), ( - "northernireland", + SurveyType.NORTHERN_IRELAND, "en", QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), - ("census", "en", QuestionnaireSchema({"survey_id": "001"}), [{"nisra": False}]), - ("census", "cy", QuestionnaireSchema({"survey_id": "001"}), [{"nisra": False}]), ( - "census-nisra", + SurveyType.CENSUS, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"nisra": False}], + ), + ( + SurveyType.CENSUS, + "cy", + QuestionnaireSchema({"survey_id": "001"}), + [{"nisra": False}], + ), + ( + SurveyType.CENSUS_NISRA, QuestionnaireSchema({"survey_id": "001"}), "en", [{"nisra": True}], diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index b012879b8f..24b3c73f26 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -11,6 +11,7 @@ from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter import RabbitMQSubmitter from app.utilities.metadata_parser import ( validate_questionnaire_claims, @@ -40,7 +41,7 @@ def parse_metadata(claims, schema_metadata): "user_id": "789473423", "schema_name": "1_0000", "collection_exercise_sid": "test-sid", - "account_service_url": "https://rh.ons.gov.uk/", + "account_service_url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/", "period_id": "2016-02-01", "period_str": "2016-01-01", "ref_p_start_date": "2016-02-02", diff --git a/tests/app/views/contexts/test_submission_metatdata_context.py b/tests/app/views/contexts/test_submission_metatdata_context.py index b99e4f2863..7600d90b79 100644 --- a/tests/app/views/contexts/test_submission_metatdata_context.py +++ b/tests/app/views/contexts/test_submission_metatdata_context.py @@ -2,12 +2,13 @@ from flask import Flask +from app.survey_config.survey_type import SurveyType from app.views.contexts.submission_metadata_context import ( build_submission_metadata_context, ) -SURVEY_TYPE_DEFAULT = "default" -SURVEY_TYPE_SOCIAL = "social" +SURVEY_TYPE_DEFAULT = SurveyType.DEFAULT +SURVEY_TYPE_SOCIAL = SurveyType.SOCIAL SUBMITTED_AT = datetime(2021, 8, 17, 10, 10, 0, tzinfo=timezone.utc) TX_ID = "6b6f90e6-6c27-4c76-8295-7a14e2c4a399" diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 07f445abc5..2ef5b96421 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -9,6 +9,7 @@ from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.settings import VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS +from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_name from app.views.contexts.view_submitted_response_context import ( build_view_submitted_response_context, @@ -22,7 +23,7 @@ def test_build_view_submitted_response_context_summary(app: Flask): with app.app_context(): questionnaire_store = fake_questionnaire_store() context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "default" + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) assert context["summary"]["answers_are_editable"] is False @@ -54,7 +55,7 @@ def test_build_view_submitted_response_context_submitted_text(app: Flask): with app.app_context(): questionnaire_store = fake_questionnaire_store() context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "default" + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) assert context["submitted_text"] == "Answers submitted for Apple" @@ -65,7 +66,7 @@ def test_build_view_submitted_response_context_submitted_text_social(app: Flask) questionnaire_store = fake_questionnaire_store() questionnaire_store.metadata["trad_as"] = "Apple Inc" context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "social" + "en", SCHEMA, questionnaire_store, SurveyType.SOCIAL ) assert context["submitted_text"] == "Answers submitted." @@ -76,7 +77,7 @@ def test_build_view_submitted_response_context_submitted_text_with_trad_as(app: questionnaire_store = fake_questionnaire_store() questionnaire_store.metadata["trad_as"] = "Apple Inc" context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "default" + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) assert ( @@ -94,7 +95,7 @@ def test_view_submitted_response_expired( seconds=VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS ) context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "default" + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) assert context["view_submitted_response"]["expired"] is True @@ -106,7 +107,7 @@ def test_build_view_submitted_response_no_submitted_at(app: Flask): questionnaire_store = fake_questionnaire_store_no_submitted_at() with pytest.raises(Exception): build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, "default" + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) diff --git a/tests/app/views/contexts/test_thank_you_context.py b/tests/app/views/contexts/test_thank_you_context.py index 8273cfb5e2..9274dcd547 100644 --- a/tests/app/views/contexts/test_thank_you_context.py +++ b/tests/app/views/contexts/test_thank_you_context.py @@ -2,11 +2,12 @@ from flask import Flask +from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_name from app.views.contexts.thank_you_context import build_thank_you_context -SURVEY_TYPE_DEFAULT = "default" -SURVEY_TYPE_SOCIAL = "social" +SURVEY_TYPE_DEFAULT = SurveyType.DEFAULT +SURVEY_TYPE_SOCIAL = SurveyType.SOCIAL SUBMITTED_AT = datetime.now(timezone.utc) SCHEMA = load_schema_from_name("test_view_submitted_response", "en") diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 43a48b5ccf..3a9f136de1 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -335,8 +335,8 @@ def assertInHead(self, content): self.assertInSelector(content, "head") # Extra Helper Assertions - def assertInBody(self, content): - self.assertInSelector(content, "body") + def assertInBody(self, contents): + self.assertInSelector(contents, "body") # Extra Helper Assertions def assertNotInHead(self, content): @@ -346,12 +346,15 @@ def assertNotInHead(self, content): def assertNotInBody(self, content): self.assertNotInSelector(content, "body") - def assertInSelector(self, content, selector): - data = self.getHtmlSoup().select(selector) - message = f"\n{content} not in \n{data}" + def assertInSelector(self, contents, selector): + contents = contents if isinstance(contents, list) else [contents] - # intentionally not using assertIn to avoid duplicating the output message - self.assertTrue(content in str(data), msg=message) + for content in contents: + data = self.getHtmlSoup().select(selector) + message = f"\n{content} not in \n{data}" + + # intentionally not using assertIn to avoid duplicating the output message + self.assertTrue(content in str(data), msg=message) def assertAnswerInSummary(self, answer, *, answer_id): # Get answer using data qa diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 835d165e78..1992a64c22 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -1,9 +1,15 @@ -from unittest.mock import patch +from mock import Mock, patch +from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL +from tests.integration.create_token import ACCOUNT_SERVICE_URL from tests.integration.integration_test_case import IntegrationTestCase +DEFAULT_URL = ACCOUNT_SERVICE_URL +BUSINESS_URL = ACCOUNT_SERVICE_BASE_URL +SOCIAL_URL = ACCOUNT_SERVICE_BASE_URL_SOCIAL -class TestErrors(IntegrationTestCase): + +class TestErrors(IntegrationTestCase): # pylint: disable=too-many-public-methods example_payload = { "user_id": "integration-test", "period_str": "April 2016", @@ -80,3 +86,288 @@ def test_errors_500_exception_during_error_handling(self): self.assertStatusCode(500) self.assertInBody("Sorry, there is a problem with this service") + + def test_401_theme_default_cookie_exists(self): + # Given + self.launchSurvey("test_introduction") + self.assertInUrl("/questionnaire/introduction/") + + # When + current_url = self.last_url + self.exit() + self.get(current_url) + + # Then + self.assertStatusUnauthorised() + cookie = self.getCookie() + self.assertEqual(cookie.get("theme"), "default") + self.assertInBody( + f'

    You will need to sign back in to access your account

    ' + ) + + def test_401_theme_social_cookie_exists(self): + # Given + self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.assertInUrl("/questionnaire/radio/") + + # When + current_url = self.last_url + self.saveAndSignOut() + self.get(current_url) + + # Then + self.assertStatusUnauthorised() + cookie = self.getCookie() + self.assertEqual(cookie.get("theme"), "social") + self.assertInBody( + f'

    To access this page you need to re-enter your access code.

    ' + ) + + def test_401_no_cookie(self): + # Given + self.launchSurvey("test_introduction") + self.assertInUrl("/questionnaire/introduction/") + + # When + current_url = self.last_url + self.exit() + self.deleteCookieAndGetUrl(current_url) + + # Then + self.assertStatusUnauthorised() + self.assertInBody( + [ + ( + f'

    If you are completing a business survey, you need to sign back in to your account.

    ' + ), + f'

    If you started your survey using an access code, you need to re-enter your code.

    ', + ] + ) + + def test_403_theme_default_cookie_exists(self): + # Given + self.launchSurvey("test_introduction") + + # When + cookie = self.getUrlAndCookie("/dump/debug") + + # Then + self.assertEqual(cookie.get("theme"), "default") + self.assertStatusForbidden() + self.assertInBody( + f'

    For further help, please contact us.

    ' + ) + + def test_403_theme_social_cookie_exists(self): + # Given + self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + + # When + cookie = self.getUrlAndCookie("/dump/debug") + + # Then + self.assertEqual(cookie.get("theme"), "social") + self.assertStatusForbidden() + self.assertInBody( + f'

    For further help, please contact us.

    ' + ) + + def test_403_no_cookie(self): + # Given + self.launchSurvey("test_introduction") + + # When + token = 123 + self.get(url=f"/session?token={token}") + + # Then + self.assertStatusForbidden() + self.assertInBody( + [ + ( + f'

    If you are completing a business survey and you need further help, please contact us.

    ' + ), + ( + f'

    If you started your survey using an access code and you need further help, please contact us." + ), + ] + ) + + def test_404_theme_default_cookie_exists(self): + # Given + self.launchSurvey("test_introduction") + + # When + cookie = self.getUrlAndCookie("/abc123") + + # Then + self.assertEqual(cookie.get("theme"), "default") + self.assertStatusNotFound() + self.assertInBody( + f'

    If the web address is correct or you selected a link or button, contact us for more help.

    ' + ) + + def test_404_theme_social_cookie_exists(self): + # Given + self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + + # When + cookie = self.getUrlAndCookie("/abc123") + + # Then + self.assertEqual(cookie.get("theme"), "social") + self.assertStatusNotFound() + self.assertInBody( + f'

    If the web address is correct or you selected a link or button, contact us for more help.

    ' + ) + + def test_404_no_cookie(self): + # Given + self.launchSurvey("test_introduction") + + # When + self.deleteCookieAndGetUrl("/abc123") + + # Then + self.assertStatusNotFound() + self.assertInBody( + [ + "

    If the web address is correct or you selected a link or button, please see the following help links.

    ", + f'

    If you are completing a business survey, please contact us.

    ', + f'

    If you started your survey using an access code, please contact us.

    ', + ] + ) + + def test_404_no_cookie_unauthenticated(self): + # Given + self.launchSurvey("test_introduction") + + # When + self.exit() + self.deleteCookieAndGetUrl("/abc123") + + # Then + self.assertStatusNotFound() + self.assertInBody( + [ + "

    If the web address is correct or you selected a link or button, please see the following help links.

    ", + f'

    If you are completing a business survey, please contact us.

    ', + f'

    If you started your survey using an access code, please contact us.

    ', + ] + ) + + def test_500_theme_default_cookie_exists(self): + # Given + self.launchSurvey("test_introduction") + + # When + with patch( + "app.routes.questionnaire.get_block_handler", + side_effect=Exception("You broke it"), + ): + self.post({"answer": "test"}) + cookie = self.getCookie() + + # Then + self.assertEqual(cookie.get("theme"), "default") + self.assertException() + self.assertInBody( + f'

    Contact us if you need to speak to someone about your survey.

    ' + ) + + def test_500_theme_social_cookie_exists(self): + # Given + self.launchSurvey("test_introduction") + + # When + with patch( + "app.routes.questionnaire.get_block_handler", + side_effect=Exception("You broke it"), + ): + self.post({"answer": "test"}) + cookie = self.getCookie() + + # Then + self.assertEqual(cookie.get("theme"), "default") + self.assertException() + self.assertInBody( + f'

    Contact us if you need to speak to someone about your survey.

    ' + ) + + def test_500_theme_census_cookie_exists(self): + # Given + self.launchSurvey("test_thank_you_census_household") + + # When + with patch( + "app.routes.questionnaire.get_block_handler", + side_effect=Exception("You broked it"), + ): + self.post({"answer": "test"}) + + # Then + self.assertException() + self.assertInBody( + "

    If you are completing a business survey and you need to speak to someone about your survey," + f' please contact us.

    ' + ) + + def test_submission_failed_theme_default_cookie_exists(self): + # Given + submitter = self._application.eq["submitter"] + submitter.send_message = Mock(return_value=False) + + # When + self.launchAndFailSubmission("test_introduction") + + # Then + self.assertStatusCode(500) + self.assertInBody( + f'

    If this problem keeps happening, please contact us for help.

    ' + ) + + def test_submission_failed_theme_social_cookie_exists(self): + # Given + submitter = self._application.eq["submitter"] + submitter.send_message = Mock(return_value=False) + + # When + self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.post() + self.post() + self.post() + + # Then + self.assertStatusCode(500) + self.assertInBody( + f'

    If this problem keeps happening, please contact us for help.

    ' + ) + + def test_submission_failed_theme_census_cookie_exists(self): + # Given + submitter = self._application.eq["submitter"] + submitter.send_message = Mock(return_value=False) + + # When + self.launchAndFailSubmission("test_thank_you_census_individual") + + # Then + self.assertStatusCode(500) + self.assertInBody( + f'

    If you are completing a business survey, please contact us.

    ' + ) + + def launchAndFailSubmission(self, schema): + self.launchSurvey(schema) + self.post() + self.post() + self.post() + + def getUrlAndCookie(self, url): + self.get(url=url) + return self.getCookie() + + def deleteCookieAndGetUrl(self, url): + self.deleteCookie() + self.get(url=url) From de423f07e692349409cf292d384e6c3abea3e7ee Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 15 Jul 2022 09:27:08 +0100 Subject: [PATCH 051/567] Add value source for calculations (#882) --- app/forms/questionnaire_form.py | 14 +- app/questionnaire/questionnaire_schema.py | 26 +- ...otal_repeating_with_dependent_section.json | 107 ++++++- ...t_validation_sum_against_value_source.json | 180 +++++++++++ tests/app/forms/test_questionnaire_form.py | 289 +++++++++++++----- tests/app/questionnaire/conftest.py | 5 + .../test_questionnaire_schema.py | 55 +++- ...otal_in_separate_section_repeating.spec.js | 17 ++ .../validation/sum/value_source.spec.js | 152 +++++++++ 9 files changed, 748 insertions(+), 97 deletions(-) create mode 100644 schemas/test/en/test_validation_sum_against_value_source.json create mode 100644 tests/functional/spec/features/validation/sum/value_source.spec.js diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 0457a3ad47..ca38174172 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -55,6 +55,15 @@ def __init__( self.question_errors: dict[str, str] = {} self.options_with_detail_answer: dict = {} self.question_title = self.question.get("title", "") + self.value_source_resolver = ValueSourceResolver( + answer_store=self.answer_store, + schema=self.schema, + metadata=self.metadata, + response_metadata=self.response_metadata, + list_store=self.list_store, + location=self.location, + list_item_id=self.location.list_item_id if self.location else None, + ) super().__init__(**kwargs) @@ -182,7 +191,10 @@ def _get_target_total_and_currency( currency: Optional[str] if "value" in calculation: - calculation_value = calculation["value"] + if isinstance(calculation["value"], dict): + calculation_value = self.value_source_resolver.resolve(calculation["value"]) # type: ignore + else: + calculation_value = calculation["value"] currency = question.get("currency") return calculation_value, currency diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 572a08142a..303051c2aa 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -245,7 +245,7 @@ def _populate_answer_dependencies(self) -> None: for question in self.get_all_questions_for_block(block): if question["type"] == "Calculated": self._update_answer_dependencies_for_calculations( - question["calculations"], + question["calculations"], block_id=block["id"] ) continue @@ -268,19 +268,23 @@ def _update_answer_dependencies_for_calculated_summary( } def _update_answer_dependencies_for_calculations( - self, - calculations: tuple[ImmutableDict[str, Any]], + self, calculations: tuple[ImmutableDict[str, Any]], *, block_id: str ) -> None: for calculation in calculations: - if not (source_answer_id := calculation.get("answer_id")): - continue - dependents = { - self._get_answer_dependent_for_block_id( - block_id=self.get_block_for_answer_id(answer_id)["id"] # type: ignore + if source_answer_id := calculation.get("answer_id"): + dependents = { + self._get_answer_dependent_for_block_id( + block_id=self.get_block_for_answer_id(answer_id)["id"] # type: ignore + ) + for answer_id in calculation["answers_to_calculate"] + } + self._answer_dependencies_map[source_answer_id] |= dependents + + elif isinstance(calculation.get("value"), dict): + self._update_answer_dependencies_for_value_source( + calculation["value"], + block_id=block_id, ) - for answer_id in calculation["answers_to_calculate"] - } - self._answer_dependencies_map[source_answer_id] |= dependents def _update_answer_dependencies_for_answer( self, answer: Mapping, *, block_id: str diff --git a/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json b/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json index eb92271169..ce2a486b0c 100644 --- a/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json +++ b/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json @@ -219,7 +219,7 @@ "id": "total-spending-block", "question": { "id": "total-spending-question", - "title": "What is the maximum spending limit for a household member per month?", + "title": "What is the maximum spending limit for a household member per month (excluding entertainment)?", "type": "General", "answers": [ { @@ -236,6 +236,29 @@ } ] } + }, + { + "type": "Question", + "id": "entertainment-spending-block", + "question": { + "id": "entertainment-spending-question", + "title": "What is the maximum spending limit on entertainment for a household member per month?", + "type": "General", + "answers": [ + { + "id": "entertainment-spending-answer", + "label": "Entertaintment spending", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": 0, + "exclusive": true + } + } + ] + } } ] } @@ -372,7 +395,7 @@ "answers": [ { "id": "spending-breakdown-1", - "label": "Entertaintment", + "label": "Housing", "mandatory": false, "type": "Currency", "currency": "GBP", @@ -380,7 +403,7 @@ }, { "id": "spending-breakdown-2", - "label": "Shopping", + "label": "Transportation", "mandatory": false, "type": "Currency", "currency": "GBP", @@ -388,6 +411,84 @@ }, { "id": "spending-breakdown-3", + "label": "Loans", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-spending-breakdown-block", + "question": { + "id": "second-spending-breakdown-question", + "title": { + "text": "How do you spend the monthly entertainment budget of {entertainment}?", + "placeholders": [ + { + "placeholder": "entertainment", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "answers", + "identifier": "entertainment-spending-answer" + } + } + } + ] + } + ] + }, + "type": "Calculated", + "warning": "These answers must add up to the entertainment budget provided in the spending breakdown question", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "source": "answers", + "identifier": "entertainment-spending-answer" + }, + "answers_to_calculate": [ + "second-spending-breakdown-1", + "second-spending-breakdown-2", + "second-spending-breakdown-3", + "second-spending-breakdown-4" + ], + "conditions": ["equals"] + } + ], + "answers": [ + { + "id": "second-spending-breakdown-1", + "label": "Cinema", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-spending-breakdown-2", + "label": "Concerts", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-spending-breakdown-3", + "label": "Sporting events", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-spending-breakdown-4", "label": "Other", "mandatory": false, "type": "Currency", diff --git a/schemas/test/en/test_validation_sum_against_value_source.json b/schemas/test/en/test_validation_sum_against_value_source.json new file mode 100644 index 0000000000..64726559f7 --- /dev/null +++ b/schemas/test/en/test_validation_sum_against_value_source.json @@ -0,0 +1,180 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated question with value sources test survey", + "theme": "default", + "description": "A survey that tests validation against value sources", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "id": "group", + "title": "Validate sum against answer, calculated summary source", + "blocks": [ + { + "type": "Question", + "id": "total-block", + "question": { + "id": "total-question", + "title": "Total", + "description": ["Enter a number to breakdown in subsequent questions and calculated summary."], + "type": "General", + "answers": [ + { + "id": "total-answer", + "label": "Total", + "mandatory": true, + "type": "Number", + "decimal_places": 2, + "minimum": { + "value": 0, + "exclusive": true + } + } + ] + } + }, + { + "type": "Question", + "id": "breakdown-block", + "question": { + "id": "breakdown-question", + "title": "Breakdown validated against an answer value source", + "description": ["This is a breakdown of the total number from the previous question."], + "type": "Calculated", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "source": "answers", + "identifier": "total-answer" + }, + "answers_to_calculate": ["breakdown-1", "breakdown-2", "breakdown-3", "breakdown-4"], + "conditions": ["equals"] + } + ], + "answers": [ + { + "id": "breakdown-1", + "label": "Breakdown 1", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "breakdown-2", + "label": "Breakdown 2", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "breakdown-3", + "label": "Breakdown 3", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "breakdown-4", + "label": "Breakdown 4", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "number-total-playback", + "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": ["breakdown-1", "breakdown-2"], + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "second-breakdown-block", + "question": { + "id": "second-breakdown-question", + "title": "Breakdown validated against calculated summary value source", + "description": ["This is a breakdown of the grand total from the previous calculated summary."], + "type": "Calculated", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "source": "calculated_summary", + "identifier": "number-total-playback" + }, + "answers_to_calculate": ["second-breakdown-1", "second-breakdown-2", "second-breakdown-3", "second-breakdown-4"], + "conditions": ["equals"] + } + ], + "answers": [ + { + "id": "second-breakdown-1", + "label": "Breakdown 1", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "second-breakdown-2", + "label": "Breakdown 2", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "second-breakdown-3", + "label": "Breakdown 3", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + }, + { + "id": "second-breakdown-4", + "label": "Breakdown 4", + "mandatory": false, + "decimal_places": 2, + "type": "Number" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 1c4b9b22c2..f2d1cf416b 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -5,6 +5,7 @@ from mock import patch from werkzeug.datastructures import MultiDict +from app.data_models import ListStore from app.data_models.answer_store import Answer, AnswerStore from app.forms import error_messages from app.forms.questionnaire_form import generate_form @@ -1094,76 +1095,188 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke assert form.question_errors["breakdown-question"] == "Test Message" -def test_empty_calculated_field(app, answer_store, list_store): - - answer_total = Answer(answer_id="total-answer", value=10) - - answer_store.add_or_update(answer_total) - - with app.test_request_context(): - schema = load_schema_from_name("test_validation_sum_against_total_equal") - - question_schema = schema.get_block("breakdown-block").get("question") - - form_data = MultiDict( +@pytest.mark.parametrize( + "schema_name, block, answers, breakdowns, expected_form_data, question, errors_text, value_dict", # pylint: disable=too-many-locals + [ + [ + "test_validation_sum_against_total_equal", + "breakdown-block", + [Answer(answer_id="total-answer", value=10)], { "breakdown-1": "", "breakdown-2": "5", "breakdown-3": "4", "breakdown-4": "", - } - ) - - expected_form_data = { - "csrf_token": None, - "breakdown-1": None, - "breakdown-2": Decimal("5"), - "breakdown-3": Decimal("4"), - "breakdown-4": None, - } - form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata={}, - response_metadata={}, - form_data=form_data, - ) - - form.validate() - assert form.data == expected_form_data - assert form.question_errors["breakdown-question"] == schema.error_messages[ - "TOTAL_SUM_NOT_EQUALS" - ] % {"total": "10"} - - -def test_sum_calculated_field(app, answer_store, list_store): - answer_total = Answer(answer_id="total-answer", value=10) - - answer_store.add_or_update(answer_total) - - with app.test_request_context(): - schema = load_schema_from_name("test_validation_sum_against_total_equal") - - question_schema = schema.get_block("breakdown-block").get("question") - - form_data = MultiDict( + }, + { + "csrf_token": None, + "breakdown-1": None, + "breakdown-2": Decimal("5"), + "breakdown-3": Decimal("4"), + "breakdown-4": None, + }, + "breakdown-question", + ["TOTAL_SUM_NOT_EQUALS"], + {"total": "10"}, + ], + [ + "test_validation_sum_against_total_equal", + "breakdown-block", + [Answer(answer_id="total-answer", value=10)], { "breakdown-1": "", "breakdown-2": "5", "breakdown-3": "4", "breakdown-4": "1", - } - ) + }, + { + "csrf_token": None, + "breakdown-1": None, + "breakdown-2": Decimal("5"), + "breakdown-3": Decimal("4"), + "breakdown-4": Decimal("1"), + }, + "breakdown-question", + None, + None, + ], + [ + "test_validation_sum_against_total_equal", + "breakdown-block", + [Answer(answer_id="total-answer", value=10)], + { + "breakdown-1": "", + "breakdown-2": "", + "breakdown-3": "", + "breakdown-4": "", + }, + { + "csrf_token": None, + "breakdown-1": None, + "breakdown-2": None, + "breakdown-3": None, + "breakdown-4": None, + }, + "breakdown-question", + ["TOTAL_SUM_NOT_EQUALS"], + {"total": "10"}, + ], + [ + "test_validation_sum_against_value_source", + "breakdown-block", + [Answer(answer_id="total-answer", value=10)], + { + "breakdown-1": "", + "breakdown-2": "5", + "breakdown-3": "4", + "breakdown-4": "1", + }, + { + "csrf_token": None, + "breakdown-1": None, + "breakdown-2": Decimal("5"), + "breakdown-3": Decimal("4"), + "breakdown-4": Decimal("1"), + }, + "breakdown-question", + None, + None, + ], + [ + "test_validation_sum_against_value_source", + "second-breakdown-block", + [ + Answer(answer_id="breakdown-1", value=5), + Answer(answer_id="breakdown-2", value=5), + ], + { + "second-breakdown-1": "", + "second-breakdown-2": "5", + "second-breakdown-3": "4", + "second-breakdown-4": "1", + }, + { + "csrf_token": None, + "second-breakdown-1": None, + "second-breakdown-2": Decimal("5"), + "second-breakdown-3": Decimal("4"), + "second-breakdown-4": Decimal("1"), + }, + "second-breakdown-question", + None, + None, + ], + [ + "test_validation_sum_against_value_source", + "breakdown-block", + [Answer(answer_id="total-answer", value=10)], + { + "breakdown-1": "", + "breakdown-2": "", + "breakdown-3": "4", + "breakdown-4": "1", + }, + { + "csrf_token": None, + "breakdown-1": None, + "breakdown-2": None, + "breakdown-3": Decimal("4"), + "breakdown-4": Decimal("1"), + }, + "breakdown-question", + ["TOTAL_SUM_NOT_EQUALS"], + {"total": "10"}, + ], + [ + "test_validation_sum_against_value_source", + "second-breakdown-block", + [ + Answer(answer_id="breakdown-1", value=5), + Answer(answer_id="breakdown-2", value=5), + ], + { + "second-breakdown-1": "", + "second-breakdown-2": "", + "second-breakdown-3": "4", + "second-breakdown-4": "1", + }, + { + "csrf_token": None, + "second-breakdown-1": None, + "second-breakdown-2": None, + "second-breakdown-3": Decimal("4"), + "second-breakdown-4": Decimal("1"), + }, + "second-breakdown-question", + ["TOTAL_SUM_NOT_EQUALS"], + {"total": "10"}, + ], # pylint: disable=too-many-locals + ], +) +def test_calculated_field( + app, + answer_store, + list_store, + schema_name, + block, + answers, + breakdowns, + expected_form_data, + question, + errors_text, + value_dict, +): + + for answer in answers: + answer_store.add_or_update(answer) + + with app.test_request_context(): + schema = load_schema_from_name(schema_name) + + question_schema = schema.get_block(block).get("question") + + form_data = MultiDict(breakdowns) - expected_form_data = { - "csrf_token": None, - "breakdown-1": None, - "breakdown-2": Decimal("5"), - "breakdown-3": Decimal("4"), - "breakdown-4": Decimal("1"), - } form = generate_form( schema, question_schema, @@ -1176,34 +1289,49 @@ def test_sum_calculated_field(app, answer_store, list_store): form.validate() assert form.data == expected_form_data + if errors_text: + for error_text in errors_text: + assert ( + form.question_errors[question] + == schema.error_messages[error_text] % value_dict + ) + else: + assert len(form.question_errors) == 0 + + +def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_validation_error( + app, answer_store, mocker +): - -def test_get_calculation_total_with_no_input(app, answer_store, list_store): - answer_total = Answer(answer_id="total-answer", value=10) - - answer_store.add_or_update(answer_total) + list_store = ListStore([{"name": "people", "items": ["lCIZsS"]}]) + answer_store.add_or_update( + Answer( + answer_id="entertainment-spending-answer", value=10, list_item_id="lCIZsS" + ) + ) + mocker.patch( + "app.questionnaire.value_source_resolver.ValueSourceResolver._resolve_list_item_id_for_value_source", + return_value="lCIZsS", + ) with app.test_request_context(): - schema = load_schema_from_name("test_validation_sum_against_total_equal") + schema = load_schema_from_name( + "test_validation_sum_against_total_repeating_with_dependent_section" + ) - question_schema = schema.get_block("breakdown-block").get("question") + question_schema = schema.get_block("second-spending-breakdown-block").get( + "question" + ) form_data = MultiDict( { - "breakdown-1": "", - "breakdown-2": "", - "breakdown-3": "", - "breakdown-4": "", + "second-spending-breakdown-1": "", + "second-spending-breakdown-2": "", + "second-spending-breakdown-3": "4", + "second-spending-breakdown-4": "1", } ) - expected_form_data = { - "csrf_token": None, - "breakdown-1": None, - "breakdown-2": None, - "breakdown-3": None, - "breakdown-4": None, - } form = generate_form( schema, question_schema, @@ -1215,10 +1343,9 @@ def test_get_calculation_total_with_no_input(app, answer_store, list_store): ) form.validate() - assert form.data == expected_form_data - assert form.question_errors["breakdown-question"] == schema.error_messages[ - "TOTAL_SUM_NOT_EQUALS" - ] % {"total": "10"} + assert form.question_errors[ + "second-spending-breakdown-question" + ] == schema.error_messages["TOTAL_SUM_NOT_EQUALS"] % {"total": "10"} def test_multi_calculation(app, answer_store, list_store): diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 095bc2da51..bde364c9b5 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1160,6 +1160,11 @@ def calculated_question_with_dependent_sections_schema_repeating(): ) +@pytest.fixture +def calculated_question_with_dependent_sections_schema(): + return load_schema_from_name("test_validation_sum_against_value_source") + + @pytest.fixture def calculated_summary_schema(): return load_schema_from_name("test_calculated_summary") diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index a14b6ebfe1..1498f21ce7 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -421,6 +421,14 @@ def test_answer_dependencies_for_calculated_question_repeating( schema = calculated_question_with_dependent_sections_schema_repeating assert schema.answer_dependencies == { + "entertainment-spending-answer": { + AnswerDependent( + section_id="breakdown-section", + block_id="second-spending-breakdown-block", + for_list="people", + answer_id=None, + ) + }, "total-spending-answer": { AnswerDependent( section_id="breakdown-section", @@ -428,7 +436,52 @@ def test_answer_dependencies_for_calculated_question_repeating( for_list="people", answer_id=None, ) - } + }, + } + + +def test_answer_dependencies_for_calculated_question_value_source( + calculated_question_with_dependent_sections_schema, +): + schema = calculated_question_with_dependent_sections_schema + + assert schema.answer_dependencies == { + "breakdown-1": { + AnswerDependent( + section_id="default-section", + block_id="number-total-playback", + for_list=None, + answer_id=None, + ), + AnswerDependent( + section_id="default-section", + block_id="second-breakdown-block", + for_list=None, + answer_id=None, + ), + }, + "breakdown-2": { + AnswerDependent( + section_id="default-section", + block_id="number-total-playback", + for_list=None, + answer_id=None, + ), + AnswerDependent( + section_id="default-section", + block_id="second-breakdown-block", + for_list=None, + answer_id=None, + ), + }, + "total-answer": { + AnswerDependent( + section_id="default-section", + block_id="breakdown-block", + for_list=None, + answer_id=None, + ) + }, } diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index 03324c4532..ab70c37be4 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -3,10 +3,12 @@ import ListCollectorAddPage from "../../../../generated_pages/validation_sum_aga import ListCollectorSummaryPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/householders-section-summary.page"; import TotalSpendingPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/total-spending-block.page"; +import EntertainmentSpendingPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/entertainment-spending-block.page"; import HouseholdOverviewSectionSummary from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/household-overview-section-summary.page"; import BreakdownDrivingPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/breakdown-driving-block.page"; import SpendingBreakdownPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/spending-breakdown-block.page"; +import EntertainmentBreakdownPage from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/second-spending-breakdown-block.page"; import BreakdownSectionSummary from "../../../../generated_pages/validation_sum_against_total_repeating_with_dependent_section/breakdown-section-summary.page"; import HubPage from "../../../../base_pages/hub.page"; @@ -31,6 +33,11 @@ const answerAndSubmitTotalSpendingQuestion = (total) => { $(TotalSpendingPage.submit()).click(); }; +const answerAndSubmitEntertainmentSpendingQuestion = (total) => { + $(EntertainmentSpendingPage.entertainmentSpending()).setValue(total); + $(EntertainmentSpendingPage.submit()).click(); +}; + const answerAndSubmitSpendingBreakdownQuestion = (breakdown1, breakdown2, breakdown3) => { $(SpendingBreakdownPage.spendingBreakdown1()).setValue(breakdown1); $(SpendingBreakdownPage.spendingBreakdown2()).setValue(breakdown2); @@ -44,6 +51,13 @@ const assertSpendingBreakdownAnswer = (breakdown1, breakdown2, breakdown3) => { expect($(SpendingBreakdownPage.spendingBreakdown3()).getValue()).to.equal(breakdown3); }; +const answerAndSubmitEntertainmentBreakdownQuestion = (breakdown1, breakdown2, breakdown3) => { + $(EntertainmentBreakdownPage.secondSpendingBreakdown1()).setValue(breakdown1); + $(EntertainmentBreakdownPage.secondSpendingBreakdown2()).setValue(breakdown2); + $(EntertainmentBreakdownPage.secondSpendingBreakdown3()).setValue(breakdown3); + $(EntertainmentBreakdownPage.submit()).click(); +}; + const assertRepeatingSectionOnChange = (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { it(`When I click 'Continue with section' on repeating section ${repeatIndex}, Then I should be taken to the spending breakdown question and my previous answers should be prefilled`, () => { $(HubPage.summaryRowLink(repeatingSectionId(repeatIndex))).click(); @@ -80,6 +94,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating // Complete household overview section answerAndSubmitTotalSpendingQuestion(1000); + answerAndSubmitEntertainmentSpendingQuestion(500); $(HouseholdOverviewSectionSummary.submit()).click(); expect($(HubPage.summaryRowState(householderSectionId)).getText()).to.equal("Completed"); @@ -103,6 +118,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating it("When I enter an answer that is equal to the total for the spending question, Then I should be able to get to the section summary and the repeating section should be marked as 'Completed'", () => { answerAndSubmitSpendingBreakdownQuestion(500, 250, 250); + answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); $(BreakdownSectionSummary.submit()).click(); @@ -143,6 +159,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating $(BreakdownDrivingPage.submit()).click(); answerAndSubmitSpendingBreakdownQuestion(1000, 500, 0); + answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); $(BreakdownSectionSummary.submit()).click(); expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js new file mode 100644 index 0000000000..2eaee37214 --- /dev/null +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -0,0 +1,152 @@ +import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/total-block.page"; +import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/breakdown-block.page"; +import TotalPlaybackPage from "../../../../generated_pages/validation_sum_against_value_source/number-total-playback.page"; +import SecondBreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/second-breakdown-block.page"; +import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; + +const answerAndSubmitBreakdownQuestion = (breakdown1, breakdown2, breakdown3, breakdown4) => { + $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); + $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); + $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); + $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); + $(BreakdownAnswerPage.submit()).click(); +}; + +const answerAndSubmitSecondBreakdownQuestion = (breakdown1, breakdown2, breakdown3, breakdown4) => { + $(SecondBreakdownAnswerPage.secondBreakdown1()).setValue(breakdown1); + $(SecondBreakdownAnswerPage.secondBreakdown2()).setValue(breakdown2); + $(SecondBreakdownAnswerPage.secondBreakdown3()).setValue(breakdown3); + $(SecondBreakdownAnswerPage.secondBreakdown4()).setValue(breakdown4); + $(SecondBreakdownAnswerPage.submit()).click(); +}; + +const answerBothBreakdownQuestions = (array1, array2) => { + answerAndSubmitBreakdownQuestion(array1[0], array1[1], array1[2], array1[3]); + + $(TotalPlaybackPage.submit()).click(); + + answerAndSubmitSecondBreakdownQuestion(array2[0], array2[1], array2[2], array2[3]); +}; + +describe("Feature: Sum of grouped answers equal to validation against value source ", () => { + beforeEach(() => { + browser.openQuestionnaire("test_validation_sum_against_value_source.json"); + }); + + describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { + it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the total playback page", () => { + $(TotalAnswerPage.total()).setValue("12"); + $(TotalAnswerPage.submit()).click(); + + answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + + expect(browser.getUrl()).to.contain(TotalPlaybackPage.pageName); + }); + }); + + describe("Given I have a calculated summary value of 12", () => { + it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the summary page", () => { + $(TotalAnswerPage.total()).setValue("12"); + $(TotalAnswerPage.submit()).click(); + + answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + }); + + describe("Given I completed both grouped answer validation questions and I am on the summary", () => { + it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the summary", () => { + $(TotalAnswerPage.total()).setValue("12"); + $(TotalAnswerPage.submit()).click(); + + answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + + $(SubmitPage.totalAnswerEdit()).click(); + $(TotalAnswerPage.total()).setValue("15"); + $(TotalAnswerPage.submit()).click(); + + $(BreakdownAnswerPage.submit()).click(); + + expect($(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + + expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + + answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); + + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + }); + + describe("Given I completed both grouped answer validation questions and I am on the summary", () => { + it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question based on answer value source with valid answers before I can continue", () => { + $(TotalAnswerPage.total()).setValue("12"); + $(TotalAnswerPage.submit()).click(); + + answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + + $(SubmitPage.totalAnswerEdit()).click(); + $(TotalAnswerPage.total()).setValue("15"); + $(TotalAnswerPage.submit()).click(); + + answerAndSubmitBreakdownQuestion("0", "3", "3", "3"); + + expect($(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + + expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + + answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); + + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + }); + + describe("Given I completed both grouped answer validation questions and I am on the summary", () => { + it("When I go back from the summary and change the first breakdown question answers so its total changes, Then I must reconfirm the second breakdown question based on calculated summary value source with valid answers before I can continue", () => { + $(TotalAnswerPage.total()).setValue("12"); + $(TotalAnswerPage.submit()).click(); + + answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + + $(SubmitPage.breakdown1Edit()).click(); + + answerAndSubmitBreakdownQuestion("6", "3", "2", "1"); + + $(TotalPlaybackPage.submit()).click(); + + $(SecondBreakdownAnswerPage.submit()).click(); + + expect($(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + + expect($(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 9"); + + answerAndSubmitSecondBreakdownQuestion("5", "4", "0", "0"); + + expect($(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.false; + + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + }); + + describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { + $(TotalAnswerPage.total()).setValue("5"); + $(TotalAnswerPage.submit()).click(); + + answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + + expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); + }); + }); + + describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { + it("When I enter 3 in each breakdown field and continue to second breakdown and enter 3 in each field, Then I should see a validation error", () => { + $(TotalAnswerPage.total()).setValue("5"); + $(TotalAnswerPage.submit()).click(); + + answerBothBreakdownQuestions(["2", "1", "1", "1"], ["3", "3", "3", "3"]); + + expect($(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 3"); + }); + }); +}); From 94d2dae8a96a7d0023e5eca2974489df9356aadf Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:22:23 +0100 Subject: [PATCH 052/567] Schemas v3.8.0 (#901) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 2272683225..40c06ccb7a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.7.2 +v3.8.0 From 3a19c81ef506c66f21d91285893610b3061d5651 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:12:07 +0100 Subject: [PATCH 053/567] Schemas v3.8.1 (#902) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 40c06ccb7a..32f8572eaf 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.8.0 +v3.8.1 From 365cd7a83c0533d9d0a1827c9da5847bc276875b Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 21 Jul 2022 08:57:02 +0100 Subject: [PATCH 054/567] Schemas v3.8.2 (#905) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 32f8572eaf..a835adcf4c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.8.1 +v3.8.2 From 2037e20faa37d81ee99b90cc8ee3912ed589da03 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 27 Jul 2022 12:56:09 +0100 Subject: [PATCH 055/567] Schemas v3.8.3 (#906) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a835adcf4c..6a39225f1f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.8.2 +v3.8.3 From b7ac9fec0802acb6f82dcbe0bd08b674e66d3ea0 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 29 Jul 2022 12:07:26 +0100 Subject: [PATCH 056/567] Update DS to 53.1.0 (#907) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 5ee343ab30..fc83534a2b 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -53.0.4 +53.1.0 From ebd7b4e5778c1d13d2fd88cba963ab447f48ae70 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 1 Aug 2022 09:17:39 +0100 Subject: [PATCH 057/567] Update DS version for label font weight bug fix (#908) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index fc83534a2b..9b9e2eedff 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -53.1.0 +53.1.1 From e1334b0f82590682114d5611a4d0b288ba541a67 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 1 Aug 2022 10:56:01 +0100 Subject: [PATCH 058/567] Update mutually exclusive options to use multiple (#890) --- app/forms/questionnaire_form.py | 7 +- app/forms/validators.py | 7 +- schemas/test/en/test_mutually_exclusive.json | 2 +- .../en/test_mutually_exclusive_multiple.json | 622 ++++++++++++++++++ .../test_mutually_exclusive_validator.py | 34 +- ...tually_exclusive_multiple_checkbox.spec.js | 224 +++++++ ...usive_multiple_day_month_year_date.spec.js | 207 ++++++ ...ually_exclusive_multiple_textfield.spec.js | 182 +++++ 8 files changed, 1272 insertions(+), 13 deletions(-) create mode 100644 schemas/test/en/test_mutually_exclusive_multiple.json create mode 100644 tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js create mode 100644 tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js create mode 100644 tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index ca38174172..861180cd90 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -164,17 +164,16 @@ def validate_mutually_exclusive_question( question["validation"].get("messages") if "validation" in question else None ) answers = (getattr(self, answer["id"]).data for answer in question["answers"]) - is_only_checkboxes = all( - answer["type"] == "Checkbox" for answer in question["answers"] + is_only_checkboxes_or_radios = all( + answer["type"] in {"Checkbox", "Radio"} for answer in question["answers"] ) - validator = MutuallyExclusiveCheck( messages=messages, question_title=self.question_title, ) try: - validator(answers, is_mandatory, is_only_checkboxes) + validator(answers, is_mandatory, is_only_checkboxes_or_radios) except validators.ValidationError as e: self.question_errors[question["id"]] = str(e) diff --git a/app/forms/validators.py b/app/forms/validators.py index c1e591c0fe..3d9c5db3c7 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -463,7 +463,10 @@ def __init__(self, question_title: str, messages: OptionalMessage = None): self.question_title = question_title def __call__( - self, answer_values: Iterable, is_mandatory: bool, is_only_checkboxes: bool + self, + answer_values: Iterable, + is_mandatory: bool, + is_only_checkboxes_or_radios: bool, ) -> None: total_answered = sum(1 for value in answer_values if value) if total_answered > 1: @@ -471,7 +474,7 @@ def __call__( if is_mandatory and total_answered < 1: message = format_message_with_title( self.messages["MANDATORY_CHECKBOX"] - if is_only_checkboxes + if is_only_checkboxes_or_radios else self.messages["MANDATORY_QUESTION"], self.question_title, ) diff --git a/schemas/test/en/test_mutually_exclusive.json b/schemas/test/en/test_mutually_exclusive.json index 581df9e89f..e691163842 100644 --- a/schemas/test/en/test_mutually_exclusive.json +++ b/schemas/test/en/test_mutually_exclusive.json @@ -65,7 +65,7 @@ }, { "label": "Other", - "description": "Choose any other topping", + "description": "Enter another Nationality", "value": "Other", "detail_answer": { "mandatory": false, diff --git a/schemas/test/en/test_mutually_exclusive_multiple.json b/schemas/test/en/test_mutually_exclusive_multiple.json new file mode 100644 index 0000000000..a4603cc5a2 --- /dev/null +++ b/schemas/test/en/test_mutually_exclusive_multiple.json @@ -0,0 +1,622 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Mutually Exclusive Multiple", + "theme": "default", + "description": "A questionnaire to demo mutually exclusive answers with multiple radio override", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": true + } + } + }, + "sections": [ + { + "id": "mutually-exclusive-checkbox-section", + "title": "Checkbox", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-checkbox-mandatory-group", + "title": "Mutually Exclusive With Multiple Radio Override - Mandatory", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "What is your nationality?", + "warning": "This is important", + "mandatory": true, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": "British", + "value": "British" + }, + { + "label": "Irish", + "value": "Irish" + }, + { + "label": "Other", + "description": "Enter another Nationality", + "value": "Other", + "detail_answer": { + "mandatory": false, + "id": "checkbox-child-other-answer", + "label": "Please specify other", + "type": "TextField" + } + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + }, + { + "label": "I am an alien", + "description": "Some description", + "value": "I am an alien" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-mandatory-date-section", + "title": "Date", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-mandatory-date-group", + "title": "Mutually Exclusive With Multiple Radio Override - Mandatory", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-mandatory-date", + "question": { + "id": "mutually-exclusive-mandatory-date-question", + "type": "MutuallyExclusive", + "title": "When did you leave your last paid job?", + "mandatory": true, + "answers": [ + { + "id": "mandatory-date-answer", + "label": "Enter a date", + "mandatory": false, + "type": "Date" + }, + { + "id": "mandatory-date-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-date-section", + "title": "Date", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-date-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-date", + "question": { + "id": "mutually-exclusive-date-question", + "type": "MutuallyExclusive", + "title": "When did you leave your last paid job?", + "mandatory": false, + "answers": [ + { + "id": "date-answer", + "label": "Enter a date", + "mandatory": false, + "type": "Date" + }, + { + "id": "date-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-currency-section", + "title": "Currency", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-currency-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-currency", + "question": { + "id": "mutually-exclusive-currency-question", + "type": "MutuallyExclusive", + "title": "What is your annual income before tax?", + "mandatory": false, + "answers": [ + { + "id": "currency-answer", + "label": "Enter your income", + "mandatory": false, + "type": "Currency", + "currency": "GBP" + }, + { + "id": "currency-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have no income", + "value": "I have no income" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-number-section", + "title": "Number", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-number-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-number", + "question": { + "id": "mutually-exclusive-number-question", + "type": "MutuallyExclusive", + "title": "What is your favourite number?", + "mandatory": false, + "answers": [ + { + "id": "number-answer", + "label": "Enter your favourite number", + "mandatory": false, + "type": "Number", + "decimal_places": 2 + }, + { + "id": "number-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I dont have a favourite number", + "value": "I dont have a favourite number" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-percentage-section", + "title": "Percentage", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-percentage-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-percentage", + "question": { + "id": "mutually-exclusive-percentage-question", + "type": "MutuallyExclusive", + "title": "What was the percentage increase in your annual income this tax year?", + "mandatory": false, + "answers": [ + { + "id": "percentage-answer", + "label": "Enter the percentage increase of your income", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + } + }, + { + "id": "percentage-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "No income change", + "value": "No income change" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-textfield-section", + "title": "Textfield", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-textfield-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-textfield", + "question": { + "id": "mutually-exclusive-textfield-question", + "type": "MutuallyExclusive", + "title": "What is your favourite colour?", + "mandatory": false, + "answers": [ + { + "id": "textfield-answer", + "label": "Enter your favourite colour", + "mandatory": false, + "type": "TextField" + }, + { + "id": "textfield-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I dont have a favorite colour", + "value": "I dont have a favorite colour" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-month-year-date-section", + "title": "Month Year Date", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-month-year-date-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-month-year-date", + "question": { + "id": "mutually-exclusive-month-year-date-question", + "type": "MutuallyExclusive", + "title": "When did you leave your last paid job?", + "mandatory": false, + "answers": [ + { + "id": "month-year-date-answer", + "label": "Enter a date", + "mandatory": false, + "type": "MonthYearDate", + "maximum": { + "value": "now" + } + }, + { + "id": "month-year-date-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-year-date-section", + "title": "Year Date", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-year-date-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-year-date", + "question": { + "id": "mutually-exclusive-year-date-question", + "type": "MutuallyExclusive", + "title": "When did you leave your last paid job?", + "mandatory": false, + "answers": [ + { + "id": "year-date-answer", + "label": "Enter a date", + "mandatory": false, + "type": "YearDate", + "maximum": { + "value": "now" + } + }, + { + "id": "year-date-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-unit-section", + "title": "Unit", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-unit-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-unit", + "question": { + "id": "mutually-exclusive-unit-question", + "type": "MutuallyExclusive", + "title": "How many years have you been in the UK?", + "mandatory": false, + "answers": [ + { + "id": "unit-answer", + "label": "Enter the number of years you have lived in the UK", + "unit": "duration-year", + "type": "Unit", + "unit_length": "long", + "mandatory": false + }, + { + "id": "unit-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never lived in the UK", + "value": "I have never lived in the UK" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-duration-section", + "title": "Duration", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-duration-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-duration", + "question": { + "id": "mutually-exclusive-duration-question", + "type": "MutuallyExclusive", + "title": "How long have you been employed for?", + "mandatory": false, + "answers": [ + { + "id": "duration-answer", + "mandatory": false, + "units": ["years", "months"], + "type": "Duration" + }, + { + "id": "duration-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "mutually-exclusive-textarea-section", + "title": "TextArea", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "mutually-exclusive-textarea-group", + "title": "Mutually Exclusive With Multiple Radio Override - Optional", + "blocks": [ + { + "type": "Question", + "id": "mutually-exclusive-textarea", + "question": { + "id": "mutually-exclusive-textarea-question", + "type": "MutuallyExclusive", + "title": "Why did you leave your last job?", + "mandatory": false, + "answers": [ + { + "id": "textarea-answer", + "mandatory": false, + "type": "TextArea" + }, + { + "id": "textarea-exclusive-answer", + "mandatory": false, + "type": "Radio", + "options": [ + { + "label": "I prefer not to say", + "value": "I prefer not to say" + }, + { + "label": "I have never worked", + "value": "I have never worked" + } + ] + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/forms/validation/test_mutually_exclusive_validator.py b/tests/app/forms/validation/test_mutually_exclusive_validator.py index f0dba36152..edfa25efc7 100644 --- a/tests/app/forms/validation/test_mutually_exclusive_validator.py +++ b/tests/app/forms/validation/test_mutually_exclusive_validator.py @@ -6,7 +6,7 @@ @pytest.mark.parametrize( - "answer_permutations, is_mandatory, is_only_checkboxes, error_type", + "answer_permutations, is_mandatory, is_only_checkboxes_or_radios, error_type", ( ([[], []], True, True, "MANDATORY_CHECKBOX"), ([None, []], True, True, "MANDATORY_CHECKBOX"), @@ -22,17 +22,32 @@ ), (["123", ["I prefer not to say"]], True, True, "MUTUALLY_EXCLUSIVE"), (["2018-09-01", ["I prefer not to say"]], True, True, "MUTUALLY_EXCLUSIVE"), + (["", None], True, True, "MANDATORY_CHECKBOX"), + ( + [["British, Irish"], "I prefer not to say"], + True, + True, + "MUTUALLY_EXCLUSIVE", + ), + ( + ["British, Irish", "I prefer not to say"], + True, + True, + "MUTUALLY_EXCLUSIVE", + ), + (["123", "I prefer not to say"], True, True, "MUTUALLY_EXCLUSIVE"), + (["2018-09-01", "I prefer not to say"], True, True, "MUTUALLY_EXCLUSIVE"), ), ) -def test_mutually_exclusive_mandatory_checkbox_raises_ValidationError( - answer_permutations, is_mandatory, is_only_checkboxes, error_type +def test_mutually_exclusive_mandatory_answers_raise_validation_error( + answer_permutations, is_mandatory, is_only_checkboxes_or_radios, error_type ): validator = MutuallyExclusiveCheck(question_title="") with pytest.raises(ValidationError) as exc: validator( answer_values=iter(answer_permutations), is_mandatory=is_mandatory, - is_only_checkboxes=is_only_checkboxes, + is_only_checkboxes_or_radios=is_only_checkboxes_or_radios, ) assert format_message_with_title(error_messages[error_type], "") == str(exc.value) @@ -50,12 +65,19 @@ def test_mutually_exclusive_mandatory_checkbox_raises_ValidationError( ([[], []], False), ([None, []], False), (["", []], False), + (["", None], False), + ([None, None], False), + ([None, ""], False), + ([["British, Irish"], None], True), + (["British, Irish", None], True), + ([None, "I prefer not to say"], True), + (["", "I prefer not to say"], True), ), ) -def test_mutually_exclusive_mandatory_checkbox(answer_permutations, is_mandatory): +def test_mutually_exclusive_mandatory_answers(answer_permutations, is_mandatory): validator = MutuallyExclusiveCheck(question_title="") validator( answer_values=iter(answer_permutations), is_mandatory=is_mandatory, - is_only_checkboxes=True, + is_only_checkboxes_or_radios=True, ) diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js new file mode 100644 index 0000000000..9099aaa8ac --- /dev/null +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js @@ -0,0 +1,224 @@ +import MandatoryCheckboxPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-checkbox.page"; +import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-checkbox-section-summary.page"; + +describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", () => { + beforeEach(() => { + browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + }); + + describe("Given the user has clicked multiple non-exclusive options", () => { + beforeEach(() => { + // Given + $(MandatoryCheckboxPage.checkboxBritish()).click(); + $(MandatoryCheckboxPage.checkboxIrish()).click(); + $(MandatoryCheckboxPage.checkboxOther()).click(); + $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); + + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); + }); + + it("When then user clicks the first mutually exclusive option, Then only the first mutually exclusive option should be checked.", () => { + // When + $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + + // Then + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + + $(MandatoryCheckboxPage.submit()).click(); + + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + }); + + it("When then user clicks the second mutually exclusive option, Then only the second mutually exclusive option should be checked.", () => { + // When + $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + // Then + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + + $(MandatoryCheckboxPage.submit()).click(); + + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + }); + }); + + describe("Given the user has clicked the first mutually exclusive option", () => { + it("When the user returns to the question, Then the mutually exclusive option should remain checked.", () => { + // Given + $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + $(MandatoryCheckboxPage.submit()).click(); + + // When + $(SummaryPage.previous()).click(); + + // Then + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + }); + }); + + describe("Given the user has clicked the second mutually exclusive option", () => { + it("When the user returns to the question, Then the mutually exclusive option should remain checked.", () => { + // Given + $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + $(MandatoryCheckboxPage.submit()).click(); + + // When + $(SummaryPage.previous()).click(); + + // Then + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + }); + }); + + describe("Given the user has clicked the first mutually exclusive option", () => { + it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", () => { + // Given + $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + + // When + $(MandatoryCheckboxPage.checkboxBritish()).click(); + $(MandatoryCheckboxPage.checkboxIrish()).click(); + + // Then + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + + $(MandatoryCheckboxPage.submit()).click(); + + expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + }); + }); + + describe("Given the user has clicked the second mutually exclusive option", () => { + it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", () => { + // Given + $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + + // When + $(MandatoryCheckboxPage.checkboxBritish()).click(); + $(MandatoryCheckboxPage.checkboxIrish()).click(); + + // Then + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + + $(MandatoryCheckboxPage.submit()).click(); + + expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + }); + }); + + describe("Given the user has not clicked a mutually exclusive option", () => { + it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", () => { + // Given + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + + // When + $(MandatoryCheckboxPage.checkboxBritish()).click(); + $(MandatoryCheckboxPage.checkboxIrish()).click(); + + // Then + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + + $(MandatoryCheckboxPage.submit()).click(); + + expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + }); + }); + + describe("Given the user has not clicked any of the non-exclusive options", () => { + beforeEach(() => { + // Given + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + }); + it("When the user clicks the first mutually exclusive option, Then only the first exclusive option should be checked.", () => { + // When + $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + $(MandatoryCheckboxPage.submit()).click(); + + // Then + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I am an alien"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + }); + it("When the user clicks the second mutually exclusive option, Then only the second exclusive option should be checked.", () => { + // When + $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + $(MandatoryCheckboxPage.submit()).click(); + + // Then + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + }); + }); + + describe("Given the user has clicked a mutually exclusive option", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + // Given + $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + + // When + $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + $(MandatoryCheckboxPage.submit()).click(); + + // Then + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + }); + }); + + describe("Given the user has not clicked any options and the question is mandatory", () => { + it("When the user clicks the Continue button, Then a validation error message should be displayed.", () => { + // Given + expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + + // When + $(MandatoryCheckboxPage.submit()).click(); + + // Then + expect($(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + expect($(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); + expect($(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + }); + }); +}); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js new file mode 100644 index 0000000000..3ea0baca8c --- /dev/null +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js @@ -0,0 +1,207 @@ +import DatePage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-date.page"; +import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-date-section-summary.page"; + +describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio Override", () => { + beforeEach(() => { + browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + browser.url("/questionnaire/mutually-exclusive-date"); + }); + + describe("Given the user has entered a value for the non-exclusive month year date answer", () => { + beforeEach(() => { + // Given + $(DatePage.dateday()).setValue("17"); + $(DatePage.datemonth()).setValue("3"); + $(DatePage.dateyear()).setValue("2018"); + expect($(DatePage.dateday()).getValue()).to.contain("17"); + expect($(DatePage.datemonth()).getValue()).to.contain("3"); + expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + }); + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + // When + $(DatePage.dateExclusiveIPreferNotToSay()).click(); + + // Then + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + expect($(DatePage.dateday()).getValue()).to.contain(""); + expect($(DatePage.datemonth()).getValue()).to.contain(""); + expect($(DatePage.dateyear()).getValue()).to.contain(""); + + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + }); + + it("When then user clicks the second mutually exclusive radio answer, Then only the second mutually exclusive radio should be answered.", () => { + // When + $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + + // Then + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(DatePage.dateday()).getValue()).to.contain(""); + expect($(DatePage.datemonth()).getValue()).to.contain(""); + expect($(DatePage.dateyear()).getValue()).to.contain(""); + + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + }); + }); + + describe("Given the user has clicked the first mutually exclusive radio answer", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + // Given + $(DatePage.dateExclusiveIPreferNotToSay()).click(); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + // When + $(DatePage.dateday()).setValue("17"); + $(DatePage.datemonth()).setValue("3"); + $(DatePage.dateyear()).setValue("2018"); + + // Then + expect($(DatePage.dateday()).getValue()).to.contain("17"); + expect($(DatePage.datemonth()).getValue()).to.contain("3"); + expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + }); + }); + + describe("Given the user has clicked the second mutually exclusive radio answer", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + // Given + $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + // When + $(DatePage.dateday()).setValue("17"); + $(DatePage.datemonth()).setValue("3"); + $(DatePage.dateyear()).setValue("2018"); + + // Then + expect($(DatePage.dateday()).getValue()).to.contain("17"); + expect($(DatePage.datemonth()).getValue()).to.contain("3"); + expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + }); + }); + + describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { + it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", () => { + // Given + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + // When + $(DatePage.dateday()).setValue("17"); + $(DatePage.datemonth()).setValue("3"); + $(DatePage.dateyear()).setValue("2018"); + + // Then + expect($(DatePage.dateday()).getValue()).to.contain("17"); + expect($(DatePage.datemonth()).getValue()).to.contain("3"); + expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + $(DatePage.submit()).click(); + expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + }); + }); + + describe("Given the user has not answered the non-exclusive month year date answer", () => { + beforeEach(() => { + // Given + expect($(DatePage.dateday()).getValue()).to.contain(""); + expect($(DatePage.datemonth()).getValue()).to.contain(""); + expect($(DatePage.dateyear()).getValue()).to.contain(""); + }); + it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", () => { + // When + $(DatePage.dateExclusiveIPreferNotToSay()).click(); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + // Then + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + }); + + it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", () => { + // When + $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + // Then + $(DatePage.submit()).click(); + + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + }); + }); + + describe("Given the user has not answered the question and the question is optional", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + // Given + expect($(DatePage.dateday()).getValue()).to.contain(""); + expect($(DatePage.datemonth()).getValue()).to.contain(""); + expect($(DatePage.dateyear()).getValue()).to.contain(""); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + // When + $(DatePage.submit()).click(); + + // Then + expect($(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); + }); + }); + + describe("Given the user has clicked a mutually exclusive option", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + // Given + $(DatePage.dateExclusiveIPreferNotToSay()).click(); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + // When + $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + $(DatePage.submit()).click(); + + // Then + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + }); + }); +}); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js new file mode 100644 index 0000000000..a3231d4813 --- /dev/null +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js @@ -0,0 +1,182 @@ +import TextFieldPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-textfield.page"; +import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-textfield-section-summary.page"; + +describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", () => { + beforeEach(() => { + browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + browser.url("/questionnaire/mutually-exclusive-textfield"); + }); + + describe("Given the user has entered a value for the non-exclusive textfield answer", () => { + beforeEach(() => { + // Given + $(TextFieldPage.textfield()).setValue("Blue"); + expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + }); + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + // When + $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + + // Then + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + }); + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + // When + $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + + // Then + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + }); + }); + + describe("Given the user has clicked the first mutually exclusive checkbox answer", () => { + it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", () => { + // Given + $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + // When + $(TextFieldPage.textfield()).setValue("Blue"); + + // Then + expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + }); + }); + + describe("Given the user has clicked the second mutually exclusive checkbox answer", () => { + it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", () => { + // Given + $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + + // When + $(TextFieldPage.textfield()).setValue("Blue"); + + // Then + expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + }); + }); + + describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { + it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", () => { + // Given + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + // When + $(TextFieldPage.textfield()).setValue("Blue"); + + // Then + expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + }); + }); + + describe("Given the user has not answered the non-exclusive textfield answer", () => { + beforeEach(() => { + // Given + expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + }); + it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", () => { + // When + $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + // Then + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + }); + it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", () => { + // When + $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + // Then + $(TextFieldPage.submit()).click(); + + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + }); + }); + + describe("Given the user has not answered the question and the question is optional", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + // Given + expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + // When + $(TextFieldPage.submit()).click(); + + // Then + expect($(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); + }); + }); + + describe("Given the user has clicked a mutually exclusive option", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + // Given + $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + + // When + $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + $(TextFieldPage.submit()).click(); + + // Then + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + }); + }); +}); From fc46c327c3e6241f4cc523283b486ab8388e3013 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 2 Aug 2022 09:38:24 +0100 Subject: [PATCH 059/567] Remove metadata from session (#900) --- app/authentication/authenticator.py | 13 ++++-- app/data_models/session_data.py | 26 ++++++----- app/helpers/language_helper.py | 19 ++++---- app/helpers/schema_helpers.py | 15 ++++--- app/helpers/template_helpers.py | 10 ++--- app/routes/dump.py | 11 +++-- app/routes/flush.py | 2 +- app/routes/individual_response.py | 15 +++---- app/routes/questionnaire.py | 24 +++++----- app/routes/session.py | 2 +- app/setup.py | 6 ++- app/utilities/schema.py | 16 +++---- app/views/contexts/thank_you_context.py | 22 +++++----- app/views/handlers/confirm_email.py | 12 +++-- app/views/handlers/feedback.py | 4 +- app/views/handlers/thank_you.py | 8 +++- tests/app/conftest.py | 34 +++++++++++++- tests/app/data_model/test_session_store.py | 21 +++------ tests/app/helpers/test_schema_helper.py | 9 +++- tests/app/utilities/test_schema.py | 16 +++++++ tests/app/views/contexts/conftest.py | 24 ++++++---- .../views/contexts/test_thank_you_context.py | 44 +++++++++++-------- tests/app/views/handlers/conftest.py | 29 ------------ ...t_confirmation_email_fulfilment_request.py | 12 +++-- 24 files changed, 231 insertions(+), 163 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 14e3c408e5..61031fe5d3 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -13,7 +13,12 @@ from app.authentication.user import User from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore -from app.globals import create_session_store, get_questionnaire_store, get_session_store +from app.globals import ( + create_session_store, + get_metadata, + get_questionnaire_store, + get_session_store, +) from app.keys import KEY_PURPOSE_AUTHENTICATION from app.settings import EQ_SESSION_ID, USER_IK @@ -103,8 +108,10 @@ def load_user(extend_session: bool = True) -> Optional[User]: user_ik = cookie_session.get(USER_IK) user = User(user_id, user_ik) - if session_store.session_data and session_store.session_data.tx_id: - logger.bind(tx_id=session_store.session_data.tx_id) + metadata = get_metadata(user) + + if metadata and (tx_id := metadata.get("tx_id")): + logger.bind(tx_id=tx_id) if extend_session: _extend_session_expiry(session_store) diff --git a/app/data_models/session_data.py b/app/data_models/session_data.py index d7b35ff524..31a3647098 100644 --- a/app/data_models/session_data.py +++ b/app/data_models/session_data.py @@ -4,15 +4,15 @@ class SessionData: def __init__( self, - tx_id: Optional[str], - schema_name: Optional[str], - period_str: Optional[str], - language_code: Optional[str], - launch_language_code: Optional[str], - ru_name: Optional[str], - ru_ref: Optional[str], - response_id: Optional[str], - case_id: Optional[str], + tx_id: Optional[str] = None, + schema_name: Optional[str] = None, + period_str: Optional[str] = None, + language_code: Optional[str] = None, + launch_language_code: Optional[str] = None, + ru_name: Optional[str] = None, + ru_ref: Optional[str] = None, + response_id: Optional[str] = None, + case_id: Optional[str] = None, case_ref: Optional[str] = None, account_service_base_url: Optional[str] = None, account_service_log_out_url: Optional[str] = None, @@ -24,10 +24,14 @@ def __init__( survey_url: Optional[str] = None, # pylint: disable=unused-argument **_: Any, ): # pylint: disable=too-many-locals + self.language_code = language_code + self.confirmation_email_count = confirmation_email_count + self.feedback_count = feedback_count + + # :TODO: Will be removed in the next deployment, kept temporarily to support in-flight sessions and potential rollback self.tx_id = tx_id self.schema_name = schema_name self.period_str = period_str - self.language_code = language_code self.launch_language_code = launch_language_code self.ru_name = ru_name self.ru_ref = ru_ref @@ -38,8 +42,6 @@ def __init__( self.account_service_base_url = account_service_base_url self.account_service_log_out_url = account_service_log_out_url self.display_address = display_address - self.confirmation_email_count = confirmation_email_count - self.feedback_count = feedback_count self.schema_url = schema_url # :TODO: Remove once `schema_url` has been rolled out successfully. diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 0d5ab20947..502360a430 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -1,9 +1,10 @@ -from typing import Optional, Union +from typing import Any, Mapping, Optional, Union from urllib.parse import urlencode from flask import g, request +from flask_login import current_user -from app.globals import get_session_store +from app.globals import get_metadata, get_session_store from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import get_allowed_languages @@ -13,14 +14,16 @@ } -def handle_language() -> None: +def handle_language(metadata: Optional[Mapping[str, Any]] = None) -> None: session_store = get_session_store() - if session_store and (session_data := session_store.session_data): - launch_language = session_data.launch_language_code or DEFAULT_LANGUAGE_CODE + + if session_store and session_store.session_data: + metadata = metadata or get_metadata(current_user) or {} + schema_name = metadata["schema_name"] + + launch_language = metadata.get("language_code") or DEFAULT_LANGUAGE_CODE # pylint: disable=assigning-non-slot - g.allowed_languages = get_allowed_languages( - session_data.schema_name, launch_language - ) + g.allowed_languages = get_allowed_languages(schema_name, launch_language) request_language = request.args.get("language_code") if request_language and request_language in g.allowed_languages: session_store.session_data.language_code = request_language diff --git a/app/helpers/schema_helpers.py b/app/helpers/schema_helpers.py index 1680b6c8f7..6398b820eb 100644 --- a/app/helpers/schema_helpers.py +++ b/app/helpers/schema_helpers.py @@ -1,10 +1,11 @@ from functools import wraps from typing import Any, Callable +from flask_login import current_user from werkzeug.exceptions import Unauthorized -from app.globals import get_session_store -from app.utilities.schema import load_schema_from_session_data +from app.globals import get_metadata, get_session_store +from app.utilities.schema import load_schema_from_metadata def with_schema(function: Callable) -> Any: @@ -24,11 +25,15 @@ def get_block(routing_path, schema, *args): @wraps(function) def wrapped_function(*args: Any, **kwargs: Any) -> Any: session_store = get_session_store() - if not session_store: + if not session_store or not session_store.session_data: raise Unauthorized - session_data = session_store.session_data - schema = load_schema_from_session_data(session_data) + metadata = get_metadata(current_user) or {} + language_code = session_store.session_data.language_code + + schema = load_schema_from_metadata( + metadata=metadata, language_code=language_code + ) return function(schema, *args, **kwargs) return wrapped_function diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index e3f027cf6c..f8e3b92254 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -23,7 +23,7 @@ WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType -from app.utilities.schema import load_schema_from_session_data +from app.utilities.schema import load_schema_from_metadata class ContextHelper: @@ -200,11 +200,11 @@ def get_survey_config( ) -> SurveyConfig: # The fallback to assigning SURVEY_TYPE to theme is only being added until # business feedback on the differentiation between theme and SURVEY_TYPE. - if session_store := get_session_store(): - if session_data := session_store.session_data: - schema = load_schema_from_session_data(session_data) - language = language or get_locale().language + if metadata := get_metadata(current_user): + language = language or get_locale().language + schema = load_schema_from_metadata(metadata=metadata, language_code=language) + survey_theme = theme or get_survey_type() base_url = base_url or ( diff --git a/app/routes/dump.py b/app/routes/dump.py index 91ed88cfc3..81e9d0462e 100644 --- a/app/routes/dump.py +++ b/app/routes/dump.py @@ -1,14 +1,15 @@ from functools import wraps from flask import Blueprint, g +from flask_babel import get_locale from flask_login import current_user, login_required from app.authentication.roles import role_required -from app.globals import get_questionnaire_store, get_session_store +from app.globals import get_metadata, get_questionnaire_store from app.helpers.session_helpers import with_questionnaire_store from app.questionnaire.router import Router from app.utilities.json import json_dumps -from app.utilities.schema import load_schema_from_session_data +from app.utilities.schema import load_schema_from_metadata from app.views.handlers.submission import SubmissionHandler dump_blueprint = Blueprint("dump", __name__) @@ -17,9 +18,11 @@ def requires_schema(func): @wraps(func) def wrapper(*args, **kwargs): - session = get_session_store() # pylint: disable=assigning-non-slot - g.schema = load_schema_from_session_data(session.session_data) + metadata = get_metadata(current_user) + g.schema = load_schema_from_metadata( + metadata=metadata, language_code=get_locale().language + ) result = func(g.schema, *args, **kwargs) return result diff --git a/app/routes/flush.py b/app/routes/flush.py index 7aeee29536..d4b6c8322c 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -60,7 +60,7 @@ def _submit_data(user): progress_store = questionnaire_store.progress_store list_store = questionnaire_store.list_store submitted_at = datetime.now(timezone.utc) - schema = load_schema_from_metadata(metadata) + schema = load_schema_from_metadata(metadata=metadata) router = Router( schema, diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index 5fb9334f4b..0f8576826b 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -1,5 +1,5 @@ from flask import Blueprint, g, redirect, request, url_for -from flask_babel import lazy_gettext +from flask_babel import get_locale, lazy_gettext from flask_login import current_user, login_required from itsdangerous import BadSignature from structlog import get_logger @@ -14,7 +14,7 @@ from app.helpers.schema_helpers import with_schema from app.helpers.session_helpers import with_questionnaire_store from app.helpers.template_helpers import render_template -from app.utilities.schema import load_schema_from_session_data +from app.utilities.schema import load_schema_from_metadata from app.views.handlers.individual_response import ( IndividualResponseChangeHandler, IndividualResponseHandler, @@ -60,11 +60,13 @@ def before_individual_response_request(): "individual-response request", method=request.method, url_path=request.full_path ) - handle_language() + handle_language(metadata) - session_store = get_session_store() # pylint: disable=assigning-non-slot - g.schema = load_schema_from_session_data(session_store.session_data) + g.schema = load_schema_from_metadata( + metadata=questionnaire_store.metadata, + language_code=get_locale().language, + ) @individual_response_blueprint.route("/", methods=["GET"]) @@ -91,7 +93,6 @@ def request_individual_response(schema, questionnaire_store): @with_schema def individual_response_how(schema, questionnaire_store, list_item_id): language_code = get_session_store().session_data.language_code - individual_response_handler = IndividualResponseHowHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -134,7 +135,6 @@ def individual_response_change(schema, questionnaire_store, list_item_id): @with_schema def individual_response_post_address_confirm(schema, questionnaire_store, list_item_id): language_code = get_session_store().session_data.language_code - try: individual_response_handler = IndividualResponsePostAddressConfirmHandler( schema=schema, @@ -184,7 +184,6 @@ def individual_response_post_address_confirmation(schema, questionnaire_store): @with_schema def individual_response_who(schema, questionnaire_store): language_code = get_session_store().session_data.language_code - individual_response_handler = IndividualResponseWhoHandler( schema=schema, questionnaire_store=questionnaire_store, diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index b907e2d372..1e6f9e713f 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -6,6 +6,7 @@ from flask import Blueprint, g, redirect, request, send_file from flask import session as cookie_session from flask import url_for +from flask_babel import get_locale from flask_login import current_user, login_required from itsdangerous import BadSignature from structlog import get_logger @@ -19,7 +20,6 @@ from app.globals import ( get_metadata, get_questionnaire_store, - get_session_store, get_session_timeout_in_seconds, ) from app.helpers import url_safe_serializer @@ -31,7 +31,7 @@ from app.questionnaire.location import InvalidLocationException from app.questionnaire.router import Router from app.submitter.previously_submitted_exception import PreviouslySubmittedException -from app.utilities.schema import load_schema_from_session_data +from app.utilities.schema import load_schema_from_metadata from app.views.contexts import HubContext from app.views.handlers.block_factory import get_block_handler from app.views.handlers.confirm_email import ConfirmEmail @@ -95,11 +95,12 @@ def before_questionnaire_request(): "questionnaire request", method=request.method, url_path=request.full_path ) - handle_language() + handle_language(metadata) - session_store = get_session_store() # pylint: disable=assigning-non-slot - g.schema = load_schema_from_session_data(session_store.session_data) + g.schema = load_schema_from_metadata( + metadata=metadata, language_code=get_locale().language + ) @post_submission_blueprint.before_request @@ -119,14 +120,17 @@ def before_post_submission_request(): if not questionnaire_store.submitted_at: raise NotFound - handle_language() + handle_language(metadata) - session_store = get_session_store() - session_data = session_store.session_data # pylint: disable=assigning-non-slot - g.schema = load_schema_from_session_data(session_data) + g.schema = load_schema_from_metadata( + metadata=metadata, language_code=get_locale().language + ) - logger.bind(tx_id=session_data.tx_id, schema_name=session_data.schema_name) + logger.bind( + tx_id=metadata.get("tx_id"), + schema_name=metadata.get("schema_name"), + ) logger.info( "questionnaire request", method=request.method, url_path=request.full_path diff --git a/app/routes/session.py b/app/routes/session.py index f841013faa..0e036bb30c 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -55,7 +55,7 @@ def login(): except ValidationError as e: raise InvalidTokenException("Invalid runner claims") from e # pylint: disable=assigning-non-slot - g.schema = load_schema_from_metadata(runner_claims) + g.schema = load_schema_from_metadata(metadata=runner_claims) schema_metadata = g.schema.json["metadata"] try: diff --git a/app/setup.py b/app/setup.py index e3ce2637f1..7cfd7fc193 100644 --- a/app/setup.py +++ b/app/setup.py @@ -440,7 +440,11 @@ def setup_babel(application): @application.babel.localeselector def get_locale(): # pylint: disable=unused-variable session = get_session_store() - return session.session_data.language_code if session else None + + if session and (session_data := session.session_data): + return session_data.language_code + + return None @application.babel.timezoneselector def get_timezone(): # pylint: disable=unused-variable diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 94357378be..c9d322bf66 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -3,7 +3,7 @@ from functools import lru_cache from glob import glob from pathlib import Path -from typing import Mapping, Optional +from typing import Any, Mapping, Optional import requests from requests import RequestException @@ -98,14 +98,18 @@ def get_allowed_languages(schema_name, launch_language): return [DEFAULT_LANGUAGE_CODE] -def load_schema_from_metadata(metadata): +def load_schema_from_metadata( + metadata: Mapping[str, Any], *, language_code: Optional[str] = None +) -> QuestionnaireSchema: + metadata = metadata or {} + language_code = language_code or metadata.get("language_code") if schema_url := metadata.get("schema_url"): # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. # This should not be used in production. start = time.time() - schema = load_schema_from_url(schema_url, metadata.get("language_code")) + schema = load_schema_from_url(schema_url, language_code) duration_in_milliseconds = (time.time() - start) * 1_000 cache_info = ( @@ -122,14 +126,10 @@ def load_schema_from_metadata(metadata): return schema return load_schema_from_name( - metadata.get("schema_name"), language_code=metadata.get("language_code") + metadata.get("schema_name"), language_code=language_code ) -def load_schema_from_session_data(session_data): - return load_schema_from_metadata(vars(session_data)) - - def load_schema_from_name(schema_name, language_code=DEFAULT_LANGUAGE_CODE): return _load_schema_from_name(schema_name, language_code) diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index 627ea71c81..c19702cec9 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -4,7 +4,6 @@ from flask import url_for from flask_babel import lazy_gettext -from app.data_models.session_data import SessionData from app.globals import ( get_view_submitted_response_expiration_time, has_view_submitted_response_expired, @@ -19,28 +18,31 @@ def build_thank_you_context( schema: QuestionnaireSchema, - session_data: SessionData, + metadata: dict, submitted_at: datetime, survey_type: SurveyType, guidance_content: Optional[dict] = None, ) -> Mapping: if survey_type is SurveyType.SOCIAL: submission_text = lazy_gettext("Your answers have been submitted.") - elif session_data.trad_as and session_data.ru_name: + elif metadata.get("trad_as") and metadata.get("ru_name"): submission_text = lazy_gettext( "Your answers have been submitted for {company_name} ({trading_name})" - ).format(company_name=session_data.ru_name, trading_name=session_data.trad_as) + ).format( + company_name=metadata["ru_name"], + trading_name=metadata["trad_as"], + ) else: submission_text = lazy_gettext( "Your answers have been submitted for {company_name}" - ).format(company_name=session_data.ru_name) - metadata = build_submission_metadata_context( - survey_type, submitted_at, session_data.tx_id # type: ignore + ).format(company_name=metadata["ru_name"]) + context_metadata = build_submission_metadata_context( + survey_type, submitted_at, metadata["tx_id"] # type: ignore ) return { "hide_sign_out_button": True, "submission_text": submission_text, - "metadata": metadata, + "metadata": context_metadata, "guidance": guidance_content, "view_submitted_response": build_view_submitted_response_context( schema, submitted_at @@ -67,10 +69,10 @@ def build_view_submitted_response_context(schema, submitted_at): def build_census_thank_you_context( - session_data: SessionData, confirmation_email_form, form_type + metadata: dict, confirmation_email_form, form_type ) -> Mapping: context = { - "display_address": session_data.display_address, + "display_address": metadata["display_address"], "form_type": form_type, "hide_sign_out_button": False, "sign_out_url": url_for("session.get_sign_out"), diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index 51bf67d5b7..c095e4961b 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from functools import cached_property -from typing import Mapping +from typing import Any, Mapping from flask import current_app, url_for from flask_babel import gettext, lazy_gettext @@ -136,7 +136,10 @@ def handle_post(self): def _publish_fulfilment_request(self): fulfilment_request = ConfirmationEmailFulfilmentRequest( - self._email, self._session_store.session_data, self._schema + self._email, + self._session_store.session_data, + self._questionnaire_store.metadata, + self._schema, ) try: @@ -154,16 +157,17 @@ def _publish_fulfilment_request(self): class ConfirmationEmailFulfilmentRequest(FulfilmentRequest): email_address: str session_data: SessionData + metadata: Mapping[str, Any] schema: QuestionnaireSchema def _payload(self) -> Mapping: return { "fulfilmentRequest": { "email_address": self.email_address, - "display_address": self.session_data.display_address, + "display_address": self.metadata.get("display_address"), "form_type": self.schema.form_type, "language_code": self.session_data.language_code, "region_code": self.schema.region_code, - "tx_id": self.session_data.tx_id, + "tx_id": self.metadata["tx_id"], } } diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 42340440a4..a701bc853c 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -86,7 +86,7 @@ def handle_post(self) -> None: session_data: SessionData = self._session_store.session_data # type: ignore session_data.feedback_count += 1 - feedback_metadata = FeedbackMetadata(session_data.case_id, session_data.tx_id) # type: ignore + feedback_metadata = FeedbackMetadata(self._questionnaire_store.metadata["case_id"], self._questionnaire_store.metadata["tx_id"]) # type: ignore # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation @@ -94,7 +94,7 @@ def handle_post(self) -> None: metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, schema=self._schema, - case_id=session_data.case_id, + case_id=self._questionnaire_store.metadata["case_id"], submission_language_code=session_data.language_code, feedback_count=session_data.feedback_count, feedback_text=self.form.data.get("feedback-text"), diff --git a/app/views/handlers/thank_you.py b/app/views/handlers/thank_you.py index abb9c45e1e..e414f84eec 100644 --- a/app/views/handlers/thank_you.py +++ b/app/views/handlers/thank_you.py @@ -3,8 +3,10 @@ from flask import session as cookie_session from flask_babel import gettext +from flask_login import current_user from app.data_models.session_store import SessionStore +from app.globals import get_metadata from app.helpers.template_helpers import get_survey_type from app.questionnaire import QuestionnaireSchema from app.views.contexts.thank_you_context import ( @@ -51,11 +53,13 @@ def confirmation_email(self): return None def get_context(self): + metadata = get_metadata(current_user) or {} + if not self._is_census_theme: guidance_content = self._schema.get_post_submission().get("guidance") return build_thank_you_context( self._schema, - self._session_store.session_data, + metadata, self._submitted_at, get_survey_type(), guidance_content, @@ -66,7 +70,7 @@ def get_context(self): ) return build_census_thank_you_context( - self._session_store.session_data, + metadata, confirmation_email_form, self._schema.form_type, ) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index b02db6203b..c1bbf5609d 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -4,7 +4,9 @@ import fakeredis import pytest +from mock import MagicMock +from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.progress_store import ProgressStore @@ -73,11 +75,41 @@ def expires_at(): @pytest.fixture(name="session_store") def fixture_session_store(session_data): - session_store = SessionStore("user_ik", "pepper", "eq_session_id") + session_store = SessionStore( + "user_ik", + "pepper", + "eq_session_id", + ) session_store.session_data = session_data + session_store.user_id = "user_id" return session_store +@pytest.fixture +def fake_questionnaire_store(): + storage = MagicMock() + storage.get_user_data = MagicMock(return_value=("{}", "ce_sid", 1, None)) + storage.add_or_update = MagicMock() + store = QuestionnaireStore(storage) + store.metadata = { + "schema_name": "test_checkbox", + "display_address": "68 Abingdon Road, Goathill", + "tx_id": "tx_id", + "language_code": "en", + } + + return store + + +@pytest.fixture +def fake_metadata(): + return { + "tx_id": "tx_id", + "language_code": "en", + "display_address": "68 Abingdon Road, Goathill", + } + + @pytest.fixture def session_data(): return SessionData( diff --git a/tests/app/data_model/test_session_store.py b/tests/app/data_model/test_session_store.py index 10d68e70c0..1f3159acfa 100644 --- a/tests/app/data_model/test_session_store.py +++ b/tests/app/data_model/test_session_store.py @@ -1,5 +1,3 @@ -from datetime import datetime, timezone - import pytest from flask import current_app from jwcrypto import jwe @@ -42,7 +40,8 @@ def test_save(app, app_session_store): expires_at=app_session_store.expires_at, ).save() session_store = SessionStore("user_ik", "pepper", "eq_session_id") - assert session_store.session_data.tx_id == "tx_id" + + assert session_store.session_data.confirmation_email_count == 0 def test_delete(app, app_session_store): @@ -66,12 +65,12 @@ def test_add_data_to_session(app, app_session_store): session_data=app_session_store.session_data, expires_at=app_session_store.expires_at, ).save() - display_address = "68 Abingdon Road, Goathill" - app_session_store.session_store.session_data.display_address = display_address + feedback_count = 9 + app_session_store.session_store.session_data.feedback_count = feedback_count app_session_store.session_store.save() session_store = SessionStore("user_ik", "pepper", "eq_session_id") - assert session_store.session_data.display_address == display_address + assert session_store.session_data.feedback_count == feedback_count def test_should_not_delete_when_no_session(app, app_session_store): @@ -158,18 +157,8 @@ def test_load_existing_session_does_not_error_when_session_data_contains_survey_ app, app_session_store ): session_data_with_survey_url = SessionData( - tx_id="123", - schema_name="some_schema_name", - display_address="68 Abingdon Road, Goathill", - period_str=None, language_code="cy", - launch_language_code="en", survey_url="some-url", - ru_name=None, - ru_ref=None, - submitted_at=datetime.now(timezone.utc).isoformat(), - response_id="321", - case_id="789", ) with app.test_request_context(): diff --git a/tests/app/helpers/test_schema_helper.py b/tests/app/helpers/test_schema_helper.py index bc29b87111..d4200ad8b5 100644 --- a/tests/app/helpers/test_schema_helper.py +++ b/tests/app/helpers/test_schema_helper.py @@ -6,7 +6,14 @@ @pytest.mark.usefixtures("app") -def test_questionnaire_schema_passed_into_function(mocker, session_store): +def test_questionnaire_schema_passed_into_function( + mocker, session_store, fake_questionnaire_store +): + mocker.patch( + "app.helpers.schema_helpers.get_metadata", + return_value=fake_questionnaire_store.metadata, + ) + mocker.patch( "app.helpers.schema_helpers.get_session_store", return_value=session_store, diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index a70ffee0cb..facd03914d 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -229,6 +229,22 @@ def test_load_schema_from_metadata_with_schema_url(): assert loaded_schema.language_code == mock_schema.language_code +@responses.activate +def test_load_schema_from_metadata_with_schema_url_and_override_language_code(): + load_schema_from_url.cache_clear() + language_code = "en" + + metadata = {"schema_url": TEST_SCHEMA_URL, "language_code": "cy"} + mock_schema = QuestionnaireSchema({}, language_code="cy") + responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) + loaded_schema = load_schema_from_metadata( + metadata=metadata, language_code=language_code + ) + + assert loaded_schema.json == mock_schema.json + assert loaded_schema.language_code == language_code + + @pytest.fixture(name="mocked_response_content") def mocked_response_content_fixture(mocker): decodable_content = Mock() diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 66955621da..f0c7498450 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -197,19 +197,25 @@ def response_metadata(): @pytest.fixture def fake_session_data(): return SessionData( - tx_id="tx_id", - schema_name="some_schema_name", - period_str="period_str", language_code=None, - launch_language_code=None, - schema_url=None, - ru_name="ru_name", - ru_ref="ru_ref", - response_id="response_id", - case_id="case_id", ) +@pytest.fixture +def fake_questionnaire_store_metadata(): + return { + "tx_id": "tx_id", + "schema_name": "some_schema_name", + "period_str": "period_str", + "language_code": None, + "schema_url": None, + "ru_name": "ru_name", + "ru_ref": "ru_ref", + "response_id": "response_id", + "case_id": "case_id", + } + + @pytest.fixture def test_calculated_summary_schema(): return load_schema_from_name("test_calculated_summary") diff --git a/tests/app/views/contexts/test_thank_you_context.py b/tests/app/views/contexts/test_thank_you_context.py index 9274dcd547..f99d468448 100644 --- a/tests/app/views/contexts/test_thank_you_context.py +++ b/tests/app/views/contexts/test_thank_you_context.py @@ -12,21 +12,21 @@ SCHEMA = load_schema_from_name("test_view_submitted_response", "en") -def test_social_survey_context(fake_session_data, app: Flask): +def test_social_survey_context(fake_questionnaire_store_metadata, app: Flask): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_SOCIAL + SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_SOCIAL ) assert context["submission_text"] == "Your answers have been submitted." assert len(context["metadata"]["itemsList"]) == 1 -def test_default_survey_context(fake_session_data, app: Flask): +def test_default_survey_context(fake_questionnaire_store_metadata, app: Flask): with app.app_context(): - fake_session_data.ru_name = "ESSENTIAL ENTERPRISE LTD" + fake_questionnaire_store_metadata["ru_name"] = "ESSENTIAL ENTERPRISE LTD" context = build_thank_you_context( - SCHEMA, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert ( @@ -36,12 +36,14 @@ def test_default_survey_context(fake_session_data, app: Flask): assert len(context["metadata"]["itemsList"]) == 2 -def test_default_survey_context_with_trad_as(fake_session_data, app: Flask): +def test_default_survey_context_with_trad_as( + fake_questionnaire_store_metadata, app: Flask +): with app.app_context(): - fake_session_data.ru_name = "ESSENTIAL ENTERPRISE LTD" - fake_session_data.trad_as = "EE" + fake_questionnaire_store_metadata["ru_name"] = "ESSENTIAL ENTERPRISE LTD" + fake_questionnaire_store_metadata["trad_as"] = "EE" context = build_thank_you_context( - SCHEMA, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert ( @@ -51,53 +53,57 @@ def test_default_survey_context_with_trad_as(fake_session_data, app: Flask): assert len(context["metadata"]["itemsList"]) == 2 -def test_view_submitted_response_enabled(fake_session_data, app: Flask): +def test_view_submitted_response_enabled(fake_questionnaire_store_metadata, app: Flask): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["enabled"] is True -def test_view_submitted_response_not_enabled(fake_session_data, app: Flask): +def test_view_submitted_response_not_enabled( + fake_questionnaire_store_metadata, app: Flask +): with app.app_context(): schema = load_schema_from_name("test_title", "en") context = build_thank_you_context( - schema, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + schema, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["enabled"] is False -def test_view_submitted_response_not_expired(fake_session_data, app: Flask): +def test_view_submitted_response_not_expired( + fake_questionnaire_store_metadata, app: Flask +): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_session_data, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["expired"] is False assert context["view_submitted_response"]["url"] == "/submitted/view-response/" -def test_view_submitted_response_expired(fake_session_data, app: Flask): +def test_view_submitted_response_expired(fake_questionnaire_store_metadata, app: Flask): submitted_at = SUBMITTED_AT - timedelta(minutes=46) with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_session_data, submitted_at, SURVEY_TYPE_DEFAULT + SCHEMA, fake_questionnaire_store_metadata, submitted_at, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["expired"] is True -def test_custom_guidance(fake_session_data, app: Flask): +def test_custom_guidance(fake_questionnaire_store_metadata, app: Flask): with app.app_context(): custom_guidance = {"contents": [{"description": "Custom guidance"}]} context = build_thank_you_context( SCHEMA, - fake_session_data, + fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT, custom_guidance, diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index be204c8ddb..daa08b1b41 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -43,18 +43,7 @@ @freeze_time(time_to_freeze) def session_data(): return SessionData( - tx_id="123", - schema_name="some_schema_name", - display_address="68 Abingdon Road, Goathill", - period_str=None, language_code="cy", - launch_language_code="en", - schema_url=None, - ru_name=None, - ru_ref=None, - submitted_at=datetime.now(timezone.utc).isoformat(), - response_id="321", - case_id="789", ) @@ -103,16 +92,7 @@ def set_storage_data( @pytest.fixture def session_data_feedback(): return SessionData( - tx_id=tx_id, - schema_name=schema_name, - response_id=response_id, - period_str=period_str, language_code=language_code, - launch_language_code=None, - schema_url=None, - ru_name=ru_name, - ru_ref=ru_ref, - case_id=case_id, feedback_count=feedback_count, ) @@ -159,16 +139,7 @@ def submission_payload_expires_at(): @pytest.fixture def submission_payload_session_data(): return SessionData( - tx_id="tx_id", - schema_name="schema_name", - response_id="response_id", - period_str="period_str", language_code="cy", - launch_language_code="en", - schema_url=None, - ru_name="ru_name", - ru_ref="ru_ref", - case_id="0123456789000000", ) diff --git a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py index 5316f72e40..9f3cf12328 100644 --- a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py +++ b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py @@ -8,22 +8,26 @@ @freeze_time(time_to_freeze) def test_confirmation_email_fulfilment_request_message( - session_data, confirmation_email_fulfilment_schema + session_data, metadata, confirmation_email_fulfilment_schema ): email_address = "name@example.com" + fulfilment_request = ConfirmationEmailFulfilmentRequest( - email_address, session_data, confirmation_email_fulfilment_schema + email_address, + session_data, + metadata, + confirmation_email_fulfilment_schema, ) confirmation_email_json_message = json_loads(fulfilment_request.message) expected_payload = { "email_address": "name@example.com", - "display_address": "68 Abingdon Road, Goathill", + "display_address": metadata["display_address"], "form_type": confirmation_email_fulfilment_schema.form_type, "language_code": session_data.language_code, "region_code": confirmation_email_fulfilment_schema.region_code, - "tx_id": session_data.tx_id, + "tx_id": metadata["tx_id"], } assert ( From 54ddcf6596f679ee24b154653647cb1587819f44 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:21:00 +0100 Subject: [PATCH 060/567] Add conditional title in survey header (#910) --- app/helpers/template_helpers.py | 5 +- tests/app/helpers/test_template_helpers.py | 221 ++++++++++++++------- 2 files changed, 151 insertions(+), 75 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index f8e3b92254..b749a4ba09 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -112,10 +112,11 @@ def page_header_context(self) -> dict[str, Union[bool, str, LazyString]]: context: dict[str, Union[bool, str, LazyString]] = { "orgLogo": f"{self._survey_config.page_header_logo}", "orgLogoAlt": f"{self._survey_config.page_header_logo_alt}", + "title": self._survey_title + if self._survey_title and self._survey_type + else "ONS Surveys", } - if self._survey_title: - context["title"] = self._survey_title if self._survey_config.title_logo: context["titleLogo"] = self._survey_config.title_logo if self._survey_config.title_logo_alt: diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 2c2b36d1c6..621fe95420 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -103,80 +103,155 @@ def test_footer_context_census_nisra_theme(app: Flask, expected_footer_nisra_the assert result == expected_footer_nisra_theme -def test_get_page_header_context_business(app: Flask): - expected = { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - } - - with app.app_context(): - survey_config = SurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["page_header"] - - assert result == expected - - -def test_get_page_header_context_social(app: Flask): - expected = { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Social Surveys", - } - - with app.app_context(): - survey_config = SocialSurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["page_header"] - - assert result == expected - - -def test_get_page_header_context_census(app: Flask): - expected = { - "title": "Census 2021", - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - } - - with app.app_context(): - survey_config = CensusSurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["page_header"] - - assert result == expected - - -def test_get_page_header_context_census_nisra(app: Flask): - expected = { - "title": "Census 2021", - "orgLogo": "nisra-logo", - "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - "customHeaderLogo": True, - "orgMobileLogo": "nisra-logo-mobile", - } - +@pytest.mark.parametrize( + "theme, survey_title, survey_config, expected", + ( + ( + SurveyType.BUSINESS, + None, + BusinessSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "ONS Business Surveys", + }, + ), + ( + SurveyType.BUSINESS, + "Test", + BusinessSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "Test", + }, + ), + ( + None, + None, + BusinessSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "ONS Surveys", + }, + ), + ( + SurveyType.SOCIAL, + None, + SocialSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "ONS Social Surveys", + }, + ), + ( + SurveyType.SOCIAL, + "Test", + SocialSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "Test", + }, + ), + ( + None, + None, + SocialSurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "ONS Surveys", + }, + ), + ( + SurveyType.CENSUS, + None, + CensusSurveyConfig(), + { + "title": "Census 2021", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "titleLogo": "census-logo-en", + "titleLogoAlt": "Census 2021", + }, + ), + ( + SurveyType.CENSUS, + "Test", + CensusSurveyConfig(), + { + "title": "Test", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "titleLogo": "census-logo-en", + "titleLogoAlt": "Census 2021", + }, + ), + ( + None, + None, + CensusSurveyConfig(), + { + "title": "ONS Surveys", + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "titleLogo": "census-logo-en", + "titleLogoAlt": "Census 2021", + }, + ), + ( + SurveyType.CENSUS_NISRA, + None, + CensusNISRASurveyConfig(), + { + "orgLogo": "nisra-logo", + "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", + "titleLogo": "census-logo-en", + "titleLogoAlt": "Census 2021", + "customHeaderLogo": True, + "orgMobileLogo": "nisra-logo-mobile", + "title": "Census 2021", + }, + ), + ( + None, + None, + CensusNISRASurveyConfig(), + { + "orgLogo": "nisra-logo", + "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", + "titleLogo": "census-logo-en", + "titleLogoAlt": "Census 2021", + "customHeaderLogo": True, + "orgMobileLogo": "nisra-logo-mobile", + "title": "ONS Surveys", + }, + ), + ( + None, + None, + SurveyConfig(), + { + "orgLogo": "ons-logo-en", + "orgLogoAlt": "Office for National Statistics logo", + "title": "ONS Surveys", + }, + ), + ), +) +def test_get_page_header_context( + app: Flask, theme, survey_title, survey_config, expected +): with app.app_context(): - survey_config = CensusNISRASurveyConfig() + for cookie_name, cookie_value in { + "theme": theme, + "survey_title": survey_title, + }.items(): + if cookie_value: + cookie_session[cookie_name] = cookie_value result = ContextHelper( language="en", From 0f5577eda7dc27766f8d692f799c24e6ef6a4fa6 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 3 Aug 2022 13:45:01 +0100 Subject: [PATCH 061/567] Schemas v3.9.0 (#912) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6a39225f1f..5f22788f5d 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.8.3 +v3.9.0 From c2f84ff7319f3d5f7a80eadacb8f1c6e54c6d70d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 9 Aug 2022 11:47:35 +0100 Subject: [PATCH 062/567] Add conditional links in footer (#911) --- app/helpers/template_helpers.py | 12 +- app/survey_config/business_config.py | 43 ++-- app/survey_config/census_config.py | 235 ++++++++++++--------- app/survey_config/social_survey_config.py | 37 ++-- app/survey_config/survey_config.py | 10 + app/translations/messages.pot | 159 +++++++------- tests/app/helpers/conftest.py | 156 ++++++++++++-- tests/app/helpers/test_template_helpers.py | 83 ++++---- 8 files changed, 466 insertions(+), 269 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index b749a4ba09..cee8fec5be 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -143,11 +143,15 @@ def footer_context(self) -> dict[str, Any]: if self._footer_warning: context["footerWarning"] = self._footer_warning - if self._survey_config.footer_links: - context["rows"] = [{"itemsList": self._survey_config.footer_links}] + if footer_links := self._survey_config.get_footer_links( + cookie_has_theme=bool(self._survey_type), + ): + context["rows"] = [{"itemsList": footer_links}] - if self._survey_config.footer_legal_links: - context["legal"] = [{"itemsList": self._survey_config.footer_legal_links}] + if footer_legal_links := self._survey_config.get_footer_legal_links( + cookie_has_theme=bool(self._survey_type), + ): + context["legal"] = [{"itemsList": footer_legal_links}] if ( self._survey_config.powered_by_logo diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 1c02fc910f..4630dad954 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -30,22 +30,6 @@ def __post_init__(self): if not self.account_service_todo_url: self.account_service_todo_url: str = f"{self.base_url}/surveys/todo" - self.footer_links = [ - Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__, - Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__, - Link( - lazy_gettext("Accessibility"), - self.accessibility_url, - ).__dict__, - ] - self.footer_legal_links = [ - Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, - Link( - lazy_gettext("Privacy and data protection"), - self.privacy_and_data_protection_url, - ).__dict__, - ] - def _get_account_service_help_url( self, *, is_authenticated: bool, ru_ref: Optional[str] ) -> str: @@ -101,6 +85,33 @@ def get_service_links( return links + def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: + links = [Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__] + + if cookie_has_theme: + links.append(Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__) + + links.append( + Link( + lazy_gettext("Accessibility"), + self.accessibility_url, + ).__dict__ + ) + + return links + + def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + if cookie_has_theme: + return [ + Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + self.privacy_and_data_protection_url, + ).__dict__, + ] + + return None + def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: data_layer = [{"tx_id": tx_id}] if tx_id else [] if self.schema: diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index 8a602f7131..9562277a6a 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping, Optional, Union +from dataclasses import dataclass +from typing import Optional, Union from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString @@ -21,42 +21,6 @@ class CensusSurveyConfig( base_url: str = EN_BASE_URL account_service_log_out_url: str = f"{base_url}/en/start" design_system_theme: str = "census" - footer_links: Iterable[MutableMapping] = field( - default_factory=lambda: [ - Link( - lazy_gettext("Help"), - f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", - ).__dict__, - Link(lazy_gettext("Contact us"), f"{EN_BASE_URL}/contact-us/").__dict__, - Link( - lazy_gettext("Languages"), - f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", - ).__dict__, - Link( - lazy_gettext("BSL and audio videos"), - f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", - ).__dict__, - ], - compare=False, - ) - footer_legal_links: Iterable[Mapping] = field( - default_factory=lambda: [ - Link(lazy_gettext("Cookies"), f"{EN_BASE_URL}/cookies/").__dict__, - Link( - lazy_gettext("Accessibility statement"), - f"{EN_BASE_URL}/accessibility-statement/", - ).__dict__, - Link( - lazy_gettext("Privacy and data protection"), - f"{EN_BASE_URL}/privacy-and-data-protection/", - ).__dict__, - Link( - lazy_gettext("Terms and conditions"), - f"{EN_BASE_URL}/terms-and-conditions/", - ).__dict__, - ], - compare=False, - ) survey_title: LazyString = lazy_gettext("Census 2021") sign_out_button_text: str = lazy_gettext("Save and complete later") @@ -71,6 +35,54 @@ def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: return data_layer + def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: + links = [ + Link( + lazy_gettext("Help"), + f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", + ).__dict__ + ] + + if cookie_has_theme: + links.append( + Link(lazy_gettext("Contact us"), f"{EN_BASE_URL}/contact-us/").__dict__ + ) + + links.extend( + [ + Link( + lazy_gettext("Languages"), + f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", + ).__dict__, + Link( + lazy_gettext("BSL and audio videos"), + f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", + ).__dict__, + ] + ) + + return links + + def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + if cookie_has_theme: + return [ + Link(lazy_gettext("Cookies"), f"{EN_BASE_URL}/cookies/").__dict__, + Link( + lazy_gettext("Accessibility statement"), + f"{EN_BASE_URL}/accessibility-statement/", + ).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + f"{EN_BASE_URL}/privacy-and-data-protection/", + ).__dict__, + Link( + lazy_gettext("Terms and conditions"), + f"{EN_BASE_URL}/terms-and-conditions/", + ).__dict__, + ] + + return None + @dataclass class WelshCensusSurveyConfig( @@ -79,44 +91,55 @@ class WelshCensusSurveyConfig( title_logo: str = "census-logo-cy" base_url: str = CY_BASE_URL account_service_log_out_url: str = f"{base_url}/en/start" - footer_links: Iterable[MutableMapping] = field( - default_factory=lambda: [ + + def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: + + links = [ Link( lazy_gettext("Help"), f"{CY_BASE_URL}/help/sut-i-ateb-y-cwestiynau/help-y-cwestiynau-ar-lein/", - ).__dict__, - Link(lazy_gettext("Contact us"), f"{CY_BASE_URL}/cysylltu-a-ni/").__dict__, - Link( - lazy_gettext("Languages"), - f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", - ).__dict__, - Link( - lazy_gettext("BSL and audio videos"), - f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", - ).__dict__, - ], - compare=False, - hash=False, - ) - footer_legal_links: Iterable[Mapping] = field( - default_factory=lambda: [ - Link(lazy_gettext("Cookies"), f"{CY_BASE_URL}/cwcis/").__dict__, - Link( - lazy_gettext("Accessibility statement"), - f"{CY_BASE_URL}/datganiad-hygyrchedd/", - ).__dict__, - Link( - lazy_gettext("Privacy and data protection"), - f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", - ).__dict__, - Link( - lazy_gettext("Terms and conditions"), - f"{CY_BASE_URL}/telerau-ac-amodau/", - ).__dict__, - ], - compare=False, - hash=False, - ) + ).__dict__ + ] + + if cookie_has_theme: + links.append( + Link( + lazy_gettext("Contact us"), f"{CY_BASE_URL}/cysylltu-a-ni/" + ).__dict__ + ) + links.extend( + [ + Link( + lazy_gettext("Languages"), + f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", + ).__dict__, + Link( + lazy_gettext("BSL and audio videos"), + f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", + ).__dict__, + ] + ) + return links + + def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + if cookie_has_theme: + return [ + Link(lazy_gettext("Cookies"), f"{CY_BASE_URL}/cwcis/").__dict__, + Link( + lazy_gettext("Accessibility statement"), + f"{CY_BASE_URL}/datganiad-hygyrchedd/", + ).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", + ).__dict__, + Link( + lazy_gettext("Terms and conditions"), + f"{CY_BASE_URL}/telerau-ac-amodau/", + ).__dict__, + ] + + return None @dataclass @@ -137,36 +160,42 @@ class CensusNISRASurveyConfig( copyright_text: LazyString = lazy_gettext( "Use of address data is subject to the terms and conditions." ) - footer_links: Iterable[MutableMapping] = field( - default_factory=lambda: [ - Link( - lazy_gettext("Help"), - f"{NIR_BASE_URL}/help/help-with-the-questions/online-questions-help/", - ).__dict__, - Link(lazy_gettext("Contact us"), f"{NIR_BASE_URL}/contact-us/").__dict__, - ], - compare=False, - hash=False, - ) - footer_legal_links: Iterable[Mapping] = field( - default_factory=lambda: [ - Link(lazy_gettext("Cookies"), f"{NIR_BASE_URL}/cookies/").__dict__, - Link( - lazy_gettext("Accessibility statement"), - f"{NIR_BASE_URL}/accessibility-statement/", - ).__dict__, - Link( - lazy_gettext("Privacy and data protection"), - f"{NIR_BASE_URL}/privacy-and-data-protection/", - ).__dict__, - Link( - lazy_gettext("Terms and conditions"), - f"{NIR_BASE_URL}/terms-and-conditions/", - ).__dict__, - ], - compare=False, - hash=False, - ) powered_by_logo: str = "nisra-logo" powered_by_logo_alt: str = "NISRA - Northern Ireland Statistics and Research Agency" _is_nisra: bool = True + + def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: + + links = [ + Link( + lazy_gettext("Help"), + f"{NIR_BASE_URL}/help/help-with-the-questions/online-questions-help/", + ).__dict__ + ] + + if cookie_has_theme: + links.append( + Link(lazy_gettext("Contact us"), f"{NIR_BASE_URL}/contact-us/").__dict__ + ) + + return links + + def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + if cookie_has_theme: + return [ + Link(lazy_gettext("Cookies"), f"{NIR_BASE_URL}/cookies/").__dict__, + Link( + lazy_gettext("Accessibility statement"), + f"{NIR_BASE_URL}/accessibility-statement/", + ).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + f"{NIR_BASE_URL}/privacy-and-data-protection/", + ).__dict__, + Link( + lazy_gettext("Terms and conditions"), + f"{NIR_BASE_URL}/terms-and-conditions/", + ).__dict__, + ] + + return None diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 0fb1820e45..aa1a4cf447 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping +from typing import Iterable, Mapping, MutableMapping, Optional from flask_babel import lazy_gettext @@ -32,18 +32,29 @@ def __post_init__(self): if not self.account_service_log_out_url: self.account_service_log_out_url: str = f"{self.base_url}/sign-in/logout" - self.footer_links = [ - Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__, - Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__, + def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: + links = [Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__] + + if cookie_has_theme: + links.append(Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__) + + links.append( Link( lazy_gettext("Accessibility"), self.accessibility_url, - ).__dict__, - ] - self.footer_legal_links = [ - Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, - Link( - lazy_gettext("Privacy and data protection"), - self.privacy_and_data_protection_url, - ).__dict__, - ] + ).__dict__ + ) + + return links + + def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + if cookie_has_theme: + return [ + Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, + Link( + lazy_gettext("Privacy and data protection"), + self.privacy_and_data_protection_url, + ).__dict__, + ] + + return None diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 0bea609b07..2641e7c0e7 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -65,6 +65,16 @@ def get_service_links( # pylint: disable=unused-argument, no-self-use ) -> Optional[list[dict]]: return None + def get_footer_links( # pylint: disable=unused-argument, no-self-use + self, cookie_has_theme: bool + ) -> Optional[list[dict]]: + return None + + def get_footer_legal_links( # pylint: disable=unused-argument, no-self-use + self, cookie_has_theme: bool + ) -> Optional[list[dict]]: + return None + def get_data_layer( # pylint: disable=no-self-use self, tx_id: Optional[str] = None ) -> list[dict]: diff --git a/app/translations/messages.pot b/app/translations/messages.pot index b8c272fbb3..f0807c905c 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-06-27 13:29+0100\n" +"POT-Creation-Date: 2022-08-09 08:03+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -203,15 +203,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:97 +#: app/helpers/template_helpers.py:94 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:138 +#: app/helpers/template_helpers.py:136 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:168 +#: app/helpers/template_helpers.py:170 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -235,55 +235,55 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:133 +#: app/routes/errors.py:136 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:136 +#: app/routes/errors.py:139 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:152 +#: app/routes/errors.py:155 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:155 +#: app/routes/errors.py:158 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:187 +#: app/routes/errors.py:190 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:193 +#: app/routes/errors.py:196 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:196 app/routes/errors.py:219 app/routes/errors.py:241 +#: app/routes/errors.py:199 app/routes/errors.py:222 app/routes/errors.py:244 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:215 +#: app/routes/errors.py:218 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:216 +#: app/routes/errors.py:219 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:237 templates/errors/403.html:3 -#: templates/errors/403.html:7 templates/errors/submission-failed.html:5 +#: app/routes/errors.py:240 templates/errors/403.html:3 +#: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:238 +#: app/routes/errors.py:241 msgid "You can try to submit your feedback again." msgstr "" @@ -291,94 +291,105 @@ msgstr "" msgid "An individual access code has been sent by post" msgstr "" -#: app/routes/individual_response.py:272 +#: app/routes/individual_response.py:271 msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:34 -#: app/survey_config/social_survey_config.py:35 -msgid "What we do" +#: app/survey_config/business_config.py:60 +#: app/survey_config/census_config.py:43 app/survey_config/census_config.py:59 +#: app/survey_config/census_config.py:106 +#: app/survey_config/census_config.py:124 +#: app/survey_config/census_config.py:185 +#: app/survey_config/census_config.py:195 +msgid "Help" msgstr "" -#: app/survey_config/business_config.py:35 -#: app/survey_config/census_config.py:30 app/survey_config/census_config.py:88 -#: app/survey_config/census_config.py:146 -#: app/survey_config/social_survey_config.py:36 -msgid "Contact us" +#: app/survey_config/business_config.py:74 +msgid "My account" msgstr "" -#: app/survey_config/business_config.py:37 -#: app/survey_config/social_survey_config.py:38 -msgid "Accessibility" +#: app/survey_config/business_config.py:79 +msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:42 -#: app/survey_config/census_config.py:44 app/survey_config/census_config.py:103 -#: app/survey_config/census_config.py:153 -#: app/survey_config/social_survey_config.py:43 -msgid "Cookies" +#: app/survey_config/business_config.py:92 +#: app/survey_config/business_config.py:101 +#: app/survey_config/social_survey_config.py:39 +#: app/survey_config/social_survey_config.py:48 +msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:44 -#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:109 -#: app/survey_config/census_config.py:159 -#: app/survey_config/social_survey_config.py:45 -msgid "Privacy and data protection" +#: app/survey_config/business_config.py:93 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:110 +#: app/survey_config/census_config.py:189 +#: app/survey_config/social_survey_config.py:40 +msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:76 -#: app/survey_config/census_config.py:27 app/survey_config/census_config.py:85 -#: app/survey_config/census_config.py:143 -msgid "Help" +#: app/survey_config/business_config.py:95 +#: app/survey_config/business_config.py:103 +#: app/survey_config/social_survey_config.py:42 +#: app/survey_config/social_survey_config.py:50 +msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:90 -msgid "My account" +#: app/survey_config/business_config.py:111 +#: app/survey_config/census_config.py:75 app/survey_config/census_config.py:140 +#: app/survey_config/census_config.py:203 +#: app/survey_config/social_survey_config.py:58 +msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:95 -msgid "Sign out" +#: app/survey_config/business_config.py:113 +#: app/survey_config/census_config.py:81 app/survey_config/census_config.py:146 +#: app/survey_config/census_config.py:209 +#: app/survey_config/social_survey_config.py:60 +msgid "Privacy and data protection" msgstr "" -#: app/survey_config/business_config.py:128 +#: app/survey_config/business_config.py:144 msgid "Northern Ireland Department of Finance logo" msgstr "" -#: app/survey_config/census_config.py:20 app/survey_config/census_config.py:61 +#: app/survey_config/census_config.py:20 app/survey_config/census_config.py:25 msgid "Census 2021" msgstr "" -#: app/survey_config/census_config.py:32 app/survey_config/census_config.py:90 +#: app/survey_config/census_config.py:26 +msgid "Save and complete later" +msgstr "" + +#: app/survey_config/census_config.py:48 app/survey_config/census_config.py:63 +#: app/survey_config/census_config.py:113 +#: app/survey_config/census_config.py:128 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:36 app/survey_config/census_config.py:94 +#: app/survey_config/census_config.py:52 app/survey_config/census_config.py:67 +#: app/survey_config/census_config.py:117 +#: app/survey_config/census_config.py:132 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:105 -#: app/survey_config/census_config.py:155 +#: app/survey_config/census_config.py:77 app/survey_config/census_config.py:142 +#: app/survey_config/census_config.py:205 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:54 app/survey_config/census_config.py:113 -#: app/survey_config/census_config.py:163 +#: app/survey_config/census_config.py:85 app/survey_config/census_config.py:150 +#: app/survey_config/census_config.py:213 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:62 -msgid "Save and complete later" -msgstr "" - -#: app/survey_config/census_config.py:129 +#: app/survey_config/census_config.py:165 msgid "Northern Ireland Statistics and Research Agency logo" msgstr "" -#: app/survey_config/census_config.py:134 +#: app/survey_config/census_config.py:170 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:137 app/survey_config/survey_config.py:23 +#: app/survey_config/census_config.py:173 app/survey_config/survey_config.py:23 msgid "Use of address data is subject to the terms and conditions." msgstr "" @@ -462,15 +473,15 @@ msgstr "" msgid " (You)" msgstr "" -#: app/views/contexts/submission_metadata_context.py:12 +#: app/views/contexts/submission_metadata_context.py:13 msgid "Submitted on:" msgstr "" -#: app/views/contexts/submission_metadata_context.py:15 +#: app/views/contexts/submission_metadata_context.py:16 msgid "{date} at {time}" msgstr "" -#: app/views/contexts/submission_metadata_context.py:23 +#: app/views/contexts/submission_metadata_context.py:24 msgid "Submission reference:" msgstr "" @@ -496,19 +507,19 @@ msgid "" "({trading_name})" msgstr "" -#: app/views/contexts/thank_you_context.py:33 +#: app/views/contexts/thank_you_context.py:36 msgid "Your answers have been submitted for {company_name}" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:28 +#: app/views/contexts/view_submitted_response_context.py:29 msgid "Answers submitted." msgstr "" -#: app/views/contexts/view_submitted_response_context.py:30 +#: app/views/contexts/view_submitted_response_context.py:31 msgid "Answers submitted for {ru_name} ({trad_as})" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:34 +#: app/views/contexts/view_submitted_response_context.py:35 msgid "Answers submitted for {ru_name}" msgstr "" @@ -713,7 +724,7 @@ msgstr "" msgid "Confirm mobile number" msgstr "" -#: app/views/handlers/thank_you.py:24 templates/census-thank-you.html:24 +#: app/views/handlers/thank_you.py:26 templates/census-thank-you.html:24 msgid "Thank you for completing the census" msgstr "" @@ -1018,25 +1029,25 @@ msgid "" "href='{url}'>re-enter your code." msgstr "" -#: templates/errors/403.html:8 +#: templates/errors/403.html:7 msgid "You may need to update your browser to a newer version." msgstr "" -#: templates/errors/403.html:9 +#: templates/errors/403.html:8 msgid "If the problem still occurs, try using a different browser or device." msgstr "" -#: templates/errors/403.html:11 +#: templates/errors/403.html:10 msgid "For further help, please contact us." msgstr "" -#: templates/errors/403.html:14 +#: templates/errors/403.html:13 msgid "" "If you are completing a business survey and you need further help, please" " contact us." msgstr "" -#: templates/errors/403.html:16 +#: templates/errors/403.html:15 msgid "" "If you started your survey using an access code and you need further " "help, please contact us." diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index f75f962eb7..c4fb0dcf76 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -2,7 +2,7 @@ from app.helpers.template_helpers import ContextHelper from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL -from app.survey_config.census_config import EN_BASE_URL +from app.survey_config.census_config import CY_BASE_URL, EN_BASE_URL @fixture @@ -21,8 +21,7 @@ def _context_helper( return _context_helper -@fixture(name="footer_context") -def footer(): +def footer_context(): return { "lang": "en", "crest": True, @@ -34,8 +33,7 @@ def footer(): } -@fixture -def expected_footer_census_theme(footer_context): +def expected_footer_census_theme(): census = { "rows": [ { @@ -90,11 +88,37 @@ def expected_footer_census_theme(footer_context): } ], } - return {**footer_context, **census} + return {**footer_context(), **census} -@fixture -def expected_footer_business_theme(footer_context): +def expected_footer_census_theme_no_cookie(): + census = { + "rows": [ + { + "itemsList": [ + { + "text": "Help", + "url": f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", + "target": "_blank", + }, + { + "text": "Languages", + "url": f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", + "target": "_blank", + }, + { + "text": "BSL and audio videos", + "url": f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", + "target": "_blank", + }, + ] + } + ], + } + return {**footer_context(), **census} + + +def expected_footer_business_theme(): business = { "rows": [ { @@ -134,11 +158,32 @@ def expected_footer_business_theme(footer_context): } ], } - return {**footer_context, **business} + return {**footer_context(), **business} -@fixture -def expected_footer_social_theme(footer_context): +def expected_footer_business_theme_no_cookie(): + business = { + "rows": [ + { + "itemsList": [ + { + "text": "What we do", + "url": "https://www.ons.gov.uk/aboutus/whatwedo/", + "target": "_blank", + }, + { + "text": "Accessibility", + "url": "https://www.ons.gov.uk/help/accessibility/", + "target": "_blank", + }, + ] + } + ], + } + return {**footer_context(), **business} + + +def expected_footer_social_theme(): social = { "rows": [ { @@ -178,10 +223,31 @@ def expected_footer_social_theme(footer_context): } ], } - return {**footer_context, **social} + return {**footer_context(), **social} + + +def expected_footer_social_theme_no_cookie(): + social = { + "rows": [ + { + "itemsList": [ + { + "text": "What we do", + "url": "https://www.ons.gov.uk/aboutus/whatwedo/", + "target": "_blank", + }, + { + "text": "Accessibility", + "url": "https://www.ons.gov.uk/help/accessibility/", + "target": "_blank", + }, + ] + } + ], + } + return {**footer_context(), **social} -@fixture def expected_footer_nisra_theme(): return { "lang": "en", @@ -238,3 +304,67 @@ def expected_footer_nisra_theme(): "alt": "NISRA - Northern Ireland Statistics and Research Agency", }, } + + +def expected_footer_census_welsh_theme(): + return { + "lang": "en", + "crest": True, + "newTabWarning": "The following links open in a new tab", + "copyrightDeclaration": { + "copyright": "Crown copyright and database rights 2020 OS 100019153.", + "text": "Use of address data is subject to the terms and conditions.", + }, + "rows": [ + { + "itemsList": [ + { + "text": "Help", + "url": f"{CY_BASE_URL}/help/sut-i-ateb-y-cwestiynau/help-y-cwestiynau-ar-lein/", + "target": "_blank", + }, + { + "text": "Contact us", + "url": f"{CY_BASE_URL}/cysylltu-a-ni/", + "target": "_blank", + }, + { + "text": "Languages", + "url": f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", + "target": "_blank", + }, + { + "text": "BSL and audio videos", + "url": f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", + "target": "_blank", + }, + ] + } + ], + "legal": [ + { + "itemsList": [ + { + "text": "Cookies", + "url": f"{CY_BASE_URL}/cwcis/", + "target": "_blank", + }, + { + "text": "Accessibility statement", + "url": f"{CY_BASE_URL}/datganiad-hygyrchedd/", + "target": "_blank", + }, + { + "text": "Privacy and data protection", + "url": f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", + "target": "_blank", + }, + { + "text": "Terms and conditions", + "url": f"{CY_BASE_URL}/telerau-ac-amodau/", + "target": "_blank", + }, + ] + } + ], + } diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 621fe95420..3d00c96e11 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -17,49 +17,54 @@ WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType +from tests.app.helpers.conftest import ( + expected_footer_business_theme, + expected_footer_business_theme_no_cookie, + expected_footer_census_theme, + expected_footer_census_theme_no_cookie, + expected_footer_census_welsh_theme, + expected_footer_nisra_theme, + expected_footer_social_theme, + expected_footer_social_theme_no_cookie, +) DEFAULT_URL = "http://localhost" -def test_footer_context_census_theme(app: Flask, expected_footer_census_theme): +@pytest.mark.parametrize( + "theme, survey_config, expected_footer", + [ + (SurveyType.CENSUS, CensusSurveyConfig(), expected_footer_census_theme()), + (None, CensusSurveyConfig(), expected_footer_census_theme_no_cookie()), + (SurveyType.BUSINESS, BusinessSurveyConfig(), expected_footer_business_theme()), + (None, BusinessSurveyConfig(), expected_footer_business_theme_no_cookie()), + (SurveyType.SOCIAL, SocialSurveyConfig(), expected_footer_social_theme()), + (None, SocialSurveyConfig(), expected_footer_social_theme_no_cookie()), + ( + SurveyType.CENSUS_NISRA, + CensusNISRASurveyConfig(), + expected_footer_nisra_theme(), + ), + ( + SurveyType.CENSUS, + WelshCensusSurveyConfig(), + expected_footer_census_welsh_theme(), + ), + ], +) +def test_footer_context(app: Flask, theme, survey_config, expected_footer): with app.app_context(): - survey_config = CensusSurveyConfig() + if theme: + cookie_session["theme"] = theme + config = survey_config result = ContextHelper( language="en", is_post_submission=False, include_csrf_token=True, - survey_config=survey_config, + survey_config=config, ).context["footer"] - assert result == expected_footer_census_theme - - -def test_footer_context_business_theme(app: Flask, expected_footer_business_theme): - with app.test_client(): - survey_config = BusinessSurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["footer"] - - assert result == expected_footer_business_theme - - -def test_footer_context_social_theme(app: Flask, expected_footer_social_theme): - with app.test_client(): - survey_config = SocialSurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["footer"] - - assert result == expected_footer_social_theme + assert result == expected_footer def test_footer_warning_in_context_census_theme(app: Flask): @@ -89,20 +94,6 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): ).context["footer"]["footerWarning"] -def test_footer_context_census_nisra_theme(app: Flask, expected_footer_nisra_theme): - with app.app_context(): - survey_config = CensusNISRASurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=survey_config, - ).context["footer"] - - assert result == expected_footer_nisra_theme - - @pytest.mark.parametrize( "theme, survey_title, survey_config, expected", ( From bc4fa92406f8ab184422d8948c5706411ecdccdd Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 17 Aug 2022 16:04:50 +0100 Subject: [PATCH 063/567] Fix yarn typescript warning (#916) --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 1114c090f6..0f0b48beb7 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "node-forge": "^1.2.1", "node-jose": "^2.1.0", "prettier": "^2.5.1", + "typescript": "^4.7.4", "uuid": "^8.3.2", "wdio-chromedriver-service": "^7.2.8", "webdriverio": "^7.17.4" diff --git a/yarn.lock b/yarn.lock index 739f1e7758..958e87b2ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5028,6 +5028,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + ua-parser-js@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.2.tgz#e2976c34dbfb30b15d2c300b2a53eac87c57a775" From cdbbc1a9d6fcc5c5c77a72c6244239df9c821726 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 18 Aug 2022 08:40:36 +0100 Subject: [PATCH 064/567] Schemas v3.10.0 (#918) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5f22788f5d..3331c193d1 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.9.0 +v3.10.0 From ae40430c952e2f07bab84cd9b986b2fdffff7da8 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:55:18 +0100 Subject: [PATCH 065/567] Schemas v3.11.0 (#921) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3331c193d1..41e615f399 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.10.0 +v3.11.0 From 7da924e94619aeb96dc8559ff0ec0d67e2e53e8d Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 25 Aug 2022 08:26:19 +0100 Subject: [PATCH 066/567] Schemas v3.11.1 (#923) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 41e615f399..c431007116 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.11.0 +v3.11.1 From 7dcbcfd1264385150458fc94f19cb4ca1f9a8f4b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 25 Aug 2022 10:12:02 +0100 Subject: [PATCH 067/567] Schemas v3.11.2 (#924) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c431007116..89d85a8a9b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.11.1 +v3.11.2 From f53a2bde331ef0dcfe3cdbe04810ea737b41dcbf Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:53:11 +0100 Subject: [PATCH 068/567] Remove metadata from session (#917) --- app/authentication/authenticator.py | 13 ------ app/data_models/session_data.py | 38 +-------------- app/views/handlers/submission.py | 6 --- tests/app/conftest.py | 17 +++---- tests/app/data_model/test_session_data.py | 32 ------------- tests/app/data_model/test_session_store.py | 54 ++++++---------------- 6 files changed, 21 insertions(+), 139 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 61031fe5d3..ea3920a19f 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -130,20 +130,7 @@ def _create_session_data_from_metadata(metadata: Mapping[str, Any]) -> SessionDa """ return SessionData( - tx_id=metadata.get("tx_id"), - schema_name=metadata.get("schema_name"), - period_str=metadata.get("period_str"), language_code=metadata.get("language_code"), - launch_language_code=metadata.get("language_code"), - schema_url=metadata.get("schema_url"), - ru_name=metadata.get("ru_name"), - ru_ref=metadata.get("ru_ref"), - response_id=metadata.get("response_id"), - case_id=metadata["case_id"], - case_ref=metadata.get("case_ref"), - trad_as=metadata.get("trad_as"), - account_service_base_url=metadata.get("account_service_url"), - account_service_log_out_url=metadata.get("account_service_log_out_url"), ) diff --git a/app/data_models/session_data.py b/app/data_models/session_data.py index 31a3647098..723eda8a85 100644 --- a/app/data_models/session_data.py +++ b/app/data_models/session_data.py @@ -4,47 +4,11 @@ class SessionData: def __init__( self, - tx_id: Optional[str] = None, - schema_name: Optional[str] = None, - period_str: Optional[str] = None, language_code: Optional[str] = None, - launch_language_code: Optional[str] = None, - ru_name: Optional[str] = None, - ru_ref: Optional[str] = None, - response_id: Optional[str] = None, - case_id: Optional[str] = None, - case_ref: Optional[str] = None, - account_service_base_url: Optional[str] = None, - account_service_log_out_url: Optional[str] = None, - trad_as: Optional[str] = None, - display_address: Optional[str] = None, confirmation_email_count: int = 0, feedback_count: int = 0, - schema_url: Optional[str] = None, - survey_url: Optional[str] = None, # pylint: disable=unused-argument **_: Any, - ): # pylint: disable=too-many-locals + ): self.language_code = language_code self.confirmation_email_count = confirmation_email_count self.feedback_count = feedback_count - - # :TODO: Will be removed in the next deployment, kept temporarily to support in-flight sessions and potential rollback - self.tx_id = tx_id - self.schema_name = schema_name - self.period_str = period_str - self.launch_language_code = launch_language_code - self.ru_name = ru_name - self.ru_ref = ru_ref - self.response_id = response_id - self.case_id = case_id - self.case_ref = case_ref - self.trad_as = trad_as - self.account_service_base_url = account_service_base_url - self.account_service_log_out_url = account_service_log_out_url - self.display_address = display_address - self.schema_url = schema_url - - # :TODO: Remove once `schema_url` has been rolled out successfully. - # This is only to support a rollback in the event `schema_url` deploy is not successful. - # Survey URL will not be used to load surveys. - self.survey_url = None diff --git a/app/views/handlers/submission.py b/app/views/handlers/submission.py index 081e172828..1ca01c2784 100644 --- a/app/views/handlers/submission.py +++ b/app/views/handlers/submission.py @@ -43,7 +43,6 @@ def submit_questionnaire(self): cookie_session["submitted"] = True - self._store_display_address_in_session() self._questionnaire_store.submitted_at = self.submitted_at self._questionnaire_store.save() @@ -58,8 +57,3 @@ def get_payload(self): self._session_store.session_data.language_code or DEFAULT_LANGUAGE_CODE ) return payload - - def _store_display_address_in_session(self): - session_data = self._session_store.session_data - session_data.display_address = self._metadata.get("display_address") - self._session_store.save() diff --git a/tests/app/conftest.py b/tests/app/conftest.py index c1bbf5609d..8ad7adf83f 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -113,17 +113,14 @@ def fake_metadata(): @pytest.fixture def session_data(): return SessionData( - tx_id="tx_id", - schema_name="test_checkbox", - period_str="period_str", language_code=None, - launch_language_code=None, - schema_url=None, - ru_name="ru_name", - ru_ref="ru_ref", - response_id="response_id", - trad_as="trading_as", - case_id="case_id", + ) + + +@pytest.fixture +def session_data_with_language_code(): + return SessionData( + language_code="en", ) diff --git a/tests/app/data_model/test_session_data.py b/tests/app/data_model/test_session_data.py index 04af32d8b7..9ea26f2c96 100644 --- a/tests/app/data_model/test_session_data.py +++ b/tests/app/data_model/test_session_data.py @@ -6,42 +6,10 @@ def test_session_data_default_properties(): try: session_data = SessionData( - tx_id="123", - schema_name="some_schema_name", - period_str=None, language_code="cy", - launch_language_code="en", - ru_name=None, - ru_ref=None, - response_id="321", - case_id="789", ) except TypeError: return pytest.fail("An error occurred when creating session data") - assert session_data.case_ref is None - assert session_data.account_service_base_url is None - assert session_data.account_service_log_out_url is None - assert session_data.trad_as is None - assert session_data.display_address is None assert session_data.confirmation_email_count == 0 assert session_data.feedback_count == 0 - assert session_data.schema_url is None - assert session_data.survey_url is None - - -def test_session_data_survey_url_always_set_to_none(): - session_data = SessionData( - tx_id="123", - schema_name="some_schema_name", - period_str=None, - language_code="cy", - launch_language_code="en", - ru_name=None, - ru_ref=None, - response_id="321", - case_id="789", - survey_url="some-url", - ) - - assert session_data.survey_url is None diff --git a/tests/app/data_model/test_session_store.py b/tests/app/data_model/test_session_store.py index 1f3159acfa..6a0d391a4e 100644 --- a/tests/app/data_model/test_session_store.py +++ b/tests/app/data_model/test_session_store.py @@ -3,7 +3,6 @@ from jwcrypto import jwe from jwcrypto.common import base64url_encode -from app.data_models import SessionData from app.data_models.app_models import EQSession from app.data_models.session_store import SessionStore from app.utilities.json import json_dumps @@ -115,31 +114,30 @@ def test_session_store_ignores_multiple_new_values_in_session_data( ).save() session_store = SessionStore("user_ik", "pepper", "eq_session_id") - assert hasattr(session_store.session_data, "additional_value") is False assert hasattr(session_store.session_data, "second_additional_value") is False -def test_session_store_stores_trading_as_value_if_present( - app, app_session_store, session_data +def test_session_store_stores_language_code_value( + app, app_session_store, session_data_with_language_code ): with app.test_request_context(): app_session_store.session_store.create( eq_session_id="eq_session_id", user_id="test", - session_data=session_data, + session_data=session_data_with_language_code, expires_at=app_session_store.expires_at, ).save() session_store = SessionStore("user_ik", "pepper", "eq_session_id") - assert hasattr(session_store.session_data, "trad_as") is True + assert session_store.session_data.language_code == "en" -def test_session_store_stores_none_for_trading_as_if_not_present( +def test_session_store_stores_none_for_language_code_value( app, app_session_store, session_data ): - session_data.trad_as = None + session_data.language_code = None with app.test_request_context(): app_session_store.session_store.create( eq_session_id="eq_session_id", @@ -150,39 +148,12 @@ def test_session_store_stores_none_for_trading_as_if_not_present( session_store = SessionStore("user_ik", "pepper", "eq_session_id") - assert session_store.session_data.trad_as is None - - -def test_load_existing_session_does_not_error_when_session_data_contains_survey_url( - app, app_session_store -): - session_data_with_survey_url = SessionData( - language_code="cy", - survey_url="some-url", - ) - - with app.test_request_context(): - # Given a session store with session data that has a survey url - app_session_store.session_store.create( - eq_session_id="eq_session_id", - user_id="test", - session_data=session_data_with_survey_url, - expires_at=app_session_store.expires_at, - ).save() - - # When a SessionStore is loaded (Session matching 'eq_session_id' exists at this point) - loaded_session_store = SessionStore("user_ik", "pepper", "eq_session_id") - - # Then - assert ( - loaded_session_store.session_data.__dict__ - == session_data_with_survey_url.__dict__ - ) - assert loaded_session_store.session_data.survey_url is None + assert session_store.session_data.language_code is None @pytest.mark.usefixtures("app") def test_legacy_load(app_session_store_encoded): + _save_session( app_session_store_encoded, app_session_store_encoded.session_id, @@ -196,8 +167,8 @@ def test_legacy_load(app_session_store_encoded): app_session_store_encoded.session_id, ) - assert ( - session_store.session_data.tx_id == app_session_store_encoded.session_data.tx_id + assert vars(session_store.session_data) == vars( + app_session_store_encoded.session_data ) @@ -214,8 +185,9 @@ def test_load(app_session_store_encoded): app_session_store_encoded.pepper, app_session_store_encoded.session_id, ) - assert ( - session_store.session_data.tx_id == app_session_store_encoded.session_data.tx_id + + assert vars(session_store.session_data) == vars( + app_session_store_encoded.session_data ) From 85db4d9f71609edd9282eb02a777a2a036ee3f8e Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 31 Aug 2022 09:41:18 +0100 Subject: [PATCH 069/567] Fix DAC issue summary table headers (#903) --- app/translations/messages.pot | 93 ++++++++++--------- .../contexts/calculated_summary_context.py | 7 ++ app/views/contexts/section_summary_context.py | 6 ++ app/views/contexts/summary_context.py | 7 ++ package.json | 2 +- .../partials/summary/collapsible-summary.html | 2 +- templates/partials/summary/summary.html | 3 +- .../test_calculated_summary_context.py | 2 +- .../contexts/test_section_summary_context.py | 5 +- .../test_submitted_response_context.py | 11 +++ .../test_questionnaire_content_variants.py | 0 .../test_questionnaire_submit.py | 10 ++ .../routes/test_view_submitted_response.py | 3 + yarn.lock | 8 +- 14 files changed, 107 insertions(+), 52 deletions(-) delete mode 100644 tests/integration/questionnaire/test_questionnaire_content_variants.py diff --git a/app/translations/messages.pot b/app/translations/messages.pot index f0807c905c..e358361ea7 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-08-09 08:03+0100\n" +"POT-Creation-Date: 2022-08-23 14:24+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -296,11 +296,8 @@ msgid "An individual access code has been sent by text" msgstr "" #: app/survey_config/business_config.py:60 -#: app/survey_config/census_config.py:43 app/survey_config/census_config.py:59 -#: app/survey_config/census_config.py:106 -#: app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:185 -#: app/survey_config/census_config.py:195 +#: app/survey_config/census_config.py:41 app/survey_config/census_config.py:99 +#: app/survey_config/census_config.py:171 msgid "Help" msgstr "" @@ -312,42 +309,38 @@ msgstr "" msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:92 -#: app/survey_config/business_config.py:101 -#: app/survey_config/social_survey_config.py:39 -#: app/survey_config/social_survey_config.py:48 +#: app/survey_config/business_config.py:89 +#: app/survey_config/social_survey_config.py:36 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:93 -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:110 -#: app/survey_config/census_config.py:189 -#: app/survey_config/social_survey_config.py:40 +#: app/survey_config/business_config.py:92 +#: app/survey_config/census_config.py:48 app/survey_config/census_config.py:107 +#: app/survey_config/census_config.py:178 +#: app/survey_config/social_survey_config.py:39 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:95 -#: app/survey_config/business_config.py:103 -#: app/survey_config/social_survey_config.py:42 -#: app/survey_config/social_survey_config.py:50 +#: app/survey_config/business_config.py:96 +#: app/survey_config/social_survey_config.py:43 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:111 -#: app/survey_config/census_config.py:75 app/survey_config/census_config.py:140 -#: app/survey_config/census_config.py:203 -#: app/survey_config/social_survey_config.py:58 +#: app/survey_config/business_config.py:106 +#: app/survey_config/census_config.py:69 app/survey_config/census_config.py:127 +#: app/survey_config/census_config.py:186 +#: app/survey_config/social_survey_config.py:53 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:113 -#: app/survey_config/census_config.py:81 app/survey_config/census_config.py:146 -#: app/survey_config/census_config.py:209 -#: app/survey_config/social_survey_config.py:60 +#: app/survey_config/business_config.py:108 +#: app/survey_config/census_config.py:75 app/survey_config/census_config.py:133 +#: app/survey_config/census_config.py:192 +#: app/survey_config/social_survey_config.py:55 msgid "Privacy and data protection" msgstr "" -#: app/survey_config/business_config.py:144 +#: app/survey_config/business_config.py:139 msgid "Northern Ireland Department of Finance logo" msgstr "" @@ -359,37 +352,33 @@ msgstr "" msgid "Save and complete later" msgstr "" -#: app/survey_config/census_config.py:48 app/survey_config/census_config.py:63 -#: app/survey_config/census_config.py:113 -#: app/survey_config/census_config.py:128 +#: app/survey_config/census_config.py:54 app/survey_config/census_config.py:113 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:52 app/survey_config/census_config.py:67 -#: app/survey_config/census_config.py:117 -#: app/survey_config/census_config.py:132 +#: app/survey_config/census_config.py:58 app/survey_config/census_config.py:117 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:77 app/survey_config/census_config.py:142 -#: app/survey_config/census_config.py:205 +#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:129 +#: app/survey_config/census_config.py:188 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:85 app/survey_config/census_config.py:150 -#: app/survey_config/census_config.py:213 +#: app/survey_config/census_config.py:79 app/survey_config/census_config.py:137 +#: app/survey_config/census_config.py:196 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:165 +#: app/survey_config/census_config.py:152 msgid "Northern Ireland Statistics and Research Agency logo" msgstr "" -#: app/survey_config/census_config.py:170 +#: app/survey_config/census_config.py:157 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:173 app/survey_config/survey_config.py:23 +#: app/survey_config/census_config.py:160 app/survey_config/survey_config.py:23 msgid "Use of address data is subject to the terms and conditions." msgstr "" @@ -405,6 +394,24 @@ msgstr "" msgid "Save and exit survey" msgstr "" +#: app/views/contexts/calculated_summary_context.py:72 +#: app/views/contexts/section_summary_context.py:62 +#: app/views/contexts/summary_context.py:18 +msgid "Question" +msgstr "" + +#: app/views/contexts/calculated_summary_context.py:72 +#: app/views/contexts/section_summary_context.py:62 +#: app/views/contexts/summary_context.py:18 +msgid "Answer given" +msgstr "" + +#: app/views/contexts/calculated_summary_context.py:72 +#: app/views/contexts/section_summary_context.py:62 +#: app/views/contexts/summary_context.py:20 +msgid "Change answer" +msgstr "" + #: app/views/contexts/hub_context.py:14 msgid "Completed" msgstr "" @@ -1527,18 +1534,18 @@ msgid "Start survey" msgstr "" #: templates/partials/summary/collapsible-summary.html:37 -#: templates/partials/summary/summary.html:19 +#: templates/partials/summary/summary.html:20 msgid "No answer provided" msgstr "" #: templates/partials/summary/collapsible-summary.html:38 #: templates/partials/summary/list-summary.html:8 -#: templates/partials/summary/summary.html:20 +#: templates/partials/summary/summary.html:21 msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:39 -#: templates/partials/summary/summary.html:21 +#: templates/partials/summary/summary.html:22 msgid "Change your answer for:" msgstr "" diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 7dc2841aef..88a6964d64 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,5 +1,7 @@ from copy import deepcopy +from flask_babel import lazy_gettext + from app.jinja_filters import ( format_number, format_percentage, @@ -68,6 +70,11 @@ def build_view_context_for_calculated_summary(self, current_location): "title": block.get("title") % dict(total=formatted_total), "collapsible": block.get("collapsible", False), "summary_type": "CalculatedSummary", + "headers": [ + lazy_gettext("Question"), + lazy_gettext("Answer given"), + lazy_gettext("Change answer"), + ], } } diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index df3c87ada4..f5bec93721 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -2,6 +2,7 @@ from typing import Any, Mapping, Optional from flask import url_for +from flask_babel import lazy_gettext from app.data_models import AnswerStore, ListStore, ProgressStore from app.questionnaire import QuestionnaireSchema @@ -58,6 +59,11 @@ def __call__(self, return_to: Optional[str] = "section-summary") -> Mapping: "page_title": page_title, "summary_type": "SectionSummary", "answers_are_editable": True, + "headers": [ + lazy_gettext("Question"), + lazy_gettext("Answer given"), + lazy_gettext("Change answer"), + ], **summary, } } diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index a3b3c8f19d..88fb22ced0 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,5 +1,7 @@ from typing import Generator, Mapping, Optional, Union +from flask_babel import lazy_gettext + from app.questionnaire.location import Location from .context import Context @@ -14,11 +16,16 @@ def __call__( groups = list(self._build_all_groups(return_to)) summary_options = self._schema.get_summary_options() collapsible = summary_options.get("collapsible", False) + headers = [lazy_gettext("Question"), lazy_gettext("Answer given")] + if answers_are_editable: + headers.append(lazy_gettext("Change answer")) + return { "groups": groups, "answers_are_editable": answers_are_editable, "collapsible": collapsible, "summary_type": "Summary", + "headers": headers, } def _build_all_groups( diff --git a/package.json b/package.json index 0f0b48beb7..c95fe861a8 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^103.0.0", + "chromedriver": "^104.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index 4b0ac9f72f..d132d27adc 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -29,7 +29,7 @@ { "groups": [ { - "headers":["Question", "Answer given", "Change answer"], + "headers": content.summary.headers, "rows": map_summary_item_config( group, content.summary.summary_type, diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 4034a39946..6e22b332fa 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -4,6 +4,7 @@ {% from "components/summary/_macro.njk" import onsSummary %} {% set summaryGroups = [] %} + {%- for group in content.summary.groups if group.blocks -%} {% do summaryGroups.append ( { @@ -11,7 +12,7 @@ { "groupTitle": group.title if group.title else None, "id": group.id if group.id else None, - "headers": ["Question", "Answer given", "Change answer"], + "headers": content.summary.headers, "rows": map_summary_item_config( group, content.summary.summary_type, diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 0a5d0597d9..528b8401bd 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -94,7 +94,7 @@ def test_build_view_context_for_currency_calculated_summary( assert "summary" in context assert_summary_context(context) - assert len(context["summary"]) == 6 + assert len(context["summary"]) == 7 context_summary = context["summary"] assert "title" in context_summary assert context_summary["title"] == title diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 8fdb7a2ff4..9d66f672ae 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -53,7 +53,7 @@ def test_build_view_context_for_section_summary( assert "summary" in context assert_summary_context(context) - assert len(context["summary"]) == 6 + assert len(context["summary"]) == 7 assert "title" in context["summary"] @@ -152,6 +152,7 @@ def test_context_for_section_list_summary(people_answer_store): expected = { "summary": { "answers_are_editable": True, + "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { @@ -231,6 +232,7 @@ def test_context_for_driving_question_summary_empty_list(): expected = { "summary": { "answers_are_editable": True, + "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { @@ -285,6 +287,7 @@ def test_context_for_driving_question_summary(): expected = { "summary": { "answers_are_editable": True, + "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 2ef5b96421..3e5bf0e48c 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -111,6 +111,17 @@ def test_build_view_submitted_response_no_submitted_at(app: Flask): ) +def test_summary_headers_without_change_link( + app: Flask, +): + with app.app_context(): + questionnaire_store = fake_questionnaire_store() + context = build_view_submitted_response_context( + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT + ) + assert context["summary"]["headers"] == ["Question", "Answer given"] + + def fake_questionnaire_store(): storage = Mock() storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) diff --git a/tests/integration/questionnaire/test_questionnaire_content_variants.py b/tests/integration/questionnaire/test_questionnaire_content_variants.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/integration/questionnaire/test_questionnaire_submit.py b/tests/integration/questionnaire/test_questionnaire_submit.py index da2f60860e..e0b425c680 100644 --- a/tests/integration/questionnaire/test_questionnaire_submit.py +++ b/tests/integration/questionnaire/test_questionnaire_submit.py @@ -127,3 +127,13 @@ def test_is_displayed(self): self.post() self.assertInUrl(THANK_YOU_URL_PATH) + + def test_summary_headers_with_change_link(self): + self.launchSurvey("test_section_summary") + self.post() + self.post() + self.post() + self.post() + self.assertInBody("Question") + self.assertInBody("Answer given") + self.assertInBody("Change answer") diff --git a/tests/integration/routes/test_view_submitted_response.py b/tests/integration/routes/test_view_submitted_response.py index 2e4f92e41d..a6ceddc1d9 100644 --- a/tests/integration/routes/test_view_submitted_response.py +++ b/tests/integration/routes/test_view_submitted_response.py @@ -58,6 +58,9 @@ def test_enabled(self): self.assertInBody("NP10 8XG") self.assertIsNotNone(self.get_print_button()) self.assertIsNotNone(self.get_download_button()) + self.assertInBody("Question") + self.assertInBody("Answer given") + self.assertNotInBody("Change answer") def test_not_enabled(self): # Given I launch and complete a questionnaire that does not have view-submitted-response enabled diff --git a/yarn.lock b/yarn.lock index 958e87b2ff..ee1d4a4a24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1892,10 +1892,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^103.0.0: - version "103.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-103.0.0.tgz#2ef086d62076e3ff6df6cfb84895d11d2c18d9cf" - integrity sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w== +chromedriver@^104.0.0: + version "104.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-104.0.0.tgz#2f730f52a567280872567bf3497e1c673b6f4275" + integrity sha512-zbHZutN2ATo19xA6nXwwLn+KueD/5w8ap5m4b6bCb8MIaRFnyDwMbFoy7oFAjlSMpCFL3KSaZRiWUwjj//N3yQ== dependencies: "@testim/chrome-version" "^1.1.2" axios "^0.27.2" From 13a0802276f22bdf90b5d77e36717428a1b7ca9d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 31 Aug 2022 13:33:44 +0100 Subject: [PATCH 070/567] Add conditional links in cookie banner (#919) --- app/helpers/template_helpers.py | 8 ++- app/settings.py | 2 +- app/survey_config/survey_config.py | 4 ++ app/translations/messages.pot | 12 ++-- templates/layouts/_base.html | 4 +- tests/app/helpers/test_template_helpers.py | 74 +++++++++++++++++++-- tests/functional/spec/cookie_banner.spec.js | 32 +++++++++ 7 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 tests/functional/spec/cookie_banner.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index cee8fec5be..aa4a5430aa 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -54,14 +54,13 @@ def __init__( @property def context(self) -> dict[str, Any]: - return { + context = { "sign_out_button_text": self._survey_config.sign_out_button_text, "account_service_my_account_url": self._survey_config.account_service_my_account_url, "account_service_log_out_url": self._survey_config.account_service_log_out_url, "account_service_todo_url": self._survey_config.account_service_todo_url, "contact_us_url": self._survey_config.contact_us_url, "thank_you_url": url_for("post_submission.get_thank_you"), - "cookie_settings_url": self._survey_config.cookie_settings_url, "page_header": self.page_header_context, "service_links": self.service_links_context, "footer": self.footer_context, @@ -77,6 +76,11 @@ def context(self) -> dict[str, Any]: "google_tag_manager_auth": self._google_tag_manager_auth, "survey_type": self._survey_type, } + if self._survey_type: + context["cookie_settings_url"] = self._survey_config.cookie_settings_url + context["cookie_domain"] = self._survey_config.cookie_domain + + return context @property def service_links_context( diff --git a/app/settings.py b/app/settings.py index b80fa0be9d..b11d8747ca 100644 --- a/app/settings.py +++ b/app/settings.py @@ -149,7 +149,7 @@ def utcoffset_or_fail(date_value, key): "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" ) -ACCOUNT_SERVICE_BASE_URL_SOCIAL = "https://rh.ons.gov.uk" +ACCOUNT_SERVICE_BASE_URL_SOCIAL = "https://start.surveys.ons.gov.uk" PRINT_STYLE_SHEET_FILE_PATH = os.getenv( "PRINT_STYLE_SHEET_FILEPATH", "templates/assets/styles" diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 2641e7c0e7..fa6c17bc27 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -46,11 +46,15 @@ class SurveyConfig: sign_out_button_text: str = lazy_gettext("Save and exit survey") contact_us_url: str = field(init=False) cookie_settings_url: str = field(init=False) + cookie_domain: str = field(init=False) privacy_and_data_protection_url: str = field(init=False) def __post_init__(self): self.contact_us_url: str = f"{self.base_url}/contact-us/" self.cookie_settings_url: str = f"{self.base_url}/cookies/" + self.cookie_domain: str = self.cookie_settings_url.split("://")[-1].split("/")[ + 0 + ] # get the FQDN of the cookie settings URL self.privacy_and_data_protection_url: str = ( f"{self.base_url}/privacy-and-data-protection/" ) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index e358361ea7..cc79730ff6 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-08-23 14:24+0100\n" +"POT-Creation-Date: 2022-08-25 10:23+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -203,15 +203,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:94 +#: app/helpers/template_helpers.py:101 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:136 +#: app/helpers/template_helpers.py:143 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:170 +#: app/helpers/template_helpers.py:177 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -1221,8 +1221,8 @@ msgstr "" #: templates/layouts/_base.html:72 msgid "" "We use cookies to collect information" -" about how you use surveys.ons.gov.uk. We use this information to make " -"the website work as well as possible and improve our services." +" about how you use {cookie_domain}. We use this information to make the " +"website work as well as possible and improve our services." msgstr "" #: templates/layouts/_base.html:73 diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 6f81b35ff8..9e595f0a5d 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -64,12 +64,12 @@ {% endif %} - {% if cookie_settings_url %} + {% if cookie_settings_url and cookie_domain %} {{ onsCookiesBanner({ "secondaryButtonUrl": cookie_settings_url, "statementTitle": _('Tell us whether you accept cookies'), - "statementText": _("We use cookies to collect information about how you use surveys.ons.gov.uk. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url), + "statementText": _("We use cookies to collect information about how you use {cookie_domain}. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url, cookie_domain=cookie_domain), "confirmationText": _("You’ve accepted all cookies. You can change your cookie preferences at any time.").format(cookie_settings_url=cookie_settings_url), "primaryButtonText": _('Accept all cookies'), "secondaryButtonText": _('Set cookie preferences'), diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 3d00c96e11..92281ae0db 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -400,37 +400,101 @@ def test_sign_out_button_text_context( @pytest.mark.parametrize( - "survey_config, expected", + "survey_config, cookie_present, expected", [ - (SurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/cookies/"), + (SurveyConfig(), True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/"), ( BusinessSurveyConfig(), + True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( NorthernIrelandBusinessSurveyConfig(), + True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( SocialSurveyConfig(), + True, f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cookies/", ), + (SurveyConfig(), False, None), ], ) def test_cookie_settings_url_context( - app: Flask, survey_config: SurveyConfig, expected: str + app: Flask, survey_config: SurveyConfig, cookie_present: bool, expected: str ): with app.app_context(): - result = ContextHelper( + if cookie_present: + cookie_session["theme"] = "dummy_value" + context_helper = ContextHelper( language="en", is_post_submission=False, include_csrf_token=True, survey_config=survey_config, - ).context["cookie_settings_url"] + ) + result = context_helper.context.get("cookie_settings_url") assert result == expected +@pytest.mark.parametrize( + "survey_config, address", + [ + (SurveyConfig(), ACCOUNT_SERVICE_BASE_URL), + ( + BusinessSurveyConfig(), + ACCOUNT_SERVICE_BASE_URL, + ), + ( + NorthernIrelandBusinessSurveyConfig(), + ACCOUNT_SERVICE_BASE_URL, + ), + ( + SocialSurveyConfig(), + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ), + ], +) +def test_cookie_domain_context(app: Flask, survey_config: SurveyConfig, address: str): + with app.app_context(): + cookie_session["theme"] = "dummy_value" + context_helper = ContextHelper( + language="en", + is_post_submission=False, + include_csrf_token=True, + survey_config=survey_config, + ) + + expected = address.replace("https://", "") + result = context_helper.context.get("cookie_domain") + + assert result == expected + + +@pytest.mark.parametrize( + "survey_config", + [ + SurveyConfig(), + BusinessSurveyConfig(), + NorthernIrelandBusinessSurveyConfig(), + SocialSurveyConfig(), + ], +) +def test_cookie_domain_context_cookie_not_provided( + app: Flask, survey_config: SurveyConfig +): + with app.app_context(): + context_helper = ContextHelper( + language="en", + is_post_submission=False, + include_csrf_token=True, + survey_config=survey_config, + ) + + assert "cookie_domain" not in context_helper.context + + @pytest.mark.parametrize( "survey_config, expected", [ diff --git a/tests/functional/spec/cookie_banner.spec.js b/tests/functional/spec/cookie_banner.spec.js new file mode 100644 index 0000000000..b8ebfb52f6 --- /dev/null +++ b/tests/functional/spec/cookie_banner.spec.js @@ -0,0 +1,32 @@ +import InitialPage from "../generated_pages/checkbox/mandatory-checkbox.page"; + +describe("Given I am not authenticated and have no cookie,", () => { + it("When I visit a page in runner, Then the cookie banner shouldn‘t be displayed", () => { + browser.url("/"); + expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + }); +}); + +describe("Given I start a survey,", () => { + beforeEach(() => { + browser.openQuestionnaire("test_checkbox.json"); + }); + it("When I open the page, Then the cookie banner should be displayed", () => { + expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + }); + it("When I delete all cookies from the browser and refresh the page, Then the cookie banner shouldn‘t be displayed", () => { + browser.deleteAllCookies(); + browser.refresh(); + expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + }); + it("When I sign out and click the browser back button, Then the cookie banner should be displayed", () => { + $(InitialPage.saveSignOut()).click(); + browser.back(); + expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + }); + it("When I accept the cookies and refresh the page, Then the cookie banner shouldn‘t be displayed", () => { + $(InitialPage.acceptCookies()).click(); + browser.refresh(); + expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + }); +}); From a95f154471b6ceb07abfb5c2f520e0207e3b76ac Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:45:33 +0100 Subject: [PATCH 071/567] Schemas v3.12.0 (#925) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 89d85a8a9b..b0ab92d423 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.11.2 +v3.12.0 From 6991fec3ee2b960f5e3553537d8989d15383cdf3 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 5 Sep 2022 12:03:34 +0100 Subject: [PATCH 072/567] Upgrade to DS v55 (#913) --- .design-system-version | 2 +- templates/partials/answers/currency.html | 1 + templates/partials/answers/duration.html | 2 + templates/partials/answers/percentage.html | 1 + templates/partials/answers/unit.html | 2 +- tests/functional/base_pages/submit.page.js | 2 +- tests/functional/generate_pages.py | 7 --- .../submit_page_collapsible_summary.spec.js | 8 +-- .../spec/interstitial_definition.spec.js | 26 ++-------- .../spec/question_definitions.spec.js | 49 +++---------------- 10 files changed, 24 insertions(+), 76 deletions(-) diff --git a/.design-system-version b/.design-system-version index 9b9e2eedff..7bfbc462cc 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -53.1.1 +55.1.0 diff --git a/templates/partials/answers/currency.html b/templates/partials/answers/currency.html index f2587c2f7e..7cbd7e1be6 100644 --- a/templates/partials/answers/currency.html +++ b/templates/partials/answers/currency.html @@ -13,6 +13,7 @@ "description": answer.description }, "prefix": { + "id": answer.id ~ "-type", "title": answer.currency, "text": get_currency_symbol(answer.currency) }, diff --git a/templates/partials/answers/duration.html b/templates/partials/answers/duration.html index b967b47815..0259ec477f 100644 --- a/templates/partials/answers/duration.html +++ b/templates/partials/answers/duration.html @@ -17,6 +17,7 @@ "name": years.name, "value": years.data if years.data is not none else '', "suffix": { + "id": years.id ~ "-type", "text": _(years.label.text) } }) %} @@ -28,6 +29,7 @@ "name": months.name, "value": months.data if months.data is not none else '', "suffix": { + "id": months.id ~ "-type", "text": _(months.label.text) } }) %} diff --git a/templates/partials/answers/percentage.html b/templates/partials/answers/percentage.html index 03b2021efd..1c929c0a32 100644 --- a/templates/partials/answers/percentage.html +++ b/templates/partials/answers/percentage.html @@ -17,6 +17,7 @@ "data-qa": "input-text" }, "suffix": { + "id": answer.id ~ "-type", "title": "%" }, "dontWrap": true if mutuallyExclusive else false, diff --git a/templates/partials/answers/unit.html b/templates/partials/answers/unit.html index 9d65d346d3..cf21db83f2 100644 --- a/templates/partials/answers/unit.html +++ b/templates/partials/answers/unit.html @@ -14,7 +14,7 @@ "description": answer.description }, "suffix": { - "id": answer.id + "-type", + "id": answer.id ~ "-type", "title": format_unit_input_label(answer.unit, unit_length="long"), "text": format_unit_input_label(answer.unit, unit_length=answer.unit_length) }, diff --git a/tests/functional/base_pages/submit.page.js b/tests/functional/base_pages/submit.page.js index c07e9f6b92..97ae06719c 100644 --- a/tests/functional/base_pages/submit.page.js +++ b/tests/functional/base_pages/submit.page.js @@ -14,7 +14,7 @@ class SubmitBasePage extends BasePage { } summaryShowAllButton() { - return ".ons-js-collapsible-all"; + return ".ons-js-accordion-all"; } } diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 028c123107..03c055de22 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -95,12 +95,6 @@ """ ) -DEFINITION_BUTTON_GETTER = Template( - r""" definitionButton(definitionIndex) { return `[data-qa='${definitionId}-${definitionIndex}-button']`; } - -""" -) - GUIDANCE_PANEL_GETTER = Template( r""" guidancePanel(guidanceIndex) { return `[data-qa='${guidanceId}-${guidanceIndex}']`; } @@ -544,7 +538,6 @@ def process_view_submitted_response(schema_data, require_path, dir_out, spec_fil def process_definition(context, page_spec): page_spec.write(DEFINITION_TITLE_GETTER.safe_substitute(context)) page_spec.write(DEFINITION_CONTENT_GETTER.safe_substitute(context)) - page_spec.write(DEFINITION_BUTTON_GETTER.safe_substitute(context)) def process_guidance(context, page_spec): diff --git a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js index 45db6ceab9..a7d8247977 100644 --- a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js @@ -30,15 +30,15 @@ describe("Collapsible Summary", () => { expect($(SubmitPage.collapsibleSummary()).getText()).to.contain("Property Details"); expect($(SubmitPage.collapsibleSummary()).getText()).to.contain("House Details"); - expect($(SubmitPage.insuranceAddressQuestion()).isDisplayed()).to.be.false; - expect($(SubmitPage.numberOfPeopleQuestion()).isDisplayed()).to.be.false; + expect($(SubmitPage.insuranceAddressQuestion()).getText()).to.equal(""); + expect($(SubmitPage.numberOfPeopleQuestion()).getText()).to.equal(""); }); it("When I click the Show all button, Then the summary should be expanded and questions should be displayed", () => { $(SubmitPage.summaryShowAllButton()).click(); - expect($(SubmitPage.insuranceAddressQuestion()).isDisplayed()).to.be.true; - expect($(SubmitPage.numberOfPeopleQuestion()).isDisplayed()).to.be.true; + expect($(SubmitPage.insuranceAddressQuestion()).getText()).to.contain("What is the address you would like to insure?"); + expect($(SubmitPage.numberOfPeopleQuestion()).getText()).to.contain("Title"); }); }); }); diff --git a/tests/functional/spec/interstitial_definition.spec.js b/tests/functional/spec/interstitial_definition.spec.js index 2e1d97855b..9b09a8ed90 100644 --- a/tests/functional/spec/interstitial_definition.spec.js +++ b/tests/functional/spec/interstitial_definition.spec.js @@ -8,36 +8,20 @@ describe("Component: Interstitial Definition", () => { it("When there is a definition on an interstitial, then the page is displayed correctly", () => { expect($(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(1)).isDisplayed()).to.be.false; - expect($(InterstitialDefinitionPage.definitionButton(1)).isDisplayed()).to.be.false; + expect($(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal(""); expect($(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(2)).isDisplayed()).to.be.false; - expect($(InterstitialDefinitionPage.definitionButton(2)).isDisplayed()).to.be.false; + expect($(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); }); - it("When I click on a definition title, the content and button is display for just that definition", () => { + it("When I click on a definition title, the content is displayed for just that definition", () => { $(InterstitialDefinitionPage.definitionTitle(1)).click(); expect($(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionButton(1)).isDisplayed()).to.be.true; + expect($(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal("In a way that accomplishes a desired aim or result"); expect($(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(2)).isDisplayed()).to.be.false; - expect($(InterstitialDefinitionPage.definitionButton(2)).isDisplayed()).to.be.false; - }); - - it("When I click on the hide content button, then the page is displayed correctly", () => { - $(InterstitialDefinitionPage.definitionButton(1)).click(); - - expect($(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(1)).isDisplayed()).to.be.false; - expect($(InterstitialDefinitionPage.definitionButton(1)).isDisplayed()).to.be.false; - - expect($(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(2)).isDisplayed()).to.be.false; - expect($(InterstitialDefinitionPage.definitionButton(2)).isDisplayed()).to.be.false; + expect($(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); }); }); }); diff --git a/tests/functional/spec/question_definitions.spec.js b/tests/functional/spec/question_definitions.spec.js index 4984b9df9f..81a15e9238 100644 --- a/tests/functional/spec/question_definitions.spec.js +++ b/tests/functional/spec/question_definitions.spec.js @@ -6,60 +6,27 @@ describe("Component: Definition", () => { browser.openQuestionnaire("test_question_definition.json"); }); - it('When I click the title link, then the description and "Hide this" button should be visible', () => { - expect($(DefinitionPage.definitionContent(1)).isDisplayed()).to.be.false; - expect($(DefinitionPage.definitionButton(1)).isDisplayed()).to.be.false; + it("When I click the title link, then the description should be visible", () => { + expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); // When $(DefinitionPage.definitionTitle("1")).click(); // Then - $(DefinitionPage.definitionContent(1)).waitForDisplayed({ timeout: 300 }); - $(DefinitionPage.definitionButton(1)).waitForDisplayed({ timeout: 300 }); + expect($(DefinitionPage.definitionContent(1)).getText()).to.contain( + "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." + ); }); - it('When I click the title link twice, then the description and "Hide this" button should not be visible', () => { - expect($(DefinitionPage.definitionContent(1)).isDisplayed()).to.be.false; - expect($(DefinitionPage.definitionButton(1)).isDisplayed()).to.be.false; + it("When I click the title link twice, then the description should not be visible", () => { + expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); // When $(DefinitionPage.definitionTitle("1")).click(); $(DefinitionPage.definitionTitle("1")).click(); // Then - $(DefinitionPage.definitionContent(1)).waitForDisplayed({ timeout: 300, reverse: true }); - $(DefinitionPage.definitionButton(1)).waitForDisplayed({ timeout: 300, reverse: true }); - }); - - it('When I click the title link then click "Hide this" button, then the description and button should not be visible', () => { - expect($(DefinitionPage.definitionContent(1)).isDisplayed()).to.be.false; - expect($(DefinitionPage.definitionButton(1)).isDisplayed()).to.be.false; - - // When - $(DefinitionPage.definitionTitle("1")).click(); - - // Then - $(DefinitionPage.definitionContent(1)).waitForDisplayed({ timeout: 300 }); - $(DefinitionPage.definitionButton(1)).waitForDisplayed({ timeout: 300 }); - - // When - $(DefinitionPage.definitionButton(1)).click(); - - // Then - $(DefinitionPage.definitionContent(1)).waitForDisplayed({ timeout: 300, reverse: true }); - $(DefinitionPage.definitionButton(1)).waitForDisplayed({ timeout: 300, reverse: true }); - }); - - it('When I click the second definition\'s title link then the description and "Hide this" button for the second definition should be visible', () => { - expect($(DefinitionPage.definitionContent(2)).isDisplayed()).to.be.false; - expect($(DefinitionPage.definitionButton(2)).isDisplayed()).to.be.false; - - // When - $(DefinitionPage.definitionTitle("2")).click(); - - // Then - $(DefinitionPage.definitionContent(2)).waitForDisplayed({ timeout: 300 }); - $(DefinitionPage.definitionButton(2)).waitForDisplayed({ timeout: 300 }); + expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); }); }); }); From 07099d6b4948528d80ad6563cfeca21eb120185f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 6 Sep 2022 08:56:58 +0100 Subject: [PATCH 073/567] Schemas v3.13.0 (#926) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b0ab92d423..017d61fcd9 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.12.0 +v3.13.0 From f9122f9160f83b1bf6ad051faa02dd5bbd5ccb6d Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:14:56 +0100 Subject: [PATCH 074/567] schemas v3.14.0 (#928) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 017d61fcd9..5ef3202ea0 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.13.0 +v3.14.0 From cdd7e8a0771727d394d8df383169af68b4b519dd Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 9 Sep 2022 10:59:28 +0100 Subject: [PATCH 075/567] Check that operators are of type Mapping (#922) --- app/questionnaire/path_finder.py | 11 +++----- app/questionnaire/questionnaire_schema.py | 18 ++++++------ app/questionnaire/rules/operations.py | 5 +--- app/questionnaire/rules/rule_evaluator.py | 6 ++-- .../test/en/test_confirmation_question.json | 6 ++-- .../en/test_new_confirmation_question.json | 6 ++-- tests/app/questionnaire/test_path_finder.py | 6 ++-- .../test_questionnaire_schema.py | 28 +++++++++++++++++++ .../features/confirmation_question.spec.js | 2 +- 9 files changed, 56 insertions(+), 32 deletions(-) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 1667c35dfb..d16fed872b 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -8,7 +8,6 @@ from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath -from app.questionnaire.rules.operator import OPERATION_MAPPING from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.when_rules import evaluate_goto, evaluate_when_rules @@ -304,17 +303,15 @@ def _remove_current_blocks_answers_for_new_backwards_routing( self, rules: dict, answer_ids_for_current_block: list[str] ) -> None: operands = self.schema.get_operands(rules) + for rule in operands: if isinstance(rule, dict) and ( "identifier" in rule and rule["identifier"] in answer_ids_for_current_block ): - if ( - "identifier" in rule - and rule["identifier"] in answer_ids_for_current_block - ): - self.answer_store.remove_answer(rule["identifier"]) - if any(operator in rule for operator in OPERATION_MAPPING): + self.answer_store.remove_answer(rule["identifier"]) + + if QuestionnaireSchema.has_operator(rule): return self._remove_current_blocks_answers_for_new_backwards_routing( rule, answer_ids_for_current_block ) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 303051c2aa..c5f289d085 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -380,9 +380,7 @@ def get_post_submission(self) -> ImmutableDict: def _is_list_name_in_rule( self, rules: Union[Mapping, Sequence], list_name: str ) -> bool: - if isinstance(rules, Mapping) and any( - operator in rules for operator in OPERATION_MAPPING - ): + if isinstance(rules, Mapping) and QuestionnaireSchema.has_operator(rules): rules = self.get_operands(rules) for rule in rules: @@ -400,7 +398,7 @@ def _is_list_name_in_rule( ) # Nested rules - if any(operator in rule for operator in OPERATION_MAPPING): + if QuestionnaireSchema.has_operator(rule): return self._is_list_name_in_rule(rule, list_name) @staticmethod @@ -770,6 +768,12 @@ def has_address_lookup_answer(question: Mapping) -> bool: if answer["type"] == "Address" and "lookup_options" in answer ) + @staticmethod + def has_operator(rule: Any) -> bool: + return isinstance(rule, Mapping) and any( + operator in rule for operator in OPERATION_MAPPING + ) + def _get_values_for_key( self, block: Mapping, key: str, ignore_keys: Optional[list[str]] = None ) -> Generator: @@ -853,9 +857,7 @@ def _get_rules_section_dependencies( ) -> set[str]: rules_section_dependencies: set[str] = set() - if isinstance(rules, Mapping) and any( - operator in rules for operator in OPERATION_MAPPING - ): + if isinstance(rules, Mapping) and QuestionnaireSchema.has_operator(rules): rules = self.get_operands(rules) for rule in rules: @@ -885,7 +887,7 @@ def _get_rules_section_dependencies( ) rules_section_dependencies.add(section_id) # type: ignore - if any(operator in rule for operator in OPERATION_MAPPING): + if QuestionnaireSchema.has_operator(rule): rules_section_dependencies.update( self._get_rules_section_dependencies(current_section_id, rule) ) diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 0c2953f939..b9266fae18 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -191,9 +191,7 @@ def _resolve_self_reference( ) -> list[Union[ValueSourceTypes, date]]: resolved_operands = [] for operand in operands: - if isinstance(operand, dict) and any( - operator in operand for operator in OPERATION_MAPPING - ): + if isinstance(operand, dict) and QuestionnaireSchema.has_operator(operand): operator_name = next(iter(operand)) resolved_nested_operands = self._resolve_self_reference( self_reference_value, operand[operator_name] @@ -201,7 +199,6 @@ def _resolve_self_reference( resolved_value = getattr(self, OPERATION_MAPPING[operator_name])( *resolved_nested_operands ) - else: resolved_value = ( self_reference_value if operand == SELF_REFERENCE_KEY else operand diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index c6f0be0d39..2e5140f807 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -8,7 +8,7 @@ from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.operations import Operations -from app.questionnaire.rules.operator import OPERATION_MAPPING, Operator +from app.questionnaire.rules.operator import Operator from app.questionnaire.value_source_resolver import ( ValueSourceResolver, ValueSourceTypes, @@ -81,9 +81,7 @@ def _resolve_operand( if isinstance(operand, dict) and "source" in operand: return self.value_source_resolver.resolve(operand) - if isinstance(operand, dict) and any( - operator in operand for operator in OPERATION_MAPPING - ): + if QuestionnaireSchema.has_operator(operand) and isinstance(operand, dict): return self._evaluate(operand) return operand diff --git a/schemas/test/en/test_confirmation_question.json b/schemas/test/en/test_confirmation_question.json index fd104db534..14308c2362 100644 --- a/schemas/test/en/test_confirmation_question.json +++ b/schemas/test/en/test_confirmation_question.json @@ -114,8 +114,8 @@ "value": "Yes this is correct" }, { - "label": "No I need to change this", - "value": "No I need to change this" + "label": "No I need to correct this", + "value": "No I need to correct this" } ], "mandatory": true @@ -153,7 +153,7 @@ "goto": { "when": [ { - "value": "No I need to change this", + "value": "No I need to correct this", "id": "confirm-zero-employees-answer", "condition": "equals" } diff --git a/schemas/test/en/test_new_confirmation_question.json b/schemas/test/en/test_new_confirmation_question.json index 6e8af6989d..53555fb1cc 100644 --- a/schemas/test/en/test_new_confirmation_question.json +++ b/schemas/test/en/test_new_confirmation_question.json @@ -129,8 +129,8 @@ "value": "Yes" }, { - "label": "No", - "value": "No" + "label": "No I need to correct this", + "value": "No I need to correct this" } ], "mandatory": true @@ -150,7 +150,7 @@ "source": "answers", "identifier": "confirm-zero-employees-answer" }, - "No" + "No I need to correct this" ] }, { diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index 0c1c9096f7..ec19ed14bc 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -431,7 +431,7 @@ def test_remove_answer_and_block_if_routing_backwards(list_store): 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_id="confirm-zero-employees-answer", value="No I need to correct this" ) answer_store = AnswerStore({}) answer_store.add_or_update(number_of_employees_answer) @@ -497,7 +497,9 @@ def test_new_remove_answer_and_block_if_routing_backwards(list_store): answer_store = AnswerStore() route_backwards_answer = Answer(answer_id="route-backwards-answer", value="Yes") 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") + confirm_zero_answer = Answer( + answer_id="confirm-zero-employees-answer", value="No I need to correct this" + ) answer_store.add_or_update(route_backwards_answer) answer_store.add_or_update(number_of_employees_answer) answer_store.add_or_update(confirm_zero_answer) diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 1498f21ce7..3230447f0b 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -771,3 +771,31 @@ def test_when_rules_section_dependencies_calculated_summary( "bread-answer": {"dependent-enabled-section", "dependent-question-section"}, "cheese-answer": {"dependent-enabled-section", "dependent-question-section"}, } == schema.when_rules_section_dependencies_by_answer + + +@pytest.mark.parametrize( + "rule, expected_result", + ( + ([], False), + ("This is a string", False), + ({"key": "value"}, False), + ( + {"invalid-operator": ({"source": "answers", "identifier": "answer"}, 123)}, + False, + ), + ({"==": ({"source": "answers", "identifier": "answer"}, 123)}, True), + ({">": ({"source": "answers", "identifier": "answer"}, 123)}, True), + ( + { + "or": ( + {"source": "answers", "identifier": "answer"}, + "No I need to correct this", + ) + }, + True, + ), + ), +) +def test_has_operator_returns_correct_value(rule, expected_result): + result = QuestionnaireSchema.has_operator(rule) + assert result == expected_result diff --git a/tests/functional/spec/features/confirmation_question.spec.js b/tests/functional/spec/features/confirmation_question.spec.js index f67c3dacff..a23954fa97 100644 --- a/tests/functional/spec/features/confirmation_question.spec.js +++ b/tests/functional/spec/features/confirmation_question.spec.js @@ -22,7 +22,7 @@ describe("Feature: Confirmation Question", () => { it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", () => { browser.openQuestionnaire("test_confirmation_question.json"); $(NumberOfEmployeesTotalBlockPage.submit()).click(); - $(ConfirmZeroEmployeesBlockPage.noINeedToChangeThis()).click(); + $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); $(ConfirmZeroEmployeesBlockPage.submit()).click(); expect(browser.getUrl()).to.contain(NumberOfEmployeesTotalBlockPage.pageName); }); From 1a16aaba004fbfeffa3392fee98a707e4b908027 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 14 Sep 2022 15:54:13 +0100 Subject: [PATCH 076/567] schemas v3.15.0 (#929) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5ef3202ea0..64d4aab162 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.14.0 +v3.15.0 From 7f73633ce2b7e2e4f1819b2e901899ea5ceaf338 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:34:11 +0100 Subject: [PATCH 077/567] update id name and remove unneeded param (#927) --- schemas/test/en/test_benchmark_business.json | 2 +- templates/layouts/configs/_save-sign-out-button.html | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index f33ec283ad..8151270c36 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -25,7 +25,7 @@ "title": "Quarterly Stocks Survey", "groups": [ { - "id": "groupquestionnaire-introduction", + "id": "group-questionnaire-introduction", "title": "Introduction", "blocks": [ { diff --git a/templates/layouts/configs/_save-sign-out-button.html b/templates/layouts/configs/_save-sign-out-button.html index faa7b6f3ca..a645f3f15d 100644 --- a/templates/layouts/configs/_save-sign-out-button.html +++ b/templates/layouts/configs/_save-sign-out-button.html @@ -4,7 +4,6 @@ {% if save_on_signout %} {% do pageConfig | setAttribute("signoutButton", { "text": sign_out_button_text, - "name": "btn-save-sign-out", "url": sign_out_url, "attributes": { "data-qa": "btn-save-sign-out", @@ -19,7 +18,6 @@ {% else %} {% do pageConfig | setAttribute("signoutButton", { "text": _("Exit"), - "name": "btn-exit", "url": sign_out_url, "attributes": { "data-qa": "btn-exit", From 18b84e1c66b57bb1eaae5dbd2dfb697d513b639b Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:48:22 +0100 Subject: [PATCH 078/567] Change jinja config to mimic nunjucks more closely (#909) --- .design-system-version | 2 +- app/setup.py | 2 ++ tests/functional/generate_pages.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.design-system-version b/.design-system-version index 7bfbc462cc..afb1988b41 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -55.1.0 +55.2.0 diff --git a/app/setup.py b/app/setup.py index 7cfd7fc193..70653943e0 100644 --- a/app/setup.py +++ b/app/setup.py @@ -15,6 +15,7 @@ from flask_wtf.csrf import CSRFProtect from google.cloud import datastore from htmlmin.main import minify +from jinja2 import ChainableUndefined from sdc.crypto.key_store import KeyStore, validate_required_keys from structlog import get_logger @@ -238,6 +239,7 @@ def setup_jinja_env(application): # Enable whitespace removal application.jinja_env.trim_blocks = True application.jinja_env.lstrip_blocks = True + application.jinja_env.undefined = ChainableUndefined # Switch off flask default autoescaping as schema content can contain html application.jinja_env.autoescape = False diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 03c055de22..1c2d19f036 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -173,7 +173,7 @@ ANSWER_SUFFIX_GETTER = Template( r""" ${answerName}Suffix() { - return `#${answerId} + abbr`; + return `#${answerId} + span`; } """ From c7f4054b4e9abc3d80931c81ce8e8ec49eed29e6 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 21 Sep 2022 10:33:28 +0100 Subject: [PATCH 079/567] Fix percentage symbol missing on input (#930) --- templates/partials/answers/percentage.html | 3 ++- tests/integration/components/test_render_percentage_widget.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/partials/answers/percentage.html b/templates/partials/answers/percentage.html index 1c929c0a32..bf1683d254 100644 --- a/templates/partials/answers/percentage.html +++ b/templates/partials/answers/percentage.html @@ -18,7 +18,8 @@ }, "suffix": { "id": answer.id ~ "-type", - "title": "%" + "title": "Percent", + "text": "%" }, "dontWrap": true if mutuallyExclusive else false, "mutuallyExclusive": mutuallyExclusive, diff --git a/tests/integration/components/test_render_percentage_widget.py b/tests/integration/components/test_render_percentage_widget.py index 2502a7f0b5..f1e82551eb 100644 --- a/tests/integration/components/test_render_percentage_widget.py +++ b/tests/integration/components/test_render_percentage_widget.py @@ -8,7 +8,7 @@ def setUp(self): self.launchSurvey("test_percentage") def test_percentage_widget_has_icon(self): - self.assertInSelectorCSS("%", "abbr", {"class": "ons-input-type__type"}) + self.assertInSelectorCSS(">%", "abbr", {"class": "ons-input-type__type"}) def test_entering_invalid_number_displays_error(self): self.post({"answer": "not a percentage"}) From 62c51b214158cdeb8a91f63e69b3dde7df591950 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 28 Sep 2022 11:55:08 +0100 Subject: [PATCH 080/567] Update Chromedriver to v105 (#938) --- package.json | 2 +- yarn.lock | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index c95fe861a8..4c640d24cb 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^104.0.0", + "chromedriver": "^105.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index ee1d4a4a24..e257f376be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1015,10 +1015,10 @@ dependencies: defer-to-connect "^2.0.0" -"@testim/chrome-version@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.2.tgz#092005c5b77bd3bb6576a4677110a11485e11864" - integrity sha512-1c4ZOETSRpI0iBfIFUqU4KqwBAB2lHUAlBjZz/YqOHqwM9dTTzjV6Km0ZkiEiSCx/tLr1BtESIKyWWMww+RUqw== +"@testim/chrome-version@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.3.tgz#fbb68696899d7b8c1b9b891eded9c04fe2cd5529" + integrity sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A== "@types/aria-query@^5.0.0": version "5.0.0" @@ -1892,16 +1892,16 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^104.0.0: - version "104.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-104.0.0.tgz#2f730f52a567280872567bf3497e1c673b6f4275" - integrity sha512-zbHZutN2ATo19xA6nXwwLn+KueD/5w8ap5m4b6bCb8MIaRFnyDwMbFoy7oFAjlSMpCFL3KSaZRiWUwjj//N3yQ== +chromedriver@^105.0.0: + version "105.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-105.0.1.tgz#325cf05aca200328176438991d236ddb6c61711b" + integrity sha512-QqylH9mvl4Ybq3mmHsym7jeq/LhEi2sPtD8ffd9ixiDFdPRlh2F4vzrzK+myj1MiXb0TYJK7+OCcMEmsB3Sm/Q== dependencies: - "@testim/chrome-version" "^1.1.2" + "@testim/chrome-version" "^1.1.3" axios "^0.27.2" - del "^6.0.0" + del "^6.1.1" extract-zip "^2.0.1" - https-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" @@ -2193,10 +2193,10 @@ define-properties@^1.1.3: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -del@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" - integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== +del@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== dependencies: globby "^11.0.1" graceful-fs "^4.2.4" @@ -3094,7 +3094,7 @@ https-proxy-agent@5.0.0: agent-base "6" debug "4" -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== From 442bda778b2c9e0282de4fbfc8946776a474ee05 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:08:53 +0100 Subject: [PATCH 081/567] Schemas v3.16.0 (#940) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 64d4aab162..3490df664c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.15.0 +v3.16.0 From 30f8ffc50816e69a1c0975a82531474b5087a916 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 4 Oct 2022 10:31:27 +0100 Subject: [PATCH 082/567] Fix session expired page links (#941) --- app/routes/session.py | 3 ++- tests/integration/routes/test_session.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/routes/session.py b/app/routes/session.py index 0e036bb30c..56b7bdae5c 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -13,6 +13,7 @@ from app.authentication.jti_claim_storage import JtiTokenUsed, use_jti_claim from app.globals import get_session_store, get_session_timeout_in_seconds from app.helpers.template_helpers import get_survey_config, render_template +from app.routes.errors import _render_error_page from app.utilities.metadata_parser import ( validate_questionnaire_claims, validate_runner_claims, @@ -118,7 +119,7 @@ def get_session_expired(): if request.method == "GET": logout_user() - return render_template("errors/401") + return _render_error_page(200, template="401") @session_blueprint.route("/session-expiry", methods=["GET", "PATCH"]) diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index d36093c778..04e724913f 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -29,6 +29,13 @@ def setUp(self): def test_session_expired(self): self.get("/session-expired") self.assertInBody("Sorry, you need to sign in again") + self.assertInBody( + '

    If you are completing a business survey, you need to sign back in to your account.

    ' + ) + self.assertInBody( + '

    If you started your survey using an access code, you need to re-enter your code.' + "

    " + ) def test_session_jti_token_expired(self): self.launchSurvey(exp=time.time() - float(60)) From 8efdbdd62d9f2e45b45d28db88b75635f0c70068 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 4 Oct 2022 11:19:09 +0100 Subject: [PATCH 083/567] Fix double h1 on thank you page (#937) --- templates/thank-you.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/thank-you.html b/templates/thank-you.html index 4c262a4197..6a715e17ed 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -61,7 +61,7 @@

    {{ _("Thank you for completing the {survey_title}").format( {% else %} {% set countdown_text = _("For security, your answers will only be available to view for another ") %} -

    {{ _("Get a copy of your answers") }}

    +

    {{ _("Get a copy of your answers") }}

    {{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }}

    From 17863f68b5e3157c49a0534fab951310295d1b8d Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 4 Oct 2022 15:23:48 +0100 Subject: [PATCH 084/567] Fix h1 DAC accessibility issue (#936) --- schemas/test/en/test_benchmark_business.json | 4 +- templates/introduction.html | 52 +++++++++--------- templates/partials/contents.html | 56 +++++++++----------- templates/partials/guidance.html | 13 +++-- templates/partials/introduction/basic.html | 12 ++--- 5 files changed, 66 insertions(+), 71 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 8151270c36..af8e0d6d55 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -108,9 +108,7 @@ "id": "secondary-content", "contents": [ { - "title": "How we use your data" - }, - { + "title": "How we use your data", "list": [ "The information supplied is used to estimate changes in stock levels which are used in the compilation of Gross Domestic Product (GDP), the total UK economic activity.", "GDP is used to measure the UK's financial health and prosperity over time and in comparison to other countries.", diff --git a/templates/introduction.html b/templates/introduction.html index 3327110483..16b344dbee 100644 --- a/templates/introduction.html +++ b/templates/introduction.html @@ -12,31 +12,33 @@ {% block main %} {% block intro_content %} - {% if content.block.primary_content %} - {% for content_block in content.block.primary_content %} - {% include 'partials/introduction/basic.html' %} - {% endfor %} - {% endif %} - - {%- if legal_basis -%} -

    {{ _("Your response is legally required") }}

    -

    {{ legal_basis }}

    - {%- endif -%} - - {% block start_survey %} - {% include 'partials/introduction/start-survey.html' %} - {% endblock start_survey %} - - {% if content.block.preview_content %} - {% set intro = content.block.preview_content %} - {% include 'partials/introduction/preview.html' %} - {% endif %} - - {% if content.block.secondary_content %} - {% for content_block in content.block.secondary_content %} - {% include 'partials/introduction/basic.html' %} - {% endfor %} - {% endif %} + {% if content.block.primary_content %} + {% for content_block in content.block.primary_content %} + {% set title_tag = 'h1' %} + {% include 'partials/introduction/basic.html' %} + {% endfor %} + {% endif %} + + {%- if legal_basis -%} +

    {{ _("Your response is legally required") }}

    +

    {{ legal_basis }}

    + {%- endif -%} + + {% block start_survey %} + {% include 'partials/introduction/start-survey.html' %} + {% endblock start_survey %} + + {% if content.block.preview_content %} + {% set intro = content.block.preview_content %} + {% include 'partials/introduction/preview.html' %} + {% endif %} + + {% if content.block.secondary_content %} + {% for content_block in content.block.secondary_content %} + {% set title_tag = 'h2' %} + {% include 'partials/introduction/basic.html' %} + {% endfor %} + {% endif %} {% endblock intro_content %} {% endblock %} diff --git a/templates/partials/contents.html b/templates/partials/contents.html index 1efa447efd..23148d2f46 100644 --- a/templates/partials/contents.html +++ b/templates/partials/contents.html @@ -2,38 +2,34 @@ {% set guidance_count = namespace(value=1) %} {% for item in contents %} - {%- if item.definition -%} - {% set definition = item.definition %} - {% set definition_id = 'definition-' ~ definition_count.value %} - {% set category = 'Definition' %} - {%- include 'partials/definition.html' -%} - {% set definition_count.value = definition_count.value + 1 %} - {%- endif -%} - - {%- if item.guidance -%} - {% set guidance = item.guidance %} - {% set guidance_id = 'guidance-' ~ guidance_count.value %} - {%- include 'partials/guidance.html' -%} - {% set guidance_count.value = guidance_count.value + 1 %} - {%- endif -%} + {%- if item.definition -%} + {% set definition = item.definition %} + {% set definition_id = 'definition-' ~ definition_count.value %} + {% set category = 'Definition' %} + {%- include 'partials/definition.html' -%} + {% set definition_count.value = definition_count.value + 1 %} + {%- endif -%} - {%- if item.title -%} - {%- if block and block.type == 'Interstitial' -%} -

    {{item.title}}

    - {%- else -%} -

    {{item.title}}

    + {%- if item.guidance -%} + {% set guidance = item.guidance %} + {% set guidance_id = 'guidance-' ~ guidance_count.value %} + {%- include 'partials/guidance.html' -%} + {% set guidance_count.value = guidance_count.value + 1 %} {%- endif -%} - {% endif %} - {%- if item.description -%} -

    {{item.description}}

    - {% endif %} + {%- if item.title -%} +

    {{ item.title }}

    + {% endif %} + + {%- if item.description -%} +

    {{ item.description }}

    + {% endif %} - {%- if item.list -%} -
      - {%- for list_item in item.list -%} -
    • {{list_item}}
    • - {% endfor %} -
    - {% endif %} + {%- if item.list -%} +
      + {%- for list_item in item.list -%} +
    • {{ list_item }}
    • + {% endfor %} +
    + {% endif %} {% endfor %} diff --git a/templates/partials/guidance.html b/templates/partials/guidance.html index 28b5dce63d..90e0e9a593 100644 --- a/templates/partials/guidance.html +++ b/templates/partials/guidance.html @@ -1,13 +1,12 @@ {% from "components/panel/_macro.njk" import onsPanel %} {% call onsPanel({ - "id": guidance_id, - "classes": "ons-u-mb-m", - "attributes": { - "data-qa": guidance_id - } - }) %} - + "id": guidance_id, + "classes": "ons-u-mb-m", + "attributes": { + "data-qa": guidance_id + } +}) %} {% set contents = guidance.contents %} {% include 'partials/contents.html' %} {% endcall %} diff --git a/templates/partials/introduction/basic.html b/templates/partials/introduction/basic.html index 1aa59a9cd4..648d32d7cb 100644 --- a/templates/partials/introduction/basic.html +++ b/templates/partials/introduction/basic.html @@ -1,7 +1,7 @@ -
    - {% if content_block.title %} -

    {{ content_block.title }}

    - {% endif %} - {% set contents = content_block.contents %} - {% include 'partials/contents.html' %} +
    + {% if content_block.title %} + <{{ title_tag }}>{{ content_block.title }} + {% endif %} + {% set contents = content_block.contents %} + {% include 'partials/contents.html' %}
    From 805e005e8f414fe9f2abd7802e78f247cffb00c5 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 5 Oct 2022 11:07:08 +0100 Subject: [PATCH 085/567] Fix test interstitial page title schema (#939) --- .../test/en/test_interstitial_page_title.json | 23 ++++++------------- .../test_questionnaire_page_titles.py | 4 +--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/schemas/test/en/test_interstitial_page_title.json b/schemas/test/en/test_interstitial_page_title.json index e9d57c6b8c..1e8df298c4 100644 --- a/schemas/test/en/test_interstitial_page_title.json +++ b/schemas/test/en/test_interstitial_page_title.json @@ -7,11 +7,6 @@ "title": "Interstitial Page Titles", "theme": "default", "description": "A questionnaire to demo interstitial pages titles.", - "messages": { - "NUMBER_TOO_LARGE": "Number is too large", - "NUMBER_TOO_SMALL": "Number cannot be less than zero", - "INVALID_NUMBER": "Please enter an integer" - }, "metadata": [ { "name": "user_id", @@ -24,10 +19,6 @@ { "name": "ru_name", "type": "string" - }, - { - "name": "case_id", - "type": "string" } ], "questionnaire_flow": { @@ -41,31 +32,31 @@ { "blocks": [ { - "id": "breakfast-interstitial", + "id": "interstitial-page", "content": { "title": { "placeholders": [ { - "placeholder": "case_id", + "placeholder": "ru_name", "value": { - "identifier": "case_id", + "identifier": "ru_name", "source": "metadata" } } ], - "text": "This is the content title {case_id}" + "text": "Your RU name: {ru_name}" }, "contents": [ { - "description": "You have successfully completed the breakfast section. Next we want to know about your lunch." + "description": "You have successfully completed the section." } ] }, "type": "Interstitial" } ], - "id": "favourite-foods", - "title": "Favourite food" + "id": "interstitial-page-titles", + "title": "Interstitial page titles" } ] } diff --git a/tests/integration/questionnaire/test_questionnaire_page_titles.py b/tests/integration/questionnaire/test_questionnaire_page_titles.py index 9b296ba069..eb3e890ae5 100644 --- a/tests/integration/questionnaire/test_questionnaire_page_titles.py +++ b/tests/integration/questionnaire/test_questionnaire_page_titles.py @@ -99,9 +99,7 @@ def test_content_page_should_use_nested_content_text_in_page_title_if_it_exists( self.launchSurvey("test_interstitial_page_title") # When # Then - self.assertEqualPageTitle( - "This is the content title … - Interstitial Page Titles" - ) + self.assertEqualPageTitle("Your RU name: … - Interstitial Page Titles") def test_should_have_error_in_page_title_when_fail_validation(self): # Given From 53a6a574fdd9d73c5775e49e741bd6980a445d36 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 7 Oct 2022 14:48:27 +0100 Subject: [PATCH 086/567] Update DS version and resolve DAC summary row title issue (#920) --- .design-system-version | 2 +- README.md | 2 +- app/translations/messages.pot | 56 +++++++------------ .../contexts/calculated_summary_context.py | 7 --- app/views/contexts/section_summary_context.py | 6 -- app/views/contexts/summary_context.py | 6 -- templates/calculatedsummary.html | 2 +- templates/confirm-email.html | 2 +- templates/feedback.html | 2 +- templates/hub.html | 1 - .../confirmation-post.html | 2 +- .../confirmation-text-message.html | 2 +- .../individual_response/interstitial.html | 2 +- templates/interstitial.html | 2 +- templates/layouts/_questionnaire.html | 2 +- templates/layouts/_submit.html | 2 +- .../configs/_save-sign-out-button.html | 10 +--- templates/partials/email-form.html | 2 +- .../partials/introduction/start-survey.html | 2 +- .../partials/summary/collapsible-summary.html | 1 - templates/partials/summary/list-summary.html | 3 - templates/partials/summary/summary.html | 1 - templates/sectionsummary.html | 2 +- templates/view-submitted-response.html | 7 +-- .../test_calculated_summary_context.py | 2 +- .../contexts/test_section_summary_context.py | 5 +- .../test_submitted_response_context.py | 11 ---- tests/functional/base_pages/hub.page.js | 2 +- tests/functional/generate_pages.py | 2 +- .../test_questionnaire_submit.py | 10 ---- .../routes/test_view_submitted_response.py | 3 - 31 files changed, 41 insertions(+), 120 deletions(-) diff --git a/.design-system-version b/.design-system-version index afb1988b41..af7d1ce565 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -55.2.0 +58.0.1 diff --git a/README.md b/README.md index 22b8432007..cd8fc74c36 100644 --- a/README.md +++ b/README.md @@ -487,7 +487,7 @@ Run `make load-design-system-templates` in the terminal to make sure you have th Then edit the first line in the `templates/layout/_template.njk` file to remove the version number. Should now look like this: ``` shell -{% set release_version = "" %} +{% set release_version = '' %} ``` Then spin up launcher and runner with `make dev-compose-up` and `make run` diff --git a/app/translations/messages.pot b/app/translations/messages.pot index cc79730ff6..d1e8e25eac 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-08-25 10:23+0100\n" +"POT-Creation-Date: 2022-09-21 15:33+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -203,15 +203,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:101 +#: app/helpers/template_helpers.py:98 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:143 +#: app/helpers/template_helpers.py:140 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:177 +#: app/helpers/template_helpers.py:174 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -394,24 +394,6 @@ msgstr "" msgid "Save and exit survey" msgstr "" -#: app/views/contexts/calculated_summary_context.py:72 -#: app/views/contexts/section_summary_context.py:62 -#: app/views/contexts/summary_context.py:18 -msgid "Question" -msgstr "" - -#: app/views/contexts/calculated_summary_context.py:72 -#: app/views/contexts/section_summary_context.py:62 -#: app/views/contexts/summary_context.py:18 -msgid "Answer given" -msgstr "" - -#: app/views/contexts/calculated_summary_context.py:72 -#: app/views/contexts/section_summary_context.py:62 -#: app/views/contexts/summary_context.py:20 -msgid "Change answer" -msgstr "" - #: app/views/contexts/hub_context.py:14 msgid "Completed" msgstr "" @@ -942,7 +924,7 @@ msgstr "" msgid "For more information on how we use this data." msgstr "" -#: templates/thank-you.html:51 templates/view-submitted-response.html:66 +#: templates/thank-you.html:51 templates/view-submitted-response.html:63 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" @@ -984,7 +966,7 @@ msgstr "" msgid "Print answers" msgstr "" -#: templates/view-submitted-response.html:46 +#: templates/view-submitted-response.html:44 msgid "Save answers as PDF" msgstr "" @@ -1276,7 +1258,7 @@ msgstr "" msgid "Choose another section and return to this later" msgstr "" -#: templates/layouts/configs/_save-sign-out-button.html:21 +#: templates/layouts/configs/_save-sign-out-button.html:17 msgid "Exit" msgstr "" @@ -1520,12 +1502,12 @@ msgid "Show" msgstr "" #: templates/partials/introduction/preview.html:48 -#: templates/partials/summary/collapsible-summary.html:59 +#: templates/partials/summary/collapsible-summary.html:58 msgid "Show all" msgstr "" #: templates/partials/introduction/preview.html:49 -#: templates/partials/summary/collapsible-summary.html:60 +#: templates/partials/summary/collapsible-summary.html:59 msgid "Hide all" msgstr "" @@ -1533,31 +1515,31 @@ msgstr "" msgid "Start survey" msgstr "" -#: templates/partials/summary/collapsible-summary.html:37 -#: templates/partials/summary/summary.html:20 +#: templates/partials/summary/collapsible-summary.html:36 +#: templates/partials/summary/summary.html:19 msgid "No answer provided" msgstr "" -#: templates/partials/summary/collapsible-summary.html:38 -#: templates/partials/summary/list-summary.html:8 -#: templates/partials/summary/summary.html:21 +#: templates/partials/summary/collapsible-summary.html:37 +#: templates/partials/summary/list-summary.html:7 +#: templates/partials/summary/summary.html:20 msgid "Change" msgstr "" -#: templates/partials/summary/collapsible-summary.html:39 -#: templates/partials/summary/summary.html:22 +#: templates/partials/summary/collapsible-summary.html:38 +#: templates/partials/summary/summary.html:21 msgid "Change your answer for:" msgstr "" -#: templates/partials/summary/list-summary.html:9 +#: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "" -#: templates/partials/summary/list-summary.html:10 +#: templates/partials/summary/list-summary.html:9 msgid "Remove" msgstr "" -#: templates/partials/summary/list-summary.html:11 +#: templates/partials/summary/list-summary.html:10 msgid "Remove {item_name}" msgstr "" diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 88a6964d64..7dc2841aef 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,7 +1,5 @@ from copy import deepcopy -from flask_babel import lazy_gettext - from app.jinja_filters import ( format_number, format_percentage, @@ -70,11 +68,6 @@ def build_view_context_for_calculated_summary(self, current_location): "title": block.get("title") % dict(total=formatted_total), "collapsible": block.get("collapsible", False), "summary_type": "CalculatedSummary", - "headers": [ - lazy_gettext("Question"), - lazy_gettext("Answer given"), - lazy_gettext("Change answer"), - ], } } diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index f5bec93721..df3c87ada4 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -2,7 +2,6 @@ from typing import Any, Mapping, Optional from flask import url_for -from flask_babel import lazy_gettext from app.data_models import AnswerStore, ListStore, ProgressStore from app.questionnaire import QuestionnaireSchema @@ -59,11 +58,6 @@ def __call__(self, return_to: Optional[str] = "section-summary") -> Mapping: "page_title": page_title, "summary_type": "SectionSummary", "answers_are_editable": True, - "headers": [ - lazy_gettext("Question"), - lazy_gettext("Answer given"), - lazy_gettext("Change answer"), - ], **summary, } } diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index 88fb22ced0..7c1d918d0b 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,7 +1,5 @@ from typing import Generator, Mapping, Optional, Union -from flask_babel import lazy_gettext - from app.questionnaire.location import Location from .context import Context @@ -16,16 +14,12 @@ def __call__( groups = list(self._build_all_groups(return_to)) summary_options = self._schema.get_summary_options() collapsible = summary_options.get("collapsible", False) - headers = [lazy_gettext("Question"), lazy_gettext("Answer given")] - if answers_are_editable: - headers.append(lazy_gettext("Change answer")) return { "groups": groups, "answers_are_editable": answers_are_editable, "collapsible": collapsible, "summary_type": "Summary", - "headers": headers, } def _build_all_groups( diff --git a/templates/calculatedsummary.html b/templates/calculatedsummary.html index 6fb734b2cc..33727eae82 100644 --- a/templates/calculatedsummary.html +++ b/templates/calculatedsummary.html @@ -22,7 +22,7 @@

    {{content.summary.title}}

    {{ onsButton({ "text": _("Yes, I confirm these are correct"), - "submitType": 'timer', + "variants": 'timer', "attributes": { "data-qa": "btn-submit" } diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 15124058a5..c02bc8423d 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -23,7 +23,7 @@

    {{ conten {{ onsButton({ "text": _("Continue"), - "submitType": 'timer', + "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { "data-qa": "btn-submit" diff --git a/templates/feedback.html b/templates/feedback.html index a2391977cb..ee8456f9bf 100644 --- a/templates/feedback.html +++ b/templates/feedback.html @@ -36,7 +36,7 @@

    {{ content.que {{ onsButton({ "text": _("Send feedback"), - "submitType": 'timer', + "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { "data-qa": "btn-submit" diff --git a/templates/hub.html b/templates/hub.html index 1cb83d3a6e..7151a244b3 100644 --- a/templates/hub.html +++ b/templates/hub.html @@ -16,7 +16,6 @@ "classes": "ons-u-mt-m", "summaries": [{ "groups": [{ - "headers": ["Name of section or person", "Section progress", "Access section"], "rows": content.rows }] }] diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index ba0418b190..6940f39df0 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -19,7 +19,7 @@

    {{ onsButton({ "text": _("Continue"), - "submitType": 'timer', + "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-l', "attributes": {'data-qa': 'btn-submit'} }) diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index 7640297dc2..6b6aff4115 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -29,7 +29,7 @@

    {{ onsButton({ "text": _("Continue"), - "submitType": 'timer', + "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-l', "attributes": {'data-qa': 'btn-submit'} }) diff --git a/templates/individual_response/interstitial.html b/templates/individual_response/interstitial.html index 2cd76abc88..a6df98882a 100644 --- a/templates/individual_response/interstitial.html +++ b/templates/individual_response/interstitial.html @@ -11,7 +11,7 @@

    {{_("If you can't answer questions for others in your hou {{ onsButton({ "text": _("Request separate census"), - "submitType": 'timer', + "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-s', "url": next_location_url, "attributes": {'data-qa': 'btn-submit'} diff --git a/templates/interstitial.html b/templates/interstitial.html index 783255aa09..2d695587e8 100644 --- a/templates/interstitial.html +++ b/templates/interstitial.html @@ -30,7 +30,7 @@

    {{ block.content.title }}

    {{ onsButton({ "text": continue_button_text | default(_("Save and continue")), - "submitType": 'timer', + "variants": 'timer', "classes": "ons-u-mt-l", "attributes": { "data-qa": "btn-submit" diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index ef8b0407c0..e703e8ff58 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -23,7 +23,7 @@ {% block submit_button %} {{ onsButton({ - "submitType": "timer", + "variants": 'timer', "text": continue_button_text | default(_("Save and continue")), "attributes": { "data-qa": "btn-submit" diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index f9a0c078a4..b42dac347a 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -36,7 +36,7 @@

    {{ content.title }}

    {{ onsButton({ "text": content.submit_button, - "submitType": 'timer', + "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-' ~ ("s" if content.guidance else "xl"), "attributes": { 'data-qa': 'btn-submit' diff --git a/templates/layouts/configs/_save-sign-out-button.html b/templates/layouts/configs/_save-sign-out-button.html index a645f3f15d..a1a6e5c4bb 100644 --- a/templates/layouts/configs/_save-sign-out-button.html +++ b/templates/layouts/configs/_save-sign-out-button.html @@ -10,11 +10,8 @@ "data-ga": "click", "data-ga-category": "Navigation", "data-ga-action": "Save and sign out click" - }, - "iconType": "exit", - "iconPosition": "after" + } }) %} - {% else %} {% do pageConfig | setAttribute("signoutButton", { "text": _("Exit"), @@ -24,11 +21,8 @@ "data-ga": "click", "data-ga-category": "Navigation", "data-ga-action": "Exit click" - }, - "iconType": "exit", - "iconPosition": 'after' + } }) %} - {% endif %} {% endif %} diff --git a/templates/partials/email-form.html b/templates/partials/email-form.html index 64f802a76e..2656d0a0a1 100644 --- a/templates/partials/email-form.html +++ b/templates/partials/email-form.html @@ -37,7 +37,7 @@ {{ onsButton({ "text": _("Send confirmation"), - "submitType": 'timer', + "variants": 'timer', "classes": "ons-u-mt-s", "attributes": { "data-qa": "btn-submit" diff --git a/templates/partials/introduction/start-survey.html b/templates/partials/introduction/start-survey.html index 5ae95473e9..0d2dd36542 100644 --- a/templates/partials/introduction/start-survey.html +++ b/templates/partials/introduction/start-survey.html @@ -3,7 +3,7 @@ {{ onsButton({ "text": _("Start survey"), - "submitType": 'timer', + "variants": 'timer', "classes": "qa-btn-get-started", "name": "action[start_questionnaire]" }) diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index d132d27adc..de063a642a 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -29,7 +29,6 @@ { "groups": [ { - "headers": content.summary.headers, "rows": map_summary_item_config( group, content.summary.summary_type, diff --git a/templates/partials/summary/list-summary.html b/templates/partials/summary/list-summary.html index 246dd96d03..4c5e8f759c 100644 --- a/templates/partials/summary/list-summary.html +++ b/templates/partials/summary/list-summary.html @@ -1,7 +1,6 @@ {% from "components/summary/_macro.njk" import onsSummary %} {% if list.editable %} - {% set headers = ["Name of person", "Action"] %} {% set rows = map_list_collector_config( list.list_items, "person", @@ -11,7 +10,6 @@ _("Remove {item_name}") ) %} {% else %} - {% set headers = ["Name of person"] %} {% set rows = map_list_collector_config( list.list_items, "person" @@ -20,7 +18,6 @@ {% set group_config = { "groupTitle": list_title, - "headers": headers, "rows": rows, "placeholderText": empty_list_text, } %} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 6e22b332fa..4fb17ed2e9 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -12,7 +12,6 @@ { "groupTitle": group.title if group.title else None, "id": group.id if group.id else None, - "headers": content.summary.headers, "rows": map_summary_item_config( group, content.summary.summary_type, diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 01c4c845dd..516721e9b7 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -44,7 +44,7 @@

    {{content.summary.title}}

    {{ onsButton({ "text": continue_button_text | default(_("Continue")), - "submitType": 'timer', + "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { "data-qa": "btn-submit" diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index 1a05d061a2..bab61b56c1 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -33,8 +33,7 @@

    onsButton({ "type": 'button', "text": _('Print answers'), - "buttonStyle": "print", - "variants": ['small', 'secondary'], + "variants": ['small', 'secondary', 'print'], "attributes": { "data-qa": "btn-print" } @@ -42,10 +41,8 @@

    }} {{ onsButton({ - "buttonStyle": 'download', "text": _('Save answers as PDF'), - "submitType": "timer", - "variants": ['small', 'secondary'], + "variants": ['small', 'secondary', 'download', 'timer'], "url": content.pdf_url, "removeDownloadAttribute": true, "attributes": { diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 528b8401bd..0a5d0597d9 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -94,7 +94,7 @@ def test_build_view_context_for_currency_calculated_summary( assert "summary" in context assert_summary_context(context) - assert len(context["summary"]) == 7 + assert len(context["summary"]) == 6 context_summary = context["summary"] assert "title" in context_summary assert context_summary["title"] == title diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 9d66f672ae..8fdb7a2ff4 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -53,7 +53,7 @@ def test_build_view_context_for_section_summary( assert "summary" in context assert_summary_context(context) - assert len(context["summary"]) == 7 + assert len(context["summary"]) == 6 assert "title" in context["summary"] @@ -152,7 +152,6 @@ def test_context_for_section_list_summary(people_answer_store): expected = { "summary": { "answers_are_editable": True, - "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { @@ -232,7 +231,6 @@ def test_context_for_driving_question_summary_empty_list(): expected = { "summary": { "answers_are_editable": True, - "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { @@ -287,7 +285,6 @@ def test_context_for_driving_question_summary(): expected = { "summary": { "answers_are_editable": True, - "headers": ["Question", "Answer given", "Change answer"], "collapsible": False, "custom_summary": [ { diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 3e5bf0e48c..2ef5b96421 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -111,17 +111,6 @@ def test_build_view_submitted_response_no_submitted_at(app: Flask): ) -def test_summary_headers_without_change_link( - app: Flask, -): - with app.app_context(): - questionnaire_store = fake_questionnaire_store() - context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT - ) - assert context["summary"]["headers"] == ["Question", "Answer given"] - - def fake_questionnaire_store(): storage = Mock() storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) diff --git a/tests/functional/base_pages/hub.page.js b/tests/functional/base_pages/hub.page.js index 921619db39..c56f39926e 100644 --- a/tests/functional/base_pages/hub.page.js +++ b/tests/functional/base_pages/hub.page.js @@ -6,7 +6,7 @@ class HubPage extends BasePage { } summaryItems() { - return "table.ons-summary__items"; + return "div.ons-summary__items"; } summaryRowState(sectionId) { diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 1c2d19f036..7006160d41 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -254,7 +254,7 @@ """ LIST_SECTION_SUMMARY_LABEL_GETTER = Template( - r""" ${list_name}ListLabel(listItemInstance) { return `div[data-qa="${list_name}-list-summary"] td[data-qa="list-item-` + listItemInstance + `-label"]`; } + r""" ${list_name}ListLabel(listItemInstance) { return `div[data-qa="${list_name}-list-summary"] dt[data-qa="list-item-` + listItemInstance + `-label"]`; } """ ) diff --git a/tests/integration/questionnaire/test_questionnaire_submit.py b/tests/integration/questionnaire/test_questionnaire_submit.py index e0b425c680..da2f60860e 100644 --- a/tests/integration/questionnaire/test_questionnaire_submit.py +++ b/tests/integration/questionnaire/test_questionnaire_submit.py @@ -127,13 +127,3 @@ def test_is_displayed(self): self.post() self.assertInUrl(THANK_YOU_URL_PATH) - - def test_summary_headers_with_change_link(self): - self.launchSurvey("test_section_summary") - self.post() - self.post() - self.post() - self.post() - self.assertInBody("Question") - self.assertInBody("Answer given") - self.assertInBody("Change answer") diff --git a/tests/integration/routes/test_view_submitted_response.py b/tests/integration/routes/test_view_submitted_response.py index a6ceddc1d9..2e4f92e41d 100644 --- a/tests/integration/routes/test_view_submitted_response.py +++ b/tests/integration/routes/test_view_submitted_response.py @@ -58,9 +58,6 @@ def test_enabled(self): self.assertInBody("NP10 8XG") self.assertIsNotNone(self.get_print_button()) self.assertIsNotNone(self.get_download_button()) - self.assertInBody("Question") - self.assertInBody("Answer given") - self.assertNotInBody("Change answer") def test_not_enabled(self): # Given I launch and complete a questionnaire that does not have view-submitted-response enabled From 6966fb24d9485439d6869ec1b0247a46118848a8 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:23:39 +0100 Subject: [PATCH 087/567] Schemas v3.17.0 (#942) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3490df664c..597e63fe66 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.16.0 +v3.17.0 From 56777bb4d574d128528bbe6f4d40fac4c5901c5d Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:11:06 +0100 Subject: [PATCH 088/567] Schemas v3.17.1 (#943) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 597e63fe66..794341d1c0 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.17.0 +v3.17.1 From d2d21910d4cc01c30c9f7a62bca1815ca4eabfb6 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 19 Oct 2022 09:31:55 +0100 Subject: [PATCH 089/567] Fix button GA tracking (#935) --- templates/calculatedsummary.html | 9 ++- templates/confirm-email.html | 5 +- templates/feedback-sent.html | 47 ++++++++-------- templates/feedback.html | 10 +++- .../confirmation-post.html | 17 ++++-- .../confirmation-text-message.html | 21 ++++--- .../individual_response/interstitial.html | 7 ++- templates/interstitial.html | 36 ++++++------ templates/layouts/_questionnaire.html | 55 ++++++++++--------- templates/layouts/_submit.html | 11 ++-- .../configs/_save-sign-out-button.html | 16 ++++-- templates/partials/email-form.html | 12 ++-- .../partials/introduction/start-survey.html | 8 ++- templates/sectionsummary.html | 18 ++++-- templates/view-submitted-response.html | 14 +++-- 15 files changed, 171 insertions(+), 115 deletions(-) diff --git a/templates/calculatedsummary.html b/templates/calculatedsummary.html index 33727eae82..674524fea9 100644 --- a/templates/calculatedsummary.html +++ b/templates/calculatedsummary.html @@ -8,8 +8,8 @@

    {{content.summary.title}}

    {% call onsPanel({ - "classes": "ons-u-mb-l" - }) %} + "classes": "ons-u-mb-l" + }) %}

    {{ _("Please review your answers and confirm these are correct") }}

    {% endcall %} @@ -24,7 +24,10 @@

    {{content.summary.title}}

    "text": _("Yes, I confirm these are correct"), "variants": 'timer', "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Confirm", + "data-ga-label": "Confirm button click" } }) }} diff --git a/templates/confirm-email.html b/templates/confirm-email.html index c02bc8423d..8c0420be35 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -26,7 +26,10 @@

    {{ conten "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click" } }) }} diff --git a/templates/feedback-sent.html b/templates/feedback-sent.html index 0b177ae3d7..71da0142a4 100644 --- a/templates/feedback-sent.html +++ b/templates/feedback-sent.html @@ -7,28 +7,29 @@ {% set sign_out_url = content.sign_out_url %} {% block main %} - {% call onsPanel({ - "spacious": true, - "type": "success", - "classes": "ons-u-mb-s", - "iconType": "check", - "iconSize": "xl" - }) - %} - -

    {{ _("Thank you for your feedback") }}

    -

    {{ _("Your comments will help us make improvements to our surveys. We are not able to reply to comments, but we appreciate your feedback") }}

    - - {% endcall %} - - {{ - onsButton({ - "attributes": {"data-qa": "btn-done"}, - "type": "button", - "text": _("Done"), - "classes": "ons-u-mb-m", - "url": url_for('post_submission.get_thank_you') - }) - }} + {% call onsPanel({ + "spacious": true, + "type": "success", + "classes": "ons-u-mb-s", + "iconType": "check", + "iconSize": "xl" + }) %} +

    {{ _("Thank you for your feedback") }}

    +

    {{ _("Your comments will help us make improvements to our surveys. We are not able to reply to comments, but we appreciate your feedback") }}

    + {% endcall %} + {{ + onsButton({ + "type": "button", + "text": _("Done"), + "classes": "ons-u-mb-m", + "url": url_for('post_submission.get_thank_you'), + "attributes": { + "data-qa": "btn-done", + "data-ga-category": "Submit button", + "data-ga-action": "Done", + "data-ga-label": "Done button click" + } + }) + }} {% endblock %} diff --git a/templates/feedback.html b/templates/feedback.html index ee8456f9bf..9395af7e02 100644 --- a/templates/feedback.html +++ b/templates/feedback.html @@ -13,9 +13,10 @@ "id": "top-previous", "text": _("Back"), "attributes": { - "data-ga": 'click', + "data-ga": 'previous-link', "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' + "data-ga-action": 'Previous', + "data-ga-label": "Previous link click" } } ] @@ -39,7 +40,10 @@

    {{ content.que "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Send feedback", + "data-ga-label": "Send feedback button click" } }) }} diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index 6940f39df0..df05095a2d 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -10,18 +10,23 @@ "classes": "ons-u-mb-m", "iconType": "check", "iconSize": "l" - }) %} -

    - {{ _("A letter has been sent to Individual Resident at {display_address}").format(display_address=display_address) }} -

    -

    {{ _("The letter with an individual access code should arrive soon for them to complete their own census") }}

    + }) %} +

    + {{ _("A letter has been sent to Individual Resident at {display_address}").format(display_address=display_address) }} +

    +

    {{ _("The letter with an individual access code should arrive soon for them to complete their own census") }}

    {% endcall %} {{ onsButton({ "text": _("Continue"), "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-l', - "attributes": {'data-qa': 'btn-submit'} + "attributes": { + 'data-qa': 'btn-submit', + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click" + } }) }} {% endblock %} diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index 6b6aff4115..da28d39fe7 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -10,28 +10,31 @@ "classes": "ons-u-mb-m", "iconType": "check", "iconSize": "l" - }) %} -

    - {{ _("We have sent a text to {mobile_number}").format(mobile_number=mobile_number) }} -

    -

    {{ _("The text message with an individual access code should arrive soon for them to complete their own census") }}

    + }) %} +

    + {{ _("We have sent a text to {mobile_number}").format(mobile_number=mobile_number) }} +

    +

    {{ _("The text message with an individual access code should arrive soon for them to complete their own census") }}

    {% endcall %} {% call onsPanel({ "type": "bare", "classes": "ons-u-mb-s", "iconType": "lock" - }) - %} + }) %}

    {{ _("The text will be sent from Census2021") }}

    {% endcall %} - {{ onsButton({ "text": _("Continue"), "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-l', - "attributes": {'data-qa': 'btn-submit'} + "attributes": { + 'data-qa': 'btn-submit', + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click" + } }) }} {% endblock %} diff --git a/templates/individual_response/interstitial.html b/templates/individual_response/interstitial.html index a6df98882a..440cb6be1c 100644 --- a/templates/individual_response/interstitial.html +++ b/templates/individual_response/interstitial.html @@ -14,7 +14,12 @@

    {{_("If you can't answer questions for others in your hou "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-s', "url": next_location_url, - "attributes": {'data-qa': 'btn-submit'} + "attributes": { + 'data-qa': 'btn-submit', + "data-ga-category": "Submit button", + "data-ga-action": "Request separate census", + "data-ga-label": "Request separate census button click" + } }) }} {% endblock %} diff --git a/templates/interstitial.html b/templates/interstitial.html index 2d695587e8..eb5ff6ae58 100644 --- a/templates/interstitial.html +++ b/templates/interstitial.html @@ -8,22 +8,21 @@ {% set continue_button_text = _("Continue") %} {% block form_content %} - {% set interstitial_instruction = format_paragraphs(block.content.instruction) %} - {% if block.interviewer_only %} -

    {{ interviewer_note(block.content.title) }}

    - {% else %} -

    {{ block.content.title }}

    - {% endif %} - {% if interstitial_instruction %} -
    {{ interstitial_instruction | safe }}
    - {% endif %} - {% set contents = block.content.contents %} - {% include 'partials/contents.html' %} - {% if content.individual_response_url %} - {% set title = _("If you can’t answer questions for this person") %} - {% include 'partials/individual-response-guidance.html' %} - {% endif %} - + {% set interstitial_instruction = format_paragraphs(block.content.instruction) %} + {% if block.interviewer_only %} +

    {{ interviewer_note(block.content.title) }}

    + {% else %} +

    {{ block.content.title }}

    + {% endif %} + {% if interstitial_instruction %} +
    {{ interstitial_instruction | safe }}
    + {% endif %} + {% set contents = block.content.contents %} + {% include 'partials/contents.html' %} + {% if content.individual_response_url %} + {% set title = _("If you can’t answer questions for this person") %} + {% include 'partials/individual-response-guidance.html' %} + {% endif %} {% endblock form_content %} {% block submit_button %} @@ -33,7 +32,10 @@

    {{ block.content.title }}

    "variants": 'timer', "classes": "ons-u-mt-l", "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Save and continue", + "data-ga-label": "Save and continue button click" } }) }} diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index e703e8ff58..b42af9dfa1 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -7,35 +7,38 @@ {% set last_viewed_question_guidance = content.last_viewed_question_guidance %} {% block main %} - + - {% block form_errors %}{% endblock %} + {% block form_errors %}{% endblock %} - {% block form_content %} -
    - {% if last_viewed_question_guidance %} - {% include 'partials/last_viewed_question_guidance.html' %} - {% endif %} - {% include 'partials/block.html' %} -
    - {% endblock %} + {% block form_content %} +
    + {% if last_viewed_question_guidance %} + {% include 'partials/last_viewed_question_guidance.html' %} + {% endif %} + {% include 'partials/block.html' %} +
    + {% endblock %} - {% block submit_button %} - {{ - onsButton({ - "variants": 'timer', - "text": continue_button_text | default(_("Save and continue")), - "attributes": { - "data-qa": "btn-submit" - } - }) - }} - {% endblock %} + {% block submit_button %} + {{ + onsButton({ + "variants": 'timer', + "text": continue_button_text | default(_("Save and continue")), + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Save and continue", + "data-ga-label": "Save and continue button click" + } + }) + }} + {% endblock %} - {% block after_submit_button_content %} - {% if content.return_to_hub_url %} -

    {{ _("Choose another section and return to this later") }}

    - {% endif %} - {% endblock %} + {% block after_submit_button_content %} + {% if content.return_to_hub_url %} +

    {{ _("Choose another section and return to this later") }}

    + {% endif %} + {% endblock %} {% endblock %} diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index b42dac347a..3a41b32b24 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -23,9 +23,9 @@

    {{ content.title }}

    {% if content.guidance %}
    -

    - {{ content.guidance }} -

    +

    + {{ content.guidance }} +

    {% endif %} @@ -39,7 +39,10 @@

    {{ content.title }}

    "variants": 'timer', "classes": 'ons-u-mb-m ons-u-mt-' ~ ("s" if content.guidance else "xl"), "attributes": { - 'data-qa': 'btn-submit' + 'data-qa': 'btn-submit', + "data-ga-category": "Submit Button", + "data-ga-action": "Submit", + "data-ga-label": "Submit Button click" } }) }} diff --git a/templates/layouts/configs/_save-sign-out-button.html b/templates/layouts/configs/_save-sign-out-button.html index a1a6e5c4bb..3666b8cfc6 100644 --- a/templates/layouts/configs/_save-sign-out-button.html +++ b/templates/layouts/configs/_save-sign-out-button.html @@ -7,10 +7,12 @@ "url": sign_out_url, "attributes": { "data-qa": "btn-save-sign-out", - "data-ga": "click", "data-ga-category": "Navigation", - "data-ga-action": "Save and sign out click" - } + "data-ga-action": "Save and sign out", + "data-ga-label": "Save and sign out button click" + }, + "iconType": "exit", + "iconPosition": "after" }) %} {% else %} {% do pageConfig | setAttribute("signoutButton", { @@ -18,10 +20,12 @@ "url": sign_out_url, "attributes": { "data-qa": "btn-exit", - "data-ga": "click", "data-ga-category": "Navigation", - "data-ga-action": "Exit click" - } + "data-ga-action": "Exit", + "data-ga-label": "Exit button click" + }, + "iconType": "exit", + "iconPosition": 'after' }) %} {% endif %} diff --git a/templates/partials/email-form.html b/templates/partials/email-form.html index 2656d0a0a1..87d2476dab 100644 --- a/templates/partials/email-form.html +++ b/templates/partials/email-form.html @@ -13,7 +13,7 @@ "data-ga-action": "Confirmation Email", "data-ga-label": email_field.id } - }%} + } %} {% endif %} {% set config = { @@ -28,9 +28,8 @@ "attributes": { "data-qa": "input-text", }, - "error": error - } -%} + "error": error +} %} {{ onsInput(config) }} @@ -40,7 +39,10 @@ "variants": 'timer', "classes": "ons-u-mt-s", "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Send confirmation", + "data-ga-label": "Send confirmation button click" } }) }} diff --git a/templates/partials/introduction/start-survey.html b/templates/partials/introduction/start-survey.html index 0d2dd36542..2b91d0a686 100644 --- a/templates/partials/introduction/start-survey.html +++ b/templates/partials/introduction/start-survey.html @@ -5,6 +5,12 @@ "text": _("Start survey"), "variants": 'timer', "classes": "qa-btn-get-started", - "name": "action[start_questionnaire]" + "name": "action[start_questionnaire]", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Start survey", + "data-ga-label": "Start button click" + } }) }} diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 516721e9b7..d906748373 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -9,7 +9,10 @@ onsButton({ "text": _("Continue"), "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click" } }) }} @@ -30,10 +33,10 @@

    {{content.summary.title}}

    {% set list_title = summary.title %}
    - {% if summary.list %} - {% set list = summary.list %} - {% include 'partials/summary/list-summary.html' %} - {% endif %} + {% if summary.list %} + {% set list = summary.list %} + {% include 'partials/summary/list-summary.html' %} + {% endif %}
    {% endif %} {% endfor %} @@ -47,7 +50,10 @@

    {{content.summary.title}}

    "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { - "data-qa": "btn-submit" + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click" } }) }} diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index bab61b56c1..d9bb9150e6 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -35,18 +35,24 @@

    "text": _('Print answers'), "variants": ['small', 'secondary', 'print'], "attributes": { - "data-qa": "btn-print" + "data-qa": "btn-print", + "data-ga-category": "Print button", + "data-ga-action": "Open print Dialogue", + "data-ga-label": "Print button click" } }) }} {{ onsButton({ "text": _('Save answers as PDF'), - "variants": ['small', 'secondary', 'download', 'timer'], + "variants": ['small', 'secondary', 'timer', 'download'], "url": content.pdf_url, "removeDownloadAttribute": true, "attributes": { - "data-qa": "btn-pdf" + "data-qa": "btn-pdf", + "data-ga-category": "PDF button", + "data-ga-action": "Download PDF", + "data-ga-label": "PDF button click" } }) }} @@ -60,7 +66,7 @@

    "id": "view-submitted-guidance", "classes": "ons-u-mb-m" }) %} - {{ _("For security, you can no longer view or get a copy of your answers") }} + {{ _("For security, you can no longer view or get a copy of your answers") }} {% endcall %} {% endif %} {% endblock %} From 2dbed43e13cd9ab5cb3bcf428f173013e293fb75 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 26 Oct 2022 13:41:16 +0100 Subject: [PATCH 090/567] Update chromedriver to v107 (#946) --- package.json | 2 +- yarn.lock | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4c640d24cb..219134718a 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^105.0.0", + "chromedriver": "^107.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index e257f376be..52af579d39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1892,13 +1892,14 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^105.0.0: - version "105.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-105.0.1.tgz#325cf05aca200328176438991d236ddb6c61711b" - integrity sha512-QqylH9mvl4Ybq3mmHsym7jeq/LhEi2sPtD8ffd9ixiDFdPRlh2F4vzrzK+myj1MiXb0TYJK7+OCcMEmsB3Sm/Q== +chromedriver@^107.0.0: + version "107.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-107.0.0.tgz#9443ceb6020190f1a0f96ae6b5fad5453c0cd582" + integrity sha512-/VpGc83szXYUu9gBhCl6tg6XvtVwj2RQjOZ4wDA5TPSqudTMgWcMbkjeZbCfHwReJ9Qqo0hJ1jipG1IXWDxg3g== dependencies: "@testim/chrome-version" "^1.1.3" axios "^0.27.2" + compare-versions "^5.0.1" del "^6.1.1" extract-zip "^2.0.1" https-proxy-agent "^5.0.1" @@ -2019,6 +2020,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compare-versions@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" + integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== + compress-brotli@^1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" From dff899aa318dc0f5891e0a69f01c6c3c47f3bb1e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 26 Oct 2022 14:21:12 +0100 Subject: [PATCH 091/567] Schemas v3.18.0 (#945) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 794341d1c0..dedd793388 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.17.1 +v3.18.0 From 66099c32645384a8c745f58d3bca06743d3f196f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 28 Oct 2022 09:48:40 +0100 Subject: [PATCH 092/567] Schemas v3.18.1 (#947) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index dedd793388..0f734a422c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.18.0 +v3.18.1 From fa3cebd1560938d18d4d35b6e8a14d9cf8fea352 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:06:21 +0000 Subject: [PATCH 093/567] Enable GH PR actions for feature branches (#949) --- .github/workflows/pull_request.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index c6740978a4..a601f2470c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -6,6 +6,7 @@ on: - "master" - "branch-v*" - "bug-fix-*" + - "feature-*" jobs: python-dependencies: From 9e3b623eb1fb10f75202560968feb68c4f2b6002 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 3 Nov 2022 09:55:57 +0000 Subject: [PATCH 094/567] Generic launch and receipting pattern (#915) --- app/authentication/auth_payload_version.py | 5 + app/authentication/authenticator.py | 6 +- app/authentication/roles.py | 6 +- app/data_models/metadata_proxy.py | 98 +++ app/data_models/questionnaire_store.py | 8 +- app/forms/questionnaire_form.py | 7 +- app/globals.py | 5 +- app/helpers/language_helper.py | 14 +- app/helpers/schema_helpers.py | 4 +- app/helpers/template_helpers.py | 12 +- app/questionnaire/path_finder.py | 3 +- app/questionnaire/placeholder_parser.py | 3 +- app/questionnaire/placeholder_renderer.py | 6 +- app/questionnaire/questionnaire_schema.py | 11 +- app/questionnaire/router.py | 5 +- app/questionnaire/rules/rule_evaluator.py | 3 +- app/questionnaire/value_source_resolver.py | 8 +- app/questionnaire/when_rules.py | 2 +- app/routes/errors.py | 5 +- app/routes/flush.py | 14 +- app/routes/individual_response.py | 17 +- app/routes/questionnaire.py | 24 +- app/routes/session.py | 89 ++- app/submitter/convert_payload_0_0_1.py | 4 +- app/submitter/convert_payload_0_0_3.py | 1 + app/submitter/converter.py | 81 +-- app/submitter/converter_v2.py | 137 ++++ app/submitter/submitter.py | 23 +- app/utilities/make_immutable.py | 14 + app/utilities/metadata_parser.py | 71 +- app/utilities/metadata_parser_v2.py | 152 +++++ app/utilities/metadata_validators.py | 35 + app/utilities/schema.py | 15 +- app/views/contexts/context.py | 5 +- app/views/contexts/section_summary_context.py | 5 +- .../contexts/submission_metadata_context.py | 1 + app/views/contexts/thank_you_context.py | 14 +- .../view_submitted_response_context.py | 18 +- app/views/handlers/confirm_email.py | 9 +- app/views/handlers/content.py | 1 - app/views/handlers/feedback.py | 128 +++- app/views/handlers/individual_response.py | 11 +- app/views/handlers/submission.py | 33 +- ...w_routing_case_insensitive_text_field.json | 12 +- schemas/test/en/test_theme_social.json | 11 +- tests/app/authentication/test_roles.py | 33 +- tests/app/conftest.py | 27 +- tests/app/data_model/test_metadata_proxy.py | 65 ++ .../data_model/test_questionnaire_store.py | 6 +- tests/app/forms/field_handlers/conftest.py | 4 +- tests/app/forms/test_questionnaire_form.py | 107 +-- tests/app/helpers/test_template_helpers.py | 4 +- tests/app/parser/conftest.py | 99 ++- tests/app/parser/test_metadata_parser.py | 388 +++++++++-- tests/app/questionnaire/conftest.py | 23 +- .../rules/test_rule_evaluator.py | 18 +- tests/app/questionnaire/test_path_finder.py | 36 +- .../questionnaire/test_placeholder_parser.py | 56 +- .../test_placeholder_transforms.py | 1 + tests/app/questionnaire/test_router.py | 3 +- .../test_value_source_resolver.py | 41 +- tests/app/questionnaire/test_when_rules.py | 66 +- tests/app/submitter/conftest.py | 131 ++-- .../submitter/test_convert_payload_0_0_1.py | 533 ++++++++++----- .../submitter/test_convert_payload_0_0_3.py | 621 +++++++++++++----- tests/app/submitter/test_converter.py | 383 +++++++---- tests/app/submitter/test_submitter.py | 41 ++ tests/app/utilities/test_schema.py | 9 +- tests/app/views/contexts/conftest.py | 15 - tests/app/views/contexts/test_hub_context.py | 15 +- .../contexts/test_section_summary_context.py | 21 +- .../test_submitted_response_context.py | 34 +- .../views/contexts/test_thank_you_context.py | 48 +- tests/app/views/handlers/conftest.py | 124 +++- ...t_confirmation_email_fulfilment_request.py | 2 +- .../views/handlers/test_feedback_upload.py | 76 ++- ..._individual_response_fulfilment_request.py | 76 ++- .../views/handlers/test_submission_handler.py | 98 +++ tests/functional/jwt_helper.js | 121 +++- tests/functional/spec/thank_you.spec.js | 2 +- tests/functional/wdio.conf.js | 6 +- tests/integration/create_token.py | 96 ++- .../test_individual_response.py | 6 +- tests/integration/integration_test_case.py | 39 +- .../test_questionnaire_submission.py | 33 + tests/integration/routes/test_errors.py | 24 +- tests/integration/routes/test_feedback.py | 31 + tests/integration/routes/test_thank_you.py | 2 +- tests/integration/session/test_login.py | 87 ++- tests/integration/test_header_links.py | 6 +- 90 files changed, 3641 insertions(+), 1152 deletions(-) create mode 100644 app/authentication/auth_payload_version.py create mode 100644 app/data_models/metadata_proxy.py create mode 100644 app/submitter/converter_v2.py create mode 100644 app/utilities/make_immutable.py create mode 100644 app/utilities/metadata_parser_v2.py create mode 100644 app/utilities/metadata_validators.py create mode 100644 tests/app/data_model/test_metadata_proxy.py diff --git a/app/authentication/auth_payload_version.py b/app/authentication/auth_payload_version.py new file mode 100644 index 0000000000..fe4d338d37 --- /dev/null +++ b/app/authentication/auth_payload_version.py @@ -0,0 +1,5 @@ +from enum import Enum + + +class AuthPayloadVersion(Enum): + V2 = "v2" diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index ea3920a19f..e5bbb5ee9a 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -108,10 +108,8 @@ def load_user(extend_session: bool = True) -> Optional[User]: user_ik = cookie_session.get(USER_IK) user = User(user_id, user_ik) - metadata = get_metadata(user) - - if metadata and (tx_id := metadata.get("tx_id")): - logger.bind(tx_id=tx_id) + if metadata := get_metadata(user): + logger.bind(tx_id=metadata.tx_id) if extend_session: _extend_session_expiry(session_store) diff --git a/app/authentication/roles.py b/app/authentication/roles.py index 2e5a97bba4..ee1fa2fe6e 100644 --- a/app/authentication/roles.py +++ b/app/authentication/roles.py @@ -29,8 +29,10 @@ def dump(): def role_required_decorator(func: Callable) -> Callable: @wraps(func) def role_required_wrapper(*args: tuple, **kwargs: dict) -> Any: - metadata = get_metadata(current_user) - roles: list = (metadata.get("roles", []) or []) if metadata else [] + roles: list = [] + if metadata := get_metadata(current_user): + roles = metadata.roles or [] + if current_user.is_authenticated and role in roles: return func(*args, **kwargs) raise Forbidden diff --git a/app/data_models/metadata_proxy.py b/app/data_models/metadata_proxy.py new file mode 100644 index 0000000000..3eed9a85f5 --- /dev/null +++ b/app/data_models/metadata_proxy.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from copy import deepcopy +from dataclasses import dataclass +from datetime import datetime +from typing import Any, Mapping, Optional + +from werkzeug.datastructures import ImmutableDict + +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.utilities.make_immutable import make_immutable + + +class NoMetadataException(Exception): + pass + + +# "version" is excluded here as it is handled independently +TOP_LEVEL_METADATA_KEYS = [ + "tx_id", + "account_service_url", + "case_id", + "collection_exercise_sid", + "response_id", + "response_expires_at", + "language_code", + "schema_name", + "schema_url", + "channel", + "region_code", + "roles", +] + + +@dataclass(frozen=True) +class SurveyMetadata: + data: ImmutableDict + receipting_keys: Optional[tuple] = None + + def __getitem__(self, key: str) -> Optional[Any]: + return self.data.get(key) + + +# pylint: disable=too-many-locals +@dataclass(frozen=True) +class MetadataProxy: + tx_id: str + account_service_url: str + case_id: str + collection_exercise_sid: str + response_id: str + survey_metadata: Optional[SurveyMetadata] = None + schema_url: Optional[str] = None + schema_name: Optional[str] = None + language_code: Optional[str] = None + response_expires_at: Optional[datetime] = None + channel: Optional[str] = None + region_code: Optional[str] = None + version: Optional[AuthPayloadVersion] = None + roles: Optional[list] = None + + def __getitem__(self, key: str) -> Optional[Any]: + if self.survey_metadata and key in self.survey_metadata.data: + return self.survey_metadata[key] + + return getattr(self, key, None) + + @classmethod + def from_dict(cls, metadata: Mapping) -> MetadataProxy: + _metadata = deepcopy(dict(metadata)) + version = ( + AuthPayloadVersion(_metadata.pop("version")) + if "version" in _metadata + else None + ) + + survey_metadata = None + if version is AuthPayloadVersion.V2: + serialized_metadata = cls.serialize(_metadata.pop("survey_metadata", {})) + if serialized_metadata: + survey_metadata = SurveyMetadata(**serialized_metadata) + else: + serialized_metadata = cls.serialize(_metadata) + survey_metadata = SurveyMetadata(data=serialized_metadata) + + top_level_data = { + key: _metadata.pop(key, None) for key in TOP_LEVEL_METADATA_KEYS + } + + return cls( + **top_level_data, + version=version, + survey_metadata=survey_metadata, + ) + + @classmethod + def serialize(cls, data: Any) -> Any: + return make_immutable(data) diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 2c5552d87d..1c4953d069 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -1,11 +1,11 @@ from __future__ import annotations from datetime import datetime -from types import MappingProxyType from typing import TYPE_CHECKING, Any, Mapping, Optional from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.questionnaire.rules.utils import parse_iso_8601_datetime from app.utilities.json import json_dumps, json_loads @@ -28,7 +28,9 @@ def __init__( self.version = version self._metadata: dict[str, Any] = {} # self.metadata is a read-only view over self._metadata - self.metadata: Mapping[str, Any] = MappingProxyType(self._metadata) + self.metadata: Optional[MetadataProxy] = ( + MetadataProxy.from_dict(self._metadata) if self._metadata else None + ) self.response_metadata: Mapping[str, Any] = {} self.list_store = ListStore() self.answer_store = AnswerStore() @@ -57,7 +59,7 @@ def set_metadata(self, to_set: dict[str, Any]) -> QuestionnaireStore: Metadata should normally be read only. """ self._metadata = to_set - self.metadata = MappingProxyType(self._metadata) + self.metadata = MetadataProxy.from_dict(self._metadata) return self diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 861180cd90..255de37df4 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -13,6 +13,7 @@ from wtforms import validators from app.data_models import AnswerStore, AnswerValueTypes, ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.forms import error_messages from app.forms.field_handlers import DateHandler, FieldHandler, get_field_handler from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck @@ -40,7 +41,7 @@ def __init__( question_schema: QuestionSchemaType, answer_store: AnswerStore, list_store: ListStore, - metadata: Mapping[str, Any], + metadata: Optional[MetadataProxy], response_metadata: Mapping[str, Any], location: Union[None, Location, RelationshipLocation], **kwargs: Union[MultiDict[str, Any], Mapping[str, Any], None], @@ -445,7 +446,7 @@ def get_answer_fields( schema: QuestionnaireSchema, answer_store: AnswerStore, list_store: ListStore, - metadata: Mapping[str, Any], + metadata: Optional[MetadataProxy], response_metadata: Mapping[str, Any], location: Union[Location, RelationshipLocation, None], ) -> dict[str, FieldHandler]: @@ -560,7 +561,7 @@ def generate_form( question_schema: QuestionSchemaType, answer_store: AnswerStore, list_store: ListStore, - metadata: Mapping[str, Any], + metadata: Optional[MetadataProxy], response_metadata: Mapping[str, Any], location: Union[None, Location, RelationshipLocation] = None, data: Optional[dict[str, Any]] = None, diff --git a/app/globals.py b/app/globals.py index a5f0f5732c..a5987cbaf9 100644 --- a/app/globals.py +++ b/app/globals.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta, timezone -from typing import Any, Mapping, Optional, Union +from typing import Optional from flask import current_app, g from flask import session as cookie_session @@ -8,6 +8,7 @@ from app.authentication.user import User from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore from app.questionnaire import QuestionnaireSchema @@ -91,7 +92,7 @@ def create_session_store( ) -def get_metadata(user: User) -> Union[None, Mapping[str, Any]]: +def get_metadata(user: User) -> Optional[MetadataProxy]: if user.is_anonymous: logger.debug("anonymous user requesting metadata get instance") return None diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 502360a430..5e59a71f8e 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -1,9 +1,10 @@ -from typing import Any, Mapping, Optional, Union +from typing import Optional, Union from urllib.parse import urlencode from flask import g, request from flask_login import current_user +from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_metadata, get_session_store from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import get_allowed_languages @@ -14,14 +15,17 @@ } -def handle_language(metadata: Optional[Mapping[str, Any]] = None) -> None: +def handle_language(metadata: Optional[MetadataProxy] = None) -> None: session_store = get_session_store() if session_store and session_store.session_data: - metadata = metadata or get_metadata(current_user) or {} - schema_name = metadata["schema_name"] + if not metadata: + metadata = get_metadata(current_user) - launch_language = metadata.get("language_code") or DEFAULT_LANGUAGE_CODE + schema_name = metadata.schema_name if metadata else None + language_code = metadata.language_code if metadata else None + + launch_language = language_code or DEFAULT_LANGUAGE_CODE # pylint: disable=assigning-non-slot g.allowed_languages = get_allowed_languages(schema_name, launch_language) request_language = request.args.get("language_code") diff --git a/app/helpers/schema_helpers.py b/app/helpers/schema_helpers.py index 6398b820eb..f163afeaf5 100644 --- a/app/helpers/schema_helpers.py +++ b/app/helpers/schema_helpers.py @@ -28,11 +28,11 @@ def wrapped_function(*args: Any, **kwargs: Any) -> Any: if not session_store or not session_store.session_data: raise Unauthorized - metadata = get_metadata(current_user) or {} + metadata = get_metadata(current_user) language_code = session_store.session_data.language_code schema = load_schema_from_metadata( - metadata=metadata, language_code=language_code + metadata=metadata, language_code=language_code # type: ignore ) return function(schema, *args, **kwargs) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index aa4a5430aa..3f3d830490 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -86,12 +86,16 @@ def context(self) -> dict[str, Any]: def service_links_context( self, ) -> Optional[dict[str, Union[dict[str, str], list[dict]]]]: - metadata = get_metadata(current_user) + + ru_ref = ( + metadata["ru_ref"] if (metadata := get_metadata(current_user)) else None + ) + if service_links := self._survey_config.get_service_links( sign_out_url=self._sign_out_url, is_authenticated=current_user.is_authenticated, cookie_has_theme=bool(self._survey_type), - ru_ref=metadata.get("ru_ref") if metadata else None, # type: ignore + ru_ref=ru_ref, ): return { "toggleServicesButton": { @@ -107,8 +111,8 @@ def service_links_context( def data_layer_context( self, ) -> list[dict]: - metadata = get_metadata(current_user) - tx_id = metadata.get("tx_id") if metadata else None + tx_id = metadata.tx_id if (metadata := get_metadata(current_user)) else None + return self._survey_config.get_data_layer(tx_id=tx_id) @property diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index d16fed872b..077d5a10d3 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -4,6 +4,7 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import CompletionStatus, ProgressStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema @@ -19,7 +20,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Mapping, + metadata: Optional[MetadataProxy], response_metadata: Mapping, ): self.answer_store = answer_store diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 05b801df65..a21df30c82 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -11,6 +11,7 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_transforms import PlaceholderTransforms from app.questionnaire.relationship_location import RelationshipLocation @@ -39,7 +40,7 @@ def __init__( language: str, answer_store: AnswerStore, list_store: ListStore, - metadata: Mapping, + metadata: Optional[MetadataProxy], response_metadata: Mapping, schema: QuestionnaireSchema, renderer: "PlaceholderRenderer", diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index c603dbb98d..389fadc74e 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -5,6 +5,7 @@ from app.data_models.answer import AnswerValueTypes from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.plural_forms import get_plural_form_key @@ -23,7 +24,7 @@ def __init__( language: str, answer_store: AnswerStore, list_store: ListStore, - metadata: Mapping, + metadata: Optional[MetadataProxy], response_metadata: Mapping, schema: QuestionnaireSchema, location: Union[None, Location, RelationshipLocation] = None, @@ -58,8 +59,7 @@ def get_plural_count( if source == "list": return len(self._list_store[source_id]) - metadata_source_id: str = self._metadata[source_id] - return metadata_source_id + return self._metadata[source_id] if self._metadata else None def render_placeholder( self, diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index c5f289d085..580871fb0f 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -1,4 +1,4 @@ -from collections import abc, defaultdict +from collections import defaultdict from copy import deepcopy from dataclasses import dataclass from functools import cached_property @@ -10,6 +10,7 @@ from app.data_models.answer import Answer from app.forms import error_messages from app.questionnaire.rules.operator import OPERATION_MAPPING +from app.utilities.make_immutable import make_immutable DEFAULT_LANGUAGE_CODE = "en" @@ -120,13 +121,7 @@ def parent_id_map(self) -> Any: @classmethod def serialize(cls, data: Any) -> Any: - if isinstance(data, abc.Hashable): - return data - if isinstance(data, list): - return tuple((cls.serialize(item) for item in data)) - if isinstance(data, dict): - key_value_tuples = {k: cls.serialize(v) for k, v in data.items()} - return ImmutableDict(key_value_tuples) + return make_immutable(data) @classmethod def get_mutable_deepcopy(cls, data: Any) -> Any: diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index fc86455c4a..990b01ffe2 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -1,8 +1,9 @@ -from typing import Generator, Mapping, Optional, Union +from typing import Generator, Mapping, Optional from flask import url_for from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import SectionKeyType from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location @@ -19,7 +20,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Mapping[str, Union[str, int, list]], + metadata: Optional[MetadataProxy], response_metadata: Mapping, ): self._schema = schema diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 2e5140f807..89f0bd8bc8 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -3,6 +3,7 @@ from typing import Generator, Iterable, Mapping, Optional, Sequence, Union from app.data_models import AnswerStore, ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE @@ -22,7 +23,7 @@ class RuleEvaluator: schema: QuestionnaireSchema answer_store: AnswerStore list_store: ListStore - metadata: Mapping + metadata: Optional[MetadataProxy] response_metadata: Mapping location: Union[None, Location, RelationshipLocation] routing_path_block_ids: Optional[list] = None diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 028c58852b..6087aead78 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -7,6 +7,7 @@ from app.data_models.answer import AnswerValueTypes, escape_answer_value from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListModel, ListStore +from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.location import InvalidLocationException from app.questionnaire.relationship_location import RelationshipLocation @@ -23,7 +24,7 @@ class ValueSourceResolver: answer_store: AnswerStore list_store: ListStore - metadata: Mapping + metadata: Optional[MetadataProxy] response_metadata: Mapping schema: QuestionnaireSchema location: Union[None, Location, RelationshipLocation] @@ -155,7 +156,10 @@ def resolve( return self._resolve_list_value_source(value_source) if source == "metadata": - return self.metadata.get(value_source.get("identifier")) + if not self.metadata: + raise NoMetadataException + identifier = value_source["identifier"] + return self.metadata[identifier] if source == "location" and value_source.get("identifier") == "list_item_id": # This does not use the location object because diff --git a/app/questionnaire/when_rules.py b/app/questionnaire/when_rules.py index b999179798..142b7e7d28 100644 --- a/app/questionnaire/when_rules.py +++ b/app/questionnaire/when_rules.py @@ -315,7 +315,7 @@ def get_answer_value( def get_metadata_value(metadata, key): - return metadata.get(key) + return metadata[key] def get_list_count(list_store, list_name): diff --git a/app/routes/errors.py b/app/routes/errors.py index 0ac26bf492..14f3523a14 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -35,9 +35,8 @@ def log_exception(exception, status_code): - metadata = get_metadata(current_user) - if metadata: - logger.bind(tx_id=metadata["tx_id"]) + if metadata := get_metadata(current_user): + logger.bind(tx_id=metadata.tx_id) log = logger.warning if status_code < 500 else logger.error diff --git a/app/routes/flush.py b/app/routes/flush.py index d4b6c8322c..67f0499ff8 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -13,6 +13,7 @@ from app.submitter.submission_failed import SubmissionFailedException from app.utilities.json import json_dumps from app.utilities.schema import load_schema_from_metadata +from app.views.handlers.submission import get_receipting_metadata flush_blueprint = Blueprint("flush", __name__) @@ -40,9 +41,9 @@ def flush_data(): if roles and "flusher" in roles: user = _get_user(decrypted_token["response_id"]) - metadata = get_metadata(user) - if "tx_id" in metadata: - logger.bind(tx_id=metadata["tx_id"]) + + if metadata := get_metadata(user): + logger.bind(tx_id=metadata.tx_id) if _submit_data(user): return Response(status=200) return Response(status=404) @@ -86,10 +87,13 @@ def _submit_data(user): message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION ) + additional_metadata = get_receipting_metadata(metadata) + sent = current_app.eq["submitter"].send_message( encrypted_message, - tx_id=metadata.get("tx_id"), - case_id=metadata["case_id"], + tx_id=metadata.tx_id, + case_id=metadata.case_id, + **additional_metadata, ) if not sent: diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index 0f8576826b..648dc5452f 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -51,11 +51,16 @@ def before_individual_response_request(): return redirect(url_for("post_submission.get_thank_you")) logger.bind( - tx_id=metadata["tx_id"], - schema_name=metadata["schema_name"], - ce_id=metadata["collection_exercise_sid"], + tx_id=metadata.tx_id, + ce_id=metadata.collection_exercise_sid, ) + if schema_name := metadata.schema_name: + logger.bind(schema_name=schema_name) + + if schema_url := metadata.schema_url: + logger.bind(schema_url=schema_url) # pragma: no cover + logger.info( "individual-response request", method=request.method, url_path=request.full_path ) @@ -64,7 +69,7 @@ def before_individual_response_request(): # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( - metadata=questionnaire_store.metadata, + metadata=metadata, language_code=get_locale().language, ) @@ -170,9 +175,11 @@ def individual_response_post_address_confirmation(schema, questionnaire_store): if request.method == "POST": return redirect(url_for("questionnaire.get_questionnaire")) + metadata = questionnaire_store.metadata + return render_template( template="individual_response/confirmation-post", - display_address=questionnaire_store.metadata.get("display_address"), + display_address=metadata["display_address"], page_title=individual_response_handler.page_title( lazy_gettext("An individual access code has been sent by post") ), diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 1e6f9e713f..4d3710f8e4 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -86,11 +86,16 @@ def before_questionnaire_request(): return redirect(url_for("post_submission.get_thank_you")) logger.bind( - tx_id=metadata["tx_id"], - schema_name=metadata["schema_name"], - ce_id=metadata["collection_exercise_sid"], + tx_id=metadata.tx_id, + ce_id=metadata.collection_exercise_sid, ) + if schema_name := metadata.schema_name: + logger.bind(schema_name=schema_name) + + if schema_url := metadata.schema_url: + logger.bind(schema_url=schema_url) + logger.info( "questionnaire request", method=request.method, url_path=request.full_path ) @@ -120,17 +125,20 @@ def before_post_submission_request(): if not questionnaire_store.submitted_at: raise NotFound - handle_language(metadata) + handle_language(questionnaire_store.metadata) # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( metadata=metadata, language_code=get_locale().language ) - logger.bind( - tx_id=metadata.get("tx_id"), - schema_name=metadata.get("schema_name"), - ) + logger.bind(tx_id=metadata.tx_id) + + if schema_name := metadata.schema_name: + logger.bind(schema_name=schema_name) + + if schema_url := metadata.schema_url: + logger.bind(schema_url=schema_url) logger.info( "questionnaire request", method=request.method, url_path=request.full_path diff --git a/app/routes/session.py b/app/routes/session.py index 56b7bdae5c..6224a9d904 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -4,19 +4,22 @@ from flask import session as cookie_session from flask import url_for from flask_login import login_required, logout_user -from marshmallow import ValidationError +from marshmallow import INCLUDE, ValidationError from sdc.crypto.exceptions import InvalidTokenException from structlog import get_logger from werkzeug.exceptions import Unauthorized +from app.authentication.auth_payload_version import AuthPayloadVersion from app.authentication.authenticator import decrypt_token, store_session from app.authentication.jti_claim_storage import JtiTokenUsed, use_jti_claim +from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store, get_session_timeout_in_seconds from app.helpers.template_helpers import get_survey_config, render_template from app.routes.errors import _render_error_page -from app.utilities.metadata_parser import ( +from app.utilities.metadata_parser import validate_runner_claims +from app.utilities.metadata_parser_v2 import ( validate_questionnaire_claims, - validate_runner_claims, + validate_runner_claims_v2, ) from app.utilities.schema import load_schema_from_metadata @@ -51,34 +54,47 @@ def login(): validate_jti(decrypted_token) - try: - runner_claims = validate_runner_claims(decrypted_token) - except ValidationError as e: - raise InvalidTokenException("Invalid runner claims") from e + runner_claims = get_runner_claims(decrypted_token) + + metadata = MetadataProxy.from_dict(runner_claims) + # pylint: disable=assigning-non-slot - g.schema = load_schema_from_metadata(metadata=runner_claims) + g.schema = load_schema_from_metadata(metadata=metadata) schema_metadata = g.schema.json["metadata"] - try: - questionnaire_claims = validate_questionnaire_claims( - decrypted_token, schema_metadata - ) - except ValidationError as e: - raise InvalidTokenException("Invalid questionnaire claims") from e + questionnaire_claims = get_questionnaire_claims( + decrypted_token=decrypted_token, schema_metadata=schema_metadata + ) + + if metadata.version is AuthPayloadVersion.V2: + if questionnaire_claims: + runner_claims["survey_metadata"]["data"] = questionnaire_claims - claims = {**runner_claims, **questionnaire_claims} + ru_ref = questionnaire_claims.get("ru_ref") + questionnaire_id = questionnaire_claims.get("questionnaire_id") + claims = runner_claims + else: + ru_ref = runner_claims["ru_ref"] + questionnaire_id = None + claims = {**runner_claims, **questionnaire_claims} - schema_name = claims["schema_name"] tx_id = claims["tx_id"] - ru_ref = claims["ru_ref"] case_id = claims["case_id"] - logger.bind( - schema_name=schema_name, - tx_id=tx_id, - ru_ref=ru_ref, - case_id=case_id, - ) + logger_args = { + key: value + for key, value in { + "tx_id": tx_id, + "case_id": case_id, + "schema_name": metadata.schema_name, + "schema_url": metadata.schema_url, + "ru_ref": ru_ref, + "questionnaire_id": questionnaire_id, + }.items() + if value + } + logger.bind(**logger_args) + logger.info("decrypted token and parsed metadata") store_session(claims) @@ -151,3 +167,30 @@ def get_sign_out(): @session_blueprint.route("/signed-out", methods=["GET"]) def get_signed_out(): return render_template(template="signed-out") + + +def get_runner_claims(decrypted_token): + try: + if version := decrypted_token.get("version"): + if version == AuthPayloadVersion.V2.value: + return validate_runner_claims_v2(decrypted_token) + + raise InvalidTokenException(f"Invalid runner claims version: {version}") + + return validate_runner_claims(decrypted_token) + except ValidationError as e: + raise InvalidTokenException("Invalid runner claims") from e + + +def get_questionnaire_claims(decrypted_token, schema_metadata): + try: + if decrypted_token.get("version") == AuthPayloadVersion.V2.value: + claims = decrypted_token.get("survey_metadata", {}).get("data", {}) + return validate_questionnaire_claims( + claims, schema_metadata, unknown=INCLUDE + ) + + return validate_questionnaire_claims(decrypted_token, schema_metadata) + + except ValidationError as e: + raise InvalidTokenException("Invalid questionnaire claims") from e diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index d098a6be62..90796e7efd 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -4,6 +4,7 @@ from app.data_models import AnswerStore, ListStore from app.data_models.answer import AnswerValueTypes, ListAnswer +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.routing_path import RoutingPath @@ -14,7 +15,8 @@ # pylint: disable=too-many-locals,too-many-nested-blocks def convert_answers_to_payload_0_0_1( - metadata: MetadataType, + *, + metadata: MetadataProxy, response_metadata: MetadataType, answer_store: AnswerStore, list_store: ListStore, diff --git a/app/submitter/convert_payload_0_0_3.py b/app/submitter/convert_payload_0_0_3.py index 25e75ce445..20d58160e3 100644 --- a/app/submitter/convert_payload_0_0_3.py +++ b/app/submitter/convert_payload_0_0_3.py @@ -10,6 +10,7 @@ # pylint: disable=too-many-locals def convert_answers_to_payload_0_0_3( + *, answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 8d19bf706a..78151dd946 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -1,29 +1,20 @@ from datetime import datetime -from typing import Any, Mapping, Union +from typing import Any, Mapping, Optional, Union from structlog import get_logger from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, ) from app.questionnaire.routing_path import RoutingPath -from app.submitter.convert_payload_0_0_1 import convert_answers_to_payload_0_0_1 -from app.submitter.convert_payload_0_0_3 import convert_answers_to_payload_0_0_3 +from app.submitter.converter_v2 import get_payload_data logger = get_logger() -MetadataType = Mapping[str, Union[str, int, list]] - - -class DataVersionError(Exception): - def __init__(self, version: str): - super().__init__() - self.version = version - - def __str__(self) -> str: - return f"Data version {self.version} not supported" +MetadataType = Mapping[str, Optional[Union[str, list]]] def convert_answers( @@ -72,6 +63,9 @@ def convert_answers( Data payload """ metadata = questionnaire_store.metadata + if not metadata: + raise NoMetadataException + response_metadata = questionnaire_store.response_metadata answer_store = questionnaire_store.answer_store list_store = questionnaire_store.list_store @@ -79,8 +73,8 @@ def convert_answers( survey_id = schema.json["survey_id"] payload = { - "case_id": metadata["case_id"], - "tx_id": metadata["tx_id"], + "case_id": metadata.case_id, + "tx_id": metadata.tx_id, "type": "uk.gov.ons.edc.eq:surveyresponse", "version": schema.json["data_version"], "origin": "uk.gov.ons.edc.eq", @@ -89,63 +83,60 @@ def convert_answers( "submitted_at": submitted_at.isoformat(), "collection": build_collection(metadata), "metadata": build_metadata(metadata), - "launch_language_code": metadata.get("language_code", DEFAULT_LANGUAGE_CODE), + "launch_language_code": metadata.language_code or DEFAULT_LANGUAGE_CODE, } optional_properties = get_optional_payload_properties(metadata, response_metadata) - if schema.json["data_version"] == "0.0.3": - payload["data"] = { - "answers": convert_answers_to_payload_0_0_3( - answer_store, list_store, schema, routing_path - ), - "lists": list_store.serialize(), - } - elif schema.json["data_version"] == "0.0.1": - payload["data"] = convert_answers_to_payload_0_0_1( - metadata, response_metadata, answer_store, list_store, schema, routing_path - ) - else: - raise DataVersionError(schema.json["data_version"]) - - logger.info("converted answer ready for submission") + payload["data"] = get_payload_data( + answer_store=answer_store, + list_store=list_store, + schema=schema, + routing_path=routing_path, + metadata=metadata, + response_metadata=response_metadata, + ) return payload | optional_properties -def build_collection(metadata: MetadataType) -> MetadataType: +def build_collection(metadata: MetadataProxy) -> MetadataType: collection_metadata = { - "exercise_sid": metadata["collection_exercise_sid"], - "schema_name": metadata["schema_name"], + "exercise_sid": metadata.collection_exercise_sid, + "schema_name": metadata.schema_name, "period": metadata["period_id"], } - if form_type := metadata.get("form_type"): + if form_type := metadata["form_type"]: collection_metadata["instrument_id"] = form_type return collection_metadata -def build_metadata(metadata: MetadataType) -> MetadataType: - downstream_metadata = {"user_id": metadata["user_id"], "ru_ref": metadata["ru_ref"]} +def build_metadata(metadata: MetadataProxy) -> MetadataType: + + downstream_metadata = { + "user_id": metadata["user_id"], + "ru_ref": metadata["ru_ref"], + } - if metadata.get("ref_p_start_date"): - downstream_metadata["ref_period_start_date"] = metadata["ref_p_start_date"] - if metadata.get("ref_p_end_date"): - downstream_metadata["ref_period_end_date"] = metadata["ref_p_end_date"] - if metadata.get("display_address"): - downstream_metadata["display_address"] = metadata["display_address"] + if ref_p_start_date := metadata["ref_p_start_date"]: + downstream_metadata["ref_period_start_date"] = ref_p_start_date + if ref_p_end_date := metadata["ref_p_end_date"]: + downstream_metadata["ref_period_end_date"] = ref_p_end_date + if display_address := metadata["display_address"]: + downstream_metadata["display_address"] = display_address return downstream_metadata def get_optional_payload_properties( - metadata: MetadataType, response_metadata: Mapping + metadata: MetadataProxy, response_metadata: Mapping ) -> MetadataType: payload = {} for key in ["channel", "case_type", "form_type", "region_code", "case_ref"]: - if value := metadata.get(key): + if value := metadata[key]: payload[key] = value if started_at := response_metadata.get("started_at"): payload["started_at"] = started_at diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py new file mode 100644 index 0000000000..56997e396b --- /dev/null +++ b/app/submitter/converter_v2.py @@ -0,0 +1,137 @@ +from datetime import datetime +from typing import Any, Mapping, Optional, OrderedDict, Union + +from structlog import get_logger + +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.data_models import AnswerStore, ListStore, QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException +from app.questionnaire.questionnaire_schema import ( + DEFAULT_LANGUAGE_CODE, + QuestionnaireSchema, +) +from app.questionnaire.routing_path import RoutingPath +from app.submitter.convert_payload_0_0_1 import convert_answers_to_payload_0_0_1 +from app.submitter.convert_payload_0_0_3 import convert_answers_to_payload_0_0_3 + +logger = get_logger() + +MetadataType = Mapping[str, Optional[Union[str, list]]] + + +class DataVersionError(Exception): + def __init__(self, version: str): + super().__init__() + self.version = version + + def __str__(self) -> str: + return f"Data version {self.version} not supported" + + +def convert_answers_v2( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + routing_path: RoutingPath, + submitted_at: datetime, + flushed: bool = False, +) -> dict[str, Any]: + """ + Create the JSON answer format for down stream processing, the format can be found here: + https://github.com/ONSdigital/ons-schema-definitions/blob/main/docs/eq_runner_to_downstream_payload_v2.md + + Args: + schema: QuestionnaireSchema instance with populated schema json + questionnaire_store: EncryptedQuestionnaireStorage instance for accessing current questionnaire data + routing_path: The full routing path followed by the user when answering the questionnaire + submitted_at: The date and time of submission + flushed: True when system submits the users answers, False when submitted by user. + Returns: + Data payload + """ + metadata = questionnaire_store.metadata + if not metadata: + raise NoMetadataException + + response_metadata = questionnaire_store.response_metadata + answer_store = questionnaire_store.answer_store + list_store = questionnaire_store.list_store + + survey_id = schema.json["survey_id"] + + payload: dict = { + "case_id": metadata.case_id, + "tx_id": metadata.tx_id, + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": AuthPayloadVersion.V2.value, + "data_version": schema.json["data_version"], + "origin": "uk.gov.ons.edc.eq", + "collection_exercise_sid": metadata.collection_exercise_sid, + "schema_name": metadata.schema_name, + "flushed": flushed, + "submitted_at": submitted_at.isoformat(), + "launch_language_code": metadata.language_code or DEFAULT_LANGUAGE_CODE, + } + + optional_properties = get_optional_payload_properties(metadata, response_metadata) + + payload["survey_metadata"] = {"survey_id": survey_id} + if metadata.survey_metadata: + payload["survey_metadata"].update(metadata.survey_metadata.data) + + payload["data"] = get_payload_data( + answer_store=answer_store, + list_store=list_store, + schema=schema, + routing_path=routing_path, + metadata=metadata, + response_metadata=response_metadata, + ) + + logger.info("converted answer ready for submission") + + return payload | optional_properties + + +def get_optional_payload_properties( + metadata: MetadataProxy, response_metadata: Mapping +) -> MetadataType: + payload = {} + + for key in ["channel", "region_code"]: + if value := metadata[key]: + payload[key] = value + if started_at := response_metadata.get("started_at"): + payload["started_at"] = started_at + + return payload + + +def get_payload_data( + answer_store: AnswerStore, + list_store: ListStore, + schema: QuestionnaireSchema, + routing_path: RoutingPath, + metadata: MetadataProxy, + response_metadata: Mapping, +) -> Union[OrderedDict[str, Any], dict[str, Union[list[Any]]]]: + if schema.json["data_version"] == "0.0.1": + return convert_answers_to_payload_0_0_1( + metadata=metadata, + response_metadata=response_metadata, + answer_store=answer_store, + list_store=list_store, + schema=schema, + full_routing_path=routing_path, + ) + if schema.json["data_version"] == "0.0.3": + return { + "answers": convert_answers_to_payload_0_0_3( + answer_store=answer_store, + list_store=list_store, + schema=schema, + full_routing_path=routing_path, + ), + "lists": list_store.serialize(), + } + + raise DataVersionError(schema.json["data_version"]) diff --git a/app/submitter/submitter.py b/app/submitter/submitter.py index c5779d59b4..6094bdaa25 100644 --- a/app/submitter/submitter.py +++ b/app/submitter/submitter.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional +from typing import Mapping, Optional, Union from uuid import uuid4 from google.cloud import storage # type: ignore @@ -14,13 +14,19 @@ class LogSubmitter: @staticmethod - def send_message(message: str, tx_id: str, case_id: str) -> bool: + def send_message( + message: str, + tx_id: str, + case_id: str, + **kwargs: Mapping[str, Union[str, int]], + ) -> bool: logger.info("sending message") logger.info( "message payload", message=message, case_id=case_id, tx_id=tx_id, + **kwargs, ) return True @@ -31,11 +37,20 @@ def __init__(self, bucket_name: str) -> None: client = storage.Client() self.bucket = client.get_bucket(bucket_name) - def send_message(self, message: str, tx_id: str, case_id: str) -> bool: + def send_message( + self, + message: str, + tx_id: str, + case_id: str, + **kwargs: dict, + ) -> bool: logger.info("sending message") blob = self.bucket.blob(tx_id) - blob.metadata = {"tx_id": tx_id, "case_id": case_id} + + metadata: dict = {"tx_id": tx_id, "case_id": case_id, **kwargs} + + blob.metadata = metadata # DEFAULT_RETRY is not idempotent. # However, this behaviour was deemed acceptable for our use case. diff --git a/app/utilities/make_immutable.py b/app/utilities/make_immutable.py new file mode 100644 index 0000000000..b48cb20b83 --- /dev/null +++ b/app/utilities/make_immutable.py @@ -0,0 +1,14 @@ +from collections import abc +from typing import Any + +from werkzeug.datastructures import ImmutableDict + + +def make_immutable(data: Any) -> Any: + if isinstance(data, abc.Hashable): + return data + if isinstance(data, list): + return tuple((make_immutable(item) for item in data)) + if isinstance(data, dict): + key_value_tuples = {k: make_immutable(v) for k, v in data.items()} + return ImmutableDict(key_value_tuples) diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index 3730056b77..54624ca98a 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -15,45 +15,11 @@ from structlog import get_logger from app.questionnaire.rules.utils import parse_iso_8601_datetime +from app.utilities.metadata_validators import DateString, RegionCode, UUIDString from app.utilities.schema import get_schema_name_from_params logger = get_logger() - -class RegionCode(validate.Regexp): - """A region code defined as per ISO 3166-2:GB - Currently, this does not validate the subdivision, but only checks length - """ - - def __init__(self, *args, **kwargs): - super().__init__("^GB-[A-Z]{3}$", *args, **kwargs) - - -class UUIDString(fields.UUID): - """Currently, runner cannot handle UUID objects in metadata - Since all metadata is serialized and deserialized to JSON. - This custom field deserializes UUIDs to strings. - """ - - def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ - return str(super()._deserialize(*args, **kwargs)) - - -class DateString(fields.DateTime): - """Currently, runner cannot handle Date objects in metadata - Since all metadata is serialized and deserialized to JSON. - This custom field deserializes Dates to strings. - """ - - def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ - date = super()._deserialize(*args, **kwargs) - - if self.format == "iso8601": - return date.isoformat() - - return date.strftime(self.format) - - VALIDATORS = { "date": functools.partial(DateString, format="%Y-%m-%d", required=True), "uuid": functools.partial(UUIDString, required=True), @@ -176,41 +142,6 @@ def update_response_id( ) -def validate_questionnaire_claims(claims, questionnaire_specific_metadata): - """Validate any survey specific claims required for a questionnaire""" - dynamic_fields = {} - - for metadata_field in questionnaire_specific_metadata: - field_arguments = {} - validators = [] - - if metadata_field.get("optional"): - field_arguments["required"] = False - - if any( - length_limit in metadata_field - for length_limit in ("min_length", "max_length", "length") - ): - validators.append( - validate.Length( - min=metadata_field.get("min_length"), - max=metadata_field.get("max_length"), - equal=metadata_field.get("length"), - ) - ) - - dynamic_fields[metadata_field["name"]] = VALIDATORS[metadata_field["type"]]( - validate=validators, **field_arguments - ) - - questionnaire_metadata_schema = type( - "QuestionnaireMetadataSchema", (Schema, StripWhitespaceMixin), dynamic_fields - )(unknown=EXCLUDE) - - # The load method performs validation. - return questionnaire_metadata_schema.load(claims) - - def validate_runner_claims(claims: Dict): """Validate claims required for runner to function""" runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py new file mode 100644 index 0000000000..2a4dfb7cc4 --- /dev/null +++ b/app/utilities/metadata_parser_v2.py @@ -0,0 +1,152 @@ +import functools +from datetime import datetime, timezone +from typing import Dict + +from marshmallow import ( + EXCLUDE, + INCLUDE, + Schema, + ValidationError, + fields, + pre_load, + validate, + validates_schema, +) +from structlog import get_logger + +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.questionnaire.rules.utils import parse_iso_8601_datetime +from app.utilities.metadata_validators import DateString, RegionCode, UUIDString + +logger = get_logger() + + +VALIDATORS = { + "date": functools.partial(DateString, format="%Y-%m-%d", required=True), + "uuid": functools.partial(UUIDString, required=True), + "boolean": functools.partial(fields.Boolean, required=True), + "string": functools.partial(fields.String, required=True), + "url": functools.partial(fields.Url, required=True), + "iso_8601_date_string": functools.partial( + DateString, format="iso8601", required=True + ), +} + + +class StripWhitespaceMixin: + @pre_load() + def strip_whitespace( + self, items, **kwargs + ): # pylint: disable=no-self-use, unused-argument + for key, value in items.items(): + if isinstance(value, str): + items[key] = value.strip() + return items + + +class Data(Schema, StripWhitespaceMixin): + pass + + +class SurveyMetadata(Schema, StripWhitespaceMixin): + data = fields.Nested(Data, unknown=INCLUDE, validate=validate.Length(min=1)) + receipting_keys = fields.List(fields.String) + + @validates_schema + def validate_receipting_keys(self, data, **kwargs): + # pylint: disable=no-self-use, unused-argument + if data and (receipting_keys := data.get("receipting_keys", {})): + missing_receipting_keys = [ + receipting_key + for receipting_key in receipting_keys + if receipting_key not in data.get("data", {}) + ] + + if missing_receipting_keys: + raise ValidationError( + f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" + ) + + +class RunnerMetadataSchema(Schema, StripWhitespaceMixin): + """Metadata which is required for the operation of runner itself""" + + jti = VALIDATORS["uuid"]() # type:ignore + tx_id = VALIDATORS["uuid"]() # type:ignore + case_id = VALIDATORS["uuid"]() # type:ignore + collection_exercise_sid = VALIDATORS["string"]( + validate=validate.Length(min=1) + ) # type:ignore + version = VALIDATORS["string"]( + required=True, validate=validate.OneOf([AuthPayloadVersion.V2.value]) + ) # type:ignore + schema_name = VALIDATORS["string"](required=False) # type:ignore + schema_url = VALIDATORS["url"](required=False) # type:ignore + response_id = VALIDATORS["string"](required=True) # type:ignore + account_service_url = VALIDATORS["url"](required=True) # type:ignore + + language_code = VALIDATORS["string"](required=False) # type:ignore + channel = VALIDATORS["string"]( + required=False, validate=validate.Length(min=1) + ) # type:ignore + response_expires_at = VALIDATORS["iso_8601_date_string"]( + required=False, + validate=lambda x: parse_iso_8601_datetime(x) > datetime.now(tz=timezone.utc), + ) # type:ignore + region_code = VALIDATORS["string"]( + required=False, validate=RegionCode() + ) # type:ignore + + roles = fields.List(fields.String(), required=False) + survey_metadata = fields.Nested(SurveyMetadata, required=False) + + @validates_schema + def validate_schema_name_is_set(self, data, **kwargs): + # pylint: disable=no-self-use, unused-argument + if data and not (data.get("schema_name") or data.get("schema_url")): + raise ValidationError( + "Neither schema_name or schema_url has been set in metadata" + ) + + +def validate_questionnaire_claims( + claims, questionnaire_specific_metadata, unknown=EXCLUDE +): + """Validate any survey specific claims required for a questionnaire""" + dynamic_fields = {} + + for metadata_field in questionnaire_specific_metadata: + field_arguments = {} + validators = [] + + if metadata_field.get("optional"): + field_arguments["required"] = False + + if any( + length_limit in metadata_field + for length_limit in ("min_length", "max_length", "length") + ): + validators.append( + validate.Length( + min=metadata_field.get("min_length"), + max=metadata_field.get("max_length"), + equal=metadata_field.get("length"), + ) + ) + + dynamic_fields[metadata_field["name"]] = VALIDATORS[metadata_field["type"]]( + validate=validators, **field_arguments + ) + + questionnaire_metadata_schema = type( + "QuestionnaireMetadataSchema", (Schema, StripWhitespaceMixin), dynamic_fields + )(unknown=unknown) + + # The load method performs validation. + return questionnaire_metadata_schema.load(claims) + + +def validate_runner_claims_v2(claims: Dict): + """Validate claims required for runner to function""" + runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) + return runner_metadata_schema.load(claims) diff --git a/app/utilities/metadata_validators.py b/app/utilities/metadata_validators.py new file mode 100644 index 0000000000..4fe55ca3cb --- /dev/null +++ b/app/utilities/metadata_validators.py @@ -0,0 +1,35 @@ +from marshmallow import fields, validate + + +class RegionCode(validate.Regexp): + """A region code defined as per ISO 3166-2:GB + Currently, this does not validate the subdivision, but only checks length + """ + + def __init__(self, *args, **kwargs): + super().__init__("^GB-[A-Z]{3}$", *args, **kwargs) + + +class UUIDString(fields.UUID): + """Currently, runner cannot handle UUID objects in metadata + Since all metadata is serialized and deserialized to JSON. + This custom field deserializes UUIDs to strings. + """ + + def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ + return str(super()._deserialize(*args, **kwargs)) + + +class DateString(fields.DateTime): + """Currently, runner cannot handle Date objects in metadata + Since all metadata is serialized and deserialized to JSON. + This custom field deserializes Dates to strings. + """ + + def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ + date = super()._deserialize(*args, **kwargs) + + if self.format == "iso8601": + return date.isoformat() + + return date.strftime(self.format) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index c9d322bf66..62b0d3ba3f 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -3,13 +3,14 @@ from functools import lru_cache from glob import glob from pathlib import Path -from typing import Any, Mapping, Optional +from typing import Mapping, Optional import requests from requests import RequestException from requests.adapters import HTTPAdapter, Retry from structlog import get_logger +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, @@ -99,11 +100,12 @@ def get_allowed_languages(schema_name, launch_language): def load_schema_from_metadata( - metadata: Mapping[str, Any], *, language_code: Optional[str] = None + metadata: MetadataProxy, *, language_code: Optional[str] = None ) -> QuestionnaireSchema: - metadata = metadata or {} - language_code = language_code or metadata.get("language_code") - if schema_url := metadata.get("schema_url"): + + language_code = language_code or metadata.language_code + + if metadata and (schema_url := metadata.schema_url): # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. # This should not be used in production. @@ -126,7 +128,8 @@ def load_schema_from_metadata( return schema return load_schema_from_name( - metadata.get("schema_name"), language_code=language_code + metadata.schema_name, + language_code=language_code, ) diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index b137a4391d..9de5e1af31 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -1,8 +1,9 @@ from abc import ABC -from typing import Any, Mapping +from typing import Mapping, Optional from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import QuestionnaireSchema @@ -17,7 +18,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Mapping[str, Any], + metadata: Optional[MetadataProxy], response_metadata: Mapping, ): self._language = language diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index df3c87ada4..3b6b2d1ad9 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Any, Mapping, Optional +from typing import Mapping, Optional from flask import url_for @@ -9,6 +9,7 @@ from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content +from ...data_models.metadata_proxy import MetadataProxy from .context import Context from .list_context import ListContext from .summary import Group @@ -22,7 +23,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Mapping[str, Any], + metadata: Optional[MetadataProxy], response_metadata: Mapping, routing_path: RoutingPath, current_location: Location, diff --git a/app/views/contexts/submission_metadata_context.py b/app/views/contexts/submission_metadata_context.py index 5fc4b78103..1a216ff1b5 100644 --- a/app/views/contexts/submission_metadata_context.py +++ b/app/views/contexts/submission_metadata_context.py @@ -20,6 +20,7 @@ def build_submission_metadata_context( } ], } + submission_reference = { "term": lazy_gettext("Submission reference:"), "descriptions": [{"description": convert_tx_id(tx_id)}], diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index c19702cec9..1dcfa02544 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -4,6 +4,7 @@ from flask import url_for from flask_babel import lazy_gettext +from app.data_models.metadata_proxy import MetadataProxy from app.globals import ( get_view_submitted_response_expiration_time, has_view_submitted_response_expired, @@ -18,26 +19,26 @@ def build_thank_you_context( schema: QuestionnaireSchema, - metadata: dict, + metadata: MetadataProxy, submitted_at: datetime, survey_type: SurveyType, guidance_content: Optional[dict] = None, ) -> Mapping: if survey_type is SurveyType.SOCIAL: submission_text = lazy_gettext("Your answers have been submitted.") - elif metadata.get("trad_as") and metadata.get("ru_name"): + elif (trad_as := metadata["trad_as"]) and (ru_name := metadata["ru_name"]): submission_text = lazy_gettext( "Your answers have been submitted for {company_name} ({trading_name})" ).format( - company_name=metadata["ru_name"], - trading_name=metadata["trad_as"], + company_name=ru_name, + trading_name=trad_as, ) else: submission_text = lazy_gettext( "Your answers have been submitted for {company_name}" ).format(company_name=metadata["ru_name"]) context_metadata = build_submission_metadata_context( - survey_type, submitted_at, metadata["tx_id"] # type: ignore + survey_type, submitted_at, metadata.tx_id ) return { "hide_sign_out_button": True, @@ -69,8 +70,9 @@ def build_view_submitted_response_context(schema, submitted_at): def build_census_thank_you_context( - metadata: dict, confirmation_email_form, form_type + metadata: MetadataProxy, confirmation_email_form, form_type ) -> Mapping: + context = { "display_address": metadata["display_address"], "form_type": form_type, diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index a95dfdfbfe..4c62eeea3f 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -5,6 +5,7 @@ from flask_babel import lazy_gettext from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import NoMetadataException from app.globals import has_view_submitted_response_expired from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.survey_config.survey_type import SurveyType @@ -25,21 +26,28 @@ def build_view_submitted_response_context( questionnaire_store.submitted_at # type: ignore ) + metadata = questionnaire_store.metadata + if not metadata: + raise NoMetadataException + + trad_as = metadata["trad_as"] + ru_name = metadata["ru_name"] + if survey_type is SurveyType.SOCIAL: submitted_text = lazy_gettext("Answers submitted.") - elif trad_as := questionnaire_store.metadata.get("trad_as"): + elif trad_as: submitted_text = lazy_gettext( "Answers submitted for {ru_name} ({trad_as})" - ).format(ru_name=questionnaire_store.metadata["ru_name"], trad_as=trad_as) + ).format(ru_name=ru_name, trad_as=trad_as) else: submitted_text = lazy_gettext( "Answers submitted for {ru_name}" - ).format(ru_name=questionnaire_store.metadata["ru_name"]) + ).format(ru_name=ru_name) metadata = build_submission_metadata_context( survey_type, questionnaire_store.submitted_at, # type: ignore - questionnaire_store.metadata["tx_id"], + metadata.tx_id, ) context = { "hide_sign_out_button": True, @@ -57,7 +65,7 @@ def build_view_submitted_response_context( answer_store=questionnaire_store.answer_store, list_store=questionnaire_store.list_store, progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, # type: ignore + metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, ) context["summary"] = summary_context() diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index c095e4961b..2dfd07e7cb 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from functools import cached_property -from typing import Any, Mapping +from typing import Mapping from flask import current_app, url_for from flask_babel import gettext, lazy_gettext @@ -15,6 +15,7 @@ SessionData, SessionStore, ) +from app.data_models.metadata_proxy import MetadataProxy from app.forms.questionnaire_form import generate_form from app.helpers import url_safe_serializer from app.questionnaire import QuestionnaireSchema, QuestionSchemaType @@ -157,17 +158,17 @@ def _publish_fulfilment_request(self): class ConfirmationEmailFulfilmentRequest(FulfilmentRequest): email_address: str session_data: SessionData - metadata: Mapping[str, Any] + metadata: MetadataProxy schema: QuestionnaireSchema def _payload(self) -> Mapping: return { "fulfilmentRequest": { "email_address": self.email_address, - "display_address": self.metadata.get("display_address"), + "display_address": self.metadata["display_address"], "form_type": self.schema.form_type, "language_code": self.session_data.language_code, "region_code": self.schema.region_code, - "tx_id": self.metadata["tx_id"], + "tx_id": self.metadata.tx_id, } } diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index 55c9be7a9c..3c97870e1a 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -29,7 +29,6 @@ def rendered_block(self): def get_context(self): return { "block": self.rendered_block, - "metadata": dict(self._questionnaire_store.metadata), "individual_response_url": individual_response_url( self._schema.get_individual_response_list(), self._current_location.list_item_id, diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index a701bc853c..b735ca9600 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -7,7 +7,9 @@ from sdc.crypto.encrypter import encrypt from werkzeug.datastructures import MultiDict +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore from app.forms.questionnaire_form import QuestionnaireForm, generate_form @@ -16,12 +18,14 @@ DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, ) +from app.submitter import GCSFeedbackSubmitter, LogFeedbackSubmitter, converter_v2 from app.submitter.converter import ( build_collection, build_metadata, get_optional_payload_properties, ) from app.views.contexts.feedback_form_context import build_feedback_context +from app.views.handlers.submission import get_receipting_metadata class FeedbackNotEnabled(Exception): @@ -86,30 +90,43 @@ def handle_post(self) -> None: session_data: SessionData = self._session_store.session_data # type: ignore session_data.feedback_count += 1 - feedback_metadata = FeedbackMetadata(self._questionnaire_store.metadata["case_id"], self._questionnaire_store.metadata["tx_id"]) # type: ignore + metadata = self._questionnaire_store.metadata + if not metadata: + raise NoMetadataException # pragma: no cover + + case_id = metadata.case_id + tx_id = metadata.tx_id # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation - feedback_message = FeedbackPayload( - metadata=self._questionnaire_store.metadata, + feedback_converter = ( + FeedbackPayloadV2 + if metadata.version is AuthPayloadVersion.V2 + else FeedbackPayload + ) + feedback_message = feedback_converter( + metadata=metadata, response_metadata=self._questionnaire_store.response_metadata, schema=self._schema, - case_id=self._questionnaire_store.metadata["case_id"], + case_id=case_id, submission_language_code=session_data.language_code, feedback_count=session_data.feedback_count, feedback_text=self.form.data.get("feedback-text"), feedback_type=self.form.data.get("feedback-type"), ) - message = feedback_message() - metadata = feedback_metadata() - message.update(metadata) + encrypted_message = encrypt( - message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION # type: ignore + feedback_message(), current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION # type: ignore ) - if not current_app.eq["feedback_submitter"].upload( # type: ignore - metadata, encrypted_message - ): + additional_metadata = get_receipting_metadata(metadata) + + feedback_metadata = FeedbackMetadata( + tx_id=tx_id, case_id=case_id, **additional_metadata + ) + + submitter: Union[GCSFeedbackSubmitter, LogFeedbackSubmitter] = current_app.eq["feedback_submitter"] # type: ignore + if not submitter.upload(feedback_metadata(), encrypted_message): raise FeedbackUploadFailed() self._session_store.save() @@ -187,10 +204,13 @@ def is_enabled(schema: QuestionnaireSchema) -> bool: class FeedbackMetadata: - def __init__(self, case_id: str, tx_id: str): + def __init__(self, case_id: str, tx_id: str, **kwargs: dict): self.case_id = case_id self.tx_id = tx_id + for key, value in kwargs.items(): + setattr(self, key, value) + def __call__(self) -> dict[str, str]: return vars(self) @@ -247,7 +267,7 @@ class FeedbackPayload: def __init__( self, - metadata: Mapping[str, Union[str, int, list]], + metadata: MetadataProxy, response_metadata: Mapping[str, Union[str, int, list]], schema: QuestionnaireSchema, case_id: Optional[str], @@ -277,11 +297,10 @@ def __call__(self) -> dict[str, Any]: "submission_language_code": ( self.submission_language_code or DEFAULT_LANGUAGE_CODE ), - "tx_id": self.metadata["tx_id"], + "tx_id": self.metadata.tx_id, "type": "uk.gov.ons.edc.eq:feedback", - "launch_language_code": self.metadata.get( - "language_code", DEFAULT_LANGUAGE_CODE - ), + "launch_language_code": self.metadata.language_code + or DEFAULT_LANGUAGE_CODE, "version": "0.0.1", } @@ -296,3 +315,78 @@ def __call__(self) -> dict[str, Any]: } return payload | optional_properties + + +class FeedbackPayloadV2: + """ + Create the feedback payload object for down stream processing in the following format: + v0.0.1: https://github.com/ONSdigital/ons-schema-definitions/blob/main/examples/eq_runner_to_downstream/payload_v2/business/feedback_0_0_1.json + v0.0.3: https://github.com/ONSdigital/ons-schema-definitions/blob/main/examples/eq_runner_to_downstream/payload_v2/business/feedback_0_0_3.json + ``` + :param metadata: Questionnaire metadata + :param response_metadata: Response metadata + :param schema: QuestionnaireSchema class with populated schema json + :param case_id: Questionnaire case id + :param submission_language_code: Language being used at the point of feedback submission + :param feedback_count: Number of feedback submissions attempted by the user + :param feedback_text: Feedback text input by the user + :param feedback_type: Type of feedback selected by the user + + + :return payload: Feedback payload object + """ + + def __init__( + self, + metadata: MetadataProxy, + response_metadata: Mapping[str, Union[str, int, list]], + schema: QuestionnaireSchema, + case_id: Optional[str], + submission_language_code: Optional[str], + feedback_count: int, + feedback_text: str, + feedback_type: str, + ): + self.metadata = metadata + self.response_metadata = response_metadata + self.case_id = case_id + self.schema = schema + self.submission_language_code = submission_language_code + self.feedback_count = feedback_count + self.feedback_text = feedback_text + self.feedback_type = feedback_type + + def __call__(self) -> dict[str, Any]: + payload = { + "tx_id": self.metadata.tx_id, + "type": "uk.gov.ons.edc.eq:feedback", + "version": AuthPayloadVersion.V2.value, + "data_version": self.schema.json["data_version"], + "origin": "uk.gov.ons.edc.eq", + "flushed": False, + "submitted_at": datetime.now(tz=timezone.utc).isoformat(), + "launch_language_code": self.metadata.language_code + or DEFAULT_LANGUAGE_CODE, + "submission_language_code": ( + self.submission_language_code or DEFAULT_LANGUAGE_CODE + ), + "collection_exercise_sid": self.metadata.collection_exercise_sid, + "schema_name": self.metadata.schema_name, + "case_id": self.case_id, + "survey_metadata": {"survey_id": self.schema.json["survey_id"]}, + } + + if self.metadata.survey_metadata: + payload["survey_metadata"] |= self.metadata.survey_metadata.data + + optional_properties = converter_v2.get_optional_payload_properties( + self.metadata, self.response_metadata + ) + + payload["data"] = { + "feedback_text": self.feedback_text, + "feedback_type": self.feedback_type, + "feedback_count": str(self.feedback_count), + } + + return payload | optional_properties diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 21ee27ea79..185b3f6c77 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -10,6 +10,7 @@ from werkzeug.exceptions import BadRequest, NotFound from app.data_models import CompletionStatus, FulfilmentRequest +from app.data_models.metadata_proxy import MetadataProxy from app.forms.questionnaire_form import generate_form from app.forms.validators import sanitise_mobile_number from app.helpers import url_safe_serializer @@ -935,7 +936,7 @@ def handle_post(self): class IndividualResponseFulfilmentRequest(FulfilmentRequest): - def __init__(self, metadata: Mapping, mobile_number: Optional[str] = None): + def __init__(self, metadata: MetadataProxy, mobile_number: Optional[str] = None): self._metadata = metadata self._mobile_number = mobile_number self._fulfilment_type = "sms" if self._mobile_number else "postal" @@ -943,7 +944,7 @@ def __init__(self, metadata: Mapping, mobile_number: Optional[str] = None): def _get_individual_case_id_mapping(self) -> Mapping: return ( {} - if self._metadata.get("case_type") in ["SPG", "CE"] + if self._metadata["case_type"] in ["SPG", "CE"] else {"individualCaseId": str(uuid4())} ) @@ -967,15 +968,15 @@ def _get_fulfilment_code(self) -> str: GB_NIR_REGION_CODE: "P_UAC_UACIPA4", }, } - region_code = self._metadata["region_code"] - return fulfilment_codes[self._fulfilment_type][region_code] + if region_code := self._metadata.region_code: + return fulfilment_codes[self._fulfilment_type][region_code] def _payload(self) -> Mapping: return { "fulfilmentRequest": { **self._get_individual_case_id_mapping(), "fulfilmentCode": self._get_fulfilment_code(), - "caseId": self._metadata["case_id"], + "caseId": self._metadata.case_id, "contact": self._get_contact_mapping(), } } diff --git a/app/views/handlers/submission.py b/app/views/handlers/submission.py index 1ca01c2784..97857f6578 100644 --- a/app/views/handlers/submission.py +++ b/app/views/handlers/submission.py @@ -5,14 +5,29 @@ from flask import session as cookie_session from sdc.crypto.encrypter import encrypt +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store from app.keys import KEY_PURPOSE_SUBMISSION from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.submitter.converter import convert_answers +from app.submitter.converter_v2 import convert_answers_v2 from app.submitter.submission_failed import SubmissionFailedException from app.utilities.json import json_dumps +def get_receipting_metadata(metadata: MetadataProxy) -> dict: + return ( + {item: metadata[item] for item in metadata.survey_metadata.receipting_keys} + if ( + metadata.version is AuthPayloadVersion.V2 + and metadata.survey_metadata + and metadata.survey_metadata.receipting_keys + ) + else {} + ) + + class SubmissionHandler: def __init__(self, schema, questionnaire_store, full_routing_path): self._schema = schema @@ -27,15 +42,20 @@ def submitted_at(self): def submit_questionnaire(self): payload = self.get_payload() + message = json_dumps(payload) encrypted_message = encrypt( message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION ) + + additional_metadata = get_receipting_metadata(self._metadata) + submitted = current_app.eq["submitter"].send_message( encrypted_message, - case_id=self._metadata["case_id"], - tx_id=self._metadata.get("tx_id"), + case_id=self._metadata.case_id, + tx_id=self._metadata.tx_id, + **additional_metadata, ) if not submitted: @@ -47,12 +67,19 @@ def submit_questionnaire(self): self._questionnaire_store.save() def get_payload(self): - payload = convert_answers( + answer_converter = ( + convert_answers_v2 + if self._metadata.version is AuthPayloadVersion.V2 + else convert_answers + ) + + payload = answer_converter( self._schema, self._questionnaire_store, self._full_routing_path, self.submitted_at, ) + payload["submission_language_code"] = ( self._session_store.session_data.language_code or DEFAULT_LANGUAGE_CODE ) diff --git a/schemas/test/en/test_new_routing_case_insensitive_text_field.json b/schemas/test/en/test_new_routing_case_insensitive_text_field.json index d573f8793d..2b910c9284 100644 --- a/schemas/test/en/test_new_routing_case_insensitive_text_field.json +++ b/schemas/test/en/test_new_routing_case_insensitive_text_field.json @@ -93,25 +93,25 @@ ] }, { - "id": "country-interstitial-india-or-azerbaijan", + "id": "country-interstitial-georgia", "type": "Interstitial", "content": { - "title": "Condition: Submitted India or Azerbaijan", + "title": "Condition: Submitted Georgia", "contents": [ { - "description": "You submitted India or Azerbaijan." + "description": "You submitted Georgia." } ] } }, { - "id": "country-interstitial-georgia", + "id": "country-interstitial-india-or-azerbaijan", "type": "Interstitial", "content": { - "title": "Condition: Submitted Georgia", + "title": "Condition: Submitted India or Azerbaijan", "contents": [ { - "description": "You submitted Georgia." + "description": "You submitted India or Azerbaijan." } ] } diff --git a/schemas/test/en/test_theme_social.json b/schemas/test/en/test_theme_social.json index 3be2f8dfef..c860e574a3 100644 --- a/schemas/test/en/test_theme_social.json +++ b/schemas/test/en/test_theme_social.json @@ -9,18 +9,17 @@ "description": "A questionnaire to demo the social survey theme", "metadata": [ { - "name": "user_id", + "name": "case_ref", "type": "string" }, { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", + "name": "questionnaire_id", "type": "string" } ], + "post_submission": { + "feedback": true + }, "questionnaire_flow": { "type": "Linear", "options": { diff --git a/tests/app/authentication/test_roles.py b/tests/app/authentication/test_roles.py index 2d8174e9c5..8f9535a846 100644 --- a/tests/app/authentication/test_roles.py +++ b/tests/app/authentication/test_roles.py @@ -2,6 +2,7 @@ from werkzeug.exceptions import Forbidden from app.authentication.roles import role_required +from tests.app.questionnaire.conftest import get_metadata def test_role_required_unauthenticated_no_metadata( @@ -59,7 +60,7 @@ def test_role_required_authenticated_with_metadata_none_roles( # Given I am authenticated but my metadata contains a # roles list set to None mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": None} + mock_get_metadata.return_value = get_metadata({"roles": None}) # And I have decorated a function with role_required wrapper = role_required("dumper") @@ -77,7 +78,7 @@ def test_role_required_authenticated_with_metadata_empty_roles( # Given I am authenticated and my metadata contains an empty # roles list mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": []} + mock_get_metadata.return_value = get_metadata({"roles": []}) # And I have decorated a function with role_required wrapper = role_required("dumper") @@ -94,7 +95,7 @@ def test_role_required_authenticated_with_metadata_wrong_role( ): # Given I am authenticated and my metadata contains a single role. mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["flusher"]} + mock_get_metadata.return_value = get_metadata({"roles": ["flusher"]}) # And I have decorated a function with role_required, specifying a # role that isn't in the metadata roles list. @@ -112,7 +113,7 @@ def test_role_required_authenticated_with_metadata_matching_role( ): # Given I am authenticated and my metadata contains a single role. mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["dumper"]} + mock_get_metadata.return_value = get_metadata({"roles": ["dumper"]}) # And I have decorated a function with role_required, specifying a # role that is listed in the metadata roles list. @@ -129,7 +130,9 @@ def test_role_required_authenticated_with_metadata_matching_multiple_role( ): # Given I am authenticated but my metadata contains multiples roles mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function with role_required, specifying a # role that is listed in the metadata roles list. @@ -146,7 +149,9 @@ def test_role_required_wrapped_with_positional_arguments( ): # Given I am authenticated and my metadata contains roles mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function that takes multiple positional arguments # with role_required, specifying a role that is listed in the metadata @@ -164,7 +169,9 @@ def test_role_required_wrapped_with_keyword_arguments( ): # Given I am authenticated and my metadata contains roles mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function that takes multiple positional arguments # with role_required, specifying a role that is listed in the metadata @@ -182,7 +189,9 @@ def test_role_required_wrapped_with_positional_and_keyword_arguments( ): # Given I am authenticated and my metadata contains roles mock_current_user.is_authenticated = True - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function that takes multiple positional arguments # with role_required, specifying a role that is listed in the metadata @@ -217,7 +226,9 @@ def test_role_required_unauthenticated_wrapped_with_keyword_arguments( ): # Given I am not authenticated mock_current_user.is_authenticated = False - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function that takes multiple positional arguments # with role_required, specifying a role that is listed in the metadata @@ -236,7 +247,9 @@ def test_role_required_unauthenticated_wrapped_with_positional_and_keyword_argum ): # Given I am not authenticated mock_current_user.is_authenticated = False - mock_get_metadata.return_value = {"roles": ["flusher", "other", "dumper"]} + mock_get_metadata.return_value = get_metadata( + {"roles": ["flusher", "other", "dumper"]} + ) # And I have decorated a function that takes multiple positional arguments # with role_required, specifying a role that is listed in the metadata diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 8ad7adf83f..6c6a2cff45 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -9,6 +9,7 @@ from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore @@ -91,23 +92,27 @@ def fake_questionnaire_store(): storage.get_user_data = MagicMock(return_value=("{}", "ce_sid", 1, None)) storage.add_or_update = MagicMock() store = QuestionnaireStore(storage) - store.metadata = { - "schema_name": "test_checkbox", - "display_address": "68 Abingdon Road, Goathill", - "tx_id": "tx_id", - "language_code": "en", - } + store.metadata = MetadataProxy.from_dict( + { + "schema_name": "test_checkbox", + "display_address": "68 Abingdon Road, Goathill", + "tx_id": "tx_id", + "language_code": "en", + } + ) return store @pytest.fixture def fake_metadata(): - return { - "tx_id": "tx_id", - "language_code": "en", - "display_address": "68 Abingdon Road, Goathill", - } + return MetadataProxy.from_dict( + { + "tx_id": "tx_id", + "language_code": "en", + "display_address": "68 Abingdon Road, Goathill", + } + ) @pytest.fixture diff --git a/tests/app/data_model/test_metadata_proxy.py b/tests/app/data_model/test_metadata_proxy.py new file mode 100644 index 0000000000..73804f5860 --- /dev/null +++ b/tests/app/data_model/test_metadata_proxy.py @@ -0,0 +1,65 @@ +import pytest +from werkzeug.datastructures import ImmutableDict + +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.data_models.metadata_proxy import MetadataProxy, SurveyMetadata + +METADATA_V1 = { + "schema_name": "1_0000", + "ru_ref": "432423423423", + "response_id": "1", + "account_service_url": "account_service_url", + "tx_id": "tx_id", + "collection_exercise_sid": "collection_exercise_sid", + "case_id": "case_id", +} + +METADATA_V2 = { + "version": AuthPayloadVersion.V2.value, + "schema_name": "1_0000", + "response_id": "1", + "account_service_url": "account_service_url", + "tx_id": "tx_id", + "collection_exercise_sid": "collection_exercise_sid", + "case_id": "case_id", + "survey_metadata": { + "data": { + "ru_ref": "432423423423", + }, + }, +} + + +@pytest.mark.parametrize( + "resolved_metadata_proxy_value, metadata_var", + ( + (MetadataProxy.from_dict(METADATA_V1)["ru_ref"], METADATA_V1["ru_ref"]), + ( + MetadataProxy.from_dict(METADATA_V2)["ru_ref"], + METADATA_V2["survey_metadata"]["data"]["ru_ref"], + ), + ( + MetadataProxy.from_dict(METADATA_V1)["schema_name"], + METADATA_V1["schema_name"], + ), + ( + MetadataProxy.from_dict(METADATA_V2)["schema_name"], + METADATA_V2["schema_name"], + ), + (MetadataProxy.from_dict(METADATA_V1)["non_existing"], None), + (MetadataProxy.from_dict(METADATA_V2)["non_existing"], None), + ), +) +def test_metadata_proxy_returns_value_for_valid_key( + resolved_metadata_proxy_value, metadata_var +): + assert resolved_metadata_proxy_value == metadata_var + + +def test_survey_metadata_returns_valid_key(): + expected_values = {"key": "value"} + data = ImmutableDict(expected_values) + + survey_metadata = SurveyMetadata(data=data) + + assert survey_metadata["key"] == expected_values["key"] diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index c86f161fe9..e416ac9dbb 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -2,6 +2,7 @@ from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.utilities.json import json_dumps, json_loads @@ -19,7 +20,7 @@ def test_questionnaire_store_json_loads( # When store = QuestionnaireStore(questionnaire_store.storage) # Then - assert store.metadata.copy() == basic_input["METADATA"] + assert store.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) assert store.response_metadata == basic_input["RESPONSE_METADATA"] assert store.answer_store == AnswerStore(basic_input["ANSWERS"]) assert not hasattr(store, "NOT_A_LEGAL_TOP_LEVEL_KEY") @@ -44,7 +45,7 @@ def test_questionnaire_store_missing_keys(questionnaire_store, basic_input): # When store = QuestionnaireStore(questionnaire_store.storage) # Then - assert store.metadata.copy() == basic_input["METADATA"] + assert store.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) assert store.response_metadata == basic_input["RESPONSE_METADATA"] assert store.answer_store == AnswerStore(basic_input["ANSWERS"]) assert not store.progress_store.serialize() @@ -96,7 +97,6 @@ def test_questionnaire_store_deletes(questionnaire_store, basic_input): # Then assert "a-test-section" not in store.progress_store - assert store.metadata.copy() == {} assert len(store.answer_store) == 0 assert store.response_metadata == {} diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 63ef58a25b..2e807dcb26 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -28,7 +28,7 @@ def rule_evaluator(): evaluator = RuleEvaluator( answer_store=AnswerStore(), list_store=ListStore(), - metadata={}, + metadata=None, response_metadata=get_mock_response_metadata(), schema=get_mock_schema(), location=None, @@ -42,7 +42,7 @@ def value_source_resolver(): resolver = ValueSourceResolver( answer_store=AnswerStore(), list_store=ListStore(), - metadata={}, + metadata=None, response_metadata=get_mock_response_metadata(), schema=get_mock_schema(), location=None, diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index f2d1cf416b..34262a9687 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -17,6 +17,7 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.utilities.schema import load_schema_from_name +from tests.app.questionnaire.conftest import get_metadata def error_exists(answer_id, msg, mapped_errors): @@ -38,7 +39,7 @@ def test_form_ids_match_block_answer_ids(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -73,7 +74,7 @@ def test_form_date_range_populates_data(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -108,7 +109,7 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -151,7 +152,7 @@ def test_date_range_to_precedes_from_raises_question_error( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -193,7 +194,7 @@ def test_date_range_too_large_period_raises_question_error( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -234,7 +235,7 @@ def test_date_range_too_small_period_raises_question_error( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -273,7 +274,7 @@ def test_date_range_valid_period(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -299,11 +300,13 @@ def test_date_combined_single_validation(app, answer_store, list_store): } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-21", "ref_p_end_date": "2017-02-21", } + metadata = get_metadata(test_metadata) + response_metadata = {} expected_form_data = { @@ -349,11 +352,13 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-20", "ref_p_end_date": "2017-02-20", } + metadata = get_metadata(test_metadata) + expected_form_data = { "csrf_token": None, "date-range-from": "2017-01-01", @@ -393,11 +398,13 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-20", "ref_p_end_date": "2017-02-20", } + metadata = get_metadata(test_metadata) + response_metadata = {} expected_form_data = { @@ -437,11 +444,13 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + response_metadata = {} expected_form_data = { @@ -487,11 +496,13 @@ def test_date_mm_yyyy_combined_range_too_small_validation( } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + expected_form_data = { "csrf_token": None, "date-range-from": "2017-01", @@ -531,11 +542,13 @@ def test_date_mm_yyyy_combined_range_too_large_validation( } ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + response_metadata = {} expected_form_data = { @@ -570,11 +583,13 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): {"date-range-from-year": "2015", "date-range-to-year": "2021"} ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + expected_form_data = { "csrf_token": None, "date-range-from": "2015", @@ -611,11 +626,13 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s {"date-range-from-year": "2016", "date-range-to-year": "2017"} ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + expected_form_data = { "csrf_token": None, "date-range-from": "2016", @@ -648,11 +665,13 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s {"date-range-from-year": "2016", "date-range-to-year": "2020"} ) - metadata = { + test_metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } + metadata = get_metadata(test_metadata) + expected_form_data = { "csrf_token": None, "date-range-from": "2016", @@ -694,7 +713,7 @@ def test_date_raises_ValueError_when_any_date_range_parts_are_falsy( } ) - metadata = {"ref_p_start_date": "2017-01-21"} + metadata = get_metadata() response_metadata = {} @@ -755,7 +774,7 @@ def test_bespoke_message_for_date_validation_range( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -812,7 +831,7 @@ def test_invalid_minimum_period_limit_and_single_date_periods( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -872,7 +891,7 @@ def test_invalid_maximum_period_limit_and_single_date_periods( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -933,7 +952,7 @@ def test_period_limits_minimum_not_set_and_single_date_periods( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -998,7 +1017,7 @@ def test_invalid_date_range_and_single_date_periods( question_schema, answer_store, list_store, - metadata=metadata, + metadata=get_metadata(metadata), response_metadata={}, form_data=form_data, ) @@ -1043,7 +1062,7 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1081,7 +1100,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1282,7 +1301,7 @@ def test_calculated_field( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1337,7 +1356,7 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1373,7 +1392,7 @@ def test_multi_calculation(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1389,7 +1408,7 @@ def test_multi_calculation(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1405,7 +1424,7 @@ def test_multi_calculation(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1439,7 +1458,7 @@ def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_st question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1459,7 +1478,7 @@ def test_form_errors_are_correctly_mapped(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -1482,7 +1501,7 @@ def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -1517,7 +1536,7 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"mandatory-checkbox-answer": "Your choice"}), ) @@ -1531,7 +1550,7 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, data={"mandatory-checkbox-answer": "Ham"}, ) @@ -1555,7 +1574,7 @@ def test_answer_with_detail_answer_errors_are_correctly_mapped( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"radio-mandatory-answer": "Other"}), ) @@ -1586,7 +1605,7 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"set-minimum": "-1"}), ) @@ -1611,7 +1630,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), ) @@ -1641,7 +1660,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=schema, ) @@ -1652,7 +1671,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( rendered_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), ) @@ -1687,7 +1706,7 @@ def test_mutually_exclusive_question_raises_error_when_both_answered( question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1709,7 +1728,7 @@ def test_date_range_form(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -1744,7 +1763,7 @@ def test_date_range_form_with_data(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1782,7 +1801,7 @@ def test_form_for_radio_other_not_selected(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) @@ -1811,7 +1830,7 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): question_schema, answer_store, list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, form_data=form_data, ) diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 92281ae0db..d943ecfefd 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -27,6 +27,7 @@ expected_footer_social_theme, expected_footer_social_theme_no_cookie, ) +from tests.app.questionnaire.conftest import get_metadata DEFAULT_URL = "http://localhost" @@ -57,6 +58,7 @@ def test_footer_context(app: Flask, theme, survey_config, expected_footer): if theme: cookie_session["theme"] = theme config = survey_config + result = ContextHelper( language="en", is_post_submission=False, @@ -330,7 +332,7 @@ def test_service_links_context( if is_authenticated: mocker.patch( "app.helpers.template_helpers.get_metadata", - return_value={"ru_ref": "63782964754U"}, + return_value=get_metadata({"ru_ref": "63782964754U", "tx_id": "tx_id"}), ) result = ContextHelper( diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index bbb16afce9..0898d6c668 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -3,8 +3,29 @@ import pytest +from app.authentication.auth_payload_version import AuthPayloadVersion + + +def get_metadata(version): + return ( + fake_metadata_runner_v2() + if version is AuthPayloadVersion.V2 + else fake_metadata_runner() + ) + + +def get_metadata_full(version): + return ( + fake_metadata_full_v2_business() + if version is AuthPayloadVersion.V2 + else fake_metadata_full() + ) + + +def get_metadata_social(): + return fake_metadata_full_v2_social() + -@pytest.fixture def fake_metadata_runner(): """Generate the set of claims required for runner to function""" return { @@ -19,19 +40,19 @@ def fake_metadata_runner(): } -@pytest.fixture -def fake_business_metadata_runner(fake_metadata_runner): +@pytest.fixture() +def fake_business_metadata_runner(): """Generate a set of claims required for runner using business parameters instead of schema_name""" - del fake_metadata_runner["schema_name"] + metadata = get_metadata(version=None) + del metadata["schema_name"] - fake_metadata_runner["eq_id"] = "mbs" - fake_metadata_runner["form_type"] = "0253" + metadata["eq_id"] = "mbs" + metadata["form_type"] = "0253" - return fake_metadata_runner + return metadata -@pytest.fixture -def fake_metadata_full(fake_metadata_runner): +def fake_metadata_full(): """Generate a fake set of claims These claims should represent all claims known to runner, including common questionnaire level claims. @@ -48,7 +69,65 @@ def fake_metadata_full(fake_metadata_runner): "case_id": str(uuid.uuid4()), } - return dict(fake_metadata_runner, **fake_questionnaire_claims) + return dict(fake_metadata_runner(), **fake_questionnaire_claims) + + +def fake_metadata_runner_v2(): + """Generate the set of claims required for runner to function""" + return { + "tx_id": str(uuid.uuid4()), + "jti": str(uuid.uuid4()), + "schema_name": "2_a", + "collection_exercise_sid": "test-sid", + "response_id": str(uuid.uuid4()), + "account_service_url": "https://ras.ons.gov.uk", + "case_id": str(uuid.uuid4()), + "version": AuthPayloadVersion.V2.value, + "survey_metadata": {"data": {"key": "value"}}, + } + + +def fake_metadata_full_v2_business(): + """Generate a fake set of claims + These claims should represent all claims known to runner, including common questionnaire + level claims. + """ + fake_survey_metadata_claims = { + "user_id": "1", + "period_id": "3", + "period_str": "2016-01-01", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "ru_name": "Apple", + "return_by": "2016-07-07", + "case_ref": "1000000000000001", + "ru_ref": "123456789", + "form_type": "I", + } + + metadata = fake_metadata_runner_v2() + + metadata["survey_metadata"]["data"] = fake_survey_metadata_claims + + return metadata + + +def fake_metadata_full_v2_social(): + """Generate a fake set of claims + These claims should represent all claims known to runner, including common questionnaire + level claims. + """ + fake_survey_metadata_claims = { + "case_ref": "1000000000000001", + "questionnaire_id": "2000000000000002", + } + + metadata = fake_metadata_runner_v2() + + metadata["survey_metadata"]["data"] = fake_survey_metadata_claims + metadata["survey_metadata"]["receipting_keys"] = ["questionnaire_id"] + + return metadata @pytest.fixture diff --git a/tests/app/parser/test_metadata_parser.py b/tests/app/parser/test_metadata_parser.py index 395bc18c41..e1e561332b 100644 --- a/tests/app/parser/test_metadata_parser.py +++ b/tests/app/parser/test_metadata_parser.py @@ -4,182 +4,432 @@ from freezegun import freeze_time from marshmallow import ValidationError -from app.utilities.metadata_parser import ( +from app.authentication.auth_payload_version import AuthPayloadVersion +from app.utilities.metadata_parser import validate_runner_claims +from app.utilities.metadata_parser_v2 import ( validate_questionnaire_claims, - validate_runner_claims, + validate_runner_claims_v2, +) +from tests.app.parser.conftest import ( + get_metadata, + get_metadata_full, + get_metadata_social, ) -def test_spaces_are_stripped_from_string_fields(fake_metadata_runner): - fake_metadata_runner["collection_exercise_sid"] = " stripped " - - output = validate_runner_claims(fake_metadata_runner) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_spaces_are_stripped_from_string_fields(version): + metadata = get_metadata(version) + metadata["collection_exercise_sid"] = " stripped " + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + output = validator(metadata) assert output["collection_exercise_sid"] == "stripped" -def test_empty_strings_are_not_valid(fake_metadata_runner): - fake_metadata_runner["schema_name"] = "" +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_empty_strings_are_not_valid(version): + metadata = get_metadata(version) + metadata["schema_name"] = "" + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) with pytest.raises(ValidationError): - validate_runner_claims(fake_metadata_runner) + validator(metadata) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_validation_does_not_change_metadata( - fake_metadata_full, fake_questionnaire_metadata_requirements_full + version, fake_questionnaire_metadata_requirements_full ): - fake_metadata_copy = deepcopy(fake_metadata_full) + metadata = get_metadata_full(version) + + fake_metadata_copy = deepcopy(metadata) + + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata + validate_questionnaire_claims( - fake_metadata_full, fake_questionnaire_metadata_requirements_full + questionnaire_claims, fake_questionnaire_metadata_requirements_full ) - assert fake_metadata_full == fake_metadata_copy + assert metadata == fake_metadata_copy -def test_validation_no_error_when_optional_field_not_passed(fake_metadata_runner): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_validation_no_error_when_optional_field_not_passed(version): + metadata = get_metadata_full(version) + field_specification = [ {"name": "optional_field", "type": "string", "optional": True} ] - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(metadata, field_specification) -def test_validation_field_required_by_default(fake_metadata_runner): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_validation_field_required_by_default(version): + metadata = get_metadata_full(version) + field_specification = [{"name": "required_field", "type": "string"}] with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(metadata, field_specification) + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_minimum_length(version): + metadata = get_metadata_full(version) -def test_minimum_length(fake_metadata_runner): field_specification = [{"name": "some_field", "type": "string", "min_length": 5}] - fake_metadata_runner["some_field"] = "123456" + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata + + questionnaire_claims["some_field"] = "123456" - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) - fake_metadata_runner["some_field"] = "1" + questionnaire_claims["some_field"] = "1" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) -def test_maximum_length(fake_metadata_runner): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_maximum_length(version): + metadata = get_metadata_full(version) + field_specification = [{"name": "some_field", "type": "string", "max_length": 5}] - fake_metadata_runner["some_field"] = "1234" + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata + + questionnaire_claims["some_field"] = "1234" - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) - fake_metadata_runner["some_field"] = "123456" + questionnaire_claims["some_field"] = "123456" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_min_and_max_length(version): + metadata = get_metadata_full(version) -def test_min_and_max_length(fake_metadata_runner): field_specification = [ {"name": "some_field", "type": "string", "min_length": 4, "max_length": 5} ] - fake_metadata_runner["some_field"] = "1234" + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata - validate_questionnaire_claims(fake_metadata_runner, field_specification) + questionnaire_claims["some_field"] = "1234" - fake_metadata_runner["some_field"] = "123456" + validate_questionnaire_claims(questionnaire_claims, field_specification) + + questionnaire_claims["some_field"] = "123456" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) - fake_metadata_runner["some_field"] = "123" + questionnaire_claims["some_field"] = "123" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) -def test_length_equals(fake_metadata_runner): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_length_equals(version): + metadata = get_metadata_full(version) + field_specification = [{"name": "some_field", "type": "string", "length": 4}] - fake_metadata_runner["some_field"] = "1234" + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata + + questionnaire_claims["some_field"] = "1234" - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) - fake_metadata_runner["some_field"] = "123456" + questionnaire_claims["some_field"] = "123456" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) - fake_metadata_runner["some_field"] = "123" + questionnaire_claims["some_field"] = "123" with pytest.raises(ValidationError): - validate_questionnaire_claims(fake_metadata_runner, field_specification) + validate_questionnaire_claims(questionnaire_claims, field_specification) -def test_uuid_deserialisation(fake_metadata_runner): - claims = validate_runner_claims(fake_metadata_runner) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_uuid_deserialisation(version): + metadata = get_metadata_full(version) + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + claims = validator(metadata) assert isinstance(claims["tx_id"], str) -def test_unknown_claims_are_not_deserialized(fake_metadata_runner): - fake_metadata_runner["unknown_key"] = "some value" - claims = validate_runner_claims(fake_metadata_runner) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_unknown_claims_are_not_deserialized(version): + metadata = get_metadata_full(version) + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + metadata["unknown_key"] = "some value" + claims = validator(metadata) assert "unknown_key" not in claims -def test_minimum_length_on_runner_metadata(fake_metadata_runner): - validate_runner_claims(fake_metadata_runner) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_minimum_length_on_runner_metadata(version): + metadata = get_metadata_full(version) + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + validator(metadata) - fake_metadata_runner["schema_name"] = "" + metadata["collection_exercise_sid"] = "" with pytest.raises(ValidationError): - validate_runner_claims(fake_metadata_runner) + validate_runner_claims(metadata) -def test_deserialisation_iso_8601_dates(fake_metadata_runner): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_deserialisation_iso_8601_dates(version): """Runner cannot currently handle date objects in metadata""" + metadata = get_metadata_full(version) + field_specification = [{"name": "birthday", "type": "date"}] - fake_metadata_runner["birthday"] = "2019-11-1" - claims = validate_questionnaire_claims(fake_metadata_runner, field_specification) + if version is AuthPayloadVersion.V2: + questionnaire_claims = metadata["survey_metadata"]["data"] + else: + questionnaire_claims = metadata + + questionnaire_claims["birthday"] = "2019-11-1" + claims = validate_questionnaire_claims(questionnaire_claims, field_specification) assert isinstance(claims["birthday"], str) @freeze_time("2021-11-15T15:34:54+00:00") @pytest.mark.parametrize( - "date_string", - ["2021-11-22T15:34:54+00:00", "2021-11-22T15:34:54Z"], + "date_string, version", + [ + ("2021-11-22T15:34:54+00:00", None), + ("2021-11-22T15:34:54+00:00", AuthPayloadVersion.V2), + ("2021-11-22T15:34:54Z", None), + ("2021-11-22T15:34:54Z", AuthPayloadVersion.V2), + ], ) -def test_deserialisation_iso_8601_date(date_string, fake_metadata_runner): - fake_metadata_runner["response_expires_at"] = date_string - claims = validate_runner_claims(fake_metadata_runner) +def test_deserialisation_iso_8601_date(date_string, version): + metadata = get_metadata_full(version) + + metadata["response_expires_at"] = date_string + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + claims = validator(metadata) + assert claims["response_expires_at"] == "2021-11-22T15:34:54+00:00" +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_deserialisation_iso_8601_datetime_past_datetime_raises_ValidationError( - fake_metadata_runner, + version, ): - fake_metadata_runner["response_expires_at"] = "1900-11-22T15:34:54+00:00" + metadata = get_metadata_full(version) + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + + metadata["response_expires_at"] = "1900-11-22T15:34:54+00:00" with pytest.raises(ValidationError): - validate_runner_claims(fake_metadata_runner) + validator(metadata) @freeze_time("2021-11-15T15:34:54+00:00") +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_deserialisation_iso_8601_datetime_bad_datetime_raises_ValidationError( - fake_metadata_runner, + version, ): - fake_metadata_runner["response_expires_at"] = "2021-11-22" + metadata = get_metadata_full(version) + + validator = ( + validate_runner_claims_v2 + if version is AuthPayloadVersion.V2 + else validate_runner_claims + ) + + metadata["response_expires_at"] = "2021-11-22" + with pytest.raises(ValidationError): + validator(metadata) + + +def test_empty_schema_name_and_schema_url_not_valid_v2(): + metadata = get_metadata_full(AuthPayloadVersion.V2) + del metadata["schema_name"] + + with pytest.raises(ValidationError): + validate_runner_claims_v2(metadata) + + +def test_valid_v2_social_claims(): + metadata = get_metadata_social() + + fake_metadata_copy = deepcopy(metadata) + + claims = validate_runner_claims_v2(metadata) + + assert claims == fake_metadata_copy + + +def test_invalid_v2_social_claims_missing_receipting_key_raises_error(): + metadata = get_metadata_social() + + del metadata["survey_metadata"]["data"]["questionnaire_id"] + with pytest.raises(ValidationError): - validate_runner_claims(fake_metadata_runner) + validate_runner_claims_v2(metadata) -def test_business_params_without_schema_name(fake_business_metadata_runner): +def test_business_params_without_schema_name_v1(fake_business_metadata_runner): claims = validate_runner_claims(fake_business_metadata_runner) assert claims["schema_name"] == "mbs_0253" -def test_when_response_id_is_missing(fake_business_metadata_runner): +def test_when_response_id_is_missing_v1(fake_business_metadata_runner): expected = ( f"{fake_business_metadata_runner['ru_ref']}" f"{fake_business_metadata_runner['collection_exercise_sid']}" @@ -191,7 +441,7 @@ def test_when_response_id_is_missing(fake_business_metadata_runner): assert claims["response_id"] == expected -def test_when_response_id_is_present(fake_business_metadata_runner): +def test_when_response_id_is_present_v1(fake_business_metadata_runner): claims = validate_runner_claims(fake_business_metadata_runner) assert claims["response_id"] == fake_business_metadata_runner["response_id"] @@ -199,7 +449,7 @@ def test_when_response_id_is_present(fake_business_metadata_runner): @pytest.mark.parametrize( "metadata", ["eq_id", "form_type", "ru_ref", "collection_exercise_sid"] ) -def test_response_id_for_missing_metadata(metadata, fake_business_metadata_runner): +def test_response_id_for_missing_metadata_v1(metadata, fake_business_metadata_runner): fake_business_metadata_runner["schema_name"] = "schema_name" del fake_business_metadata_runner["response_id"] del fake_business_metadata_runner[metadata] @@ -207,7 +457,7 @@ def test_response_id_for_missing_metadata(metadata, fake_business_metadata_runne validate_runner_claims(fake_business_metadata_runner) -def test_response_id_for_empty_value(fake_business_metadata_runner): +def test_response_id_for_empty_value_v1(fake_business_metadata_runner): expected = ( f"{fake_business_metadata_runner['ru_ref']}" f"{fake_business_metadata_runner['collection_exercise_sid']}" diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index bde364c9b5..1019ef7c47 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1,11 +1,11 @@ -# pylint: disable=redefined-outer-name +# pylint: disable=redefined-outer-name, too-many-lines import pytest -from werkzeug.datastructures import ImmutableDict from app.data_models import QuestionnaireStore from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location @@ -20,6 +20,19 @@ from app.utilities.schema import load_schema_from_name +def get_metadata(extra_metadata: dict = None): + extra_metadata = extra_metadata or {} + metadata = { + "response_id": "1", + "account_service_url": "account_service_url", + "tx_id": "tx_id", + "collection_exercise_sid": "collection_exercise_sid", + "case_id": "case_id", + **extra_metadata, + } + return MetadataProxy.from_dict(metadata) + + @pytest.fixture def placeholder_list(): return [ @@ -51,7 +64,7 @@ def parser(answer_store, location, mock_schema, mock_renderer): language="en", answer_store=answer_store, list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=mock_schema, location=location, @@ -918,7 +931,7 @@ def placeholder_renderer(option_label_from_value_schema): language="en", answer_store=answer_store, list_store=ListStore(), - metadata=ImmutableDict({"trad_as": "ESSENTIAL SERVICES LTD"}), + metadata=get_metadata({"trad_as": "ESSENTIAL SERVICES LTD"}), response_metadata={}, schema=option_label_from_value_schema, ) @@ -931,7 +944,7 @@ def mock_renderer(mock_schema): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=ImmutableDict({}), + metadata=get_metadata(), response_metadata={}, schema=mock_schema, ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 8c4712c04c..f5e3d291d3 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -7,10 +7,12 @@ from app.data_models import AnswerStore, ListStore from app.data_models.answer import Answer +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.operator import Operator from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from tests.app.questionnaire.conftest import get_metadata from tests.app.questionnaire.test_value_source_resolver import get_list_items current_date = datetime.now(timezone.utc).date() @@ -37,7 +39,7 @@ def get_rule_evaluator( schema: QuestionnaireSchema = None, answer_store: AnswerStore = AnswerStore(), list_store: ListStore = ListStore(), - metadata: Optional[dict] = None, + metadata: Optional[MetadataProxy] = None, response_metadata: Mapping = None, location: Union[Location, RelationshipLocation] = Location( section_id="test-section", block_id="test-block" @@ -236,14 +238,14 @@ def test_answer_source_with_dict_answer_selector(answer_value, expected_result): ) def test_metadata_source(metadata_value, expected_result): rule_evaluator = get_rule_evaluator( - metadata={"some-metadata": metadata_value}, + metadata=get_metadata({"some_key": metadata_value}) ) assert ( rule_evaluator.evaluate( rule={ Operator.EQUAL: [ - {"source": "metadata", "identifier": "some-metadata"}, + {"source": "metadata", "identifier": "some_key"}, 3, ] }, @@ -517,7 +519,7 @@ def test_nested_rules(operator, operands, expected_result): }, ] ), - metadata={"region_code": "GB-NIR", "language_code": "en"}, + metadata=get_metadata({"region_code": "GB-NIR", "language_code": "en"}), list_store=ListStore( [ { @@ -556,7 +558,7 @@ def test_nested_rules(operator, operands, expected_result): ], ) def test_comparison_operator_rule_with_nonetype_operands(operator_name, operands): - rule_evaluator = get_rule_evaluator() + rule_evaluator = get_rule_evaluator(metadata=get_metadata()) assert rule_evaluator.evaluate(rule={operator_name: operands}) is False @@ -575,7 +577,9 @@ def test_comparison_operator_rule_with_nonetype_operands(operator_name, operands "operator_name", [Operator.ALL_IN, Operator.ANY_IN, Operator.IN] ) def test_array_operator_rule_with_nonetype_operands(operator_name, operands): - rule_evaluator = get_rule_evaluator() + rule_evaluator = get_rule_evaluator( + metadata=get_metadata(), + ) assert ( rule_evaluator.evaluate( rule={operator_name: operands}, @@ -706,7 +710,7 @@ def test_date_value(rule, expected_result): } ] ), - metadata={"some-metadata": current_date_as_yyyy_mm_dd}, + metadata=get_metadata({"some-metadata": current_date_as_yyyy_mm_dd}), ) assert ( diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index ec19ed14bc..faeeab2d4a 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -6,6 +6,7 @@ from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name +from tests.app.questionnaire.conftest import get_metadata def test_simple_path(answer_store, list_store): @@ -20,7 +21,7 @@ def test_simple_path(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) section_id = schema.get_section_id_for_block_id("name-block") routing_path = path_finder.routing_path(section_id=section_id) @@ -34,7 +35,7 @@ def test_introduction_in_path_when_in_schema(answer_store, list_store, progress_ schema = load_schema_from_name("test_introduction") current_section = schema.get_section("introduction-section") - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=current_section["id"]) assert "introduction" in routing_path @@ -45,7 +46,7 @@ def test_introduction_not_in_path_when_not_in_schema( ): schema = load_schema_from_name("test_checkbox") current_section = schema.get_section("default-section") - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) mocker.patch("app.questionnaire.when_rules.evaluate_when_rules", return_value=False) routing_path = path_finder.routing_path(section_id=current_section["id"]) @@ -80,7 +81,7 @@ def test_routing_path_with_conditional_path(schema, answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) @@ -103,7 +104,7 @@ def test_new_routing_basic_and_conditional_path( answer_store.add_or_update(answer_1) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -128,7 +129,7 @@ def test_routing_path_with_complete_introduction(answer_store, list_store): section_id="introduction-section", ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_routing_path @@ -157,7 +158,7 @@ def test_routing_path(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_path @@ -180,7 +181,7 @@ def test_routing_path_with_repeating_sections(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) repeating_section_id = "personal-details-section" routing_path = path_finder.routing_path( @@ -224,7 +225,7 @@ def test_routing_path_empty_routing_rules(answer_store, list_store): ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_path @@ -248,7 +249,10 @@ def test_routing_path_with_conditional_value_not_in_metadata(answer_store, list_ ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder( + schema, answer_store, list_store, progress_store, get_metadata(), {} + ) + routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_path @@ -283,7 +287,7 @@ def test_new_routing_path_should_skip_block( ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -322,7 +326,7 @@ def test_routing_path_should_skip_group(schema, answer_store, list_store): ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -361,7 +365,7 @@ def test_routing_path_should_not_skip_group(schema, answer_store, list_store): ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -383,7 +387,7 @@ def test_get_routing_path_when_first_block_in_group_skipped( ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) # Then expected_route = RoutingPath( @@ -656,7 +660,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( answer_store, list_store, progress_store, - metadata={}, + metadata=None, response_metadata={}, ) routing_path = path_finder.routing_path(section_id=section_id) @@ -737,7 +741,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating answer_store, list_store, progress_store, - metadata={}, + metadata=None, response_metadata={}, ) routing_path = path_finder.routing_path( diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 084a8c58d6..e096700911 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -4,6 +4,7 @@ from app.data_models.list_store import ListStore from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from tests.app.questionnaire.conftest import get_metadata def test_parse_placeholders(placeholder_list, parser): @@ -18,7 +19,10 @@ def test_metadata_placeholder(mock_renderer): placeholder_list = [ { "placeholder": "period", - "value": {"source": "metadata", "identifier": "period_str"}, + "value": { + "source": "metadata", + "identifier": "period_str", + }, } ] @@ -27,7 +31,7 @@ def test_metadata_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"period_str": period_str}, + metadata=get_metadata({"period_str": period_str}), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -65,7 +69,7 @@ def test_previous_answer_transform_placeholder(mock_renderer): language="en", answer_store=answer_store, list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -98,7 +102,7 @@ def test_metadata_transform_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"ref_p_start_date": "2019-02-11"}, + metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -132,7 +136,7 @@ def test_response_metadata_transform_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"ref_p_start_date": "2019-02-11"}, + metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), response_metadata={"started_at": "2019-02-11"}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -170,7 +174,7 @@ def test_multiple_answer_transform_placeholder(mock_renderer): ] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -203,7 +207,7 @@ def test_first_non_empty_item_transform_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"ru_name": "ru_name"}, + metadata=get_metadata({"trad_as": None, "ru_name": "ru_name"}), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -238,7 +242,7 @@ def test_format_list_answer_transform_placeholder(mock_renderer): [{"answer_id": "checkbox-answer", "value": ["Ham", "Cheese"]}] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -278,7 +282,7 @@ def test_placeholder_parser_escapes_answers(mock_renderer): ] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -322,7 +326,7 @@ def test_multiple_metadata_transform_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"ref_p_start_date": "2019-02-11"}, + metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -356,7 +360,9 @@ def test_multiple_metadata_list_transform_placeholder(mock_renderer): language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={"ref_p_start_date": "2019-02-11", "ref_p_end_date": "2019-10-11"}, + metadata=get_metadata( + {"ref_p_start_date": "2019-02-11", "ref_p_end_date": "2019-10-11"} + ), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -392,7 +398,7 @@ def test_checkbox_transform_placeholder(mock_renderer): ] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -431,7 +437,7 @@ def test_mixed_transform_placeholder(mock_renderer): [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] ), list_store=ListStore(), - metadata={"second-date": "2019-02-02"}, + metadata=get_metadata({"second-date": "2019-02-02"}), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -466,7 +472,7 @@ def test_mixed_transform_placeholder_value(mock_renderer): [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -492,7 +498,7 @@ def test_list_source_count(mock_renderer): language="en", answer_store=AnswerStore(), list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -529,7 +535,7 @@ def test_list_source_count_in_transform(mock_renderer): language="en", answer_store=AnswerStore(), list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -571,7 +577,7 @@ def test_chain_transform_placeholder(mock_renderer): ] ), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -613,7 +619,7 @@ def test_placeholder_resolves_answer_value_based_on_first_item_in_list(mock_rend language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -643,7 +649,7 @@ def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( language="en", answer_store=AnswerStore(), list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -679,7 +685,7 @@ def test_placeholder_resolves_same_name_items(mock_renderer): language="en", answer_store=AnswerStore(), list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=QuestionnaireSchema({}), renderer=mock_renderer, @@ -781,7 +787,7 @@ def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=mock_schema, list_item_id="abc123", @@ -796,7 +802,7 @@ def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=mock_schema, list_item_id="cde456", @@ -894,7 +900,7 @@ def test_placeholder_resolves_list_has_items_chain(mock_schema, mock_renderer): language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=mock_schema, list_item_id="abc123", @@ -909,7 +915,7 @@ def test_placeholder_resolves_list_has_items_chain(mock_schema, mock_renderer): language="en", answer_store=answer_store, list_store=list_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=mock_schema, list_item_id="cde456", @@ -939,7 +945,7 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata={}, + metadata=get_metadata(), response_metadata={}, schema=default_placeholder_value_schema, renderer=mock_renderer, diff --git a/tests/app/questionnaire/test_placeholder_transforms.py b/tests/app/questionnaire/test_placeholder_transforms.py index 77178bb52d..f32b92f402 100644 --- a/tests/app/questionnaire/test_placeholder_transforms.py +++ b/tests/app/questionnaire/test_placeholder_transforms.py @@ -490,6 +490,7 @@ def test_option_label_from_value_with_placeholder_label( option_label_from_value_schema, ): label_renderer = placeholder_renderer + placeholder_transform = PlaceholderTransforms( language="en", schema=option_label_from_value_schema, renderer=label_renderer ) diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index bce950d3a9..a0c6e518d6 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -11,6 +11,7 @@ from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name +from tests.app.questionnaire.conftest import get_metadata class RouterTestCase: @@ -18,7 +19,7 @@ class RouterTestCase: answer_store = AnswerStore() list_store = ListStore() progress_store = ProgressStore() - metadata = {} + metadata = get_metadata() response_metadata = {} @cached_property diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index e0a4d82275..a95c940fd0 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -3,13 +3,16 @@ import pytest from mock import Mock +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models import AnswerStore, ListStore from app.data_models.answer import Answer, AnswerDict +from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.location import InvalidLocationException from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.value_source_resolver import ValueSourceResolver from tests.app.data_model.test_answer import ESCAPED_CONTENT, HTML_CONTENT +from tests.app.questionnaire.conftest import get_metadata def get_list_items(num: int): @@ -34,7 +37,7 @@ def get_value_source_resolver( schema: QuestionnaireSchema = None, answer_store: AnswerStore = AnswerStore(), list_store: ListStore = ListStore(), - metadata: Optional[dict] = None, + metadata: MetadataProxy = None, response_metadata: Mapping = None, location: Union[Location, RelationshipLocation] = Location( section_id="test-section", block_id="test-block" @@ -343,13 +346,47 @@ def test_answer_source_default_answer(use_default_answer): ) def test_metadata_source(metadata_identifier, expected_result): value_source_resolver = get_value_source_resolver( - metadata={"region_code": "GB-ENG"}, + metadata=get_metadata({"region_code": "GB-ENG"}) ) source = {"source": "metadata", "identifier": metadata_identifier} assert value_source_resolver.resolve(source) == expected_result +def test_resolve_metadata_source_with_no_metadata_raises_exception(): + value_source_resolver = get_value_source_resolver(metadata=None) + + source = {"source": "metadata", "identifier": "identifier"} + + with pytest.raises(NoMetadataException): + value_source_resolver.resolve(source) + + +@pytest.mark.parametrize( + "metadata_identifier, expected_result", + [ + ("region_code", "GB-ENG"), + ("display_address", "68 Abingdon Road, Goathill"), + ("language_code", None), + ], +) +def test_metadata_source_v2_metadata_structure(metadata_identifier, expected_result): + metadata = get_metadata( + { + "version": AuthPayloadVersion.V2, + "region_code": "GB-ENG", + "survey_metadata": { + "data": {"display_address": "68 Abingdon Road, Goathill"} + }, + } + ) + + value_source_resolver = get_value_source_resolver(metadata=metadata) + + source = {"source": "metadata", "identifier": metadata_identifier} + assert value_source_resolver.resolve(source) == expected_result + + @pytest.mark.parametrize( "list_count", [0, 1, 5, 10], diff --git a/tests/app/questionnaire/test_when_rules.py b/tests/app/questionnaire/test_when_rules.py index 5cc78d00d1..44de91c5ca 100644 --- a/tests/app/questionnaire/test_when_rules.py +++ b/tests/app/questionnaire/test_when_rules.py @@ -1,6 +1,7 @@ # pylint: disable=too-many-lines import pytest +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models.answer_store import Answer from app.data_models.list_store import ListStore from app.questionnaire.location import Location @@ -12,6 +13,7 @@ evaluate_rule, evaluate_when_rules, ) +from tests.app.questionnaire.conftest import get_metadata @pytest.mark.parametrize( @@ -108,7 +110,7 @@ def test_evaluate_rule(when_rule, answers, expected): "when": [{"id": "my_answer", "condition": "equals", "value": "Yes"}], }, [{"answer_id": "my_answer", "value": "Yes"}], - None, + get_metadata(), True, ), ( @@ -117,7 +119,7 @@ def test_evaluate_rule(when_rule, answers, expected): "when": [{"id": "my_answer", "condition": "equals", "value": "Yes"}], }, [{"answer_id": "my_answer", "value": "No"}], - None, + get_metadata(), False, ), ( @@ -128,7 +130,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answer", "value": "No"}], - None, + get_metadata(), False, ), ( @@ -143,7 +145,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answer", "value": "No"}], - None, + get_metadata(), False, ), ( @@ -154,7 +156,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": ["answer1", "answer2", "answer3"]}], - None, + get_metadata(), True, ), ( @@ -169,7 +171,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": ["answer2", "answer3"]}], - None, + get_metadata(), True, ), ( @@ -184,7 +186,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": ["answer1", "answer4"]}], - None, + get_metadata(), True, ), ( @@ -199,7 +201,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": ["answer1", "answer2", "answer3"]}], - None, + get_metadata(), True, ), ( @@ -214,7 +216,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": "answer2"}], - None, + get_metadata(), True, ), ( @@ -229,7 +231,7 @@ def test_evaluate_rule(when_rule, answers, expected): ], }, [{"answer_id": "my_answers", "value": "answer3"}], - None, + get_metadata(), True, ), ( @@ -247,7 +249,7 @@ def test_evaluate_rule(when_rule, answers, expected): {"answer_id": "my_answer", "value": "Yes"}, {"answer_id": "my_other_answer", "value": "2"}, ], - None, + get_metadata(), True, ), ( @@ -261,7 +263,7 @@ def test_evaluate_rule(when_rule, answers, expected): [ {"answer_id": "my_answer", "value": "No"}, ], - None, + get_metadata(), False, ), ( @@ -275,7 +277,12 @@ def test_evaluate_rule(when_rule, answers, expected): [ {"answer_id": "my_answer", "value": "Yes"}, ], - {"sexual_identity": True}, + get_metadata( + { + "version": AuthPayloadVersion.V2, + "survey_metadata": {"data": {"sexual_identity": True}}, + } + ), True, ), ( @@ -289,7 +296,12 @@ def test_evaluate_rule(when_rule, answers, expected): [ {"answer_id": "my_answer", "value": "Yes"}, ], - {"varient_flags": {"sexual_identity": True}}, + get_metadata( + { + "version": AuthPayloadVersion.V2, + "survey_metadata": {"data": {"sexual_identity": True}}, + } + ), False, ), ( @@ -306,7 +318,9 @@ def test_evaluate_rule(when_rule, answers, expected): [ {"answer_id": "my_answer", "value": "Yes"}, ], - {"sexual_identity": True}, + get_metadata( + {"version": AuthPayloadVersion.V2, "survey_metadata": {"data": {}}} + ), False, ), ), @@ -418,7 +432,7 @@ def test_skip_conditions( questionnaire_schema, answer_store, list_store=list_store, - metadata={}, + metadata=None, progress_store=progress_store, response_metadata={}, ) @@ -452,7 +466,7 @@ def test_evaluate_not_set_when_rules_should_return_true( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -512,7 +526,7 @@ def test_when_rule_comparing_answer_values( evaluate_when_rules( when_rules=when, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -554,7 +568,7 @@ def test_evaluate_when_rule_with_list_item_id( evaluate_when_rules( when_rules=when_rules, schema=schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -571,7 +585,7 @@ def test_evaluate_when_rule_raises_if_bad_when_condition( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -598,7 +612,7 @@ def test_evaluate_when_rule_with_list_rules( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -672,7 +686,7 @@ def test_routing_answer_on_path_when_in_a_repeat( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -693,7 +707,7 @@ def test_routing_ignores_answers_not_on_path( assert evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -706,7 +720,7 @@ def test_routing_ignores_answers_not_on_path( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -762,7 +776,7 @@ def test_primary_person_checks_location( evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, @@ -795,7 +809,7 @@ def test_when_rule_returns_first_item_in_list(answer_store, questionnaire_schema assert evaluate_when_rules( when_rules=when_rules, schema=questionnaire_schema, - metadata={}, + metadata=None, answer_store=answer_store, list_store=list_store, current_location=current_location, diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 24b3c73f26..67a15ef3e1 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -7,72 +7,73 @@ from mock import MagicMock from requests import Response +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models import QuestionnaireStore from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter import RabbitMQSubmitter -from app.utilities.metadata_parser import ( - validate_questionnaire_claims, - validate_runner_claims, -) +METADATA_V1 = MetadataProxy.from_dict( + { + "tx_id": str(uuid.uuid4()), + "user_id": "789473423", + "schema_name": "1_0000", + "collection_exercise_sid": "test-sid", + "account_service_url": ACCOUNT_SERVICE_BASE_URL_SOCIAL, + "period_id": "2016-02-01", + "period_str": "2016-01-01", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "ru_ref": "432423423423", + "response_id": "1234567890123456", + "ru_name": "Apple", + "return_by": "2016-07-07", + "case_id": str(uuid.uuid4()), + "form_type": "I", + "case_type": "SPG", + "region_code": "GB-ENG", + "channel": "RH", + "display_address": "68 Abingdon Road, Goathill", + "case_ref": "1000000000000001", + "jti": str(uuid.uuid4()), + } +) -@pytest.fixture -def fake_metadata(): - def parse_metadata(claims, schema_metadata): - runner_claims = validate_runner_claims(claims) - questionnaire_claims = validate_questionnaire_claims(claims, schema_metadata) - return {**runner_claims, **questionnaire_claims} - - schema_metadata = [ - {"name": "user_id", "type": "string"}, - {"name": "period_id", "type": "string"}, - {"name": "ref_p_start_date", "type": "string"}, - {"name": "ref_p_end_date", "type": "string"}, - {"name": "display_address", "type": "string"}, - {"name": "case_ref", "type": "string"}, - ] - - metadata = parse_metadata( - { - "tx_id": str(uuid.uuid4()), - "user_id": "789473423", - "schema_name": "1_0000", - "collection_exercise_sid": "test-sid", - "account_service_url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/", - "period_id": "2016-02-01", - "period_str": "2016-01-01", - "ref_p_start_date": "2016-02-02", - "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", - "response_id": "1234567890123456", - "ru_name": "Apple", - "return_by": "2016-07-07", - "case_id": str(uuid.uuid4()), - "form_type": "I", - "case_type": "SPG", - "region_code": "GB-ENG", - "channel": "RH", - "display_address": "68 Abingdon Road, Goathill", - "case_ref": "1000000000000001", - "jti": str(uuid.uuid4()), +METADATA_V2 = MetadataProxy.from_dict( + { + "version": AuthPayloadVersion.V2.value, + "tx_id": str(uuid.uuid4()), + "schema_name": "1_0000", + "collection_exercise_sid": "test-sid", + "account_service_url": ACCOUNT_SERVICE_BASE_URL_SOCIAL, + "survey_metadata": { + "data": { + "period_id": "2016-02-01", + "period_str": "2016-01-01", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "ru_ref": "432423423423", + "ru_name": "Apple", + "case_type": "SPG", + "form_type": "I", + "case_ref": "1000000000000001", + "display_address": "68 Abingdon Road, Goathill", + "user_id": "789473423", + }, }, - schema_metadata, - ) - - return metadata - - -@pytest.fixture -def fake_response_metadata(): - response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} - return response_metadata + "response_id": "1234567890123456", + "case_id": str(uuid.uuid4()), + "region_code": "GB-ENG", + "channel": "RH", + "jti": str(uuid.uuid4()), + } +) -@pytest.fixture -def fake_questionnaire_store(fake_metadata, fake_response_metadata): +def get_questionnaire_store(version): user_answer = Answer(answer_id="GHI", value=0, list_item_id=None) storage = MagicMock() @@ -83,12 +84,28 @@ def fake_questionnaire_store(fake_metadata, fake_response_metadata): store.answer_store = AnswerStore() store.answer_store.add_or_update(user_answer) - store.metadata = fake_metadata - store.response_metadata = fake_response_metadata + store.metadata = METADATA_V2 if version is AuthPayloadVersion.V2 else METADATA_V1 + store.response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} return store +@pytest.fixture +def fake_metadata(): + return METADATA_V1 + + +@pytest.fixture +def fake_metadata_v2(): + return METADATA_V2 + + +@pytest.fixture +def fake_response_metadata(): + response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} + return response_metadata + + @pytest.fixture def fake_questionnaire_schema(): questionnaire = {"survey_id": "021", "data_version": "0.0.3"} diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 9c91367ad2..9a5e12037c 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -2,12 +2,14 @@ import pytest +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.submitter.convert_payload_0_0_1 import convert_answers_to_payload_0_0_1 -from app.submitter.converter import convert_answers +from app.submitter.converter_v2 import get_payload_data +from tests.app.submitter.conftest import get_questionnaire_store from tests.app.submitter.schema import make_schema SUBMITTED_AT = datetime.now(timezone.utc) @@ -17,8 +19,17 @@ def create_answer(answer_id, value): return {"answer_id": answer_id, "value": value} -def test_convert_answers_to_payload_0_0_1_with_key_error(fake_questionnaire_store): - fake_questionnaire_store.answer_store = AnswerStore( +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.answer_store = AnswerStore( [ Answer("ABC", "2016-01-01").to_dict(), Answer("DEF", "2016-03-30").to_dict(), @@ -42,19 +53,28 @@ def test_convert_answers_to_payload_0_0_1_with_key_error(fake_questionnaire_stor RoutingPath(["block-1"], section_id="section-1", list_item_id=None) ] answer_object = convert_answers_to_payload_0_0_1( - fake_questionnaire_store.metadata, - fake_questionnaire_store.response_metadata, - fake_questionnaire_store.answer_store, - fake_questionnaire_store.list_store, - QuestionnaireSchema(questionnaire), - full_routing_path, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + schema=QuestionnaireSchema(questionnaire), + full_routing_path=full_routing_path, ) assert answer_object["002"] == "2016-03-30" assert len(answer_object) == 1 -def test_answer_with_zero(fake_questionnaire_store): - fake_questionnaire_store.answer_store = AnswerStore([Answer("GHI", 0).to_dict()]) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_answer_with_zero(version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.answer_store = AnswerStore([Answer("GHI", 0).to_dict()]) question = { "id": "question-2", @@ -68,20 +88,31 @@ def test_answer_with_zero(fake_questionnaire_store): RoutingPath(["block-1"], section_id="section-1", list_item_id=None) ] - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert answer_object["data"]["003"] == "0" + assert data_payload["003"] == "0" -def test_answer_with_float(fake_questionnaire_store): - fake_questionnaire_store.answer_store = AnswerStore( - [Answer("GHI", 10.02).to_dict()] - ) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_answer_with_float(version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.answer_store = AnswerStore([Answer("GHI", 10.02).to_dict()]) question = { "id": "question-2", @@ -95,19 +126,32 @@ def test_answer_with_float(fake_questionnaire_store): RoutingPath(["block-1"], section_id="section-1", list_item_id=None) ] - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Check the converter correctly - assert answer_object["data"]["003"] == "10.02" + assert data_payload["003"] == "10.02" -def test_answer_with_string(fake_questionnaire_store): - fake_questionnaire_store.answer_store = AnswerStore( +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_answer_with_string(version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.answer_store = AnswerStore( [Answer("GHI", "String test + !").to_dict()] ) @@ -123,19 +167,32 @@ def test_answer_with_string(fake_questionnaire_store): RoutingPath(["block-1"], section_id="section-1", list_item_id=None) ] - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Check the converter correctly - assert answer_object["data"]["003"] == "String test + !" + assert data_payload["003"] == "String test + !" -def test_answer_without_qcode(fake_questionnaire_store): - fake_questionnaire_store.answer_store = AnswerStore( +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_answer_without_qcode(version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.answer_store = AnswerStore( [Answer("GHI", "String test + !").to_dict()] ) @@ -151,19 +208,32 @@ def test_answer_without_qcode(fake_questionnaire_store): RoutingPath(["block-1"], section_id="section-1", list_item_id=None) ] - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert not answer_object["data"] + assert not data_payload + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_converter_checkboxes_with_q_codes(version): + questionnaire_store = get_questionnaire_store(version) -def test_converter_checkboxes_with_q_codes(fake_questionnaire_store): full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -204,32 +274,40 @@ def test_converter_checkboxes_with_q_codes(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 2 - assert answer_object["data"]["1"] == "Ready salted" - assert answer_object["data"]["2"] == "Sweet chilli" + assert len(data_payload) == 2 + assert data_payload["1"] == "Ready salted" + assert data_payload["2"] == "Sweet chilli" @pytest.mark.parametrize( - "detail_answer_q_code_field, expected_data_length", + "detail_answer_q_code_field, expected_data_length, version", [ - ({"q_code": "401"}, 3), - ({}, 2), + ({"q_code": "401"}, 3, None), + ({}, 2, None), + ({"q_code": "401"}, 3, AuthPayloadVersion.V2), + ({}, 2, AuthPayloadVersion.V2), ], ) def test_converter_checkboxes_with_q_codes_and_other_value( - detail_answer_q_code_field, expected_data_length, fake_questionnaire_store + detail_answer_q_code_field, expected_data_length, version ): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ Answer("crisps-answer", ["Ready salted", "Other"]).to_dict(), Answer("other-answer-mandatory", "Bacon").to_dict(), @@ -274,29 +352,40 @@ def test_converter_checkboxes_with_q_codes_and_other_value( ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == expected_data_length - assert answer_object["data"]["1"] == "Ready salted" - assert answer_object["data"]["4"] == "Bacon" + assert len(data_payload) == expected_data_length + assert data_payload["1"] == "Ready salted" + assert data_payload["4"] == "Bacon" # If detail answer has a q_code then that should be used in the data outputted in the payload if detail_answer_q_code_field: - assert answer_object["data"][detail_answer_q_code_field["q_code"]] == "Bacon" + assert data_payload[detail_answer_q_code_field["q_code"]] == "Bacon" -def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store( - fake_questionnaire_store, -): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ Answer("crisps-answer", ["Ready salted", "Other"]).to_dict(), ] @@ -339,25 +428,36 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store( ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 2 - assert answer_object["data"]["1"] == "Ready salted" - assert answer_object["data"]["4"] == "Other" + assert len(data_payload) == 2 + assert data_payload["1"] == "Ready salted" + assert data_payload["4"] == "Other" -def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code( - fake_questionnaire_store, -): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -399,21 +499,34 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code( ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["0"], "['Ready salted' == 'Sweet chilli']" + assert len(data_payload) == 1 + assert data_payload["0"], "['Ready salted' == 'Sweet chilli']" -def test_converter_q_codes_for_empty_strings(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_converter_q_codes_for_empty_strings(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ Answer("crisps-answer", "").to_dict(), Answer("other-crisps-answer", "Ready salted").to_dict(), @@ -439,23 +552,36 @@ def test_converter_q_codes_for_empty_strings(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["2"] == "Ready salted" + assert len(data_payload) == 1 + assert data_payload["2"] == "Ready salted" -def test_radio_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_radio_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["radio-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ Answer("radio-answer", "Coffee").to_dict(), Answer("other-answer-mandatory", "Water").to_dict(), @@ -493,24 +619,37 @@ def test_radio_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 2 - assert answer_object["data"]["1"] == "Coffee" - assert answer_object["data"]["101"] == "Water" + assert len(data_payload) == 2 + assert data_payload["1"] == "Coffee" + assert data_payload["101"] == "Water" + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_number_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_number_answer(fake_questionnaire_store): full_routing_path = [ RoutingPath(["number-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("number-answer", 0.9999).to_dict()] ) @@ -525,23 +664,36 @@ def test_number_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "0.9999" + assert len(data_payload) == 1 + assert data_payload["1"] == "0.9999" -def test_percentage_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_percentage_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["percentage-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("percentage-answer", 100).to_dict()] ) @@ -556,23 +708,36 @@ def test_percentage_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "100" + assert len(data_payload) == 1 + assert data_payload["1"] == "100" -def test_textarea_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_textarea_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["textarea-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("textarea-answer", "example text.").to_dict()] ) @@ -587,23 +752,36 @@ def test_textarea_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "example text." + assert len(data_payload) == 1 + assert data_payload["1"] == "example text." + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_currency_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_currency_answer(fake_questionnaire_store): full_routing_path = [ RoutingPath(["currency-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("currency-answer", 99.99).to_dict()] ) @@ -618,23 +796,36 @@ def test_currency_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "99.99" + assert len(data_payload) == 1 + assert data_payload["1"] == "99.99" + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_dropdown_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_dropdown_answer(fake_questionnaire_store): full_routing_path = [ RoutingPath(["dropdown-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("dropdown-answer", "Liverpool").to_dict()] ) @@ -660,24 +851,37 @@ def test_dropdown_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "Liverpool" + assert len(data_payload) == 1 + assert data_payload["1"] == "Liverpool" -def test_date_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_date_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["date-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ create_answer("single-date-answer", "1990-02-01"), create_answer("month-year-answer", "1990-01"), @@ -698,24 +902,37 @@ def test_date_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 2 - assert answer_object["data"]["1"] == "01/02/1990" - assert answer_object["data"]["2"] == "01/1990" + assert len(data_payload) == 2 + assert data_payload["1"] == "01/02/1990" + assert data_payload["2"] == "01/1990" -def test_unit_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_unit_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["unit-block"], section_id="section-1", list_item_id=None) ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [Answer("unit-answer", 10).to_dict()] ) @@ -730,13 +947,17 @@ def test_unit_answer(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]) == 1 - assert answer_object["data"]["1"] == "10" + assert len(data_payload) == 1 + assert data_payload["1"] == "10" diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 32874f6223..6c3b807df1 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -1,24 +1,38 @@ +# pylint: disable=too-many-lines from datetime import datetime, timezone +import pytest + +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath -from app.submitter.converter import convert_answers +from app.submitter.converter_v2 import get_payload_data from app.utilities.json import json_dumps, json_loads from app.utilities.schema import load_schema_from_name +from tests.app.submitter.conftest import get_questionnaire_store from tests.app.submitter.schema import make_schema SUBMITTED_AT = datetime.now(timezone.utc) -def test_convert_answers_to_payload_0_0_3(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_convert_answers_v2_to_payload_0_0_3(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [ RoutingPath(["about you", "where you live"], section_id="household-section") ] - fake_questionnaire_store.answer_store = AnswerStore( + questionnaire_store.answer_store = AnswerStore( [ Answer("name", "Joe Bloggs", None).to_dict(), Answer("address", "62 Somewhere", None).to_dict(), @@ -66,25 +80,38 @@ def test_convert_answers_to_payload_0_0_3(fake_questionnaire_store): } # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]["answers"]) == 2 - assert answer_object["data"]["answers"][0].value == "Joe Bloggs" - assert answer_object["data"]["answers"][1].value, "62 Somewhere" + assert len(data_payload["answers"]) == 2 + assert data_payload["answers"][0].value == "Joe Bloggs" + assert data_payload["answers"][1].value, "62 Somewhere" -def test_convert_payload_0_0_3_multiple_answers(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_convert_payload_0_0_3_multiple_answers(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["crisps"], section_id="section-1")] answers = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -109,21 +136,35 @@ def test_convert_payload_0_0_3_multiple_answers(fake_questionnaire_store): ) # When - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) + # Then - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == ["Ready salted", "Sweet chilli"] + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == ["Ready salted", "Sweet chilli"] -def test_radio_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_radio_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["radio-block"], section_id="section-1")] answers = AnswerStore([Answer("radio-answer", "Coffee").to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -146,21 +187,34 @@ def test_radio_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == "Coffee" + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == "Coffee" + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_number_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_number_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["number-block"], section_id="section-1")] answers = AnswerStore([Answer("number-answer", 1.755).to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -174,21 +228,34 @@ def test_number_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == 1.755 + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == 1.755 + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_percentage_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_percentage_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["percentage-block"], section_id="section-1")] answers = AnswerStore([Answer("percentage-answer", 99).to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -202,23 +269,36 @@ def test_percentage_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == 99 + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == 99 + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_textarea_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_textarea_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["textarea-block"], section_id="section-1")] answers = AnswerStore( [Answer("textarea-answer", "This is an example text!").to_dict()] ) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -232,21 +312,34 @@ def test_textarea_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == "This is an example text!" + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == "This is an example text!" -def test_currency_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_currency_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["currency-block"], section_id="section-1")] answers = AnswerStore([Answer("currency-answer", 100).to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -260,21 +353,34 @@ def test_currency_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == 100 + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == 100 -def test_dropdown_answer(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_dropdown_answer(version): + questionnaire_store = get_questionnaire_store(version) + full_routing_path = [RoutingPath(["dropdown-block"], section_id="section-1")] answers = AnswerStore([Answer("dropdown-answer", "Rugby is better!").to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -298,19 +404,32 @@ def test_dropdown_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Then - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == "Rugby is better!" + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == "Rugby is better!" + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_date_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_date_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["date-block"], section_id="section-1")] answers = AnswerStore( [ @@ -318,7 +437,7 @@ def test_date_answer(fake_questionnaire_store): Answer("month-year-answer", "01-1990").to_dict(), ] ) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -332,19 +451,32 @@ def test_date_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 + assert len(data_payload["answers"]) == 1 + + assert data_payload["answers"][0].value == "01-01-1990" - assert answer_object["data"]["answers"][0].value == "01-01-1990" +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_month_year_date_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_month_year_date_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["date-block"], section_id="section-1")] answers = AnswerStore( [ @@ -352,7 +484,7 @@ def test_month_year_date_answer(fake_questionnaire_store): Answer("month-year-answer", "01-1990").to_dict(), ] ) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -366,22 +498,35 @@ def test_month_year_date_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 + assert len(data_payload["answers"]) == 1 + + assert data_payload["answers"][0].value == "01-1990" - assert answer_object["data"]["answers"][0].value == "01-1990" +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_unit_answer(version): + questionnaire_store = get_questionnaire_store(version) -def test_unit_answer(fake_questionnaire_store): full_routing_path = [RoutingPath(["unit-block"], section_id="section-1")] answers = AnswerStore([Answer("unit-answer", 10).to_dict()]) - fake_questionnaire_store.answer_store = answers + questionnaire_store.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -395,18 +540,31 @@ def test_unit_answer(fake_questionnaire_store): }, ) - answer_object = convert_answers( - QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + schema = QuestionnaireSchema(questionnaire) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, full_routing_path, - SUBMITTED_AT, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - assert len(answer_object["data"]["answers"]) == 1 - assert answer_object["data"]["answers"][0].value == 10 + assert len(data_payload["answers"]) == 1 + assert data_payload["answers"][0].value == 10 -def test_primary_person_list_item_conversion(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_primary_person_list_item_conversion(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["primary-person-list-collector", "list-collector"], section_id="section-1" @@ -434,23 +592,37 @@ def test_primary_person_list_item_conversion(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_list_collector_primary_person") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data_dict = json_loads(json_dumps(output["data"]["answers"])) + data_dict = json_loads(json_dumps(output["answers"])) assert sorted(answer_objects, key=lambda x: x["answer_id"]) == sorted( data_dict, key=lambda x: x["answer_id"] ) -def test_list_item_conversion(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_list_item_conversion(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "next-interstitial", "another-list-collector-block"], @@ -474,27 +646,41 @@ def test_list_item_conversion(fake_questionnaire_store): existing_items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_list_collector") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) del answer_objects[-1] - data_dict = json_loads(json_dumps(output["data"]["answers"])) + data_dict = json_loads(json_dumps(output["answers"])) assert sorted(answer_objects, key=lambda x: x["answer_id"]) == sorted( data_dict, key=lambda x: x["answer_id"] ) -def test_list_item_conversion_empty_list(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_list_item_conversion_empty_list(version): """Test that the list store is populated with an empty list for lists which do not have answers yet.""" + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "next-interstitial", "another-list-collector-block"], @@ -509,35 +695,49 @@ def test_list_item_conversion_empty_list(fake_questionnaire_store): {"answer_id": "extraneous-answer", "value": "Bad", "list_item_id": "123"}, ] - fake_questionnaire_store.answer_store = AnswerStore(answer_objects) - fake_questionnaire_store.list_store = ListStore() + questionnaire_store.answer_store = AnswerStore(answer_objects) + questionnaire_store.list_store = ListStore() schema = load_schema_from_name("test_list_collector") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) # Answers not on the routing path del answer_objects[0] del answer_objects[-1] - data_dict = json_loads(json_dumps(output["data"]["answers"])) + data_dict = json_loads(json_dumps(output["answers"])) assert sorted(answer_objects, key=lambda x: x["answer_id"]) == sorted( data_dict, key=lambda x: x["answer_id"] ) -def test_default_answers_not_present_when_not_answered(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_default_answers_not_present_when_not_answered(version): """Test that default values aren't submitted downstream when an answer with a default value is not present in the answer store.""" + questionnaire_store = get_questionnaire_store(version) + schema = load_schema_from_name("test_default") answer_objects = [{"answer_id": "number-question-two", "value": "12"}] - fake_questionnaire_store.answer_store = AnswerStore(answer_objects) - fake_questionnaire_store.list_store = ListStore() + questionnaire_store.answer_store = AnswerStore(answer_objects) + questionnaire_store.list_store = ListStore() routing_path = [ RoutingPath( @@ -545,16 +745,31 @@ def test_default_answers_not_present_when_not_answered(fake_questionnaire_store) ) ] - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answer_ids = {answer["answer_id"] for answer in data} assert "answer-one" not in answer_ids -def test_list_structure_in_payload_is_as_expected(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_list_structure_in_payload_is_as_expected(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["primary-person-list-collector", "list-collector"], section_id="section-1" @@ -582,23 +797,37 @@ def test_list_structure_in_payload_is_as_expected(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_list_collector_primary_person") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data_dict = json_loads(json_dumps(output["data"]["lists"])) + data_dict = json_loads(json_dumps(output["lists"])) assert data_dict[0]["name"] == "people" assert "xJlKBy" in data_dict[0]["items"] assert data_dict[0]["primary_person"] == "xJlKBy" -def test_primary_person_not_in_payload_when_not_answered(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_primary_person_not_in_payload_when_not_answered(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "next-interstitial", "another-list-collector-block"], @@ -622,21 +851,35 @@ def test_primary_person_not_in_payload_when_not_answered(fake_questionnaire_stor existing_items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_list_collector") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data_dict = json_loads(json_dumps(output["data"]["lists"])) + data_dict = json_loads(json_dumps(output["lists"])) assert "primary_person" not in data_dict[0] -def test_relationships_in_payload(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_relationships_in_payload(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "relationships"], @@ -684,15 +927,21 @@ def test_relationships_in_payload(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_relationships") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answers = {answer["answer_id"]: answer for answer in data} expected_relationships_answer = [ @@ -712,7 +961,16 @@ def test_relationships_in_payload(fake_questionnaire_store): assert expected_relationships_answer == relationships_answer["value"] -def test_no_relationships_in_payload(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_no_relationships_in_payload(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "relationships"], @@ -745,21 +1003,36 @@ def test_no_relationships_in_payload(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answers = {answer["answer_id"]: answer for answer in data} assert "relationship-answer" not in answers -def test_unrelated_block_answers_in_payload(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_unrelated_block_answers_in_payload(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "relationships"], @@ -823,15 +1096,21 @@ def test_unrelated_block_answers_in_payload(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answers = { (answer["answer_id"], answer.get("list_item_id")): answer for answer in data } @@ -859,7 +1138,16 @@ def test_unrelated_block_answers_in_payload(fake_questionnaire_store): assert expected_relationships_answer == relationships_answer["value"] -def test_unrelated_block_answers_not_on_path_not_in_payload(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_unrelated_block_answers_not_on_path_not_in_payload(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "relationships"], @@ -918,15 +1206,21 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(fake_questionnaire_s ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answers = { (answer["answer_id"], answer.get("list_item_id")): answer for answer in data } @@ -934,7 +1228,16 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(fake_questionnaire_s assert ("related-to-anyone-else-answer", "person1") not in answers -def test_relationship_answers_not_on_path_in_payload(fake_questionnaire_store): +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_relationship_answers_not_on_path_in_payload(version): + questionnaire_store = get_questionnaire_store(version) + routing_path = [ RoutingPath( ["list-collector", "relationships"], @@ -1003,15 +1306,21 @@ def test_relationship_answers_not_on_path_in_payload(fake_questionnaire_store): ] ) - fake_questionnaire_store.answer_store = answers - fake_questionnaire_store.list_store = list_store + questionnaire_store.answer_store = answers + questionnaire_store.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") - output = convert_answers( - schema, fake_questionnaire_store, routing_path, SUBMITTED_AT + output = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, ) - data = json_loads(json_dumps(output["data"]["answers"])) + + data = json_loads(json_dumps(output["answers"])) answers = { (answer["answer_id"], answer.get("list_item_id")): answer for answer in data } diff --git a/tests/app/submitter/test_converter.py b/tests/app/submitter/test_converter.py index 76bcb237ad..6dde299be1 100644 --- a/tests/app/submitter/test_converter.py +++ b/tests/app/submitter/test_converter.py @@ -2,54 +2,62 @@ import pytest +from app.authentication.auth_payload_version import AuthPayloadVersion from app.questionnaire.questionnaire_schema import QuestionnaireSchema -from app.submitter.converter import DataVersionError, convert_answers +from app.submitter.converter import convert_answers +from app.submitter.converter_v2 import ( + DataVersionError, + NoMetadataException, + convert_answers_v2, +) +from tests.app.questionnaire.conftest import get_metadata +from tests.app.submitter.conftest import get_questionnaire_store SUBMITTED_AT = datetime.now(timezone.utc) -def test_convert_answers_flushed_flag_default_is_false( - fake_questionnaire_schema, fake_questionnaire_store +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_convert_answers_v2_flushed_flag_default_is_false( + fake_questionnaire_schema, version ): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) - - assert not answer_object["flushed"] + questionnaire_store = get_questionnaire_store(version) - -def test_ref_period_end_date_is_not_in_output( - fake_questionnaire_schema, fake_questionnaire_store -): - fake_questionnaire_store.metadata["ref_p_end_date"] = None - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers ) - assert "ref_period_end_date" not in answer_object["metadata"] - del fake_questionnaire_store.metadata["ref_p_end_date"] - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) - assert "ref_period_end_date" not in answer_object["metadata"] + assert not answer_object["flushed"] -def test_ref_period_start_and_end_date_is_in_output( - fake_questionnaire_schema, fake_questionnaire_store + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_convert_answers_v2_flushed_flag_overriden_to_true( + fake_questionnaire_schema, version ): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) - assert answer_object["metadata"]["ref_period_start_date"] == "2016-02-02" - assert answer_object["metadata"]["ref_period_end_date"] == "2016-03-03" + questionnaire_store = get_questionnaire_store(version) + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) -def test_convert_answers_flushed_flag_overriden_to_true( - fake_questionnaire_schema, fake_questionnaire_store -): - answer_object = convert_answers( + answer_object = converter( fake_questionnaire_schema, - fake_questionnaire_store, + questionnaire_store, {}, SUBMITTED_AT, flushed=True, @@ -58,110 +66,174 @@ def test_convert_answers_flushed_flag_overriden_to_true( assert answer_object["flushed"] +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_started_at_should_be_set_in_payload_if_present_in_response_metadata( - fake_questionnaire_schema, fake_questionnaire_store + fake_questionnaire_schema, version ): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + questionnaire_store = get_questionnaire_store(version) + + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) assert ( answer_object["started_at"] - == fake_questionnaire_store.response_metadata["started_at"] + == questionnaire_store.response_metadata["started_at"] ) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_started_at_should_not_be_set_in_payload_if_absent_in_response_metadata( - fake_questionnaire_schema, - fake_questionnaire_store, - fake_response_metadata, - fake_metadata, + fake_questionnaire_schema, fake_response_metadata, version ): del fake_response_metadata["started_at"] + questionnaire_store = get_questionnaire_store(version) + questionnaire_store.response_metadata = fake_response_metadata - fake_questionnaire_store.set_metadata(fake_metadata) - fake_questionnaire_store.response_metadata = fake_response_metadata + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) assert "started_at" not in answer_object -def test_submitted_at_should_be_set_in_payload( - fake_questionnaire_schema, fake_questionnaire_store -): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_submitted_at_should_be_set_in_payload(fake_questionnaire_schema, version): + questionnaire_store = get_questionnaire_store(version) - assert SUBMITTED_AT.isoformat() == answer_object["submitted_at"] - - -def test_case_id_should_be_set_in_payload( - fake_questionnaire_schema, fake_questionnaire_store -): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers ) - assert answer_object["case_id"] == fake_questionnaire_store.metadata["case_id"] + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + assert SUBMITTED_AT.isoformat() == answer_object["submitted_at"] -def test_case_ref_should_be_set_in_payload( - fake_questionnaire_schema, fake_questionnaire_store -): - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) - assert answer_object["case_ref"], fake_questionnaire_store.metadata["case_ref"] +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_case_id_should_be_set_in_payload(fake_questionnaire_schema, version): + questionnaire_store = get_questionnaire_store(version) + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) -def test_display_address_should_be_set_in_payload_metadata( - fake_questionnaire_schema, fake_questionnaire_store -): - payload = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) - assert payload["metadata"]["display_address"], fake_questionnaire_store.metadata[ - "display_address" - ] + assert answer_object["case_id"] == questionnaire_store.metadata.case_id -def test_instrument_id_is_not_in_payload_collection_if_form_type_absent_in_metadata( - fake_questionnaire_schema, fake_questionnaire_store, fake_metadata -): - del fake_metadata["form_type"] - fake_questionnaire_store.set_metadata(fake_metadata) - payload = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_case_ref_should_be_set_in_payload(fake_questionnaire_schema, version): + questionnaire_store = get_questionnaire_store(version) - assert "instrument_id" not in payload["collection"] + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) -def test_instrument_id_should_be_set_in_payload_collection_if_form_type_in_metadata( - fake_questionnaire_schema, fake_questionnaire_store + if version is AuthPayloadVersion.V2: + assert answer_object["survey_metadata"][ + "case_ref" + ], questionnaire_store.metadata["survey_metadata"]["data"]["case_ref"] + else: + assert answer_object["case_ref"], questionnaire_store.metadata["case_ref"] + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_display_address_should_be_set_in_payload_metadata( + fake_questionnaire_schema, version ): - payload = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT - ) + questionnaire_store = get_questionnaire_store(version) - assert payload["collection"]["instrument_id"], "I" + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + payload = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) -def test_converter_raises_runtime_error_for_unsupported_version( - fake_questionnaire_store, -): + if version is AuthPayloadVersion.V2: + assert payload["survey_metadata"][ + "display_address" + ], questionnaire_store.metadata["survey_metadata"]["data"]["display_address"] + else: + assert payload["metadata"]["display_address"], questionnaire_store.metadata[ + "display_address" + ] + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_converter_raises_runtime_error_for_unsupported_version(version): + questionnaire_store = get_questionnaire_store(version) questionnaire = {"survey_id": "021", "data_version": "-0.0.1"} + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + with pytest.raises(DataVersionError) as err: - convert_answers( + converter( QuestionnaireSchema(questionnaire), - fake_questionnaire_store, + questionnaire_store, {}, SUBMITTED_AT, ) @@ -169,37 +241,122 @@ def test_converter_raises_runtime_error_for_unsupported_version( assert "Data version -0.0.1 not supported" in str(err.value) +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_converter_language_code_not_set_in_payload( - fake_questionnaire_schema, - fake_questionnaire_store, - fake_response_metadata, - fake_metadata, + fake_questionnaire_schema, fake_response_metadata, version ): - fake_questionnaire_store.set_metadata(fake_metadata) - fake_questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store = get_questionnaire_store(version) + questionnaire_store.response_metadata = fake_response_metadata - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers ) - with pytest.raises(KeyError): - assert fake_questionnaire_store.metadata["language_code"] + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert questionnaire_store.metadata["language_code"] is None assert answer_object["launch_language_code"] == "en" +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) def test_converter_language_code_set_in_payload( - fake_questionnaire_schema, - fake_questionnaire_store, - fake_response_metadata, - fake_metadata, + fake_questionnaire_schema, fake_response_metadata, version ): - fake_metadata["language_code"] = "ga" - fake_questionnaire_store.set_metadata(fake_metadata) - fake_questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store = get_questionnaire_store(version) + questionnaire_store.metadata = get_metadata({"language_code": "ga"}) + questionnaire_store.response_metadata = fake_response_metadata - answer_object = convert_answers( - fake_questionnaire_schema, fake_questionnaire_store, {}, SUBMITTED_AT + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) assert answer_object["launch_language_code"] == "ga" + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_no_metadata_raises_exception(fake_questionnaire_schema, version): + questionnaire_store = get_questionnaire_store(version) + + questionnaire_store.metadata = None + + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + + with pytest.raises(NoMetadataException): + converter(fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT) + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_data_object_set_in_payload( + fake_questionnaire_schema, fake_response_metadata, version +): + questionnaire_store = get_questionnaire_store(version) + questionnaire_store.response_metadata = fake_response_metadata + + converter = ( + convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers + ) + + answer_object = converter( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert "data" in answer_object + + +def test_instrument_id_is_not_in_payload_collection_if_form_type_absent_in_metadata( + fake_questionnaire_schema, +): + questionnaire_store = get_questionnaire_store("v1") + + questionnaire_store.metadata = get_metadata({"form_type": None}) + + payload = convert_answers( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert "instrument_id" not in payload["collection"] + + +def test_instrument_id_should_be_set_in_payload_collection_if_form_type_in_metadata( + fake_questionnaire_schema, +): + questionnaire_store = get_questionnaire_store("v1") + + payload = convert_answers( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert payload["collection"]["instrument_id"], "I" diff --git a/tests/app/submitter/test_submitter.py b/tests/app/submitter/test_submitter.py index f6c857b7ce..d700d8a48d 100644 --- a/tests/app/submitter/test_submitter.py +++ b/tests/app/submitter/test_submitter.py @@ -212,6 +212,47 @@ def test_gcs_submitter_adds_metadata_when_sends_message(patch_gcs_client): } +def test_gcs_submitter_adds_additional_keys_to_metadata_when_set(patch_gcs_client): + gcs_submitter = GCSSubmitter(bucket_name="test_bucket") + + # When + gcs_submitter.send_message( + message={"test_data"}, tx_id="123", case_id="456", **{"questionnaire_id": "1"} + ) + + # Then + bucket = patch_gcs_client.return_value.get_bucket.return_value + blob = bucket.blob.return_value + + assert blob.metadata == { + "tx_id": "123", + "case_id": "456", + "questionnaire_id": "1", + } + + +def test_gcs_feedback_submitter_adds_additional_keys_to_metadata_when_set( + patch_gcs_client, +): + gcs_submitter = GCSFeedbackSubmitter(bucket_name="test_bucket") + + # When + gcs_submitter.upload( + payload=json_dumps({"some-data": "some-value"}), + metadata={"tx_id": "123", "case_id": "456", "questionnaire_id": "1"}, + ) + + # Then + bucket = patch_gcs_client.return_value.get_bucket.return_value + blob = bucket.blob.return_value + + assert blob.metadata == { + "tx_id": "123", + "case_id": "456", + "questionnaire_id": "1", + } + + @pytest.mark.parametrize( "submitter, entrypoint, data_to_upload", [ diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index facd03914d..746fc5efb9 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -23,6 +23,7 @@ load_schema_from_name, load_schema_from_url, ) +from tests.app.questionnaire.conftest import get_metadata TEST_SCHEMA_URL = "http://test.domain/schema.json" @@ -220,7 +221,9 @@ def test_load_schema_from_url_uses_cache(): def test_load_schema_from_metadata_with_schema_url(): load_schema_from_url.cache_clear() - metadata = {"schema_url": TEST_SCHEMA_URL, "language_code": "cy"} + metadata = get_metadata( + {"schema_url": TEST_SCHEMA_URL, "language_code": "cy"}, + ) mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) loaded_schema = load_schema_from_metadata(metadata=metadata) @@ -234,9 +237,11 @@ def test_load_schema_from_metadata_with_schema_url_and_override_language_code(): load_schema_from_url.cache_clear() language_code = "en" - metadata = {"schema_url": TEST_SCHEMA_URL, "language_code": "cy"} + metadata = get_metadata({"schema_url": TEST_SCHEMA_URL, "language_code": "cy"}) + mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) + loaded_schema = load_schema_from_metadata( metadata=metadata, language_code=language_code ) diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index f0c7498450..8dfbacb695 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -201,21 +201,6 @@ def fake_session_data(): ) -@pytest.fixture -def fake_questionnaire_store_metadata(): - return { - "tx_id": "tx_id", - "schema_name": "some_schema_name", - "period_str": "period_str", - "language_code": None, - "schema_url": None, - "ru_name": "ru_name", - "ru_ref": "ru_ref", - "response_id": "response_id", - "case_id": "case_id", - } - - @pytest.fixture def test_calculated_summary_schema(): return load_schema_from_name("test_calculated_summary") diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 3fe5da2755..8ae1b64c26 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -5,6 +5,7 @@ from app.questionnaire.router import Router from app.utilities.schema import load_schema_from_name from app.views.contexts import HubContext +from tests.app.questionnaire.conftest import get_metadata @pytest.fixture @@ -14,7 +15,7 @@ def router(schema, answer_store, list_store, progress_store): answer_store, list_store, progress_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -47,7 +48,7 @@ def test_get_not_started_row_for_section( list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -90,7 +91,7 @@ def test_get_completed_row_for_section( list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -112,7 +113,7 @@ def test_get_context(progress_store, answer_store, list_store, router): list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -141,7 +142,7 @@ def test_get_context_custom_content_incomplete( list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -170,7 +171,7 @@ def test_get_context_custom_content_complete( list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) @@ -199,7 +200,7 @@ def test_get_context_no_list_items_survey_incomplete_individual_response_disable list_store=list_store, schema=schema, answer_store=answer_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, ) diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 8fdb7a2ff4..e1d4f864ad 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -9,6 +9,7 @@ from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name from app.views.contexts import SectionSummaryContext +from tests.app.questionnaire.conftest import get_metadata from tests.app.views.contexts import assert_summary_context @@ -21,7 +22,7 @@ def test_build_summary_rendering_context( answer_store, list_store, progress_store, - metadata={}, + metadata=get_metadata(), response_metadata={}, current_location=Location(section_id="property-details-section"), routing_path=mocker.MagicMock(), @@ -41,7 +42,7 @@ def test_build_view_context_for_section_summary( answer_store, list_store, progress_store, - metadata={}, + metadata=None, response_metadata={}, current_location=Location( section_id="property-details-section", @@ -112,7 +113,7 @@ def test_custom_section( answer_store, list_store, progress_store, - metadata={}, + metadata=None, response_metadata={}, current_location=location, routing_path=mocker.MagicMock(), @@ -136,7 +137,7 @@ def test_context_for_section_list_summary(people_answer_store): ] ), progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, + metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( @@ -221,7 +222,7 @@ def test_context_for_driving_question_summary_empty_list(): AnswerStore([{"answer_id": "anyone-usually-live-at-answer", "value": "No"}]), ListStore(), ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath(["anyone-usually-live-at"], section_id="section"), @@ -272,7 +273,7 @@ def test_context_for_driving_question_summary(): ), ListStore([{"items": ["PlwgoG"], "name": "people"}]), ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( @@ -332,7 +333,7 @@ def test_titles_for_repeating_section_summary(people_answer_store): ] ), ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location( section_id="personal-details-section", @@ -357,7 +358,7 @@ def test_titles_for_repeating_section_summary(people_answer_store): ] ), ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location( block_id="personal-summary", @@ -384,7 +385,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): [{"items": ["PlwgoG"], "name": "people", "primary_person": "PlwgoG"}] ), progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, + metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( @@ -421,7 +422,7 @@ def test_primary_links_for_section_summary(people_answer_store): ] ), progress_store=ProgressStore(), - metadata={"display_address": "70 Abingdon Road, Goathill"}, + metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 2ef5b96421..cca4c6d114 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -9,11 +9,13 @@ from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.settings import VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS +from app.submitter.converter_v2 import NoMetadataException from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_name from app.views.contexts.view_submitted_response_context import ( build_view_submitted_response_context, ) +from tests.app.questionnaire.conftest import get_metadata SUBMITTED_AT = datetime.now(timezone.utc) SCHEMA = load_schema_from_name("test_view_submitted_response", "en") @@ -64,7 +66,6 @@ def test_build_view_submitted_response_context_submitted_text(app: Flask): def test_build_view_submitted_response_context_submitted_text_social(app: Flask): with app.app_context(): questionnaire_store = fake_questionnaire_store() - questionnaire_store.metadata["trad_as"] = "Apple Inc" context = build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.SOCIAL ) @@ -74,8 +75,7 @@ def test_build_view_submitted_response_context_submitted_text_social(app: Flask) def test_build_view_submitted_response_context_submitted_text_with_trad_as(app: Flask): with app.app_context(): - questionnaire_store = fake_questionnaire_store() - questionnaire_store.metadata["trad_as"] = "Apple Inc" + questionnaire_store = fake_questionnaire_store_with_trad_as() context = build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) @@ -111,12 +111,26 @@ def test_build_view_submitted_response_no_submitted_at(app: Flask): ) +def test_no_metadata_raises_error( + app: Flask, +): + with app.app_context(): + questionnaire_store = fake_questionnaire_store() + + questionnaire_store.metadata = None + + with pytest.raises(NoMetadataException): + build_view_submitted_response_context( + "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT + ) + + def fake_questionnaire_store(): storage = Mock() storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) questionnaire_store = QuestionnaireStore(storage) + questionnaire_store.metadata = get_metadata({"tx_id": "tx_id", "ru_name": "Apple"}) questionnaire_store.submitted_at = SUBMITTED_AT - questionnaire_store.metadata = {"tx_id": "123456789", "ru_name": "Apple"} questionnaire_store.answer_store = AnswerStore( [ Answer("name-answer", "John Smith", None).to_dict(), @@ -126,6 +140,18 @@ def fake_questionnaire_store(): return questionnaire_store +def fake_questionnaire_store_with_trad_as(): + storage = Mock() + storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) + questionnaire_store = QuestionnaireStore(storage) + questionnaire_store.metadata = get_metadata( + {"tx_id": "tx_id", "ru_name": "Apple", "trad_as": "Apple Inc"} + ) + questionnaire_store.submitted_at = SUBMITTED_AT + questionnaire_store.answer_store = AnswerStore() + return questionnaire_store + + def format_submitted_on_description(): date = format_datetime(SUBMITTED_AT, format="dd LLLL yyyy") time = format_datetime(SUBMITTED_AT, format="HH:mm") diff --git a/tests/app/views/contexts/test_thank_you_context.py b/tests/app/views/contexts/test_thank_you_context.py index f99d468448..8f45eb37b3 100644 --- a/tests/app/views/contexts/test_thank_you_context.py +++ b/tests/app/views/contexts/test_thank_you_context.py @@ -5,6 +5,7 @@ from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_name from app.views.contexts.thank_you_context import build_thank_you_context +from tests.app.questionnaire.conftest import get_metadata SURVEY_TYPE_DEFAULT = SurveyType.DEFAULT SURVEY_TYPE_SOCIAL = SurveyType.SOCIAL @@ -12,21 +13,23 @@ SCHEMA = load_schema_from_name("test_view_submitted_response", "en") -def test_social_survey_context(fake_questionnaire_store_metadata, app: Flask): +def test_social_survey_context(app: Flask): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_SOCIAL + SCHEMA, get_metadata(), SUBMITTED_AT, SURVEY_TYPE_SOCIAL ) assert context["submission_text"] == "Your answers have been submitted." assert len(context["metadata"]["itemsList"]) == 1 -def test_default_survey_context(fake_questionnaire_store_metadata, app: Flask): +def test_default_survey_context(app: Flask): with app.app_context(): - fake_questionnaire_store_metadata["ru_name"] = "ESSENTIAL ENTERPRISE LTD" context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, + get_metadata({"ru_name": "ESSENTIAL ENTERPRISE LTD"}), + SUBMITTED_AT, + SURVEY_TYPE_DEFAULT, ) assert ( @@ -36,14 +39,13 @@ def test_default_survey_context(fake_questionnaire_store_metadata, app: Flask): assert len(context["metadata"]["itemsList"]) == 2 -def test_default_survey_context_with_trad_as( - fake_questionnaire_store_metadata, app: Flask -): +def test_default_survey_context_with_trad_as(app: Flask): with app.app_context(): - fake_questionnaire_store_metadata["ru_name"] = "ESSENTIAL ENTERPRISE LTD" - fake_questionnaire_store_metadata["trad_as"] = "EE" context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, + get_metadata({"ru_name": "ESSENTIAL ENTERPRISE LTD", "trad_as": "EE"}), + SUBMITTED_AT, + SURVEY_TYPE_DEFAULT, ) assert ( @@ -53,57 +55,53 @@ def test_default_survey_context_with_trad_as( assert len(context["metadata"]["itemsList"]) == 2 -def test_view_submitted_response_enabled(fake_questionnaire_store_metadata, app: Flask): +def test_view_submitted_response_enabled(app: Flask): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, get_metadata(), SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["enabled"] is True -def test_view_submitted_response_not_enabled( - fake_questionnaire_store_metadata, app: Flask -): +def test_view_submitted_response_not_enabled(app: Flask): with app.app_context(): schema = load_schema_from_name("test_title", "en") context = build_thank_you_context( - schema, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + schema, get_metadata(), SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["enabled"] is False -def test_view_submitted_response_not_expired( - fake_questionnaire_store_metadata, app: Flask -): +def test_view_submitted_response_not_expired(app: Flask): with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, SUBMITTED_AT, SURVEY_TYPE_DEFAULT + SCHEMA, get_metadata(), SUBMITTED_AT, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["expired"] is False assert context["view_submitted_response"]["url"] == "/submitted/view-response/" -def test_view_submitted_response_expired(fake_questionnaire_store_metadata, app: Flask): +def test_view_submitted_response_expired(app: Flask): submitted_at = SUBMITTED_AT - timedelta(minutes=46) with app.app_context(): context = build_thank_you_context( - SCHEMA, fake_questionnaire_store_metadata, submitted_at, SURVEY_TYPE_DEFAULT + SCHEMA, get_metadata(), submitted_at, SURVEY_TYPE_DEFAULT ) assert context["view_submitted_response"]["expired"] is True -def test_custom_guidance(fake_questionnaire_store_metadata, app: Flask): +def test_custom_guidance(app: Flask): with app.app_context(): custom_guidance = {"contents": [{"description": "Custom guidance"}]} context = build_thank_you_context( SCHEMA, - fake_questionnaire_store_metadata, + get_metadata(), SUBMITTED_AT, SURVEY_TYPE_DEFAULT, custom_guidance, diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index daa08b1b41..003f9e973a 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -5,7 +5,9 @@ from freezegun import freeze_time from mock import Mock +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore from app.questionnaire import QuestionnaireSchema @@ -104,24 +106,60 @@ def schema_feedback(): @pytest.fixture def metadata(): - return { - "tx_id": tx_id, - "user_id": user_id, - "schema_name": schema_name, - "collection_exercise_sid": collection_exercise_sid, - "period_id": period_id, - "period_str": period_str, - "ref_p_start_date": ref_p_start_date, - "ref_p_end_date": ref_p_end_date, - "ru_ref": ru_ref, - "response_id": response_id, - "form_type": form_type, - "display_address": display_address, - "case_type": case_type, - "channel": channel, - "case_ref": case_ref, - "region_code": region_code, - } + return MetadataProxy.from_dict( + { + "tx_id": tx_id, + "user_id": user_id, + "schema_name": schema_name, + "collection_exercise_sid": collection_exercise_sid, + "period_id": period_id, + "period_str": period_str, + "ref_p_start_date": ref_p_start_date, + "ref_p_end_date": ref_p_end_date, + "ru_ref": ru_ref, + "response_id": response_id, + "form_type": form_type, + "display_address": display_address, + "case_type": case_type, + "channel": channel, + "case_ref": case_ref, + "region_code": region_code, + "case_id": case_id, + "language_code": language_code, + } + ) + + +@pytest.fixture +def metadata_v2(): + return MetadataProxy.from_dict( + { + "version": AuthPayloadVersion.V2, + "tx_id": tx_id, + "case_id": case_id, + "schema_name": schema_name, + "collection_exercise_sid": collection_exercise_sid, + "response_id": response_id, + "channel": channel, + "region_code": region_code, + "account_service_url": "account_service_url", + "survey_metadata": { + "data": { + "period_id": period_id, + "period_str": period_str, + "ref_p_start_date": ref_p_start_date, + "ref_p_end_date": ref_p_end_date, + "ru_ref": ru_ref, + "ru_name": ru_name, + "case_type": case_type, + "form_type": form_type, + "case_ref": case_ref, + "display_address": display_address, + "user_id": user_id, + } + }, + } + ) @pytest.fixture @@ -161,5 +199,53 @@ def mock_questionnaire_store(mocker): storage_ = mocker.Mock() storage_.get_user_data = mocker.Mock(return_value=("{}", "ce_id", 1, None)) questionnaire_store = QuestionnaireStore(storage_) - questionnaire_store.metadata = {"tx_id": "tx_id", "case_id": "case_id"} + questionnaire_store.metadata = MetadataProxy.from_dict( + { + "tx_id": "tx_id", + "case_id": "case_id", + "ru_ref": ru_ref, + "user_id": user_id, + "collection_exercise_sid": collection_exercise_sid, + "period_id": period_id, + "schema_name": schema_name, + "account_service_url": "account_service_url", + "response_id": "response_id", + } + ) + return questionnaire_store + + +@pytest.fixture +def mock_questionnaire_store_v2(mocker): + storage_ = mocker.Mock() + storage_.get_user_data = mocker.Mock(return_value=("{}", "ce_id", 1, None)) + questionnaire_store = QuestionnaireStore(storage_) + questionnaire_store.metadata = MetadataProxy.from_dict( + { + "version": AuthPayloadVersion.V2, + "tx_id": "tx_id", + "case_id": case_id, + "schema_name": schema_name, + "collection_exercise_sid": collection_exercise_sid, + "response_id": response_id, + "channel": channel, + "region_code": region_code, + "account_service_url": "account_service_url", + "survey_metadata": { + "data": { + "period_id": period_id, + "period_str": period_str, + "ref_p_start_date": ref_p_start_date, + "ref_p_end_date": ref_p_end_date, + "ru_ref": ru_ref, + "ru_name": ru_name, + "case_type": case_type, + "form_type": form_type, + "case_ref": case_ref, + "display_address": display_address, + "user_id": user_id, + } + }, + } + ) return questionnaire_store diff --git a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py index 9f3cf12328..5c144693f0 100644 --- a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py +++ b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py @@ -27,7 +27,7 @@ def test_confirmation_email_fulfilment_request_message( "form_type": confirmation_email_fulfilment_schema.form_type, "language_code": session_data.language_code, "region_code": confirmation_email_fulfilment_schema.region_code, - "tx_id": metadata["tx_id"], + "tx_id": metadata.tx_id, } assert ( diff --git a/tests/app/views/handlers/test_feedback_upload.py b/tests/app/views/handlers/test_feedback_upload.py index b1c0aba0bf..f8806d5e5c 100644 --- a/tests/app/views/handlers/test_feedback_upload.py +++ b/tests/app/views/handlers/test_feedback_upload.py @@ -2,8 +2,13 @@ from freezegun import freeze_time +from app.authentication.auth_payload_version import AuthPayloadVersion from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE -from app.views.handlers.feedback import FeedbackMetadata, FeedbackPayload +from app.views.handlers.feedback import ( + FeedbackMetadata, + FeedbackPayload, + FeedbackPayloadV2, +) from .conftest import ( case_id, @@ -19,9 +24,11 @@ form_type, language_code, period_id, + period_str, ref_p_end_date, ref_p_start_date, region_code, + ru_name, ru_ref, schema_name, started_at, @@ -59,7 +66,7 @@ def test_feedback_payload( "feedback_type": feedback_type, }, "form_type": form_type, - "launch_language_code": "en", + "launch_language_code": language_code, "metadata": { "display_address": display_address, "ref_period_end_date": ref_p_end_date, @@ -86,6 +93,61 @@ def test_feedback_payload( assert expected_payload == feedback_payload() +@freeze_time(datetime.now(tz=timezone.utc).isoformat()) +def test_feedback_payload_v2( + session_data_feedback, schema_feedback, metadata_v2, response_metadata +): + feedback_payload = FeedbackPayloadV2( + metadata=metadata_v2, + response_metadata=response_metadata, + schema=schema_feedback, + case_id=case_id, + submission_language_code=language_code, + feedback_count=session_data_feedback.feedback_count, + feedback_text=feedback_text, + feedback_type=feedback_type, + ) + + expected_payload = { + "case_id": case_id, + "channel": channel, + "collection_exercise_sid": collection_exercise_sid, + "data": { + "feedback_count": str(feedback_count), + "feedback_text": feedback_text, + "feedback_type": feedback_type, + }, + "data_version": data_version, + "flushed": False, + "launch_language_code": "en", + "origin": "uk.gov.ons.edc.eq", + "region_code": region_code, + "schema_name": schema_name, + "started_at": started_at, + "submission_language_code": language_code, + "submitted_at": datetime.now(tz=timezone.utc).isoformat(), + "survey_metadata": { + "survey_id": survey_id, + "case_ref": case_ref, + "case_type": case_type, + "display_address": display_address, + "form_type": form_type, + "period_id": period_id, + "period_str": period_str, + "ref_p_end_date": ref_p_end_date, + "ref_p_start_date": ref_p_start_date, + "ru_name": ru_name, + "ru_ref": ru_ref, + "user_id": user_id, + }, + "tx_id": tx_id, + "type": "uk.gov.ons.edc.eq:feedback", + "version": AuthPayloadVersion.V2.value, + } + + assert expected_payload == feedback_payload() + + def test_submission_language_code_uses_default_language_when_session_language_none( session_data_feedback, schema_feedback, metadata, response_metadata ): @@ -112,3 +174,13 @@ def test_feedback_metadata(): } assert feedback_metadata() == expected_metadata + + +def test_feedback_metadata_with_receipting_keys(): + receipting_keys = {"questionnaire_id": "1"} + + feedback_metadata = FeedbackMetadata(case_id, tx_id, **receipting_keys) + + expected_metadata = {"case_id": case_id, "tx_id": tx_id, "questionnaire_id": "1"} + + assert feedback_metadata() == expected_metadata diff --git a/tests/app/views/handlers/test_individual_response_fulfilment_request.py b/tests/app/views/handlers/test_individual_response_fulfilment_request.py index 40add84768..ae295f8d3f 100644 --- a/tests/app/views/handlers/test_individual_response_fulfilment_request.py +++ b/tests/app/views/handlers/test_individual_response_fulfilment_request.py @@ -4,6 +4,7 @@ import pytest from freezegun import freeze_time +from app.data_models.metadata_proxy import MetadataProxy from app.forms.validators import sanitise_mobile_number from app.helpers.uuid_helper import is_valid_uuid4 from app.utilities.json import json_loads @@ -19,7 +20,15 @@ @freeze_time(datetime.now(tz=timezone.utc).isoformat()) def test_sms_fulfilment_request_payload(): - metadata = {"region_code": "GB-ENG", "case_id": str(uuid4())} + metadata = MetadataProxy( + region_code="GB-ENG", + case_id=str(uuid4()), + tx_id="tx_id", + response_id="response_id", + account_service_url="account_service_url", + collection_exercise_sid="collection_exercise_sid", + ) + fulfilment_request = IndividualResponseFulfilmentRequest( metadata, DUMMY_MOBILE_NUMBER ) @@ -39,7 +48,17 @@ def test_sms_fulfilment_request_payload(): @freeze_time(datetime.now(tz=timezone.utc).isoformat()) def test_postal_fulfilment_request_message(): - metadata = {"region_code": "GB-ENG", "case_id": str(uuid4())} + metadata = { + "region_code": "GB-ENG", + "case_id": str(uuid4()), + "tx_id": "tx_id", + "response_id": "response_id", + "account_service_url": "account_service_url", + "collection_exercise_sid": "collection_exercise_sid", + } + + metadata = MetadataProxy.from_dict(metadata) + fulfilment_request = IndividualResponseFulfilmentRequest(metadata) postal_json_message = json_loads(fulfilment_request.message) @@ -65,7 +84,18 @@ def validate_uuids_in_payload(payload): @freeze_time(datetime.now(tz=timezone.utc).isoformat()) def test_individual_case_id_not_present_when_case_type_spg(): - metadata = {"region_code": "GB-ENG", "case_id": str(uuid4()), "case_type": "SPG"} + metadata = { + "region_code": "GB-ENG", + "case_id": str(uuid4()), + "case_type": "SPG", + "tx_id": "tx_id", + "response_id": "response_id", + "account_service_url": "account_service_url", + "collection_exercise_sid": "collection_exercise_sid", + } + + metadata = MetadataProxy.from_dict(metadata) + fulfilment_request = IndividualResponseFulfilmentRequest(metadata) json_message = json_loads(fulfilment_request.message) @@ -74,7 +104,18 @@ def test_individual_case_id_not_present_when_case_type_spg(): @freeze_time(datetime.now(tz=timezone.utc).isoformat()) def test_individual_case_id_not_present_when_case_type_ce(): - metadata = {"region_code": "GB-ENG", "case_id": str(uuid4()), "case_type": "CE"} + metadata = { + "region_code": "GB-ENG", + "case_id": str(uuid4()), + "case_type": "CE", + "tx_id": "tx_id", + "response_id": "response_id", + "account_service_url": "account_service_url", + "collection_exercise_sid": "collection_exercise_sid", + } + + metadata = MetadataProxy.from_dict(metadata) + fulfilment_request = IndividualResponseFulfilmentRequest(metadata) json_message = json_loads(fulfilment_request.message) @@ -90,7 +131,18 @@ def test_individual_case_id_not_present_when_case_type_ce(): ], ) def test_fulfilment_code_for_sms(region_code, expected_fulfilment_code): - metadata = {"region_code": region_code, "case_id": str(uuid4()), "case_type": "SPG"} + metadata = { + "region_code": region_code, + "case_id": str(uuid4()), + "case_type": "SPG", + "tx_id": "tx_id", + "response_id": "response_id", + "account_service_url": "account_service_url", + "collection_exercise_sid": "collection_exercise_sid", + } + + metadata = MetadataProxy.from_dict(metadata) + fulfilment_request = IndividualResponseFulfilmentRequest( metadata, DUMMY_MOBILE_NUMBER ) @@ -110,8 +162,20 @@ def test_fulfilment_code_for_sms(region_code, expected_fulfilment_code): ], ) def test_fulfilment_code_for_postal(region_code, expected_fulfilment_code): - metadata = {"region_code": region_code, "case_id": str(uuid4()), "case_type": "SPG"} + metadata = { + "region_code": region_code, + "case_id": str(uuid4()), + "case_type": "SPG", + "tx_id": "tx_id", + "response_id": "response_id", + "account_service_url": "account_service_url", + "collection_exercise_sid": "collection_exercise_sid", + } + + metadata = MetadataProxy.from_dict(metadata) + fulfilment_request = IndividualResponseFulfilmentRequest(metadata) + json_message = json_loads(fulfilment_request.message) assert ( diff --git a/tests/app/views/handlers/test_submission_handler.py b/tests/app/views/handlers/test_submission_handler.py index 6e93553f33..c4466ce2a3 100644 --- a/tests/app/views/handlers/test_submission_handler.py +++ b/tests/app/views/handlers/test_submission_handler.py @@ -3,8 +3,10 @@ import pytest from freezegun import freeze_time +from app.authentication.auth_payload_version import AuthPayloadVersion from app.data_models.session_store import SessionStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.utilities.schema import load_schema_from_name from app.views.handlers.submission import SubmissionHandler @@ -80,3 +82,99 @@ def test_submit_view_submitted_response_true_submitted_at_set( assert mock_questionnaire_store.submitted_at == datetime.now(timezone.utc) assert mock_questionnaire_store.save.called assert not mock_questionnaire_store.delete.called + + +@freeze_time(datetime.now(timezone.utc).replace(second=0, microsecond=0)) +@pytest.mark.usefixtures("app") +def test_submission_payload_structure_v1( + app, submission_payload_session_store, mock_questionnaire_store, mocker +): + expected_payload = { + "case_id": "case_id", + "collection": { + "exercise_sid": "ce_sid", + "period": "2016-02-01", + "schema_name": "1_0000", + }, + "data": {"answers": [], "lists": []}, + "flushed": False, + "launch_language_code": "en", + "metadata": {"ru_ref": "432423423423", "user_id": "789473423"}, + "origin": "uk.gov.ons.edc.eq", + "submission_language_code": "cy", + "submitted_at": datetime.now(timezone.utc).isoformat(), + "survey_id": "0", + "tx_id": "tx_id", + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "0.0.3", + } + + with app.test_request_context(): + mocker.patch( + "app.views.handlers.submission.get_session_store", + return_value=submission_payload_session_store, + ) + schema = load_schema_from_name("test_checkbox") + + submission_handler = SubmissionHandler( + schema, + mock_questionnaire_store, + full_routing_path=[], + ) + payload = submission_handler.get_payload() + + assert expected_payload == payload + + +@freeze_time(datetime.now(timezone.utc).replace(second=0, microsecond=0)) +@pytest.mark.usefixtures("app") +def test_submission_payload_structure_v2( + app, submission_payload_session_store, mock_questionnaire_store_v2, mocker +): + expected_payload = { + "case_id": "case_id", + "tx_id": "tx_id", + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": AuthPayloadVersion.V2.value, + "data_version": "0.0.3", + "origin": "uk.gov.ons.edc.eq", + "collection_exercise_sid": "ce_sid", + "schema_name": "1_0000", + "flushed": False, + "submitted_at": datetime.now(timezone.utc).isoformat(), + "launch_language_code": "en", + "channel": "H", + "region_code": "GB_WLS", + "survey_metadata": { + "period_id": "2016-02-01", + "period_str": "2016-01-01", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "ru_ref": "432423423423", + "ru_name": "ru_name", + "case_type": "I", + "form_type": "I", + "case_ref": "1000000000000001", + "display_address": "68 Abingdon Road, Goathill", + "user_id": "789473423", + "survey_id": "0", + }, + "submission_language_code": "cy", + "data": {"answers": [], "lists": []}, + } + + with app.test_request_context(): + mocker.patch( + "app.views.handlers.submission.get_session_store", + return_value=submission_payload_session_store, + ) + schema = load_schema_from_name("test_checkbox") + + submission_handler = SubmissionHandler( + schema, + mock_questionnaire_store_v2, + full_routing_path=[], + ) + payload = submission_handler.get_payload() + + assert expected_payload == payload diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index 7c01784f1d..e8758f6a2d 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -61,6 +61,8 @@ export function getRandomString(length) { export function generateToken( schema, { + version, + theme, userId, collectionId, responseId, @@ -68,12 +70,7 @@ export function generateToken( periodStr = "May 2016", regionCode = "GB-ENG", languageCode = "en", - sexualIdentity = false, includeLogoutUrl = true, - country = "", - locality = "", - townName = "", - postcode = "", displayAddress = "", } ) { @@ -87,35 +84,57 @@ export function generateToken( }; // Payload - const oPayload = { - tx_id: uuidv4(), - jti: uuidv4(), - iat: KJUR.jws.IntDate.get("now"), - exp: KJUR.jws.IntDate.get("now") + 1800, - user_id: userId, - case_id: uuidv4(), - ru_ref: "12346789012A", - response_id: responseId, - ru_name: "Apple", - trad_as: "Apple", - schema_name: `${schemaParts[1]}_${schemaParts[2]}`, - collection_exercise_sid: collectionId, - period_id: periodId, - period_str: periodStr, - ref_p_start_date: "2017-01-01", - ref_p_end_date: "2017-02-01", - employment_date: "2016-06-10", - return_by: "2017-03-01", - country, - locality, - town_name: townName, - postcode, - display_address: displayAddress, - region_code: regionCode, - language_code: languageCode, - sexual_identity: sexualIdentity, - account_service_url: "http://localhost:8000", - }; + let payload = {}; + const txId = uuidv4(); + const jti = uuidv4(); + const iat = KJUR.jws.IntDate.get("now"); + const exp = KJUR.jws.IntDate.get("now") + 1800; + const caseId = uuidv4(); + + if (version === "v2") { + payload = { + tx_id: txId, + jti: jti, + iat: iat, + exp: exp, + case_id: caseId, + response_id: responseId, + schema_name: `${schemaParts[1]}_${schemaParts[2]}`, + collection_exercise_sid: collectionId, + region_code: regionCode, + language_code: languageCode, + account_service_url: "http://localhost:8000", + survey_metadata: getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr), + version: "v2", + }; + } else { + payload = { + tx_id: txId, + jti: jti, + iat: iat, + exp: exp, + user_id: userId, + case_id: caseId, + ru_ref: "12346789012A", + response_id: responseId, + ru_name: "Apple", + trad_as: "Apple", + schema_name: `${schemaParts[1]}_${schemaParts[2]}`, + collection_exercise_sid: collectionId, + period_id: periodId, + period_str: periodStr, + ref_p_start_date: "2017-01-01", + ref_p_end_date: "2017-02-01", + employment_date: "2016-06-10", + return_by: "2017-03-01", + display_address: displayAddress, + region_code: regionCode, + language_code: languageCode, + account_service_url: "http://localhost:8000", + }; + } + + const oPayload = payload; if (includeLogoutUrl) { oPayload.account_service_log_out_url = "http://localhost:8000"; @@ -156,3 +175,37 @@ export function generateToken( return token; }); } + +function getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr) { + let surveyMetadata = {}; + + if (theme === "social") { + surveyMetadata = { + data: { + case_ref: "1000000000000001", + questionnaire_id: "1000000000000001", + }, + receipting_keys: ["questionnaire_id"], + }; + } else { + surveyMetadata = { + survey_metadata: { + data: { + user_id: userId, + display_address: displayAddress, + ru_ref: "12346789012A", + period_id: periodId, + period_str: periodStr, + ref_p_start_date: "2017-01-01", + ref_p_end_date: "2017-02-01", + employment_date: "2016-06-10", + return_by: "2017-03-01", + ru_name: "Apple", + trad_as: "Apple", + }, + }, + }; + } + + return surveyMetadata; +} diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index ad52461a15..ff0aee0b0a 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -8,7 +8,7 @@ import ThankYouSubmitPage from "../generated_pages/thank_you/submit.page"; describe("Thank You Social", () => { describe("Given I launch a social themed questionnaire", () => { beforeEach(() => { - browser.openQuestionnaire("test_theme_social.json"); + browser.openQuestionnaire("test_theme_social.json", { version: "v2", theme: "social" }); }); it("When I navigate to the thank you page, Then I should see social theme content", () => { diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index edc15ed7c4..81040a547c 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -201,6 +201,8 @@ exports.config = { async function ( schema, { + version = "v1", + theme = "default", userId = JwtHelper.getRandomString(10), collectionId = JwtHelper.getRandomString(10), responseId = JwtHelper.getRandomString(16), @@ -208,11 +210,12 @@ exports.config = { periodStr = "May 2016", region = "GB-ENG", language = "en", - sexualIdentity = false, includeLogoutUrl = false, } = {} ) { const token = await JwtHelper.generateToken(schema, { + version, + theme, userId, collectionId, responseId, @@ -220,7 +223,6 @@ exports.config = { periodStr, regionCode: region, languageCode: language, - sexualIdentity, includeLogoutUrl, }); this.url(`/session?token=${token}`); diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index c272cf363f..45583eea66 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -3,6 +3,7 @@ from sdc.crypto.encrypter import encrypt +from app.authentication.auth_payload_version import AuthPayloadVersion from app.keys import KEY_PURPOSE_AUTHENTICATION ACCOUNT_SERVICE_URL = "http://upstream.url" @@ -26,6 +27,45 @@ "display_address": "68 Abingdon Road, Goathill", } +PAYLOAD_V2_BUSINESS = { + "version": AuthPayloadVersion.V2.value, + "survey_metadata": { + "data": { + "user_id": "integration-test", + "period_str": "April 2016", + "period_id": "201604", + "ru_ref": "123456789012A", + "ru_name": "Integration Testing", + "ref_p_start_date": "2016-04-01", + "ref_p_end_date": "2016-04-30", + "trad_as": "Integration Tests", + "employment_date": "1983-06-02", + "display_address": "68 Abingdon Road, Goathill", + } + }, + "collection_exercise_sid": "789", + "response_id": "1234567890123456", + "language_code": "en", + "roles": [], + "account_service_url": ACCOUNT_SERVICE_URL, +} + +PAYLOAD_V2_SOCIAL = { + "version": AuthPayloadVersion.V2.value, + "survey_metadata": { + "data": { + "case_ref": "1000000000000001", + "questionnaire_id": str(uuid4()), + }, + "receipting_keys": ["questionnaire_id"], + }, + "collection_exercise_sid": "789", + "response_id": "1234567890123456", + "language_code": "en", + "roles": [], + "account_service_url": ACCOUNT_SERVICE_URL, +} + class TokenGenerator: def __init__(self, key_store, upstream_kid, sr_public_kid): @@ -34,10 +74,13 @@ def __init__(self, key_store, upstream_kid, sr_public_kid): self._sr_public_kid = sr_public_kid @staticmethod - def _get_payload_with_params(schema_name, schema_url=None, **extra_payload): - payload_vars = PAYLOAD.copy() + def _get_payload_with_params( + schema_name=None, schema_url=None, payload=PAYLOAD, **extra_payload + ): # pylint: disable=dangerous-default-value + payload_vars = payload.copy() payload_vars["tx_id"] = str(uuid4()) - payload_vars["schema_name"] = schema_name + if schema_name: + payload_vars["schema_name"] = schema_name if schema_url: payload_vars["schema_url"] = schema_url @@ -52,31 +95,68 @@ def _get_payload_with_params(schema_name, schema_url=None, **extra_payload): return payload_vars def create_token(self, schema_name, **extra_payload): - payload_vars = self._get_payload_with_params(schema_name, None, **extra_payload) + payload_vars = self._get_payload_with_params( + schema_name=schema_name, schema_url=None, **extra_payload + ) return self.generate_token(payload_vars) + def create_token_v2(self, schema_name, theme="default", **extra_payload): + payload_for_theme = ( + PAYLOAD_V2_SOCIAL if theme == "social" else PAYLOAD_V2_BUSINESS + ) + payload = self._get_payload_with_params( + schema_name=schema_name, payload=payload_for_theme, **extra_payload + ) + + return self.generate_token(payload) + + def create_token_invalid_version(self, schema_name, **extra_payload): + payload = self._get_payload_with_params( + schema_name=schema_name, payload=PAYLOAD_V2_BUSINESS, **extra_payload + ) + + payload["version"] = "v3" + + return self.generate_token(payload) + def create_token_without_jti(self, schema_name, **extra_payload): - payload_vars = self._get_payload_with_params(schema_name, None, **extra_payload) + payload_vars = self._get_payload_with_params( + schema_name=schema_name, schema_url=None, **extra_payload + ) del payload_vars["jti"] return self.generate_token(payload_vars) def create_token_without_case_id(self, schema_name, **extra_payload): - payload_vars = self._get_payload_with_params(schema_name, None, **extra_payload) + payload_vars = self._get_payload_with_params( + schema_name=schema_name, schema_url=None, **extra_payload + ) del payload_vars["case_id"] return self.generate_token(payload_vars) def create_token_without_trad_as(self, schema_name, **extra_payload): - payload_vars = self._get_payload_with_params(schema_name, None, **extra_payload) + payload_vars = self._get_payload_with_params( + schema_name=schema_name, schema_url=None, **extra_payload + ) del payload_vars["trad_as"] return self.generate_token(payload_vars) + def create_token_v2_social_token_invalid_receipting_key( + self, schema_name, **extra_payload + ): + payload_vars = self._get_payload_with_params( + schema_name=schema_name, payload=PAYLOAD_V2_SOCIAL, **extra_payload + ) + del payload_vars["survey_metadata"]["data"]["questionnaire_id"] + + return self.generate_token(payload_vars) + def create_token_with_schema_url(self, schema_name, schema_url, **extra_payload): payload_vars = self._get_payload_with_params( - schema_name, schema_url, **extra_payload + schema_name=schema_name, schema_url=schema_url, **extra_payload ) return self.generate_token(payload_vars) diff --git a/tests/integration/individual_response/test_individual_response.py b/tests/integration/individual_response/test_individual_response.py index 5439f133bc..5915332833 100644 --- a/tests/integration/individual_response/test_individual_response.py +++ b/tests/integration/individual_response/test_individual_response.py @@ -22,7 +22,11 @@ def setUp(self): self.DUMMY_MOBILE_NUMBER = "07700900258" super().setUp() - self.launchSurvey("test_individual_response", region_code="GB-ENG") + + self.launchSurvey( + schema_name="test_individual_response", + region_code="GB-ENG", + ) @property def individual_section_link(self): diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 3a9f136de1..1f47e831ed 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -53,26 +53,21 @@ def test_app(self): def _set_up_app(self, setting_overrides=None): self._ds = patch("app.setup.datastore.Client", MockDatastore) self._ds.start() - self._redis = patch("app.setup.redis.Redis", fakeredis.FakeStrictRedis) self._redis.start() - configure_logging() - overrides = { "EQ_ENABLE_HTML_MINIFY": False, "EQ_SUBMISSION_CONFIRMATION_BACKEND": "log", } if setting_overrides: - overrides = overrides | setting_overrides - + overrides |= setting_overrides with patch( "google.auth._default._get_explicit_environ_credentials", return_value=(Mock(), "test-project-id"), ): self._application = create_app(overrides) - self._key_store = KeyStore( { "keys": { @@ -129,29 +124,31 @@ def launchSurvey(self, schema_name="test_dates", **payload_kwargs): token = self.token_generator.create_token( schema_name=schema_name, **payload_kwargs ) - self.get("/session?token=" + token) - def dumpAnswers(self): + self.get(f"/session?token={token}") - self.get("/dump/answers") + def launchSurveyV2( + self, theme="default", schema_name="test_dates", **payload_kwargs + ): + """ + Launch a survey as an authenticated user and follow re-directs + :param schema_name: The name of the schema to load + """ + token = self.token_generator.create_token_v2( + theme=theme, schema_name=schema_name, **payload_kwargs + ) - # Then I get a 200 OK response - self.assertStatusOK() + self.get(f"/session?token={token}") - # And the JSON response contains the data I submitted - dump_answers = json_loads(self.getResponseData()) - return dump_answers + def dumpAnswers(self): + self.get("/dump/answers") + self.assertStatusOK() + return json_loads(self.getResponseData()) def dumpSubmission(self): - self.get("/dump/submission") - - # Then I get a 200 OK response self.assertStatusOK() - - # And the JSON response contains the data I submitted - dump_submission = json_loads(self.getResponseData()) - return dump_submission + return json_loads(self.getResponseData()) def dump_debug(self): self.get("/dump/debug") diff --git a/tests/integration/questionnaire/test_questionnaire_submission.py b/tests/integration/questionnaire/test_questionnaire_submission.py index 77ad39d143..1ebedc1f68 100644 --- a/tests/integration/questionnaire/test_questionnaire_submission.py +++ b/tests/integration/questionnaire/test_questionnaire_submission.py @@ -1,10 +1,15 @@ from unittest.mock import Mock +from httmock import HTTMock, urlmatch + +from app.utilities.schema import get_schema_path_map from tests.integration.integration_test_case import IntegrationTestCase from tests.integration.questionnaire import HUB_URL_PATH, THANK_YOU_URL_PATH SUBMIT_URL_PATH = "/questionnaire/submit" +SCHEMA_PATH_MAP = get_schema_path_map(include_test_schemas=True) + class SubmissionTestCase(IntegrationTestCase): @property @@ -52,6 +57,34 @@ def test_unsuccessful_submission(self): self.assertInUrl(SUBMIT_URL_PATH) +class TestQuestionnaireSubmissionSchemaURL(SubmissionTestCase): + def test_login_token_with_schema_url_should_redirect_to_survey(self): + schema_url = "http://eq-survey-register.url/my-test-schema" + + # Given + token = self.token_generator.create_token_with_schema_url( + "test_textarea", schema_url + ) + + # When + with HTTMock(self.schema_url_mock): + self.get(url=f"/session?token={token}") + + self.assertStatusOK() + self.assertInUrl("/questionnaire") + self.post() + self.post() + self.assertInUrl(THANK_YOU_URL_PATH) + + @staticmethod + @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") + def schema_url_mock(_url, _request): + schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] + + with open(schema_path, encoding="utf8") as json_data: + return json_data.read() + + class TestQuestionnaireSubmissionHub(SubmissionTestCase): def _launch_and_submit_questionnaire(self): # Launch questionnaire diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 1992a64c22..605238b42f 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -107,7 +107,11 @@ def test_401_theme_default_cookie_exists(self): def test_401_theme_social_cookie_exists(self): # Given - self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.launchSurveyV2( + schema_name="test_theme_social", + theme="social", + account_service_url=SOCIAL_URL, + ) self.assertInUrl("/questionnaire/radio/") # When @@ -160,7 +164,11 @@ def test_403_theme_default_cookie_exists(self): def test_403_theme_social_cookie_exists(self): # Given - self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.launchSurveyV2( + schema_name="test_theme_social", + theme="social", + account_service_url=SOCIAL_URL, + ) # When cookie = self.getUrlAndCookie("/dump/debug") @@ -210,7 +218,11 @@ def test_404_theme_default_cookie_exists(self): def test_404_theme_social_cookie_exists(self): # Given - self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.launchSurveyV2( + schema_name="test_theme_social", + theme="social", + account_service_url=SOCIAL_URL, + ) # When cookie = self.getUrlAndCookie("/abc123") @@ -333,7 +345,11 @@ def test_submission_failed_theme_social_cookie_exists(self): submitter.send_message = Mock(return_value=False) # When - self.launchSurvey("test_theme_social", account_service_url=SOCIAL_URL) + self.launchSurveyV2( + schema_name="test_theme_social", + theme="social", + account_service_url=SOCIAL_URL, + ) self.post() self.post() self.post() diff --git a/tests/integration/routes/test_feedback.py b/tests/integration/routes/test_feedback.py index 2fb4e89a08..7147e8bd95 100644 --- a/tests/integration/routes/test_feedback.py +++ b/tests/integration/routes/test_feedback.py @@ -253,6 +253,37 @@ def test_feedback_submission(self): ) self.assertInUrl("/submitted/feedback/sent") + def test_feedback_submission_v2_business(self): + # Given I submit the email confirmation form + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") + self.post({"answer_id": "Yes"}) + self.post() + self.post({"email": "email@example.com"}) + + # When I request the feedback page + self.get("/submitted/feedback/send") + + # Then I am able to submit feedback + self.post( + {"feedback-type": "Page design and structure", "feedback-text": "Feedback"} + ) + self.assertInUrl("/submitted/feedback/sent") + + def test_feedback_submission_v2_social(self): + # Given I submit the email confirmation form + self.launchSurveyV2(schema_name="test_theme_social", theme="social") + self.post({"radio-answer": "Bacon"}) + self.post() + + # When I request the feedback page + self.get("/submitted/feedback/send") + + # Then I am able to submit feedback + self.post( + {"feedback-type": "Page design and structure", "feedback-text": "Feedback"} + ) + self.assertInUrl("/submitted/feedback/sent") + def test_feedback_call_to_action_visible_on_email_confirmation(self): # Given I complete the survey self.launchSurvey("test_feedback_email_confirmation") diff --git a/tests/integration/routes/test_thank_you.py b/tests/integration/routes/test_thank_you.py index c9fa87f69b..3f373c8bc3 100644 --- a/tests/integration/routes/test_thank_you.py +++ b/tests/integration/routes/test_thank_you.py @@ -105,7 +105,7 @@ def test_back_to_surveys_link_on_thank_you(self): self.assertInBody(ACCOUNT_SERVICE_TODO_PATH) def test_back_to_surveys_link_not_on_thank_you_theme_social(self): - self.launchSurvey("test_theme_social") + self.launchSurveyV2(schema_name="test_theme_social", theme="social") self.post() self.post() diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index 90cf41e1c2..819ba25817 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -41,6 +41,30 @@ def test_login_with_valid_token_should_redirect_to_survey(self): self.assertStatusOK() self.assertInUrl("/questionnaire") + def test_login_with_valid_v2_business_token_should_redirect_to_survey(self): + # Given + token = self.token_generator.create_token_v2(schema_name="test_checkbox") + + # When + self.get(url=f"/session?token={token}") + + # Then + self.assertStatusOK() + self.assertInUrl("/questionnaire") + + def test_login_with_valid_v2_social_token_should_redirect_to_survey(self): + # Given + token = self.token_generator.create_token_v2( + schema_name="test_theme_social", theme="social" + ) + + # When + self.get(url=f"/session?token={token}") + + # Then + self.assertStatusOK() + self.assertInUrl("/questionnaire") + def test_login_with_token_twice_is_unauthorised_when_same_jti_provided(self): # Given token = self.token_generator.create_token("test_checkbox") @@ -70,6 +94,26 @@ def test_login_with_valid_token_no_schema_name(self): # Then self.assertStatusForbidden() + def test_login_with_valid_v2_business_token_no_schema_name(self): + # Given + token = self.token_generator.create_token_v2(schema_name="") + + # When + self.get(url=f"/session?token={token}") + + # Then + self.assertStatusForbidden() + + def test_login_with_valid_v2_social_token_no_schema_name(self): + # Given + token = self.token_generator.create_token_v2(schema_name="", theme="social") + + # When + self.get(url=f"/session?token={token}") + + # Then + self.assertStatusForbidden() + def test_http_head_request_to_login_returns_successfully_and_get_still_works(self): # Given token = self.token_generator.create_token("test_checkbox") @@ -104,6 +148,13 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() + def test_login_with_invalid_version_should_be_forbidden(self): + token = self.token_generator.create_token_invalid_version("test_checkbox") + + self.get(url=f"/session?token={token}") + + self.assertStatusForbidden() + def test_login_token_with_schema_url_should_redirect_to_survey(self): schema_url = "http://eq-survey-register.url/my-test-schema" @@ -204,7 +255,7 @@ def test_http_head_request_to_login_returns_successfully_and_post_still_works(se token = self.token_generator.create_token("test_checkbox") # When - self.head("/session?token=" + token) + self.head(f"/session?token={token}") self.post(url=f"/session?token={token}") # Then @@ -233,6 +284,40 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() + def test_v2_business_login_with_invalid_questionnaire_claims_should_be_forbidden( + self, + ): + # flag_1 should be a boolean + token = self.token_generator.create_token_v2( + "test_metadata_routing", flag_1=123 + ) + + self.post(url=f"/session?token={token}") + + self.assertStatusForbidden() + + def test_v2_social_login_with_invalid_questionnaire_claims_should_be_forbidden( + self, + ): + token = self.token_generator.create_token_v2( + schema_name="test_address", theme="social" + ) + + self.post(url=f"/session?token={token}") + + self.assertStatusForbidden() + + def test_v2_social_login_with_invalid_receipting_key_should_be_forbidden(self): + token = ( + self.token_generator.create_token_v2_social_token_invalid_receipting_key( + "test_theme_social" + ) + ) + + self.post(url=f"/session?token={token}") + + self.assertStatusForbidden() + def test_login_token_with_schema_url_should_redirect_to_survey(self): schema_url = "http://eq-survey-register.url/my-test-schema" diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index 8e76b37cad..92a11ff9fc 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -96,7 +96,7 @@ def test_links_in_header_when_no_session_but_cookie_exists(self): def test_links_in_header_when_no_session_but_cookie_exists_theme_social(self): # Given - self.launchSurvey("test_theme_social") + self.launchSurveyV2(schema_name="test_theme_social", theme="social") self.assertInUrl("/questionnaire/radio/") self.saveAndSignOut() @@ -126,7 +126,7 @@ def test_links_not_in_header_when_no_session(self): def test_links_not_in_header_when_valid_session_theme_social(self): # Given - self.launchSurvey("test_theme_social") + self.launchSurveyV2(schema_name="test_theme_social", theme="social") # When self.assertStatusOK() @@ -167,7 +167,7 @@ def test_links_not_in_header_when_no_session(self): def test_links_not_in_header_when_valid_session_theme_social(self): # Given - self.launchSurvey("test_theme_social") + self.launchSurveyV2(schema_name="test_theme_social", theme="social") self.post() self.post() From f52cf9620a8897f51ce5ac2140662b414c16c0da Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 3 Nov 2022 12:25:19 +0000 Subject: [PATCH 095/567] Support for 'health' theme within runner (#948) --- app/helpers/template_helpers.py | 2 +- schemas/test/en/test_theme_health.json | 71 ++++++++++++++++++++++ tests/app/helpers/test_template_helpers.py | 6 +- 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 schemas/test/en/test_theme_health.json diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 3f3d830490..b7aec7d05a 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -189,7 +189,7 @@ def survey_config_mapping( survey_type_to_config: dict[SurveyType, Type[SurveyConfig]] = { SurveyType.DEFAULT: BusinessSurveyConfig, SurveyType.BUSINESS: BusinessSurveyConfig, - SurveyType.HEALTH: SurveyConfig, + SurveyType.HEALTH: SocialSurveyConfig, SurveyType.SOCIAL: SocialSurveyConfig, SurveyType.NORTHERN_IRELAND: NorthernIrelandBusinessSurveyConfig, SurveyType.CENSUS: ( diff --git a/schemas/test/en/test_theme_health.json b/schemas/test/en/test_theme_health.json new file mode 100644 index 0000000000..d0eaa524fa --- /dev/null +++ b/schemas/test/en/test_theme_health.json @@ -0,0 +1,71 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Health Survey", + "theme": "health", + "description": "A questionnaire to demo the health survey theme", + "metadata": [ + { + "name": "case_ref", + "type": "string" + }, + { + "name": "questionnaire_id", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "Health Theme Test" + } + ] + } + ] +} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index d943ecfefd..70eaa54556 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -576,8 +576,8 @@ def test_account_service_log_out_url_context( (SurveyType.DEFAULT, "cy", SurveyConfig), (SurveyType.BUSINESS, "en", BusinessSurveyConfig), (SurveyType.BUSINESS, "cy", BusinessSurveyConfig), - (SurveyType.HEALTH, "en", SurveyConfig), - (SurveyType.HEALTH, "cy", SurveyConfig), + (SurveyType.HEALTH, "en", SocialSurveyConfig), + (SurveyType.HEALTH, "cy", SocialSurveyConfig), (SurveyType.SOCIAL, "en", SocialSurveyConfig), (SurveyType.SOCIAL, "cy", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NorthernIrelandBusinessSurveyConfig), @@ -706,7 +706,7 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte [ (SurveyType.DEFAULT, "en", "ONS Business Surveys"), (SurveyType.BUSINESS, "en", "ONS Business Surveys"), - (SurveyType.HEALTH, "en", None), + (SurveyType.HEALTH, "en", "ONS Social Surveys"), (SurveyType.SOCIAL, "en", "ONS Social Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), (SurveyType.CENSUS, "en", "Census 2021"), From 1330a0312d78bef201a32f73e1e07c81c36eb320 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:31:34 +0000 Subject: [PATCH 096/567] Rename questionnaire_id to qid (#953) --- app/routes/session.py | 6 +++--- schemas/test/en/test_theme_health.json | 2 +- schemas/test/en/test_theme_social.json | 2 +- tests/app/parser/conftest.py | 4 ++-- tests/app/parser/test_metadata_parser.py | 2 +- tests/app/submitter/test_submitter.py | 8 ++++---- tests/app/views/handlers/test_feedback_upload.py | 4 ++-- tests/functional/jwt_helper.js | 4 ++-- tests/integration/create_token.py | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/routes/session.py b/app/routes/session.py index 6224a9d904..d48c77cf7c 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -71,11 +71,11 @@ def login(): runner_claims["survey_metadata"]["data"] = questionnaire_claims ru_ref = questionnaire_claims.get("ru_ref") - questionnaire_id = questionnaire_claims.get("questionnaire_id") + qid = questionnaire_claims.get("qid") claims = runner_claims else: ru_ref = runner_claims["ru_ref"] - questionnaire_id = None + qid = None claims = {**runner_claims, **questionnaire_claims} tx_id = claims["tx_id"] @@ -89,7 +89,7 @@ def login(): "schema_name": metadata.schema_name, "schema_url": metadata.schema_url, "ru_ref": ru_ref, - "questionnaire_id": questionnaire_id, + "qid": qid, }.items() if value } diff --git a/schemas/test/en/test_theme_health.json b/schemas/test/en/test_theme_health.json index d0eaa524fa..2a3c3ec8b0 100644 --- a/schemas/test/en/test_theme_health.json +++ b/schemas/test/en/test_theme_health.json @@ -13,7 +13,7 @@ "type": "string" }, { - "name": "questionnaire_id", + "name": "qid", "type": "string" } ], diff --git a/schemas/test/en/test_theme_social.json b/schemas/test/en/test_theme_social.json index c860e574a3..b80c137dec 100644 --- a/schemas/test/en/test_theme_social.json +++ b/schemas/test/en/test_theme_social.json @@ -13,7 +13,7 @@ "type": "string" }, { - "name": "questionnaire_id", + "name": "qid", "type": "string" } ], diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index 0898d6c668..d408e09103 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -119,13 +119,13 @@ def fake_metadata_full_v2_social(): """ fake_survey_metadata_claims = { "case_ref": "1000000000000001", - "questionnaire_id": "2000000000000002", + "qid": "2000000000000002", } metadata = fake_metadata_runner_v2() metadata["survey_metadata"]["data"] = fake_survey_metadata_claims - metadata["survey_metadata"]["receipting_keys"] = ["questionnaire_id"] + metadata["survey_metadata"]["receipting_keys"] = ["qid"] return metadata diff --git a/tests/app/parser/test_metadata_parser.py b/tests/app/parser/test_metadata_parser.py index e1e561332b..74121bfc2c 100644 --- a/tests/app/parser/test_metadata_parser.py +++ b/tests/app/parser/test_metadata_parser.py @@ -417,7 +417,7 @@ def test_valid_v2_social_claims(): def test_invalid_v2_social_claims_missing_receipting_key_raises_error(): metadata = get_metadata_social() - del metadata["survey_metadata"]["data"]["questionnaire_id"] + del metadata["survey_metadata"]["data"]["qid"] with pytest.raises(ValidationError): validate_runner_claims_v2(metadata) diff --git a/tests/app/submitter/test_submitter.py b/tests/app/submitter/test_submitter.py index d700d8a48d..0e75ac607e 100644 --- a/tests/app/submitter/test_submitter.py +++ b/tests/app/submitter/test_submitter.py @@ -217,7 +217,7 @@ def test_gcs_submitter_adds_additional_keys_to_metadata_when_set(patch_gcs_clien # When gcs_submitter.send_message( - message={"test_data"}, tx_id="123", case_id="456", **{"questionnaire_id": "1"} + message={"test_data"}, tx_id="123", case_id="456", **{"qid": "1"} ) # Then @@ -227,7 +227,7 @@ def test_gcs_submitter_adds_additional_keys_to_metadata_when_set(patch_gcs_clien assert blob.metadata == { "tx_id": "123", "case_id": "456", - "questionnaire_id": "1", + "qid": "1", } @@ -239,7 +239,7 @@ def test_gcs_feedback_submitter_adds_additional_keys_to_metadata_when_set( # When gcs_submitter.upload( payload=json_dumps({"some-data": "some-value"}), - metadata={"tx_id": "123", "case_id": "456", "questionnaire_id": "1"}, + metadata={"tx_id": "123", "case_id": "456", "qid": "1"}, ) # Then @@ -249,7 +249,7 @@ def test_gcs_feedback_submitter_adds_additional_keys_to_metadata_when_set( assert blob.metadata == { "tx_id": "123", "case_id": "456", - "questionnaire_id": "1", + "qid": "1", } diff --git a/tests/app/views/handlers/test_feedback_upload.py b/tests/app/views/handlers/test_feedback_upload.py index f8806d5e5c..3f4415d265 100644 --- a/tests/app/views/handlers/test_feedback_upload.py +++ b/tests/app/views/handlers/test_feedback_upload.py @@ -177,10 +177,10 @@ def test_feedback_metadata(): def test_feedback_metadata_with_receipting_keys(): - receipting_keys = {"questionnaire_id": "1"} + receipting_keys = {"qid": "1"} feedback_metadata = FeedbackMetadata(case_id, tx_id, **receipting_keys) - expected_metadata = {"case_id": case_id, "tx_id": tx_id, "questionnaire_id": "1"} + expected_metadata = {"case_id": case_id, "tx_id": tx_id, "qid": "1"} assert feedback_metadata() == expected_metadata diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index e8758f6a2d..678b7d8d8d 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -183,9 +183,9 @@ function getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr) { surveyMetadata = { data: { case_ref: "1000000000000001", - questionnaire_id: "1000000000000001", + qid: "1000000000000001", }, - receipting_keys: ["questionnaire_id"], + receipting_keys: ["qid"], }; } else { surveyMetadata = { diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 45583eea66..1c88bf92bb 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -55,9 +55,9 @@ "survey_metadata": { "data": { "case_ref": "1000000000000001", - "questionnaire_id": str(uuid4()), + "qid": str(uuid4()), }, - "receipting_keys": ["questionnaire_id"], + "receipting_keys": ["qid"], }, "collection_exercise_sid": "789", "response_id": "1234567890123456", @@ -150,7 +150,7 @@ def create_token_v2_social_token_invalid_receipting_key( payload_vars = self._get_payload_with_params( schema_name=schema_name, payload=PAYLOAD_V2_SOCIAL, **extra_payload ) - del payload_vars["survey_metadata"]["data"]["questionnaire_id"] + del payload_vars["survey_metadata"]["data"]["qid"] return self.generate_token(payload_vars) From 0e6f467b713bbe7e8068b1c643da5757e0e77fa1 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 4 Nov 2022 12:50:07 +0000 Subject: [PATCH 097/567] Remove case_ref from Social/Health schemas (#952) --- schemas/test/en/test_theme_health.json | 4 ---- schemas/test/en/test_theme_social.json | 4 ---- 2 files changed, 8 deletions(-) diff --git a/schemas/test/en/test_theme_health.json b/schemas/test/en/test_theme_health.json index 2a3c3ec8b0..2319c32624 100644 --- a/schemas/test/en/test_theme_health.json +++ b/schemas/test/en/test_theme_health.json @@ -8,10 +8,6 @@ "theme": "health", "description": "A questionnaire to demo the health survey theme", "metadata": [ - { - "name": "case_ref", - "type": "string" - }, { "name": "qid", "type": "string" diff --git a/schemas/test/en/test_theme_social.json b/schemas/test/en/test_theme_social.json index b80c137dec..07562d2570 100644 --- a/schemas/test/en/test_theme_social.json +++ b/schemas/test/en/test_theme_social.json @@ -8,10 +8,6 @@ "theme": "social", "description": "A questionnaire to demo the social survey theme", "metadata": [ - { - "name": "case_ref", - "type": "string" - }, { "name": "qid", "type": "string" From f610ed62f17fb5aba7507e46427e8d74f6ef0927 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 9 Nov 2022 13:27:38 +0000 Subject: [PATCH 098/567] Evaluate calculated summary is complete when returning to a calculated summary (#954) --- app/questionnaire/router.py | 23 ++- ...alculated_summary_dependent_questions.json | 167 ++++++++++++++++++ tests/app/questionnaire/test_router.py | 54 +++++- 3 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 schemas/test/en/test_calculated_summary_dependent_questions.json diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index fc86455c4a..379478b51e 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -219,15 +219,20 @@ def _get_return_to_location_url( if not return_to: return None - if return_to == "calculated-summary": - if return_to_block_id in routing_path: - return url_for( - "questionnaire.block", - list_item_id=location.list_item_id, - block_id=return_to_block_id, - _anchor=return_to_answer_id, - ) - return None + if return_to == "calculated-summary" and self.can_access_location( + Location( + block_id=return_to_block_id, + section_id=location.section_id, + list_item_id=location.list_item_id, + ), + routing_path, + ): + return url_for( + "questionnaire.block", + list_item_id=location.list_item_id, + block_id=return_to_block_id, + _anchor=return_to_answer_id, + ) if is_section_complete is None: is_section_complete = self._progress_store.is_section_complete( diff --git a/schemas/test/en/test_calculated_summary_dependent_questions.json b/schemas/test/en/test_calculated_summary_dependent_questions.json new file mode 100644 index 0000000000..85c0964a49 --- /dev/null +++ b/schemas/test/en/test_calculated_summary_dependent_questions.json @@ -0,0 +1,167 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to demo Calculated Summary", + "description": "A schema to showcase Calculated Summary with dependent questions.", + "questionnaire_flow": { + "type": "Linear", + "options": {} + }, + "sections": [ + { + "id": "default-section", + "title": "Section 1", + "summary": { + "show_on_completion": false, + "collapsible": false + }, + "show_on_hub": true, + "groups": [ + { + "id": "group-1", + "blocks": [ + { + "id": "block-1", + "type": "Question", + "question": { + "id": "question-1", + "title": "How much did you spend on food?", + "type": "General", + "answers": [ + { + "id": "answer-1", + "mandatory": true, + "type": "Currency", + "label": "Money spent on food", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-2", + "type": "Question", + "question": { + "id": "question-2", + "title": "Of the money spent on food, how much did you spend on vegetables?", + "type": "General", + "answers": [ + { + "id": "answer-2", + "mandatory": true, + "type": "Currency", + "label": "Money spent on vegetables", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "maximum": { + "value": { + "identifier": "answer-1", + "source": "answers" + }, + "exclusive": false + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-3", + "type": "Question", + "question": { + "id": "question-3", + "title": "How much did you spend on clothes?", + "type": "General", + "answers": [ + { + "id": "answer-3", + "mandatory": true, + "type": "Currency", + "label": "Money spent on clothes", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-4", + "type": "Question", + "question": { + "id": "question-4", + "title": "Of the money spent on clothes, how much did you spend on shoes?", + "type": "General", + "answers": [ + { + "id": "answer-4", + "mandatory": true, + "type": "Currency", + "label": "Money spent on shoes", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "maximum": { + "value": { + "identifier": "answer-3", + "source": "answers" + }, + "exclusive": false + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + + { + "id": "calculated-summary-block", + "type": "CalculatedSummary", + "title": "We have calculated your total spend to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": ["answer-1", "answer-3"], + "title": "Total capital expenditure" + } + } + ] + } + ] + } + ], + "theme": "default", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ] +} diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index bce950d3a9..57e75771ca 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -511,13 +511,12 @@ def test_return_to_calculated_summary(self): self.schema = load_schema_from_name("test_calculated_summary") current_location = Location( - section_id="default-section", block_id="fifth-number-block" + section_id="default-section", block_id="second-number-block" ) routing_path = RoutingPath( [ - "fifth-number-block", - "sixth-number-block", + "second-number-block", "currency-total-playback-skipped-fourth", ], section_id="default-section", @@ -539,7 +538,50 @@ def test_return_to_calculated_summary(self): "questionnaire.block", list_item_id=expected_location.list_item_id, block_id=expected_location.block_id, - _anchor="first-number-answer", + return_to="calculated-summary", + return_to_answer_id="first-number-answer", + return_to_block_id="currency-total-playback-skipped-fourth", + ) + + assert expected_location_url == next_location_url + + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary_not_on_allowable_path(self): + self.schema = load_schema_from_name( + "test_calculated_summary_dependent_questions" + ) + + current_location = Location(section_id="default-section", block_id="block-3") + + routing_path = RoutingPath( + [ + "block-3", + "block-4", + "calculated-summary-block", + ], + section_id="default-section", + ) + + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to_answer_id="answer-3", + return_to="calculated-summary", + return_to_block_id="calculated-summary-block", + ) + + expected_location = Location( + section_id="default-section", + block_id="block-4", + ) + + expected_location_url = url_for( + "questionnaire.block", + list_item_id=expected_location.list_item_id, + block_id=expected_location.block_id, + return_to="calculated-summary", + return_to_answer_id="answer-3", + return_to_block_id="calculated-summary-block", ) assert expected_location_url == next_location_url @@ -736,13 +778,11 @@ def test_return_to_calculated_summary(self): self.schema = load_schema_from_name("test_calculated_summary") current_location = Location( - section_id="default-section", block_id="fifth-number-block" + section_id="default-section", block_id="second-number-block" ) routing_path = RoutingPath( [ - "fifth-number-block", - "sixth-number-block", "currency-total-playback-skipped-fourth", ], section_id="default-section", From 38ffee37ad2f510c340846440dc0eb0745010b7e Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:17:51 +0000 Subject: [PATCH 099/567] Schemas v3.19.0 (#956) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0f734a422c..3282124eea 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.18.1 +v3.19.0 From 34253d1974ab7e10252b14040024951e2cdd4336 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 16 Nov 2022 10:31:23 +0000 Subject: [PATCH 100/567] Schemas v3.20.0 (#958) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3282124eea..c383e22aa8 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.19.0 +v3.20.0 From dce02451ea3d02a20b859197530a5fc0071bf6cd Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:27:45 +0000 Subject: [PATCH 101/567] Handling double submission error (#957) Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/submitter/submitter.py | 13 ++++++- tests/app/submitter/test_submitter.py | 56 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/app/submitter/submitter.py b/app/submitter/submitter.py index 6094bdaa25..958d17b812 100644 --- a/app/submitter/submitter.py +++ b/app/submitter/submitter.py @@ -1,6 +1,7 @@ from typing import Mapping, Optional, Union from uuid import uuid4 +from google.api_core.exceptions import Forbidden from google.cloud import storage # type: ignore from google.cloud.storage.retry import DEFAULT_RETRY from pika import BasicProperties, BlockingConnection, URLParameters @@ -54,8 +55,18 @@ def send_message( # DEFAULT_RETRY is not idempotent. # However, this behaviour was deemed acceptable for our use case. - blob.upload_from_string(str(message).encode("utf8"), retry=DEFAULT_RETRY) + try: + blob.upload_from_string(str(message).encode("utf8"), retry=DEFAULT_RETRY) + except Forbidden as e: + # If an object exists then the GCS Client will attempt to delete the existing object before reuploading. + # However, in an attempt to reduce duplicate receipts, runner does not have a delete permission. + # The first version of the object is acceptable as it is an extreme edge case for two submissions to contain different response data. + if "storage.objects.delete" not in e.message: + raise + logger.info( + "Questionnaire submission exists, ignoring delete operation error" + ) return True diff --git a/tests/app/submitter/test_submitter.py b/tests/app/submitter/test_submitter.py index 0e75ac607e..e18027a66e 100644 --- a/tests/app/submitter/test_submitter.py +++ b/tests/app/submitter/test_submitter.py @@ -1,6 +1,8 @@ import uuid import pytest +from google.api_core.exceptions import Forbidden +from google.cloud.storage import Blob from pika.exceptions import AMQPError, NackError from app.submitter import GCSFeedbackSubmitter, GCSSubmitter, RabbitMQSubmitter @@ -336,3 +338,57 @@ def test_gcs_feedback_submitter_uploads_feedback(patch_gcs_client): b'"form_type": "H", "language_code": "cy", "region_code": "GB-ENG", "tx_id": "12345"}' ) assert feedback_upload is True + + +def test_double_submission_passes_when_delete_operation_error( + patch_gcs_client, gcs_blob_delete_forbidden +): # pylint: disable=redefined-outer-name + # Given + gcs_submitter = GCSSubmitter(bucket_name="test_bucket") + + # When + bucket = patch_gcs_client.return_value.get_bucket.return_value + bucket.blob.return_value = gcs_blob_delete_forbidden + published = gcs_submitter.send_message( + message={"test_data"}, tx_id="123", case_id="456" + ) + # Then + assert published + + +def test_double_submission_is_forbidden_when_not_delete_operation_error( + patch_gcs_client, gcs_blob_create_forbidden +): # pylint: disable=redefined-outer-name + + # Given + gcs_submitter = GCSSubmitter(bucket_name="test_bucket") + + # When + bucket = patch_gcs_client.return_value.get_bucket.return_value + bucket.blob.return_value = gcs_blob_create_forbidden + + # Then + with pytest.raises(Forbidden): + gcs_submitter.send_message(message={"test_data"}, tx_id="123", case_id="456") + + +@pytest.fixture +def gcs_blob_create_forbidden(mocker): + blob = Blob(name="test-blob", bucket=mocker.Mock()) + + blob.upload_from_string = mocker.Mock( # pylint: disable=protected-access + side_effect=Forbidden("storage.objects.create") + ) + + return blob + + +@pytest.fixture +def gcs_blob_delete_forbidden(mocker): + blob = Blob(name="test-blob", bucket=mocker.Mock()) + + blob.upload_from_string = mocker.Mock( # pylint: disable=protected-access + side_effect=Forbidden("storage.objects.delete") + ) + + return blob From 5ae114fafa0931ab7a10f20d352ea512f264b533 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:34:07 +0000 Subject: [PATCH 102/567] Schemas v3.21.0 (#959) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c383e22aa8..3fd52f2fcd 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.20.0 +v3.21.0 From c189ccf341ab4127591ed24ab81a673638340457 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 21 Nov 2022 14:16:06 +0000 Subject: [PATCH 103/567] Schemas v3.22.0 (#961) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3fd52f2fcd..0bf0fe3b02 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.21.0 +v3.22.0 From 4ef329ff1a2b0ea6e616edc80d24475fb38c1c81 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 23 Nov 2022 12:57:17 +0000 Subject: [PATCH 104/567] Update schemas to 3.23 (#962) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0bf0fe3b02..b1ef60824c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.22.0 +v3.23.0 From 56ff2f164cc80292128a4fb3972d175005aaf37b Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 24 Nov 2022 10:12:13 +0000 Subject: [PATCH 105/567] Update DS to v60 (#951) --- .design-system-version | 2 +- app/helpers/template_helpers.py | 35 +---- app/survey_config/business_config.py | 14 +- app/survey_config/census_config.py | 13 +- app/survey_config/survey_config.py | 12 +- app/translations/messages.pot | 136 ++++++++---------- app/views/handlers/thank_you.py | 5 +- schemas/test/en/test_benchmark_business.json | 2 +- templates/assets/images/ni-finance-logo.svg | 16 +++ .../assets/images/ni-finance-mobile-logo.svg | 16 +++ templates/layouts/_base.html | 5 +- templates/layouts/configs/_header.html | 40 ++++++ .../configs/_save-sign-out-button.html | 32 ----- tests/app/helpers/conftest.py | 4 - tests/app/helpers/test_template_helpers.py | 110 ++++---------- 15 files changed, 176 insertions(+), 266 deletions(-) create mode 100644 templates/assets/images/ni-finance-logo.svg create mode 100644 templates/assets/images/ni-finance-mobile-logo.svg create mode 100644 templates/layouts/configs/_header.html delete mode 100644 templates/layouts/configs/_save-sign-out-button.html diff --git a/.design-system-version b/.design-system-version index af7d1ce565..46d391bd12 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -58.0.1 +60.0.2 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index b7aec7d05a..712f3345d7 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -6,7 +6,7 @@ from flask import request from flask import session as cookie_session from flask import url_for -from flask_babel import LazyString, get_locale, lazy_gettext +from flask_babel import get_locale, lazy_gettext from flask_login import current_user from app.globals import get_metadata, get_session_store @@ -61,7 +61,6 @@ def context(self) -> dict[str, Any]: "account_service_todo_url": self._survey_config.account_service_todo_url, "contact_us_url": self._survey_config.contact_us_url, "thank_you_url": url_for("post_submission.get_thank_you"), - "page_header": self.page_header_context, "service_links": self.service_links_context, "footer": self.footer_context, "languages": get_languages_context(self._language), @@ -75,6 +74,8 @@ def context(self) -> dict[str, Any]: "google_tag_manager_id": self._google_tag_manager_id, "google_tag_manager_auth": self._google_tag_manager_auth, "survey_type": self._survey_type, + "mastheadLogo": self._survey_config.masthead_logo, + "mastheadLogoMobile": self._survey_config.masthead_logo_mobile, } if self._survey_type: context["cookie_settings_url"] = self._survey_config.cookie_settings_url @@ -115,27 +116,6 @@ def data_layer_context( return self._survey_config.get_data_layer(tx_id=tx_id) - @property - def page_header_context(self) -> dict[str, Union[bool, str, LazyString]]: - context: dict[str, Union[bool, str, LazyString]] = { - "orgLogo": f"{self._survey_config.page_header_logo}", - "orgLogoAlt": f"{self._survey_config.page_header_logo_alt}", - "title": self._survey_title - if self._survey_title and self._survey_type - else "ONS Surveys", - } - - if self._survey_config.title_logo: - context["titleLogo"] = self._survey_config.title_logo - if self._survey_config.title_logo_alt: - context["titleLogoAlt"] = self._survey_config.title_logo_alt - if self._survey_config.custom_header_logo: - context["customHeaderLogo"] = self._survey_config.custom_header_logo - if self._survey_config.mobile_logo: - context["orgMobileLogo"] = self._survey_config.mobile_logo - - return context - @property def footer_context(self) -> dict[str, Any]: context = { @@ -161,15 +141,6 @@ def footer_context(self) -> dict[str, Any]: ): context["legal"] = [{"itemsList": footer_legal_links}] - if ( - self._survey_config.powered_by_logo - or self._survey_config.powered_by_logo_alt - ): - context["poweredBy"] = { - "logo": self._survey_config.powered_by_logo, - "alt": self._survey_config.powered_by_logo_alt, - } - return context @cached_property diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 4630dad954..84bdf1a2b1 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -5,14 +5,13 @@ from flask_babel import lazy_gettext +from app.settings import read_file from app.survey_config.link import HeaderLink, Link from app.survey_config.survey_config import SurveyConfig @dataclass -class BusinessSurveyConfig( - SurveyConfig, -): +class BusinessSurveyConfig(SurveyConfig): survey_title: str = "ONS Business Surveys" footer_links: Iterable[MutableMapping] = field(default_factory=list) footer_legal_links: Iterable[Mapping] = field(default_factory=list) @@ -134,10 +133,7 @@ def _stripped_base_url(self) -> str: @dataclass class NorthernIrelandBusinessSurveyConfig(BusinessSurveyConfig): - - page_header_logo: str = "ni-finance-logo" - page_header_logo_alt: str = lazy_gettext( - "Northern Ireland Department of Finance logo" + masthead_logo: str = read_file("./templates/assets/images/ni-finance-logo.svg") + masthead_logo_mobile: str = read_file( + "./templates/assets/images/ni-finance-logo.svg" ) - mobile_logo: str = "ni-finance-logo-mobile" - custom_header_logo: bool = True diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index 9562277a6a..decf852fdb 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -16,8 +16,6 @@ class CensusSurveyConfig( SurveyConfig, ): - title_logo: str = "census-logo-en" - title_logo_alt: LazyString = lazy_gettext("Census 2021") base_url: str = EN_BASE_URL account_service_log_out_url: str = f"{base_url}/en/start" design_system_theme: str = "census" @@ -88,7 +86,6 @@ def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]] class WelshCensusSurveyConfig( CensusSurveyConfig, ): - title_logo: str = "census-logo-cy" base_url: str = CY_BASE_URL account_service_log_out_url: str = f"{base_url}/en/start" @@ -142,26 +139,20 @@ def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]] return None +# Census and Nisra theme will no longer work as of 23/11/22 +# Theming has been removed from DS in v60 (https://github.com/ONSdigital/eq-questionnaire-runner/pull/951) @dataclass class CensusNISRASurveyConfig( CensusSurveyConfig, ): base_url: str = NIR_BASE_URL account_service_log_out_url: str = base_url - page_header_logo: str = "nisra-logo" - page_header_logo_alt: str = lazy_gettext( - "Northern Ireland Statistics and Research Agency logo" - ) - custom_header_logo: bool = True - mobile_logo: str = "nisra-logo-mobile" copyright_declaration: LazyString = lazy_gettext( "Crown copyright and database rights 2021 NIMA MOU577.501." ) copyright_text: LazyString = lazy_gettext( "Use of address data is subject to the terms and conditions." ) - powered_by_logo: str = "nisra-logo" - powered_by_logo_alt: str = "NISRA - Northern Ireland Statistics and Research Agency" _is_nisra: bool = True def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index fa6c17bc27..d0de9e781b 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -13,10 +13,6 @@ class SurveyConfig: """Valid options for defining survey-based configuration.""" schema: Optional[QuestionnaireSchema] = None - page_header_logo: Optional[str] = "ons-logo-en" - page_header_logo_alt: Optional[LazyString] = lazy_gettext( - "Office for National Statistics logo" - ) copyright_declaration: Optional[LazyString] = lazy_gettext( "Crown copyright and database rights 2020 OS 100019153." ) @@ -27,14 +23,10 @@ class SurveyConfig: account_service_my_account_url: Optional[str] = None account_service_todo_url: Optional[str] = None account_service_log_out_url: Optional[str] = None - title_logo: Optional[str] = None - title_logo_alt: Optional[str] = None accessibility_url: str = f"{ONS_URL}/help/accessibility/" what_we_do_url: str = f"{ONS_URL}/aboutus/whatwedo/" - custom_header_logo: bool = False - mobile_logo: Optional[str] = None - powered_by_logo: Optional[str] = None - powered_by_logo_alt: Optional[str] = None + masthead_logo: Optional[str] = None + masthead_logo_mobile: Optional[str] = None crest: bool = True footer_links: Optional[Iterable[MutableMapping]] = None footer_legal_links: Optional[Iterable[Mapping]] = None diff --git a/app/translations/messages.pot b/app/translations/messages.pot index d1e8e25eac..9cf8a04e17 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-09-21 15:33+0100\n" +"POT-Creation-Date: 2022-11-10 16:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -203,15 +203,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:98 +#: app/helpers/template_helpers.py:103 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:140 +#: app/helpers/template_helpers.py:124 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:174 +#: app/helpers/template_helpers.py:152 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -295,102 +295,90 @@ msgstr "" msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:60 -#: app/survey_config/census_config.py:41 app/survey_config/census_config.py:99 -#: app/survey_config/census_config.py:171 +#: app/survey_config/business_config.py:58 +#: app/survey_config/census_config.py:39 app/survey_config/census_config.py:96 +#: app/survey_config/census_config.py:166 msgid "Help" msgstr "" -#: app/survey_config/business_config.py:74 +#: app/survey_config/business_config.py:72 msgid "My account" msgstr "" -#: app/survey_config/business_config.py:79 +#: app/survey_config/business_config.py:77 msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:89 +#: app/survey_config/business_config.py:87 #: app/survey_config/social_survey_config.py:36 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:92 -#: app/survey_config/census_config.py:48 app/survey_config/census_config.py:107 -#: app/survey_config/census_config.py:178 +#: app/survey_config/business_config.py:90 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 +#: app/survey_config/census_config.py:173 #: app/survey_config/social_survey_config.py:39 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:96 +#: app/survey_config/business_config.py:94 #: app/survey_config/social_survey_config.py:43 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:106 -#: app/survey_config/census_config.py:69 app/survey_config/census_config.py:127 -#: app/survey_config/census_config.py:186 +#: app/survey_config/business_config.py:104 +#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 +#: app/survey_config/census_config.py:181 #: app/survey_config/social_survey_config.py:53 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:108 -#: app/survey_config/census_config.py:75 app/survey_config/census_config.py:133 -#: app/survey_config/census_config.py:192 +#: app/survey_config/business_config.py:106 +#: app/survey_config/census_config.py:73 app/survey_config/census_config.py:130 +#: app/survey_config/census_config.py:187 #: app/survey_config/social_survey_config.py:55 msgid "Privacy and data protection" msgstr "" -#: app/survey_config/business_config.py:139 -msgid "Northern Ireland Department of Finance logo" -msgstr "" - -#: app/survey_config/census_config.py:20 app/survey_config/census_config.py:25 +#: app/survey_config/census_config.py:23 msgid "Census 2021" msgstr "" -#: app/survey_config/census_config.py:26 +#: app/survey_config/census_config.py:24 msgid "Save and complete later" msgstr "" -#: app/survey_config/census_config.py:54 app/survey_config/census_config.py:113 +#: app/survey_config/census_config.py:52 app/survey_config/census_config.py:110 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:58 app/survey_config/census_config.py:117 +#: app/survey_config/census_config.py:56 app/survey_config/census_config.py:114 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:129 -#: app/survey_config/census_config.py:188 +#: app/survey_config/census_config.py:69 app/survey_config/census_config.py:126 +#: app/survey_config/census_config.py:183 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:79 app/survey_config/census_config.py:137 -#: app/survey_config/census_config.py:196 +#: app/survey_config/census_config.py:77 app/survey_config/census_config.py:134 +#: app/survey_config/census_config.py:191 msgid "Terms and conditions" msgstr "" #: app/survey_config/census_config.py:152 -msgid "Northern Ireland Statistics and Research Agency logo" -msgstr "" - -#: app/survey_config/census_config.py:157 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:160 app/survey_config/survey_config.py:23 +#: app/survey_config/census_config.py:155 app/survey_config/survey_config.py:19 msgid "Use of address data is subject to the terms and conditions." msgstr "" -#: app/survey_config/survey_config.py:17 -msgid "Office for National Statistics logo" -msgstr "" - -#: app/survey_config/survey_config.py:20 +#: app/survey_config/survey_config.py:16 msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" -#: app/survey_config/survey_config.py:46 +#: app/survey_config/survey_config.py:39 msgid "Save and exit survey" msgstr "" @@ -452,9 +440,9 @@ msgstr "" #: app/views/contexts/hub_context.py:61 templates/confirm-email.html:25 #: templates/individual_response/confirmation-post.html:21 -#: templates/individual_response/confirmation-text-message.html:31 +#: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/sectionsummary.html:10 templates/sectionsummary.html:46 +#: templates/sectionsummary.html:10 templates/sectionsummary.html:49 msgid "Continue" msgstr "" @@ -823,17 +811,17 @@ msgstr "" msgid "Feedback sent" msgstr "" -#: templates/feedback-sent.html:19 +#: templates/feedback-sent.html:17 msgid "Thank you for your feedback" msgstr "" -#: templates/feedback-sent.html:20 +#: templates/feedback-sent.html:18 msgid "" "Your comments will help us make improvements to our surveys. We are not " "able to reply to comments, but we appreciate your feedback" msgstr "" -#: templates/feedback-sent.html:28 +#: templates/feedback-sent.html:24 msgid "Done" msgstr "" @@ -841,14 +829,14 @@ msgstr "" msgid "Back" msgstr "" -#: templates/feedback.html:27 +#: templates/feedback.html:28 #, python-format msgid "There is a problem with your feedback" msgid_plural "There are %(num)s problems with your feedback" msgstr[0] "" msgstr[1] "" -#: templates/feedback.html:38 +#: templates/feedback.html:39 msgid "Send feedback" msgstr "" @@ -860,7 +848,7 @@ msgstr "" msgid "If you can’t answer questions for this person" msgstr "" -#: templates/interstitial.html:32 templates/layouts/_questionnaire.html:27 +#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:27 msgid "Save and continue" msgstr "" @@ -868,7 +856,7 @@ msgstr "" msgid "Introduction" msgstr "" -#: templates/introduction.html:22 +#: templates/introduction.html:23 msgid "Your response is legally required" msgstr "" @@ -924,7 +912,7 @@ msgstr "" msgid "For more information on how we use this data." msgstr "" -#: templates/thank-you.html:51 templates/view-submitted-response.html:63 +#: templates/thank-you.html:51 templates/view-submitted-response.html:69 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" @@ -942,19 +930,19 @@ msgid "" "your answers for your records." msgstr "" -#: templates/layouts/_base.html:158 templates/thank-you.html:71 +#: templates/layouts/_base.html:155 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/layouts/_base.html:159 templates/thank-you.html:72 +#: templates/layouts/_base.html:156 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:160 templates/thank-you.html:73 +#: templates/layouts/_base.html:157 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/layouts/_base.html:161 templates/thank-you.html:74 +#: templates/layouts/_base.html:158 templates/thank-you.html:74 msgid "seconds" msgstr "" @@ -966,7 +954,7 @@ msgstr "" msgid "Print answers" msgstr "" -#: templates/view-submitted-response.html:44 +#: templates/view-submitted-response.html:47 msgid "Save answers as PDF" msgstr "" @@ -1162,7 +1150,7 @@ msgid "" "them to complete their own census" msgstr "" -#: templates/individual_response/confirmation-text-message.html:26 +#: templates/individual_response/confirmation-text-message.html:25 msgid "The text will be sent from Census2021" msgstr "" @@ -1196,69 +1184,69 @@ msgstr "" msgid "Previous" msgstr "" -#: templates/layouts/_base.html:71 +#: templates/layouts/_base.html:68 msgid "Tell us whether you accept cookies" msgstr "" -#: templates/layouts/_base.html:72 +#: templates/layouts/_base.html:69 msgid "" "We use cookies to collect information" " about how you use {cookie_domain}. We use this information to make the " "website work as well as possible and improve our services." msgstr "" -#: templates/layouts/_base.html:73 +#: templates/layouts/_base.html:70 msgid "" "You’ve accepted all cookies. You can change your cookie preferences at any " "time." msgstr "" -#: templates/layouts/_base.html:74 +#: templates/layouts/_base.html:71 msgid "Accept all cookies" msgstr "" -#: templates/layouts/_base.html:75 +#: templates/layouts/_base.html:72 msgid "Set cookie preferences" msgstr "" -#: templates/layouts/_base.html:76 +#: templates/layouts/_base.html:73 #: templates/partials/introduction/preview.html:30 #: templates/partials/summary/collapsible-summary.html:15 msgid "Hide" msgstr "" -#: templates/layouts/_base.html:131 +#: templates/layouts/_base.html:128 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:153 +#: templates/layouts/_base.html:150 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:154 +#: templates/layouts/_base.html:151 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:155 +#: templates/layouts/_base.html:152 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:156 +#: templates/layouts/_base.html:153 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:157 +#: templates/layouts/_base.html:154 msgid "Continue survey" msgstr "" -#: templates/layouts/_questionnaire.html:37 +#: templates/layouts/_questionnaire.html:40 msgid "Choose another section and return to this later" msgstr "" -#: templates/layouts/configs/_save-sign-out-button.html:17 +#: templates/layouts/configs/_header.html:10 msgid "Exit" msgstr "" @@ -1280,7 +1268,7 @@ msgstr "" msgid "This will not be stored and only used once to send your confirmation" msgstr "" -#: templates/partials/email-form.html:39 +#: templates/partials/email-form.html:38 msgid "Send confirmation" msgstr "" diff --git a/app/views/handlers/thank_you.py b/app/views/handlers/thank_you.py index e414f84eec..183e99bff1 100644 --- a/app/views/handlers/thank_you.py +++ b/app/views/handlers/thank_you.py @@ -35,10 +35,7 @@ def __init__( self._schema: QuestionnaireSchema = schema self._submitted_at = submitted_at - self._is_census_theme = cookie_session.get("theme") in [ - "census", - "census-nisra", - ] + self._is_census_theme = cookie_session.get("theme") == "census" self.template = ( self.CENSUS_THANK_YOU_TEMPLATE if self._is_census_theme diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index af8e0d6d55..0a29cd137f 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -5,6 +5,7 @@ "data_version": "0.0.1", "survey_id": "017", "form_type": "0070", + "theme": "northernireland", "legal_basis": "Notice is given under section 1 of the Statistics of Trade Act 1947.", "title": "Quarterly Stocks Survey", "questionnaire_flow": { @@ -543,7 +544,6 @@ ] } ], - "theme": "default", "navigation": { "visible": false }, diff --git a/templates/assets/images/ni-finance-logo.svg b/templates/assets/images/ni-finance-logo.svg new file mode 100644 index 0000000000..e0d55e03bd --- /dev/null +++ b/templates/assets/images/ni-finance-logo.svg @@ -0,0 +1,16 @@ + diff --git a/templates/assets/images/ni-finance-mobile-logo.svg b/templates/assets/images/ni-finance-mobile-logo.svg new file mode 100644 index 0000000000..e5415d8430 --- /dev/null +++ b/templates/assets/images/ni-finance-mobile-logo.svg @@ -0,0 +1,16 @@ + diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 9e595f0a5d..0bff4987a0 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -36,10 +36,7 @@ {% set pageConfig = { "title": full_page_title, - "header": page_header, - "serviceLinks": service_links, "footer": footer, - "language": languages, "cdn": { "url": cdn_url }, @@ -51,7 +48,7 @@ {% do pageConfig.update({"theme":theme}) %} {% endif %} -{% include 'layouts/configs/_save-sign-out-button.html' %} +{% include 'layouts/configs/_header.html' %} {# if there is not a previous link add extra margin top to the page #} diff --git a/templates/layouts/configs/_header.html b/templates/layouts/configs/_header.html new file mode 100644 index 0000000000..293ab814be --- /dev/null +++ b/templates/layouts/configs/_header.html @@ -0,0 +1,40 @@ +{% if current_user.is_authenticated and not hide_sign_out_button %} + {% set sign_out_url = url_for('session.get_sign_out', todo=true) %} + + {% if save_on_signout %} + {% set header_button_text = sign_out_button_text %} + {% set header_button_data_qa = "btn-save-sign-out" %} + {% set header_button_data_ga_action = "Save and sign out" %} + {% set header_button_data_ga_label = "Save and sign out button click" %} + {% else %} + {% set header_button_text = _("Exit") %} + {% set header_button_data_qa = "btn-exit" %} + {% set header_button_data_ga_action = "Exit" %} + {% set header_button_data_ga_label = "Exit button click" %} + {% endif %} + + {% set signout_button = { + "text": header_button_text, + "url": sign_out_url, + "attributes": { + "data-qa": header_button_data_qa, + "data-ga-category": "Navigation", + "data-ga-action": header_button_data_ga_action, + "data-ga-label": header_button_data_ga_label + }, + "iconType": "exit", + "iconPosition": "after" + } %} + +{% endif %} + +{% do pageConfig | setAttribute("header", { + "title": survey_title, + "signoutButton": signout_button, + "language": languages, + "serviceLinks": service_links, + "mastheadLogo": { + "large": mastheadLogo, + "small": mastheadLogoMobile + } +}) %} diff --git a/templates/layouts/configs/_save-sign-out-button.html b/templates/layouts/configs/_save-sign-out-button.html deleted file mode 100644 index 3666b8cfc6..0000000000 --- a/templates/layouts/configs/_save-sign-out-button.html +++ /dev/null @@ -1,32 +0,0 @@ -{% if current_user.is_authenticated and not hide_sign_out_button %} - {% set sign_out_url = url_for('session.get_sign_out', todo=true) %} - - {% if save_on_signout %} - {% do pageConfig | setAttribute("signoutButton", { - "text": sign_out_button_text, - "url": sign_out_url, - "attributes": { - "data-qa": "btn-save-sign-out", - "data-ga-category": "Navigation", - "data-ga-action": "Save and sign out", - "data-ga-label": "Save and sign out button click" - }, - "iconType": "exit", - "iconPosition": "after" - }) %} - {% else %} - {% do pageConfig | setAttribute("signoutButton", { - "text": _("Exit"), - "url": sign_out_url, - "attributes": { - "data-qa": "btn-exit", - "data-ga-category": "Navigation", - "data-ga-action": "Exit", - "data-ga-label": "Exit button click" - }, - "iconType": "exit", - "iconPosition": 'after' - }) %} - {% endif %} - -{% endif %} diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index c4fb0dcf76..0daab86383 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -299,10 +299,6 @@ def expected_footer_nisra_theme(): ] } ], - "poweredBy": { - "logo": "nisra-logo", - "alt": "NISRA - Northern Ireland Statistics and Research Agency", - }, } diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 70eaa54556..f6d37c7961 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -103,141 +103,77 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): SurveyType.BUSINESS, None, BusinessSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Business Surveys", - }, + ["ONS Business Surveys", None, None], ), ( SurveyType.BUSINESS, "Test", BusinessSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "Test", - }, + ["Test", None, None], ), ( None, None, BusinessSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Surveys", - }, + ["ONS Business Surveys", None, None], ), ( SurveyType.SOCIAL, None, SocialSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Social Surveys", - }, + ["ONS Social Surveys", None, None], ), ( SurveyType.SOCIAL, "Test", SocialSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "Test", - }, + ["Test", None, None], ), ( None, None, SocialSurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Surveys", - }, + ["ONS Social Surveys", None, None], + ), + ( + None, + None, + SurveyConfig(), + [None, None, None], ), ( SurveyType.CENSUS, None, CensusSurveyConfig(), - { - "title": "Census 2021", - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - }, + ["Census 2021", None, None], ), ( SurveyType.CENSUS, "Test", CensusSurveyConfig(), - { - "title": "Test", - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - }, + ["Test", None, None], ), ( None, None, CensusSurveyConfig(), - { - "title": "ONS Surveys", - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - }, + ["Census 2021", None, None], ), ( SurveyType.CENSUS_NISRA, None, CensusNISRASurveyConfig(), - { - "orgLogo": "nisra-logo", - "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - "customHeaderLogo": True, - "orgMobileLogo": "nisra-logo-mobile", - "title": "Census 2021", - }, + ["Census 2021", None, None], ), ( None, None, CensusNISRASurveyConfig(), - { - "orgLogo": "nisra-logo", - "orgLogoAlt": "Northern Ireland Statistics and Research Agency logo", - "titleLogo": "census-logo-en", - "titleLogoAlt": "Census 2021", - "customHeaderLogo": True, - "orgMobileLogo": "nisra-logo-mobile", - "title": "ONS Surveys", - }, - ), - ( - None, - None, - SurveyConfig(), - { - "orgLogo": "ons-logo-en", - "orgLogoAlt": "Office for National Statistics logo", - "title": "ONS Surveys", - }, + ["Census 2021", None, None], ), ), ) -def test_get_page_header_context( - app: Flask, theme, survey_title, survey_config, expected -): +def test_header_context(app: Flask, theme, survey_title, survey_config, expected): with app.app_context(): for cookie_name, cookie_value in { "theme": theme, @@ -246,12 +182,18 @@ def test_get_page_header_context( if cookie_value: cookie_session[cookie_name] = cookie_value - result = ContextHelper( + context_helper = ContextHelper( language="en", is_post_submission=False, include_csrf_token=True, survey_config=survey_config, - ).context["page_header"] + ) + + result = [ + context_helper.context["survey_title"], + context_helper.context["mastheadLogo"], + context_helper.context["mastheadLogoMobile"], + ] assert result == expected From c96d0d9b43a00323551e7efc250d46d5eec209df Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 24 Nov 2022 11:01:24 +0000 Subject: [PATCH 106/567] Revert schema theme change (#963) --- schemas/test/en/test_benchmark_business.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 0a29cd137f..548bbaf6ef 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -5,7 +5,7 @@ "data_version": "0.0.1", "survey_id": "017", "form_type": "0070", - "theme": "northernireland", + "theme": "business", "legal_basis": "Notice is given under section 1 of the Statistics of Trade Act 1947.", "title": "Quarterly Stocks Survey", "questionnaire_flow": { From 1632fd1220bc6ad09ce52b3bbd5392409fa76ade Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 25 Nov 2022 11:39:00 +0000 Subject: [PATCH 107/567] Add multilingual support to Social Survey Config (#960) --- app/helpers/template_helpers.py | 3 +- app/settings.py | 6 +- app/survey_config/social_survey_config.py | 16 ++- app/survey_config/survey_config.py | 3 + tests/app/helpers/conftest.py | 32 ++++-- tests/app/helpers/test_template_helpers.py | 113 +++++++++++++++++---- tests/integration/routes/test_errors.py | 27 +++-- tests/integration/routes/test_session.py | 3 +- 8 files changed, 156 insertions(+), 47 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 712f3345d7..d30ab5a520 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -170,8 +170,7 @@ def survey_config_mapping( } return survey_type_to_config[theme]( - base_url=base_url, - schema=schema, + base_url=base_url, schema=schema, language_code=language ) diff --git a/app/settings.py b/app/settings.py index b11d8747ca..b5922ffe75 100644 --- a/app/settings.py +++ b/app/settings.py @@ -149,10 +149,14 @@ def utcoffset_or_fail(date_value, key): "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" ) -ACCOUNT_SERVICE_BASE_URL_SOCIAL = "https://start.surveys.ons.gov.uk" +ACCOUNT_SERVICE_BASE_URL_SOCIAL = os.getenv( + "ACCOUNT_SERVICE_BASE_URL_SOCIAL", "https://start.surveys.ons.gov.uk" +) PRINT_STYLE_SHEET_FILE_PATH = os.getenv( "PRINT_STYLE_SHEET_FILEPATH", "templates/assets/styles" ) ONS_URL = os.getenv("ONS_URL", "https://www.ons.gov.uk") + +ONS_URL_CY = os.getenv("ONS_URL_CY", "https://cy.ons.gov.uk") diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index aa1a4cf447..90149e9655 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -3,7 +3,7 @@ from flask_babel import lazy_gettext -from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL +from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL, ONS_URL, ONS_URL_CY from app.survey_config.link import Link from app.survey_config.survey_config import SurveyConfig @@ -29,8 +29,20 @@ def __post_init__(self): } ] + upstream_base_url = f"{self.base_url}/{self.language_code}" + ons_url = ONS_URL_CY if self.language_code == "cy" else ONS_URL + if not self.account_service_log_out_url: - self.account_service_log_out_url: str = f"{self.base_url}/sign-in/logout" + self.account_service_log_out_url: str = f"{upstream_base_url}/start/" + + self.cookie_settings_url: str = f"{upstream_base_url}/cookies/" + self.privacy_and_data_protection_url: str = ( + f"{upstream_base_url}/privacy-and-data-protection/" + ) + + self.contact_us_url: str = f"{ons_url}/aboutus/contactus/surveyenquiries/" + self.accessibility_url: str = f"{ons_url}/help/accessibility/" + self.what_we_do_url: str = f"{ons_url}/aboutus/whatwedo/" def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: links = [Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__] diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index d0de9e781b..4297a9c05f 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -5,6 +5,7 @@ from flask_babel.speaklater import LazyString from app.questionnaire import QuestionnaireSchema +from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.settings import ACCOUNT_SERVICE_BASE_URL, ONS_URL @@ -40,6 +41,7 @@ class SurveyConfig: cookie_settings_url: str = field(init=False) cookie_domain: str = field(init=False) privacy_and_data_protection_url: str = field(init=False) + language_code: Optional[str] = None def __post_init__(self): self.contact_us_url: str = f"{self.base_url}/contact-us/" @@ -50,6 +52,7 @@ def __post_init__(self): self.privacy_and_data_protection_url: str = ( f"{self.base_url}/privacy-and-data-protection/" ) + self.language_code: str = self.language_code or DEFAULT_LANGUAGE_CODE def get_service_links( # pylint: disable=unused-argument, no-self-use self, diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index 0daab86383..954679a633 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -1,7 +1,12 @@ from pytest import fixture from app.helpers.template_helpers import ContextHelper -from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL +from app.settings import ( + ACCOUNT_SERVICE_BASE_URL, + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ONS_URL, + ONS_URL_CY, +) from app.survey_config.census_config import CY_BASE_URL, EN_BASE_URL @@ -183,24 +188,35 @@ def expected_footer_business_theme_no_cookie(): return {**footer_context(), **business} -def expected_footer_social_theme(): +def expected_footer_social_theme(language_code: str): + ons_url = ONS_URL_CY if language_code == "cy" else ONS_URL + upstream_url = f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/{language_code}" + social_footer_context = { + "lang": language_code, + "crest": True, + "newTabWarning": "The following links open in a new tab", + "copyrightDeclaration": { + "copyright": "Crown copyright and database rights 2020 OS 100019153.", + "text": "Use of address data is subject to the terms and conditions.", + }, + } social = { "rows": [ { "itemsList": [ { "text": "What we do", - "url": "https://www.ons.gov.uk/aboutus/whatwedo/", + "url": f"{ons_url}/aboutus/whatwedo/", "target": "_blank", }, { "text": "Contact us", - "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/contact-us/", + "url": f"{ons_url}/aboutus/contactus/surveyenquiries/", "target": "_blank", }, { "text": "Accessibility", - "url": "https://www.ons.gov.uk/help/accessibility/", + "url": f"{ons_url}/help/accessibility/", "target": "_blank", }, ] @@ -211,19 +227,19 @@ def expected_footer_social_theme(): "itemsList": [ { "text": "Cookies", - "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cookies/", + "url": f"{upstream_url}/cookies/", "target": "_blank", }, { "text": "Privacy and data protection", - "url": f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/privacy-and-data-protection/", + "url": f"{upstream_url}/privacy-and-data-protection/", "target": "_blank", }, ] } ], } - return {**footer_context(), **social} + return social_footer_context | social def expected_footer_social_theme_no_cookie(): diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index f6d37c7961..1d0e9b810f 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -6,7 +6,12 @@ from app.helpers.template_helpers import ContextHelper, get_survey_config from app.questionnaire import QuestionnaireSchema -from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL +from app.settings import ( + ACCOUNT_SERVICE_BASE_URL, + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ONS_URL, + ONS_URL_CY, +) from app.survey_config import ( BusinessSurveyConfig, CensusNISRASurveyConfig, @@ -33,34 +38,57 @@ @pytest.mark.parametrize( - "theme, survey_config, expected_footer", + "theme, survey_config, language, expected_footer", [ - (SurveyType.CENSUS, CensusSurveyConfig(), expected_footer_census_theme()), - (None, CensusSurveyConfig(), expected_footer_census_theme_no_cookie()), - (SurveyType.BUSINESS, BusinessSurveyConfig(), expected_footer_business_theme()), - (None, BusinessSurveyConfig(), expected_footer_business_theme_no_cookie()), - (SurveyType.SOCIAL, SocialSurveyConfig(), expected_footer_social_theme()), - (None, SocialSurveyConfig(), expected_footer_social_theme_no_cookie()), + (SurveyType.CENSUS, CensusSurveyConfig(), "en", expected_footer_census_theme()), + (None, CensusSurveyConfig(), "en", expected_footer_census_theme_no_cookie()), + ( + SurveyType.BUSINESS, + BusinessSurveyConfig(), + "en", + expected_footer_business_theme(), + ), + ( + None, + BusinessSurveyConfig(), + "en", + expected_footer_business_theme_no_cookie(), + ), + ( + SurveyType.SOCIAL, + SocialSurveyConfig(), + "en", + expected_footer_social_theme("en"), + ), + (None, SocialSurveyConfig(), "en", expected_footer_social_theme_no_cookie()), + ( + SurveyType.SOCIAL, + SocialSurveyConfig(language_code="cy"), + "cy", + expected_footer_social_theme("cy"), + ), ( SurveyType.CENSUS_NISRA, CensusNISRASurveyConfig(), + "en", expected_footer_nisra_theme(), ), ( SurveyType.CENSUS, WelshCensusSurveyConfig(), + "en", expected_footer_census_welsh_theme(), ), ], ) -def test_footer_context(app: Flask, theme, survey_config, expected_footer): +def test_footer_context(app: Flask, theme, survey_config, language, expected_footer): with app.app_context(): if theme: cookie_session["theme"] = theme config = survey_config result = ContextHelper( - language="en", + language=language, is_post_submission=False, include_csrf_token=True, survey_config=config, @@ -129,6 +157,12 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): SocialSurveyConfig(), ["Test", None, None], ), + ( + SurveyType.SOCIAL, + "Test", + SocialSurveyConfig(language_code="cy"), + ["Test", None, None], + ), ( None, None, @@ -288,32 +322,44 @@ def test_service_links_context( @pytest.mark.parametrize( - "survey_config, expected", + "survey_config, language, expected", [ ( SurveyConfig(), + "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( BusinessSurveyConfig(), + "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( NorthernIrelandBusinessSurveyConfig(), + "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( SocialSurveyConfig(), - f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/contact-us/", + "en", + f"{ONS_URL}/aboutus/contactus/surveyenquiries/", + ), + ( + SocialSurveyConfig(language_code="cy"), + "cy", + f"{ONS_URL_CY}/aboutus/contactus/surveyenquiries/", ), ], ) def test_contact_us_url_context( - app: Flask, survey_config: SurveyConfig, expected: dict[str, str] + app: Flask, + survey_config: SurveyConfig, + language: str, + expected: dict[str, str], ): with app.app_context(): result = ContextHelper( - language="en", + language=language, is_post_submission=False, include_csrf_token=True, survey_config=survey_config, @@ -360,7 +406,12 @@ def test_sign_out_button_text_context( ( SocialSurveyConfig(), True, - f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cookies/", + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/cookies/", + ), + ( + SocialSurveyConfig(language_code="cy"), + True, + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cy/cookies/", ), (SurveyConfig(), False, None), ], @@ -383,28 +434,38 @@ def test_cookie_settings_url_context( @pytest.mark.parametrize( - "survey_config, address", + "survey_config, language, address", [ - (SurveyConfig(), ACCOUNT_SERVICE_BASE_URL), + (SurveyConfig(), "en", ACCOUNT_SERVICE_BASE_URL), ( BusinessSurveyConfig(), + "en", ACCOUNT_SERVICE_BASE_URL, ), ( NorthernIrelandBusinessSurveyConfig(), + "en", ACCOUNT_SERVICE_BASE_URL, ), ( SocialSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ), + ( + SocialSurveyConfig(), + "cy", ACCOUNT_SERVICE_BASE_URL_SOCIAL, ), ], ) -def test_cookie_domain_context(app: Flask, survey_config: SurveyConfig, address: str): +def test_cookie_domain_context( + app: Flask, survey_config: SurveyConfig, language: str, address: str +): with app.app_context(): cookie_session["theme"] = "dummy_value" context_helper = ContextHelper( - language="en", + language=language, is_post_submission=False, include_csrf_token=True, survey_config=survey_config, @@ -498,7 +559,11 @@ def test_account_service_my_todo_url_context( ), ( SocialSurveyConfig(), - f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/sign-in/logout", + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", + ), + ( + SocialSurveyConfig(language_code="cy"), + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cy/start/", ), ], ) @@ -519,9 +584,7 @@ def test_account_service_log_out_url_context( (SurveyType.BUSINESS, "en", BusinessSurveyConfig), (SurveyType.BUSINESS, "cy", BusinessSurveyConfig), (SurveyType.HEALTH, "en", SocialSurveyConfig), - (SurveyType.HEALTH, "cy", SocialSurveyConfig), (SurveyType.SOCIAL, "en", SocialSurveyConfig), - (SurveyType.SOCIAL, "cy", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NorthernIrelandBusinessSurveyConfig), (SurveyType.NORTHERN_IRELAND, "cy", NorthernIrelandBusinessSurveyConfig), (SurveyType.CENSUS, "en", CensusSurveyConfig), @@ -564,6 +627,10 @@ def test_survey_config_base_url_provided_used_in_links( result.privacy_and_data_protection_url, ] + if survey_config_type == SocialSurveyConfig: + + urls_to_check.remove(result.contact_us_url) + for url in urls_to_check: if url: assert base_url in url @@ -625,6 +692,7 @@ def test_context_set_from_app_config(app): (SurveyType.BUSINESS, "en", None), (SurveyType.HEALTH, "en", None), (SurveyType.SOCIAL, "en", None), + (SurveyType.SOCIAL, "cy", None), (SurveyType.NORTHERN_IRELAND, "en", None), (SurveyType.CENSUS, "en", "census"), (SurveyType.CENSUS, "cy", "census"), @@ -650,6 +718,7 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.BUSINESS, "en", "ONS Business Surveys"), (SurveyType.HEALTH, "en", "ONS Social Surveys"), (SurveyType.SOCIAL, "en", "ONS Social Surveys"), + (SurveyType.SOCIAL, "cy", "ONS Social Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), (SurveyType.CENSUS, "en", "Census 2021"), (SurveyType.CENSUS, "cy", "Census 2021"), diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 605238b42f..65bebfdf80 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -1,6 +1,11 @@ from mock import Mock, patch -from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL +from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.settings import ( + ACCOUNT_SERVICE_BASE_URL, + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ONS_URL, +) from tests.integration.create_token import ACCOUNT_SERVICE_URL from tests.integration.integration_test_case import IntegrationTestCase @@ -124,7 +129,7 @@ def test_401_theme_social_cookie_exists(self): cookie = self.getCookie() self.assertEqual(cookie.get("theme"), "social") self.assertInBody( - f'

    To access this page you need to re-enter your access code.

    ' + f'

    To access this page you need to re-enter your access code.

    ' ) def test_401_no_cookie(self): @@ -144,7 +149,8 @@ def test_401_no_cookie(self): ( f'

    If you are completing a business survey, you need to sign back in to your account.

    ' ), - f'

    If you started your survey using an access code, you need to re-enter your code.

    ', + f'

    If you started your survey using an access code, you need to re-enter your code.' + "

    ", ] ) @@ -177,7 +183,7 @@ def test_403_theme_social_cookie_exists(self): self.assertEqual(cookie.get("theme"), "social") self.assertStatusForbidden() self.assertInBody( - f'

    For further help, please contact us.

    ' + f'

    For further help, please contact us.

    ' ) def test_403_no_cookie(self): @@ -196,8 +202,8 @@ def test_403_no_cookie(self): f'

    If you are completing a business survey and you need further help, please contact us.

    ' ), ( - f'

    If you started your survey using an access code and you need further help, please contact us." + f'

    If you started your survey using an access code and you need further help, please contact us.

    ' ), ] ) @@ -231,7 +237,8 @@ def test_404_theme_social_cookie_exists(self): self.assertEqual(cookie.get("theme"), "social") self.assertStatusNotFound() self.assertInBody( - f'

    If the web address is correct or you selected a link or button, contact us for more help.

    ' + f'

    If the web address is correct or you selected a link or button, contact us for more' + " help.

    " ) def test_404_no_cookie(self): @@ -247,7 +254,7 @@ def test_404_no_cookie(self): [ "

    If the web address is correct or you selected a link or button, please see the following help links.

    ", f'

    If you are completing a business survey, please contact us.

    ', - f'

    If you started your survey using an access code, please contact us.

    ', + f'

    If you started your survey using an access code, please contact us.

    ', ] ) @@ -265,7 +272,7 @@ def test_404_no_cookie_unauthenticated(self): [ "

    If the web address is correct or you selected a link or button, please see the following help links.

    ", f'

    If you are completing a business survey, please contact us.

    ', - f'

    If you started your survey using an access code, please contact us.

    ', + f'

    If you started your survey using an access code, please contact us.

    ', ] ) @@ -357,7 +364,7 @@ def test_submission_failed_theme_social_cookie_exists(self): # Then self.assertStatusCode(500) self.assertInBody( - f'

    If this problem keeps happening, please contact us for help.

    ' + f'

    If this problem keeps happening, please contact us for help.

    ' ) def test_submission_failed_theme_census_cookie_exists(self): diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 04e724913f..f60852e834 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -33,8 +33,7 @@ def test_session_expired(self): '

    If you are completing a business survey, you need to sign back in to your account.

    ' ) self.assertInBody( - '

    If you started your survey using an access code, you need to re-enter your code.' - "

    " + '

    If you started your survey using an access code, you need to re-enter your code.

    ' ) def test_session_jti_token_expired(self): From 98c5fa2c24e446080f015d7dccbd26d985aed9e6 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 29 Nov 2022 13:17:06 +0000 Subject: [PATCH 108/567] Add language toggle support for survey title (#966) --- app/helpers/language_helper.py | 10 +++++++++- app/routes/flush.py | 4 +++- app/routes/session.py | 4 +++- app/utilities/schema.py | 4 +--- tests/app/utilities/test_schema.py | 2 +- tests/functional/spec/language_code.spec.js | 2 ++ 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 5e59a71f8e..8c83854ed5 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -2,12 +2,13 @@ from urllib.parse import urlencode from flask import g, request +from flask import session as cookie_session from flask_login import current_user from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_metadata, get_session_store from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE -from app.utilities.schema import get_allowed_languages +from app.utilities.schema import get_allowed_languages, load_schema_from_metadata LANGUAGE_TEXT = { "en": "English", @@ -30,6 +31,13 @@ def handle_language(metadata: Optional[MetadataProxy] = None) -> None: g.allowed_languages = get_allowed_languages(schema_name, launch_language) request_language = request.args.get("language_code") if request_language and request_language in g.allowed_languages: + if metadata: + schema = load_schema_from_metadata( + metadata=metadata, language_code=request_language + ) + if schema.json["title"] != cookie_session.get("survey_title"): + cookie_session["survey_title"] = schema.json["title"] + session_store.session_data.language_code = request_language session_store.save() diff --git a/app/routes/flush.py b/app/routes/flush.py index 67f0499ff8..09b5a24a9d 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -61,7 +61,9 @@ def _submit_data(user): progress_store = questionnaire_store.progress_store list_store = questionnaire_store.list_store submitted_at = datetime.now(timezone.utc) - schema = load_schema_from_metadata(metadata=metadata) + schema = load_schema_from_metadata( + metadata=metadata, language_code=metadata.language_code + ) router = Router( schema, diff --git a/app/routes/session.py b/app/routes/session.py index d48c77cf7c..6bbcace058 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -59,7 +59,9 @@ def login(): metadata = MetadataProxy.from_dict(runner_claims) # pylint: disable=assigning-non-slot - g.schema = load_schema_from_metadata(metadata=metadata) + g.schema = load_schema_from_metadata( + metadata=metadata, language_code=metadata.language_code + ) schema_metadata = g.schema.json["metadata"] questionnaire_claims = get_questionnaire_claims( diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 62b0d3ba3f..88734703b4 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -100,11 +100,9 @@ def get_allowed_languages(schema_name, launch_language): def load_schema_from_metadata( - metadata: MetadataProxy, *, language_code: Optional[str] = None + metadata: MetadataProxy, *, language_code: str ) -> QuestionnaireSchema: - language_code = language_code or metadata.language_code - if metadata and (schema_url := metadata.schema_url): # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 746fc5efb9..c93689431a 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -226,7 +226,7 @@ def test_load_schema_from_metadata_with_schema_url(): ) mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) - loaded_schema = load_schema_from_metadata(metadata=metadata) + loaded_schema = load_schema_from_metadata(metadata=metadata, language_code="cy") assert loaded_schema.json == mock_schema.json assert loaded_schema.language_code == mock_schema.language_code diff --git a/tests/functional/spec/language_code.spec.js b/tests/functional/spec/language_code.spec.js index 0995597de0..8e1a235478 100644 --- a/tests/functional/spec/language_code.spec.js +++ b/tests/functional/spec/language_code.spec.js @@ -163,8 +163,10 @@ describe("Language Code", () => { $(HubPage.submit()).click(); expect($(NamePage.questionText()).getText()).to.contain("Please enter a name"); + expect($("header").getText()).to.contain("Test Language Survey"); $(NamePage.switchLanguage("cy")).click(); expect($(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + expect($("header").getText()).to.contain("Arolwg Iaith Prawf"); $(NamePage.switchLanguage("en")).click(); $(NamePage.firstName()).setValue("Catherine"); From 2e7233ed28a2854868cbfc1ea2b13fc305e0d6b1 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:46:16 +0000 Subject: [PATCH 109/567] Schemas v3.24.0 (#968) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b1ef60824c..4fa0b58aa4 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.23.0 +v3.24.0 From fea24c7ec9b4dad48cd08f861528cd6b1f58650e Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:19:00 +0000 Subject: [PATCH 110/567] Fix footer welsh logo (#967) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 46d391bd12..137b8ff557 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -60.0.2 +60.0.3 From 4d1a4e33d133b124b60be6d62c1fc3a91c3b549c Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 30 Nov 2022 10:32:19 +0000 Subject: [PATCH 111/567] Fix mobile ni logo (#964) --- app/survey_config/business_config.py | 2 +- tests/app/helpers/test_template_helpers.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 84bdf1a2b1..c038cd60ca 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -135,5 +135,5 @@ def _stripped_base_url(self) -> str: class NorthernIrelandBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo: str = read_file("./templates/assets/images/ni-finance-logo.svg") masthead_logo_mobile: str = read_file( - "./templates/assets/images/ni-finance-logo.svg" + "./templates/assets/images/ni-finance-mobile-logo.svg" ) diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 1d0e9b810f..6a4aae4115 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -11,6 +11,7 @@ ACCOUNT_SERVICE_BASE_URL_SOCIAL, ONS_URL, ONS_URL_CY, + read_file, ) from app.survey_config import ( BusinessSurveyConfig, @@ -205,6 +206,16 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): CensusNISRASurveyConfig(), ["Census 2021", None, None], ), + ( + None, + None, + NorthernIrelandBusinessSurveyConfig(), + [ + "ONS Business Surveys", + read_file("./templates/assets/images/ni-finance-logo.svg"), + read_file("./templates/assets/images/ni-finance-mobile-logo.svg"), + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): From 45e5901865e84a42526690e0bb63aa320b40aaf0 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:13:20 +0000 Subject: [PATCH 112/567] Schemas v3.24.1 (#969) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 4fa0b58aa4..e3611f9e84 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.24.0 +v3.24.1 From 34f2c128ddeec064240ac6802131ce76c5d0d05e Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 1 Dec 2022 12:05:02 +0000 Subject: [PATCH 113/567] Schemas v3.24.2 (#971) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e3611f9e84..7d4778b83c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.24.1 +v3.24.2 From 742392494800ac3776cded6d83dc385219acd8f4 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 5 Dec 2022 09:43:11 +0000 Subject: [PATCH 114/567] Schemas v3.24.3 (#972) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 7d4778b83c..355f20c80f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.24.2 +v3.24.3 From 7df429bc9ef9be83b610c863ad5b1bdbb53f3ca1 Mon Sep 17 00:00:00 2001 From: Hajara Iyal Date: Wed, 7 Dec 2022 12:57:36 +0000 Subject: [PATCH 115/567] Fix mutually exclusive validation for zero as unanswered (#973) * Fix mutually exclusive validation for zero as unanswered * Import EMPTY_ANSWER_VALUES tuple into validators.py and add decimal to unit test --- app/forms/validators.py | 8 +++++++- .../forms/validation/test_mutually_exclusive_validator.py | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/forms/validators.py b/app/forms/validators.py index 3d9c5db3c7..6a3c3fbe7c 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -20,6 +20,7 @@ IntegerFieldWithSeparator, ) from app.jinja_filters import format_number, get_formatted_currency +from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.rules.utils import parse_datetime from app.utilities import safe_content @@ -468,7 +469,12 @@ def __call__( is_mandatory: bool, is_only_checkboxes_or_radios: bool, ) -> None: - total_answered = sum(1 for value in answer_values if value) + + total_answered = sum( + value not in QuestionnaireStoreUpdater.EMPTY_ANSWER_VALUES + for value in answer_values + ) + if total_answered > 1: raise validators.ValidationError(self.messages["MUTUALLY_EXCLUSIVE"]) if is_mandatory and total_answered < 1: diff --git a/tests/app/forms/validation/test_mutually_exclusive_validator.py b/tests/app/forms/validation/test_mutually_exclusive_validator.py index edfa25efc7..322fec5e16 100644 --- a/tests/app/forms/validation/test_mutually_exclusive_validator.py +++ b/tests/app/forms/validation/test_mutually_exclusive_validator.py @@ -1,3 +1,5 @@ +from decimal import Decimal + import pytest from wtforms.validators import ValidationError @@ -72,6 +74,8 @@ def test_mutually_exclusive_mandatory_answers_raise_validation_error( (["British, Irish", None], True), ([None, "I prefer not to say"], True), (["", "I prefer not to say"], True), + ([0, []], True), + ([Decimal(0), []], True), ), ) def test_mutually_exclusive_mandatory_answers(answer_permutations, is_mandatory): From a7160d088894a459d70dd1dd926f94509bafcb1e Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 7 Dec 2022 13:42:18 +0000 Subject: [PATCH 116/567] Schemas v3.25.0 (#976) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 355f20c80f..44d6016d4a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.24.3 +v3.25.0 From c23b1cc21bb35243868d6b543c5b47d73c048368 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 8 Dec 2022 13:06:01 +0000 Subject: [PATCH 117/567] Update DS version to v61 (#975) --- .design-system-version | 2 +- templates/partials/answer-guidance.html | 4 ++-- templates/partials/definition.html | 4 ++-- templates/partials/individual-response-guidance.html | 4 ++-- templates/partials/question-definition.html | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.design-system-version b/.design-system-version index 137b8ff557..e60f6929fc 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -60.0.3 +61.0.1 diff --git a/templates/partials/answer-guidance.html b/templates/partials/answer-guidance.html index 67f75393eb..fc0274c4ea 100644 --- a/templates/partials/answer-guidance.html +++ b/templates/partials/answer-guidance.html @@ -1,6 +1,6 @@ -{% from "components/collapsible/_macro.njk" import onsCollapsible %} +{% from "components/details/_macro.njk" import onsDetails %} -{% call onsCollapsible({ +{% call onsDetails({ "id": "answer-guidance-" ~ answer.id, "classes": "ons-u-mt-s", "title": _(answer_guidance.schema_item.show_guidance), diff --git a/templates/partials/definition.html b/templates/partials/definition.html index b2155f90bc..f758221b0d 100644 --- a/templates/partials/definition.html +++ b/templates/partials/definition.html @@ -1,6 +1,6 @@ -{% from "components/collapsible/_macro.njk" import onsCollapsible %} +{% from "components/details/_macro.njk" import onsDetails %} -{% call onsCollapsible({ +{% call onsDetails({ "id": definition_id, "title": definition.title, "classes": "ons-u-mb-s", diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index d50a37de49..9f87af189d 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -1,6 +1,6 @@ -{% from "components/collapsible/_macro.njk" import onsCollapsible %} +{% from "components/details/_macro.njk" import onsDetails %} -{% call onsCollapsible({ +{% call onsDetails({ "classes": "ons-u-mt-s", "title": title, "headingAttributes": { diff --git a/templates/partials/question-definition.html b/templates/partials/question-definition.html index 8435680445..8cb5e658a0 100644 --- a/templates/partials/question-definition.html +++ b/templates/partials/question-definition.html @@ -1,4 +1,4 @@ -{% from "components/collapsible/_macro.njk" import onsCollapsible %} +{% from "components/details/_macro.njk" import onsDetails %} {% for definition in question.definitions %} {% set definition_id = "question-definition-" ~ loop.index %} From 1aaf19e34756b19fa2f6eb634ad8ee07c487f9ec Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 12 Dec 2022 16:08:59 +0000 Subject: [PATCH 118/567] Update Chromedriver to v108 (#978) --- package.json | 2 +- yarn.lock | 160 ++++++++------------------------------------------- 2 files changed, 24 insertions(+), 138 deletions(-) diff --git a/package.json b/package.json index 219134718a..c10c22366d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^107.0.0", + "chromedriver": "^108.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 52af579d39..40b1631885 100644 --- a/yarn.lock +++ b/yarn.lock @@ -982,27 +982,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" @@ -1552,11 +1531,6 @@ array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - array.prototype.flat@^1.2.5: version "1.3.0" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" @@ -1607,13 +1581,14 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== +axios@^1.1.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" + integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== dependencies: - follow-redirects "^1.14.9" + follow-redirects "^1.15.0" form-data "^4.0.0" + proxy-from-env "^1.1.0" babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" @@ -1892,15 +1867,14 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^107.0.0: - version "107.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-107.0.0.tgz#9443ceb6020190f1a0f96ae6b5fad5453c0cd582" - integrity sha512-/VpGc83szXYUu9gBhCl6tg6XvtVwj2RQjOZ4wDA5TPSqudTMgWcMbkjeZbCfHwReJ9Qqo0hJ1jipG1IXWDxg3g== +chromedriver@^108.0.0: + version "108.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-108.0.0.tgz#7994013f423d8b95a513bb9553a55088de81b252" + integrity sha512-/kb0rb0dlC4RfXh2BOT7RV87K6d+It3VV5YXebLzO5a8t2knNffiTE23XPJQCH+l1xmgoW8/sOX/NB9irskvOQ== dependencies: "@testim/chrome-version" "^1.1.3" - axios "^0.27.2" + axios "^1.1.3" compare-versions "^5.0.1" - del "^6.1.1" extract-zip "^2.0.1" https-proxy-agent "^5.0.1" proxy-from-env "^1.1.0" @@ -2199,20 +2173,6 @@ define-properties@^1.1.3: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -del@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" - integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== - dependencies: - globby "^11.0.1" - graceful-fs "^4.2.4" - is-glob "^4.0.1" - is-path-cwd "^2.2.0" - is-path-inside "^3.0.2" - p-map "^4.0.0" - rimraf "^3.0.2" - slash "^3.0.0" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2262,13 +2222,6 @@ diff@5.0.0, diff@^5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2674,17 +2627,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -2695,13 +2637,6 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -2809,10 +2744,10 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.14.9: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== form-data@^4.0.0: version "4.0.0" @@ -2918,13 +2853,6 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - glob-parent@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" @@ -2932,6 +2860,13 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@7.2.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -2968,18 +2903,6 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.1: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - globule@^1.0.0: version "1.3.3" resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.3.tgz#811919eeac1ab7344e905f2e3be80a13447973c2" @@ -3006,7 +2929,7 @@ got@^11.0.2, got@^11.8.1: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -3295,16 +3218,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-cwd@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-inside@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -3806,11 +3719,6 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -4241,11 +4149,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" @@ -4399,11 +4302,6 @@ query-selector-shadow-dom@^1.0.0: resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz#8fa7459a4620f094457640e74e953a9dbe61a38e" integrity sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg== -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -4580,11 +4478,6 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -4607,13 +4500,6 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - rxjs@^7.2.0, rxjs@^7.5.5: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" From ed1049d0f88e1b6da16db1c6f7fc1833dcf65894 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:26:50 +0000 Subject: [PATCH 119/567] Change GA dataLayer config for social (#974) --- app/survey_config/business_config.py | 12 ----------- app/survey_config/social_survey_config.py | 9 --------- app/survey_config/survey_config.py | 23 +++++++++++----------- tests/app/helpers/test_template_helpers.py | 18 +++++++++++++++-- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index c038cd60ca..b9c4e60435 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -111,18 +111,6 @@ def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]] return None - def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: - data_layer = [{"tx_id": tx_id}] if tx_id else [] - if self.schema: - data_layer.append( - { - key: self.schema.json[key] - for key in ["form_type", "survey_id", "title"] - if key in self.schema.json - } - ) - return data_layer - @property def _stripped_base_url(self) -> str: warn( diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 90149e9655..43dbc0e0c4 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -20,15 +20,6 @@ class SocialSurveyConfig( def __post_init__(self): super().__post_init__() - if self.schema: - self.data_layer: list[dict] = [ - { - key: self.schema.json[key] - for key in ["survey_id", "title"] - if key in self.schema.json - } - ] - upstream_base_url = f"{self.base_url}/{self.language_code}" ons_url = ONS_URL_CY if self.language_code == "cy" else ONS_URL diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 4297a9c05f..9699c21677 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping, Optional, Union +from typing import Iterable, Mapping, MutableMapping, Optional from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString @@ -33,9 +33,6 @@ class SurveyConfig: footer_legal_links: Optional[Iterable[Mapping]] = None survey_title: Optional[LazyString] = None design_system_theme: Optional[str] = None - data_layer: list[dict[str, Union[str, bool]]] = field( - default_factory=list, compare=False - ) sign_out_button_text: str = lazy_gettext("Save and exit survey") contact_us_url: str = field(init=False) cookie_settings_url: str = field(init=False) @@ -74,10 +71,14 @@ def get_footer_legal_links( # pylint: disable=unused-argument, no-self-use ) -> Optional[list[dict]]: return None - def get_data_layer( # pylint: disable=no-self-use - self, tx_id: Optional[str] = None - ) -> list[dict]: - if tx_id: - return [{"tx_id": tx_id}] - - return [] + def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: + data_layer = [{"tx_id": tx_id}] if tx_id else [] + if self.schema: + data_layer.append( + { + key: self.schema.json[key] + for key in ["form_type", "survey_id", "title"] + if key in self.schema.json + } + ) + return data_layer diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 6a4aae4115..6a5e529927 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -773,8 +773,22 @@ def test_correct_survey_title_in_context( ), [{"form_type": "test", "survey_id": "001", "title": "test_title"}], ), - (SurveyType.HEALTH, "en", QuestionnaireSchema({"survey_id": "001"}), []), - (SurveyType.SOCIAL, "en", QuestionnaireSchema({"survey_id": "001"}), []), + ( + SurveyType.HEALTH, + "en", + QuestionnaireSchema( + {"survey_id": "001", "form_type": "test", "title": "test_title"} + ), + [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + ), + ( + SurveyType.SOCIAL, + "en", + QuestionnaireSchema( + {"survey_id": "001", "form_type": "test", "title": "test_title"} + ), + [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + ), ( SurveyType.NORTHERN_IRELAND, "en", From 4fe5390fefb6553a268ddbd6fdc81efb15127f55 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 13 Dec 2022 15:06:26 +0000 Subject: [PATCH 120/567] Schemas v3.26.0 (#979) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 44d6016d4a..07d88dbe5e 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.25.0 +v3.26.0 From b55f8d4587ceb305c9e216811279f85af2af1b42 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:36:36 +0000 Subject: [PATCH 121/567] Schemas v3.27.0 (#981) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 07d88dbe5e..38a4b69555 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.26.0 +v3.27.0 From 32581f29afa2173b4ba779fbb93f754bdc5057bd Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:38:20 +0000 Subject: [PATCH 122/567] Add support for list collectors on section summary (#914) --- app/jinja_filters.py | 125 ++-- app/questionnaire/questionnaire_schema.py | 29 + app/survey_config/business_config.py | 12 +- app/survey_config/census_config.py | 46 +- app/survey_config/link.py | 6 +- app/survey_config/social_survey_config.py | 12 +- app/translations/messages.pot | 223 +++---- .../contexts/calculated_summary_context.py | 19 +- app/views/contexts/list_context.py | 2 +- app/views/contexts/section_summary_context.py | 198 +++--- app/views/contexts/summary/group.py | 66 +- .../contexts/summary/list_collector_block.py | 234 +++++++ mypy.ini | 5 + .../en/test_list_collector_list_summary.json | 523 ++++++++++++++++ .../test_list_collector_section_summary.json | 490 +++++++-------- ...st_collector_variants_section_summary.json | 537 ++++++++++++++++ .../partials/summary/collapsible-summary.html | 17 +- templates/partials/summary/summary.html | 47 +- templates/sectionsummary.html | 1 + tests/app/test_jinja_filters.py | 313 +++++++++- tests/app/views/contexts/conftest.py | 122 ++++ .../contexts/test_section_summary_context.py | 583 +++++++++++++++++- tests/functional/generate_pages.py | 85 ++- tests/functional/spec/list_collector.spec.js | 20 +- .../list_collector_section_summary.spec.js | 203 ++++++ 25 files changed, 3278 insertions(+), 640 deletions(-) create mode 100644 app/views/contexts/summary/list_collector_block.py create mode 100644 schemas/test/en/test_list_collector_list_summary.json create mode 100644 schemas/test/en/test_list_collector_variants_section_summary.json create mode 100644 tests/functional/spec/list_collector_section_summary.spec.js diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 60889febdb..62a64ad75d 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -417,12 +417,12 @@ class SummaryAction: def __init__( self, answer: SelectFieldBase._Option, - answer_title: str, + item_title: str, edit_link_text: str, edit_link_aria_label: str, ) -> None: self.text = edit_link_text - self.ariaLabel = edit_link_aria_label + " " + answer_title + self.ariaLabel = edit_link_aria_label + " " + item_title self.url = answer["link"] self.attributes = { @@ -567,26 +567,43 @@ def __init__( @blueprint.app_template_filter() # type: ignore def map_summary_item_config( - group: SelectFieldBase._Option, + group: dict[str, Union[list, dict]], summary_type: str, answers_are_editable: bool, no_answer_provided: str, edit_link_text: str, edit_link_aria_label: str, - calculated_question: SelectFieldBase._Option, + calculated_question: Optional[dict[str, list]], + icon: Optional[str] = None, ) -> list[SummaryRow]: - rows = [ - SummaryRow( - block["question"], - summary_type, - answers_are_editable, - no_answer_provided, - edit_link_text, - edit_link_aria_label, - ) - for block in group["blocks"] - ] + rows = [] + + for block in group["blocks"]: + if block.get("question"): + rows.append( + SummaryRow( + block["question"], + summary_type, + answers_are_editable, + no_answer_provided, + edit_link_text, + edit_link_aria_label, + ) + ) + else: + list_collector_rows = map_list_collector_config( + list_items=block["list"]["list_items"], + icon=icon, + edit_link_text=edit_link_text, + edit_link_aria_label=edit_link_aria_label, + remove_link_text=flask_babel.lazy_gettext("Remove"), + remove_link_aria_label=flask_babel.lazy_gettext("Remove {item_name}"), + related_answers=block.get("related_answers"), + item_label=block.get("item_label"), + item_anchor=block.get("item_anchor"), + ) + rows.extend(list_collector_rows) if summary_type == "CalculatedSummary": rows.append(SummaryRow(calculated_question, summary_type, False, "", "", "")) @@ -599,15 +616,19 @@ def map_summary_item_config_processor() -> dict[str, Callable]: return dict(map_summary_item_config=map_summary_item_config) +# pylint: disable=too-many-locals @blueprint.app_template_filter() # type: ignore def map_list_collector_config( list_items: list[dict[str, Union[str, int]]], icon: str, - edit_link_text: Optional[str] = None, - edit_link_aria_label: Optional[str] = None, + edit_link_text: str = "", + edit_link_aria_label: str = "", remove_link_text: Optional[str] = None, remove_link_aria_label: Optional[str] = None, -) -> list[dict[str, list[dict[str, Any]]]]: + related_answers: Optional[dict] = None, + item_label: Optional[str] = None, + item_anchor: Optional[str] = None, +) -> list[dict[str, list]]: rows = [] for index, list_item in enumerate(list_items, start=1): @@ -618,19 +639,26 @@ def map_list_collector_config( remove_link_aria_label_text = None if edit_link_text: + url = ( + f'{list_item.get("edit_link")}{item_anchor}' + if item_anchor + else list_item.get("edit_link") + ) + + edit_link = { + "text": edit_link_text, + "ariaLabel": edit_link_aria_label_text, + "url": url, + "attributes": {"data-qa": f"list-item-change-{index}-link"}, + } + if edit_link_aria_label: edit_link_aria_label_text = edit_link_aria_label.format( item_name=item_name ) + edit_link["ariaLabel"] = edit_link_aria_label_text - actions.append( - { - "text": edit_link_text, - "ariaLabel": edit_link_aria_label_text, - "url": list_item.get("edit_link"), - "attributes": {"data-qa": f"list-item-change-{index}-link"}, - } - ) + actions.append(edit_link) if not list_item.get("primary_person") and remove_link_text: if remove_link_aria_label: @@ -647,22 +675,35 @@ def map_list_collector_config( } ) - rows.append( - { - "rowItems": [ - { - "iconType": icon, - "actions": actions, - "rowTitle": item_name, - "id": list_item.get("list_item_id"), - "rowTitleAttributes": { - "data-qa": f"list-item-{index}-label", - "data-list-item-id": list_item.get("list_item_id"), - }, - } - ] - } - ) + row_item = { + "iconType": icon, + "actions": actions, + "id": list_item.get("list_item_id"), + "rowTitleAttributes": { + "data-qa": f"list-item-{index}-label", + "data-list-item-id": list_item.get("list_item_id"), + }, + } + + if item_label: + row_item["valueList"] = [{"text": item_name}] + + row_item["rowTitle"] = item_label or item_name + row_items: list = [row_item] + + if related_answers: + for block in related_answers[list_item["list_item_id"]]: + summary_row = SummaryRow( + block["question"], + summary_type="SectionSummary", + answers_are_editable=True, + no_answer_provided=flask_babel.lazy_gettext("No answer provided"), + edit_link_text=edit_link_text, + edit_link_aria_label=edit_link_aria_label, + ) + row_items.extend(summary_row.rowItems) + + rows.append({"rowItems": row_items}) return rows diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 580871fb0f..5a8130d856 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -888,3 +888,32 @@ def _get_rules_section_dependencies( ) return rules_section_dependencies + + def get_summary_item_for_list_for_section( + self, *, section_id: str, list_name: str + ) -> Optional[ImmutableDict]: + if summary := self.get_summary_for_section(section_id): + for item in summary.get("items", []): + if item.get("for_list") == list_name: + return item # type: ignore + + def get_related_answers_for_list_for_section( + self, *, section_id: str, list_name: str + ) -> Optional[tuple[ImmutableDict]]: + if item := self.get_summary_item_for_list_for_section( + section_id=section_id, list_name=list_name + ): + return item.get("related_answers") + + def get_item_label(self, section_id: str, list_name: str) -> Optional[str]: + if summary := self.get_summary_for_section(section_id): + for item in summary.get("items", []): + if item["for_list"] == list_name and item.get("item_label"): + + return str(item["item_label"]) + + def get_item_anchor(self, section_id: str, list_name: str) -> Optional[str]: + if summary := self.get_summary_for_section(section_id): + for item in summary.get("items", []): + if item["for_list"] == list_name and item.get("item_anchor_answer_id"): + return f"#{str(item['item_anchor_answer_id'])}" diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index b9c4e60435..5af1d543b0 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -85,16 +85,18 @@ def get_service_links( return links def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__] + links = [Link(lazy_gettext("What we do"), self.what_we_do_url).as_dict()] if cookie_has_theme: - links.append(Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__) + links.append( + Link(lazy_gettext("Contact us"), self.contact_us_url).as_dict() + ) links.append( Link( lazy_gettext("Accessibility"), self.accessibility_url, - ).__dict__ + ).as_dict() ) return links @@ -102,11 +104,11 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: if cookie_has_theme: return [ - Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, + Link(lazy_gettext("Cookies"), self.cookie_settings_url).as_dict(), Link( lazy_gettext("Privacy and data protection"), self.privacy_and_data_protection_url, - ).__dict__, + ).as_dict(), ] return None diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index decf852fdb..d96d95009f 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -38,12 +38,12 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: Link( lazy_gettext("Help"), f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", - ).__dict__ + ).as_dict() ] if cookie_has_theme: links.append( - Link(lazy_gettext("Contact us"), f"{EN_BASE_URL}/contact-us/").__dict__ + Link(lazy_gettext("Contact us"), f"{EN_BASE_URL}/contact-us/").as_dict() ) links.extend( @@ -51,11 +51,11 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: Link( lazy_gettext("Languages"), f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("BSL and audio videos"), f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", - ).__dict__, + ).as_dict(), ] ) @@ -64,19 +64,19 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: if cookie_has_theme: return [ - Link(lazy_gettext("Cookies"), f"{EN_BASE_URL}/cookies/").__dict__, + Link(lazy_gettext("Cookies"), f"{EN_BASE_URL}/cookies/").as_dict(), Link( lazy_gettext("Accessibility statement"), f"{EN_BASE_URL}/accessibility-statement/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Privacy and data protection"), f"{EN_BASE_URL}/privacy-and-data-protection/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Terms and conditions"), f"{EN_BASE_URL}/terms-and-conditions/", - ).__dict__, + ).as_dict(), ] return None @@ -95,25 +95,25 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: Link( lazy_gettext("Help"), f"{CY_BASE_URL}/help/sut-i-ateb-y-cwestiynau/help-y-cwestiynau-ar-lein/", - ).__dict__ + ).as_dict() ] if cookie_has_theme: links.append( Link( lazy_gettext("Contact us"), f"{CY_BASE_URL}/cysylltu-a-ni/" - ).__dict__ + ).as_dict() ) links.extend( [ Link( lazy_gettext("Languages"), f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("BSL and audio videos"), f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", - ).__dict__, + ).as_dict(), ] ) return links @@ -121,19 +121,19 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: if cookie_has_theme: return [ - Link(lazy_gettext("Cookies"), f"{CY_BASE_URL}/cwcis/").__dict__, + Link(lazy_gettext("Cookies"), f"{CY_BASE_URL}/cwcis/").as_dict(), Link( lazy_gettext("Accessibility statement"), f"{CY_BASE_URL}/datganiad-hygyrchedd/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Privacy and data protection"), f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Terms and conditions"), f"{CY_BASE_URL}/telerau-ac-amodau/", - ).__dict__, + ).as_dict(), ] return None @@ -161,12 +161,14 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: Link( lazy_gettext("Help"), f"{NIR_BASE_URL}/help/help-with-the-questions/online-questions-help/", - ).__dict__ + ).as_dict() ] if cookie_has_theme: links.append( - Link(lazy_gettext("Contact us"), f"{NIR_BASE_URL}/contact-us/").__dict__ + Link( + lazy_gettext("Contact us"), f"{NIR_BASE_URL}/contact-us/" + ).as_dict() ) return links @@ -174,19 +176,19 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: if cookie_has_theme: return [ - Link(lazy_gettext("Cookies"), f"{NIR_BASE_URL}/cookies/").__dict__, + Link(lazy_gettext("Cookies"), f"{NIR_BASE_URL}/cookies/").as_dict(), Link( lazy_gettext("Accessibility statement"), f"{NIR_BASE_URL}/accessibility-statement/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Privacy and data protection"), f"{NIR_BASE_URL}/privacy-and-data-protection/", - ).__dict__, + ).as_dict(), Link( lazy_gettext("Terms and conditions"), f"{NIR_BASE_URL}/terms-and-conditions/", - ).__dict__, + ).as_dict(), ] return None diff --git a/app/survey_config/link.py b/app/survey_config/link.py index cb5d05eaf2..155117c344 100644 --- a/app/survey_config/link.py +++ b/app/survey_config/link.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import Optional from flask_babel.speaklater import LazyString @@ -9,6 +9,10 @@ class Link: text: LazyString url: str target: Optional[str] = "_blank" + attributes: Optional[dict] = field(default_factory=dict) + + def as_dict(self): + return {k: v for k, v in self.__dict__.items() if v} @dataclass diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 43dbc0e0c4..4b21e7cb97 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -36,16 +36,18 @@ def __post_init__(self): self.what_we_do_url: str = f"{ons_url}/aboutus/whatwedo/" def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [Link(lazy_gettext("What we do"), self.what_we_do_url).__dict__] + links = [Link(lazy_gettext("What we do"), self.what_we_do_url).as_dict()] if cookie_has_theme: - links.append(Link(lazy_gettext("Contact us"), self.contact_us_url).__dict__) + links.append( + Link(lazy_gettext("Contact us"), self.contact_us_url).as_dict() + ) links.append( Link( lazy_gettext("Accessibility"), self.accessibility_url, - ).__dict__ + ).as_dict() ) return links @@ -53,11 +55,11 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: if cookie_has_theme: return [ - Link(lazy_gettext("Cookies"), self.cookie_settings_url).__dict__, + Link(lazy_gettext("Cookies"), self.cookie_settings_url).as_dict(), Link( lazy_gettext("Privacy and data protection"), self.privacy_and_data_protection_url, - ).__dict__, + ).as_dict(), ] return None diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 9cf8a04e17..303808d3b5 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-11-10 16:01+0000\n" +"POT-Creation-Date: 2022-11-29 21:21+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,6 +41,20 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" +#: app/jinja_filters.py:600 templates/partials/summary/list-summary.html:9 +msgid "Remove" +msgstr "" + +#: app/jinja_filters.py:601 templates/partials/summary/list-summary.html:10 +msgid "Remove {item_name}" +msgstr "" + +#: app/jinja_filters.py:702 +#: templates/partials/summary/collapsible-summary.html:36 +#: templates/partials/summary/summary.html:39 +msgid "No answer provided" +msgstr "" + #: app/forms/error_messages.py:11 app/forms/error_messages.py:12 #: app/forms/error_messages.py:13 app/forms/error_messages.py:14 msgid "Enter an answer" @@ -211,7 +225,7 @@ msgstr "" msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:152 +#: app/helpers/template_helpers.py:149 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -235,108 +249,108 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:136 +#: app/routes/errors.py:135 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:139 +#: app/routes/errors.py:138 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:155 +#: app/routes/errors.py:154 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:158 +#: app/routes/errors.py:157 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:190 +#: app/routes/errors.py:189 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:196 +#: app/routes/errors.py:195 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:199 app/routes/errors.py:222 app/routes/errors.py:244 +#: app/routes/errors.py:198 app/routes/errors.py:221 app/routes/errors.py:243 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:218 +#: app/routes/errors.py:217 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:219 +#: app/routes/errors.py:218 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:240 templates/errors/403.html:3 +#: app/routes/errors.py:239 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:241 +#: app/routes/errors.py:240 msgid "You can try to submit your feedback again." msgstr "" -#: app/routes/individual_response.py:177 +#: app/routes/individual_response.py:184 msgid "An individual access code has been sent by post" msgstr "" -#: app/routes/individual_response.py:271 +#: app/routes/individual_response.py:278 msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:58 +#: app/survey_config/business_config.py:59 #: app/survey_config/census_config.py:39 app/survey_config/census_config.py:96 -#: app/survey_config/census_config.py:166 +#: app/survey_config/census_config.py:162 msgid "Help" msgstr "" -#: app/survey_config/business_config.py:72 +#: app/survey_config/business_config.py:73 msgid "My account" msgstr "" -#: app/survey_config/business_config.py:77 +#: app/survey_config/business_config.py:78 msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:87 -#: app/survey_config/social_survey_config.py:36 +#: app/survey_config/business_config.py:88 +#: app/survey_config/social_survey_config.py:48 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:90 +#: app/survey_config/business_config.py:91 #: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 -#: app/survey_config/census_config.py:173 -#: app/survey_config/social_survey_config.py:39 +#: app/survey_config/census_config.py:169 +#: app/survey_config/social_survey_config.py:51 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:94 -#: app/survey_config/social_survey_config.py:43 +#: app/survey_config/business_config.py:95 +#: app/survey_config/social_survey_config.py:55 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:104 +#: app/survey_config/business_config.py:105 #: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:181 -#: app/survey_config/social_survey_config.py:53 +#: app/survey_config/census_config.py:177 +#: app/survey_config/social_survey_config.py:65 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:106 +#: app/survey_config/business_config.py:107 #: app/survey_config/census_config.py:73 app/survey_config/census_config.py:130 -#: app/survey_config/census_config.py:187 -#: app/survey_config/social_survey_config.py:55 +#: app/survey_config/census_config.py:183 +#: app/survey_config/social_survey_config.py:67 msgid "Privacy and data protection" msgstr "" @@ -357,24 +371,24 @@ msgid "BSL and audio videos" msgstr "" #: app/survey_config/census_config.py:69 app/survey_config/census_config.py:126 -#: app/survey_config/census_config.py:183 +#: app/survey_config/census_config.py:179 msgid "Accessibility statement" msgstr "" #: app/survey_config/census_config.py:77 app/survey_config/census_config.py:134 -#: app/survey_config/census_config.py:191 +#: app/survey_config/census_config.py:187 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:152 +#: app/survey_config/census_config.py:150 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:155 app/survey_config/survey_config.py:19 +#: app/survey_config/census_config.py:153 app/survey_config/survey_config.py:20 msgid "Use of address data is subject to the terms and conditions." msgstr "" -#: app/survey_config/survey_config.py:16 +#: app/survey_config/survey_config.py:17 msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" @@ -442,7 +456,7 @@ msgstr "" #: templates/individual_response/confirmation-post.html:21 #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/sectionsummary.html:10 templates/sectionsummary.html:49 +#: templates/sectionsummary.html:10 templates/sectionsummary.html:50 msgid "Continue" msgstr "" @@ -458,7 +472,7 @@ msgstr "" msgid "{date} at {time}" msgstr "" -#: app/views/contexts/submission_metadata_context.py:24 +#: app/views/contexts/submission_metadata_context.py:25 msgid "Submission reference:" msgstr "" @@ -474,53 +488,53 @@ msgstr "" msgid "Please submit this survey to complete it" msgstr "" -#: app/views/contexts/thank_you_context.py:27 +#: app/views/contexts/thank_you_context.py:28 msgid "Your answers have been submitted." msgstr "" -#: app/views/contexts/thank_you_context.py:29 +#: app/views/contexts/thank_you_context.py:30 msgid "" "Your answers have been submitted for {company_name} " "({trading_name})" msgstr "" -#: app/views/contexts/thank_you_context.py:36 +#: app/views/contexts/thank_you_context.py:37 msgid "Your answers have been submitted for {company_name}" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:29 +#: app/views/contexts/view_submitted_response_context.py:37 msgid "Answers submitted." msgstr "" -#: app/views/contexts/view_submitted_response_context.py:31 +#: app/views/contexts/view_submitted_response_context.py:39 msgid "Answers submitted for {ru_name} ({trad_as})" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:35 +#: app/views/contexts/view_submitted_response_context.py:43 msgid "Answers submitted for {ru_name}" msgstr "" -#: app/views/handlers/confirm_email.py:37 +#: app/views/handlers/confirm_email.py:38 msgid "Yes, send the confirmation email" msgstr "" -#: app/views/handlers/confirm_email.py:68 +#: app/views/handlers/confirm_email.py:69 msgid "Confirm your email address" msgstr "" -#: app/views/handlers/confirm_email.py:88 +#: app/views/handlers/confirm_email.py:89 msgid "Is this email address correct?" msgstr "" -#: app/views/handlers/confirm_email.py:101 #: app/views/handlers/confirm_email.py:102 -#: app/views/handlers/individual_response.py:877 +#: app/views/handlers/confirm_email.py:103 +#: app/views/handlers/individual_response.py:878 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:128 +#: app/views/handlers/confirm_email.py:129 #: app/views/handlers/confirmation_email.py:68 -#: app/views/handlers/feedback.py:79 app/views/handlers/question.py:165 +#: app/views/handlers/feedback.py:83 app/views/handlers/question.py:165 msgid "Error: {page_title}" msgstr "" @@ -528,176 +542,176 @@ msgstr "" msgid "Confirmation email" msgstr "" -#: app/views/handlers/feedback.py:40 +#: app/views/handlers/feedback.py:44 msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:123 +#: app/views/handlers/feedback.py:140 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:129 app/views/handlers/feedback.py:153 +#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:170 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:132 app/views/handlers/feedback.py:133 +#: app/views/handlers/feedback.py:149 app/views/handlers/feedback.py:150 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:134 +#: app/views/handlers/feedback.py:151 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:139 app/views/handlers/feedback.py:140 +#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:157 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:143 app/views/handlers/feedback.py:146 +#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:163 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:161 app/views/handlers/feedback.py:171 +#: app/views/handlers/feedback.py:178 app/views/handlers/feedback.py:188 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:162 +#: app/views/handlers/feedback.py:179 msgid "Do not include confidential information, such as your contact details" msgstr "" -#: app/views/handlers/individual_response.py:139 +#: app/views/handlers/individual_response.py:140 msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:251 +#: app/views/handlers/individual_response.py:252 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:321 +#: app/views/handlers/individual_response.py:322 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:344 +#: app/views/handlers/individual_response.py:345 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:346 +#: app/views/handlers/individual_response.py:347 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:354 +#: app/views/handlers/individual_response.py:355 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:356 +#: app/views/handlers/individual_response.py:357 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:365 +#: app/views/handlers/individual_response.py:366 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:367 +#: app/views/handlers/individual_response.py:368 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:370 +#: app/views/handlers/individual_response.py:371 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:416 +#: app/views/handlers/individual_response.py:417 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:447 +#: app/views/handlers/individual_response.py:448 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:462 +#: app/views/handlers/individual_response.py:463 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:468 +#: app/views/handlers/individual_response.py:469 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:472 +#: app/views/handlers/individual_response.py:473 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:478 +#: app/views/handlers/individual_response.py:479 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:520 +#: app/views/handlers/individual_response.py:521 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:585 +#: app/views/handlers/individual_response.py:586 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:593 +#: app/views/handlers/individual_response.py:594 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:600 +#: app/views/handlers/individual_response.py:601 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:613 +#: app/views/handlers/individual_response.py:614 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:619 +#: app/views/handlers/individual_response.py:620 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:652 +#: app/views/handlers/individual_response.py:653 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:707 -#: app/views/handlers/individual_response.py:745 +#: app/views/handlers/individual_response.py:708 +#: app/views/handlers/individual_response.py:746 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:711 +#: app/views/handlers/individual_response.py:712 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:769 +#: app/views/handlers/individual_response.py:770 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:781 +#: app/views/handlers/individual_response.py:782 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:782 +#: app/views/handlers/individual_response.py:783 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:815 +#: app/views/handlers/individual_response.py:816 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:864 +#: app/views/handlers/individual_response.py:865 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:873 +#: app/views/handlers/individual_response.py:874 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:911 +#: app/views/handlers/individual_response.py:912 msgid "Confirm mobile number" msgstr "" @@ -1490,12 +1504,12 @@ msgid "Show" msgstr "" #: templates/partials/introduction/preview.html:48 -#: templates/partials/summary/collapsible-summary.html:58 +#: templates/partials/summary/collapsible-summary.html:59 msgid "Show all" msgstr "" #: templates/partials/introduction/preview.html:49 -#: templates/partials/summary/collapsible-summary.html:59 +#: templates/partials/summary/collapsible-summary.html:60 msgid "Hide all" msgstr "" @@ -1503,19 +1517,14 @@ msgstr "" msgid "Start survey" msgstr "" -#: templates/partials/summary/collapsible-summary.html:36 -#: templates/partials/summary/summary.html:19 -msgid "No answer provided" -msgstr "" - #: templates/partials/summary/collapsible-summary.html:37 #: templates/partials/summary/list-summary.html:7 -#: templates/partials/summary/summary.html:20 +#: templates/partials/summary/summary.html:40 msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:38 -#: templates/partials/summary/summary.html:21 +#: templates/partials/summary/summary.html:41 msgid "Change your answer for:" msgstr "" @@ -1523,11 +1532,3 @@ msgstr "" msgid "Change details for {item_name}" msgstr "" -#: templates/partials/summary/list-summary.html:9 -msgid "Remove" -msgstr "" - -#: templates/partials/summary/list-summary.html:10 -msgid "Remove {item_name}" -msgstr "" - diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 7dc2841aef..c80ff98eee 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -23,15 +23,16 @@ def build_groups_for_section(self, section, return_to_block_id): return [ Group( - group, - routing_path, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, - self._schema, - location, - self._language, + group_schema=group, + routing_path=routing_path, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=location, + language=self._language, + progress_store=self._progress_store, return_to="calculated-summary", return_to_block_id=return_to_block_id, ).serialize() diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index d1d608e34a..1322045731 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -3,7 +3,7 @@ from flask import url_for from flask_babel import lazy_gettext -from . import Context +from app.views.contexts.context import Context class ListContext(Context): diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 3b6b2d1ad9..f2df8efdc9 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -1,8 +1,6 @@ from functools import cached_property from typing import Mapping, Optional -from flask import url_for - from app.data_models import AnswerStore, ListStore, ProgressStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location @@ -11,8 +9,8 @@ from ...data_models.metadata_proxy import MetadataProxy from .context import Context -from .list_context import ListContext from .summary import Group +from .summary.list_collector_block import ListCollectorBlock class SectionSummaryContext(Context): @@ -67,18 +65,6 @@ def __call__(self, return_to: Optional[str] = "section-summary") -> Mapping: def section(self): return self._schema.get_section(self.current_location.section_id) - @property - def list_context(self): - return ListContext( - self._language, - self._schema, - self._answer_store, - self._list_store, - self._progress_store, - self._metadata, - self._response_metadata, - ) - def get_page_title(self, title_for_location: str) -> str: section_repeating_page_title = ( @@ -109,7 +95,9 @@ def _build_summary(self, return_to: Optional[str]): summary = self.section.get("summary", {}) collapsible = {"collapsible": summary.get("collapsible", False)} - if summary.get("items"): + show_non_item_answers = summary.get("show_non_item_answers", False) + + if summary.get("items") and not show_non_item_answers: summary_elements = { "custom_summary": list( self._custom_summary_elements( @@ -118,122 +106,108 @@ def _build_summary(self, return_to: Optional[str]): ) } - return {**collapsible, **summary_elements} + return collapsible | summary_elements - return { + refactored_groups = self._get_refactored_groups(self.section["groups"]) + + groups = { **collapsible, "groups": [ Group( - group, - self.routing_path, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, - self._schema, - self.current_location, - self._language, - return_to, + group_schema=group, + routing_path=self.routing_path, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=self.current_location, + language=self._language, + progress_store=self._progress_store, + return_to=return_to, + return_to_block_id=None, ).serialize() - for group in self.section["groups"] + for group in refactored_groups ], } + return groups + def _title_for_location(self): section_id = self.current_location.section_id - title = ( + return ( self._schema.get_repeating_title_for_section(section_id) or self._schema.get_summary_title_for_section(section_id) or self._schema.get_title_for_section(section_id) ) - return title def _custom_summary_elements(self, section_summary): for summary_element in section_summary: if summary_element["type"] == "List": - yield self._list_summary_element(summary_element) - - def _list_summary_element(self, summary) -> Mapping: - list_collector_block = None - edit_block_id, remove_block_id, primary_person_edit_block_id = None, None, None - current_list = self._list_store[summary["for_list"]] - - list_collector_blocks = list( - self._schema.get_list_collectors_for_list( - self.section, for_list=summary["for_list"] - ) - ) - - list_collector_blocks_on_path = [ - list_collector_block - for list_collector_block in list_collector_blocks - if list_collector_block["id"] in self.routing_path.block_ids - ] - - if list_collector_blocks_on_path: - list_collector_block = list_collector_blocks_on_path[0] - edit_block_id = list_collector_block["edit_block"]["id"] - remove_block_id = list_collector_block["remove_block"]["id"] - - add_link = self._add_link(summary, list_collector_block) - - if len(current_list) == 1 and current_list.primary_person: - - if primary_person_block := self._schema.get_list_collector_for_list( - self.section, for_list=summary["for_list"], primary=True - ): - primary_person_edit_block_id = primary_person_block[ - "add_or_edit_block" - ]["id"] - edit_block_id = primary_person_block["add_or_edit_block"]["id"] - - rendered_summary = self._placeholder_renderer.render( - summary, self.current_location.list_item_id - ) - - list_collector_block = list_collector_block or list_collector_blocks[0] - - list_summary_context = self.list_context( - list_collector_block["summary"], - for_list=list_collector_block["for_list"], - return_to="section-summary", - edit_block_id=edit_block_id, - remove_block_id=remove_block_id, - primary_person_edit_block_id=primary_person_edit_block_id, - ) - - return { - "title": rendered_summary["title"], - "type": rendered_summary["type"], - "add_link": add_link, - "add_link_text": rendered_summary["add_link_text"], - "empty_list_text": rendered_summary.get("empty_list_text"), - "list_name": rendered_summary["for_list"], - **list_summary_context, - } - - def _add_link(self, summary, list_collector_block): - - if list_collector_block: - return url_for( - "questionnaire.block", - list_name=summary["for_list"], - block_id=list_collector_block["add_block"]["id"], - return_to="section-summary", - ) - - driving_question_block = QuestionnaireSchema.get_driving_question_for_list( - self.section, summary["for_list"] - ) - - if driving_question_block: - return url_for( - "questionnaire.block", - block_id=driving_question_block["id"], - return_to="section-summary", - ) + list_collector_block = ListCollectorBlock( + routing_path=self.routing_path, + answer_store=self._answer_store, + list_store=self._list_store, + progress_store=self._progress_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=self.current_location, + language=self._language, + ) + yield list_collector_block.list_summary_element(summary_element) def _get_safe_page_title(self, title): return ( safe_content(self._schema.get_single_string_value(title)) if title else "" ) + + @staticmethod + def _get_refactored_groups(original_groups: dict) -> list[dict]: + """original schema groups are refactored into groups based on block types, it follows the order/sequence of blocks in the original groups, all the + non list collector blocks are put together into groups, list collectors are put into separate groups, this way summary groups are displayed correctly + on section summary""" + refactored_groups = [] + group_number = 0 + + for group in list(original_groups): + group_name = group["id"] + non_list_collector_blocks: list[dict[str, str]] = [] + list_collector_blocks: list[dict[str, str]] = [] + for block in group["blocks"]: + if block["type"] == "ListCollector": + # if list collector block encountered, close the previously started non list collector blocks list if exists + if non_list_collector_blocks: + previously_started_group = { + "id": f"{group_name}-{group_number}", + "blocks": non_list_collector_blocks, + } + # add previous non list collector blocks group to all groups and increase the group number for the list collector group + # that you handle next + refactored_groups.append(previously_started_group) + group_number += 1 + list_collector_blocks.append(block) + list_collector_group = { + "id": f"{group_name}-{group_number}", + "blocks": list_collector_blocks, + } + # add current list collector group to all groups and increase the group number for the next group + refactored_groups.append(list_collector_group) + group_number += 1 + # reset both types of block lists for next iterations of this loop if any + list_collector_blocks = [] + non_list_collector_blocks = [] + + else: + # if list collector not encountered keep adding blocks or add first one to an empty non list collector blocks list + non_list_collector_blocks.append(block) + + # on exiting the loop, accumulated list of blocks gets added as a group + non_list_collector_group = { + "id": f"{group_name}-{group_number}", + "blocks": non_list_collector_blocks, + "title": group.get("title"), + } + refactored_groups.append(non_list_collector_group) + + return refactored_groups diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index abb938c487..b99322da30 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,12 +1,15 @@ -from typing import Optional +from typing import Mapping, Optional from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.survey_config.link import Link from app.views.contexts.summary.block import Block +from app.views.contexts.summary.list_collector_block import ListCollectorBlock class Group: def __init__( self, + *, group_schema, routing_path, answer_store, @@ -16,13 +19,16 @@ def __init__( schema, location, language, + progress_store, return_to, return_to_block_id: Optional[str] = None, ): self.id = group_schema["id"] self.title = group_schema.get("title") self.location = location - self.blocks = self._build_blocks( + self.placeholder_text = None + self.links: Mapping[str, Link] = {} + self.blocks = self._build_blocks_and_links( group_schema=group_schema, routing_path=routing_path, answer_store=answer_store, @@ -32,6 +38,8 @@ def __init__( schema=schema, location=location, return_to=return_to, + progress_store=progress_store, + language=language, return_to_block_id=return_to_block_id, ) self.placeholder_renderer = PlaceholderRenderer( @@ -43,8 +51,9 @@ def __init__( schema=schema, ) - @staticmethod - def _build_blocks( + # pylint: disable=too-many-locals + def _build_blocks_and_links( + self, *, group_schema, routing_path, @@ -55,12 +64,19 @@ def _build_blocks( schema, location, return_to, + progress_store, + language, return_to_block_id, ): blocks = [] for block in group_schema["blocks"]: - if block["id"] in routing_path and block["type"] == "Question": + if block["id"] not in routing_path: + continue + if block["type"] in [ + "Question", + "ListCollectorDrivingQuestion", + ]: blocks.extend( [ Block( @@ -77,10 +93,48 @@ def _build_blocks( ] ) + elif block["type"] == "ListCollector": + section = schema.get_section(location.section_id) + + if summary_item := schema.get_summary_item_for_list_for_section( + section_id=section["id"], + list_name=block["for_list"], + ): + list_collector_block = ListCollectorBlock( + routing_path=routing_path, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=metadata, + response_metadata=response_metadata, + schema=schema, + location=location, + language=language, + ) + + list_summary_element = list_collector_block.list_summary_element( + summary_item + ) + blocks.extend([list_summary_element]) + self.links["add_link"] = Link( + target="_self", + text=list_summary_element["add_link_text"], + url=list_summary_element["add_link"], + attributes={"data-qa": "add-item-link"}, + ) + + self.placeholder_text = list_summary_element["empty_list_text"] + return blocks def serialize(self): return self.placeholder_renderer.render( - {"id": self.id, "title": self.title, "blocks": self.blocks}, + { + "id": self.id, + "title": self.title, + "blocks": self.blocks, + "links": self.links, + "placeholder_text": self.placeholder_text, + }, self.location.list_item_id, ) diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py new file mode 100644 index 0000000000..4d83019a66 --- /dev/null +++ b/app/views/contexts/summary/list_collector_block.py @@ -0,0 +1,234 @@ +from collections import defaultdict +from typing import Any, Mapping, Optional + +from flask import url_for +from werkzeug.datastructures import ImmutableDict + +from app.data_models import AnswerStore, ProgressStore +from app.data_models.list_store import ListModel, ListStore +from app.data_models.metadata_proxy import MetadataProxy +from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.questionnaire.routing_path import RoutingPath +from app.views.contexts.list_context import ListContext +from app.views.contexts.summary.block import Block + + +class ListCollectorBlock: + def __init__( + self, + routing_path: RoutingPath, + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: MetadataProxy, + response_metadata: Mapping, + schema: QuestionnaireSchema, + location: Location, + language: str, + ): + self._location = location + self._placeholder_renderer = PlaceholderRenderer( + language=language, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, + schema=schema, + ) + self._list_store = list_store + self._schema = schema + self._location = location + # type ignore added as section should exist + self._section: ImmutableDict = self._schema.get_section(self._location.section_id) # type: ignore + self._language = language + self._answer_store = answer_store + self._metadata = metadata + self._response_metadata = response_metadata + self._routing_path = routing_path + self._progress_store = progress_store + + # pylint: disable=too-many-locals + def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: + list_collector_block = None + ( + edit_block_id, + remove_block_id, + primary_person_edit_block_id, + related_answers, + item_label, + item_anchor, + ) = (None, None, None, None, None, None) + current_list = self._list_store[summary["for_list"]] + + list_collector_blocks = list( + self._schema.get_list_collectors_for_list( + self._section, for_list=summary["for_list"] + ) + ) + + add_link = self._add_link(summary, list_collector_block) + + list_collector_blocks_on_path = [ + list_collector_block + for list_collector_block in list_collector_blocks + if list_collector_block["id"] in self._routing_path.block_ids + ] + + list_collector_block = ( + list_collector_blocks_on_path[0] + if list_collector_blocks_on_path + else list_collector_blocks[0] + ) + + rendered_summary = self._placeholder_renderer.render( + summary, self._location.list_item_id + ) + + section_id = self._section["id"] + if list_collector_blocks_on_path: + edit_block_id = list_collector_block["edit_block"]["id"] + remove_block_id = list_collector_block["remove_block"]["id"] + add_link = self._add_link(summary, list_collector_block) + related_answers = self._get_related_answers(current_list) + item_anchor = self._schema.get_item_anchor(section_id, current_list.name) + item_label = self._schema.get_item_label(section_id, current_list.name) + + if len(current_list) == 1 and current_list.primary_person: + if primary_person_block := self._schema.get_list_collector_for_list( + self._section, for_list=summary["for_list"], primary=True + ): + primary_person_edit_block_id = edit_block_id = primary_person_block[ + "add_or_edit_block" + ]["id"] + + list_summary_context = self.list_context( + list_collector_block["summary"], + for_list=list_collector_block["for_list"], + return_to="section-summary", + edit_block_id=edit_block_id, + remove_block_id=remove_block_id, + primary_person_edit_block_id=primary_person_edit_block_id, + ) + + return { + "title": rendered_summary["title"], + "type": rendered_summary["type"], + "add_link": add_link, + "add_link_text": rendered_summary["add_link_text"], + "empty_list_text": rendered_summary.get("empty_list_text"), + "list_name": rendered_summary["for_list"], + "related_answers": related_answers, + "item_label": item_label, + "item_anchor": item_anchor, + **list_summary_context, + } + + @property + def list_context(self) -> ListContext: + return ListContext( + self._language, + self._schema, + self._answer_store, + self._list_store, + self._progress_store, + self._metadata, + self._response_metadata, + ) + + def _add_link( + self, + summary: Mapping[str, Any], + list_collector_block: Optional[Mapping[str, Any]], + ) -> Optional[str]: + + if list_collector_block: + return url_for( + "questionnaire.block", + list_name=summary["for_list"], + block_id=list_collector_block["add_block"]["id"], + return_to="section-summary", + ) + + if driving_question_block := self._schema.get_driving_question_for_list( + self._section, summary["for_list"] + ): + return url_for( + "questionnaire.block", + block_id=driving_question_block["id"], + return_to="section-summary", + ) + + def _get_related_answers( + self, list_model: ListModel + ) -> Optional[dict[str, list[Block]]]: + section_id = self._section["id"] + + related_answers = self._schema.get_related_answers_for_list_for_section( + section_id=section_id, list_name=list_model.name + ) + if not related_answers: + return None + + related_answers_blocks = {} + + blocks = self.get_blocks_for_related_answers(related_answers) + + for list_id in list_model: + serialized_blocks = [ + Block( + block, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=Location( + list_name=list_model.name, + list_item_id=list_id, + section_id=section_id, + ), + return_to="section-summary", + return_to_block_id=None, + ).serialize() + for block in blocks + ] + + related_answers_blocks[list_id] = serialized_blocks + + return related_answers_blocks + + def get_blocks_for_related_answers( + self, related_answers: tuple + ) -> list[Optional[ImmutableDict]]: + blocks = [] + answers_by_block = defaultdict(list) + + for answer in related_answers: + answer_id = answer["identifier"] + # block is not optional at this point + block: Mapping = self._schema.get_block_for_answer_id(answer_id) # type: ignore + + block_to_keep = ( + block["edit_block"] if block["type"] == "ListCollector" else block + ) + answers_by_block[block_to_keep].append(answer_id) + + for immutable_block, answer_ids in answers_by_block.items(): + mutable_block = self._schema.get_mutable_deepcopy(immutable_block) + + # We need to filter out answers for both variants and normal questions + for variant_or_block in mutable_block.get( + "question_variants", [mutable_block] + ): + answers = [ + answer + for answer in variant_or_block["question"].get("answers", {}) + if answer["id"] in answer_ids + ] + # Mutate the answers to only keep the related answers + variant_or_block["question"]["answers"] = answers + + blocks.append(mutable_block) + + return blocks diff --git a/mypy.ini b/mypy.ini index 645d74fefc..d822e9dbad 100644 --- a/mypy.ini +++ b/mypy.ini @@ -61,6 +61,11 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True +[mypy-app.views.contexts.summary.list_collector_block] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True + [mypy-app.authentication.*] disallow_untyped_defs = True warn_return_any = True diff --git a/schemas/test/en/test_list_collector_list_summary.json b/schemas/test/en/test_list_collector_list_summary.json new file mode 100644 index 0000000000..a7637c7bf7 --- /dev/null +++ b/schemas/test/en/test_list_collector_list_summary.json @@ -0,0 +1,523 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test ListCollector", + "theme": "default", + "description": "A questionnaire to test ListCollector", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "display_address", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": {} + }, + "individual_response": { + "for_list": "people", + "individual_section_id": "section" + }, + "sections": [ + { + "id": "section", + "title": "People who live here and overnight visitors", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "people", + "title": { + "text": "Household members staying overnight on {census_date} at {household_address}", + "placeholders": [ + { + "placeholder": "census_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "value": "2019-10-13" + } + }, + "transform": "format_date" + } + ] + }, + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ] + }, + "add_link_text": "Add someone to this household", + "empty_list_text": "There are no householders" + }, + { + "type": "List", + "for_list": "visitors", + "title": { + "text": "Visitors staying overnight on {census_date} at {household_address}", + "placeholders": [ + { + "placeholder": "census_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "value": "2019-10-13" + } + }, + "transform": "format_date" + } + ] + }, + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ] + }, + "add_link_text": "Add another visitor to this household", + "empty_list_text": "There are no visitors" + } + ] + }, + "groups": [ + { + "id": "group", + "title": "Questions", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": { + "placeholders": [ + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ], + "text": "Do you live at {household_address}?" + }, + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": { + "placeholders": [ + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ], + "text": "Does anyone else live at {household_address}?" + }, + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": { + "text": "Household members staying overnight on {census_date} at {household_address}", + "placeholders": [ + { + "placeholder": "census_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "value": "2019-10-13" + } + }, + "transform": "format_date" + } + ] + }, + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ] + }, + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "id": "visitor-list-collector", + "type": "ListCollector", + "for_list": "visitors", + "question": { + "id": "confirmation-visitor-question", + "type": "General", + "title": { + "placeholders": [ + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ], + "text": "Are there any other visitors staying overnight at {household_address}?" + }, + "answers": [ + { + "id": "any-more-visitors", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-visitor", + "type": "ListAddQuestion", + "question": { + "id": "add-visitor-question", + "type": "General", + "title": "What is the name of the visitor?", + "answers": [ + { + "id": "first-name-visitor", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name-visitor", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-visitor-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-visitor-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name-visitor", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name-visitor", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-visitor", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-visitor-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-visitor-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": { + "text": "Visitors staying overnight on {census_date} at {household_address}", + "placeholders": [ + { + "placeholder": "census_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "value": "2019-10-13" + } + }, + "transform": "format_date" + } + ] + }, + { + "placeholder": "household_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ] + }, + "item_title": { + "text": "{visitor_name}", + "placeholders": [ + { + "placeholder": "visitor_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name-visitor" + }, + { + "source": "answers", + "identifier": "last-name-visitor" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index a7637c7bf7..e319bfc419 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Test ListCollector", + "title": "Test List Collector Section Summary Items", "theme": "default", - "description": "A questionnaire to test ListCollector", + "description": "A questionnaire to test list collector section summary items", "metadata": [ { "name": "user_id", @@ -19,151 +19,72 @@ { "name": "ru_name", "type": "string" - }, - { - "name": "display_address", - "type": "string" } ], "questionnaire_flow": { "type": "Linear", - "options": {} - }, - "individual_response": { - "for_list": "people", - "individual_section_id": "section" + "options": { + "summary": { + "collapsible": false + } + } }, "sections": [ { - "id": "section", - "title": "People who live here and overnight visitors", + "id": "section-companies", + "title": "General insurance business", "summary": { "show_on_completion": true, "items": [ { "type": "List", - "for_list": "people", - "title": { - "text": "Household members staying overnight on {census_date} at {household_address}", - "placeholders": [ - { - "placeholder": "census_date", - "transforms": [ - { - "arguments": { - "date_format": "d MMMM yyyy", - "date_to_format": { - "value": "2019-10-13" - } - }, - "transform": "format_date" - } - ] - }, - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ] - }, - "add_link_text": "Add someone to this household", - "empty_list_text": "There are no householders" - }, - { - "type": "List", - "for_list": "visitors", - "title": { - "text": "Visitors staying overnight on {census_date} at {household_address}", - "placeholders": [ - { - "placeholder": "census_date", - "transforms": [ - { - "arguments": { - "date_format": "d MMMM yyyy", - "date_to_format": { - "value": "2019-10-13" - } - }, - "transform": "format_date" - } - ] - }, - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ] - }, - "add_link_text": "Add another visitor to this household", - "empty_list_text": "There are no visitors" + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "related_answers": [ + { + "source": "answers", + "identifier": "registration-number" + }, + { + "source": "answers", + "identifier": "authorised-insurer-radio" + } + ] } - ] + ], + "show_non_item_answers": true }, "groups": [ { - "id": "group", - "title": "Questions", + "id": "group-companies", "blocks": [ { - "id": "primary-person-list-collector", - "type": "PrimaryPersonListCollector", - "for_list": "people", - "add_or_edit_block": { - "id": "add-or-edit-primary-person", - "type": "PrimaryPersonListAddOrEditQuestion", - "question": { - "id": "primary-person-add-or-edit-question", - "type": "General", - "title": "What is your name?", - "answers": [ - { - "id": "first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", "question": { - "id": "primary-confirmation-question", "type": "General", - "title": { - "placeholders": [ - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ], - "text": "Do you live at {household_address}?" - }, + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches within your United Kingdom group undertake general insurance business?", "answers": [ { - "id": "you-live-here", - "mandatory": true, "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, "options": [ { "label": "Yes", "value": "Yes", "action": { - "type": "RedirectToListAddBlock" + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } } }, { @@ -173,30 +94,38 @@ ] } ] - } + }, + "routing_rules": [ + { + "goto": { + "block": "confirmation-checkbox", + "when": [ + { + "id": "any-companies-or-branches-answer", + "condition": "equals", + "value": "No" + } + ] + } + }, + { + "goto": { + "block": "any-other-companies-or-branches" + } + } + ] }, { - "id": "list-collector", + "id": "any-other-companies-or-branches", "type": "ListCollector", - "for_list": "people", + "for_list": "companies", "question": { - "id": "confirmation-question", + "id": "any-other-companies-or-branches-question", "type": "General", - "title": { - "placeholders": [ - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ], - "text": "Does anyone else live at {household_address}?" - }, + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", "answers": [ { - "id": "anyone-else", + "id": "any-other-companies-or-branches-answer", "mandatory": true, "type": "Radio", "options": [ @@ -216,58 +145,95 @@ ] }, "add_block": { - "id": "add-person", + "id": "add-company", "type": "ListAddQuestion", "question": { - "id": "add-question", + "id": "add-question-companies", "type": "General", - "title": "What is the name of the person?", + "title": "Give details about the company or branch that undertakes general insurance business", "answers": [ { - "id": "first-name", - "label": "First name", + "id": "company-or-branch-name", + "label": "Name of UK company or branch", "mandatory": true, "type": "TextField" }, { - "id": "last-name", - "label": "Last name", + "id": "registration-number", + "label": "Registration number", "mandatory": true, - "type": "TextField" + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] } ] } }, "edit_block": { - "id": "edit-person", + "id": "edit-company", "type": "ListEditQuestion", "question": { - "id": "edit-question", + "id": "edit-question-companies", "type": "General", - "title": "What is the name of the person?", + "title": "What is the name of the company?", "answers": [ { - "id": "first-name", - "label": "First name", + "id": "company-or-branch-name", + "label": "Name of UK company or branch", "mandatory": true, "type": "TextField" }, { - "id": "last-name", - "label": "Last name", + "id": "registration-number", + "label": "Registration number", "mandatory": true, - "type": "TextField" + "type": "Number" + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] } ] } }, "remove_block": { - "id": "remove-person", + "id": "remove-company", "type": "ListRemoveQuestion", "question": { - "id": "remove-question", + "id": "remove-question-companies", "type": "General", - "title": "Are you sure you want to remove this person?", + "title": "Are you sure you want to remove this company or UK branch?", "answers": [ { "id": "remove-confirmation", @@ -291,82 +257,85 @@ } }, "summary": { - "title": { - "text": "Household members staying overnight on {census_date} at {household_address}", + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", "placeholders": [ { - "placeholder": "census_date", - "transforms": [ - { - "arguments": { - "date_format": "d MMMM yyyy", - "date_to_format": { - "value": "2019-10-13" - } - }, - "transform": "format_date" - } - ] - }, - { - "placeholder": "household_address", + "placeholder": "company_name", "value": { - "identifier": "display_address", - "source": "metadata" + "source": "answers", + "identifier": "company-or-branch-name" } } ] - }, - "item_title": { - "text": "{person_name}", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "arguments": { - "delimiter": " ", - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name" - }, - { - "source": "answers", - "identifier": "last-name" - } - ] - }, - "transform": "concatenate_list" - } - ] - } - ] } } }, { - "id": "visitor-list-collector", + "type": "Question", + "id": "confirmation-checkbox", + "question": { + "answers": [ + { + "id": "confirmation-checkbox-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "confirmation-checkbox-question", + "title": "Are all companies or branches based in UK?", + "type": "General" + } + } + ] + } + ] + }, + { + "id": "section-household", + "title": "Household Section", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "people", + "title": "Who lives here", + "item_anchor_answer_id": "first-name", + "item_label": "Name of householder", + "add_link_text": "Add someone to this household", + "empty_list_text": "There are no householders" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-household", + "title": "Household Questions", + "blocks": [ + { + "id": "list-collector", "type": "ListCollector", - "for_list": "visitors", + "for_list": "people", "question": { - "id": "confirmation-visitor-question", + "id": "confirmation-question", "type": "General", - "title": { - "placeholders": [ - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ], - "text": "Are there any other visitors staying overnight at {household_address}?" - }, + "title": "Does anyone else live here?", "answers": [ { - "id": "any-more-visitors", + "id": "anyone-else", "mandatory": true, "type": "Radio", "options": [ @@ -386,21 +355,22 @@ ] }, "add_block": { - "id": "add-visitor", + "id": "add-person", "type": "ListAddQuestion", + "cancel_text": "Don’t need to add anyone else?", "question": { - "id": "add-visitor-question", + "id": "add-question-people", "type": "General", - "title": "What is the name of the visitor?", + "title": "What is the name of the person?", "answers": [ { - "id": "first-name-visitor", + "id": "first-name", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "last-name-visitor", + "id": "last-name", "label": "Last name", "mandatory": true, "type": "TextField" @@ -409,21 +379,22 @@ } }, "edit_block": { - "id": "edit-visitor-person", + "id": "edit-person", "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", "question": { - "id": "edit-visitor-question", + "id": "edit-question-people", "type": "General", "title": "What is the name of the person?", "answers": [ { - "id": "first-name-visitor", + "id": "first-name", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "last-name-visitor", + "id": "last-name", "label": "Last name", "mandatory": true, "type": "TextField" @@ -432,15 +403,17 @@ } }, "remove_block": { - "id": "remove-visitor", + "id": "remove-person", "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this person?", "question": { - "id": "remove-visitor-question", + "id": "remove-question-people", "type": "General", "title": "Are you sure you want to remove this person?", + "warning": "All of the information about this person will be deleted", "answers": [ { - "id": "remove-visitor-confirmation", + "id": "remove-confirmation", "mandatory": true, "type": "Radio", "options": [ @@ -461,37 +434,12 @@ } }, "summary": { - "title": { - "text": "Visitors staying overnight on {census_date} at {household_address}", - "placeholders": [ - { - "placeholder": "census_date", - "transforms": [ - { - "arguments": { - "date_format": "d MMMM yyyy", - "date_to_format": { - "value": "2019-10-13" - } - }, - "transform": "format_date" - } - ] - }, - { - "placeholder": "household_address", - "value": { - "identifier": "display_address", - "source": "metadata" - } - } - ] - }, + "title": "Household members", "item_title": { - "text": "{visitor_name}", + "text": "{person_name}", "placeholders": [ { - "placeholder": "visitor_name", + "placeholder": "person_name", "transforms": [ { "arguments": { @@ -499,11 +447,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "first-name-visitor" + "identifier": "first-name" }, { "source": "answers", - "identifier": "last-name-visitor" + "identifier": "last-name" } ] }, @@ -514,6 +462,32 @@ ] } } + }, + { + "type": "Question", + "id": "householder-checkbox", + "question": { + "answers": [ + { + "id": "householder-checkbox-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "householder-checkbox-question", + "title": "Are all these people based in the UK?", + "type": "General" + } } ] } diff --git a/schemas/test/en/test_list_collector_variants_section_summary.json b/schemas/test/en/test_list_collector_variants_section_summary.json new file mode 100644 index 0000000000..6290356767 --- /dev/null +++ b/schemas/test/en/test_list_collector_variants_section_summary.json @@ -0,0 +1,537 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test List Collector Variants Section Summary Items", + "theme": "default", + "description": "A questionnaire to test list collector section summary items for variants", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section-companies", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK or non-UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "related_answers": [ + { + "source": "answers", + "identifier": "registration-number" + }, + { + "source": "answers", + "identifier": "authorised-insurer-radio" + } + ] + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-companies", + "blocks": [ + { + "type": "Question", + "id": "uk-based-block", + "question": { + "type": "General", + "id": "uk-based-question", + "title": "Are the companies UK based?", + "answers": [ + { + "type": "Radio", + "id": "uk-based-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", + "question": { + "type": "General", + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "goto": { + "block": "confirmation-checkbox", + "when": [ + { + "id": "any-companies-or-branches-answer", + "condition": "equals", + "value": "No" + } + ] + } + }, + { + "goto": { + "block": "any-other-companies-or-branches" + } + } + ] + }, + { + "id": "any-other-companies-or-branches", + "type": "ListCollector", + "for_list": "companies", + "question_variants": [ + { + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "Yes" + } + ] + }, + { + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other non-UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "No" + } + ] + } + ], + "add_block": { + "id": "add-company", + "type": "ListAddQuestion", + "question_variants": [ + { + "question": { + "id": "add-question-companies", + "type": "General", + "title": "Give details about the company or branch that undertakes general insurance business", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "UK Registration number", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "Yes" + } + ] + }, + { + "question": { + "id": "add-question-companies", + "type": "General", + "title": "Give details about the company or branch that undertakes general insurance business", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of non-UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "Non-UK Registration number", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "type": "Radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "No" + } + ] + } + ] + }, + "edit_block": { + "id": "edit-company", + "type": "ListEditQuestion", + "question_variants": [ + { + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "UK Registration number", + "mandatory": true, + "type": "Number" + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "Yes" + } + ] + }, + { + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of non-UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "Non-UK Registration number", + "mandatory": true, + "type": "Number" + }, + { + "type": "Radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "No" + } + ] + } + ] + }, + "remove_block": { + "id": "remove-company", + "type": "ListRemoveQuestion", + "question_variants": [ + { + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or non-UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "No" + } + ] + }, + { + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or non-UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "when": [ + { + "id": "uk-based-answer", + "condition": "equals", + "value": "Yes" + } + ] + } + ] + }, + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + } + }, + { + "type": "Question", + "id": "confirmation-checkbox", + "question": { + "answers": [ + { + "id": "confirmation-checkbox-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "confirmation-checkbox-question", + "title": "Are all companies or branches based in UK?", + "type": "General" + } + } + ] + } + ] + } + ] +} diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index de063a642a..8ee344d198 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -30,14 +30,15 @@ "groups": [ { "rows": map_summary_item_config( - group, - content.summary.summary_type, - content.summary.answers_are_editable, - _("No answer provided"), - _("Change"), - _("Change your answer for:"), - content.summary.calculated_question - ) + group=group, + summary_type=content.summary.summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + edit_link_text=_("Change"), + edit_link_aria_label=_("Change your answer for:"), + calculated_question=content.summary.calculated_question, + icon=None + ) } ] } diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 4fb17ed2e9..af9f0177a5 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -6,30 +6,37 @@ {% set summaryGroups = [] %} {%- for group in content.summary.groups if group.blocks -%} - {% do summaryGroups.append ( - { - "groups": [ - { - "groupTitle": group.title if group.title else None, - "id": group.id if group.id else None, - "rows": map_summary_item_config( - group, - content.summary.summary_type, - content.summary.answers_are_editable, - _("No answer provided"), - _("Change"), - _("Change your answer for:"), - content.summary.calculated_question - ), - "classes": "ons-u-mt-m" if loop.index > 1 else "" - } - ] - } - ) + {% do summaryGroups.append + ( + { + "groups": [ + { + "groupTitle": group.title if group.title else None, + "id": group.id if group.id else None, + "rows": map_summary_item_config( + group=group, + summary_type=content.summary.summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + edit_link_text=_("Change"), + edit_link_aria_label=_("Change your answer for:"), + calculated_question=content.summary.calculated_question, + icon=None + ), + "classes": "ons-u-mt-m" if loop.index > 1 else "", + "placeholderText": group.placeholder_text, + "summaryLink": group.links.add_link, + } + ] + } + ) %} {%- endfor -%} + + {{ onsSummary({ "summaries": summaryGroups }) }} + {%- endif -%} diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index d906748373..fee6de6d15 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -17,6 +17,7 @@ }) }} {% block form_content %} + {% if content.summary.groups %} {% set group = content.summary.groups %}

    {{content.summary.title}}

    diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 291c46f233..f219bac98d 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -320,12 +320,12 @@ def test_map_list_collector_config_no_actions(): { "actions": [], "iconType": "icon", - "rowTitle": "Mark Bloggs", "id": "one", "rowTitleAttributes": { - "data-qa": "list-item-1-label", "data-list-item-id": "one", + "data-qa": "list-item-1-label", }, + "rowTitle": "Mark Bloggs", } ] }, @@ -334,12 +334,12 @@ def test_map_list_collector_config_no_actions(): { "actions": [], "iconType": "icon", - "rowTitle": "Joe Bloggs", "id": "two", "rowTitleAttributes": { - "data-qa": "list-item-2-label", "data-list-item-id": "two", + "data-qa": "list-item-2-label", }, + "rowTitle": "Joe Bloggs", } ] }, @@ -390,12 +390,12 @@ def test_map_list_collector_config(): } ], "iconType": "icon", - "rowTitle": "Mark Bloggs (You)", "id": "primary", "rowTitleAttributes": { - "data-qa": "list-item-1-label", "data-list-item-id": "primary", + "data-qa": "list-item-1-label", }, + "rowTitle": "Mark Bloggs (You)", } ] }, @@ -417,12 +417,12 @@ def test_map_list_collector_config(): }, ], "iconType": "icon", - "rowTitle": "Joe Bloggs", "id": "nonprimary", "rowTitleAttributes": { - "data-qa": "list-item-2-label", "data-list-item-id": "nonprimary", + "data-qa": "list-item-2-label", }, + "rowTitle": "Joe Bloggs", } ] }, @@ -431,6 +431,144 @@ def test_map_list_collector_config(): assert output == expected +@pytest.mark.usefixtures("gb_locale") +def test_map_list_collector_config_with_related_answers_and_answer_title(): + list_items = [ + { + "remove_link": "/nonprimary/remove", + "edit_link": "/nonprimary/change", + "primary_person": False, + "item_title": "Name of UK company or branch", + "id": "nonprimary", + "list_item_id": "VHoiow", + }, + ] + + output = map_list_collector_config( + list_items, + "icon", + "edit_link_text", + "edit_link_aria_label", + "remove_link_text", + "remove_link_aria_label", + { + "VHoiow": [ + { + "id": "edit-company", + "title": None, + "number": None, + "question": { + "id": "add-question", + "type": "General", + "title": " ", + "number": None, + "answers": [ + { + "id": "registration-number", + "label": "Registration number", + "value": 123, + "type": "number", + "unit": None, + "unit_length": None, + "currency": None, + "link": "registration_edit_link_url", + }, + { + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "value": {"label": "Yes", "detail_answer_value": None}, + "type": "radio", + "unit": None, + "unit_length": None, + "currency": None, + "link": "authorised_edit_link_url", + }, + ], + }, + } + ] + }, + "Name of UK company or branch", + "", + ) + + expected = [ + { + "rowItems": [ + { + "actions": [ + { + "ariaLabel": "edit_link_aria_label", + "attributes": {"data-qa": "list-item-change-1-link"}, + "text": "edit_link_text", + "url": "/nonprimary/change", + }, + { + "ariaLabel": "remove_link_aria_label", + "attributes": {"data-qa": "list-item-remove-1-link"}, + "text": "remove_link_text", + "url": "/nonprimary/remove", + }, + ], + "iconType": "icon", + "id": "VHoiow", + "rowTitle": "Name of UK company or branch", + "rowTitleAttributes": { + "data-list-item-id": "VHoiow", + "data-qa": "list-item-1-label", + }, + "valueList": [{"text": "Name of UK company or branch"}], + }, + { + "actions": [ + { + "ariaLabel": "edit_link_aria_label Registration number", + "attributes": { + "data-ga": "click", + "data-ga-action": "Edit click", + "data-ga-category": "Summary", + "data-qa": "registration-number-edit", + }, + "text": "edit_link_text", + "url": "registration_edit_link_url", + } + ], + "attributes": {"data-qa": "registration-number"}, + "id": "registration-number", + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "valueList": [{"text": "123"}], + }, + { + "actions": [ + { + "ariaLabel": "edit_link_aria_label Is this UK " + "company or branch an authorised " + "insurer?", + "attributes": { + "data-ga": "click", + "data-ga-action": "Edit click", + "data-ga-category": "Summary", + "data-qa": "authorised-insurer-radio-edit", + }, + "text": "edit_link_text", + "url": "authorised_edit_link_url", + } + ], + "attributes": {"data-qa": "authorised-insurer-radio"}, + "id": "authorised-insurer-radio", + "rowTitle": "Is this UK company or branch an authorised " + "insurer?", + "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "valueList": [{"text": "Yes"}], + }, + ] + } + ] + + assert to_dict(output) == to_dict(expected) + + @pytest.mark.parametrize( "address_fields, formatted_address", ( @@ -634,6 +772,165 @@ def test_calculated_summary_config(): "id": "calculated-summary-question", "answers": [{"id": "calculated-summary-answer", "value": "£2.00"}], }, + icon="", + ) + + assert to_dict(expected) == to_dict(result) + + +@pytest.mark.usefixtures("gb_locale") +def test_summary_item_config_with_list_collector(): + expected = [ + { + "rowItems": [ + { + "actions": [ + { + "ariaLabel": "Change your answer for:", + "attributes": {"data-qa": "list-item-change-1-link"}, + "text": "Change", + "url": "change_link_url", + }, + { + "ariaLabel": "Remove Company A", + "attributes": {"data-qa": "list-item-remove-1-link"}, + "text": { + "_args": ["Remove"], + "_func": {}, + "_kwargs": {}, + }, + "url": "remove_link_url", + }, + ], + "iconType": "", + "id": "vmmPmD", + "rowTitle": "Company A", + "rowTitleAttributes": { + "data-list-item-id": "vmmPmD", + "data-qa": "list-item-1-label", + }, + }, + { + "actions": [ + { + "ariaLabel": "Change your answer for: " + "Registration number", + "attributes": { + "data-ga": "click", + "data-ga-action": "Edit click", + "data-ga-category": "Summary", + "data-qa": "registration-number-edit", + }, + "text": "Change", + "url": "edit_link_url", + } + ], + "attributes": {"data-qa": "registration-number"}, + "id": "registration-number", + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "valueList": [{"text": "123"}], + }, + { + "actions": [ + { + "ariaLabel": "Change your answer for: Is this UK " + "company or branch an authorised " + "insurer?", + "attributes": { + "data-ga": "click", + "data-ga-action": "Edit click", + "data-ga-category": "Summary", + "data-qa": "authorised-insurer-radio-edit", + }, + "text": "Change", + "url": "edit_link_url", + } + ], + "attributes": {"data-qa": "authorised-insurer-radio"}, + "id": "authorised-insurer-radio", + "rowTitle": "Is this UK company or branch an authorised " + "insurer?", + "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "valueList": [{"text": "Yes"}], + }, + ] + } + ] + + result = map_summary_item_config( + group={ + "blocks": [ + { + "title": "Companies or UK branches", + "type": "List", + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "list_name": "companies", + "related_answers": { + "vmmPmD": [ + { + "id": "edit-company", + "title": None, + "number": None, + "question": { + "id": "add-question", + "type": "General", + "title": " ", + "number": None, + "answers": [ + { + "id": "registration-number", + "label": "Registration number", + "value": 123, + "type": "number", + "unit": None, + "unit_length": None, + "currency": None, + "link": "edit_link_url", + }, + { + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "value": { + "label": "Yes", + "detail_answer_value": None, + }, + "type": "radio", + "unit": None, + "unit_length": None, + "currency": None, + "link": "edit_link_url", + }, + ], + }, + } + ] + }, + "answer_title": "Name of UK company or branch", + "list": { + "list_items": [ + { + "item_title": "Company A", + "primary_person": False, + "list_item_id": "vmmPmD", + "edit_link": "change_link_url", + "remove_link": "remove_link_url", + } + ], + "editable": True, + }, + } + ], + }, + summary_type="SectionSummary", + answers_are_editable=True, + no_answer_provided="No answer Provided", + edit_link_text="Change", + edit_link_aria_label="Change your answer for:", + calculated_question={}, + icon="", ) assert to_dict(expected) == to_dict(result) diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 8dfbacb695..82e01357dd 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -184,6 +184,128 @@ def people_answer_store(): ) +@pytest.fixture +def companies_answer_store(): + return AnswerStore( + [ + { + "answer_id": "company-or-branch-name", + "value": "company a", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": 123, + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "company b", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": 456, + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + ] + ) + + +@pytest.fixture +def companies_variants_answer_store_first_variant(): + return AnswerStore( + [ + { + "answer_id": "uk-based-answer", + "value": "Yes", + }, + { + "answer_id": "company-or-branch-name", + "value": "company a", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": 123, + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "company b", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": 456, + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + ] + ) + + +@pytest.fixture +def companies_variants_answer_store_second_variant(): + return AnswerStore( + [ + { + "answer_id": "uk-based-answer", + "value": "No", + }, + { + "answer_id": "company-or-branch-name", + "value": "company a", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": 123, + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "company b", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": 456, + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-insurer-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + ] + ) + + @pytest.fixture def people_list_store(): return ListStore([{"items": ["PlwgoG", "UHPLbX"], "name": "people"}]) diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index e1d4f864ad..6542df3329 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -7,6 +7,7 @@ from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.questionnaire.routing_path import RoutingPath +from app.survey_config.link import Link from app.utilities.schema import load_schema_from_name from app.views.contexts import SectionSummaryContext from tests.app.questionnaire.conftest import get_metadata @@ -124,7 +125,7 @@ def test_custom_section( @pytest.mark.usefixtures("app") def test_context_for_section_list_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") + schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, @@ -158,6 +159,8 @@ def test_context_for_section_list_summary(people_answer_store): { "add_link": "/questionnaire/people/add-person/?return_to=section-summary", "add_link_text": "Add someone to this household", + "item_anchor": None, + "item_label": None, "empty_list_text": "There are no householders", "list": { "editable": True, @@ -165,26 +168,31 @@ def test_context_for_section_list_summary(people_answer_store): { "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", "item_title": "Toni Morrison", + "list_item_id": "PlwgoG", "primary_person": False, "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", - "list_item_id": "PlwgoG", }, { "edit_link": "/questionnaire/people/UHPLbX/edit-person/?return_to=section-summary", "item_title": "Barry Pheloung", + "list_item_id": "UHPLbX", "primary_person": False, "remove_link": "/questionnaire/people/UHPLbX/remove-person/?return_to=section-summary", - "list_item_id": "UHPLbX", }, ], }, "list_name": "people", - "title": "Household members staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", + "related_answers": None, + "title": "Household members staying overnight " + "on 13 October 2019 at 70 Abingdon " + "Road, Goathill", "type": "List", }, { "add_link": "/questionnaire/visitors/add-visitor/?return_to=section-summary", "add_link_text": "Add another visitor to this household", + "item_anchor": None, + "item_label": None, "empty_list_text": "There are no visitors", "list": { "editable": True, @@ -192,14 +200,17 @@ def test_context_for_section_list_summary(people_answer_store): { "edit_link": "/questionnaire/visitors/gTrlio/edit-visitor-person/?return_to=section-summary", "item_title": "", + "list_item_id": "gTrlio", "primary_person": False, "remove_link": "/questionnaire/visitors/gTrlio/remove-visitor/?return_to=section-summary", - "list_item_id": "gTrlio", } ], }, "list_name": "visitors", - "title": "Visitors staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", + "related_answers": None, + "title": "Visitors staying overnight on 13 " + "October 2019 at 70 Abingdon Road, " + "Goathill", "type": "List", }, ], @@ -212,6 +223,550 @@ def test_context_for_section_list_summary(people_answer_store): assert context == expected +@pytest.mark.usefixtures("app") +def test_context_for_section_summary_with_list_summary(companies_answer_store): + schema = load_schema_from_name("test_list_collector_section_summary") + + summary_context = SectionSummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + answer_store=companies_answer_store, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, + ] + ), + progress_store=ProgressStore(), + metadata={}, + response_metadata={}, + current_location=Location(section_id="section-companies"), + routing_path=RoutingPath( + [ + "any-other-companies-or-branches", + ], + section_id="section-companies", + ), + ) + context = summary_context() + expected = { + "summary": { + "answers_are_editable": True, + "collapsible": False, + "groups": [ + { + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], + }, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) + }, + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, + ], + "page_title": "General insurance business", + "summary_type": "SectionSummary", + "title": "General insurance business", + } + } + + assert context == expected + + +@pytest.mark.usefixtures("app") +def test_context_for_section_summary_with_list_summary_and_first_variant( + companies_variants_answer_store_first_variant, +): + schema = load_schema_from_name("test_list_collector_variants_section_summary") + + summary_context = SectionSummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + answer_store=companies_variants_answer_store_first_variant, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, + ] + ), + progress_store=ProgressStore(), + metadata={}, + response_metadata={}, + current_location=Location(section_id="section-companies"), + routing_path=RoutingPath( + [ + "any-other-companies-or-branches", + ], + section_id="section-companies", + ), + ) + context = summary_context() + expected = { + "summary": { + "answers_are_editable": True, + "collapsible": False, + "groups": [ + { + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK or non-UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], + }, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "UK Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "UK Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) + }, + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, + ], + "page_title": "General insurance business", + "summary_type": "SectionSummary", + "title": "General insurance business", + } + } + + assert context == expected + + +@pytest.mark.usefixtures("app") +def test_context_for_section_summary_with_list_summary_and_second_variant( + companies_variants_answer_store_second_variant, +): + schema = load_schema_from_name("test_list_collector_variants_section_summary") + + summary_context = SectionSummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + answer_store=companies_variants_answer_store_second_variant, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, + ] + ), + progress_store=ProgressStore(), + metadata={}, + response_metadata={}, + current_location=Location(section_id="section-companies"), + routing_path=RoutingPath( + [ + "any-other-companies-or-branches", + ], + section_id="section-companies", + ), + ) + context = summary_context() + expected = { + "summary": { + "answers_are_editable": True, + "collapsible": False, + "groups": [ + { + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK or non-UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], + }, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Non-UK Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Non-UK Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" + "ration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" + "ised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", + }, + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) + }, + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, + ], + "page_title": "General insurance business", + "summary_type": "SectionSummary", + "title": "General insurance business", + } + } + + assert context == expected + + @pytest.mark.usefixtures("app") def test_context_for_driving_question_summary_empty_list(): schema = load_schema_from_name("test_list_collector_driving_question") @@ -237,9 +792,12 @@ def test_context_for_driving_question_summary_empty_list(): { "add_link": "/questionnaire/anyone-usually-live-at/?return_to=section-summary", "add_link_text": "Add someone to this household", + "item_anchor": None, + "item_label": None, "empty_list_text": "There are no householders", "list": {"editable": False, "list_items": []}, "list_name": "people", + "related_answers": None, "title": "Household members", "type": "List", } @@ -249,7 +807,6 @@ def test_context_for_driving_question_summary_empty_list(): "title": "List Collector Driving Question Summary", } } - assert context == expected @@ -291,20 +848,23 @@ def test_context_for_driving_question_summary(): { "add_link": "/questionnaire/people/add-person/?return_to=section-summary", "add_link_text": "Add someone to this household", + "item_anchor": None, + "item_label": None, "empty_list_text": "There are no householders", "list": { "editable": True, "list_items": [ { + "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", "item_title": "Toni Morrison", + "list_item_id": "PlwgoG", "primary_person": False, - "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", - "list_item_id": "PlwgoG", } ], }, "list_name": "people", + "related_answers": None, "title": "Household members", "type": "List", } @@ -314,7 +874,6 @@ def test_context_for_driving_question_summary(): "title": "List Collector Driving Question Summary", } } - assert context == expected @@ -375,7 +934,7 @@ def test_titles_for_repeating_section_summary(people_answer_store): @pytest.mark.usefixtures("app") def test_primary_only_links_for_section_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") + schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, @@ -406,7 +965,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): @pytest.mark.usefixtures("app") def test_primary_links_for_section_summary(people_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") + schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 7006160d41..c971fb36b6 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -278,6 +278,32 @@ """ ) + +NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_LABEL_GETTER = Template( + r""" ${list_name}ListLabel(listItemInstance) { return `dt[data-qa="list-item-` + listItemInstance + `-label"]`; } + +""" +) + +NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_ADD_LINK_GETTER = Template( + r""" ${list_name}ListAddLink() { return `a[data-qa="add-item-link"]`; } + +""" +) + +# pylint: disable=line-too-long +NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_EDIT_LINK_GETTER = Template( + r""" ${list_name}ListEditLink(listItemInstance) { return `a[data-qa="list-item-change-` + listItemInstance + `-link"]`; } + +""" +) +# pylint: disable=line-too-long +NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_REMOVE_LINK_GETTER = Template( + r""" ${list_name}ListRemoveLink(listItemInstance) { return `a[data-qa="list-item-remove-` + listItemInstance + `-link"]`; } + +""" +) + RELATIONSHIP_PLAYBACK_GETTER = r""" playback() { return `[class*="relationships__playback"]`; } """ @@ -544,7 +570,14 @@ def process_guidance(context, page_spec): page_spec.write(GUIDANCE_PANEL_GETTER.safe_substitute(context)) -def write_summary_spec(page_spec, section, collapsible, answers_are_editable=False): +# pylint: disable=too-many-locals +def write_summary_spec( + page_spec, + section, + collapsible, + answers_are_editable=False, + show_non_item_answers=False, +): list_summaries = [ summary_element for summary_element in section.get("summary", {}).get("items", []) @@ -553,16 +586,41 @@ def write_summary_spec(page_spec, section, collapsible, answers_are_editable=Fal for list_block in list_summaries: list_context = {"list_name": list_block["for_list"]} if answers_are_editable: + if show_non_item_answers: + page_spec.write( + NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_ADD_LINK_GETTER.substitute( + list_context + ) + ) + page_spec.write( + NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_EDIT_LINK_GETTER.substitute( + list_context + ) + ) + page_spec.write( + NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_REMOVE_LINK_GETTER.substitute( + list_context + ) + ) + + else: + page_spec.write( + LIST_SECTION_SUMMARY_ADD_LINK_GETTER.substitute(list_context) + ) + page_spec.write( + LIST_SECTION_SUMMARY_EDIT_LINK_GETTER.substitute(list_context) + ) + page_spec.write( + LIST_SECTION_SUMMARY_REMOVE_LINK_GETTER.substitute(list_context) + ) + if show_non_item_answers: page_spec.write( - LIST_SECTION_SUMMARY_ADD_LINK_GETTER.substitute(list_context) - ) - page_spec.write( - LIST_SECTION_SUMMARY_EDIT_LINK_GETTER.substitute(list_context) - ) - page_spec.write( - LIST_SECTION_SUMMARY_REMOVE_LINK_GETTER.substitute(list_context) + NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_LABEL_GETTER.substitute( + list_context + ) ) - page_spec.write(LIST_SECTION_SUMMARY_LABEL_GETTER.substitute(list_context)) + else: + page_spec.write(LIST_SECTION_SUMMARY_LABEL_GETTER.substitute(list_context)) for group in section["groups"]: for block in group["blocks"]: @@ -593,7 +651,7 @@ def write_summary_spec(page_spec, section, collapsible, answers_are_editable=Fal if not collapsible: group_context = { "group_id_camel": camel_case(generate_pascal_case_from_id(group["id"])), - "group_id": group["id"], + "group_id": f'{group["id"]}-0', } page_spec.write(SUMMARY_TITLE_GETTER.substitute(group_context)) @@ -940,11 +998,18 @@ def process_section_summary( page_spec.write(CLASS_NAME.substitute(section_context)) page_spec.write(CONSTRUCTOR.substitute(section_context)) page_spec.write(SECTION_SUMMARY_PAGE_URL) + + show_non_item_answers = False + if summary := section.get("summary"): + if summary.get("show_non_item_answers"): + show_non_item_answers = True + write_summary_spec( page_spec, section, collapsible=False, answers_are_editable=True, + show_non_item_answers=show_non_item_answers, ) page_spec.write(FOOTER.substitute(section_context)) diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index 69c9de3d30..101a87f65c 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -9,15 +9,15 @@ import ListCollectorEditPage from "../generated_pages/list_collector/list-collec import ListCollectorRemovePage from "../generated_pages/list_collector/list-collector-remove.page.js"; import NextInterstitialPage from "../generated_pages/list_collector/next-interstitial.page.js"; import SummaryPage from "../generated_pages/list_collector/section-summary.page.js"; -import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_section_summary/primary-person-list-collector.page.js"; -import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_section_summary/primary-person-list-collector-add.page.js"; -import SectionSummaryListCollectorPage from "../generated_pages/list_collector_section_summary/list-collector.page.js"; -import SectionSummaryListCollectorAddPage from "../generated_pages/list_collector_section_summary/list-collector-add.page.js"; -import SectionSummaryListCollectorEditPage from "../generated_pages/list_collector_section_summary/list-collector-edit.page.js"; -import SectionSummaryListCollectorRemovePage from "../generated_pages/list_collector_section_summary/list-collector-remove.page.js"; -import VisitorListCollectorPage from "../generated_pages/list_collector_section_summary/visitor-list-collector.page.js"; -import VisitorListCollectorAddPage from "../generated_pages/list_collector_section_summary/visitor-list-collector-add.page.js"; -import PeopleListSectionSummaryPage from "../generated_pages/list_collector_section_summary/section-summary.page.js"; +import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_list_summary/primary-person-list-collector.page.js"; +import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_list_summary/primary-person-list-collector-add.page.js"; +import SectionSummaryListCollectorPage from "../generated_pages/list_collector_list_summary/list-collector.page.js"; +import SectionSummaryListCollectorAddPage from "../generated_pages/list_collector_list_summary/list-collector-add.page.js"; +import SectionSummaryListCollectorEditPage from "../generated_pages/list_collector_list_summary/list-collector-edit.page.js"; +import SectionSummaryListCollectorRemovePage from "../generated_pages/list_collector_list_summary/list-collector-remove.page.js"; +import VisitorListCollectorPage from "../generated_pages/list_collector_list_summary/visitor-list-collector.page.js"; +import VisitorListCollectorAddPage from "../generated_pages/list_collector_list_summary/visitor-list-collector-add.page.js"; +import PeopleListSectionSummaryPage from "../generated_pages/list_collector_list_summary/section-summary.page.js"; import { SubmitPage } from "../base_pages/submit.page.js"; describe("List Collector", () => { @@ -171,7 +171,7 @@ describe("List Collector", () => { describe("Given I start a list collector survey and complete to Section Summary", () => { beforeEach(() => { - browser.openQuestionnaire("test_list_collector_section_summary.json"); + browser.openQuestionnaire("test_list_collector_list_summary.json"); $(PrimaryPersonListCollectorPage.yes()).click(); $(PrimaryPersonListCollectorPage.submit()).click(); $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js new file mode 100644 index 0000000000..39d75ba65b --- /dev/null +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -0,0 +1,203 @@ +import AnyCompaniesOrBranchesDrivingQuestionPage from "../generated_pages/list_collector_section_summary/any-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-add.page.js"; +import AnyCompaniesOrBranchesRemovePage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-remove.page.js"; +import SectionSummaryPage from "../generated_pages/list_collector_section_summary/section-companies-summary.page"; +import UkBasedPage from "../generated_pages/list_collector_section_summary/confirmation-checkbox.page"; + +describe("List Collector Section Summary Items", () => { + describe("Given I launch the test list collector section summary items survey", () => { + beforeEach(() => { + browser.openQuestionnaire("test_list_collector_section_summary.json"); + }); + it("When I get to the section summary, Then the driving question should be visible.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).to.be.true; + expect($(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).to.contain("Yes"); + }); + it("When I add my own item, Then the item should be visible on the section summary and have correct values", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect($(SectionSummaryPage.companiesListLabel(1)).getText()).to.contain("Name of UK company or branch"); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + expect($(companiesListRowItem(1, 2)).getText()).to.contain("123"); + expect($(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); + expect($(companiesListRowItemAnchor(1)).getHTML()).to.contain("return_to=section-summary#company-or-branch-name"); + expect($(companiesListRowItemAnchor(2)).getHTML()).to.contain("return_to_answer_id=registration-number#registration-number"); + expect($(companiesListRowItemAnchor(3)).getHTML()).to.contain("return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio"); + }); + it("When I add multiple items, Then all the items should be visible on the section summary and have correct values", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", false); + anyMoreCompaniesYes(); + addCompany("Company C", "789", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + expect($(companiesListRowItem(1, 2)).getText()).to.contain("123"); + expect($(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); + expect($(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); + expect($(companiesListRowItem(2, 2)).getText()).to.contain("456"); + expect($(companiesListRowItem(2, 3)).getText()).to.contain("No"); + expect($(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); + expect($(companiesListRowItem(3, 2)).getText()).to.contain("789"); + expect($(companiesListRowItem(3, 3)).getText()).to.contain("Yes"); + }); + it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + removeFirstCompany(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($("body").getText()).to.not.have.string("Company A"); + expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + }); + it("When I remove an item but the list collector is still on the path, Then the placeholder text should be visible on the section summary.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + removeFirstCompany(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($("body").getText()).to.contain("No UK company or branch added"); + }); + it("When I have multiple items in the list and I remove the first item, Then only the item that was not deleted should be visible on the section summary.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "234", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + removeFirstCompany(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($("body").getText()).to.not.have.string("Company A"); + expect($("body").getText()).to.contain("Company B"); + }); + it("When I add an item and relevant data and answer No on the additional items page, Then I should get to the section summary page.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + }); + it("When I add an item and relevant data and answer Yes on the additional items page, Then I should be able to and add a new item and relevant data.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + expect($(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).isExisting()).to.be.true; + expect($(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).to.be.true; + expect($(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).to.be.true; + expect($(AnyCompaniesOrBranchesAddPage.heading()).getText()).to.contain( + "Give details about the company or branch that undertakes general insurance business" + ); + }); + it("When I add an item and relevant data, Then I should be able to edit that item from the section summary page.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + $(SectionSummaryPage.companiesListEditLink(1)).click(); + expect(browser.getUrl()).to.contain("edit-company/?return_to=section-summary"); + expect($(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).to.equal("Company A"); + }); + it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", () => { + drivingQuestionNo(); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; + expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; + expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + }); + it("When I add an item and relevant data but change my answer to the driving question to No, Then I should see the original item on the summary if change the answer back to Yes.", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + answerUkBasedQuestion(); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + drivingQuestionNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + expect($("body").getText()).to.not.have.string("No UK company or branch added"); + expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + drivingQuestionYes(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; + expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; + expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + }); + }); +}); + +const drivingQuestionYes = () => { + $(AnyCompaniesOrBranchesDrivingQuestionPage.yes()).click(); + $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); +}; + +const drivingQuestionNo = () => { + $(AnyCompaniesOrBranchesDrivingQuestionPage.no()).click(); + $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); +}; + +const addCompany = (name, number, authorised) => { + $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue(name); + $(AnyCompaniesOrBranchesAddPage.registrationNumber()).setValue(number); + if (authorised) { + $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).click(); + } else { + $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); + } + $(AnyCompaniesOrBranchesAddPage.submit()).click(); +}; + +const anyMoreCompaniesYes = () => { + $(AnyCompaniesOrBranchesPage.yes()).click(); + $(AnyCompaniesOrBranchesPage.submit()).click(); +}; + +const anyMoreCompaniesNo = () => { + $(AnyCompaniesOrBranchesPage.no()).click(); + $(AnyCompaniesOrBranchesPage.submit()).click(); +}; + +const removeFirstCompany = () => { + $(SectionSummaryPage.companiesListRemoveLink(1)).click(); + $(AnyCompaniesOrBranchesRemovePage.yes()).click(); + $(AnyCompaniesOrBranchesRemovePage.submit()).click(); +}; + +const answerUkBasedQuestion = () => { + $(UkBasedPage.yes()).click(); + $(UkBasedPage.submit()).click(); +}; + +const companiesListRowItem = (row, index) => { + return `#group-companies-1 .ons-summary__items .ons-summary__item:nth-of-type(${row}) .ons-summary__row:nth-of-type(${index})`; +}; + +const companiesListRowItemAnchor = (index) => { + return `#group-companies-1 .ons-summary__items .ons-summary__item .ons-summary__row:nth-of-type(${index}) a`; +}; From 4ba25b322926270baa9e4dca3ae75da536f4934f Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:58:27 +0000 Subject: [PATCH 123/567] Update to DS 61.0.2 (#982) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index e60f6929fc..2e3288ea8f 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -61.0.1 +61.0.2 From ef193b65f7fea39581a2c3198d7904e205bd6048 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 16 Dec 2022 12:25:02 +0000 Subject: [PATCH 124/567] Refactor calculated summary (#965) --- app/jinja_filters.py | 4 +- app/questionnaire/questionnaire_schema.py | 36 +- .../questionnaire_store_updater.py | 30 +- app/questionnaire/router.py | 4 +- app/questionnaire/rules/operations.py | 6 +- app/questionnaire/rules/operator.py | 2 + app/questionnaire/rules/rule_evaluator.py | 5 +- app/questionnaire/value_source_resolver.py | 27 +- .../contexts/calculated_summary_context.py | 126 ++- .../test/en/test_new_calculated_summary.json | 594 +++++++++++++ ...alculated_summary_dependent_questions.json | 177 ++++ ..._calculated_summary_repeating_section.json | 825 ++++++++++++++++++ ...n_dependencies_new_calculated_summary.json | 300 +++++++ tests/app/questionnaire/conftest.py | 7 + .../app/questionnaire/rules/test_operators.py | 17 + .../test_questionnaire_schema.py | 13 + .../test_questionnaire_store_updater.py | 79 +- tests/app/questionnaire/test_router.py | 60 +- .../test_value_source_resolver.py | 106 +++ tests/functional/generate_pages.py | 35 +- .../spec/features/calculated_summary.spec.js | 331 +------ .../features/calculated_summary_test_case.js | 333 +++++++ ...lculated_summary_repeating_section.spec.js | 424 +++++++++ .../test_questionnaire_calculated_summary.py | 73 +- 24 files changed, 3191 insertions(+), 423 deletions(-) create mode 100644 schemas/test/en/test_new_calculated_summary.json create mode 100644 schemas/test/en/test_new_calculated_summary_dependent_questions.json create mode 100644 schemas/test/en/test_new_calculated_summary_repeating_section.json create mode 100644 schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json create mode 100644 tests/functional/spec/features/calculated_summary_test_case.js create mode 100644 tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 62a64ad75d..dd1d80bfc1 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -69,7 +69,7 @@ def format_percentage(value: Union[int, Decimal]) -> str: def format_unit( - unit: Union[int, Decimal], value: Union[int, Decimal], length: str = "short" + unit: str, value: Union[int, float, Decimal], length: str = "short" ) -> str: formatted_unit: str = units.format_unit( value=value, @@ -183,7 +183,7 @@ def get_format_date_range(start_date: Markup, end_date: Markup) -> Markup: @blueprint.app_context_processor def format_unit_processor() -> dict[ - str, Callable[[Union[int, Decimal], Union[int, Decimal], str], str] + str, Callable[[str, Union[int, Decimal], str], str] ]: return dict(format_unit=format_unit) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 5a8130d856..f8c44b9e5a 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -233,10 +233,12 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: def _populate_answer_dependencies(self) -> None: for block in self.get_blocks(): if block["type"] == "CalculatedSummary": + answer_ids_for_block = self.get_calculated_summary_answer_ids(block) self._update_answer_dependencies_for_calculated_summary( - block["calculation"]["answers_to_calculate"], block["id"] + answer_ids_for_block, block["id"] ) continue + for question in self.get_all_questions_for_block(block): if question["type"] == "Calculated": self._update_answer_dependencies_for_calculations( @@ -317,12 +319,15 @@ def _update_answer_dependencies_for_value_source( } if value_source["source"] == "calculated_summary": identifier = value_source["identifier"] - calculated_summary_block = self.get_block(identifier) - answer_ids_for_block = calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore - for answer_id_for_block in answer_ids_for_block: - self._answer_dependencies_map[answer_id_for_block] |= { - self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore - } + if calculated_summary_block := self.get_block(identifier): + answer_ids_for_block = self.get_calculated_summary_answer_ids( + calculated_summary_block + ) + + for answer_id_for_block in answer_ids_for_block: + self._answer_dependencies_map[answer_id_for_block] |= { + self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore + } def _get_answer_dependent_for_block_id( self, *, block_id: str, answer_id: Optional[str] = None @@ -869,9 +874,10 @@ def _get_rules_section_dependencies( answer_id_list.append(identifier) elif source == "calculated_summary" and identifier: calculated_summary_block = self.get_block(identifier) - calculated_summary_answer_ids = calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + calculated_summary_answer_ids = self.get_calculated_summary_answer_ids( + calculated_summary_block # type: ignore + ) answer_id_list.extend(iter(calculated_summary_answer_ids)) - for answer_id in answer_id_list: block = self.get_block_for_answer_id(answer_id) # type: ignore section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore @@ -889,6 +895,18 @@ def _get_rules_section_dependencies( return rules_section_dependencies + def get_calculated_summary_answer_ids( + self, calculated_summary_block: Mapping[str, Any] + ) -> list[str]: + if calculated_summary_block["calculation"].get("answers_to_calculate"): + return calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + + values = self._get_dictionaries_with_key( + "source", calculated_summary_block["calculation"]["operation"] + ) + + return [value["identifier"] for value in values if value["source"] == "answers"] + def get_summary_item_for_list_for_section( self, *, section_id: str, list_name: str ) -> Optional[ImmutableDict]: diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index bd67d6fd86..2a9be96820 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -272,15 +272,12 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: answer_id, set() ) - for dependency in dependencies: - if dependency.for_list: - list_item_ids: Union[list[str], list[None]] = self._list_store[ - dependency.for_list - ].items - else: - list_item_ids = [None] + is_repeating_answer = self._schema.is_answer_in_repeating_section(answer_id) - for list_item_id in list_item_ids: + for dependency in dependencies: + for list_item_id in self._get_list_item_ids_for_dependency( + dependency, is_repeating_answer + ): if dependency.answer_id: self._answer_store.remove_answer( dependency.answer_id, list_item_id=list_item_id @@ -290,6 +287,23 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: (dependency.section_id, list_item_id) ].add(dependency.block_id) + def _get_list_item_ids_for_dependency( + self, dependency: AnswerDependent, is_repeating_answer: Optional[bool] = False + ) -> Union[list[str], list[None]]: + if dependency.for_list: + list_item_ids: Union[list[str], list[None]] + + if is_repeating_answer: + # If the source answer is repeating, then we must be in the current repeating section. + # A repeating answer should only ever be depended on by itself. + list_item_ids = [self._current_location.list_item_id] # type: ignore + else: + list_item_ids = self._list_store[dependency.for_list].items + else: + list_item_ids = [None] + + return list_item_ids + def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: """Captures a unique list of section ids that are dependents of the provided answer id.""" diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 77604c44b7..40c34b637d 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -230,8 +230,10 @@ def _get_return_to_location_url( ): return url_for( "questionnaire.block", - list_item_id=location.list_item_id, block_id=return_to_block_id, + list_name=location.list_name, + list_item_id=location.list_item_id, + return_to=return_to, _anchor=return_to_answer_id, ) diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index b9266fae18..26dc082c9f 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -240,5 +240,9 @@ def evaluate_option_label_from_value(self, value: str, answer_id: str) -> str: else: label = self.renderer.render_placeholder(label_options, list_item_id=None) - return label + + @staticmethod + def evaluate_sum(*args: tuple) -> Union[int, float, Decimal]: + # type ignore added as will only return int, float or decimal + return sum(value for value in args if isinstance(value, (int, float, Decimal))) # type: ignore diff --git a/app/questionnaire/rules/operator.py b/app/questionnaire/rules/operator.py index 12150c86bf..0b7ec073b4 100644 --- a/app/questionnaire/rules/operator.py +++ b/app/questionnaire/rules/operator.py @@ -26,6 +26,7 @@ class Operator: FORMAT_DATE = "format-date" MAP = "map" OPTION_LABEL_FROM_VALUE = "option-label-from-value" + SUM = "+" def __init__(self, name: str, operations: "Operations") -> None: self.name = name @@ -78,4 +79,5 @@ def _any_operands_none(*operands: Union[Sequence, ValueTypes]) -> bool: Operator.FORMAT_DATE: "format_date", Operator.MAP: "evaluate_map", Operator.OPTION_LABEL_FROM_VALUE: "evaluate_option_label_from_value", + Operator.SUM: "evaluate_sum", } diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 89f0bd8bc8..64b7607741 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from datetime import date +from decimal import Decimal from typing import Generator, Iterable, Mapping, Optional, Sequence, Union from app.data_models import AnswerStore, ListStore @@ -15,7 +16,9 @@ ValueSourceTypes, ) -RuleEvaluatorTypes = Union[bool, Optional[date], list[str], list[date]] +RuleEvaluatorTypes = Union[ + bool, Optional[date], list[str], list[date], int, float, Decimal +] @dataclass diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 6087aead78..45ac31d9c2 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -11,6 +11,7 @@ from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.location import InvalidLocationException from app.questionnaire.relationship_location import RelationshipLocation +from app.questionnaire.rules import rule_evaluator ValueSourceTypes = Union[None, str, int, Decimal, list] ValueSourceEscapedTypes = Union[ @@ -127,13 +128,25 @@ def _resolve_calculated_summary_value_source( """ calculated_summary_block: Mapping[str, Any] = self.schema.get_block(value_source["identifier"]) # type: ignore calculation = calculated_summary_block["calculation"] - operator = self.get_calculation_operator(calculation["calculation_type"]) - list_item_id = self._resolve_list_item_id_for_value_source(value_source) - values = [ - self._get_answer_value(answer_id=answer_id, list_item_id=list_item_id) - for answer_id in calculation["answers_to_calculate"] - ] - return operator([value for value in values if value]) # type: ignore + if calculation.get("answers_to_calculate"): + operator = self.get_calculation_operator(calculation["calculation_type"]) + list_item_id = self._resolve_list_item_id_for_value_source(value_source) + values = [ + self._get_answer_value(answer_id=answer_id, list_item_id=list_item_id) + for answer_id in calculation["answers_to_calculate"] + ] + return operator([value for value in values if value]) # type: ignore + + evaluate_calculated_summary = rule_evaluator.RuleEvaluator( + self.schema, + self.answer_store, + self.list_store, + self.metadata, + self.response_metadata, + location=self.location, + ) + + return evaluate_calculated_summary.evaluate(calculation["operation"]) # type: ignore @staticmethod def get_calculation_operator( diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index c80ff98eee..c772ce550f 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,4 +1,8 @@ from copy import deepcopy +from decimal import Decimal +from typing import Callable, List, Mapping, Tuple, Union + +from werkzeug.datastructures import ImmutableDict from app.jinja_filters import ( format_number, @@ -6,8 +10,9 @@ format_unit, get_formatted_currency, ) -from app.questionnaire.location import Location +from app.questionnaire import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.schema_utils import get_answer_ids_in_block from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_question_to_display, transform_variants @@ -16,11 +21,11 @@ class CalculatedSummaryContext(Context): - def build_groups_for_section(self, section, return_to_block_id): + def build_groups_for_section( + self, section: Mapping, return_to_block_id: str, current_location: Location + ) -> List[Group]: routing_path = self._router.routing_path(section["id"]) - location = Location(section["id"]) - return [ Group( group_schema=group, @@ -30,7 +35,7 @@ def build_groups_for_section(self, section, return_to_block_id): metadata=self._metadata, response_metadata=self._response_metadata, schema=self._schema, - location=location, + location=current_location, language=self._language, progress_store=self._progress_store, return_to="calculated-summary", @@ -39,51 +44,71 @@ def build_groups_for_section(self, section, return_to_block_id): for group in section["groups"] ] - def build_view_context_for_calculated_summary(self, current_location): - block = self._schema.get_block(current_location.block_id) - - return_to_block_id = block["id"] + def build_view_context_for_calculated_summary( + self, current_location: Location + ) -> Mapping: + # type ignores added as block will exist at this point + block_id: str = current_location.block_id # type: ignore + block: ImmutableDict = self._schema.get_block(block_id) # type: ignore calculated_section = self._build_calculated_summary_section( block, current_location ) calculation = block["calculation"] - groups = self.build_groups_for_section(calculated_section, return_to_block_id) + groups = self.build_groups_for_section( + calculated_section, block_id, current_location + ) formatted_total = self._get_formatted_total( groups or [], current_location=current_location, - calculation_operator=ValueSourceResolver.get_calculation_operator( + calculation=ValueSourceResolver.get_calculation_operator( calculation["calculation_type"] - ), + ) + if calculation.get("answers_to_calculate") + else calculation["operation"], ) - context = { + collapsible = block.get("collapsible") or False + block_title = block["title"] + + return { "summary": { "groups": groups, "answers_are_editable": True, "calculated_question": self._get_calculated_question( calculation, formatted_total ), - "title": block.get("title") % dict(total=formatted_total), - "collapsible": block.get("collapsible", False), + "title": block_title % dict(total=formatted_total), + "collapsible": collapsible, "summary_type": "CalculatedSummary", } } - return context - - def _build_calculated_summary_section(self, rendered_block, current_location): + def _build_calculated_summary_section( + self, rendered_block: ImmutableDict, current_location: Location + ) -> Mapping: """Build up the list of blocks only including blocks / questions / answers which are relevant to the summary""" - section_id = self._schema.get_section_id_for_block_id(current_location.block_id) - group = self._schema.get_group_for_block_id(current_location.block_id) + # type ignores added as block will exist at this point + block_id: str = current_location.block_id # type: ignore + group: ImmutableDict = self._schema.get_group_for_block_id(block_id) # type: ignore + + section_id = self._schema.get_section_id_for_block_id(block_id) + blocks = [] - answers_to_calculate = rendered_block["calculation"]["answers_to_calculate"] - blocks_to_calculate = [ + if rendered_block["calculation"].get("answers_to_calculate"): + answers_to_calculate = rendered_block["calculation"]["answers_to_calculate"] + else: + answers_to_calculate = self._schema.get_calculated_summary_answer_ids( + rendered_block + ) + + blocks_to_calculate: list = [ self._schema.get_block_for_answer_id(answer_id) for answer_id in answers_to_calculate ] + unique_blocks = list( {block["id"]: block for block in blocks_to_calculate}.values() ) @@ -102,7 +127,7 @@ def _build_calculated_summary_section(self, rendered_block, current_location): def _remove_unwanted_questions_answers( self, block, answer_ids_to_keep, current_location - ): + ) -> str: """ Evaluates questions in a block and removes any questions not containing a relevant answer """ @@ -137,9 +162,37 @@ def _remove_unwanted_questions_answers( return transformed_block - def _get_formatted_total(self, groups, current_location, calculation_operator): - values_to_calculate = [] - answer_format = {"type": None} + def _get_formatted_total( + self, + groups: list, + current_location: Location, + calculation: Union[Callable, ImmutableDict], + ) -> str: + answer_format, values_to_calculate = self._get_answer_format( + groups, current_location + ) + + if isinstance(calculation, ImmutableDict): + evaluate_calculated_summary = RuleEvaluator( + self._schema, + self._answer_store, + self._list_store, + self._metadata, + self._response_metadata, + location=current_location, + ) + + calculated_total: Union[int, float, Decimal] = evaluate_calculated_summary.evaluate(calculation) # type: ignore + else: + calculated_total = calculation(values_to_calculate) + + return self._format_total(answer_format, calculated_total) + + def _get_answer_format( + self, groups: list, current_location: Location + ) -> Tuple[Mapping, list]: + values_to_calculate: list = [] + answer_format: Mapping = {"type": None} for group in groups: for block in group["blocks"]: question = choose_question_to_display( @@ -162,22 +215,31 @@ def _get_formatted_total(self, groups, current_location, calculation_operator): answer_value = answer.get("value") or 0 values_to_calculate.append(answer_value) - calculated_total = calculation_operator(values_to_calculate) + return answer_format, values_to_calculate + + @staticmethod + def _format_total( + answer_format: Mapping[str, str], total: Union[int, float, Decimal] + ) -> str: if answer_format["type"] == "currency": - return get_formatted_currency(calculated_total, answer_format["currency"]) + return get_formatted_currency(total, answer_format["currency"]) if answer_format["type"] == "unit": return format_unit( - answer_format["unit"], calculated_total, answer_format["unit_length"] + answer_format["unit"], + total, + answer_format["unit_length"], ) if answer_format["type"] == "percentage": - return format_percentage(calculated_total) + return format_percentage(total) - return format_number(calculated_total) + return format_number(total) @staticmethod - def _get_calculated_question(calculation_question, formatted_total): + def _get_calculated_question( + calculation_question: ImmutableDict, formatted_total: str + ) -> Mapping: calculation_title = calculation_question["title"] return { diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json new file mode 100644 index 0000000000..c8e563678d --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary.json @@ -0,0 +1,594 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to demo Calculated Summary", + "theme": "default", + "description": "A schema to showcase Calculated Summary pages and usage in value source.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "id": "group", + "title": "Total a range of values", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-number-answer-unit-total", + "label": "Second answer label in unit total", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-centimeter" + }, + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "third-and-a-half-number-block", + "question": { + "id": "third-and-a-half-number-question-unit-total", + "title": "Third Number Question Title Unit Total", + "type": "General", + "answers": [ + { + "id": "third-and-a-half-number-answer-unit-total", + "label": "Third answer label in unit total", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-centimeter" + } + ] + } + }, + { + "type": "Question", + "id": "skip-fourth-block", + "question": { + "type": "General", + "id": "skip-fourth-block-question", + "title": "Skip Fourth Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-fourth-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": [ + { + "when": [ + { + "id": "skip-fourth-block-answer", + "condition": "equals", + "value": "Yes" + } + ] + } + ], + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "Fourth Number Question Title", + "type": "General", + "answers": [ + { + "id": "fourth-number-answer", + "label": "Fourth answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": [ + { + "when": [ + { + "id": "skip-fourth-block-answer", + "condition": "equals", + "value": "Yes" + } + ] + } + ], + "type": "Question", + "id": "fourth-and-a-half-number-block", + "question": { + "id": "fourth-and-a-half-number-question-also-in-total", + "title": "Fourth Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "fourth-and-a-half-number-answer-also-in-total", + "label": "Fourth answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "fifth-number-block", + "question": { + "id": "fifth-number-question", + "title": "Fifth Number Question Title Percentage", + "type": "General", + "answers": [ + { + "id": "fifth-percent-answer", + "label": "Fifth answer label percentage total", + "mandatory": true, + "type": "Percentage", + "maximum": { + "value": 100 + } + }, + { + "id": "fifth-number-answer", + "label": "Fifth answer label number total", + "mandatory": false, + "type": "Number", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "sixth-number-block", + "question": { + "id": "sixth-number-question", + "title": "Sixth Number Question Title Percentage", + "type": "General", + "answers": [ + { + "id": "sixth-percent-answer", + "label": "Sixth answer label percentage total", + "mandatory": true, + "type": "Percentage", + "maximum": { + "value": 100 + } + }, + { + "id": "sixth-number-answer", + "label": "Sixth answer label number total", + "mandatory": false, + "type": "Number", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-skipped-fourth", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "third-number-answer" + } + ] + }, + "title": "Grand total of previous values" + }, + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-with-fourth", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "fourth-number-answer" + }, + { + "source": "answers", + "identifier": "fourth-and-a-half-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + }, + "skip_conditions": [ + { + "when": [ + { + "id": "skip-fourth-block-answer", + "condition": "equals", + "value": "Yes" + } + ] + } + ] + }, + { + "type": "CalculatedSummary", + "id": "unit-total-playback", + "title": "We calculate the total of unit values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "second-number-answer-unit-total" + }, + { + "source": "answers", + "identifier": "third-and-a-half-number-answer-unit-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "CalculatedSummary", + "id": "percentage-total-playback", + "title": "We calculate the total of percentage values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "fifth-percent-answer" + }, + { + "source": "answers", + "identifier": "sixth-percent-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "CalculatedSummary", + "id": "number-total-playback", + "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "fifth-number-answer" + }, + { + "source": "answers", + "identifier": "sixth-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Interstitial", + "id": "calculated-summary-total-confirmation", + "content": { + "title": "You have provided the following grand totals.", + "contents": [ + { + "list": [ + { + "text": "Total currency values (if Q4 not skipped): {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "currency-total-playback-with-fourth" + } + } + } + ] + } + ] + }, + { + "text": "Total currency values (if Q4 skipped)): {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + } + ] + } + ] + }, + { + "text": "Total unit values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "unit-total-playback" + } + } + } + ] + } + ] + }, + { + "text": "Total percentage values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "percentage-total-playback" + } + } + } + ] + } + ] + }, + { + "text": "Total number values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "number-total-playback" + } + } + } + ] + } + ] + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" + ] + } + }, + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-skipped-fourth", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_dependent_questions.json b/schemas/test/en/test_new_calculated_summary_dependent_questions.json new file mode 100644 index 0000000000..b23550329e --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_dependent_questions.json @@ -0,0 +1,177 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to demo Calculated Summary", + "description": "A schema to showcase Calculated Summary with dependent questions.", + "questionnaire_flow": { + "type": "Linear", + "options": {} + }, + "sections": [ + { + "id": "default-section", + "title": "Section 1", + "summary": { + "show_on_completion": false, + "collapsible": false + }, + "show_on_hub": true, + "groups": [ + { + "id": "group-1", + "blocks": [ + { + "id": "block-1", + "type": "Question", + "question": { + "id": "question-1", + "title": "How much did you spend on food?", + "type": "General", + "answers": [ + { + "id": "answer-1", + "mandatory": true, + "type": "Currency", + "label": "Money spent on food", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-2", + "type": "Question", + "question": { + "id": "question-2", + "title": "Of the money spent on food, how much did you spend on vegetables?", + "type": "General", + "answers": [ + { + "id": "answer-2", + "mandatory": true, + "type": "Currency", + "label": "Money spent on vegetables", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "maximum": { + "value": { + "identifier": "answer-1", + "source": "answers" + }, + "exclusive": false + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-3", + "type": "Question", + "question": { + "id": "question-3", + "title": "How much did you spend on clothes?", + "type": "General", + "answers": [ + { + "id": "answer-3", + "mandatory": true, + "type": "Currency", + "label": "Money spent on clothes", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "id": "block-4", + "type": "Question", + "question": { + "id": "question-4", + "title": "Of the money spent on clothes, how much did you spend on shoes?", + "type": "General", + "answers": [ + { + "id": "answer-4", + "mandatory": true, + "type": "Currency", + "label": "Money spent on shoes", + "description": "Enter the full value", + "minimum": { + "value": 0, + "exclusive": true + }, + "maximum": { + "value": { + "identifier": "answer-3", + "source": "answers" + }, + "exclusive": false + }, + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + + { + "id": "calculated-summary-block", + "type": "CalculatedSummary", + "title": "We have calculated your total spend to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "answer-1" + }, + { + "source": "answers", + "identifier": "answer-3" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + } + ], + "theme": "default", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json new file mode 100644 index 0000000000..97f3084361 --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -0,0 +1,825 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to demo Calculated Summary", + "theme": "default", + "description": "A schema to showcase Calculated Summary pages and usage in value source in repeating sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section", + "title": "Household", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": "Do you live here?", + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "personal-details-section", + "title": "Personal Details", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "personal-details-group", + "title": "Personal Details", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-number-answer-unit-total", + "label": "Second answer label in unit total", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-centimeter" + }, + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "third-and-a-half-number-block", + "question": { + "id": "third-and-a-half-number-question-unit-total", + "title": "Third Number Question Title Unit Total", + "type": "General", + "answers": [ + { + "id": "third-and-a-half-number-answer-unit-total", + "label": "Third answer label in unit total", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-centimeter" + } + ] + } + }, + { + "type": "Question", + "id": "skip-fourth-block", + "question": { + "type": "General", + "id": "skip-fourth-block-question", + "title": "Skip Fourth Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-fourth-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "Fourth Number Question Title", + "type": "General", + "answers": [ + { + "id": "fourth-number-answer", + "label": "Fourth answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "fourth-and-a-half-number-block", + "question": { + "id": "fourth-and-a-half-number-question-also-in-total", + "title": "Fourth Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "fourth-and-a-half-number-answer-also-in-total", + "label": "Fourth answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "fifth-number-block", + "question": { + "id": "fifth-number-question", + "title": "Fifth Number Question Title Percentage", + "type": "General", + "answers": [ + { + "id": "fifth-percent-answer", + "label": "Fifth answer label percentage total", + "mandatory": true, + "type": "Percentage", + "maximum": { + "value": 100 + } + }, + { + "id": "fifth-number-answer", + "label": "Fifth answer label number total", + "mandatory": false, + "type": "Number", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "sixth-number-block", + "question": { + "id": "sixth-number-question", + "title": "Sixth Number Question Title Percentage", + "type": "General", + "answers": [ + { + "id": "sixth-percent-answer", + "label": "Sixth answer label percentage total", + "mandatory": true, + "type": "Percentage", + "maximum": { + "value": 100 + } + }, + { + "id": "sixth-number-answer", + "label": "Sixth answer label number total", + "mandatory": false, + "type": "Number", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-skipped-fourth", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", + "page_title": "Calculated Summary", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "third-number-answer" + } + ] + }, + "title": "Grand total of previous values" + }, + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-with-fourth", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", + "page_title": "Calculated Summary", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "fourth-number-answer" + }, + { + "source": "answers", + "identifier": "fourth-and-a-half-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + }, + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "unit-total-playback", + "page_title": "Calculated Summary", + "title": "We calculate the total of unit values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "second-number-answer-unit-total" + }, + { + "source": "answers", + "identifier": "third-and-a-half-number-answer-unit-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "CalculatedSummary", + "id": "percentage-total-playback", + "page_title": "Calculated Summary", + "title": "We calculate the total of percentage values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "fifth-percent-answer" + }, + { + "source": "answers", + "identifier": "sixth-percent-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "CalculatedSummary", + "id": "number-total-playback", + "page_title": "Calculated Summary", + "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "fifth-number-answer" + }, + { + "source": "answers", + "identifier": "sixth-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Interstitial", + "id": "calculated-summary-total-confirmation", + "content": { + "title": "You have provided the following grand totals.", + "contents": [ + { + "list": [ + { + "text": "Total currency values (if Q4 not skipped): {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "currency-total-playback-with-fourth" + } + } + } + ] + } + ] + }, + { + "text": "Total currency values (if Q4 skipped)): {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + } + ] + } + ] + }, + { + "text": "Total unit values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "unit-total-playback" + } + } + } + ] + } + ] + }, + { + "text": "Total percentage values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "percentage-total-playback" + } + } + } + ] + } + ] + }, + { + "text": "Total number values: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "number-total-playback" + } + } + } + ] + } + ] + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" + ] + } + }, + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-skipped-fourth" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-skipped-fourth", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json b/schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json new file mode 100644 index 0000000000..f1c53305e4 --- /dev/null +++ b/schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json @@ -0,0 +1,300 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Routing and Skipping Section Dependencies based on Calculated Summary", + "theme": "default", + "description": "A questionnaire to test routing and skipping rules, when the rule references a different section to its current section", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "title": "Calculated Summary Section", + "summary": { "show_on_completion": true }, + "id": "calculated-summary-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "first-question-block", + "question": { + "id": "first-question", + "title": "How much do you spend on the following items?", + "description": [ + "If the total is equal to £100 a new section will appear on the hub and if it is greater than or equal to £100 a dependent question will appear in the dependent question section" + ], + "type": "General", + "answers": [ + { + "id": "milk-answer", + "label": "Milk", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "eggs-answer", + "label": "Eggs", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "bread-answer", + "label": "Bread", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "cheese-answer", + "label": "Cheese", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "milk-answer" + }, + { + "source": "answers", + "identifier": "eggs-answer" + }, + { + "source": "answers", + "identifier": "bread-answer" + }, + { + "source": "answers", + "identifier": "cheese-answer" + } + ] + }, + "title": "Grand total of previous values" + } + } + ], + "id": "calculated-summary-group" + } + ] + }, + { + "title": "Dependent question Section", + "summary": { "show_on_completion": true }, + "id": "dependent-question-section", + "groups": [ + { + "blocks": [ + { + "skip_conditions": { + "when": { + ">=": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + }, + 10 + ] + } + }, + "type": "Question", + "id": "fruit", + "question": { + "answers": [ + { + "id": "fruit-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "fruit-question", + "title": "Do you like eating fruit", + "type": "General" + } + }, + { + "routing_rules": [ + { + "block": "second-question-block", + "when": { + ">=": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + }, + 100 + ] + } + }, + { + "section": "End" + } + ], + "type": "Question", + "id": "vegetables", + "question": { + "answers": [ + { + "id": "vegetables-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "vegetables-question", + "title": "Do you like eating vegetables", + "type": "General" + } + }, + { + "type": "Question", + "id": "second-question-block", + "question": { + "id": "second-question", + "title": "How much do you spend on the following items?", + "type": "General", + "answers": [ + { + "id": "apples-answer", + "label": "Apples", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "bananas-answer", + "label": "Bananas", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "oranges-answer", + "label": "Oranges", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "lemons-answer", + "label": "Lemons", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + } + ], + "id": "dependent-question-group" + } + ] + }, + { + "enabled": { + "when": { + "==": [ + 100, + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + } + ] + } + }, + "title": "Dependent Enabled Section", + "summary": { "show_on_completion": true }, + "id": "dependent-enabled-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "desserts", + "question": { + "answers": [ + { + "id": "desserts-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "desserts-question", + "title": "Do you like eating desserts", + "type": "General" + } + } + ], + "id": "dependent-enabled-section-group" + } + ] + } + ] +} diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 1019ef7c47..b91188f178 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1208,3 +1208,10 @@ def section_dependencies_calculated_summary_schema(): return load_schema_from_name( "test_new_routing_and_skipping_section_dependencies_calculated_summary" ) + + +@pytest.fixture +def section_dependencies_new_calculated_summary_schema(): + return load_schema_from_name( + "test_new_routing_and_skipping_section_dependencies_new_calculated_summary" + ) diff --git a/tests/app/questionnaire/rules/test_operators.py b/tests/app/questionnaire/rules/test_operators.py index 1953d79888..3b67da88ca 100644 --- a/tests/app/questionnaire/rules/test_operators.py +++ b/tests/app/questionnaire/rules/test_operators.py @@ -498,3 +498,20 @@ def test_operation_option_label_from_value(get_operator, mock_schema, mocker): mock_schema.get_answers_by_answer_id = mocker.Mock(return_value=answer_schema) assert operator.evaluate(operands) == "Head-label" + + +@pytest.mark.parametrize( + "operands, expected_result", + [ + ([1, 2, 3, 4], 10), + ([1.1, 2.2, 3.3, 4.4], 11.0), + ([[]], 0), + ([None], 0), + ([1, 2.2, 3, 4], 10.2), + (["a", 1], 1), + ], +) +def test_operator_sum(get_operator, operands, expected_result): + operator = get_operator(Operator.SUM) + + assert operator.evaluate(operands) == expected_result diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 3230447f0b..7a4aac5d6b 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -773,6 +773,19 @@ def test_when_rules_section_dependencies_calculated_summary( } == schema.when_rules_section_dependencies_by_answer +def test_when_rules_section_dependencies_new_calculated_summary( + section_dependencies_new_calculated_summary_schema, +): + schema = section_dependencies_new_calculated_summary_schema + + assert { + "milk-answer": {"dependent-enabled-section", "dependent-question-section"}, + "eggs-answer": {"dependent-enabled-section", "dependent-question-section"}, + "bread-answer": {"dependent-enabled-section", "dependent-question-section"}, + "cheese-answer": {"dependent-enabled-section", "dependent-question-section"}, + } == schema.when_rules_section_dependencies_by_answer + + @pytest.mark.parametrize( "rule, expected_result", ( diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index a67446df9a..19db35c138 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -14,7 +14,7 @@ ) -# pylint: disable=too-many-locals +# pylint: disable=too-many-locals, too-many-lines def test_save_answers_with_form_data( mock_location, mock_empty_schema, @@ -429,24 +429,41 @@ def get_answer_dependencies(for_list=None): @pytest.mark.parametrize( - "answer_id, answer_updated, answer_dependencies, expected_output", + "answer_id, answer_updated, answer_dependencies, is_repeating, expected_output", [ ( "total-employees-answer", True, get_answer_dependencies(), + False, {("breakdown-section", None): {"employees-breakdown-block"}}, ), ( "total-turnover-answer", True, get_answer_dependencies(), + False, {("breakdown-section", None): {"turnover-breakdown-block"}}, ), ( "total-employees-answer", True, get_answer_dependencies(for_list="people"), + True, + {("breakdown-section", "person-1"): {"employees-breakdown-block"}}, + ), + ( + "total-turnover-answer", + True, + get_answer_dependencies(for_list="people"), + True, + {("breakdown-section", "person-1"): {"turnover-breakdown-block"}}, + ), + ( + "total-employees-answer", + True, + get_answer_dependencies(for_list="people"), + False, { ("breakdown-section", "person-1"): {"employees-breakdown-block"}, ("breakdown-section", "person-2"): {"employees-breakdown-block"}, @@ -457,6 +474,7 @@ def get_answer_dependencies(for_list=None): "total-turnover-answer", True, get_answer_dependencies(for_list="people"), + False, { ("breakdown-section", "person-1"): {"turnover-breakdown-block"}, ("breakdown-section", "person-2"): {"turnover-breakdown-block"}, @@ -467,6 +485,7 @@ def get_answer_dependencies(for_list=None): "total-employees-answer", False, get_answer_dependencies(), + False, {}, ), ], @@ -477,12 +496,12 @@ def test_update_answers_captures_answer_dependencies( answer_id, answer_updated, answer_dependencies, + is_repeating, expected_output, mock_schema, ): location = Location( - section_id="default-section", - block_id="default-block", + section_id="default-section", block_id="default-block", list_item_id="person-1" ) list_store = ListStore( @@ -496,6 +515,7 @@ def test_update_answers_captures_answer_dependencies( mock_schema.get_answer_ids_for_question.return_value = [answer_id] mock_schema.answer_dependencies = answer_dependencies + mock_schema.is_answer_in_repeating_section.return_value = is_repeating mock_empty_answer_store.add_or_update.return_value = answer_updated form_data = MultiDict({answer_id: "some-value"}) @@ -601,11 +621,49 @@ def test_update_answers_with_answer_dependents( assert answer_store == expected_output -def test_update_repeating_answers_with_answer_dependents(mock_schema, mock_router): +@pytest.mark.parametrize( + "is_repeating, expected_output", + [ + ( + False, + AnswerStore( + [ + AnswerDict( + answer_id="first-answer", + value="answer updated", + list_item_id="abc123", + ), + ] + ), + ), + ( + True, + AnswerStore( + [ + AnswerDict( + answer_id="first-answer", + value="answer updated", + list_item_id="abc123", + ), + AnswerDict( + answer_id="second-answer", + value="second answer", + list_item_id="xyz456", + ), + ] + ), + ), + ], +) +def test_update_repeating_answers_with_answer_dependents( + mock_schema, mock_router, is_repeating, expected_output +): # Given repeating dependent answers answer_store = AnswerStore( [ - AnswerDict(answer_id="first-answer", value="original-answer"), + AnswerDict( + answer_id="first-answer", value="original-answer", list_item_id="abc123" + ), AnswerDict( answer_id="second-answer", value="second answer", list_item_id="abc123" ), @@ -631,10 +689,11 @@ def test_update_repeating_answers_with_answer_dependents(mock_schema, mock_route form_data = MultiDict({"first-answer": "answer updated"}) location = Location( - section_id="section", - block_id="first-block", + section_id="section", block_id="first-block", list_item_id="abc123" ) current_question = mock_schema.get_block(location.block_id)["question"] + + mock_schema.is_answer_in_repeating_section.return_value = is_repeating questionnaire_store_updater = get_questionnaire_store_updater( schema=mock_schema, answer_store=answer_store, @@ -648,9 +707,7 @@ def test_update_repeating_answers_with_answer_dependents(mock_schema, mock_route questionnaire_store_updater.update_answers(form_data) # Then all repeating dependent answers should be removed from the answer store - assert answer_store == AnswerStore( - [AnswerDict(answer_id="first-answer", value="answer updated")] - ) + assert answer_store == expected_output @pytest.mark.parametrize( diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index b9e74a5b7a..21f00722ae 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines from functools import cached_property import pytest @@ -508,8 +509,15 @@ def test_section_summary_on_completion_false(self): assert expected_location_url == next_location @pytest.mark.usefixtures("app") - def test_return_to_calculated_summary(self): - self.schema = load_schema_from_name("test_calculated_summary") + @pytest.mark.parametrize( + "schema", + ( + "test_calculated_summary", + "test_new_calculated_summary", + ), + ) + def test_return_to_calculated_summary(self, schema): + self.schema = load_schema_from_name(schema) current_location = Location( section_id="default-section", block_id="second-number-block" @@ -547,10 +555,15 @@ def test_return_to_calculated_summary(self): assert expected_location_url == next_location_url @pytest.mark.usefixtures("app") - def test_return_to_calculated_summary_not_on_allowable_path(self): - self.schema = load_schema_from_name( - "test_calculated_summary_dependent_questions" - ) + @pytest.mark.parametrize( + "schema", + ( + "test_calculated_summary_dependent_questions", + "test_new_calculated_summary_dependent_questions", + ), + ) + def test_return_to_calculated_summary_not_on_allowable_path(self, schema): + self.schema = load_schema_from_name(schema) current_location = Location(section_id="default-section", block_id="block-3") @@ -589,19 +602,34 @@ def test_return_to_calculated_summary_not_on_allowable_path(self): @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "return_to_block_id, expected_url", + "schema, return_to_block_id, expected_url", [ ( + "test_calculated_summary", "non-valid-block", "/questionnaire/sixth-number-block/?return_to=calculated-summary&return_to_block_id=non-valid-block", ), - (None, "/questionnaire/sixth-number-block/?return_to=calculated-summary"), + ( + "test_calculated_summary", + None, + "/questionnaire/sixth-number-block/?return_to=calculated-summary", + ), + ( + "test_new_calculated_summary", + "non-valid-block", + "/questionnaire/sixth-number-block/?return_to=calculated-summary&return_to_block_id=non-valid-block", + ), + ( + "test_new_calculated_summary", + None, + "/questionnaire/sixth-number-block/?return_to=calculated-summary", + ), ], ) def test_return_to_calculated_summary_invalid_return_to_block_id( - self, return_to_block_id, expected_url + self, schema, return_to_block_id, expected_url ): - self.schema = load_schema_from_name("test_calculated_summary") + self.schema = load_schema_from_name(schema) current_location = Location( section_id="default-section", block_id="fifth-number-block" @@ -621,8 +649,15 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( assert expected_url == next_location_url @pytest.mark.usefixtures("app") - def test_return_to_calculated_summary_return_to_block_id_not_on_path(self): - self.schema = load_schema_from_name("test_calculated_summary") + @pytest.mark.parametrize( + "schema", + ( + "test_calculated_summary", + "test_new_calculated_summary", + ), + ) + def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schema): + self.schema = load_schema_from_name(schema) current_location = Location( section_id="default-section", block_id="fifth-number-block" @@ -804,6 +839,7 @@ def test_return_to_calculated_summary(self): expected_location_url = url_for( "questionnaire.block", list_item_id=expected_location.list_item_id, + return_to="calculated-summary", block_id=expected_location.block_id, _anchor="first-number-answer", ) diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index a95c940fd0..7a487cb604 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -534,6 +534,112 @@ def test_calculated_summary_value_source(mocker, list_item_id): ) +@pytest.mark.parametrize( + "list_item_id", + [None, "item-1"], +) +def test_new_calculated_summary_value_source(mocker, list_item_id): + schema = mocker.MagicMock() + schema.get_block = Mock( + return_value={ + "id": "number-total", + "type": "CalculatedSummary", + "calculation": { + "operation": { + "+": [ + {"source": "answers", "identifier": "number-answer-1"}, + {"source": "answers", "identifier": "number-answer-2"}, + ] + } + }, + }, + ) + + location = Location( + section_id="test-section", block_id="test-block", list_item_id=list_item_id + ) + + value_source_resolver = get_value_source_resolver( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="number-answer-1", + value=10, + list_item_id=location.list_item_id, + ), + AnswerDict( + answer_id="number-answer-2", value=5, list_item_id=list_item_id + ), + ] + ), + schema=schema, + list_item_id=list_item_id, + location=location, + ) + assert ( + value_source_resolver.resolve( + {"source": "calculated_summary", "identifier": "number-total"} + ) + == 15 + ) + + +@pytest.mark.parametrize( + "list_item_id", + [None, "item-1"], +) +def test_new_calculated_summary_nested_value_source(mocker, list_item_id): + schema = mocker.MagicMock() + schema.get_block = Mock( + return_value={ + "id": "number-total", + "type": "CalculatedSummary", + "calculation": { + "operation": { + "+": [ + { + "+": [ + {"source": "answers", "identifier": "number-answer-1"}, + {"source": "answers", "identifier": "number-answer-2"}, + ] + }, + {"source": "answers", "identifier": "number-answer-3"}, + ] + } + }, + }, + ) + + location = Location( + section_id="test-section", block_id="test-block", list_item_id=list_item_id + ) + + value_source_resolver = get_value_source_resolver( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="number-answer-1", value=10, list_item_id=list_item_id + ), + AnswerDict( + answer_id="number-answer-2", value=5, list_item_id=list_item_id + ), + AnswerDict( + answer_id="number-answer-3", value=5, list_item_id=list_item_id + ), + ] + ), + schema=schema, + list_item_id=list_item_id, + location=location, + ) + assert ( + value_source_resolver.resolve( + {"source": "calculated_summary", "identifier": "number-total"} + ) + == 20 + ) + + @pytest.mark.parametrize( "answer_value, escaped_value", [ diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index c971fb36b6..57b1af69fc 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -6,6 +6,7 @@ import os import re from string import Template +from typing import Mapping, Sequence from app.utilities.json import json_loads @@ -871,9 +872,23 @@ def process_block( process_guidance(context, page_spec) elif block["type"] == "CalculatedSummary": - process_calculated_summary( - block["calculation"]["answers_to_calculate"], page_spec - ) + if block["calculation"].get("answers_to_calculate"): + process_calculated_summary( + block["calculation"]["answers_to_calculate"], page_spec + ) + else: + values = _get_dictionaries_with_key( + "source", block["calculation"]["operation"] + ) + + calculated_summary_answer_ids = [ + value["identifier"] + for value in values + if value["source"] == "answers" + ] + + process_calculated_summary(calculated_summary_answer_ids, page_spec) + elif block["type"] == "Interstitial": has_definition = False if "content_variants" in block: @@ -923,6 +938,20 @@ def _has_guidance_in_primary_contents(block_contents): return any("guidance" in element for element in block_contents) +def _get_dictionaries_with_key( + key, + dictionary, +): + if key in dictionary: + yield dictionary + + for value in dictionary.values(): + if isinstance(value, Sequence): + for element in value: + if isinstance(element, Mapping): + yield from _get_dictionaries_with_key(key, element) + + def process_schema(in_schema, out_dir, spec_file, require_path=".."): try: with open(in_schema, encoding="utf-8") as schema: diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index ff1ca873d1..334a367c1d 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -1,331 +1,10 @@ -import FirstNumberBlockPage from "../../generated_pages/calculated_summary/first-number-block.page.js"; -import SecondNumberBlockPage from "../../generated_pages/calculated_summary/second-number-block.page.js"; -import ThirdNumberBlockPage from "../../generated_pages/calculated_summary/third-number-block.page.js"; -import ThirdAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/third-and-a-half-number-block.page.js"; -import SkipFourthBlockPage from "../../generated_pages/calculated_summary/skip-fourth-block.page.js"; -import FourthNumberBlockPage from "../../generated_pages/calculated_summary/fourth-number-block.page.js"; -import FourthAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/fourth-and-a-half-number-block.page.js"; -import FifthNumberBlockPage from "../../generated_pages/calculated_summary/fifth-number-block.page.js"; -import SixthNumberBlockPage from "../../generated_pages/calculated_summary/sixth-number-block.page.js"; -import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page.js"; -import SetMinMaxBlockPage from "../../generated_pages/calculated_summary/set-min-max-block.page.js"; -import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/calculated_summary/currency-total-playback-skipped-fourth.page.js"; -import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page.js"; -import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page.js"; -import NumberTotalPlaybackPage from "../../generated_pages/calculated_summary/number-total-playback.page.js"; -import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page.js"; -import SubmitPage from "../../generated_pages/calculated_summary/submit.page"; -import ThankYouPage from "../../base_pages/thank-you.page.js"; +import { CalculatedSummaryTestCase } from "./calculated_summary_test_case.js"; describe("Feature: Calculated Summary", () => { describe("Given I have a Calculated Summary", () => { - before("Get to Calculated Summary", () => { - browser.openQuestionnaire("test_calculated_summary.json"); - - $(FirstNumberBlockPage.firstNumber()).setValue(1.23); - $(FirstNumberBlockPage.submit()).click(); - - $(SecondNumberBlockPage.secondNumber()).setValue(4.56); - $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); - $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); - $(SecondNumberBlockPage.submit()).click(); - - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - - $(SkipFourthBlockPage.no()).click(); - $(SkipFourthBlockPage.submit()).click(); - - $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); - $(FourthNumberBlockPage.submit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); - $(FourthAndAHalfNumberBlockPage.submit()).click(); - - $(FifthNumberBlockPage.fifthPercent()).setValue(56); - $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); - $(FifthNumberBlockPage.submit()).click(); - - $(SixthNumberBlockPage.sixthPercent()).setValue(23); - $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); - $(SixthNumberBlockPage.submit()).click(); - - const browserUrl = browser.getUrl(); - - expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - }); - - it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { - expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); - }); - - it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { - // Totals and titles should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £20.71. Is this correct?" - ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); - - // Answers included in calculation should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Second answer label also in currency total (optional)" - ); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Fourth answer label also in total (optional)" - ); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); - - // Answers not included in calculation should not be shown - expect($$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; - expect($$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; - expect($$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; - expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; - }); - - it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( - "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" - ); - }); - - it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.previous()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/#third-number-answer"); - }); - - it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/#third-number-answer"); - }); - - it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); - $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); - $(FourthNumberBlockPage.submit()).click(); - - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £30.71. Is this correct?" - ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); - }); - - it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); - $(FourthAndAHalfNumberBlockPage.submit()).click(); - - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £28.37. Is this correct?" - ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); - }); - - it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { - $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); - $(SixthNumberBlockPage.previous()).click(); - $(FifthNumberBlockPage.previous()).click(); - $(FourthAndAHalfNumberBlockPage.previous()).click(); - $(FourthNumberBlockPage.previous()).click(); - - $(SkipFourthBlockPage.yes()).click(); - $(SkipFourthBlockPage.submit()).click(); - - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - const expectedUrl = browser.getUrl(); - - expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.36. Is this correct?" - ); - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); - }); - - it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", () => { - // Totals and titles should be shown - $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" - ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); - - // Answers included in calculation should be shown - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); - }); - - it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { - // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of percentage values entered to be 79%. Is this correct?" - ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); - - // Answers included in calculation should be shown - expect($(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); - }); - - it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { - // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of number values entered to be 124.58. Is this correct?" - ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); - - // Answers included in calculation should be shown - expect($(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); - expect($(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); - }); - - it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { - $(NumberTotalPlaybackPage.submit()).click(); - - const content = $("h1 + ul").getText(); - const textsToAssert = [ - "Total currency values (if Q4 not skipped): £28.37", - "Total currency values (if Q4 skipped)): £9.36", - "Total unit values: 1,467", - "Total percentage values: 79", - "Total number values: 124.58", - ]; - - textsToAssert.forEach((text) => expect(content).to.contain(text)); - }); - - it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); - }); - - it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); - $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); - $(SetMinMaxBlockPage.submit()).click(); - }); - - it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.41. Is this correct?" - ); - - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - }); - - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £15.91. Is this correct?" - ); - - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); - $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - }); - - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £6.91. Is this correct?" - ); - - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); - $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - }); - - it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - }); + CalculatedSummaryTestCase.testCase("test_calculated_summary.json"); + }); + describe("Given I have a Calculated Summary with the new format", () => { + CalculatedSummaryTestCase.testCase("test_new_calculated_summary.json"); }); }); diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary_test_case.js new file mode 100644 index 0000000000..3dc34baa46 --- /dev/null +++ b/tests/functional/spec/features/calculated_summary_test_case.js @@ -0,0 +1,333 @@ +import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page"; +import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page"; +import NumberTotalPlaybackPage from "../../generated_pages/calculated_summary/number-total-playback.page"; +import ThirdNumberBlockPage from "../../generated_pages/calculated_summary/third-number-block.page"; +import FourthNumberBlockPage from "../../generated_pages/calculated_summary/fourth-number-block.page"; +import FourthAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/fourth-and-a-half-number-block.page"; +import SixthNumberBlockPage from "../../generated_pages/calculated_summary/sixth-number-block.page"; +import FifthNumberBlockPage from "../../generated_pages/calculated_summary/fifth-number-block.page"; +import SkipFourthBlockPage from "../../generated_pages/calculated_summary/skip-fourth-block.page"; +import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/calculated_summary/currency-total-playback-skipped-fourth.page"; +import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page"; +import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page"; +import SetMinMaxBlockPage from "../../generated_pages/calculated_summary/set-min-max-block.page"; +import SubmitPage from "../../generated_pages/calculated_summary/submit.page"; +import ThirdAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/third-and-a-half-number-block.page"; +import ThankYouPage from "../../base_pages/thank-you.page"; +import FirstNumberBlockPage from "../../generated_pages/calculated_summary/first-number-block.page"; +import SecondNumberBlockPage from "../../generated_pages/calculated_summary/second-number-block.page"; + +class TestCase { + testCase(schema) { + before("Get to Calculated Summary", () => { + browser.openQuestionnaire(schema); + + $(FirstNumberBlockPage.firstNumber()).setValue(1.23); + $(FirstNumberBlockPage.submit()).click(); + + $(SecondNumberBlockPage.secondNumber()).setValue(4.56); + $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); + $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); + $(SecondNumberBlockPage.submit()).click(); + + $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + + $(SkipFourthBlockPage.no()).click(); + $(SkipFourthBlockPage.submit()).click(); + + $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); + $(FourthNumberBlockPage.submit()).click(); + $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); + $(FourthAndAHalfNumberBlockPage.submit()).click(); + + $(FifthNumberBlockPage.fifthPercent()).setValue(56); + $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); + $(FifthNumberBlockPage.submit()).click(); + + $(SixthNumberBlockPage.sixthPercent()).setValue(23); + $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); + $(SixthNumberBlockPage.submit()).click(); + + const browserUrl = browser.getUrl(); + + expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + }); + + it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { + expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + }); + + it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { + // Totals and titles should be shown + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £20.71. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + + // Answers included in calculation should be shown + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + "Second answer label also in currency total (optional)" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + "Fourth answer label also in total (optional)" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + + // Answers not included in calculation should not be shown + expect($$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; + expect($$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; + expect($$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; + expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + }); + + it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" + ); + }); + + it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.previous()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + }); + + it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + }); + + it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { + $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); + $(FourthNumberBlockPage.submit()).click(); + + expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £30.71. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + }); + + it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { + $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); + $(FourthAndAHalfNumberBlockPage.submit()).click(); + + expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £28.37. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + }); + + it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { + $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + $(SixthNumberBlockPage.previous()).click(); + $(FifthNumberBlockPage.previous()).click(); + $(FourthAndAHalfNumberBlockPage.previous()).click(); + $(FourthNumberBlockPage.previous()).click(); + + $(SkipFourthBlockPage.yes()).click(); + $(SkipFourthBlockPage.submit()).click(); + + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + const expectedUrl = browser.getUrl(); + + expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + expect($$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; + expect($$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £9.36. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + }); + + it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + + // Answers included in calculation should be shown + expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); + expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); + expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); + expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + }); + + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(UnitTotalPlaybackPage.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of percentage values entered to be 79%. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + + // Answers included in calculation should be shown + expect($(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); + expect($(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); + expect($(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); + expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + }); + + it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(UnitTotalPlaybackPage.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of number values entered to be 124.58. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + + // Answers included in calculation should be shown + expect($(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); + expect($(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); + expect($(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); + expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + }); + + it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { + $(NumberTotalPlaybackPage.submit()).click(); + + const content = $("h1 + ul").getText(); + const textsToAssert = [ + "Total currency values (if Q4 not skipped): £28.37", + "Total currency values (if Q4 skipped)): £9.36", + "Total unit values: 1,467", + "Total percentage values: 79", + "Total number values: 124.58", + ]; + + textsToAssert.forEach((text) => expect(content).to.contain(text)); + }); + + it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + + it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { + $(SubmitPage.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £9.41. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £15.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £6.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", () => { + $(SubmitPage.submit()).click(); + expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + }); + } +} + +export const CalculatedSummaryTestCase = new TestCase(); diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js new file mode 100644 index 0000000000..5cfbde60d9 --- /dev/null +++ b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js @@ -0,0 +1,424 @@ +import FirstNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/first-number-block.page.js"; +import SecondNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/second-number-block.page.js"; +import ThirdNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/third-number-block.page.js"; +import ThirdAndAHalfNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/third-and-a-half-number-block.page.js"; +import SkipFourthBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/skip-fourth-block.page.js"; +import FourthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fourth-number-block.page.js"; +import FourthAndAHalfNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fourth-and-a-half-number-block.page.js"; +import FifthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fifth-number-block.page.js"; +import SixthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/sixth-number-block.page.js"; +import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page.js"; +import SetMinMaxBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/set-min-max-block.page.js"; +import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/new_calculated_summary_repeating_section/currency-total-playback-skipped-fourth.page.js"; +import UnitTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/unit-total-playback.page.js"; +import PercentageTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/percentage-total-playback.page.js"; +import NumberTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/number-total-playback.page.js"; +import CalculatedSummaryTotalConfirmation from "../../generated_pages/new_calculated_summary_repeating_section/calculated-summary-total-confirmation.page.js"; +import SubmitPage from "../../generated_pages/new_calculated_summary_repeating_section/personal-details-section-summary.page.js"; +import ThankYouPage from "../../base_pages/thank-you.page.js"; +import HubPage from "../../base_pages/hub.page.js"; +import PrimaryPersonListCollectorPage from "../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector.page"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector-add.page.js"; +import ListCollectorPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector.page"; +import ListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector-add.page"; + +describe("Feature: Calculated Summary Repeating Section", () => { + describe("Given I have a Calculated Summary in a Repeating Section", () => { + before("Get to Calculated Summary", () => { + browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); + $(HubPage.submit()).click(); + $(PrimaryPersonListCollectorPage.yes()).click(); + $(PrimaryPersonListCollectorPage.submit()).click(); + $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + $(PrimaryPersonListCollectorAddPage.submit()).click(); + $(ListCollectorPage.no()).click(); + $(ListCollectorPage.submit()).click(); + $(HubPage.submit()).click(); + + getToFirstCalculatedSummary(); + + const browserUrl = browser.getUrl(); + + expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + }); + + it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { + expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + }); + + it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { + // Totals and titles should be shown + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £20.71. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + + // Answers included in calculation should be shown + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + "Second answer label also in currency total (optional)" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + "Fourth answer label also in total (optional)" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + + // Answers not included in calculation should not be shown + expect($$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; + expect($$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; + expect($$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; + expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + }); + + it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { + expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" + ); + }); + + it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.previous()).click(); + expect(browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + }); + + it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { + $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + }); + + it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { + $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); + $(FourthNumberBlockPage.submit()).click(); + + expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £30.71. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + }); + + it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { + $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); + $(FourthAndAHalfNumberBlockPage.submit()).click(); + + expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £28.37. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + }); + + it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { + $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + $(SixthNumberBlockPage.previous()).click(); + $(FifthNumberBlockPage.previous()).click(); + $(FourthAndAHalfNumberBlockPage.previous()).click(); + $(FourthNumberBlockPage.previous()).click(); + + $(SkipFourthBlockPage.yes()).click(); + $(SkipFourthBlockPage.submit()).click(); + + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + const expectedUrl = browser.getUrl(); + + expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + expect($$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; + expect($$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £9.36. Is this correct?" + ); + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + }); + + it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + + // Answers included in calculation should be shown + expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); + expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); + expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); + expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + }); + + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(UnitTotalPlaybackPage.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of percentage values entered to be 79%. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + + // Answers included in calculation should be shown + expect($(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); + expect($(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); + expect($(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); + expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + }); + + it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { + // Totals and titles should be shown + $(UnitTotalPlaybackPage.submit()).click(); + expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of number values entered to be 124.58. Is this correct?" + ); + expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + + // Answers included in calculation should be shown + expect($(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); + expect($(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); + expect($(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); + expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + }); + + it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { + $(NumberTotalPlaybackPage.submit()).click(); + + const content = $("h1 + ul").getText(); + const textsToAssert = [ + "Total currency values (if Q4 not skipped): £28.37", + "Total currency values (if Q4 skipped)): £9.36", + "Total unit values: 1,467", + "Total percentage values: 79", + "Total number values: 124.58", + ]; + + textsToAssert.forEach((text) => expect(content).to.contain(text)); + }); + + it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + + it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { + $(SubmitPage.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); + $(SetMinMaxBlockPage.submit()).click(); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £9.41. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £15.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.thirdNumberAnswerEdit()).click(); + $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + $(SkipFourthBlockPage.submit()).click(); + $(FifthNumberBlockPage.submit()).click(); + $(SixthNumberBlockPage.submit()).click(); + + expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of currency values entered to be £6.91. Is this correct?" + ); + + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + $(SetMinMaxBlockPage.submit()).click(); + expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + $(SetMinMaxBlockPage.submit()).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + }); + + it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", () => { + $(SubmitPage.submit()).click(); + $(HubPage.submit()).click(); + expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + }); + }); + + describe("Given I have a Calculated Summary in a Repeating Section", () => { + before("Get to Final Summary", () => { + browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); + $(HubPage.submit()).click(); + $(PrimaryPersonListCollectorPage.no()).click(); + $(PrimaryPersonListCollectorPage.submit()).click(); + $(ListCollectorPage.yes()).click(); + $(ListCollectorPage.submit()).click(); + $(ListCollectorAddPage.firstName()).setValue("Jean"); + $(ListCollectorAddPage.lastName()).setValue("Clemens"); + $(ListCollectorAddPage.submit()).click(); + $(ListCollectorPage.yes()).click(); + $(ListCollectorPage.submit()).click(); + $(ListCollectorAddPage.firstName()).setValue("Jane"); + $(ListCollectorAddPage.lastName()).setValue("Doe"); + $(ListCollectorAddPage.submit()).click(); + $(ListCollectorPage.no()).click(); + $(ListCollectorPage.submit()).click(); + $(HubPage.submit()).click(); + getToFirstCalculatedSummary(); + getToSubmitPage(); + $(SubmitPage.submit()).click(); + $(HubPage.submit()).click(); + getToFirstCalculatedSummary(); + getToSubmitPage(); + $(SubmitPage.submit()).click(); + }); + + it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", () => { + expect(browser.getUrl()).to.contain(HubPage.pageName); + expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); + expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + }); + + it("Given I change an answer with a dependent calculated summary question, When I return to the hub, Then only the section status for the repeating section I updated should be incomplete", () => { + expect(browser.getUrl()).to.contain(HubPage.pageName); + $(HubPage.summaryRowLink("personal-details-section-1")).click(); + expect(browser.getUrl()).to.contain(SubmitPage.pageName); + $(SubmitPage.skipFourthBlockAnswerEdit()).click(); + $(SkipFourthBlockPage.yes()).click(); + $(SkipFourthBlockPage.submit()).click(); + browser.url(HubPage.url()); + expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + }); + + it("Given I return to a partially completed section with a calculated summary, When I answer the dependent questions and return to the hub, Then the section status for the repeating section I updated should be complete", () => { + expect(browser.getUrl()).to.contain(HubPage.pageName); + expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + $(HubPage.summaryRowLink("personal-details-section-1")).click(); + expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); + $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + $(SetMinMaxBlockPage.submit()).click(); + $(SubmitPage.submit()).click(); + expect(browser.getUrl()).to.contain(HubPage.pageName); + expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); + expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + }); + }); +}); + +const getToFirstCalculatedSummary = () => { + $(FirstNumberBlockPage.firstNumber()).setValue(1.23); + $(FirstNumberBlockPage.submit()).click(); + + $(SecondNumberBlockPage.secondNumber()).setValue(4.56); + $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); + $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); + $(SecondNumberBlockPage.submit()).click(); + + $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); + $(ThirdNumberBlockPage.submit()).click(); + $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); + $(ThirdAndAHalfNumberBlockPage.submit()).click(); + + $(SkipFourthBlockPage.no()).click(); + $(SkipFourthBlockPage.submit()).click(); + + $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); + $(FourthNumberBlockPage.submit()).click(); + $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); + $(FourthAndAHalfNumberBlockPage.submit()).click(); + + $(FifthNumberBlockPage.fifthPercent()).setValue(56); + $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); + $(FifthNumberBlockPage.submit()).click(); + + $(SixthNumberBlockPage.sixthPercent()).setValue(23); + $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); + $(SixthNumberBlockPage.submit()).click(); +}; + +const getToSubmitPage = () => { + $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + $(UnitTotalPlaybackPage.submit()).click(); + $(PercentageTotalPlaybackPage.submit()).click(); + $(NumberTotalPlaybackPage.submit()).click(); + $(CalculatedSummaryTotalConfirmation.submit()).click(); +}; diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 0e4ab8faf1..b357c0741d 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -1,11 +1,19 @@ -from tests.integration.integration_test_case import IntegrationTestCase +from . import QuestionnaireTestCase -class TestQuestionnaireCalculatedSummary(IntegrationTestCase): +class TestQuestionnaireCalculatedSummary(QuestionnaireTestCase): BASE_URL = "/questionnaire/" - def test_calculated_summary(self): - self.launchSurvey("test_calculated_summary") + def _add_list_items(self): + self.post() + self.post({"you-live-here": "Yes"}) + self.add_person("Marie Claire", "Doe") + self.post({"anyone-else": "Yes"}) + self.add_person("John", "Doe") + self.post({"anyone-else": "No"}) + self.post() + + def _complete_calculated_summary_path_with_skip(self): self.post({"first-number-answer": "10"}) self.post( { @@ -19,13 +27,8 @@ def test_calculated_summary(self): self.post({"skip-fourth-block-answer": "Yes"}) self.post({"fifth-percent-answer": "50", "fifth-number-answer": "50"}) self.post({"sixth-percent-answer": "60", "sixth-number-answer": "60"}) - self.assertInBody("Skipped Fourth") - self.assertInBody( - "We calculate the total of currency values entered to be £80.00" - ) - def test_calculated_summary_no_skip(self): - self.launchSurvey("test_calculated_summary") + def _complete_calculated_summary_path_no_skip(self): self.post({"first-number-answer": "10"}) self.post( { @@ -41,6 +44,56 @@ def test_calculated_summary_no_skip(self): self.post({"fourth-and-a-half-number-answer-also-in-total": "50"}) self.post({"fifth-percent-answer": "50", "fifth-number-answer": "50"}) self.post({"sixth-percent-answer": "60", "sixth-number-answer": "60"}) + + def test_calculated_summary(self): + self.launchSurvey("test_calculated_summary") + self._complete_calculated_summary_path_with_skip() + self.assertInBody("Skipped Fourth") + self.assertInBody( + "We calculate the total of currency values entered to be £80.00" + ) + + def test_calculated_summary_no_skip(self): + self.launchSurvey("test_calculated_summary") + self._complete_calculated_summary_path_no_skip() + self.assertNotInBody("Skipped Fourth") + self.assertInBody( + "We calculate the total of currency values entered to be £180.00" + ) + + def test_new_calculated_summary(self): + self.launchSurvey("test_new_calculated_summary") + self._complete_calculated_summary_path_with_skip() + self.assertInBody("Skipped Fourth") + self.assertInBody( + "We calculate the total of currency values entered to be £80.00" + ) + + def test_new_calculated_summary_no_skip(self): + self.launchSurvey("test_new_calculated_summary") + self._complete_calculated_summary_path_no_skip() + self.assertNotInBody("Skipped Fourth") + self.assertInBody( + "We calculate the total of currency values entered to be £180.00" + ) + + def test_new_calculated_summary_repeating_section(self): + self.launchSurvey("test_new_calculated_summary_repeating_section") + self._add_list_items() + self.post() + + self._complete_calculated_summary_path_with_skip() + self.assertInBody("Skipped Fourth") + self.assertInBody( + "We calculate the total of currency values entered to be £80.00" + ) + + def test_new_calculated_summary_no_skip_repeating_section(self): + self.launchSurvey("test_new_calculated_summary_repeating_section") + self._add_list_items() + self.post() + + self._complete_calculated_summary_path_no_skip() self.assertNotInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £180.00" From 8fb9973855c239af2e4a0cb14f4b2edf65c63b28 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 19 Dec 2022 15:52:18 +0000 Subject: [PATCH 125/567] Schemas v3.28.0 (#984) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 38a4b69555..0409497038 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.27.0 +v3.28.0 From 6fdaec61dd3990fb391bf8d03090075c3a9e31dc Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 20 Dec 2022 15:41:39 +0000 Subject: [PATCH 126/567] Schemas v3.29.0 (#987) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0409497038..5a85d046d1 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.28.0 +v3.29.0 From 1ceee222743ef1af33d83a4e0deee2b4228a7961 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 21 Dec 2022 09:58:11 +0000 Subject: [PATCH 127/567] Add BEIS logos (#983) --- app/helpers/template_helpers.py | 2 + app/survey_config/__init__.py | 7 +- app/survey_config/business_config.py | 8 +++ app/survey_config/survey_type.py | 1 + schemas/test/en/test_theme_beis.json | 75 ++++++++++++++++++++ templates/assets/images/beis-logo.svg | 10 +++ templates/assets/images/beis-mobile-logo.svg | 11 +++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 62 +++++++++++++++- tests/functional/spec/theme_beis.spec.js | 14 ++++ 10 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 schemas/test/en/test_theme_beis.json create mode 100644 templates/assets/images/beis-logo.svg create mode 100644 templates/assets/images/beis-mobile-logo.svg create mode 100644 tests/functional/spec/theme_beis.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index d30ab5a520..fcbf649440 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -14,6 +14,7 @@ from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( + BEISBusinessSurveyConfig, BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, @@ -163,6 +164,7 @@ def survey_config_mapping( SurveyType.HEALTH: SocialSurveyConfig, SurveyType.SOCIAL: SocialSurveyConfig, SurveyType.NORTHERN_IRELAND: NorthernIrelandBusinessSurveyConfig, + SurveyType.BEIS: BEISBusinessSurveyConfig, SurveyType.CENSUS: ( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig ), diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index c95a79fc3d..4d4f1c17da 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -1,4 +1,8 @@ -from .business_config import BusinessSurveyConfig, NorthernIrelandBusinessSurveyConfig +from .business_config import ( + BEISBusinessSurveyConfig, + BusinessSurveyConfig, + NorthernIrelandBusinessSurveyConfig, +) from .census_config import ( CensusNISRASurveyConfig, CensusSurveyConfig, @@ -16,5 +20,6 @@ "WelshCensusSurveyConfig", "BusinessSurveyConfig", "NorthernIrelandBusinessSurveyConfig", + "BEISBusinessSurveyConfig", "Link", ] diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 5af1d543b0..4c6de681bd 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -127,3 +127,11 @@ class NorthernIrelandBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo_mobile: str = read_file( "./templates/assets/images/ni-finance-mobile-logo.svg" ) + + +@dataclass +class BEISBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file("./templates/assets/images/beis-logo.svg") + masthead_logo_mobile: str = read_file( + "./templates/assets/images/beis-mobile-logo.svg" + ) diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index 01c5236efa..1cce5fc336 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -7,5 +7,6 @@ class SurveyType(Enum): DEFAULT = "default" HEALTH = "health" NORTHERN_IRELAND = "northernireland" + BEIS = "beis" CENSUS = "census" CENSUS_NISRA = "census-nisra" diff --git a/schemas/test/en/test_theme_beis.json b/schemas/test/en/test_theme_beis.json new file mode 100644 index 0000000000..87ff1364ed --- /dev/null +++ b/schemas/test/en/test_theme_beis.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Department for Business, Energy and Industrial Strategy", + "theme": "beis", + "description": "A questionnaire to demo the BEIS survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "BEIS Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/beis-logo.svg b/templates/assets/images/beis-logo.svg new file mode 100644 index 0000000000..619462dcfd --- /dev/null +++ b/templates/assets/images/beis-logo.svg @@ -0,0 +1,10 @@ + diff --git a/templates/assets/images/beis-mobile-logo.svg b/templates/assets/images/beis-mobile-logo.svg new file mode 100644 index 0000000000..081c29cde3 --- /dev/null +++ b/templates/assets/images/beis-mobile-logo.svg @@ -0,0 +1,11 @@ + diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 228a1d6aa8..154754e4b1 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,5 +1,5 @@ {% extends 'layouts/_base.html' %} -{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business"] %} +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "beis"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 6a5e529927..3376362245 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -14,6 +14,7 @@ read_file, ) from app.survey_config import ( + BEISBusinessSurveyConfig, BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, @@ -216,6 +217,36 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): read_file("./templates/assets/images/ni-finance-mobile-logo.svg"), ], ), + ( + SurveyType.NORTHERN_IRELAND, + "Test", + NorthernIrelandBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/ni-finance-logo.svg"), + read_file("./templates/assets/images/ni-finance-mobile-logo.svg"), + ], + ), + ( + None, + None, + BEISBusinessSurveyConfig(), + [ + "ONS Business Surveys", + read_file("./templates/assets/images/beis-logo.svg"), + read_file("./templates/assets/images/beis-mobile-logo.svg"), + ], + ), + ( + SurveyType.BEIS, + "Test", + BEISBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/beis-logo.svg"), + read_file("./templates/assets/images/beis-mobile-logo.svg"), + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): @@ -350,6 +381,11 @@ def test_service_links_context( "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), + ( + BEISBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), ( SocialSurveyConfig(), "en", @@ -414,6 +450,11 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), + ( + BEISBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), ( SocialSurveyConfig(), True, @@ -458,6 +499,11 @@ def test_cookie_settings_url_context( "en", ACCOUNT_SERVICE_BASE_URL, ), + ( + BEISBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), ( SocialSurveyConfig(), "en", @@ -494,6 +540,7 @@ def test_cookie_domain_context( SurveyConfig(), BusinessSurveyConfig(), NorthernIrelandBusinessSurveyConfig(), + BEISBusinessSurveyConfig(), SocialSurveyConfig(), ], ) @@ -568,6 +615,10 @@ def test_account_service_my_todo_url_context( NorthernIrelandBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), + ( + BEISBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), ( SocialSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", @@ -597,11 +648,10 @@ def test_account_service_log_out_url_context( (SurveyType.HEALTH, "en", SocialSurveyConfig), (SurveyType.SOCIAL, "en", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NorthernIrelandBusinessSurveyConfig), - (SurveyType.NORTHERN_IRELAND, "cy", NorthernIrelandBusinessSurveyConfig), + (SurveyType.BEIS, "en", BEISBusinessSurveyConfig), (SurveyType.CENSUS, "en", CensusSurveyConfig), (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), (SurveyType.CENSUS_NISRA, "en", CensusNISRASurveyConfig), - (SurveyType.CENSUS_NISRA, "cy", CensusNISRASurveyConfig), (None, None, BusinessSurveyConfig), ], ) @@ -705,6 +755,7 @@ def test_context_set_from_app_config(app): (SurveyType.SOCIAL, "en", None), (SurveyType.SOCIAL, "cy", None), (SurveyType.NORTHERN_IRELAND, "en", None), + (SurveyType.BEIS, "en", None), (SurveyType.CENSUS, "en", "census"), (SurveyType.CENSUS, "cy", "census"), (SurveyType.CENSUS_NISRA, "en", "census"), @@ -731,6 +782,7 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.SOCIAL, "en", "ONS Social Surveys"), (SurveyType.SOCIAL, "cy", "ONS Social Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), + (SurveyType.BEIS, "en", "ONS Business Surveys"), (SurveyType.CENSUS, "en", "Census 2021"), (SurveyType.CENSUS, "cy", "Census 2021"), (SurveyType.CENSUS_NISRA, "en", "Census 2021"), @@ -795,6 +847,12 @@ def test_correct_survey_title_in_context( QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), + ( + SurveyType.BEIS, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), ( SurveyType.CENSUS, "en", diff --git a/tests/functional/spec/theme_beis.spec.js b/tests/functional/spec/theme_beis.spec.js new file mode 100644 index 0000000000..c95e5495bb --- /dev/null +++ b/tests/functional/spec/theme_beis.spec.js @@ -0,0 +1,14 @@ +import RadioPage from "../generated_pages/theme_beis/radio.page"; + +describe("Theme BEIS", () => { + describe("Given I launch a BEIS themed questionnaire", () => { + before(() => { + browser.openQuestionnaire("test_theme_beis.json"); + }); + + it("When I navigate to the radio page, Then I should see BEIS theme content", () => { + expect(browser.getUrl()).to.contain(RadioPage.pageName); + expect($("#beis-logo-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); + }); + }); +}); From 3163cc733029a6d423fb85e98f6af5b0f74c9a82 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:18:42 +0000 Subject: [PATCH 128/567] Update year in test to fix new year bug (#990) --- app/translations/messages.pot | 56 +++++++++---------- ..._question_within_repeating_section.spec.js | 4 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 303808d3b5..ddedc5053f 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -1,14 +1,14 @@ # Translations template for PROJECT. -# Copyright (C) 2022 ORGANIZATION +# Copyright (C) 2023 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2022. +# FIRST AUTHOR , 2023. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2022-11-29 21:21+0000\n" +"POT-Creation-Date: 2023-01-03 12:56+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,14 +17,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: app/forms/validators.py:372 app/jinja_filters.py:115 +#: app/forms/validators.py:373 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:376 app/jinja_filters.py:123 +#: app/forms/validators.py:377 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" @@ -49,9 +49,9 @@ msgstr "" msgid "Remove {item_name}" msgstr "" -#: app/jinja_filters.py:702 +#: app/jinja_filters.py:700 #: templates/partials/summary/collapsible-summary.html:36 -#: templates/partials/summary/summary.html:39 +#: templates/partials/summary/summary.html:20 msgid "No answer provided" msgstr "" @@ -206,7 +206,7 @@ msgstr "" msgid "Remove an answer" msgstr "" -#: app/forms/validators.py:383 +#: app/forms/validators.py:384 #, python-format msgid "%(num)s day" msgid_plural "%(num)s days" @@ -217,15 +217,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:103 +#: app/helpers/template_helpers.py:104 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:124 +#: app/helpers/template_helpers.py:125 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:149 +#: app/helpers/template_helpers.py:150 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -324,33 +324,33 @@ msgid "Sign out" msgstr "" #: app/survey_config/business_config.py:88 -#: app/survey_config/social_survey_config.py:48 +#: app/survey_config/social_survey_config.py:39 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:91 +#: app/survey_config/business_config.py:92 #: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 -#: app/survey_config/census_config.py:169 -#: app/survey_config/social_survey_config.py:51 +#: app/survey_config/census_config.py:170 +#: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:95 -#: app/survey_config/social_survey_config.py:55 +#: app/survey_config/business_config.py:97 +#: app/survey_config/social_survey_config.py:48 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:105 +#: app/survey_config/business_config.py:107 #: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:177 -#: app/survey_config/social_survey_config.py:65 +#: app/survey_config/census_config.py:179 +#: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:107 +#: app/survey_config/business_config.py:109 #: app/survey_config/census_config.py:73 app/survey_config/census_config.py:130 -#: app/survey_config/census_config.py:183 -#: app/survey_config/social_survey_config.py:67 +#: app/survey_config/census_config.py:185 +#: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "" @@ -371,12 +371,12 @@ msgid "BSL and audio videos" msgstr "" #: app/survey_config/census_config.py:69 app/survey_config/census_config.py:126 -#: app/survey_config/census_config.py:179 +#: app/survey_config/census_config.py:181 msgid "Accessibility statement" msgstr "" #: app/survey_config/census_config.py:77 app/survey_config/census_config.py:134 -#: app/survey_config/census_config.py:187 +#: app/survey_config/census_config.py:189 msgid "Terms and conditions" msgstr "" @@ -392,7 +392,7 @@ msgstr "" msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" -#: app/survey_config/survey_config.py:39 +#: app/survey_config/survey_config.py:36 msgid "Save and exit survey" msgstr "" @@ -1519,12 +1519,12 @@ msgstr "" #: templates/partials/summary/collapsible-summary.html:37 #: templates/partials/summary/list-summary.html:7 -#: templates/partials/summary/summary.html:40 +#: templates/partials/summary/summary.html:21 msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:38 -#: templates/partials/summary/summary.html:41 +#: templates/partials/summary/summary.html:22 msgid "Change your answer for:" msgstr "" diff --git a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js index f33ae93edc..35e1c0bd82 100644 --- a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js +++ b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js @@ -24,7 +24,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { // Answer question preceding confirmation question $(DateOfBirthPage.day()).setValue("01"); $(DateOfBirthPage.month()).setValue("01"); - $(DateOfBirthPage.year()).setValue("2007"); + $(DateOfBirthPage.year()).setValue("2015"); $(DateOfBirthPage.submit()).click(); // Answer 'No' to confirmation question @@ -38,7 +38,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { it("When I view the summary, Then the confirmation question should not be displayed", () => { $(DateOfBirthPage.day()).setValue("01"); $(DateOfBirthPage.month()).setValue("01"); - $(DateOfBirthPage.year()).setValue("2007"); + $(DateOfBirthPage.year()).setValue("2015"); $(DateOfBirthPage.submit()).click(); $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); From b1463f52c8ae8248bf6a01ae56fae2de6c36abc3 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:45:13 +0000 Subject: [PATCH 129/567] Add ORR theme and SurveyConfig class (#986) --- app/helpers/template_helpers.py | 2 + app/jinja_filters.py | 2 +- app/survey_config/__init__.py | 2 + app/survey_config/business_config.py | 8 +++ app/survey_config/survey_type.py | 1 + app/translations/messages.pot | 8 +-- schemas/test/en/test_theme_orr.json | 75 +++++++++++++++++++++ templates/assets/images/orr-logo.svg | 13 ++++ templates/assets/images/orr-mobile-logo.svg | 13 ++++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 52 +++++++++++++- tests/functional/spec/theme_orr.spec.js | 14 ++++ 12 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 schemas/test/en/test_theme_orr.json create mode 100644 templates/assets/images/orr-logo.svg create mode 100644 templates/assets/images/orr-mobile-logo.svg create mode 100644 tests/functional/spec/theme_orr.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index fcbf649440..5a007dd7ea 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -19,6 +19,7 @@ CensusNISRASurveyConfig, CensusSurveyConfig, NorthernIrelandBusinessSurveyConfig, + ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, WelshCensusSurveyConfig, @@ -165,6 +166,7 @@ def survey_config_mapping( SurveyType.SOCIAL: SocialSurveyConfig, SurveyType.NORTHERN_IRELAND: NorthernIrelandBusinessSurveyConfig, SurveyType.BEIS: BEISBusinessSurveyConfig, + SurveyType.ORR: ORRBusinessSurveyConfig, SurveyType.CENSUS: ( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig ), diff --git a/app/jinja_filters.py b/app/jinja_filters.py index dd1d80bfc1..1f4c56fd8d 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -82,7 +82,7 @@ def format_unit( def format_unit_input_label(unit: str, unit_length: str = "short") -> str: """ - This function is used to only get the unit of measurement text. If the unit_length + This function is used to only get the unit of measurement text. If the unit_length is long then only the plural form of the word is returned (e.g., Hours, Years, etc). :param (str) unit unit of measurement diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 4d4f1c17da..c8740bd1b5 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -2,6 +2,7 @@ BEISBusinessSurveyConfig, BusinessSurveyConfig, NorthernIrelandBusinessSurveyConfig, + ORRBusinessSurveyConfig, ) from .census_config import ( CensusNISRASurveyConfig, @@ -21,5 +22,6 @@ "BusinessSurveyConfig", "NorthernIrelandBusinessSurveyConfig", "BEISBusinessSurveyConfig", + "ORRBusinessSurveyConfig", "Link", ] diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 4c6de681bd..e0d79d0691 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -135,3 +135,11 @@ class BEISBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo_mobile: str = read_file( "./templates/assets/images/beis-mobile-logo.svg" ) + + +@dataclass +class ORRBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file("./templates/assets/images/orr-logo.svg") + masthead_logo_mobile: str = read_file( + "./templates/assets/images/orr-mobile-logo.svg" + ) diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index 1cce5fc336..ad7add1037 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -8,5 +8,6 @@ class SurveyType(Enum): HEALTH = "health" NORTHERN_IRELAND = "northernireland" BEIS = "beis" + ORR = "orr" CENSUS = "census" CENSUS_NISRA = "census-nisra" diff --git a/app/translations/messages.pot b/app/translations/messages.pot index ddedc5053f..26d7c92db4 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-03 12:56+0000\n" +"POT-Creation-Date: 2023-01-03 11:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -217,15 +217,15 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:104 +#: app/helpers/template_helpers.py:105 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:125 +#: app/helpers/template_helpers.py:126 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:150 +#: app/helpers/template_helpers.py:151 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" diff --git a/schemas/test/en/test_theme_orr.json b/schemas/test/en/test_theme_orr.json new file mode 100644 index 0000000000..15aad627c3 --- /dev/null +++ b/schemas/test/en/test_theme_orr.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Rail and Road Survey", + "theme": "orr", + "description": "A questionnaire to demo the ORR survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "ORR Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/orr-logo.svg b/templates/assets/images/orr-logo.svg new file mode 100644 index 0000000000..5a93c4aeb1 --- /dev/null +++ b/templates/assets/images/orr-logo.svg @@ -0,0 +1,13 @@ + diff --git a/templates/assets/images/orr-mobile-logo.svg b/templates/assets/images/orr-mobile-logo.svg new file mode 100644 index 0000000000..aaf428ef5d --- /dev/null +++ b/templates/assets/images/orr-mobile-logo.svg @@ -0,0 +1,13 @@ + diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 154754e4b1..627f9f27c5 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,5 +1,5 @@ {% extends 'layouts/_base.html' %} -{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "beis"] %} +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "beis", "orr"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 3376362245..6537e14dad 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -19,6 +19,7 @@ CensusNISRASurveyConfig, CensusSurveyConfig, NorthernIrelandBusinessSurveyConfig, + ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, WelshCensusSurveyConfig, @@ -247,6 +248,26 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): read_file("./templates/assets/images/beis-mobile-logo.svg"), ], ), + ( + None, + None, + ORRBusinessSurveyConfig(), + [ + "ONS Business Surveys", + read_file("./templates/assets/images/orr-logo.svg"), + read_file("./templates/assets/images/orr-mobile-logo.svg"), + ], + ), + ( + SurveyType.ORR, + "Test", + ORRBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/orr-logo.svg"), + read_file("./templates/assets/images/orr-mobile-logo.svg"), + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): @@ -386,6 +407,11 @@ def test_service_links_context( "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), + ( + ORRBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), ( SocialSurveyConfig(), "en", @@ -455,6 +481,11 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), + ( + ORRBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), ( SocialSurveyConfig(), True, @@ -504,6 +535,11 @@ def test_cookie_settings_url_context( "en", ACCOUNT_SERVICE_BASE_URL, ), + ( + ORRBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), ( SocialSurveyConfig(), "en", @@ -539,9 +575,10 @@ def test_cookie_domain_context( [ SurveyConfig(), BusinessSurveyConfig(), + SocialSurveyConfig(), NorthernIrelandBusinessSurveyConfig(), BEISBusinessSurveyConfig(), - SocialSurveyConfig(), + ORRBusinessSurveyConfig(), ], ) def test_cookie_domain_context_cookie_not_provided( @@ -619,6 +656,10 @@ def test_account_service_my_todo_url_context( BEISBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), + ( + ORRBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), ( SocialSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", @@ -649,6 +690,7 @@ def test_account_service_log_out_url_context( (SurveyType.SOCIAL, "en", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NorthernIrelandBusinessSurveyConfig), (SurveyType.BEIS, "en", BEISBusinessSurveyConfig), + (SurveyType.ORR, "en", ORRBusinessSurveyConfig), (SurveyType.CENSUS, "en", CensusSurveyConfig), (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), (SurveyType.CENSUS_NISRA, "en", CensusNISRASurveyConfig), @@ -756,6 +798,7 @@ def test_context_set_from_app_config(app): (SurveyType.SOCIAL, "cy", None), (SurveyType.NORTHERN_IRELAND, "en", None), (SurveyType.BEIS, "en", None), + (SurveyType.ORR, "en", None), (SurveyType.CENSUS, "en", "census"), (SurveyType.CENSUS, "cy", "census"), (SurveyType.CENSUS_NISRA, "en", "census"), @@ -783,6 +826,7 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.SOCIAL, "cy", "ONS Social Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), (SurveyType.BEIS, "en", "ONS Business Surveys"), + (SurveyType.ORR, "en", "ONS Business Surveys"), (SurveyType.CENSUS, "en", "Census 2021"), (SurveyType.CENSUS, "cy", "Census 2021"), (SurveyType.CENSUS_NISRA, "en", "Census 2021"), @@ -853,6 +897,12 @@ def test_correct_survey_title_in_context( QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), + ( + SurveyType.ORR, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), ( SurveyType.CENSUS, "en", diff --git a/tests/functional/spec/theme_orr.spec.js b/tests/functional/spec/theme_orr.spec.js new file mode 100644 index 0000000000..a75346323b --- /dev/null +++ b/tests/functional/spec/theme_orr.spec.js @@ -0,0 +1,14 @@ +import RadioPage from "../generated_pages/theme_orr/radio.page"; + +describe("Theme Rail and Road", () => { + describe("Given I launch a Rail and Road themed questionnaire", () => { + before(() => { + browser.openQuestionnaire("test_theme_orr.json"); + }); + + it("When I navigate to the radio page, Then I should see Rail and Road theme content", () => { + expect(browser.getUrl()).to.contain(RadioPage.pageName); + expect($("#orr-logo-mobile-alt").getHTML()).to.contain("Office of Rail and Road logo"); + }); + }); +}); From eaf701bfb3b13de37921e4d9af3c9445f4590733 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 3 Jan 2023 16:16:38 +0000 Subject: [PATCH 130/567] Schemas v3.30.0 (#991) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5a85d046d1..b1d98a6868 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.29.0 +v3.30.0 From 3e8bef9bbfe4a8f39656dc9d423c04e3c025b8cb Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 5 Jan 2023 10:27:39 +0000 Subject: [PATCH 131/567] Fix dates allowing 2 digit years (#988) --- app/forms/error_messages.py | 3 ++ app/forms/validators.py | 3 ++ app/translations/messages.pot | 12 ++++--- tests/app/forms/conftest.py | 2 +- .../validation/test_date_check_validator.py | 34 ++++++++++++++----- .../date-combined-mm-yyyy.spec.js | 2 +- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/app/forms/error_messages.py b/app/forms/error_messages.py index 765affc190..8814067489 100644 --- a/app/forms/error_messages.py +++ b/app/forms/error_messages.py @@ -67,4 +67,7 @@ "SINGLE_DATE_PERIOD_TOO_EARLY": lazy_gettext("Enter a date after %(min)s"), "SINGLE_DATE_PERIOD_TOO_LATE": lazy_gettext("Enter a date before %(max)s"), "MUTUALLY_EXCLUSIVE": lazy_gettext("Remove an answer"), + "INVALID_YEAR_FORMAT": lazy_gettext( + "Enter the year in a valid format. For example, 2023." + ), } diff --git a/app/forms/validators.py b/app/forms/validators.py index 6a3c3fbe7c..30c945e5d6 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -253,6 +253,9 @@ def __call__(self, form: "QuestionnaireForm", field: StringField) -> None: if not form.data: raise validators.StopValidation(self.message) + if hasattr(form, "year") and len(form["year"].data) < 4: + raise validators.StopValidation(error_messages["INVALID_YEAR_FORMAT"]) + try: if hasattr(form, "day"): datetime.strptime(form.data, "%Y-%m-%d").replace(tzinfo=timezone.utc) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 26d7c92db4..b55fe895b2 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-03 11:23+0000\n" +"POT-Creation-Date: 2023-01-04 15:33+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,14 +17,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -#: app/forms/validators.py:373 app/jinja_filters.py:115 +#: app/forms/validators.py:376 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:377 app/jinja_filters.py:123 +#: app/forms/validators.py:380 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" @@ -206,7 +206,11 @@ msgstr "" msgid "Remove an answer" msgstr "" -#: app/forms/validators.py:384 +#: app/forms/error_messages.py:70 +msgid "Enter the year in a valid format. For example, 2023." +msgstr "" + +#: app/forms/validators.py:387 #, python-format msgid "%(num)s day" msgid_plural "%(num)s days" diff --git a/tests/app/forms/conftest.py b/tests/app/forms/conftest.py index 1e88dd02fe..d5891ae308 100644 --- a/tests/app/forms/conftest.py +++ b/tests/app/forms/conftest.py @@ -3,7 +3,7 @@ @pytest.fixture def mock_form(mocker): - return mocker.Mock() + return mocker.MagicMock() @pytest.fixture diff --git a/tests/app/forms/validation/test_date_check_validator.py b/tests/app/forms/validation/test_date_check_validator.py index 55e6100558..3d7e82d2e0 100644 --- a/tests/app/forms/validation/test_date_check_validator.py +++ b/tests/app/forms/validation/test_date_check_validator.py @@ -7,18 +7,16 @@ @pytest.mark.parametrize( "date", [ - "", "2016-12-", - "2016--03", - "-12-03", "2016-12-40", "2016-13-20", - "20000-12-20", "2015-02-29", # 2015 was not a leap year + "20000-12-20", ], ) def test_invalid_day_month_year(date_check, mock_form, mock_field, date): mock_form.data = date + mock_form["year"].data = date.split("-")[0] assert_invalid_date_error(date_check, mock_form, mock_field) @@ -27,22 +25,34 @@ def test_invalid_day_month_year(date_check, mock_form, mock_field, date): "date", [ "2016-", - "-12", "2016-13", + "2016--03", "20000-12", ], ) def test_invalid_month_year(date_check, mock_form, mock_field, date): mock_form.data = date + mock_form["year"].data = date.split("-")[0] del mock_form.day assert_invalid_date_error(date_check, mock_form, mock_field) -def test_invalid_year(date_check, mock_form, mock_field): +@pytest.mark.parametrize( + "date", + ["", "-12-03", "-12", "200", "20", "2"], +) +def test_invalid_year(date_check, mock_form, mock_field, date): + mock_form["year"].data = date.split("-")[0] + + assert_invalid_year_format_error(date_check, mock_form, mock_field) + + +def test_invalid_data(date_check, mock_form, mock_field): del mock_form.day del mock_form.month - mock_form.data = "20000" + del mock_form.year + mock_form.data = "abc" assert_invalid_date_error(date_check, mock_form, mock_field) @@ -56,7 +66,7 @@ def test_invalid_year(date_check, mock_form, mock_field): ) def test_valid_day_month_year(date_check, mock_form, mock_field, date): mock_form.data = date - + mock_form["year"].data = date.split("-")[0] try: date_check(mock_form, mock_field) except StopValidation: @@ -66,6 +76,7 @@ def test_valid_day_month_year(date_check, mock_form, mock_field, date): def test_valid_month_year(date_check, mock_form, mock_field): del mock_form.day mock_form.data = "2016-12" + mock_form["year"].data = "2016" try: date_check(mock_form, mock_field) @@ -77,6 +88,7 @@ def test_valid_year(date_check, mock_form, mock_field): del mock_form.day del mock_form.month mock_form.data = "2016" + mock_form["year"].data = "2016" try: date_check(mock_form, mock_field) @@ -88,3 +100,9 @@ def assert_invalid_date_error(date_check, mock_form, mock_field): with pytest.raises(StopValidation) as ite: date_check(mock_form, mock_field) assert error_messages["INVALID_DATE"] == str(ite.value) + + +def assert_invalid_year_format_error(date_check, mock_form, mock_field): + with pytest.raises(StopValidation) as ite: + date_check(mock_form, mock_field) + assert error_messages["INVALID_YEAR_FORMAT"] == str(ite.value) diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js index b6475742d7..22bee8120f 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js @@ -36,7 +36,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat $(DateRangePage.dateRangeToMonth()).setValue(4); $(DateRangePage.dateRangeToYear()).setValue(2017); $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter the year in a valid format. For example, 2023."); expect($(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); From bb9a6e5bc2482601bf96e340ad01e4e2ae9a0dd9 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 5 Jan 2023 14:33:34 +0000 Subject: [PATCH 132/567] Enable GA tracking on timeout modal (#931) --- app/helpers/language_helper.py | 4 ++-- app/helpers/template_helpers.py | 20 +++++++++++++++---- app/routes/session.py | 16 ++++++++++++--- app/survey_config/census_config.py | 12 +++-------- app/survey_config/survey_config.py | 15 ++++---------- tests/app/helpers/test_template_helpers.py | 12 ++++++----- tests/integration/routes/test_cookie.py | 5 +++-- .../integration/test_application_variables.py | 7 ++++--- 8 files changed, 52 insertions(+), 39 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 8c83854ed5..006bf5b7f3 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -35,8 +35,8 @@ def handle_language(metadata: Optional[MetadataProxy] = None) -> None: schema = load_schema_from_metadata( metadata=metadata, language_code=request_language ) - if schema.json["title"] != cookie_session.get("survey_title"): - cookie_session["survey_title"] = schema.json["title"] + if schema.json["title"] != cookie_session.get("title"): + cookie_session["title"] = schema.json["title"] session_store.session_data.language_code = request_language session_store.save() diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 5a007dd7ea..d30ee4630f 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -27,6 +27,8 @@ from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_metadata +DATA_LAYER_KEYS = {"title", "survey_id", "form_type"} + class ContextHelper: def __init__( @@ -41,7 +43,7 @@ def __init__( self._include_csrf_token = include_csrf_token self._survey_config = survey_config self._survey_title = cookie_session.get( - "survey_title", self._survey_config.survey_title + "title", self._survey_config.survey_title ) self._sign_out_url = url_for("session.get_sign_out") self._cdn_url = ( @@ -114,9 +116,19 @@ def service_links_context( def data_layer_context( self, ) -> list[dict]: - tx_id = metadata.tx_id if (metadata := get_metadata(current_user)) else None - - return self._survey_config.get_data_layer(tx_id=tx_id) + tx_id_context = ( + {"tx_id": metadata.tx_id} + if (metadata := get_metadata(current_user)) + else None + ) + additional_context = self._survey_config.get_additional_data_layer_context() + schema_context = { + key: value for key in DATA_LAYER_KEYS if (value := cookie_session.get(key)) + } + context = [*additional_context, schema_context] + if tx_id_context: + context.append(tx_id_context) + return context @property def footer_context(self) -> dict[str, Any]: diff --git a/app/routes/session.py b/app/routes/session.py index 6bbcace058..1a1d5bf6d8 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -14,7 +14,11 @@ from app.authentication.jti_claim_storage import JtiTokenUsed, use_jti_claim from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store, get_session_timeout_in_seconds -from app.helpers.template_helpers import get_survey_config, render_template +from app.helpers.template_helpers import ( + DATA_LAYER_KEYS, + get_survey_config, + render_template, +) from app.routes.errors import _render_error_page from app.utilities.metadata_parser import validate_runner_claims from app.utilities.metadata_parser_v2 import ( @@ -39,6 +43,12 @@ def login_head(): return "", 204 +def set_schema_context_in_cookie(schema) -> None: + for key in [*DATA_LAYER_KEYS, "theme"]: + if value := schema.json.get(key): + cookie_session[key] = value + + @session_blueprint.route("/session", methods=["GET", "POST"]) def login(): """ @@ -101,10 +111,10 @@ def login(): store_session(claims) - cookie_session["theme"] = g.schema.json["theme"] - cookie_session["survey_title"] = g.schema.json["title"] cookie_session["expires_in"] = get_session_timeout_in_seconds(g.schema) + set_schema_context_in_cookie(g.schema) + if account_service_url := claims.get("account_service_url"): cookie_session["account_service_base_url"] = account_service_url diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index d96d95009f..1b06a6032b 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Optional, Union +from typing import Optional from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString @@ -24,14 +24,8 @@ class CensusSurveyConfig( sign_out_button_text: str = lazy_gettext("Save and complete later") _is_nisra: bool = False - def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: - data_layer: list[Union[dict[str, bool], dict[str, str]]] = [ - {"nisra": self._is_nisra} - ] - if tx_id: - data_layer.append({"tx_id": tx_id}) - - return data_layer + def get_additional_data_layer_context(self) -> list[dict]: + return [{"nisra": self._is_nisra}] def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: links = [ diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 9699c21677..b945911400 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -71,14 +71,7 @@ def get_footer_legal_links( # pylint: disable=unused-argument, no-self-use ) -> Optional[list[dict]]: return None - def get_data_layer(self, tx_id: Optional[str] = None) -> list[dict]: - data_layer = [{"tx_id": tx_id}] if tx_id else [] - if self.schema: - data_layer.append( - { - key: self.schema.json[key] - for key in ["form_type", "survey_id", "title"] - if key in self.schema.json - } - ) - return data_layer + def get_additional_data_layer_context( # pylint: disable=no-self-use + self, + ) -> list: + return [] diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 6537e14dad..3966ba5adc 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -6,6 +6,7 @@ from app.helpers.template_helpers import ContextHelper, get_survey_config from app.questionnaire import QuestionnaireSchema +from app.routes.session import set_schema_context_in_cookie from app.settings import ( ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL, @@ -274,7 +275,7 @@ def test_header_context(app: Flask, theme, survey_title, survey_config, expected with app.app_context(): for cookie_name, cookie_value in { "theme": theme, - "survey_title": survey_title, + "title": survey_title, }.items(): if cookie_value: cookie_session[cookie_name] = cookie_value @@ -907,19 +908,19 @@ def test_correct_survey_title_in_context( SurveyType.CENSUS, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"nisra": False}], + [{"nisra": False}, {"survey_id": "001"}], ), ( SurveyType.CENSUS, "cy", QuestionnaireSchema({"survey_id": "001"}), - [{"nisra": False}], + [{"nisra": False}, {"survey_id": "001"}], ), ( SurveyType.CENSUS_NISRA, - QuestionnaireSchema({"survey_id": "001"}), "en", - [{"nisra": True}], + QuestionnaireSchema({"survey_id": "001"}), + [{"nisra": True}, {"survey_id": "001"}], ), ], ) @@ -927,6 +928,7 @@ def test_correct_data_layer_in_context( app: Flask, theme: str, language: str, schema: QuestionnaireSchema, expected: str ): with app.app_context(): + set_schema_context_in_cookie(schema) survey_config = get_survey_config(theme=theme, language=language, schema=schema) result = ContextHelper( diff --git a/tests/integration/routes/test_cookie.py b/tests/integration/routes/test_cookie.py index 84604c67e8..053732379f 100644 --- a/tests/integration/routes/test_cookie.py +++ b/tests/integration/routes/test_cookie.py @@ -11,10 +11,11 @@ def test_cookie_contents(self): self.assertIsNotNone(cookie.get("eq-session-id")) self.assertIsNotNone(cookie.get("expires_in")) self.assertIsNotNone(cookie.get("theme")) - self.assertIsNotNone(cookie.get("survey_title")) + self.assertIsNotNone(cookie.get("title")) + self.assertIsNotNone(cookie.get("survey_id")) self.assertIsNotNone(cookie.get("user_ik")) self.assertIsNotNone(cookie.get("account_service_base_url")) - self.assertEqual(len(cookie), 8) + self.assertEqual(len(cookie), 9) self.assertIsNone(cookie.get("user_id")) self.assertIsNone(cookie.get("_permanent")) diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 42a2dd8b1d..0acc0ec6f7 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -27,7 +27,7 @@ def test_google_analytics_code_and_credentials_are_present(self): self.assertStatusOK() self.assertInHead("gtm.start") self.assertInHead( - f'dataLayer = [{{"tx_id": "{actual["METADATA"]["tx_id"]}"}}, {{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}]' + f'dataLayer = [{{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) self.assertInBody("https://www.googletagmanager.com") self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_AUTH) @@ -45,7 +45,7 @@ def test_google_analytics_data_layer_has_no_null_fields(self): self.assertInHead("gtm.start") # form_type is empty so should not be present self.assertInHead( - f'dataLayer = [{{"tx_id": "{actual["METADATA"]["tx_id"]}"}}, {{"survey_id": "001", "title": "Other input fields"}}]' + f'dataLayer = [{{"survey_id": "001", "title": "Other input fields"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) def test_google_analytics_data_layer_is_set_to_nisra_false(self): @@ -58,8 +58,9 @@ def test_google_analytics_data_layer_is_set_to_nisra_false(self): self.get("/questionnaire/individual-confirmation/") self.assertStatusOK() self.assertInHead("gtm.start") + # pylint: disable=line-too-long self.assertInHead( - f'dataLayer = [{{"nisra": false}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' + f'dataLayer = [{{"nisra": false}}, {{"form_type": "I", "survey_id": "0", "title": "Census individual test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) def test_livereload_script_rendered(self): From 84b10cdb05ce8e710d5a57d384ca07bc43506a21 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 9 Jan 2023 16:09:20 +0000 Subject: [PATCH 133/567] Fix page title bug in repeating Calculated Summary pages (#992) Co-authored-by: Mebin Abraham --- app/views/handlers/block.py | 11 ++++++++--- app/views/handlers/calculated_summary.py | 4 +++- schemas/test/en/test_calculated_summary.json | 1 + schemas/test/en/test_new_calculated_summary.json | 1 + ...test_new_calculated_summary_repeating_section.json | 7 ++----- .../spec/features/calculated_summary_test_case.js | 4 ++++ .../new_calculated_summary_repeating_section.spec.js | 10 +++++++++- 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 91b2f49a36..723a5c3764 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -3,6 +3,7 @@ from typing import MutableMapping, Optional, Union from structlog import get_logger +from werkzeug.datastructures import ImmutableDict from app.data_models import QuestionnaireStore from app.questionnaire.location import InvalidLocationException, Location @@ -34,9 +35,10 @@ def __init__( self._form_data = form_data if self._current_location.block_id: - self.block = self._schema.get_block(self._current_location.block_id) + # Type ignore: Block has to exist at this point. Block existence is checked beforehand in block_factory.py + self.block: ImmutableDict = self._schema.get_block(self._current_location.block_id) # type: ignore self._routing_path = self._get_routing_path() - self.page_title = None + self.page_title: Optional[str] = None self._return_to = request_args.get("return_to") self._return_to_answer_id = request_args.get("return_to_answer_id") self._return_to_block_id = request_args.get("return_to_block_id") @@ -147,7 +149,10 @@ def _resolve_custom_page_title_vars(self) -> MutableMapping: ) return {"list_item_position": list_item_position} - def _set_page_title(self, page_title): + def _set_page_title(self, page_title: Optional[str]): + if not page_title: + return None + section_repeating_page_title = ( self._schema.get_repeating_page_title_for_section( self._current_location.section_id diff --git a/app/views/handlers/calculated_summary.py b/app/views/handlers/calculated_summary.py index a05a04e0f0..693b581311 100644 --- a/app/views/handlers/calculated_summary.py +++ b/app/views/handlers/calculated_summary.py @@ -16,6 +16,8 @@ def get_context(self): context = calculated_summary_context.build_view_context_for_calculated_summary( self._current_location ) - self.page_title = context["summary"]["calculated_question"]["title"] + + if not self.page_title: + self.page_title = context["summary"]["calculated_question"]["title"] return context diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index b6a2ce79a3..5fb6e20df9 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -327,6 +327,7 @@ "type": "CalculatedSummary", "id": "unit-total-playback", "title": "We calculate the total of unit values entered to be %(total)s. Is this correct?", + "page_title": "Total Unit Values", "calculation": { "calculation_type": "sum", "answers_to_calculate": ["second-number-answer-unit-total", "third-and-a-half-number-answer-unit-total"], diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index c8e563678d..041ce17c21 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -359,6 +359,7 @@ "type": "CalculatedSummary", "id": "unit-total-playback", "title": "We calculate the total of unit values entered to be %(total)s. Is this correct?", + "page_title": "Total Unit Values", "calculation": { "operation": { "+": [ diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json index 97f3084361..8127b68420 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_section.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -499,7 +499,6 @@ "type": "CalculatedSummary", "id": "currency-total-playback-skipped-fourth", "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", - "page_title": "Calculated Summary", "calculation": { "operation": { "+": [ @@ -539,7 +538,6 @@ "type": "CalculatedSummary", "id": "currency-total-playback-with-fourth", "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", - "page_title": "Calculated Summary", "calculation": { "operation": { "+": [ @@ -586,8 +584,8 @@ { "type": "CalculatedSummary", "id": "unit-total-playback", - "page_title": "Calculated Summary", "title": "We calculate the total of unit values entered to be %(total)s. Is this correct?", + "page_title": "Total Unit Values", "calculation": { "operation": { "+": [ @@ -607,8 +605,8 @@ { "type": "CalculatedSummary", "id": "percentage-total-playback", - "page_title": "Calculated Summary", "title": "We calculate the total of percentage values entered to be %(total)s. Is this correct?", + "page_title": "Percentage Calculated Summary: Person {list_item_position}", "calculation": { "operation": { "+": [ @@ -628,7 +626,6 @@ { "type": "CalculatedSummary", "id": "number-total-playback", - "page_title": "Calculated Summary", "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", "calculation": { "operation": { diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary_test_case.js index 3dc34baa46..3bac1c8919 100644 --- a/tests/functional/spec/features/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary_test_case.js @@ -176,6 +176,10 @@ class TestCase { expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); }); + it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", () => { + expect(browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + }); + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { // Totals and titles should be shown $(UnitTotalPlaybackPage.submit()).click(); diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js index 5cfbde60d9..a06743693f 100644 --- a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js @@ -43,7 +43,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); }); - it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { + it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", () => { expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); }); @@ -163,6 +163,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); }); + it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", () => { + expect(browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + }); + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { // Totals and titles should be shown $(UnitTotalPlaybackPage.submit()).click(); @@ -179,6 +183,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); }); + it("Given the calculated summary has a custom title with the list item position, When I am on the percentage calculated summary, Then the page title should use the custom title with the list item position", () => { + expect(browser.getTitle()).to.equal("Percentage Calculated Summary: Person 1 - A test schema to demo Calculated Summary"); + }); + it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { // Totals and titles should be shown $(UnitTotalPlaybackPage.submit()).click(); From 45f93084fa33d991e527565754a55b756ab3388f Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Tue, 10 Jan 2023 08:25:45 +0000 Subject: [PATCH 134/567] Make survey title generic [Bug] (#985) * update default survey to social * Adding a check for cookie session * Fix ONS Survey as Default Signed-off-by: Yuyutsu Rai * added duck typing * adding lazy_gettext to ONS Surveys * reforamtted with black * refactoring the get_survey_title * updating translations messages.pot * updated logic for get_ survey_title * added type hinting Signed-off-by: Yuyutsu Rai * refactoring tests * refactoring unit test * updating message.pot * format by black * make survey type generic * Make survey title generic * update message.pot * update tests for generic title * Update tests/app/helpers/test_template_helpers.py Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Signed-off-by: Yuyutsu Rai Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/helpers/template_helpers.py | 4 +- app/translations/messages.pot | 42 ++++++++++--------- app/views/contexts/summary/block.py | 2 +- tests/app/helpers/test_template_helpers.py | 48 +++++++++++----------- tests/integration/session/test_timeout.py | 2 +- 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index d30ee4630f..2233709619 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -42,9 +42,7 @@ def __init__( self._is_post_submission = is_post_submission self._include_csrf_token = include_csrf_token self._survey_config = survey_config - self._survey_title = cookie_session.get( - "title", self._survey_config.survey_title - ) + self._survey_title = cookie_session.get("title", lazy_gettext("ONS Surveys")) self._sign_out_url = url_for("session.get_sign_out") self._cdn_url = ( f'{current_app.config["CDN_URL"]}{current_app.config["CDN_ASSETS_PATH"]}' diff --git a/app/translations/messages.pot b/app/translations/messages.pot index b55fe895b2..abc3794859 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-04 15:33+0000\n" +"POT-Creation-Date: 2023-01-09 10:37+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -221,15 +221,19 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" +#: app/helpers/template_helpers.py:45 +msgid "ONS Surveys" +msgstr "" + #: app/helpers/template_helpers.py:105 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:126 +#: app/helpers/template_helpers.py:136 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:151 +#: app/helpers/template_helpers.py:161 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -314,8 +318,8 @@ msgid "An individual access code has been sent by text" msgstr "" #: app/survey_config/business_config.py:59 -#: app/survey_config/census_config.py:39 app/survey_config/census_config.py:96 -#: app/survey_config/census_config.py:162 +#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:90 +#: app/survey_config/census_config.py:156 msgid "Help" msgstr "" @@ -333,8 +337,8 @@ msgid "What we do" msgstr "" #: app/survey_config/business_config.py:92 -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 -#: app/survey_config/census_config.py:170 +#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:98 +#: app/survey_config/census_config.py:164 #: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "" @@ -345,15 +349,15 @@ msgid "Accessibility" msgstr "" #: app/survey_config/business_config.py:107 -#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:179 +#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:118 +#: app/survey_config/census_config.py:173 #: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "" #: app/survey_config/business_config.py:109 -#: app/survey_config/census_config.py:73 app/survey_config/census_config.py:130 -#: app/survey_config/census_config.py:185 +#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 +#: app/survey_config/census_config.py:179 #: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "" @@ -366,29 +370,29 @@ msgstr "" msgid "Save and complete later" msgstr "" -#: app/survey_config/census_config.py:52 app/survey_config/census_config.py:110 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:56 app/survey_config/census_config.py:114 +#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:108 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:69 app/survey_config/census_config.py:126 -#: app/survey_config/census_config.py:181 +#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:120 +#: app/survey_config/census_config.py:175 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:77 app/survey_config/census_config.py:134 -#: app/survey_config/census_config.py:189 +#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:128 +#: app/survey_config/census_config.py:183 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:150 +#: app/survey_config/census_config.py:144 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:153 app/survey_config/survey_config.py:20 +#: app/survey_config/census_config.py:147 app/survey_config/survey_config.py:20 msgid "Use of address data is subject to the terms and conditions." msgstr "" diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 54f56b6277..2a4c6cb8ba 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -69,7 +69,7 @@ def get_question( return_to, return_to_block_id, ): - """ Taking question variants into account, return the question which was displayed to the user """ + """Taking question variants into account, return the question which was displayed to the user""" variant = choose_variant( block_schema, diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 3966ba5adc..0d2d70906f 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -135,7 +135,7 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): SurveyType.BUSINESS, None, BusinessSurveyConfig(), - ["ONS Business Surveys", None, None], + ["ONS Surveys", None, None], ), ( SurveyType.BUSINESS, @@ -147,13 +147,13 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, None, BusinessSurveyConfig(), - ["ONS Business Surveys", None, None], + ["ONS Surveys", None, None], ), ( SurveyType.SOCIAL, None, SocialSurveyConfig(), - ["ONS Social Surveys", None, None], + ["ONS Surveys", None, None], ), ( SurveyType.SOCIAL, @@ -171,19 +171,19 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, None, SocialSurveyConfig(), - ["ONS Social Surveys", None, None], + ["ONS Surveys", None, None], ), ( None, None, SurveyConfig(), - [None, None, None], + ["ONS Surveys", None, None], ), ( SurveyType.CENSUS, None, CensusSurveyConfig(), - ["Census 2021", None, None], + ["ONS Surveys", None, None], ), ( SurveyType.CENSUS, @@ -195,26 +195,26 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, None, CensusSurveyConfig(), - ["Census 2021", None, None], + ["ONS Surveys", None, None], ), ( SurveyType.CENSUS_NISRA, None, CensusNISRASurveyConfig(), - ["Census 2021", None, None], + ["ONS Surveys", None, None], ), ( None, None, CensusNISRASurveyConfig(), - ["Census 2021", None, None], + ["ONS Surveys", None, None], ), ( None, None, NorthernIrelandBusinessSurveyConfig(), [ - "ONS Business Surveys", + "ONS Surveys", read_file("./templates/assets/images/ni-finance-logo.svg"), read_file("./templates/assets/images/ni-finance-mobile-logo.svg"), ], @@ -234,7 +234,7 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, BEISBusinessSurveyConfig(), [ - "ONS Business Surveys", + "ONS Surveys", read_file("./templates/assets/images/beis-logo.svg"), read_file("./templates/assets/images/beis-mobile-logo.svg"), ], @@ -254,7 +254,7 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, ORRBusinessSurveyConfig(), [ - "ONS Business Surveys", + "ONS Surveys", read_file("./templates/assets/images/orr-logo.svg"), read_file("./templates/assets/images/orr-mobile-logo.svg"), ], @@ -820,20 +820,20 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte @pytest.mark.parametrize( "theme, language, expected", [ - (SurveyType.DEFAULT, "en", "ONS Business Surveys"), - (SurveyType.BUSINESS, "en", "ONS Business Surveys"), - (SurveyType.HEALTH, "en", "ONS Social Surveys"), - (SurveyType.SOCIAL, "en", "ONS Social Surveys"), - (SurveyType.SOCIAL, "cy", "ONS Social Surveys"), - (SurveyType.NORTHERN_IRELAND, "en", "ONS Business Surveys"), - (SurveyType.BEIS, "en", "ONS Business Surveys"), - (SurveyType.ORR, "en", "ONS Business Surveys"), - (SurveyType.CENSUS, "en", "Census 2021"), - (SurveyType.CENSUS, "cy", "Census 2021"), - (SurveyType.CENSUS_NISRA, "en", "Census 2021"), + (SurveyType.DEFAULT, "en", "ONS Surveys"), + (SurveyType.BUSINESS, "en", "ONS Surveys"), + (SurveyType.HEALTH, "en", "ONS Surveys"), + (SurveyType.SOCIAL, "en", "ONS Surveys"), + (SurveyType.SOCIAL, "cy", "ONS Surveys"), + (SurveyType.NORTHERN_IRELAND, "en", "ONS Surveys"), + (SurveyType.BEIS, "en", "ONS Surveys"), + (SurveyType.ORR, "en", "ONS Surveys"), + (SurveyType.CENSUS, "en", "ONS Surveys"), + (SurveyType.CENSUS, "cy", "ONS Surveys"), + (SurveyType.CENSUS_NISRA, "en", "ONS Surveys"), ], ) -def test_correct_survey_title_in_context( +def test_use_default_survey_title_in_context_when_no_cookie( app: Flask, theme: str, language: str, expected: str ): with app.app_context(): diff --git a/tests/integration/session/test_timeout.py b/tests/integration/session/test_timeout.py index 5a90df43be..d175191810 100644 --- a/tests/integration/session/test_timeout.py +++ b/tests/integration/session/test_timeout.py @@ -30,7 +30,7 @@ def test_alternate_401_page_is_displayed_when_no_cookie(self): self.get("/session") self.assertStatusUnauthorised() self.assertInBody("followed a link to a page you are not signed in to") - self.assertEqualPageTitle("Page is not available - ONS Business Surveys") + self.assertEqualPageTitle("Page is not available - ONS Surveys") def test_schema_defined_timeout_cant_be_higher_than_server(self): self.launchSurvey("test_timeout") From ab9dc41dec2cf1ef1b2028d7e64e9c2011ccc2ab Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 12 Jan 2023 08:27:24 +0000 Subject: [PATCH 135/567] Fix placeholders referencing calculated summary values in repeating sections (#997) --- app/views/contexts/summary/group.py | 1 + ..._calculated_summary_repeating_section.json | 65 +++++++++++++++++++ ...lculated_summary_repeating_section.spec.js | 30 ++++++++- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index b99322da30..e71845451a 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -46,6 +46,7 @@ def __init__( language=language, answer_store=answer_store, list_store=list_store, + location=location, metadata=metadata, response_metadata=response_metadata, schema=schema, diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json index 8127b68420..195c3a67ad 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_section.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -643,6 +643,71 @@ "title": "Grand total of previous values" } }, + { + "id": "breakdown", + "type": "Question", + "question": { + "id": "breakdown-question", + "title": { + "text": "Enter two values that add up to the previous calculated summary total of {total_first}", + "placeholders": [ + { + "placeholder": "total_first", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "number-total-playback", + "source": "calculated_summary" + } + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "breakdown-answer-1", + "mandatory": false, + "type": "Currency", + "label": "First Value", + "decimal_places": 2, + "currency": "GBP" + }, + { + "id": "breakdown-answer-2", + "mandatory": false, + "type": "Currency", + "label": "Second Value", + "decimal_places": 2, + "currency": "GBP" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "second-currency-total-playback", + "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "breakdown-answer-1" + }, + { + "source": "answers", + "identifier": "breakdown-answer-2" + } + ] + }, + "title": "Grand total of previous values" + } + }, { "type": "Interstitial", "id": "calculated-summary-total-confirmation", diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js index a06743693f..0fa7976b43 100644 --- a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js @@ -13,6 +13,8 @@ import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/new_ca import UnitTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/unit-total-playback.page.js"; import PercentageTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/percentage-total-playback.page.js"; import NumberTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/number-total-playback.page.js"; +import BreakdownPage from "../../generated_pages/new_calculated_summary_repeating_section/breakdown.page.js"; +import SecondCurrencyTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/second-currency-total-playback.page.js"; import CalculatedSummaryTotalConfirmation from "../../generated_pages/new_calculated_summary_repeating_section/calculated-summary-total-confirmation.page.js"; import SubmitPage from "../../generated_pages/new_calculated_summary_repeating_section/personal-details-section-summary.page.js"; import ThankYouPage from "../../base_pages/thank-you.page.js"; @@ -203,8 +205,22 @@ describe("Feature: Calculated Summary Repeating Section", () => { expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); }); - it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { + it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", () => { $(NumberTotalPlaybackPage.submit()).click(); + expect(browser.getUrl()).to.contain(BreakdownPage.pageName); + $(BreakdownPage.answer1()).setValue(100.0); + $(BreakdownPage.answer2()).setValue(24.58); + $(BreakdownPage.submit()).click(); + expect(browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); + expect($(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total of number values entered to be £124.58. Is this correct?" + ); + expect($("body").getText()).to.have.string("Enter two values that add up to the previous calculated summary total of £124.58"); + expect($(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + }); + + it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { + $(SecondCurrencyTotalPlaybackPage.submit()).click(); const content = $("h1 + ul").getText(); const textsToAssert = [ @@ -256,6 +272,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { $(UnitTotalPlaybackPage.submit()).click(); $(PercentageTotalPlaybackPage.submit()).click(); $(NumberTotalPlaybackPage.submit()).click(); + $(BreakdownPage.submit()).click(); + $(SecondCurrencyTotalPlaybackPage.submit()).click(); $(CalculatedSummaryTotalConfirmation.submit()).click(); expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); @@ -282,6 +300,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { $(UnitTotalPlaybackPage.submit()).click(); $(PercentageTotalPlaybackPage.submit()).click(); $(NumberTotalPlaybackPage.submit()).click(); + $(BreakdownPage.submit()).click(); + $(SecondCurrencyTotalPlaybackPage.submit()).click(); $(CalculatedSummaryTotalConfirmation.submit()).click(); expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); $(SetMinMaxBlockPage.submit()).click(); @@ -309,6 +329,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { $(UnitTotalPlaybackPage.submit()).click(); $(PercentageTotalPlaybackPage.submit()).click(); $(NumberTotalPlaybackPage.submit()).click(); + $(BreakdownPage.submit()).click(); + $(SecondCurrencyTotalPlaybackPage.submit()).click(); $(CalculatedSummaryTotalConfirmation.submit()).click(); expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); $(SetMinMaxBlockPage.submit()).click(); @@ -380,6 +402,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { $(UnitTotalPlaybackPage.submit()).click(); $(PercentageTotalPlaybackPage.submit()).click(); $(NumberTotalPlaybackPage.submit()).click(); + $(BreakdownPage.submit()).click(); + $(SecondCurrencyTotalPlaybackPage.submit()).click(); $(CalculatedSummaryTotalConfirmation.submit()).click(); $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); @@ -428,5 +452,9 @@ const getToSubmitPage = () => { $(UnitTotalPlaybackPage.submit()).click(); $(PercentageTotalPlaybackPage.submit()).click(); $(NumberTotalPlaybackPage.submit()).click(); + $(BreakdownPage.answer1()).setValue(100.0); + $(BreakdownPage.answer2()).setValue(24.58); + $(BreakdownPage.submit()).click(); + $(SecondCurrencyTotalPlaybackPage.submit()).click(); $(CalculatedSummaryTotalConfirmation.submit()).click(); }; From 966781c1c9101a56eb80043c70f45a6ede7cb0b0 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 17 Jan 2023 14:04:15 +0000 Subject: [PATCH 136/567] Add BEIS-NI theme (#998) --- .design-system-version | 2 +- app/helpers/template_helpers.py | 11 ++- app/survey_config/__init__.py | 6 +- app/survey_config/business_config.py | 13 ++- app/survey_config/survey_type.py | 1 + schemas/test/en/test_theme_beis_ni.json | 75 +++++++++++++++++ templates/assets/images/beis-mobile-logo.svg | 2 +- .../assets/images/finance-ni-logo-stacked.svg | 9 ++ ...i-finance-logo.svg => finance-ni-logo.svg} | 4 +- ...le-logo.svg => finance-ni-mobile-logo.svg} | 4 +- templates/errors/_base.html | 2 +- templates/layouts/configs/_header.html | 4 +- tests/app/helpers/test_template_helpers.py | 82 +++++++++++++++---- tests/functional/spec/theme_beis_ni.spec.js | 15 ++++ .../spec/theme_northernireland.spec.js | 2 +- 15 files changed, 198 insertions(+), 34 deletions(-) create mode 100644 schemas/test/en/test_theme_beis_ni.json create mode 100644 templates/assets/images/finance-ni-logo-stacked.svg rename templates/assets/images/{ni-finance-logo.svg => finance-ni-logo.svg} (99%) rename templates/assets/images/{ni-finance-mobile-logo.svg => finance-ni-mobile-logo.svg} (99%) create mode 100644 tests/functional/spec/theme_beis_ni.spec.js diff --git a/.design-system-version b/.design-system-version index 2e3288ea8f..1a1f1a1013 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -61.0.2 +61.0.4 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 2233709619..f751eb898d 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -15,10 +15,11 @@ from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( BEISBusinessSurveyConfig, + BEISNIBusinessSurveyConfig, BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, - NorthernIrelandBusinessSurveyConfig, + NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, @@ -76,9 +77,10 @@ def context(self) -> dict[str, Any]: "google_tag_manager_id": self._google_tag_manager_id, "google_tag_manager_auth": self._google_tag_manager_auth, "survey_type": self._survey_type, - "mastheadLogo": self._survey_config.masthead_logo, - "mastheadLogoMobile": self._survey_config.masthead_logo_mobile, + "masthead_logo": self._survey_config.masthead_logo, + "masthead_logo_mobile": self._survey_config.masthead_logo_mobile, } + if self._survey_type: context["cookie_settings_url"] = self._survey_config.cookie_settings_url context["cookie_domain"] = self._survey_config.cookie_domain @@ -174,8 +176,9 @@ def survey_config_mapping( SurveyType.BUSINESS: BusinessSurveyConfig, SurveyType.HEALTH: SocialSurveyConfig, SurveyType.SOCIAL: SocialSurveyConfig, - SurveyType.NORTHERN_IRELAND: NorthernIrelandBusinessSurveyConfig, + SurveyType.NORTHERN_IRELAND: NIBusinessSurveyConfig, SurveyType.BEIS: BEISBusinessSurveyConfig, + SurveyType.BEIS_NI: BEISNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, SurveyType.CENSUS: ( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index c8740bd1b5..75a25c7a0f 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -1,7 +1,8 @@ from .business_config import ( BEISBusinessSurveyConfig, + BEISNIBusinessSurveyConfig, BusinessSurveyConfig, - NorthernIrelandBusinessSurveyConfig, + NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) from .census_config import ( @@ -20,8 +21,9 @@ "CensusNISRASurveyConfig", "WelshCensusSurveyConfig", "BusinessSurveyConfig", - "NorthernIrelandBusinessSurveyConfig", + "NIBusinessSurveyConfig", "BEISBusinessSurveyConfig", + "BEISNIBusinessSurveyConfig", "ORRBusinessSurveyConfig", "Link", ] diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index e0d79d0691..10a3b6c1ac 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -122,10 +122,10 @@ def _stripped_base_url(self) -> str: @dataclass -class NorthernIrelandBusinessSurveyConfig(BusinessSurveyConfig): - masthead_logo: str = read_file("./templates/assets/images/ni-finance-logo.svg") +class NIBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file("./templates/assets/images/finance-ni-logo.svg") masthead_logo_mobile: str = read_file( - "./templates/assets/images/ni-finance-mobile-logo.svg" + "./templates/assets/images/finance-ni-mobile-logo.svg" ) @@ -137,6 +137,13 @@ class BEISBusinessSurveyConfig(BusinessSurveyConfig): ) +@dataclass +class BEISNIBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/beis-mobile-logo.svg" + ) + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") + + @dataclass class ORRBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo: str = read_file("./templates/assets/images/orr-logo.svg") diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index ad7add1037..30f645a336 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -8,6 +8,7 @@ class SurveyType(Enum): HEALTH = "health" NORTHERN_IRELAND = "northernireland" BEIS = "beis" + BEIS_NI = "beis-ni" ORR = "orr" CENSUS = "census" CENSUS_NISRA = "census-nisra" diff --git a/schemas/test/en/test_theme_beis_ni.json b/schemas/test/en/test_theme_beis_ni.json new file mode 100644 index 0000000000..3c0e688d27 --- /dev/null +++ b/schemas/test/en/test_theme_beis_ni.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test NI Department for Business, Energy and Industrial Strategy", + "theme": "beis-ni", + "description": "A questionnaire to demo the BEIS-NI survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "BEIS-NI Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/beis-mobile-logo.svg b/templates/assets/images/beis-mobile-logo.svg index 081c29cde3..ffb2816967 100644 --- a/templates/assets/images/beis-mobile-logo.svg +++ b/templates/assets/images/beis-mobile-logo.svg @@ -1,4 +1,4 @@ -
    ", "Space": " "} answer_separator = answer_separators.get(concatenation_type, " ") @@ -119,20 +134,25 @@ def _concatenate_answers(self, answer_store, concatenation_type): for answer_schema in self.answer_schemas ] - values_to_concatenate = [] + values_to_concatenate: list[AnswerValueEscapedTypes] = [] for answer_value in answer_values: if not answer_value: continue - values_to_concatenate.extend( - answer_value if isinstance(answer_value, list) else [answer_value] - ) + if isinstance(answer_value, list): + values_to_concatenate.extend(answer_value) + else: + values_to_concatenate.append(answer_value) return answer_separator.join(str(value) for value in values_to_concatenate) def _build_answer( - self, answer_store, question_schema, answer_schema, answer_value=None - ): + self, + answer_store: AnswerStore, + question_schema: QuestionSchemaType, + answer_schema: Mapping[str, Any], + answer_value: Optional[AnswerValueEscapedTypes] = None, + ) -> InferredAnswerValueTypes: if answer_value is None: return None @@ -146,23 +166,23 @@ def _build_answer( "Checkbox": self._build_checkbox_answers, "Radio": self._build_radio_answer, } - + # Type ignore: Answer value will be a Markup(String) at this stage if answer_schema["type"] in answer_builder: - return answer_builder[answer_schema["type"]]( - answer_value, answer_schema, answer_store - ) + return answer_builder[answer_schema["type"]](answer_value, answer_schema, answer_store) # type: ignore return answer_value - def _build_date_range_answer(self, answer_store, answer): + def _build_date_range_answer( + self, answer_store: AnswerStore, answer: Optional[AnswerValueEscapedTypes] + ) -> dict[str, Optional[AnswerValueEscapedTypes]]: next_answer = next(self.answer_schemas) to_date = self.get_answer(answer_store, next_answer["id"]) return {"from": answer, "to": to_date} def _get_dynamic_answer_options( self, - answer_schema, - ): + answer_schema: Mapping[str, Any], + ) -> tuple[dict[str, str], ...]: if not (dynamic_options_schema := answer_schema.get("dynamic_options")): return () @@ -174,12 +194,19 @@ def _get_dynamic_answer_options( return dynamic_options.evaluate() - def get_answer_options(self, answer_schema): + def get_answer_options( + self, answer_schema: Mapping[str, Any] + ) -> tuple[dict[str, str], ...]: return tuple(answer_schema.get("options", ())) + tuple( self._get_dynamic_answer_options(answer_schema) ) - def _build_checkbox_answers(self, answer, answer_schema, answer_store): + def _build_checkbox_answers( + self, + answer: Markup, + answer_schema: Mapping[str, Any], + answer_store: AnswerStore, + ) -> Optional[list[RadioCheckboxTypes]]: multiple_answers = [] for option in self.get_answer_options(answer_schema): if escape(option["value"]) in answer: @@ -196,7 +223,13 @@ def _build_checkbox_answers(self, answer, answer_schema, answer_store): return multiple_answers or None - def _build_radio_answer(self, answer, answer_schema, answer_store): + def _build_radio_answer( + self, + answer: Markup, + answer_schema: Mapping[str, Any], + answer_store: AnswerStore, + ) -> Optional[RadioCheckboxTypes]: + for option in self.get_answer_options(answer_schema): if answer == escape(option["value"]): detail_answer_value = self._get_detail_answer_value( @@ -207,16 +240,22 @@ def _build_radio_answer(self, answer, answer_schema, answer_store): "detail_answer_value": detail_answer_value, } - def _get_detail_answer_value(self, option, answer_store): + def _get_detail_answer_value( + self, option: dict, answer_store: AnswerStore + ) -> Optional[AnswerValueEscapedTypes]: if "detail_answer" in option: return self.get_answer(answer_store, option["detail_answer"]["id"]) - def _build_dropdown_answer(self, answer, answer_schema): + def _build_dropdown_answer( + self, + answer: Optional[AnswerValueEscapedTypes], + answer_schema: Mapping[str, Any], + ) -> Optional[str]: for option in self.get_answer_options(answer_schema): if answer == option["value"]: return option["label"] - def serialize(self): + def serialize(self) -> dict[str, Any]: return { "id": self.id, "type": self.type, diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index 7c1d918d0b..dce01d96e9 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,4 +1,6 @@ -from typing import Generator, Mapping, Optional, Union +from typing import Any, Generator, Mapping, Optional, Union + +from werkzeug.datastructures import ImmutableDict from app.questionnaire.location import Location @@ -24,7 +26,7 @@ def __call__( def _build_all_groups( self, return_to: Optional[str] - ) -> Generator[dict, None, None]: + ) -> Generator[ImmutableDict[str, Any], None, None]: """NB: Does not support repeating sections""" for section_id in self._router.enabled_section_ids: diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index 1dcfa02544..1b8a3ca301 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -1,10 +1,11 @@ from datetime import datetime -from typing import Mapping, Optional +from typing import Any, Optional, Union from flask import url_for from flask_babel import lazy_gettext from app.data_models.metadata_proxy import MetadataProxy +from app.forms.email_form import EmailForm from app.globals import ( get_view_submitted_response_expiration_time, has_view_submitted_response_expired, @@ -23,7 +24,7 @@ def build_thank_you_context( submitted_at: datetime, survey_type: SurveyType, guidance_content: Optional[dict] = None, -) -> Mapping: +) -> dict[str, Any]: if survey_type is SurveyType.SOCIAL: submission_text = lazy_gettext("Your answers have been submitted.") elif (trad_as := metadata["trad_as"]) and (ru_name := metadata["ru_name"]): @@ -51,8 +52,12 @@ def build_thank_you_context( } -def build_view_submitted_response_context(schema, submitted_at): - view_submitted_response = {"enabled": schema.is_view_submitted_response_enabled} +def build_view_submitted_response_context( + schema: QuestionnaireSchema, submitted_at: datetime +) -> dict[str, Union[bool, str]]: + view_submitted_response: dict[str, Union[bool, str]] = { + "enabled": schema.is_view_submitted_response_enabled + } if schema.is_view_submitted_response_enabled: expired = has_view_submitted_response_expired(submitted_at) @@ -70,9 +75,10 @@ def build_view_submitted_response_context(schema, submitted_at): def build_census_thank_you_context( - metadata: MetadataProxy, confirmation_email_form, form_type -) -> Mapping: - + metadata: MetadataProxy, + confirmation_email_form: Optional[EmailForm], + form_type: str, +) -> dict[str, Union[bool, str, None]]: context = { "display_address": metadata["display_address"], "form_type": form_type, diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index 4c62eeea3f..e43629119d 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -21,7 +21,6 @@ def build_view_submitted_response_context( questionnaire_store: QuestionnaireStore, survey_type: SurveyType, ) -> dict[str, Union[str, datetime, dict]]: - view_submitted_response_expired = has_view_submitted_response_expired( questionnaire_store.submitted_at # type: ignore ) diff --git a/mypy.ini b/mypy.ini index d822e9dbad..3e085e0701 100644 --- a/mypy.ini +++ b/mypy.ini @@ -46,22 +46,12 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.views.contexts.submit_context] +[mypy-app.views.contexts.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.views.contexts.view_submitted_response_context] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.views.contexts.view_summary_context] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.views.contexts.summary.list_collector_block] +[mypy-app.views.contexts.summary.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True From b9c405874c95836861b62f2666ec7892f3108b10 Mon Sep 17 00:00:00 2001 From: Hajara Iyal Date: Tue, 7 Feb 2023 11:14:34 +0000 Subject: [PATCH 150/567] Bug fix: Fields not honouring schema defined decimal places (#1011) * Fix decimal places bug * get_field refactored * Run functional test for percentage_decimal * Run yarn lint and yarn format commands * Update tests and add new block to percentage schema * Lint and format * Empty-Commit * Empty-Commit * Update unit test * Update title field in test_percentage schema * Empty-Commit * get_field refactored with additional_args --- app/forms/field_handlers/number_handler.py | 10 +++++++- schemas/test/en/test_percentage.json | 24 +++++++++++++++++- .../spec/percentage_decimal.spec.js | 25 +++++++++++++++++++ .../test_render_percentage_widget.py | 1 + .../test_questionnaire_page_titles.py | 1 + tests/integration/test_broken_submission.py | 1 + 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/functional/spec/percentage_decimal.spec.js diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index 8142c7c983..70c70f57a6 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -65,8 +65,16 @@ def _field_type( ) def get_field(self) -> Union[DecimalField, IntegerField]: + additional_args = ( + {"places": self.max_decimals} + if self._field_type == DecimalFieldWithSeparator + else {} + ) return self._field_type( - label=self.label, validators=self.validators, description=self.guidance + label=self.label, + validators=self.validators, + description=self.guidance, + **additional_args, ) def _get_number_field_validators( diff --git a/schemas/test/en/test_percentage.json b/schemas/test/en/test_percentage.json index 4fdda14a66..29d9c586d4 100644 --- a/schemas/test/en/test_percentage.json +++ b/schemas/test/en/test_percentage.json @@ -56,7 +56,29 @@ } ], "id": "question", - "title": "Title", + "title": "Enter a percentage", + "type": "General" + } + }, + { + "type": "Question", + "id": "block-decimal", + "question": { + "answers": [ + { + "description": "Enter percentage of growth", + "id": "answer-decimal", + "label": "New to the market 2012-2014", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 3 + } + ], + "id": "question-decimal", + "title": "Enter a percentage with up to 3 decimal places", "type": "General" } } diff --git a/tests/functional/spec/percentage_decimal.spec.js b/tests/functional/spec/percentage_decimal.spec.js new file mode 100644 index 0000000000..1de8800b3d --- /dev/null +++ b/tests/functional/spec/percentage_decimal.spec.js @@ -0,0 +1,25 @@ +import PercentagePage from "../generated_pages/percentage/block.page.js"; +import PercentageDecimalPage from "../generated_pages/percentage/block-decimal.page.js"; +import SubmitPage from "../generated_pages/percentage/submit.page.js"; + +describe("Decimal places", () => { + it("Given an answer allows 3 decimal places, When I enter a value to 3 decimal places and return to edit the value, Then the answer should be displayed with 3 decimal places", () => { + browser.openQuestionnaire("test_percentage.json"); + $(PercentagePage.submit()).click(); + $(PercentageDecimalPage.decimal()).setValue("3.333"); + $(PercentageDecimalPage.submit()).click(); + $(SubmitPage.previous()).click(); + expect(browser.getUrl()).to.contain(PercentageDecimalPage.pageName); + expect($(PercentageDecimalPage.decimal()).getValue()).to.equal("3.333"); + }); + + it("Given an answer allows 3 decimal places, When I enter a value to 1 decimal place and return to edit the value, Then the answer should be displayed with 3 decimal places", () => { + browser.openQuestionnaire("test_percentage.json"); + $(PercentagePage.submit()).click(); + $(PercentageDecimalPage.decimal()).setValue("3.3"); + $(PercentageDecimalPage.submit()).click(); + $(SubmitPage.previous()).click(); + expect(browser.getUrl()).to.contain(PercentageDecimalPage.pageName); + expect($(PercentageDecimalPage.decimal()).getValue()).to.equal("3.300"); + }); +}); diff --git a/tests/integration/components/test_render_percentage_widget.py b/tests/integration/components/test_render_percentage_widget.py index f1e82551eb..a071a0b007 100644 --- a/tests/integration/components/test_render_percentage_widget.py +++ b/tests/integration/components/test_render_percentage_widget.py @@ -32,6 +32,7 @@ def test_entering_float_displays_error(self): def test_entering_valid_percentage_redirects_to_summary(self): self.post({"answer": "50"}) + self.post({"answer-decimal": "5.5"}) self.assertStatusOK() self.assertInUrl(SUBMIT_URL_PATH) diff --git a/tests/integration/questionnaire/test_questionnaire_page_titles.py b/tests/integration/questionnaire/test_questionnaire_page_titles.py index eb3e890ae5..afa5d2f976 100644 --- a/tests/integration/questionnaire/test_questionnaire_page_titles.py +++ b/tests/integration/questionnaire/test_questionnaire_page_titles.py @@ -32,6 +32,7 @@ def test_should_have_question_in_page_title_on_submit_page_with_summary(self): self.launchSurvey("test_percentage") # When self.post({"answer": ""}) + self.post({"answer-decimal": ""}) # Then self.assertEqualPageTitle( "Check your answers and submit - Percentage Field Demo" diff --git a/tests/integration/test_broken_submission.py b/tests/integration/test_broken_submission.py index f6f557364d..07cb65a2d2 100644 --- a/tests/integration/test_broken_submission.py +++ b/tests/integration/test_broken_submission.py @@ -19,6 +19,7 @@ def tearDown(self): def test_broken_submitter_results_in_500(self): self.post({"answer": "50"}) + self.post({"answer-decimal": "5.5"}) self.assertStatusOK() self.post() From 2b0bf3266cbc1e2476d89fed959ecc31f2e4ebfb Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:03:20 +0000 Subject: [PATCH 151/567] Update chromedriver to v109 (#1023) --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index c10c22366d..6afe1aaca4 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^108.0.0", + "chromedriver": "^109.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 40b1631885..9784a215f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,10 +1581,10 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -axios@^1.1.3: - version "1.2.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" - integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== +axios@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3" + integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -1867,13 +1867,13 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^108.0.0: - version "108.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-108.0.0.tgz#7994013f423d8b95a513bb9553a55088de81b252" - integrity sha512-/kb0rb0dlC4RfXh2BOT7RV87K6d+It3VV5YXebLzO5a8t2knNffiTE23XPJQCH+l1xmgoW8/sOX/NB9irskvOQ== +chromedriver@^109.0.0: + version "109.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-109.0.0.tgz#9407af72f2a01bb19e6db1b85cfc87eb3acd4f6b" + integrity sha512-jdmBq11IUwfThLFiygGTZ89qbROSQI4bICQjvOVQy2Bqr1LwC+MFkhwyZp3YG99eehQbZuTlQmmfCZBfpewTNA== dependencies: "@testim/chrome-version" "^1.1.3" - axios "^1.1.3" + axios "^1.2.1" compare-versions "^5.0.1" extract-zip "^2.0.1" https-proxy-agent "^5.0.1" From b983b1677c0fb87fbffd7677e01be4ad3ac27707 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:19:22 +0000 Subject: [PATCH 152/567] Bump http-cache-semantics from 4.1.0 to 4.1.1 (#1019) Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/kornelski/http-cache-semantics/releases) - [Commits](https://github.com/kornelski/http-cache-semantics/commits) --- updated-dependencies: - dependency-name: http-cache-semantics dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9784a215f1..264e7f3c30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3003,9 +3003,9 @@ hosted-git-info@^2.1.4: integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" From faa9a63de139d97852213ff70b6f5207ab9ddfda Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 8 Feb 2023 09:41:56 +0000 Subject: [PATCH 153/567] Add support for answer codes in runner (#1015) --- app/submitter/converter_v2.py | 40 ++++- schemas/test/en/test_answer_codes.json | 164 ++++++++++++++++++ .../test_list_collector_section_summary.json | 34 ++++ .../submitter/test_convert_payload_0_0_3.py | 107 ++++++++++++ 4 files changed, 337 insertions(+), 8 deletions(-) create mode 100644 schemas/test/en/test_answer_codes.json diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index 56997e396b..b75e788ca7 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Mapping, Optional, OrderedDict, Union +from typing import Any, Iterable, Mapping, Optional, OrderedDict, Union from structlog import get_logger @@ -123,15 +123,39 @@ def get_payload_data( schema=schema, full_routing_path=routing_path, ) + if schema.json["data_version"] == "0.0.3": - return { - "answers": convert_answers_to_payload_0_0_3( - answer_store=answer_store, - list_store=list_store, - schema=schema, - full_routing_path=routing_path, - ), + answers = convert_answers_to_payload_0_0_3( + answer_store=answer_store, + list_store=list_store, + schema=schema, + full_routing_path=routing_path, + ) + + data: dict[str, Union[list[Any]]] = { + "answers": answers, "lists": list_store.serialize(), } + if answer_codes := schema.json.get("answer_codes"): + answer_ids_to_filter = {answer.answer_id for answer in answers} + if filtered_answer_codes := get_filtered_answer_codes( + answer_codes=answer_codes, answer_ids_to_filter=answer_ids_to_filter + ): + data["answer_codes"] = filtered_answer_codes + + return data + raise DataVersionError(schema.json["data_version"]) + + +def get_filtered_answer_codes( + *, answer_codes: Iterable[dict], answer_ids_to_filter: set[str] +) -> list[dict[str, str]]: + filtered_answer_codes: list[dict] = [] + filtered_answer_codes.extend( + answer_code + for answer_code in answer_codes + if answer_code["answer_id"] in answer_ids_to_filter + ) + return filtered_answer_codes diff --git a/schemas/test/en/test_answer_codes.json b/schemas/test/en/test_answer_codes.json new file mode 100644 index 0000000000..6ec68041a9 --- /dev/null +++ b/schemas/test/en/test_answer_codes.json @@ -0,0 +1,164 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Answer Codes", + "theme": "default", + "description": "A questionnaire to demo answer codes.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "answer_codes": [ + { + "answer_id": "mandatory-checkbox-answer", + "answer_value": "None", + "code": "1a" + }, + { + "answer_id": "mandatory-checkbox-answer", + "answer_value": "Ham & Cheese", + "code": "1b" + }, + { + "answer_id": "mandatory-checkbox-answer", + "answer_value": "Ham", + "code": "1c" + }, + { + "answer_id": "mandatory-checkbox-answer", + "answer_value": "Pepperoni", + "code": "1d" + }, + { + "answer_id": "mandatory-checkbox-answer", + "answer_value": "Other", + "code": "1e" + }, + { + "answer_id": "other-answer-mandatory", + "code": "1f" + }, + { + "answer_id": "name-answer", + "code": "2" + }, + { + "answer_id": "name-answer-2", + "code": "3" + } + ], + "sections": [ + { + "id": "default-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "mandatory-checkbox", + "question": { + "answers": [ + { + "id": "mandatory-checkbox-answer", + "mandatory": false, + "options": [ + { + "label": "None", + "value": "None" + }, + { + "label": "Ham & Cheese", + "value": "Ham & Cheese" + }, + { + "label": "Ham", + "value": "Ham" + }, + { + "label": "Pepperoni", + "value": "Pepperoni" + }, + { + "label": "Other", + "description": "Choose any other topping", + "value": "Other", + "detail_answer": { + "mandatory": true, + "id": "other-answer-mandatory", + "label": "Please specify other", + "type": "TextField" + } + } + ], + "type": "Checkbox" + } + ], + "id": "mandatory-checkbox-question", + "title": "Which pizza toppings would you like?", + "type": "General" + } + }, + { + "type": "Question", + "id": "name-block", + "question": { + "answers": [ + { + "id": "name-answer", + "label": "What is your name?", + "max_length": 20, + "mandatory": false, + "type": "TextField" + } + ], + "id": "name-question", + "title": "Title", + "type": "General" + } + }, + { + "type": "Question", + "id": "name-block-2", + "question": { + "answers": [ + { + "id": "name-answer-2", + "label": "What is your surname?", + "max_length": 20, + "mandatory": false, + "type": "TextField" + } + ], + "id": "name-question-2", + "title": "Title", + "type": "General" + } + } + ], + "id": "checkboxes" + } + ] + } + ] +} diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index e319bfc419..dcdf3d1ca1 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -21,6 +21,40 @@ "type": "string" } ], + "answer_codes": [ + { + "answer_id": "any-companies-or-branches-answer", + "code": "1" + }, + { + "answer_id": "company-or-branch-name", + "code": "1a" + }, + { + "answer_id": "registration-number", + "code": "1b" + }, + { + "answer_id": "authorised-insurer-radio", + "code": "1c" + }, + { + "answer_id": "any-other-companies-or-branches-answer", + "code": "2" + }, + { + "answer_id": "confirmation-checkbox-answer", + "code": "3" + }, + { + "answer_id": "anyone-else", + "code": "4" + }, + { + "answer_id": "householder-checkbox-answer", + "code": "5" + } + ], "questionnaire_flow": { "type": "Linear", "options": { diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 6c3b807df1..1f4b2ad4ca 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -1351,3 +1351,110 @@ def test_relationship_answers_not_on_path_in_payload(version): assert ("related-to-anyone-else-answer", "person1") in answers relationships_answer = answers[("relationship-answer", None)] assert expected_relationships_answer == relationships_answer["value"] + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_answers_codes_only_present_for_answered_questions(version): + questionnaire_store = get_questionnaire_store(version) + + full_routing_path = [ + RoutingPath(["mandatory-checkbox", "name-block"], section_id="default-section") + ] + + questionnaire_store.answer_store = AnswerStore( + [ + Answer("name-answer", "Joe Bloggs", None).to_dict(), + ] + ) + + schema = load_schema_from_name("test_answer_codes") + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + ) + + # Then + assert len(data_payload["answer_codes"]) == 1 + assert data_payload["answer_codes"][0]["answer_id"] == "name-answer" + assert data_payload["answer_codes"][0]["code"] == "2" + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(version): + questionnaire_store = get_questionnaire_store(version) + + full_routing_path = [ + RoutingPath(["mandatory-checkbox"], section_id="default-section") + ] + + questionnaire_store.answer_store = AnswerStore( + [ + Answer("mandatory-checkbox-answer", ["Ham"]).to_dict(), + ] + ) + + schema = load_schema_from_name("test_answer_codes") + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + ) + + # Then + assert len(data_payload["answer_codes"]) == 5 + assert all( + answer_code["answer_id"] == "mandatory-checkbox-answer" + for answer_code in data_payload["answer_codes"] + ) + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_no_answers_codes_in_payload_when_no_questions_answered(version): + questionnaire_store = get_questionnaire_store(version) + + full_routing_path = [ + RoutingPath(["mandatory-checkbox"], section_id="default-section") + ] + + questionnaire_store.answer_store = AnswerStore() + + schema = load_schema_from_name("test_answer_codes") + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + ) + + # Then + assert "answer_codes" not in data_payload From ce09a1c6dc44c7461d5cddac8eb56ba44d1c5032 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Wed, 8 Feb 2023 09:55:27 +0000 Subject: [PATCH 154/567] Bug: Flush always using v1 converter (#1020) * fixing phm-flush-using-v1 and not v2 updating mock payload * fixing linting issue * review comment refactor --- app/routes/flush.py | 45 +++++++++++++--- tests/integration/test_flush_data.py | 78 ++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/app/routes/flush.py b/app/routes/flush.py index 09b5a24a9d..9ebf20505d 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -5,11 +5,17 @@ from sdc.crypto.encrypter import encrypt from structlog import get_logger +from app.authentication.auth_payload_version import AuthPayloadVersion from app.authentication.user import User +from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_answer_store, get_metadata, get_questionnaire_store from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SUBMISSION +from app.questionnaire import QuestionnaireSchema from app.questionnaire.router import Router +from app.questionnaire.routing_path import RoutingPath from app.submitter.converter import convert_answers +from app.submitter.converter_v2 import convert_answers_v2 from app.submitter.submission_failed import SubmissionFailedException from app.utilities.json import json_dumps from app.utilities.schema import load_schema_from_metadata @@ -75,14 +81,8 @@ def _submit_data(user): ) full_routing_path = router.full_routing_path() - message = json_dumps( - convert_answers( - schema, - questionnaire_store, - full_routing_path, - submitted_at, - flushed=True, - ) + message: str = _get_converted_answers_message( + full_routing_path, metadata, questionnaire_store, schema, submitted_at ) encrypted_message = encrypt( @@ -109,6 +109,35 @@ def _submit_data(user): return False +def _get_converted_answers_message( + full_routing_path: RoutingPath, + metadata: MetadataProxy, + questionnaire_store: QuestionnaireStore, + schema: QuestionnaireSchema, + submitted_at: datetime, +) -> str: + """ + This gets converted answer message based on the selected version. + For version 1 `app.submitter.converter.convert_answers` is used whereas for version 2 `app.submitter.converter_v2.convert_answers_v2` is used + Returns: + object: str + """ + answer_converter = ( + convert_answers_v2 + if metadata.version is AuthPayloadVersion.V2 + else convert_answers + ) + return json_dumps( + answer_converter( + schema, + questionnaire_store, + full_routing_path, + submitted_at, + flushed=True, + ) + ) + + def _get_user(response_id): id_generator = current_app.eq["id_generator"] user_id = id_generator.generate_id(response_id) diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 10e9468a5e..3ca7de5d95 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -113,3 +113,81 @@ def get_payload(): "response_id": "1234567890123456", "roles": ["flusher"], } + + @patch("app.routes.flush.convert_answers_v2") + @patch("app.routes.flush.convert_answers") + def test_flush_data_successful_v1( + self, mock_convert_answers, mock_convert_answers_v2 + ): + mock_convert_answer_payload = { + "case_id": "7e0fd167-36af-4506-806d-421e5ba3544b", + "tx_id": "5432e910-c6ef-418a-abcc-a8de8c23b0f9", + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "0.0.3", + "origin": "uk.gov.ons.edc.eq", + "survey_id": "001", + "flushed": True, + "submitted_at": "2023-02-07T11:41:12.126783+00:00", + "collection": { + "exercise_sid": "f9fb5e81-9820-44cc-a2c3-16bf382b4d8d", + "schema_name": "test_textfield", + "period": "201605", + }, + "metadata": {"user_id": "UNKNOWN", "ru_ref": "12346789012A"}, + "launch_language_code": "en", + "data": { + "answers": [{"answer_id": "name-answer", "value": "sdfsdaf"}], + "lists": [], + }, + "started_at": "2023-02-07T11:40:46.845149+00:00", + } + mock_convert_answers.return_value = mock_convert_answer_payload + self.post( + url="/flush?token=" + + self.token_generator.generate_token(self.get_payload()) + ) + self.assertStatusOK() + mock_convert_answers.assert_called_once() + mock_convert_answers_v2.assert_not_called() + + @patch("app.routes.flush.convert_answers_v2") + @patch("app.routes.flush.convert_answers") + def test_flush_data_successful_v2( + self, mock_convert_answers, mock_convert_answers_v2 + ): + mock_convert_answer_payload = { + "case_id": "19300487-87e7-42df-9330-718efb08e660", + "tx_id": "5d8b97f7-c8bd-42e1-88c9-e7721388463b", + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "v2", + "data_version": "0.0.3", + "origin": "uk.gov.ons.edc.eq", + "collection_exercise_sid": "1eeb58ec-bae7-414d-a02e-5c3c23052dc7", + "schema_name": "test_textfield", + "flushed": True, + "submitted_at": "2023-02-07T11:42:59.575214+00:00", + "launch_language_code": "en", + "survey_metadata": { + "survey_id": "001", + "period_id": "201605", + "ru_name": "ESSENTIAL ENTERPRISE LTD.", + "user_id": "UNKNOWN", + "ru_ref": "12346789012A", + }, + "data": { + "answers": [{"answer_id": "name-answer", "value": "sdfsdf"}], + "lists": [], + }, + "started_at": "2023-02-07T11:42:32.380784+00:00", + } + self.launchSurveyV2("test_textfield") + form_data = {"name-answer": "Joe Bloggs"} + self.post(form_data) + mock_convert_answers_v2.return_value = mock_convert_answer_payload + self.post( + url="/flush?token=" + + self.token_generator.generate_token(self.get_payload()) + ) + self.assertStatusOK() + mock_convert_answers_v2.assert_called_once() + mock_convert_answers.assert_not_called() From 41572bf6876515fe75413cea718d6c4f52c332d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 10:23:22 +0000 Subject: [PATCH 155/567] Bump ua-parser-js from 1.0.2 to 1.0.33 (#1007) Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 1.0.2 to 1.0.33. - [Release notes](https://github.com/faisalman/ua-parser-js/releases) - [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md) - [Commits](https://github.com/faisalman/ua-parser-js/compare/1.0.2...1.0.33) --- updated-dependencies: - dependency-name: ua-parser-js dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 264e7f3c30..945036eeff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4926,9 +4926,9 @@ typescript@^4.7.4: integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== ua-parser-js@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.2.tgz#e2976c34dbfb30b15d2c300b2a53eac87c57a775" - integrity sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg== + version "1.0.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4" + integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ== unbox-primitive@^1.0.1: version "1.0.2" From 0891432f478e7ad1fe46cee0ec9f8b70c9b89a1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Feb 2023 10:35:57 +0000 Subject: [PATCH 156/567] Bump json5 from 1.0.1 to 1.0.2 (#993) Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 945036eeff..2703db54db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3412,9 +3412,9 @@ json-web-key@^0.4.0: asn1.js "^5.0.1" json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -3795,9 +3795,9 @@ minimatch@~3.0.2: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== mkdirp-classic@^0.5.2: version "0.5.3" From 52752b1c1813046d642459ac62b09553597b3f53 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:44:46 +0000 Subject: [PATCH 157/567] Schemas v3.37.1 (#1025) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index df9588eae1..8974c75259 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.37.0 +v3.37.1 From 88f66d2420142315b9682aea665f85f48e90ae28 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 9 Feb 2023 15:02:35 +0000 Subject: [PATCH 158/567] Update Ubuntu to version 22 (#1008) --- .github/PULL_REQUEST_TEMPLATE.md | 3 +-- .github/workflows/codeql-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3f8d7a0bdf..d404e42927 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,9 @@ ### What is the context of this PR? Describe what you have changed and why, link to other PRs or Issues as appropriate. -### How to review +### How to review Describe the steps required to test the changes (include screenshots if appropriate). ### Checklist - * [ ] New static content marked up for translation * [ ] Newly defined schema content included in eq-translations repo diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9f37f9ea62..591f2f0365 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -17,7 +17,7 @@ on: jobs: analyze: name: Analyze - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false From 5432c40068553d298406bc17384065e60aff83e0 Mon Sep 17 00:00:00 2001 From: Guilhem <122792081+ONS-Guilhem-Forey@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:06:37 +0000 Subject: [PATCH 159/567] Fix List block handlers: (#1003) - After adding, ensure next location is always parent location - After adding, ensure list section is in progress - After removing, ensure next location is always next incomplete location by updating the path - Ensure URL parameters are kept all the way through the list pages - Test all these with integration and functional tests --- app/questionnaire/router.py | 16 +- app/views/handlers/list_action.py | 72 ++++- app/views/handlers/list_add_question.py | 20 ++ app/views/handlers/list_collector.py | 4 + app/views/handlers/list_remove_question.py | 3 + app/views/handlers/question.py | 1 + .../test_list_collector_section_summary.json | 33 +- ...eating_sections_with_hub_and_spoke.spec.js | 13 +- tests/functional/spec/list_collector.spec.js | 2 + .../list_collector_section_summary.spec.js | 116 ++++++- ...ionnaire_list_change_evaluates_sections.py | 4 +- .../test_questionnaire_list_collector.py | 286 ++++++++++++++++++ 12 files changed, 524 insertions(+), 46 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 5b947279e1..6104c1fcd9 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -316,8 +316,8 @@ def full_routing_path(self) -> list[RoutingPath]: ) return full_routing_path - def _is_block_complete( - self, block_id: str, section_id: str, list_item_id: str + def is_block_complete( + self, *, block_id: str, section_id: str, list_item_id: str ) -> bool: return block_id in self._progress_store.get_completed_block_ids( section_id, list_item_id @@ -327,8 +327,10 @@ def _get_first_incomplete_location_in_section( self, routing_path: RoutingPath ) -> Optional[Location]: for block_id in routing_path: - if not self._is_block_complete( - block_id, routing_path.section_id, routing_path.list_item_id + if not self.is_block_complete( + block_id=block_id, + section_id=routing_path.section_id, + list_item_id=routing_path.list_item_id, ): return Location( block_id=block_id, @@ -347,8 +349,10 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: for block_id in routing_path: allowable_path.append(block_id) - if not self._is_block_complete( - block_id, routing_path.section_id, routing_path.list_item_id + if not self.is_block_complete( + block_id=block_id, + section_id=routing_path.section_id, + list_item_id=routing_path.list_item_id, ): return allowable_path diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index e9175bbba4..79e4e4ec2e 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -34,11 +34,21 @@ def is_location_valid(self): return True def get_previous_location_url(self): - if self._return_to == "section-summary": + if ( + self._return_to == "section-summary" + and self.router.can_display_section_summary( + self.parent_location.section_id, self.parent_location.list_item_id + ) + ): return self.get_section_summary_url() block_id = self._request_args.get("previous") - return self._get_location_url(block_id) + return self._get_location_url( + block_id=block_id, + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) def get_section_summary_url(self): return url_for( @@ -52,26 +62,56 @@ def get_next_location_url(self): ): return self.get_section_summary_url() - return self.parent_location.url() + if self.router.is_block_complete( + block_id=self.parent_location.block_id, + section_id=self.parent_location.section_id, + list_item_id=self.parent_location.list_item_id, + ): + return self.router.get_next_location_url( + self.parent_location, + self._routing_path, + self._return_to, + self._return_to_answer_id, + self._return_to_block_id, + ) + + return self.parent_location.url( + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) def handle_post(self): self.questionnaire_store_updater.update_same_name_items( self.parent_block["for_list"], self.parent_block.get("same_name_answer_ids"), ) - # Clear the answer from the confirmation question on the list collector question - answer_ids_to_remove = self._schema.get_answer_ids_for_block( - self.parent_location.block_id - ) - self.questionnaire_store_updater.remove_answers(answer_ids_to_remove) - self.evaluate_and_update_section_status_on_list_change( - self.parent_block["for_list"] - ) - self.questionnaire_store_updater.save() - def _get_location_url(self, block_id): + if self.questionnaire_store_updater.is_dirty(): + self._routing_path = self.router.routing_path( + self.current_location.section_id, self.current_location.list_item_id + ) + + self.questionnaire_store_updater.save() + + def _get_location_url( + self, + *, + block_id=None, + return_to=None, + return_to_answer_id=None, + return_to_block_id=None, + ): if block_id and self._schema.is_block_valid(block_id): section_id = self._schema.get_section_id_for_block_id(block_id) - return Location(section_id=section_id, block_id=block_id).url() - - return self.parent_location.url() + return Location(section_id=section_id, block_id=block_id).url( + return_to=return_to, + return_to_answer_id=return_to_answer_id, + return_to_block_id=return_to_block_id, + ) + + return self.parent_location.url( + return_to=return_to, + return_to_answer_id=return_to_answer_id, + return_to_block_id=return_to_block_id, + ) diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index cb5e61da83..4e0fe60745 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -9,13 +9,33 @@ def is_location_valid(self): return False return True + def get_next_location_url(self): + return self.parent_location.url( + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + def handle_post(self): + # Ensure the section is in progress when user adds an item list_item_id = self.questionnaire_store_updater.add_list_item( self.parent_block["for_list"] ) + + # Clear the answer from the confirmation question on the list collector question + answer_ids_to_remove = self._schema.get_answer_ids_for_block( + self.parent_location.block_id + ) + self.questionnaire_store_updater.remove_answers(answer_ids_to_remove) + self.questionnaire_store_updater.remove_completed_location(self.parent_location) + # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data, list_item_id) + + self.evaluate_and_update_section_status_on_list_change( + self.parent_block["for_list"] + ) return super().handle_post() def _resolve_custom_page_title_vars(self) -> MutableMapping: diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 7b24cc34d6..8a8ade19f5 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -15,6 +15,9 @@ def get_next_location_url(self): "questionnaire.block", list_name=self.rendered_block["for_list"], block_id=self.rendered_block["add_block"]["id"], + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, ) return add_url @@ -39,6 +42,7 @@ def get_context(self): for_list=self.rendered_block["for_list"], edit_block_id=self.rendered_block["edit_block"]["id"], remove_block_id=self.rendered_block["remove_block"]["id"], + return_to=self._return_to, ), } diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index 94a329ba47..cc35814826 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -28,6 +28,9 @@ def handle_post(self): self.questionnaire_store_updater.remove_list_item_and_answers( list_name, self._current_location.list_item_id ) + self.evaluate_and_update_section_status_on_list_change( + self.parent_block["for_list"] + ) return super().handle_post() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 1b0af18438..c41df3451e 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -207,6 +207,7 @@ def get_return_to_hub_url(self): def evaluate_and_update_section_status_on_list_change(self, list_name): section_ids = self._schema.get_section_ids_dependent_on_list(list_name) + section_ids.append(self.current_location.section_id) section_keys_to_evaluate = ( self.questionnaire_store_updater.started_section_keys( diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index dcdf3d1ca1..26b12aca82 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -131,21 +131,13 @@ }, "routing_rules": [ { - "goto": { - "block": "confirmation-checkbox", - "when": [ - { - "id": "any-companies-or-branches-answer", - "condition": "equals", - "value": "No" - } - ] - } + "when": { + "==": [{ "source": "answers", "identifier": "any-companies-or-branches-answer" }, "No"] + }, + "block": "confirmation-checkbox" }, { - "goto": { - "block": "any-other-companies-or-branches" - } + "block": "any-other-companies-or-branches" } ] }, @@ -330,6 +322,21 @@ "id": "confirmation-checkbox-question", "title": "Are all companies or branches based in UK?", "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "companies" + } + ] + }, + 3 + ] + } } } ] diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index 438f4b9f65..e4b21120e5 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -165,12 +165,19 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); $(VisitorsListCollectorAddPage.submit()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/visitors-block"); // Add second visitor - $(SectionSummaryPage.visitorListAddLink()).click(); + $(VisitorsListCollectorPage.yes()).click(); + $(VisitorsListCollectorPage.submit()).click(); $(VisitorsListCollectorAddPage.firstName()).setValue("Yvonne"); $(VisitorsListCollectorAddPage.lastName()).setValue("Yoe"); $(VisitorsListCollectorAddPage.submit()).click(); + + // Exit the visitors list collector + $(VisitorsListCollectorPage.no()).click(); + $(VisitorsListCollectorPage.submit()).click(); + $(SectionSummaryPage.submit()).click(); expect($(HubPage.summaryRowState("visitors-section-1")).getText()).to.equal("Not started"); @@ -257,6 +264,10 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { $(VisitorsListCollectorAddPage.firstName()).setValue("Anna"); $(VisitorsListCollectorAddPage.lastName()).setValue("Doe"); $(VisitorsListCollectorAddPage.submit()).click(); + + $(VisitorsListCollectorPage.no()).click(); + $(VisitorsListCollectorPage.submit()).click(); + $(SectionSummaryPage.submit()).click(); // New visitor added to hub diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index 101a87f65c..b325a73f77 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -204,6 +204,8 @@ describe("List Collector", () => { $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Joe"); $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Bloggs"); $(VisitorListCollectorAddPage.submit()).click(); + $(VisitorListCollectorPage.no()).click(); + $(VisitorListCollectorPage.submit()).click(); expect($(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).to.contain("Joe Bloggs"); }); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index 39d75ba65b..6aa495fe9e 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -14,7 +14,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).to.be.true; expect($(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).to.contain("Yes"); @@ -23,7 +22,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); expect($(SectionSummaryPage.companiesListLabel(1)).getText()).to.contain("Name of UK company or branch"); expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); expect($(companiesListRowItem(1, 2)).getText()).to.contain("123"); @@ -55,7 +53,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); removeFirstCompany(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($("body").getText()).to.not.have.string("Company A"); @@ -66,7 +63,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); removeFirstCompany(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($("body").getText()).to.contain("No UK company or branch added"); @@ -77,7 +73,6 @@ describe("List Collector Section Summary Items", () => { anyMoreCompaniesYes(); addCompany("Company B", "234", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); removeFirstCompany(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($("body").getText()).to.not.have.string("Company A"); @@ -87,7 +82,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; }); @@ -106,15 +100,24 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); $(SectionSummaryPage.companiesListEditLink(1)).click(); expect(browser.getUrl()).to.contain("edit-company/?return_to=section-summary"); expect($(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).to.equal("Company A"); }); + it("When I edit an item after adding it, Then I should be redirected to the summary page", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + $(SectionSummaryPage.companiesListEditLink(1)).click(); + $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); + $(AnyCompaniesOrBranchesAddPage.submit()).click(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + expect($(companiesListRowItem(1, 1)).getText()).to.contain("Changed Company"); + }); it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", () => { drivingQuestionNo(); - answerUkBasedQuestion(); expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; @@ -132,7 +135,6 @@ describe("List Collector Section Summary Items", () => { drivingQuestionYes(); addCompany("Company A", "123", true); anyMoreCompaniesNo(); - answerUkBasedQuestion(); expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); drivingQuestionNo(); @@ -149,6 +151,102 @@ describe("List Collector Section Summary Items", () => { expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; }); + it("When I add another company from the summary page, Then I am asked if I want to add any more company before accessing the section summary", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesNo(); + $(SectionSummaryPage.companiesListAddLink()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + expect(browser.getUrl()).to.contain("?return_to=section-summary"); + addCompany("Company B", "456", true); + expect(browser.getUrl()).to.contain(AnyCompaniesOrBranchesPage.url()); + expect($("body").getText()).to.have.string("Company A"); + expect($("body").getText()).to.have.string("Company B"); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); + it("When I add three companies, Then I am prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesYes(); + addCompany("Company C", "789", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(UkBasedPage.url()); + }); + it("When I add less than 3 companies, Then I am not prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); + it("When I add more than 3 companies, Then I am not prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesYes(); + addCompany("Company C", "789", true); + anyMoreCompaniesYes(); + addCompany("Company D", "135", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); + it("When I add another company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + $(SectionSummaryPage.companiesListAddLink()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + expect(browser.getUrl()).to.contain("?return_to=section-summary"); + addCompany("Company C", "234", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(UkBasedPage.url()); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); + it("When I remove a company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesYes(); + addCompany("Company C", "234", true); + anyMoreCompaniesYes(); + addCompany("Company D", "345", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + removeFirstCompany(); + expect(browser.getUrl()).to.contain(UkBasedPage.url()); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); + it("When I remove a company from the summary page, and the amount then totals to 3, but the confirmation question has already been answered, Then I am not prompted with the confirmation question", () => { + drivingQuestionYes(); + addCompany("Company A", "123", true); + anyMoreCompaniesYes(); + addCompany("Company B", "456", true); + anyMoreCompaniesYes(); + addCompany("Company C", "234", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(UkBasedPage.url()); + answerUkBasedQuestion(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + $(SectionSummaryPage.companiesListAddLink()).click(); + expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + expect(browser.getUrl()).to.contain("?return_to=section-summary"); + addCompany("Company C", "234", true); + anyMoreCompaniesNo(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + removeFirstCompany(); + expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + }); }); }); diff --git a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py index bb68a8d70c..8110c9fac1 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py +++ b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py @@ -30,9 +30,11 @@ def test_without_primary_person(self): self.post() self.assertEqualUrl("/questionnaire/") - self.get("questionnaire/people/add-person/?return_to=section-summary") + self.get("questionnaire/people/add-person") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) + self.assertEqualUrl("/questionnaire/sections/who-lives-here/") + self.post() self.assertEqualUrl("/questionnaire/") self.assertInSelector( diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector.py b/tests/integration/questionnaire/test_questionnaire_list_collector.py index 619edd880b..bf04bdac0c 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector.py @@ -1,5 +1,7 @@ from . import SUBMIT_URL_PATH, QuestionnaireTestCase +# pylint: disable=too-many-public-methods + class TestQuestionnaireListCollector(QuestionnaireTestCase): def get_add_someone_link(self): @@ -320,3 +322,287 @@ def test_adding_person_using_second_list_collector_when_no_people( self.get(first_person_remove_link) self.assertInBody("Are you sure you want to remove this person?") + + def test_adding_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Make another mistake + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.assertInUrl("?return_to=section-summary") + + def test_removing_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Make another mistake + + remove_link = self.get_link("remove", 1) + + self.get(remove_link) + + self.assertInUrl("?return_to=section-summary") + + def test_changing_item_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + change_link = self.get_link("change", 1) + + self.get(change_link) + + self.assertInUrl("?return_to=section-summary") + + def test_adding_from_the_summary_page_and_then_removing_from_parent_page_keeps_return_to_url_param( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Add another one form the summary + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.add_person("Don", "Page") + + self.assertInSelector("Don Page", "[data-qa='list-item-2-label']") + + remove_link = self.get_link("remove", 2) + + self.get(remove_link) + + self.assertInUrl("?return_to=section-summary") + + self.post({"remove-confirmation": "Yes"}) + + self.assertInUrl("?return_to=section-summary") + + def test_adding_from_the_summary_page_and_then_changing_from_parent_page_keeps_return_to_url_param( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Add another one form the summary + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.add_person("Don", "Page") + + self.assertInSelector("Don Page", "[data-qa='list-item-2-label']") + + change_link = self.get_link("change", 2) + + self.get(change_link) + + self.assertInUrl("?return_to=section-summary") + + self.post({"first-name": "Another", "last-name": "Name"}) + + self.assertInUrl("?return_to=section-summary") + + def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_edit_question_block_persists_return_to_url_param( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Add another one form the summary + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.add_person("Don", "Page") + + self.assertInSelector("Don Page", "[data-qa='list-item-2-label']") + + change_link = self.get_link("change", 2) + + self.get(change_link) + + self.previous() + + self.assertInUrl("?return_to=section-summary") + + def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_remove_question_block_persists_return_to_url_param( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Add another one form the summary + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.add_person("Don", "Page") + + self.assertInSelector("Don Page", "[data-qa='list-item-2-label']") + + remove_link = self.get_link("remove", 2) + + self.get(remove_link) + + self.previous() + + self.assertInUrl("?return_to=section-summary") + + def test_adding_from_the_summary_page_and_then_adding_again_from_list_collector_persists_the_return_to_url_param( + self, + ): + self.launchSurvey("test_list_collector") + + self.assertInBody("Does anyone else live here?") + + self.post({"anyone-else": "Yes"}) + + self.add_person("Marie Claire", "Doe") + + self.assertInSelector("Marie Claire Doe", "[data-qa='list-item-1-label']") + + self.post({"anyone-else": "No"}) + + self.post() + + self.post({"another-anyone-else": "No"}) + + self.assertInBody("List Collector Summary") + + # Add another one form the summary + + add_link = self.get_add_someone_link() + + self.get(add_link) + + self.add_person("Don", "Page") + + self.assertInSelector("Don Page", "[data-qa='list-item-2-label']") + + self.post({"anyone-else": "Yes"}) + + self.assertInUrl("?return_to=section-summary") + + self.add_person("Another", "Person") + + self.assertInUrl("?return_to=section-summary") From fbf3a2763a525ce2769119c5ed66dbb8c2de873b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 10 Feb 2023 11:38:38 +0000 Subject: [PATCH 160/567] Schemas v3.38.0 (#1032) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 8974c75259..d6edd6928b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.37.1 +v3.38.0 From 9403a9037179ac0d01e6bda8765eca9d6f49536a Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:18:49 +0000 Subject: [PATCH 161/567] Schemas v3.39.0 (#1034) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d6edd6928b..d6f9644a87 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.38.0 +v3.39.0 From a680122744b1b36dbe0a1f9c893179cc6afbaeb9 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:50:00 +0000 Subject: [PATCH 162/567] Split tests into matrix suites (#1013) --- .github/workflows/pull_request.yml | 91 +++++++++++-------- .../functional/spec/timeout/timeout_modal.js | 6 +- .../spec/timeout/timeout_modal.spec.js | 11 --- .../timeout_modal_expired.spec.js | 11 +++ ...eout_modal_post_submission_expired.spec.js | 15 +++ .../timeout_modal_extended.spec.js | 11 +++ ...out_modal_post_submission_extended.spec.js | 15 +++ .../timeout_modal_extended_new_window.spec.js | 11 +++ ...ost_submission_extended_new_window.spec.js | 15 +++ .../timeout_modal_post_submission.spec.js | 16 ---- tests/functional/wdio.conf.js | 4 +- 11 files changed, 137 insertions(+), 69 deletions(-) delete mode 100644 tests/functional/spec/timeout/timeout_modal.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js create mode 100644 tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js delete mode 100644 tests/functional/spec/timeout/timeout_modal_post_submission.spec.js diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ab13b394f6..d928305a6f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -8,6 +8,10 @@ on: - "bug-fix-*" - "feature-*" +concurrency: + group: '${{ github.head_ref }}' + cancel-in-progress: true + jobs: python-dependencies: runs-on: ubuntu-22.04 @@ -18,24 +22,10 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - - name: Install apt dependencies - run: | - sudo apt-get install libsnappy-dev libgconf-2-4 - - # Install wkthtmltopdf with patched Qt - sudo apt-get install -y xfonts-base xfonts-75dpi - wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb - sudo dpkg -i wkhtmltox_0.12.6-1.bionic_amd64.deb + cache: 'pipenv' - name: Install Pipenv run: pip install pipenv==2022.11.30 - - name: Cache virtualenv - id: cache-virtualenv - uses: actions/cache@v3 - with: - path: ~/.local/share/virtualenvs/ - key: ${{ runner.os }}-${{ env.PYTHON_VERSION }}-virtualenvs-${{ hashFiles('Pipfile.lock') }} - name: Install virtual environment - if: steps.cache-virtualenv.outputs.cache-hit != 'true' run: pipenv install --dev node-dependencies: runs-on: ubuntu-22.04 @@ -56,6 +46,47 @@ jobs: - name: Install yarn deps if: steps.cache-yarn.outputs.cache-hit != 'true' run: yarn + lint: + needs: [python-dependencies, node-dependencies] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - run: | + echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV + - uses: actions/setup-python@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + cache: 'pipenv' + - uses: actions/setup-node@v3 + with: + node-version: "14.19.0" + - name: Write app version + run: printf "${{ github.event.pull_request.head.sha }}" > .application-version + - name: Install pipenv + run: pip install pipenv==2022.11.30 + - name: Install virtual environment + run: pipenv install --dev + - name: Compile translations + run: make translate + - name: Running translation tests + run: pipenv run python -m scripts.extract_translation_templates --test + - name: Python linting + run: pipenv run ./scripts/run_lint_python.sh + - name: Get yarn cache + id: get-yarn-cache + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + - name: Cache yarn modules + uses: actions/cache@v3 + id: cache-yarn + with: + path: ${{ steps.get-yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + - name: Install yarn deps + run: yarn install + - name: Functional tests spec lint + run: ./scripts/lint_functional_test_specs.sh + - name: Javascript linting + run: yarn lint test-unit: needs: python-dependencies runs-on: ubuntu-22.04 @@ -66,6 +97,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} + cache: 'pipenv' - name: Install apt dependencies run: | sudo apt-get install libsnappy-dev libgconf-2-4 jq @@ -78,14 +110,7 @@ jobs: run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv run: pip install pipenv==2022.11.30 - - name: Cache virtualenv - id: cache-virtualenv - uses: actions/cache@v3 - with: - path: ~/.local/share/virtualenvs/ - key: ${{ runner.os }}-${{ env.PYTHON_VERSION }}-virtualenvs-${{ hashFiles('Pipfile.lock') }} - name: Install virtual environment - if: steps.cache-virtualenv.outputs.cache-hit != 'true' run: pipenv install --dev - name: Load templates run: make load-design-system-templates @@ -93,10 +118,6 @@ jobs: run: make translate - name: Link env vars run: ln -sf .development.env .env - - name: Running translation tests - run: pipenv run python -m scripts.extract_translation_templates --test - - name: Running lint tests - run: pipenv run ./scripts/run_lint_python.sh - name: Running unit tests run: pipenv run ./scripts/run_tests_unit.sh validate-schemas: @@ -108,9 +129,10 @@ jobs: - name: Running schema tests run: ./scripts/validate_test_schemas.sh test-functional: + needs: [python-dependencies, node-dependencies] strategy: matrix: - suite: [ timeout_modal, features, general, components ] + suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, general, components ] runs-on: ubuntu-22.04 env: EQ_RUN_FUNCTIONAL_TESTS_HEADLESS: True @@ -128,18 +150,11 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} + cache: 'pipenv' - name: Install pipenv run: pip install pipenv==2022.11.30 - - name: Cache virtualenv - id: cache-virtualenv - uses: actions/cache@v3 - with: - path: ~/.local/share/virtualenvs/ - key: ${{ runner.os }}-${{ env.PYTHON_VERSION }}-virtualenvs-${{ hashFiles('Pipfile.lock') }} - name: Install virtual environment - if: steps.cache-virtualenv.outputs.cache-hit != 'true' - run: | - pipenv install --dev + run: pipenv install --dev - name: Get yarn cache id: get-yarn-cache run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT @@ -151,10 +166,6 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - name: Install yarn deps run: yarn install - - name: Functional tests spec lint - run: ./scripts/lint_functional_test_specs.sh - - name: Javascript linting check - run: yarn lint - name: Docker compose run: docker-compose --version && RUNNER_ENV_FILE=.functional-tests.env docker-compose up --build -d - name: Functional tests diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index d8e3ed000e..386a81daea 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -1,7 +1,7 @@ import { TimeoutModalPage } from "../../base_pages/timeout-modal.page.js"; class TestCase { - testCase(page) { + testCaseExpired(page) { it("When the timeout modal is displayed, and I do not extend my session, Then I will be redirected to the session expired page", () => { this.checkTimeoutModal(); browser.pause(65000); // We are waiting for the session to expire @@ -16,7 +16,9 @@ class TestCase { ) .to.not.include("To protect your information, your progress will be saved and you will be signed out in"); }).timeout(140000); + } + testCaseExtended(page) { it("When the timeout modal is displayed, and I click the “Continue survey” button, Then my session will be extended", () => { this.checkTimeoutModal(); $(TimeoutModalPage.submit()).click(); @@ -26,7 +28,9 @@ class TestCase { expect(browser.getUrl()).to.contain(page.pageName); expect($("body").getHTML()).to.not.include("Sorry, you need to sign in again"); }).timeout(140000); + } + testCaseExtendedNewWindow(page) { it("When the timeout modal is displayed, but I open a new window and then focus back on the timeout modal window, Then my session will be extended", () => { this.checkTimeoutModal(); browser.newWindow(""); diff --git a/tests/functional/spec/timeout/timeout_modal.spec.js b/tests/functional/spec/timeout/timeout_modal.spec.js deleted file mode 100644 index cbb423a349..0000000000 --- a/tests/functional/spec/timeout/timeout_modal.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -import TimeoutInterstitialPage from "../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; -import { TimeoutModalTestCase } from "./timeout_modal.js"; - -describe("Timeout Modal", () => { - describe("Given I am completing the survey,", () => { - beforeEach(() => { - browser.openQuestionnaire("test_timeout_modal.json"); - }); - TimeoutModalTestCase.testCase(TimeoutInterstitialPage); - }); -}); diff --git a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js new file mode 100644 index 0000000000..f64cf124e6 --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js @@ -0,0 +1,11 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Expired", () => { + describe("Given I am completing the survey,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + }); + TimeoutModalTestCase.testCaseExpired(TimeoutInterstitialPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js new file mode 100644 index 0000000000..a96fcc7428 --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js @@ -0,0 +1,15 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Post Submission Expired", () => { + describe("Given I am completing the survey and get to post submission page,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + $(TimeoutInterstitialPage.submit()).click(); + $(TimeoutSubmitPage.submit()).click(); + }); + TimeoutModalTestCase.testCaseExpired(ThankYouPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js new file mode 100644 index 0000000000..4348c33cf3 --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js @@ -0,0 +1,11 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Expired", () => { + describe("Given I am completing the survey,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + }); + TimeoutModalTestCase.testCaseExtended(TimeoutInterstitialPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js new file mode 100644 index 0000000000..2279cf22c6 --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js @@ -0,0 +1,15 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Post Submission Expired", () => { + describe("Given I am completing the survey and get to post submission page,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + $(TimeoutInterstitialPage.submit()).click(); + $(TimeoutSubmitPage.submit()).click(); + }); + TimeoutModalTestCase.testCaseExtended(ThankYouPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js new file mode 100644 index 0000000000..3cf469800f --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js @@ -0,0 +1,11 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Expired", () => { + describe("Given I am completing the survey,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + }); + TimeoutModalTestCase.testCaseExtendedNewWindow(TimeoutInterstitialPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js new file mode 100644 index 0000000000..bf670da745 --- /dev/null +++ b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js @@ -0,0 +1,15 @@ +import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; +import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import { TimeoutModalTestCase } from "../timeout_modal.js"; + +describe("Timeout Modal Post Submission Expired", () => { + describe("Given I am completing the survey and get to post submission page,", () => { + before(() => { + browser.openQuestionnaire("test_timeout_modal.json"); + $(TimeoutInterstitialPage.submit()).click(); + $(TimeoutSubmitPage.submit()).click(); + }); + TimeoutModalTestCase.testCaseExtendedNewWindow(ThankYouPage); + }); +}); diff --git a/tests/functional/spec/timeout/timeout_modal_post_submission.spec.js b/tests/functional/spec/timeout/timeout_modal_post_submission.spec.js deleted file mode 100644 index 43fa2680dd..0000000000 --- a/tests/functional/spec/timeout/timeout_modal_post_submission.spec.js +++ /dev/null @@ -1,16 +0,0 @@ -import TimeoutInterstitialPage from "../../generated_pages/timeout_modal/timeout-modal-interstitial.page"; -import TimeoutSubmitPage from "../../generated_pages/timeout_modal/submit.page"; -import ThankYouPage from "../../base_pages/thank-you.page.js"; - -import { TimeoutModalTestCase } from "./timeout_modal.js"; - -describe("Timeout Modal Post Submission", () => { - describe("Given I am completing the survey and get to post submission page,", () => { - beforeEach(() => { - browser.openQuestionnaire("test_timeout_modal.json"); - $(TimeoutInterstitialPage.submit()).click(); - $(TimeoutSubmitPage.submit()).click(); - }); - TimeoutModalTestCase.testCase(ThankYouPage); - }); -}); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 81040a547c..8b371a6ad6 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -18,7 +18,9 @@ exports.config = { // specs: ["./tests/functional/spec/**/*.js"], suites: { - timeout_modal: ["./tests/functional/spec/timeout/*.spec.js"], + timeout_modal_expired: ["./tests/functional/spec/timeout/timeout_modal_expired/*.js"], + timeout_modal_extended: ["./tests/functional/spec/timeout/timeout_modal_extended/*.js"], + timeout_modal_extended_new_window: ["./tests/functional/spec/timeout/timeout_modal_extended_new_window/*.js"], components: ["./tests/functional/spec/components/**/*.js"], features: ["./tests/functional/spec/features/**/*.js"], general: ["./tests/functional/spec/*.spec.js"], From 8cc6a35eb78c68256a33f142234bff5556999441 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Tue, 14 Feb 2023 10:28:59 +0000 Subject: [PATCH 163/567] Bug: Answer codes validation skips validation-schema fix (#1035) * Bug: Answer codes validation skips validation-schema fix * Prettier fix --- schemas/test/en/test_list_collector_section_summary.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index 26b12aca82..1a1046aeeb 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -53,6 +53,14 @@ { "answer_id": "householder-checkbox-answer", "code": "5" + }, + { + "answer_id": "first-name", + "code": "6" + }, + { + "answer_id": "last-name", + "code": "7" } ], "questionnaire_flow": { From 00a3af835e997855a7c87bb8d33daa4105bd734b Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 15 Feb 2023 09:23:36 +0000 Subject: [PATCH 164/567] update to ds version 62 (#1029) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 1a1f1a1013..8f7cbd8b05 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -61.0.4 +62.0.0 From eead8ca008603d65637df67d3e07032cc6281bd2 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 15 Feb 2023 11:59:19 +0000 Subject: [PATCH 165/567] Schemas v3.40.0 (#1036) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d6f9644a87..189a6408d6 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.39.0 +v3.40.0 From ceae26d3cd2bc0d45c3110f5902c5d94c0aca448 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 16 Feb 2023 09:57:25 +0000 Subject: [PATCH 166/567] Bug: Runner page width set to 12 columns (#1037) --- templates/layouts/_base.html | 1 + tests/functional/spec/page_layout.spec.js | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/functional/spec/page_layout.spec.js diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 0bff4987a0..8268ae156d 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -36,6 +36,7 @@ {% set pageConfig = { "title": full_page_title, + "pageColNumber": 8, "footer": footer, "cdn": { "url": cdn_url diff --git a/tests/functional/spec/page_layout.spec.js b/tests/functional/spec/page_layout.spec.js new file mode 100644 index 0000000000..8b9bc6c0f4 --- /dev/null +++ b/tests/functional/spec/page_layout.spec.js @@ -0,0 +1,10 @@ +import HubPage from "../base_pages/hub.page"; + +describe("Page Layout", () => { + it("Given a page in the questionnaire, When I visit the page, Then the page width should be as expected", () => { + browser.url(HubPage.url()); + + const cssWidthSelector = $('div[class*="ons-col-"][class*="@m"]').getAttribute("class"); + expect(cssWidthSelector).to.contain("ons-col-8@m"); + }); +}); From 13bbf9c7ee14802b8937a825c79b06a808b3c0e7 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:12:54 +0000 Subject: [PATCH 167/567] Update Runner logger binds to use contextvars and unpin dependencies (#1024) --- .pylintrc | 4 +- Pipfile | 8 +- Pipfile.lock | 375 +++++++++--------- app/authentication/authenticator.py | 4 +- app/cloud_tasks/cloud_task_publishers.py | 1 - app/forms/field_handlers/date_handlers.py | 1 - .../fields/decimal_field_with_separator.py | 1 - .../fields/integer_field_with_separator.py | 1 - ...ultiple_select_field_with_detail_answer.py | 9 +- .../fields/select_field_with_detail_answer.py | 9 +- app/forms/questionnaire_form.py | 6 +- app/forms/validators.py | 48 +-- app/helpers/template_helpers.py | 1 - app/jinja_filters.py | 24 +- app/questionnaire/path_finder.py | 2 - app/questionnaire/placeholder_parser.py | 1 - app/questionnaire/placeholder_transforms.py | 7 +- .../questionnaire_store_updater.py | 5 +- app/questionnaire/when_rules.py | 4 +- app/routes/errors.py | 4 +- app/routes/flush.py | 4 +- app/routes/individual_response.py | 8 +- app/routes/questionnaire.py | 14 +- app/routes/session.py | 4 +- app/secrets.py | 2 +- app/settings.py | 4 +- app/setup.py | 75 ++-- app/submitter/converter.py | 1 - app/survey_config/census_config.py | 2 - app/utilities/schema.py | 1 - .../contexts/calculated_summary_context.py | 4 +- app/views/contexts/section_summary_context.py | 1 - app/views/contexts/summary/group.py | 1 - .../contexts/summary/list_collector_block.py | 2 - app/views/contexts/summary/question.py | 3 - app/views/contexts/summary_context.py | 1 - app/views/handlers/__init__.py | 1 - app/views/handlers/confirm_email.py | 1 - app/views/handlers/confirmation_email.py | 1 - app/views/handlers/feedback.py | 1 - application.py | 5 +- profile_application.py | 1 - tests/app/data_model/conftest.py | 3 - tests/app/data_model/test_session_store.py | 1 - tests/app/forms/test_questionnaire_form.py | 3 - .../test_single_date_range_validator.py | 1 - tests/app/helpers/test_template_helpers.py | 1 - tests/app/mock_data_store.py | 1 - .../test_dynamic_answer_options.py | 1 - .../test_questionnaire_store_updater.py | 3 - .../test_relationship_location.py | 23 +- tests/app/questionnaire/test_routing_path.py | 1 - tests/app/questionnaire/test_when_rules.py | 1 - tests/app/storage/test_datastore.py | 3 - tests/app/submitter/test_submitter.py | 1 - .../views/contexts/summary/test_question.py | 1 - tests/functional/generate_pages.py | 1 - tests/integration/integration_test_case.py | 2 - .../integration/routes/test_questionnaire.py | 23 ++ tests/integration/test_app_create.py | 12 +- 60 files changed, 359 insertions(+), 375 deletions(-) diff --git a/.pylintrc b/.pylintrc index f1d279b219..64a37772b8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -302,8 +302,8 @@ max-complexity=10 [EXCEPTIONS] # Exceptions that will emit a warning when caught. -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtins.BaseException, + builtins.Exception [FORMAT] diff --git a/Pipfile b/Pipfile index d8d10ed8a1..aa1c5a8c06 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,7 @@ verify_ssl = true mock = "*" pytest-cov = "*" jsonschema = "*" -pylint = "==2.15.9" +pylint = "*" pylint-mccabe = "*" pylint-quotes = "*" "beautifulsoup4" = "*" @@ -24,7 +24,7 @@ freezegun = "*" pytest-xdist = "*" fakeredis = "*" mypy = "*" -black = "==22.12.0" +black = "*" pytest-flask = "*" pytest = "*" pytest-sugar = "*" @@ -39,7 +39,7 @@ pytest-mock = "*" [packages] colorama = "*" flask = "*" -flask-babel = "==2.0.0" +flask-babel = "*" flask-login = "*" flask-wtf = "*" gevent = {version = "*",platform_python_implementation = "=='CPython'"} @@ -50,7 +50,7 @@ pika = "*" pyyaml = "*" requests = "*" sdc-cryptography = "*" -structlog = "==21.5.0" +structlog = "*" ua-parser = "*" blinker = "*" "boto3" = "*" diff --git a/Pipfile.lock b/Pipfile.lock index fd6c69bde1..1291e46a7f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e95ad34cf14946c58da90b3fe00c8f1b7d440eb8de0b9cb71dba47a1b07fdc0c" + "sha256": "65c2887dc528a01d63be44fc18d26e792c56501ddfb961c4cfb01a7ce7c925b7" }, "pipfile-spec": 6, "requires": { @@ -42,19 +42,19 @@ }, "boto3": { "hashes": [ - "sha256:5194c16e1f2371c25ee038e8291faf99117b4f2d272d70f48851ba47bcea44b4", - "sha256:78d75f6a08c33618d9ce37acca78e4f80e316a8ce3ac040c562f8054afe6451e" + "sha256:9e23ec51e1fe162ecb5efee41bd9346d90ebbb8ff094ef8088223305d48ffaec", + "sha256:9fc94b3078f5047c1fc40529fa12eb82a2432bb1dca0ee37b1c54f902f46191f" ], "index": "pypi", - "version": "==1.26.61" + "version": "==1.26.65" }, "botocore": { "hashes": [ - "sha256:22ead51f900e3465d0e4e670d02d091e613c88be8b333c47bfda727f8988eef3", - "sha256:c7a7133d02c5e9a8fcc9e5a238bf15bde2d5369366553520332a88bc05b7358a" + "sha256:6b7ae2dee621cb863935461ba4b8aa32420e945f5b7d94bb2852b79f90b9f8de", + "sha256:f566a9aa1b477cd1bdedb8f694f99fc257240eacdd7a73cc7d19d43cb20870b9" ], "markers": "python_version >= '3.7'", - "version": "==1.29.61" + "version": "==1.29.65" }, "brotli": { "hashes": [ @@ -409,11 +409,11 @@ }, "flask-babel": { "hashes": [ - "sha256:e6820a052a8d344e178cdd36dd4bb8aea09b4bda3d5f9fa9f008df2c7f2f5468", - "sha256:f9faf45cdb2e1a32ea2ec14403587d4295108f35017a7821a2b1acb8cfd9257d" + "sha256:ceb8c82039954a6b29da33ec5deb84878b78069d1ea628b21cac3f8233e9189c", + "sha256:d408cace25514bea8b92e898fd7e55877fbac79b71bc230e266ff515408eba38" ], "index": "pypi", - "version": "==2.0.0" + "version": "==3.0.1" }, "flask-compress": { "hashes": [ @@ -813,11 +813,11 @@ }, "humanize": { "hashes": [ - "sha256:127e333677183070b82e90e0faef9440f9a16dab92143e52f4523afb08ca9290", - "sha256:d6ed00ed4dc59a66df71012e3d69cf655d7d21b02112d435871998969e8aedc8" + "sha256:401201aca462749773f02920139f302450cb548b70489b9b4b92be39fe3c3c50", + "sha256:5f1f22bc65911eb1a6ffe7659bd6598e33dcfeeb904eb16ee1e705a09bf75916" ], "index": "pypi", - "version": "==4.5.0" + "version": "==4.6.0" }, "idna": { "hashes": [ @@ -1183,89 +1183,90 @@ }, "setuptools": { "hashes": [ - "sha256:a7687c12b444eaac951ea87a9627c4f904ac757e7abdc5aac32833234af90378", - "sha256:e261cdf010c11a41cb5cb5f1bf3338a7433832029f559a6a7614bd42a967c300" + "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c", + "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48" ], "markers": "python_version >= '3.7'", - "version": "==67.1.0" + "version": "==67.2.0" }, "simplejson": { "hashes": [ - "sha256:039b27f5a02b0919bea8af92bef1929ffd84ec98381f4fd72b6d29dd4742f7ee", - "sha256:04dddf0661b5e90110010d949421b2a34e2ee3aebb507c9a6a9847b8b0123223", - "sha256:05b907cdf2bfd5e8d24696f5b4ec7dcf98303ea0d2ca2e1544beabfdc1a60dcb", - "sha256:081a1e525bef7954d7e22b981aaed7af25866c6cd2213109319d007f8653ac18", - "sha256:0bc11f3957035b74b8c02cdcd8501bb04e0c03600d6407089171195e3f8ab311", - "sha256:0c1773f2eee5e8a4d74475caed96cb6364fbebeeafb5a915af7d93d9c0ac28af", - "sha256:0fd04f41446062c5b2f73d7fb95f26622329d7e4c676ed9b26133149ac110435", - "sha256:128b931ff9ef86ac252e7c2503fbdd951d2985874fcf7b69ed267ddaa5a17b58", - "sha256:188d3e1a20d11507af0606eed14accb72e629c0e28830feb487c6b0e134be625", - "sha256:1ae965e2906209a529f519edb1d101aa53c77208845973bc80343ae9cfb92297", - "sha256:1ba2f1e29b4c3bb1fb6a120df971e65380695149882c7edf90cfaf099998c9d9", - "sha256:1e8da2f4f87cc71f61e35ee7c1b8a7a6c8ff3ecdeac2e00abe4e49bf8a5a9cae", - "sha256:2869ae5d90655fd2f817411a54008107c3aca0a199cf978b60f8eea082ac5ca0", - "sha256:28908114288372941a8bf9cd98795bd9db527bcb4f6a782e73e18c410909f3d0", - "sha256:2b1eae836e388851d3d95260c113df66aeeedb1dd20e60dbc4439aa2b346903c", - "sha256:2d4646c075da38dd668177e192cd5a6fd42f06bdf722cc1622b768e26ed97611", - "sha256:2dec2152af65fffc469b49bab6b27898a8e0669b49fd1ade23fcaa2f98e0d4f8", - "sha256:32252907f6638f5781b182bda1326f63f53bfe549307eeba70c2ba5fafa9e5dd", - "sha256:3d2fbcd446bcdf7792dc53bde5b68091b69f1b94856db1fc35d904907b8fba96", - "sha256:44fb979ea0790c2818e36c739b401734930aeeb4df51d5b04a02ce4c18103326", - "sha256:487462150adf9fdbd2212b8cf04ad573a927e32a7a777b920a66cc5b2667bfce", - "sha256:4d9c6e9404a4508f929feb50fbe6ea50345fc72470a2bb748ebf0319180a4559", - "sha256:4f2bca68f02c2ca66cb97860307dd5abad405417a6f100f167d37b126a6bae93", - "sha256:4fa6b86bb10bec92b4ac566ef403d2bf31cccf89687556200086b6d3028a1b4f", - "sha256:5010b2657e03ea230766f789369708a5dff2aa0a29d54a5c92fdf851f6aae37b", - "sha256:514bd993830a2f975d2841607a1dae755c0417374fe9716f2d468a9fa98272ab", - "sha256:5627dd758ac5e3a2260b4d63f0ae1276c215e968dc73a77ff8cb99b814f04741", - "sha256:57b6ccc4f6676977901f65e6fb576e3d24e534e2066799669d528591b49ad405", - "sha256:5b06af5e5932f01922137e196708658284aa9333c753ac81d4dbe394bf15879c", - "sha256:64a3b477359ab66674e3b56e04c9c370e8df19161ecce260b94f7c8555adb8bf", - "sha256:652e2c36f6b186e52ff7e59a3e3cfedd9585fd19f133ec57c5b3e93fd2a633c1", - "sha256:66dae3897156b3654214e48cc807f66ed100d884b3fa1ac93fea34ff5998dfc9", - "sha256:6adb7667764d583f7c76eec446f07d337bbfd37edc9e79b702bd45f2a9b7d42d", - "sha256:70b92cd8a476b1ecefa3867669eac06fde3fff354a8cb9f91c45cd3ff706c0ee", - "sha256:746086e3ef6d74b53599df31b491d88a355abf2e31c837137dd90f8c4561cafa", - "sha256:766314378ba1eda1d5c2779ca7a79b16e7f438e266cdfff5e748e585d322643f", - "sha256:77742d20075fc5cb492c807616be100e725fc791c0ce116a3fe439e17035efed", - "sha256:78f4feb838b35035a01ece5e473797db0dd7df5fbc14ee7dd00f76b98160fb14", - "sha256:7a6479f76f10546faa2af5cba80855345822eaa2b294a48851086f4be2189bdb", - "sha256:7b76e623fd8553379a37e97219e51f66b0fca6bb9d6ed13da07bdb768f29d966", - "sha256:7e50ddacae67d9423309cc3eb04bda8161b2c5c49432b4a32c83575f603e78cc", - "sha256:802dfb04449be795f6ba8e082c3f3de14ea63fc10d6b699567632b6e457147d3", - "sha256:8061f5f4c1fbd1a6cb2174eb3ecd63e287a21ef59c4a1465f84315d541eaecc1", - "sha256:86e8cdb5136ac816a7237cba5e738ff28b6976fa0a0e0fef93ae31a097c1bf8b", - "sha256:876b8a1ac493342ac88ebcd6fbdf7442d4c8209d0fe01a8637840ae1e0e7ef32", - "sha256:884c1aa219cc0c7de2e82566f429eb8550b40c09a7de008acdc6fc2b99913eab", - "sha256:8c390830b16aecb53b1afb0c863aa4084e965731c2b08419c9e4e79667627b35", - "sha256:8e913e3a0818b178138658a84969b6d1f057be41a0eabdb0caf49ff4a577e060", - "sha256:91ed1b72edd36c68ec76e94ee4511048b2bd0b066963c1696c42a7a59a585a93", - "sha256:9608b83e2c3d0d86b618e1d8c18b12572b0c688f8d08c6cbc7f8639c731e8e0b", - "sha256:9bac4575a64bfc97890d10b6953800c397cda1d8c241eff9b4dbcb382a337532", - "sha256:9cb5c93c967189909723ee73e01df78cda868af868f824e4620483db52d2c028", - "sha256:a39ff00d2ba06c05b0130ee7b3a431e8a81356bf3a0008b98c41cf1bef3662ee", - "sha256:a63816ddad1fd3788cfdb31dacd4ee187205d9501a3c093560e2d50952ff5367", - "sha256:a8f6523a69a0fee56f65749487fd47b11489a98f1a913f35f3be778fefa45ff4", - "sha256:aaa8d8e799eb1fd1b985d7b13954908ecc861b26b11b9e047320caad0adb4476", - "sha256:affb755fda33d2db93c05674ef5a50ee0395fa0ae2d9d0156820cf57273a5f48", - "sha256:b0f9b98010145cab76d1d61ccd753e7f558128375cd63926d0d28718847f13fd", - "sha256:b34081b9b39e27fb7c8189a0b78ef8d9e1bb8a2238d8ab1cf7c5a17d517cad95", - "sha256:ba6ff90fc11a23236dd4ac3745dd58fa9673470ec1ac79df218ae0946acf702a", - "sha256:c4d899d2080cb91c9319a086852ec8d7bd0e4fb87fa9055f6200009fb247213b", - "sha256:c70b314b5ffc3b97239cf491b4b4f65c5882c9653359791914995d78aeb4791d", - "sha256:cac4b2af2b52585eb0c1c8fa5baff09856040a3e5575f93b3ce65136ca7840cc", - "sha256:cbb77592448dbd1641cedc2de4b374c45ac9db463c9ba467ef9960e8d5cd4c63", - "sha256:cd7b2e53f2fd34048b73f49e81c540f0735d7bd34518b36b47ecc770eb28eda5", - "sha256:d2b0ea9020454c6b9725289282ff8df0e38ef7d9e44613cb62c044cfde518b7f", - "sha256:dc534f8ca83b672774bca72850022fa8ae9e34e2f6fe401a655be823873fd14c", - "sha256:e153cd584d63aa9c70db25b7c094e15ec2dae804ab78291a1a8709be768dcaa2", - "sha256:eb7755521d3a6501e6a435430f5594f8c409b80525a5a027fd1e0d388d5170ee", - "sha256:ec3e189915167ac4c0f49f7916dde3387ad8dba74680342232c88baf68aa37fc", - "sha256:ee8a49e42fed15e53ea4bf584ade744de1be251f26e9d8d25af856ab7b6d50b7", - "sha256:fdf064e57bcaadf7df7402bce22101103b9c7ff4f9615c8fa5e9027828abaa1d" - ], - "index": "pypi", - "version": "==3.18.1" + "sha256:04a4b9a297cccbc9e1d66fe652fbffd55b36d6579c43132e821d315957302194", + "sha256:063db62a9251e61ea0c17e49c3e7bed465bfcc5359655abcb8c0bc6130a4e0d4", + "sha256:070ab073ce72f1624107dfd6d095c87ac32aafe7ba54a5c5055a3dd83cb06e51", + "sha256:099bbd3b5b4ea83159a980348cd481a34984dee5fe1b9fac31a9137158f46960", + "sha256:0baf8c60efef74944ed4adb034d14bcf737731576f0e4c3c56fb875ea256af69", + "sha256:0e7c3fae6c9540064e06a653780b4f263675cd69ca6841345029fee3e27e9bb5", + "sha256:141782a0a25c1792627575b37b4951583358ccc7137623aa45947f8425ee8d96", + "sha256:14b35fb90083218e59df5dba733c7086655f2938f3fcabe36ad849623941d660", + "sha256:169c2c7446ef33439c304a6aa5b7b5a2dbc938c9c2dd882dd3f2553f9518ebf6", + "sha256:16cc750d19852fa5ebafd55da86fa357f87991e07b4e2afb37a5975dfdde0153", + "sha256:1907d49d70c75530976119c13785db91168d2599288debaca7d25da9cd2f3747", + "sha256:1b79e2607ac5ba98381c2e068727acc1e4dd385a6d216914c0613f8f568a06a5", + "sha256:1e49c84df6e71e3c23169d3df481565dd607cbee4aa1e0af15c493cccad7c745", + "sha256:23fce984045804194f513a2739dcd82be350198470d5ade5058da019a48cf3f8", + "sha256:24823364fee93bab141621b3a2e10612e31be7ca58788bf9b2cd2b1ce37ab07d", + "sha256:290bbcdcbb37af3f7e43378f592ab7a9168fca640da6af63d42cdb535f96bbf2", + "sha256:2a1b3222bc8f6ac91b5ebe3263111c7dc4dc4b01c52f0153f5bb1f3ef3bf0023", + "sha256:2b0f6de11f5ce4b80f51bc49d08b898602e190547f8efe4e44af8ae3cda7779d", + "sha256:2be75f4cb9951efeb2616e16f944ee4f9a09768475a3f5c40a6ac4dc5ee68dfd", + "sha256:2c7ee643ee93684bf76196e2d84a2090c6df8f01737a016e869b579593827b6e", + "sha256:37bdef13412c0bc338db2993a38f3911d5bd2a0ba8d00b3bc66d1063edd7c33e", + "sha256:3bab9ea49ff477c926c5787f79ec47cf51c7ffb15c9d8dd0f09e728807d44f4b", + "sha256:44d6c52d4f5c0c087a6e88a92bf9f94234321d21be32c6471ba39856e304bbe3", + "sha256:4b8d4d958c5ab3489d1174917a7fad82da642560c39ce559a624e63deaaa36b1", + "sha256:4de9fed1166aeedee44150fa83bc059aca6b612940281f8b5a39374781f16196", + "sha256:502d86fbfe914263642479b87ed61af3b27b9e039df77acd2416cfccfc892e68", + "sha256:508342d7227ed66beecfbba7a38b46e1a713faeb034216f43f03ec5c175e0622", + "sha256:50f4b6d52f3a2d1cffd11834a1fe7f9516f0e3f20cbe78027aa88ff990fad7d6", + "sha256:52465a5578cfc2c5e374a574df14dfb75e04c6cb6a100b7abc8bf6c89bea8f5e", + "sha256:55aa983575b0aef143845f5bfbb35075475eccaebf7d4b30f4037a2fe8414666", + "sha256:55df3dfd8777bf134e1078d2f195352432a77f23ccb90b92b08218123d56adc9", + "sha256:56f186d44a9f625b5e5d9ba4b9551e263604000a7df60cb373b3e789ca603b2a", + "sha256:5780e3929435a8d39671537174f8ce0ccafb4f6e0c748ffe139916ffbdca39d3", + "sha256:59a629240cfbc5b4f390a8578dca74ae77ab617de971862acb946822d2eb1b11", + "sha256:5b009342e712026ffabe8a471d5b4a4ff2a038687387e74eae601574c04dae33", + "sha256:62628ea5df8c830d00a7417d5ecd949a1b24a8d0a5063a2a77f7ec7522110a0f", + "sha256:694332fd6fd10fe8868c2508583220d1a1a7be9ff049dab5bd6b9aedfb9edc50", + "sha256:6a49665169c18f27a0fc10935466332ee7406ee14ced8dc0a1b4d465547299aa", + "sha256:6b997739fdbc9b7030ff490fc8e5f8c144b8ec80f3605eff643983672bb8cfde", + "sha256:6bd81d10cb3384f64242316da8a2b2f88618776bc1ef38bcc79f1afe8ad36616", + "sha256:6c4c56c5abb82e22877b913186e5c0fd7d9eef0c930719e28fa451d3f11defb4", + "sha256:6fe1173b4146641c872bafa6f9a21f3a2012f502d54fbb523a76e6320024fae9", + "sha256:75eb555dc349d0cbe2c95ea2be665b306c6ac6d5b64e3a3920af9b805ecdb5f7", + "sha256:7c26fe63755ecc59c502ddde8e58ce8b765bf4fdd3f5858d2b7c8ab28bc2a9c8", + "sha256:7e73d9d6af3c29b60a92e28b3144d951110f59a3d05fc402c3f6c5248b883400", + "sha256:7ff65b475091084e5bdb7f26e9c555956be7355b573ce494fa96f9f8e34541ac", + "sha256:8209c40279ed9b2cd5fbe2d617a29a074e90ea97fce7c07a0128a01cb3e8afc5", + "sha256:88f59a07873dc1f06fd9e6712dd71286f1b297a066ad2fd9110ad080d3cb011c", + "sha256:96ade36640734b54176c4765d00a60767bd7fae5b7a5b3574accc055ac18e34c", + "sha256:9cf299fbb7d476676dfea372a3262654af98694bd1df35b060ce0fe1b68087f1", + "sha256:a2960b95f3ba822d077d1afa7e1fea9799cfb2990028cf010e666f64195ecb5a", + "sha256:a80bd9a3db88a76a401155c64e3499376c702307c2206cb381cc2a8dd9cc4f1f", + "sha256:aad323e92cb1bd3b1db6f57c007dca964d13c52247ad844203ce381e94066601", + "sha256:ab5bdf0b8d07f7fd603b2d0c1982412cd9f8ade997088ddced251f7e656c7fd4", + "sha256:b0352428b35da859a98770949e7353866ae65463026f1c8e4c89a6395d4b5fd7", + "sha256:b2c4e8b65987f3c6529149495d28e23efe213e94dc3659176c4ab22d18a9ee4a", + "sha256:bcd9eac304a133ee4af58e68c5ded4c5ba663d3ee4602e8613359b776a1f8c8f", + "sha256:c3b696770b504f881f271f97b94a687487ec1ef20bfbd5f20d92bbab7a85952d", + "sha256:c4514675f6571da8190fea52a110bca686fa844972e8b2b3bc07ace9e632ee4f", + "sha256:c98fddc374468158778a8afb3fd7296412a2b2fc34cebba64212ac3e018e7382", + "sha256:cde5a3ff5e0bd5d6da676314dfae86c9e99bff77bca03d30223c9718a58f9e83", + "sha256:cf7168b2046db0eceb83d8ed2ee31c0847ce18b2d8baf3e93de9560f3921a8c3", + "sha256:d774782159347d66563cd7ac18b9dd37010438a825160cde4818caa18110a746", + "sha256:d990ea42ba908cb57a3df97d283aa26c1822f10a0a60e250b54ee21cd08c48d0", + "sha256:e762e9d8556fa9f3a99f8a278eeba50a35b5f554b82deeb282ddbdd85816e638", + "sha256:e8a4750e8db92109e6f1f7783a7faae4254d6d5dc28a41ff7eff7d2265f0586b", + "sha256:eb81cfef0c0039010f0212f4e5eb6909641b8a54c761584054ac97fd7bd0c21a", + "sha256:ebb53837c5ffcb6100646018565d3f1afed6f4b185b14b2c9cbccf874fe40157", + "sha256:efa70fd9b6c7b57b048ecadb909683acd535cddebc5b22f3c05ba3b369739caf", + "sha256:f73bae5e315adf7bc8cb7f0a13a1e9e33bead42e8ce174be83ac9ecc2513c86a", + "sha256:f89f078114cacedb9a3392615cc099cf02a51efa7507f90e2006bf7ec38c880d", + "sha256:f9f72d2b539512f382a48cc9ad6cea2d3a572e71e92c40e03d2140041eeaa233", + "sha256:fc8df5831b645e96a318ea51a66ce6e2bb869eebc3fa9a860bbf67aecd270055" + ], + "index": "pypi", + "version": "==3.18.3" }, "six": { "hashes": [ @@ -1277,11 +1278,11 @@ }, "structlog": { "hashes": [ - "sha256:68c4c29c003714fe86834f347cb107452847ba52414390a7ee583472bde00fc9", - "sha256:fd7922e195262b337da85c2a91c84be94ccab1f8fd1957bd6986f6904e3761c8" + "sha256:b403f344f902b220648fa9f286a23c0cc5439a5844d271fec40562dbadbc70ad", + "sha256:e7509391f215e4afb88b1b80fa3ea074be57a5a17d794bd436a5c949da023333" ], "index": "pypi", - "version": "==21.5.0" + "version": "==22.3.0" }, "ua-parser": { "hashes": [ @@ -1445,11 +1446,11 @@ "develop": { "astroid": { "hashes": [ - "sha256:6891f444625b6edb2ac798829b689e95297e100ddf89dbed5a8c610e34901501", - "sha256:df164d5ac811b9f44105a72b8f9d5edfb7b5b2d7e979b04ea377a77b3229114a" + "sha256:23c718921acab5f08cbbbe9293967f1f8fec40c336d19cd75dc12a9ea31d2eb2", + "sha256:bd1aa4f9915c98e8aaebcd4e71930154d4e8c9aaf05d35ac0a63d1956091ae3f" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.13.5" + "version": "==2.14.1" }, "async-timeout": { "hashes": [ @@ -1477,37 +1478,50 @@ }, "black": { "hashes": [ - "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320", - "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351", - "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350", - "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f", - "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf", - "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148", - "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4", - "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d", - "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc", - "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d", - "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2", - "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f" - ], - "index": "pypi", - "version": "==22.12.0" + "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd", + "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555", + "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481", + "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468", + "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9", + "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a", + "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958", + "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580", + "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26", + "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32", + "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8", + "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753", + "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b", + "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074", + "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651", + "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24", + "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6", + "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad", + "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac", + "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221", + "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06", + "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27", + "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648", + "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739", + "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104" + ], + "index": "pypi", + "version": "==23.1.0" }, "boto3": { "hashes": [ - "sha256:5194c16e1f2371c25ee038e8291faf99117b4f2d272d70f48851ba47bcea44b4", - "sha256:78d75f6a08c33618d9ce37acca78e4f80e316a8ce3ac040c562f8054afe6451e" + "sha256:9e23ec51e1fe162ecb5efee41bd9346d90ebbb8ff094ef8088223305d48ffaec", + "sha256:9fc94b3078f5047c1fc40529fa12eb82a2432bb1dca0ee37b1c54f902f46191f" ], "index": "pypi", - "version": "==1.26.61" + "version": "==1.26.65" }, "botocore": { "hashes": [ - "sha256:22ead51f900e3465d0e4e670d02d091e613c88be8b333c47bfda727f8988eef3", - "sha256:c7a7133d02c5e9a8fcc9e5a238bf15bde2d5369366553520332a88bc05b7358a" + "sha256:6b7ae2dee621cb863935461ba4b8aa32420e945f5b7d94bb2852b79f90b9f8de", + "sha256:f566a9aa1b477cd1bdedb8f694f99fc257240eacdd7a73cc7d19d43cb20870b9" ], "markers": "python_version >= '3.7'", - "version": "==1.29.61" + "version": "==1.29.65" }, "certifi": { "hashes": [ @@ -1803,11 +1817,11 @@ }, "fakeredis": { "hashes": [ - "sha256:3f8f72c5b990a70ae69444dd1cfe0c9805811a53752d3d21fdcda29255b00e51", - "sha256:94c7867edc259b81c55b93902e81a727cc1986ba09de3fcf9df51a3ad8e29b03" + "sha256:854d758794dab9953be16b9a0f7fbd4bbd6b6964db7a9684e163291c1342ece6", + "sha256:e2f7a88dad23be1191ad6212008e170d75c2d63dde979c2694be8cbfd917428e" ], "index": "pypi", - "version": "==2.6.0" + "version": "==2.7.1" }, "flake8": { "hashes": [ @@ -2051,54 +2065,51 @@ }, "moto": { "hashes": [ - "sha256:6c45755e541a85e5382f809c75255d0b6335de334e7e8eb717f8fdc755830a8c", - "sha256:c4df7ae86f7cdd8ffcb2fcf55a45a7ab9f854ec631f1b9c82b1f47ae8c033326" + "sha256:1b361ece638c74a657325378a259276f368aafce2f8be84f8143e69fa93ce8ec", + "sha256:63431733d2a02c7bd652ad71ec1da442a0e0d580cbac5eeb50d440a2ce066eac" ], "index": "pypi", - "version": "==4.1.1" + "version": "==4.1.2" }, "mypy": { "hashes": [ - "sha256:0714258640194d75677e86c786e80ccf294972cc76885d3ebbb560f11db0003d", - "sha256:0c8f3be99e8a8bd403caa8c03be619544bc2c77a7093685dcf308c6b109426c6", - "sha256:0cca5adf694af539aeaa6ac633a7afe9bbd760df9d31be55ab780b77ab5ae8bf", - "sha256:1c8cd4fb70e8584ca1ed5805cbc7c017a3d1a29fb450621089ffed3e99d1857f", - "sha256:1f7d1a520373e2272b10796c3ff721ea1a0712288cafaa95931e66aa15798813", - "sha256:209ee89fbb0deed518605edddd234af80506aec932ad28d73c08f1400ef80a33", - "sha256:26efb2fcc6b67e4d5a55561f39176821d2adf88f2745ddc72751b7890f3194ad", - "sha256:37bd02ebf9d10e05b00d71302d2c2e6ca333e6c2a8584a98c00e038db8121f05", - "sha256:3a700330b567114b673cf8ee7388e949f843b356a73b5ab22dd7cff4742a5297", - "sha256:3c0165ba8f354a6d9881809ef29f1a9318a236a6d81c690094c5df32107bde06", - "sha256:3d80e36b7d7a9259b740be6d8d906221789b0d836201af4234093cae89ced0cd", - "sha256:4175593dc25d9da12f7de8de873a33f9b2b8bdb4e827a7cae952e5b1a342e243", - "sha256:4307270436fd7694b41f913eb09210faff27ea4979ecbcd849e57d2da2f65305", - "sha256:5e80e758243b97b618cdf22004beb09e8a2de1af481382e4d84bc52152d1c476", - "sha256:641411733b127c3e0dab94c45af15fea99e4468f99ac88b39efb1ad677da5711", - "sha256:652b651d42f155033a1967739788c436491b577b6a44e4c39fb340d0ee7f0d70", - "sha256:6d7464bac72a85cb3491c7e92b5b62f3dcccb8af26826257760a552a5e244aa5", - "sha256:74e259b5c19f70d35fcc1ad3d56499065c601dfe94ff67ae48b85596b9ec1461", - "sha256:7d17e0a9707d0772f4a7b878f04b4fd11f6f5bcb9b3813975a9b13c9332153ab", - "sha256:901c2c269c616e6cb0998b33d4adbb4a6af0ac4ce5cd078afd7bc95830e62c1c", - "sha256:98e781cd35c0acf33eb0295e8b9c55cdbef64fcb35f6d3aa2186f289bed6e80d", - "sha256:a12c56bf73cdab116df96e4ff39610b92a348cc99a1307e1da3c3768bbb5b135", - "sha256:ac6e503823143464538efda0e8e356d871557ef60ccd38f8824a4257acc18d93", - "sha256:b8472f736a5bfb159a5e36740847808f6f5b659960115ff29c7cecec1741c648", - "sha256:b86ce2c1866a748c0f6faca5232059f881cda6dda2a893b9a8373353cfe3715a", - "sha256:bc9ec663ed6c8f15f4ae9d3c04c989b744436c16d26580eaa760ae9dd5d662eb", - "sha256:c9166b3f81a10cdf9b49f2d594b21b31adadb3d5e9db9b834866c3258b695be3", - "sha256:d13674f3fb73805ba0c45eb6c0c3053d218aa1f7abead6e446d474529aafc372", - "sha256:de32edc9b0a7e67c2775e574cb061a537660e51210fbf6006b0b36ea695ae9bb", - "sha256:e62ebaad93be3ad1a828a11e90f0e76f15449371ffeecca4a0a0b9adc99abcef" - ], - "index": "pypi", - "version": "==0.991" + "sha256:01b1b9e1ed40544ef486fa8ac022232ccc57109f379611633ede8e71630d07d2", + "sha256:0ab090d9240d6b4e99e1fa998c2d0aa5b29fc0fb06bd30e7ad6183c95fa07593", + "sha256:14d776869a3e6c89c17eb943100f7868f677703c8a4e00b3803918f86aafbc52", + "sha256:1ace23f6bb4aec4604b86c4843276e8fa548d667dbbd0cb83a3ae14b18b2db6c", + "sha256:2efa963bdddb27cb4a0d42545cd137a8d2b883bd181bbc4525b568ef6eca258f", + "sha256:2f6ac8c87e046dc18c7d1d7f6653a66787a4555085b056fe2d599f1f1a2a2d21", + "sha256:3ae4c7a99e5153496243146a3baf33b9beff714464ca386b5f62daad601d87af", + "sha256:3cfad08f16a9c6611e6143485a93de0e1e13f48cfb90bcad7d5fde1c0cec3d36", + "sha256:4e5175026618c178dfba6188228b845b64131034ab3ba52acaffa8f6c361f805", + "sha256:50979d5efff8d4135d9db293c6cb2c42260e70fb010cbc697b1311a4d7a39ddb", + "sha256:5cd187d92b6939617f1168a4fe68f68add749902c010e66fe574c165c742ed88", + "sha256:5cfca124f0ac6707747544c127880893ad72a656e136adc935c8600740b21ff5", + "sha256:5e398652d005a198a7f3c132426b33c6b85d98aa7dc852137a2a3be8890c4072", + "sha256:67cced7f15654710386e5c10b96608f1ee3d5c94ca1da5a2aad5889793a824c1", + "sha256:7306edca1c6f1b5fa0bc9aa645e6ac8393014fa82d0fa180d0ebc990ebe15964", + "sha256:7cc2c01dfc5a3cbddfa6c13f530ef3b95292f926329929001d45e124342cd6b7", + "sha256:87edfaf344c9401942883fad030909116aa77b0fa7e6e8e1c5407e14549afe9a", + "sha256:8845125d0b7c57838a10fd8925b0f5f709d0e08568ce587cc862aacce453e3dd", + "sha256:92024447a339400ea00ac228369cd242e988dd775640755fa4ac0c126e49bb74", + "sha256:a86b794e8a56ada65c573183756eac8ac5b8d3d59daf9d5ebd72ecdbb7867a43", + "sha256:bb2782a036d9eb6b5a6efcdda0986774bf798beef86a62da86cb73e2a10b423d", + "sha256:be78077064d016bc1b639c2cbcc5be945b47b4261a4f4b7d8923f6c69c5c9457", + "sha256:c7cf862aef988b5fbaa17764ad1d21b4831436701c7d2b653156a9497d92c83c", + "sha256:e0626db16705ab9f7fa6c249c017c887baf20738ce7f9129da162bb3075fc1af", + "sha256:f34495079c8d9da05b183f9f7daec2878280c2ad7cc81da686ef0b484cea2ecf", + "sha256:fe523fcbd52c05040c7bee370d66fee8373c5972171e4fbc323153433198592d" + ], + "index": "pypi", + "version": "==1.0.0" }, "mypy-extensions": { "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" ], - "version": "==0.4.3" + "markers": "python_version >= '3.5'", + "version": "==1.0.0" }, "packaging": { "hashes": [ @@ -2126,11 +2137,11 @@ }, "platformdirs": { "hashes": [ - "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490", - "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2" + "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9", + "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567" ], "markers": "python_version >= '3.7'", - "version": "==2.6.2" + "version": "==3.0.0" }, "pluggy": { "hashes": [ @@ -2165,11 +2176,11 @@ }, "pylint": { "hashes": [ - "sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4", - "sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb" + "sha256:bad9d7c36037f6043a1e848a43004dfd5ea5ceb05815d713ba56ca4503a9fe37", + "sha256:ffe7fa536bb38ba35006a7c8a6d2efbfdd3d95bbf21199cad31f76b1c50aaf30" ], "index": "pypi", - "version": "==2.15.9" + "version": "==2.16.1" }, "pylint-mccabe": { "hashes": [ @@ -2351,7 +2362,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_full_version < '3.11.0a7'", + "markers": "python_version < '3.11'", "version": "==2.0.1" }, "tomlkit": { @@ -2379,27 +2390,27 @@ }, "types-pyyaml": { "hashes": [ - "sha256:17ce17b3ead8f06e416a3b1d5b8ddc6cb82a422bb200254dd8b469434b045ffc", - "sha256:879700e9f215afb20ab5f849590418ab500989f83a57e635689e1d50ccc63f0c" + "sha256:ade6e328a5a3df816c47c912c2e1e946ae2bace90744aa73111ee6834b03a314", + "sha256:de3bacfc4e0772d9b1baf007c37354f3c34c8952e90307d5155b6de0fc183a67" ], "index": "pypi", - "version": "==6.0.12.3" + "version": "==6.0.12.4" }, "types-redis": { "hashes": [ - "sha256:802e893ad3f88e03d3a2feb0d23a715d60b0bb330bc598a52f1de237fc2547a5", - "sha256:b70829ca3401d3153d628e28d860070eff1b36b2fa3e5af3e583c1d167383cab" + "sha256:57f8b3706afe47ef36496d70a97a3783560e6cb19e157be12985dbb31de1d853", + "sha256:8b40d6bf3a54352d4cb2aa7d01294c572a39d40a9d289b96bdf490b51d3a42d2" ], "index": "pypi", - "version": "==4.4.0.4" + "version": "==4.4.0.6" }, "types-requests": { "hashes": [ - "sha256:61960554baca0008ae7e2db2bd3b322ca9a144d3e80ce270f5fb640817e40994", - "sha256:e67424525f84adfbeab7268a159d3c633862dafae15c5b19547ce1b55954f0a3" + "sha256:19622ace35a5da1838ee9cad0df4a50c7e3a420f8a37e8357ce870fed492fa81", + "sha256:b4adf81c6bcd2f9fa0f6ab8669e96fad7f04429d6e22c486b2b3382d729ca364" ], "index": "pypi", - "version": "==2.28.11.8" + "version": "==2.28.11.11" }, "types-simplejson": { "hashes": [ @@ -2411,17 +2422,17 @@ }, "types-toml": { "hashes": [ - "sha256:171bdb3163d79a520560f24ba916a9fc9bff81659c5448a9fea89240923722be", - "sha256:b7b5c4977f96ab7b5ac06d8a6590d17c0bf252a96efc03b109c2711fb3e0eafd" + "sha256:a2286a053aea6ab6ff814659272b1d4a05d86a1dd52b807a87b23511993b46c5", + "sha256:f37244eff4cd7eace9cb70d0bac54d3eba77973aa4ef26c271ac3d1c6503a48e" ], - "version": "==0.10.8.1" + "version": "==0.10.8.3" }, "types-urllib3": { "hashes": [ - "sha256:ed6b9e8a8be488796f72306889a06a3fc3cb1aa99af02ab8afb50144d7317e49", - "sha256:eec5556428eec862b1ac578fb69aab3877995a99ffec9e5a12cf7fbd0cc9daee" + "sha256:5630e578246d170d91ebe3901788cd28d53c4e044dc2e2488e3b0d55fb6895d8", + "sha256:e8f25c8bb85cde658c72ee931e56e7abd28803c26032441eea9ff4a4df2b0c31" ], - "version": "==1.26.25.4" + "version": "==1.26.25.5" }, "typing-extensions": { "hashes": [ diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index e5bbb5ee9a..0d508d56e5 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -7,7 +7,7 @@ from flask import session as cookie_session from flask_login import LoginManager, user_logged_out from sdc.crypto.decrypter import decrypt -from structlog import get_logger +from structlog import contextvars, get_logger from app.authentication.no_token_exception import NoTokenException from app.authentication.user import User @@ -109,7 +109,7 @@ def load_user(extend_session: bool = True) -> Optional[User]: user = User(user_id, user_ik) if metadata := get_metadata(user): - logger.bind(tx_id=metadata.tx_id) + contextvars.bind_contextvars(tx_id=metadata.tx_id) if extend_session: _extend_session_expiry(session_store) diff --git a/app/cloud_tasks/cloud_task_publishers.py b/app/cloud_tasks/cloud_task_publishers.py index 7778cdedeb..32a7212f9e 100644 --- a/app/cloud_tasks/cloud_task_publishers.py +++ b/app/cloud_tasks/cloud_task_publishers.py @@ -53,7 +53,6 @@ def create_task( function_name: str, fulfilment_request_transaction_id: str, ) -> Task: - parent = self._client.queue_path(self._project_id, "europe-west2", queue_name) try: task = self._create_task_with_retry(body, function_name, parent) diff --git a/app/forms/field_handlers/date_handlers.py b/app/forms/field_handlers/date_handlers.py index cecd104d5f..5ea2d6bc1d 100644 --- a/app/forms/field_handlers/date_handlers.py +++ b/app/forms/field_handlers/date_handlers.py @@ -27,7 +27,6 @@ class DateHandler(FieldHandler): @cached_property def validators(self) -> DateValidatorTypes: - validate_with: DateValidatorTypes = [OptionalForm()] if self.answer_schema["mandatory"] is True: diff --git a/app/forms/fields/decimal_field_with_separator.py b/app/forms/fields/decimal_field_with_separator.py index 3790f2fbba..666540a56a 100644 --- a/app/forms/fields/decimal_field_with_separator.py +++ b/app/forms/fields/decimal_field_with_separator.py @@ -21,7 +21,6 @@ def __init__(self, **kwargs): self.data = None def process_formdata(self, valuelist): - if valuelist: try: self.data = Decimal( diff --git a/app/forms/fields/integer_field_with_separator.py b/app/forms/fields/integer_field_with_separator.py index 812e807a5c..7a3098a8c7 100644 --- a/app/forms/fields/integer_field_with_separator.py +++ b/app/forms/fields/integer_field_with_separator.py @@ -19,7 +19,6 @@ def __init__(self, **kwargs): self.data = None def process_formdata(self, valuelist): - if valuelist: try: self.data = int( diff --git a/app/forms/fields/multiple_select_field_with_detail_answer.py b/app/forms/fields/multiple_select_field_with_detail_answer.py index 9b1db7385a..619ec0b646 100644 --- a/app/forms/fields/multiple_select_field_with_detail_answer.py +++ b/app/forms/fields/multiple_select_field_with_detail_answer.py @@ -11,9 +11,12 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def __iter__(self): - opts = dict( - widget=self.option_widget, name=self.name, _form=None, _meta=self.meta - ) + opts = { + "widget": self.option_widget, + "name": self.name, + "_form": None, + "_meta": self.meta, + } for i, (value, label, checked, detail_answer_id) in enumerate( self.iter_choices() ): diff --git a/app/forms/fields/select_field_with_detail_answer.py b/app/forms/fields/select_field_with_detail_answer.py index 87040c935f..5a5299939a 100644 --- a/app/forms/fields/select_field_with_detail_answer.py +++ b/app/forms/fields/select_field_with_detail_answer.py @@ -12,9 +12,12 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def __iter__(self): - opts = dict( - widget=self.option_widget, name=self.name, _form=None, _meta=self.meta - ) + opts = { + "widget": self.option_widget, + "name": self.name, + "_form": None, + "_meta": self.meta, + } for i, (value, label, checked, detail_answer_id) in enumerate( self.iter_choices() ): diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 255de37df4..30e37641e0 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -186,7 +186,6 @@ def _get_target_total_and_currency( calculation: Calculation, question: QuestionSchemaType, ) -> Optional[tuple[Union[Calculation, AnswerValueTypes], Optional[str]]]: - calculation_value: Union[Calculation, AnswerValueTypes] currency: Optional[str] @@ -222,7 +221,7 @@ def validate_date_range_with_period_limits_and_single_date_limits( if period_min and period_range < self._get_offset_value(period_min): exception = f"The schema has invalid period_limits for {question_id}" - raise Exception(exception) + raise ValueError(exception) @staticmethod def validate_date_range_with_single_date_limits( @@ -233,7 +232,7 @@ def validate_date_range_with_single_date_limits( exception = f"The schema has invalid date answer limits for {question_id}" if period_range < timedelta(0): - raise Exception(exception) + raise ValueError(exception) def _validate_date_range_question( self, @@ -299,7 +298,6 @@ def _get_period_range_for_single_date( date_from: Mapping[str, dict], date_to: Mapping[str, dict], ) -> timedelta: - list_item_id = self.location.list_item_id if self.location else None value_source_resolver = ValueSourceResolver( answer_store=self.answer_store, diff --git a/app/forms/validators.py b/app/forms/validators.py index 4fd7ab9c2f..9366b54126 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -136,13 +136,14 @@ def validate_minimum(self, value: NumType) -> Optional[str]: return None if self.minimum_exclusive and value <= self.minimum: - return self.messages["NUMBER_TOO_SMALL_EXCLUSIVE"] % dict( - min=format_playback_value(self.minimum, self.currency) - ) + return self.messages["NUMBER_TOO_SMALL_EXCLUSIVE"] % { + "min": format_playback_value(self.minimum, self.currency) + } + if value < self.minimum: - return self.messages["NUMBER_TOO_SMALL"] % dict( - min=format_playback_value(self.minimum, self.currency) - ) + return self.messages["NUMBER_TOO_SMALL"] % { + "min": format_playback_value(self.minimum, self.currency) + } return None @@ -151,13 +152,13 @@ def validate_maximum(self, value: NumType) -> Optional[str]: return None if self.maximum_exclusive and value >= self.maximum: - return self.messages["NUMBER_TOO_LARGE_EXCLUSIVE"] % dict( - max=format_playback_value(self.maximum, self.currency) - ) + return self.messages["NUMBER_TOO_LARGE_EXCLUSIVE"] % { + "max": format_playback_value(self.maximum, self.currency) + } if value > self.maximum: - return self.messages["NUMBER_TOO_LARGE"] % dict( - max=format_playback_value(self.maximum, self.currency) - ) + return self.messages["NUMBER_TOO_LARGE"] % { + "max": format_playback_value(self.maximum, self.currency) + } return None @@ -189,7 +190,7 @@ def __call__( raise validators.ValidationError(self.messages["INVALID_INTEGER"]) if len(data.split(decimal_symbol)[1]) > self.max_decimals: raise validators.ValidationError( - self.messages["INVALID_DECIMAL"] % dict(max=self.max_decimals) + self.messages["INVALID_DECIMAL"] % {"max": self.max_decimals} ) @@ -286,21 +287,21 @@ def __call__(self, form: "QuestionnaireForm", field: StringField) -> None: if self.minimum_date and date and date < self.minimum_date: raise validators.ValidationError( self.messages["SINGLE_DATE_PERIOD_TOO_EARLY"] - % dict( - min=self._format_playback_date( + % { + "min": self._format_playback_date( self.minimum_date + relativedelta(days=-1), self.date_format ) - ) + } ) if self.maximum_date and date and date > self.maximum_date: raise validators.ValidationError( self.messages["SINGLE_DATE_PERIOD_TOO_LATE"] - % dict( - max=self._format_playback_date( + % { + "max": self._format_playback_date( self.maximum_date + relativedelta(days=+1), self.date_format ) - ) + } ) @staticmethod @@ -339,7 +340,7 @@ def __call__( ): raise validators.ValidationError( self.messages["DATE_PERIOD_TOO_SMALL"] - % dict(min=self._build_range_length_error(self.period_min)) + % {"min": self._build_range_length_error(self.period_min)} ) if self.period_max: @@ -349,7 +350,7 @@ def __call__( ): raise validators.ValidationError( self.messages["DATE_PERIOD_TOO_LARGE"] - % dict(max=self._build_range_length_error(self.period_max)) + % {"max": self._build_range_length_error(self.period_max)} ) @staticmethod @@ -412,7 +413,7 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: - raise Exception( + raise ValueError( "There are multiple conditions, but equals is not one of them. " "We only support <= and >=" ) from exc @@ -426,7 +427,7 @@ def __call__( if not is_valid: raise validators.ValidationError( self.messages[message] - % dict(total=format_playback_value(target_total, self.currency)) + % {"total": format_playback_value(target_total, self.currency)} ) @staticmethod @@ -474,7 +475,6 @@ def __call__( is_mandatory: bool, is_only_checkboxes_or_radios: bool, ) -> None: - total_answered = sum( value not in QuestionnaireStoreUpdater.EMPTY_ANSWER_VALUES for value in answer_values diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 5f4775558e..a37a8526c5 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -91,7 +91,6 @@ def context(self) -> dict[str, Any]: def service_links_context( self, ) -> Optional[dict[str, Union[dict[str, str], list[dict]]]]: - ru_ref = ( metadata["ru_ref"] if (metadata := get_metadata(current_user)) else None ) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 1d1b6fae92..a52d6d4248 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -182,20 +182,20 @@ def get_format_date_range(start_date: Markup, end_date: Markup) -> Markup: @blueprint.app_context_processor -def format_unit_processor() -> dict[ - str, Callable[[str, Union[int, Decimal], str], str] -]: - return dict(format_unit=format_unit) +def format_unit_processor() -> ( + dict[str, Callable[[str, Union[int, Decimal], str], str]] +): + return {"format_unit": format_unit} @blueprint.app_context_processor def format_unit_input_label_processor() -> dict[str, Callable]: - return dict(format_unit_input_label=format_unit_input_label) + return {"format_unit_input_label": format_unit_input_label} @blueprint.app_context_processor def get_currency_symbol_processor() -> dict[str, Callable]: - return dict(get_currency_symbol=get_currency_symbol) + return {"get_currency_symbol": get_currency_symbol} @blueprint.app_template_filter() # type: ignore @@ -375,7 +375,7 @@ def map_select_config(form: FormType, answer: AnswerType) -> list[SelectConfig]: @blueprint.app_context_processor def map_select_config_processor() -> dict[str, Callable]: - return dict(map_select_config=map_select_config) + return {"map_select_config": map_select_config} @blueprint.app_template_filter() # type: ignore @@ -391,7 +391,7 @@ def map_relationships_config( @blueprint.app_context_processor def map_relationships_config_processor() -> dict[str, Callable]: - return dict(map_relationships_config=map_relationships_config) + return {"map_relationships_config": map_relationships_config} class DropdownConfig: @@ -410,7 +410,7 @@ def map_dropdown_config(select: SelectFieldBase._Option) -> list[DropdownConfig] @blueprint.app_context_processor def map_dropdown_config_processor() -> dict[str, Callable]: - return dict(map_dropdown_config=map_dropdown_config) + return {"map_dropdown_config": map_dropdown_config} class SummaryAction: @@ -453,7 +453,6 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche edit_link_aria_label: str, summary_type: str, ) -> None: - if "type" in answer: answer_type = answer["type"] else: @@ -576,7 +575,6 @@ def map_summary_item_config( calculated_question: Optional[dict[str, list]], icon: Optional[str] = None, ) -> list[Union[dict[str, list], SummaryRow]]: - rows: list[Union[dict[str, list], SummaryRow]] = [] for block in group["blocks"]: @@ -614,7 +612,7 @@ def map_summary_item_config( @blueprint.app_context_processor def map_summary_item_config_processor() -> dict[str, Callable]: - return dict(map_summary_item_config=map_summary_item_config) + return {"map_summary_item_config": map_summary_item_config} # pylint: disable=too-many-locals @@ -711,4 +709,4 @@ def map_list_collector_config( @blueprint.app_context_processor def map_list_collector_config_processor() -> dict[str, Callable]: - return dict(map_list_collector_config=map_list_collector_config) + return {"map_list_collector_config": map_list_collector_config} diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index ad3aa04e0d..52312c7b33 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -85,7 +85,6 @@ def _get_not_skipped_blocks_in_section( if section: not_skipped_blocks: list[Mapping] = [] for group in section["groups"]: - if "skip_conditions" in group: skip_conditions = group.get("skip_conditions") if self.evaluate_skip_conditions( @@ -282,7 +281,6 @@ def _get_next_block_id(self, rule): def _remove_current_blocks_answers_for_backwards_routing( self, rules: dict, this_location: Location ) -> None: - if block_id := this_location.block_id: answer_ids_for_current_block = self.schema.get_answer_ids_for_block( block_id diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index a21df30c82..fc3c174e32 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -47,7 +47,6 @@ def __init__( list_item_id: Optional[str] = None, location: Union[Location, RelationshipLocation, None] = None, ): - self._answer_store = answer_store self._list_store = list_store self._metadata = metadata diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 927af03b2c..d8e0b0d98a 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -59,15 +59,12 @@ def format_date(self, date_to_format: str, date_format: str) -> str: @staticmethod def format_list(list_to_format: Sequence[str]) -> str: - formatted_list = "
      " - for item in list_to_format: - formatted_list += f"
    • {item}
    • " + formatted_list = "
        " + "".join(f"
      • {item}
      • " for item in list_to_format) formatted_list += "
      " return formatted_list @staticmethod def remove_empty_from_list(list_to_filter: Sequence[str]) -> list[str]: - """ :param list_to_filter: anything that is iterable :return: a list with no empty values @@ -154,7 +151,6 @@ def format_unit( @staticmethod def calculate_date_difference(first_date: str, second_date: str) -> str: - time = relativedelta( parse_datetime(second_date), parse_datetime(first_date), @@ -248,7 +244,6 @@ def add(lhs: Union[int, Decimal], rhs: Union[int, Decimal]) -> Union[int, Decima def format_ordinal( self, number_to_format: int, determiner: Optional[str] = None ) -> str: - indicator = self.get_ordinal_indicator(number_to_format) if determiner == "a_or_an" and self.language in ["en", "eo"]: diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 0755bf10ac..4699871ffb 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -85,7 +85,6 @@ def update_relationship_question_completeness(self, list_name: str) -> None: list_items = self._list_store[list_name] for collector in relationship_collectors: - relationship_answer_id = self._schema.get_first_answer_id_for_block( collector["id"] ) @@ -341,12 +340,12 @@ def update_answers( def update_progress_for_dependent_sections(self) -> None: """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. - Section progress is not updated for the current location as it is handled by `handle_post` on block handlers.""" + Section progress is not updated for the current location as it is handled by `handle_post` on block handlers. + """ self._remove_dependent_blocks_and_capture_dependent_sections() for section in self.dependent_sections: - if ( section.section_id, section.list_item_id, diff --git a/app/questionnaire/when_rules.py b/app/questionnaire/when_rules.py index 142b7e7d28..7eec2bde06 100644 --- a/app/questionnaire/when_rules.py +++ b/app/questionnaire/when_rules.py @@ -65,7 +65,6 @@ def evaluate_condition(condition, answer_value, match_value): answer_and_match = answer_value is not None and match_value is not None if condition in {"equals", "not equals", "equals any", "not equals any"}: - answer_value = casefold(answer_value) if isinstance(match_value, (list, tuple)): @@ -228,7 +227,7 @@ def _get_when_rule_value( elif "list" in when_rule: value = get_list_count(list_store, when_rule["list"]) else: - raise Exception("The when rule is invalid") + raise NotImplementedError("The when rule is invalid") return value @@ -254,7 +253,6 @@ def evaluate_when_rules( :return: True if the when condition has been met otherwise False """ for when_rule in when_rules: - list_item_id = current_location.list_item_id if current_location else None value = _get_when_rule_value( diff --git a/app/routes/errors.py b/app/routes/errors.py index 14f3523a14..b5b011a38e 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -6,7 +6,7 @@ from flask_login import current_user from flask_wtf.csrf import CSRFError from sdc.crypto.exceptions import InvalidTokenException -from structlog import get_logger +from structlog import contextvars, get_logger from werkzeug.exceptions import BadRequestKeyError from app.authentication.no_questionnaire_state_exception import ( @@ -36,7 +36,7 @@ def log_exception(exception, status_code): if metadata := get_metadata(current_user): - logger.bind(tx_id=metadata.tx_id) + contextvars.bind_contextvars(tx_id=metadata.tx_id) log = logger.warning if status_code < 500 else logger.error diff --git a/app/routes/flush.py b/app/routes/flush.py index 9ebf20505d..924439c2e3 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -3,7 +3,7 @@ from flask import Blueprint, Response, current_app, request, session from sdc.crypto.decrypter import decrypt from sdc.crypto.encrypter import encrypt -from structlog import get_logger +from structlog import contextvars, get_logger from app.authentication.auth_payload_version import AuthPayloadVersion from app.authentication.user import User @@ -49,7 +49,7 @@ def flush_data(): user = _get_user(decrypted_token["response_id"]) if metadata := get_metadata(user): - logger.bind(tx_id=metadata.tx_id) + contextvars.bind_contextvars(tx_id=metadata.tx_id) if _submit_data(user): return Response(status=200) return Response(status=404) diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index 648dc5452f..9cc39d9049 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -2,7 +2,7 @@ from flask_babel import get_locale, lazy_gettext from flask_login import current_user, login_required from itsdangerous import BadSignature -from structlog import get_logger +from structlog import contextvars, get_logger from werkzeug.exceptions import BadRequest from app.authentication.no_questionnaire_state_exception import ( @@ -50,16 +50,16 @@ def before_individual_response_request(): if questionnaire_store.submitted_at: return redirect(url_for("post_submission.get_thank_you")) - logger.bind( + contextvars.bind_contextvars( tx_id=metadata.tx_id, ce_id=metadata.collection_exercise_sid, ) if schema_name := metadata.schema_name: - logger.bind(schema_name=schema_name) + contextvars.bind_contextvars(schema_name=schema_name) if schema_url := metadata.schema_url: - logger.bind(schema_url=schema_url) # pragma: no cover + contextvars.bind_contextvars(schema_url=schema_url) # pragma: no cover logger.info( "individual-response request", method=request.method, url_path=request.full_path diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 4d3710f8e4..3c6fadc435 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -9,7 +9,7 @@ from flask_babel import get_locale from flask_login import current_user, login_required from itsdangerous import BadSignature -from structlog import get_logger +from structlog import contextvars, get_logger from werkzeug import Response from werkzeug.exceptions import BadRequest, NotFound @@ -85,16 +85,16 @@ def before_questionnaire_request(): if questionnaire_store.submitted_at: return redirect(url_for("post_submission.get_thank_you")) - logger.bind( + contextvars.bind_contextvars( tx_id=metadata.tx_id, ce_id=metadata.collection_exercise_sid, ) if schema_name := metadata.schema_name: - logger.bind(schema_name=schema_name) + contextvars.bind_contextvars(schema_name=schema_name) if schema_url := metadata.schema_url: - logger.bind(schema_url=schema_url) + contextvars.bind_contextvars(schema_url=schema_url) logger.info( "questionnaire request", method=request.method, url_path=request.full_path @@ -132,13 +132,13 @@ def before_post_submission_request(): metadata=metadata, language_code=get_locale().language ) - logger.bind(tx_id=metadata.tx_id) + contextvars.bind_contextvars(tx_id=metadata.tx_id) if schema_name := metadata.schema_name: - logger.bind(schema_name=schema_name) + contextvars.bind_contextvars(schema_name=schema_name) if schema_url := metadata.schema_url: - logger.bind(schema_url=schema_url) + contextvars.bind_contextvars(schema_url=schema_url) logger.info( "questionnaire request", method=request.method, url_path=request.full_path diff --git a/app/routes/session.py b/app/routes/session.py index 1a1d5bf6d8..d3d803dc14 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -6,7 +6,7 @@ from flask_login import login_required, logout_user from marshmallow import INCLUDE, ValidationError from sdc.crypto.exceptions import InvalidTokenException -from structlog import get_logger +from structlog import contextvars, get_logger from werkzeug.exceptions import Unauthorized from app.authentication.auth_payload_version import AuthPayloadVersion @@ -105,7 +105,7 @@ def login(): }.items() if value } - logger.bind(**logger_args) + contextvars.bind_contextvars(**logger_args) logger.info("decrypted token and parsed metadata") diff --git a/app/secrets.py b/app/secrets.py index 338e38a3a6..840f6047cb 100644 --- a/app/secrets.py +++ b/app/secrets.py @@ -22,7 +22,7 @@ def validate_required_secrets( ) for required_secret in all_required_secrets: if required_secret not in secrets["secrets"]: - raise Exception(f"Missing Secret [{required_secret}]") + raise ValueError(f"Missing Secret [{required_secret}]") class SecretStore: diff --git a/app/settings.py b/app/settings.py index b5922ffe75..d5e212698f 100644 --- a/app/settings.py +++ b/app/settings.py @@ -37,14 +37,14 @@ def read_file(file_name): def get_env_or_fail(key): value = os.getenv(key) if value is None: - raise Exception(f"Setting '{key}' Missing") + raise ValueError(f"Setting '{key}' Missing") return value def utcoffset_or_fail(date_value, key): if date_value.utcoffset() is None: - raise Exception(f"'{key}' datetime offset missing") + raise ValueError(f"'{key}' datetime offset missing") return date_value diff --git a/app/setup.py b/app/setup.py index 70653943e0..451f5edce5 100644 --- a/app/setup.py +++ b/app/setup.py @@ -17,7 +17,7 @@ from htmlmin.main import minify from jinja2 import ChainableUndefined from sdc.crypto.key_store import KeyStore, validate_required_keys -from structlog import get_logger +from structlog import contextvars, get_logger from app import settings from app.authentication.authenticator import login_manager @@ -87,6 +87,10 @@ logger = get_logger() +class MissingEnvironmentVariable(Exception): + pass + + class AWSReverseProxied: def __init__(self, app): self.app = app @@ -131,12 +135,15 @@ def create_app( # noqa: C901 pylint: disable=too-complex, too-many-statements # request will use the logger context of the previous request. @application.before_request def before_request(): # pylint: disable=unused-variable + contextvars.clear_contextvars() + request_id = str(uuid4()) - logger.new(request_id=request_id) + + contextvars.bind_contextvars(request_id=request_id) span, trace = get_span_and_trace(flask_request.headers) if span and trace: - logger.bind(span=span, trace=trace) + contextvars.bind_contextvars(span=span, trace=trace) logger.info( "request", @@ -286,7 +293,7 @@ def setup_storage(application): elif application.config["EQ_STORAGE_BACKEND"] == "dynamodb": setup_dynamodb(application) else: - raise Exception("Unknown EQ_STORAGE_BACKEND") + raise NotImplementedError("Unknown EQ_STORAGE_BACKEND") setup_redis(application) @@ -323,10 +330,10 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": - bucket_name = application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID") - - if not bucket_name: - raise Exception("Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing") + if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): + raise MissingEnvironmentVariable( + "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" + ) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) @@ -335,9 +342,11 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - raise Exception("Setting EQ_RABBITMQ_HOST Missing") + raise MissingEnvironmentVariable("Setting EQ_RABBITMQ_HOST Missing") if not secondary_host: - raise Exception("Setting EQ_RABBITMQ_HOST_SECONDARY Missing") + raise MissingEnvironmentVariable( + "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" + ) application.eq["submitter"] = RabbitMQSubmitter( host=host, @@ -356,7 +365,7 @@ def setup_submitter(application): application.eq["submitter"] = LogSubmitter() else: - raise Exception("Unknown EQ_SUBMISSION_BACKEND") + raise NotImplementedError("Unknown EQ_SUBMISSION_BACKEND") def setup_task_client(application): @@ -365,7 +374,7 @@ def setup_task_client(application): elif application.config["EQ_SUBMISSION_CONFIRMATION_BACKEND"] == "log": application.eq["cloud_tasks"] = LogCloudTaskPublisher() else: - raise Exception("Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND") + raise NotImplementedError("Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND") def setup_publisher(application): @@ -376,15 +385,15 @@ def setup_publisher(application): application.eq["publisher"] = LogPublisher() else: - raise Exception("Unknown EQ_PUBLISHER_BACKEND") + raise NotImplementedError("Unknown EQ_PUBLISHER_BACKEND") def setup_feedback(application): if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": - bucket_name = application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID") - - if not bucket_name: - raise Exception("Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing") + if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): + raise MissingEnvironmentVariable( + "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" + ) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( bucket_name=bucket_name @@ -393,7 +402,7 @@ def setup_feedback(application): elif application.config["EQ_FEEDBACK_BACKEND"] == "log": application.eq["feedback_submitter"] = LogFeedbackSubmitter() else: - raise Exception("Unknown EQ_FEEDBACK_BACKEND") + raise NotImplementedError("Unknown EQ_FEEDBACK_BACKEND") def add_blueprints(application): @@ -439,19 +448,9 @@ def setup_babel(application): application.babel = Babel(application) application.jinja_env.add_extension("jinja2.ext.i18n") - @application.babel.localeselector - def get_locale(): # pylint: disable=unused-variable - session = get_session_store() - - if session and (session_data := session.session_data): - return session_data.language_code - - return None - - @application.babel.timezoneselector - def get_timezone(): # pylint: disable=unused-variable - # For now regardless of locale we will show times in GMT/BST - return "Europe/London" + application.babel.init_app( + application, locale_selector=get_locale, timezone_selector=get_timezone + ) def setup_compression(application): @@ -478,3 +477,17 @@ def get_minimized_asset(filename): elif "js" in filename: filename = filename.replace(".js", ".min.js") return filename + + +def get_locale(): + session = get_session_store() + + if session and (session_data := session.session_data): + return session_data.language_code + + return None + + +def get_timezone(): + # For now regardless of locale we will show times in GMT/BST + return "Europe/London" diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 78151dd946..84bacd55b1 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -114,7 +114,6 @@ def build_collection(metadata: MetadataProxy) -> MetadataType: def build_metadata(metadata: MetadataProxy) -> MetadataType: - downstream_metadata = { "user_id": metadata["user_id"], "ru_ref": metadata["ru_ref"], diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py index 1b06a6032b..8522bed2e6 100644 --- a/app/survey_config/census_config.py +++ b/app/survey_config/census_config.py @@ -84,7 +84,6 @@ class WelshCensusSurveyConfig( account_service_log_out_url: str = f"{base_url}/en/start" def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [ Link( lazy_gettext("Help"), @@ -150,7 +149,6 @@ class CensusNISRASurveyConfig( _is_nisra: bool = True def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [ Link( lazy_gettext("Help"), diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 88734703b4..a84e01f4a2 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -102,7 +102,6 @@ def get_allowed_languages(schema_name, launch_language): def load_schema_from_metadata( metadata: MetadataProxy, *, language_code: str ) -> QuestionnaireSchema: - if metadata and (schema_url := metadata.schema_url): # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 922dd4e52a..a4e516f3b0 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -49,7 +49,6 @@ def build_groups_for_section( def build_view_context_for_calculated_summary( self, current_location: Location ) -> dict[str, dict[str, Any]]: - # type ignores added as block will exist at this point block_id: str = current_location.block_id # type: ignore block: ImmutableDict = self._schema.get_block(block_id) # type: ignore @@ -83,7 +82,7 @@ def build_view_context_for_calculated_summary( "calculated_question": self._get_calculated_question( calculation, formatted_total ), - "title": block_title % dict(total=formatted_total), + "title": block_title % {"total": formatted_total}, "collapsible": collapsible, "summary_type": "CalculatedSummary", } @@ -247,7 +246,6 @@ def _get_calculated_question( calculation_question: ImmutableDict[str, Any], formatted_total: str, ) -> dict[str, Any]: - calculation_title = calculation_question["title"] return { diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 09b3908227..01d9ce1242 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -72,7 +72,6 @@ def section(self) -> ImmutableDict: return section def get_page_title(self, title_for_location: Union[Mapping, str]) -> str: - section_repeating_page_title = ( self._schema.get_repeating_page_title_for_section( self.current_location.section_id diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index ae64c7e212..735425464e 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -75,7 +75,6 @@ def _build_blocks_and_links( language: str, return_to_block_id: Optional[str], ) -> list[dict[str, Block]]: - blocks = [] for block in group_schema["blocks"]: diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 8651f562a6..3aad850ebf 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -141,7 +141,6 @@ def _add_link( summary: Mapping[str, Any], list_collector_block: Optional[Mapping[str, Any]], ) -> Optional[str]: - if list_collector_block: return url_for( "questionnaire.block", @@ -162,7 +161,6 @@ def _add_link( def _get_related_answers( self, list_model: ListModel ) -> Optional[dict[str, list[dict[str, Any]]]]: - section_id = self._section["id"] related_answers = self._schema.get_related_answers_for_list_for_section( diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 6a7063c7e8..de26e19e53 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -72,7 +72,6 @@ def _build_answers( return_to: Optional[str], return_to_block_id: Optional[str], ) -> list[dict[str, Any]]: - if self.summary: answer_id = f"{self.id}-concatenated-answer" link = url_for( @@ -125,7 +124,6 @@ def _build_answers( def _concatenate_answers( self, answer_store: AnswerStore, concatenation_type: str ) -> str: - answer_separators = {"Newline": "
      ", "Space": " "} answer_separator = answer_separators.get(concatenation_type, " ") @@ -229,7 +227,6 @@ def _build_radio_answer( answer_schema: Mapping[str, Any], answer_store: AnswerStore, ) -> Optional[RadioCheckboxTypes]: - for option in self.get_answer_options(answer_schema): if answer == escape(option["value"]): detail_answer_value = self._get_detail_answer_value( diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index dce01d96e9..ed7db2bcb5 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -12,7 +12,6 @@ class SummaryContext(Context): def __call__( self, answers_are_editable: bool = False, return_to: Optional[str] = None ) -> dict[str, Union[str, list, bool]]: - groups = list(self._build_all_groups(return_to)) summary_options = self._schema.get_summary_options() collapsible = summary_options.get("collapsible", False) diff --git a/app/views/handlers/__init__.py b/app/views/handlers/__init__.py index 1cd2ffd907..13e41d3c8d 100644 --- a/app/views/handlers/__init__.py +++ b/app/views/handlers/__init__.py @@ -25,7 +25,6 @@ def individual_response_url( journey: Optional[str] = None, ) -> Union[str, None]: if individual_response_for_list: - if ( list_item_id != questionnaire_store.list_store[ diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index 2dfd07e7cb..da0e7b5f68 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -47,7 +47,6 @@ def __init__( serialized_email, form_data: Mapping, ): - if not ConfirmationEmail.is_enabled(schema): raise ConfirmationEmailNotEnabled diff --git a/app/views/handlers/confirmation_email.py b/app/views/handlers/confirmation_email.py index 2651480b9c..46112bd095 100644 --- a/app/views/handlers/confirmation_email.py +++ b/app/views/handlers/confirmation_email.py @@ -29,7 +29,6 @@ def __init__( page_title: Optional[str] = None, serialised_email: Optional[str] = None, ): - if not self.is_enabled(schema): raise ConfirmationEmailNotEnabled diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index b735ca9600..39e20e56d2 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -133,7 +133,6 @@ def handle_post(self) -> None: @cached_property def question_schema(self) -> Mapping[str, Union[str, list]]: - return { "type": "General", "id": "feedback", diff --git a/application.py b/application.py index 7b128606ff..8f28c2e004 100755 --- a/application.py +++ b/application.py @@ -3,11 +3,10 @@ import os import sys -from structlog import configure +from structlog import configure, contextvars from structlog.dev import ConsoleRenderer from structlog.processors import JSONRenderer, TimeStamper, format_exc_info from structlog.stdlib import LoggerFactory, add_log_level -from structlog.threadlocal import wrap_dict from app.utilities.json import json_dumps @@ -46,6 +45,7 @@ def parse_exception(_, __, event_dict): ConsoleRenderer() if debug else JSONRenderer(serializer=json_dumps) ) processors = [ + contextvars.merge_contextvars, add_log_level, TimeStamper(key="created", fmt="iso"), add_service, @@ -55,7 +55,6 @@ def parse_exception(_, __, event_dict): ] configure( - context_class=wrap_dict(dict), logger_factory=LoggerFactory(), processors=processors, cache_logger_on_first_use=True, diff --git a/profile_application.py b/profile_application.py index e19d344540..82e847a30d 100644 --- a/profile_application.py +++ b/profile_application.py @@ -8,7 +8,6 @@ def setup_profiling(application): - profiling_dir = "profiling" if os.path.exists(profiling_dir): diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index ecc3d9b4af..50905ab2f7 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -10,7 +10,6 @@ @pytest.fixture def basic_answer_store(answer_store): - answer_store.add_or_update( Answer(answer_id="answer1", value=10, list_item_id="abc123") ) @@ -40,7 +39,6 @@ def basic_answer_store(answer_store): @pytest.fixture def relationship_answer_store(answer_store): - answer_store.add_or_update( Answer( answer_id="relationship-answer", @@ -69,7 +67,6 @@ def relationship_answer_store(answer_store): @pytest.fixture def store_to_serialize(answer_store): - answer_store.add_or_update( Answer(answer_id="answer1", value=10, list_item_id="abc123") ) diff --git a/tests/app/data_model/test_session_store.py b/tests/app/data_model/test_session_store.py index 6a0d391a4e..60cc3bb05d 100644 --- a/tests/app/data_model/test_session_store.py +++ b/tests/app/data_model/test_session_store.py @@ -153,7 +153,6 @@ def test_session_store_stores_none_for_language_code_value( @pytest.mark.usefixtures("app") def test_legacy_load(app_session_store_encoded): - _save_session( app_session_store_encoded, app_session_store_encoded.session_id, diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 8e3be23de3..85d931cbfc 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1077,7 +1077,6 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocker): - answer_total = Answer(answer_id="total-answer", value=10) answer_store.add_or_update(answer_total) @@ -1285,7 +1284,6 @@ def test_calculated_field( errors_text, value_dict, ): # pylint: disable=too-many-locals - for answer in answers: answer_store.add_or_update(answer) @@ -1321,7 +1319,6 @@ def test_calculated_field( def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_validation_error( app, answer_store, mocker ): - list_store = ListStore([{"name": "people", "items": ["lCIZsS"]}]) answer_store.add_or_update( Answer( diff --git a/tests/app/forms/validation/test_single_date_range_validator.py b/tests/app/forms/validation/test_single_date_range_validator.py index 530a53db92..a6a56f173a 100644 --- a/tests/app/forms/validation/test_single_date_range_validator.py +++ b/tests/app/forms/validation/test_single_date_range_validator.py @@ -27,7 +27,6 @@ def test_single_date_period_invalid_raises_ValidationError( validator, data, error_type, error_message, mock_form, mock_field ): - mock_form.data = data with pytest.raises(ValidationError) as exc: diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 64cdffada7..a3df6d8360 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -776,7 +776,6 @@ def test_survey_config_base_url_provided_used_in_links( ] if survey_config_type == SocialSurveyConfig: - urls_to_check.remove(result.contact_us_url) for url in urls_to_check: diff --git a/tests/app/mock_data_store.py b/tests/app/mock_data_store.py index c233d32f0b..c0e105e400 100644 --- a/tests/app/mock_data_store.py +++ b/tests/app/mock_data_store.py @@ -4,7 +4,6 @@ class MockDatastore: - # pylint: disable=unused-argument def __init__(self, **kwargs): self.storage = {} diff --git a/tests/app/questionnaire/test_dynamic_answer_options.py b/tests/app/questionnaire/test_dynamic_answer_options.py index 78bcad596f..0154079dd2 100644 --- a/tests/app/questionnaire/test_dynamic_answer_options.py +++ b/tests/app/questionnaire/test_dynamic_answer_options.py @@ -40,7 +40,6 @@ def value_source_resolver(mock_schema, response_metadata): def test_dynamic_answer_options(rule_evaluator, value_source_resolver): - dynamic_options = DynamicAnswerOptions( { "values": { diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index 19db35c138..a658370323 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -239,7 +239,6 @@ def test_add_primary_person( populated_list_store, mocker, ): - mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], @@ -578,7 +577,6 @@ def test_update_answers_with_answer_dependents( updated_answer_value, expected_output, ): - answer_store = AnswerStore( [ AnswerDict(answer_id="first-answer", value="original answer"), @@ -842,7 +840,6 @@ def test_answer_id_section_dependents_repeating( mock_schema, mock_router, ): - mock_schema.get_repeating_list_for_section.return_value = "list-name" mock_schema.get_answer_ids_for_question.return_value = ["first-answer"] mock_schema.when_rules_section_dependencies_by_answer = { diff --git a/tests/app/questionnaire/test_relationship_location.py b/tests/app/questionnaire/test_relationship_location.py index 77af655622..ed1d981f1b 100644 --- a/tests/app/questionnaire/test_relationship_location.py +++ b/tests/app/questionnaire/test_relationship_location.py @@ -14,20 +14,15 @@ def test_location_url(): ) location_url = location.url() - assert ( - location_url - == "http://test.localdomain/questionnaire/relationships/household/id1/to/id2/", - ) - assert ( - location.for_json() - == { - "section_id": "household", - "block_id": "relationships", - "list_item_id": "id1", - "to_list_item_id": "id2", - "list_name": "household", - }, - ) + assert location_url == "/questionnaire/relationships/household/id1/to/id2/" + + assert location.for_json() == { + "section_id": "household", + "block_id": "relationships", + "list_item_id": "id1", + "to_list_item_id": "id2", + "list_name": "household", + } def test_create_location_from_dict(): diff --git a/tests/app/questionnaire/test_routing_path.py b/tests/app/questionnaire/test_routing_path.py index da64c2b54d..a6cb6cde77 100644 --- a/tests/app/questionnaire/test_routing_path.py +++ b/tests/app/questionnaire/test_routing_path.py @@ -2,7 +2,6 @@ def test_eq_to_routing_path(block_ids, routing_path): - assert routing_path == RoutingPath( block_ids, section_id="section-1", diff --git a/tests/app/questionnaire/test_when_rules.py b/tests/app/questionnaire/test_when_rules.py index 44de91c5ca..aee9ec288b 100644 --- a/tests/app/questionnaire/test_when_rules.py +++ b/tests/app/questionnaire/test_when_rules.py @@ -510,7 +510,6 @@ def test_when_rule_comparing_answer_values( questionnaire_schema, current_location, ): - for answer in answers.values(): answer_store.add_or_update(answer) diff --git a/tests/app/storage/test_datastore.py b/tests/app/storage/test_datastore.py index 3a320545ac..f6f90b9972 100644 --- a/tests/app/storage/test_datastore.py +++ b/tests/app/storage/test_datastore.py @@ -40,7 +40,6 @@ def test_get_not_found(datastore, mock_client): @pytest.mark.usefixtures("app") def test_put(datastore, mock_client, questionnaire_state): - datastore.put(questionnaire_state, True) put_data = mock_client.put.call_args[0][0] @@ -57,7 +56,6 @@ def test_put(datastore, mock_client, questionnaire_state): @pytest.mark.usefixtures("app") def test_put_without_overwrite(datastore, questionnaire_state): - with pytest.raises(NotImplementedError) as exc: datastore.put(questionnaire_state, False) @@ -97,7 +95,6 @@ def test_delete(datastore, mock_client, questionnaire_state): @pytest.mark.usefixtures("app") def test_retry(datastore, mock_client, mocker, questionnaire_state): - mock_client.put = mocker.Mock( side_effect=[exceptions.InternalServerError("error"), mocker.DEFAULT] ) diff --git a/tests/app/submitter/test_submitter.py b/tests/app/submitter/test_submitter.py index e18027a66e..5756f9b783 100644 --- a/tests/app/submitter/test_submitter.py +++ b/tests/app/submitter/test_submitter.py @@ -359,7 +359,6 @@ def test_double_submission_passes_when_delete_operation_error( def test_double_submission_is_forbidden_when_not_delete_operation_error( patch_gcs_client, gcs_blob_create_forbidden ): # pylint: disable=redefined-outer-name - # Given gcs_submitter = GCSSubmitter(bucket_name="test_bucket") diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index 47507bed85..8f21774458 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -774,7 +774,6 @@ def test_radio_answer_with_detail_answers_returns_correct_value( def test_answer_types_selected_option_label( answer_type, options, answers, expected, answer_store, list_store, mock_schema ): - for answer in answers: answer_store.add_or_update(answer) diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 57b1af69fc..8e93a640e9 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -1011,7 +1011,6 @@ def process_section_summary( logger.info("creating %s...", page_path) with open(page_path, "w", encoding="utf-8") as page_spec: - section_context = { "pageName": generate_pascal_case_from_id(section_id), "basePage": "SubmitBasePage", diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 43d59b5cc4..44f0f08709 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -385,13 +385,11 @@ def assertNotInSelector(self, content, selector): self.assertFalse(content in str(data), msg=message) def assertNotInPage(self, content, message=None): - self.assertNotIn( member=str(content), container=self.getResponseData(), msg=str(message) ) def assertRegexPage(self, regex, message=None): - self.assertRegex( text=self.getResponseData(), expected_regex=str(regex), msg=str(message) ) diff --git a/tests/integration/routes/test_questionnaire.py b/tests/integration/routes/test_questionnaire.py index d24eef44b6..9bad635b05 100644 --- a/tests/integration/routes/test_questionnaire.py +++ b/tests/integration/routes/test_questionnaire.py @@ -53,3 +53,26 @@ def test_options_request_before_request(self): for output in logs.output: self.assertNotIn("questionnaire request", output) + + def test_get_request_logs_output(self): + self.launchSurvey("test_hub_and_spoke") + with self.assertLogs() as logs: + self.get("/questionnaire/") + self.assertStatusOK() + + request_log = logs.output[0] + questionnaire_request_log = logs.output[1] + + self.assertNotIn("tx_id", request_log) + self.assertNotIn("ce_id", request_log) + self.assertNotIn("schema_name", request_log) + self.assertIn("url_path", request_log) + self.assertIn("request_id", request_log) + self.assertIn("method", request_log) + + self.assertIn("tx_id", questionnaire_request_log) + self.assertIn("ce_id", questionnaire_request_log) + self.assertIn("schema_name", questionnaire_request_log) + self.assertIn("url_path", questionnaire_request_log) + self.assertIn("request_id", questionnaire_request_log) + self.assertIn("method", questionnaire_request_log) diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index a0e78a1540..a547908dfa 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -70,17 +70,17 @@ def test_adds_i18n_to_application(self): self.assertIsInstance(babel, Babel) def test_adds_logging_of_request_ids(self): - with patch("app.setup.logger") as logger: + with patch("structlog.contextvars.bind_contextvars") as bind_contextvars: self._setting_overrides.update({"EQ_APPLICATION_VERSION": False}) application = create_app(self._setting_overrides) application.test_client().get("/") - self.assertEqual(1, logger.new.call_count) - _, kwargs = logger.new.call_args + self.assertEqual(1, bind_contextvars.call_count) + _, kwargs = bind_contextvars.call_args self.assertTrue(UUID(kwargs["request_id"], version=4)) def test_adds_logging_of_span_and_trace(self): - with patch("app.setup.logger") as logger: + with patch("structlog.contextvars.bind_contextvars") as bind_contextvars: self._setting_overrides.update({"EQ_APPLICATION_VERSION": False}) application = create_app(self._setting_overrides) @@ -89,8 +89,8 @@ def test_adds_logging_of_span_and_trace(self): } application.test_client().get("/", headers=x_cloud_headers) - self.assertEqual(1, logger.bind.call_count) - _, kwargs = logger.bind.call_args + self.assertEqual(2, bind_contextvars.call_count) + _, kwargs = bind_contextvars.call_args self.assertTrue(kwargs["span"] == "0123456789012345678901") self.assertTrue(kwargs["trace"] == "0123456789") From cc279537384666c19e76460d7aae42386a5c82c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Feb 2023 11:35:43 +0000 Subject: [PATCH 168/567] Bump cryptography from 39.0.0 to 39.0.1 (#1022) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Pipfile.lock | 174 +++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 1291e46a7f..53ccf6d636 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -50,11 +50,11 @@ }, "botocore": { "hashes": [ - "sha256:6b7ae2dee621cb863935461ba4b8aa32420e945f5b7d94bb2852b79f90b9f8de", - "sha256:f566a9aa1b477cd1bdedb8f694f99fc257240eacdd7a73cc7d19d43cb20870b9" + "sha256:77fff109e1bdbf030d8400ccab9d28454c03c7be62c1696a239b21792b0873df", + "sha256:8710f53af0e20f08f36a3bf434d18bc7ceba5d9835495b02aedbedd35df5de9a" ], "markers": "python_version >= '3.7'", - "version": "==1.29.65" + "version": "==1.29.72" }, "brotli": { "hashes": [ @@ -348,32 +348,32 @@ }, "cryptography": { "hashes": [ - "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b", - "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f", - "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190", - "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f", - "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f", - "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb", - "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c", - "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773", - "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72", - "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8", - "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717", - "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9", - "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856", - "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96", - "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288", - "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39", - "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e", - "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce", - "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1", - "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de", - "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df", - "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf", - "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458" - ], - "markers": "python_version >= '3.6'", - "version": "==39.0.0" + "sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4", + "sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f", + "sha256:4789d1e3e257965e960232345002262ede4d094d1a19f4d3b52e48d4d8f3b885", + "sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502", + "sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41", + "sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965", + "sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e", + "sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc", + "sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad", + "sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505", + "sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388", + "sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6", + "sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2", + "sha256:c5caeb8188c24888c90b5108a441c106f7faa4c4c075a2bcae438c6e8ca73cef", + "sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac", + "sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695", + "sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6", + "sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336", + "sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0", + "sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c", + "sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106", + "sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a", + "sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8" + ], + "index": "pypi", + "version": "==39.0.1" }, "deprecated": { "hashes": [ @@ -388,7 +388,7 @@ "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9", "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.3.0" }, "email-validator": { @@ -1183,11 +1183,11 @@ }, "setuptools": { "hashes": [ - "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c", - "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48" + "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012", + "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48" ], "markers": "python_version >= '3.7'", - "version": "==67.2.0" + "version": "==67.3.2" }, "simplejson": { "hashes": [ @@ -1309,11 +1309,11 @@ }, "werkzeug": { "hashes": [ - "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", - "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" + "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe", + "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612" ], "markers": "python_version >= '3.7'", - "version": "==2.2.2" + "version": "==2.2.3" }, "wrapt": { "hashes": [ @@ -1446,11 +1446,11 @@ "develop": { "astroid": { "hashes": [ - "sha256:23c718921acab5f08cbbbe9293967f1f8fec40c336d19cd75dc12a9ea31d2eb2", - "sha256:bd1aa4f9915c98e8aaebcd4e71930154d4e8c9aaf05d35ac0a63d1956091ae3f" + "sha256:0e0e3709d64fbffd3037e4ff403580550f14471fd3eaae9fa11cc9a5c7901153", + "sha256:a3cf9f02c53dd259144a7e8f3ccd75d67c9a8c716ef183e0c1f291bc5d7bb3cf" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.14.1" + "version": "==2.14.2" }, "async-timeout": { "hashes": [ @@ -1517,11 +1517,11 @@ }, "botocore": { "hashes": [ - "sha256:6b7ae2dee621cb863935461ba4b8aa32420e945f5b7d94bb2852b79f90b9f8de", - "sha256:f566a9aa1b477cd1bdedb8f694f99fc257240eacdd7a73cc7d19d43cb20870b9" + "sha256:77fff109e1bdbf030d8400ccab9d28454c03c7be62c1696a239b21792b0873df", + "sha256:8710f53af0e20f08f36a3bf434d18bc7ceba5d9835495b02aedbedd35df5de9a" ], "markers": "python_version >= '3.7'", - "version": "==1.29.65" + "version": "==1.29.72" }, "certifi": { "hashes": [ @@ -1764,32 +1764,32 @@ }, "cryptography": { "hashes": [ - "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b", - "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f", - "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190", - "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f", - "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f", - "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb", - "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c", - "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773", - "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72", - "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8", - "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717", - "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9", - "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856", - "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96", - "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288", - "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39", - "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e", - "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce", - "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1", - "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de", - "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df", - "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf", - "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458" - ], - "markers": "python_version >= '3.6'", - "version": "==39.0.0" + "sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4", + "sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f", + "sha256:4789d1e3e257965e960232345002262ede4d094d1a19f4d3b52e48d4d8f3b885", + "sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502", + "sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41", + "sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965", + "sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e", + "sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc", + "sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad", + "sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505", + "sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388", + "sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6", + "sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2", + "sha256:c5caeb8188c24888c90b5108a441c106f7faa4c4c075a2bcae438c6e8ca73cef", + "sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac", + "sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695", + "sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6", + "sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336", + "sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0", + "sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c", + "sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106", + "sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a", + "sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8" + ], + "index": "pypi", + "version": "==39.0.1" }, "dill": { "hashes": [ @@ -2335,11 +2335,11 @@ }, "soupsieve": { "hashes": [ - "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759", - "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d" + "sha256:49e5368c2cda80ee7e84da9dbe3e110b70a4575f196efb74e51b94549d921955", + "sha256:e28dba9ca6c7c00173e34e4ba57448f0688bb681b7c5e8bf4971daafc093d69a" ], - "markers": "python_version >= '3.6'", - "version": "==2.3.2.post1" + "markers": "python_version >= '3.7'", + "version": "==2.4" }, "termcolor": { "hashes": [ @@ -2375,10 +2375,10 @@ }, "types-pyopenssl": { "hashes": [ - "sha256:2e95f9a667d5eeb0af699196f857f7d23d5b4d642437bd37355bc13a87e9f4ae", - "sha256:ea7e5d06f9190a1cb013ad4b13d48896e5cd1e785c04491f38b206d1bc4b8dc1" + "sha256:6ca54d593f8b946f9570f9ed7457c41da3b518feff5e344851941a6209bea62b", + "sha256:847ab17a16475a882dc29898648a6a35ad0d3e11a5bba5aa8ab2f3435a8647cb" ], - "version": "==23.0.0.2" + "version": "==23.0.0.3" }, "types-python-dateutil": { "hashes": [ @@ -2422,25 +2422,25 @@ }, "types-toml": { "hashes": [ - "sha256:a2286a053aea6ab6ff814659272b1d4a05d86a1dd52b807a87b23511993b46c5", - "sha256:f37244eff4cd7eace9cb70d0bac54d3eba77973aa4ef26c271ac3d1c6503a48e" + "sha256:306b1bb8b5bbc5f1b60387dbcc4b489e79f8490ce20e93af5f422a68b470d94b", + "sha256:c8748dd225b28eb80ce712e2d7d61b57599815e7b48d07ef53df51ed148fa6b1" ], - "version": "==0.10.8.3" + "version": "==0.10.8.4" }, "types-urllib3": { "hashes": [ - "sha256:5630e578246d170d91ebe3901788cd28d53c4e044dc2e2488e3b0d55fb6895d8", - "sha256:e8f25c8bb85cde658c72ee931e56e7abd28803c26032441eea9ff4a4df2b0c31" + "sha256:35586727cbd7751acccf2c0f34a88baffc092f435ab62458f10776466590f2d5", + "sha256:a6c23c41bd03e542eaee5423a018f833077b51c4bf9ceb5aa544e12b812d5604" ], - "version": "==1.26.25.5" + "version": "==1.26.25.6" }, "typing-extensions": { "hashes": [ - "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", - "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" + "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", + "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" ], "markers": "python_version >= '3.7'", - "version": "==4.4.0" + "version": "==4.5.0" }, "urllib3": { "hashes": [ @@ -2452,11 +2452,11 @@ }, "werkzeug": { "hashes": [ - "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", - "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" + "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe", + "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612" ], "markers": "python_version >= '3.7'", - "version": "==2.2.2" + "version": "==2.2.3" }, "wrapt": { "hashes": [ From 1db8756d3893d4f4d8f92f1688fa648493aa1669 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 12:41:07 +0000 Subject: [PATCH 169/567] Bump node-jose from 2.1.1 to 2.2.0 (#1039) Bumps [node-jose](https://github.com/cisco/node-jose) from 2.1.1 to 2.2.0. - [Release notes](https://github.com/cisco/node-jose/releases) - [Changelog](https://github.com/cisco/node-jose/blob/master/CHANGELOG.md) - [Commits](https://github.com/cisco/node-jose/compare/v2.1.1...v2.2.0) --- updated-dependencies: - dependency-name: node-jose dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6afe1aaca4..c94246ac76 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "lint-staged": "^12.3.5", "livereload": "^0.9.3", "node-forge": "^1.2.1", - "node-jose": "^2.1.0", + "node-jose": "^2.2.0", "prettier": "^2.5.1", "typescript": "^4.7.4", "uuid": "^8.3.2", diff --git a/yarn.lock b/yarn.lock index 2703db54db..9031cb1e99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3881,10 +3881,10 @@ node-forge@^1.2.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-jose@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.1.1.tgz#7d82e8d9cef8d0a722d7fa385524babfa9e30614" - integrity sha512-19nyuUGShNmFmVTeqDfP6ZJCiikbcjI0Pw2kykBCH7rl8AZgSiDZK2Ww8EDaMrOSbRg6IlfIMhI5ZvCklmOhzg== +node-jose@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.2.0.tgz#b64f3225ad6bec328509a420800de597ba2bf3ed" + integrity sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw== dependencies: base64url "^3.0.1" buffer "^6.0.3" @@ -3894,7 +3894,7 @@ node-jose@^2.1.0: node-forge "^1.2.1" pako "^2.0.4" process "^0.11.10" - uuid "^8.3.2" + uuid "^9.0.0" node-releases@^2.0.3: version "2.0.3" @@ -4993,6 +4993,11 @@ uuid@^8.0.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" From ef1c81f4843192f8a76b05f36045c060a40d2c28 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Tue, 21 Feb 2023 13:24:21 +0000 Subject: [PATCH 170/567] Fixing schema validation failure on large errors (#1031) --- scripts/validate_test_schemas.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/validate_test_schemas.sh b/scripts/validate_test_schemas.sh index 9a601c626f..9462d7d3b0 100755 --- a/scripts/validate_test_schemas.sh +++ b/scripts/validate_test_schemas.sh @@ -37,11 +37,15 @@ echo "--- Testing Schemas in $file_path ---" failed=0 passed=0 -for schema in $(find $file_path -name '*.json'); do +file_path_name=$(find "$file_path" -name '*.json') + +for schema in ${file_path_name}; do result="$(curl -s -w 'HTTPSTATUS:%{http_code}' -X POST -H "Content-Type: application/json" -d @"$schema" http://localhost:5001/validate | tr -d '\n')" + # shellcheck disable=SC2001 + HTTP_BODY=$(echo "${result}" | sed -e 's/HTTPSTATUS\:.*//g') result_response="${result//*HTTPSTATUS:/}" - result_body=$(echo "${result//HTTPSTATUS:*/}" | python -m json.tool) + result_body=$(echo "$HTTP_BODY" | python -m json.tool) if [ "$result_response" == "200" ] && [ "$result_body" == "{}" ]; then echo -e "${green}$schema - PASSED${default}" From e4082f972c8a30296d7e9376592d9c1c8e432516 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:05:48 +0000 Subject: [PATCH 171/567] Schemas v3.41.0 (#1043) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 189a6408d6..1d48bbfedd 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.40.0 +v3.41.0 From caff9b764cbc39fb846eeb3ad14e935dff3ac53a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:40:05 +0000 Subject: [PATCH 172/567] Bug: Context for health theme is incorrect (#1042) --- app/translations/messages.pot | 146 +++++++++--------- .../contexts/submission_metadata_context.py | 2 +- app/views/contexts/thank_you_context.py | 10 +- .../view_submitted_response_context.py | 11 +- schemas/test/en/test_theme_health.json | 3 + schemas/test/en/test_theme_social.json | 1 + .../test_submission_metatdata_context.py | 15 +- .../test_submitted_response_context.py | 65 ++++---- .../views/contexts/test_thank_you_context.py | 29 ++-- 9 files changed, 147 insertions(+), 135 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 7297d896b5..c10fd908ab 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-31 13:07+0000\n" +"POT-Creation-Date: 2023-02-21 13:30+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,14 +17,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.11.0\n" -#: app/forms/validators.py:376 app/jinja_filters.py:115 +#: app/forms/validators.py:377 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:380 app/jinja_filters.py:123 +#: app/forms/validators.py:381 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" @@ -41,15 +41,15 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:600 templates/partials/summary/list-summary.html:9 +#: app/jinja_filters.py:598 templates/partials/summary/list-summary.html:9 msgid "Remove" msgstr "" -#: app/jinja_filters.py:601 templates/partials/summary/list-summary.html:10 +#: app/jinja_filters.py:599 templates/partials/summary/list-summary.html:10 msgid "Remove {item_name}" msgstr "" -#: app/jinja_filters.py:701 +#: app/jinja_filters.py:699 msgid "No answer provided" msgstr "" @@ -208,14 +208,14 @@ msgstr "" msgid "Enter the year in a valid format. For example, 2023." msgstr "" -#: app/forms/validators.py:387 +#: app/forms/validators.py:388 #, python-format msgid "%(num)s day" msgid_plural "%(num)s days" msgstr[0] "" msgstr[1] "" -#: app/forms/fields/select_field_with_detail_answer.py:36 +#: app/forms/fields/select_field_with_detail_answer.py:39 msgid "Not a valid choice." msgstr "" @@ -223,33 +223,33 @@ msgstr "" msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:107 +#: app/helpers/template_helpers.py:106 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:138 +#: app/helpers/template_helpers.py:137 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:163 +#: app/helpers/template_helpers.py:162 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" msgstr "" -#: app/questionnaire/placeholder_transforms.py:164 +#: app/questionnaire/placeholder_transforms.py:160 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:170 +#: app/questionnaire/placeholder_transforms.py:166 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:175 +#: app/questionnaire/placeholder_transforms.py:171 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "" @@ -316,8 +316,8 @@ msgid "An individual access code has been sent by text" msgstr "" #: app/survey_config/business_config.py:59 -#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:90 -#: app/survey_config/census_config.py:156 +#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:89 +#: app/survey_config/census_config.py:154 msgid "Help" msgstr "" @@ -335,8 +335,8 @@ msgid "What we do" msgstr "" #: app/survey_config/business_config.py:92 -#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:98 -#: app/survey_config/census_config.py:164 +#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:97 +#: app/survey_config/census_config.py:162 #: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "" @@ -347,15 +347,15 @@ msgid "Accessibility" msgstr "" #: app/survey_config/business_config.py:107 -#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:118 -#: app/survey_config/census_config.py:173 +#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:117 +#: app/survey_config/census_config.py:171 #: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "" #: app/survey_config/business_config.py:109 -#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:179 +#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:123 +#: app/survey_config/census_config.py:177 #: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "" @@ -368,29 +368,29 @@ msgstr "" msgid "Save and complete later" msgstr "" -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:103 msgid "Languages" msgstr "" -#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:108 +#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:107 msgid "BSL and audio videos" msgstr "" -#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:120 -#: app/survey_config/census_config.py:175 +#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:119 +#: app/survey_config/census_config.py:173 msgid "Accessibility statement" msgstr "" -#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:128 -#: app/survey_config/census_config.py:183 +#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:127 +#: app/survey_config/census_config.py:181 msgid "Terms and conditions" msgstr "" -#: app/survey_config/census_config.py:144 +#: app/survey_config/census_config.py:143 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "" -#: app/survey_config/census_config.py:147 app/survey_config/survey_config.py:20 +#: app/survey_config/census_config.py:146 app/survey_config/survey_config.py:20 msgid "Use of address data is subject to the terms and conditions." msgstr "" @@ -402,63 +402,63 @@ msgstr "" msgid "Save and exit survey" msgstr "" -#: app/views/contexts/hub_context.py:14 +#: app/views/contexts/hub_context.py:15 msgid "Completed" msgstr "" -#: app/views/contexts/hub_context.py:16 +#: app/views/contexts/hub_context.py:17 msgid "View answers" msgstr "" -#: app/views/contexts/hub_context.py:17 +#: app/views/contexts/hub_context.py:18 msgid "View answers for {section_name}" msgstr "" -#: app/views/contexts/hub_context.py:21 +#: app/views/contexts/hub_context.py:22 msgid "Partially completed" msgstr "" -#: app/views/contexts/hub_context.py:23 +#: app/views/contexts/hub_context.py:24 msgid "Continue with section" msgstr "" -#: app/views/contexts/hub_context.py:24 +#: app/views/contexts/hub_context.py:25 msgid "Continue with {section_name} section" msgstr "" -#: app/views/contexts/hub_context.py:28 +#: app/views/contexts/hub_context.py:29 msgid "Not started" msgstr "" -#: app/views/contexts/hub_context.py:30 +#: app/views/contexts/hub_context.py:31 msgid "Start section" msgstr "" -#: app/views/contexts/hub_context.py:31 +#: app/views/contexts/hub_context.py:32 msgid "Start {section_name} section" msgstr "" -#: app/views/contexts/hub_context.py:35 +#: app/views/contexts/hub_context.py:36 msgid "Separate census requested" msgstr "" -#: app/views/contexts/hub_context.py:37 app/views/contexts/hub_context.py:38 +#: app/views/contexts/hub_context.py:38 app/views/contexts/hub_context.py:39 msgid "Change or resend" msgstr "" -#: app/views/contexts/hub_context.py:48 app/views/contexts/hub_context.py:49 +#: app/views/contexts/hub_context.py:51 app/views/contexts/hub_context.py:52 msgid "Submit survey" msgstr "" -#: app/views/contexts/hub_context.py:53 +#: app/views/contexts/hub_context.py:56 msgid "You must submit this survey to complete it" msgstr "" -#: app/views/contexts/hub_context.py:60 +#: app/views/contexts/hub_context.py:63 msgid "Choose another section to complete" msgstr "" -#: app/views/contexts/hub_context.py:61 templates/confirm-email.html:25 +#: app/views/contexts/hub_context.py:64 templates/confirm-email.html:25 #: templates/individual_response/confirmation-post.html:21 #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 @@ -466,19 +466,19 @@ msgstr "" msgid "Continue" msgstr "" -#: app/views/contexts/list_context.py:104 +#: app/views/contexts/list_context.py:109 msgid " (You)" msgstr "" -#: app/views/contexts/submission_metadata_context.py:13 +#: app/views/contexts/submission_metadata_context.py:14 msgid "Submitted on:" msgstr "" -#: app/views/contexts/submission_metadata_context.py:16 +#: app/views/contexts/submission_metadata_context.py:17 msgid "{date} at {time}" msgstr "" -#: app/views/contexts/submission_metadata_context.py:25 +#: app/views/contexts/submission_metadata_context.py:26 msgid "Submission reference:" msgstr "" @@ -494,57 +494,57 @@ msgstr "" msgid "Please submit this survey to complete it" msgstr "" -#: app/views/contexts/thank_you_context.py:28 -msgid "Your answers have been submitted." -msgstr "" - -#: app/views/contexts/thank_you_context.py:30 +#: app/views/contexts/thank_you_context.py:29 msgid "" "Your answers have been submitted for {company_name} " "({trading_name})" msgstr "" -#: app/views/contexts/thank_you_context.py:37 +#: app/views/contexts/thank_you_context.py:36 msgid "Your answers have been submitted for {company_name}" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:37 -msgid "Answers submitted." +#: app/views/contexts/thank_you_context.py:40 +msgid "Your answers have been submitted." msgstr "" -#: app/views/contexts/view_submitted_response_context.py:39 +#: app/views/contexts/view_submitted_response_context.py:33 msgid "Answers submitted for {ru_name} ({trad_as})" msgstr "" -#: app/views/contexts/view_submitted_response_context.py:43 +#: app/views/contexts/view_submitted_response_context.py:37 msgid "Answers submitted for {ru_name}" msgstr "" +#: app/views/contexts/view_submitted_response_context.py:41 +msgid "Answers submitted." +msgstr "" + #: app/views/handlers/confirm_email.py:38 msgid "Yes, send the confirmation email" msgstr "" -#: app/views/handlers/confirm_email.py:69 +#: app/views/handlers/confirm_email.py:68 msgid "Confirm your email address" msgstr "" -#: app/views/handlers/confirm_email.py:89 +#: app/views/handlers/confirm_email.py:88 msgid "Is this email address correct?" msgstr "" +#: app/views/handlers/confirm_email.py:101 #: app/views/handlers/confirm_email.py:102 -#: app/views/handlers/confirm_email.py:103 #: app/views/handlers/individual_response.py:878 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:129 -#: app/views/handlers/confirmation_email.py:68 +#: app/views/handlers/confirm_email.py:128 +#: app/views/handlers/confirmation_email.py:67 #: app/views/handlers/feedback.py:83 app/views/handlers/question.py:165 msgid "Error: {page_title}" msgstr "" -#: app/views/handlers/confirmation_email.py:46 +#: app/views/handlers/confirmation_email.py:45 msgid "Confirmation email" msgstr "" @@ -552,35 +552,35 @@ msgstr "" msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:140 +#: app/views/handlers/feedback.py:139 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:170 +#: app/views/handlers/feedback.py:145 app/views/handlers/feedback.py:169 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:149 app/views/handlers/feedback.py:150 +#: app/views/handlers/feedback.py:148 app/views/handlers/feedback.py:149 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:151 +#: app/views/handlers/feedback.py:150 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:157 +#: app/views/handlers/feedback.py:155 app/views/handlers/feedback.py:156 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:163 +#: app/views/handlers/feedback.py:159 app/views/handlers/feedback.py:162 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:178 app/views/handlers/feedback.py:188 +#: app/views/handlers/feedback.py:177 app/views/handlers/feedback.py:187 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:179 +#: app/views/handlers/feedback.py:178 msgid "Do not include confidential information, such as your contact details" msgstr "" diff --git a/app/views/contexts/submission_metadata_context.py b/app/views/contexts/submission_metadata_context.py index 95551e9664..9b31816ce6 100644 --- a/app/views/contexts/submission_metadata_context.py +++ b/app/views/contexts/submission_metadata_context.py @@ -26,7 +26,7 @@ def build_submission_metadata_context( "term": lazy_gettext("Submission reference:"), "descriptions": [{"description": convert_tx_id(tx_id)}], } - if survey_type is SurveyType.SOCIAL: + if survey_type in {SurveyType.SOCIAL, SurveyType.HEALTH}: return { "data-qa": "metadata", "termCol": 3, diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index 1b8a3ca301..bf7feb2dc3 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -25,19 +25,19 @@ def build_thank_you_context( survey_type: SurveyType, guidance_content: Optional[dict] = None, ) -> dict[str, Any]: - if survey_type is SurveyType.SOCIAL: - submission_text = lazy_gettext("Your answers have been submitted.") - elif (trad_as := metadata["trad_as"]) and (ru_name := metadata["ru_name"]): + if (ru_name := metadata["ru_name"]) and (trad_as := metadata["trad_as"]): submission_text = lazy_gettext( "Your answers have been submitted for {company_name} ({trading_name})" ).format( company_name=ru_name, trading_name=trad_as, ) - else: + elif ru_name: submission_text = lazy_gettext( "Your answers have been submitted for {company_name}" - ).format(company_name=metadata["ru_name"]) + ).format(company_name=ru_name) + else: + submission_text = lazy_gettext("Your answers have been submitted.") context_metadata = build_submission_metadata_context( survey_type, submitted_at, metadata.tx_id ) diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index e43629119d..512503353e 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -29,19 +29,16 @@ def build_view_submitted_response_context( if not metadata: raise NoMetadataException - trad_as = metadata["trad_as"] - ru_name = metadata["ru_name"] - - if survey_type is SurveyType.SOCIAL: - submitted_text = lazy_gettext("Answers submitted.") - elif trad_as: + if (ru_name := metadata["ru_name"]) and (trad_as := metadata["trad_as"]): submitted_text = lazy_gettext( "Answers submitted for {ru_name} ({trad_as})" ).format(ru_name=ru_name, trad_as=trad_as) - else: + elif ru_name: submitted_text = lazy_gettext( "Answers submitted for {ru_name}" ).format(ru_name=ru_name) + else: + submitted_text = lazy_gettext("Answers submitted.") metadata = build_submission_metadata_context( survey_type, diff --git a/schemas/test/en/test_theme_health.json b/schemas/test/en/test_theme_health.json index 2319c32624..e88814e7c6 100644 --- a/schemas/test/en/test_theme_health.json +++ b/schemas/test/en/test_theme_health.json @@ -21,6 +21,9 @@ } } }, + "post_submission": { + "view_response": true + }, "sections": [ { "id": "section", diff --git a/schemas/test/en/test_theme_social.json b/schemas/test/en/test_theme_social.json index 07562d2570..689a74f0b9 100644 --- a/schemas/test/en/test_theme_social.json +++ b/schemas/test/en/test_theme_social.json @@ -14,6 +14,7 @@ } ], "post_submission": { + "view_response": true, "feedback": true }, "questionnaire_flow": { diff --git a/tests/app/views/contexts/test_submission_metatdata_context.py b/tests/app/views/contexts/test_submission_metatdata_context.py index 7600d90b79..f507b8a90d 100644 --- a/tests/app/views/contexts/test_submission_metatdata_context.py +++ b/tests/app/views/contexts/test_submission_metatdata_context.py @@ -1,5 +1,6 @@ from datetime import datetime, timezone +import pytest from flask import Flask from app.survey_config.survey_type import SurveyType @@ -9,15 +10,21 @@ SURVEY_TYPE_DEFAULT = SurveyType.DEFAULT SURVEY_TYPE_SOCIAL = SurveyType.SOCIAL +SURVEY_TYPE_HEALTH = SurveyType.HEALTH SUBMITTED_AT = datetime(2021, 8, 17, 10, 10, 0, tzinfo=timezone.utc) TX_ID = "6b6f90e6-6c27-4c76-8295-7a14e2c4a399" -def test_metadata_survey_type_social(app: Flask): +@pytest.mark.parametrize( + "survey_type", + ( + (SURVEY_TYPE_SOCIAL), + (SURVEY_TYPE_HEALTH), + ), +) +def test_metadata_survey_types_without_ru_name(app: Flask, survey_type): with app.app_context(): - metadata = build_submission_metadata_context( - SURVEY_TYPE_SOCIAL, SUBMITTED_AT, TX_ID - ) + metadata = build_submission_metadata_context(survey_type, SUBMITTED_AT, TX_ID) assert len(metadata["itemsList"]) == 1 assert metadata["data-qa"] == "metadata" assert metadata["termCol"] == 3 diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index cca4c6d114..d5639b9d5b 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -23,7 +23,9 @@ def test_build_view_submitted_response_context_summary(app: Flask): with app.app_context(): - questionnaire_store = fake_questionnaire_store() + questionnaire_store = fake_questionnaire_store( + {"tx_id": "tx_id", "ru_name": "Apple"}, SUBMITTED_AT + ) context = build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) @@ -53,9 +55,11 @@ def test_build_view_submitted_response_context_summary(app: Flask): ) -def test_build_view_submitted_response_context_submitted_text(app: Flask): +def test_view_submitted_response_context_submitted_text_with_ru_name(app: Flask): with app.app_context(): - questionnaire_store = fake_questionnaire_store() + questionnaire_store = fake_questionnaire_store( + {"tx_id": "tx_id", "ru_name": "Apple"}, SUBMITTED_AT + ) context = build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) @@ -63,11 +67,20 @@ def test_build_view_submitted_response_context_submitted_text(app: Flask): assert context["submitted_text"] == "Answers submitted for Apple" -def test_build_view_submitted_response_context_submitted_text_social(app: Flask): +@pytest.mark.parametrize( + "survey_type", + ( + (SurveyType.SOCIAL), + (SurveyType.HEALTH), + ), +) +def test_view_submitted_response_context_submitted_text_without_ru_name( + app: Flask, survey_type +): with app.app_context(): - questionnaire_store = fake_questionnaire_store() + questionnaire_store = fake_questionnaire_store({"tx_id": "tx_id"}, SUBMITTED_AT) context = build_view_submitted_response_context( - "en", SCHEMA, questionnaire_store, SurveyType.SOCIAL + "en", SCHEMA, questionnaire_store, survey_type ) assert context["submitted_text"] == "Answers submitted." @@ -75,7 +88,9 @@ def test_build_view_submitted_response_context_submitted_text_social(app: Flask) def test_build_view_submitted_response_context_submitted_text_with_trad_as(app: Flask): with app.app_context(): - questionnaire_store = fake_questionnaire_store_with_trad_as() + questionnaire_store = fake_questionnaire_store( + {"tx_id": "tx_id", "ru_name": "Apple", "trad_as": "Apple Inc"}, SUBMITTED_AT + ) context = build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT ) @@ -90,7 +105,9 @@ def test_view_submitted_response_expired( app: Flask, ): with app.app_context(): - questionnaire_store = fake_questionnaire_store() + questionnaire_store = fake_questionnaire_store( + {"tx_id": "tx_id", "ru_name": "Apple"}, SUBMITTED_AT + ) questionnaire_store.submitted_at = datetime.now(timezone.utc) - timedelta( seconds=VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS ) @@ -104,7 +121,7 @@ def test_view_submitted_response_expired( def test_build_view_submitted_response_no_submitted_at(app: Flask): with app.app_context(): - questionnaire_store = fake_questionnaire_store_no_submitted_at() + questionnaire_store = fake_questionnaire_store({}, None) with pytest.raises(Exception): build_view_submitted_response_context( "en", SCHEMA, questionnaire_store, SurveyType.DEFAULT @@ -115,7 +132,7 @@ def test_no_metadata_raises_error( app: Flask, ): with app.app_context(): - questionnaire_store = fake_questionnaire_store() + questionnaire_store = fake_questionnaire_store({}, SUBMITTED_AT) questionnaire_store.metadata = None @@ -125,12 +142,12 @@ def test_no_metadata_raises_error( ) -def fake_questionnaire_store(): +def fake_questionnaire_store(metadata, submitted_at): storage = Mock() storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) questionnaire_store = QuestionnaireStore(storage) - questionnaire_store.metadata = get_metadata({"tx_id": "tx_id", "ru_name": "Apple"}) - questionnaire_store.submitted_at = SUBMITTED_AT + questionnaire_store.metadata = get_metadata(metadata) + questionnaire_store.submitted_at = submitted_at questionnaire_store.answer_store = AnswerStore( [ Answer("name-answer", "John Smith", None).to_dict(), @@ -140,29 +157,7 @@ def fake_questionnaire_store(): return questionnaire_store -def fake_questionnaire_store_with_trad_as(): - storage = Mock() - storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) - questionnaire_store = QuestionnaireStore(storage) - questionnaire_store.metadata = get_metadata( - {"tx_id": "tx_id", "ru_name": "Apple", "trad_as": "Apple Inc"} - ) - questionnaire_store.submitted_at = SUBMITTED_AT - questionnaire_store.answer_store = AnswerStore() - return questionnaire_store - - def format_submitted_on_description(): date = format_datetime(SUBMITTED_AT, format="dd LLLL yyyy") time = format_datetime(SUBMITTED_AT, format="HH:mm") return f"{date} at {time}" - - -def fake_questionnaire_store_no_submitted_at(): - storage = Mock() - storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) - questionnaire_store = QuestionnaireStore(storage) - questionnaire_store.submitted_at = None - questionnaire_store.metadata = {} - questionnaire_store.answer_store = AnswerStore() - return questionnaire_store diff --git a/tests/app/views/contexts/test_thank_you_context.py b/tests/app/views/contexts/test_thank_you_context.py index 8f45eb37b3..5878f8ee9a 100644 --- a/tests/app/views/contexts/test_thank_you_context.py +++ b/tests/app/views/contexts/test_thank_you_context.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta, timezone +import pytest from flask import Flask from app.survey_config.survey_type import SurveyType @@ -9,20 +10,11 @@ SURVEY_TYPE_DEFAULT = SurveyType.DEFAULT SURVEY_TYPE_SOCIAL = SurveyType.SOCIAL +SURVEY_TYPE_HEALTH = SurveyType.HEALTH SUBMITTED_AT = datetime.now(timezone.utc) SCHEMA = load_schema_from_name("test_view_submitted_response", "en") -def test_social_survey_context(app: Flask): - with app.app_context(): - context = build_thank_you_context( - SCHEMA, get_metadata(), SUBMITTED_AT, SURVEY_TYPE_SOCIAL - ) - - assert context["submission_text"] == "Your answers have been submitted." - assert len(context["metadata"]["itemsList"]) == 1 - - def test_default_survey_context(app: Flask): with app.app_context(): context = build_thank_you_context( @@ -39,6 +31,23 @@ def test_default_survey_context(app: Flask): assert len(context["metadata"]["itemsList"]) == 2 +@pytest.mark.parametrize( + "survey_type", + ( + (SURVEY_TYPE_SOCIAL), + (SURVEY_TYPE_HEALTH), + ), +) +def test_survey_context_without_ru_name(app: Flask, survey_type): + with app.app_context(): + context = build_thank_you_context( + SCHEMA, get_metadata(), SUBMITTED_AT, survey_type + ) + + assert context["submission_text"] == "Your answers have been submitted." + assert len(context["metadata"]["itemsList"]) == 1 + + def test_default_survey_context_with_trad_as(app: Flask): with app.app_context(): context = build_thank_you_context( From bc39bb04432271cb3bf900a77e70a059357add3e Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:05:18 +0000 Subject: [PATCH 173/567] Rename default branch to main (#1045) * Update Ubuntu to version 22 (#1008) * rename default branch to main --- .codecov.yml | 2 +- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/{master.yml => main.yml} | 4 ++-- .github/workflows/pull_request.yml | 2 +- .pylintrc | 4 ++-- README.md | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename .github/workflows/{master.yml => main.yml} (95%) diff --git a/.codecov.yml b/.codecov.yml index f0ae880aff..bb79cac6b4 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -10,5 +10,5 @@ coverage: changes: default: off codecov: - branch: master + branch: main comment: false diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 591f2f0365..bf962b8af4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,10 +7,10 @@ name: "CodeQL" on: push: - branches: [master] + branches: [main] pull_request: # The branches below must be a subset of the branches above - branches: [master] + branches: [main] schedule: - cron: '0 15 * * 3' @@ -47,7 +47,7 @@ jobs: # queries: ./path/to/local/query, your-org/your-repo/queries@main setup-python-dependencies: false - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 diff --git a/.github/workflows/master.yml b/.github/workflows/main.yml similarity index 95% rename from .github/workflows/master.yml rename to .github/workflows/main.yml index 22095512f0..84693d3aba 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/main.yml @@ -1,9 +1,9 @@ -name: Master +name: Main on: push: branches: - - master + - main jobs: docker-push: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index d928305a6f..5b665a1bdc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -3,7 +3,7 @@ name: PR on: pull_request: branches: - - "master" + - "main" - "branch-v*" - "bug-fix-*" - "feature-*" diff --git a/.pylintrc b/.pylintrc index 64a37772b8..29c372eb45 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -[MASTER] +[MAIN] # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists @@ -611,4 +611,4 @@ init-import=no # List of qualified module names which can have objects that can redefine # builtins. -redefining-builtins-modules=six.moves,future.builtins \ No newline at end of file +redefining-builtins-modules=six.moves,future.builtins diff --git a/README.md b/README.md index cd8fc74c36..b06253250b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # eQ Questionnaire Runner -![Build Status](https://github.com/ONSdigital/eq-questionnaire-runner/workflows/Master/badge.svg) -[![codecov](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/master/graph/badge.svg)](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/master) +![Build Status](https://github.com/ONSdigital/eq-questionnaire-runner/workflows/main/badge.svg) +[![codecov](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/main) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/4c39ddd3285748f8bfb6b70fd5aaf9cc)](https://www.codacy.com/manual/ONSDigital/eq-questionnaire-runner?utm_source=github.com&utm_medium=referral&utm_content=ONSdigital/eq-questionnaire-runner&utm_campaign=Badge_Grade) ## Run with Docker From b483089e66d2c6b9040d9e77c17898e83d1d5c79 Mon Sep 17 00:00:00 2001 From: Hajara Iyal Date: Wed, 1 Mar 2023 10:45:40 +0000 Subject: [PATCH 174/567] Update python type hints style guide (#1026) * Update optional arguments section with | pipe operator * Update on using type ignore * Update file with paramspec * Update Type Ignore in md file * Add section for TypeVar * Update type ignore section * Update TypeVar section * Update to include self type section * Update to include section on type alias * Empty-Commit * update file with corrections * refactored to remove optional keyword * refactored to remove optional keyword * update section on type ignore * refactor section on type ignore --- doc/python-type-hinting.md | 105 +++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 17 deletions(-) diff --git a/doc/python-type-hinting.md b/doc/python-type-hinting.md index c293c20f86..f6132ff9df 100644 --- a/doc/python-type-hinting.md +++ b/doc/python-type-hinting.md @@ -7,7 +7,7 @@ As a team we have committed to adding type hints throughout the Python code. Thi Specify types for variables initialised with `None`: ```python -description: Optional[str] = None +description: var: str | None = None ``` Specify types for empty collections: @@ -17,8 +17,6 @@ items: list[str] = [] mappings: dict[str, int] = {} ``` -In all other places, types for variables are optional. - ## Standard collections For standard collections, use lower case names e.g `list` rather than `List`: @@ -40,18 +38,16 @@ def get_objects_matching(ids: Sequence[str]) -> dict[int, dict] ## Optional arguments -When `Optional` is used for function arguments, it means that the argument can have the value `None`, not that the argument is optional. - -For arguments that can be a single type or `None`, use `Optional`: +For arguments that can be a single type or None, use the shorthand `|` instead of using the older `Optional` keyword: ```python -def test(self, var: Optional[int]) -> None: +def test(self, var: None | int) -> None: ``` -For arguments that can be one of multiple types or `None`, use `Union` with `None`: +For arguments that can be one of multiple types or None, use the shorthand `|` instead of using the older `Union` keyword: ```python -def test(self, var: Union[None, int, str]) -> None: +def test(self, var: None | int | str) -> None: ``` ## Abstract vs concrete @@ -64,24 +60,99 @@ def test(self, var: Union[None, int, str]) -> None: return [value + 1 for value in values] ``` -## Forward declarations +## Self Type -To reference a type before it has been declared e.g. using a class as a type within the class declaration, add the special `annotations` import: +To annotate methods that return an instance of their class, use the `Self` type is as it is bound to it's encapsulating class. In the example below, the type checker will correctly infer the type of `Circle().set_scale(0.5)` to be `Circle`: ```python -from __future__ import annotations +from typing import Self + +class Shape: + def set_scale(self, scale: float) -> Self: + self.scale = scale + return self -class TestClass: - def test(self, var: Sequence[TestClass]) -> None: +class Circle(Shape): + def set_radius(self, radius: float) -> Self: + self.radius = radius + return self +``` + +This is recommended as forward declarations are now redundant in 3.10 +https://peps.python.org/pep-0673/ + +## Type Alias + +Use the special annotation `TypeAlias` to declare type aliases more explicitly so type checkers are able to distinguish between type aliases and ordinary assignments: + +```python +MyType: TypeAlias = "ClassName" +def foo() -> MyType: ... ``` -This import is not necessary in Python 3.10. +## Type Ignore + +To mark portions of the program that should not be covered by type hinting, use `# type: ignore` on the specific line. When used in a line by itself at the top of a file, it silences all errors in the file: + +```python +# type: ignore +``` + +`# type: ignore` should only be used when unavoidable. Ensure that a comment is added to explain why it has been used and have a prefix of `Type ignore:` + +```python +def format_number(number: int) -> str: + # Type ignore: babel.format_number is untyped therefore returns Any. + formatted_number: str = babel.format_number(number) # type: ignore + return formatted_number +``` -https://www.python.org/dev/peps/pep-0563/ +## ParamSpec + +Use to forward the parameter types of one callable to another callable. + +E.g. a basic logging function decorator: + +```python +T = TypeVar('T') +P = ParamSpec('P') + +def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + +@add_logging +def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y +``` + +## TypeVar + +Use `TypeVar` when the type returned by a function is the same as the type which was passed in: + +```python +T = TypeVar('T') + +def increment_value(self, value: T) -> T: + return value + 1 +``` + +`TypeVar` also accepts extra positional arguments to restrict the type parameter for improving code documentation and error prevention. + +```python +T = TypeVar('T', int, float) + +def increment_value(self, value: T) -> T: + return value + 1 +``` ## Useful links - https://www.python.org/dev/peps/pep-0484/ - https://www.pythonsheets.com/notes/python-typing.html -- https://google.github.io/styleguide/pyguide.html#319-type-annotations \ No newline at end of file +- https://google.github.io/styleguide/pyguide.html#319-type-annotations From 5119f79633a647a476fb8606864a34c78c829090 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 1 Mar 2023 14:46:07 +0000 Subject: [PATCH 175/567] Schemas v3.42.0 (#1046) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 1d48bbfedd..228fbf8102 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.41.0 +v3.42.0 From 9998dec82207365a785e6d1819f3c7baee85cd9d Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:00:02 +0000 Subject: [PATCH 176/567] Schemas v3.43.0 (#1047) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 228fbf8102..005235b81d 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.42.0 +v3.43.0 From 47ce0e831b3888cc2d67062f241098b071a9ab62 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 3 Mar 2023 14:51:41 +0000 Subject: [PATCH 177/567] Schemas v3.44.0 (#1049) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 005235b81d..e034f3ff97 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.43.0 +v3.44.0 From e4007e7ac69199766cde3fb8e9e717e25b849239 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 6 Mar 2023 10:08:49 +0000 Subject: [PATCH 178/567] Schemas v3.44.1 (#1050) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e034f3ff97..11bcdc9bee 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.44.0 +v3.44.1 From 79b7c4fdc551ef933e465b7ba14ddd38191d8c9b Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 8 Mar 2023 13:58:33 +0000 Subject: [PATCH 179/567] Update to chromedriver v110 (#1053) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c94246ac76..b4ef793dfc 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/sync": "^7.16.16", "chai": "^4.3.6", - "chromedriver": "^109.0.0", + "chromedriver": "^110.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 9031cb1e99..45bdb34848 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1867,10 +1867,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^109.0.0: - version "109.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-109.0.0.tgz#9407af72f2a01bb19e6db1b85cfc87eb3acd4f6b" - integrity sha512-jdmBq11IUwfThLFiygGTZ89qbROSQI4bICQjvOVQy2Bqr1LwC+MFkhwyZp3YG99eehQbZuTlQmmfCZBfpewTNA== +chromedriver@^110.0.0: + version "110.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-110.0.0.tgz#d00a1a2976592d933faa8e9839e97692922834a4" + integrity sha512-Le6q8xrA/3fAt+g8qiN0YjsYxINIhQMC6wj9X3W5L77uN4NspEzklDrqYNwBcEVn7PcAEJ73nLlS7mTyZRspHA== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From 7a779a688370b86069116d1ac1db6fb2af804254 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:11:08 +0000 Subject: [PATCH 180/567] Schemas v3.44.2 (#1052) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 11bcdc9bee..c7db277f79 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.44.1 +v3.44.2 From 8ed372aab87f2cd7070fbb5aee4436c9fd9dd6e1 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Thu, 9 Mar 2023 10:34:50 +0000 Subject: [PATCH 181/567] Fixing validation schema failure due to missing title (#1054) --- schemas/test/en/test_thank_you_census_household.json | 1 + 1 file changed, 1 insertion(+) diff --git a/schemas/test/en/test_thank_you_census_household.json b/schemas/test/en/test_thank_you_census_household.json index 00d23e749d..676e0929bf 100644 --- a/schemas/test/en/test_thank_you_census_household.json +++ b/schemas/test/en/test_thank_you_census_household.json @@ -30,6 +30,7 @@ "sections": [ { "id": "household-section", + "title": "Household Section", "groups": [ { "blocks": [ From 04f9338f3d9caf72ebd174e1937167d597a82309 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Thu, 9 Mar 2023 10:46:17 +0000 Subject: [PATCH 182/567] Updated Runner's static translation (#1048) --- app/translations/cy/LC_MESSAGES/messages.po | 1121 ++++++++++--------- app/translations/messages.pot | 2 +- 2 files changed, 576 insertions(+), 547 deletions(-) diff --git a/app/translations/cy/LC_MESSAGES/messages.po b/app/translations/cy/LC_MESSAGES/messages.po index c700c6b466..dcc21e7d68 100644 --- a/app/translations/cy/LC_MESSAGES/messages.po +++ b/app/translations/cy/LC_MESSAGES/messages.po @@ -1,54 +1,66 @@ msgid "" msgstr "" -"Project-Id-Version: eq-census\n" +"Project-Id-Version: phm\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2021-03-16 12:13+0000\n" -"PO-Revision-Date: 2021-03-16 15:52\n" +"POT-Creation-Date: 2023-01-31 13:07+0000\n" +"PO-Revision-Date: 2023-03-03 11:11\n" "Last-Translator: \n" "Language-Team: Welsh\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.0\n" +"Generated-By: Babel 2.11.0\n" "Plural-Forms: nplurals=6; plural=(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n == 3) ? 3 : ((n == 6) ? 4 : 5))));\n" -"X-Crowdin-Project: eq-census\n" -"X-Crowdin-Project-ID: 345379\n" +"X-Crowdin-Project: phm\n" +"X-Crowdin-Project-ID: 566447\n" "X-Crowdin-Language: cy\n" "X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 20\n" +"X-Crowdin-File-ID: 1\n" "Language: cy_GB\n" -#: app/forms/validators.py:341 app/jinja_filters.py:94 +#: app/forms/validators.py:376 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "%(num)s blwyddyn" -msgstr[1] "%(num)s blwyddyn" -msgstr[2] "%(num)s blwyddyn" -msgstr[3] "%(num)s blwyddyn" -msgstr[4] "%(num)s blwyddyn" -msgstr[5] "%(num)s blwyddyn" +msgstr[1] "%(num)s flwyddyn" +msgstr[2] "%(num)s flynedd" +msgstr[3] "%(num)s blynedd" +msgstr[4] "%(num)s blynedd" +msgstr[5] "%(num)s o flynyddoedd" -#: app/forms/validators.py:345 app/jinja_filters.py:102 +#: app/forms/validators.py:380 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" msgstr[0] "%(num)s mis" msgstr[1] "%(num)s mis" -msgstr[2] "%(num)s mis" +msgstr[2] "%(num)s fis" msgstr[3] "%(num)s mis" msgstr[4] "%(num)s mis" msgstr[5] "%(num)s mis" -#: app/jinja_filters.py:145 +#: app/jinja_filters.py:165 #, python-format msgid "%(date)s at %(time)s" msgstr "%(date)s am %(time)s" -#: app/jinja_filters.py:156 +#: app/jinja_filters.py:179 #, python-format msgid "%(from_date)s to %(to_date)s" -msgstr "rhwng %(from_date)s a/ac %(to_date)s" +msgstr "%(from_date)s i %(to_date)s" + +#: app/jinja_filters.py:600 templates/partials/summary/list-summary.html:9 +msgid "Remove" +msgstr "Dileu" + +#: app/jinja_filters.py:601 templates/partials/summary/list-summary.html:10 +msgid "Remove {item_name}" +msgstr "Dileu{item_name}" + +#: app/jinja_filters.py:701 +msgid "No answer provided" +msgstr "Dim ateb wedi'i roi" #: app/forms/error_messages.py:11 app/forms/error_messages.py:12 #: app/forms/error_messages.py:13 app/forms/error_messages.py:14 @@ -58,29 +70,29 @@ msgstr "Rhowch ateb" #: app/forms/error_messages.py:15 #, python-format msgid "Select an answer to ‘%(question_title)s’" -msgstr "Dewiswch ateb i ‘%(question_title)s’" +msgstr "Dewiswch ateb i %(question_title)s" #: app/forms/error_messages.py:18 -#: app/forms/field_handlers/dropdown_handler.py:11 +#: app/forms/field_handlers/dropdown_handler.py:16 msgid "Select an answer" msgstr "Dewiswch ateb" #: app/forms/error_messages.py:19 #, python-format msgid "Select at least one answer to ‘%(question_title)s’" -msgstr "Dewiswch o leiaf un ateb to ‘%(question_title)s’" +msgstr "Dewiswch o leiaf un atebi %(question_title)s" #: app/forms/error_messages.py:22 msgid "Enter a date" -msgstr "" +msgstr "Rhowch ddyddiad" -#: app/forms/error_messages.py:23 templates/partials/answers/address.html:57 +#: app/forms/error_messages.py:23 templates/partials/answers/address.html:56 msgid "Enter an address" msgstr "Rhowch gyfeiriad" #: app/forms/error_messages.py:24 msgid "Enter a duration" -msgstr "" +msgstr "Rhowch hyd" #: app/forms/error_messages.py:25 msgid "Enter an email address" @@ -93,47 +105,47 @@ msgstr "Rhowch rif ffôn symudol yn y Deyrnas Unedig" #: app/forms/error_messages.py:27 #, python-format msgid "Enter an answer more than or equal to %(min)s" -msgstr "" +msgstr "Rhowch ateb sy'n fwy na neu'n neu'n hafal i %(min)s" #: app/forms/error_messages.py:28 #, python-format msgid "Enter an answer less than or equal to %(max)s" -msgstr "Nodwch ateb sy'n hafal i %(max)s neu’n llai" +msgstr "Rhowch ateb sy'n llai na neu'n hafal i %(max)s" #: app/forms/error_messages.py:29 #, python-format msgid "Enter an answer more than %(min)s" -msgstr "" +msgstr "Rhowch ateb sy'n fwy na %(min)s" #: app/forms/error_messages.py:30 #, python-format msgid "Enter an answer less than %(max)s" -msgstr "" +msgstr "Rhowch ateb sy'n llai na %(max)s" #: app/forms/error_messages.py:31 #, python-format msgid "Enter answers that add up to %(total)s" -msgstr "Nodwch atebion sy'n creu cyfanswm o %(total)s." +msgstr "Rhowch atebion sy'n creu cyfanswm o %(total)s" #: app/forms/error_messages.py:32 #, python-format msgid "Enter answers that add up to or are less than %(total)s" -msgstr "Nodwch atebion sy'n creu cyfanswm o neu sydd o dan %(total)s" +msgstr "Rhowch atebion sy'n creu cyfanswm o neu sy'n llai na %(total)s" #: app/forms/error_messages.py:35 #, python-format msgid "Enter answers that add up to less than %(total)s" -msgstr "Nodwch atebion sy'n creu cyfanswm sydd o dan %(total)s" +msgstr "Rhowch atebion sy'n creu cyfanswm o lai na%(total)s" #: app/forms/error_messages.py:38 #, python-format msgid "Enter answers that add up to greater than %(total)s" -msgstr "Nodwch atebion sy'n creu cyfanswm sydd dros %(total)s" +msgstr "Rhowch atebion sy'n creu cyfanswm o fwy na %(total)s" #: app/forms/error_messages.py:41 #, python-format msgid "Enter answers that add up to or are greater than %(total)s" -msgstr "Nodwch atebion sy'n creu cyfanswm o neu sydd dros %(total)s" +msgstr "Rhowch atebion sy'n creu cyfanswm o neu sy'n fwy na %(total)s" #: app/forms/error_messages.py:44 msgid "Enter an email address in a valid format, for example name@example.com" @@ -141,63 +153,67 @@ msgstr "Rhowch gyfeiriad e-bost mewn fformat dilys, er enghraifft enw@enghraifft #: app/forms/error_messages.py:47 msgid "Enter a number" -msgstr "Nodwch rif" +msgstr "Rhowch rif" #: app/forms/error_messages.py:48 msgid "Enter a whole number" -msgstr "" +msgstr "Rhowch rif cyfan" #: app/forms/error_messages.py:49 #, python-format msgid "Enter a number rounded to %(max)d decimal places" -msgstr "" +msgstr "Rhowch rif wedi'i dalgrynnu i %(max)d le degol" #: app/forms/error_messages.py:50 #, python-format msgid "You have entered too many characters. Enter up to %(max)d characters" -msgstr "Rydych wedi defnyddio gormod o nodau. Rhowch hyd at %(max)d nod" +msgstr "Rydych chi wedi defnyddio gormod o nodau. Rhowch hyd at %(max)d o nodau" #: app/forms/error_messages.py:53 msgid "Enter a valid date" -msgstr "Nodwch ddyddiad dilys" +msgstr "Rhowch ddyddiad dilys" #: app/forms/error_messages.py:54 msgid "Enter a 'period to' date later than the 'period from' date" -msgstr "" +msgstr "Rhowch ddyddiad 'cyfnod hyd at' yn hytrach na dyddiad 'cyfnod o'" #: app/forms/error_messages.py:57 msgid "Enter a valid duration" -msgstr "" +msgstr "Rhowch hyd dilys" #: app/forms/error_messages.py:58 msgid "Enter a UK mobile number in a valid format, for example, 07700 900345 or +44 7700 900345" -msgstr "Rhowch rif ffôn symudol yn y Deyrnas Unedig mewn fformat dilys, er enghraifft, 07700 912345 neu +44 7700 912345" +msgstr "Rhowch rif ffôn symudol yn y Deyrnas Unedig mewn fformat dilys, er enghraifft, 07700 900345 neu +44 7700 900345" #: app/forms/error_messages.py:61 #, python-format msgid "Enter a reporting period greater than or equal to %(min)s" -msgstr "" +msgstr "Rhowch gyfnod adrodd sy'n fwy na neu'n hafal i %(min)s" #: app/forms/error_messages.py:64 #, python-format msgid "Enter a reporting period less than or equal to %(max)s" -msgstr "" +msgstr "Rhowch gyfnod adrodd sy'n llai na neu'n hafal i %(max)s" #: app/forms/error_messages.py:67 #, python-format msgid "Enter a date after %(min)s" -msgstr "Nodwch ddyddiad ar ôl %(min)s" +msgstr "Rhowch ddyddiad ar ôl %(min)s" #: app/forms/error_messages.py:68 #, python-format msgid "Enter a date before %(max)s" -msgstr "Nodwch ddyddiad cyn %(max)s" +msgstr "Rhowch ddyddiad cyn %(max)s" #: app/forms/error_messages.py:69 msgid "Remove an answer" -msgstr "" +msgstr "Dileu ateb" + +#: app/forms/error_messages.py:70 +msgid "Enter the year in a valid format. For example, 2023." +msgstr "Rhowch y flwyddyn mewn fformat dilys. Er enghraifft, 2023." -#: app/forms/validators.py:352 +#: app/forms/validators.py:387 #, python-format msgid "%(num)s day" msgid_plural "%(num)s days" @@ -208,220 +224,258 @@ msgstr[3] "%(num)s diwrnod" msgstr[4] "%(num)s diwrnod" msgstr[5] "%(num)s diwrnod" -#: app/forms/fields/select_field_with_detail_answer.py:35 -msgid "Not a valid choice" -msgstr "Ddim yn ddewis dilys" - -#: app/helpers/template_helpers.py:24 -msgid "Office for National Statistics logo" -msgstr "logo Swyddfa Ystadegau Gwladol" - -#: app/helpers/template_helpers.py:33 app/helpers/template_helpers.py:42 -#: app/helpers/template_helpers.py:170 templates/signed-out.html:3 -msgid "Census 2021" -msgstr "Cyfrifiad 2021" - -#: app/helpers/template_helpers.py:38 -msgid "Northern Ireland Statistics and Research Agency logo" -msgstr "" - -#: app/helpers/template_helpers.py:55 -msgid "Help" -msgstr "Help" - -#: app/helpers/template_helpers.py:60 -msgid "Contact us" -msgstr "Cysylltu â ni" +#: app/forms/fields/select_field_with_detail_answer.py:36 +msgid "Not a valid choice." +msgstr "Nid yw'n ddewis dilys." -#: app/helpers/template_helpers.py:69 -msgid "Languages" -msgstr "Ieithoedd" - -#: app/helpers/template_helpers.py:74 -msgid "BSL and audio videos" -msgstr "Fideos BSL a sain" - -#: app/helpers/template_helpers.py:84 -msgid "The following links open in a new tab" -msgstr "Bydd y dolenni canlynol yn agor mewn tab newydd" - -#: app/helpers/template_helpers.py:86 -msgid "Crown copyright and database rights 2021 NIMA MOU577.501." -msgstr "" - -#: app/helpers/template_helpers.py:90 -msgid "Crown copyright and database rights 2020 OS 100019153." -msgstr "Hawlfraint y goron a hawliau cronfa ddata 2020 OS 100019153." - -#: app/helpers/template_helpers.py:91 -msgid "Use of address data is subject to the terms and conditions." -msgstr "Mae defnyddio data cyfeiriadau yn ddarostyngedig i’r telerau ac amodau." - -#: app/helpers/template_helpers.py:102 -msgid "Cookies" -msgstr "Cwcis" +#: app/helpers/template_helpers.py:46 +msgid "ONS Surveys" +msgstr "Arolygon SYG" #: app/helpers/template_helpers.py:107 -msgid "Accessibility statement" -msgstr "Datganiad hygyrchedd" +msgid "Menu" +msgstr "Dewislen" -#: app/helpers/template_helpers.py:112 -msgid "Privacy and data protection" -msgstr "Preifatrwydd a diogelu data" - -#: app/helpers/template_helpers.py:117 -msgid "Terms and conditions" -msgstr "Telerau ac amodau" +#: app/helpers/template_helpers.py:138 +msgid "The following links open in a new tab" +msgstr "Mae'r dolenni canlynol yn agor mewn tab newydd" -#: app/helpers/template_helpers.py:127 +#: app/helpers/template_helpers.py:163 msgid "Make sure you leave this page or close your browser if using a shared device" -msgstr "Cofiwch adael y dudalen hon neu gau eich porwr os ydych chi'n defnyddio dyfais sy'n cael ei rhannu" +msgstr "Cofiwch adael y dudalen hon neu gau eich porwr os ydych chi'n defnyddio dyfais sy'n cael ei rhannu" -#: app/questionnaire/placeholder_transforms.py:92 +#: app/questionnaire/placeholder_transforms.py:164 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" -msgstr[0] "{number_of_years} oed" -msgstr[1] "{number_of_years} oed" -msgstr[2] "{number_of_years} oed" -msgstr[3] "{number_of_years} oed" -msgstr[4] "{number_of_years} oed" -msgstr[5] "{number_of_years} oed" - -#: app/questionnaire/placeholder_transforms.py:98 +msgstr[0] "{number_of_years} blwyddyn" +msgstr[1] "{number_of_years} flwyddyn" +msgstr[2] "{number_of_years} flynedd" +msgstr[3] "{number_of_years} blynedd" +msgstr[4] "{number_of_years} blynedd" +msgstr[5] "{number_of_years} o flynyddoedd" + +#: app/questionnaire/placeholder_transforms.py:170 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" -msgstr[0] "{number_of_months} mis oed" -msgstr[1] "{number_of_months} mis oed" -msgstr[2] "{number_of_months} fis oed" -msgstr[3] "{number_of_months} mis oed" -msgstr[4] "{number_of_months} mis oed" -msgstr[5] "{number_of_months} mis oed" - -#: app/questionnaire/placeholder_transforms.py:103 +msgstr[0] "{number_of_months} mis" +msgstr[1] "{number_of_months} mis" +msgstr[2] "{number_of_months} fis" +msgstr[3] "{number_of_months} mis" +msgstr[4] "{number_of_months} mis" +msgstr[5] "{number_of_months} mis" + +#: app/questionnaire/placeholder_transforms.py:175 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" -msgstr[0] "{number_of_days} diwrnod oed" -msgstr[1] "{number_of_days} diwrnod oed" -msgstr[2] "{number_of_days} ddiwrnod oed" -msgstr[3] "{number_of_days} diwrnod oed" -msgstr[4] "{number_of_days} diwrnod oed" -msgstr[5] "{number_of_days} diwrnod oed" - -#: app/routes/errors.py:115 +msgstr[0] "{number_of_days} diwrnod" +msgstr[1] "{number_of_days} diwrnod" +msgstr[2] "{number_of_days} ddiwrnod" +msgstr[3] "{number_of_days} diwrnod" +msgstr[4] "{number_of_days} diwrnod" +msgstr[5] "{number_of_days} diwrnod" + +#: app/routes/errors.py:135 msgid "You have reached the maximum number of individual access codes" -msgstr "Rydych wedi cyrraedd y nifer fwyaf o godau mynediad unigol" +msgstr "Rydych wedi cyrraedd y nifer mwyaf o godau mynediad unigol" -#: app/routes/errors.py:118 +#: app/routes/errors.py:138 msgid "If you need more individual access codes, please contact us." msgstr "Os bydd angen i chi gael mwy o godau mynediad unigol, cysylltwch â ni." -#: app/routes/errors.py:134 +#: app/routes/errors.py:154 msgid "You have reached the maximum number of times for submitting feedback" msgstr "Ni allwch gyflwyno rhagor o adborth" -#: app/routes/errors.py:137 +#: app/routes/errors.py:157 msgid "If you need to give more feedback, please contact us." msgstr "Cysylltwch â ni os bydd angen i chi roi rhagor o adborth." -#: app/routes/errors.py:169 +#: app/routes/errors.py:189 msgid "Sorry, there was a problem sending the access code" -msgstr "Mae'n ddrwg gennym, roedd problem wrth anfon y cod" +msgstr "Mae'n ddrwg gennym, roedd problem wrth anfon y cod mynediad" -#: app/routes/errors.py:175 +#: app/routes/errors.py:195 msgid "You can try to request a new access code again." msgstr "Gallwch geisio gofyn am god mynediad newydd eto." -#: app/routes/errors.py:178 app/routes/errors.py:201 app/routes/errors.py:223 +#: app/routes/errors.py:198 app/routes/errors.py:221 app/routes/errors.py:243 msgid "If this problem keeps happening, please contact us for help." msgstr "Os bydd y broblem hon yn parhau, cysylltwch â ni i gael cymorth." -#: app/routes/errors.py:197 +#: app/routes/errors.py:217 msgid "Sorry, there was a problem sending the confirmation email" msgstr "Mae'n ddrwg gennym, roedd problem wrth anfon yr e-bost cadarnhau" -#: app/routes/errors.py:198 +#: app/routes/errors.py:218 msgid "You can try to send the email again." msgstr "Gallwch chi geisio anfon yr e-bost eto." -#: app/routes/errors.py:219 templates/errors/403.html:3 +#: app/routes/errors.py:239 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "Mae'n ddrwg gennym, mae problem wedi codi" -#: app/routes/errors.py:220 +#: app/routes/errors.py:240 msgid "You can try to submit your feedback again." msgstr "Gallwch chi geisio cyflwyno eich adborth eto." -#: app/routes/individual_response.py:167 +#: app/routes/individual_response.py:184 msgid "An individual access code has been sent by post" msgstr "Mae cod mynediad unigol wedi cael ei anfon drwy'r post" -#: app/routes/individual_response.py:262 +#: app/routes/individual_response.py:278 msgid "An individual access code has been sent by text" msgstr "Mae cod mynediad unigol wedi cael ei anfon drwy neges destun" -#: app/views/contexts/hub_context.py:15 +#: app/survey_config/business_config.py:59 +#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:90 +#: app/survey_config/census_config.py:156 +msgid "Help" +msgstr "Help" + +#: app/survey_config/business_config.py:73 +msgid "My account" +msgstr "Fy nghyfrif" + +#: app/survey_config/business_config.py:78 +msgid "Sign out" +msgstr "Allgofnodi" + +#: app/survey_config/business_config.py:88 +#: app/survey_config/social_survey_config.py:39 +msgid "What we do" +msgstr "Beth rydym yn ei wneud" + +#: app/survey_config/business_config.py:92 +#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:98 +#: app/survey_config/census_config.py:164 +#: app/survey_config/social_survey_config.py:43 +msgid "Contact us" +msgstr "Cysylltu â ni" + +#: app/survey_config/business_config.py:97 +#: app/survey_config/social_survey_config.py:48 +msgid "Accessibility" +msgstr "Hygyrchedd" + +#: app/survey_config/business_config.py:107 +#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:118 +#: app/survey_config/census_config.py:173 +#: app/survey_config/social_survey_config.py:58 +msgid "Cookies" +msgstr "Cwcis" + +#: app/survey_config/business_config.py:109 +#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 +#: app/survey_config/census_config.py:179 +#: app/survey_config/social_survey_config.py:60 +msgid "Privacy and data protection" +msgstr "Preifatrwydd a diogelu data" + +#: app/survey_config/census_config.py:23 +msgid "Census 2021" +msgstr "Cyfrifiad 2021" + +#: app/survey_config/census_config.py:24 +msgid "Save and complete later" +msgstr "Cadw a chwblhau yn nes ymlaen" + +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 +msgid "Languages" +msgstr "Ieithoedd" + +#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:108 +msgid "BSL and audio videos" +msgstr "Fideos sain ac Iaith Arwyddion Prydain" + +#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:120 +#: app/survey_config/census_config.py:175 +msgid "Accessibility statement" +msgstr "Datganiad hygyrchedd" + +#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:128 +#: app/survey_config/census_config.py:183 +msgid "Terms and conditions" +msgstr "Telerau ac amodau" + +#: app/survey_config/census_config.py:144 +msgid "Crown copyright and database rights 2021 NIMA MOU577.501." +msgstr "Hawlfraint y goron a hawliau cronfa ddata 2021 NIMA MOU577.501." + +#: app/survey_config/census_config.py:147 app/survey_config/survey_config.py:20 +msgid "Use of address data is subject to the terms and conditions." +msgstr "Mae defnyddio data cyfeiriadau yn ddarostyngedig i’r telerau ac amodau." + +#: app/survey_config/survey_config.py:17 +msgid "Crown copyright and database rights 2020 OS 100019153." +msgstr "Hawlfraint y goron a hawliau cronfa ddata 2020 OS 100019153." + +#: app/survey_config/survey_config.py:36 +msgid "Save and exit survey" +msgstr "Cadw a gadael yr arolwg" + +#: app/views/contexts/hub_context.py:14 msgid "Completed" msgstr "Cwblhawyd" -#: app/views/contexts/hub_context.py:17 +#: app/views/contexts/hub_context.py:16 msgid "View answers" msgstr "Gweld yr atebion" -#: app/views/contexts/hub_context.py:18 +#: app/views/contexts/hub_context.py:17 msgid "View answers for {section_name}" msgstr "Gweld yr atebion ar gyfer {section_name}" -#: app/views/contexts/hub_context.py:22 +#: app/views/contexts/hub_context.py:21 msgid "Partially completed" msgstr "Wedi’i chwblhau’n rhannol" -#: app/views/contexts/hub_context.py:24 +#: app/views/contexts/hub_context.py:23 msgid "Continue with section" msgstr "Parhau â'r adran" -#: app/views/contexts/hub_context.py:25 +#: app/views/contexts/hub_context.py:24 msgid "Continue with {section_name} section" -msgstr "Parhau â’r adran {section_name}" +msgstr "Parhau ag adran {section_name}" -#: app/views/contexts/hub_context.py:29 +#: app/views/contexts/hub_context.py:28 msgid "Not started" msgstr "Heb ddechrau" -#: app/views/contexts/hub_context.py:31 +#: app/views/contexts/hub_context.py:30 msgid "Start section" msgstr "Dechrau'r adran" -#: app/views/contexts/hub_context.py:32 +#: app/views/contexts/hub_context.py:31 msgid "Start {section_name} section" msgstr "Dechrau adran {section_name}" -#: app/views/contexts/hub_context.py:36 +#: app/views/contexts/hub_context.py:35 msgid "Separate census requested" -msgstr "Cais am gyfrifiad ar wahân" +msgstr "Cais am gyfrifiad ar wahân wedi'i wneud" -#: app/views/contexts/hub_context.py:38 app/views/contexts/hub_context.py:39 +#: app/views/contexts/hub_context.py:37 app/views/contexts/hub_context.py:38 msgid "Change or resend" msgstr "Newid neu ailanfon" -#: app/views/contexts/hub_context.py:49 app/views/contexts/hub_context.py:50 +#: app/views/contexts/hub_context.py:48 app/views/contexts/hub_context.py:49 msgid "Submit survey" msgstr "Cyflwyno'r arolwg" -#: app/views/contexts/hub_context.py:54 +#: app/views/contexts/hub_context.py:53 msgid "You must submit this survey to complete it" -msgstr "Cyflwynwch y arolwg hwn i'w gwblhau" +msgstr "Mae'n rhaid i chi gyflwyno'r arolwg hwn er mwyn ei gwblhau" -#: app/views/contexts/hub_context.py:61 +#: app/views/contexts/hub_context.py:60 msgid "Choose another section to complete" msgstr "Dewiswch adran arall i'w chwblhau" -#: app/views/contexts/hub_context.py:62 templates/confirm-email.html:26 +#: app/views/contexts/hub_context.py:61 templates/confirm-email.html:25 #: templates/individual_response/confirmation-post.html:21 -#: templates/individual_response/confirmation-text-message.html:31 +#: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/sectionsummary.html:11 templates/sectionsummary.html:48 +#: templates/sectionsummary.html:10 templates/sectionsummary.html:50 msgid "Continue" msgstr "Parhau" @@ -429,40 +483,75 @@ msgstr "Parhau" msgid " (You)" msgstr " (Chi)" -#: app/views/contexts/questionnaire_summary_context.py:15 +#: app/views/contexts/submission_metadata_context.py:13 +msgid "Submitted on:" +msgstr "Cyflwynwyd ar:" + +#: app/views/contexts/submission_metadata_context.py:16 +msgid "{date} at {time}" +msgstr "{date} am {time}" + +#: app/views/contexts/submission_metadata_context.py:25 +msgid "Submission reference:" +msgstr "Cyfeirnod cyflwyno:" + +#: app/views/contexts/submit_questionnaire_context.py:13 msgid "Check your answers and submit" msgstr "Gwiriwch eich atebion a'u cyflwyno" -#: app/views/contexts/questionnaire_summary_context.py:18 -#: templates/confirmation.html:6 templates/confirmation.html:28 +#: app/views/contexts/submit_questionnaire_context.py:16 msgid "Submit answers" msgstr "Cyflwyno atebion" -#: app/views/contexts/questionnaire_summary_context.py:21 +#: app/views/contexts/submit_questionnaire_context.py:19 msgid "Please submit this survey to complete it" msgstr "Cyflwynwch yr arolwg hwn i'w gwblhau" -#: app/views/handlers/confirm_email.py:31 +#: app/views/contexts/thank_you_context.py:28 +msgid "Your answers have been submitted." +msgstr "Mae eich atebion wedi cael eu cyflwyno." + +#: app/views/contexts/thank_you_context.py:30 +msgid "Your answers have been submitted for {company_name} ({trading_name})" +msgstr "Mae eich atebion wedi cael eu cyflwyno ar gyfer {company_name} ({trading_name})" + +#: app/views/contexts/thank_you_context.py:37 +msgid "Your answers have been submitted for {company_name}" +msgstr "Mae eich atebion wedi cael eu cyflwyno ar gyfer {company_name}" + +#: app/views/contexts/view_submitted_response_context.py:37 +msgid "Answers submitted." +msgstr "Atebion wedi'u cyflwyno." + +#: app/views/contexts/view_submitted_response_context.py:39 +msgid "Answers submitted for {ru_name} ({trad_as})" +msgstr "Atebion wedi'u cyflwyno ar gyfer {ru_name} ({trad_as})" + +#: app/views/contexts/view_submitted_response_context.py:43 +msgid "Answers submitted for {ru_name}" +msgstr "Atebion wedi'u cyflwyno ar gyfer {ru_name}" + +#: app/views/handlers/confirm_email.py:38 msgid "Yes, send the confirmation email" -msgstr "Ydy, gellir anfon yr e-bost cadarnhau" +msgstr "Ydy, anfonwch yr e-bost cadarnhau" -#: app/views/handlers/confirm_email.py:60 +#: app/views/handlers/confirm_email.py:69 msgid "Confirm your email address" msgstr "Cadarnhewch eich cyfeiriad e-bost" -#: app/views/handlers/confirm_email.py:78 +#: app/views/handlers/confirm_email.py:89 msgid "Is this email address correct?" -msgstr "A yw’r cyfeiriad e-bost hwn yn gywir?" +msgstr "Ydy'r cyfeiriad e-bost hwn yn gywir?" -#: app/views/handlers/confirm_email.py:91 -#: app/views/handlers/confirm_email.py:92 -#: app/views/handlers/individual_response.py:867 +#: app/views/handlers/confirm_email.py:102 +#: app/views/handlers/confirm_email.py:103 +#: app/views/handlers/individual_response.py:878 msgid "No, I need to change it" msgstr "Nac ydy, mae angen i mi ei newid" -#: app/views/handlers/confirm_email.py:116 -#: app/views/handlers/confirmation_email.py:66 -#: app/views/handlers/feedback.py:62 app/views/handlers/question.py:150 +#: app/views/handlers/confirm_email.py:129 +#: app/views/handlers/confirmation_email.py:68 +#: app/views/handlers/feedback.py:83 app/views/handlers/question.py:165 msgid "Error: {page_title}" msgstr "Gwall: {page_title}" @@ -470,231 +559,194 @@ msgstr "Gwall: {page_title}" msgid "Confirmation email" msgstr "E-bost cadarnhau" -#: app/views/handlers/feedback.py:28 +#: app/views/handlers/feedback.py:44 msgid "Feedback" msgstr "Adborth" -#: app/views/handlers/feedback.py:94 app/views/handlers/feedback.py:100 -#: app/views/handlers/feedback.py:108 -msgid "General" -msgstr "Cyffredinol" - -#: app/views/handlers/feedback.py:95 -msgid "This establishment" -msgstr "Y sefydliad hwn" - -#: app/views/handlers/feedback.py:96 app/views/handlers/feedback.py:109 -msgid "People who live here" -msgstr "Pobl sy'n byw yma" - -#: app/views/handlers/feedback.py:97 app/views/handlers/feedback.py:110 -msgid "Visitors" -msgstr "Ymwelwyr" - -#: app/views/handlers/feedback.py:101 -msgid "Accommodation" -msgstr "Llety" - -#: app/views/handlers/feedback.py:102 app/views/handlers/feedback.py:112 -msgid "Personal details" -msgstr "Manylion Personol" - -#: app/views/handlers/feedback.py:103 app/views/handlers/feedback.py:113 -msgid "Health" -msgstr "Iechyd" - -#: app/views/handlers/feedback.py:104 app/views/handlers/feedback.py:114 -msgid "Qualifications" -msgstr "Cymwysterau" - -#: app/views/handlers/feedback.py:105 app/views/handlers/feedback.py:115 -msgid "Employment" -msgstr "Cyflogaeth" - -#: app/views/handlers/feedback.py:111 -msgid "Household and accommodation" -msgstr "Cartref a llety" - -#: app/views/handlers/feedback.py:127 +#: app/views/handlers/feedback.py:140 msgid "Give feedback about this service" -msgstr "Rhoi adborth ar y gwasanaeth hwn" +msgstr "Rhowch adborth ar y gwasanaeth hwn" -#: app/views/handlers/feedback.py:128 app/views/handlers/feedback.py:172 +#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:170 msgid "Select what your feedback is about" msgstr "Dewiswch am beth mae eich adborth yn sôn" -#: app/views/handlers/feedback.py:136 app/views/handlers/feedback.py:137 -msgid "The census questions" -msgstr "Cwestiynau’r cyfrifiad" +#: app/views/handlers/feedback.py:149 app/views/handlers/feedback.py:150 +msgid "The survey questions" +msgstr "Cwestiynau'r arolwg" -#: app/views/handlers/feedback.py:138 +#: app/views/handlers/feedback.py:151 msgid "For example, questions not clear, answer options not relevant" -msgstr "Er enghraifft, nid yw’r cwestiynau’n glir, opsiynau ddim yn berthnasol" - -#: app/views/handlers/feedback.py:145 -msgid "Question topic" -msgstr "Pwnc cwestiwn" - -#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:149 -msgid "Select an option" -msgstr "Dewiswch opsiwn" +msgstr "Er enghraifft, nid yw'r cwestiynau'n glir, nid yw'r atebion posibl yn berthnasol" -#: app/views/handlers/feedback.py:158 app/views/handlers/feedback.py:159 +#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:157 msgid "Page design and structure" msgstr "Dyluniad a strwythur tudalen" -#: app/views/handlers/feedback.py:162 app/views/handlers/feedback.py:165 +#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:163 msgid "General feedback about this service" msgstr "Adborth cyffredinol ar y gwasanaeth hwn" -#: app/views/handlers/feedback.py:180 app/views/handlers/feedback.py:190 +#: app/views/handlers/feedback.py:178 app/views/handlers/feedback.py:188 msgid "Enter your feedback" msgstr "Rhowch eich adborth" -#: app/views/handlers/feedback.py:181 +#: app/views/handlers/feedback.py:179 msgid "Do not include confidential information, such as your contact details" msgstr "Peidiwch â chynnwys gwybodaeth gyfrinachol, fel eich manylion cyswllt" -#: app/views/handlers/individual_response.py:137 +#: app/views/handlers/individual_response.py:140 msgid "Person {list_item_position}" -msgstr "Unigolion {list_item_position}" +msgstr "Person {list_item_position}" -#: app/views/handlers/individual_response.py:245 +#: app/views/handlers/individual_response.py:252 msgid "Cannot answer questions for others in your household" msgstr "Methu ateb cwestiynau ar ran pobl eraill yn eich cartref" -#: app/views/handlers/individual_response.py:311 +#: app/views/handlers/individual_response.py:322 msgid "How would you like {person_name} to receive a separate census?" msgstr "Sut hoffech chi i {person_name} gael cyfrifiad ar wahân?" -#: app/views/handlers/individual_response.py:334 +#: app/views/handlers/individual_response.py:345 msgid "Text message" msgstr "Neges destun" -#: app/views/handlers/individual_response.py:336 +#: app/views/handlers/individual_response.py:347 msgid "We will need their mobile number for this" msgstr "Bydd angen rhif ffôn symudol yr unigolyn arnom ar gyfer hyn" -#: app/views/handlers/individual_response.py:344 +#: app/views/handlers/individual_response.py:355 msgid "Post" msgstr "Post" -#: app/views/handlers/individual_response.py:346 +#: app/views/handlers/individual_response.py:357 msgid "We can only send this to an unnamed resident at the registered household address" -msgstr "Dim ond i gyfeiriad cofrestredig y cartref y gallwn anfon hwn" +msgstr "Dim ond at breswylydd dienw yng nghyfeiriad cofrestredig y cartref y gallwn anfon hwn" -#: app/views/handlers/individual_response.py:355 +#: app/views/handlers/individual_response.py:366 msgid "It is no longer possible to receive an access code by post." -msgstr "Nid yw’n bosibl cael cod mynediad drwy’r post mwyach." +msgstr "Ni ellir cael cod myneidad drwy'r post mwyach." -#: app/views/handlers/individual_response.py:357 +#: app/views/handlers/individual_response.py:368 msgid "Select how to send access code." msgstr "Dewiswch sut i anfon y cod mynediad." -#: app/views/handlers/individual_response.py:360 +#: app/views/handlers/individual_response.py:371 msgid "For someone to complete a separate census, we need to send them an individual access code." -msgstr "Er mwyn i unigolyn allu cwblhau cyfrifiad ar wahân, bydd angen i ni anfon cod mynediad unigol atynt." +msgstr "Er mwyn i rywun allu cwblhau cyfrifiad ar wahân, bydd angen i ni anfon cod mynediad unigol ato." -#: app/views/handlers/individual_response.py:406 +#: app/views/handlers/individual_response.py:417 msgid "Send individual access code" msgstr "Anfon cod mynediad unigol" -#: app/views/handlers/individual_response.py:437 +#: app/views/handlers/individual_response.py:448 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "Sut hoffech chi ateb cwestiynau {person_name_possessive}?" -#: app/views/handlers/individual_response.py:452 +#: app/views/handlers/individual_response.py:463 msgid "I would like to request a separate census for them to complete" msgstr "Hoffwn i wneud cais am gyfrifiad ar wahân i’r unigolyn ei gwblhau" -#: app/views/handlers/individual_response.py:458 +#: app/views/handlers/individual_response.py:469 msgid "I will ask them to answer their own questions" msgstr "Byddaf yn gofyn i’r unigolyn ateb ei gwestiynau ei hun" -#: app/views/handlers/individual_response.py:462 +#: app/views/handlers/individual_response.py:473 msgid "They will need the household access code from the letter we sent you" msgstr "Bydd angen cod mynediad y cartref ar yr unigolyn o’r llythyr y gwnaethom ei anfon atoch" -#: app/views/handlers/individual_response.py:468 +#: app/views/handlers/individual_response.py:479 msgid "I will answer for {person_name}" msgstr "Byddaf yn ateb ar ran {person_name}" -#: app/views/handlers/individual_response.py:510 +#: app/views/handlers/individual_response.py:521 msgid "How to answer questions" msgstr "Sut i ateb cwestiynau" -#: app/views/handlers/individual_response.py:575 +#: app/views/handlers/individual_response.py:586 msgid "Do you want to send an individual access code for {person_name} by post?" -msgstr "Ydych chi am anfon cod mynediad unigolyn at {person_name} drwy’r post?" +msgstr "Ydych chi am anfon cod mynediad unigol at {person_name} drwy’r post?" -#: app/views/handlers/individual_response.py:583 +#: app/views/handlers/individual_response.py:594 msgid "A letter with an individual access code will be sent to your registered household address" -msgstr "Bydd llythyr â chod mynediad unigryw yn cael ei anfon i'ch cyfeiriad cartref" +msgstr "Bydd llythyr â chod mynediad unigol yn cael ei anfon i gyfeiriad cofrestredig eich cartref" -#: app/views/handlers/individual_response.py:590 +#: app/views/handlers/individual_response.py:601 msgid "The letter will be addressed to Individual Resident instead of the name provided" -msgstr "Bydd y llythyr wedi'i gyfeirio at y Preswylydd Unigol yn hytrach na'r enw a gafodd ei roi" +msgstr "Bydd y llythyr wedi'i gyfeirio at y Preswylydd Unigol yn hytrach na'r enw a gafodd ei roi" -#: app/views/handlers/individual_response.py:603 +#: app/views/handlers/individual_response.py:614 msgid "Yes, send the access code by post" msgstr "Ydw, anfonwch y cod mynediad drwy'r post" -#: app/views/handlers/individual_response.py:609 +#: app/views/handlers/individual_response.py:620 msgid "No, send it another way" msgstr "Nac ydw, anfonwch y cod mynediad mewn ffordd arall" -#: app/views/handlers/individual_response.py:642 +#: app/views/handlers/individual_response.py:653 msgid "Confirm address" msgstr "Cadarnhau'r cyfeiriad" -#: app/views/handlers/individual_response.py:697 -#: app/views/handlers/individual_response.py:735 +#: app/views/handlers/individual_response.py:708 +#: app/views/handlers/individual_response.py:746 msgid "Separate Census" msgstr "Cyfrifiad ar wahân" -#: app/views/handlers/individual_response.py:701 +#: app/views/handlers/individual_response.py:712 msgid "Who do you need to request a separate census for?" msgstr "Ar gyfer pwy y mae angen i chi wneud cais am gyfrifiad ar wahân?" -#: app/views/handlers/individual_response.py:759 +#: app/views/handlers/individual_response.py:770 msgid "What is {person_name_possessive} mobile number?" msgstr "Beth yw rhif ffôn symudol {person_name_possessive}?" -#: app/views/handlers/individual_response.py:771 +#: app/views/handlers/individual_response.py:782 msgid "UK mobile number" -msgstr "Rhif ffôn symudol" +msgstr "Rhif ffôn symudol yn y Deyrnas Unedig" -#: app/views/handlers/individual_response.py:772 +#: app/views/handlers/individual_response.py:783 msgid "This will not be stored and only used once to send the access code" msgstr "Ni chaiff ei storio a bydd ond yn cael ei ddefnyddio unwaith i anfon y cod mynediad" -#: app/views/handlers/individual_response.py:805 +#: app/views/handlers/individual_response.py:816 msgid "Mobile number" msgstr "Rhif ffôn symudol" -#: app/views/handlers/individual_response.py:854 +#: app/views/handlers/individual_response.py:865 msgid "Is this mobile number correct?" msgstr "Ydy’r rhif ffôn symudol hwn yn gywir?" -#: app/views/handlers/individual_response.py:863 +#: app/views/handlers/individual_response.py:874 msgid "Yes, send the text" -msgstr "Ydy, anfonwch y neges destun nawr" +msgstr "Ydy, anfonwch y neges destun" -#: app/views/handlers/individual_response.py:901 +#: app/views/handlers/individual_response.py:912 msgid "Confirm mobile number" msgstr "Cadarnhau'r rhif ffôn symudol" -#: app/views/handlers/thank_you.py:22 templates/census-thank-you.html:24 +#: app/views/handlers/thank_you.py:26 templates/census-thank-you.html:24 msgid "Thank you for completing the census" msgstr "Diolch am gwblhau'r cyfrifiad" +#: app/views/handlers/view_submitted_response.py:52 +msgid "View Submitted Response" +msgstr "Gweld yr Ymateb a Gyflwynwyd" + +#: templates/calculatedsummary.html:13 +msgid "Please review your answers and confirm these are correct" +msgstr "Darllenwch dros eich atebion a chadarnhewch fod y rhain yn gywir" + +#: templates/calculatedsummary.html:24 +msgid "Yes, I confirm these are correct" +msgstr "Rwy'n cadarnhau bod y rhain yn gywir" + #: templates/census-thank-you.html:9 templates/confirmation-email.html:8 msgid "There is a problem with this page" msgstr "Mae problem gyda'r dudalen hon" #: templates/census-thank-you.html:22 msgid "Thank you for completing your census" -msgstr "Diolch am gwblhau'r cyfrifiad" +msgstr "Diolch am gwblhau eich cyfrifiad" #: templates/census-thank-you.html:26 msgid "Thank you for completing the survey" @@ -714,7 +766,7 @@ msgstr "Mae eich cyfrifiad wedi cael ei gyflwyno ar gyfer y llety yn {di #: templates/census-thank-you.html:37 msgid "Anyone staying at this accommodation for at least 6 months needs to fill in their own individual census, including staff. Your Census Officer will provide you with census forms for your residents." -msgstr "Bydd angen i unrhyw un sy'n aros yn y sefydliad hwn am o leiaf 6 mis lenwi ei gyfrifiad unigol ei hun, gan gynnwys y staff. Bydd Swyddog y Cyfrifiad yn dosbarthu ffurflenni'r cyfrifiad i'ch preswylwyr." +msgstr "Bydd angen i unrhyw un sy'n aros yn y llety hwn am o leiaf 6 mis lenwi ei gyfrifiad unigol ei hun, gan gynnwys y staff. Bydd Swyddog y Cyfrifiad yn dosbarthu ffurflenni'r cyfrifiad i'ch preswylwyr." #: templates/census-thank-you.html:49 msgid "Your personal information is protected by law and will be kept confidential" @@ -726,9 +778,9 @@ msgstr "Cael e-bost cadarnhau" #: templates/census-thank-you.html:56 msgid "If you would like to be sent confirmation that you have completed your census, enter your email address" -msgstr "Os hoffech gael cadarnhad eich bod wedi cwblhau eich cyfrifiad, nodwch eich cyfeiriad e-bost" +msgstr "Os hoffech gael cadarnhad eich bod wedi cwblhau eich cyfrifiad, rhowch eich cyfeiriad e-bost" -#: templates/confirm-email.html:13 templates/partials/answers/address.html:56 +#: templates/confirm-email.html:12 templates/partials/answers/address.html:55 #: templates/question.html:10 #, python-format msgid "There is a problem with your answer" @@ -736,9 +788,9 @@ msgid_plural "There are %(num)s problems with your answer" msgstr[0] "" msgstr[1] "Mae problem gyda'ch ateb" msgstr[2] "Mae %(num)s broblem gyda'ch ateb" -msgstr[3] "Mae %(num)s broblem gyda'ch ateb" -msgstr[4] "Mae %(num)s broblem gyda'ch ateb" -msgstr[5] "Mae %(num)s broblem gyda'ch ateb" +msgstr[3] "Mae %(num)s problem gyda'ch ateb" +msgstr[4] "Mae %(num)s phroblem gyda'ch ateb" +msgstr[5] "Mae %(num)s problem gyda'ch ateb" #: templates/confirmation-email-sent.html:3 msgid "Confirmation email sent" @@ -750,7 +802,7 @@ msgstr "Mae e-bost cadarnhau wedi cael ei anfon i {email}" #: templates/confirmation-email-sent.html:25 msgid "The email will be sent from census.2021@notifications.service.gov.uk" -msgstr "Caiff yr e-bost ei anfon gan census.2021@notifications.service.gov.uk" +msgstr "Caiff yr e-bost ei anfon o census.2021@notifications.service.gov.uk" #: templates/confirmation-email-sent.html:29 msgid "Didn't receive an email?" @@ -764,31 +816,23 @@ msgstr "Gall gymryd ychydig funudau i’r e-bost gyrraedd. Os na fydd yn cyrraed msgid "Send a confirmation email" msgstr "Anfon e-bost cadarnhau" -#: templates/confirmation.html:12 -msgid "Yes, I confirm these are correct" -msgstr "Ydw, rwy'n cadarnhau bod y rhain yn gywir" - -#: templates/confirmation.html:33 -msgid "Submitting" -msgstr "Yn cyflwyno" - #: templates/feedback-sent.html:6 msgid "Feedback sent" msgstr "Wedi anfon adborth" -#: templates/feedback-sent.html:19 +#: templates/feedback-sent.html:17 msgid "Thank you for your feedback" msgstr "Diolch am eich adborth" -#: templates/feedback-sent.html:20 +#: templates/feedback-sent.html:18 msgid "Your comments will help us make improvements to our surveys. We are not able to reply to comments, but we appreciate your feedback" msgstr "Bydd eich sylwadau yn ein helpu ni i wella ein harolygon. Ni allwn ymateb i sylwadau, ond rydym yn gwerthfawrogi eich adborth" -#: templates/feedback-sent.html:28 +#: templates/feedback-sent.html:24 msgid "Done" msgstr "Cwblhawyd" -#: templates/feedback.html:15 +#: templates/feedback.html:14 templates/view-submitted-response.html:16 msgid "Back" msgstr "Yn ôl" @@ -796,48 +840,34 @@ msgstr "Yn ôl" #, python-format msgid "There is a problem with your feedback" msgid_plural "There are %(num)s problems with your feedback" -msgstr[1] "Mae problem gyda'ch adborth" -msgstr[2] "Mae %(num)s o broblemau gyda'ch adborth" -msgstr[3] "Mae %(num)s o broblemau gyda'ch adborth" -msgstr[4] "Mae %(num)s o broblemau gyda'ch adborth" -msgstr[5] "Mae %(num)s o broblemau gyda'ch adborth" +msgstr[0] "" +msgstr[1] "Mae problem gyda'ch ateb" +msgstr[2] "Mae %(num)s broblem gyda'ch adborth" +msgstr[3] "Mae %(num)s problem gyda'ch adborth" +msgstr[4] "Mae %(num)s phroblem gyda'ch adborth" +msgstr[5] "Mae %(num)s problem gyda'ch adborth" -#: templates/feedback.html:41 +#: templates/feedback.html:39 msgid "Send feedback" msgstr "Anfon adborth" -#: templates/hub.html:40 +#: templates/hub.html:7 msgid "If you can’t answer someone else’s questions" msgstr "Os na allwch chi ateb cwestiynau rhywun arall" -#: templates/interstitial.html:23 templates/partials/question.html:27 +#: templates/interstitial.html:23 templates/partials/question.html:25 msgid "If you can’t answer questions for this person" -msgstr "Os na allwch ateb cwestiynau ar ran yr unigolyn hwn" +msgstr "Os na allwch chi ateb cwestiynau ar ran yr unigolyn hwn" -#: templates/interstitial.html:32 templates/layouts/_questionnaire.html:27 +#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:27 msgid "Save and continue" msgstr "Cadw a pharhau" #: templates/introduction.html:10 msgid "Introduction" -msgstr "" - -#: templates/introduction.html:17 -#, python-format -msgid "You are completing this for %(ru_name)s (%(trading_as_name)s)" -msgstr "Rydych chi'n cwblhau hwn ar gyfer %(ru_name)s (%(trading_as_name)s)" - -#: templates/introduction.html:21 -#, python-format -msgid "You are completing this for %(ru_name)s" -msgstr "Rydych chi'n cwblhau hwn ar gyfer %(ru_name)s" - -#: templates/introduction.html:25 -#, python-format -msgid "If the company details or structure have changed contact us on %(telephone_number)s or email %(email_address)s" -msgstr "Os yw manylion neu strwythur y cwmni wedi newid, cysylltwch â ni ar %(telephone_number)s neu e-bost % (email_address)s" +msgstr "Cyflwyniad" -#: templates/introduction.html:40 +#: templates/introduction.html:23 msgid "Your response is legally required" msgstr "Mae eich ymateb yn ofynnol yn gyfreithiol" @@ -869,41 +899,113 @@ msgstr "Mae eich atebion i'r arolwg wedi'u cadw. Rydych chi nawr wedi'ch allgofn msgid "Return to your account" msgstr "Dychwelyd i'ch cyfrif" -#: templates/summary.html:28 -msgid "Please review your answers and confirm these are correct" -msgstr "Adolygwch eich atebion a chadarnhewch fod y rhain yn gywir" - -#: templates/thank-you.html:4 +#: templates/thank-you.html:6 msgid "We’ve received your answers" -msgstr "" +msgstr "Rydym ni wedi derbyn eich atebion" -#: templates/thank-you.html:9 -msgid "Submission successful" -msgstr "" +#: templates/thank-you.html:15 +msgid "Back to surveys" +msgstr "Yn ôl i'r arolygon" -#: templates/thank-you.html:21 -msgid "Your answers were submitted for {ru_name} ({trading_as_name}) on {submitted_date_time}" -msgstr "" - -#: templates/thank-you.html:26 -msgid "Your answers were submitted for {ru_name} on {submitted_date_time}" -msgstr "" - -#: templates/thank-you.html:32 -msgid "Transaction ID: {transaction_id}" -msgstr "" +#: templates/thank-you.html:33 +msgid "Thank you for completing the {survey_title}" +msgstr "Diolch am gwblhau'r {survey_title}" -#: templates/thank-you.html:35 +#: templates/thank-you.html:45 msgid "Your answers will be processed in the next few weeks." -msgstr "" +msgstr "Caiff eich atebion eu prosesu yn ystod yr ychydig wythnosau nesaf." -#: templates/thank-you.html:36 +#: templates/thank-you.html:46 msgid "We may contact you to query your answers via phone or secure message." -msgstr "" +msgstr "Efallai y byddwn ni'n cysylltu â chi i drafod eich atebion dros y ffôn neu drwy neges ddiogel." -#: templates/thank-you.html:37 +#: templates/thank-you.html:47 msgid "For more information on how we use this data." -msgstr "" +msgstr "I gael rhagor o wybodaeth am sut rydym ni'n defnyddio'r data hyn." + +#: templates/thank-you.html:51 templates/view-submitted-response.html:69 +msgid "For security, you can no longer view or get a copy of your answers" +msgstr "Er diogelwch, ni allwch weld eich atebion na chael copi ohonynt mwyach" + +#: templates/thank-you.html:63 +msgid "For security, your answers will only be available to view for another " +msgstr "Er diogelwch, byddwch ond yn gallu gweld eich atebion am " + +#: templates/thank-you.html:64 +msgid "Get a copy of your answers" +msgstr "Cael copi o'ch atebion" + +#: templates/thank-you.html:66 +msgid "You can save or print your answers for your records." +msgstr "Gallwch gadw neu argraffu eich atebion ar gyfer eich cofnodion." + +#: templates/thank-you.html:71 +msgid "minute" +msgstr "munud" + +#: templates/thank-you.html:72 +msgid "minutes" +msgstr "munudau" + +#: templates/thank-you.html:73 +msgid "second" +msgstr "eiliad" + +#: templates/thank-you.html:74 +msgid "seconds" +msgstr "eiliadau" + +#: templates/thank-you.html:76 +msgid "For security, your answers will only be available to view for 45 minutes" +msgstr "Er diogelwch, byddwch ond yn gallu gweld eich atebion am 45 munud" + +#: templates/view-submitted-response.html:35 +msgid "Print answers" +msgstr "Argraffu atebion" + +#: templates/view-submitted-response.html:47 +msgid "Save answers as PDF" +msgstr "Cadw atebion fel PDF" + +#: templates/errors/401.html:3 +msgid "Page is not available" +msgstr "Nid yw'r dudalen ar gael" + +#: templates/errors/401.html:6 +msgid "Sorry, you need to sign in again" +msgstr "Mae'n ddrwg gennym, mae angen i chi fewngofnodi eto" + +#: templates/errors/401.html:7 +msgid "This is because you have either:" +msgstr "Mae hyn oherwydd eich bod chi naill ai:" + +#: templates/errors/401.html:9 +msgid "been inactive for 45 minutes and your session has timed out to protect your information" +msgstr "wedi bod yn anweithgar am 45 munud a bod eich sesiwn wedi cyrraedd y terfyn amser er mwyn diogelu eich gwybodaeth" + +#: templates/errors/401.html:10 +msgid "followed a link to a page you are not signed in to" +msgstr "wedi dilyn dolen i dudalen nad ydych wedi mewngofnodi iddi" + +#: templates/errors/401.html:11 +msgid "followed a link to a survey that has already been submitted" +msgstr "wedi dilyn dolen i arolwg sydd eisoes wedi'i gyflwyno" + +#: templates/errors/401.html:14 +msgid "You will need to sign back in to access your account" +msgstr "Bydd angen i chi fewngofnodi eto i gael mynediad i'ch cyfrif" + +#: templates/errors/401.html:17 +msgid "To access this page you need to re-enter your access code." +msgstr "I fynd i'r dudalen hon, bydd angen i chi roi eich cod mynediad eto." + +#: templates/errors/401.html:21 +msgid "If you are completing a business survey, you need to sign back in to your account." +msgstr "Os ydych chi'n cwblhau arolwg busnes, bydd angen i chi fewngofnodi eto i'ch cyfrif." + +#: templates/errors/401.html:23 +msgid "If you started your survey using an access code, you need to re-enter your code." +msgstr "Os gwnaethoch chi ddechrau eich arolwg gan ddefnyddio cod mynediad, bydd angen i chi roi eich cod eto." #: templates/errors/403.html:7 msgid "You may need to update your browser to a newer version." @@ -913,10 +1015,18 @@ msgstr "Efallai y bydd angen i chi ddiweddaru eich porwr i fersiwn fwy newydd." msgid "If the problem still occurs, try using a different browser or device." msgstr "Os bydd y broblem yn parhau, rhowch gynnig ar ddefnyddio porwr neu ddyfais wahanol." -#: templates/errors/403.html:9 +#: templates/errors/403.html:10 msgid "For further help, please contact us." msgstr "I gael rhagor o gymorth, cysylltwch â ni." +#: templates/errors/403.html:13 +msgid "If you are completing a business survey and you need further help, please contact us." +msgstr "Os ydych chi'n cwblhau arolwg busnes a bod angen rhagor o gymorth arnoch, cysylltwch â ni." + +#: templates/errors/403.html:15 +msgid "If you started your survey using an access code and you need further help, please contact us." +msgstr "Os gwnaethoch chi ddechrau eich arolwg gan ddefnyddio cod mynediad a bod angen rhagor o gymorth arnoch, cysylltwch â ni." + #: templates/errors/404.html:3 templates/errors/404.html:6 msgid "Page not found" msgstr "Heb ddod o hyd i'r dudalen" @@ -929,10 +1039,22 @@ msgstr "Os gwnaethoch chi roi cyfeiriad gwe, gwnewch yn siŵr ei fod yn gywir." msgid "If you pasted the web address, check you copied the whole address." msgstr "Os gwnaethoch chi ludo'r cyfeiriad gwe, gwnewch yn siŵr eich bod wedi copïo'r cyfeiriad cyfan." -#: templates/errors/404.html:9 +#: templates/errors/404.html:10 msgid "If the web address is correct or you selected a link or button, contact us for more help." msgstr "Os yw'r cyfeiriad gwe yn gywir, neu os gwnaethoch chi ddewis dolen neu fotwm, cysylltwch â ni am fwy o gymorth." +#: templates/errors/404.html:12 +msgid "If the web address is correct or you selected a link or button, please see the following help links." +msgstr "Os yw'r cyfeiriad gwe yn gywir, neu os gwnaethoch chi ddewis dolen neu fotwm, edrychwch ar y dolenni cymorth canlynol." + +#: templates/errors/404.html:14 templates/errors/submission-failed.html:15 +msgid "If you are completing a business survey, please contact us." +msgstr "Os ydych chi'n cwblhau arolwg busnes, cysylltwch â ni." + +#: templates/errors/404.html:16 templates/errors/submission-failed.html:17 +msgid "If you started your survey using an access code, please contact us." +msgstr "Os gwnaethoch chi ddechrau eich arolwg gan ddefnyddio cod mynediad, cysylltwch â ni." + #: templates/errors/500.html:3 msgid "An error has occurred" msgstr "Mae gwall wedi digwydd" @@ -949,65 +1071,53 @@ msgstr "Rhowch gynnig arall arni yn nes ymlaen." msgid "If you have started a survey, your answers have been saved." msgstr "Os ydych chi wedi dechrau arolwg, mae eich atebion wedi cael eu cadw." -#: templates/errors/500.html:9 +#: templates/errors/500.html:10 msgid "Contact us if you need to speak to someone about your survey." msgstr "Cysylltwch â ni os oes angen i chi siarad â rhywun am eich arolwg." -#: templates/errors/no-cookie.html:3 -msgid "Page is not available" -msgstr "Nid yw'r dudalen ar gael" - -#: templates/errors/no-cookie.html:6 -msgid "Sorry there is a problem" -msgstr "Mae'n ddrwg gennym, mae problem wedi codi" +#: templates/errors/500.html:13 +msgid "If you are completing a business survey and you need to speak to someone about your survey, please contact us." +msgstr "Os ydych chi'n cwblhau arolwg busnes a bod angen i chi siarad â rhywun am eich arolwg, cysylltwch â ni." -#: templates/errors/no-cookie.html:7 -msgid "To access this page you need to enter your 16-character access code." -msgstr "I fynd i'r dudalen hon, bydd angen i chi roi eich cod mynediad 16 nod." +#: templates/errors/500.html:15 +msgid "If you started your survey using an access code and you need to speak to someone about your survey, please contact us." +msgstr "Os gwnaethoch chi ddechrau eich arolwg gan ddefnyddio cod mynediad a bod angen i chi siarad â rhywun am eich arolwg, cysylltwch â ni." -#: templates/errors/session-expired.html:3 -msgid "Session timed out" -msgstr "" - -#: templates/errors/session-expired.html:6 -msgid "Your session has timed out due to inactivity" -msgstr "Mae eich sesiwn wedi dod i ben gan nad oedd gweithgarwch" - -#: templates/errors/session-expired.html:7 -msgid "To help protect your information we have timed you out" -msgstr "Er mwyn helpu i ddiogelu eich gwybodaeth, rydyn ni wedi dod â’ch sesiwn i ben" - -#: templates/errors/session-expired.html:8 -msgid "You will need to enter your 16-character access code again to continue your census." -msgstr "Bydd angen i chi roi eich cod mynediad 16 nod i barhau â'ch cyfrifiad." - -#: templates/errors/submission-complete.html:3 +#: templates/errors/previously-submitted.html:3 msgid "Submission Complete" -msgstr "" +msgstr "Cyflwyniad wedi'i Gwblhau" -#: templates/errors/submission-complete.html:6 +#: templates/errors/previously-submitted.html:6 msgid "This page is no longer available" msgstr "Nid yw'r dudalen hon ar gael mwyach" -#: templates/errors/submission-complete.html:7 -msgid "Your census has been submitted" -msgstr "Mae eich cyfrifiad wedi cael ei gyflwyno" +#: templates/errors/previously-submitted.html:7 +msgid "Your survey has been submitted" +msgstr "Mae eich arolwg wedi cael ei gyflwyno" + +#: templates/errors/previously-submitted.html:8 +msgid "Return to previous page" +msgstr "Dychwelyd i'r dudalen flaenorol" #: templates/errors/submission-failed.html:9 -msgid "You can try to submit your census again" -msgstr "Gallwch geisio cyflwyno eich cyfrifiad eto" +msgid "You can try to submit your survey again" +msgstr "Gallwch geisio cyflwyno eich arolwg eto" -#: templates/errors/submission-failed.html:10 +#: templates/errors/submission-failed.html:11 msgid "If this problem keeps happening, please contact us for help." msgstr "Os bydd y broblem hon yn parhau, cysylltwch â ni i gael cymorth." +#: templates/errors/submission-failed.html:13 +msgid "If this problem keeps happening, please see the following help links." +msgstr "Os bydd y broblem hon yn parhau, edrychwch ar y dolenni cymorth canlynol." + #: templates/individual_response/confirmation-post.html:15 msgid "A letter has been sent to Individual Resident at {display_address}" msgstr "Mae llythyr wedi cael ei anfon at Breswylydd Unigol yn {display_address}" #: templates/individual_response/confirmation-post.html:17 msgid "The letter with an individual access code should arrive soon for them to complete their own census" -msgstr "Dylai’r llythyr â chod mynediad unigol gyrraedd yn fuan er mwyn i’r unigolyn allu llenwi ei gyfrifiad ei hun" +msgstr "Dylai’r llythyr â chod mynediad unigol gyrraedd yn fuan er mwyn i’r unigolyn allu cwblhau ei gyfrifiad ei hun" #: templates/individual_response/confirmation-text-message.html:15 msgid "We have sent a text to {mobile_number}" @@ -1015,11 +1125,11 @@ msgstr "Rydym wedi anfon neges destun i {mobile_number}" #: templates/individual_response/confirmation-text-message.html:17 msgid "The text message with an individual access code should arrive soon for them to complete their own census" -msgstr "Dylai’r neges destun â chod mynediad unigol gyrraedd yn fuan er mwyn i’r unigolyn allu llenwi ei gyfrifiad ei hun" +msgstr "Dylai’r neges destun â chod mynediad unigol gyrraedd yn fuan er mwyn i’r unigolyn allu cwblhau ei gyfrifiad ei hun" -#: templates/individual_response/confirmation-text-message.html:26 +#: templates/individual_response/confirmation-text-message.html:25 msgid "The text will be sent from Census2021" -msgstr "Caiff y testun ei anfon gan Census2021" +msgstr "Caiff y neges destun ei hanfon o Census2021" #: templates/individual_response/interstitial.html:8 msgid "If you can't answer questions for others in your household" @@ -1041,63 +1151,17 @@ msgstr "Gwneud cais am gyfrifiad ar wahân" msgid "To request a census in a different format or for further help, please contact us" msgstr "I wneud cais am gyfrifiad mewn fformat gwahanol neu am fwy o gymorth, cysylltwch â ni" -#: templates/layouts/_base.html:19 -msgid "Previous" -msgstr "Blaenorol" - -#: templates/layouts/_base.html:62 -msgid "Tell us whether you accept cookies" -msgstr "Dywedwch wrthym a ydych chi'n derbyn cwcis" - -#: templates/layouts/_base.html:63 -msgid "We use cookies to collect information about how you use census.gov.uk. We use this information to make the website work as well as possible and improve our services." -msgstr "Rydym ni'n defnyddio cwcis i gasglu gwybodaeth am y ffordd rydych chi'n defnyddio cyfrifiad.gov.uk. Rydym ni'n defnyddio'r wybodaeth hon i sicrhau bod y wefan yn gweithio cystal â phosibl ac i wella ein gwasanaethau." - -#: templates/layouts/_base.html:64 -msgid "You’ve accepted all cookies. You can change your cookie preferences at any time." -msgstr "Rydych chi wedi derbyn yr holl gwcis Gallwch chi newid eich dewisiadau o ran cwcis ar unrhyw adeg." - -#: templates/layouts/_base.html:65 -msgid "Accept all cookies" -msgstr "Derbyn yr holl gwcis" - -#: templates/layouts/_base.html:66 -msgid "Set cookie preferences" -msgstr "Gosod dewisiadau o ran cwcis" - -#: templates/layouts/_base.html:67 -#: templates/partials/introduction/preview.html:30 -#: templates/partials/summary/collapsible-summary.html:14 -msgid "Hide" -msgstr "Cuddio" - -#: templates/layouts/_base.html:115 -msgid "Skip to main content" -msgstr "Neidio i’r prif gynnwy" - -#: templates/layouts/_questionnaire.html:38 +#: templates/layouts/_questionnaire.html:40 msgid "Choose another section and return to this later" msgstr "Dewiswch adran arall a dod yn ôl yn nes ymlaen" -#: templates/layouts/configs/_save-sign-out-button.html:5 -msgid "Save and sign out" -msgstr "Cadw ac allgofnodi" - -#: templates/layouts/configs/_save-sign-out-button.html:7 -msgid "Save and complete later" -msgstr "Cadw a chwblhau yn nes ymlaen" - -#: templates/layouts/configs/_save-sign-out-button.html:25 -msgid "Exit" -msgstr "Cau" - -#: templates/macros/helpers.html:29 +#: templates/macros/helpers.html:13 msgid "Interviewer note:" msgstr "Nodyn i’r cyfwelydd:" -#: templates/partials/answer-guidance.html:19 -#: templates/partials/definition.html:19 -#: templates/partials/individual-response-guidance.html:14 +#: templates/partials/answer-guidance.html:18 +#: templates/partials/definition.html:18 +#: templates/partials/individual-response-guidance.html:13 msgid "Hide this" msgstr "Cuddio hwn" @@ -1109,7 +1173,7 @@ msgstr "Cyfeiriad e-bost" msgid "This will not be stored and only used once to send your confirmation" msgstr "Ni fydd hwn yn cael ei storio a dim ond unwaith y bydd yn cael ei ddefnyddio er mwyn anfon cadarnhad atoch" -#: templates/partials/email-form.html:39 +#: templates/partials/email-form.html:38 msgid "Send confirmation" msgstr "Anfon cadarnhad" @@ -1119,17 +1183,17 @@ msgstr "Beth yw eich barn chi am y gwasanaeth hwn?" #: templates/partials/feedback-call-to-action.html:7 msgid "Your comments will help us make improvements" -msgstr "Bydd eich sylwadau yn ein helpu ni i wella’r gwasanaeth" +msgstr "Bydd eich sylwadau yn ein helpu ni i wneud gwelliannau" #: templates/partials/feedback-call-to-action.html:9 msgid "Give feedback" msgstr "Rhoi adborth" -#: templates/partials/individual-response-guidance.html:25 +#: templates/partials/individual-response-guidance.html:24 msgid "You can share your household access code with the people you live with so they can complete their own sections." msgstr "Gallwch rannu cod mynediad eich cartref â’r bobl rydych chi’n byw gyda nhw fel y gallant gwblhau eu hadrannau eu hunain." -#: templates/partials/individual-response-guidance.html:26 +#: templates/partials/individual-response-guidance.html:25 msgid "If this is not possible, there are other ways each person can complete their own census." msgstr "Os nad yw hyn yn bosibl, mae ffyrdd eraill y gall pob person lenwi ei gyfrifiad ei hun." @@ -1141,115 +1205,115 @@ msgstr "Dyma'r cwestiwn a gafodd ei weld ddiwethaf yn yr adran hon" msgid "You can also go back to the start of the section" msgstr "Gallwch chi hefyd fynd yn ôl i ddechrau'r adran" -#: templates/partials/question.html:62 +#: templates/partials/question.html:60 msgid "Selecting this will clear your answer" msgstr "Bydd dewis hwn yn clirio eich ateb" -#: templates/partials/question.html:63 +#: templates/partials/question.html:61 msgid "cleared" msgstr "wedi'i glirio" -#: templates/partials/question.html:66 +#: templates/partials/question.html:64 msgid "Selecting this will deselect any selected options" msgstr "Bydd dewis hwn yn dad-ddewis unrhyw opsiynau sydd wedi'u dewis" -#: templates/partials/question.html:67 templates/partials/question.html:75 +#: templates/partials/question.html:65 templates/partials/question.html:73 msgid "deselected" msgstr "dad-ddewiswyd" -#: templates/partials/question.html:71 +#: templates/partials/question.html:69 msgid "Or" msgstr "Neu" -#: templates/partials/answers/address.html:10 +#: templates/partials/answers/address.html:9 msgid "Address line 1" msgstr "Llinell cyfeiriad 1" -#: templates/partials/answers/address.html:15 +#: templates/partials/answers/address.html:14 msgid "Address line 2" msgstr "Llinell cyfeiriad 2" -#: templates/partials/answers/address.html:19 +#: templates/partials/answers/address.html:18 msgid "Town or city" msgstr "Tref neu ddinas" -#: templates/partials/answers/address.html:23 +#: templates/partials/answers/address.html:22 msgid "Postcode" msgstr "Cod post" -#: templates/partials/answers/address.html:29 +#: templates/partials/answers/address.html:33 +msgid "Enter address or postcode and select from results" +msgstr "Rhowch y cyfeiriad neu'r cod post a dewiswch o'r canlyniadau" + +#: templates/partials/answers/address.html:35 msgid "Search for an address" msgstr "Chwilio am gyfeiriad" -#: templates/partials/answers/address.html:30 +#: templates/partials/answers/address.html:36 msgid "Manually enter address" msgstr "Nodwch y cyfeiriad â llaw" -#: templates/partials/answers/address.html:36 -msgid "Enter address or postcode and select from results" -msgstr "Nodwch y cyfeiriad neu'r cod post a dewiswch o'r canlyniadau" - -#: templates/partials/answers/address.html:42 +#: templates/partials/answers/address.html:41 msgid "Use up and down keys to navigate suggestions once you’ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures." msgstr "Defnyddiwch y bysellau i fyny ac i lawr i edrych drwy'r awgrymiadau unwaith y byddwch chi wedi teipio mwy na dau nod. Defnyddiwch y fysell ‘enter’ i ddewis awgrym. Gall defnyddwyr dyfeisiau cyffwrdd symud o gwmpas drwy gyffwrdd neu sweipio." -#: templates/partials/answers/address.html:43 +#: templates/partials/answers/address.html:42 msgid "You have selected" msgstr "Rydych chi wedi dewis" -#: templates/partials/answers/address.html:44 +#: templates/partials/answers/address.html:43 msgid "Enter 3 or more characters for suggestions." msgstr "Rhowch 3 nod neu fwy i gael awgrymiadau." -#: templates/partials/answers/address.html:45 +#: templates/partials/answers/address.html:44 msgid "There is one suggestion available." msgstr "Mae un awgrym ar gael." -#: templates/partials/answers/address.html:46 +#: templates/partials/answers/address.html:45 msgid "There are {n} suggestions available." -msgstr "Mae {n} o awgrymiadau ar gael." +msgstr "Mae {n} awgrym ar gael." -#: templates/partials/answers/address.html:47 +#: templates/partials/answers/address.html:46 msgid "Results have been limited to 10 suggestions. Type more characters to improve your search" msgstr "Mae'r canlyniadau wedi cael eu cyfyngu i 10 awgrym. Teipiwch fwy o nodau i wella eich chwiliad" -#: templates/partials/answers/address.html:48 +#: templates/partials/answers/address.html:47 msgid "There are {n} for {x}" msgstr "Mae {n} ar gyfer {x}" -#: templates/partials/answers/address.html:49 +#: templates/partials/answers/address.html:48 msgid "{n} addresses" msgstr "{n} o gyfeiriadau" -#: templates/partials/answers/address.html:50 +#: templates/partials/answers/address.html:49 msgid "Enter more of the address to improve results" msgstr "Rhowch fwy o'r cyfeiriad i wella canlyniadau" -#: templates/partials/answers/address.html:51 +#: templates/partials/answers/address.html:50 msgid "Select an address" msgstr "Dewiswch gyfeiriad" -#: templates/partials/answers/address.html:52 +#: templates/partials/answers/address.html:51 msgid "No results found. Try entering a different part of the address" msgstr "Heb ddod o hyd i unrhyw ganlyniadau. Ceisiwch ddefnyddio rhan wahanol o'r cyfeiriad" -#: templates/partials/answers/address.html:53 +#: templates/partials/answers/address.html:52 msgid "{n} results found. Enter more of the address to improve results" -msgstr "Wedi dod o hyd i {n} o ganlyniadau. Rhowch fwy o'r cyfeiriad i wella'r canlyniadau" +msgstr "Wedi dod o hyd i {n} ganlyniad. Rhowch fwy o'r cyfeiriad i wella'r canlyniadau" -#: templates/partials/answers/address.html:54 +#: templates/partials/answers/address.html:53 msgid "Enter more of the address to get results" msgstr "Rhowch fwy o'r cyfeiriad i gael canlyniadau" -#: templates/partials/answers/address.html:58 +#: templates/partials/answers/address.html:57 msgid "Select or manually enter an address" msgstr "Dewiswch gyfeiriad neu nodwch â llaw" -#: templates/partials/answers/address.html:59 +#: templates/partials/answers/address.html:58 msgid "Sorry, there was a problem loading addresses" msgstr "Mae'n ddrwg gennym, roedd problem wrth lwytho cyfeiriadau" -#: templates/partials/answers/address.html:60 +#: templates/partials/answers/address.html:59 msgid "Enter address manually" msgstr "Teipiwch y cyfeiriad eich hun" @@ -1269,92 +1333,57 @@ msgstr "Mis" msgid "Year" msgstr "Blwyddyn" -#: templates/partials/answers/radio.html:14 +#: templates/partials/answers/radio.html:15 msgid "Clear selection" msgstr "Clirio’r dewis" -#: templates/partials/answers/textarea.html:16 -#: templates/partials/answers/textfield.html:28 +#: templates/partials/answers/textarea.html:20 +#: templates/partials/answers/textfield.html:45 msgid "You have {x} character remaining" msgstr "Mae gennych chi {x} nod ar ôl" -#: templates/partials/answers/textarea.html:17 -#: templates/partials/answers/textfield.html:29 +#: templates/partials/answers/textarea.html:21 +#: templates/partials/answers/textfield.html:46 msgid "You have {x} characters remaining" -msgstr "Mae gennych chi {x} nod ar ôl" - -#: templates/partials/answers/textfield.html:26 -msgid "{x} character too many" -msgstr "{x} o nodau yn ormod" +msgstr "Mae gennych chi {x} o nodau ar ôl" -#: templates/partials/answers/textfield.html:27 -msgid "{x} characters too many" -msgstr "{x} nod yn ormod" - -#: templates/partials/answers/textfield.html:35 +#: templates/partials/answers/textfield.html:25 msgid "Use up and down keys to navigate suggestions once you've typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures." msgstr "Defnyddiwch y bysellau i fyny ac i lawr i edrych drwy'r awgrymiadau unwaith y byddwch chi wedi teipio mwy na dau nod. Defnyddiwch y fysell ‘enter’ i ddewis awgrym. Gall defnyddwyr dyfeisiau cyffwrdd symud o gwmpas drwy gyffwrdd neu sweipio." -#: templates/partials/answers/textfield.html:36 +#: templates/partials/answers/textfield.html:26 msgid "Continue entering to improve suggestions" msgstr "Parhewch i roi nodau er mwyn gwella'r awgrymiadau" -#: templates/partials/answers/textfield.html:37 +#: templates/partials/answers/textfield.html:27 msgid "Suggestions" msgstr "Awgrymiadau" -#: templates/partials/answers/textfield.html:38 +#: templates/partials/answers/textfield.html:28 msgid "No results found" msgstr "Heb ganfod unrhyw ganlyniadau" -#: templates/partials/answers/textfield.html:39 +#: templates/partials/answers/textfield.html:29 msgid "Continue entering to get suggestions" msgstr "Parhewch i roi nodau er mwyn cael awgrymiadau" -#: templates/partials/introduction/preview.html:29 -#: templates/partials/summary/collapsible-summary.html:13 -msgid "Show" -msgstr "Dangos" - -#: templates/partials/introduction/preview.html:48 -#: templates/partials/summary/collapsible-summary.html:57 -msgid "Show all" -msgstr "Dangos popeth" +#: templates/partials/answers/textfield.html:43 +msgid "{x} character too many" +msgstr "{x} nod yn ormod" -#: templates/partials/introduction/preview.html:49 -#: templates/partials/summary/collapsible-summary.html:58 -msgid "Hide all" -msgstr "Cuddio popeth" +#: templates/partials/answers/textfield.html:44 +msgid "{x} characters too many" +msgstr "{x} o nodau yn ormod" #: templates/partials/introduction/start-survey.html:5 msgid "Start survey" -msgstr "Cychwyn yr arolwg" - -#: templates/partials/summary/collapsible-summary.html:35 -#: templates/partials/summary/summary.html:18 -msgid "No answer provided" -msgstr "Ni roddwyd ateb" +msgstr "Dechrau'r arolwg" -#: templates/partials/summary/collapsible-summary.html:36 -#: templates/partials/summary/list-summary.html:8 -#: templates/partials/summary/summary.html:19 +#: templates/partials/summary/list-summary.html:7 msgid "Change" msgstr "Newid" -#: templates/partials/summary/collapsible-summary.html:37 -#: templates/partials/summary/summary.html:20 -msgid "Change your answer for:" -msgstr "Newidiwch eich ateb ar gyfer:" - -#: templates/partials/summary/list-summary.html:9 +#: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "Newid manylion ar gyfer {item_name}" -#: templates/partials/summary/list-summary.html:10 -msgid "Remove" -msgstr "Dileu" - -#: templates/partials/summary/list-summary.html:11 -msgid "Remove {item_name}" -msgstr "Tynnu {item_name}" - diff --git a/app/translations/messages.pot b/app/translations/messages.pot index c10fd908ab..904aa41080 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-02-21 13:30+0000\n" +"POT-Creation-Date: 2023-03-03 11:01+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" From 9bdfdc569ababe6266e8749a78db0d26d698e7b4 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:53:14 +0000 Subject: [PATCH 183/567] Schemas v3.44.3 (#1055) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c7db277f79..60eabaa921 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.44.2 +v3.44.3 From 00ffe0a6950a3cf1318debf4cd0f31ef97781458 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:39:38 +0000 Subject: [PATCH 184/567] Add preview questions (#996) --- .design-system-version | 2 +- app/helpers/template_helpers.py | 6 + app/questionnaire/placeholder_parser.py | 15 +- app/questionnaire/placeholder_renderer.py | 9 +- app/questionnaire/questionnaire_schema.py | 15 +- app/questionnaire/variants.py | 2 +- app/routes/questionnaire.py | 60 ++ app/translations/messages.pot | 37 +- app/views/contexts/context.py | 2 + app/views/contexts/preview/__init__.py | 3 + app/views/contexts/preview/preview_block.py | 30 + app/views/contexts/preview/preview_group.py | 31 + .../contexts/preview/preview_question.py | 67 ++ app/views/contexts/preview_context.py | 67 ++ app/views/contexts/section_preview_context.py | 50 ++ app/views/handlers/pdf_response.py | 75 +++ app/views/handlers/preview_questions_pdf.py | 14 + app/views/handlers/view_preview_questions.py | 43 ++ .../handlers/view_submitted_response_pdf.py | 54 +- schemas/test/en/test_benchmark_business.json | 6 +- schemas/test/en/test_hub_and_spoke.json | 2 +- .../en/test_hub_and_spoke_custom_content.json | 2 +- schemas/test/en/test_introduction.json | 463 ++++++++++++- schemas/test/en/test_introduction_hub.json | 636 ++++++++++++++++++ ..._calculated_summary_repeating_section.json | 2 +- .../test_new_routing_answered_unanswered.json | 2 +- templates/introduction.html | 6 + templates/partials/answer-guidance.html | 2 +- .../individual-response-guidance.html | 4 +- templates/partials/introduction/basic.html | 2 +- templates/partials/introduction/preview.html | 1 - templates/partials/preview-question.html | 73 ++ templates/partials/question.html | 3 +- templates/preview.html | 126 ++++ tests/app/questionnaire/test_path_finder.py | 12 +- tests/app/views/contexts/__init__.py | 14 +- tests/app/views/contexts/conftest.py | 29 + .../views/contexts/test_preview_context.py | 289 ++++++++ .../handlers/test_preview_questions_pdf.py | 17 + .../handlers/test_view_preview_questions.py | 44 ++ .../base_pages/introduction.page.js | 4 + .../base_pages/question-preview.page.js | 13 + tests/functional/spec/preview.spec.js | 87 +++ tests/integration/routes/test_errors.py | 9 +- .../routes/test_view_preview_questions.py | 45 ++ 45 files changed, 2387 insertions(+), 88 deletions(-) create mode 100644 app/views/contexts/preview/__init__.py create mode 100644 app/views/contexts/preview/preview_block.py create mode 100644 app/views/contexts/preview/preview_group.py create mode 100644 app/views/contexts/preview/preview_question.py create mode 100644 app/views/contexts/preview_context.py create mode 100644 app/views/contexts/section_preview_context.py create mode 100644 app/views/handlers/pdf_response.py create mode 100644 app/views/handlers/preview_questions_pdf.py create mode 100644 app/views/handlers/view_preview_questions.py create mode 100644 schemas/test/en/test_introduction_hub.json create mode 100644 templates/partials/preview-question.html create mode 100644 templates/preview.html create mode 100644 tests/app/views/contexts/test_preview_context.py create mode 100644 tests/app/views/handlers/test_preview_questions_pdf.py create mode 100644 tests/app/views/handlers/test_view_preview_questions.py create mode 100644 tests/functional/base_pages/question-preview.page.js create mode 100644 tests/functional/spec/preview.spec.js create mode 100644 tests/integration/routes/test_view_preview_questions.py diff --git a/.design-system-version b/.design-system-version index 8f7cbd8b05..33eba4acf9 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -62.0.0 +62.0.1 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index a37a8526c5..9dcec3de5c 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -54,6 +54,11 @@ def __init__( "EQ_GOOGLE_TAG_MANAGER_AUTH" ) self._survey_type = cookie_session.get("theme") + self._preview_enabled = ( + self._survey_config.schema.preview_enabled + if self._survey_config.schema + else False + ) @property def context(self) -> dict[str, Any]: @@ -77,6 +82,7 @@ def context(self) -> dict[str, Any]: "google_tag_manager_id": self._google_tag_manager_id, "google_tag_manager_auth": self._google_tag_manager_auth, "survey_type": self._survey_type, + "preview_enabled": self._preview_enabled, "masthead_logo": self._survey_config.masthead_logo, "masthead_logo_mobile": self._survey_config.masthead_logo_mobile, } diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index fc3c174e32..0f5aac3a9e 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -46,6 +46,7 @@ def __init__( renderer: "PlaceholderRenderer", list_item_id: Optional[str] = None, location: Union[Location, RelationshipLocation, None] = None, + placeholder_preview_mode: Optional[bool] = False, ): self._answer_store = answer_store self._list_store = list_store @@ -58,6 +59,7 @@ def __init__( self._placeholder_map: MutableMapping[ str, Union[ValueSourceEscapedTypes, ValueSourceTypes, None] ] = {} + self._placeholder_preview_mode = placeholder_preview_mode self._value_source_resolver = ValueSourceResolver( answer_store=self._answer_store, @@ -82,9 +84,12 @@ def __call__( ] = self._parse_placeholder(placeholder) return self._placeholder_map - def _parse_placeholder( - self, placeholder: Mapping - ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes, TransformedValueTypes]: + def _parse_placeholder(self, placeholder: Mapping) -> Any: + if self._placeholder_preview_mode and not self._all_value_sources_metadata( + placeholder + ): + return f'{{{placeholder["placeholder"]}}}' + try: return self._parse_transforms(placeholder["transforms"]) except KeyError: @@ -134,3 +139,7 @@ def _resolve_value_source_list( else: values.append(value) return values + + def _all_value_sources_metadata(self, placeholder: Mapping) -> bool: + sources = self._schema.get_values_for_key(placeholder, key="source") + return all(source == "metadata" for source in sources) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 2d524c8cba..67cad82565 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -28,7 +28,9 @@ def __init__( response_metadata: Mapping, schema: QuestionnaireSchema, location: Union[None, Location, RelationshipLocation] = None, + placeholder_preview_mode: Optional[bool] = False, ): + self._placeholder_preview_mode = placeholder_preview_mode self._language = language self._answer_store = answer_store self._list_store = list_store @@ -76,13 +78,18 @@ def render_placeholder( list_item_id=list_item_id, location=self._location, renderer=self, + placeholder_preview_mode=self._placeholder_preview_mode, ) placeholder_data = QuestionnaireSchema.get_mutable_deepcopy(placeholder_data) if "text_plural" in placeholder_data: plural_schema: Mapping[str, dict] = placeholder_data["text_plural"] - count = self.get_plural_count(plural_schema["count"]) + count = ( + 0 + if self._placeholder_preview_mode + else self.get_plural_count(plural_schema["count"]) + ) plural_form_key = get_plural_form_key(count, self._language) plural_forms: Mapping[str, str] = plural_schema["forms"] diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index fd8f254cce..e293823464 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -115,6 +115,11 @@ def region_code(self) -> Optional[str]: region_code: Optional[str] = self.json.get("region_code") return region_code + @cached_property + def preview_enabled(self) -> bool: + preview_enabled: bool = self.json.get("preview_questions", False) + return preview_enabled + @cached_property def parent_id_map(self) -> Any: return self.serialize(self._parent_id_map) @@ -414,7 +419,7 @@ def _section_ids_associated_to_list_name(self, list_name: str) -> list[str]: for section in self.get_sections(): ignore_keys = ["question_variants", "content_variants"] - when_rules = self._get_values_for_key(section, "when", ignore_keys) + when_rules = self.get_values_for_key(section, "when", ignore_keys) rule: Union[Mapping, list] = next(when_rules, []) if self._is_list_name_in_rule(rule, list_name): @@ -777,7 +782,7 @@ def has_operator(rule: Any) -> bool: operator in rule for operator in OPERATION_MAPPING ) - def _get_values_for_key( + def get_values_for_key( self, block: Mapping, key: str, ignore_keys: Optional[list[str]] = None ) -> Generator: ignore_keys = ignore_keys or [] @@ -788,10 +793,10 @@ def _get_values_for_key( if k == key: yield v if isinstance(v, dict): - yield from self._get_values_for_key(v, key, ignore_keys) + yield from self.get_values_for_key(v, key, ignore_keys) elif isinstance(v, (list, tuple)): for d in v: - yield from self._get_values_for_key(d, key, ignore_keys) + yield from self.get_values_for_key(d, key, ignore_keys) except AttributeError: continue @@ -845,7 +850,7 @@ def _get_error_messages(self) -> dict: def _populate_when_rules_section_dependencies(self) -> None: for section in self.get_sections(): - when_rules = self._get_values_for_key(section, "when") + when_rules = self.get_values_for_key(section, "when") rules: Union[Mapping, list] = next(when_rules, []) if rules_section_dependencies := self._get_rules_section_dependencies( diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index 8a42360d1b..d6c207ea4e 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -95,7 +95,7 @@ def transform_variants( answer_store, list_store, current_location, -): +) -> ImmutableDict: output_block = dict(block) if "question_variants" in block: question = choose_question_to_display( diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 3c6fadc435..52c05f1cb1 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -33,6 +33,10 @@ from app.submitter.previously_submitted_exception import PreviouslySubmittedException from app.utilities.schema import load_schema_from_metadata from app.views.contexts import HubContext +from app.views.contexts.preview_context import ( + PreviewContext, + PreviewNotEnabledException, +) from app.views.handlers.block_factory import get_block_handler from app.views.handlers.confirm_email import ConfirmEmail from app.views.handlers.confirmation_email import ( @@ -41,6 +45,7 @@ ConfirmationEmailNotEnabled, ) from app.views.handlers.feedback import Feedback, FeedbackNotEnabled +from app.views.handlers.preview_questions_pdf import PreviewQuestionsPDF from app.views.handlers.section import SectionHandler from app.views.handlers.submission import SubmissionHandler from app.views.handlers.submit_questionnaire import SubmitQuestionnaireHandler @@ -224,6 +229,36 @@ def submit_questionnaire( ) +@questionnaire_blueprint.route("/preview", methods=["GET"]) +@with_questionnaire_store +@with_schema +def get_preview(schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore): + try: + preview_context = PreviewContext( + language=flask_babel.get_locale().language, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, + ) + except PreviewNotEnabledException as exc: + raise NotFound from exc + + schema_type = schema.json["questionnaire_flow"].get("type") + + context = { + "schema_type": schema_type, + "preview": preview_context(), + "pdf_url": url_for(".get_preview_questions_pdf"), + } + + return render_template( + template="preview", content=context, page_title=preview_context.get_page_title() + ) + + @questionnaire_blueprint.route("sections//", methods=["GET", "POST"]) @questionnaire_blueprint.route( "sections///", methods=["GET", "POST"] @@ -417,6 +452,31 @@ def get_view_submitted_response(schema, questionnaire_store): return view_submitted_response.get_rendered_html() +@questionnaire_blueprint.route("/preview/download-pdf", methods=["GET"]) +@with_questionnaire_store +@with_schema +def get_preview_questions_pdf( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> Response: + view_preview_questions_pdf = PreviewQuestionsPDF( + schema, + questionnaire_store, + flask_babel.get_locale().language, + ) + + try: + path_or_file = view_preview_questions_pdf.get_pdf() + except PreviewNotEnabledException as exc: + raise NotFound from exc + + return send_file( + path_or_file=path_or_file, + mimetype=view_preview_questions_pdf.mimetype, + as_attachment=True, + download_name=view_preview_questions_pdf.filename, + ) + + @post_submission_blueprint.route("download-pdf", methods=["GET"]) @with_questionnaire_store @with_schema diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 904aa41080..fc21a30b16 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -223,15 +223,15 @@ msgstr "" msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:106 +#: app/helpers/template_helpers.py:112 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:137 +#: app/helpers/template_helpers.py:143 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:162 +#: app/helpers/template_helpers.py:168 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -470,6 +470,10 @@ msgstr "" msgid " (You)" msgstr "" +#: app/views/contexts/preview_context.py:66 +msgid "Preview survey questions" +msgstr "" + #: app/views/contexts/submission_metadata_context.py:14 msgid "Submitted on:" msgstr "" @@ -520,6 +524,14 @@ msgstr "" msgid "Answers submitted." msgstr "" +#: app/views/contexts/preview/preview_question.py:31 +msgid "You can answer with the following options:" +msgstr "" + +#: app/views/contexts/preview/preview_question.py:35 +msgid "You can answer with one of the following options:" +msgstr "" + #: app/views/handlers/confirm_email.py:38 msgid "Yes, send the confirmation email" msgstr "" @@ -876,7 +888,11 @@ msgstr "" msgid "Introduction" msgstr "" -#: templates/introduction.html:23 +#: templates/introduction.html:24 +msgid "View the questions you will be asked in this survey" +msgstr "" + +#: templates/introduction.html:29 msgid "Your response is legally required" msgstr "" @@ -1260,6 +1276,15 @@ msgid "" " of the section" msgstr "" +#: templates/partials/preview-question.html:46 +#: templates/partials/question.html:69 +msgid "Or" +msgstr "" + +#: templates/partials/preview-question.html:75 +msgid "{max_characters} characters can be added." +msgstr "" + #: templates/partials/question.html:60 msgid "Selecting this will clear your answer" msgstr "" @@ -1276,10 +1301,6 @@ msgstr "" msgid "deselected" msgstr "" -#: templates/partials/question.html:69 -msgid "Or" -msgstr "" - #: templates/partials/answers/address.html:9 msgid "Address line 1" msgstr "" diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index d714b900c3..4ba0301f6a 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -28,6 +28,7 @@ def __init__( self._progress_store = progress_store self._metadata = metadata self._response_metadata = response_metadata + self._placeholder_preview_mode = self._schema.preview_enabled self._router = Router( self._schema, @@ -45,4 +46,5 @@ def __init__( metadata=self._metadata, response_metadata=self._response_metadata, schema=self._schema, + placeholder_preview_mode=self._placeholder_preview_mode, ) diff --git a/app/views/contexts/preview/__init__.py b/app/views/contexts/preview/__init__.py new file mode 100644 index 0000000000..4adc806531 --- /dev/null +++ b/app/views/contexts/preview/__init__.py @@ -0,0 +1,3 @@ +from app.views.contexts.preview.preview_group import PreviewGroup + +__all__ = ["PreviewGroup"] diff --git a/app/views/contexts/preview/preview_block.py b/app/views/contexts/preview/preview_block.py new file mode 100644 index 0000000000..187a0677cd --- /dev/null +++ b/app/views/contexts/preview/preview_block.py @@ -0,0 +1,30 @@ +from typing import Any, Union + +from werkzeug.datastructures import ImmutableDict + +from app.views.contexts.preview.preview_question import PreviewQuestion + + +class PreviewBlock: + def __init__( + self, + *, + block: ImmutableDict, + ): + self._block = block + self._question = self._get_question( + block=self._block, + ) + + @staticmethod + def _get_question( + block: ImmutableDict, + ) -> dict[str, Union[str, dict]]: + return PreviewQuestion( + block=block, + ).serialize() + + def serialize(self) -> dict[str, Union[str, dict, Any]]: + return { + "question": self._question, + } diff --git a/app/views/contexts/preview/preview_group.py b/app/views/contexts/preview/preview_group.py new file mode 100644 index 0000000000..f3e86e6268 --- /dev/null +++ b/app/views/contexts/preview/preview_group.py @@ -0,0 +1,31 @@ +from typing import Any, Mapping + +from app.views.contexts.preview.preview_block import PreviewBlock + + +class PreviewGroup: + def __init__( + self, + *, + group_schema: Mapping[str, Any], + ): + self._blocks = self._build_blocks( + group_schema=group_schema, + ) + + @staticmethod + def _build_blocks( + group_schema: Mapping[str, Any], + ) -> list[dict]: + return [ + PreviewBlock( + block=block, + ).serialize() + for block in group_schema["blocks"] + if block["type"] == "Question" + ] + + def serialize( + self, + ) -> dict[str, Any]: + return {"blocks": self._blocks} diff --git a/app/views/contexts/preview/preview_question.py b/app/views/contexts/preview/preview_question.py new file mode 100644 index 0000000000..29364aa80d --- /dev/null +++ b/app/views/contexts/preview/preview_question.py @@ -0,0 +1,67 @@ +from typing import Any, Union + +from flask_babel import lazy_gettext +from werkzeug.datastructures import ImmutableDict + + +class PreviewQuestion: + def __init__( + self, + *, + block: ImmutableDict, + ): + self._block = block + self._block_id = block["id"] + self._question = self.get_question() + self._title = self._question["title"] + self._answers = self._build_answers() + self._descriptions = self._question.get("description") + self._guidance = self._question.get("guidance") + self._type = self._question.get("type") + + def _build_answers(self) -> list[dict]: + answers_list = [] + for answer in self._question.get("answers", []): + answer_dict = {} + answer_type = answer.get("type") + if options := answer.get("options"): + options_list = [option["label"] for option in options] + answer_dict["options"] = options_list + if answer_type == "Checkbox": + answer_dict["options_text"] = lazy_gettext( + "You can answer with the following options:" + ) + else: + answer_dict["options_text"] = lazy_gettext( + "You can answer with one of the following options:" + ) + elif answer_label := answer.get("label"): + answer_dict["label"] = answer_label + + if description := answer.get("description"): + answer_dict["description"] = description + + if guidance := answer.get("guidance"): + answer_dict["guidance"] = guidance + + if answer_type == "TextArea" and (length := answer.get("max_length")): + answer_dict["max_length"] = length + + answers_list.append(answer_dict) + return answers_list + + def serialize(self) -> dict[str, Union[str, dict, Any]]: + return { + "id": self._block_id, + "title": self._title, + "answers": self._answers, + "descriptions": self._descriptions, + "guidance": self._guidance, + "type": self._type, + } + + def get_question(self) -> Any: + if "question_variants" in self._block: + return self._block["question_variants"][0]["question"] + + return self._block["question"] diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py new file mode 100644 index 0000000000..f458488483 --- /dev/null +++ b/app/views/contexts/preview_context.py @@ -0,0 +1,67 @@ +from typing import Generator, Mapping, Optional, Union + +from flask_babel import lazy_gettext + +from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models.metadata_proxy import MetadataProxy +from app.questionnaire import QuestionnaireSchema +from app.views.contexts import Context +from app.views.contexts.section_preview_context import SectionPreviewContext + + +class PreviewNotEnabledException(Exception): + pass + + +class PreviewContext(Context): + def __init__( + self, + language: str, + schema: QuestionnaireSchema, + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: Optional[MetadataProxy], + response_metadata: Mapping[str, Union[str, int, list]], + ): + if not schema.preview_enabled: + raise PreviewNotEnabledException + + super().__init__( + language, + schema, + answer_store, + list_store, + progress_store, + metadata, + response_metadata, + ) + + def __call__(self) -> dict[str, Union[str, list, bool]]: + sections = list(self.build_all_sections()) + return { + "sections": sections, + } + + def build_all_sections(self) -> Generator[dict, None, None]: + """NB: Does not support repeating sections""" + + for section in self._schema.get_sections(): + section_id = section["id"] + section_preview_context = SectionPreviewContext( + language=self._language, + schema=self._schema, + answer_store=self._answer_store, + list_store=self._list_store, + progress_store=self._progress_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + section_id=section_id, + ) + + yield from section_preview_context()["preview"] + + @staticmethod + def get_page_title() -> str: + title: str = lazy_gettext("Preview survey questions") + return title diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py new file mode 100644 index 0000000000..495a6149a9 --- /dev/null +++ b/app/views/contexts/section_preview_context.py @@ -0,0 +1,50 @@ +from typing import Mapping, Optional, Union + +from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models.metadata_proxy import MetadataProxy +from app.questionnaire import QuestionnaireSchema +from app.views.contexts.context import Context +from app.views.contexts.preview import PreviewGroup + + +class SectionPreviewContext(Context): + def __init__( + self, + *, + language: str, + schema: QuestionnaireSchema, + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: Optional[MetadataProxy], + response_metadata: Mapping[str, Union[str, int, list]], + section_id: str, + ): + super().__init__( + language, + schema, + answer_store, + list_store, + progress_store, + metadata, + response_metadata, + ) + self._section_id = section_id + + def __call__(self) -> dict: + return {"preview": self._build_preview()} + + def _build_preview(self) -> list[dict]: + # Type ignore: The section has to exist at this point + section = self._placeholder_renderer.render(self._schema.get_section(self._section_id), None) # type: ignore + + groups = [ + PreviewGroup(group_schema=group).serialize() for group in section["groups"] + ] + section_dict: dict = { + "title": section["title"], + "id": section["id"], + "blocks": [block for group in groups for block in group["blocks"]], + } + + return [section_dict] diff --git a/app/views/handlers/pdf_response.py b/app/views/handlers/pdf_response.py new file mode 100644 index 0000000000..c1e10ff834 --- /dev/null +++ b/app/views/handlers/pdf_response.py @@ -0,0 +1,75 @@ +import io +import re +from datetime import datetime, timezone + +import pdfkit +from flask import current_app + +from app.data_models import QuestionnaireStore +from app.questionnaire import QuestionnaireSchema + + +class PDFResponse: + """ + Responsible for the PDF generation for the view submitted response. + + Subclassed from `ViewSubmittedResponse`. + + Attributes: + schema: The questionnaire schema object representing the schema JSON. + questionnaire_store: The questionnaire store object. + language: The language the user is currently in. + + Raises: + ViewSubmittedResponseExpired: If the submitted response has expired. + """ + + # The mimetype to use for response + mimetype = "application/pdf" + + # Options to be passed to wkhtmltopdf via PDFKit + wkhtmltopdf_options = { + "quiet": "", + "margin-top": "0.30in", + "margin-right": "0.40in", + "margin-bottom": "0.30in", + "margin-left": "0.40in", + "dpi": 365, + } + + def __init__( + self, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + language: str, + ): + self._schema = schema + self._questionnaire_store = questionnaire_store + self._language = language + + @property + def filename(self) -> str: + """The name to use for the PDF file""" + formatted_title = re.sub( + "[^0-9a-zA-Z]+", "-", self._schema.json["title"].lower() + ) + if self._questionnaire_store.submitted_at: + formatted_date = self._questionnaire_store.submitted_at.date().isoformat() + else: + formatted_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") # type: ignore + return f"{formatted_title}-{formatted_date}.pdf" + + def _get_pdf(self, rendered_html: str) -> io.BytesIO: + """ + Generates a PDF document from the rendered html. + :return: The generated PDF document as BytesIO + :rtype: io.BytesIO + """ + content_as_bytes = pdfkit.from_string( + input=rendered_html, + output_path=None, + css=f'{current_app.config["PRINT_STYLE_SHEET_FILE_PATH"]}/print.css', + options=self.wkhtmltopdf_options, + ) + + return io.BytesIO(content_as_bytes) diff --git a/app/views/handlers/preview_questions_pdf.py b/app/views/handlers/preview_questions_pdf.py new file mode 100644 index 0000000000..87539e80b1 --- /dev/null +++ b/app/views/handlers/preview_questions_pdf.py @@ -0,0 +1,14 @@ +import io + +from app.views.handlers.pdf_response import PDFResponse +from app.views.handlers.view_preview_questions import ViewPreviewQuestions + + +class PreviewQuestionsPDF(PDFResponse, ViewPreviewQuestions): + def get_pdf(self) -> io.BytesIO: + """ + Generates a PDF document from the rendered ViewSubmittedResponse html. + :return: The generated PDF document as BytesIO + :rtype: io.BytesIO + """ + return self._get_pdf(rendered_html=self.get_rendered_html()) diff --git a/app/views/handlers/view_preview_questions.py b/app/views/handlers/view_preview_questions.py new file mode 100644 index 0000000000..cc800cb790 --- /dev/null +++ b/app/views/handlers/view_preview_questions.py @@ -0,0 +1,43 @@ +from flask import url_for + +from app.data_models import QuestionnaireStore +from app.helpers.template_helpers import render_template +from app.questionnaire import QuestionnaireSchema +from app.views.contexts.preview_context import PreviewContext + + +class ViewPreviewQuestions: + def __init__( + self, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + language: str, + ): + self._schema = schema + self._questionnaire_store = questionnaire_store + self._language = language + + def get_context(self) -> dict[str, object]: + preview_context = PreviewContext( + language=self._language, + schema=self._schema, + answer_store=self._questionnaire_store.answer_store, + list_store=self._questionnaire_store.list_store, + progress_store=self._questionnaire_store.progress_store, + metadata=self._questionnaire_store.metadata, + response_metadata=self._questionnaire_store.response_metadata, + ) + context = { + "hide_sign_out_button": True, + "preview": preview_context(), + "pdf_url": url_for("questionnaire.get_preview_questions_pdf"), + } + + return context + + def get_rendered_html(self) -> str: + return render_template( + template="preview", + content=self.get_context(), + page_title=PreviewContext.get_page_title(), + ) diff --git a/app/views/handlers/view_submitted_response_pdf.py b/app/views/handlers/view_submitted_response_pdf.py index ef1ba02893..d76885ae19 100644 --- a/app/views/handlers/view_submitted_response_pdf.py +++ b/app/views/handlers/view_submitted_response_pdf.py @@ -1,45 +1,15 @@ import io -import re - -import pdfkit -from flask import current_app from app.data_models import QuestionnaireStore from app.questionnaire import QuestionnaireSchema +from app.views.handlers.pdf_response import PDFResponse from app.views.handlers.view_submitted_response import ( ViewSubmittedResponse, ViewSubmittedResponseExpired, ) -class ViewSubmittedResponsePDF(ViewSubmittedResponse): - """ - Responsible for the PDF generation for the view submitted response. - - Subclassed from `ViewSubmittedResponse`. - - Attributes: - schema: The questionnaire schema object representing the schema JSON. - questionnaire_store: The questionnaire store object. - language: The language the user is currently in. - - Raises: - ViewSubmittedResponseExpired: If the submitted response has expired. - """ - - # The mimetype to use for response - mimetype = "application/pdf" - - # Options to be passed to wkhtmltopdf via PDFKit - wkhtmltopdf_options = { - "quiet": "", - "margin-top": "0.30in", - "margin-right": "0.40in", - "margin-bottom": "0.30in", - "margin-left": "0.40in", - "dpi": 365, - } - +class ViewSubmittedResponsePDF(ViewSubmittedResponse, PDFResponse): def __init__( self, schema: QuestionnaireSchema, @@ -47,31 +17,13 @@ def __init__( language: str, ): super().__init__(schema, questionnaire_store, language) - self._metadata = self._questionnaire_store.metadata - if self.has_expired: raise ViewSubmittedResponseExpired - @property - def filename(self) -> str: - """The name to use for the PDF file""" - formatted_title = re.sub( - "[^0-9a-zA-Z]+", "-", self._schema.json["title"].lower() - ) - formatted_date = self._questionnaire_store.submitted_at.date().isoformat() # type: ignore - return f"{formatted_title}-{formatted_date}.pdf" - def get_pdf(self) -> io.BytesIO: """ Generates a PDF document from the rendered ViewSubmittedResponse html. :return: The generated PDF document as BytesIO :rtype: io.BytesIO """ - content_as_bytes = pdfkit.from_string( - input=self.get_rendered_html(), - output_path=None, - css=f'{current_app.config["PRINT_STYLE_SHEET_FILE_PATH"]}/print.css', - options=self.wkhtmltopdf_options, - ) - - return io.BytesIO(content_as_bytes) + return self._get_pdf(rendered_html=self.get_rendered_html()) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 548bbaf6ef..4caabb0766 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -214,7 +214,7 @@ "type": "DateRange", "answers": [ { - "id": "answerfrom", + "id": "answer-from", "type": "Date", "mandatory": true, "label": "Period from", @@ -230,7 +230,7 @@ } }, { - "id": "answerto", + "id": "answer-to", "type": "Date", "mandatory": true, "label": "Period to", @@ -525,7 +525,7 @@ "question": { "id": "question383", "title": "Explain any differences between this quarter's opening value and the previously returned closing value", - "description": ["

      Include any unusual fluctuations in figures

      "], + "description": ["

      Include any unusual fluctuations in figures

      "], "type": "General", "answers": [ { diff --git a/schemas/test/en/test_hub_and_spoke.json b/schemas/test/en/test_hub_and_spoke.json index fa5920796f..0cdfd52000 100644 --- a/schemas/test/en/test_hub_and_spoke.json +++ b/schemas/test/en/test_hub_and_spoke.json @@ -217,7 +217,7 @@ } ], "id": "does-anyone-live-here-question", - "title": "Does anyone live here? ", + "title": "Does anyone live here?", "type": "General" }, "type": "Question" diff --git a/schemas/test/en/test_hub_and_spoke_custom_content.json b/schemas/test/en/test_hub_and_spoke_custom_content.json index ca9a17bbee..a92000ff8d 100644 --- a/schemas/test/en/test_hub_and_spoke_custom_content.json +++ b/schemas/test/en/test_hub_and_spoke_custom_content.json @@ -58,7 +58,7 @@ } ], "id": "does-anyone-live-here-question", - "title": "Does anyone live here? ", + "title": "Does anyone live here?", "type": "General" }, "type": "Question" diff --git a/schemas/test/en/test_introduction.json b/schemas/test/en/test_introduction.json index 14ac1d5b99..fe88af00f0 100644 --- a/schemas/test/en/test_introduction.json +++ b/schemas/test/en/test_introduction.json @@ -5,6 +5,7 @@ "data_version": "0.0.3", "survey_id": "144", "theme": "default", + "preview_questions": true, "title": "Test introduction", "legal_basis": "Notice is given under section 999 of the Test Act 2000", "metadata": [ @@ -28,6 +29,18 @@ "name": "trad_as", "type": "string", "optional": true + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + }, + { + "name": "display_address", + "type": "string" } ], "questionnaire_flow": { @@ -37,7 +50,7 @@ "sections": [ { "id": "introduction-section", - "title": "Introduction", + "title": "Main section", "groups": [ { "id": "introduction-group", @@ -239,6 +252,454 @@ } ] }, + { + "type": "Question", + "id": "report-radio", + "question": { + "guidance": { + "contents": [ + { + "description": "Please provide figures for the period in which you were trading." + } + ] + }, + "description": ["

      Your return should relate to the calendar year 2021.

      "], + "instruction": ["Select your answer"], + "answers": [ + { + "id": "report-radio-answer", + "mandatory": true, + "description": "Select your answer", + "guidance": { + "show_guidance": "Additional guidance", + "hide_guidance": "Additional guidance", + "contents": [ + { + "description": "For example select `yes` if you can report for this period" + } + ] + }, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ], + "id": "report-radio-question", + "title": { + "text": "Are you able to report for the calendar month {start_date} to {end_date}?", + "placeholders": [ + { + "placeholder": "start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "reporting-date", + "question": { + "id": "projects-checkbox-question", + "title": "What dates will you be reporting for?", + "description": [ + "

      If figures are not available for the calendar year 2021, your return should relate to a 12 month business year that ends between 6 April 2021 and 5 April 2022.

      " + ], + "type": "DateRange", + "answers": [ + { + "id": "answer-from", + "type": "Date", + "mandatory": true, + "label": "Period from", + "minimum": { + "value": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "offset_by": { + "days": -31 + } + } + }, + { + "id": "answer-to", + "type": "Date", + "mandatory": true, + "label": "Period to", + "maximum": { + "value": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "offset_by": { + "days": 31 + } + } + } + ], + "guidance": { + "contents": [ + { + "description": "

      Only traded for a part of the year?

      " + }, + { + "description": "

      Please provide figures for the period in which you were trading.

      " + }, + { + "description": "

      Only commenced trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period from the commencement of your business until 31 December 2021 or, alternatively, any date up to 5 April 2022.

      " + }, + { + "description": "

      Ceased trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period 1 January 2021 to the date you ceased to trade or, alternatively, from the beginning of your last business year up to the cessation date.

      " + } + ] + } + } + }, + { + "type": "Question", + "id": "report-radio-second", + "question": { + "description": ["

      Your return should relate to the calendar year 2021.

      "], + "instruction": ["Select your answer"], + "answers": [ + { + "id": "report-radio-second-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ], + "id": "report-radio-second-question", + "title": { + "text": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "placeholders": [ + { + "placeholder": "start_date", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "answer-from", + "source": "answers" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "end_date", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "answer-to", + "source": "answers" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "projects-checkbox", + "question": { + "answers": [ + { + "id": "projects-checkbox-answer", + "instruction": "Select any answers that apply", + "mandatory": true, + "options": [ + { + "label": "Public sector projects", + "value": "Public sector projects", + "description": "This includes public housing and government owned organisations such as local, regional and national public authorities and agencies" + }, + { + "label": "Private sector projects", + "value": "Private sector projects", + "description": "This refers to the part of the economy that is for profit and is owned by private organisations. For example privately owned businesses, housing associations, partnerships and sole traders, joint ventures and privately owned housing" + } + ], + "type": "Checkbox" + } + ], + "id": "projects-checkbox-question-2", + "title": { + "text": "Which sector did {ru_name} carry out work for?", + "placeholders": [ + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "General", + "guidance": { + "contents": [ + { + "description": "Include:" + }, + { + "list": ["Local public authorities and agencies", "Regional and national authorities and agencies"] + } + ] + } + } + }, + { + "type": "Question", + "id": "turnover-variants-block", + "question_variants": [ + { + "question": { + "guidance": { + "contents": [ + { + "description": "Include:" + }, + { + "list": [ + "exports", + "payments for work in progress", + "costs incurred and passed on to customers", + "income from sub-contracted activities", + "commission", + "sales of goods purchased for resale", + "revenue earned from other parts of the business not named, please supply at fair value" + ] + }, + { + "description": "Exclude:" + }, + { + "list": [ + "VAT", + "income from the sale of fixed capital assets", + "grants and subsidies", + "insurance claims", + "interest received" + ] + } + ] + }, + "id": "turnover-variants-question", + "title": "What was your total turnover", + "type": "General", + "answers": [ + { + "id": "turnover-variants-answer", + "mandatory": false, + "type": "TextField", + "label": "Total turnover" + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "report-radio-answer" + }, + "Yes" + ] + } + }, + { + "question": { + "id": "turnover-variants-question", + "title": "Why are you not able to report?", + "type": "General", + "answers": [ + { + "id": "turnover-variants-answer", + "mandatory": false, + "type": "TextField", + "label": "Details" + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "report-radio-answer" + }, + "No" + ] + } + } + ] + }, + { + "type": "Question", + "id": "address-mutually-exclusive-checkbox", + "question": { + "id": "address-mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Were your company based at any of the following addresses?", + "mandatory": true, + "answers": [ + { + "id": "address-checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "company_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ], + "text": "{company_address}" + }, + "value": "{company_address}" + }, + { + "label": "7 Evelyn Street, Barry", + "value": "7 Evelyn Street, Barry" + }, + { + "label": "251 Argae Lane, Barry", + "value": "251 Argae Lane, Barry" + } + ] + }, + { + "id": "address-checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "id": "further-details-text-area", + "type": "Question", + "question": { + "id": "further-details-text-area-question", + "title": "Please provide any further details", + "type": "General", + "description": [ + { + "text": "

      Answer for {ru_name}

      ", + "placeholders": [ + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + } + ], + "answers": [ + { + "id": "further-details-text-area-answer", + "mandatory": false, + "type": "TextArea", + "label": "Comments", + "max_length": 2000 + } + ] + } + }, { "type": "Interstitial", "id": "general-business-information-completed", diff --git a/schemas/test/en/test_introduction_hub.json b/schemas/test/en/test_introduction_hub.json new file mode 100644 index 0000000000..59fb42325e --- /dev/null +++ b/schemas/test/en/test_introduction_hub.json @@ -0,0 +1,636 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "144", + "theme": "default", + "preview_questions": true, + "title": "Test introduction preview questions with hub", + "legal_basis": "Notice is given under section 999 of the Test Act 2000", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "ru_ref", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + }, + { + "name": "display_address", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["introduction-section"] + } + }, + "sections": [ + { + "id": "introduction-section", + "title": "Main section", + "summary": { + "page_title": "Summary title", + "show_on_completion": true + }, + "show_on_hub": true, + "groups": [ + { + "id": "introduction-group", + "title": "General Business Information", + "blocks": [ + { + "id": "introduction", + "type": "Introduction", + "primary_content": [ + { + "id": "business-details", + "title": { + "text": "You are completing this for {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "metadata", + "identifier": "trad_as" + }, + { + "source": "metadata", + "identifier": "ru_name" + } + ] + } + } + ] + } + ] + }, + "contents": [ + { + "description": { + "text": "If the company details or structure have changed contact us on {telephone_number_link} or email {email_link}", + "placeholders": [ + { + "placeholder": "telephone_number_link", + "transforms": [ + { + "transform": "telephone_number_link", + "arguments": { + "telephone_number": "0300 1234 931" + } + } + ] + }, + { + "placeholder": "email_link", + "transforms": [ + { + "transform": "email_link", + "arguments": { + "email_address": "surveys@ons.gov.uk", + "email_subject": "Change of details reference", + "email_subject_append": { + "identifier": "ru_ref", + "source": "metadata" + } + } + } + ] + } + ] + } + }, + { + "guidance": { + "contents": [ + { + "title": "Coronavirus (COVID-19) guidance", + "description": "Explain your figures in the comment section to minimise us contacting you and to help us tell an industry story" + } + ] + } + } + ] + }, + { + "id": "use-of-information", + "contents": [ + { + "list": [ + "Data should relate to all sites in England, Scotland and Wales unless otherwise stated.", + "You can provide informed estimates if actual figures aren’t available.", + "We will treat your data securely and confidentially." + ] + }, + { + "description": "To take part, all you need to do is check that you have the information you need to answer the survey questions." + } + ] + } + ], + "secondary_content": [ + { + "id": "how-we-use-your-data", + "contents": [ + { + "title": "How we use your data", + "list": [ + "You cannot appeal your selection. Your business was selected to give us a comprehensive view of the UK economy", + "The data from you business is essential is it helps us calculate the GDP of the UK", + "Our surveys inform government decisions. For example, past statistics from our surveys led to the introduction of business grants" + ] + } + ] + } + ] + }, + { + "type": "Question", + "id": "report-radio", + "question": { + "guidance": { + "contents": [ + { + "description": "Please provide figures for the period in which you were trading." + } + ] + }, + "description": ["

      Your return should relate to the calendar year 2021.

      "], + "instruction": ["Select your answer"], + "answers": [ + { + "id": "report-radio-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ], + "id": "report-radio-question", + "title": { + "text": "Are you able to report for the calendar month {start_date} to {end_date}?", + "placeholders": [ + { + "placeholder": "start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "reporting-date", + "question": { + "id": "projects-checkbox-question", + "title": "What dates will you be reporting for?", + "description": [ + "

      If figures are not available for the calendar year 2021, your return should relate to a 12 month business year that ends between 6 April 2021 and 5 April 2022.

      " + ], + "type": "DateRange", + "answers": [ + { + "id": "answer-from", + "type": "Date", + "mandatory": true, + "label": "Period from", + "minimum": { + "value": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "offset_by": { + "days": -31 + } + } + }, + { + "id": "answer-to", + "type": "Date", + "mandatory": true, + "label": "Period to", + "maximum": { + "value": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "offset_by": { + "days": 31 + } + } + } + ], + "guidance": { + "contents": [ + { + "description": "

      Only traded for a part of the year?

      " + }, + { + "description": "

      Please provide figures for the period in which you were trading.

      " + }, + { + "description": "

      Only commenced trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period from the commencement of your business until 31 December 2021 or, alternatively, any date up to 5 April 2022.

      " + }, + { + "description": "

      Ceased trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period 1 January 2021 to the date you ceased to trade or, alternatively, from the beginning of your last business year up to the cessation date.

      " + } + ] + } + } + }, + { + "type": "Question", + "id": "report-radio-second", + "question": { + "description": ["

      Your return should relate to the calendar year 2021.

      "], + "instruction": ["Select your answer"], + "answers": [ + { + "id": "report-radio-second-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ], + "id": "report-radio-second-question", + "title": { + "text": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "placeholders": [ + { + "placeholder": "start_date", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "answer-from", + "source": "answers" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "end_date", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "answer-to", + "source": "answers" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "projects-checkbox", + "question": { + "answers": [ + { + "id": "projects-checkbox-answer", + "instruction": "Select any answers that apply", + "mandatory": true, + "options": [ + { + "label": "Public sector projects", + "value": "Public sector projects", + "description": "This includes public housing and government owned organisations such as local, regional and national public authorities and agencies" + }, + { + "label": "Private sector projects", + "value": "Private sector projects", + "description": "This refers to the part of the economy that is for profit and is owned by private organisations. For example privately owned businesses, housing associations, partnerships and sole traders, joint ventures and privately owned housing" + } + ], + "type": "Checkbox" + } + ], + "id": "projects-checkbox-question-2", + "title": { + "text": "Which sector did {ru_name} carry out work for?", + "placeholders": [ + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "General", + "guidance": { + "contents": [ + { + "description": "Include:" + }, + { + "list": ["Local public authorities and agencies", "Regional and national authorities and agencies"] + } + ] + } + } + }, + { + "type": "Question", + "id": "turnover-variants-block", + "question_variants": [ + { + "question": { + "guidance": { + "contents": [ + { + "description": "Include:" + }, + { + "list": [ + "exports", + "payments for work in progress", + "costs incurred and passed on to customers", + "income from sub-contracted activities", + "commission", + "sales of goods purchased for resale", + "revenue earned from other parts of the business not named, please supply at fair value" + ] + }, + { + "description": "Exclude:" + }, + { + "list": [ + "VAT", + "income from the sale of fixed capital assets", + "grants and subsidies", + "insurance claims", + "interest received" + ] + } + ] + }, + "id": "turnover-variants-question", + "title": "What was your total turnover", + "type": "General", + "answers": [ + { + "id": "turnover-variants-answer", + "mandatory": false, + "type": "TextField", + "label": "Total turnover" + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "report-radio-answer" + }, + "Yes" + ] + } + }, + { + "question": { + "id": "turnover-variants-question", + "title": "Why are you not able to report?", + "type": "General", + "answers": [ + { + "id": "turnover-variants-answer", + "mandatory": false, + "type": "TextField", + "label": "Details" + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "report-radio-answer" + }, + "No" + ] + } + } + ] + }, + { + "type": "Question", + "id": "address-mutually-exclusive-checkbox", + "question": { + "id": "address-mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Were your company based at any of the following addresses?", + "mandatory": true, + "answers": [ + { + "id": "address-checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "company_address", + "value": { + "identifier": "display_address", + "source": "metadata" + } + } + ], + "text": "{company_address}" + }, + "value": "{company_address}" + }, + { + "label": "7 Evelyn Street, Barry", + "value": "7 Evelyn Street, Barry" + }, + { + "label": "251 Argae Lane, Barry", + "value": "251 Argae Lane, Barry" + } + ] + }, + { + "id": "address-checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "additional-section", + "title": "Additional section", + "summary": { + "page_title": "Summary title", + "show_on_completion": true + }, + "show_on_hub": true, + "groups": [ + { + "id": "additional-group", + "title": "Additional Business Information", + "blocks": [ + { + "id": "further-details-text-area", + "type": "Question", + "question": { + "id": "further-details-text-area-question", + "title": "Please provide any further details", + "type": "General", + "description": [ + { + "text": "

      Answer for {ru_name}

      ", + "placeholders": [ + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + } + ], + "answers": [ + { + "id": "further-details-text-area-answer", + "mandatory": false, + "type": "TextArea", + "label": "Comments", + "max_length": 2000 + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json index 195c3a67ad..d61b7d57a0 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_section.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -606,7 +606,7 @@ "type": "CalculatedSummary", "id": "percentage-total-playback", "title": "We calculate the total of percentage values entered to be %(total)s. Is this correct?", - "page_title": "Percentage Calculated Summary: Person {list_item_position}", + "page_title": "Percentage Calculated Summary: Person {list_item_position}", "calculation": { "operation": { "+": [ diff --git a/schemas/test/en/test_new_routing_answered_unanswered.json b/schemas/test/en/test_new_routing_answered_unanswered.json index 8737d61180..ad598a5a8b 100644 --- a/schemas/test/en/test_new_routing_answered_unanswered.json +++ b/schemas/test/en/test_new_routing_answered_unanswered.json @@ -218,7 +218,7 @@ "id": "answered-question-3", "type": "Interstitial", "content": { - "title": "You chose at least 1 slice" + "title": "You chose at least 1 slice" }, "routing_rules": [ { diff --git a/templates/introduction.html b/templates/introduction.html index 16b344dbee..5c7727fa2e 100644 --- a/templates/introduction.html +++ b/templates/introduction.html @@ -19,6 +19,12 @@ {% endfor %} {% endif %} + {% if preview_enabled %} +
      + {{ _("View the questions you will be asked in this survey").format(url=url_for('questionnaire.get_preview')) }} +
      + {% endif %} + {%- if legal_basis -%}

      {{ _("Your response is legally required") }}

      {{ legal_basis }}

      diff --git a/templates/partials/answer-guidance.html b/templates/partials/answer-guidance.html index fc0274c4ea..110a9876da 100644 --- a/templates/partials/answer-guidance.html +++ b/templates/partials/answer-guidance.html @@ -2,7 +2,7 @@ {% call onsDetails({ "id": "answer-guidance-" ~ answer.id, - "classes": "ons-u-mt-s", + "classes": "ons-u-mt-s ons-u-mb-m", "title": _(answer_guidance.schema_item.show_guidance), "headingAttributes": { "data-ga": "click", diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index 9f87af189d..0a43fa04b4 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -21,7 +21,7 @@ } }) %}
      -

      {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }}

      -

      {{ _("If this is not possible, there are other ways each person can complete their own census.").format(url=content.individual_response_url)}}

      +

      {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }}

      +

      {{ _("If this is not possible, there are other ways each person can complete their own census.").format(url=content.individual_response_url)}}

      {% endcall %} diff --git a/templates/partials/introduction/basic.html b/templates/partials/introduction/basic.html index 648d32d7cb..50a9ba5c57 100644 --- a/templates/partials/introduction/basic.html +++ b/templates/partials/introduction/basic.html @@ -1,4 +1,4 @@ -
      +
      {% if content_block.title %} <{{ title_tag }}>{{ content_block.title }} {% endif %} diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index e60cc6b035..117711e408 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -43,7 +43,6 @@

      {{ intro.title }}

      {{ onsAccordion({ "id": "intro-questions", - "classes": "ons-u-mb-s", "allButton": { "open": _('Show all'), "close": _('Hide all'), diff --git a/templates/partials/preview-question.html b/templates/partials/preview-question.html new file mode 100644 index 0000000000..0d79df5a80 --- /dev/null +++ b/templates/partials/preview-question.html @@ -0,0 +1,73 @@ +{% from 'components/panel/_macro.njk' import onsPanel %} +{% from 'macros/helpers.html' import format_paragraphs %} + +{% set answers = question.answers %} + +
      + +

      {{ question.title }}

      + + {% set answers_length = answers | length %} + + {% if question.descriptions %} + {% set descriptions = question.descriptions %} + {% set descriptions_length = descriptions | length %} + + {% for description in descriptions %} +
      {{- description | safe -}}
      + {% endfor %} + {% endif %} + + {%- if question.guidance -%} + {% set contents = question.guidance.contents %} + {% call onsPanel({ + "id": "question-guidance-" ~ question.id, + "classes": "ons-u-mb-m" + }) %} + {% set contents = question.guidance.contents %} + {% include 'partials/contents.html' %} + {% endcall %} + {% endif %} + + {% for answer in answers %} + + {% if loop.last and question.type == "MutuallyExclusive" %} +

      {{ _("Or") }}

      + {% endif %} + + {% if not loop.last or (answer.options and answers_length == 1) %} +

      {{ answer.options_text }}

      + {% endif %} + + {% if answer.options %} + {% if answer.label %} +

      {{- answer.label | safe -}}

      + {% endif %} +
        + {% for option in answer.options %} +
      • {{ option }}
      • + {% endfor %} +
      + {% else %} + {% if answer.label %} +

      {{- answer.label | safe -}}

      + {% endif %} + {% if answer.max_length %} +

      {{ _("{max_characters} characters can be added.").format(max_characters = answer.max_length) }}

      + {% endif %} + {% endif %} + + {# answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 #} + {# {% if answer.guidance %} + {% with answer_guidance = { + 'id': answer.id, + 'label': answer.label, + 'schema_item': answer.guidance + } %} + {% include 'partials/answer-guidance.html' %} + {% endwith %} + {% endif %} #} + + {% endfor %} + +
      diff --git a/templates/partials/question.html b/templates/partials/question.html index a00c36c3ef..83234fcb6e 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -1,5 +1,4 @@ {% from "components/question/_macro.njk" import onsQuestion %} -{% from "components/fieldset/_macro.njk" import onsFieldset %} {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/error/_macro.njk" import onsError %} @@ -33,7 +32,7 @@ "id": "question-warning-" ~ question.id, "type": "warn" }) %} -

      {{question.warning}}

      +

      {{ question.warning }}

      {% endcall %} {% endif %} {% endset %} diff --git a/templates/preview.html b/templates/preview.html new file mode 100644 index 0000000000..d5309be46b --- /dev/null +++ b/templates/preview.html @@ -0,0 +1,126 @@ +{% extends 'layouts/_base.html' %} + +{% from "components/panel/_macro.njk" import onsPanel %} +{% from "components/button/_macro.njk" import onsButton %} +{% from "components/accordion/_macro.njk" import onsAccordion %} + +{% set save_on_signout = true %} + +{% set breadcrumbs = { + "ariaLabel": 'Back', + "itemsList": [ + { + "url": url_for("questionnaire.get_questionnaire"), + "id": "top-previous", + "text": _("Back"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Previous link click' + } + } + ] +} %} + +{% macro preview_blocks_for_sections(blocks) -%} + {% for block in blocks %} + {% if 'question' in block %} + {% set question = block['question'] %} + {% include 'partials/preview-question.html' %} + {% endif %} + {% endfor %} +{%- endmacro %} + +{% block main %} + +

      {{ _("Preview of the questions in this survey") }}

      + + {% call onsPanel({ + "classes": 'ons-u-mb-m ons-u-ph' + }) %} +

      {{ _("To answer these questions you need to start survey").format(url=url_for('questionnaire.get_questionnaire')) }}

      + {% endcall %} +

      {{ _("You may not have to answer all of these questions. The questions you see will depend on the answers you provide.") }}

      + + {{ + onsButton({ + "type": 'button', + "text": _('Print questions'), + "variants": ['small', 'secondary', 'print'], + "attributes": { + "data-qa": "btn-print", + "data-ga-category": "Print button", + "data-ga-action": "Open print Dialogue", + "data-ga-label": "Print button click" + } + }) + }} + {{ + onsButton({ + "text": _('Save questions as PDF'), + "variants": ['small', 'secondary', 'timer', 'download'], + "url": content.pdf_url, + "removeDownloadAttribute": true, + "attributes": { + "data-qa": "btn-pdf", + "data-ga-category": "PDF button", + "data-ga-action": "Download PDF", + "data-ga-label": "PDF button click" + } + }) + }} +
      + + {%- if content.preview.sections | length > 1 -%} + {%- set itemList = [] -%} + {%- for section in content.preview.sections if section.blocks -%} + {%- set item = { + "title": section.title, + "id": section.id, + "button": { + "open": _('Show'), + "close": _('Hide'), + "attributes": { + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Open panel", + "data-ga-label": section.title + } + } + } -%} + + {%- set block_previews = preview_blocks_for_sections(blocks=section["blocks"]) -%} + {%- do item | setAttribute("content", block_previews) -%} + + {%- do itemList.append(item) -%} + {%- endfor -%} + + {{ + onsAccordion({ + "id": "summary-accordion", + "allButton": { + "open": _('Show all'), + "close": _('Hide all'), + "attributes": { + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Show all", + "data-ga-label": "Show all" + } + }, + "itemsList": itemList + }) + }} + + {%- else %} + {%- for section in content.preview.sections if section.blocks -%} +
      +

      {{ section.title }}

      + {{ preview_blocks_for_sections(blocks=section["blocks"]) }} +
      + {%- endfor -%} + {%- endif -%} + +
      + +{% endblock main %} diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index faeeab2d4a..f74baac314 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -125,7 +125,17 @@ def test_routing_path_with_complete_introduction(answer_store, list_store): ] ) expected_routing_path = RoutingPath( - ["introduction", "general-business-information-completed"], + [ + "introduction", + "report-radio", + "reporting-date", + "report-radio-second", + "projects-checkbox", + "turnover-variants-block", + "address-mutually-exclusive-checkbox", + "further-details-text-area", + "general-business-information-completed", + ], section_id="introduction-section", ) diff --git a/tests/app/views/contexts/__init__.py b/tests/app/views/contexts/__init__.py index e71bf451a3..9ae3029b5c 100644 --- a/tests/app/views/contexts/__init__.py +++ b/tests/app/views/contexts/__init__.py @@ -5,7 +5,7 @@ def assert_summary_context(context): key_value in summary_context ), f"Key value {key_value} missing from context['summary']" - for group in context["summary"]["groups"]: + for group in summary_context["groups"]: assert "id" in group assert "blocks" in group for block in group["blocks"]: @@ -16,3 +16,15 @@ def assert_summary_context(context): assert "id" in answer assert "value" in answer assert "type" in answer + + +def assert_preview_context(context): + for key_value in ("blocks", "title", "id"): + assert ( + key_value in context["sections"][0] + ), f"Key value {key_value} missing from context" + + for block in context["sections"][0]["blocks"]: + assert "question" in block + for answers in block["question"]["answers"]: + assert len(answers) != 0 diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 82e01357dd..025cfe3cf1 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -1,6 +1,7 @@ import pytest from mock import MagicMock +from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.progress_store import ProgressStore @@ -351,3 +352,31 @@ def test_calculated_summary_answers(): @pytest.fixture def test_section_summary_schema(): return load_schema_from_name("test_section_summary") + + +@pytest.fixture +def test_introduction_preview_linear_schema(): + return load_schema_from_name("test_introduction") + + +@pytest.fixture +def questionnaire_store(): + storage = MagicMock() + storage.get_user_data = MagicMock(return_value=("{}", "ce_sid", 1, None)) + storage.add_or_update = MagicMock() + + store = QuestionnaireStore(storage) + + store.answer_store = AnswerStore() + store.metadata = { + "ru_name": "ESSENTIAL ENTERPRISE LTD.", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "display_address": "68 Abingdon Road, Goathill", + "trad_as": "ESSENTIAL ENTERPRISE LTD.", + "ru_ref": "12346789012A", + } + + store.response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} + + return store diff --git a/tests/app/views/contexts/test_preview_context.py b/tests/app/views/contexts/test_preview_context.py new file mode 100644 index 0000000000..524ce75768 --- /dev/null +++ b/tests/app/views/contexts/test_preview_context.py @@ -0,0 +1,289 @@ +import pytest +from flask_babel import lazy_gettext + +from app.questionnaire import QuestionnaireSchema +from app.views.contexts.preview_context import ( + PreviewContext, + PreviewNotEnabledException, +) +from tests.app.views.contexts import assert_preview_context + + +def test_build_preview_rendering_context( + test_introduction_preview_linear_schema, + answer_store, + list_store, + progress_store, + questionnaire_store, +): + preview_context = PreviewContext( + "en", + test_introduction_preview_linear_schema, + answer_store, + list_store, + progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, + ) + + preview_context = preview_context() + + assert_preview_context(preview_context) + + +def test_build_preview_context( + test_introduction_preview_linear_schema, + answer_store, + list_store, + progress_store, + questionnaire_store, +): + preview_context = PreviewContext( + "en", + test_introduction_preview_linear_schema, + answer_store, + list_store, + progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, + ) + context = preview_context() + + expected_context = { + "sections": [ + { + "blocks": [ + { + "question": { + "answers": [ + { + "description": "Select your answer", + "guidance": { + "contents": [ + { + "description": "For example select `yes` if you can report for this period" + } + ], + "hide_guidance": "Additional guidance", + "show_guidance": "Additional guidance", + }, + "options": ["Yes", "No"], + "options_text": lazy_gettext( + "You can answer with one of the following options:" + ), + } + ], + "descriptions": [ + "

      Your return should relate to the calendar year 2021.

      " + ], + "guidance": { + "contents": [ + { + "description": "Please provide figures for the period in which you were trading." + } + ] + }, + "id": "report-radio", + "title": "Are you able to report for the calendar month 2 February 2016 to 3 March 2016?", + "type": "General", + } + }, + { + "question": { + "answers": [ + {"label": "Period from"}, + {"label": "Period to"}, + ], + "descriptions": [ + "

      If figures are not available for the calendar year 2021, your return should " + "relate to a 12 month business year that ends between 6 April 2021 and 5 April 2022.

      " + ], + "guidance": { + "contents": [ + { + "description": "

      Only traded for a part of the year?

      " + }, + { + "description": "

      Please provide figures for the period in which you were trading.

      " + }, + { + "description": "

      Only commenced trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period from the commencement of " + "your business until 31 December 2021 or, alternatively, any date up to 5 April 2022.

      " + }, + { + "description": "

      Ceased trading during 2021?

      " + }, + { + "description": "

      Your return should cover the period 1 January 2021 to the date " + "you ceased to trade or, alternatively, from the beginning of your last business year up to the cessation date.

      " + }, + ] + }, + "id": "reporting-date", + "title": "What dates will you be reporting for?", + "type": "DateRange", + } + }, + { + "question": { + "answers": [ + { + "options": ["Yes", "No"], + "options_text": lazy_gettext( + "You can answer with one of the following options:" + ), + } + ], + "descriptions": [ + "

      Your return should relate to the calendar year 2021.

      " + ], + "guidance": None, + "id": "report-radio-second", + "title": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "type": "General", + } + }, + { + "question": { + "answers": [ + { + "options": [ + "Public sector projects", + "Private sector projects", + ], + "options_text": lazy_gettext( + "You can answer with the following options:" + ), + } + ], + "descriptions": None, + "guidance": { + "contents": [ + {"description": "Include:"}, + { + "list": [ + "Local public authorities and agencies", + "Regional and national authorities and agencies", + ] + }, + ] + }, + "id": "projects-checkbox", + "title": "Which sector did ESSENTIAL " + "ENTERPRISE LTD. carry out work " + "for?", + "type": "General", + } + }, + { + "question": { + "answers": [{"label": "Total turnover"}], + "descriptions": None, + "guidance": { + "contents": [ + {"description": "Include:"}, + { + "list": [ + "exports", + "payments for work in progress", + "costs incurred and passed on to customers", + "income from sub-contracted activities", + "commission", + "sales of goods purchased for resale", + "revenue earned from other parts of the business not named, please supply at fair value", + ] + }, + {"description": "Exclude:"}, + { + "list": [ + "VAT", + "income from the sale of fixed capital assets", + "grants and subsidies", + "insurance claims", + "interest received", + ] + }, + ] + }, + "id": "turnover-variants-block", + "title": "What was your total turnover", + "type": "General", + } + }, + { + "question": { + "answers": [ + { + "options": [ + "68 Abingdon Road, Goathill", + "7 Evelyn Street, Barry", + "251 Argae Lane, Barry", + ], + "options_text": lazy_gettext( + "You can answer with the following options:" + ), + }, + { + "options": ["I prefer not to say"], + "options_text": lazy_gettext( + "You can answer with the following options:" + ), + }, + ], + "descriptions": None, + "guidance": None, + "id": "address-mutually-exclusive-checkbox", + "title": "Were your company based at any of the following addresses?", + "type": "MutuallyExclusive", + } + }, + { + "question": { + "answers": [ + { + "label": "Comments", + "max_length": 2000, + } + ], + "descriptions": [ + "

      Answer for ESSENTIAL ENTERPRISE LTD.

      " + ], + "guidance": None, + "id": "further-details-text-area", + "title": "Please provide any further details", + "type": "General", + } + }, + ], + "title": "Main section", + "id": "introduction-section", + } + ] + } + assert "sections" in context + assert_preview_context(context) + assert len(context["sections"][0]) == 3 + assert "blocks" in context["sections"][0] + assert context == expected_context + + +def test_preview_questions_disabled_raises_exception( + answer_store, + list_store, + progress_store, + questionnaire_store, +): + schema = QuestionnaireSchema({"preview_questions": False}) + with pytest.raises(PreviewNotEnabledException): + PreviewContext( + "en", + schema, + answer_store, + list_store, + progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, + ) diff --git a/tests/app/views/handlers/test_preview_questions_pdf.py b/tests/app/views/handlers/test_preview_questions_pdf.py new file mode 100644 index 0000000000..1b4cd98868 --- /dev/null +++ b/tests/app/views/handlers/test_preview_questions_pdf.py @@ -0,0 +1,17 @@ +import pytest + +from app.data_models import QuestionnaireStore +from app.views.contexts.preview_context import PreviewNotEnabledException +from app.views.handlers.preview_questions_pdf import PreviewQuestionsPDF + +from .conftest import set_storage_data + + +@pytest.mark.usefixtures("app") +def test_preview_questions_disabled_raises_exception(storage, schema, language): + set_storage_data(storage) + + questionnaire_store = QuestionnaireStore(storage) + questionnaire_store.set_metadata({"schema_name": "test_checkbox"}) + with pytest.raises(PreviewNotEnabledException): + PreviewQuestionsPDF(schema, questionnaire_store, language).get_context() diff --git a/tests/app/views/handlers/test_view_preview_questions.py b/tests/app/views/handlers/test_view_preview_questions.py new file mode 100644 index 0000000000..423ab6ec62 --- /dev/null +++ b/tests/app/views/handlers/test_view_preview_questions.py @@ -0,0 +1,44 @@ +from datetime import datetime, timezone + +import pytest +from freezegun import freeze_time + +from app.data_models import QuestionnaireStore +from app.questionnaire import QuestionnaireSchema +from app.views.contexts.preview_context import PreviewNotEnabledException +from app.views.handlers.preview_questions_pdf import PreviewQuestionsPDF +from app.views.handlers.view_preview_questions import ViewPreviewQuestions + +from .conftest import set_storage_data + + +@pytest.mark.usefixtures("app") +@freeze_time("2022-06-01T15:34:54+00:00") +def test_view_preview_questions_context(storage, language): + submitted_at = datetime.now(timezone.utc) + set_storage_data(storage, submitted_at=submitted_at) + + questionnaire_store = QuestionnaireStore(storage) + schema = QuestionnaireSchema({"preview_questions": True}) + preview = ViewPreviewQuestions(schema, questionnaire_store, language) + + assert preview.get_context() == { + "hide_sign_out_button": True, + "pdf_url": "/questionnaire/preview/download-pdf", + "preview": {"sections": []}, + } + + +def test_not_enabled(storage, language): + submitted_at = datetime.now(timezone.utc) + set_storage_data(storage, submitted_at=submitted_at) + + questionnaire_store = QuestionnaireStore(storage) + + with pytest.raises(PreviewNotEnabledException): + preview_questions_pdf = PreviewQuestionsPDF( + QuestionnaireSchema({"preview_questions": False}), + questionnaire_store, + language, + ) + preview_questions_pdf.get_context() diff --git a/tests/functional/base_pages/introduction.page.js b/tests/functional/base_pages/introduction.page.js index 7de98a03dd..381155653c 100644 --- a/tests/functional/base_pages/introduction.page.js +++ b/tests/functional/base_pages/introduction.page.js @@ -32,6 +32,10 @@ class IntroductionBasePage extends BasePage { introDescription() { return "#use-of-information p"; } + + previewQuestions() { + return 'a[href="/questionnaire/preview"]'; + } } export default IntroductionBasePage; diff --git a/tests/functional/base_pages/question-preview.page.js b/tests/functional/base_pages/question-preview.page.js new file mode 100644 index 0000000000..f9a6437d29 --- /dev/null +++ b/tests/functional/base_pages/question-preview.page.js @@ -0,0 +1,13 @@ +import BasePage from "./base.page"; + +class QuestionPreviewBasePage extends BasePage { + url() { + return `/submitted/feedback/${this.pageName}`; + } + + showButton() { + return '[data-ga-category="Preview Survey"]'; + } +} + +export default QuestionPreviewBasePage; diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js new file mode 100644 index 0000000000..8849a52cf6 --- /dev/null +++ b/tests/functional/spec/preview.spec.js @@ -0,0 +1,87 @@ +import IntroductionPageHub from "../generated_pages/introduction_hub/introduction.page"; +import IntroductionPageLinear from "../generated_pages/introduction/introduction.page"; +import { expect } from "chai"; + +describe("Introduction preview questions", () => { + const introductionSchemaHub = "test_introduction_hub.json"; + const introductionSchemaLinear = "test_introduction.json"; + const showButton = 'button[data-ga-category="Preview Survey"]'; + const previewSummaryContent = "#summary-accordion-1-content"; + const previewSectionTitle = ".ons-summary__group-title"; + const previewQuestion = ".ons-summary__item"; + const printButton = 'button[data-qa="btn-print"]'; + const pdfButton = 'a[data-qa="btn-pdf"]'; + // const detailsHeading = ".ons-details__heading"; + const startSurveyButton = ".qa-btn-get-started"; + const noRadio = "#report-radio-answer-1"; + const submitButton = 'button[data-qa="btn-submit"]'; + const answerFromDay = "#answer-from-day"; + const answerFromMonth = "#answer-from-month"; + const answerFromYear = "#answer-from-year"; + const answerToDay = "#answer-to-day"; + const answerToMonth = "#answer-to-month"; + const answerToYear = "#answer-to-year"; + + function testPreview(schema, page) { + browser.openQuestionnaire(schema); + $(page.previewQuestions()).click(); + expect(browser.getUrl()).to.contain("questionnaire/preview"); + if (schema === "test_introduction.json") { + expect($(previewSectionTitle).getText()).to.equal("Main section"); + } else { + $(showButton).click(); + } + // :TODO: Add data attributes to elements below so we don't rely on tags or classes that are subject to DS changes + expect($(previewQuestion).$("h3").getText()).to.equal("Are you able to report for the calendar month 1 January 2017 to 1 February 2017?"); + expect($(previewQuestion).$(".ons-question__description").getText()).to.equal("Your return should relate to the calendar year 2021."); + expect($(previewQuestion).$$(".ons-panel__body")[0].getText()).to.equal("Please provide figures for the period in which you were trading."); + expect($(showButton).length).to.be.undefined; + expect($(printButton).isClickable()).to.be.true; + expect($(pdfButton).isClickable()).to.be.true; + // answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 + // $(detailsHeading).click(); + // expect($(previewQuestion).$("#answer-guidance--content div p").getText()).to.equal("For example select `yes` if you can report for this period"); + expect($(previewQuestion).$$("p")[2].getText()).to.equal("You can answer with one of the following options:"); + expect($(previewQuestion).$$("ul")[0].getText()).to.equal("Yes\nNo"); + } + + it("Given I start a survey, When I view the preview page, Then all preview elements should be visible and any metadata piped answers are resolved", () => { + testPreview(introductionSchemaHub, IntroductionPageHub); + testPreview(introductionSchemaLinear, IntroductionPageLinear); + }); + + it("Given I complete some of a survey and the piped answers should be being populated, Then preview answers should still be showing placeholders", () => { + browser.openQuestionnaire(introductionSchemaLinear); + $(startSurveyButton).click(); + $(noRadio).click(); + $(submitButton).click(); + $(answerFromDay).setValue(5); + $(answerFromMonth).setValue(12); + $(answerFromYear).setValue(2016); + $(answerToDay).setValue(20); + $(answerToMonth).setValue(12); + $(answerToYear).setValue(2016); + $(submitButton).click(); + expect($("h1").getText()).to.equal("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); + browser.url("questionnaire/introduction/"); + $(IntroductionPageLinear.previewQuestions()).click(); + expect(browser.getUrl()).to.contain("questionnaire/preview"); + expect($(previewSectionTitle).getText()).to.equal("Main section"); + expect($$(previewQuestion)[2].$("h3").getText()).to.equal("Are you sure you are able to report for the calendar month {start_date} to {end_date}?"); + }); + + it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", () => { + browser.openQuestionnaire(introductionSchemaHub); + $(IntroductionPageHub.previewQuestions()).click(); + expect(browser.getUrl()).to.contain("questionnaire/preview"); + expect($(printButton).isClickable()).to.be.true; + expect($(pdfButton).isClickable()).to.be.true; + expect($(showButton).getText()).to.equal("Show all"); + expect($(previewSummaryContent).isClickable()).to.be.false; + it("and if the twisty button is clicked, Then the twisty button should read 'Hide all' and the answers should be visible", () => { + $(showButton).click(); + expect($(showButton).getText()).to.equal("Hide all"); + expect($(previewSummaryContent).isClickable()).to.be.true; + }); + }); +}); diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 64b7d85423..33604dae66 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -336,7 +336,8 @@ def test_submission_failed_theme_default_cookie_exists(self): submitter.send_message = Mock(return_value=False) # When - self.launchAndFailSubmission("test_introduction") + self.launchAndFailSubmission("test_instructions") + self.post() # Then self.assertStatusCode(500) @@ -379,6 +380,12 @@ def test_submission_failed_theme_census_cookie_exists(self): f'

      If you are completing a business survey, please contact us.

      ' ) + def test_preview_not_enabled_results_in_404(self): + self.launchSurvey("test_checkbox") + self.post(action="start_questionnaire") + self.get("/questionnaire/preview/") + self.assertStatusCode(404) + def launchAndFailSubmission(self, schema): self.launchSurvey(schema) self.post() diff --git a/tests/integration/routes/test_view_preview_questions.py b/tests/integration/routes/test_view_preview_questions.py new file mode 100644 index 0000000000..5d37835b0b --- /dev/null +++ b/tests/integration/routes/test_view_preview_questions.py @@ -0,0 +1,45 @@ +from tests.integration.integration_test_case import IntegrationTestCase + + +class TestPreviewPDF(IntegrationTestCase): + def test_download_pdf(self): + super().setUp() + + # Given I launch a questionnaire and open preview of questions + self.launchSurvey("test_introduction") + self.get("/questionnaire/preview/") + + # When I try to download preview of questions from the preview page + download_pdf_url = ( + self.getHtmlSoup() + .find("a", {"href": "/questionnaire/preview/download-pdf"}) + .attrs["href"] + ) + + self.get(download_pdf_url) + + # Then I get 200 status code + self.assertStatusOK() + + def test_download_pdf_no_preview(self): + super().setUp() + + # Given I launch a questionnaire without preview enabled + self.launchSurvey("test_checkbox") + + # When I try to download preview of questions + self.get("/questionnaire/preview/download-pdf") + + # Then I get 404 status code + self.assertStatusNotFound() + + def test_print_button(self): + super().setUp() + + # Given I launch a questionnaire and open preview of questions + self.launchSurvey("test_introduction") + self.get("/questionnaire/preview/") + + # Then the print button is displayed correctly + print_button = self.getHtmlSoup().find("button", {"data-qa": "btn-print"}) + self.assertIsNotNone(print_button) From 67d107225b8f06b8eb0236da6a5c3bc1181a7d2b Mon Sep 17 00:00:00 2001 From: Guilhem <122792081+ONS-Guilhem-Forey@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:13:05 +0000 Subject: [PATCH 185/567] Remove support old when rules (#1016) Remove support for old when rules Co-authored-by: Guilhem Forey --- app/questionnaire/path_finder.py | 82 +- app/questionnaire/questionnaire_schema.py | 37 +- app/questionnaire/router.py | 35 +- app/questionnaire/variants.py | 29 +- app/questionnaire/when_rules.py | 320 ------- schemas/test/en/test_benchmark_business.json | 32 +- schemas/test/en/test_calculated_summary.json | 64 +- .../en/test_conditional_combined_routing.json | 31 +- .../test/en/test_confirmation_question.json | 38 +- ...firmation_question_backwards_routing.json} | 0 ...ion_question_within_repeating_section.json | 57 +- schemas/test/en/test_custom_page_titles.json | 17 +- schemas/test/en/test_default_with_skip.json | 32 +- schemas/test/en/test_dob_date.json | 100 ++- schemas/test/en/test_hub_and_spoke.json | 45 +- .../en/test_hub_and_spoke_custom_content.json | 20 +- .../test/en/test_hub_complete_sections.json | 25 +- ...test_hub_section_required_and_enabled.json | 14 +- .../test/en/test_interstitial_definition.json | 33 +- .../test_last_viewed_question_guidance.json | 64 +- .../test_list_change_evaluates_sections.json | 23 +- .../test_list_collector_driving_checkbox.json | 128 +-- .../test_list_collector_driving_question.json | 18 +- ...y_and_collector_with_driving_question.json | 18 +- .../test_list_collector_primary_person.json | 47 +- .../test_list_collector_same_name_items.json | 55 +- ...st_list_collector_two_list_collectors.json | 8 +- .../test/en/test_list_collector_variants.json | 128 +-- ...ist_collector_variants_primary_person.json | 64 +- ...st_collector_variants_section_summary.json | 146 ++-- schemas/test/en/test_metadata_routing.json | 18 +- .../test/en/test_new_calculated_summary.json | 48 +- ..._new_hub_section_required_and_enabled.json | 122 --- ...ing_and_skipping_section_dependencies.json | 623 ------------- .../test/en/test_new_routing_date_equals.json | 225 ----- .../test_new_routing_date_greater_than.json | 168 ---- .../en/test_new_routing_date_less_than.json | 108 --- .../en/test_new_routing_date_not_equals.json | 108 --- .../en/test_new_routing_number_equals.json | 129 --- .../test_new_routing_number_greater_than.json | 129 --- ..._routing_number_greater_than_or_equal.json | 131 --- .../en/test_new_routing_number_less_than.json | 129 --- ...new_routing_number_less_than_or_equal.json | 131 --- .../test_new_routing_number_not_equals.json | 129 --- .../en/test_new_section_enabled_checkbox.json | 168 ---- .../test/en/test_new_section_enabled_hub.json | 163 ---- .../en/test_new_section_enabled_radio.json | 115 --- .../en/test_new_skip_condition_block.json | 100 --- .../en/test_new_skip_condition_group.json | 106 --- .../test/en/test_new_variants_content.json | 106 --- .../test_new_variants_first_item_in_list.json | 298 ------- .../test/en/test_new_variants_question.json | 622 ------------- ...aceholder_based_on_first_item_in_list.json | 84 +- .../test_placeholder_difference_in_years.json | 18 +- ...holder_difference_in_years_month_year.json | 18 +- ..._difference_in_years_month_year_range.json | 18 +- ...placeholder_difference_in_years_range.json | 18 +- schemas/test/en/test_placeholder_full.json | 18 +- .../en/test_placeholder_playback_list.json | 22 +- schemas/test/en/test_relationships.json | 34 +- .../test/en/test_relationships_primary.json | 57 +- .../test/en/test_relationships_unrelated.json | 74 +- .../en/test_repeating_section_summaries.json | 36 +- ...repeating_sections_with_hub_and_spoke.json | 209 +++-- ...routing_and.json => test_routing_and.json} | 0 ...ing_and_skipping_section_dependencies.json | 184 +++- ...tion_dependencies_calculated_summary.json} | 0 ..._dependencies_new_calculated_summary.json} | 0 ...on => test_routing_answer_comparison.json} | 0 ... => test_routing_answered_unanswered.json} | 0 ..._routing_case_insensitive_text_field.json} | 0 .../en/test_routing_checkbox_contains.json | 30 +- ...> test_routing_checkbox_contains_all.json} | 0 ...> test_routing_checkbox_contains_any.json} | 0 ...=> test_routing_checkbox_contains_in.json} | 0 ....json => test_routing_checkbox_count.json} | 0 schemas/test/en/test_routing_date_equals.json | 253 +++--- .../en/test_routing_date_greater_than.json | 25 +- ..._routing_date_greater_than_or_equals.json} | 0 .../test/en/test_routing_date_less_than.json | 23 +- ...est_routing_date_less_than_or_equals.json} | 0 .../test/en/test_routing_date_not_equals.json | 27 +- ...ing_group.json => test_routing_group.json} | 0 ...routing_not.json => test_routing_not.json} | 0 ..._not_affected_by_answers_not_on_path.json} | 0 .../test/en/test_routing_number_equals.json | 26 +- .../en/test_routing_number_greater_than.json | 32 +- ..._routing_number_greater_than_or_equal.json | 40 +- ...reater_than_or_equal_single_condition.json | 22 +- .../en/test_routing_number_less_than.json | 32 +- ...est_routing_number_less_than_or_equal.json | 42 +- ...r_less_than_or_equal_single_condition.json | 22 +- .../en/test_routing_number_not_equals.json | 22 +- .../en/test_routing_on_multiple_select.json | 16 +- ...w_routing_or.json => test_routing_or.json} | 0 ..._questionnaire_end_multiple_sections.json} | 0 ..._to_questionnaire_end_single_section.json} | 0 ....json => test_routing_to_section_end.json} | 0 .../en/test_section_enabled_checkbox.json | 28 +- schemas/test/en/test_section_enabled_hub.json | 28 +- .../test/en/test_section_enabled_radio.json | 14 +- schemas/test/en/test_section_summary.json | 16 +- ...st_show_section_summary_on_completion.json | 21 +- ...est_skip_condition_answer_comparison.json} | 0 .../test/en/test_skip_condition_block.json | 51 +- .../test/en/test_skip_condition_group.json | 36 +- ....json => test_skip_condition_not_set.json} | 0 ..._set.json => test_skip_condition_set.json} | 0 .../en/test_titles_radio_and_checkbox.json | 96 ++- schemas/test/en/test_variants_content.json | 36 +- .../en/test_variants_first_item_in_list.json | 44 +- schemas/test/en/test_variants_question.json | 252 +++--- tests/app/data_model/test_list_store.py | 10 + tests/app/forms/test_questionnaire_form.py | 20 +- tests/app/questionnaire/conftest.py | 283 +++--- tests/app/questionnaire/test_date_rules.py | 132 --- tests/app/questionnaire/test_path_finder.py | 172 +--- .../test_questionnaire_schema.py | 4 +- tests/app/questionnaire/test_router.py | 32 +- tests/app/questionnaire/test_schema_utils.py | 4 +- tests/app/questionnaire/test_when_rules.py | 815 ------------------ .../functional/spec/content_variants.spec.js | 2 +- .../enabled_section_checkbox.spec.js | 2 +- .../enabled_section_hub.spec.js | 2 +- .../enabled_section_radio.spec.js | 2 +- .../spec/features/routing/all_in.spec.js | 8 +- .../spec/features/routing/and.spec.js | 10 +- .../routing/answer_comparison_routing.spec.js | 6 +- .../routing/answer_not_on_path.spec.js | 12 +- .../routing/answered_unanswered.spec.js | 24 +- .../spec/features/routing/any_in.spec.js | 8 +- .../features/routing/checkbox_count.spec.js | 8 +- .../spec/features/routing/date.spec.js | 30 +- .../spec/features/routing/in.spec.js | 8 +- .../spec/features/routing/not.spec.js | 8 +- .../spec/features/routing/number.spec.js | 18 +- .../spec/features/routing/or.spec.js | 10 +- .../answer_comparison_skip_conditions.spec.js | 6 +- .../functional/spec/question_variants.spec.js | 2 +- ...estion_variants_first_item_in_list.spec.js | 4 +- ..._and_skipping_section_dependencies.spec.js | 46 +- ...on_dependencies_calculated_summary.spec.js | 16 +- .../spec/skip_condition_block.spec.js | 2 +- .../spec/skip_condition_group.spec.js | 2 +- .../spec/skip_conditions_not_set.spec.js | 8 +- .../spec/skip_conditions_set.spec.js | 8 +- .../questionnaire/test_questionnaire_hub.py | 4 +- .../test_questionnaire_question_variants.py | 14 +- ...stionnaire_routing_to_questionnaire_end.py | 6 +- ...st_questionnaire_routing_to_section_end.py | 8 +- .../test_questionnaire_submit.py | 8 +- .../routing/test_answer_comparison.py | 4 +- .../routing/test_routing_case_insensitive.py | 4 +- .../session/test_sign_out_and_exit.py | 2 +- 154 files changed, 2184 insertions(+), 7200 deletions(-) delete mode 100644 app/questionnaire/when_rules.py rename schemas/test/en/{test_new_confirmation_question.json => test_confirmation_question_backwards_routing.json} (100%) delete mode 100644 schemas/test/en/test_new_hub_section_required_and_enabled.json delete mode 100644 schemas/test/en/test_new_routing_and_skipping_section_dependencies.json delete mode 100644 schemas/test/en/test_new_routing_date_equals.json delete mode 100644 schemas/test/en/test_new_routing_date_greater_than.json delete mode 100644 schemas/test/en/test_new_routing_date_less_than.json delete mode 100644 schemas/test/en/test_new_routing_date_not_equals.json delete mode 100644 schemas/test/en/test_new_routing_number_equals.json delete mode 100644 schemas/test/en/test_new_routing_number_greater_than.json delete mode 100644 schemas/test/en/test_new_routing_number_greater_than_or_equal.json delete mode 100644 schemas/test/en/test_new_routing_number_less_than.json delete mode 100644 schemas/test/en/test_new_routing_number_less_than_or_equal.json delete mode 100644 schemas/test/en/test_new_routing_number_not_equals.json delete mode 100644 schemas/test/en/test_new_section_enabled_checkbox.json delete mode 100644 schemas/test/en/test_new_section_enabled_hub.json delete mode 100644 schemas/test/en/test_new_section_enabled_radio.json delete mode 100644 schemas/test/en/test_new_skip_condition_block.json delete mode 100644 schemas/test/en/test_new_skip_condition_group.json delete mode 100644 schemas/test/en/test_new_variants_content.json delete mode 100644 schemas/test/en/test_new_variants_first_item_in_list.json delete mode 100644 schemas/test/en/test_new_variants_question.json rename schemas/test/en/{test_new_routing_and.json => test_routing_and.json} (100%) rename schemas/test/en/{test_new_routing_and_skipping_section_dependencies_calculated_summary.json => test_routing_and_skipping_section_dependencies_calculated_summary.json} (100%) rename schemas/test/en/{test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json => test_routing_and_skipping_section_dependencies_new_calculated_summary.json} (100%) rename schemas/test/en/{test_new_routing_answer_comparison.json => test_routing_answer_comparison.json} (100%) rename schemas/test/en/{test_new_routing_answered_unanswered.json => test_routing_answered_unanswered.json} (100%) rename schemas/test/en/{test_new_routing_case_insensitive_text_field.json => test_routing_case_insensitive_text_field.json} (100%) rename schemas/test/en/{test_new_routing_checkbox_contains_all.json => test_routing_checkbox_contains_all.json} (100%) rename schemas/test/en/{test_new_routing_checkbox_contains_any.json => test_routing_checkbox_contains_any.json} (100%) rename schemas/test/en/{test_new_routing_checkbox_contains.json => test_routing_checkbox_contains_in.json} (100%) rename schemas/test/en/{test_new_routing_checkbox_count.json => test_routing_checkbox_count.json} (100%) rename schemas/test/en/{test_new_routing_date_greater_than_or_equals.json => test_routing_date_greater_than_or_equals.json} (100%) rename schemas/test/en/{test_new_routing_date_less_than_or_equals.json => test_routing_date_less_than_or_equals.json} (100%) rename schemas/test/en/{test_new_routing_group.json => test_routing_group.json} (100%) rename schemas/test/en/{test_new_routing_not.json => test_routing_not.json} (100%) rename schemas/test/en/{test_new_routing_not_affected_by_answers_not_on_path.json => test_routing_not_affected_by_answers_not_on_path.json} (100%) rename schemas/test/en/{test_new_routing_or.json => test_routing_or.json} (100%) rename schemas/test/en/{test_new_routing_to_questionnaire_end_multiple_sections.json => test_routing_to_questionnaire_end_multiple_sections.json} (100%) rename schemas/test/en/{test_new_routing_to_questionnaire_end_single_section.json => test_routing_to_questionnaire_end_single_section.json} (100%) rename schemas/test/en/{test_new_routing_to_section_end.json => test_routing_to_section_end.json} (100%) rename schemas/test/en/{test_new_skip_condition_answer_comparison.json => test_skip_condition_answer_comparison.json} (100%) rename schemas/test/en/{test_new_skip_condition_not_set.json => test_skip_condition_not_set.json} (100%) rename schemas/test/en/{test_new_skip_condition_set.json => test_skip_condition_set.json} (100%) delete mode 100644 tests/app/questionnaire/test_date_rules.py delete mode 100644 tests/app/questionnaire/test_when_rules.py diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 52312c7b33..7b3ef524f4 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -10,7 +10,6 @@ from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.questionnaire.when_rules import evaluate_goto, evaluate_when_rules class PathFinder: @@ -196,21 +195,13 @@ def _evaluate_routing_rules( routing_path_block_ids=routing_path_block_ids, ) for rule in routing_rules: - if "goto" in rule: - rule = rule["goto"] - should_goto = evaluate_goto( - rule, - self.schema, - self.metadata, - self.answer_store, - self.list_store, - current_location=this_location, - routing_path_block_ids=routing_path_block_ids, - ) - else: - should_goto = should_goto_new(rule, when_rule_evaluator) + rule_valid = ( + when_rule_evaluator.evaluate(when_rule) + if (when_rule := rule.get("when")) + else True + ) - if should_goto: + if rule_valid: if rule.get("section") == "End": return None @@ -246,32 +237,17 @@ def evaluate_skip_conditions( when_rules_block_dependencies + routing_path_block_ids ) - if isinstance(skip_conditions, dict): - when_rule_evaluator = RuleEvaluator( - self.schema, - self.answer_store, - self.list_store, - self.metadata, - self.response_metadata, - location=this_location, - routing_path_block_ids=routing_path_block_ids, - ) + when_rule_evaluator = RuleEvaluator( + self.schema, + self.answer_store, + self.list_store, + self.metadata, + self.response_metadata, + location=this_location, + routing_path_block_ids=routing_path_block_ids, + ) - return when_rule_evaluator.evaluate(skip_conditions["when"]) - - for when in skip_conditions: - condition = evaluate_when_rules( - when["when"], - self.schema, - self.metadata, - self.answer_store, - self.list_store, - current_location=this_location, - routing_path_block_ids=routing_path_block_ids, - ) - if condition is True: - return True - return False + return when_rule_evaluator.evaluate(skip_conditions["when"]) def _get_next_block_id(self, rule): if "group" in rule: @@ -279,28 +255,23 @@ def _get_next_block_id(self, rule): return rule["block"] def _remove_current_blocks_answers_for_backwards_routing( - self, rules: dict, this_location: Location + self, rule: dict, this_location: Location ) -> None: if block_id := this_location.block_id: answer_ids_for_current_block = self.schema.get_answer_ids_for_block( block_id ) - if "when" in rules: - if isinstance(rules["when"], dict): - self._remove_current_blocks_answers_for_new_backwards_routing( - rules["when"], answer_ids_for_current_block - ) - else: - for rule in rules["when"]: - if "id" in rule and rule["id"] in answer_ids_for_current_block: - self.answer_store.remove_answer(rule["id"]) + if "when" in rule: + self._remove_block_anwers_for_backward_routing_according_to_when_rule( + rule["when"], answer_ids_for_current_block + ) self.progress_store.remove_location_for_backwards_routing(this_location) self.progress_store.update_section_status( CompletionStatus.IN_PROGRESS, this_location.section_id ) - def _remove_current_blocks_answers_for_new_backwards_routing( + def _remove_block_anwers_for_backward_routing_according_to_when_rule( self, rules: dict, answer_ids_for_current_block: list[str] ) -> None: operands = self.schema.get_operands(rules) @@ -313,13 +284,6 @@ def _remove_current_blocks_answers_for_new_backwards_routing( self.answer_store.remove_answer(rule["identifier"]) if QuestionnaireSchema.has_operator(rule): - return self._remove_current_blocks_answers_for_new_backwards_routing( + return self._remove_block_anwers_for_backward_routing_according_to_when_rule( rule, answer_ids_for_current_block ) - - -def should_goto_new(rule, when_rule_evaluator): - if when_rule := rule.get("when"): - return when_rule_evaluator.evaluate(when_rule) - - return True diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index e293823464..754f5680ef 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -382,29 +382,24 @@ def get_post_submission(self) -> ImmutableDict: schema: ImmutableDict = self.json.get("post_submission", ImmutableDict({})) return schema - def _is_list_name_in_rule( - self, rules: Union[Mapping, Sequence], list_name: str - ) -> bool: - if isinstance(rules, Mapping) and QuestionnaireSchema.has_operator(rules): - rules = self.get_operands(rules) + def _is_list_name_in_rule(self, when_rule: Mapping, list_name: str) -> bool: + if not QuestionnaireSchema.has_operator(when_rule): + return False - for rule in rules: - if not isinstance(rule, Mapping): - continue - - # Old rules - if "list" in rule: - return rule.get("list") == list_name + operands = self.get_operands(when_rule) - # New rules - if "source" in rule: - return ( - rule.get("source") == "list" and rule.get("identifier") == list_name + for operand in operands: + if not isinstance(operand, Mapping): + continue + if "source" in operand: + return bool( + operand.get("source") == "list" + and operand.get("identifier") == list_name ) # Nested rules - if QuestionnaireSchema.has_operator(rule): - return self._is_list_name_in_rule(rule, list_name) + if QuestionnaireSchema.has_operator(operand): + return self._is_list_name_in_rule(operand, list_name) return False @@ -421,7 +416,7 @@ def _section_ids_associated_to_list_name(self, list_name: str) -> list[str]: ignore_keys = ["question_variants", "content_variants"] when_rules = self.get_values_for_key(section, "when", ignore_keys) - rule: Union[Mapping, list] = next(when_rules, []) + rule: Mapping = next(when_rules, {}) if self._is_list_name_in_rule(rule, list_name): section_ids.append(section["id"]) return section_ids @@ -876,9 +871,7 @@ def _get_rules_section_dependencies( identifier: Optional[str] = rule.get("identifier") source: Optional[str] = rule.get("source") - if "id" in rule: - answer_id_list.append(rule["id"]) - elif source == "answers" and identifier: + if source == "answers" and identifier: answer_id_list.append(identifier) elif source == "calculated_summary" and identifier: calculated_summary_block = self.get_block(identifier) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 6104c1fcd9..739ec34e54 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -10,7 +10,6 @@ from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.questionnaire.when_rules import evaluate_when_rules class Router: @@ -392,32 +391,18 @@ def _is_section_enabled(self, section: Mapping) -> bool: section["id"] ) - if isinstance(enabled, dict): - when_rule_evaluator = RuleEvaluator( - self._schema, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, - location=None, - routing_path_block_ids=routing_path_block_ids, - ) - - return bool(when_rule_evaluator.evaluate(enabled["when"])) - - return any( - evaluate_when_rules( - condition["when"], - self._schema, - self._metadata, - self._answer_store, - self._list_store, - current_location=None, - routing_path_block_ids=routing_path_block_ids, - ) - for condition in enabled + when_rule_evaluator = RuleEvaluator( + self._schema, + self._answer_store, + self._list_store, + self._metadata, + self._response_metadata, + location=None, + routing_path_block_ids=routing_path_block_ids, ) + return bool(when_rule_evaluator.evaluate(enabled["when"])) + @staticmethod def get_next_block_url( location: Location, routing_path: RoutingPath, **kwargs: Optional[str] diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index d6c207ea4e..3e4c23dfc5 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -1,7 +1,6 @@ from werkzeug.datastructures import ImmutableDict from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.questionnaire.when_rules import evaluate_when_rules def choose_variant( @@ -18,28 +17,18 @@ def choose_variant( if block.get(single_key): return block[single_key] for variant in block.get(variants_key, []): - when_rules = variant.get("when", []) - - if isinstance(when_rules, dict): - when_rule_evaluator = RuleEvaluator( - schema, - answer_store, - list_store, - metadata, - response_metadata, - location=current_location, - ) - - if when_rule_evaluator.evaluate(when_rules): - return variant[single_key] - elif evaluate_when_rules( - when_rules, + when_rules = variant["when"] + + when_rule_evaluator = RuleEvaluator( schema, - metadata, answer_store, list_store, - current_location=current_location, - ): + metadata, + response_metadata, + location=current_location, + ) + + if when_rule_evaluator.evaluate(when_rules): return variant[single_key] diff --git a/app/questionnaire/when_rules.py b/app/questionnaire/when_rules.py deleted file mode 100644 index 7eec2bde06..0000000000 --- a/app/questionnaire/when_rules.py +++ /dev/null @@ -1,320 +0,0 @@ -import logging -from datetime import datetime, timezone -from typing import Optional - -from dateutil.relativedelta import relativedelta - -from app.data_models.answer import AnswerValueTypes -from app.questionnaire.rules.utils import parse_datetime - -MAX_REPEATS = 25 - -logger = logging.getLogger(__name__) - - -def evaluate_comparison_rule(when, answer_value, comparison_value): - """ - Determine whether a comparison rule will be satisfied based on an - answer value, and a value to compare it to. - :param when: The when clause to evaluate - :param answer_value: The value of the answer - :param comparison_value: The value to compare the answer to. - :return (bool): The result of the evaluation - """ - condition = when["condition"] - - return evaluate_condition(condition, answer_value, comparison_value) - - -def evaluate_rule(when, answer_value): - """ - Determine whether a rule will be satisfied based on a given answer - :param when: The when clause to evaluate - :param answer_value: The value of the answer - :return (bool): The result of the evaluation - """ - - match_value = when.get("value", when.get("values")) - - condition = when["condition"] - # Evaluate the condition on the routing rule - return evaluate_condition(condition, answer_value, match_value) - - -def evaluate_date_rule(when, answer_store, schema, metadata, answer_value): - date_comparison = when["date_comparison"] - - answer_value = parse_datetime(answer_value) - match_value = get_date_match_value(date_comparison, answer_store, schema, metadata) - condition = when.get("condition") - - if not answer_value or not match_value or not condition: - return False - - # Evaluate the condition on the routing rule - return evaluate_condition(condition, answer_value, match_value) - - -def evaluate_condition(condition, answer_value, match_value): - """ - :param condition: string representation of comparison operator - :param answer_value: the left hand side operand in the comparison - :param match_value: the right hand side operand in the comparison - :return: boolean value of comparing lhs and rhs using the specified operator - """ - answer_and_match = answer_value is not None and match_value is not None - - if condition in {"equals", "not equals", "equals any", "not equals any"}: - answer_value = casefold(answer_value) - - if isinstance(match_value, (list, tuple)): - match_value = list(map(casefold, match_value)) - else: - match_value = casefold(match_value) - - comparison_operators = { - "equals": lambda answer_value, match_value: answer_value == match_value, - "not equals": lambda answer_value, match_value: answer_value != match_value, - "equals any": lambda answer_value, match_values: answer_value in match_values, - "not equals any": lambda answer_value, match_values: answer_value - not in match_values, - "contains": lambda answer_values, match_value: answer_and_match - and match_value in answer_values, - "not contains": lambda answer_values, match_value: answer_and_match - and match_value not in answer_values, - "contains any": lambda answer_values, match_values: answer_and_match - and any(match_value in answer_values for match_value in match_values), - "contains all": lambda answer_values, match_values: answer_and_match - and all(match_value in answer_values for match_value in match_values), - "set": lambda answer_value, _: answer_value not in (None, []), - "not set": lambda answer_value, _: answer_value in (None, []), - "greater than": lambda answer_value, match_value: answer_and_match - and answer_value > match_value, - "greater than or equal to": lambda answer_value, match_value: answer_and_match - and answer_value >= match_value, - "less than": lambda answer_value, match_value: answer_and_match - and answer_value < match_value, - "less than or equal to": lambda answer_value, match_value: answer_and_match - and answer_value <= match_value, - } - - match_function = comparison_operators[condition] - - return match_function(answer_value, match_value) - - -def casefold(value): - try: - return value.casefold() - except AttributeError: - return value - - -def get_date_match_value(date_comparison, answer_store, schema, metadata): - match_value = None - - if "value" in date_comparison: - if date_comparison["value"] == "now": - match_value = datetime.now(timezone.utc).strftime("%Y-%m-%d") - else: - match_value = date_comparison["value"] - elif "id" in date_comparison: - match_value = get_answer_value(date_comparison["id"], answer_store, schema) - elif "meta" in date_comparison: - match_value = get_metadata_value(metadata, date_comparison["meta"]) - - match_value = parse_datetime(match_value) - - if "offset_by" in date_comparison and match_value: - offset = date_comparison["offset_by"] - match_value = match_value + relativedelta( - days=offset.get("days", 0), - months=offset.get("months", 0), - years=offset.get("years", 0), - ) - - return match_value - - -def evaluate_goto( - goto_rule, - schema, - metadata, - answer_store, - list_store, - current_location, - routing_path_block_ids=None, -): - """ - Determine whether a goto rule will be satisfied based on a given answer - :param goto_rule: goto rule to evaluate - :param schema: survey schema - :param metadata: metadata for evaluating rules with metadata conditions - :param answer_store: store of answers to evaluate - :param list_store: store of lists to evaluate - :param current_location: the location to use when evaluating when rules - :param routing_path_block_ids: the routing path block ids used to evaluate if answer is on the path - :return: True if the when condition has been met otherwise False - """ - if "when" in goto_rule: - return evaluate_when_rules( - goto_rule["when"], - schema, - metadata, - answer_store, - list_store, - current_location, - routing_path_block_ids=routing_path_block_ids, - ) - return True - - -def _is_answer_on_path(schema, answer, routing_path_block_ids): - block_id = schema.get_block_for_answer_id(answer.answer_id)["id"] - return block_id in routing_path_block_ids - - -def _get_comparison_id_value( - when_rule, answer_store, schema, current_location=None, routing_path_block_ids=None -): - """ - Gets the value of a comparison id specified as an operand in a comparator - """ - if current_location and when_rule["comparison"]["source"] == "location": - try: - return getattr(current_location, when_rule["comparison"]["id"]) - except AttributeError: - return None - - answer_id = when_rule["comparison"]["id"] - list_item_id = current_location.list_item_id if current_location else None - - return get_answer_value( - answer_id, - answer_store, - schema, - list_item_id=list_item_id, - routing_path_block_ids=routing_path_block_ids, - ) - - -def _get_when_rule_value( - when_rule, - answer_store, - list_store, - schema, - metadata, - list_item_id=None, - routing_path_block_ids=None, -): - """ - Get the value from a when rule. - :raises: Exception if none of `id` or `meta` are provided. - :return: The value to use in a when rule - """ - if "id" in when_rule: - value = get_answer_value( - when_rule["id"], - answer_store, - schema, - list_item_id=list_item_id, - routing_path_block_ids=routing_path_block_ids, - ) - elif "meta" in when_rule: - value = get_metadata_value(metadata, when_rule["meta"]) - elif "id_selector" in when_rule: - value = getattr(list_store.get(when_rule["list"]), when_rule["id_selector"]) - elif "list" in when_rule: - value = get_list_count(list_store, when_rule["list"]) - else: - raise NotImplementedError("The when rule is invalid") - - return value - - -def evaluate_when_rules( - when_rules, - schema, - metadata, - answer_store, - list_store, - current_location=None, - routing_path_block_ids=None, -): - """ - Whether the skip condition has been met. - :param when_rules: when rules to evaluate - :param schema: survey schema - :param metadata: metadata for evaluating rules with metadata conditions - :param answer_store: store of answers to evaluate - :param list_store: store of lists to evaluate - :param current_location: The location to use when evaluating when rules - :param routing_path_block_ids: The routing path block ids to use when evaluating when rules - :return: True if the when condition has been met otherwise False - """ - for when_rule in when_rules: - list_item_id = current_location.list_item_id if current_location else None - - value = _get_when_rule_value( - when_rule, - answer_store, - list_store, - schema, - metadata, - list_item_id=list_item_id, - routing_path_block_ids=routing_path_block_ids, - ) - - if "date_comparison" in when_rule: - if not evaluate_date_rule(when_rule, answer_store, schema, metadata, value): - return False - elif "comparison" in when_rule: - comparison_id_value = _get_comparison_id_value( - when_rule, - answer_store, - schema, - current_location, - routing_path_block_ids, - ) - if not evaluate_comparison_rule(when_rule, value, comparison_id_value): - return False - else: - if not evaluate_rule(when_rule, value): - return False - - return True - - -def get_answer_for_answer_id(answer_id, answer_store, schema, list_item_id): - list_item_id = ( - list_item_id if list_item_id and schema.is_repeating_answer(answer_id) else None - ) - - answer = answer_store.get_answer( - answer_id, list_item_id - ) or schema.get_default_answer(answer_id) - - return answer - - -def get_answer_value( - answer_id, answer_store, schema, list_item_id=None, routing_path_block_ids=None -) -> Optional[AnswerValueTypes]: - answer = get_answer_for_answer_id(answer_id, answer_store, schema, list_item_id) - - if not answer: - return None - - if routing_path_block_ids: - if _is_answer_on_path(schema, answer, routing_path_block_ids): - return answer.value - else: - return answer.value - - -def get_metadata_value(metadata, key): - return metadata[key] - - -def get_list_count(list_store, list_name): - return len(list_store[list_name].items) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 4caabb0766..7be13f12fc 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -187,21 +187,13 @@ }, "routing_rules": [ { - "goto": { - "block": "block381", - "when": [ - { - "id": "answer434", - "condition": "equals any", - "values": ["Yes"] - } - ] + "block": "block381", + "when": { + "==": [{ "identifier": "answer434", "source": "answers" }, "Yes"] } }, { - "goto": { - "block": "block380" - } + "block": "block380" } ] }, @@ -424,21 +416,13 @@ }, "routing_rules": [ { - "goto": { - "block": "block4953", - "when": [ - { - "id": "answer6287", - "condition": "equals any", - "values": ["Yes"] - } - ] + "block": "block4953", + "when": { + "==": [{ "identifier": "answer6287", "source": "answers" }, "Yes"] } }, { - "goto": { - "block": "block383" - } + "block": "block383" } ] }, diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 5fb6e20df9..38e8483bdc 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -156,17 +156,17 @@ } }, { - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" ] } - ], + }, "type": "Question", "id": "fourth-number-block", "question": { @@ -186,17 +186,17 @@ } }, { - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" ] } - ], + }, "type": "Question", "id": "fourth-and-a-half-number-block", "question": { @@ -283,17 +283,17 @@ ], "title": "Grand total of previous values" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "No" - } + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "No" ] } - ] + } }, { "type": "CalculatedSummary", @@ -311,17 +311,17 @@ ], "title": "Grand total of previous values" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" ] } - ] + } }, { "type": "CalculatedSummary", diff --git a/schemas/test/en/test_conditional_combined_routing.json b/schemas/test/en/test_conditional_combined_routing.json index 85eaf1b7c8..a8a2935baa 100644 --- a/schemas/test/en/test_conditional_combined_routing.json +++ b/schemas/test/en/test_conditional_combined_routing.json @@ -74,33 +74,32 @@ }, "routing_rules": [ { - "goto": { - "block": "response-any", - "when": [ + "block": "response-any", + "when": { + "in": [ { - "id": "conditional-routing-answer", - "condition": "equals any", - "values": ["Yes", "Sometimes"] - } + "identifier": "conditional-routing-answer", + "source": "answers" + }, + ["Yes", "Sometimes"] ] } }, { - "goto": { - "block": "response-not-any", - "when": [ + "block": "response-not-any", + "when": { + "not": [ { - "id": "conditional-routing-answer", - "condition": "not equals any", - "values": ["Yes", "Sometimes", "I don’t like coffee", "No, I don’t drink any hot drinks"] + "in": [ + { "identifier": "conditional-routing-answer", "source": "answers" }, + ["Yes", "Sometimes", "I don’t like coffee", "No, I don’t drink any hot drinks"] + ] } ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_confirmation_question.json b/schemas/test/en/test_confirmation_question.json index 14308c2362..5ac6abcfb7 100644 --- a/schemas/test/en/test_confirmation_question.json +++ b/schemas/test/en/test_confirmation_question.json @@ -91,17 +91,17 @@ { "type": "ConfirmationQuestion", "id": "confirm-zero-employees-block", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + ">": [ { - "id": "number-of-employees-total", - "condition": "greater than", - "value": 0 - } + "source": "answers", + "identifier": "number-of-employees-total" + }, + 0 ] } - ], + }, "question": { "type": "General", "answers": [ @@ -150,21 +150,19 @@ }, "routing_rules": [ { - "goto": { - "when": [ + "when": { + "==": [ { - "value": "No I need to correct this", - "id": "confirm-zero-employees-answer", - "condition": "equals" - } - ], - "block": "number-of-employees-total-block" - } + "identifier": "confirm-zero-employees-answer", + "source": "answers" + }, + "No I need to correct this" + ] + }, + "block": "number-of-employees-total-block" }, { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_new_confirmation_question.json b/schemas/test/en/test_confirmation_question_backwards_routing.json similarity index 100% rename from schemas/test/en/test_new_confirmation_question.json rename to schemas/test/en/test_confirmation_question_backwards_routing.json diff --git a/schemas/test/en/test_confirmation_question_within_repeating_section.json b/schemas/test/en/test_confirmation_question_within_repeating_section.json index a9026f32db..938a8a5f07 100644 --- a/schemas/test/en/test_confirmation_question_within_repeating_section.json +++ b/schemas/test/en/test_confirmation_question_within_repeating_section.json @@ -186,7 +186,9 @@ { "id": "default-section", "title": "Questions", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "repeat": { "for_list": "people", "title": { @@ -289,22 +291,21 @@ { "type": "ConfirmationQuestion", "id": "confirm-dob-block", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<=": [ { - "condition": "less than or equal to", - "date_comparison": { - "offset_by": { - "years": -16 - }, - "value": "now" - }, - "id": "date-of-birth-answer" - } + "date": [ + { + "source": "answers", + "identifier": "date-of-birth-answer" + } + ] + }, + { "date": ["now", { "years": -16 }] } ] } - ], + }, "question": { "answers": [ { @@ -413,21 +414,19 @@ }, "routing_rules": [ { - "goto": { - "block": "dob-block", - "when": [ + "block": "dob-block", + "when": { + "==": [ { - "id": "confirm-date-of-birth-answer", - "condition": "equals", - "value": "No, I need to change their date of birth" - } + "source": "answers", + "identifier": "confirm-date-of-birth-answer" + }, + "No, I need to change their date of birth" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] }, @@ -470,8 +469,14 @@ "type": "Radio", "default": "Yes", "options": [ - { "label": "Yes", "value": "Yes" }, - { "label": "No", "value": "No" } + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } ] } ] diff --git a/schemas/test/en/test_custom_page_titles.json b/schemas/test/en/test_custom_page_titles.json index 4246ac6519..8d1c99f1b2 100644 --- a/schemas/test/en/test_custom_page_titles.json +++ b/schemas/test/en/test_custom_page_titles.json @@ -892,17 +892,18 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "household", - "condition": "less than", - "value": 2 - } + "source": "list", + "identifier": "household", + "selector": "count" + }, + 2 ] } - ] + } } ] } diff --git a/schemas/test/en/test_default_with_skip.json b/schemas/test/en/test_default_with_skip.json index 473de10588..08963ca5bf 100644 --- a/schemas/test/en/test_default_with_skip.json +++ b/schemas/test/en/test_default_with_skip.json @@ -70,17 +70,17 @@ "title": "Question Two", "type": "General" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "!=": [ { - "condition": "not equals", - "id": "answer-one", - "value": 1 - } + "source": "answers", + "identifier": "answer-one" + }, + 1 ] } - ] + } }, { "type": "Question", @@ -98,17 +98,17 @@ "title": "Question Three", "type": "General" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "condition": "equals", - "id": "answer-two", - "value": 1 - } + "source": "answers", + "identifier": "answer-two" + }, + 1 ] } - ] + } } ], "id": "group" diff --git a/schemas/test/en/test_dob_date.json b/schemas/test/en/test_dob_date.json index 0a7005e378..70581d7670 100644 --- a/schemas/test/en/test_dob_date.json +++ b/schemas/test/en/test_dob_date.json @@ -58,43 +58,43 @@ }, "routing_rules": [ { - "goto": { - "block": "over-sixteen", - "when": [ + "block": "over-sixteen", + "when": { + "<": [ { - "id": "date-of-birth-answer", - "condition": "less than", - "date_comparison": { - "value": "now", - "offset_by": { - "years": -16 + "date": [ + { + "identifier": "date-of-birth-answer", + "source": "answers" } - } + ] + }, + { + "date": ["now", { "years": -16 }] } ] } }, { - "goto": { - "block": "under-sixteen", - "when": [ + "block": "under-sixteen", + "when": { + ">=": [ { - "id": "date-of-birth-answer", - "condition": "greater than or equal to", - "date_comparison": { - "value": "now", - "offset_by": { - "years": -16 + "date": [ + { + "identifier": "date-of-birth-answer", + "source": "answers" } - } + ] + }, + { + "date": ["now", { "years": -16 }] } ] } }, { - "goto": { - "block": "dob-age" - } + "block": "dob-age" } ] }, @@ -121,21 +121,19 @@ }, "routing_rules": [ { - "goto": { - "block": "over-sixteen", - "when": [ + "block": "over-sixteen", + "when": { + ">=": [ { - "id": "dob-age-answer", - "condition": "greater than or equal to", - "value": 16 - } + "source": "answers", + "identifier": "dob-age-answer" + }, + 16 ] } }, { - "goto": { - "block": "under-sixteen" - } + "block": "under-sixteen" } ] }, @@ -182,31 +180,29 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "or": [ { - "id": "dob-age-answer", - "condition": "greater than or equal to", - "value": 16 - } - ] - }, - { - "when": [ + ">=": [ + { + "source": "answers", + "identifier": "dob-age-answer" + }, + 16 + ] + }, { - "id": "date-of-birth-answer", - "condition": "less than", - "date_comparison": { - "value": "now", - "offset_by": { - "years": -16 + "<": [ + { "date": [{ "identifier": "date-of-birth-answer", "source": "answers" }] }, + { + "date": ["now", { "years": -16 }] } - } + ] } ] } - ] + } } ], "id": "test" diff --git a/schemas/test/en/test_hub_and_spoke.json b/schemas/test/en/test_hub_and_spoke.json index 0cdfd52000..7535553170 100644 --- a/schemas/test/en/test_hub_and_spoke.json +++ b/schemas/test/en/test_hub_and_spoke.json @@ -92,20 +92,19 @@ }, "routing_rules": [ { - "goto": { - "group": "checkboxes", - "when": [ + "group": "checkboxes", + "when": { + "!=": [ { - "id": "employment-status-answer", - "condition": "set" - } + "identifier": "employment-status-answer", + "source": "answers" + }, + null ] } }, { - "goto": { - "block": "employment-type" - } + "block": "employment-type" } ] }, @@ -155,7 +154,9 @@ { "id": "accommodation-section", "title": "Accommodation", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ @@ -192,7 +193,9 @@ ] }, { - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ @@ -251,17 +254,17 @@ "type": "General" }, "type": "Question", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "does-anyone-live-here-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "does-anyone-live-here-answer" + }, + "No" ] } - ] + } } ], "id": "household-question-group", @@ -275,7 +278,9 @@ "id": "relationships-section", "title": "Relationships", "show_on_hub": false, - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ diff --git a/schemas/test/en/test_hub_and_spoke_custom_content.json b/schemas/test/en/test_hub_and_spoke_custom_content.json index a92000ff8d..b951e23cba 100644 --- a/schemas/test/en/test_hub_and_spoke_custom_content.json +++ b/schemas/test/en/test_hub_and_spoke_custom_content.json @@ -33,7 +33,9 @@ }, "sections": [ { - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ @@ -92,17 +94,17 @@ "type": "General" }, "type": "Question", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "does-anyone-live-here-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "does-anyone-live-here-answer" + }, + "No" ] } - ] + } } ], "id": "household-question-group", diff --git a/schemas/test/en/test_hub_complete_sections.json b/schemas/test/en/test_hub_complete_sections.json index e19f7c53db..e0691427e9 100644 --- a/schemas/test/en/test_hub_complete_sections.json +++ b/schemas/test/en/test_hub_complete_sections.json @@ -23,7 +23,9 @@ ], "questionnaire_flow": { "type": "Hub", - "options": { "required_completed_sections": ["employment-section"] } + "options": { + "required_completed_sections": ["employment-section"] + } }, "sections": [ { @@ -92,20 +94,19 @@ }, "routing_rules": [ { - "goto": { - "group": "checkboxes", - "when": [ + "group": "checkboxes", + "when": { + "!=": [ { - "id": "employment-status-answer", - "condition": "set" - } + "identifier": "employment-status-answer", + "source": "answers" + }, + null ] } }, { - "goto": { - "block": "employment-type" - } + "block": "employment-type" } ] }, @@ -155,7 +156,9 @@ { "id": "accommodation-section", "title": "Accommodation", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ diff --git a/schemas/test/en/test_hub_section_required_and_enabled.json b/schemas/test/en/test_hub_section_required_and_enabled.json index 8da0b88f61..6320062ce6 100644 --- a/schemas/test/en/test_hub_section_required_and_enabled.json +++ b/schemas/test/en/test_hub_section_required_and_enabled.json @@ -106,17 +106,17 @@ "title": "Relationships count" } ], - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "==": [ + "Yes", { - "id": "household-relationships-answer", - "condition": "equals", - "value": "Yes" + "source": "answers", + "identifier": "household-relationships-answer" } ] } - ] + } } ] } diff --git a/schemas/test/en/test_interstitial_definition.json b/schemas/test/en/test_interstitial_definition.json index a8f9659189..4c9eed4472 100644 --- a/schemas/test/en/test_interstitial_definition.json +++ b/schemas/test/en/test_interstitial_definition.json @@ -91,7 +91,6 @@ "type": "Radio" } ], - "id": "content-variant-definition-question", "type": "General", "title": "What would you like to see a definition about?" @@ -130,13 +129,15 @@ } ] }, - "when": [ - { - "id": "content-variant-definition-answer", - "condition": "equals", - "value": "Answer" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "content-variant-definition-answer" + }, + "Answer" + ] + } }, { "content": { @@ -157,13 +158,15 @@ } ] }, - "when": [ - { - "id": "content-variant-definition-answer", - "condition": "equals", - "value": "Question" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "content-variant-definition-answer" + }, + "Question" + ] + } } ] } diff --git a/schemas/test/en/test_last_viewed_question_guidance.json b/schemas/test/en/test_last_viewed_question_guidance.json index 478e1db2ed..037a022254 100644 --- a/schemas/test/en/test_last_viewed_question_guidance.json +++ b/schemas/test/en/test_last_viewed_question_guidance.json @@ -171,39 +171,46 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "and": [ { - "condition": "equals", - "id": "anyone-usually-live-at-answer", - "value": "No" + "==": [ + { + "identifier": "anyone-usually-live-at-answer", + "source": "answers" + }, + "No" + ] }, { - "condition": "less than", - "list": "people", - "value": 1 + "<": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 1 + ] } ] } }, { - "goto": { - "block": "list-collector" - } + "block": "list-collector" } ], - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "condition": "equals", - "id": "you-live-here", - "value": "Yes" - } + "identifier": "you-live-here", + "source": "answers" + }, + "Yes" ] } - ], + }, "type": "Question" }, { @@ -946,17 +953,18 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "source": "list", + "identifier": "people", + "selector": "count" + }, + 2 ] } - ] + } } ], "id": "relationship-group", diff --git a/schemas/test/en/test_list_change_evaluates_sections.json b/schemas/test/en/test_list_change_evaluates_sections.json index 17c1f926bc..fe038d1578 100644 --- a/schemas/test/en/test_list_change_evaluates_sections.json +++ b/schemas/test/en/test_list_change_evaluates_sections.json @@ -238,7 +238,9 @@ ] }, { - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "groups": [ { "blocks": [ @@ -295,21 +297,20 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "==": [ { - "condition": "equals", - "list": "people", - "value": 0 - } + "source": "list", + "identifier": "people", + "selector": "count" + }, + 0 ] } }, { - "goto": { - "block": "own-or-rent" - } + "block": "own-or-rent" } ], "type": "Question" diff --git a/schemas/test/en/test_list_collector_driving_checkbox.json b/schemas/test/en/test_list_collector_driving_checkbox.json index d10bb8e63c..6637a42efb 100644 --- a/schemas/test/en/test_list_collector_driving_checkbox.json +++ b/schemas/test/en/test_list_collector_driving_checkbox.json @@ -154,13 +154,15 @@ } ] }, - "when": [ - { - "id": "you-live-here", - "condition": "equals", - "value": "Yes, I usually live here" - } - ] + "when": { + "==": [ + { + "identifier": "you-live-here", + "source": "answers" + }, + "Yes, I usually live here" + ] + } }, { "question": { @@ -212,44 +214,44 @@ } ] }, - "when": [ - { - "id": "you-live-here", - "condition": "equals", - "value": "No, I don’t usually live here" - } - ] + "when": { + "==": [ + { + "identifier": "you-live-here", + "source": "answers" + }, + "No, I don’t usually live here" + ] + } } ], "routing_rules": [ { - "goto": { - "block": "list-collector-temporary-away-stay", - "when": [ + "block": "list-collector-temporary-away-stay", + "when": { + "in": [ + "None of these apply, no-one usually lives here", { - "id": "anyone-usually-live-at-answer-exclusive", - "condition": "contains", - "value": "None of these apply, no-one usually lives here" + "identifier": "anyone-usually-live-at-answer-exclusive", + "source": "answers" } ] } }, { - "goto": { - "block": "list-collector-temporary-away-stay", - "when": [ + "block": "list-collector-temporary-away-stay", + "when": { + "in": [ + "None of the these apply, I am the only person who usually lives here", { - "id": "anyone-usually-live-at-answer-exclusive", - "condition": "contains", - "value": "None of the these apply, I am the only person who usually lives here" + "identifier": "anyone-usually-live-at-answer-exclusive", + "source": "answers" } ] } }, { - "goto": { - "block": "list-collector" - } + "block": "list-collector" } ] }, @@ -306,13 +308,16 @@ } ] }, - "when": [ - { - "condition": "equals", - "list": "people", - "value": 0 - } - ] + "when": { + "==": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 0 + ] + } }, { "question": { @@ -334,13 +339,16 @@ } ] }, - "when": [ - { - "condition": "greater than", - "list": "people", - "value": 0 - } - ] + "when": { + ">": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 0 + ] + } } ] }, @@ -539,13 +547,16 @@ } ] }, - "when": [ - { - "condition": "equals", - "list": "people", - "value": 0 - } - ] + "when": { + "==": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 0 + ] + } }, { "question": { @@ -567,13 +578,16 @@ } ] }, - "when": [ - { - "condition": "greater than", - "list": "people", - "value": 0 - } - ] + "when": { + ">": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 0 + ] + } } ] }, diff --git a/schemas/test/en/test_list_collector_driving_question.json b/schemas/test/en/test_list_collector_driving_question.json index 20514e00f7..9046699d67 100644 --- a/schemas/test/en/test_list_collector_driving_question.json +++ b/schemas/test/en/test_list_collector_driving_question.json @@ -93,21 +93,19 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "==": [ { - "id": "anyone-usually-live-at-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "anyone-usually-live-at-answer" + }, + "No" ] } }, { - "goto": { - "block": "anyone-else-live-at" - } + "block": "anyone-else-live-at" } ] }, diff --git a/schemas/test/en/test_list_collector_primary_and_collector_with_driving_question.json b/schemas/test/en/test_list_collector_primary_and_collector_with_driving_question.json index af119748a6..5b47d6e0d5 100644 --- a/schemas/test/en/test_list_collector_primary_and_collector_with_driving_question.json +++ b/schemas/test/en/test_list_collector_primary_and_collector_with_driving_question.json @@ -146,21 +146,19 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "==": [ { - "id": "anyone-else-usually-live-at-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "anyone-else-usually-live-at-answer" + }, + "No" ] } }, { - "goto": { - "block": "anyone-else-live-at" - } + "block": "anyone-else-live-at" } ] }, diff --git a/schemas/test/en/test_list_collector_primary_person.json b/schemas/test/en/test_list_collector_primary_person.json index dcf177c54c..1ada4c69aa 100644 --- a/schemas/test/en/test_list_collector_primary_person.json +++ b/schemas/test/en/test_list_collector_primary_person.json @@ -139,39 +139,46 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "and": [ { - "condition": "equals", - "id": "anyone-usually-live-at-answer", - "value": "No" + "==": [ + { + "source": "answers", + "identifier": "anyone-usually-live-at-answer" + }, + "No" + ] }, { - "condition": "less than", - "list": "people", - "value": 1 + "<": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 1 + ] } ] } }, { - "goto": { - "block": "list-collector" - } + "block": "list-collector" } ], - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "condition": "equals", - "id": "you-live-here", - "value": "Yes" - } + "source": "answers", + "identifier": "you-live-here" + }, + "Yes" ] } - ], + }, "type": "Question" }, { diff --git a/schemas/test/en/test_list_collector_same_name_items.json b/schemas/test/en/test_list_collector_same_name_items.json index 864fec0323..18109e2242 100644 --- a/schemas/test/en/test_list_collector_same_name_items.json +++ b/schemas/test/en/test_list_collector_same_name_items.json @@ -140,39 +140,46 @@ }, "routing_rules": [ { - "goto": { - "section": "End", - "when": [ + "section": "End", + "when": { + "and": [ { - "condition": "equals", - "id": "anyone-usually-live-at-answer", - "value": "No" + "==": [ + { + "identifier": "anyone-usually-live-at-answer", + "source": "answers" + }, + "No" + ] }, { - "condition": "less than", - "list": "people", - "value": 1 + "<": [ + { + "source": "list", + "identifier": "people", + "selector": "count" + }, + 1 + ] } ] } }, { - "goto": { - "block": "list-collector" - } + "block": "list-collector" } ], - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "condition": "equals", - "id": "you-live-here", - "value": "Yes" - } + "source": "answers", + "identifier": "you-live-here" + }, + "Yes" ] } - ], + }, "type": "Question" }, { @@ -263,7 +270,9 @@ { "transform": "format_name", "arguments": { - "include_middle_names": { "source": "previous_transform" }, + "include_middle_names": { + "source": "previous_transform" + }, "first_name": { "source": "answers", "identifier": "first-name" @@ -354,7 +363,9 @@ { "transform": "format_name", "arguments": { - "include_middle_names": { "source": "previous_transform" }, + "include_middle_names": { + "source": "previous_transform" + }, "first_name": { "source": "answers", "identifier": "first-name" diff --git a/schemas/test/en/test_list_collector_two_list_collectors.json b/schemas/test/en/test_list_collector_two_list_collectors.json index 001faf7cf4..8f6c798d43 100644 --- a/schemas/test/en/test_list_collector_two_list_collectors.json +++ b/schemas/test/en/test_list_collector_two_list_collectors.json @@ -81,9 +81,7 @@ }, "routing_rules": [ { - "goto": { - "block": "list-collector" - } + "block": "list-collector" } ] }, @@ -273,9 +271,7 @@ }, "routing_rules": [ { - "goto": { - "block": "another-list-collector" - } + "block": "another-list-collector" } ], "type": "Question" diff --git a/schemas/test/en/test_list_collector_variants.json b/schemas/test/en/test_list_collector_variants.json index db15f07f79..44d1975b70 100644 --- a/schemas/test/en/test_list_collector_variants.json +++ b/schemas/test/en/test_list_collector_variants.json @@ -90,13 +90,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "Yes" + ] + } }, { "question": { @@ -124,13 +126,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "No" + ] + } } ], "add_block": { @@ -157,13 +161,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "No" + ] + } }, { "question": { @@ -185,13 +191,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "Yes" + ] + } } ] }, @@ -219,13 +227,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "Yes" + ] + } }, { "question": { @@ -247,13 +257,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "No" + ] + } } ] }, @@ -287,13 +299,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "Yes" + ] + } }, { "question": { @@ -321,13 +335,15 @@ } ] }, - "when": [ - { - "id": "you-live-here-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "you-live-here-answer" + }, + "No" + ] + } } ] }, diff --git a/schemas/test/en/test_list_collector_variants_primary_person.json b/schemas/test/en/test_list_collector_variants_primary_person.json index 582d8e9164..d4b7246a6d 100644 --- a/schemas/test/en/test_list_collector_variants_primary_person.json +++ b/schemas/test/en/test_list_collector_variants_primary_person.json @@ -91,13 +91,15 @@ } ] }, - "when": [ - { - "id": "variant-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "variant-answer" + }, + "No" + ] + } }, { "question": { @@ -119,13 +121,15 @@ } ] }, - "when": [ - { - "id": "variant-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "variant-answer" + }, + "Yes" + ] + } } ] }, @@ -156,13 +160,15 @@ } ] }, - "when": [ - { - "id": "variant-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "variant-answer" + }, + "Yes" + ] + } }, { "question": { @@ -190,13 +196,15 @@ } ] }, - "when": [ - { - "id": "variant-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "variant-answer" + }, + "No" + ] + } } ] }, diff --git a/schemas/test/en/test_list_collector_variants_section_summary.json b/schemas/test/en/test_list_collector_variants_section_summary.json index 6290356767..5b8ac29666 100644 --- a/schemas/test/en/test_list_collector_variants_section_summary.json +++ b/schemas/test/en/test_list_collector_variants_section_summary.json @@ -123,21 +123,19 @@ }, "routing_rules": [ { - "goto": { - "block": "confirmation-checkbox", - "when": [ + "block": "confirmation-checkbox", + "when": { + "==": [ { - "id": "any-companies-or-branches-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "any-companies-or-branches-answer" + }, + "No" ] } }, { - "goto": { - "block": "any-other-companies-or-branches" - } + "block": "any-other-companies-or-branches" } ] }, @@ -172,13 +170,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "Yes" + ] + } }, { "question": { @@ -206,13 +206,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "No" + ] + } } ], "add_block": { @@ -260,13 +262,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "Yes" + ] + } }, { "question": { @@ -309,13 +313,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "No" + ] + } } ] }, @@ -359,13 +365,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "Yes" + ] + } }, { "question": { @@ -403,13 +411,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "No" + ] + } } ] }, @@ -443,13 +453,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "No" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "No" + ] + } }, { "question": { @@ -477,13 +489,15 @@ } ] }, - "when": [ - { - "id": "uk-based-answer", - "condition": "equals", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "uk-based-answer" + }, + "Yes" + ] + } } ] }, diff --git a/schemas/test/en/test_metadata_routing.json b/schemas/test/en/test_metadata_routing.json index ae8f7ff5d2..4a4269e610 100644 --- a/schemas/test/en/test_metadata_routing.json +++ b/schemas/test/en/test_metadata_routing.json @@ -55,21 +55,19 @@ }, "routing_rules": [ { - "goto": { - "block": "block3", - "when": [ + "block": "block3", + "when": { + "==": [ { - "meta": "flag_1", - "condition": "equals", - "value": true - } + "identifier": "flag_1", + "source": "metadata" + }, + "true" ] } }, { - "goto": { - "block": "block2" - } + "block": "block2" } ] }, diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index 041ce17c21..5d247cfde3 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -156,17 +156,17 @@ } }, { - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "source": "answers", + "identifier": "skip-fourth-block-answer" + }, + "Yes" ] } - ], + }, "type": "Question", "id": "fourth-number-block", "question": { @@ -186,17 +186,17 @@ } }, { - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "source": "answers", + "identifier": "skip-fourth-block-answer" + }, + "Yes" ] } - ], + }, "type": "Question", "id": "fourth-and-a-half-number-block", "question": { @@ -343,17 +343,17 @@ }, "title": "Grand total of previous values" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "skip-fourth-block-answer", - "condition": "equals", - "value": "Yes" - } + "identifier": "skip-fourth-block-answer", + "source": "answers" + }, + "Yes" ] } - ] + } }, { "type": "CalculatedSummary", diff --git a/schemas/test/en/test_new_hub_section_required_and_enabled.json b/schemas/test/en/test_new_hub_section_required_and_enabled.json deleted file mode 100644 index 6320062ce6..0000000000 --- a/schemas/test/en/test_new_hub_section_required_and_enabled.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Hub & Spoke required and enabled sections", - "theme": "default", - "description": "A questionnaire to demo hub and spoke required and enabled sections", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": { "required_completed_sections": ["household-section", "relationships-section"] } - }, - "sections": [ - { - "id": "household-section", - "title": "Household", - "groups": [ - { - "id": "radio", - "title": "Household Relationships", - "blocks": [ - { - "type": "Question", - "id": "household-relationships-block", - "question": { - "type": "General", - "id": "household-relationships-question", - "title": "Is anyone related in this household?", - "answers": [ - { - "type": "Radio", - "id": "household-relationships-answer", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - } - } - ] - } - ] - }, - { - "id": "relationships-section", - "title": "Relationships", - "show_on_hub": false, - "groups": [ - { - "blocks": [ - { - "id": "relationships-count", - "question": { - "type": "General", - "id": "relationships-count-question", - "title": "How many people are related?", - "answers": [ - { - "type": "Radio", - "id": "relationships-count-answer", - "mandatory": true, - "options": [ - { - "label": "1", - "value": "1" - }, - { - "label": "2", - "value": "2" - }, - { - "label": "3+", - "value": "3+" - } - ] - } - ] - }, - "type": "Question" - } - ], - "id": "relationships-count-group", - "title": "Relationships count" - } - ], - "enabled": { - "when": { - "==": [ - "Yes", - { - "source": "answers", - "identifier": "household-relationships-answer" - } - ] - } - } - } - ] -} diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json b/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json deleted file mode 100644 index f3e6bea266..0000000000 --- a/schemas/test/en/test_new_routing_and_skipping_section_dependencies.json +++ /dev/null @@ -1,623 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Routing and Skipping Section Dependencies", - "theme": "default", - "description": "A questionnaire to test routing and skipping rules, when the rule references a different section to its current section", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": {} - }, - "sections": [ - { - "title": "Skip question", - "summary": { "show_on_completion": true }, - "id": "skip-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "skip-age", - "question": { - "answers": [ - { - "id": "skip-age-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ], - "id": "skip-age-question", - "title": "Do you want to skip all age questions in all sections", - "type": "General" - } - }, - { - "type": "Question", - "id": "skip-household-section", - "question": { - "id": "skip-household-section-question", - "title": "Do you want to skip the question about skipping the household summary section?", - "type": "General", - "answers": [ - { - "id": "skip-household-section-answer", - "label": "It will remove the enable section question from the routing path", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ] - } - }, - { - "type": "Question", - "id": "enable-section", - "question": { - "id": "enable-section-question", - "title": "Do you want to enable the household summary section?", - "type": "General", - "answers": [ - { - "id": "enable-section-answer", - "label": "Depending on the answer it will enable or disable the household summary section", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ] - }, - "skip_conditions": [ - { - "when": [ - { - "id": "skip-household-section-answer", - "condition": "equals", - "value": "Yes" - } - ] - } - ] - } - ], - "id": "skip-age-group" - } - ] - }, - { - "title": "Skip question confirmation", - "summary": { "show_on_completion": true }, - "id": "skip-confirmation-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "security", - "question": { - "answers": [ - { - "id": "security-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ], - "id": "security-question", - "title": "You understand that your personal details will be held securely and not shared with anyone?", - "type": "General" - }, - "routing_rules": [ - { - "block": "skip-confirmation", - "when": { - "==": [ - "Yes", - { - "source": "answers", - "identifier": "skip-age-answer" - } - ] - } - }, - { - "section": "End" - } - ] - }, - { - "type": "Question", - "id": "skip-confirmation", - "question": { - "answers": [ - { - "id": "skip-confirmation-answer", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ], - "id": "skip-confirmation-question", - "title": "Are you sure you want to skip all age questions in all sections?", - "type": "General" - } - } - ], - "id": "skip-confirmation-group" - } - ] - }, - { - "title": "Primary Person", - "summary": { "show_on_completion": true }, - "id": "primary-person", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "name-block", - "question": { - "answers": [ - { - "label": "Full Name", - "id": "name-answer", - "mandatory": false, - "type": "TextField" - } - ], - "id": "name-question", - "title": "What is your name?", - "type": "General" - } - }, - { - "type": "Question", - "id": "age", - "question": { - "answers": [ - { - "label": "Age in years", - "id": "age-answer", - "mandatory": false, - "type": "Number" - } - ], - "id": "age-question", - "title": "What is your age?", - "type": "General" - }, - "skip_conditions": { - "when": { - "and": [ - { - "==": [ - { - "source": "answers", - "identifier": "skip-age-answer" - }, - "Yes" - ] - }, - { - "!=": [ - { - "source": "answers", - "identifier": "skip-confirmation-answer" - }, - "No" - ] - } - ] - } - } - } - ], - "id": "primary-person-group", - "title": "Primary Person" - }, - { - "blocks": [ - { - "type": "Question", - "id": "reason-no-confirmation", - "question": { - "answers": [ - { - "id": "reason-no-confirmation-answer", - "mandatory": false, - "options": [ - { - "label": "I did not visit section 2, so confirmation was not needed", - "value": "I did not visit section 2, so confirmation was not needed" - }, - { - "label": "I did, but it was removed from the path as I changed my answer to No on the skip question", - "value": "I did, but it was removed from the path as I changed my answer to No on the skip question" - } - ], - "type": "Radio" - } - ], - "id": "reason-no-confirmation-question", - "title": "Why did you not answer the age skipping confirmation question?", - "type": "General" - } - } - ], - "id": "confirmation-group", - "title": "Confirmation Question", - "skip_conditions": { - "when": { - "or": [ - { - "==": [ - { - "source": "answers", - "identifier": "skip-confirmation-answer" - }, - "Yes" - ] - }, - { - "==": [ - { - "source": "answers", - "identifier": "skip-confirmation-answer" - }, - "No" - ] - } - ] - } - } - } - ] - }, - { - "enabled": { - "when": { - "==": [ - "Yes", - { - "source": "answers", - "identifier": "enable-section-answer" - } - ] - } - }, - "id": "household-section", - "title": "Household Summary", - "summary": { - "show_on_completion": true, - "items": [ - { - "type": "List", - "for_list": "people", - "title": "Household members", - "add_link_text": "Add someone to this household", - "empty_list_text": "There are no householders" - } - ] - }, - "groups": [ - { - "id": "group", - "title": "List", - "blocks": [ - { - "id": "list-collector", - "type": "ListCollector", - "for_list": "people", - "question": { - "id": "confirmation-question", - "type": "General", - "title": "Does anyone else live here?", - "answers": [ - { - "id": "anyone-else", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RedirectToListAddBlock" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "add_block": { - "id": "add-person", - "type": "ListAddQuestion", - "cancel_text": "Don’t need to add anyone else?", - "question": { - "id": "add-question", - "type": "General", - "title": "What is the name of the person?", - "answers": [ - { - "id": "first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "edit_block": { - "id": "edit-person", - "type": "ListEditQuestion", - "cancel_text": "Don’t need to change anything?", - "question": { - "id": "edit-question", - "type": "General", - "title": "What is the name of the person?", - "answers": [ - { - "id": "first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "remove_block": { - "id": "remove-person", - "type": "ListRemoveQuestion", - "cancel_text": "Don’t need to remove this person?", - "question": { - "id": "remove-question", - "type": "General", - "title": "Are you sure you want to remove this person?", - "warning": "All of the information about this person will be deleted", - "answers": [ - { - "id": "remove-confirmation", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RemoveListItemAndAnswers" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - } - }, - "summary": { - "title": "Household members", - "item_title": { - "text": "{person_name}", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "arguments": { - "delimiter": " ", - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name" - }, - { - "source": "answers", - "identifier": "last-name" - } - ] - }, - "transform": "concatenate_list" - } - ] - } - ] - } - } - } - ] - } - ] - }, - { - "id": "household-personal-details-section", - "title": "Personal Details", - "summary": { "show_on_completion": true }, - "repeat": { - "for_list": "people", - "title": { - "text": "{person_name}", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name" - }, - { - "source": "answers", - "identifier": "last-name" - } - ], - "delimiter": " " - } - } - ] - } - ] - } - }, - "groups": [ - { - "id": "personal-details-group", - "title": "Personal Details", - "blocks": [ - { - "id": "repeating-sex", - "question": { - "answers": [ - { - "id": "repeating-sex-answer", - "mandatory": false, - "options": [ - { - "label": "Male", - "value": "Male" - }, - { - "label": "Female", - "value": "Female" - } - ], - "type": "Radio" - } - ], - "id": "repeating-sex-question", - "title": "What sex is this person?", - "type": "General" - }, - "type": "Question" - }, - { - "type": "Question", - "id": "repeating-age", - "question": { - "answers": [ - { - "label": "Age in years", - "id": "repeating-age-answer", - "mandatory": false, - "type": "Number" - } - ], - "id": "repeating-age-question", - "title": "What age is this person?", - "type": "General" - }, - "skip_conditions": { - "when": { - "and": [ - { - "==": [ - { - "source": "answers", - "identifier": "skip-age-answer" - }, - "Yes" - ] - }, - { - "!=": [ - { - "source": "answers", - "identifier": "skip-confirmation-answer" - }, - "No" - ] - } - ] - } - } - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_date_equals.json b/schemas/test/en/test_new_routing_date_equals.json deleted file mode 100644 index 111437b93c..0000000000 --- a/schemas/test/en/test_new_routing_date_equals.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Date Equals", - "theme": "default", - "description": "A test survey for routing based on equal dates", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "comparison-date-block", - "question": { - "answers": [ - { - "id": "comparison-date-answer", - "mandatory": true, - "type": "Date" - } - ], - "id": "comparison-date-question", - "title": "Title", - "type": "General", - "guidance": { - "contents": [ - { - "title": "If you enter 31/03/2018 the following dates will be valid", - "list": [ - "Yesterday 30/03/2018", - "Today 31/03/2018", - "Tomorrow 01/04/2018", - "Last Month 28/02/2018 (28th as no 31st February)", - "Next Month 30/04/2018 (30th as no 31st April)", - "Last Year 31/03/2017", - "Next Year 31/03/2019" - ] - } - ] - } - } - }, - { - "type": "Question", - "id": "date-question", - "question": { - "answers": [ - { - "id": "single-date-answer", - "label": "Today", - "mandatory": true, - "type": "Date" - } - ], - "id": "date-questions", - "title": { - "text": "Enter {date} or offset by one day, month or year in either direction", - "placeholders": [ - { - "placeholder": "date", - "transforms": [ - { - "transform": "format_date", - "arguments": { - "date_to_format": { - "source": "answers", - "identifier": "comparison-date-answer" - }, - "date_format": "d MMMM yyyy" - } - } - ] - } - ] - }, - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "or": [ - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "days": -1 }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "days": 1 }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "months": -1 }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "months": 1 }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "years": -1 }] - } - ] - }, - { - "==": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "answers", "identifier": "comparison-date-answer" }, { "years": 1 }] - } - ] - } - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect Date", - "contents": [ - { - "description": "You entered an incorrect date" - } - ] - }, - "routing_rules": [ - { - "goto": { - "section": "End" - } - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct Date", - "contents": [ - { - "description": "You entered a correct date." - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_date_greater_than.json b/schemas/test/en/test_new_routing_date_greater_than.json deleted file mode 100644 index cecc09776f..0000000000 --- a/schemas/test/en/test_new_routing_date_greater_than.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Date Greater Than", - "theme": "default", - "description": "A test survey for routing based on a date greater than", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - }, - { - "name": "return_by", - "type": "date" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "date-question", - "question": { - "answers": [ - { - "id": "single-date-answer", - "mandatory": true, - "type": "Date" - } - ], - "id": "date-questions", - "title": { - "text": "Enter a date greater than Return date: {date}", - "placeholders": [ - { - "placeholder": "date", - "transforms": [ - { - "transform": "format_date", - "arguments": { - "date_to_format": { - "source": "metadata", - "identifier": "return_by" - }, - "date_format": "d MMMM yyyy" - } - } - ] - } - ] - }, - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - ">": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": [{ "source": "metadata", "identifier": "return_by" }] - } - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect answer", - "contents": [ - { - "description": { - "text": "You entered a return date earlier than {date}", - "placeholders": [ - { - "placeholder": "date", - "transforms": [ - { - "transform": "format_date", - "arguments": { - "date_to_format": { - "source": "metadata", - "identifier": "return_by" - }, - "date_format": "d MMMM yyyy" - } - } - ] - } - ] - } - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct answer", - "contents": [ - { - "description": { - "text": "You entered a return date later than {date}", - "placeholders": [ - { - "placeholder": "date", - "transforms": [ - { - "transform": "format_date", - "arguments": { - "date_to_format": { - "source": "metadata", - "identifier": "return_by" - }, - "date_format": "d MMMM yyyy" - } - } - ] - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_date_less_than.json b/schemas/test/en/test_new_routing_date_less_than.json deleted file mode 100644 index 30925081ff..0000000000 --- a/schemas/test/en/test_new_routing_date_less_than.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Date Less Than", - "theme": "default", - "description": "A test survey for routing based on a Date less than", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "date-question", - "question": { - "answers": [ - { - "id": "single-date-answer", - "label": "Today", - "mandatory": true, - "type": "Date" - } - ], - "id": "date-questions", - "title": "Enter a date less than Today", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "<": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": ["now"] - } - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect answer", - "contents": [ - { - "description": "You entered a date later than yesterday." - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct answer", - "contents": [ - { - "description": "You entered a date older than Today." - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_date_not_equals.json b/schemas/test/en/test_new_routing_date_not_equals.json deleted file mode 100644 index 0cbb7d5ee8..0000000000 --- a/schemas/test/en/test_new_routing_date_not_equals.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Date Not Equals", - "theme": "default", - "description": "A test survey for routing based on a date not equals", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "date-question", - "question": { - "answers": [ - { - "id": "single-date-answer", - "label": "Today", - "mandatory": true, - "type": "MonthYearDate" - } - ], - "id": "date-questions", - "title": "Enter a date other than February 2018", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "!=": [ - { - "date": [{ "source": "answers", "identifier": "single-date-answer" }] - }, - { - "date": ["2018-02"] - } - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect Date", - "contents": [ - { - "description": "You entered 28 February 2018." - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct Date", - "contents": [ - { - "description": "You entered a date other than 28 February 2018." - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_equals.json b/schemas/test/en/test_new_routing_number_equals.json deleted file mode 100644 index 6338404ceb..0000000000 --- a/schemas/test/en/test_new_routing_number_equals.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Equals", - "theme": "default", - "description": "A test survey for routing based on a number equals", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "Enter 123" - } - ], - "id": "question", - "title": "Enter the number 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "==": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "You did not enter 123", - "contents": [ - { - "description": { - "text": "You were asked to enter 123 but you actually entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct", - "contents": [ - { - "description": { - "text": "You were asked to enter 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_greater_than.json b/schemas/test/en/test_new_routing_number_greater_than.json deleted file mode 100644 index da375ab8f8..0000000000 --- a/schemas/test/en/test_new_routing_number_greater_than.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Greater Than", - "theme": "default", - "description": "A test survey for routing based on a number greater than", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "Enter a number greater than 123" - } - ], - "id": "question", - "title": "Enter a number greater than 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - ">": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "You did not enter a number greater than 123", - "contents": [ - { - "description": { - "text": "You were asked to enter a number greater than 123 but you actually entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct", - "contents": [ - { - "description": { - "text": "You were asked to enter a number greater than 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_greater_than_or_equal.json b/schemas/test/en/test_new_routing_number_greater_than_or_equal.json deleted file mode 100644 index 1380ec8b39..0000000000 --- a/schemas/test/en/test_new_routing_number_greater_than_or_equal.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Greater Than Or Equal To", - "theme": "default", - "description": "A test survey for routing based on a number greater than or equal to", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "123 or greater" - } - ], - "id": "question", - "title": "Enter the number greater than or equal to 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - ">=": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect answer", - "contents": [ - { - "description": { - "text": "You were asked to enter a number greater than or equal to 123 but you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "goto": { - "section": "End" - } - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct answer", - "contents": [ - { - "description": { - "text": "You were asked to enter a number greater than or equal to 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_less_than.json b/schemas/test/en/test_new_routing_number_less_than.json deleted file mode 100644 index 5036a036b9..0000000000 --- a/schemas/test/en/test_new_routing_number_less_than.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Less Than", - "theme": "default", - "description": "A test survey for routing based on a number less than", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "Enter a number less than 123" - } - ], - "id": "question", - "title": "Enter a number less than 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "<": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "You did not enter a number less than 123", - "contents": [ - { - "description": { - "text": "You were asked to enter a number less than 123 but you actually entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct", - "contents": [ - { - "description": { - "text": "You were asked to enter a number less than 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_less_than_or_equal.json b/schemas/test/en/test_new_routing_number_less_than_or_equal.json deleted file mode 100644 index ccdfca3517..0000000000 --- a/schemas/test/en/test_new_routing_number_less_than_or_equal.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Less Than Or Equal To", - "theme": "default", - "description": "A test survey for routing based on a number less than or equal to", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "Number" - } - ], - "id": "question", - "title": "Enter the number less than or equal to 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "<=": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect answer", - "contents": [ - { - "description": { - "text": "You were asked to enter a number less than or equal to 123 but you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "goto": { - "section": "End" - } - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct answer", - "contents": [ - { - "description": { - "text": "You were asked to enter a number less than or equal to 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_routing_number_not_equals.json b/schemas/test/en/test_new_routing_number_not_equals.json deleted file mode 100644 index c400bcf04a..0000000000 --- a/schemas/test/en/test_new_routing_number_not_equals.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "001", - "title": "Test Routing Number Not Equals", - "theme": "default", - "description": "A test survey for routing based on a number not equals", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "number-question", - "question": { - "answers": [ - { - "id": "answer", - "mandatory": true, - "type": "Number", - "label": "Not 123" - } - ], - "id": "question", - "title": "Enter the number that isn’t 123", - "type": "General" - }, - "routing_rules": [ - { - "block": "correct-answer", - "when": { - "!=": [ - { - "source": "answers", - "identifier": "answer" - }, - 123 - ] - } - }, - { - "block": "incorrect-answer" - } - ] - }, - { - "type": "Interstitial", - "id": "incorrect-answer", - "content": { - "title": "Incorrect answer", - "contents": [ - { - "description": { - "text": "You were asked not to enter 123 but you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - }, - "routing_rules": [ - { - "section": "End" - } - ] - }, - { - "type": "Interstitial", - "id": "correct-answer", - "content": { - "title": "Correct answer", - "contents": [ - { - "description": { - "text": "You were asked not to enter 123 and you entered {answer}.", - "placeholders": [ - { - "placeholder": "answer", - "value": { - "source": "answers", - "identifier": "answer" - } - } - ] - } - } - ] - } - } - ], - "id": "group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_section_enabled_checkbox.json b/schemas/test/en/test_new_section_enabled_checkbox.json deleted file mode 100644 index 7c60b826c5..0000000000 --- a/schemas/test/en/test_new_section_enabled_checkbox.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test Section Enabled", - "theme": "default", - "description": "A questionnaire to demo section enabled key usage with checkbox options", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "section-1", - "title": "Section 1", - "groups": [ - { - "blocks": [ - { - "id": "section-1-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-1-answer", - "label": "label 1", - "mandatory": false, - "options": [ - { - "label": "Section 2", - "value": "Section 2" - }, - { - "label": "Section 3", - "value": "Section 3" - } - ], - "type": "Checkbox" - }, - { - "id": "section-1-answer-exclusive", - "mandatory": false, - "options": [ - { - "label": "Neither", - "value": "Neither" - } - ], - "type": "Checkbox" - } - ], - "mandatory": false, - "description": ["This is section 1."], - "id": "section-1-question", - "title": "Which sections do you want to enable?", - "type": "MutuallyExclusive" - } - } - ], - "id": "section-1-group", - "title": "Section 1" - } - ] - }, - { - "id": "section-2", - "title": "Section 2", - "enabled": { - "when": { - "in": [ - "Section 2", - { - "source": "answers", - "identifier": "section-1-answer" - } - ] - } - }, - "groups": [ - { - "blocks": [ - { - "id": "section-2-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-2-answer", - "label": "label 2", - "mandatory": false, - "type": "Number" - } - ], - "description": ["This is section 2."], - "id": "section-2-question", - "title": "Which section is this?", - "type": "General" - } - } - ], - "id": "section-2-group", - "title": "Section 2" - } - ] - }, - { - "id": "section-3", - "title": "Section 3", - "enabled": { - "when": { - "in": [ - "Section 3", - { - "source": "answers", - "identifier": "section-1-answer" - } - ] - } - }, - "groups": [ - { - "blocks": [ - { - "id": "section-3-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-3-answer", - "label": "label 3", - "mandatory": false, - "type": "Number" - } - ], - "description": ["This is section 3."], - "id": "section-3-question", - "title": "Which section is this?", - "type": "General" - } - } - ], - "id": "section-3-group", - "title": "Section 3" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_section_enabled_hub.json b/schemas/test/en/test_new_section_enabled_hub.json deleted file mode 100644 index 5f6e82f73b..0000000000 --- a/schemas/test/en/test_new_section_enabled_hub.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test Section Enabled", - "theme": "default", - "description": "A questionnaire to demo section enabled key usage with hub enabled", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": { "required_completed_sections": ["section-1"] } - }, - "sections": [ - { - "id": "section-1", - "title": "Section 1", - "groups": [ - { - "blocks": [ - { - "id": "section-1-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-1-answer", - "mandatory": false, - "options": [ - { - "label": "Section 2", - "value": "Section 2" - }, - { - "label": "Section 3", - "value": "Section 3" - } - ], - "type": "Checkbox" - }, - { - "id": "section-1-answer-exclusive", - "mandatory": false, - "options": [ - { - "label": "Neither", - "value": "Neither" - } - ], - "type": "Checkbox" - } - ], - "mandatory": false, - "description": ["This is section 1."], - "id": "section-1-question", - "title": "Which sections do you want to enable?", - "type": "MutuallyExclusive" - } - } - ], - "id": "section-1-group", - "title": "Section 1" - } - ] - }, - { - "id": "section-2", - "title": "Section 2", - "enabled": { - "when": { - "in": [ - "Section 2", - { - "source": "answers", - "identifier": "section-1-answer" - } - ] - } - }, - "groups": [ - { - "blocks": [ - { - "id": "section-2-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-2-answer", - "label": "Section 2", - "mandatory": false, - "type": "Number" - } - ], - "description": ["This is section 2."], - "id": "section-2-question", - "title": "Which section is this?", - "type": "General" - } - } - ], - "id": "section-2-group", - "title": "Section 2" - } - ] - }, - { - "id": "section-3", - "title": "Section 3", - "enabled": { - "when": { - "in": [ - "Section 3", - { - "source": "answers", - "identifier": "section-1-answer" - } - ] - } - }, - "groups": [ - { - "blocks": [ - { - "id": "section-3-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-3-answer", - "label": "Section 3", - "mandatory": false, - "type": "Number" - } - ], - "description": ["This is section 3."], - "id": "section-3-question", - "title": "Which section is this?", - "type": "General" - } - } - ], - "id": "section-3-group", - "title": "Section 3" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_section_enabled_radio.json b/schemas/test/en/test_new_section_enabled_radio.json deleted file mode 100644 index 3141ae569f..0000000000 --- a/schemas/test/en/test_new_section_enabled_radio.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test Section Enabled", - "theme": "default", - "description": "A questionnaire to demo section enabled key usage with radio options", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "section-1", - "title": "Section 1", - "groups": [ - { - "blocks": [ - { - "id": "section-1-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-1-answer", - "label": "Section 1", - "mandatory": false, - "options": [ - { - "label": "Yes, enable section 2", - "value": "Yes, enable section 2" - }, - { - "label": "No, disable section 2", - "value": "No, disable section 2" - } - ], - "type": "Radio" - } - ], - "description": ["This is section 1."], - "id": "section-1-question", - "title": "Do you want to enable section 2?", - "type": "General" - } - } - ], - "id": "section-1-group", - "title": "Section 1" - } - ] - }, - { - "id": "section-2", - "title": "Section 2", - "enabled": { - "when": { - "==": [ - "Yes, enable section 2", - { - "source": "answers", - "identifier": "section-1-answer" - } - ] - } - }, - "groups": [ - { - "blocks": [ - { - "id": "section-2-block", - "type": "Question", - "question": { - "answers": [ - { - "id": "section-2-answer", - "label": "Section 2", - "mandatory": false, - "type": "Number" - } - ], - "description": ["This is section 2."], - "id": "section-2-question", - "title": "Which section is this?", - "type": "General" - } - } - ], - "id": "section-2-group", - "title": "Section 2" - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_skip_condition_block.json b/schemas/test/en/test_new_skip_condition_block.json deleted file mode 100644 index 389aa2095e..0000000000 --- a/schemas/test/en/test_new_skip_condition_block.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Skip block", - "theme": "default", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "id": "default-group", - "title": "Group 1", - "blocks": [ - { - "type": "Question", - "id": "do-you-want-to-skip", - "question": { - "id": "do-you-want-to-skip-question", - "title": "Do you want to skip the next question?", - "type": "General", - "description": ["Select “Yes” to skip the next question and go straight to the summary"], - "answers": [ - { - "id": "do-you-want-to-skip-answer", - "label": "Select an answer", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ] - } - }, - { - "type": "Question", - "id": "should-skip", - "question": { - "id": "should-skip-question", - "title": "Why didn’t you skip the block?", - "type": "General", - "answers": [ - { - "id": "should-skip-answer", - "label": "Enter your answer", - "mandatory": true, - "type": "TextArea" - } - ] - }, - "skip_conditions": { - "when": { - "==": [ - { - "source": "answers", - "identifier": "do-you-want-to-skip-answer" - }, - "Yes" - ] - } - } - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_skip_condition_group.json b/schemas/test/en/test_new_skip_condition_group.json deleted file mode 100644 index 1640b7ee88..0000000000 --- a/schemas/test/en/test_new_skip_condition_group.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Skip group", - "theme": "default", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "default-section", - "groups": [ - { - "id": "default-group", - "title": "Group 1", - "blocks": [ - { - "type": "Question", - "id": "do-you-want-to-skip", - "question": { - "id": "do-you-want-to-skip-question", - "title": "Do you want to skip the next question?", - "type": "General", - "description": ["Select “Yes” to skip the next question and go straight to the summary"], - "answers": [ - { - "id": "do-you-want-to-skip-answer", - "label": "Select an answer", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ] - } - } - ] - }, - { - "id": "should-skip-group", - "title": "Group 2 (Skippable)", - "skip_conditions": { - "when": { - "==": [ - { - "source": "answers", - "identifier": "do-you-want-to-skip-answer" - }, - "Yes" - ] - } - }, - "blocks": [ - { - "type": "Question", - "id": "should-skip", - "question": { - "id": "should-skip-question", - "title": "Why didn’t you skip the group?", - "type": "General", - "answers": [ - { - "id": "should-skip-answer", - "label": "Enter your answer", - "mandatory": true, - "type": "TextArea" - } - ] - } - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_variants_content.json b/schemas/test/en/test_new_variants_content.json deleted file mode 100644 index ccec714151..0000000000 --- a/schemas/test/en/test_new_variants_content.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test New Content Variants", - "theme": "default", - "description": "A questionnaire to test new content variants and variant choices", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "section", - "groups": [ - { - "id": "group", - "title": "Variants", - "blocks": [ - { - "type": "Question", - "id": "age-question-block", - "question": { - "id": "age-question", - "type": "General", - "title": "What is your age?", - "answers": [ - { - "id": "age-answer", - "label": "Your age?", - "mandatory": true, - "type": "Number" - } - ] - } - }, - { - "type": "Interstitial", - "id": "age-display-block", - "content_variants": [ - { - "content": { - "title": "You are 16 or older", - "contents": [ - { - "description": "According to your answer" - } - ] - }, - "when": { - ">": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - } - }, - { - "content": { - "title": "You are 16 or younger", - "contents": [ - { - "description": "According to your answer" - } - ] - }, - "when": { - "<=": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - } - } - ] - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_variants_first_item_in_list.json b/schemas/test/en/test_new_variants_first_item_in_list.json deleted file mode 100644 index 0e3f8f366f..0000000000 --- a/schemas/test/en/test_new_variants_first_item_in_list.json +++ /dev/null @@ -1,298 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test New Question Variants Using List", - "theme": "default", - "description": "A questionnaire to test new question variants using the first item in a list", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": {} - }, - "sections": [ - { - "id": "section", - "title": "Household", - "groups": [ - { - "id": "group", - "title": "List", - "blocks": [ - { - "id": "list-collector", - "type": "ListCollector", - "for_list": "people", - "question": { - "id": "confirmation-question", - "type": "General", - "title": "Does anyone else live here?", - "answers": [ - { - "id": "anyone-else", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RedirectToListAddBlock" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "add_block": { - "id": "add-person", - "type": "ListAddQuestion", - "question": { - "id": "add-question", - "type": "General", - "title": "What is the name of the person?", - "answers": [ - { - "id": "first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "edit_block": { - "id": "edit-person", - "type": "ListEditQuestion", - "question": { - "id": "edit-question", - "type": "General", - "title": "What is the name of the person?", - "answers": [ - { - "id": "first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "remove_block": { - "id": "remove-person", - "type": "ListRemoveQuestion", - "question": { - "id": "remove-question", - "type": "General", - "title": "Are you sure you want to remove this person?", - "answers": [ - { - "id": "remove-confirmation", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RemoveListItemAndAnswers" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - } - }, - "summary": { - "title": "Household members", - "item_title": { - "text": "{person_name}", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "arguments": { - "delimiter": " ", - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name" - }, - { - "source": "answers", - "identifier": "last-name" - } - ] - }, - "transform": "concatenate_list" - } - ] - } - ] - } - } - } - ] - } - ] - }, - { - "id": "personal-details-section", - "title": "Personal Details", - "summary": { "show_on_completion": true }, - "repeat": { - "for_list": "people", - "title": { - "text": "{person_name}", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name" - }, - { - "source": "answers", - "identifier": "last-name" - } - ], - "delimiter": " " - } - } - ] - } - ] - } - }, - "groups": [ - { - "id": "personal-details-group", - "title": "Personal Details", - "blocks": [ - { - "id": "list-status", - "question_variants": [ - { - "question": { - "answers": [ - { - "id": "list-status-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ], - "id": "list-status-question", - "title": "You are the first person in the list", - "type": "General" - }, - "when": { - "==": [ - { - "source": "list", - "identifier": "people", - "selector": "first" - }, - { - "source": "location", - "identifier": "list_item_id" - } - ] - } - }, - { - "question": { - "answers": [ - { - "id": "list-status-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ], - "type": "Radio" - } - ], - "id": "list-status-question", - "title": "You are not the first person in the list", - "type": "General" - }, - "when": { - "!=": [ - { - "source": "list", - "identifier": "people", - "selector": "first" - }, - { - "source": "location", - "identifier": "list_item_id" - } - ] - } - } - ], - "type": "Question" - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_new_variants_question.json b/schemas/test/en/test_new_variants_question.json deleted file mode 100644 index bb3cb64a11..0000000000 --- a/schemas/test/en/test_new_variants_question.json +++ /dev/null @@ -1,622 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Test New Question Variants", - "theme": "default", - "description": "A questionnaire to test new question variants", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "variant-proxy-section", - "groups": [ - { - "id": "variant-proxy-group", - "title": "Variants for proxy", - "blocks": [ - { - "type": "Question", - "id": "name-block", - "question": { - "id": "name-question", - "title": "Who is this questionnaire about?", - "type": "General", - "answers": [ - { - "id": "first-name-answer", - "label": "First Name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "last-name-answer", - "label": "Last Name", - "mandatory": false, - "type": "TextField" - } - ] - } - }, - { - "type": "Question", - "id": "proxy-block", - "question": { - "id": "proxy-question", - "title": { - "text": "Are you {person_name}?", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name-answer" - }, - { - "source": "answers", - "identifier": "last-name-answer" - } - ], - "delimiter": " " - } - } - ] - } - ] - }, - "type": "General", - "answers": [ - { - "id": "proxy-answer", - "mandatory": true, - "options": [ - { - "label": "Yes, I am", - "value": "Yes, I am" - }, - { - "label": "No, I am answering on their behalf", - "value": "No, I am answering on their behalf" - } - ], - "type": "Radio" - } - ] - } - } - ] - } - ] - }, - { - "id": "basic-question-variant-section", - "summary": { "show_on_completion": true }, - "title": "Question variant section", - "groups": [ - { - "id": "basic-question-variant-group", - "title": "Variants", - "blocks": [ - { - "type": "Question", - "id": "age-block", - "question_variants": [ - { - "question": { - "id": "age-question", - "type": "General", - "title": "What is your age?", - "answers": [ - { - "id": "age-answer", - "mandatory": false, - "type": "Number", - "label": "Age" - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "Yes, I am" - ] - } - }, - { - "question": { - "id": "age-question", - "type": "General", - "title": { - "text": "What age is {person_name}?", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name-answer" - }, - { - "source": "answers", - "identifier": "last-name-answer" - } - ], - "delimiter": " " - } - } - ] - } - ] - }, - "answers": [ - { - "id": "age-answer", - "mandatory": true, - "type": "Number", - "label": "Age" - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "No, I am answering on their behalf" - ] - } - } - ] - }, - { - "type": "ConfirmationQuestion", - "id": "age-confirmation-block", - "question_variants": [ - { - "question": { - "id": "age-confirmation-question", - "type": "General", - "title": "You are over 16?", - "answers": [ - { - "id": "age-confirm-answer", - "type": "Radio", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "when": { - "and": [ - { - ">=": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - }, - { - "!=": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "No, I am answering on their behalf" - ] - } - ] - } - }, - { - "question": { - "id": "age-confirmation-question", - "type": "General", - "title": "You are under 16?", - "answers": [ - { - "id": "age-confirm-answer", - "type": "Radio", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "when": { - "and": [ - { - "<=": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - }, - { - "!=": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "No, I am answering on their behalf" - ] - } - ] - } - }, - { - "question": { - "id": "age-confirmation-question", - "type": "General", - "title": { - "text": "{person_name} is over 16?", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name-answer" - }, - { - "source": "answers", - "identifier": "last-name-answer" - } - ], - "delimiter": " " - } - } - ] - } - ] - }, - "answers": [ - { - "id": "age-confirm-answer", - "type": "Radio", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "when": { - "and": [ - { - ">=": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - }, - { - "==": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "No, I am answering on their behalf" - ] - } - ] - } - }, - { - "question": { - "id": "age-confirmation-question", - "type": "General", - "title": { - "text": "{person_name} is under 16?", - "placeholders": [ - { - "placeholder": "person_name", - "transforms": [ - { - "transform": "concatenate_list", - "arguments": { - "list_to_concatenate": [ - { - "source": "answers", - "identifier": "first-name-answer" - }, - { - "source": "answers", - "identifier": "last-name-answer" - } - ], - "delimiter": " " - } - } - ] - } - ] - }, - "answers": [ - { - "id": "age-confirm-answer", - "type": "Radio", - "mandatory": true, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "when": { - "and": [ - { - "<=": [ - { - "source": "answers", - "identifier": "age-answer" - }, - 16 - ] - }, - { - "==": [ - { - "source": "answers", - "identifier": "proxy-answer" - }, - "No, I am answering on their behalf" - ] - } - ] - } - } - ], - "routing_rules": [ - { - "block": "age-block", - "when": { - "==": [ - { - "source": "answers", - "identifier": "age-confirm-answer" - }, - "No" - ] - } - }, - { - "section": "End" - } - ] - } - ] - } - ] - }, - { - "id": "currency-section", - "summary": { "show_on_completion": true }, - "groups": [ - { - "id": "currency-group", - "title": "Section Summary With Variants", - "blocks": [ - { - "type": "Question", - "id": "currency-block", - "question": { - "id": "currency-question", - "type": "General", - "title": "What currency would you like", - "answers": [ - { - "id": "currency-answer", - "type": "Radio", - "mandatory": true, - "options": [ - { - "label": "US Dollars", - "value": "US Dollars" - }, - { - "label": "Sterling", - "value": "Sterling" - } - ] - } - ] - } - }, - { - "type": "Question", - "id": "first-number-block", - "question_variants": [ - { - "question": { - "id": "first-number-question", - "title": "First Number Question Title", - "type": "General", - "answers": [ - { - "id": "first-number-answer", - "label": "First answer in GBP", - "mandatory": true, - "type": "Currency", - "currency": "GBP", - "decimal_places": 2 - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "currency-answer" - }, - "Sterling" - ] - } - }, - { - "question": { - "id": "first-number-question", - "title": "First Number Question Title", - "type": "General", - "answers": [ - { - "id": "first-number-answer", - "label": "First answer in USD", - "mandatory": true, - "type": "Currency", - "currency": "USD", - "decimal_places": 2 - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "currency-answer" - }, - "US Dollars" - ] - } - } - ] - }, - { - "type": "Question", - "id": "second-number-block", - "question_variants": [ - { - "question": { - "id": "second-number-question", - "title": "Second Number Question Title", - "type": "General", - "answers": [ - { - "id": "second-number-answer", - "label": "Second answer in GBP", - "mandatory": true, - "type": "Currency", - "currency": "GBP", - "decimal_places": 2 - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "currency-answer" - }, - "Sterling" - ] - } - }, - { - "question": { - "id": "second-number-question", - "title": "Second Number Question Title", - "type": "General", - "answers": [ - { - "id": "second-number-answer", - "label": "Second answer in USD", - "mandatory": true, - "type": "Currency", - "currency": "USD", - "decimal_places": 2 - } - ] - }, - "when": { - "==": [ - { - "source": "answers", - "identifier": "currency-answer" - }, - "US Dollars" - ] - } - } - ] - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_placeholder_based_on_first_item_in_list.json b/schemas/test/en/test_placeholder_based_on_first_item_in_list.json index b6a6d08b8f..5b6a1543b0 100644 --- a/schemas/test/en/test_placeholder_based_on_first_item_in_list.json +++ b/schemas/test/en/test_placeholder_based_on_first_item_in_list.json @@ -175,7 +175,9 @@ { "id": "personal-details-section", "title": "Personal Details", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "repeat": { "for_list": "people", "title": { @@ -221,17 +223,19 @@ ], "title": "List Status" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "equals", - "comparison": { + "when": { + "==": [ + { + "identifier": "people", + "source": "list", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } }, { "content": { @@ -242,17 +246,19 @@ ], "title": "List Status" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "not equals", - "comparison": { + "when": { + "!=": [ + { + "identifier": "people", + "source": "list", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } } ], "id": "list-status", @@ -302,17 +308,19 @@ "title": "What is your second favourite drink?", "type": "General" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "equals", - "comparison": { + "when": { + "==": [ + { + "identifier": "people", + "source": "list", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } }, { "question": { @@ -353,17 +361,19 @@ "title": "What is your second favourite drink?", "type": "General" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "not equals", - "comparison": { + "when": { + "!=": [ + { + "identifier": "people", + "source": "list", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } } ], "type": "Question" diff --git a/schemas/test/en/test_placeholder_difference_in_years.json b/schemas/test/en/test_placeholder_difference_in_years.json index f4a12e4904..ab633d7326 100644 --- a/schemas/test/en/test_placeholder_difference_in_years.json +++ b/schemas/test/en/test_placeholder_difference_in_years.json @@ -103,21 +103,19 @@ }, "routing_rules": [ { - "goto": { - "block": "age-block", - "when": [ + "block": "age-block", + "when": { + "==": [ { - "id": "date-test-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "date-test-answer" + }, + "No" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_placeholder_difference_in_years_month_year.json b/schemas/test/en/test_placeholder_difference_in_years_month_year.json index 526033720d..e25029ffdf 100644 --- a/schemas/test/en/test_placeholder_difference_in_years_month_year.json +++ b/schemas/test/en/test_placeholder_difference_in_years_month_year.json @@ -103,21 +103,19 @@ }, "routing_rules": [ { - "goto": { - "block": "age-block", - "when": [ + "block": "age-block", + "when": { + "==": [ { - "id": "date-test-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "date-test-answer" + }, + "No" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_placeholder_difference_in_years_month_year_range.json b/schemas/test/en/test_placeholder_difference_in_years_month_year_range.json index 7b84830fbf..61071e78ee 100644 --- a/schemas/test/en/test_placeholder_difference_in_years_month_year_range.json +++ b/schemas/test/en/test_placeholder_difference_in_years_month_year_range.json @@ -110,21 +110,19 @@ }, "routing_rules": [ { - "goto": { - "block": "date-block", - "when": [ + "block": "date-block", + "when": { + "==": [ { - "id": "date-test-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "date-test-answer" + }, + "No" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_placeholder_difference_in_years_range.json b/schemas/test/en/test_placeholder_difference_in_years_range.json index 42ac8db467..35f2356af4 100644 --- a/schemas/test/en/test_placeholder_difference_in_years_range.json +++ b/schemas/test/en/test_placeholder_difference_in_years_range.json @@ -110,21 +110,19 @@ }, "routing_rules": [ { - "goto": { - "block": "date-block", - "when": [ + "block": "date-block", + "when": { + "==": [ { - "id": "date-test-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "date-test-answer" + }, + "No" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_placeholder_full.json b/schemas/test/en/test_placeholder_full.json index dd4299d0ae..8218e923af 100644 --- a/schemas/test/en/test_placeholder_full.json +++ b/schemas/test/en/test_placeholder_full.json @@ -250,21 +250,19 @@ }, "routing_rules": [ { - "goto": { - "group": "dob-input-group", - "when": [ + "group": "dob-input-group", + "when": { + "==": [ { - "id": "confirm-date-of-birth-answer-proxy", - "condition": "equals", - "value": "No, I need to change their date of birth" - } + "source": "answers", + "identifier": "confirm-date-of-birth-answer-proxy" + }, + "No, I need to change their date of birth" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_placeholder_playback_list.json b/schemas/test/en/test_placeholder_playback_list.json index a3ecaa4f29..6844986e74 100644 --- a/schemas/test/en/test_placeholder_playback_list.json +++ b/schemas/test/en/test_placeholder_playback_list.json @@ -139,21 +139,19 @@ }, "routing_rules": [ { - "goto": { - "when": [ + "when": { + "==": [ { - "value": "No I need to change this", - "id": "confirm-answers", - "condition": "equals" - } - ], - "block": "mandatory-checkbox" - } + "source": "answers", + "identifier": "confirm-answers" + }, + "No I need to change this" + ] + }, + "block": "mandatory-checkbox" }, { - "goto": { - "section": "End" - } + "section": "End" } ] } diff --git a/schemas/test/en/test_relationships.json b/schemas/test/en/test_relationships.json index 9ceaf00f22..f2019bff4d 100644 --- a/schemas/test/en/test_relationships.json +++ b/schemas/test/en/test_relationships.json @@ -889,17 +889,18 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "source": "list", + "identifier": "people", + "selector": "count" + }, + 2 ] } - ] + } }, { "id": "relationship-interstitial", @@ -912,17 +913,18 @@ ] }, "type": "Interstitial", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "source": "list", + "identifier": "people", + "selector": "count" + }, + 2 ] } - ] + } } ] } diff --git a/schemas/test/en/test_relationships_primary.json b/schemas/test/en/test_relationships_primary.json index 912e0873d3..73ca1d5ed9 100644 --- a/schemas/test/en/test_relationships_primary.json +++ b/schemas/test/en/test_relationships_primary.json @@ -614,17 +614,19 @@ } ] }, - "when": [ - { - "list": "people", - "id_selector": "primary_person", - "condition": "equals", - "comparison": { + "when": { + "==": [ + { + "source": "list", + "identifier": "people", + "selector": "primary_person" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } }, { "question": { @@ -1326,30 +1328,33 @@ } ] }, - "when": [ - { - "list": "people", - "id_selector": "primary_person", - "condition": "not equals", - "comparison": { + "when": { + "!=": [ + { + "identifier": "people", + "source": "list", + "selector": "primary_person" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } } ], - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "identifier": "people", + "source": "list", + "selector": "count" + }, + 2 ] } - ] + } } ] } diff --git a/schemas/test/en/test_relationships_unrelated.json b/schemas/test/en/test_relationships_unrelated.json index 9855319290..e535e4b349 100644 --- a/schemas/test/en/test_relationships_unrelated.json +++ b/schemas/test/en/test_relationships_unrelated.json @@ -605,17 +605,18 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "identifier": "people", + "source": "list", + "selector": "count" + }, + 2 ] } - ], + }, "unrelated_block": { "type": "UnrelatedQuestion", "id": "related-to-anyone-else", @@ -689,17 +690,19 @@ } ] }, - "when": [ - { - "comparison": { - "id": "list_item_id", + "when": { + "==": [ + { + "identifier": "list_item_id", "source": "location" }, - "condition": "equals", - "id_selector": "first", - "list": "people" - } - ] + { + "source": "list", + "identifier": "people", + "selector": "first" + } + ] + } }, { "question": { @@ -803,17 +806,19 @@ } ] }, - "when": [ - { - "comparison": { - "id": "list_item_id", + "when": { + "!=": [ + { + "identifier": "list_item_id", "source": "location" }, - "condition": "not equals", - "id_selector": "first", - "list": "people" - } - ] + { + "source": "list", + "identifier": "people", + "selector": "first" + } + ] + } } ] } @@ -829,17 +834,18 @@ ] }, "type": "Interstitial", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "<": [ { - "list": "people", - "condition": "less than", - "value": 2 - } + "source": "list", + "identifier": "people", + "selector": "count" + }, + 2 ] } - ] + } } ] } diff --git a/schemas/test/en/test_repeating_section_summaries.json b/schemas/test/en/test_repeating_section_summaries.json index 9176af0cdf..c8ec5c3ca7 100644 --- a/schemas/test/en/test_repeating_section_summaries.json +++ b/schemas/test/en/test_repeating_section_summaries.json @@ -228,7 +228,9 @@ { "id": "personal-details-section", "title": "Personal Details", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "repeat": { "for_list": "people", "title": { @@ -322,13 +324,15 @@ "title": "What is your date of birth?", "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "No, I’m answering for myself" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] + } }, { "question": { @@ -386,13 +390,15 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] + } } ], "type": "Question" diff --git a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json index c8fb6343f0..6fd40a7e8b 100644 --- a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json +++ b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json @@ -635,13 +635,15 @@ "title": "What is your date of birth?", "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "No, I’m answering for myself" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] + } }, { "question": { @@ -705,13 +707,15 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] + } } ], "type": "Question" @@ -784,13 +788,15 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "No, I’m answering for myself" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] + } }, { "question": { @@ -899,44 +905,44 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] + } } ], "routing_rules": [ { - "goto": { - "block": "date-of-birth", - "when": [ + "block": "date-of-birth", + "when": { + "==": [ { - "condition": "equals", - "id": "confirm-date-of-birth-answer", - "value": "No, I need to change my date of birth" - } + "source": "answers", + "identifier": "confirm-date-of-birth-answer" + }, + "No, I need to change my date of birth" ] } }, { - "goto": { - "block": "date-of-birth", - "when": [ + "block": "date-of-birth", + "when": { + "==": [ { - "condition": "equals", - "id": "confirm-date-of-birth-answer", - "value": "No, I need to change their date of birth" - } + "source": "answers", + "identifier": "confirm-date-of-birth-answer" + }, + "No, I need to change their date of birth" ] } }, { - "goto": { - "block": "sex" - } + "block": "sex" } ], "type": "ConfirmationQuestion" @@ -974,23 +980,34 @@ "title": "What is your sex?", "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "No, I’m answering for myself" - }, - { - "condition": "less than or equal to", - "date_comparison": { - "offset_by": { - "years": -16 - }, - "value": "now" + "when": { + "and": [ + { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] }, - "id": "date-of-birth-answer" - } - ] + { + "<=": [ + { + "date": ["now", { "years": -16 }] + }, + { + "date": [ + { + "source": "answers", + "identifier": "date-of-birth-answer" + } + ] + } + ] + } + ] + } }, { "question": { @@ -1055,23 +1072,29 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "Yes" - }, - { - "condition": "less than or equal to", - "date_comparison": { - "offset_by": { - "years": -16 - }, - "value": "now" + "when": { + "and": [ + { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] }, - "id": "date-of-birth-answer" - } - ] + { + "<=": [ + { + "date": ["now", { "years": -16 }] + }, + { + "date": [{ "source": "answers", "identifier": "date-of-birth-answer" }] + } + ] + } + ] + } }, { "question": { @@ -1096,13 +1119,15 @@ "title": "What is your sex?", "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "No, I’m answering for myself" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] + } }, { "question": { @@ -1160,13 +1185,15 @@ }, "type": "General" }, - "when": [ - { - "condition": "equals", - "id": "proxy-answer", - "value": "Yes" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] + } } ], "type": "Question" diff --git a/schemas/test/en/test_new_routing_and.json b/schemas/test/en/test_routing_and.json similarity index 100% rename from schemas/test/en/test_new_routing_and.json rename to schemas/test/en/test_routing_and.json diff --git a/schemas/test/en/test_routing_and_skipping_section_dependencies.json b/schemas/test/en/test_routing_and_skipping_section_dependencies.json index 9e6331159d..6bdf9d4611 100644 --- a/schemas/test/en/test_routing_and_skipping_section_dependencies.json +++ b/schemas/test/en/test_routing_and_skipping_section_dependencies.json @@ -58,6 +58,71 @@ "title": "Do you want to skip all age questions in all sections", "type": "General" } + }, + { + "type": "Question", + "id": "skip-household-section", + "question": { + "id": "skip-household-section-question", + "title": "Do you want to skip the question about skipping the household summary section?", + "type": "General", + "answers": [ + { + "id": "skip-household-section-answer", + "label": "It will remove the enable section question from the routing path", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ] + } + }, + { + "type": "Question", + "id": "enable-section", + "question": { + "id": "enable-section-question", + "title": "Do you want to enable the household summary section?", + "type": "General", + "answers": [ + { + "id": "enable-section-answer", + "label": "Depending on the answer it will enable or disable the household summary section", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ] + }, + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-household-section-answer", + "source": "answers" + }, + "Yes" + ] + } + } } ], "id": "skip-age-group" @@ -98,21 +163,19 @@ }, "routing_rules": [ { - "goto": { - "block": "skip-confirmation", - "when": [ + "block": "skip-confirmation", + "when": { + "==": [ + "Yes", { - "id": "skip-age-answer", - "condition": "equals", - "value": "Yes" + "source": "answers", + "identifier": "skip-age-answer" } ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] }, @@ -187,22 +250,30 @@ "title": "What is your age?", "type": "General" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "and": [ { - "id": "skip-age-answer", - "condition": "equals", - "value": "Yes" + "==": [ + { + "source": "answers", + "identifier": "skip-age-answer" + }, + "Yes" + ] }, { - "id": "skip-confirmation-answer", - "condition": "not equals", - "value": "No" + "!=": [ + { + "source": "answers", + "identifier": "skip-confirmation-answer" + }, + "No" + ] } ] } - ] + } } ], "id": "primary-person-group", @@ -239,30 +310,45 @@ ], "id": "confirmation-group", "title": "Confirmation Question", - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "or": [ { - "id": "skip-confirmation-answer", - "condition": "equals", - "value": "Yes" - } - ] - }, - { - "when": [ + "==": [ + { + "source": "answers", + "identifier": "skip-confirmation-answer" + }, + "Yes" + ] + }, { - "id": "skip-confirmation-answer", - "condition": "equals", - "value": "No" + "==": [ + { + "source": "answers", + "identifier": "skip-confirmation-answer" + }, + "No" + ] } ] } - ] + } } ] }, { + "enabled": { + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "enable-section-answer" + } + ] + } + }, "id": "household-section", "title": "Household Summary", "summary": { @@ -467,7 +553,7 @@ "question": { "answers": [ { - "id": "repeating-answer", + "id": "repeating-sex-answer", "mandatory": false, "options": [ { @@ -504,22 +590,30 @@ "title": "What age is this person?", "type": "General" }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "and": [ { - "id": "skip-age-answer", - "condition": "equals", - "value": "Yes" + "==": [ + { + "source": "answers", + "identifier": "skip-age-answer" + }, + "Yes" + ] }, { - "id": "skip-confirmation-answer", - "condition": "not equals", - "value": "No" + "!=": [ + { + "source": "answers", + "identifier": "skip-confirmation-answer" + }, + "No" + ] } ] } - ] + } } ] } diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json b/schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json similarity index 100% rename from schemas/test/en/test_new_routing_and_skipping_section_dependencies_calculated_summary.json rename to schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json diff --git a/schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json b/schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json similarity index 100% rename from schemas/test/en/test_new_routing_and_skipping_section_dependencies_new_calculated_summary.json rename to schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json diff --git a/schemas/test/en/test_new_routing_answer_comparison.json b/schemas/test/en/test_routing_answer_comparison.json similarity index 100% rename from schemas/test/en/test_new_routing_answer_comparison.json rename to schemas/test/en/test_routing_answer_comparison.json diff --git a/schemas/test/en/test_new_routing_answered_unanswered.json b/schemas/test/en/test_routing_answered_unanswered.json similarity index 100% rename from schemas/test/en/test_new_routing_answered_unanswered.json rename to schemas/test/en/test_routing_answered_unanswered.json diff --git a/schemas/test/en/test_new_routing_case_insensitive_text_field.json b/schemas/test/en/test_routing_case_insensitive_text_field.json similarity index 100% rename from schemas/test/en/test_new_routing_case_insensitive_text_field.json rename to schemas/test/en/test_routing_case_insensitive_text_field.json diff --git a/schemas/test/en/test_routing_checkbox_contains.json b/schemas/test/en/test_routing_checkbox_contains.json index 54d879b936..cf16b416ac 100644 --- a/schemas/test/en/test_routing_checkbox_contains.json +++ b/schemas/test/en/test_routing_checkbox_contains.json @@ -70,33 +70,31 @@ }, "routing_rules": [ { - "goto": { - "block": "country-interstitial-all", - "when": [ + "block": "country-interstitial-all", + "when": { + "all-in": [ + ["India", "Azerbaijan", "Liechtenstein"], { - "id": "country-checkbox-answer", - "condition": "contains all", - "values": ["India", "Azerbaijan", "Liechtenstein"] + "identifier": "country-checkbox-answer", + "source": "answers" } ] } }, { - "goto": { - "block": "country-interstitial-any", - "when": [ + "block": "country-interstitial-any", + "when": { + "any-in": [ { - "id": "country-checkbox-answer", - "condition": "contains any", - "values": ["India", "Azerbaijan"] - } + "identifier": "country-checkbox-answer", + "source": "answers" + }, + ["India", "Azerbaijan"] ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_new_routing_checkbox_contains_all.json b/schemas/test/en/test_routing_checkbox_contains_all.json similarity index 100% rename from schemas/test/en/test_new_routing_checkbox_contains_all.json rename to schemas/test/en/test_routing_checkbox_contains_all.json diff --git a/schemas/test/en/test_new_routing_checkbox_contains_any.json b/schemas/test/en/test_routing_checkbox_contains_any.json similarity index 100% rename from schemas/test/en/test_new_routing_checkbox_contains_any.json rename to schemas/test/en/test_routing_checkbox_contains_any.json diff --git a/schemas/test/en/test_new_routing_checkbox_contains.json b/schemas/test/en/test_routing_checkbox_contains_in.json similarity index 100% rename from schemas/test/en/test_new_routing_checkbox_contains.json rename to schemas/test/en/test_routing_checkbox_contains_in.json diff --git a/schemas/test/en/test_new_routing_checkbox_count.json b/schemas/test/en/test_routing_checkbox_count.json similarity index 100% rename from schemas/test/en/test_new_routing_checkbox_count.json rename to schemas/test/en/test_routing_checkbox_count.json diff --git a/schemas/test/en/test_routing_date_equals.json b/schemas/test/en/test_routing_date_equals.json index 22bf06bdf4..3101d2760d 100644 --- a/schemas/test/en/test_routing_date_equals.json +++ b/schemas/test/en/test_routing_date_equals.json @@ -104,125 +104,172 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "or": [ { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "days": -1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "days": -1 + } + ] } - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer" - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + } + ] + } + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "days": 1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "days": 1 + } + ] } - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "months": -1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "months": -1 + } + ] } - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "months": 1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "months": 1 + } + ] } - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "years": -1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "years": -1 + } + ] } - } - } - ] - } - }, - { - "goto": { - "block": "correct-answer", - "when": [ + ] + }, { - "id": "single-date-answer", - "condition": "equals", - "date_comparison": { - "id": "comparison-date-answer", - "offset_by": { - "years": 1 + "==": [ + { + "date": [ + { + "source": "answers", + "identifier": "single-date-answer" + } + ] + }, + { + "date": [ + { + "source": "answers", + "identifier": "comparison-date-answer" + }, + { + "years": 1 + } + ] } - } + ] } ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -239,9 +286,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_date_greater_than.json b/schemas/test/en/test_routing_date_greater_than.json index 605a01262d..cecc09776f 100644 --- a/schemas/test/en/test_routing_date_greater_than.json +++ b/schemas/test/en/test_routing_date_greater_than.json @@ -47,7 +47,7 @@ { "id": "single-date-answer", "mandatory": true, - "type": "MonthYearDate" + "type": "Date" } ], "id": "date-questions", @@ -75,23 +75,20 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + ">": [ { - "id": "single-date-answer", - "condition": "greater than", - "date_comparison": { - "meta": "return_by" - } + "date": [{ "source": "answers", "identifier": "single-date-answer" }] + }, + { + "date": [{ "source": "metadata", "identifier": "return_by" }] } ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -127,9 +124,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_new_routing_date_greater_than_or_equals.json b/schemas/test/en/test_routing_date_greater_than_or_equals.json similarity index 100% rename from schemas/test/en/test_new_routing_date_greater_than_or_equals.json rename to schemas/test/en/test_routing_date_greater_than_or_equals.json diff --git a/schemas/test/en/test_routing_date_less_than.json b/schemas/test/en/test_routing_date_less_than.json index 8ccdd7953d..30925081ff 100644 --- a/schemas/test/en/test_routing_date_less_than.json +++ b/schemas/test/en/test_routing_date_less_than.json @@ -53,23 +53,20 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "<": [ { - "id": "single-date-answer", - "condition": "less than", - "date_comparison": { - "value": "now" - } + "date": [{ "source": "answers", "identifier": "single-date-answer" }] + }, + { + "date": ["now"] } ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -86,9 +83,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_new_routing_date_less_than_or_equals.json b/schemas/test/en/test_routing_date_less_than_or_equals.json similarity index 100% rename from schemas/test/en/test_new_routing_date_less_than_or_equals.json rename to schemas/test/en/test_routing_date_less_than_or_equals.json diff --git a/schemas/test/en/test_routing_date_not_equals.json b/schemas/test/en/test_routing_date_not_equals.json index 9f183279e9..0cbb7d5ee8 100644 --- a/schemas/test/en/test_routing_date_not_equals.json +++ b/schemas/test/en/test_routing_date_not_equals.json @@ -44,32 +44,29 @@ "id": "single-date-answer", "label": "Today", "mandatory": true, - "type": "Date" + "type": "MonthYearDate" } ], "id": "date-questions", - "title": "Enter a date other than 28 February 2018", + "title": "Enter a date other than February 2018", "type": "General" }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "!=": [ { - "id": "single-date-answer", - "condition": "not equals", - "date_comparison": { - "value": "2018-02-28" - } + "date": [{ "source": "answers", "identifier": "single-date-answer" }] + }, + { + "date": ["2018-02"] } ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -86,9 +83,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_new_routing_group.json b/schemas/test/en/test_routing_group.json similarity index 100% rename from schemas/test/en/test_new_routing_group.json rename to schemas/test/en/test_routing_group.json diff --git a/schemas/test/en/test_new_routing_not.json b/schemas/test/en/test_routing_not.json similarity index 100% rename from schemas/test/en/test_new_routing_not.json rename to schemas/test/en/test_routing_not.json diff --git a/schemas/test/en/test_new_routing_not_affected_by_answers_not_on_path.json b/schemas/test/en/test_routing_not_affected_by_answers_not_on_path.json similarity index 100% rename from schemas/test/en/test_new_routing_not_affected_by_answers_not_on_path.json rename to schemas/test/en/test_routing_not_affected_by_answers_not_on_path.json diff --git a/schemas/test/en/test_routing_number_equals.json b/schemas/test/en/test_routing_number_equals.json index 4436eed82d..6338404ceb 100644 --- a/schemas/test/en/test_routing_number_equals.json +++ b/schemas/test/en/test_routing_number_equals.json @@ -6,7 +6,7 @@ "survey_id": "001", "title": "Test Routing Number Equals", "theme": "default", - "description": "A test survey for routing based on an number equals", + "description": "A test survey for routing based on a number equals", "metadata": [ { "name": "user_id", @@ -44,7 +44,7 @@ "id": "answer", "mandatory": true, "type": "Number", - "label": "123" + "label": "Enter 123" } ], "id": "question", @@ -53,21 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "==": [ { - "id": "answer", - "condition": "equals", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_number_greater_than.json b/schemas/test/en/test_routing_number_greater_than.json index d68b1e4afd..da375ab8f8 100644 --- a/schemas/test/en/test_routing_number_greater_than.json +++ b/schemas/test/en/test_routing_number_greater_than.json @@ -44,30 +44,28 @@ "id": "answer", "mandatory": true, "type": "Number", - "label": "Greater than 123" + "label": "Enter a number greater than 123" } ], "id": "question", - "title": "Enter the number greater than 123", + "title": "Enter a number greater than 123", "type": "General" }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + ">": [ { - "id": "answer", - "condition": "greater than", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -75,11 +73,11 @@ "type": "Interstitial", "id": "incorrect-answer", "content": { - "title": "Incorrect answer", + "title": "You did not enter a number greater than 123", "contents": [ { "description": { - "text": "You were asked to enter a number greater than 123 but you entered {answer}.", + "text": "You were asked to enter a number greater than 123 but you actually entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, @@ -105,7 +101,7 @@ "type": "Interstitial", "id": "correct-answer", "content": { - "title": "Correct answer", + "title": "Correct", "contents": [ { "description": { diff --git a/schemas/test/en/test_routing_number_greater_than_or_equal.json b/schemas/test/en/test_routing_number_greater_than_or_equal.json index ed149f9527..39c5df0825 100644 --- a/schemas/test/en/test_routing_number_greater_than_or_equal.json +++ b/schemas/test/en/test_routing_number_greater_than_or_equal.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "001", - "title": "Test Routing Number Greater Than or Equal", + "title": "Test Routing Number Greater Than Or Equal To", "theme": "default", - "description": "A test survey for routing based on a number greater than or equal", + "description": "A test survey for routing based on a number greater than or equal to", "metadata": [ { "name": "user_id", @@ -44,7 +44,7 @@ "id": "answer", "mandatory": true, "type": "Number", - "label": "Greater than 123" + "label": "123 or greater" } ], "id": "question", @@ -53,33 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + ">=": [ { - "id": "answer", - "condition": "greater than", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "correct-answer", - "when": [ - { - "id": "answer", - "condition": "equals", - "value": 123 - } - ] - } - }, - { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -107,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json b/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json index 0445e41c06..c4896eb621 100644 --- a/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json +++ b/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json @@ -53,21 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + ">=": [ { - "id": "answer", - "condition": "greater than or equal to", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_number_less_than.json b/schemas/test/en/test_routing_number_less_than.json index 68b73b0bcc..5036a036b9 100644 --- a/schemas/test/en/test_routing_number_less_than.json +++ b/schemas/test/en/test_routing_number_less_than.json @@ -44,30 +44,28 @@ "id": "answer", "mandatory": true, "type": "Number", - "label": "Less than 123" + "label": "Enter a number less than 123" } ], "id": "question", - "title": "Enter the number less than 123", + "title": "Enter a number less than 123", "type": "General" }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "<": [ { - "id": "answer", - "condition": "less than", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -75,11 +73,11 @@ "type": "Interstitial", "id": "incorrect-answer", "content": { - "title": "Incorrect answer", + "title": "You did not enter a number less than 123", "contents": [ { "description": { - "text": "You were asked to enter a number less than 123 but you entered {answer}.", + "text": "You were asked to enter a number less than 123 but you actually entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, @@ -105,7 +101,7 @@ "type": "Interstitial", "id": "correct-answer", "content": { - "title": "Correct answer", + "title": "Correct", "contents": [ { "description": { diff --git a/schemas/test/en/test_routing_number_less_than_or_equal.json b/schemas/test/en/test_routing_number_less_than_or_equal.json index f1514929eb..6953c80bc6 100644 --- a/schemas/test/en/test_routing_number_less_than_or_equal.json +++ b/schemas/test/en/test_routing_number_less_than_or_equal.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "001", - "title": "Test Routing Number Less Than or Equal", + "title": "Test Routing Number Less Than Or Equal To", "theme": "default", - "description": "A test survey for routing based on a number less than or equal", + "description": "A test survey for routing based on a number less than or equal to", "metadata": [ { "name": "user_id", @@ -44,7 +44,7 @@ "id": "answer", "mandatory": true, "type": "Number", - "label": "Less than or equal to 123" + "label": "Number" } ], "id": "question", @@ -53,33 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "<=": [ { - "id": "answer", - "condition": "less than", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "correct-answer", - "when": [ - { - "id": "answer", - "condition": "equals", - "value": 123 - } - ] - } - }, - { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -107,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, @@ -117,7 +101,7 @@ "type": "Interstitial", "id": "correct-answer", "content": { - "title": "correct answer", + "title": "Correct answer", "contents": [ { "description": { diff --git a/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json b/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json index cadf01cbdb..6948cdd64e 100644 --- a/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json +++ b/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json @@ -53,21 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "<=": [ { - "id": "answer", - "condition": "less than or equal to", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_number_not_equals.json b/schemas/test/en/test_routing_number_not_equals.json index 71e176b597..c400bcf04a 100644 --- a/schemas/test/en/test_routing_number_not_equals.json +++ b/schemas/test/en/test_routing_number_not_equals.json @@ -53,21 +53,19 @@ }, "routing_rules": [ { - "goto": { - "block": "correct-answer", - "when": [ + "block": "correct-answer", + "when": { + "!=": [ { - "id": "answer", - "condition": "not equals", - "value": 123 - } + "source": "answers", + "identifier": "answer" + }, + 123 ] } }, { - "goto": { - "block": "incorrect-answer" - } + "block": "incorrect-answer" } ] }, @@ -95,9 +93,7 @@ }, "routing_rules": [ { - "goto": { - "section": "End" - } + "section": "End" } ] }, diff --git a/schemas/test/en/test_routing_on_multiple_select.json b/schemas/test/en/test_routing_on_multiple_select.json index ae98c6168c..b198117b8e 100644 --- a/schemas/test/en/test_routing_on_multiple_select.json +++ b/schemas/test/en/test_routing_on_multiple_select.json @@ -64,21 +64,19 @@ }, "routing_rules": [ { - "goto": { - "block": "block3", - "when": [ + "block": "block3", + "when": { + "in": [ + "United Kingdom", { - "id": "passports-answer", - "condition": "contains", - "value": "United Kingdom" + "identifier": "passports-answer", + "source": "answers" } ] } }, { - "goto": { - "block": "block2" - } + "block": "block2" } ] }, diff --git a/schemas/test/en/test_new_routing_or.json b/schemas/test/en/test_routing_or.json similarity index 100% rename from schemas/test/en/test_new_routing_or.json rename to schemas/test/en/test_routing_or.json diff --git a/schemas/test/en/test_new_routing_to_questionnaire_end_multiple_sections.json b/schemas/test/en/test_routing_to_questionnaire_end_multiple_sections.json similarity index 100% rename from schemas/test/en/test_new_routing_to_questionnaire_end_multiple_sections.json rename to schemas/test/en/test_routing_to_questionnaire_end_multiple_sections.json diff --git a/schemas/test/en/test_new_routing_to_questionnaire_end_single_section.json b/schemas/test/en/test_routing_to_questionnaire_end_single_section.json similarity index 100% rename from schemas/test/en/test_new_routing_to_questionnaire_end_single_section.json rename to schemas/test/en/test_routing_to_questionnaire_end_single_section.json diff --git a/schemas/test/en/test_new_routing_to_section_end.json b/schemas/test/en/test_routing_to_section_end.json similarity index 100% rename from schemas/test/en/test_new_routing_to_section_end.json rename to schemas/test/en/test_routing_to_section_end.json diff --git a/schemas/test/en/test_section_enabled_checkbox.json b/schemas/test/en/test_section_enabled_checkbox.json index deb294cc6b..7c60b826c5 100644 --- a/schemas/test/en/test_section_enabled_checkbox.json +++ b/schemas/test/en/test_section_enabled_checkbox.json @@ -85,17 +85,17 @@ { "id": "section-2", "title": "Section 2", - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "in": [ + "Section 2", { - "id": "section-1-answer", - "condition": "contains", - "value": "Section 2" + "source": "answers", + "identifier": "section-1-answer" } ] } - ], + }, "groups": [ { "blocks": [ @@ -126,17 +126,17 @@ { "id": "section-3", "title": "Section 3", - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "in": [ + "Section 3", { - "id": "section-1-answer", - "condition": "contains", - "value": "Section 3" + "source": "answers", + "identifier": "section-1-answer" } ] } - ], + }, "groups": [ { "blocks": [ diff --git a/schemas/test/en/test_section_enabled_hub.json b/schemas/test/en/test_section_enabled_hub.json index a32a3ed80d..5f6e82f73b 100644 --- a/schemas/test/en/test_section_enabled_hub.json +++ b/schemas/test/en/test_section_enabled_hub.json @@ -80,17 +80,17 @@ { "id": "section-2", "title": "Section 2", - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "in": [ + "Section 2", { - "id": "section-1-answer", - "condition": "contains", - "value": "Section 2" + "source": "answers", + "identifier": "section-1-answer" } ] } - ], + }, "groups": [ { "blocks": [ @@ -121,17 +121,17 @@ { "id": "section-3", "title": "Section 3", - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "in": [ + "Section 3", { - "id": "section-1-answer", - "condition": "contains", - "value": "Section 3" + "source": "answers", + "identifier": "section-1-answer" } ] } - ], + }, "groups": [ { "blocks": [ diff --git a/schemas/test/en/test_section_enabled_radio.json b/schemas/test/en/test_section_enabled_radio.json index 47ab94200c..3141ae569f 100644 --- a/schemas/test/en/test_section_enabled_radio.json +++ b/schemas/test/en/test_section_enabled_radio.json @@ -73,17 +73,17 @@ { "id": "section-2", "title": "Section 2", - "enabled": [ - { - "when": [ + "enabled": { + "when": { + "==": [ + "Yes, enable section 2", { - "id": "section-1-answer", - "condition": "equals", - "value": "Yes, enable section 2" + "source": "answers", + "identifier": "section-1-answer" } ] } - ], + }, "groups": [ { "blocks": [ diff --git a/schemas/test/en/test_section_summary.json b/schemas/test/en/test_section_summary.json index 23d82f2d6f..53b66398b3 100644 --- a/schemas/test/en/test_section_summary.json +++ b/schemas/test/en/test_section_summary.json @@ -122,17 +122,17 @@ } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "insurance-type-answer", - "condition": "equals", - "value": "Both" - } + "source": "answers", + "identifier": "insurance-type-answer" + }, + "Both" ] } - ] + } } ] }, diff --git a/schemas/test/en/test_show_section_summary_on_completion.json b/schemas/test/en/test_show_section_summary_on_completion.json index 6d0614c5f7..25317ebfc0 100644 --- a/schemas/test/en/test_show_section_summary_on_completion.json +++ b/schemas/test/en/test_show_section_summary_on_completion.json @@ -23,7 +23,9 @@ ], "questionnaire_flow": { "type": "Hub", - "options": { "required_completed_sections": ["employment-section"] } + "options": { + "required_completed_sections": ["employment-section"] + } }, "sections": [ { @@ -95,20 +97,19 @@ }, "routing_rules": [ { - "goto": { - "group": "checkboxes", - "when": [ + "block": "employment-type", + "when": { + "==": [ { - "id": "employment-status-answer", - "condition": "set" - } + "identifier": "employment-status-answer", + "source": "answers" + }, + null ] } }, { - "goto": { - "block": "employment-type" - } + "group": "checkboxes" } ] }, diff --git a/schemas/test/en/test_new_skip_condition_answer_comparison.json b/schemas/test/en/test_skip_condition_answer_comparison.json similarity index 100% rename from schemas/test/en/test_new_skip_condition_answer_comparison.json rename to schemas/test/en/test_skip_condition_answer_comparison.json diff --git a/schemas/test/en/test_skip_condition_block.json b/schemas/test/en/test_skip_condition_block.json index 8da02177a5..389aa2095e 100644 --- a/schemas/test/en/test_skip_condition_block.json +++ b/schemas/test/en/test_skip_condition_block.json @@ -4,7 +4,7 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Skip group", + "title": "Skip block", "theme": "default", "metadata": [ { @@ -33,20 +33,21 @@ "id": "default-section", "groups": [ { - "id": "do-you-want-to-skip-group", - "title": "Do you want to skip the next block?", + "id": "default-group", + "title": "Group 1", "blocks": [ { "type": "Question", "id": "do-you-want-to-skip", "question": { "id": "do-you-want-to-skip-question", - "title": "Do you want to skip?", + "title": "Do you want to skip the next question?", "type": "General", + "description": ["Select “Yes” to skip the next question and go straight to the summary"], "answers": [ { "id": "do-you-want-to-skip-answer", - "label": "Do you want to skip?", + "label": "Select an answer", "mandatory": true, "options": [ { @@ -58,10 +59,7 @@ "value": "No" } ], - "type": "Radio", - "validation": { - "messages": {} - } + "type": "Radio" } ] } @@ -71,44 +69,27 @@ "id": "should-skip", "question": { "id": "should-skip-question", - "title": "Do you want to skip?", + "title": "Why didn’t you skip the block?", "type": "General", "answers": [ { "id": "should-skip-answer", - "label": "Why didn’t you skip the block?", + "label": "Enter your answer", "mandatory": true, "type": "TextArea" } ] }, - "skip_conditions": [ - { - "when": [ + "skip_conditions": { + "when": { + "==": [ { - "id": "do-you-want-to-skip-answer", - "condition": "equals", - "value": "Yes" - } + "source": "answers", + "identifier": "do-you-want-to-skip-answer" + }, + "Yes" ] } - ] - }, - { - "type": "Question", - "id": "a-non-skipped-block", - "question": { - "id": "will-not-be-skipped-question", - "title": "Always ask this question", - "type": "General", - "answers": [ - { - "id": "will-not-be-skipped-answer", - "label": "never skipped", - "mandatory": true, - "type": "TextArea" - } - ] } } ] diff --git a/schemas/test/en/test_skip_condition_group.json b/schemas/test/en/test_skip_condition_group.json index b474e09151..1640b7ee88 100644 --- a/schemas/test/en/test_skip_condition_group.json +++ b/schemas/test/en/test_skip_condition_group.json @@ -33,20 +33,21 @@ "id": "default-section", "groups": [ { - "id": "do-you-want-to-skip-group", - "title": "Do you want to skip the next group?", + "id": "default-group", + "title": "Group 1", "blocks": [ { "type": "Question", "id": "do-you-want-to-skip", "question": { "id": "do-you-want-to-skip-question", - "title": "Do you want to skip?", + "title": "Do you want to skip the next question?", "type": "General", + "description": ["Select “Yes” to skip the next question and go straight to the summary"], "answers": [ { "id": "do-you-want-to-skip-answer", - "label": "Do you want to skip?", + "label": "Select an answer", "mandatory": true, "options": [ { @@ -58,10 +59,7 @@ "value": "No" } ], - "type": "Radio", - "validation": { - "messages": {} - } + "type": "Radio" } ] } @@ -70,30 +68,30 @@ }, { "id": "should-skip-group", - "title": "This question may or may not be skipped", - "skip_conditions": [ - { - "when": [ + "title": "Group 2 (Skippable)", + "skip_conditions": { + "when": { + "==": [ { - "id": "do-you-want-to-skip-answer", - "condition": "equals", - "value": "Yes" - } + "source": "answers", + "identifier": "do-you-want-to-skip-answer" + }, + "Yes" ] } - ], + }, "blocks": [ { "type": "Question", "id": "should-skip", "question": { "id": "should-skip-question", - "title": "Do you want to skip?", + "title": "Why didn’t you skip the group?", "type": "General", "answers": [ { "id": "should-skip-answer", - "label": "Why didn’t you skip the group?", + "label": "Enter your answer", "mandatory": true, "type": "TextArea" } diff --git a/schemas/test/en/test_new_skip_condition_not_set.json b/schemas/test/en/test_skip_condition_not_set.json similarity index 100% rename from schemas/test/en/test_new_skip_condition_not_set.json rename to schemas/test/en/test_skip_condition_not_set.json diff --git a/schemas/test/en/test_new_skip_condition_set.json b/schemas/test/en/test_skip_condition_set.json similarity index 100% rename from schemas/test/en/test_new_skip_condition_set.json rename to schemas/test/en/test_skip_condition_set.json diff --git a/schemas/test/en/test_titles_radio_and_checkbox.json b/schemas/test/en/test_titles_radio_and_checkbox.json index bb2f23760c..98b10beb8c 100644 --- a/schemas/test/en/test_titles_radio_and_checkbox.json +++ b/schemas/test/en/test_titles_radio_and_checkbox.json @@ -69,13 +69,15 @@ "id": "checkbox-block", "question_variants": [ { - "when": [ - { - "id": "name-answer", - "condition": "equals", - "value": "Peter" - } - ], + "when": { + "==": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Peter" + ] + }, "question": { "id": "checkbox-question", "type": "General", @@ -108,13 +110,15 @@ } }, { - "when": [ - { - "id": "name-answer", - "condition": "equals", - "value": "Mary" - } - ], + "when": { + "==": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Mary" + ] + }, "question": { "id": "checkbox-question", "type": "General", @@ -147,13 +151,15 @@ } }, { - "when": [ - { - "id": "name-answer", - "condition": "not equals", - "value": "Mary" - } - ], + "when": { + "!=": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Mary" + ] + }, "question": { "id": "checkbox-question", "type": "General", @@ -192,13 +198,15 @@ "id": "radio-block", "question_variants": [ { - "when": [ - { - "id": "name-answer", - "condition": "equals", - "value": "Peter" - } - ], + "when": { + "==": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Peter" + ] + }, "question": { "id": "radio-question", "type": "General", @@ -235,13 +243,15 @@ } }, { - "when": [ - { - "id": "name-answer", - "condition": "equals", - "value": "Mary" - } - ], + "when": { + "==": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Mary" + ] + }, "question": { "id": "radio-question", "type": "General", @@ -278,13 +288,15 @@ } }, { - "when": [ - { - "id": "name-answer", - "condition": "not equals", - "value": "Mary" - } - ], + "when": { + "!=": [ + { + "source": "answers", + "identifier": "name-answer" + }, + "Mary" + ] + }, "question": { "id": "radio-question", "type": "General", diff --git a/schemas/test/en/test_variants_content.json b/schemas/test/en/test_variants_content.json index bccf527a32..ccec714151 100644 --- a/schemas/test/en/test_variants_content.json +++ b/schemas/test/en/test_variants_content.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Test Content Variants", + "title": "Test New Content Variants", "theme": "default", - "description": "A questionnaire to test content variants and variant choices", + "description": "A questionnaire to test new content variants and variant choices", "metadata": [ { "name": "user_id", @@ -67,13 +67,15 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "greater than", - "value": 16 - } - ] + "when": { + ">": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + } }, { "content": { @@ -84,13 +86,15 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "less than or equal to", - "value": 16 - } - ] + "when": { + "<=": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + } } ] } diff --git a/schemas/test/en/test_variants_first_item_in_list.json b/schemas/test/en/test_variants_first_item_in_list.json index 9605a532f7..0e3f8f366f 100644 --- a/schemas/test/en/test_variants_first_item_in_list.json +++ b/schemas/test/en/test_variants_first_item_in_list.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Test Question Variants Using List", + "title": "Test New Question Variants Using List", "theme": "default", - "description": "A questionnaire to test question variants using the first item in a list", + "description": "A questionnaire to test new question variants using the first item in a list", "metadata": [ { "name": "user_id", @@ -236,17 +236,19 @@ "title": "You are the first person in the list", "type": "General" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "equals", - "comparison": { + "when": { + "==": [ + { + "source": "list", + "identifier": "people", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } }, { "question": { @@ -271,17 +273,19 @@ "title": "You are not the first person in the list", "type": "General" }, - "when": [ - { - "list": "people", - "id_selector": "first", - "condition": "not equals", - "comparison": { + "when": { + "!=": [ + { + "source": "list", + "identifier": "people", + "selector": "first" + }, + { "source": "location", - "id": "list_item_id" + "identifier": "list_item_id" } - } - ] + ] + } } ], "type": "Question" diff --git a/schemas/test/en/test_variants_question.json b/schemas/test/en/test_variants_question.json index 2bbdeff2f9..bfca17b3b2 100644 --- a/schemas/test/en/test_variants_question.json +++ b/schemas/test/en/test_variants_question.json @@ -6,7 +6,7 @@ "survey_id": "0", "title": "Test Question Variants", "theme": "default", - "description": "A questionnaire to test question variants", + "description": "A questionnaire to test new question variants", "metadata": [ { "name": "user_id", @@ -142,13 +142,15 @@ } ] }, - "when": [ - { - "id": "proxy-answer", - "condition": "equals", - "value": "Yes, I am" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes, I am" + ] + } }, { "question": { @@ -189,13 +191,15 @@ } ] }, - "when": [ - { - "id": "proxy-answer", - "condition": "equals", - "value": "No, I am answering on their behalf" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I am answering on their behalf" + ] + } } ] }, @@ -226,18 +230,28 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "greater than", - "value": 16 - }, - { - "id": "proxy-answer", - "condition": "not equals", - "value": "No, I am answering on their behalf" - } - ] + "when": { + "and": [ + { + ">=": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + }, + { + "!=": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I am answering on their behalf" + ] + } + ] + } }, { "question": { @@ -262,18 +276,28 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "less than or equal to", - "value": 16 - }, - { - "id": "proxy-answer", - "condition": "not equals", - "value": "No, I am answering on their behalf" - } - ] + "when": { + "and": [ + { + "<=": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + }, + { + "!=": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I am answering on their behalf" + ] + } + ] + } }, { "question": { @@ -323,18 +347,28 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "greater than or equal to", - "value": 16 - }, - { - "id": "proxy-answer", - "condition": "equals", - "value": "No, I am answering on their behalf" - } - ] + "when": { + "and": [ + { + ">=": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + }, + { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I am answering on their behalf" + ] + } + ] + } }, { "question": { @@ -384,37 +418,45 @@ } ] }, - "when": [ - { - "id": "age-answer", - "condition": "less than or equal to", - "value": 16 - }, - { - "id": "proxy-answer", - "condition": "equals", - "value": "No, I am answering on their behalf" - } - ] + "when": { + "and": [ + { + "<=": [ + { + "source": "answers", + "identifier": "age-answer" + }, + 16 + ] + }, + { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I am answering on their behalf" + ] + } + ] + } } ], "routing_rules": [ { - "goto": { - "block": "age-block", - "when": [ + "block": "age-block", + "when": { + "==": [ { - "id": "age-confirm-answer", - "condition": "equals", - "value": "No" - } + "source": "answers", + "identifier": "age-confirm-answer" + }, + "No" ] } }, { - "goto": { - "section": "End" - } + "section": "End" } ] } @@ -476,13 +518,15 @@ } ] }, - "when": [ - { - "id": "currency-answer", - "condition": "equals", - "value": "Sterling" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "Sterling" + ] + } }, { "question": { @@ -500,13 +544,15 @@ } ] }, - "when": [ - { - "id": "currency-answer", - "condition": "equals", - "value": "US Dollars" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "US Dollars" + ] + } } ] }, @@ -530,13 +576,15 @@ } ] }, - "when": [ - { - "id": "currency-answer", - "condition": "equals", - "value": "Sterling" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "Sterling" + ] + } }, { "question": { @@ -554,13 +602,15 @@ } ] }, - "when": [ - { - "id": "currency-answer", - "condition": "equals", - "value": "US Dollars" - } - ] + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "US Dollars" + ] + } } ] } diff --git a/tests/app/data_model/test_list_store.py b/tests/app/data_model/test_list_store.py index 0f99322cac..23cc6b23c5 100644 --- a/tests/app/data_model/test_list_store.py +++ b/tests/app/data_model/test_list_store.py @@ -151,3 +151,13 @@ def test_first_raises_index_error_when_list_is_empty(): assert "unable to access first item in list, list 'people' is empty" in str( error.value ) + + +def test_get_item_using_method(): + store = ListStore() + + first_id = store.add_list_item("people") + + item = store.get("people") + + assert item.items[0] == first_id diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 85d931cbfc..7440b4d93a 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -2,7 +2,6 @@ from decimal import Decimal import pytest -from mock import patch from werkzeug.datastructures import MultiDict from app.data_models import ListStore @@ -1449,16 +1448,15 @@ def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_st expected_form_data = {"csrf_token": None, "feeling-answer": "Good"} - with patch("app.questionnaire.path_finder.evaluate_goto", return_value=False): - form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata=get_metadata(), - response_metadata={}, - form_data=form_data, - ) + form = generate_form( + schema, + question_schema, + answer_store, + list_store, + metadata=get_metadata(), + response_metadata={}, + form_data=form_data, + ) form.validate() assert form.data == expected_form_data diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index b91188f178..03771dc58f 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -109,13 +109,15 @@ def question_variant_schema(): "title": "Block 1", "question_variants": [ { - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "yes", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, "question": { "id": "question1", "type": "General", @@ -129,13 +131,15 @@ def question_variant_schema(): }, }, { - "when": [ - { - "id": "when-answer", - "condition": "not equals", - "value": "yes", - } - ], + "when": { + "!=": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, "question": { "id": "question1", "type": "General", @@ -246,13 +250,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "yes", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, }, { "question": { @@ -266,13 +272,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "no", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "no", + ] + }, }, ], "add_block": { @@ -291,13 +299,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "yes", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, }, { "question": { @@ -311,13 +321,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "no", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "no", + ] + }, }, ], }, @@ -337,13 +349,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "yes", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, }, { "question": { @@ -357,13 +371,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "no", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "no", + ] + }, }, ], }, @@ -386,13 +402,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "yes", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "yes", + ] + }, }, { "question": { @@ -406,13 +424,15 @@ def list_collector_variant_schema(): } ], }, - "when": [ - { - "id": "when-answer", - "condition": "equals", - "value": "no", - } - ], + "when": { + "==": [ + { + "identifier": "when-answer", + "source": "answers", + }, + "no", + ] + }, }, ], }, @@ -481,7 +501,6 @@ def sections_dependent_on_list_schema(): }, "when": { ">": [ - 0, { "count": [ { @@ -490,6 +509,7 @@ def sections_dependent_on_list_schema(): } ] }, + 0, ] }, }, @@ -535,7 +555,6 @@ def sections_dependent_on_list_schema(): }, "when": { ">": [ - 0, { "count": [ { @@ -544,6 +563,7 @@ def sections_dependent_on_list_schema(): } ] }, + 0, ] }, }, @@ -572,13 +592,16 @@ def sections_dependent_on_list_schema(): "title": {"text": "Does anyone else live here?"}, "type": "General", }, - "when": [ - { - "condition": "greater than", - "list": "not-the-list", - "value": 0, - } - ], + "when": { + "<": [ + 0, + { + "identifier": "not-the-list", + "source": "list", + "selector": "count", + }, + ] + }, } ], } @@ -605,13 +628,16 @@ def sections_dependent_on_list_schema(): "title": {"text": "Does anyone else live here?"}, "type": "General", }, - "when": [ - { - "condition": "greater than", - "list": "list", - "value": 0, - } - ], + "when": { + ">": [ + { + "identifier": "list", + "source": "list", + "selector": "count", + }, + 0, + ] + }, } ], } @@ -656,6 +682,41 @@ def sections_dependent_on_list_schema(): } ], }, + { + "id": "section5", + "groups": [ + { + "id": "group5", + "blocks": [ + { + "type": "Question", + "id": "block5", + "question": { + "answers": [ + { + "id": "answer1", + "mandatory": True, + "type": "General", + } + ], + "id": "question1", + "title": {"text": "Does anyone else live here?"}, + "type": "General", + }, + "when": { + ">": [ + { + "identifier": "missing-the-source-attribute", + "selector": "count", + }, + 0, + ] + }, + } + ], + } + ], + }, ] } @@ -695,29 +756,39 @@ def content_variant_schema(): "content_variants": [ { "content": [{"title": "You are over 16"}], - "when": [ - { - "id": "age-answer", - "condition": "greater than", - "value": "16", - } - ], + "when": { + ">": [ + { + "identifier": "age-answer", + "source": "answers", + }, + 16, + ] + }, }, { "content": [{"title": "You are under 16"}], - "when": [ - { - "id": "age-answer", - "condition": "less than or equal to", - "value": "16", - } - ], + "when": { + "<=": [ + { + "identifier": "age-answer", + "source": "answers", + }, + 16, + ] + }, }, { "content": [{"title": "You are ageless"}], - "when": [ - {"id": "age-answer", "condition": "not set"} - ], + "when": { + "==": [ + { + "identifier": "age-answer", + "source": "answers", + }, + None, + ] + }, }, ], }, @@ -1200,18 +1271,18 @@ def dynamic_answer_options_function_driven_schema(): @pytest.fixture def skipping_section_dependencies_schema(): - return load_schema_from_name("test_new_routing_and_skipping_section_dependencies") + return load_schema_from_name("test_routing_and_skipping_section_dependencies") @pytest.fixture def section_dependencies_calculated_summary_schema(): return load_schema_from_name( - "test_new_routing_and_skipping_section_dependencies_calculated_summary" + "test_routing_and_skipping_section_dependencies_calculated_summary" ) @pytest.fixture def section_dependencies_new_calculated_summary_schema(): return load_schema_from_name( - "test_new_routing_and_skipping_section_dependencies_new_calculated_summary" + "test_routing_and_skipping_section_dependencies_new_calculated_summary" ) diff --git a/tests/app/questionnaire/test_date_rules.py b/tests/app/questionnaire/test_date_rules.py deleted file mode 100644 index fbf7a290ab..0000000000 --- a/tests/app/questionnaire/test_date_rules.py +++ /dev/null @@ -1,132 +0,0 @@ -from datetime import datetime, timezone - -import pytest - -from app.data_models.answer_store import Answer, AnswerStore -from app.data_models.list_store import ListStore -from app.questionnaire.when_rules import evaluate_date_rule, evaluate_goto - - -@pytest.mark.parametrize( - "date, condition, comparison, expected", - ( - ( - datetime.now(tz=timezone.utc).strftime("%Y-%m-%d"), - "equals", - {"value": "now"}, - True, - ), - ("2000-01-01", "equals", {"value": "now"}, False), - ( - "2020-05-01", - "equals", - { - "value": "2019-03-31", - "offset_by": {"days": 1, "months": 1, "years": 1}, - }, - True, - ), - ( - "2020-02-29", - "equals", - { - "value": "2021-04-01", - "offset_by": {"days": -1, "months": -1, "years": -1}, - }, - True, - ), - ( - "2018-02", - "not equals", - {"value": "2018-01"}, - True, - ), - ( - "2018-01", - "not equals", - {"value": "2018-01"}, - False, - ), - ( - "2018-01", - "not equals", - {"value": "2018-01"}, - False, - ), - ( - "2016-06-11", - "less than", - {"meta": "return_by"}, - True, - ), - ( - "2016-06-12", - "less than", - {"meta": "return_by"}, - False, - ), - ( - "2018-02-04", - "greater than", - {"id": "compare_date_answer"}, - True, - ), - ( - "2018-02-03", - "greater than", - {"id": "compare_date_answer"}, - False, - ), - ( - "2018-02-03", - "greater than", - {"id": "non_existent_answer"}, - False, - ), - ), -) -def test_evaluate_date_rule_equals_with_value( - date, condition, comparison, expected, questionnaire_schema -): - when = { - "id": "date-answer", - "condition": condition, - "date_comparison": comparison, - } - metadata = {"return_by": "2016-06-12"} - answer_store = AnswerStore({}) - answer_store.add_or_update( - Answer(answer_id="compare_date_answer", value="2018-02-03") - ) - - assert ( - evaluate_date_rule(when, answer_store, questionnaire_schema, metadata, date) - is expected - ) - - -def test_do_not_go_to_next_question_for_date_answer( - current_location, questionnaire_schema -): - 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")) - - assert not evaluate_goto( - goto_rule=goto_rule, - schema=questionnaire_schema, - metadata={}, - answer_store=answer_store, - list_store=ListStore(), - current_location=current_location, - ) diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index f74baac314..2e194714c1 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -42,57 +42,20 @@ def test_introduction_in_path_when_in_schema(answer_store, list_store, progress_ def test_introduction_not_in_path_when_not_in_schema( - answer_store, list_store, progress_store, mocker + answer_store, list_store, progress_store ): schema = load_schema_from_name("test_checkbox") current_section = schema.get_section("default-section") path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) - mocker.patch("app.questionnaire.when_rules.evaluate_when_rules", return_value=False) routing_path = path_finder.routing_path(section_id=current_section["id"]) assert "introduction" not in routing_path -@pytest.mark.parametrize( - "schema", - ( - "test_new_routing_number_equals", - "test_routing_number_equals", - ), -) -def test_routing_path_with_conditional_path(schema, answer_store, list_store): - schema = load_schema_from_name(schema) - 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.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, list_store, progress_store, None, {}) - - routing_path = path_finder.routing_path(section_id=section_id) - - assert routing_path == expected_path - - -def test_new_routing_basic_and_conditional_path( - answer_store, list_store, progress_store -): +def test_routing_basic_and_conditional_path(answer_store, list_store, progress_store): # Given - schema = load_schema_from_name("test_new_routing_number_equals") + schema = load_schema_from_name("test_routing_number_equals") section_id = schema.get_section_id_for_block_id("number-question") expected_path = RoutingPath( ["number-question", "correct-answer"], @@ -268,18 +231,9 @@ def test_routing_path_with_conditional_value_not_in_metadata(answer_store, list_ assert routing_path == expected_path -@pytest.mark.parametrize( - "schema, expected_routing_path_ids", - ( - ("test_new_skip_condition_block", ["do-you-want-to-skip"]), - ("test_skip_condition_block", ["do-you-want-to-skip", "a-non-skipped-block"]), - ), -) -def test_new_routing_path_should_skip_block( - schema, expected_routing_path_ids, answer_store, list_store -): +def test_routing_path_should_skip_block(answer_store, list_store): # Given - schema = load_schema_from_name(schema) + schema = load_schema_from_name("test_skip_condition_block") section_id = schema.get_section_id_for_block_id("should-skip") answer_store.add_or_update( Answer(answer_id="do-you-want-to-skip-answer", value="Yes") @@ -301,6 +255,7 @@ def test_new_routing_path_should_skip_block( routing_path = path_finder.routing_path(section_id=section_id) # Then + expected_routing_path_ids = ["do-you-want-to-skip"] expected_routing_path = RoutingPath( expected_routing_path_ids, section_id="default-section", @@ -309,16 +264,9 @@ def test_new_routing_path_should_skip_block( assert routing_path == expected_routing_path -@pytest.mark.parametrize( - "schema", - ( - "test_skip_condition_group", - "test_new_skip_condition_group", - ), -) -def test_routing_path_should_skip_group(schema, answer_store, list_store): +def test_routing_path_should_skip_group(answer_store, list_store): # Given - schema = load_schema_from_name(schema) + 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.add_or_update( @@ -348,16 +296,9 @@ def test_routing_path_should_skip_group(schema, answer_store, list_store): assert routing_path == expected_routing_path -@pytest.mark.parametrize( - "schema", - ( - "test_skip_condition_group", - "test_new_skip_condition_group", - ), -) -def test_routing_path_should_not_skip_group(schema, answer_store, list_store): +def test_routing_path_should_not_skip_group(answer_store, list_store): # Given - schema = load_schema_from_name(schema) + 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.add_or_update( @@ -410,7 +351,7 @@ def test_get_routing_path_when_first_block_in_group_skipped( def test_build_path_with_group_routing(answer_store, list_store, progress_store): # Given i have answered the routing question - schema = load_schema_from_name("test_new_routing_group") + schema = load_schema_from_name("test_routing_group") section_id = schema.get_section_id_for_block_id("group2-block") answer_store.add_or_update(Answer(answer_id="which-group-answer", value="group2")) @@ -425,71 +366,7 @@ def test_build_path_with_group_routing(answer_store, list_store, progress_store) def test_remove_answer_and_block_if_routing_backwards(list_store): - 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", - ], - } - ] - ) - - 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 correct this" - ) - answer_store = AnswerStore({}) - answer_store.add_or_update(number_of_employees_answer) - answer_store.add_or_update(confirm_zero_answer) - - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) - - assert ( - len( - path_finder.progress_store.get_completed_block_ids( - section_id="default-section" - ) - ) - == 2 - ) - assert 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", - ) - assert routing_path == expected_path - - assert path_finder.progress_store.get_completed_block_ids( - section_id="default-section" - ) == [progress_store.get_completed_block_ids(section_id="default-section")[0]] - - assert len(path_finder.answer_store) == 1 - assert not path_finder.answer_store.get_answer("confirm-zero-employees-answer") - - assert ( - progress_store.get_section_status(section_id="default-section") - == CompletionStatus.IN_PROGRESS - ) - - -def test_new_remove_answer_and_block_if_routing_backwards(list_store): - schema = load_schema_from_name("test_new_confirmation_question") + schema = load_schema_from_name("test_confirmation_question_backwards_routing") section_id = schema.get_section_id_for_block_id("confirm-zero-employees-block") # All blocks completed @@ -614,26 +491,16 @@ def test_new_remove_answer_and_block_if_routing_backwards(list_store): ), ), ) -@pytest.mark.parametrize( - "schema_name", - ( - [ - "test_new_routing_and_skipping_section_dependencies", - "test_routing_and_skipping_section_dependencies", - ] - ), -) def test_routing_path_block_ids_dependent_on_other_sections_when_rules( list_store, skip_age_answer, skip_confirmation_answer, - schema_name, section_id, expected_route, answer_store, ): # Given a schema which has when rules in a section which has dependencies on other sections answers - schema = load_schema_from_name(schema_name) + schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") answer_store.add_or_update( Answer(answer_id="skip-age-answer", value=skip_age_answer) ) @@ -700,20 +567,11 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( ), ), ) -@pytest.mark.parametrize( - "schema_name", - ( - [ - "test_new_routing_and_skipping_section_dependencies", - "test_routing_and_skipping_section_dependencies", - ] - ), -) def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating( - skip_age_answer, schema_name, expected_route, answer_store + skip_age_answer, expected_route, answer_store ): # Given a schema with repeating sections which has when rules dependent on another section - schema = load_schema_from_name(schema_name) + schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") answer_store.add_or_update( Answer(answer_id="skip-age-answer", value=skip_age_answer) ) diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 7a4aac5d6b..6dbcf02a8f 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -220,7 +220,7 @@ def test_get_all_questions_for_block_question_variants(): "title": "Question 1", "answers": [{"id": "answer1", "label": "Variant 1"}], }, - "when": [], + "when": {}, }, { "question": { @@ -228,7 +228,7 @@ def test_get_all_questions_for_block_question_variants(): "title": "Question 1", "answers": [{"id": "answer1", "label": "Variant 2"}], }, - "when": [], + "when": {}, }, ], } diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 21f00722ae..2772e5ad09 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -55,10 +55,6 @@ def test_enabled_section_ids(self): assert expected_section_ids == self.router.enabled_section_ids - self.schema = load_schema_from_name("test_new_section_enabled_checkbox") - - assert expected_section_ids == self.router.enabled_section_ids - def test_full_routing_path_without_repeating_sections(self): self.schema = load_schema_from_name("test_checkbox") routing_path = self.router.full_routing_path() @@ -290,7 +286,7 @@ def test_cant_access(self): assert not can_access_location def test_cant_access_section_disabled(self): - self.schema = load_schema_from_name("test_new_section_enabled_checkbox") + self.schema = load_schema_from_name("test_section_enabled_checkbox") current_location = Location( section_id="section-2", block_id="section-2-block", list_item_id=None @@ -511,10 +507,7 @@ def test_section_summary_on_completion_false(self): @pytest.mark.usefixtures("app") @pytest.mark.parametrize( "schema", - ( - "test_calculated_summary", - "test_new_calculated_summary", - ), + ("test_calculated_summary",), ) def test_return_to_calculated_summary(self, schema): self.schema = load_schema_from_name(schema) @@ -614,16 +607,6 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): None, "/questionnaire/sixth-number-block/?return_to=calculated-summary", ), - ( - "test_new_calculated_summary", - "non-valid-block", - "/questionnaire/sixth-number-block/?return_to=calculated-summary&return_to_block_id=non-valid-block", - ), - ( - "test_new_calculated_summary", - None, - "/questionnaire/sixth-number-block/?return_to=calculated-summary", - ), ], ) def test_return_to_calculated_summary_invalid_return_to_block_id( @@ -651,10 +634,7 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( @pytest.mark.usefixtures("app") @pytest.mark.parametrize( "schema", - ( - "test_calculated_summary", - "test_new_calculated_summary", - ), + ("test_calculated_summary",), ) def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schema): self.schema = load_schema_from_name(schema) @@ -710,7 +690,7 @@ def test_redirects_to_submit_page_when_questionnaire_complete( @pytest.mark.usefixtures("app") def test_return_to_final_summary_questionnaire_and_section_is_complete(self): self.schema = load_schema_from_name( - "test_new_routing_to_questionnaire_end_single_section" + "test_routing_to_questionnaire_end_single_section" ) self.progress_store = ProgressStore( [ @@ -759,7 +739,7 @@ def test_return_to_final_summary_section_is_in_progress(self): @pytest.mark.usefixtures("app") def test_return_to_final_summary_questionnaire_is_not_complete(self): self.schema = load_schema_from_name( - "test_new_routing_to_questionnaire_end_multiple_sections" + "test_routing_to_questionnaire_end_multiple_sections" ) self.answer_store = AnswerStore([{"answer_id": "test-answer", "value": "Yes"}]) self.progress_store = ProgressStore( @@ -1085,7 +1065,7 @@ def test_block_on_path(self): @pytest.mark.usefixtures("app") def test_last_block_not_on_path(self): self.schema = load_schema_from_name( - "test_new_routing_to_questionnaire_end_multiple_sections" + "test_routing_to_questionnaire_end_multiple_sections" ) self.answer_store = AnswerStore( [ diff --git a/tests/app/questionnaire/test_schema_utils.py b/tests/app/questionnaire/test_schema_utils.py index 6880e44ceb..b1d43af8d0 100644 --- a/tests/app/questionnaire/test_schema_utils.py +++ b/tests/app/questionnaire/test_schema_utils.py @@ -55,7 +55,7 @@ def test_transform_variants_with_question_variants(question_variant_schema): def test_transform_variants_with_content(content_variant_schema): schema = QuestionnaireSchema(content_variant_schema) answer_store = AnswerStore({}) - answer_store.add_or_update(Answer(answer_id="age-answer", value="18")) + answer_store.add_or_update(Answer(answer_id="age-answer", value=18)) metadata = {} response_metadata = {} @@ -155,7 +155,7 @@ def test_transform_variants_list_collector(list_collector_variant_schema): 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")) + answer_store.add_or_update(Answer(answer_id="age-answer", value=18)) metadata = {} response_metadata = {} diff --git a/tests/app/questionnaire/test_when_rules.py b/tests/app/questionnaire/test_when_rules.py deleted file mode 100644 index aee9ec288b..0000000000 --- a/tests/app/questionnaire/test_when_rules.py +++ /dev/null @@ -1,815 +0,0 @@ -# pylint: disable=too-many-lines -import pytest - -from app.authentication.auth_payload_version import AuthPayloadVersion -from app.data_models.answer_store import Answer -from app.data_models.list_store import ListStore -from app.questionnaire.location import Location -from app.questionnaire.path_finder import PathFinder -from app.questionnaire.relationship_location import RelationshipLocation -from app.questionnaire.routing_path import RoutingPath -from app.questionnaire.when_rules import ( - evaluate_goto, - evaluate_rule, - evaluate_when_rules, -) -from tests.app.questionnaire.conftest import get_metadata - - -@pytest.mark.parametrize( - "when_rule, answers, expected", - ( - ({"value": "singleAnswer", "condition": "contains"}, ["singleAnswer"], True), - ( - {"value": "firstAnswer", "condition": "equals"}, - ["firstAnswer", "secondAnswer"], - False, - ), - ({"value": False, "condition": "equals"}, False, True), - ({"value": True, "condition": "not equals"}, False, True), - ({"condition": "set"}, "", True), - ({"condition": "set"}, "0", True), - ({"condition": "set"}, "Yes", True), - ({"condition": "set"}, "No", True), - ({"condition": "set"}, 0, True), - ({"condition": "set"}, 1, True), - ({"condition": "set"}, None, False), - ({"condition": "not set"}, None, True), - ({"condition": "not set"}, "", False), - ({"condition": "not set"}, "some text", False), - ({"condition": "not set"}, [], True), - ({"condition": "not set"}, ["123"], False), - ({"condition": "set"}, ["123"], True), - ({"condition": "set"}, [], False), - ({"value": 0, "condition": "equals"}, 2, False), - ({"value": 0, "condition": "equals"}, 0, True), - ({"value": "answervalue", "condition": "equals"}, "answerValue", True), - ({"value": "answervalue", "condition": "equals"}, "answervalue", True), - ({"value": "answervalue", "condition": "equals"}, "answer-value", False), - ({"value": "answervalue", "condition": "not equals"}, "answerValue", False), - ({"value": "answervalue", "condition": "not equals"}, "answervalue", False), - ({"value": "answervalue", "condition": "not equals"}, "answer-value", True), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "equals any"}, - "answerValue", - True, - ), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "equals any"}, - "answervalue", - True, - ), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "equals any"}, - "answer-value", - False, - ), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "not equals any"}, - "answerValue", - False, - ), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "not equals any"}, - "answervalue", - False, - ), - ( - {"value": ["answerValue", "notAnswerValue"], "condition": "not equals any"}, - "answer-value", - True, - ), - ({"value": 0, "condition": "not equals"}, 2, True), - ({"value": 0, "condition": "not equals"}, 0, False), - ({"value": 4, "condition": "greater than or equal to"}, 4, True), - ({"value": 4, "condition": "greater than or equal to"}, 5, True), - ({"value": 4, "condition": "greater than or equal to"}, 3, False), - ({"value": 4, "condition": "greater than or equal to"}, None, False), - ({"value": 4, "condition": "less than or equal to"}, 4, True), - ({"value": 4, "condition": "less than or equal to"}, 3, True), - ({"value": 4, "condition": "less than or equal to"}, 5, False), - ({"value": 4, "condition": "less than or equal to"}, None, False), - ({"value": 5, "condition": "greater than"}, 7, True), - ({"value": 5, "condition": "greater than"}, 5, False), - ({"value": 5, "condition": "greater than"}, 3, False), - ({"value": 5, "condition": "less than"}, 3, True), - ({"value": 5, "condition": "less than"}, 5, False), - ({"value": 5, "condition": "less than"}, 7, False), - ), -) -def test_evaluate_rule(when_rule, answers, expected): - assert evaluate_rule(when_rule, answers) is expected - - -@pytest.mark.parametrize( - "goto, answers, metadata, expected", - ( - ( - { - "id": "next-question", - "when": [{"id": "my_answer", "condition": "equals", "value": "Yes"}], - }, - [{"answer_id": "my_answer", "value": "Yes"}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [{"id": "my_answer", "condition": "equals", "value": "Yes"}], - }, - [{"answer_id": "my_answer", "value": "No"}], - get_metadata(), - False, - ), - ( - { - "id": "next-question", - "when": [ - {"id": "my_answers", "condition": "contains", "value": "answer1"} - ], - }, - [{"answer_id": "my_answer", "value": "No"}], - get_metadata(), - False, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "not contains", - "value": "answer1", - } - ], - }, - [{"answer_id": "my_answer", "value": "No"}], - get_metadata(), - False, - ), - ( - { - "id": "next-question", - "when": [ - {"id": "my_answers", "condition": "contains", "value": "answer1"} - ], - }, - [{"answer_id": "my_answers", "value": ["answer1", "answer2", "answer3"]}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "not contains", - "value": "answer1", - } - ], - }, - [{"answer_id": "my_answers", "value": ["answer2", "answer3"]}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "contains any", - "value": ["answer1", "answer2"], - } - ], - }, - [{"answer_id": "my_answers", "value": ["answer1", "answer4"]}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "contains all", - "value": ["answer1", "answer2"], - } - ], - }, - [{"answer_id": "my_answers", "value": ["answer1", "answer2", "answer3"]}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "equals any", - "values": ["answer1", "answer2"], - } - ], - }, - [{"answer_id": "my_answers", "value": "answer2"}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "not equals any", - "values": ["answer1", "answer2"], - } - ], - }, - [{"answer_id": "my_answers", "value": "answer3"}], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - { - "id": "my_answers", - "condition": "not equals any", - "values": ["answer1", "answer2"], - } - ], - }, - [ - {"answer_id": "my_answer", "value": "Yes"}, - {"answer_id": "my_other_answer", "value": "2"}, - ], - get_metadata(), - True, - ), - ( - { - "id": "next-question", - "when": [ - {"id": "my_answer", "condition": "equals", "value": "Yes"}, - {"id": "my_other_answer", "condition": "equals", "value": "2"}, - ], - }, - [ - {"answer_id": "my_answer", "value": "No"}, - ], - get_metadata(), - False, - ), - ( - { - "id": "next-question", - "when": [ - {"id": "my_answer", "condition": "equals", "value": "Yes"}, - {"condition": "equals", "meta": "sexual_identity", "value": True}, - ], - }, - [ - {"answer_id": "my_answer", "value": "Yes"}, - ], - get_metadata( - { - "version": AuthPayloadVersion.V2, - "survey_metadata": {"data": {"sexual_identity": True}}, - } - ), - True, - ), - ( - { - "id": "next-question", - "when": [ - {"id": "my_answer", "condition": "equals", "value": "Yes"}, - {"condition": "equals", "meta": "sexual_identity", "value": False}, - ], - }, - [ - {"answer_id": "my_answer", "value": "Yes"}, - ], - get_metadata( - { - "version": AuthPayloadVersion.V2, - "survey_metadata": {"data": {"sexual_identity": True}}, - } - ), - False, - ), - ( - { - "id": "next-question", - "when": [ - { - "condition": "equals", - "meta": "variant_flags.does_not_exist.does_not_exist", - "value": True, - } - ], - }, - [ - {"answer_id": "my_answer", "value": "Yes"}, - ], - get_metadata( - {"version": AuthPayloadVersion.V2, "survey_metadata": {"data": {}}} - ), - False, - ), - ), -) -def test_go_to( - goto, - answers, - metadata, - expected, - answer_store, - list_store, - current_location, - questionnaire_schema, -): - for answer in answers: - answer_store.add_or_update(Answer(**answer)) - - assert ( - evaluate_goto( - goto_rule=goto, - schema=questionnaire_schema, - metadata=metadata or {}, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - is expected - ) - - -@pytest.mark.parametrize( - "skip_conditions, answers, expected", - ( - ( - [ - {"when": [{"id": "this", "condition": "equals", "value": "value"}]}, - { - "when": [ - {"id": "that", "condition": "equals", "value": "other value"} - ] - }, - ], - [{"answer_id": "this", "value": "value"}], - True, - ), - ( - [ - {"when": [{"id": "this", "condition": "equals", "value": "value"}]}, - { - "when": [ - {"id": "that", "condition": "equals", "value": "other value"} - ] - }, - ], - [{"answer_id": "that", "value": "other value"}], - True, - ), - ( - [ - {"when": [{"id": "this", "condition": "equals", "value": "value"}]}, - { - "when": [ - {"id": "that", "condition": "equals", "value": "other value"} - ] - }, - ], - [ - {"answer_id": "that", "value": "other value"}, - {"answer_id": "this", "value": "value"}, - ], - True, - ), - ( - [ - {"when": [{"id": "this", "condition": "equals", "value": "value"}]}, - { - "when": [ - {"id": "that", "condition": "equals", "value": "other value"} - ] - }, - ], - [ - {"answer_id": "that", "value": "not correct"}, - {"answer_id": "this", "value": "not correct"}, - ], - False, - ), - ( - None, - [], - False, - ), - ), -) -def test_skip_conditions( - skip_conditions, - answers, - expected, - answer_store, - list_store, - current_location, - questionnaire_schema, - progress_store, -): - for answer in answers: - answer_store.add_or_update(Answer(**answer)) - - path_finder = PathFinder( - questionnaire_schema, - answer_store, - list_store=list_store, - metadata=None, - progress_store=progress_store, - response_metadata={}, - ) - - routing_path_block_ids = [] - - condition = path_finder.evaluate_skip_conditions( - current_location, routing_path_block_ids, skip_conditions, [] - ) - - assert condition is expected - - -@pytest.mark.parametrize( - "when_rules, answers, expected", - (([{"id": "my_answers", "condition": "not set"}], {}, True),), -) -def test_evaluate_not_set_when_rules_should_return_true( - when_rules, - answers, - expected, - answer_store, - list_store, - current_location, - questionnaire_schema, -): - for answer in answers.values(): - answer_store.add_or_update(answer) - - assert ( - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - is expected - ) - - -@pytest.mark.parametrize( - "lhs_answer, comparison, rhs_answer, expected", - ( - ("medium", "equals", "medium", True), - ("medium", "equals", "low", False), - ("medium", "greater than", "low", True), - ("medium", "greater than", "high", False), - ("medium", "less than", "high", True), - ("medium", "less than", "low", False), - ("medium", "equals", "missing_answer", False), - ("list_answer", "contains", "text_answer", True), - ("list_answer", "contains", "other_text_answer", False), - ("list_answer", "not contains", "other_text_answer", True), - ("list_answer", "not contains", "text_answer", False), - ("list_answer", "contains any", "other_list_answer_2", True), - ("list_answer", "contains any", "other_list_answer", False), - ("list_answer", "contains all", "other_list_answer", False), - ("list_answer", "contains all", "other_list_answer_2", True), - ("text_answer", "equals any", "list_answer", True), - ("text_answer", "equals any", "other_list_answer", False), - ("text_answer", "not equals any", "other_list_answer", True), - ("text_answer", "not equals any", "list_answer", False), - ), -) -def test_when_rule_comparing_answer_values( - lhs_answer, - comparison, - rhs_answer, - expected, - answers, - answer_store, - list_store, - questionnaire_schema, - current_location, -): - for answer in answers.values(): - answer_store.add_or_update(answer) - - when = [ - { - "id": answers[lhs_answer].answer_id, - "condition": comparison, - "comparison": {"id": answers[rhs_answer].answer_id, "source": "answers"}, - } - ] - - assert ( - evaluate_when_rules( - when_rules=when, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - routing_path_block_ids=None, - ) - is expected - ) - - -@pytest.mark.parametrize( - "list_item_id, expected", - ( - ("abc123", True), - ("123abc", False), - ), -) -def test_evaluate_when_rule_with_list_item_id( - list_item_id, - expected, - answer_store, - list_store, - questionnaire_schema, - mocker, -): - when_rules = [{"id": "my_answer", "condition": "equals", "value": "an answer"}] - - 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=list_item_id - ) - - schema = mocker.Mock(questionnaire_schema) - schema.is_repeating_answer = mocker.Mock(return_value=True) - - assert ( - evaluate_when_rules( - when_rules=when_rules, - schema=schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - is expected - ) - - -def test_evaluate_when_rule_raises_if_bad_when_condition( - answer_store, list_store, questionnaire_schema, current_location -): - when_rules = [{"condition": "not set"}] - with pytest.raises(Exception): - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - - -@pytest.mark.parametrize( - "when_rules, expected", - ( - ([{"list": "people", "condition": "less than", "value": 2}], True), - ([{"list": "people", "condition": "equals", "value": 1}], True), - ), -) -def test_evaluate_when_rule_with_list_rules( - when_rules, - expected, - answer_store, - questionnaire_schema, - current_location, -): - list_store = ListStore(existing_items=[{"name": "people", "items": ["abcdef"]}]) - - assert ( - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - is expected - ) - - -@pytest.mark.parametrize( - "routing_path, is_on_answer_path, expected", - ( - ( - RoutingPath( - ["test_block_id", "some-block"], - section_id="some-section", - list_name="people", - list_item_id="abc123", - ), - True, - True, - ), - ( - [ - Location( - section_id="some-section", - block_id="test_block_id", - list_name="people", - list_item_id="abc123", - ) - ], - False, - False, - ), - ), -) -def test_routing_answer_on_path_when_in_a_repeat( - routing_path, - is_on_answer_path, - expected, - answer_store, - list_store, - questionnaire_schema, - mocker, -): - when_rules = [{"id": "some-answer", "condition": "equals", "value": "some value"}] - - answer = Answer(answer_id="some-answer", value="some value") - answer_store.add_or_update(answer) - - routing_path = RoutingPath( - ["test_block_id", "some-block"], - section_id="some-section", - list_name="people", - list_item_id="abc123", - ) - - current_location = Location( - section_id="some-section", - block_id="some-block", - list_name="people", - list_item_id="abc123", - ) - - with mocker.patch( - "app.questionnaire.when_rules.get_answer_for_answer_id", return_value=answer - ), mocker.patch( - "app.questionnaire.when_rules._is_answer_on_path", - return_value=is_on_answer_path, - ): - assert ( - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - routing_path_block_ids=routing_path, - ) - is expected - ) - - -def test_routing_ignores_answers_not_on_path( - answer_store, list_store, current_location, questionnaire_schema, mocker -): - when_rules = [{"id": "some-answer", "condition": "equals", "value": "some value"}] - answer_store.add_or_update(Answer(answer_id="some-answer", value="some value")) - - routing_path = [Location(section_id="some-section", block_id="test_block_id")] - - assert evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - - with mocker.patch( - "app.questionnaire.when_rules._is_answer_on_path", return_value=False - ): - assert not ( - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - routing_path_block_ids=routing_path, - ) - ) - - -@pytest.mark.parametrize( - "when_rule_comparison_id, expected", - ( - ( - "list_item_id", - True, - ), - ( - "invalid-location-id", - False, - ), - ), -) -def test_primary_person_checks_location( - when_rule_comparison_id, expected, answer_store, questionnaire_schema -): - list_store = ListStore( - existing_items=[ - { - "name": "people", - "primary_person": "abcdef", - "items": ["abcdef", "12345"], - } - ] - ) - - current_location = RelationshipLocation( - section_id="some-section", - block_id="some-block", - list_item_id="abcdef", - to_list_item_id="12345", - list_name="household", - ) - - when_rules = [ - { - "list": "people", - "id_selector": "primary_person", - "condition": "equals", - "comparison": {"source": "location", "id": when_rule_comparison_id}, - } - ] - - assert ( - evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) - is expected - ) - - -def test_when_rule_returns_first_item_in_list(answer_store, questionnaire_schema): - list_store = ListStore( - existing_items=[{"name": "people", "items": ["abcdef", "12345"]}] - ) - - current_location = Location( - section_id="some-section", - block_id="some-block", - list_name="people", - list_item_id="abcdef", - ) - - when_rules = [ - { - "list": "people", - "id_selector": "first", - "condition": "equals", - "comparison": {"source": "location", "id": "list_item_id"}, - } - ] - - assert evaluate_when_rules( - when_rules=when_rules, - schema=questionnaire_schema, - metadata=None, - answer_store=answer_store, - list_store=list_store, - current_location=current_location, - ) diff --git a/tests/functional/spec/content_variants.spec.js b/tests/functional/spec/content_variants.spec.js index 073e367d5e..e481390e2e 100644 --- a/tests/functional/spec/content_variants.spec.js +++ b/tests/functional/spec/content_variants.spec.js @@ -2,7 +2,7 @@ import ageQuestionBlock from "../generated_pages/variants_content/age-question-b describe("QuestionVariants", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_variants_content.json"); + browser.openQuestionnaire("test_variants_content.json"); }); it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", () => { diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js index 07e952ad97..a8164bf13e 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js @@ -4,7 +4,7 @@ import SubmitPage from "../../../generated_pages/section_enabled_checkbox/submit describe("Feature: Section Enabled Based On Checkbox Answers", () => { beforeEach("Open survey", () => { - browser.openQuestionnaire("test_new_section_enabled_checkbox.json"); + browser.openQuestionnaire("test_section_enabled_checkbox.json"); }); it("When the user selects `Section 2` and submits, Then section 2 should be displayed", () => { diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js index 12e47f5a70..efea6e0b8f 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js @@ -3,7 +3,7 @@ import hubPage from "../../../base_pages/hub.page"; describe("Feature: Section Enabled With Hub", () => { beforeEach("Open survey", () => { - browser.openQuestionnaire("test_new_section_enabled_hub.json"); + browser.openQuestionnaire("test_section_enabled_hub.json"); }); it("When the user selects `Section 2` and submits, Then only section 2 should be displayed on the hub", () => { diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js index b4f31ac356..1f0646312e 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js @@ -3,7 +3,7 @@ import SubmitPage from "../../../generated_pages/section_enabled_radio/submit.pa describe("Feature: Section Enabled Based On Radio Answers", () => { beforeEach("Open survey", () => { - browser.openQuestionnaire("test_new_section_enabled_radio.json"); + browser.openQuestionnaire("test_section_enabled_radio.json"); }); it("When the user answers `Yes, enable section 2` and submits, Then section 2 should be displayed", () => { diff --git a/tests/functional/spec/features/routing/all_in.spec.js b/tests/functional/spec/features/routing/all_in.spec.js index b4b45f96fa..6a52ba73e9 100644 --- a/tests/functional/spec/features/routing/all_in.spec.js +++ b/tests/functional/spec/features/routing/all_in.spec.js @@ -1,12 +1,12 @@ -import CountryCheckboxPage from "../../../generated_pages/new_routing_checkbox_contains_all/country-checkbox.page"; -import CountryInterstitialPage from "../../../generated_pages/new_routing_checkbox_contains_all/country-interstitial-india-and-malta.page"; -import CountryInterstitialOtherPage from "../../../generated_pages/new_routing_checkbox_contains_all/country-interstitial-not-india-and-malta.page"; +import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_all/country-checkbox.page"; +import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-india-and-malta.page"; +import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-not-india-and-malta.page"; describe("Feature: Routing - ALL-IN Operator", () => { describe("Equals", () => { describe("Given I start the ALL-IN operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_checkbox_contains_all.json"); + browser.openQuestionnaire("test_routing_checkbox_contains_all.json"); }); it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", () => { diff --git a/tests/functional/spec/features/routing/and.spec.js b/tests/functional/spec/features/routing/and.spec.js index 37dace104c..40c88a7cdc 100644 --- a/tests/functional/spec/features/routing/and.spec.js +++ b/tests/functional/spec/features/routing/and.spec.js @@ -1,13 +1,13 @@ -import FirstNumberQuestionPage from "../../../generated_pages/new_routing_and/number-question-1.page"; -import SecondNumberQuestionPage from "../../../generated_pages/new_routing_and/number-question-2.page"; -import CorrectAnswerPage from "../../../generated_pages/new_routing_and/correct-answer.page"; -import IncorrectAnswerPage from "../../../generated_pages/new_routing_and/incorrect-answer.page"; +import FirstNumberQuestionPage from "../../../generated_pages/routing_and/number-question-1.page"; +import SecondNumberQuestionPage from "../../../generated_pages/routing_and/number-question-2.page"; +import CorrectAnswerPage from "../../../generated_pages/routing_and/correct-answer.page"; +import IncorrectAnswerPage from "../../../generated_pages/routing_and/incorrect-answer.page"; describe("Feature: Routing - And Operator", () => { describe("Equals", () => { describe("Given I start the and operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_and.json"); + browser.openQuestionnaire("test_routing_and.json"); }); it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", () => { diff --git a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js index bce424e98a..2d92b8e5ce 100644 --- a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js +++ b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js @@ -1,9 +1,9 @@ -import RouteComparison1Page from "../../../generated_pages/new_routing_answer_comparison/route-comparison-1.page.js"; -import RouteComparison2Page from "../../../generated_pages/new_routing_answer_comparison/route-comparison-2.page.js"; +import RouteComparison1Page from "../../../generated_pages/routing_answer_comparison/route-comparison-1.page.js"; +import RouteComparison2Page from "../../../generated_pages/routing_answer_comparison/route-comparison-2.page.js"; describe("Test routing skip", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_answer_comparison.json"); + browser.openQuestionnaire("test_routing_answer_comparison.json"); }); it("Given we start the routing test survey, When we enter a low number then a high number, Then, we should be routed to the fourth page", () => { diff --git a/tests/functional/spec/features/routing/answer_not_on_path.spec.js b/tests/functional/spec/features/routing/answer_not_on_path.spec.js index 20e1fc15b9..faa52c93f0 100644 --- a/tests/functional/spec/features/routing/answer_not_on_path.spec.js +++ b/tests/functional/spec/features/routing/answer_not_on_path.spec.js @@ -1,12 +1,12 @@ -import InitialChoicePage from "../../../generated_pages/new_routing_not_affected_by_answers_not_on_path/initial-choice.page.js"; -import InvalidPathPage from "../../../generated_pages/new_routing_not_affected_by_answers_not_on_path/invalid-path.page.js"; -import InvalidPathInterstitialPage from "../../../generated_pages/new_routing_not_affected_by_answers_not_on_path/invalid-path-interstitial.page.js"; -import ValidPathPage from "../../../generated_pages/new_routing_not_affected_by_answers_not_on_path/valid-path.page.js"; -import ValidFinalInterstitialPage from "../../../generated_pages/new_routing_not_affected_by_answers_not_on_path/valid-final-interstitial.page.js"; +import InitialChoicePage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/initial-choice.page.js"; +import InvalidPathPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/invalid-path.page.js"; +import InvalidPathInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/invalid-path-interstitial.page.js"; +import ValidPathPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-path.page.js"; +import ValidFinalInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-final-interstitial.page.js"; describe("Answers not on path are not considered when routing", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_not_affected_by_answers_not_on_path.json"); + browser.openQuestionnaire("test_routing_not_affected_by_answers_not_on_path.json"); }); it("Given the user enters an answer on the first path, when they return to the second path, they should be routed to the valid path interstitial", () => { diff --git a/tests/functional/spec/features/routing/answered_unanswered.spec.js b/tests/functional/spec/features/routing/answered_unanswered.spec.js index 8044aace3e..f1ecb87ffd 100644 --- a/tests/functional/spec/features/routing/answered_unanswered.spec.js +++ b/tests/functional/spec/features/routing/answered_unanswered.spec.js @@ -1,19 +1,19 @@ -import QuestionOne from "../../../generated_pages/new_routing_answered_unanswered/block-1.page"; -import QuestionOneAnswered from "../../../generated_pages/new_routing_answered_unanswered/answered-question-1.page"; -import QuestionOneUnanswered from "../../../generated_pages/new_routing_answered_unanswered/unanswered-question-1.page"; +import QuestionOne from "../../../generated_pages/routing_answered_unanswered/block-1.page"; +import QuestionOneAnswered from "../../../generated_pages/routing_answered_unanswered/answered-question-1.page"; +import QuestionOneUnanswered from "../../../generated_pages/routing_answered_unanswered/unanswered-question-1.page"; -import QuestionTwo from "../../../generated_pages/new_routing_answered_unanswered/block-2.page"; -import QuestionTwoAnswered from "../../../generated_pages/new_routing_answered_unanswered/answered-question-2.page"; -import QuestionTwoUnanswered from "../../../generated_pages/new_routing_answered_unanswered/unanswered-question-2.page"; +import QuestionTwo from "../../../generated_pages/routing_answered_unanswered/block-2.page"; +import QuestionTwoAnswered from "../../../generated_pages/routing_answered_unanswered/answered-question-2.page"; +import QuestionTwoUnanswered from "../../../generated_pages/routing_answered_unanswered/unanswered-question-2.page"; -import QuestionThree from "../../../generated_pages/new_routing_answered_unanswered/block-3.page"; -import QuestionThreeAnsweredOrNotZero from "../../../generated_pages/new_routing_answered_unanswered/answered-question-3.page"; -import QuestionThreeUnansweredOrAnswerZero from "../../../generated_pages/new_routing_answered_unanswered/unanswered-or-zero-question-3.page"; +import QuestionThree from "../../../generated_pages/routing_answered_unanswered/block-3.page"; +import QuestionThreeAnsweredOrNotZero from "../../../generated_pages/routing_answered_unanswered/answered-question-3.page"; +import QuestionThreeUnansweredOrAnswerZero from "../../../generated_pages/routing_answered_unanswered/unanswered-or-zero-question-3.page"; describe("Test routing question answered/unanswered", () => { describe("Given I am on the first question", () => { beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_new_routing_answered_unanswered.json"); + browser.openQuestionnaire("test_routing_answered_unanswered.json"); }); it("When I select any answer and submit, Then I should see a page saying I have answered the first question", () => { @@ -38,7 +38,7 @@ describe("Test routing question answered/unanswered", () => { describe("Given I am on the second question", () => { beforeEach("Load the questionnaire and get to the second question", () => { - browser.openQuestionnaire("test_new_routing_answered_unanswered.json"); + browser.openQuestionnaire("test_routing_answered_unanswered.json"); $(QuestionOne.submit()).click(); $(QuestionOneUnanswered.submit()).click(); @@ -66,7 +66,7 @@ describe("Test routing question answered/unanswered", () => { describe("Given I am on the third question", () => { beforeEach("Load the questionnaire and get to the third question", () => { - browser.openQuestionnaire("test_new_routing_answered_unanswered.json"); + browser.openQuestionnaire("test_routing_answered_unanswered.json"); $(QuestionOne.submit()).click(); $(QuestionOneUnanswered.submit()).click(); diff --git a/tests/functional/spec/features/routing/any_in.spec.js b/tests/functional/spec/features/routing/any_in.spec.js index 44849820ec..fe1fa9ebf7 100644 --- a/tests/functional/spec/features/routing/any_in.spec.js +++ b/tests/functional/spec/features/routing/any_in.spec.js @@ -1,12 +1,12 @@ -import CountryCheckboxPage from "../../../generated_pages/new_routing_checkbox_contains_any/country-checkbox.page"; -import CountryInterstitialPage from "../../../generated_pages/new_routing_checkbox_contains_any/country-interstitial-india-or-malta-or-both.page"; -import CountryInterstitialOtherPage from "../../../generated_pages/new_routing_checkbox_contains_any/country-interstitial-not-india-or-malta-or-both.page"; +import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_any/country-checkbox.page"; +import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-india-or-malta-or-both.page"; +import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-not-india-or-malta-or-both.page"; describe("Feature: Routing - ANY-IN Operator", () => { describe("Equals", () => { describe("Given I start the ANY-IN operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_checkbox_contains_any.json"); + browser.openQuestionnaire("test_routing_checkbox_contains_any.json"); }); it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", () => { diff --git a/tests/functional/spec/features/routing/checkbox_count.spec.js b/tests/functional/spec/features/routing/checkbox_count.spec.js index 12b36841a6..ed14704b33 100644 --- a/tests/functional/spec/features/routing/checkbox_count.spec.js +++ b/tests/functional/spec/features/routing/checkbox_count.spec.js @@ -1,10 +1,10 @@ -import ToppingCheckboxPage from "../../../generated_pages/new_routing_checkbox_count/topping-checkbox.page.js"; -import CorrectAnswerPage from "../../../generated_pages/new_routing_checkbox_count/correct-answer.page"; -import IncorrectAnswerPage from "../../../generated_pages/new_routing_checkbox_count/incorrect-answer.page"; +import ToppingCheckboxPage from "../../../generated_pages/routing_checkbox_count/topping-checkbox.page.js"; +import CorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/correct-answer.page"; +import IncorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/incorrect-answer.page"; describe("Test routing using count of checkboxes checked", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_checkbox_count.json"); + browser.openQuestionnaire("test_routing_checkbox_count.json"); }); it("Given a user selects 2 checkboxes, When they submit, Then they should be routed to the correct page", () => { diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/features/routing/date.spec.js index 1835c1c000..3d979b9204 100644 --- a/tests/functional/spec/features/routing/date.spec.js +++ b/tests/functional/spec/features/routing/date.spec.js @@ -1,13 +1,13 @@ -import IncorrectAnswerPage from "../../../generated_pages/new_routing_date_equals/incorrect-answer.page.js"; -import CorrectAnswerPage from "../../../generated_pages/new_routing_date_equals/correct-answer.page.js"; +import IncorrectAnswerPage from "../../../generated_pages/routing_date_equals/incorrect-answer.page.js"; +import CorrectAnswerPage from "../../../generated_pages/routing_date_equals/correct-answer.page.js"; -import DateEqualsComparisonQuestionPage from "../../../generated_pages/new_routing_date_equals/comparison-date-block.page"; -import DateEqualsQuestionPage from "../../../generated_pages/new_routing_date_equals/date-question.page"; -import DateNotEqualsQuestionPage from "../../../generated_pages/new_routing_date_not_equals/date-question.page"; -import DateGreaterThanQuestionPage from "../../../generated_pages/new_routing_date_greater_than/date-question.page"; -import DateGreaterThanOrEqualsQuestionPage from "../../../generated_pages/new_routing_date_greater_than_or_equals/date-question.page"; -import DateLessThanQuestionPage from "../../../generated_pages/new_routing_date_less_than/date-question.page"; -import DateLessThanOrEqualsQuestionPage from "../../../generated_pages/new_routing_date_less_than_or_equals/date-question.page"; +import DateEqualsComparisonQuestionPage from "../../../generated_pages/routing_date_equals/comparison-date-block.page"; +import DateEqualsQuestionPage from "../../../generated_pages/routing_date_equals/date-question.page"; +import DateNotEqualsQuestionPage from "../../../generated_pages/routing_date_not_equals/date-question.page"; +import DateGreaterThanQuestionPage from "../../../generated_pages/routing_date_greater_than/date-question.page"; +import DateGreaterThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_greater_than_or_equals/date-question.page"; +import DateLessThanQuestionPage from "../../../generated_pages/routing_date_less_than/date-question.page"; +import DateLessThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_less_than_or_equals/date-question.page"; const today = new Date(); const dayToday = today.getDate(); @@ -30,7 +30,7 @@ describe("Feature: Routing on a Date", () => { describe("Equals", () => { describe("Given I start date routing equals survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_equals.json"); + browser.openQuestionnaire("test_routing_date_equals.json"); $(DateEqualsComparisonQuestionPage.day()).setValue(31); $(DateEqualsComparisonQuestionPage.month()).setValue(3); @@ -107,7 +107,7 @@ describe("Feature: Routing on a Date", () => { describe("Not Equals", () => { describe("Given I start date routing not equals survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_not_equals.json"); + browser.openQuestionnaire("test_routing_date_not_equals.json"); }); it("When I enter a different date to February 2018, Then I should be routed to the correct page", () => { @@ -135,7 +135,7 @@ describe("Feature: Routing on a Date", () => { describe("Greater Than", () => { describe("Given I start date routing greater than survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_greater_than.json"); + browser.openQuestionnaire("test_routing_date_greater_than.json"); }); it("When I enter a date greater than the 1st March 2017, Then I should be routed to the correct page", () => { @@ -176,7 +176,7 @@ describe("Feature: Routing on a Date", () => { describe("Greater Than Or Equals", () => { describe("Given I start date routing greater than or equals survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_greater_than_or_equals.json"); + browser.openQuestionnaire("test_routing_date_greater_than_or_equals.json"); }); it("When I enter a date greater than 2017, Then I should be routed to the correct page", () => { @@ -211,7 +211,7 @@ describe("Feature: Routing on a Date", () => { describe("Less Than", () => { describe("Given I start date routing less than survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_less_than.json"); + browser.openQuestionnaire("test_routing_date_less_than.json"); }); it("When I enter a date less than today, Then I should be routed to the correct page", () => { @@ -252,7 +252,7 @@ describe("Feature: Routing on a Date", () => { describe("Less Than Or Equals", () => { describe("Given I start date routing less than or equals survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_date_less_than_or_equals.json"); + browser.openQuestionnaire("test_routing_date_less_than_or_equals.json"); }); it("When I enter a date less than today, Then I should be routed to the correct page", () => { diff --git a/tests/functional/spec/features/routing/in.spec.js b/tests/functional/spec/features/routing/in.spec.js index d6abcfde89..a0309a730b 100644 --- a/tests/functional/spec/features/routing/in.spec.js +++ b/tests/functional/spec/features/routing/in.spec.js @@ -1,12 +1,12 @@ -import CountryCheckboxPage from "../../../generated_pages/new_routing_checkbox_contains/country-checkbox.page"; -import CountryInterstitialPage from "../../../generated_pages/new_routing_checkbox_contains/country-interstitial-india.page"; -import CountryInterstitialOtherPage from "../../../generated_pages/new_routing_checkbox_contains/country-interstitial-not-india.page"; +import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_in/country-checkbox.page"; +import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-india.page"; +import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-not-india.page"; describe("Feature: Routing - IN Operator", () => { describe("Equals", () => { describe("Given I start the IN operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_checkbox_contains.json"); + browser.openQuestionnaire("test_routing_checkbox_contains_in.json"); }); it("When I do select India, Then I should be routed to the the correct answer interstitial page", () => { diff --git a/tests/functional/spec/features/routing/not.spec.js b/tests/functional/spec/features/routing/not.spec.js index 24748b5a62..53e2f717f1 100644 --- a/tests/functional/spec/features/routing/not.spec.js +++ b/tests/functional/spec/features/routing/not.spec.js @@ -1,12 +1,12 @@ -import CountryCheckboxPage from "../../../generated_pages/new_routing_not/country-checkbox.page"; -import CountryInterstitialPage from "../../../generated_pages/new_routing_not/country-interstitial-not-india.page"; -import IndiaInterstitialPage from "../../../generated_pages/new_routing_not/country-interstitial-india.page"; +import CountryCheckboxPage from "../../../generated_pages/routing_not/country-checkbox.page"; +import CountryInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-not-india.page"; +import IndiaInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-india.page"; describe("Feature: Routing - Not Operator", () => { describe("Equals", () => { describe("Given I start the not operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_not.json"); + browser.openQuestionnaire("test_routing_not.json"); }); it("When I do not select India, Then I should be routed to the not India interstitial page", () => { diff --git a/tests/functional/spec/features/routing/number.spec.js b/tests/functional/spec/features/routing/number.spec.js index cfe000cf9a..cb66532b8e 100644 --- a/tests/functional/spec/features/routing/number.spec.js +++ b/tests/functional/spec/features/routing/number.spec.js @@ -1,12 +1,12 @@ -import NumberQuestionPage from "../../../generated_pages/new_routing_number_equals/number-question.page"; -import CorrectAnswerPage from "../../../generated_pages/new_routing_number_equals/correct-answer.page"; -import IncorrectAnswerPage from "../../../generated_pages/new_routing_number_equals/incorrect-answer.page"; +import NumberQuestionPage from "../../../generated_pages/routing_number_equals/number-question.page"; +import CorrectAnswerPage from "../../../generated_pages/routing_number_equals/correct-answer.page"; +import IncorrectAnswerPage from "../../../generated_pages/routing_number_equals/incorrect-answer.page"; describe("Feature: Routing on a Number", () => { describe("Equals", () => { describe("Given I start number routing equals survey", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_equals.json"); + browser.openQuestionnaire("test_routing_number_equals.json"); }); it("When I enter 123, Then I should be routed to the correct page", () => { @@ -27,7 +27,7 @@ describe("Feature: Routing on a Number", () => { describe("Not Equals", () => { describe("Given I start number routing not equals survey", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_not_equals.json"); + browser.openQuestionnaire("test_routing_number_not_equals.json"); }); it("When I enter a number that isn't 123, Then I should be routed to the correct page", () => { @@ -48,7 +48,7 @@ describe("Feature: Routing on a Number", () => { describe("Greater Than", () => { describe("Given I start number routing greater than survey", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_greater_than.json"); + browser.openQuestionnaire("test_routing_number_greater_than.json"); }); it("When I enter a number greater than 123, Then I should be routed to the correct page", () => { @@ -76,7 +76,7 @@ describe("Feature: Routing on a Number", () => { describe("Less Than", () => { describe("Given I start number routing less than survey", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_less_than.json"); + browser.openQuestionnaire("test_routing_number_less_than.json"); }); it("When I enter a number less than 123, Then I should be routed to the correct page", () => { @@ -104,7 +104,7 @@ describe("Feature: Routing on a Number", () => { describe("Greater Than or Equal", () => { describe("Given I have number routing with a greater than or equal", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_greater_than_or_equal.json"); + browser.openQuestionnaire("test_routing_number_greater_than_or_equal.json"); }); it("When I enter a number greater than 123, Then I should be routed to the correct page", () => { @@ -132,7 +132,7 @@ describe("Feature: Routing on a Number", () => { describe("Less Than or Equal", () => { describe("Given I have number routing with a less than or equal", () => { before(() => { - browser.openQuestionnaire("test_new_routing_number_less_than_or_equal.json"); + browser.openQuestionnaire("test_routing_number_less_than_or_equal.json"); }); it("When I enter a number less than 123, Then I should be routed to the correct page", () => { diff --git a/tests/functional/spec/features/routing/or.spec.js b/tests/functional/spec/features/routing/or.spec.js index bf1a44bb79..83ba0c193d 100644 --- a/tests/functional/spec/features/routing/or.spec.js +++ b/tests/functional/spec/features/routing/or.spec.js @@ -1,13 +1,13 @@ -import FirstNumberQuestionPage from "../../../generated_pages/new_routing_or/number-question-1.page"; -import SecondNumberQuestionPage from "../../../generated_pages/new_routing_or/number-question-2.page"; -import CorrectAnswerPage from "../../../generated_pages/new_routing_or/correct-answer.page"; -import IncorrectAnswerPage from "../../../generated_pages/new_routing_or/incorrect-answer.page"; +import FirstNumberQuestionPage from "../../../generated_pages/routing_or/number-question-1.page"; +import SecondNumberQuestionPage from "../../../generated_pages/routing_or/number-question-2.page"; +import CorrectAnswerPage from "../../../generated_pages/routing_or/correct-answer.page"; +import IncorrectAnswerPage from "../../../generated_pages/routing_or/incorrect-answer.page"; describe("Feature: Routing - OR Operator", () => { describe("Equals", () => { describe("Given I start the or operator routing survey", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_routing_or.json"); + browser.openQuestionnaire("test_routing_or.json"); }); it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", () => { diff --git a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js index acad09c9bc..8491d34257 100644 --- a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js +++ b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js @@ -1,9 +1,9 @@ -import Comparison1Page from "../../../generated_pages/new_skip_condition_answer_comparison/comparison-1.page.js"; -import Comparison2Page from "../../../generated_pages/new_skip_condition_answer_comparison/comparison-2.page.js"; +import Comparison1Page from "../../../generated_pages/skip_condition_answer_comparison/comparison-1.page.js"; +import Comparison2Page from "../../../generated_pages/skip_condition_answer_comparison/comparison-2.page.js"; describe("Test skip condition answer comparisons", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_skip_condition_answer_comparison.json"); + browser.openQuestionnaire("test_skip_condition_answer_comparison.json"); }); it("Given we start the skip condition survey, when we enter the same answers, then the interstitial should show that the answers are the same", () => { diff --git a/tests/functional/spec/question_variants.spec.js b/tests/functional/spec/question_variants.spec.js index caf3e1313c..aaba22dd10 100644 --- a/tests/functional/spec/question_variants.spec.js +++ b/tests/functional/spec/question_variants.spec.js @@ -10,7 +10,7 @@ import secondNumberBlock from "../generated_pages/variants_question/second-numbe describe("QuestionVariants", () => { beforeEach(() => { - browser.openQuestionnaire("test_new_variants_question.json"); + browser.openQuestionnaire("test_variants_question.json"); }); it("Given I am completing the survey, then the correct questions are shown based on my previous answers", () => { diff --git a/tests/functional/spec/question_variants_first_item_in_list.spec.js b/tests/functional/spec/question_variants_first_item_in_list.spec.js index 4a2e5e5f31..c015743d7e 100644 --- a/tests/functional/spec/question_variants_first_item_in_list.spec.js +++ b/tests/functional/spec/question_variants_first_item_in_list.spec.js @@ -5,7 +5,7 @@ import HubPage from "../base_pages/hub.page.js"; describe("Question Variants First Item in List", () => { it("Given I am the first person on the list, When the when rule is set, Then I should the correct question variant", () => { - browser.openQuestionnaire("test_new_variants_first_item_in_list.json"); + browser.openQuestionnaire("test_variants_first_item_in_list.json"); $(HubPage.submit()).click(); $(ListCollectorPage.yes()).click(); $(ListCollectorPage.submit()).click(); @@ -19,7 +19,7 @@ describe("Question Variants First Item in List", () => { }); it("Given I am the second person on the list, When the when rule is set, Then I should the correct question variant", () => { - browser.openQuestionnaire("test_new_variants_first_item_in_list.json"); + browser.openQuestionnaire("test_variants_first_item_in_list.json"); $(HubPage.submit()).click(); $(ListCollectorPage.yes()).click(); $(ListCollectorPage.submit()).click(); diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 5436175690..1bef146fda 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -1,27 +1,27 @@ -import AgePage from "../generated_pages/new_routing_and_skipping_section_dependencies/age.page"; -import HouseHoldPersonalDetailsSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/household-personal-details-section-summary.page"; -import HouseholdSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/household-section-summary.page"; -import ListCollectorAddPage from "../generated_pages/new_routing_and_skipping_section_dependencies/list-collector-add.page"; -import ListCollectorPage from "../generated_pages/new_routing_and_skipping_section_dependencies/list-collector.page"; -import NamePage from "../generated_pages/new_routing_and_skipping_section_dependencies/name-block.page"; -import PrimaryPersonSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/primary-person-summary.page"; -import ReasonNoConfirmationPage from "../generated_pages/new_routing_and_skipping_section_dependencies/reason-no-confirmation.page"; -import RepeatingAgePage from "../generated_pages/new_routing_and_skipping_section_dependencies/repeating-age.page"; -import RepeatingSexPage from "../generated_pages/new_routing_and_skipping_section_dependencies/repeating-sex.page"; -import SecurityPage from "../generated_pages/new_routing_and_skipping_section_dependencies/security.page"; -import SkipAgePage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-age.page"; -import SkipEnableSectionPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-household-section.page"; -import EnableSectionPage from "../generated_pages/new_routing_and_skipping_section_dependencies/enable-section.page"; -import SkipConfirmationPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-confirmation.page"; -import SkipConfirmationSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; -import SkipSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies/skip-section-summary.page"; +import AgePage from "../generated_pages/routing_and_skipping_section_dependencies/age.page"; +import HouseHoldPersonalDetailsSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/household-personal-details-section-summary.page"; +import HouseholdSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/household-section-summary.page"; +import ListCollectorAddPage from "../generated_pages/routing_and_skipping_section_dependencies/list-collector-add.page"; +import ListCollectorPage from "../generated_pages/routing_and_skipping_section_dependencies/list-collector.page"; +import NamePage from "../generated_pages/routing_and_skipping_section_dependencies/name-block.page"; +import PrimaryPersonSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/primary-person-summary.page"; +import ReasonNoConfirmationPage from "../generated_pages/routing_and_skipping_section_dependencies/reason-no-confirmation.page"; +import RepeatingAgePage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-age.page"; +import RepeatingSexPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-sex.page"; +import SecurityPage from "../generated_pages/routing_and_skipping_section_dependencies/security.page"; +import SkipAgePage from "../generated_pages/routing_and_skipping_section_dependencies/skip-age.page"; +import SkipEnableSectionPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-household-section.page"; +import EnableSectionPage from "../generated_pages/routing_and_skipping_section_dependencies/enable-section.page"; +import SkipConfirmationPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation.page"; +import SkipConfirmationSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; +import SkipSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-section-summary.page"; import HubPage from "../base_pages/hub.page"; describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire", () => { beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I answer 'No' to skipping the age question, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { @@ -126,7 +126,7 @@ describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire", () => { beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", () => { answerNoToSkipEnableQuestionAndYesToEnableSection(); @@ -142,7 +142,7 @@ describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire and I answered 'No' to skipping the section question and 'Yes' to enable the section question", () => { before("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I change my answer to skipping the section question to 'No', Then the household summary will not be visible on the hub", () => { answerNoToSkipEnableQuestionAndYesToEnableSection(); @@ -154,7 +154,7 @@ describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section", () => { before("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I change my answer to skipping age to 'No', removing the 'are you sure' question from the path, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { @@ -182,7 +182,7 @@ describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and complete the Primary Person section", () => { before("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I change my answer to skipping age to 'No', Then the Primary Person section status is changed to Partially completed", () => { @@ -208,7 +208,7 @@ describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and add 2 household members but complete only one", () => { before("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); it("When I change my answer to skipping age to 'No', Then the completed household member status is changed to Partially completed and the other stays as not started", () => { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 6c8ff0fdf4..d90c3e3fc8 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -1,17 +1,17 @@ -import CalculatedSummarySectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/calculated-summary-section-summary.page"; -import CurrencyTotalPlaybackPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/currency-total-playback.page"; -import DependentQuestionSectionSummaryPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/dependent-question-section-summary.page"; -import FirstQuestionBlockPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/first-question-block.page"; -import FruitPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; -import SecondQuestionBlockPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; -import VegetablesPage from "../generated_pages/new_routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; +import CalculatedSummarySectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/calculated-summary-section-summary.page"; +import CurrencyTotalPlaybackPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/currency-total-playback.page"; +import DependentQuestionSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/dependent-question-section-summary.page"; +import FirstQuestionBlockPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/first-question-block.page"; +import FruitPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; +import SecondQuestionBlockPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; +import VegetablesPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; import HubPage from "../base_pages/hub.page"; describe("Routing and skipping section dependencies based on calculated summaries", () => { describe("Given the section dependencies based on a calculated summary questionnaire", () => { beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_routing_and_skipping_section_dependencies_calculated_summary.json"); + browser.openQuestionnaire("test_routing_and_skipping_section_dependencies_calculated_summary.json"); }); it("When the calculated summary total has not been set, Then the dependent section should not be enabled", () => { diff --git a/tests/functional/spec/skip_condition_block.spec.js b/tests/functional/spec/skip_condition_block.spec.js index f2ca43a24e..720bbf9283 100644 --- a/tests/functional/spec/skip_condition_block.spec.js +++ b/tests/functional/spec/skip_condition_block.spec.js @@ -3,7 +3,7 @@ import SkipPage from "../generated_pages/skip_condition_block/should-skip.page"; import SubmitPage from "../generated_pages/skip_condition_block/submit.page"; describe("Skip Conditions - Block", () => { - const schema = "test_new_skip_condition_block.json"; + const schema = "test_skip_condition_block.json"; describe("Given I am completing the test skip condition block survey,", () => { beforeEach("load the survey", () => { diff --git a/tests/functional/spec/skip_condition_group.spec.js b/tests/functional/spec/skip_condition_group.spec.js index b070009ee1..2847bb7d2e 100644 --- a/tests/functional/spec/skip_condition_group.spec.js +++ b/tests/functional/spec/skip_condition_group.spec.js @@ -3,7 +3,7 @@ import SkipPage from "../generated_pages/skip_condition_group/should-skip.page"; import SubmitPage from "../generated_pages/skip_condition_group/submit.page"; describe("Skip Conditions - Group", () => { - const schema = "test_new_skip_condition_group.json"; + const schema = "test_skip_condition_group.json"; describe("Given I am completing the test skip condition group survey,", () => { beforeEach("load the survey", () => { diff --git a/tests/functional/spec/skip_conditions_not_set.spec.js b/tests/functional/spec/skip_conditions_not_set.spec.js index 50d808861c..a735460a18 100644 --- a/tests/functional/spec/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/skip_conditions_not_set.spec.js @@ -1,10 +1,10 @@ -import FoodPage from "../generated_pages/new_skip_condition_not_set/food-block.page"; -import DrinkPage from "../generated_pages/new_skip_condition_not_set/drink-block.page"; -import SubmitPage from "../generated_pages/new_skip_condition_not_set/submit.page"; +import FoodPage from "../generated_pages/skip_condition_not_set/food-block.page"; +import DrinkPage from "../generated_pages/skip_condition_not_set/drink-block.page"; +import SubmitPage from "../generated_pages/skip_condition_not_set/submit.page"; describe("Skip Conditions - Not Set", () => { beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_skip_condition_not_set.json"); + browser.openQuestionnaire("test_skip_condition_not_set.json"); }); it("Given I do not complete the first page, Then I should see the summary page", () => { diff --git a/tests/functional/spec/skip_conditions_set.spec.js b/tests/functional/spec/skip_conditions_set.spec.js index 69b0aba68f..61d579a927 100644 --- a/tests/functional/spec/skip_conditions_set.spec.js +++ b/tests/functional/spec/skip_conditions_set.spec.js @@ -1,10 +1,10 @@ -import FoodPage from "../generated_pages/new_skip_condition_set/food-block.page"; -import DrinkPage from "../generated_pages/new_skip_condition_set/drink-block.page"; -import SubmitPage from "../generated_pages/new_skip_condition_set/submit.page"; +import FoodPage from "../generated_pages/skip_condition_set/food-block.page"; +import DrinkPage from "../generated_pages/skip_condition_set/drink-block.page"; +import SubmitPage from "../generated_pages/skip_condition_set/submit.page"; describe("Skip Conditions - Set", () => { beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_new_skip_condition_set.json"); + browser.openQuestionnaire("test_skip_condition_set.json"); }); it("Given I complete the first page, Then I should see the summary page", () => { diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index a7440d65c3..adb4eb8ffd 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -158,7 +158,7 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): def test_hub_section_required_but_enabled_false(self): # Given the hub is enabled and there are two required sections - self.launchSurvey("test_new_hub_section_required_and_enabled") + self.launchSurvey("test_hub_section_required_and_enabled") # When I answer 'No' to the first section, meaning the second section is not enabled self.post({"household-relationships-answer": "No"}) @@ -170,7 +170,7 @@ def test_hub_section_required_but_enabled_false(self): def test_hub_section_required_but_enabled_true(self): # Given the hub is enabled and there are two required sections - self.launchSurvey("test_new_hub_section_required_and_enabled") + self.launchSurvey("test_hub_section_required_and_enabled") # When I answer 'Yes' to the first section, meaning the second section is enabled self.post({"household-relationships-answer": "Yes"}) diff --git a/tests/integration/questionnaire/test_questionnaire_question_variants.py b/tests/integration/questionnaire/test_questionnaire_question_variants.py index d09eefcb9d..1c8d2b9e11 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_variants.py +++ b/tests/integration/questionnaire/test_questionnaire_question_variants.py @@ -16,18 +16,8 @@ def test_proxy_answer_shows_proxy_title(self): self.complete_first_section(proxy=True) - def test_new_non_proxy_answer_shows_non_proxy_title(self): - self.launchSurvey("test_new_variants_question") - - self.complete_first_section(proxy=False) - - def test_new_proxy_answer_shows_proxy_title(self): - self.launchSurvey("test_new_variants_question") - - self.complete_first_section(proxy=True) - - def test_new_summaries_proxy(self): - self.launchSurvey("test_new_variants_question") + def test_summaries_proxy(self): + self.launchSurvey("test_variants_question") self.complete_first_section(proxy=True) diff --git a/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py b/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py index 0cbc80a545..39e7db797c 100644 --- a/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py +++ b/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py @@ -12,7 +12,7 @@ class TestRoutingToQuestionnaireEndSingleSection(TestRoutingToQuestionnaireEndBa def test_able_to_route_to_questionnaire_end(self): # Given I launch a questionnaire with a single section and answer "No" to the first question self._launch_and_complete_questionnaire( - "test_new_routing_to_questionnaire_end_single_section" + "test_routing_to_questionnaire_end_single_section" ) # Then I should be routed to the end of the questionnaire and be shown the submit page @@ -25,7 +25,7 @@ def test_able_to_route_to_questionnaire_end(self): # Given I launch a questionnaire with multiple sections # When I answer "No" to the first question self._launch_and_complete_questionnaire( - "test_new_routing_to_questionnaire_end_multiple_sections" + "test_routing_to_questionnaire_end_multiple_sections" ) # Then I should be routed to the end of the questionnaire and be shown the submit page with only 1 section @@ -39,7 +39,7 @@ def test_section_is_reenabled_when_changing_answer_after_routing_to_questionnair ): # Given I am able to route to the questionnaire end by completing only section 1 self._launch_and_complete_questionnaire( - "test_new_routing_to_questionnaire_end_multiple_sections" + "test_routing_to_questionnaire_end_multiple_sections" ) self.assertInUrl(SUBMIT_URL_PATH) diff --git a/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py b/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py index 2cd5bc6b9d..3293567457 100644 --- a/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py +++ b/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py @@ -6,7 +6,7 @@ class TestRoutingToSectionEnd(IntegrationTestCase): def test_section_summary_not_available_if_any_question_in_section_incomplete(self): # Given I launch questionnaire and have not answered questions for a section - self.launchSurvey("test_new_routing_to_section_end") + self.launchSurvey("test_routing_to_section_end") # When I try access the section summary self.get(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) @@ -16,14 +16,14 @@ def test_section_summary_not_available_if_any_question_in_section_incomplete(sel def test_section_summary_available_after_completing_section(self): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_new_routing_to_section_end") + self.launchSurvey("test_routing_to_section_end") self.post({"test-answer": "No"}) self.assertInBody("Were you forced to complete section 1?") self.assertInUrl(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) def test_section_summary_not_available_after_invalidating_section(self): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_new_routing_to_section_end") + self.launchSurvey("test_routing_to_section_end") self.post({"test-answer": "No"}) self.assertInBody("Were you forced to complete section 1?") self.assertInUrl(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) @@ -42,7 +42,7 @@ def test_section_summary_available_after_completing_section_new_routing_engine( self, ): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_new_routing_number_equals") + self.launchSurvey("test_routing_number_equals") self.post({"answer": "123"}) self.post() self.assertInBody("Check your answers and submit") diff --git a/tests/integration/questionnaire/test_questionnaire_submit.py b/tests/integration/questionnaire/test_questionnaire_submit.py index da2f60860e..4e6d06b53e 100644 --- a/tests/integration/questionnaire/test_questionnaire_submit.py +++ b/tests/integration/questionnaire/test_questionnaire_submit.py @@ -37,8 +37,8 @@ def test_invalid_block_once_questionnaire_complete_raises_404(self): def test_submit_page_not_available_after_invalidating_section(self): # Given I launch and complete the questionnaire for schema in [ - "test_new_routing_to_questionnaire_end_single_section", - "test_new_routing_to_questionnaire_end_multiple_sections", + "test_routing_to_questionnaire_end_single_section", + "test_routing_to_questionnaire_end_multiple_sections", ]: with self.subTest(schema=schema): self.launchSurvey(schema) @@ -98,7 +98,7 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest self, ): # Given a partially completed questionnaire - self.launchSurvey("test_new_routing_to_questionnaire_end_single_section") + self.launchSurvey("test_routing_to_questionnaire_end_single_section") self.post({"test-answer": "Yes"}) # When I make a GET or POST request to the submit page @@ -111,7 +111,7 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest def test_is_displayed(self): # Given I launch a questionnaire - self.launchSurvey("test_new_routing_to_questionnaire_end_multiple_sections") + self.launchSurvey("test_routing_to_questionnaire_end_multiple_sections") # When I complete the questionnaire self.post({"test-answer": "Yes"}) diff --git a/tests/integration/routing/test_answer_comparison.py b/tests/integration/routing/test_answer_comparison.py index ff80afce59..cf4720c42b 100644 --- a/tests/integration/routing/test_answer_comparison.py +++ b/tests/integration/routing/test_answer_comparison.py @@ -8,7 +8,7 @@ class TestAnswerComparisonsSkips(IntegrationTestCase): """ def test_skip_condition_answer_comparison(self): - self.launchSurvey("test_new_skip_condition_answer_comparison") + self.launchSurvey("test_skip_condition_answer_comparison") self.post(action="start_questionnaire") @@ -42,7 +42,7 @@ class TestAnswerComparisonsRoutes(IntegrationTestCase): """ def test_routes_over_interstitial(self): - self.launchSurvey("test_new_routing_answer_comparison") + self.launchSurvey("test_routing_answer_comparison") self.post(action="start_questionnaire") diff --git a/tests/integration/routing/test_routing_case_insensitive.py b/tests/integration/routing/test_routing_case_insensitive.py index d227b4addc..9b093a510a 100644 --- a/tests/integration/routing/test_routing_case_insensitive.py +++ b/tests/integration/routing/test_routing_case_insensitive.py @@ -7,7 +7,7 @@ class TestCaseInsensitiveRouting(IntegrationTestCase): """ def test_routing_equals_any_case_insensitive(self): - self.launchSurvey("test_new_routing_case_insensitive_text_field") + self.launchSurvey("test_routing_case_insensitive_text_field") # Given I launch the case insensiitive routing rules schema self.post(action="start_questionnaire") @@ -19,7 +19,7 @@ def test_routing_equals_any_case_insensitive(self): self.assertInBody("You submitted India or Azerbaijan.") def test_routing_equals_case_insensitive(self): - self.launchSurvey("test_new_routing_case_insensitive_text_field") + self.launchSurvey("test_routing_case_insensitive_text_field") # Given I launch the case insensiitive routing rules schema self.post(action="start_questionnaire") diff --git a/tests/integration/session/test_sign_out_and_exit.py b/tests/integration/session/test_sign_out_and_exit.py index 7555690ea9..93fdf51a3c 100644 --- a/tests/integration/session/test_sign_out_and_exit.py +++ b/tests/integration/session/test_sign_out_and_exit.py @@ -81,7 +81,7 @@ def _launch_and_submit_questionnaire(self, schema, **kwargs): def test_redirects_to_account_service_log_out_url_using_base_url_from_claims(self): self._launch_and_submit_questionnaire( - schema="test_new_hub_section_required_and_enabled", + schema="test_hub_section_required_and_enabled", account_service_url=ACCOUNT_SERVICE_BASE_URL, ) self.signOut() From 9f6d1c540506d350d8eed12a35233506d79b7ea3 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Tue, 14 Mar 2023 11:11:21 +0000 Subject: [PATCH 186/567] Update Node to 18.14.0 and WebdriverIO to Version 8 (#1040) Co-authored-by: Hajara Iyal --- .github/workflows/pull_request.yml | 6 +- .nvmrc | 2 +- README.md | 2 +- package.json | 16 +- tests/functional/helpers.js | 6 +- ...edirect_to_list_add_block_checkbox.spec.js | 78 +- ...n_redirect_to_list_add_block_radio.spec.js | 78 +- .../functional/spec/census_thank_you.spec.js | 14 +- tests/functional/spec/checkbox.spec.js | 176 +- .../spec/components/address/address.spec.js | 98 +- .../checkbox/checkbox_detail_answer.spec.js | 50 +- .../checkbox_detail_answer_dropdown.spec.js | 104 +- .../checkbox_detail_answer_multiple.spec.js | 108 +- .../checkbox_detail_answer_numeric.spec.js | 78 +- .../checkbox/checkbox_label.spec.js | 46 +- .../mutually_exclusive_checkbox.spec.js | 126 +- .../mutually_exclusive_currency.spec.js | 83 +- ...ally_exclusive_day_month_year_date.spec.js | 121 +- .../mutually_exclusive_duration.spec.js | 103 +- ...mutually_exclusive_month_year_date.spec.js | 101 +- ...tually_exclusive_multiple_checkbox.spec.js | 234 +- ...usive_multiple_day_month_year_date.spec.js | 234 +- ...ually_exclusive_multiple_textfield.spec.js | 187 +- .../mutually_exclusive_number.spec.js | 83 +- .../mutually_exclusive_percentage.spec.js | 84 +- .../mutually_exclusive_textarea.spec.js | 47 +- .../mutually_exclusive_textfield.spec.js | 83 +- .../mutually_exclusive_unit.spec.js | 83 +- .../mutually_exclusive_year_date.spec.js | 83 +- .../spec/components/dropdown/dropdown.spec.js | 114 +- .../functional/spec/components/radio/radio.js | 128 +- .../radio_detail_answer_dropdown.spec.js | 90 +- .../radio_detail_answer_multiple.spec.js | 86 +- .../radio/radio_detail_answer_numeric.spec.js | 76 +- .../radio/radio_visible_answers.spec.js | 34 +- .../radio/radio_voluntary_answers.spec.js | 46 +- .../spec/conditional_combined_routing.spec.js | 36 +- .../spec/confirmation_email.spec.js | 142 +- .../functional/spec/content_variants.spec.js | 20 +- tests/functional/spec/cookie_banner.spec.js | 38 +- .../spec/custom_page_titles.spec.js | 120 +- tests/functional/spec/dates.spec.js | 254 +- tests/functional/spec/dob_date.spec.js | 28 +- tests/functional/spec/durations.spec.js | 150 +- tests/functional/spec/error_messages.spec.js | 164 +- tests/functional/spec/exit.spec.js | 40 +- .../features/calculated_summary_test_case.js | 412 +- .../conditional_checkbox_title.spec.js | 54 +- .../features/confirmation_question.spec.js | 44 +- ..._question_within_repeating_section.spec.js | 68 +- .../default_value/default_value.spec.js | 50 +- .../dynamic_answer_options/function_driven.js | 224 +- .../function_driven_mandatory.js | 64 +- .../radio_options_from_checkbox_answers.js | 62 +- .../enabled_section_checkbox.spec.js | 46 +- .../enabled_section_hub.spec.js | 58 +- .../enabled_section_radio.spec.js | 42 +- .../choose_another_section.spec.js | 40 +- .../hub_and_spoke/hub_and_spoke.spec.js | 350 +- .../hub_and_spoke_custom_content.spec.js | 36 +- .../hub_and_spoke_required_enable.spec.js | 24 +- .../features/hub_and_spoke/previous.spec.js | 34 +- .../last_viewed_guidance.spec.js | 54 +- .../last_viewed_guidance_hub.spec.js | 133 +- ...lculated_summary_repeating_section.spec.js | 612 +-- ...option_based_on_first_item_in_list.spec.js | 84 +- .../placeholder_date_difference.spec.js | 70 +- .../placeholder_date_ranges.spec.js | 46 +- .../placeholder_default_value.spec.js | 40 +- .../placeholder/placeholder_metadata.spec.js | 18 +- ...laceholder_option_label_from_value.spec.js | 24 +- .../placeholder/playback_confirmation.spec.js | 16 +- .../custom_question_summary.spec.js | 46 +- ...eating_sections_with_hub_and_spoke.spec.js | 380 +- .../spec/features/routing/all_in.spec.js | 30 +- .../spec/features/routing/and.spec.js | 52 +- .../routing/answer_comparison_routing.spec.js | 40 +- .../routing/answer_not_on_path.spec.js | 32 +- .../routing/answered_unanswered.spec.js | 114 +- .../spec/features/routing/any_in.spec.js | 30 +- .../features/routing/checkbox_count.spec.js | 48 +- .../spec/features/routing/date.spec.js | 309 +- .../spec/features/routing/in.spec.js | 20 +- .../spec/features/routing/not.spec.js | 20 +- .../spec/features/routing/number.spec.js | 172 +- .../spec/features/routing/or.spec.js | 52 +- .../routing/removes_completed_block.spec.js | 14 +- .../section_summary/section_summary.spec.js | 240 +- ...section_summary_repeating_sections.spec.js | 68 +- ...show_section_summary_on_completion.spec.js | 42 +- .../answer_comparison_skip_conditions.spec.js | 40 +- .../features/submit_page/submit_page.spec.js | 24 +- .../submit_page_collapsible_summary.spec.js | 44 +- ...submit_page_custom_submission_text.spec.js | 18 +- .../submit_page/submit_page_summary.spec.js | 142 +- ...bmit_with_summary_return_to_answer.spec.js | 102 +- tests/functional/spec/features/units.spec.js | 62 +- .../date-combined-mm-yyyy.spec.js | 120 +- .../date-combined-yyyy.spec.js | 54 +- .../date_validation/date-combined.spec.js | 90 +- .../date_validation/date-range.spec.js | 112 +- .../date_validation/date-single.spec.js | 94 +- .../features/validation/sum/equal.spec.js | 72 +- .../validation/sum/equal_multiple.spec.js | 64 +- .../validation/sum/equal_or_less_than.spec.js | 94 +- ...qual_total_in_separate_section_hub.spec.js | 132 +- ...otal_in_separate_section_repeating.spec.js | 213 +- .../features/validation/sum/less_than.spec.js | 76 +- .../validation/sum/value_source.spec.js | 150 +- .../view_submitted_response.spec.js | 58 +- tests/functional/spec/feedback.spec.js | 64 +- .../spec/interstitial_definition.spec.js | 26 +- .../functional/spec/interviewer_note.spec.js | 30 +- tests/functional/spec/introduction.spec.js | 28 +- tests/functional/spec/language_code.spec.js | 226 +- tests/functional/spec/list_collector.spec.js | 330 +- .../list_collector_driving_question.spec.js | 70 +- ...ollector_driving_question_checkbox.spec.js | 114 +- .../list_collector_primary_person.spec.js | 158 +- .../list_collector_section_summary.spec.js | 486 +- .../spec/list_collector_variants.spec.js | 112 +- ..._collector_variants_primary_person.spec.js | 160 +- tests/functional/spec/mobile_number.spec.js | 28 +- .../functional/spec/multiple_answers.spec.js | 126 +- tests/functional/spec/multiple_piping.spec.js | 38 +- .../spec/my_account_header_link.spec.js | 9 +- tests/functional/spec/numbers.spec.js | 140 +- tests/functional/spec/page_layout.spec.js | 8 +- .../spec/percentage_decimal.spec.js | 32 +- tests/functional/spec/preview.spec.js | 94 +- .../spec/question_definitions.spec.js | 22 +- .../spec/question_description.spec.js | 6 +- .../functional/spec/question_variants.spec.js | 62 +- ...estion_variants_first_item_in_list.spec.js | 58 +- .../spec/radio_checkbox_descriptions.spec.js | 24 +- ...tional_with_detail_answer_optional.spec.js | 48 +- .../spec/relationships-unrelated.spec.js | 123 +- tests/functional/spec/relationships.spec.js | 309 +- .../spec/relationships_primary.spec.js | 112 +- ..._and_skipping_section_dependencies.spec.js | 464 +- ...on_dependencies_calculated_summary.spec.js | 188 +- .../spec/routing_checkbox_contains.spec.js | 70 +- tests/functional/spec/save_sign_out.spec.js | 80 +- .../spec/skip_condition_block.spec.js | 20 +- .../spec/skip_condition_group.spec.js | 20 +- .../spec/skip_condition_list.spec.js | 96 +- .../spec/skip_conditions_not_set.spec.js | 18 +- .../spec/skip_conditions_set.spec.js | 18 +- ...submit_with_custom_submission_text.spec.js | 18 +- tests/functional/spec/textarea.spec.js | 54 +- tests/functional/spec/textfield.spec.js | 34 +- .../spec/textfield_suggestions.spec.js | 42 +- tests/functional/spec/thank_you.spec.js | 78 +- tests/functional/spec/theme_beis.spec.js | 10 +- tests/functional/spec/theme_beis_ni.spec.js | 12 +- .../spec/theme_northernireland.spec.js | 10 +- tests/functional/spec/theme_orr.spec.js | 10 +- .../functional/spec/timeout/timeout_modal.js | 46 +- .../timeout_modal_expired.spec.js | 4 +- ...eout_modal_post_submission_expired.spec.js | 8 +- .../timeout_modal_extended.spec.js | 4 +- ...out_modal_post_submission_extended.spec.js | 8 +- .../timeout_modal_extended_new_window.spec.js | 4 +- ...ost_submission_extended_new_window.spec.js | 8 +- tests/functional/wdio.conf.js | 16 +- yarn.lock | 4638 +++++++++-------- 166 files changed, 9828 insertions(+), 9448 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5b665a1bdc..4125b82418 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: "14.19.0" + node-version: "18.14.0" - name: Get yarn cache id: get-yarn-cache run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT @@ -59,7 +59,7 @@ jobs: cache: 'pipenv' - uses: actions/setup-node@v3 with: - node-version: "14.19.0" + node-version: "18.14.0" - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv @@ -144,7 +144,7 @@ jobs: sudo apt install ./google-chrome-stable_current_amd64.deb - uses: actions/setup-node@v3 with: - node-version: "14.19.0" + node-version: "18.14.0" - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - uses: actions/setup-python@v4 diff --git a/.nvmrc b/.nvmrc index 7b16f790a7..49991d30ce 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v14.19.0 +v18.14.0 diff --git a/README.md b/README.md index b06253250b..6539948d3c 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ yarn test_functional This can be limited to a single spec using: ``` shell -yarn test_functional --spec save_sign_out.spec.js +yarn test_functional --spec ./tests/functional/spec/exit.spec.js ``` To run a single test, add `.only` into the name of any `describe` or `it` function: diff --git a/package.json b/package.json index b4ef793dfc..09115a0402 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "generate_pages": "rm -rf ./tests/functional/generated_pages && pipenv run python -m tests.functional.generate_pages schemas/test/en/ ./tests/functional/generated_pages -r '../../base_pages'", "format": "yarn format:tests && yarn format:test-schemas", "format:tests": "prettier \"tests/functional/**/*.js\" --write && eslint --fix \"tests/functional/**/*.js\"", - "format:test-schemas": "prettier \"schemas/test/*/*.json\" --write && eslint --fix \"schemas/test/*/*.json\"" + "format:test-schemas": "prettier \"schemas/test/*/*.json\" --write && eslint --fix \"schemas/test/*/*.json\"", + "wdio": "wdio run ./wdio.conf.js" }, "devDependencies": { "@babel/core": "^7.17.5", @@ -27,11 +28,10 @@ "@babel/preset-env": "^7.16.11", "@babel/register": "^7.17.0", "@babel/runtime": "^7.17.2", - "@wdio/cli": "^7.16.16", - "@wdio/local-runner": "^7.16.16", - "@wdio/mocha-framework": "^7.16.15", - "@wdio/spec-reporter": "^7.16.14", - "@wdio/sync": "^7.16.16", + "@wdio/cli": "^8.3.10", + "@wdio/local-runner": "^8.3.10", + "@wdio/mocha-framework": "^8.3.0", + "@wdio/spec-reporter": "^8.3.0", "chai": "^4.3.6", "chromedriver": "^110.0.0", "eslint": "^8.10.0", @@ -52,8 +52,8 @@ "prettier": "^2.5.1", "typescript": "^4.7.4", "uuid": "^8.3.2", - "wdio-chromedriver-service": "^7.2.8", - "webdriverio": "^7.17.4" + "wdio-chromedriver-service": "^8.1.1", + "webdriverio": "8.3.10" }, "prettier": {} } diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 84148d5406..bde74f09d4 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -1,8 +1,8 @@ -const checkPeopleInList = (peopleExpected, listLabel) => { - $(listLabel(1)).waitForDisplayed(); +const checkPeopleInList = async (peopleExpected, listLabel) => { + await $(listLabel(1)).waitForDisplayed(); for (let i = 1; i <= peopleExpected.length; i++) { - expect($(listLabel(i)).getText()).to.equal(peopleExpected[i - 1]); + await expect(await $(listLabel(i)).getText()).to.equal(peopleExpected[i - 1]); } }; diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js index d8efda5043..06cac9a8ac 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -6,66 +6,66 @@ import AnyoneUsuallyLiveAt from "../generated_pages/answer_action_redirect_to_li describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { describe('Given the user is on a question with a "RedirectToListAddBlock" action enabled', () => { - before("Launch survey", () => { - browser.openQuestionnaire("test_answer_action_redirect_to_list_add_block_checkbox.json"); + before("Launch survey", async () => { + await browser.openQuestionnaire("test_answer_action_redirect_to_list_add_block_checkbox.json"); }); - it('When the user selects "No", Then, they should be taken to the list collector.', () => { - $(AnyoneUsuallyLiveAt.no()).click(); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it('When the user selects "No", Then, they should be taken to the list collector.', async () => { + await $(AnyoneUsuallyLiveAt.no()).click(); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); - it('When the user selects "Yes" then they should be taken to the list collector add question.', () => { - browser.url(AnyoneUsuallyLiveAt.url()); - $(AnyoneUsuallyLiveAt.iThinkSo()).click(); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); - expect(browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); + it('When the user selects "Yes" then they should be taken to the list collector add question.', async () => { + await browser.url(AnyoneUsuallyLiveAt.url()); + await $(AnyoneUsuallyLiveAt.iThinkSo()).click(); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); }); - it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', () => { - $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { + await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); }); - it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", () => { - $(AnyoneUsuallyLiveAt.submit()).click(); - $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); - $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - $(AnyoneLiveAtListCollectorAddPage.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { + await $(AnyoneUsuallyLiveAt.submit()).click(); + await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); + await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); + await $(AnyoneLiveAtListCollectorAddPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; checkPeopleInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); - it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', () => { - $(AnyoneLiveAtListCollector.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { + await $(AnyoneLiveAtListCollector.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); }); - it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", () => { - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); - it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", () => { - $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); - $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); - $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - expect($(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; + it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { + await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); + await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); + await $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; }); - it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", () => { - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - $(AnyoneLiveAtListCollector.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await $(AnyoneLiveAtListCollector.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js index 9a4c6a942a..892f882d9b 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js @@ -6,66 +6,66 @@ import AnyoneUsuallyLiveAt from "../generated_pages/answer_action_redirect_to_li describe("Answer Action: Redirect To List Add Question (Radio)", () => { describe('Given the user is on a question with a "RedirectToListAddBlock" action enabled', () => { - before("Launch survey", () => { - browser.openQuestionnaire("test_answer_action_redirect_to_list_add_block_radio.json"); + before("Launch survey", async () => { + await browser.openQuestionnaire("test_answer_action_redirect_to_list_add_block_radio.json"); }); - it('When the user answers "No", Then, they should be taken to straight the list collector.', () => { - $(AnyoneUsuallyLiveAt.no()).click(); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it('When the user answers "No", Then, they should be taken to straight the list collector.', async () => { + await $(AnyoneUsuallyLiveAt.no()).click(); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); - it('When the user answers "Yes" then they should be taken to the list collector add question.', () => { - browser.url(AnyoneUsuallyLiveAt.url()); - $(AnyoneUsuallyLiveAt.yes()).click(); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); - expect(browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); + it('When the user answers "Yes" then they should be taken to the list collector add question.', async () => { + await browser.url(AnyoneUsuallyLiveAt.url()); + await $(AnyoneUsuallyLiveAt.yes()).click(); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); }); - it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', () => { - $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { + await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); }); - it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", () => { - $(AnyoneUsuallyLiveAt.submit()).click(); - $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); - $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - $(AnyoneLiveAtListCollectorAddPage.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { + await $(AnyoneUsuallyLiveAt.submit()).click(); + await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); + await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); + await $(AnyoneLiveAtListCollectorAddPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; checkPeopleInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); - it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', () => { - $(AnyoneLiveAtListCollector.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { + await $(AnyoneLiveAtListCollector.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); }); - it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", () => { - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); - it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", () => { - $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); - $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); - $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - expect($(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; + it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { + await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); + await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); + await $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; }); - it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", () => { - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - $(AnyoneLiveAtListCollector.previous()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await $(AnyoneLiveAtListCollector.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); - $(AnyoneUsuallyLiveAt.submit()).click(); - expect(browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await $(AnyoneUsuallyLiveAt.submit()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/census_thank_you.spec.js b/tests/functional/spec/census_thank_you.spec.js index 62d643d5c9..e939bd6aa5 100644 --- a/tests/functional/spec/census_thank_you.spec.js +++ b/tests/functional/spec/census_thank_you.spec.js @@ -5,15 +5,15 @@ import ThankYouPage from "../base_pages/thank-you.page"; describe("Thank You Census Household", () => { describe("Given I launch a census schema without feedback enabled", () => { - beforeEach(() => { - browser.openQuestionnaire("test_thank_you_census_household.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_thank_you_census_household.json"); }); - it("When I navigate to the thank you page, Then I should not see the feedback call to action", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.feedback()).isExisting()).to.equal(false); + it("When I navigate to the thank you page, Then I should not see the feedback call to action", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.feedback()).isExisting()).to.equal(false); }); }); }); diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index a5ea63434e..c40bd5a93f 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -4,148 +4,148 @@ import singleCheckboxPage from "../generated_pages/checkbox/single-checkbox.page import SubmitPage from "../generated_pages/checkbox/submit.page"; describe('Checkbox with "other" option', () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_checkbox.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_checkbox.json"); }); - it("Given a label has not been provided in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default label should be visible", () => { - expect($("body").getText()).to.have.string("Select all that apply"); + it("Given a label has not been provided in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default label should be visible", async () => { + await expect(await $("body").getText()).to.have.string("Select all that apply"); }); - it("Given a label has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the label should be visible", () => { - $(MandatoryCheckboxPage.none()).click(); - $(MandatoryCheckboxPage.submit()).click(); - expect($("body").getText()).to.have.string("Select any answers that apply"); + it("Given a label has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the label should be visible", async () => { + await $(MandatoryCheckboxPage.none()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await expect(await $("body").getText()).to.have.string("Select any answers that apply"); }); - it("Given that there is only one checkbox, When the checkbox answer is displayed, Then no label should be present", () => { - $(MandatoryCheckboxPage.none()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.submit()).click(); - expect($("body").getText()).to.not.have.string("Select all that apply"); + it("Given that there is only one checkbox, When the checkbox answer is displayed, Then no label should be present", async () => { + await $(MandatoryCheckboxPage.none()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.submit()).click(); + await expect(await $("body").getText()).to.not.have.string("Select all that apply"); }); - it('Given an "other" option is available, when the user clicks the "other" option the other input should be visible.', () => { - expect($(MandatoryCheckboxPage.otherLabelDescription()).getText()).to.have.string("Choose any other topping"); - $(MandatoryCheckboxPage.other()).click(); - expect($(MandatoryCheckboxPage.otherDetail()).isDisplayed()).to.be.true; + it('Given an "other" option is available, when the user clicks the "other" option the other input should be visible.', async () => { + await expect(await $(MandatoryCheckboxPage.otherLabelDescription()).getText()).to.have.string("Choose any other topping"); + await $(MandatoryCheckboxPage.other()).click(); + await expect(await $(MandatoryCheckboxPage.otherDetail()).isDisplayed()).to.be.true; }); - it("Given a mandatory checkbox answer, When I select the other option, leave the input field empty and submit, Then an error should be displayed.", () => { + it("Given a mandatory checkbox answer, When I select the other option, leave the input field empty and submit, Then an error should be displayed.", async () => { // When - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; }); - it("Given a mandatory checkbox answer, When I leave the input field empty and submit, Then the question text should be hidden in the error message using a span element.", () => { + it("Given a mandatory checkbox answer, When I leave the input field empty and submit, Then the question text should be hidden in the error message using a span element.", async () => { // When - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(MandatoryCheckboxPage.error()).getHTML()).to.contain( + await expect(await $(MandatoryCheckboxPage.error()).getHTML()).to.contain( 'Select at least one answer to ‘Which pizza toppings would you like?’' ); }); - it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.s", () => { - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.submit()).click(); - expect($(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.s", async () => { + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; // When - $(MandatoryCheckboxPage.otherDetail()).setValue("Other Text"); - $(MandatoryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(NonMandatoryCheckboxPage.pageName); + await $(MandatoryCheckboxPage.otherDetail()).setValue("Other Text"); + await $(MandatoryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(NonMandatoryCheckboxPage.pageName); }); - it('Given a non-mandatory checkbox answer, when the user does not select an option, then "No answer provided" should be displayed on the summary screen', () => { + it('Given a non-mandatory checkbox answer, when the user does not select an option, then "No answer provided" should be displayed on the summary screen', async () => { // When - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.submit()).click(); - $(singleCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.submit()).click(); + await $(singleCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("No answer provided"); }); - it('Given a non-mandatory checkbox answer, when the user selects Other but does not supply a value, then "Other" should be displayed on the summary screen', () => { + it('Given a non-mandatory checkbox answer, when the user selects Other but does not supply a value, then "Other" should be displayed on the summary screen', async () => { // When - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.other()).click(); - $(NonMandatoryCheckboxPage.submit()).click(); - $(singleCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.other()).click(); + await $(NonMandatoryCheckboxPage.submit()).click(); + await $(singleCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("Other"); }); - it("Given a non-mandatory checkbox answer, when the user selects Other and supplies a value, then the supplied value should be displayed on the summary screen", () => { + it("Given a non-mandatory checkbox answer, when the user selects Other and supplies a value, then the supplied value should be displayed on the summary screen", async () => { // When - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.other()).click(); - $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); - $(NonMandatoryCheckboxPage.submit()).click(); - $(singleCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.other()).click(); + await $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); + await $(NonMandatoryCheckboxPage.submit()).click(); + await $(singleCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("The other value"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("The other value"); }); - it("Given that there is an escaped character in an answer label, when the user selects the answer, then the label should be displayed on the summary screen", () => { + it("Given that there is an escaped character in an answer label, when the user selects the answer, then the label should be displayed on the summary screen", async () => { // When - $(MandatoryCheckboxPage.hamCheese()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.other()).click(); - $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); - $(NonMandatoryCheckboxPage.submit()).click(); - $(singleCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.hamCheese()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.other()).click(); + await $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); + await $(NonMandatoryCheckboxPage.submit()).click(); + await $(singleCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.contain("Ham & Cheese"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.contain("Ham & Cheese"); }); - it("Given I have previously added text in other textfield and saved, when I uncheck other options and select a different checkbox as answer, then the text entered in other field must be wiped.", () => { + it("Given I have previously added text in other textfield and saved, when I uncheck other options and select a different checkbox as answer, then the text entered in other field must be wiped.", async () => { // When - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.previous()).click(); - $(MandatoryCheckboxPage.other()).click(); - $(MandatoryCheckboxPage.hamCheese()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.previous()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.previous()).click(); + await $(MandatoryCheckboxPage.other()).click(); + await $(MandatoryCheckboxPage.hamCheese()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.previous()).click(); // Then - $(MandatoryCheckboxPage.other()).click(); - expect($(MandatoryCheckboxPage.otherDetail()).getValue()).to.equal(""); + await $(MandatoryCheckboxPage.other()).click(); + await expect(await $(MandatoryCheckboxPage.otherDetail()).getValue()).to.equal(""); }); - it("Given a mandatory checkbox answer, when the user selects only one option, then the answer should not be displayed as a list on the summary screen", () => { + it("Given a mandatory checkbox answer, when the user selects only one option, then the answer should not be displayed as a list on the summary screen", async () => { // When - $(MandatoryCheckboxPage.ham()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.ham()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.submit()).click(); // Then - const listLength = $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; + const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; // Then - expect(listLength).to.equal(0); + await expect(listLength).to.equal(0); }); - it("Given a mandatory checkbox answer, when the user selects more than one option, then the answer should be displayed as a list on the summary screen", () => { + it("Given a mandatory checkbox answer, when the user selects more than one option, then the answer should be displayed as a list on the summary screen", async () => { // When - $(MandatoryCheckboxPage.ham()).click(); - $(MandatoryCheckboxPage.hamCheese()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(NonMandatoryCheckboxPage.submit()).click(); - $(singleCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.ham()).click(); + await $(MandatoryCheckboxPage.hamCheese()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(NonMandatoryCheckboxPage.submit()).click(); + await $(singleCheckboxPage.submit()).click(); - const listLength = $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; + const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; // Then - expect(listLength).to.equal(2); + await expect(listLength).to.equal(2); }); }); diff --git a/tests/functional/spec/components/address/address.spec.js b/tests/functional/spec/components/address/address.spec.js index aeeda4e142..e3abfdc536 100644 --- a/tests/functional/spec/components/address/address.spec.js +++ b/tests/functional/spec/components/address/address.spec.js @@ -4,84 +4,84 @@ import AddressOptional from "../../../generated_pages/address/address-block-opti import SubmitPage from "../../../generated_pages/address/submit.page"; describe("Address Answer Type", () => { - beforeEach("Launch survey", () => { - browser.openQuestionnaire("test_address.json"); + beforeEach("Launch survey", async () => { + await browser.openQuestionnaire("test_address.json"); }); describe("Given the user is on an address input question", () => { - it("When the user enters all address fields, Then the summary displays the address fields", () => { - $(AddressMandatory.Line1()).setValue("Evelyn Street"); - $(AddressMandatory.Line2()).setValue("Apt 7"); - $(AddressMandatory.Town()).setValue("Barry"); - $(AddressMandatory.Postcode()).setValue("CF63 4JG"); + it("When the user enters all address fields, Then the summary displays the address fields", async () => { + await $(AddressMandatory.Line1()).setValue("Evelyn Street"); + await $(AddressMandatory.Line2()).setValue("Apt 7"); + await $(AddressMandatory.Town()).setValue("Barry"); + await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - $(AddressMandatory.submit()).click(); - $(AddressOptional.submit()).click(); - $(AddressConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); - expect($(SubmitPage.addressMandatory()).getHTML()).to.contain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); + await $(AddressMandatory.submit()).click(); + await $(AddressOptional.submit()).click(); + await $(AddressConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); + await expect(await $(SubmitPage.addressMandatory()).getHTML()).to.contain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); }); }); describe("Given the user is on an address input question", () => { - it("When the user enters only address line 1, Then the summary only displays address line 1", () => { - $(AddressMandatory.Line1()).setValue("Evelyn Street"); + it("When the user enters only address line 1, Then the summary only displays address line 1", async () => { + await $(AddressMandatory.Line1()).setValue("Evelyn Street"); - $(AddressMandatory.submit()).click(); - $(AddressOptional.submit()).click(); - $(AddressConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street"); + await $(AddressMandatory.submit()).click(); + await $(AddressOptional.submit()).click(); + await $(AddressConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street"); }); }); describe("Given the user is on an mandatory address input question", () => { - it("When the user submits the page without entering address line 1, Then an error is displayed", () => { - $(AddressMandatory.submit()).click(); - expect($(AddressMandatory.error()).getText()).to.equal("Enter an address"); + it("When the user submits the page without entering address line 1, Then an error is displayed", async () => { + await $(AddressMandatory.submit()).click(); + await expect(await $(AddressMandatory.error()).getText()).to.equal("Enter an address"); }); }); describe("Given the user is on an optional address input question", () => { - it("When the user submits the page without entering any fields, Then the summary should display `No answer provided`.", () => { + it("When the user submits the page without entering any fields, Then the summary should display `No answer provided`.", async () => { // Get to optional address question - $(AddressMandatory.Line1()).setValue("Evelyn Street"); - $(AddressMandatory.submit()).click(); + await $(AddressMandatory.Line1()).setValue("Evelyn Street"); + await $(AddressMandatory.submit()).click(); - $(AddressOptional.submit()).click(); - $(AddressConfirmation.submit()).click(); - expect($(SubmitPage.addressOptional()).getText()).to.equal("No answer provided"); + await $(AddressOptional.submit()).click(); + await $(AddressConfirmation.submit()).click(); + await expect(await $(SubmitPage.addressOptional()).getText()).to.equal("No answer provided"); }); }); describe("Given the user has submitted an address answer type question", () => { - it("When the user revisits the address question page, Then all entered fields are filled in", () => { - $(AddressMandatory.Line1()).setValue("Evelyn Street"); - $(AddressMandatory.Line2()).setValue("Apt 7"); - $(AddressMandatory.Town()).setValue("Barry"); - $(AddressMandatory.Postcode()).setValue("CF63 4JG"); + it("When the user revisits the address question page, Then all entered fields are filled in", async () => { + await $(AddressMandatory.Line1()).setValue("Evelyn Street"); + await $(AddressMandatory.Line2()).setValue("Apt 7"); + await $(AddressMandatory.Town()).setValue("Barry"); + await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - $(AddressMandatory.submit()).click(); - expect(browser.getUrl()).to.contain(AddressOptional.pageName); + await $(AddressMandatory.submit()).click(); + await expect(await browser.getUrl()).to.contain(AddressOptional.pageName); - browser.url(AddressMandatory.url()); + await browser.url(AddressMandatory.url()); - expect($(AddressMandatory.Line1()).getValue()).to.contain("Evelyn Street"); - expect($(AddressMandatory.Line2()).getValue()).to.contain("Apt 7"); - expect($(AddressMandatory.Town()).getValue()).to.contain("Barry"); - expect($(AddressMandatory.Postcode()).getValue()).to.contain("CF63 4JG"); + await expect(await $(AddressMandatory.Line1()).getValue()).to.contain("Evelyn Street"); + await expect(await $(AddressMandatory.Line2()).getValue()).to.contain("Apt 7"); + await expect(await $(AddressMandatory.Town()).getValue()).to.contain("Barry"); + await expect(await $(AddressMandatory.Postcode()).getValue()).to.contain("CF63 4JG"); }); }); describe("Given the user has submitted an address answer type question", () => { - it("When the user visits the address confirmation question page, Then the first line of the address is displayed", () => { - $(AddressMandatory.Line1()).setValue("Evelyn Street"); - $(AddressMandatory.Line2()).setValue("Apt 7"); - $(AddressMandatory.Town()).setValue("Barry"); - $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - $(AddressMandatory.submit()).click(); - $(AddressOptional.submit()).click(); - expect($(AddressConfirmation.questionText()).getText()).to.equal("Please confirm the first line of your address is Evelyn Street"); + it("When the user visits the address confirmation question page, Then the first line of the address is displayed", async () => { + await $(AddressMandatory.Line1()).setValue("Evelyn Street"); + await $(AddressMandatory.Line2()).setValue("Apt 7"); + await $(AddressMandatory.Town()).setValue("Barry"); + await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); + await $(AddressMandatory.submit()).click(); + await $(AddressOptional.submit()).click(); + await expect(await $(AddressConfirmation.questionText()).getText()).to.equal("Please confirm the first line of your address is Evelyn Street"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js index 37b5031380..31c7d3a2ba 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js @@ -4,35 +4,35 @@ import CheckboxVisibleNonePage from "../../../generated_pages/checkbox_detail_an import MutuallyExclusivePage from "../../../generated_pages/checkbox_detail_answer_textfield/mutually-exclusive.page.js"; describe("Given the checkbox detail_answer questionnaire,", () => { - beforeEach(() => { - browser.openQuestionnaire("test_checkbox_detail_answer_textfield.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_checkbox_detail_answer_textfield.json"); }); - it("When a checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", () => { - expect($(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; + it("When a checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", async () => { + await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; }); - it("When a checkbox has a detail_answer with visible set to true and another answer is checked, then the detail answer write-in field should still be shown", () => { - $(CheckboxVisibleTruePage.coffee()).click(); - expect($(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; + it("When a checkbox has a detail_answer with visible set to true and another answer is checked, then the detail answer write-in field should still be shown", async () => { + await $(CheckboxVisibleTruePage.coffee()).click(); + await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; }); - it("When a checkbox has a detail_answer with visible set to false, Then the detail answer write-in field should not be shown", () => { - $(CheckboxVisibleTruePage.coffee()).click(); - $(CheckboxVisibleTruePage.submit()).click(); - expect($(CheckboxVisibleFalsePage.otherDetail()).isDisplayed()).to.be.false; + it("When a checkbox has a detail_answer with visible set to false, Then the detail answer write-in field should not be shown", async () => { + await $(CheckboxVisibleTruePage.coffee()).click(); + await $(CheckboxVisibleTruePage.submit()).click(); + await expect(await $(CheckboxVisibleFalsePage.otherDetail()).isDisplayed()).to.be.false; }); - it("When a checkbox has a detail_answer with visible not set, Then the detail answer write-in field should not be shown", () => { - $(CheckboxVisibleTruePage.coffee()).click(); - $(CheckboxVisibleTruePage.submit()).click(); - $(CheckboxVisibleFalsePage.iceCream()).click(); - $(CheckboxVisibleFalsePage.submit()).click(); - expect($(CheckboxVisibleNonePage.otherDetail()).isDisplayed()).to.be.false; + it("When a checkbox has a detail_answer with visible not set, Then the detail answer write-in field should not be shown", async () => { + await $(CheckboxVisibleTruePage.coffee()).click(); + await $(CheckboxVisibleTruePage.submit()).click(); + await $(CheckboxVisibleFalsePage.iceCream()).click(); + await $(CheckboxVisibleFalsePage.submit()).click(); + await expect(await $(CheckboxVisibleNonePage.otherDetail()).isDisplayed()).to.be.false; }); - it("When a mutually exclusive checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", () => { - $(CheckboxVisibleTruePage.coffee()).click(); - $(CheckboxVisibleTruePage.submit()).click(); - $(CheckboxVisibleFalsePage.iceCream()).click(); - $(CheckboxVisibleFalsePage.submit()).click(); - $(CheckboxVisibleNonePage.blue()).click(); - $(CheckboxVisibleNonePage.submit()).click(); - expect($(MutuallyExclusivePage.otherDetail()).isDisplayed()).to.be.true; + it("When a mutually exclusive checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", async () => { + await $(CheckboxVisibleTruePage.coffee()).click(); + await $(CheckboxVisibleTruePage.submit()).click(); + await $(CheckboxVisibleFalsePage.iceCream()).click(); + await $(CheckboxVisibleFalsePage.submit()).click(); + await $(CheckboxVisibleNonePage.blue()).click(); + await $(CheckboxVisibleNonePage.submit()).click(); + await expect(await $(MutuallyExclusivePage.otherDetail()).isDisplayed()).to.be.true; }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js index e554d1b733..77bacb77b9 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js @@ -3,89 +3,89 @@ import SubmitPage from "../../../generated_pages/checkbox_detail_answer_dropdown import DropdownMandatoryPage from "../../../generated_pages/dropdown_mandatory/dropdown-mandatory.page"; describe("Optional Checkbox with a Dropdown detail answer", () => { - beforeEach(() => { - browser.openQuestionnaire("test_checkbox_detail_answer_dropdown.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_checkbox_detail_answer_dropdown.json"); }); describe("Given an optional checkbox with a dropdown detail answer", () => { - it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", () => { - $(CheckboxDropdownPage.fruit()).click(); + it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", async () => { + await $(CheckboxDropdownPage.fruit()).click(); - expect($(CheckboxDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); + await expect(await $(CheckboxDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); }); - it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", () => { - $(CheckboxDropdownPage.jam()).click(); + it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", async () => { + await $(CheckboxDropdownPage.jam()).click(); - expect($(CheckboxDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); + await expect(await $(CheckboxDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); }); - it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", () => { - $(CheckboxDropdownPage.submit()).click(); + it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); - it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", () => { - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); - it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", () => { - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); }); - it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", () => { - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(CheckboxDropdownPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(CheckboxDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(CheckboxDropdownPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(CheckboxDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); - it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", () => { - $(CheckboxDropdownPage.jam()).click(); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { + await $(CheckboxDropdownPage.jam()).click(); + await $(CheckboxDropdownPage.submit()).click(); - expect($(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); }); - it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its details", () => { - $(CheckboxDropdownPage.jam()).click(); - $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its details", async () => { + await $(CheckboxDropdownPage.jam()).click(); + await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); }); - it("When the user removes a previously submitted detail answer, Then the summary should not display the removed detail answer", () => { - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(CheckboxDropdownPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.submit()).click(); + it("When the user removes a previously submitted detail answer, Then the summary should not display the removed detail answer", async () => { + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(CheckboxDropdownPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); - it("When the user selects multiple options with detail answers and submits, Then the summary should display all the chosen options and their detail answer", () => { - $(CheckboxDropdownPage.fruit()).click(); - $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(CheckboxDropdownPage.jam()).click(); - $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - $(CheckboxDropdownPage.submit()).click(); + it("When the user selects multiple options with detail answers and submits, Then the summary should display all the chosen options and their detail answer", async () => { + await $(CheckboxDropdownPage.fruit()).click(); + await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(CheckboxDropdownPage.jam()).click(); + await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); + await $(CheckboxDropdownPage.submit()).click(); - expect($(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango\nJam\nStrawberry"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango\nJam\nStrawberry"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js index aece2d7822..0def38c8dd 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js @@ -4,91 +4,91 @@ import SubmitPage from "../../../generated_pages/checkbox_detail_answer_multiple describe('Checkbox with multiple "detail_answer" options', () => { const checkboxSchema = "test_checkbox_detail_answer_multiple.json"; - it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", () => { - browser.openQuestionnaire(checkboxSchema); - $(MandatoryCheckboxPage.yourChoice()).click(); - expect($(MandatoryCheckboxPage.yourChoiceDetail()).isDisplayed()).to.be.true; - $(MandatoryCheckboxPage.cheese()).click(); - expect($(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; + it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { + await browser.openQuestionnaire(checkboxSchema); + await $(MandatoryCheckboxPage.yourChoice()).click(); + await expect(await $(MandatoryCheckboxPage.yourChoiceDetail()).isDisplayed()).to.be.true; + await $(MandatoryCheckboxPage.cheese()).click(); + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; }); - it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", () => { + it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", async () => { // Given - browser.openQuestionnaire(checkboxSchema); + await browser.openQuestionnaire(checkboxSchema); // When // Non-Mandatory detail answer given - $(MandatoryCheckboxPage.cheese()).click(); - $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); + await $(MandatoryCheckboxPage.cheese()).click(); + await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); // Mandatory detail answer left blank - $(MandatoryCheckboxPage.yourChoice()).click(); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.yourChoice()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; - expect($(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Enter your topping choice to continue"); + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Enter your topping choice to continue"); }); - it("Given a selected checkbox answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", () => { + it("Given a selected checkbox answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", async () => { // Given - browser.openQuestionnaire(checkboxSchema); - $(MandatoryCheckboxPage.yourChoice()).click(); - $(MandatoryCheckboxPage.submit()).click(); - expect($(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await browser.openQuestionnaire(checkboxSchema); + await $(MandatoryCheckboxPage.yourChoice()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; // When - $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - $(MandatoryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); + await $(MandatoryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", () => { + it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // Given - browser.openQuestionnaire(checkboxSchema); + await browser.openQuestionnaire(checkboxSchema); // When - $(MandatoryCheckboxPage.cheese()).click(); - expect($(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.cheese()).click(); + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese"); }); - it("Given multiple detail answers, When the user provides text for all, Then that text should be displayed on the summary screen", () => { + it("Given multiple detail answers, When the user provides text for all, Then that text should be displayed on the summary screen", async () => { // Given - browser.openQuestionnaire(checkboxSchema); + await browser.openQuestionnaire(checkboxSchema); // When - $(MandatoryCheckboxPage.cheese()).click(); - $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); - $(MandatoryCheckboxPage.yourChoice()).click(); - $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.cheese()).click(); + await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); + await $(MandatoryCheckboxPage.yourChoice()).click(); + await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese\nMozzarella\nYour choice\nBacon"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese\nMozzarella\nYour choice\nBacon"); }); - it("Given multiple detail answers, When the user provides text for just one, Then that text should be displayed on the summary screen", () => { + it("Given multiple detail answers, When the user provides text for just one, Then that text should be displayed on the summary screen", async () => { // Given - browser.openQuestionnaire(checkboxSchema); + await browser.openQuestionnaire(checkboxSchema); // When - $(MandatoryCheckboxPage.yourChoice()).click(); - $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.yourChoice()).click(); + await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Your choice\nBacon"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Your choice\nBacon"); }); - it("Given I have previously added text in a detail answer and saved, When I uncheck the detail answer option and select a different checkbox, Then the text entered in the detail answer field should be empty.", () => { + it("Given I have previously added text in a detail answer and saved, When I uncheck the detail answer option and select a different checkbox, Then the text entered in the detail answer field should be empty.", async () => { // Given - browser.openQuestionnaire(checkboxSchema); + await browser.openQuestionnaire(checkboxSchema); // When - $(MandatoryCheckboxPage.cheese()).click(); - $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); - $(MandatoryCheckboxPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(MandatoryCheckboxPage.cheese()).click(); - $(MandatoryCheckboxPage.ham()).click(); - $(MandatoryCheckboxPage.submit()).click(); - $(SubmitPage.previous()).click(); + await $(MandatoryCheckboxPage.cheese()).click(); + await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); + await $(MandatoryCheckboxPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(MandatoryCheckboxPage.cheese()).click(); + await $(MandatoryCheckboxPage.ham()).click(); + await $(MandatoryCheckboxPage.submit()).click(); + await $(SubmitPage.previous()).click(); // Then - $(MandatoryCheckboxPage.cheese()).click(); - expect($(MandatoryCheckboxPage.cheeseDetail()).getValue()).to.equal(""); + await $(MandatoryCheckboxPage.cheese()).click(); + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).getValue()).to.equal(""); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js index be92f9a3a5..4f6862221a 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js @@ -2,74 +2,74 @@ import CheckboxNumericDetailPage from "../../../generated_pages/checkbox_detail_ import SubmitPage from "../../../generated_pages/checkbox_detail_answer_numeric/submit.page"; describe('Checkbox with a numeric "detail_answer" option', () => { - beforeEach(() => { - browser.openQuestionnaire("test_checkbox_detail_answer_numeric.json"); - $(CheckboxNumericDetailPage.other()).click(); + beforeEach(async () => { + await browser.openQuestionnaire("test_checkbox_detail_answer_numeric.json"); + await $(CheckboxNumericDetailPage.other()).click(); }); - it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", () => { - expect($(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { + await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; }); - it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", () => { + it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When - expect($(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; - $(CheckboxNumericDetailPage.submit()).click(); + await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("Other"); }); - it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", () => { + it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("15"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("15"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("15"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("15"); }); - it("Given a numeric detail answer, When the user provides text, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); }); - it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("250"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("250"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); }); - it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("-1"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("-1"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); }); - it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", () => { + it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("biscuits"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("biscuits"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); - browser.pause(1000); - expect($(CheckboxNumericDetailPage.otherDetail()).getValue()).to.equal("biscuits"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await browser.pause(1000); + await expect(await $(CheckboxNumericDetailPage.otherDetail()).getValue()).to.equal("biscuits"); }); - it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', () => { + it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { // When - $(CheckboxNumericDetailPage.otherDetail()).setValue("0"); - $(CheckboxNumericDetailPage.submit()).click(); + await $(CheckboxNumericDetailPage.otherDetail()).setValue("0"); + await $(CheckboxNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("0"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("0"); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_label.spec.js b/tests/functional/spec/components/checkbox/checkbox_label.spec.js index fb61b22323..d60cd9c073 100644 --- a/tests/functional/spec/components/checkbox/checkbox_label.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_label.spec.js @@ -3,32 +3,32 @@ import NoInstructionPage from "../../../generated_pages/checkbox_instruction/no- import CustomInstructionPage from "../../../generated_pages/checkbox_instruction/custom-instruction-checkbox.page"; describe("Given the checkbox label variants questionnaire,", () => { - beforeEach(() => { - browser.openQuestionnaire("test_checkbox_instruction.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_checkbox_instruction.json"); }); - it("Given an instruction has not been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default instruction should be visible", () => { - expect($("body").getText()).to.have.string("Select all that apply"); + it("Given an instruction has not been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default instruction should be visible", async () => { + await expect(await $("body").getText()).to.have.string("Select all that apply"); }); - it("Given an instruction has been set to null in the schema for a checkbox answer, When the checkbox answer is displayed, Then the instruction should not be visible", () => { - $(DefaultInstructionPage.red()).click(); - $(DefaultInstructionPage.submit()).click(); - expect($("body").getText()).to.not.have.string("Select all that apply"); + it("Given an instruction has been set to null in the schema for a checkbox answer, When the checkbox answer is displayed, Then the instruction should not be visible", async () => { + await $(DefaultInstructionPage.red()).click(); + await $(DefaultInstructionPage.submit()).click(); + await expect(await $("body").getText()).to.not.have.string("Select all that apply"); }); - it("Given a custom instruction has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the custom instruction should be visible", () => { - $(DefaultInstructionPage.red()).click(); - $(DefaultInstructionPage.submit()).click(); - $(NoInstructionPage.rugby()).click(); - $(NoInstructionPage.submit()).click(); - expect($("body").getText()).to.have.string("Select your answer"); + it("Given a custom instruction has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the custom instruction should be visible", async () => { + await $(DefaultInstructionPage.red()).click(); + await $(DefaultInstructionPage.submit()).click(); + await $(NoInstructionPage.rugby()).click(); + await $(NoInstructionPage.submit()).click(); + await expect(await $("body").getText()).to.have.string("Select your answer"); }); - it("Given a label and custom instruction have been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then both the custom instruction and label should be visible", () => { - $(DefaultInstructionPage.red()).click(); - $(DefaultInstructionPage.submit()).click(); - $(NoInstructionPage.rugby()).click(); - $(NoInstructionPage.submit()).click(); - $(CustomInstructionPage.monday()).click(); - $(CustomInstructionPage.submit()).click(); - expect($("body").getText()).to.have.string("Days of the Week"); - expect($("body").getText()).to.have.string("Select your answer"); + it("Given a label and custom instruction have been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then both the custom instruction and label should be visible", async () => { + await $(DefaultInstructionPage.red()).click(); + await $(DefaultInstructionPage.submit()).click(); + await $(NoInstructionPage.rugby()).click(); + await $(NoInstructionPage.submit()).click(); + await $(CustomInstructionPage.monday()).click(); + await $(CustomInstructionPage.submit()).click(); + await expect(await $("body").getText()).to.have.string("Days of the Week"); + await expect(await $("body").getText()).to.have.string("Select your answer"); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js index c9933448ff..126456e25f 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js @@ -2,129 +2,129 @@ import MandatoryCheckboxPage from "../../../../generated_pages/mutually_exclusiv import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-checkbox-section-summary.page"; describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); }); describe("Given the user has clicked multiple non-exclusive options", () => { - it("When then user clicks the mutually exclusive option, Then only the mutually exclusive option should be checked.", () => { + it("When then user clicks the mutually exclusive option, Then only the mutually exclusive option should be checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); - $(MandatoryCheckboxPage.checkboxOther()).click(); - $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxOther()).click(); + await $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); // When - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); }); describe('Given the user has clicked the mutually exclusive "other" option', () => { - it("When the user returns to the question, Then the mutually exclusive other option should remain checked.", () => { + it("When the user returns to the question, Then the mutually exclusive other option should remain checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // When - $(SummaryPage.previous()).click(); + await $(SummaryPage.previous()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; }); }); describe("Given the user has clicked the mutually exclusive option", () => { - it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", () => { + it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive option", () => { - it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", () => { + it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked any of the non-exclusive options", () => { - it("When the user clicks the mutually exclusive option, Then only the exclusive option should be checked.", () => { + it("When the user clicks the mutually exclusive option, Then only the exclusive option should be checked.", async () => { // Given - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); }); describe("Given the user has not clicked any options and the question is mandatory", () => { - it("When the user clicks the Continue button, Then a validation error message should be displayed.", () => { + it("When the user clicks the Continue button, Then a validation error message should be displayed.", async () => { // Given - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - expect($(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); + await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js index 4004ce36fe..3c8915a8ab 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js @@ -2,98 +2,99 @@ import CurrencyPage from "../../../../generated_pages/mutually_exclusive/mutuall import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-currency-section-summary.page"; describe("Component: Mutually Exclusive Currency With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-currency"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-currency"); }); describe("Given the user has entered a value for the non-exclusive currency answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(CurrencyPage.currency()).setValue("123"); - expect($(CurrencyPage.currency()).getValue()).to.contain("123"); + await $(CurrencyPage.currency()).setValue("123"); + await expect(await $(CurrencyPage.currency()).getValue()).to.contain("123"); // When - $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); + await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); // Then - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(CurrencyPage.currency()).getValue()).to.contain(""); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); - $(CurrencyPage.submit()).click(); + await $(CurrencyPage.submit()).click(); - expect($(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive currency answer and removes focus, Then only the non-exclusive currency answer should be answered.", () => { + it("When the user enters a value for the non-exclusive currency answer and removes focus, Then only the non-exclusive currency answer should be answered.", async () => { // Given - $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(CurrencyPage.currency()).setValue("123"); + await $(CurrencyPage.currency()).setValue("123"); // Then - $(CurrencyPage.currency()).getValue(); - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(CurrencyPage.currency()).getValue(); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(CurrencyPage.submit()).click(); + await $(CurrencyPage.submit()).click(); - expect($(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); - expect($(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive currency answer, Then only the non-exclusive currency answer should be answered.", () => { + it("When the user enters a value for the non-exclusive currency answer, Then only the non-exclusive currency answer should be answered.", async () => { // Given - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(CurrencyPage.currency()).setValue("123"); + await $(CurrencyPage.currency()).setValue("123"); // Then - expect($(CurrencyPage.currency()).getValue()).to.contain("123"); - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currency()).getValue()).to.contain("123"); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(CurrencyPage.submit()).click(); + await $(CurrencyPage.submit()).click(); - expect($(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); - expect($(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive currency answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(CurrencyPage.currency()).getValue()).to.contain(""); + await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); // When - $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(CurrencyPage.submit()).click(); + await $(CurrencyPage.submit()).click(); - expect($(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(CurrencyPage.currency()).getValue()).to.contain(""); - expect($(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(CurrencyPage.submit()).click(); + await $(CurrencyPage.submit()).click(); // Then - expect($(SummaryPage.currencyAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js index 2866bab6bf..c45e29245a 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js @@ -2,116 +2,117 @@ import DatePage from "../../../../generated_pages/mutually_exclusive/mutually-ex import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-date-section-summary.page"; describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-date"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-date"); }); describe("Given the user has entered a value for the non-exclusive month year date answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); // When - $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); // Then - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - $(DatePage.dateExclusiveIPreferNotToSay()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); // Then - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); // Then - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - - $(DatePage.submit()).click(); - expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + + await $(DatePage.submit()).click(); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); // When - $(DatePage.dateExclusiveIPreferNotToSay()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); // Then - expect($(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js index b84727e006..0567ec7cc5 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js @@ -2,108 +2,111 @@ import DurationPage from "../../../../generated_pages/mutually_exclusive/mutuall import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-duration-section-summary.page"; describe("Component: Mutually Exclusive Duration With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-duration"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-duration"); }); describe("Given the user has entered a value for the non-exclusive duration answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(DurationPage.durationYears()).setValue("1"); - $(DurationPage.durationMonths()).setValue("7"); + await $(DurationPage.durationYears()).setValue("1"); + await $(DurationPage.durationMonths()).setValue("7"); - expect($(DurationPage.durationYears()).getValue()).to.contain("1"); - expect($(DurationPage.durationMonths()).getValue()).to.contain("7"); + await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); // When - $(DurationPage.durationExclusiveIPreferNotToSay()).click(); + await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); // Then - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DurationPage.durationYears()).getValue()).to.contain(""); - expect($(DurationPage.durationMonths()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); - $(DurationPage.submit()).click(); + await $(DurationPage.submit()).click(); - expect($(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive duration answer and removes focus, Then only the non-exclusive duration answer should be answered.", () => { + it("When the user enters a value for the non-exclusive duration answer and removes focus, Then only the non-exclusive duration answer should be answered.", async () => { // Given - $(DurationPage.durationExclusiveIPreferNotToSay()).click(); - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(DurationPage.durationYears()).setValue("1"); - $(DurationPage.durationMonths()).setValue("7"); + await $(DurationPage.durationYears()).setValue("1"); + await $(DurationPage.durationMonths()).setValue("7"); // Then - expect($(DurationPage.durationYears()).getValue()).to.contain("1"); - expect($(DurationPage.durationMonths()).getValue()).to.contain("7"); - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(DurationPage.submit()).click(); + await $(DurationPage.submit()).click(); - expect($(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); - expect($(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive duration answer, Then only the non-exclusive duration answer should be answered.", () => { + it("When the user enters a value for the non-exclusive duration answer, Then only the non-exclusive duration answer should be answered.", async () => { // Given - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(DurationPage.durationYears()).setValue("1"); - $(DurationPage.durationMonths()).setValue("7"); + await $(DurationPage.durationYears()).setValue("1"); + await $(DurationPage.durationMonths()).setValue("7"); // Then - expect($(DurationPage.durationYears()).getValue()).to.contain("1"); - expect($(DurationPage.durationMonths()).getValue()).to.contain("7"); - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(DurationPage.submit()).click(); + await $(DurationPage.submit()).click(); - expect($(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); - expect($(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive duration answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(DurationPage.durationYears()).getValue()).to.contain(""); - expect($(DurationPage.durationMonths()).getValue()).to.contain(""); + await browser.url("/questionnaire/mutually-exclusive-duration"); + await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); // When - $(DurationPage.durationExclusiveIPreferNotToSay()).click(); - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(DurationPage.submit()).click(); + await $(DurationPage.submit()).click(); - expect($(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(DurationPage.durationYears()).getValue()).to.contain(""); - expect($(DurationPage.durationMonths()).getValue()).to.contain(""); - expect($(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await browser.url("/questionnaire/mutually-exclusive-duration"); + await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(DurationPage.submit()).click(); + await $(DurationPage.submit()).click(); // Then - expect($(SummaryPage.durationAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.durationAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js index e2c583e4a2..a5fe153634 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js @@ -2,108 +2,109 @@ import MonthYearDatePage from "../../../../generated_pages/mutually_exclusive/mu import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-month-year-date-section-summary.page"; describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-month-year-date"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-month-year-date"); }); describe("Given the user has entered a value for the non-exclusive month year date answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); - $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); + await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); + await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); // When - $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); + await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); // Then - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); - $(MonthYearDatePage.submit()).click(); + await $(MonthYearDatePage.submit()).click(); - expect($(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); - $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); + await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); + await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); // Then - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(MonthYearDatePage.submit()).click(); + await $(MonthYearDatePage.submit()).click(); - expect($(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); - expect($(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); - $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); + await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); + await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); // Then - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(MonthYearDatePage.submit()).click(); + await $(MonthYearDatePage.submit()).click(); - expect($(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); - expect($(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); // When - $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(MonthYearDatePage.submit()).click(); + await $(MonthYearDatePage.submit()).click(); - expect($(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - expect($(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); - expect($(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(MonthYearDatePage.submit()).click(); + await $(MonthYearDatePage.submit()).click(); // Then - expect($(SummaryPage.monthYearDateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js index 9099aaa8ac..0a87e9dc60 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js @@ -2,223 +2,223 @@ import MandatoryCheckboxPage from "../../../../generated_pages/mutually_exclusiv import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-checkbox-section-summary.page"; describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); }); describe("Given the user has clicked multiple non-exclusive options", () => { - beforeEach(() => { + beforeEach(async () => { // Given - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); - $(MandatoryCheckboxPage.checkboxOther()).click(); - $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); - - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxOther()).click(); + await $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); + + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); }); - it("When then user clicks the first mutually exclusive option, Then only the first mutually exclusive option should be checked.", () => { + it("When then user clicks the first mutually exclusive option, Then only the first mutually exclusive option should be checked.", async () => { // When - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; // Then - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); - it("When then user clicks the second mutually exclusive option, Then only the second mutually exclusive option should be checked.", () => { + it("When then user clicks the second mutually exclusive option, Then only the second mutually exclusive option should be checked.", async () => { // When - $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; // Then - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); }); describe("Given the user has clicked the first mutually exclusive option", () => { - it("When the user returns to the question, Then the mutually exclusive option should remain checked.", () => { + it("When the user returns to the question, Then the mutually exclusive option should remain checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // When - $(SummaryPage.previous()).click(); + await $(SummaryPage.previous()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; }); }); describe("Given the user has clicked the second mutually exclusive option", () => { - it("When the user returns to the question, Then the mutually exclusive option should remain checked.", () => { + it("When the user returns to the question, Then the mutually exclusive option should remain checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // When - $(SummaryPage.previous()).click(); + await $(SummaryPage.previous()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; }); }); describe("Given the user has clicked the first mutually exclusive option", () => { - it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", () => { + it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has clicked the second mutually exclusive option", () => { - it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", () => { + it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; // When - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); }); }); describe("Given the user has not clicked a mutually exclusive option", () => { - it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", () => { + it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.checkboxBritish()).click(); - $(MandatoryCheckboxPage.checkboxIrish()).click(); + await $(MandatoryCheckboxPage.checkboxBritish()).click(); + await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); }); }); describe("Given the user has not clicked any of the non-exclusive options", () => { - beforeEach(() => { + beforeEach(async () => { // Given - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; }); - it("When the user clicks the first mutually exclusive option, Then only the first exclusive option should be checked.", () => { + it("When the user clicks the first mutually exclusive option, Then only the first exclusive option should be checked.", async () => { // When - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I am an alien"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); - it("When the user clicks the second mutually exclusive option, Then only the second exclusive option should be checked.", () => { + it("When the user clicks the second mutually exclusive option, Then only the second exclusive option should be checked.", async () => { // When - $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); }); describe("Given the user has clicked a mutually exclusive option", () => { - it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given - $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - expect($(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked any options and the question is mandatory", () => { - it("When the user clicks the Continue button, Then a validation error message should be displayed.", () => { + it("When the user clicks the Continue button, Then a validation error message should be displayed.", async () => { // Given - expect($(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; // When - $(MandatoryCheckboxPage.submit()).click(); + await $(MandatoryCheckboxPage.submit()).click(); // Then - expect($(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - expect($(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); + await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js index 3ea0baca8c..81f7f6a67d 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js @@ -2,206 +2,206 @@ import DatePage from "../../../../generated_pages/mutually_exclusive_multiple/mu import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-date-section-summary.page"; describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); - browser.url("/questionnaire/mutually-exclusive-date"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-date"); }); - describe("Given the user has entered a value for the non-exclusive month year date answer", () => { - beforeEach(() => { + beforeEach(async () => { // Given - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); }); - it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When - $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); // Then - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); - it("When then user clicks the second mutually exclusive radio answer, Then only the second mutually exclusive radio should be answered.", () => { + it("When then user clicks the second mutually exclusive radio answer, Then only the second mutually exclusive radio should be answered.", async () => { // When - $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); // Then - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); }); describe("Given the user has clicked the first mutually exclusive radio answer", () => { - it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - $(DatePage.dateExclusiveIPreferNotToSay()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // When - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); // Then - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); }); }); describe("Given the user has clicked the second mutually exclusive radio answer", () => { - it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); // Then - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // When - $(DatePage.dateday()).setValue("17"); - $(DatePage.datemonth()).setValue("3"); - $(DatePage.dateyear()).setValue("2018"); + await $(DatePage.dateday()).setValue("17"); + await $(DatePage.datemonth()).setValue("3"); + await $(DatePage.dateyear()).setValue("2018"); // Then - expect($(DatePage.dateday()).getValue()).to.contain("17"); - expect($(DatePage.datemonth()).getValue()).to.contain("3"); - expect($(DatePage.dateyear()).getValue()).to.contain("2018"); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - - $(DatePage.submit()).click(); - expect($(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); + await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); + await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + + await $(DatePage.submit()).click(); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { - beforeEach(() => { + beforeEach(async () => { // Given - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); }); - it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", () => { + it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", async () => { // When - $(DatePage.dateExclusiveIPreferNotToSay()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // Then - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); - it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", () => { + it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", async () => { // When - $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // Then - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(DatePage.dateday()).getValue()).to.contain(""); - expect($(DatePage.datemonth()).getValue()).to.contain(""); - expect($(DatePage.dateyear()).getValue()).to.contain(""); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).to.contain(""); + await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); + await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // When - $(DatePage.submit()).click(); + await $(DatePage.submit()).click(); // Then - expect($(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); }); }); describe("Given the user has clicked a mutually exclusive option", () => { - it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given - $(DatePage.dateExclusiveIPreferNotToSay()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await $(DatePage.dateExclusiveIPreferNotToSay()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // When - $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - expect($(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - $(DatePage.submit()).click(); + await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + await $(DatePage.submit()).click(); // Then - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - expect($(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js index a3231d4813..51053059bc 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js @@ -2,181 +2,182 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive_multip import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-textfield-section-summary.page"; describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); - browser.url("/questionnaire/mutually-exclusive-textfield"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive_multiple.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-textfield"); }); describe("Given the user has entered a value for the non-exclusive textfield answer", () => { - beforeEach(() => { + beforeEach(async () => { // Given - $(TextFieldPage.textfield()).setValue("Blue"); - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); }); - it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); // Then - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); - it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", () => { + it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When - $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); // Then - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); }); describe("Given the user has clicked the first mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // When - $(TextFieldPage.textfield()).setValue("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); // Then - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); }); }); describe("Given the user has clicked the second mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; // When - $(TextFieldPage.textfield()).setValue("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); // Then - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // When - $(TextFieldPage.textfield()).setValue("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); // Then - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); }); }); describe("Given the user has not answered the non-exclusive textfield answer", () => { - beforeEach(() => { + beforeEach(async () => { // Given - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); }); - it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", () => { + it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", async () => { // When - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // Then - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); - it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", () => { + it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", async () => { // When - $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; // Then - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // When - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); // Then - expect($(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); }); }); describe("Given the user has clicked a mutually exclusive option", () => { - it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", () => { + it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // When - $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - expect($(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await $(TextFieldPage.submit()).click(); // Then - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js index f1b87b70d8..46ca5f3c80 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js @@ -2,98 +2,99 @@ import NumberPage from "../../../../generated_pages/mutually_exclusive/mutually- import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-number-section-summary.page"; describe("Component: Mutually Exclusive Number With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-number"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-number"); }); describe("Given the user has entered a value for the non-exclusive number answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(NumberPage.number()).setValue("123"); - expect($(NumberPage.number()).getValue()).to.contain("123"); + await $(NumberPage.number()).setValue("123"); + await expect(await $(NumberPage.number()).getValue()).to.contain("123"); // When - $(NumberPage.numberExclusiveIPreferNotToSay()).click(); + await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); // Then - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(NumberPage.number()).getValue()).to.contain(""); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(NumberPage.number()).getValue()).to.contain(""); - $(NumberPage.submit()).click(); + await $(NumberPage.submit()).click(); - expect($(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive number answer and removes focus, Then only the non-exclusive number answer should be answered.", () => { + it("When the user enters a value for the non-exclusive number answer and removes focus, Then only the non-exclusive number answer should be answered.", async () => { // Given - $(NumberPage.numberExclusiveIPreferNotToSay()).click(); - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(NumberPage.number()).setValue("123"); + await $(NumberPage.number()).setValue("123"); // Then - expect($(NumberPage.number()).getValue()).to.contain("123"); - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).to.contain("123"); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(NumberPage.submit()).click(); + await $(NumberPage.submit()).click(); - expect($(SummaryPage.numberAnswer()).getText()).to.have.string("123"); - expect($(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); + await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive number answer, Then only the non-exclusive number answer should be answered.", () => { + it("When the user enters a value for the non-exclusive number answer, Then only the non-exclusive number answer should be answered.", async () => { // Given - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(NumberPage.number()).setValue("123"); + await $(NumberPage.number()).setValue("123"); // Then - expect($(NumberPage.number()).getValue()).to.contain("123"); - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).to.contain("123"); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(NumberPage.submit()).click(); + await $(NumberPage.submit()).click(); - expect($(SummaryPage.numberAnswer()).getText()).to.have.string("123"); - expect($(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); + await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive number answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(NumberPage.number()).getValue()).to.contain(""); + await expect(await $(NumberPage.number()).getValue()).to.contain(""); // When - $(NumberPage.numberExclusiveIPreferNotToSay()).click(); - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(NumberPage.submit()).click(); + await $(NumberPage.submit()).click(); - expect($(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(NumberPage.number()).getValue()).to.contain(""); - expect($(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).to.contain(""); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(NumberPage.submit()).click(); + await $(NumberPage.submit()).click(); // Then - expect($(SummaryPage.numberAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.numberAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js index 5676f3bf07..ee491db85f 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js @@ -2,98 +2,100 @@ import PercentagePage from "../../../../generated_pages/mutually_exclusive/mutua import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-percentage-section-summary.page"; describe("Component: Mutually Exclusive Percentage With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-percentage"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-percentage"); }); describe("Given the user has entered a value for the non-exclusive percentage answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(PercentagePage.percentage()).setValue("99"); - expect($(PercentagePage.percentage()).getValue()).to.contain("99"); + await $(PercentagePage.percentage()).setValue("99"); + await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); // When - $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); + await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); // Then - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(PercentagePage.percentage()).getValue()).to.contain(""); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); - $(PercentagePage.submit()).click(); + await $(PercentagePage.submit()).click(); - expect($(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("99"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("99"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive percentage answer and removes focus, Then only the non-exclusive percentage answer should be answered.", () => { + it("When the user enters a value for the non-exclusive percentage answer and removes focus, Then only the non-exclusive percentage answer should be answered.", async () => { // Given - $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await browser.url("/questionnaire/mutually-exclusive-percentage"); + await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(PercentagePage.percentage()).setValue("99"); + await $(PercentagePage.percentage()).setValue("99"); // Then - expect($(PercentagePage.percentage()).getValue()).to.contain("99"); - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(PercentagePage.submit()).click(); + await $(PercentagePage.submit()).click(); - expect($(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); - expect($(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive percentage answer, Then only the non-exclusive percentage answer should be answered.", () => { + it("When the user enters a value for the non-exclusive percentage answer, Then only the non-exclusive percentage answer should be answered.", async () => { // Given - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(PercentagePage.percentage()).setValue("99"); + await $(PercentagePage.percentage()).setValue("99"); // Then - expect($(PercentagePage.percentage()).getValue()).to.contain("99"); - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(PercentagePage.submit()).click(); + await $(PercentagePage.submit()).click(); - expect($(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); - expect($(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive percentage answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(PercentagePage.percentage()).getValue()).to.contain(""); + await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); // When - $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(PercentagePage.submit()).click(); + await $(PercentagePage.submit()).click(); - expect($(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(PercentagePage.percentage()).getValue()).to.contain(""); - expect($(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(PercentagePage.submit()).click(); + await $(PercentagePage.submit()).click(); // Then - expect($(SummaryPage.percentageAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js index e1b2044a19..67ea0e6f9a 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js @@ -2,58 +2,59 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive/mutual import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textarea-section-summary.page"; describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-textarea"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-textarea"); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textarea answer, Then only the non-exclusive textarea answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textarea answer, Then only the non-exclusive textarea answer should be answered.", async () => { // Given - expect($(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(TextFieldPage.textarea()).setValue("Blue"); + await $(TextFieldPage.textarea()).setValue("Blue"); // Then - expect($(TextFieldPage.textarea()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textarea()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textareaAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textareaAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive textarea answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(TextFieldPage.textarea()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textarea()).getValue()).to.contain(""); // When - $(TextFieldPage.textareaExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textareaExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.textareaExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(TextFieldPage.textarea()).getValue()).to.contain(""); - expect($(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textarea()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); // Then - expect($(SummaryPage.textareaAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js index 3b83802a2e..fd2de49deb 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js @@ -2,98 +2,99 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive/mutual import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textfield-section-summary.page"; describe("Component: Mutually Exclusive Textfield With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-textfield"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-textfield"); }); describe("Given the user has entered a value for the non-exclusive textfield answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(TextFieldPage.textfield()).setValue("Blue"); - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); // When - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); // Then - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(TextFieldPage.textfield()).setValue("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); // Then - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", () => { + it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(TextFieldPage.textfield()).setValue("Blue"); + await $(TextFieldPage.textfield()).setValue("Blue"); // Then - expect($(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - expect($(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive textfield answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); // When - $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(TextFieldPage.textfield()).getValue()).to.contain(""); - expect($(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(TextFieldPage.submit()).click(); + await $(TextFieldPage.submit()).click(); // Then - expect($(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js index 9aaf41538a..3589bc65f9 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js @@ -2,98 +2,99 @@ import UnitPage from "../../../../generated_pages/mutually_exclusive/mutually-ex import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-unit-section-summary.page"; describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-unit"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-unit"); }); describe("Given the user has entered a value for the non-exclusive unit answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(UnitPage.unit()).setValue("10"); - expect($(UnitPage.unit()).getValue()).to.contain("10"); + await $(UnitPage.unit()).setValue("10"); + await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); // When - $(UnitPage.unitExclusiveIPreferNotToSay()).click(); + await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); // Then - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(UnitPage.unit()).getValue()).to.contain(""); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(UnitPage.unit()).getValue()).to.contain(""); - $(UnitPage.submit()).click(); + await $(UnitPage.submit()).click(); - expect($(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive unit answer and removes focus, Then only the non-exclusive unit answer should be answered.", () => { + it("When the user enters a value for the non-exclusive unit answer and removes focus, Then only the non-exclusive unit answer should be answered.", async () => { // Given - $(UnitPage.unitExclusiveIPreferNotToSay()).click(); - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(UnitPage.unit()).setValue("10"); + await $(UnitPage.unit()).setValue("10"); // Then - expect($(UnitPage.unit()).getValue()).to.contain("10"); - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(UnitPage.submit()).click(); + await $(UnitPage.submit()).click(); - expect($(SummaryPage.unitAnswer()).getText()).to.have.string("10"); - expect($(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); + await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive unit answer, Then only the non-exclusive unit answer should be answered.", () => { + it("When the user enters a value for the non-exclusive unit answer, Then only the non-exclusive unit answer should be answered.", async () => { // Given - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(UnitPage.unit()).setValue("10"); + await $(UnitPage.unit()).setValue("10"); // Then - expect($(UnitPage.unit()).getValue()).to.contain("10"); - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(UnitPage.submit()).click(); + await $(UnitPage.submit()).click(); - expect($(SummaryPage.unitAnswer()).getText()).to.have.string("10"); - expect($(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); + await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive unit answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(UnitPage.unit()).getValue()).to.contain(""); + await expect(await $(UnitPage.unit()).getValue()).to.contain(""); // When - $(UnitPage.unitExclusiveIPreferNotToSay()).click(); - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(UnitPage.submit()).click(); + await $(UnitPage.submit()).click(); - expect($(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(UnitPage.unit()).getValue()).to.contain(""); - expect($(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).to.contain(""); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(UnitPage.submit()).click(); + await $(UnitPage.submit()).click(); // Then - expect($(SummaryPage.unitAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.unitAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js index 1ce56cd09f..6f45c75d81 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js @@ -2,98 +2,99 @@ import YearDatePage from "../../../../generated_pages/mutually_exclusive/mutuall import SubmitPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-year-date-section-summary.page"; describe("Component: Mutually Exclusive Year Date With Single Checkbox Override", () => { - beforeEach(() => { - browser.openQuestionnaire("test_mutually_exclusive.json"); - browser.url("/questionnaire/mutually-exclusive-year-date"); + beforeEach(async () => { + await browser.openQuestionnaire("test_mutually_exclusive.json"); + await browser.pause(100); + await browser.url("/questionnaire/mutually-exclusive-year-date"); }); describe("Given the user has entered a value for the non-exclusive year date answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", () => { + it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given - $(YearDatePage.yearDateYear()).setValue("2018"); - expect($(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); + await $(YearDatePage.yearDateYear()).setValue("2018"); + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); // When - $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); + await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); // Then - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - expect($(YearDatePage.yearDateYear()).getValue()).to.contain(""); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); - $(YearDatePage.submit()).click(); + await $(YearDatePage.submit()).click(); - expect($(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); }); }); describe("Given the user has clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive year date answer and removes focus, Then only the non-exclusive year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive year date answer and removes focus, Then only the non-exclusive year date answer should be answered.", async () => { // Given - $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // When - $(YearDatePage.yearDateYear()).setValue("2018"); + await $(YearDatePage.yearDateYear()).setValue("2018"); // Then - expect($(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(YearDatePage.submit()).click(); + await $(YearDatePage.submit()).click(); - expect($(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); - expect($(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { - it("When the user enters a value for the non-exclusive year date answer, Then only the non-exclusive year date answer should be answered.", () => { + it("When the user enters a value for the non-exclusive year date answer, Then only the non-exclusive year date answer should be answered.", async () => { // Given - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(YearDatePage.yearDateYear()).setValue("2018"); + await $(YearDatePage.yearDateYear()).setValue("2018"); // Then - expect($(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - $(YearDatePage.submit()).click(); + await $(YearDatePage.submit()).click(); - expect($(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); - expect($(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive year date answer", () => { - it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - expect($(YearDatePage.yearDateYear()).getValue()).to.contain(""); + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); // When - $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - $(YearDatePage.submit()).click(); + await $(YearDatePage.submit()).click(); - expect($(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - expect($(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { - it("When the user clicks the Continue button, Then it should display `No answer provided`", () => { + it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - expect($(YearDatePage.yearDateYear()).getValue()).to.contain(""); - expect($(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - $(YearDatePage.submit()).click(); + await $(YearDatePage.submit()).click(); // Then - expect($(SubmitPage.yearDateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/dropdown/dropdown.spec.js b/tests/functional/spec/components/dropdown/dropdown.spec.js index 1c7a5c36c5..652a35ba30 100644 --- a/tests/functional/spec/components/dropdown/dropdown.spec.js +++ b/tests/functional/spec/components/dropdown/dropdown.spec.js @@ -7,92 +7,92 @@ import DropdownOptionalSummary from "../../../generated_pages/dropdown_optional/ describe("Component: Dropdown", () => { // Mandatory describe("Given I start a Mandatory Dropdown survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_dropdown_mandatory.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_dropdown_mandatory.json"); }); - it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", () => { - $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); }); - it("When I have not selected a dropdown option and click Continue, Then the default error message should be displayed", () => { - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatoryPage.errorNumber(1)).getText()).to.contain("Select an answer"); + it("When I have not selected a dropdown option and click Continue, Then the default error message should be displayed", async () => { + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.contain("Select an answer"); }); - it("When I have selected a dropdown option and I try to select a default (disabled) dropdown option, Then the already selected option should be displayed in summary", () => { - $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); - $(DropdownMandatoryPage.answer()).selectByAttribute("value", ""); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); + it("When I have selected a dropdown option and I try to select a default (disabled) dropdown option, Then the already selected option should be displayed in summary", async () => { + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", ""); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); }); - it("When I click the dropdown label, Then the dropdown should be focused", () => { - $(DropdownMandatoryPage.answerLabel()).click(); - expect($(DropdownMandatoryPage.answer()).isFocused()).to.be.true; + it("When I click the dropdown label, Then the dropdown should be focused", async () => { + await $(DropdownMandatoryPage.answerLabel()).click(); + await expect(await $(DropdownMandatoryPage.answer()).isFocused()).to.be.true; }); - it("When I'm on the summary page and I click Edit then Continue, Then the answer on the summary page should be unchanged", () => { - $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); - $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + it("When I'm on the summary page and I click Edit then Continue, Then the answer on the summary page should be unchanged", async () => { + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); }); - it("When I'm on the summary page and I click Edit and change the answer, Then the newly selected answer should be displayed in the summary", () => { - $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); - $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); - $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); - $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); - $(DropdownMandatoryPage.submit()).click(); - expect($(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); + it("When I'm on the summary page and I click Edit and change the answer, Then the newly selected answer should be displayed in the summary", async () => { + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); + await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); + await $(DropdownMandatoryPage.submit()).click(); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); }); }); describe("Given I start a Mandatory With Overridden Error Dropdown survey", () => { - before(() => { - browser.openQuestionnaire("test_dropdown_mandatory_with_overridden_error.json"); + before(async () => { + await browser.openQuestionnaire("test_dropdown_mandatory_with_overridden_error.json"); }); - it("When I have not selected a dropdown option and click Continue, Then the overridden error message should be displayed", () => { - $(DropdownMandatoryOverriddenPage.submit()).click(); - expect($(DropdownMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Overridden test error message."); + it("When I have not selected a dropdown option and click Continue, Then the overridden error message should be displayed", async () => { + await $(DropdownMandatoryOverriddenPage.submit()).click(); + await expect(await $(DropdownMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Overridden test error message."); }); }); // Optional describe("Given I start a Optional Dropdown survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_dropdown_optional.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_dropdown_optional.json"); }); - it('When I have not selected a dropdown option, Then the summary should display "No answer provided"', () => { - $(DropdownOptionalPage.submit()).click(); - expect($(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); + it('When I have not selected a dropdown option, Then the summary should display "No answer provided"', async () => { + await $(DropdownOptionalPage.submit()).click(); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); }); - it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", () => { - $(DropdownOptionalPage.answer()).selectByAttribute("value", "Rugby is better!"); - $(DropdownOptionalPage.submit()).click(); - expect($(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Rugby is better!"); + it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { + await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Rugby is better!"); + await $(DropdownOptionalPage.submit()).click(); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Rugby is better!"); }); - it('When I have selected a dropdown option and I reselect the default option (Select an answer), Then the summary should display "No answer provided"', () => { - $(DropdownOptionalPage.answer()).selectByAttribute("value", "Chelsea"); - $(DropdownOptionalPage.submit()).click(); - expect($(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Chelsea"); - $(DropdownOptionalSummary.dropdownOptionalAnswerEdit()).click(); - $(DropdownOptionalPage.answer()).selectByAttribute("value", ""); - $(DropdownOptionalPage.submit()).click(); - expect($(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); + it('When I have selected a dropdown option and I reselect the default option (Select an answer), Then the summary should display "No answer provided"', async () => { + await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Chelsea"); + await $(DropdownOptionalPage.submit()).click(); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Chelsea"); + await $(DropdownOptionalSummary.dropdownOptionalAnswerEdit()).click(); + await $(DropdownOptionalPage.answer()).selectByAttribute("value", ""); + await $(DropdownOptionalPage.submit()).click(); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio.js b/tests/functional/spec/components/radio/radio.js index d2bbcba1b6..ccdf6c3e88 100644 --- a/tests/functional/spec/components/radio/radio.js +++ b/tests/functional/spec/components/radio/radio.js @@ -18,128 +18,128 @@ import RadioNonMandatoryDetailAnswerSummary from "../../../generated_pages/radio describe("Component: Radio", () => { describe("Given I start a Mandatory Radio survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory.json"); }); - it("When I have selected a radio option that contains an escaped character, Then the selected option should be displayed in the summary", () => { - $(RadioMandatoryPage.teaCoffee()).click(); - $(RadioMandatoryPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioMandatorySummary.pageName); - expect($(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Tea & Coffee"); + it("When I have selected a radio option that contains an escaped character, Then the selected option should be displayed in the summary", async () => { + await $(RadioMandatoryPage.teaCoffee()).click(); + await $(RadioMandatoryPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); + await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Tea & Coffee"); }); }); describe("Given I start a Mandatory Radio survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory.json"); }); - it("When I have selected a radio option, Then the selected option should be displayed in the summary", () => { - $(RadioMandatoryPage.coffee()).click(); - $(RadioMandatoryPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioMandatorySummary.pageName); - expect($(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Coffee"); + it("When I have selected a radio option, Then the selected option should be displayed in the summary", async () => { + await $(RadioMandatoryPage.coffee()).click(); + await $(RadioMandatoryPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); + await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Coffee"); }); }); describe("Given I start a Mandatory Radio survey ", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory.json"); }); - it("When I have submitted the page without any option, Then the question text is hidden in the error message using a span element", () => { - $(RadioMandatoryOverriddenPage.submit()).click(); - expect($(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).to.contain( + it("When I have submitted the page without any option, Then the question text is hidden in the error message using a span element", async () => { + await $(RadioMandatoryOverriddenPage.submit()).click(); + await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).to.contain( 'Select an answer to ‘What do you prefer for breakfast?’' ); }); }); describe("Given I start a Mandatory Radio DetailAnswer survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_mandatory.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_mandatory.json"); }); - it("When I have selected a other text field, Then the selected option should be displayed in the summary", () => { - $(RadioMandatoryOptionalDetailAnswerPage.other()).click(); - $(RadioMandatoryOptionalDetailAnswerPage.otherDetail()).setValue("Hello World"); - $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); - expect($(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("Hello World"); + it("When I have selected a other text field, Then the selected option should be displayed in the summary", async () => { + await $(RadioMandatoryOptionalDetailAnswerPage.other()).click(); + await $(RadioMandatoryOptionalDetailAnswerPage.otherDetail()).setValue("Hello World"); + await $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); + await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("Hello World"); }); }); describe("Given I start a Mandatory Radio DetailAnswer Overridden Error survey ", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_mandatory_with_overridden_error.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_mandatory_with_overridden_error.json"); }); - it("When I submit without any data in the other text field it should Then throw an overridden error", () => { - $(RadioMandatoryDetailAnswerOverriddenPage.other()).click(); - $(RadioMandatoryDetailAnswerOverriddenPage.submit()).click(); - expect($(RadioMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + it("When I submit without any data in the other text field it should Then throw an overridden error", async () => { + await $(RadioMandatoryDetailAnswerOverriddenPage.other()).click(); + await $(RadioMandatoryDetailAnswerOverriddenPage.submit()).click(); + await expect(await $(RadioMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); describe("Given I start a Mandatory Radio DetailAnswer survey ", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_optional.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory_with_detail_answer_optional.json"); }); - it("When I submit without any data in the other text field is selected, Then the selected option should be displayed in the summary", () => { - $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); - expect($(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("No answer provided"); + it("When I submit without any data in the other text field is selected, Then the selected option should be displayed in the summary", async () => { + await $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); + await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("No answer provided"); }); }); describe("Given I start a Mandatory Radio DetailAnswer Overridden error survey ", () => { - before(() => { - browser.openQuestionnaire("test_radio_mandatory_with_overridden_error.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_mandatory_with_overridden_error.json"); }); - it("When I have submitted the page without any option, Then an overridden error is displayed", () => { - $(RadioMandatoryOverriddenPage.submit()).click(); - expect($(RadioMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + it("When I have submitted the page without any option, Then an overridden error is displayed", async () => { + await $(RadioMandatoryOverriddenPage.submit()).click(); + await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); describe("Given I start a Optional survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_optional.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_optional.json"); }); - it("When I have selected no option, Then the selected option should be displayed in the summary", () => { - $(RadioNonMandatoryPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioNonMandatorySummary.pageName); - expect($(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); + it("When I have selected no option, Then the selected option should be displayed in the summary", async () => { + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioNonMandatorySummary.pageName); + await expect(await $(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); }); }); describe("Given I start a Optional DetailAnswer Overridden error survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_optional_with_detail_answer_mandatory_with_overridden_error.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_optional_with_detail_answer_mandatory_with_overridden_error.json"); }); - it("When I have submitted an other option with an empty text field, Then an overridden error is displayed", () => { - $(RadioNonMandatoryDetailAnswerOverriddenPage.other()).click(); - $(RadioNonMandatoryDetailAnswerOverriddenPage.submit()).click(); - expect($(RadioNonMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + it("When I have submitted an other option with an empty text field, Then an overridden error is displayed", async () => { + await $(RadioNonMandatoryDetailAnswerOverriddenPage.other()).click(); + await $(RadioNonMandatoryDetailAnswerOverriddenPage.submit()).click(); + await expect(await $(RadioNonMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); describe("Given I Start a Optional Mandatory DetailAnswer survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_optional_with_detail_answer_mandatory.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_optional_with_detail_answer_mandatory.json"); }); - it("When I submit data in the other text field it should be persisted and Then displayed on the summary", () => { - $(RadioNonMandatoryDetailAnswerPage.other()).click(); - $(RadioNonMandatoryDetailAnswerPage.otherDetail()).setValue("Hello World"); - $(RadioNonMandatoryDetailAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(RadioNonMandatoryDetailAnswerSummary.pageName); - expect($(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).to.contain("Hello World"); + it("When I submit data in the other text field it should be persisted and Then displayed on the summary", async () => { + await $(RadioNonMandatoryDetailAnswerPage.other()).click(); + await $(RadioNonMandatoryDetailAnswerPage.otherDetail()).setValue("Hello World"); + await $(RadioNonMandatoryDetailAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RadioNonMandatoryDetailAnswerSummary.pageName); + await expect(await $(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).to.contain("Hello World"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js index d68d3ce3b2..3e88f65451 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js @@ -3,79 +3,79 @@ import SubmitPage from "../../../generated_pages/radio_detail_answer_dropdown/su import DropdownMandatoryPage from "../../../generated_pages/dropdown_mandatory/dropdown-mandatory.page"; describe("Optional Radio with a Dropdown detail answer", () => { - beforeEach(() => { - browser.openQuestionnaire("test_radio_detail_answer_dropdown.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_radio_detail_answer_dropdown.json"); }); describe("Given an optional radio with a dropdown detail answer", () => { - it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", () => { - $(RadioDropdownPage.fruit()).click(); + it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", async () => { + await $(RadioDropdownPage.fruit()).click(); - expect($(RadioDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); + await expect(await $(RadioDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); }); - it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", () => { - $(RadioDropdownPage.jam()).click(); + it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", async () => { + await $(RadioDropdownPage.jam()).click(); - expect($(RadioDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); + await expect(await $(RadioDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); }); - it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", () => { - $(RadioDropdownPage.submit()).click(); + it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); - it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", () => { - $(RadioDropdownPage.fruit()).click(); - $(RadioDropdownPage.submit()).click(); + it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { + await $(RadioDropdownPage.fruit()).click(); + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); - it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", () => { - $(RadioDropdownPage.fruit()).click(); - $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(RadioDropdownPage.submit()).click(); + it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { + await $(RadioDropdownPage.fruit()).click(); + await $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); }); - it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", () => { - $(RadioDropdownPage.fruit()).click(); - $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - $(RadioDropdownPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(RadioDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); - $(RadioDropdownPage.submit()).click(); + it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { + await $(RadioDropdownPage.fruit()).click(); + await $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); + await $(RadioDropdownPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(RadioDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); - it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", () => { - $(RadioDropdownPage.jam()).click(); - $(RadioDropdownPage.submit()).click(); + it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { + await $(RadioDropdownPage.jam()).click(); + await $(RadioDropdownPage.submit()).click(); - expect($(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); }); - it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its detail answer", () => { - $(RadioDropdownPage.jam()).click(); - $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - $(RadioDropdownPage.submit()).click(); + it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its detail answer", async () => { + await $(RadioDropdownPage.jam()).click(); + await $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); }); - it("When the user removes a previously submitted detail answer by selecting another radio option, Then the summary should only display the new radio option", () => { - $(RadioDropdownPage.jam()).click(); - $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Raspberry"); - $(RadioDropdownPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(RadioDropdownPage.fruit()).click(); - $(RadioDropdownPage.submit()).click(); + it("When the user removes a previously submitted detail answer by selecting another radio option, Then the summary should only display the new radio option", async () => { + await $(RadioDropdownPage.jam()).click(); + await $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Raspberry"); + await $(RadioDropdownPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(RadioDropdownPage.fruit()).click(); + await $(RadioDropdownPage.submit()).click(); - expect($(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js index cc3e888f47..59b7820211 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js @@ -4,73 +4,73 @@ import SubmitPage from "../../../generated_pages/radio_detail_answer_multiple/su describe('Radio with multiple "detail_answer" options', () => { const radioSchema = "test_radio_detail_answer_multiple.json"; - it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", () => { - browser.openQuestionnaire(radioSchema); - $(MandatoryRadioPage.eggs()).click(); - expect($(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; - $(MandatoryRadioPage.favouriteNotListed()).click(); - expect($(MandatoryRadioPage.favouriteNotListedDetail()).isDisplayed()).to.be.true; + it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { + await browser.openQuestionnaire(radioSchema); + await $(MandatoryRadioPage.eggs()).click(); + await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; + await $(MandatoryRadioPage.favouriteNotListed()).click(); + await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).isDisplayed()).to.be.true; }); - it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", () => { + it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", async () => { // Given - browser.openQuestionnaire(radioSchema); + await browser.openQuestionnaire(radioSchema); // When - $(MandatoryRadioPage.favouriteNotListed()).click(); - $(MandatoryRadioPage.submit()).click(); + await $(MandatoryRadioPage.favouriteNotListed()).click(); + await $(MandatoryRadioPage.submit()).click(); // Then - expect($(MandatoryRadioPage.error()).isDisplayed()).to.be.true; - expect($(MandatoryRadioPage.errorNumber(1)).getText()).to.contain("Enter your favourite to continue"); + await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryRadioPage.errorNumber(1)).getText()).to.contain("Enter your favourite to continue"); }); - it("Given a selected radio answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", () => { + it("Given a selected radio answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", async () => { // Given - browser.openQuestionnaire(radioSchema); - $(MandatoryRadioPage.favouriteNotListed()).click(); - $(MandatoryRadioPage.submit()).click(); - expect($(MandatoryRadioPage.error()).isDisplayed()).to.be.true; + await browser.openQuestionnaire(radioSchema); + await $(MandatoryRadioPage.favouriteNotListed()).click(); + await $(MandatoryRadioPage.submit()).click(); + await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; // When - $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); - $(MandatoryRadioPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); + await $(MandatoryRadioPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", () => { + it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // Given - browser.openQuestionnaire(radioSchema); + await browser.openQuestionnaire(radioSchema); // When - $(MandatoryRadioPage.eggs()).click(); - expect($(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; - $(MandatoryRadioPage.submit()).click(); + await $(MandatoryRadioPage.eggs()).click(); + await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; + await $(MandatoryRadioPage.submit()).click(); // Then - expect($(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs"); + await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs"); }); - it("Given a detail answer, When the user provides text, Then that text should be displayed on the summary screen", () => { + it("Given a detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { // Given - browser.openQuestionnaire(radioSchema); + await browser.openQuestionnaire(radioSchema); // When - $(MandatoryRadioPage.eggs()).click(); - $(MandatoryRadioPage.eggsDetail()).setValue("Scrambled"); - $(MandatoryRadioPage.submit()).click(); + await $(MandatoryRadioPage.eggs()).click(); + await $(MandatoryRadioPage.eggsDetail()).setValue("Scrambled"); + await $(MandatoryRadioPage.submit()).click(); // Then - expect($(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs\nScrambled"); + await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs\nScrambled"); }); - it("Given I have previously added text in a detail answer and saved, When I select a different radio and save, Then the text entered in the detail answer field should be empty.", () => { + it("Given I have previously added text in a detail answer and saved, When I select a different radio and save, Then the text entered in the detail answer field should be empty.", async () => { // Given - browser.openQuestionnaire(radioSchema); + await browser.openQuestionnaire(radioSchema); // When - $(MandatoryRadioPage.favouriteNotListed()).click(); - $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); - $(MandatoryRadioPage.submit()).click(); - $(SubmitPage.previous()).click(); - $(MandatoryRadioPage.eggs()).click(); - $(MandatoryRadioPage.submit()).click(); - $(SubmitPage.previous()).click(); + await $(MandatoryRadioPage.favouriteNotListed()).click(); + await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); + await $(MandatoryRadioPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await $(MandatoryRadioPage.eggs()).click(); + await $(MandatoryRadioPage.submit()).click(); + await $(SubmitPage.previous()).click(); // Then - $(MandatoryRadioPage.favouriteNotListed()).click(); - expect($(MandatoryRadioPage.favouriteNotListedDetail()).getValue()).to.equal(""); + await $(MandatoryRadioPage.favouriteNotListed()).click(); + await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).getValue()).to.equal(""); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js index c3c00675bd..904d6d5324 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js @@ -2,73 +2,73 @@ import RadioNumericDetailPage from "../../../generated_pages/radio_detail_answer import SubmitPage from "../../../generated_pages/radio_detail_answer_numeric/submit.page"; describe('Radio with a numeric "detail_answer" option', () => { - beforeEach(() => { - browser.openQuestionnaire("test_radio_detail_answer_numeric.json"); - $(RadioNumericDetailPage.other()).click(); + beforeEach(async () => { + await browser.openQuestionnaire("test_radio_detail_answer_numeric.json"); + await $(RadioNumericDetailPage.other()).click(); }); - it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", () => { - expect($(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { + await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; }); - it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", () => { + it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When - expect($(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; - $(RadioNumericDetailPage.submit()).click(); + await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("Other"); }); - it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", () => { + it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("15"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("15"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("15"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("15"); }); - it("Given a numeric detail answer, When the user provides text, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); }); - it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("250"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("250"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); }); - it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", () => { + it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("-1"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("-1"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); }); - it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", () => { + it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("biscuits"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("biscuits"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - expect($(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); - expect($(RadioNumericDetailPage.otherDetail()).getValue()).to.contain("biscuits"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(RadioNumericDetailPage.otherDetail()).getValue()).to.contain("biscuits"); }); - it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', () => { + it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { // When - $(RadioNumericDetailPage.otherDetail()).setValue("0"); - $(RadioNumericDetailPage.submit()).click(); + await $(RadioNumericDetailPage.otherDetail()).setValue("0"); + await $(RadioNumericDetailPage.submit()).click(); // Then - expect($(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("0"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("0"); }); }); diff --git a/tests/functional/spec/components/radio/radio_visible_answers.spec.js b/tests/functional/spec/components/radio/radio_visible_answers.spec.js index 7f477b0a65..612fbd7864 100644 --- a/tests/functional/spec/components/radio/radio_visible_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_visible_answers.spec.js @@ -3,30 +3,30 @@ import RadioVisibleFalsePage from "../../../generated_pages/radio_detail_answer_ import RadioVisibleNonePage from "../../../generated_pages/radio_detail_answer_visible/radio-visible-none.page.js"; describe("Given I start a Radio survey with a write-in option", () => { - beforeEach(() => { - browser.openQuestionnaire("test_radio_detail_answer_visible.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_radio_detail_answer_visible.json"); }); - it("When I view a write-in radio and the visible option is set to true, Then the detail answer label should be displayed", () => { - expect($(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); + it("When I view a write-in radio and the visible option is set to true, Then the detail answer label should be displayed", async () => { + await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); }); - it("When I view a write-in radio and the visible option is set to true, Then after choosing non write-in option the detail answer label should be displayed", () => { - $(RadioVisibleTruePage.coffee()).click(); - expect($(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); + it("When I view a write-in radio and the visible option is set to true, Then after choosing non write-in option the detail answer label should be displayed", async () => { + await $(RadioVisibleTruePage.coffee()).click(); + await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); }); - it("When I view a write-in radio and the visible option is set to false, Then the detail answer label should not be displayed", () => { - $(RadioVisibleTruePage.coffee()).click(); - $(RadioVisibleTruePage.submit()).click(); - expect($(RadioVisibleFalsePage.otherDetail()).isDisplayed()).to.equal(false); + it("When I view a write-in radio and the visible option is set to false, Then the detail answer label should not be displayed", async () => { + await $(RadioVisibleTruePage.coffee()).click(); + await $(RadioVisibleTruePage.submit()).click(); + await expect(await $(RadioVisibleFalsePage.otherDetail()).isDisplayed()).to.equal(false); }); - it("When I view a write-in radio and the visible option is not set, Then the detail answer label should not be displayed", () => { - $(RadioVisibleTruePage.coffee()).click(); - $(RadioVisibleFalsePage.submit()).click(); - $(RadioVisibleFalsePage.iceCream()).click(); - $(RadioVisibleFalsePage.submit()).click(); - expect($(RadioVisibleNonePage.otherDetail()).isDisplayed()).to.equal(false); + it("When I view a write-in radio and the visible option is not set, Then the detail answer label should not be displayed", async () => { + await $(RadioVisibleTruePage.coffee()).click(); + await $(RadioVisibleFalsePage.submit()).click(); + await $(RadioVisibleFalsePage.iceCream()).click(); + await $(RadioVisibleFalsePage.submit()).click(); + await expect(await $(RadioVisibleNonePage.otherDetail()).isDisplayed()).to.equal(false); }); }); diff --git a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js index 180cf105ae..c6af4619cb 100644 --- a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js @@ -3,37 +3,37 @@ import RadioVoluntaryFalsePage from "../../../generated_pages/radio_voluntary/ra describe("Component: Radio", () => { describe("Given I start a Voluntary Radio survey", () => { - before(() => { - browser.openQuestionnaire("test_radio_voluntary.json"); + before(async () => { + await browser.openQuestionnaire("test_radio_voluntary.json"); }); - it("When I select a voluntary radio option, Then the clear button should be displayed", () => { - $(RadioVoluntaryTruePage.coffee()).click(); - expect($(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(true); + it("When I select a voluntary radio option, Then the clear button should be displayed", async () => { + await $(RadioVoluntaryTruePage.coffee()).click(); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(true); }); - it("When I select a voluntary radio option and click the clear button, Then the radio option should not be selected and the clear button should not be displayed", () => { - $(RadioVoluntaryTruePage.coffee()).click(); - $(RadioVoluntaryTruePage.clearSelectionButton()).click(); - expect($(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); - expect($(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); + it("When I select a voluntary radio option and click the clear button, Then the radio option should not be selected and the clear button should not be displayed", async () => { + await $(RadioVoluntaryTruePage.coffee()).click(); + await $(RadioVoluntaryTruePage.clearSelectionButton()).click(); + await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); }); - it("When I clear a previously saved voluntary radio option and submit, Then when returning to the page the radio option is no longer selected", () => { - $(RadioVoluntaryTruePage.coffee()).click(); - $(RadioVoluntaryTruePage.submit()).click(); - $(RadioVoluntaryTruePage.previous()).click(); - $(RadioVoluntaryTruePage.clearSelectionButton()).click(); - $(RadioVoluntaryTruePage.submit()).click(); - $(RadioVoluntaryTruePage.previous()).click(); - expect($(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); - expect($(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); + it("When I clear a previously saved voluntary radio option and submit, Then when returning to the page the radio option is no longer selected", async () => { + await $(RadioVoluntaryTruePage.coffee()).click(); + await $(RadioVoluntaryTruePage.submit()).click(); + await $(RadioVoluntaryTruePage.previous()).click(); + await $(RadioVoluntaryTruePage.clearSelectionButton()).click(); + await $(RadioVoluntaryTruePage.submit()).click(); + await $(RadioVoluntaryTruePage.previous()).click(); + await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); }); - it("When I select a non-voluntary radio option, Then the clear button should not be displayed on the page", () => { - $(RadioVoluntaryTruePage.submit()).click(); - $(RadioVoluntaryFalsePage.iceCream()).click(); - expect($(RadioVoluntaryFalsePage.clearSelectionButton()).isDisplayed()).to.equal(false); + it("When I select a non-voluntary radio option, Then the clear button should not be displayed on the page", async () => { + await $(RadioVoluntaryTruePage.submit()).click(); + await $(RadioVoluntaryFalsePage.iceCream()).click(); + await expect(await $(RadioVoluntaryFalsePage.clearSelectionButton()).isDisplayed()).to.equal(false); }); }); }); diff --git a/tests/functional/spec/conditional_combined_routing.spec.js b/tests/functional/spec/conditional_combined_routing.spec.js index 6e2ce6fa65..31b1fff6d0 100644 --- a/tests/functional/spec/conditional_combined_routing.spec.js +++ b/tests/functional/spec/conditional_combined_routing.spec.js @@ -4,41 +4,41 @@ import ResponseNotAny from "../generated_pages/conditional_combined_routing/resp import SubmitPage from "../generated_pages/conditional_combined_routing/submit.page"; describe("Conditional combined routing.", () => { - beforeEach(() => { - browser.openQuestionnaire("test_conditional_combined_routing.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_conditional_combined_routing.json"); }); - it('Given a list of radio options, when I choose the option "Yes" or the option "Sometimes" then I should be routed to the relevant page', () => { + it('Given a list of radio options, when I choose the option "Yes" or the option "Sometimes" then I should be routed to the relevant page', async () => { // When - $(ConditionalCombinedRoutingPage.yes()).click(); - $(ConditionalCombinedRoutingPage.submit()).click(); + await $(ConditionalCombinedRoutingPage.yes()).click(); + await $(ConditionalCombinedRoutingPage.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ResponseAny.pageName); + await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); // Or - $(ResponseAny.previous()).click(); + await $(ResponseAny.previous()).click(); // When - $(ConditionalCombinedRoutingPage.sometimes()).click(); - $(ConditionalCombinedRoutingPage.submit()).click(); + await $(ConditionalCombinedRoutingPage.sometimes()).click(); + await $(ConditionalCombinedRoutingPage.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ResponseAny.pageName); + await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); }); - it('Given a list of radio options, when I choose the option "No, I prefer tea" then I should be routed to the relevant page', () => { + it('Given a list of radio options, when I choose the option "No, I prefer tea" then I should be routed to the relevant page', async () => { // When - $(ConditionalCombinedRoutingPage.noIPreferTea()).click(); - $(ConditionalCombinedRoutingPage.submit()).click(); + await $(ConditionalCombinedRoutingPage.noIPreferTea()).click(); + await $(ConditionalCombinedRoutingPage.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ResponseNotAny.pageName); + await expect(await browser.getUrl()).to.contain(ResponseNotAny.pageName); }); - it('Given a list of radio options, when I choose the option "No, I don\'t drink any hot drinks" then I should be routed to the submit page', () => { + it('Given a list of radio options, when I choose the option "No, I don\'t drink any hot drinks" then I should be routed to the submit page', async () => { // When - $(ConditionalCombinedRoutingPage.noIDonTDrinkAnyHotDrinks()).click(); - $(ConditionalCombinedRoutingPage.submit()).click(); + await $(ConditionalCombinedRoutingPage.noIDonTDrinkAnyHotDrinks()).click(); + await $(ConditionalCombinedRoutingPage.submit()).click(); // Then - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index 38ec60fa59..bb1cb7c429 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -7,106 +7,106 @@ import ConfirmEmailPage from "../base_pages/confirm-email.page"; describe("Email confirmation", () => { describe("Given I launch the test email confirmation survey", () => { - before(() => { - browser.openQuestionnaire("test_confirmation_email.json"); + before(async () => { + await browser.openQuestionnaire("test_confirmation_email.json"); }); - it("When I complete the survey and am on the thank you page, Then there is option to enter an email address", () => { - $(SubmitPage.submit()).click(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.email()).isExisting()).to.be.true; + it("When I complete the survey and am on the thank you page, Then there is option to enter an email address", async () => { + await $(SubmitPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.email()).isExisting()).to.be.true; }); - it("When I submit the form without providing an email address, Then I get an error message", () => { - $(ThankYouPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.errorPanel()).isExisting()).to.be.true; - expect($(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address"); + it("When I submit the form without providing an email address, Then I get an error message", async () => { + await $(ThankYouPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address"); }); - it("When I submit the form without providing a correctly formatted email address, Then I get an error message", () => { - $(ThankYouPage.email()).setValue("incorrect-format"); - $(ThankYouPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.errorPanel()).isExisting()).to.be.true; - expect($(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address in a valid format, for example name@example.com"); + it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { + await $(ThankYouPage.email()).setValue("incorrect-format"); + await $(ThankYouPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address in a valid format, for example name@example.com"); }); - it("When I submit the form with a valid email address, Then I go to the confirm email page", () => { - $(ThankYouPage.email()).setValue("name@example.com"); - $(ThankYouPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/confirm"); - expect($(ConfirmEmailPage.questionTitle()).getText()).to.equal("Is this email address correct?"); + it("When I submit the form with a valid email address, Then I go to the confirm email page", async () => { + await $(ThankYouPage.email()).setValue("name@example.com"); + await $(ThankYouPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); + await expect(await $(ConfirmEmailPage.questionTitle()).getText()).to.equal("Is this email address correct?"); }); - it("When I submit the confirm email page without providing an answer, Then I get an error message", () => { - $(ConfirmEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/confirm"); - expect($(ConfirmEmailPage.errorPanel()).isExisting()).to.be.true; - expect($(ConfirmEmailPage.errorPanel()).getText()).to.contain("Select an answer"); + it("When I submit the confirm email page without providing an answer, Then I get an error message", async () => { + await $(ConfirmEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); + await expect(await $(ConfirmEmailPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(ConfirmEmailPage.errorPanel()).getText()).to.contain("Select an answer"); }); - it("When I answer 'Yes' and submit the confirm email page, Then I go to email sent page", () => { - $(ConfirmEmailPage.yes()).click(); - $(ConfirmEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/sent"); - expect($(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); + it("When I answer 'Yes' and submit the confirm email page, Then I go to email sent page", async () => { + await $(ConfirmEmailPage.yes()).click(); + await $(ConfirmEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); + await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); }); - it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", () => { - $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); - $(ConfirmationEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/send"); - expect($(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; - expect($(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address"); + it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", async () => { + await $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); + await $(ConfirmationEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address"); }); - it("When I submit the form without providing a correctly formatted email address, Then I get an error message", () => { - $(ConfirmationEmailPage.email()).setValue("incorrect-format"); - $(ConfirmationEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/send"); - expect($(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; - expect($(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address in a valid format, for example name@example.com"); + it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { + await $(ConfirmationEmailPage.email()).setValue("incorrect-format"); + await $(ConfirmationEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address in a valid format, for example name@example.com"); }); - it("When I submit the form with a valid email and confirm it is correct, Then I go to the email confirmation page", () => { - $(ConfirmationEmailPage.email()).setValue("name@example.com"); - $(ConfirmationEmailPage.submit()).click(); - $(ConfirmEmailPage.yes()).click(); - $(ConfirmEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/sent"); - expect($(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); + it("When I submit the form with a valid email and confirm it is correct, Then I go to the email confirmation page", async () => { + await $(ConfirmationEmailPage.email()).setValue("name@example.com"); + await $(ConfirmationEmailPage.submit()).click(); + await $(ConfirmEmailPage.yes()).click(); + await $(ConfirmEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); + await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); }); }); describe("Given I launch the test email confirmation survey", () => { - before(() => { - browser.openQuestionnaire("test_confirmation_email.json"); + before(async () => { + await browser.openQuestionnaire("test_confirmation_email.json"); }); - it("When I enter an email and answer 'No' on the confirm email page, Then I go the confirmation send page with the email pre-filled", () => { - $(SubmitPage.submit()).click(); - $(SubmitPage.submit()).click(); - $(ThankYouPage.email()).setValue("name@example.com"); - $(ThankYouPage.submit()).click(); - $(ConfirmEmailPage.no()).click(); - $(ConfirmEmailPage.submit()).click(); - expect(browser.getUrl()).to.contain("confirmation-email/send"); - expect($(ConfirmationEmailPage.email()).getValue()).to.equal("name@example.com"); + it("When I enter an email and answer 'No' on the confirm email page, Then I go the confirmation send page with the email pre-filled", async () => { + await $(SubmitPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await $(ThankYouPage.email()).setValue("name@example.com"); + await $(ThankYouPage.submit()).click(); + await $(ConfirmEmailPage.no()).click(); + await $(ConfirmEmailPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.email()).getValue()).to.equal("name@example.com"); }); }); }); describe("Email confirmation", () => { describe("Given I launch the test email confirmation survey", () => { - before(() => { - browser.openQuestionnaire("test_confirmation_email.json"); + before(async () => { + await browser.openQuestionnaire("test_confirmation_email.json"); }); - it("When I view the email confirmation page, Then I should not see the feedback call to action", () => { - $(SubmitPage.submit()).click(); - $(SubmitPage.submit()).click(); - $(ThankYouPage.email()).setValue("name@example.com"); - $(ThankYouPage.submit()).click(); - expect($(ConfirmationEmailSentPage.feedbackLink()).isExisting()).to.equal(false); + it("When I view the email confirmation page, Then I should not see the feedback call to action", async () => { + await $(SubmitPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await $(ThankYouPage.email()).setValue("name@example.com"); + await $(ThankYouPage.submit()).click(); + await expect(await $(ConfirmationEmailSentPage.feedbackLink()).isExisting()).to.equal(false); }); }); }); diff --git a/tests/functional/spec/content_variants.spec.js b/tests/functional/spec/content_variants.spec.js index e481390e2e..e4938c94b6 100644 --- a/tests/functional/spec/content_variants.spec.js +++ b/tests/functional/spec/content_variants.spec.js @@ -1,19 +1,19 @@ import ageQuestionBlock from "../generated_pages/variants_content/age-question-block.page.js"; describe("QuestionVariants", () => { - beforeEach(() => { - browser.openQuestionnaire("test_variants_content.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_variants_content.json"); }); - it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", () => { - $(ageQuestionBlock.age()).setValue(12); - $(ageQuestionBlock.submit()).click(); - expect($("main.ons-page__main h1").getText()).to.contain("You are 16 or younger"); + it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { + await $(ageQuestionBlock.age()).setValue(12); + await $(ageQuestionBlock.submit()).click(); + await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or younger"); }); - it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", () => { - $(ageQuestionBlock.age()).setValue(22); - $(ageQuestionBlock.submit()).click(); - expect($("main.ons-page__main h1").getText()).to.contain("You are 16 or older"); + it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { + await $(ageQuestionBlock.age()).setValue(22); + await $(ageQuestionBlock.submit()).click(); + await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or older"); }); }); diff --git a/tests/functional/spec/cookie_banner.spec.js b/tests/functional/spec/cookie_banner.spec.js index b8ebfb52f6..d4a9525dc2 100644 --- a/tests/functional/spec/cookie_banner.spec.js +++ b/tests/functional/spec/cookie_banner.spec.js @@ -1,32 +1,32 @@ import InitialPage from "../generated_pages/checkbox/mandatory-checkbox.page"; describe("Given I am not authenticated and have no cookie,", () => { - it("When I visit a page in runner, Then the cookie banner shouldn‘t be displayed", () => { - browser.url("/"); - expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + it("When I visit a page in runner, Then the cookie banner shouldn‘t be displayed", async () => { + await browser.url("/"); + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; }); }); describe("Given I start a survey,", () => { - beforeEach(() => { - browser.openQuestionnaire("test_checkbox.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_checkbox.json"); }); - it("When I open the page, Then the cookie banner should be displayed", () => { - expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + it("When I open the page, Then the cookie banner should be displayed", async () => { + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.true; }); - it("When I delete all cookies from the browser and refresh the page, Then the cookie banner shouldn‘t be displayed", () => { - browser.deleteAllCookies(); - browser.refresh(); - expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + it("When I delete all cookies from the browser and refresh the page, Then the cookie banner shouldn‘t be displayed", async () => { + await browser.deleteAllCookies(); + await browser.refresh(); + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; }); - it("When I sign out and click the browser back button, Then the cookie banner should be displayed", () => { - $(InitialPage.saveSignOut()).click(); - browser.back(); - expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + it("When I sign out and click the browser back button, Then the cookie banner should be displayed", async () => { + await $(InitialPage.saveSignOut()).click(); + await browser.back(); + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.true; }); - it("When I accept the cookies and refresh the page, Then the cookie banner shouldn‘t be displayed", () => { - $(InitialPage.acceptCookies()).click(); - browser.refresh(); - expect($(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + it("When I accept the cookies and refresh the page, Then the cookie banner shouldn‘t be displayed", async () => { + await $(InitialPage.acceptCookies()).click(); + await browser.refresh(); + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; }); }); diff --git a/tests/functional/spec/custom_page_titles.spec.js b/tests/functional/spec/custom_page_titles.spec.js index 764a9d3e15..246a24ec9e 100644 --- a/tests/functional/spec/custom_page_titles.spec.js +++ b/tests/functional/spec/custom_page_titles.spec.js @@ -11,81 +11,81 @@ describe("Feature: Custom Page Titles", () => { const schema = "test_custom_page_titles.json"; describe("Given I am completing the test_custom_page_titles survey,", () => { - before("load the survey", () => { - browser.openQuestionnaire(schema); + before("load the survey", async () => { + await browser.openQuestionnaire(schema); }); - it("When I navigate to the list collector page, Then I should see the custom page title", () => { - $(HubPage.submit()).click(); - const expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Custom page title - Test Custom Page Titles"); + it("When I navigate to the list collector page, Then I should see the custom page title", async () => { + await $(HubPage.submit()).click(); + const expectedPageTitle = await browser.getTitle(); + await expect(expectedPageTitle).to.equal("Custom page title - Test Custom Page Titles"); }); - it("When I navigate to the add person page, Then I should see the custom page title", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - let expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Add person 1 - Test Custom Page Titles"); + it("When I navigate to the add person page, Then I should see the custom page title", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + let expectedPageTitle = await browser.getTitle(); + await expect(expectedPageTitle).to.equal("Add person 1 - Test Custom Page Titles"); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Add person 2 - Test Custom Page Titles"); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + expectedPageTitle = await browser.getTitle(); + await expect(expectedPageTitle).to.equal("Add person 2 - Test Custom Page Titles"); }); - it("When I navigate to relationship collector pages, Then I should see the custom page titles", () => { - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Olivia"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - let expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("How Person 1 is related to Person 2 - Test Custom Page Titles"); + it("When I navigate to relationship collector pages, Then I should see the custom page titles", async () => { + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Olivia"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + let expectedPageTitle = await browser.getTitle(); + await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 2 - Test Custom Page Titles"); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("How Person 1 is related to Person 3 - Test Custom Page Titles"); + await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 3 - Test Custom Page Titles"); - $(RelationshipsPage.sonOrDaughter()).click(); - $(RelationshipsPage.submit()).click(); - expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("How Person 2 is related to Person 3 - Test Custom Page Titles"); + await $(RelationshipsPage.sonOrDaughter()).click(); + await $(RelationshipsPage.submit()).click(); + expectedPageTitle = await browser.getTitle(); + await expect(await expectedPageTitle).to.equal("How Person 2 is related to Person 3 - Test Custom Page Titles"); - $(RelationshipsPage.sonOrDaughter()).click(); - $(RelationshipsPage.submit()).click(); - expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Custom section summary page title - Test Custom Page Titles"); + await $(RelationshipsPage.sonOrDaughter()).click(); + await $(RelationshipsPage.submit()).click(); + expectedPageTitle = await browser.getTitle(); + await expect(await expectedPageTitle).to.equal("Custom section summary page title - Test Custom Page Titles"); }); - it("When I navigate to list edit and remove pages Then I should see the custom page titles", () => { - $(ListCollectorPage.listEditLink(1)).click(); - let expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Edit person 1 - Test Custom Page Titles"); - $(ListCollectorEditPage.previous()).click(); - $(ListCollectorPage.listRemoveLink(1)).click(); - expectedPageTitle = browser.getTitle(); - expect(expectedPageTitle).to.equal("Remove person 1 - Test Custom Page Titles"); + it("When I navigate to list edit and remove pages Then I should see the custom page titles", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + let expectedPageTitle = await browser.getTitle(); + await expect(expectedPageTitle).to.equal("Edit person 1 - Test Custom Page Titles"); + await $(ListCollectorEditPage.previous()).click(); + await $(ListCollectorPage.listRemoveLink(1)).click(); + expectedPageTitle = await browser.getTitle(); + await expect(await expectedPageTitle).to.equal("Remove person 1 - Test Custom Page Titles"); }); - it("When I navigate to a repeating section which has custom page title, Then all page titles in the section should have the correct prefix", () => { - browser.url(HubPage.url()); - $(HubPage.submit()).click(); - expect(browser.getTitle()).to.equal("Individual interstitial: Person 1 - Test Custom Page Titles"); - $(IndividualInterstitialPage.submit()).click(); - expect(browser.getTitle()).to.equal("Proxy question: Person 1 - Test Custom Page Titles"); - $(ProxyPage.submit()).click(); - expect(browser.getTitle()).to.equal("What is your date of birth?: Person 1 - Test Custom Page Titles"); - $(DateOfBirthPage.submit()).click(); - expect(browser.getTitle()).to.equal("Summary: Person 1 - Test Custom Page Titles"); + it("When I navigate to a repeating section which has custom page title, Then all page titles in the section should have the correct prefix", async () => { + await browser.url(HubPage.url()); + await $(HubPage.submit()).click(); + await expect(await browser.getTitle()).to.equal("Individual interstitial: Person 1 - Test Custom Page Titles"); + await $(IndividualInterstitialPage.submit()).click(); + await expect(await browser.getTitle()).to.equal("Proxy question: Person 1 - Test Custom Page Titles"); + await $(ProxyPage.submit()).click(); + await expect(await browser.getTitle()).to.equal("What is your date of birth?: Person 1 - Test Custom Page Titles"); + await $(DateOfBirthPage.submit()).click(); + await expect(await browser.getTitle()).to.equal("Summary: Person 1 - Test Custom Page Titles"); }); }); }); diff --git a/tests/functional/spec/dates.spec.js b/tests/functional/spec/dates.spec.js index 998bb834fb..c3bcee459d 100644 --- a/tests/functional/spec/dates.spec.js +++ b/tests/functional/spec/dates.spec.js @@ -6,207 +6,207 @@ import DateYearDatePage from "../generated_pages/dates/date-year-date-block.page import SubmitPage from "../generated_pages/dates/submit.page"; describe("Date checks", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_dates.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_dates.json"); }); - it("Given an answer label is provided for a date question then the label should be displayed ", () => { - expect($(DateRangePage.legend()).getText()).to.contain("Period from"); + it("Given an answer label is provided for a date question then the label should be displayed ", async () => { + await expect(await $(DateRangePage.legend()).getText()).to.contain("Period from"); }); - it("Given an answer label is not provided for a date question then the question title should be used within the legend ", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(1901); + it("Given an answer label is not provided for a date question then the question title should be used within the legend ", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(1901); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(5); - $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(5); + await $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + await $(DateRangePage.submit()).click(); - expect($(DateMonthYearPage.legend()).getText()).to.contain("Date with month and year"); + await expect(await $(DateMonthYearPage.legend()).getText()).to.contain("Date with month and year"); }); - it("Given the test_dates survey is selected when dates are entered then the summary screen shows the dates entered formatted", () => { + it("Given the test_dates survey is selected when dates are entered then the summary screen shows the dates entered formatted", async () => { // When dates are entered - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(1901); + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(1901); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(5); - $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(5); + await $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + await $(DateRangePage.submit()).click(); - $(DateMonthYearPage.Month()).setValue(4); - $(DateMonthYearPage.Year()).setValue(2018); + await $(DateMonthYearPage.Month()).setValue(4); + await $(DateMonthYearPage.Year()).setValue(2018); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.submit()).click(); - $(DateSinglePage.day()).setValue(4); - $(DateSinglePage.month()).setValue(1); - $(DateSinglePage.year()).setValue(1999); + await $(DateSinglePage.day()).setValue(4); + await $(DateSinglePage.month()).setValue(1); + await $(DateSinglePage.year()).setValue(1999); - $(DateSinglePage.submit()).click(); + await $(DateSinglePage.submit()).click(); - $(DateNonMandatoryPage.submit()).click(); + await $(DateNonMandatoryPage.submit()).click(); - $(DateYearDatePage.Year()).setValue(2005); + await $(DateYearDatePage.Year()).setValue(2005); - $(DateYearDatePage.submit()).click(); + await $(DateYearDatePage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); // Then the summary screen shows the dates entered formatted - expect($(SubmitPage.dateRangeFromAnswer()).getText()).to.contain("1 January 1901 to 3 May 2017"); - expect($(SubmitPage.monthYearAnswer()).getText()).to.contain("April 2018"); - expect($(SubmitPage.singleDateAnswer()).getText()).to.contain("4 January 1999"); - expect($(SubmitPage.nonMandatoryDateAnswer()).getText()).to.contain("No answer provided"); - expect($(SubmitPage.yearDateAnswer()).getText()).to.contain("2005"); + await expect(await $(SubmitPage.dateRangeFromAnswer()).getText()).to.contain("1 January 1901 to 3 May 2017"); + await expect(await $(SubmitPage.monthYearAnswer()).getText()).to.contain("April 2018"); + await expect(await $(SubmitPage.singleDateAnswer()).getText()).to.contain("4 January 1999"); + await expect(await $(SubmitPage.nonMandatoryDateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.contain("2005"); }); - it("Given the test_dates survey is selected when the from date is greater than the to date then an error message is shown", () => { + it("Given the test_dates survey is selected when the from date is greater than the to date then an error message is shown", async () => { // When the from date is greater than the to date - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2015); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2015); - $(DateRangePage.submit()).click(); + await $(DateRangePage.submit()).click(); // Then an error message is shown and the question panel is highlighted - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); - expect($(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; // Then clicking error should focus on first input field - $(DateRangePage.errorNumber(1)).click(); - expect($(DateRangePage.dateRangeFromday()).isFocused()).to.be.true; + await $(DateRangePage.errorNumber(1)).click(); + await expect(await $(DateRangePage.dateRangeFromday()).isFocused()).to.be.true; }); - it("Given the test_dates survey is selected when the from date and the to date are the same then an error message is shown", () => { + it("Given the test_dates survey is selected when the from date and the to date are the same then an error message is shown", async () => { // When the from date is greater than the to date - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2016); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2016); - $(DateRangePage.submit()).click(); + await $(DateRangePage.submit()).click(); // Then an error message is shown and the question panel is highlighted - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); - expect($(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; }); - it("Given the test_dates survey is selected when an invalid date is entered in a date range then an error message is shown", () => { + it("Given the test_dates survey is selected when an invalid date is entered in a date range then an error message is shown", async () => { // When the from date is greater than the to date - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(""); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(""); - $(DateRangePage.submit()).click(); + await $(DateRangePage.submit()).click(); // Then an error message is shown - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); }); - it("Given the test_dates survey is selected when the year (month year type) is left empty then an error message is shown", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + it("Given the test_dates survey is selected when the year (month year type) is left empty then an error message is shown", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); // When the year (month year type) is left empty - $(DateMonthYearPage.Month()).setValue(4); - $(DateMonthYearPage.Year()).setValue(""); + await $(DateMonthYearPage.Month()).setValue(4); + await $(DateMonthYearPage.Year()).setValue(""); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.submit()).click(); // Then an error message is shown - expect($(DateMonthYearPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateMonthYearPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); }); - it("Given the test_dates survey is selected, " + "When an error message is shown and it is corrected, " + "Then the next question is displayed", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + it("Given the test_dates survey is selected, " + "When an error message is shown and it is corrected, " + "Then the next question is displayed", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); // When an error message is shown - $(DateMonthYearPage.Month()).setValue(4); - $(DateMonthYearPage.Year()).setValue(""); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.Month()).setValue(4); + await $(DateMonthYearPage.Year()).setValue(""); + await $(DateMonthYearPage.submit()).click(); - expect($(DateMonthYearPage.error()).getText()).to.contain("Enter a valid date"); + await expect(await $(DateMonthYearPage.error()).getText()).to.contain("Enter a valid date"); // Then when it is corrected, it goes to the next question - $(DateMonthYearPage.Year()).setValue(2018); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.Year()).setValue(2018); + await $(DateMonthYearPage.submit()).click(); - expect(browser.getUrl()).to.contain(DateSinglePage.url()); + await expect(await browser.getUrl()).to.contain(DateSinglePage.url()); }); - it("Given the test_dates survey is selected when an error message is shown then when it is corrected, it goes to the summary page and the information is correct", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + it("Given the test_dates survey is selected when an error message is shown then when it is corrected, it goes to the summary page and the information is correct", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); - $(DateMonthYearPage.Month()).setValue(1); - $(DateMonthYearPage.Year()).setValue(2016); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.Month()).setValue(1); + await $(DateMonthYearPage.Year()).setValue(2016); + await $(DateMonthYearPage.submit()).click(); - $(DateSinglePage.day()).setValue(1); - $(DateSinglePage.month()).setValue(1); - $(DateSinglePage.year()).setValue(2016); - $(DateMonthYearPage.submit()).click(); + await $(DateSinglePage.day()).setValue(1); + await $(DateSinglePage.month()).setValue(1); + await $(DateSinglePage.year()).setValue(2016); + await $(DateMonthYearPage.submit()).click(); // When non-mandatory is partially completed - $(DateNonMandatoryPage.day()).setValue(4); - $(DateNonMandatoryPage.month()).setValue(1); - $(DateNonMandatoryPage.submit()).click(); + await $(DateNonMandatoryPage.day()).setValue(4); + await $(DateNonMandatoryPage.month()).setValue(1); + await $(DateNonMandatoryPage.submit()).click(); // Then an error message is shown - expect($(DateNonMandatoryPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateNonMandatoryPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); }); - it("Given the test_dates survey is selected, when a user clicks the day label then the day subfield should gain the focus", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); + it("Given the test_dates survey is selected, when a user clicks the day label then the day subfield should gain the focus", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); - $(DateMonthYearPage.Month()).setValue(1); - $(DateMonthYearPage.Year()).setValue(2016); - $(DateMonthYearPage.submit()).click(); + await $(DateMonthYearPage.Month()).setValue(1); + await $(DateMonthYearPage.Year()).setValue(2016); + await $(DateMonthYearPage.submit()).click(); // When a user clicks the day label - $(DateSinglePage.dayLabel()).click(); + await $(DateSinglePage.dayLabel()).click(); // Then the day subfield should gain the focus - expect($(DateSinglePage.day()).isFocused()).to.be.true; + await expect(await $(DateSinglePage.day()).isFocused()).to.be.true; }); }); diff --git a/tests/functional/spec/dob_date.spec.js b/tests/functional/spec/dob_date.spec.js index 208e1b405e..caac178f02 100644 --- a/tests/functional/spec/dob_date.spec.js +++ b/tests/functional/spec/dob_date.spec.js @@ -2,21 +2,21 @@ import DateOfBirthPage from "../generated_pages/dob_date/date-of-birth.page"; import UnderSixteenPage from "../generated_pages/dob_date/under-sixteen.page"; describe("Date of birth check", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_dob_date.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_dob_date.json"); }); - it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to under 16 page", () => { - $(DateOfBirthPage.day()).setValue(12); - $(DateOfBirthPage.month()).setValue(4); - $(DateOfBirthPage.year()).setValue(2021); - $(DateOfBirthPage.submit()).click(); - expect($(UnderSixteenPage.legend()).getText()).to.contain("You are under 16!"); + it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to under 16 page", async () => { + await $(DateOfBirthPage.day()).setValue(12); + await $(DateOfBirthPage.month()).setValue(4); + await $(DateOfBirthPage.year()).setValue(2021); + await $(DateOfBirthPage.submit()).click(); + await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are under 16!"); }); - it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to over 16 page", () => { - $(DateOfBirthPage.day()).setValue(12); - $(DateOfBirthPage.month()).setValue(4); - $(DateOfBirthPage.year()).setValue(1980); - $(DateOfBirthPage.submit()).click(); - expect($(UnderSixteenPage.legend()).getText()).to.contain("You are over 16!"); + it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to over 16 page", async () => { + await $(DateOfBirthPage.day()).setValue(12); + await $(DateOfBirthPage.month()).setValue(4); + await $(DateOfBirthPage.year()).setValue(1980); + await $(DateOfBirthPage.submit()).click(); + await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are over 16!"); }); }); diff --git a/tests/functional/spec/durations.spec.js b/tests/functional/spec/durations.spec.js index af0840c319..025202c442 100644 --- a/tests/functional/spec/durations.spec.js +++ b/tests/functional/spec/durations.spec.js @@ -2,99 +2,99 @@ import DurationPage from "../generated_pages/durations/duration-block.page.js"; import SubmitPage from "../generated_pages/durations/submit.page.js"; describe("Durations", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_durations.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_durations.json"); }); - it("Given the test_durations survey is selected durations suffixes are visible", () => { - expect($(DurationPage.yearMonthYearsSuffix()).getText()).to.contain("Years"); - expect($(DurationPage.mandatoryYearMonthMonthsSuffix()).getText()).to.contain("Months"); - expect($(DurationPage.yearYearsSuffix()).getText()).to.contain("Years"); - expect($(DurationPage.mandatoryMonthMonthsSuffix()).getText()).to.contain("Months"); + it("Given the test_durations survey is selected durations suffixes are visible", async () => { + await expect(await $(DurationPage.yearMonthYearsSuffix()).getText()).to.contain("Years"); + await expect(await $(DurationPage.mandatoryYearMonthMonthsSuffix()).getText()).to.contain("Months"); + await expect(await $(DurationPage.yearYearsSuffix()).getText()).to.contain("Years"); + await expect(await $(DurationPage.mandatoryMonthMonthsSuffix()).getText()).to.contain("Months"); }); - it("Given the test_durations survey is selected when durations are entered then the summary screen shows the durations entered formatted", () => { - $(DurationPage.yearMonthYears()).setValue(1); - $(DurationPage.yearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearMonthYears()).setValue(1); - $(DurationPage.mandatoryYearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.yearMonthAnswer()).getText()).to.equal("1 year 2 months"); - $(SubmitPage.submit()).click(); + it("Given the test_durations survey is selected when durations are entered then the summary screen shows the durations entered formatted", async () => { + await $(DurationPage.yearMonthYears()).setValue(1); + await $(DurationPage.yearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearMonthYears()).setValue(1); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("1 year 2 months"); + await $(SubmitPage.submit()).click(); }); - it("Given the test_durations survey is selected when one of the units is 0 it is excluded from the summary", () => { - $(DurationPage.yearMonthYears()).setValue(0); - $(DurationPage.yearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearMonthYears()).setValue(1); - $(DurationPage.mandatoryYearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.yearMonthAnswer()).getText()).to.equal("2 months"); - $(SubmitPage.submit()).click(); + it("Given the test_durations survey is selected when one of the units is 0 it is excluded from the summary", async () => { + await $(DurationPage.yearMonthYears()).setValue(0); + await $(DurationPage.yearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearMonthYears()).setValue(1); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("2 months"); + await $(SubmitPage.submit()).click(); }); - it("Given the test_durations survey is selected when no duration is entered the summary shows no answer provided", () => { - $(DurationPage.mandatoryYearMonthYears()).setValue(1); - $(DurationPage.mandatoryYearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); + it("Given the test_durations survey is selected when no duration is entered the summary shows no answer provided", async () => { + await $(DurationPage.mandatoryYearMonthYears()).setValue(1); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.yearMonthAnswer()).getText()).to.equal("No answer provided"); - $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("No answer provided"); + await $(SubmitPage.submit()).click(); }); - it("Given the test_durations survey is selected when one of the units is missing an error is shown", () => { - $(DurationPage.yearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); + it("Given the test_durations survey is selected when one of the units is missing an error is shown", async () => { + await $(DurationPage.yearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); - expect($(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - expect($(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); }); - it("Given the test_durations survey is selected when one of the units not a number an error is shown", () => { - $(DurationPage.yearMonthYears()).setValue("word"); - $(DurationPage.yearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearMonthYears()).setValue("word"); - $(DurationPage.mandatoryYearMonthMonths()).setValue(2); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); - - expect($(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - expect($(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + it("Given the test_durations survey is selected when one of the units not a number an error is shown", async () => { + await $(DurationPage.yearMonthYears()).setValue("word"); + await $(DurationPage.yearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearMonthYears()).setValue("word"); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); + + await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); }); - it("Given the test_durations survey is selected when the number of months is more than 11 an error is shown", () => { - $(DurationPage.yearMonthYears()).setValue(1); - $(DurationPage.yearMonthMonths()).setValue(12); - $(DurationPage.mandatoryYearMonthYears()).setValue(1); - $(DurationPage.mandatoryYearMonthMonths()).setValue(12); - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); - - expect($(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - expect($(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + it("Given the test_durations survey is selected when the number of months is more than 11 an error is shown", async () => { + await $(DurationPage.yearMonthYears()).setValue(1); + await $(DurationPage.yearMonthMonths()).setValue(12); + await $(DurationPage.mandatoryYearMonthYears()).setValue(1); + await $(DurationPage.mandatoryYearMonthMonths()).setValue(12); + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); + + await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); }); - it("Given the test_durations survey is selected when the mandatory duration is missing an error is shown", () => { - $(DurationPage.mandatoryYearYears()).setValue(1); - $(DurationPage.mandatoryMonthMonths()).setValue(1); - $(DurationPage.submit()).click(); + it("Given the test_durations survey is selected when the mandatory duration is missing an error is shown", async () => { + await $(DurationPage.mandatoryYearYears()).setValue(1); + await $(DurationPage.mandatoryMonthMonths()).setValue(1); + await $(DurationPage.submit()).click(); - expect($(DurationPage.errorNumber(1)).getText()).to.contain("Enter a duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a duration"); }); }); diff --git a/tests/functional/spec/error_messages.spec.js b/tests/functional/spec/error_messages.spec.js index 734d42f600..c363f9433e 100644 --- a/tests/functional/spec/error_messages.spec.js +++ b/tests/functional/spec/error_messages.spec.js @@ -1,31 +1,31 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; -function answerAllButOne() { - $(AboutYou.textfield()).setValue("John Doe"); - $(AboutYou.dateday()).setValue("1"); - $(AboutYou.datemonth()).setValue("1"); - $(AboutYou.dateyear()).setValue("1995"); - $(AboutYou.checkboxBmw()).click(); - $(AboutYou.radioYes()).click(); - $(AboutYou.currency()).setValue("50000"); - $(AboutYou.monthYearDateMonth()).setValue("10"); - $(AboutYou.monthYearDateYear()).setValue("2021"); - $(AboutYou.dropdown()).selectByAttribute("value", "Silver"); - $(AboutYou.unit()).setValue("10000"); - $(AboutYou.durationMonths()).setValue("3"); - $(AboutYou.durationYears()).setValue("3"); - $(AboutYou.yearDateYear()).setValue("2019"); - $(AboutYou.number()).setValue("5"); - $(AboutYou.percentage()).setValue("3"); - $(AboutYou.mobileNumber()).setValue("07700900111"); +async function answerAllButOne() { + await $(AboutYou.textfield()).setValue("John Doe"); + await $(AboutYou.dateday()).setValue("1"); + await $(AboutYou.datemonth()).setValue("1"); + await $(AboutYou.dateyear()).setValue("1995"); + await $(AboutYou.checkboxBmw()).click(); + await $(AboutYou.radioYes()).click(); + await $(AboutYou.currency()).setValue("50000"); + await $(AboutYou.monthYearDateMonth()).setValue("10"); + await $(AboutYou.monthYearDateYear()).setValue("2021"); + await $(AboutYou.dropdown()).selectByAttribute("value", "Silver"); + await $(AboutYou.unit()).setValue("10000"); + await $(AboutYou.durationMonths()).setValue("3"); + await $(AboutYou.durationYears()).setValue("3"); + await $(AboutYou.yearDateYear()).setValue("2019"); + await $(AboutYou.number()).setValue("5"); + await $(AboutYou.percentage()).setValue("3"); + await $(AboutYou.mobileNumber()).setValue("07700900111"); } describe("Error Messages", () => { - beforeEach(() => { - browser.openQuestionnaire("test_multiple_answers.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_multiple_answers.json"); }); - it("Given a question has errors, When errors are displayed, Then the error messages are correct", () => { + it("Given a question has errors, When errors are displayed, Then the error messages are correct", async () => { const errorMessageMap = { 1: "Enter an answer", 2: "Enter a date", @@ -43,95 +43,95 @@ describe("Error Messages", () => { 14: "Enter an answer", }; - $(AboutYou.submit()).click(); - expect($(AboutYou.errorHeader()).getText()).to.equal("There are 14 problems with your answer"); + await $(AboutYou.submit()).click(); + await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There are 14 problems with your answer"); for (const [index, errorMessage] of Object.entries(errorMessageMap)) { - expect($(AboutYou.errorNumber(index)).getText()).to.contain(errorMessage); + await expect(await $(AboutYou.errorNumber(index)).getText()).to.contain(errorMessage); } }); - it("Given a question has errors, When errors are displayed, Then the error message for each answer is correct", () => { - $(AboutYou.submit()).click(); - - expect($(AboutYou.textfieldErrorItem()).getText()).to.equal("Enter an answer"); - expect($(AboutYou.dateErrorItem()).getText()).to.equal("Enter a date"); - expect($(AboutYou.checkboxErrorItem()).getText()).to.contain("Select at least one answer"); - expect($(AboutYou.radioErrorItem()).getText()).to.contain("Select an answer"); - expect($(AboutYou.currencyErrorItem()).getText()).to.equal("Enter an answer"); - expect($(AboutYou.monthYearDateErrorItem()).getText()).to.equal("Enter a date"); - expect($(AboutYou.dropdownErrorItem()).getText()).to.equal("Select an answer"); - expect($(AboutYou.unitErrorItem()).getText()).to.equal("Enter an answer"); - expect($(AboutYou.durationErrorItem()).getText()).to.equal("Enter a duration"); - expect($(AboutYou.yearDateErrorItem()).getText()).to.equal("Enter a date"); - expect($(AboutYou.numberErrorItem()).getText()).to.equal("Enter an answer"); - expect($(AboutYou.percentageErrorItem()).getText()).to.equal("Enter an answer"); - expect($(AboutYou.mobileNumberErrorItem()).getText()).to.equal("Enter a UK mobile number"); - expect($(AboutYou.textareaErrorItem()).getText()).to.equal("Enter an answer"); + it("Given a question has errors, When errors are displayed, Then the error message for each answer is correct", async () => { + await $(AboutYou.submit()).click(); + + await expect(await $(AboutYou.textfieldErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.dateErrorItem()).getText()).to.equal("Enter a date"); + await expect(await $(AboutYou.checkboxErrorItem()).getText()).to.contain("Select at least one answer"); + await expect(await $(AboutYou.radioErrorItem()).getText()).to.contain("Select an answer"); + await expect(await $(AboutYou.currencyErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.monthYearDateErrorItem()).getText()).to.equal("Enter a date"); + await expect(await $(AboutYou.dropdownErrorItem()).getText()).to.equal("Select an answer"); + await expect(await $(AboutYou.unitErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.durationErrorItem()).getText()).to.equal("Enter a duration"); + await expect(await $(AboutYou.yearDateErrorItem()).getText()).to.equal("Enter a date"); + await expect(await $(AboutYou.numberErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.percentageErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.mobileNumberErrorItem()).getText()).to.equal("Enter a UK mobile number"); + await expect(await $(AboutYou.textareaErrorItem()).getText()).to.equal("Enter an answer"); }); - it("Given a question has multiple errors, When the errors are displayed, Then the error messages are in a numbered list", () => { - $(AboutYou.submit()).click(); - expect($(AboutYou.errorList()).isDisplayed()).to.be.true; + it("Given a question has multiple errors, When the errors are displayed, Then the error messages are in a numbered list", async () => { + await $(AboutYou.submit()).click(); + await expect(await $(AboutYou.errorList()).isDisplayed()).to.be.true; }); - it("Given a question has 1 error, When the error is displayed, Then error message isn't in a numbered list", () => { - answerAllButOne(); + it("Given a question has 1 error, When the error is displayed, Then error message isn't in a numbered list", async () => { + await answerAllButOne(); - $(AboutYou.submit()).click(); - expect($(AboutYou.singleErrorLink()).isDisplayed()).to.be.true; + await $(AboutYou.submit()).click(); + await expect(await $(AboutYou.singleErrorLink()).isDisplayed()).to.be.true; }); - it("Given a question has 1 error, When the error is displayed, Then error header is correct", () => { - answerAllButOne(); + it("Given a question has 1 error, When the error is displayed, Then error header is correct", async () => { + await answerAllButOne(); - $(AboutYou.submit()).click(); - expect($(AboutYou.errorHeader()).getText()).to.equal("There is a problem with your answer"); + await $(AboutYou.submit()).click(); + await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There is a problem with your answer"); }); - it("Given a question has errors, When an error message is clicked, Then the correct answer is focused", () => { - $(AboutYou.submit()).click(); + it("Given a question has errors, When an error message is clicked, Then the correct answer is focused", async () => { + await $(AboutYou.submit()).click(); - $(AboutYou.errorNumber(1)).click(); - expect($(AboutYou.textfield()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(1)).click(); + await expect(await $(AboutYou.textfield()).isFocused()).to.be.true; - $(AboutYou.errorNumber(2)).click(); - expect($(AboutYou.dateday()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(2)).click(); + await expect(await $(AboutYou.dateday()).isFocused()).to.be.true; - $(AboutYou.errorNumber(3)).click(); - expect($(AboutYou.checkboxBmw()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(3)).click(); + await expect(await $(AboutYou.checkboxBmw()).isFocused()).to.be.true; - $(AboutYou.errorNumber(4)).click(); - expect($(AboutYou.radioYes()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(4)).click(); + await expect(await $(AboutYou.radioYes()).isFocused()).to.be.true; - $(AboutYou.errorNumber(5)).click(); - expect($(AboutYou.currency()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(5)).click(); + await expect(await $(AboutYou.currency()).isFocused()).to.be.true; - $(AboutYou.errorNumber(6)).click(); - expect($(AboutYou.monthYearDateMonth()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(6)).click(); + await expect(await $(AboutYou.monthYearDateMonth()).isFocused()).to.be.true; - $(AboutYou.errorNumber(7)).click(); - expect($(AboutYou.dropdown()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(7)).click(); + await expect(await $(AboutYou.dropdown()).isFocused()).to.be.true; - $(AboutYou.errorNumber(8)).click(); - expect($(AboutYou.unit()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(8)).click(); + await expect(await $(AboutYou.unit()).isFocused()).to.be.true; - $(AboutYou.errorNumber(9)).click(); - expect($(AboutYou.durationYears()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(9)).click(); + await expect(await $(AboutYou.durationYears()).isFocused()).to.be.true; - $(AboutYou.errorNumber(10)).click(); - expect($(AboutYou.yearDateYear()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(10)).click(); + await expect(await $(AboutYou.yearDateYear()).isFocused()).to.be.true; - $(AboutYou.errorNumber(11)).click(); - expect($(AboutYou.number()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(11)).click(); + await expect(await $(AboutYou.number()).isFocused()).to.be.true; - $(AboutYou.errorNumber(12)).click(); - expect($(AboutYou.percentage()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(12)).click(); + await expect(await $(AboutYou.percentage()).isFocused()).to.be.true; - $(AboutYou.errorNumber(13)).click(); - expect($(AboutYou.mobileNumber()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(13)).click(); + await expect(await $(AboutYou.mobileNumber()).isFocused()).to.be.true; - $(AboutYou.errorNumber(14)).click(); - expect($(AboutYou.textarea()).isFocused()).to.be.true; + await $(AboutYou.errorNumber(14)).click(); + await expect(await $(AboutYou.textarea()).isFocused()).to.be.true; }); }); diff --git a/tests/functional/spec/exit.spec.js b/tests/functional/spec/exit.spec.js index caeb849cfb..d43bfbcd79 100644 --- a/tests/functional/spec/exit.spec.js +++ b/tests/functional/spec/exit.spec.js @@ -3,31 +3,31 @@ import HubPage from "../base_pages/hub.page"; import { SubmitPage } from "../base_pages/submit.page.js"; describe("Post submission exit", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_thank_you_census_household.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_thank_you_census_household.json"); }); - it("Given I click the exit button from the thank you page which has no session cookie, When I am redirected, Then I should be redirected to the correct log out url", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - browser.deleteAllCookies(); - $(CensusThankYouPage.exit()).click(); - expect(browser.getUrl()).to.equal("https://surveys.ons.gov.uk/sign-in/"); + it("Given I click the exit button from the thank you page which has no session cookie, When I am redirected, Then I should be redirected to the correct log out url", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await browser.deleteAllCookies(); + await $(CensusThankYouPage.exit()).click(); + await expect(await browser.getUrl()).to.equal("https://surveys.ons.gov.uk/sign-in/"); }); - it("Given I click the exit button from the thank you page, When I am redirected, Then I should be redirected to the correct log out url", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - $(CensusThankYouPage.exit()).click(); - expect(browser.getUrl()).to.equal("https://census.gov.uk/en/start"); + it("Given I click the exit button from the thank you page, When I am redirected, Then I should be redirected to the correct log out url", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(CensusThankYouPage.exit()).click(); + await expect(await browser.getUrl()).to.equal("https://census.gov.uk/en/start"); }); - it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - $(CensusThankYouPage.exit()).click(); - browser.back(); - expect(browser.getUrl()).to.contain("submitted/thank-you"); - expect($("body").getHTML()).to.contain("Sorry, you need to sign in again"); + it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(CensusThankYouPage.exit()).click(); + await browser.back(); + await expect(await browser.getUrl()).to.contain("submitted/thank-you"); + await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); }); }); diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary_test_case.js index 3bac1c8919..9b886404e2 100644 --- a/tests/functional/spec/features/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary_test_case.js @@ -19,203 +19,203 @@ import SecondNumberBlockPage from "../../generated_pages/calculated_summary/seco class TestCase { testCase(schema) { - before("Get to Calculated Summary", () => { - browser.openQuestionnaire(schema); + before("Get to Calculated Summary", async () => { + await browser.openQuestionnaire(schema); - $(FirstNumberBlockPage.firstNumber()).setValue(1.23); - $(FirstNumberBlockPage.submit()).click(); + await $(FirstNumberBlockPage.firstNumber()).setValue(1.23); + await $(FirstNumberBlockPage.submit()).click(); - $(SecondNumberBlockPage.secondNumber()).setValue(4.56); - $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); - $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); - $(SecondNumberBlockPage.submit()).click(); + await $(SecondNumberBlockPage.secondNumber()).setValue(4.56); + await $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); + await $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); + await $(SecondNumberBlockPage.submit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.no()).click(); - $(SkipFourthBlockPage.submit()).click(); + await $(SkipFourthBlockPage.no()).click(); + await $(SkipFourthBlockPage.submit()).click(); - $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); - $(FourthNumberBlockPage.submit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); - $(FourthAndAHalfNumberBlockPage.submit()).click(); + await $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); + await $(FourthNumberBlockPage.submit()).click(); + await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); + await $(FourthAndAHalfNumberBlockPage.submit()).click(); - $(FifthNumberBlockPage.fifthPercent()).setValue(56); - $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); - $(FifthNumberBlockPage.submit()).click(); + await $(FifthNumberBlockPage.fifthPercent()).setValue(56); + await $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); + await $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.sixthPercent()).setValue(23); - $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); - $(SixthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.sixthPercent()).setValue(23); + await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); + await $(SixthNumberBlockPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); }); - it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", () => { - expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", async () => { + await expect(await browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); }); - it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £20.71. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); // Answers included in calculation should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Second answer label also in currency total (optional)" ); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Fourth answer label also in total (optional)" ); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); // Answers not included in calculation should not be shown - expect($$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; - expect($$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; - expect($$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; - expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; + await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; + await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; }); - it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" ); }); - it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.previous()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); }); - it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); }); - it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); - $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); - $(FourthNumberBlockPage.submit()).click(); + it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); + await $(FourthNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £30.71. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); - it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); - $(FourthAndAHalfNumberBlockPage.submit()).click(); + it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); + await $(FourthAndAHalfNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £28.37. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); }); - it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { - $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); - $(SixthNumberBlockPage.previous()).click(); - $(FifthNumberBlockPage.previous()).click(); - $(FourthAndAHalfNumberBlockPage.previous()).click(); - $(FourthNumberBlockPage.previous()).click(); + it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + await $(SixthNumberBlockPage.previous()).click(); + await $(FifthNumberBlockPage.previous()).click(); + await $(FourthAndAHalfNumberBlockPage.previous()).click(); + await $(FourthNumberBlockPage.previous()).click(); - $(SkipFourthBlockPage.yes()).click(); - $(SkipFourthBlockPage.submit()).click(); + await $(SkipFourthBlockPage.yes()).click(); + await $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; + await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.36. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); - it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); // Answers included in calculation should be shown - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); }); - it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", () => { - expect(browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { + await expect(await browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); }); - it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(UnitTotalPlaybackPage.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of percentage values entered to be 79%. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); // Answers included in calculation should be shown - expect($(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); }); - it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(UnitTotalPlaybackPage.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be 124.58. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); // Answers included in calculation should be shown - expect($(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); - expect($(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); }); - it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { - $(NumberTotalPlaybackPage.submit()).click(); + it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { + await $(NumberTotalPlaybackPage.submit()).click(); - const content = $("h1 + ul").getText(); + const content = await $("h1 + ul").getText(); const textsToAssert = [ "Total currency values (if Q4 not skipped): £28.37", "Total currency values (if Q4 skipped)): £9.36", @@ -224,112 +224,112 @@ class TestCase { "Total number values: 124.58", ]; - textsToAssert.forEach((text) => expect(content).to.contain(text)); + textsToAssert.forEach(async (text) => await expect(content).to.containasync(text)); }); - it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); + it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + await $(SetMinMaxBlockPage.submit()).click(); }); - it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); - $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); - $(SetMinMaxBlockPage.submit()).click(); + it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); + await $(SetMinMaxBlockPage.submit()).click(); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); - $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); - $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); } } diff --git a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js index 5f43fabbc8..af4efcb31c 100644 --- a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js +++ b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js @@ -4,43 +4,43 @@ import RadioButtonsPage from "../../../generated_pages/titles_radio_and_checkbox import SubmitPage from "../../../generated_pages/titles_radio_and_checkbox/submit.page"; describe("Feature: Conditional checkbox and radio question titles", () => { - beforeEach(() => { - browser.openQuestionnaire("test_titles_radio_and_checkbox.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_titles_radio_and_checkbox.json"); }); describe("Given I start the test_titles_radio_and_checkbox survey", () => { - it("When I enter an expected name and submit", () => { - $(NameEntryPage.name()).setValue("Peter"); - $(NameEntryPage.submit()).click(); - expect($(CheckBoxPage.questionText()).getText()).to.contain("Did Peter make changes to this business?"); + it("When I enter an expected name and submit", async () => { + await $(NameEntryPage.name()).setValue("Peter"); + await $(NameEntryPage.submit()).click(); + await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Peter make changes to this business?"); }); - it("When I enter an unknown name and go to the checkbox page", () => { - $(NameEntryPage.name()).setValue("Fred"); - $(NameEntryPage.submit()).click(); - expect($(CheckBoxPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); - $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategies()).click(); - expect($(RadioButtonsPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); + it("When I enter an unknown name and go to the checkbox page", async () => { + await $(NameEntryPage.name()).setValue("Fred"); + await $(NameEntryPage.submit()).click(); + await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); + await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategies()).click(); + await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); }); - it("When I enter another known name page title should include selected title", () => { - $(NameEntryPage.name()).setValue("Mary"); - $(NameEntryPage.submit()).click(); + it("When I enter another known name page title should include selected title", async () => { + await $(NameEntryPage.name()).setValue("Mary"); + await $(NameEntryPage.submit()).click(); - expect(browser.getTitle()).to.contain("Did Mary make changes to this business? - Test Survey - Checkbox and Radio titles"); + await expect(await browser.getTitle()).to.contain("Did Mary make changes to this business? - Test Survey - Checkbox and Radio titles"); }); - it("When I enter another known name and go to the summary", () => { - $(NameEntryPage.name()).setValue("Mary"); - $(NameEntryPage.submit()).click(); - expect($(CheckBoxPage.questionText()).getText()).to.contain("Did Mary make changes to this business"); - $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategiesLabel()).click(); - $(CheckBoxPage.submit()).click(); - expect($(RadioButtonsPage.questionText()).getText()).to.contain("Is Mary the boss?"); - $(RadioButtonsPage.radioMaybe()).click(); - $(RadioButtonsPage.submit()).click(); - expect($(SubmitPage.nameAnswer()).getText()).to.contain("Mary"); - expect($(SubmitPage.checkboxQuestion()).getText()).to.contain("Did Mary make changes to this business?"); + it("When I enter another known name and go to the summary", async () => { + await $(NameEntryPage.name()).setValue("Mary"); + await $(NameEntryPage.submit()).click(); + await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Mary make changes to this business"); + await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategiesLabel()).click(); + await $(CheckBoxPage.submit()).click(); + await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Is Mary the boss?"); + await $(RadioButtonsPage.radioMaybe()).click(); + await $(RadioButtonsPage.submit()).click(); + await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Mary"); + await expect(await $(SubmitPage.checkboxQuestion()).getText()).to.contain("Did Mary make changes to this business?"); }); }); }); diff --git a/tests/functional/spec/features/confirmation_question.spec.js b/tests/functional/spec/features/confirmation_question.spec.js index a23954fa97..70958b56a7 100644 --- a/tests/functional/spec/features/confirmation_question.spec.js +++ b/tests/functional/spec/features/confirmation_question.spec.js @@ -4,36 +4,36 @@ import SubmitPage from "../../generated_pages/confirmation_question/submit.page. describe("Feature: Confirmation Question", () => { describe("Given I have a completed the confirmation question", () => { - before("Get to summary", () => { - browser.openQuestionnaire("test_confirmation_question.json"); + before("Get to summary", async () => { + await browser.openQuestionnaire("test_confirmation_question.json"); }); - it("When I view the summary, Then the confirmation question should not be displayed", () => { - $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); - $(NumberOfEmployeesTotalBlockPage.submit()).click(); - $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); - $(ConfirmZeroEmployeesBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); - expect($$(SubmitPage.confirmZeroEmployeesAnswer())).to.be.empty; + it("When I view the summary, Then the confirmation question should not be displayed", async () => { + await $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); + await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); + await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); + await expect(await $$(SubmitPage.confirmZeroEmployeesAnswer())).to.be.empty; }); }); describe("Given a confirmation Question", () => { - it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", () => { - browser.openQuestionnaire("test_confirmation_question.json"); - $(NumberOfEmployeesTotalBlockPage.submit()).click(); - $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); - $(ConfirmZeroEmployeesBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(NumberOfEmployeesTotalBlockPage.pageName); + it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", async () => { + await browser.openQuestionnaire("test_confirmation_question.json"); + await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); + await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(NumberOfEmployeesTotalBlockPage.pageName); }); }); describe("Given a number of employees Question", () => { - it("When I don't answer the number of employees question and go to summary, Then default value should be displayed for the the number of employees question", () => { - browser.openQuestionnaire("test_confirmation_question.json"); - $(NumberOfEmployeesTotalBlockPage.submit()).click(); - $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); - $(ConfirmZeroEmployeesBlockPage.submit()).click(); - expect($(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); + it("When I don't answer the number of employees question and go to summary, Then default value should be displayed for the the number of employees question", async () => { + await browser.openQuestionnaire("test_confirmation_question.json"); + await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); + await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); }); }); }); diff --git a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js index 35e1c0bd82..7baa7f37f5 100644 --- a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js +++ b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js @@ -7,57 +7,57 @@ import DefaultSectionSummary from "../../generated_pages/confirmation_question_w describe("Feature: Confirmation Question Within A Repeating Section", () => { describe("Given I am in a repeating section", () => { - beforeEach("Add a person", () => { - browser.openQuestionnaire("test_confirmation_question_within_repeating_section.json"); - $(DoesAnyoneLiveHerePage.yes()).click(); - $(DoesAnyoneLiveHerePage.submit()).click(); - $(AddPersonPage.firstName()).setValue("John"); - $(AddPersonPage.lastName()).setValue("Doe"); - $(AddPersonPage.submit()).click(); - $(DoesAnyoneLiveHerePage.no()).click(); - $(DoesAnyoneLiveHerePage.submit()).click(); - expect(browser.getUrl()).to.contain(DateOfBirthPage.url().split("/").slice(-1)[0]); + beforeEach("Add a person", async () => { + await browser.openQuestionnaire("test_confirmation_question_within_repeating_section.json"); + await $(DoesAnyoneLiveHerePage.yes()).click(); + await $(DoesAnyoneLiveHerePage.submit()).click(); + await $(AddPersonPage.firstName()).setValue("John"); + await $(AddPersonPage.lastName()).setValue("Doe"); + await $(AddPersonPage.submit()).click(); + await $(DoesAnyoneLiveHerePage.no()).click(); + await $(DoesAnyoneLiveHerePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DateOfBirthPage.url().split("/").slice(-1)[0]); }); describe("Given a confirmation question", () => { - it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", () => { + it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", async () => { // Answer question preceding confirmation question - $(DateOfBirthPage.day()).setValue("01"); - $(DateOfBirthPage.month()).setValue("01"); - $(DateOfBirthPage.year()).setValue("2015"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("01"); + await $(DateOfBirthPage.month()).setValue("01"); + await $(DateOfBirthPage.year()).setValue("2015"); + await $(DateOfBirthPage.submit()).click(); // Answer 'No' to confirmation question - $(ConfirmDateOfBirthPage.noINeedToChangeTheirDateOfBirth()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); - expect(browser.getUrl()).to.contain(DateOfBirthPage.pageName); + await $(ConfirmDateOfBirthPage.noINeedToChangeTheirDateOfBirth()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DateOfBirthPage.pageName); }); }); describe("Given I have answered a confirmation question", () => { - it("When I view the summary, Then the confirmation question should not be displayed", () => { - $(DateOfBirthPage.day()).setValue("01"); - $(DateOfBirthPage.month()).setValue("01"); - $(DateOfBirthPage.year()).setValue("2015"); - $(DateOfBirthPage.submit()).click(); + it("When I view the summary, Then the confirmation question should not be displayed", async () => { + await $(DateOfBirthPage.day()).setValue("01"); + await $(DateOfBirthPage.month()).setValue("01"); + await $(DateOfBirthPage.year()).setValue("2015"); + await $(DateOfBirthPage.submit()).click(); - $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); + await $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); - expect(browser.getUrl()).to.contain("sections/default-section/"); - expect($(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).to.be.false; + await expect(await browser.getUrl()).to.contain("sections/default-section/"); + await expect(await $(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).to.be.false; }); }); describe("Given a confirmation question with a skip condition", () => { - it("When I submit an a date of birth where the age is at least '16', Then I should be skipped past the confirmation question and directed to the carer question", () => { - $(DateOfBirthPage.day()).setValue("01"); - $(DateOfBirthPage.month()).setValue("01"); - $(DateOfBirthPage.year()).setValue("2000"); - $(DateOfBirthPage.submit()).click(); + it("When I submit an a date of birth where the age is at least '16', Then I should be skipped past the confirmation question and directed to the carer question", async () => { + await $(DateOfBirthPage.day()).setValue("01"); + await $(DateOfBirthPage.month()).setValue("01"); + await $(DateOfBirthPage.year()).setValue("2000"); + await $(DateOfBirthPage.submit()).click(); - expect(browser.getUrl()).to.contain(CarerPage.pageName); - expect($(CarerPage.questionText()).getText()).to.contain("Does John Doe look"); + await expect(await browser.getUrl()).to.contain(CarerPage.pageName); + await expect(await $(CarerPage.questionText()).getText()).to.contain("Does John Doe look"); }); }); }); diff --git a/tests/functional/spec/features/default_value/default_value.spec.js b/tests/functional/spec/features/default_value/default_value.spec.js index 62c826f818..aa508dc0bb 100644 --- a/tests/functional/spec/features/default_value/default_value.spec.js +++ b/tests/functional/spec/features/default_value/default_value.spec.js @@ -5,34 +5,34 @@ import QuestionPageOneSkip from "../../../generated_pages/default_with_skip/numb import QuestionPageThreeSkip from "../../../generated_pages/default_with_skip/number-question-three.page.js"; describe("Feature: Default Value", () => { - it('Given I start default schema, When I do not answer a question, Then "no answer provided" is displayed on the Summary page', () => { - browser.openQuestionnaire("test_default.json"); - $(QuestionPageOne.submit()).click(); - expect(browser.getUrl()).to.contain(QuestionPageTwo.pageName); - $(QuestionPageTwo.two()).setValue(123); - $(QuestionPageTwo.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.answerOne()).getText()).to.contain("0"); + it('Given I start default schema, When I do not answer a question, Then "no answer provided" is displayed on the Summary page', async () => { + await browser.openQuestionnaire("test_default.json"); + await $(QuestionPageOne.submit()).click(); + await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await $(QuestionPageTwo.two()).setValue(123); + await $(QuestionPageTwo.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.answerOne()).getText()).to.contain("0"); }); - it("Given I have not answered a question containing a default value, When I return to the question, Then no value should be displayed", () => { - browser.openQuestionnaire("test_default.json"); - $(QuestionPageOne.submit()).click(); - expect(browser.getUrl()).to.contain(QuestionPageTwo.pageName); - $(QuestionPageTwo.two()).setValue(123); - $(QuestionPageTwo.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.previous()).click(); - expect(browser.getUrl()).to.contain(QuestionPageTwo.pageName); - $(QuestionPageTwo.previous()).click(); - expect(browser.getUrl()).to.contain(QuestionPageOne.pageName); - expect($(QuestionPageOne.one()).getValue()).to.equal(""); + it("Given I have not answered a question containing a default value, When I return to the question, Then no value should be displayed", async () => { + await browser.openQuestionnaire("test_default.json"); + await $(QuestionPageOne.submit()).click(); + await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await $(QuestionPageTwo.two()).setValue(123); + await $(QuestionPageTwo.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await $(QuestionPageTwo.previous()).click(); + await expect(await browser.getUrl()).to.contain(QuestionPageOne.pageName); + await expect(await $(QuestionPageOne.one()).getValue()).to.equal(""); }); - it("Given I have not answered a question containing a default value, When a skip condition checks for the default value, Then I should skip the next question", () => { - browser.openQuestionnaire("test_default_with_skip.json"); - $(QuestionPageOneSkip.submit()).click(); - expect(browser.getUrl()).to.contain(QuestionPageThreeSkip.pageName); - expect($(QuestionPageThreeSkip.questionText()).getText()).to.contain("Question Three"); + it("Given I have not answered a question containing a default value, When a skip condition checks for the default value, Then I should skip the next question", async () => { + await browser.openQuestionnaire("test_default_with_skip.json"); + await $(QuestionPageOneSkip.submit()).click(); + await expect(await browser.getUrl()).to.contain(QuestionPageThreeSkip.pageName); + await expect(await $(QuestionPageThreeSkip.questionText()).getText()).to.contain("Question Three"); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 5d07111266..409017a77b 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -21,143 +21,143 @@ const testCases = [ }, ]; -const openQuestionnaireAndSetUp = (schema) => { - browser.openQuestionnaire(schema); +const openQuestionnaireAndSetUp = async (schema) => { + await browser.openQuestionnaire(schema); // Set reference date - $(ReferenceDatePage.day()).setValue("1"); - $(ReferenceDatePage.month()).setValue("1"); - $(ReferenceDatePage.year()).setValue("2021"); - $(ReferenceDatePage.submit()).click(); + await $(ReferenceDatePage.day()).setValue("1"); + await $(ReferenceDatePage.month()).setValue("1"); + await $(ReferenceDatePage.year()).setValue("2021"); + await $(ReferenceDatePage.submit()).click(); }; -testCases.forEach((testCase) => { +testCases.forEach(async (testCase) => { describe(`Feature: Dynamically generated answer options driven by a function (${testCase.schemaName})`, () => { describe("Selecting/Deselecting", () => { - before("Open questionnaire", () => { - openQuestionnaireAndSetUp(testCase.schemaName); + before("Open questionnaire", async () => { + await openQuestionnaireAndSetUp(testCase.schemaName); }); describe("Given a dynamic answer options questionnaire and I am on a dynamic checkbox answer page", () => { - it("When I click a checkbox option, then the checkbox should be selected", () => { + it("When I click a checkbox option, then the checkbox should be selected", async () => { for (let i = 0; i < testCase.answerOptionCount; i++) { - $(DynamicCheckboxPage.answerByIndex(i)).click(); - expect($(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.true; + await $(DynamicCheckboxPage.answerByIndex(i)).click(); + await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.true; } }); - it("When I click a selected option, then it should be deselected", () => { + it("When I click a selected option, then it should be deselected", async () => { for (let i = 0; i < testCase.answerOptionCount; i++) { - $(DynamicCheckboxPage.answerByIndex(i)).click(); - expect($(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.false; + await $(DynamicCheckboxPage.answerByIndex(i)).click(); + await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.false; } }); - it("When I submit the page, then I should be taken to the next page", () => { - $(DynamicCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(DynamicRadioPage.pageName); + it("When I submit the page, then I should be taken to the next page", async () => { + await $(DynamicCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicRadioPage.pageName); }); }); describe("Given a dynamic answer options questionnaire and I am on the radio answer page", () => { - it("When I click a radio option, then the radio should be selected", () => { - for (let i = 0; i < testCase.answerOptionCount; i++) { - $(DynamicRadioPage.answerByIndex(i)).click(); - expect($(DynamicRadioPage.answerByIndex(i)).isSelected()).to.be.true; + it("When I click a radio option, then the radio should be selected", async () => { + for (let i = 0; i < (await testCase.answerOptionCount); i++) { + await $(DynamicRadioPage.answerByIndex(i)).click(); + await expect(await $(DynamicRadioPage.answerByIndex(i)).isSelected()).to.be.true; } }); - it("When I submit the page, then I should be taken to the next page", () => { - $(DynamicRadioPage.submit()).click(); - expect(browser.getUrl()).to.contain(DynamicDropdownPage.pageName); + it("When I submit the page, then I should be taken to the next page", async () => { + await $(DynamicRadioPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicDropdownPage.pageName); }); }); describe("Given a dynamic answer options questionnaire and I am on the dropdown page", () => { - it("When I select a dropdown option, then the option should be selected", () => { + it("When I select a dropdown option, then the option should be selected", async () => { for (const value of testCase.dropdownOptionValues) { - $(DynamicDropdownPage.answer()).selectByAttribute("value", value); - expect($(DynamicDropdownPage.answer()).getValue()).to.equal(value); + await $(DynamicDropdownPage.answer()).selectByAttribute("value", value); + await expect(await $(DynamicDropdownPage.answer()).getValue()).to.equal(value); } }); - it("When I submit the page, then I should be taken to the next page", () => { - $(DynamicDropdownPage.submit()).click(); - expect(browser.getUrl()).to.contain(DynamicMutuallyExclusivePage.pageName); + it("When I submit the page, then I should be taken to the next page", async () => { + await $(DynamicDropdownPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicMutuallyExclusivePage.pageName); }); }); describe("Given a dynamic answer options questionnaire and I am on the mutually exclusive page", () => { - it("When I click a dynamic checkbox option, then the checkbox should be selected", () => { - for (let i = 0; i < testCase.answerOptionCount; i++) { - $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); - expect($(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.true; + it("When I click a dynamic checkbox option, then the checkbox should be selected", async () => { + for (let i = 0; i < (await testCase.answerOptionCount); i++) { + await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); + await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.true; } }); - it("When I click a selected option, then it should be deselected", () => { - for (let i = 0; i < testCase.answerOptionCount; i++) { - $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); - expect($(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.false; + it("When I click a selected option, then it should be deselected", async () => { + for (let i = 0; i < (await testCase.answerOptionCount); i++) { + await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); + await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.false; } }); - it("When I click the static checkbox option, then the static checkbox should be selected", () => { + it("When I click the static checkbox option, then the static checkbox should be selected", async () => { // Test exclusive option (Static option) - $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - expect($(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; + await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; }); - it("When I click the selected static checkbox option, then that checkbox should be deselected", () => { + it("When I click the selected static checkbox option, then that checkbox should be deselected", async () => { // Test exclusive option (Static option) - $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - expect($(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.false; + await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.false; }); }); }); describe("Summary page", () => { - beforeEach("Open questionnaire", () => { - openQuestionnaireAndSetUp(testCase.schemaName); + beforeEach("Open questionnaire", async () => { + await openQuestionnaireAndSetUp(testCase.schemaName); }); describe("Given a dynamic answer options questionnaire", () => { - it("When I submit my questions without answering, then the summary should display `No answer provided` for each question", () => { - $(DynamicCheckboxPage.submit()).click(); - $(DynamicRadioPage.submit()).click(); - $(DynamicRadioPage.submit()).click(); - $(DynamicMutuallyExclusivePage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("No answer provided"); - expect($(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("No answer provided"); - expect($(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("No answer provided"); - expect($(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("No answer provided"); + it("When I submit my questions without answering, then the summary should display `No answer provided` for each question", async () => { + await $(DynamicCheckboxPage.submit()).click(); + await $(DynamicRadioPage.submit()).click(); + await $(DynamicRadioPage.submit()).click(); + await $(DynamicMutuallyExclusivePage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("No answer provided"); }); - it("When I select a dynamically generated answer option for each question, then my selected answers should be displayed on the summary", () => { + it("When I select a dynamically generated answer option for each question, then my selected answers should be displayed on the summary", async () => { // Answer Checkbox - $(DynamicCheckboxPage.answerByIndex(2)).click(); // Wednesday 30 December 2020 - $(DynamicCheckboxPage.answerByIndex(3)).click(); // Thursday 30 December 2020 - $(DynamicCheckboxPage.submit()).click(); + await $(DynamicCheckboxPage.answerByIndex(2)).click(); // Wednesday 30 December 2020 + await $(DynamicCheckboxPage.answerByIndex(3)).click(); // Thursday 30 December 2020 + await $(DynamicCheckboxPage.submit()).click(); // Answer Radio - $(DynamicRadioPage.answerByIndex(1)).click(); // Tuesday 29 December 2020 - $(DynamicRadioPage.submit()).click(); + await $(DynamicRadioPage.answerByIndex(1)).click(); // Tuesday 29 December 2020 + await $(DynamicRadioPage.submit()).click(); // Answer Dropdown - $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); // Saturday 2 January 2021 - $(DynamicDropdownPage.submit()).click(); + await $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); // Saturday 2 January 2021 + await $(DynamicDropdownPage.submit()).click(); // Answer Mutually exclusive - $(DynamicMutuallyExclusivePage.answerByIndex(0)).click(); // Monday 28 December 2020 - $(DynamicMutuallyExclusivePage.answerByIndex(6)).click(); // Sunday 3 January 2021 - $(DynamicMutuallyExclusivePage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("Wednesday 30 December 2020\nThursday 31 December 2020"); - expect($(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("Tuesday 29 December 2020"); - expect($(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("Saturday 2 January 2021"); - expect($(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("Monday 28 December 2020\nSunday 3 January 2021"); + await $(DynamicMutuallyExclusivePage.answerByIndex(0)).click(); // Monday 28 December 2020 + await $(DynamicMutuallyExclusivePage.answerByIndex(6)).click(); // Sunday 3 January 2021 + await $(DynamicMutuallyExclusivePage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("Wednesday 30 December 2020\nThursday 31 December 2020"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("Tuesday 29 December 2020"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("Saturday 2 January 2021"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("Monday 28 December 2020\nSunday 3 January 2021"); }); }); }); @@ -166,67 +166,67 @@ testCases.forEach((testCase) => { describe(`Feature: Dynamically generated answer options driven by a function with static options`, () => { describe("Given a dynamic answer options questionnaire with static options", () => { - before("Open questionnaire", () => { - openQuestionnaireAndSetUp("test_dynamic_answer_options_function_driven_with_static_options.json"); + before("Open questionnaire", async () => { + await openQuestionnaireAndSetUp("test_dynamic_answer_options_function_driven_with_static_options.json"); }); - it("When I select a static answer option for each question, then my selected answer(s) should be displayed on the summary", () => { + it("When I select a static answer option for each question, then my selected answer(s) should be displayed on the summary", async () => { // Answer Checkbox - $(DynamicCheckboxPage.answerByIndex(7)).click(); - $(DynamicCheckboxPage.submit()).click(); + await $(DynamicCheckboxPage.answerByIndex(7)).click(); + await $(DynamicCheckboxPage.submit()).click(); // Answer Radio - $(DynamicRadioPage.answerByIndex(7)).click(); - $(DynamicRadioPage.submit()).click(); + await $(DynamicRadioPage.answerByIndex(7)).click(); + await $(DynamicRadioPage.submit()).click(); // Answer Dropdown - $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); - $(DynamicDropdownPage.submit()).click(); + await $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); + await $(DynamicDropdownPage.submit()).click(); // Answer Mutually exclusive // Test static option for mutually exclusive from non exclusive choices - $(DynamicMutuallyExclusivePage.answerByIndex(7)).click(); - $(DynamicMutuallyExclusivePage.submit()).click(); - expect($(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("None of the above"); + await $(DynamicMutuallyExclusivePage.answerByIndex(7)).click(); + await $(DynamicMutuallyExclusivePage.submit()).click(); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("None of the above"); // Test exclusive static choice - $(SubmitPage.previous()).click(); - $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - $(DynamicMutuallyExclusivePage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("I did not work"); - expect($(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("I did not work"); - expect($(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("I did not work"); - expect($(SubmitPage.dynamicMutuallyExclusiveStaticAnswer()).getText()).to.equal("I did not work"); + await $(SubmitPage.previous()).click(); + await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); + await $(DynamicMutuallyExclusivePage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("I did not work"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("I did not work"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("I did not work"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveStaticAnswer()).getText()).to.equal("I did not work"); }); - it("When I edit and change the reference date which the other questions are dependent on, then all dependent answers are removed", () => { - $(SubmitPage.referenceDateAnswerEdit()).click(); - $(ReferenceDatePage.day()).setValue("2"); - $(ReferenceDatePage.submit()).click(); + it("When I edit and change the reference date which the other questions are dependent on, then all dependent answers are removed", async () => { + await $(SubmitPage.referenceDateAnswerEdit()).click(); + await $(ReferenceDatePage.day()).setValue("2"); + await $(ReferenceDatePage.submit()).click(); - expect($(DynamicCheckboxPage.answerByIndex(7)).isSelected()).to.be.false; + await expect(await $(DynamicCheckboxPage.answerByIndex(7)).isSelected()).to.be.false; - $(DynamicCheckboxPage.answerByIndex(7)).click(); - $(DynamicCheckboxPage.submit()).click(); + await $(DynamicCheckboxPage.answerByIndex(7)).click(); + await $(DynamicCheckboxPage.submit()).click(); - expect($(DynamicRadioPage.answerByIndex(7)).isSelected()).to.be.false; + await expect(await $(DynamicRadioPage.answerByIndex(7)).isSelected()).to.be.false; - $(DynamicRadioPage.answerByIndex(7)).click(); - $(DynamicRadioPage.submit()).click(); + await $(DynamicRadioPage.answerByIndex(7)).click(); + await $(DynamicRadioPage.submit()).click(); - expect($(DynamicDropdownPage.answer()).getText()).to.contain("Select an answer"); + await expect(await $(DynamicDropdownPage.answer()).getText()).to.contain("Select an answer"); - $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); - $(DynamicDropdownPage.submit()).click(); + await $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); + await $(DynamicDropdownPage.submit()).click(); // The Mutually exclusive answer is not removed as it is a different answer_id to the dependent, however the block must be re-submitted - expect($(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; - $(DynamicMutuallyExclusivePage.submit()).click(); + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; + await $(DynamicMutuallyExclusivePage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js index 6aa6260a88..f599bb5438 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js @@ -6,53 +6,53 @@ import DynamicMutuallyExclusivePage from "../../../generated_pages/dynamic_answe describe(`Feature: Dynamically generated mandatory answer options driven by a function with static options`, () => { describe("Given a mandatory dynamic answer options questionnaire with static options", () => { - before("Open questionnaire", () => { - browser.openQuestionnaire("test_dynamic_answer_options_function_driven_with_static_options_mandatory.json"); + before("Open questionnaire", async () => { + await browser.openQuestionnaire("test_dynamic_answer_options_function_driven_with_static_options_mandatory.json"); // Set reference date - $(ReferenceDatePage.day()).setValue("1"); - $(ReferenceDatePage.month()).setValue("1"); - $(ReferenceDatePage.year()).setValue("2021"); - $(ReferenceDatePage.submit()).click(); + await $(ReferenceDatePage.day()).setValue("1"); + await $(ReferenceDatePage.month()).setValue("1"); + await $(ReferenceDatePage.year()).setValue("2021"); + await $(ReferenceDatePage.submit()).click(); }); - it("When I do not answer the Checkbox question and submit, then an error message and the question error panel should be displayed.", () => { - $(DynamicCheckboxPage.submit()).click(); - expect($(DynamicCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(DynamicCheckboxPage.answerErrorItem()).getText()).to.contain("Select at least one answer"); - expect($(DynamicCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + it("When I do not answer the Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { + await $(DynamicCheckboxPage.submit()).click(); + await expect(await $(DynamicCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(DynamicCheckboxPage.answerErrorItem()).getText()).to.contain("Select at least one answer"); + await expect(await $(DynamicCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; }); - it("When I do not answer the Radio question and submit, then an error message and the question error panel should be displayed.", () => { + it("When I do not answer the Radio question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Radio question - $(DynamicCheckboxPage.answerByIndex(0)).click(); - $(DynamicCheckboxPage.submit()).click(); + await $(DynamicCheckboxPage.answerByIndex(0)).click(); + await $(DynamicCheckboxPage.submit()).click(); - $(DynamicRadioPage.submit()).click(); - expect($(DynamicRadioPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(DynamicRadioPage.answerErrorItem()).getText()).to.contain("Select an answer"); - expect($(DynamicRadioPage.questionErrorPanel()).isExisting()).to.be.true; + await $(DynamicRadioPage.submit()).click(); + await expect(await $(DynamicRadioPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(DynamicRadioPage.answerErrorItem()).getText()).to.contain("Select an answer"); + await expect(await $(DynamicRadioPage.questionErrorPanel()).isExisting()).to.be.true; }); - it("When I do not answer the Dropdown question and submit, then an error message and the question error panel should be displayed.", () => { + it("When I do not answer the Dropdown question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Dropdown question - $(DynamicRadioPage.answerByIndex(0)).click(); - $(DynamicRadioPage.submit()).click(); + await $(DynamicRadioPage.answerByIndex(0)).click(); + await $(DynamicRadioPage.submit()).click(); - $(DynamicDropdownPage.submit()).click(); - expect($(DynamicDropdownPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(DynamicDropdownPage.answerErrorItem()).getText()).to.contain("Select an answer"); - expect($(DynamicDropdownPage.questionErrorPanel()).isExisting()).to.be.true; + await $(DynamicDropdownPage.submit()).click(); + await expect(await $(DynamicDropdownPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(DynamicDropdownPage.answerErrorItem()).getText()).to.contain("Select an answer"); + await expect(await $(DynamicDropdownPage.questionErrorPanel()).isExisting()).to.be.true; }); - it("When I do not answer the Mutually Exclusive Checkbox question and submit, then an error message and the question error panel should be displayed.", () => { + it("When I do not answer the Mutually Exclusive Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Mutually Exclusive question - $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); - $(DynamicDropdownPage.submit()).click(); + await $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); + await $(DynamicDropdownPage.submit()).click(); - $(DynamicMutuallyExclusivePage.submit()).click(); - expect($(DynamicMutuallyExclusivePage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - expect($(DynamicMutuallyExclusivePage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - expect($(DynamicMutuallyExclusivePage.questionErrorPanel()).isExisting()).to.be.true; + await $(DynamicMutuallyExclusivePage.submit()).click(); + await expect(await $(DynamicMutuallyExclusivePage.errorHeader()).getText()).to.contain("There is a problem with your answer"); + await expect(await $(DynamicMutuallyExclusivePage.errorNumber(1)).getText()).to.contain("Select at least one answer"); + await expect(await $(DynamicMutuallyExclusivePage.questionErrorPanel()).isExisting()).to.be.true; }); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js index bf58c93895..1411498919 100644 --- a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js +++ b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js @@ -5,46 +5,46 @@ import SubmitPage from "../../../generated_pages/dynamic_radio_options_from_chec describe("Dynamic radio options from checkbox answers", () => { describe("Given the dynamic radio options from checkbox questionnaire and I am on the checkbox answer page", () => { - it("When the respondent answers the checkbox question and submits, Then the radio question should show the answers from that checkbox as options, as well as a static option", () => { - browser.openQuestionnaire("test_dynamic_radio_options_from_checkbox.json"); - $(InjurySustainedPage.head()).click(); - $(InjurySustainedPage.body()).click(); - $(InjurySustainedPage.submit()).click(); - - expect(browser.getUrl()).to.contain(MostSeriousInjuryPage.pageName); - expect($(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).to.contain("Head"); - expect($(MostSeriousInjuryPage.answerLabelByIndex(1)).getText()).to.contain("Body"); - expect($(MostSeriousInjuryPage.answerLabelByIndex(2)).getText()).to.contain("They were of equal severity (static option)"); - expect($(MostSeriousInjuryPage.answerLabelByIndex(3)).isExisting()).to.be.false; + it("When the respondent answers the checkbox question and submits, Then the radio question should show the answers from that checkbox as options, as well as a static option", async () => { + await browser.openQuestionnaire("test_dynamic_radio_options_from_checkbox.json"); + await $(InjurySustainedPage.head()).click(); + await $(InjurySustainedPage.body()).click(); + await $(InjurySustainedPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(MostSeriousInjuryPage.pageName); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).to.contain("Head"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(1)).getText()).to.contain("Body"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(2)).getText()).to.contain("They were of equal severity (static option)"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(3)).isExisting()).to.be.false; }); - it("When the respondent answers the radio question and submits, Then the next radio question should show only the answers from the first checkbox as options", () => { - $(MostSeriousInjuryPage.answerLabelByIndex(0)).click(); - $(MostSeriousInjuryPage.submit()).click(); + it("When the respondent answers the radio question and submits, Then the next radio question should show only the answers from the first checkbox as options", async () => { + await $(MostSeriousInjuryPage.answerLabelByIndex(0)).click(); + await $(MostSeriousInjuryPage.submit()).click(); - expect(browser.getUrl()).to.contain(HealedTheQuickestPage.pageName); - expect($(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).to.contain("Head"); - expect($(HealedTheQuickestPage.answerLabelByIndex(1)).getText()).to.contain("Body"); - expect($(HealedTheQuickestPage.answerLabelByIndex(2)).isExisting()).to.be.false; + await expect(await browser.getUrl()).to.contain(HealedTheQuickestPage.pageName); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).to.contain("Head"); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(1)).getText()).to.contain("Body"); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(2)).isExisting()).to.be.false; }); - it("When the respondent answers the radio question and submits, then the summary should display all the answers correctly", () => { - $(HealedTheQuickestPage.answerLabelByIndex(1)).click(); - $(HealedTheQuickestPage.submit()).click(); + it("When the respondent answers the radio question and submits, then the summary should display all the answers correctly", async () => { + await $(HealedTheQuickestPage.answerLabelByIndex(1)).click(); + await $(HealedTheQuickestPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.injurySustainedAnswer()).getText()).to.contain("Head\nBody"); - expect($(SubmitPage.mostSeriousInjuryAnswer()).getText()).to.contain("Head"); - expect($(SubmitPage.healedTheQuickestAnswer()).getText()).to.contain("Body"); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.injurySustainedAnswer()).getText()).to.contain("Head\nBody"); + await expect(await $(SubmitPage.mostSeriousInjuryAnswer()).getText()).to.contain("Head"); + await expect(await $(SubmitPage.healedTheQuickestAnswer()).getText()).to.contain("Body"); }); - it("When I edit and change the answer which the dynamic options is dependent on, then my selected answers are removed", () => { - $(SubmitPage.injurySustainedAnswerEdit()).click(); - $(InjurySustainedPage.arms()).click(); - $(InjurySustainedPage.submit()).click(); + it("When I edit and change the answer which the dynamic options is dependent on, then my selected answers are removed", async () => { + await $(SubmitPage.injurySustainedAnswerEdit()).click(); + await $(InjurySustainedPage.arms()).click(); + await $(InjurySustainedPage.submit()).click(); - expect($(MostSeriousInjuryPage.answerByIndex(0)).isSelected()).to.be.false; - expect($(MostSeriousInjuryPage.answerByIndex(1)).isSelected()).to.be.false; + await expect(await $(MostSeriousInjuryPage.answerByIndex(0)).isSelected()).to.be.false; + await expect(await $(MostSeriousInjuryPage.answerByIndex(1)).isSelected()).to.be.false; }); }); }); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js index a8164bf13e..4fd74fd116 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js @@ -3,40 +3,40 @@ import sectionTwo from "../../../generated_pages/section_enabled_checkbox/sectio import SubmitPage from "../../../generated_pages/section_enabled_checkbox/submit.page"; describe("Feature: Section Enabled Based On Checkbox Answers", () => { - beforeEach("Open survey", () => { - browser.openQuestionnaire("test_section_enabled_checkbox.json"); + beforeEach("Open survey", async () => { + await browser.openQuestionnaire("test_section_enabled_checkbox.json"); }); - it("When the user selects `Section 2` and submits, Then section 2 should be displayed", () => { - $(sectionOne.section1Section2()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 2` and submits, Then section 2 should be displayed", async () => { + await $(sectionOne.section1Section2()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain("section-2-block"); + await expect(await browser.getUrl()).to.contain("section-2-block"); }); - it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", () => { - $(sectionOne.section1Section3()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { + await $(sectionOne.section1Section3()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain("section-3-block"); + await expect(await browser.getUrl()).to.contain("section-3-block"); }); - it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", () => { - $(sectionOne.section1Section2()).click(); - $(sectionOne.section1Section3()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { + await $(sectionOne.section1Section2()).click(); + await $(sectionOne.section1Section3()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain("section-2-block"); - $(sectionTwo.submit()).click(); - expect(browser.getUrl()).to.contain("section-3-block"); + await expect(await browser.getUrl()).to.contain("section-2-block"); + await $(sectionTwo.submit()).click(); + await expect(await browser.getUrl()).to.contain("section-3-block"); }); - it("When the user selects `Neither` and submits, Then they should be taken straight to the summary", () => { - $(sectionOne.section1ExclusiveNeither()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Neither` and submits, Then they should be taken straight to the summary", async () => { + await $(sectionOne.section1ExclusiveNeither()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); - expect($(SubmitPage.section2Question()).isExisting()).to.be.false; - expect($(SubmitPage.section3Question()).isExisting()).to.be.false; + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; + await expect(await $(SubmitPage.section3Question()).isExisting()).to.be.false; }); }); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js index efea6e0b8f..6c8bd37918 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js @@ -2,50 +2,50 @@ import sectionOne from "../../../generated_pages/section_enabled_hub/section-1-b import hubPage from "../../../base_pages/hub.page"; describe("Feature: Section Enabled With Hub", () => { - beforeEach("Open survey", () => { - browser.openQuestionnaire("test_section_enabled_hub.json"); + beforeEach("Open survey", async () => { + await browser.openQuestionnaire("test_section_enabled_hub.json"); }); - it("When the user selects `Section 2` and submits, Then only section 2 should be displayed on the hub", () => { - $(sectionOne.section1Section2()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 2` and submits, Then only section 2 should be displayed on the hub", async () => { + await $(sectionOne.section1Section2()).click(); + await $(sectionOne.submit()).click(); - expect($(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; - expect($(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; + await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); - expect($(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; }); - it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", () => { - $(sectionOne.section1Section3()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { + await $(sectionOne.section1Section3()).click(); + await $(sectionOne.submit()).click(); - expect($(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; - expect($(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; + await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); - expect($(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; }); - it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", () => { - $(sectionOne.section1Section2()).click(); - $(sectionOne.section1Section3()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { + await $(sectionOne.section1Section2()).click(); + await $(sectionOne.section1Section3()).click(); + await $(sectionOne.submit()).click(); - expect($(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; - expect($(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; + await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); - expect($(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; - expect($(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; + await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); }); - it("When the user selects `Neither` and submits, Then hub should not display any other section and should be in the `Completed` state.", () => { - $(sectionOne.section1ExclusiveNeither()).click(); - $(sectionOne.submit()).click(); + it("When the user selects `Neither` and submits, Then hub should not display any other section and should be in the `Completed` state.", async () => { + await $(sectionOne.section1ExclusiveNeither()).click(); + await $(sectionOne.submit()).click(); - expect($(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; - expect($(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; - expect($(hubPage.submit()).getText()).to.equal("Submit survey"); - expect($(hubPage.heading()).getText()).to.equal("Submit survey"); + await expect(await $(hubPage.submit()).getText()).to.equal("Submit survey"); + await expect(await $(hubPage.heading()).getText()).to.equal("Submit survey"); }); }); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js index 1f0646312e..4fb655901f 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js @@ -2,40 +2,40 @@ import sectionOne from "../../../generated_pages/section_enabled_radio/section-1 import SubmitPage from "../../../generated_pages/section_enabled_radio/submit.page"; describe("Feature: Section Enabled Based On Radio Answers", () => { - beforeEach("Open survey", () => { - browser.openQuestionnaire("test_section_enabled_radio.json"); + beforeEach("Open survey", async () => { + await browser.openQuestionnaire("test_section_enabled_radio.json"); }); - it("When the user answers `Yes, enable section 2` and submits, Then section 2 should be displayed", () => { - $(sectionOne.yesEnableSection2()).click(); - $(sectionOne.submit()).click(); + it("When the user answers `Yes, enable section 2` and submits, Then section 2 should be displayed", async () => { + await $(sectionOne.yesEnableSection2()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain("section-2-block"); + await expect(await browser.getUrl()).to.contain("section-2-block"); }); - it("When the user answers `No, disable section 2` and submits, Then they should be taking straight to the summary", () => { - $(sectionOne.noDisableSection2()).click(); - $(sectionOne.submit()).click(); + it("When the user answers `No, disable section 2` and submits, Then they should be taking straight to the summary", async () => { + await $(sectionOne.noDisableSection2()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); - expect($(SubmitPage.section2Question()).isExisting()).to.be.false; + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; }); describe("Given that section 2 is enabled", () => { - beforeEach("Enable section 2", () => { - $(sectionOne.yesEnableSection2()).click(); - $(sectionOne.submit()).click(); + beforeEach("Enable section 2", async () => { + await $(sectionOne.yesEnableSection2()).click(); + await $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain("section-2-block"); + await expect(await browser.getUrl()).to.contain("section-2-block"); }); - it("When the user changes the answers and disables section 2, Then they should be taken straight to the summary", () => { - browser.back(); - expect(browser.getUrl()).to.contain("section-1-block"); + it("When the user changes the answers and disables section 2, Then they should be taken straight to the summary", async () => { + await browser.back(); + await expect(await browser.getUrl()).to.contain("section-1-block"); - $(sectionOne.noDisableSection2()).click(); - $(sectionOne.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + await $(sectionOne.noDisableSection2()).click(); + await $(sectionOne.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js index 80c8aa6605..cd8221fa3d 100644 --- a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js @@ -3,31 +3,31 @@ import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; import HubPage from "../../../base_pages/hub.page.js"; describe("Choose another section link", () => { - it("When a user first views the Hub, then the link should not be displayed", () => { - browser.openQuestionnaire("test_hub_and_spoke.json"); - expect($("body").getText()).to.not.have.string("Choose another section and return to this later"); + it("When a user first views the Hub, then the link should not be displayed", async () => { + await browser.openQuestionnaire("test_hub_and_spoke.json"); + await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); }); - it("When a user views the first question and the hub is not available, then the link should not be displayed", () => { - browser.openQuestionnaire("test_hub_complete_sections.json"); - expect($("body").getText()).to.not.have.string("Choose another section and return to this later"); + it("When a user views the first question and the hub is not available, then the link should not be displayed", async () => { + await browser.openQuestionnaire("test_hub_complete_sections.json"); + await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); }); - it("When a user starts a new section and the hub is available, then the link should be displayed", () => { - browser.openQuestionnaire("test_hub_complete_sections.json"); - $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(HubPage.summaryRowLink("accommodation-section")).click(); - expect($("body").getText()).to.contain("Choose another section and return to this later"); + it("When a user starts a new section and the hub is available, then the link should be displayed", async () => { + await browser.openQuestionnaire("test_hub_complete_sections.json"); + await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(HubPage.summaryRowLink("accommodation-section")).click(); + await expect(await $("body").getText()).to.contain("Choose another section and return to this later"); }); - it("When a user gets to a section summary and the hub is available, then the link should not be displayed", () => { - browser.openQuestionnaire("test_hub_complete_sections.json"); - $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(HubPage.summaryRowLink("accommodation-section")).click(); - $(ProxyPage.noIMAnsweringForMyself()).click(); - $(ProxyPage.submit()).click(); - expect($("body").getText()).to.not.have.string("Choose another section and return to this later"); + it("When a user gets to a section summary and the hub is available, then the link should not be displayed", async () => { + await browser.openQuestionnaire("test_hub_complete_sections.json"); + await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(HubPage.summaryRowLink("accommodation-section")).click(); + await $(ProxyPage.noIMAnsweringForMyself()).click(); + await $(ProxyPage.submit()).click(); + await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 8210466263..2fc6e8c0b2 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -13,245 +13,245 @@ describe("Feature: Hub and Spoke", () => { const hubAndSpokeSchema = "test_hub_and_spoke.json"; describe("Given I am completing the test_hub_context schema,", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire(hubAndSpokeSchema); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); }); - it("When a user first views the Hub, The Hub should be in a continue state", () => { - expect($(HubPage.submit()).getText()).to.contain("Continue"); - expect($(HubPage.heading()).getText()).to.contain("Choose another section to complete"); - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); - expect($(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); - expect($(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); + it("When a user first views the Hub, The Hub should be in a continue state", async () => { + await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); + await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); }); - it("When a user views the Hub, any section with show_on_hub set to true should appear", () => { - expect($(HubPage.summaryItems()).getText()).to.contain("Employment"); - expect($(HubPage.summaryItems()).getText()).to.contain("Accommodation"); - expect($(HubPage.summaryItems()).getText()).to.contain("Household residents"); + it("When a user views the Hub, any section with show_on_hub set to true should appear", async () => { + await expect(await $(HubPage.summaryItems()).getText()).to.contain("Employment"); + await expect(await $(HubPage.summaryItems()).getText()).to.contain("Accommodation"); + await expect(await $(HubPage.summaryItems()).getText()).to.contain("Household residents"); }); - it("When a user views the Hub, any section with show_on_hub set to false should not appear", () => { - expect($(HubPage.summaryItems()).getText()).not.to.contain("Relationships"); + it("When a user views the Hub, any section with show_on_hub set to false should not appear", async () => { + await expect(await $(HubPage.summaryItems()).getText()).not.to.contain("Relationships"); }); - it("When the user click the 'Save and sign out' button then they should be redirected to the correct log out url", () => { - $(HubPage.saveSignOut()).click(); + it("When the user click the 'Save and sign out' button then they should be redirected to the correct log out url", async () => { + await $(HubPage.saveSignOut()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain("/surveys/todo"); + await expect(expectedUrl).to.contain("/surveys/todo"); }); - it("When a user views the Hub, Then the page title should be Choose another section to complete", () => { - const pageTitle = browser.getTitle(); - expect(pageTitle).to.equal("Choose another section to complete - Hub & Spoke"); + it("When a user views the Hub, Then the page title should be Choose another section to complete", async () => { + const pageTitle = await browser.getTitle(); + await expect(pageTitle).to.equal("Choose another section to complete - Hub & Spoke"); }); }); describe("Given a user has not started a section", () => { - beforeEach("Open survey", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); - expect($(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); - expect($(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); + beforeEach("Open survey", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); }); - it("When the user starts a section, Then the first question in the section should be displayed", () => { - $(HubPage.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); + it("When the user starts a section, Then the first question in the section should be displayed", async () => { + await $(HubPage.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); }); - it("When the user starts a section and clicks the Previous link on the first question, Then they should be taken back to the Hub", () => { - $(HubPage.submit()).click(); - $(EmploymentStatusBlockPage.previous()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HubPage.url()); + it("When the user starts a section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { + await $(HubPage.submit()).click(); + await $(EmploymentStatusBlockPage.previous()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HubPage.url()); }); }); describe("Given a user has started a section", () => { - before("Start section", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("employment-section")).click(); - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); + before("Start section", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("employment-section")).click(); + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); }); - it("When the user returns to the Hub, Then the Hub should be in a continue state", () => { - browser.url(HubPage.url()); - expect($(HubPage.submit()).getText()).to.contain("Continue"); - expect($(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { + await browser.url(HubPage.url()); + await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); + await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); }); - it("When the user returns to the Hub, Then the section should be marked as 'Partially completed'", () => { - browser.url(HubPage.url()); - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); + it("When the user returns to the Hub, Then the section should be marked as 'Partially completed'", async () => { + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); }); - it("When the user returns to the Hub and restarts the same section, Then they should be redirected to the first incomplete block", () => { - browser.url(HubPage.url()); - $(HubPage.summaryRowLink("employment-section")).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); + it("When the user returns to the Hub and restarts the same section, Then they should be redirected to the first incomplete block", async () => { + await browser.url(HubPage.url()); + await $(HubPage.summaryRowLink("employment-section")).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); }); }); describe("Given a user has completed a section", () => { - beforeEach("Complete section", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("employment-section")).click(); - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(EmploymentTypeBlockPage.studying()).click(); + beforeEach("Complete section", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("employment-section")).click(); + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(EmploymentTypeBlockPage.studying()).click(); }); - it("When the user clicks the 'Continue' button, it should return them to the hub", () => { - $(EmploymentTypeBlockPage.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HubPage.url()); + it("When the user clicks the 'Continue' button, it should return them to the hub", async () => { + await $(EmploymentTypeBlockPage.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HubPage.url()); }); - it("When the user returns to the Hub, Then the Hub should be in a continue state", () => { - $(EmploymentTypeBlockPage.submit()).click(); - expect($(HubPage.submit()).getText()).to.contain("Continue"); - expect($(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { + await $(EmploymentTypeBlockPage.submit()).click(); + await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); + await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); }); - it("When the user returns to the Hub, Then the section should be marked as 'Completed'", () => { - $(EmploymentTypeBlockPage.submit()).click(); - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); + it("When the user returns to the Hub, Then the section should be marked as 'Completed'", async () => { + await $(EmploymentTypeBlockPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); }); - it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", () => { - $(EmploymentTypeBlockPage.submit()).click(); - $(HubPage.summaryRowLink("employment-section")).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); + it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", async () => { + await $(EmploymentTypeBlockPage.submit()).click(); + await $(HubPage.summaryRowLink("employment-section")).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); }); - it("When the user returns to the Hub and continues, Then they should progress to the next section", () => { - $(EmploymentTypeBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(HubPage.url()); - $(HubPage.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(ProxyPage.url()); + it("When the user returns to the Hub and continues, Then they should progress to the next section", async () => { + await $(EmploymentTypeBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.url()); + await $(HubPage.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(ProxyPage.url()); }); }); describe("Given a user has completed a section and is on the Hub page", () => { - beforeEach("Complete section", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("employment-section")).click(); - $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); - }); - - it("When the user clicks the 'View answers' link and incompletes the section, Then they the should be taken to the next incomplete question on 'Continue", () => { - $(HubPage.summaryRowLink("employment-section")).click(); - expect(browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); - }); - - it("When the user clicks the 'View answers' link and incompletes the section and returns to the hub, Then the section should be marked as 'Partially completed'", () => { - $(HubPage.summaryRowLink("employment-section")).click(); - expect(browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - browser.url(HubPage.url()); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HubPage.url()); - expect($(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); + beforeEach("Complete section", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("employment-section")).click(); + await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); + }); + + it("When the user clicks the 'View answers' link and incompletes the section, Then they the should be taken to the next incomplete question on 'Continue", async () => { + await $(HubPage.summaryRowLink("employment-section")).click(); + await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); + }); + + it("When the user clicks the 'View answers' link and incompletes the section and returns to the hub, Then the section should be marked as 'Partially completed'", async () => { + await $(HubPage.summaryRowLink("employment-section")).click(); + await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await browser.url(HubPage.url()); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HubPage.url()); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); }); }); describe("Given a user has completed all sections", () => { - beforeEach("Complete all sections", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("employment-section")).click(); - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(EmploymentTypeBlockPage.studying()).click(); - $(EmploymentTypeBlockPage.submit()).click(); - $(HubPage.submit()).click(); - $(ProxyPage.yes()).click(); - $(ProxyPage.submit()).click(); - $(AccomodationDetailsSummaryBlockPage.submit()).click(); - $(HubPage.submit()).click(); - $(DoesAnyoneLiveHere.no()).click(); - $(DoesAnyoneLiveHere.submit()).click(); - $(HouseholdSummary.submit()).click(); - $(HubPage.submit()).click(); - $(AnyoneRelated.yes()).click(); - $(AnyoneRelated.submit()).click(); - $(RelationshipsSummary.submit()).click(); - }); - - it("It should return them to the hub", () => { - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HubPage.url()); - }); - - it("When the user returns to the Hub, Then the Hub should be in a completed state", () => { - expect($(HubPage.submit()).getText()).to.contain("Submit survey"); - expect($(HubPage.heading()).getText()).to.contain("Submit survey"); - }); - - it("When the user submits, it should show the thankyou page", () => { - $(HubPage.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain("thank-you"); + beforeEach("Complete all sections", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("employment-section")).click(); + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(EmploymentTypeBlockPage.studying()).click(); + await $(EmploymentTypeBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(ProxyPage.yes()).click(); + await $(ProxyPage.submit()).click(); + await $(AccomodationDetailsSummaryBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(DoesAnyoneLiveHere.no()).click(); + await $(DoesAnyoneLiveHere.submit()).click(); + await $(HouseholdSummary.submit()).click(); + await $(HubPage.submit()).click(); + await $(AnyoneRelated.yes()).click(); + await $(AnyoneRelated.submit()).click(); + await $(RelationshipsSummary.submit()).click(); + }); + + it("It should return them to the hub", async () => { + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HubPage.url()); + }); + + it("When the user returns to the Hub, Then the Hub should be in a completed state", async () => { + await expect(await $(HubPage.submit()).getText()).to.contain("Submit survey"); + await expect(await $(HubPage.heading()).getText()).to.contain("Submit survey"); + }); + + it("When the user submits, it should show the thankyou page", async () => { + await $(HubPage.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain("thank-you"); }); }); describe("Given a user opens a schema with required sections", () => { - beforeEach("Load survey", () => { - browser.openQuestionnaire("test_hub_complete_sections.json"); + beforeEach("Load survey", async () => { + await browser.openQuestionnaire("test_hub_complete_sections.json"); }); - it("The hub should not show first of all", () => { - expect(browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + it("The hub should not show first of all", async () => { + await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); }); - it("The hub should only display when required sections are complete", () => { - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(EmploymentTypeBlockPage.studying()).click(); - $(EmploymentTypeBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(HubPage.url()); + it("The hub should only display when required sections are complete", async () => { + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(EmploymentTypeBlockPage.studying()).click(); + await $(EmploymentTypeBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.url()); }); }); describe("Given a section is complete and the user has been returned to a section summary by clicking the 'View answers' link ", () => { - beforeEach("Complete section", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("household-section")).click(); - $(DoesAnyoneLiveHere.no()).click(); - $(DoesAnyoneLiveHere.submit()).click(); - $(HouseholdSummary.submit()).click(); - }); - - it("When there are no changes, continue returns directly to the hub", () => { - $(HubPage.summaryRowLink("household-section")).click(); - $(HouseholdSummary.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HubPage.url()); - }); - - it("When there are changes, which would set the section to in_progress it routes accordingly", () => { - $(HubPage.summaryRowLink("household-section")).click(); - $(HouseholdSummary.doesAnyoneLiveHereAnswerEdit()).click(); - $(DoesAnyoneLiveHere.yes()).click(); - $(DoesAnyoneLiveHere.submit()).click(); - $(HouseholdSummary.submit()).click(); - const expectedUrl = browser.getUrl(); - expect(expectedUrl).to.contain(HowManyPeopleLiveHere.url()); + beforeEach("Complete section", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("household-section")).click(); + await $(DoesAnyoneLiveHere.no()).click(); + await $(DoesAnyoneLiveHere.submit()).click(); + await $(HouseholdSummary.submit()).click(); + }); + + it("When there are no changes, continue returns directly to the hub", async () => { + await $(HubPage.summaryRowLink("household-section")).click(); + await $(HouseholdSummary.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HubPage.url()); + }); + + it("When there are changes, which would set the section to in_progress it routes accordingly", async () => { + await $(HubPage.summaryRowLink("household-section")).click(); + await $(HouseholdSummary.doesAnyoneLiveHereAnswerEdit()).click(); + await $(DoesAnyoneLiveHere.yes()).click(); + await $(DoesAnyoneLiveHere.submit()).click(); + await $(HouseholdSummary.submit()).click(); + const expectedUrl = await browser.getUrl(); + await expect(expectedUrl).to.contain(HowManyPeopleLiveHere.url()); }); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js index 9cf3031c4f..cae42aba0a 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js @@ -6,25 +6,25 @@ import HubPage from "../../../base_pages/hub.page.js"; describe("Feature: Hub and Spoke with custom content", () => { const hubAndSpokeSchema = "test_hub_and_spoke_custom_content.json"; - it("When the questionnaire is incomplete, then custom content should be displayed correctly", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - expect($(HubPage.heading()).getText()).to.contain("Choose another section to complete"); - expect($(HubPage.guidance()).isExisting()).to.be.false; - expect($(HubPage.submit()).getText()).to.contain("Continue"); - expect($(HubPage.warning()).isExisting()).to.be.false; + it("When the questionnaire is incomplete, then custom content should be displayed correctly", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + await expect(await $(HubPage.guidance()).isExisting()).to.be.false; + await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); + await expect(await $(HubPage.warning()).isExisting()).to.be.false; }); - it("When the questionnaire is complete, then custom content should be displayed correctly", () => { - browser.openQuestionnaire(hubAndSpokeSchema); - $(HubPage.summaryRowLink("household-section")).click(); - $(DoesAnyoneLiveHere.yes()).click(); - $(DoesAnyoneLiveHere.submit()).click(); - $(HowManyPeopleLiveHere.answer1()).click(); - $(HowManyPeopleLiveHere.submit()).click(); - $(HouseholdSummary.submit()).click(); - expect($(HubPage.heading()).getText()).to.contain("Submission title"); - expect($(HubPage.guidance()).getText()).to.contain("Submission guidance"); - expect($(HubPage.submit()).getText()).to.contain("Submission button"); - expect($(HubPage.warning()).getText()).to.contain("Submission warning"); + it("When the questionnaire is complete, then custom content should be displayed correctly", async () => { + await browser.openQuestionnaire(hubAndSpokeSchema); + await $(HubPage.summaryRowLink("household-section")).click(); + await $(DoesAnyoneLiveHere.yes()).click(); + await $(DoesAnyoneLiveHere.submit()).click(); + await $(HowManyPeopleLiveHere.answer1()).click(); + await $(HowManyPeopleLiveHere.submit()).click(); + await $(HouseholdSummary.submit()).click(); + await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); + await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); + await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js index 461f9e5536..f6f330b560 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js @@ -3,19 +3,19 @@ import RelationshipsCountPage from "../../../generated_pages/hub_section_require import { SubmitPage } from "../../../base_pages/submit.page"; describe("Hub and spoke section required and enabled", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_hub_section_required_and_enabled.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_hub_section_required_and_enabled.json"); }); - it("Given a relationship question in household, When I answer 'Yes', meaning the second section is enabled, Then I am routed to second section", () => { - $(HouseholdRelationshipsBlockPage.yes()).click(); - $(HouseholdRelationshipsBlockPage.submit()).click(); - expect($(RelationshipsCountPage.legend()).getText()).to.contain("How many people are related"); + it("Given a relationship question in household, When I answer 'Yes', meaning the second section is enabled, Then I am routed to second section", async () => { + await $(HouseholdRelationshipsBlockPage.yes()).click(); + await $(HouseholdRelationshipsBlockPage.submit()).click(); + await expect(await $(RelationshipsCountPage.legend()).getText()).to.contain("How many people are related"); }); - it("Given a relationship question in household, When I answer 'No', Then I am redirected to the hub and can submit my answers without completing the other section", () => { - $(HouseholdRelationshipsBlockPage.no()).click(); - $(HouseholdRelationshipsBlockPage.submit()).click(); - expect($("body").getText()).to.contain("Submit survey"); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + it("Given a relationship question in household, When I answer 'No', Then I am redirected to the hub and can submit my answers without completing the other section", async () => { + await $(HouseholdRelationshipsBlockPage.no()).click(); + await $(HouseholdRelationshipsBlockPage.submit()).click(); + await expect(await $("body").getText()).to.contain("Submit survey"); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/previous.spec.js b/tests/functional/spec/features/hub_and_spoke/previous.spec.js index b71a743025..6248ea3d0f 100644 --- a/tests/functional/spec/features/hub_and_spoke/previous.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/previous.spec.js @@ -5,30 +5,30 @@ import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; const schema = "test_hub_complete_sections.json"; describe("Choose another section link", () => { - beforeEach(() => { - browser.openQuestionnaire(schema); + beforeEach(async () => { + await browser.openQuestionnaire(schema); }); - it("When a user gets to initial question, then the previous location link should not be displayed", () => { - expect($(EmploymentStatusBlockPage.previous()).isExisting()).to.be.false; + it("When a user gets to initial question, then the previous location link should not be displayed", async () => { + await expect(await $(EmploymentStatusBlockPage.previous()).isExisting()).to.be.false; }); - it("When a user gets to the hub, then the previous location link should not be displayed", () => { - $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - expect($(HubPage.previous()).isExisting()).to.be.false; + it("When a user gets to the hub, then the previous location link should not be displayed", async () => { + await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await expect(await $(HubPage.previous()).isExisting()).to.be.false; }); - it("When a user gets to subsequent question, then the previous location link should be displayed", () => { - $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - expect($(EmploymentTypePage.previous()).isExisting()).to.be.true; + it("When a user gets to subsequent question, then the previous location link should be displayed", async () => { + await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await expect(await $(EmploymentTypePage.previous()).isExisting()).to.be.true; }); - it("When a user gets to subsequent questions past the hub, then the previous location link should be displayed", () => { - $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - $(EmploymentStatusBlockPage.submit()).click(); - $(HubPage.summaryRowLink("accommodation-section")).click(); - expect($(ProxyPage.previous()).isExisting()).to.be.true; + it("When a user gets to subsequent questions past the hub, then the previous location link should be displayed", async () => { + await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); + await $(EmploymentStatusBlockPage.submit()).click(); + await $(HubPage.summaryRowLink("accommodation-section")).click(); + await expect(await $(ProxyPage.previous()).isExisting()).to.be.true; }); }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js index faf02232f3..752930b7ae 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js @@ -10,42 +10,44 @@ describe("Last viewed question guidance", () => { }; describe("Given the last viewed question guidance questionnaire", () => { - before("Open survey", () => { - browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); + before("Open survey", async () => { + await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); }); - it("When the respondent first launches the survey, then last question guidance is not shown", () => { - expect(browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); - expect($(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent first launches the survey, then last question guidance is not shown", async () => { + await expect(await browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent resumes on the first block of a section, then last question guidance is not shown", () => { - $(HouseholdInterstitialPage.saveSignOut()).click(); - browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); - expect(browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); - expect($(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent resumes on the first block of a section, then last question guidance is not shown", async () => { + await $(HouseholdInterstitialPage.saveSignOut()).click(); + await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); + await browser.pause(100); + await expect(await browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", () => { - $(HouseholdInterstitialPage.submit()).click(); - $(AddressConfirmationPage.saveSignOut()).click(); - browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); - expect(browser.getUrl()).to.contain(AddressConfirmationPage.url()); - expect($(AddressConfirmationPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(HouseholdInterstitialPage.url()); - expect($(AddressConfirmationPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { + await $(HouseholdInterstitialPage.submit()).click(); + await $(AddressConfirmationPage.saveSignOut()).click(); + await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); + await browser.pause(100); + await expect(await browser.getUrl()).to.contain(AddressConfirmationPage.url()); + await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(HouseholdInterstitialPage.url()); + await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; }); - it("When the respondent answers the question and saves and continues, then last question guidance is not shown on the next question", () => { - $(AddressConfirmationPage.yes()).click(); - $(AddressConfirmationPage.submit()).click(); - expect(browser.getUrl()).to.contain(PrimaryPersonListCollectorPage.url()); - expect($(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent answers the question and saves and continues, then last question guidance is not shown on the next question", async () => { + await $(AddressConfirmationPage.yes()).click(); + await $(AddressConfirmationPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(PrimaryPersonListCollectorPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent uses the previous link from the next question, then last question guidance is not shown", () => { - $(AddressConfirmationPage.submit()).click(); - $(PrimaryPersonListCollectorPage.previous()).click(); - expect($(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent uses the previous link from the next question, then last question guidance is not shown", async () => { + await $(AddressConfirmationPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.previous()).click(); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); }); }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js index 0e39c58551..7e324266df 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js @@ -16,94 +16,95 @@ describe("Last viewed question guidance", () => { }; describe("Given the hub has a required section, which has not been completed", () => { - before("Open survey", () => { - browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); + before("Open survey", async () => { + await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); }); - it("When the respondent launches the survey, then last question guidance is not shown", () => { - expect(browser.getUrl()).to.contain(WorkInterstitialPage.url()); - expect($(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent launches the survey, then last question guidance is not shown", async () => { + await expect(await browser.getUrl()).to.contain(WorkInterstitialPage.url()); + await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent saves and resumes from a section which is not started, then last question guidance is not shown", () => { - $(WorkInterstitialPage.saveSignOut()).click(); - browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); - expect(browser.getUrl()).to.contain(WorkInterstitialPage.url()); - expect($(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent saves and resumes from a section which is not started, then last question guidance is not shown", async () => { + await $(WorkInterstitialPage.saveSignOut()).click(); + await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); + await browser.pause(100); + await expect(await browser.getUrl()).to.contain(WorkInterstitialPage.url()); + await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", () => { - $(WorkInterstitialPage.submit()).click(); - $(PaidWorkPage.saveSignOut()).click(); - browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); - expect($(PaidWorkPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(WorkInterstitialPage.url()); - expect($(PaidWorkPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { + await $(WorkInterstitialPage.submit()).click(); + await $(PaidWorkPage.saveSignOut()).click(); + await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); + await expect(await $(PaidWorkPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(WorkInterstitialPage.url()); + await expect(await $(PaidWorkPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; }); }); describe("Given the respondent has completed the required section and is on the hub", () => { - before("Open survey and complete first section", () => { - browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json"); - $(WorkInterstitialPage.submit()).click(); - $(PaidWorkPage.yes()).click(); - $(PaidWorkPage.submit()).click(); - $(UnPaidWorkPage.yes()).click(); - $(UnPaidWorkPage.submit()).click(); + before("Open survey and complete first section", async () => { + await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json"); + await $(WorkInterstitialPage.submit()).click(); + await $(PaidWorkPage.yes()).click(); + await $(PaidWorkPage.submit()).click(); + await $(UnPaidWorkPage.yes()).click(); + await $(UnPaidWorkPage.submit()).click(); }); - it("When the respondent selects a section which is not started, then last question guidance is not shown", () => { - $(HubPage.summaryRowLink("education-section")).click(); - expect(browser.getUrl()).to.contain(GcsesPage.url()); - expect($(GcsesPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent selects a section which is not started, then last question guidance is not shown", async () => { + await $(HubPage.summaryRowLink("education-section")).click(); + await expect(await browser.getUrl()).to.contain(GcsesPage.url()); + await expect(await $(GcsesPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the respondent selects a section which is in progress, then last question guidance is shown", () => { - $(HubPage.submit()).click(); - $(GcsesPage.yes()).click(); - $(GcsesPage.submit()).click(); - browser.url(HubPage.url()); - $(HubPage.summaryRowLink("education-section")).click(); - expect(browser.getUrl()).to.contain(ALevelsPage.url()); - expect($(ALevelsPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(GcsesPage.url()); - expect($(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + it("When the respondent selects a section which is in progress, then last question guidance is shown", async () => { + await $(HubPage.submit()).click(); + await $(GcsesPage.yes()).click(); + await $(GcsesPage.submit()).click(); + await browser.url(HubPage.url()); + await $(HubPage.summaryRowLink("education-section")).click(); + await expect(await browser.getUrl()).to.contain(ALevelsPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(GcsesPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; }); - it("When the respondent selects a section which is complete , then last question guidance is not shown on the summary or any link clicked from the summary", () => { - $(ALevelsPage.yes()).click(); - $(ALevelsPage.submit()).click(); - expect(browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); - expect($(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; - $(EducationSectionSummaryPage.submit()).click(); - $(HubPage.summaryRowLink("education-section")).click(); - expect(browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); - $(EducationSectionSummaryPage.alevelsAnswerEdit()).click(); - expect($(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the respondent selects a section which is complete , then last question guidance is not shown on the summary or any link clicked from the summary", async () => { + await $(ALevelsPage.yes()).click(); + await $(ALevelsPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await $(EducationSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("education-section")).click(); + await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); + await $(EducationSectionSummaryPage.alevelsAnswerEdit()).click(); + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the user clicks continue on the hub and it takes you to a section which is not started, then last question guidance is not shown", () => { - browser.url(HubPage.url()); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(SportsPage.url()); - expect($(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the user clicks continue on the hub and it takes you to a section which is not started, then last question guidance is not shown", async () => { + await browser.url(HubPage.url()); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SportsPage.url()); + await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); - it("When the user clicks continue on the hub and it takes you to a section which is in progress, then last question guidance is shown", () => { - $(HubPage.submit()).click(); - $(SportsPage.yes()).click(); - $(SportsPage.submit()).click(); - browser.url(HubPage.url()); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(HobbiesPage.url()); - expect($(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(SportsPage.url()); - expect($(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + it("When the user clicks continue on the hub and it takes you to a section which is in progress, then last question guidance is shown", async () => { + await $(HubPage.submit()).click(); + await $(SportsPage.yes()).click(); + await $(SportsPage.submit()).click(); + await browser.url(HubPage.url()); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HobbiesPage.url()); + await expect(await $(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(SportsPage.url()); + await expect(await $(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; }); - it("When the user clicks continue on the hub and it takes you to a section which is complete but doesnt have a summary, then last question guidance is not shown", () => { - $(HobbiesPage.yes()).click(); - $(HobbiesPage.submit()).click(); - $(HubPage.summaryRowLink("interests-section")).click(); - expect(browser.getUrl()).to.contain(SportsPage.url()); - expect($(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + it("When the user clicks continue on the hub and it takes you to a section which is complete but doesnt have a summary, then last question guidance is not shown", async () => { + await $(HobbiesPage.yes()).click(); + await $(HobbiesPage.submit()).click(); + await $(HubPage.summaryRowLink("interests-section")).click(); + await expect(await browser.getUrl()).to.contain(SportsPage.url()); + await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); }); }); diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js index 0fa7976b43..d2e9e6d006 100644 --- a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js @@ -26,201 +26,201 @@ import ListCollectorAddPage from "../../generated_pages/new_calculated_summary_r describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { - before("Get to Calculated Summary", () => { - browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); - $(HubPage.submit()).click(); - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.submit()).click(); - - getToFirstCalculatedSummary(); - - const browserUrl = browser.getUrl(); - - expect(browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + before("Get to Calculated Summary", async () => { + await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); + await $(HubPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); + + await getToFirstCalculatedSummary(); + + const browserUrl = await browser.getUrl(); + + await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); }); - it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", () => { - expect(browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", async () => { + await expect(await browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); }); - it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £20.71. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); // Answers included in calculation should be shown - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Second answer label also in currency total (optional)" ); - expect($(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - expect($(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Fourth answer label also in total (optional)" ); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); // Answers not included in calculation should not be shown - expect($$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; - expect($$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; - expect($$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; - expect($$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; + await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; + await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; }); - it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", () => { - expect($(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { + await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" ); }); - it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.previous()).click(); - expect(browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); }); - it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); }); - it("Given I change an answer, When I get to the currency summary, Then I should see the new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); - $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); - $(FourthNumberBlockPage.submit()).click(); + it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); + await $(FourthNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £30.71. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); - it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", () => { - $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); - $(FourthAndAHalfNumberBlockPage.submit()).click(); + it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); + await $(FourthAndAHalfNumberBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £28.37. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - expect($(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); }); - it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", () => { - $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); - $(SixthNumberBlockPage.previous()).click(); - $(FifthNumberBlockPage.previous()).click(); - $(FourthAndAHalfNumberBlockPage.previous()).click(); - $(FourthNumberBlockPage.previous()).click(); + it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { + await $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + await $(SixthNumberBlockPage.previous()).click(); + await $(FifthNumberBlockPage.previous()).click(); + await $(FourthAndAHalfNumberBlockPage.previous()).click(); + await $(FourthNumberBlockPage.previous()).click(); - $(SkipFourthBlockPage.yes()).click(); - $(SkipFourthBlockPage.submit()).click(); + await $(SkipFourthBlockPage.yes()).click(); + await $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; - expect($$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; + await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.36. Is this correct?" ); - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); - it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); // Answers included in calculation should be shown - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); - expect($(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); - expect($(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); }); - it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", () => { - expect(browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { + await expect(await browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); }); - it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(UnitTotalPlaybackPage.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of percentage values entered to be 79%. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); // Answers included in calculation should be shown - expect($(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); - expect($(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); }); - it("Given the calculated summary has a custom title with the list item position, When I am on the percentage calculated summary, Then the page title should use the custom title with the list item position", () => { - expect(browser.getTitle()).to.equal("Percentage Calculated Summary: Person 1 - A test schema to demo Calculated Summary"); + it("Given the calculated summary has a custom title with the list item position, When I am on the percentage calculated summary, Then the page title should use the custom title with the list item position", async () => { + await expect(await browser.getTitle()).to.equal("Percentage Calculated Summary: Person 1 - A test schema to demo Calculated Summary"); }); - it("Given I complete every question, When I get to the number summary, Then I should see the correct total", () => { + it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown - $(UnitTotalPlaybackPage.submit()).click(); - expect($(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await $(UnitTotalPlaybackPage.submit()).click(); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be 124.58. Is this correct?" ); - expect($(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - expect($(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); // Answers included in calculation should be shown - expect($(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); - expect($(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); - expect($(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); }); - it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", () => { - $(NumberTotalPlaybackPage.submit()).click(); - expect(browser.getUrl()).to.contain(BreakdownPage.pageName); - $(BreakdownPage.answer1()).setValue(100.0); - $(BreakdownPage.answer2()).setValue(24.58); - $(BreakdownPage.submit()).click(); - expect(browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); - expect($(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", async () => { + await $(NumberTotalPlaybackPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(BreakdownPage.pageName); + await $(BreakdownPage.answer1()).setValue(100.0); + await $(BreakdownPage.answer2()).setValue(24.58); + await $(BreakdownPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be £124.58. Is this correct?" ); - expect($("body").getText()).to.have.string("Enter two values that add up to the previous calculated summary total of £124.58"); - expect($(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $("body").getText()).to.have.string("Enter two values that add up to the previous calculated summary total of £124.58"); + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); }); - it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", () => { - $(SecondCurrencyTotalPlaybackPage.submit()).click(); + it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); const content = $("h1 + ul").getText(); const textsToAssert = [ @@ -231,230 +231,230 @@ describe("Feature: Calculated Summary Repeating Section", () => { "Total number values: 124.58", ]; - textsToAssert.forEach((text) => expect(content).to.contain(text)); + textsToAssert.forEach(async (text) => await expect(content).to.contain(text)); }); - it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); + it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + await $(SetMinMaxBlockPage.submit()).click(); }); - it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); - $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); - $(SetMinMaxBlockPage.submit()).click(); + it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); + await $(SetMinMaxBlockPage.submit()).click(); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(BreakdownPage.submit()).click(); - $(SecondCurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(BreakdownPage.submit()).click(); + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(BreakdownPage.submit()).click(); - $(SecondCurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); - $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(BreakdownPage.submit()).click(); + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", () => { - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.thirdNumberAnswerEdit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.submit()).click(); - $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.submit()).click(); - - expect($(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.thirdNumberAnswerEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(FifthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.submit()).click(); + + await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?" ); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(BreakdownPage.submit()).click(); - $(SecondCurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - expect(browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - $(SetMinMaxBlockPage.submit()).click(); - expect($(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); - $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - $(SetMinMaxBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(BreakdownPage.submit()).click(); + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); describe("Given I have a Calculated Summary in a Repeating Section", () => { - before("Get to Final Summary", () => { - browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); - $(HubPage.submit()).click(); - $(PrimaryPersonListCollectorPage.no()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Jean"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Jane"); - $(ListCollectorAddPage.lastName()).setValue("Doe"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.submit()).click(); - getToFirstCalculatedSummary(); - getToSubmitPage(); - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - getToFirstCalculatedSummary(); - getToSubmitPage(); - $(SubmitPage.submit()).click(); + before("Get to Final Summary", async () => { + await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); + await $(HubPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.no()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Jean"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Jane"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); + await getToFirstCalculatedSummary(); + await getToSubmitPage(); + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await getToFirstCalculatedSummary(); + await getToSubmitPage(); + await $(SubmitPage.submit()).click(); }); - it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", () => { - expect(browser.getUrl()).to.contain(HubPage.pageName); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", async () => { + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); }); - it("Given I change an answer with a dependent calculated summary question, When I return to the hub, Then only the section status for the repeating section I updated should be incomplete", () => { - expect(browser.getUrl()).to.contain(HubPage.pageName); - $(HubPage.summaryRowLink("personal-details-section-1")).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.skipFourthBlockAnswerEdit()).click(); - $(SkipFourthBlockPage.yes()).click(); - $(SkipFourthBlockPage.submit()).click(); - browser.url(HubPage.url()); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); - expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + it("Given I change an answer with a dependent calculated summary question, When I return to the hub, Then only the section status for the repeating section I updated should be incomplete", async () => { + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); + await $(SkipFourthBlockPage.yes()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); }); - it("Given I return to a partially completed section with a calculated summary, When I answer the dependent questions and return to the hub, Then the section status for the repeating section I updated should be complete", () => { - expect(browser.getUrl()).to.contain(HubPage.pageName); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); - $(HubPage.summaryRowLink("personal-details-section-1")).click(); - expect(browser.getUrl()).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(BreakdownPage.submit()).click(); - $(SecondCurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); - $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - $(SetMinMaxBlockPage.submit()).click(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(HubPage.pageName); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + it("Given I return to a partially completed section with a calculated summary, When I answer the dependent questions and return to the hub, Then the section status for the repeating section I updated should be complete", async () => { + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(BreakdownPage.submit()).click(); + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); + await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + await $(SetMinMaxBlockPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); }); }); }); -const getToFirstCalculatedSummary = () => { - $(FirstNumberBlockPage.firstNumber()).setValue(1.23); - $(FirstNumberBlockPage.submit()).click(); +const getToFirstCalculatedSummary = async () => { + await $(FirstNumberBlockPage.firstNumber()).setValue(1.23); + await $(FirstNumberBlockPage.submit()).click(); - $(SecondNumberBlockPage.secondNumber()).setValue(4.56); - $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); - $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); - $(SecondNumberBlockPage.submit()).click(); + await $(SecondNumberBlockPage.secondNumber()).setValue(4.56); + await $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); + await $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); + await $(SecondNumberBlockPage.submit()).click(); - $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); - $(ThirdNumberBlockPage.submit()).click(); - $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); - $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); + await $(ThirdNumberBlockPage.submit()).click(); + await $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); + await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - $(SkipFourthBlockPage.no()).click(); - $(SkipFourthBlockPage.submit()).click(); + await $(SkipFourthBlockPage.no()).click(); + await $(SkipFourthBlockPage.submit()).click(); - $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); - $(FourthNumberBlockPage.submit()).click(); - $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); - $(FourthAndAHalfNumberBlockPage.submit()).click(); + await $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); + await $(FourthNumberBlockPage.submit()).click(); + await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); + await $(FourthAndAHalfNumberBlockPage.submit()).click(); - $(FifthNumberBlockPage.fifthPercent()).setValue(56); - $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); - $(FifthNumberBlockPage.submit()).click(); + await $(FifthNumberBlockPage.fifthPercent()).setValue(56); + await $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); + await $(FifthNumberBlockPage.submit()).click(); - $(SixthNumberBlockPage.sixthPercent()).setValue(23); - $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); - $(SixthNumberBlockPage.submit()).click(); + await $(SixthNumberBlockPage.sixthPercent()).setValue(23); + await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); + await $(SixthNumberBlockPage.submit()).click(); }; -const getToSubmitPage = () => { - $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - $(UnitTotalPlaybackPage.submit()).click(); - $(PercentageTotalPlaybackPage.submit()).click(); - $(NumberTotalPlaybackPage.submit()).click(); - $(BreakdownPage.answer1()).setValue(100.0); - $(BreakdownPage.answer2()).setValue(24.58); - $(BreakdownPage.submit()).click(); - $(SecondCurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummaryTotalConfirmation.submit()).click(); +const getToSubmitPage = async () => { + await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(UnitTotalPlaybackPage.submit()).click(); + await $(PercentageTotalPlaybackPage.submit()).click(); + await $(NumberTotalPlaybackPage.submit()).click(); + await $(BreakdownPage.answer1()).setValue(100.0); + await $(BreakdownPage.answer2()).setValue(24.58); + await $(BreakdownPage.submit()).click(); + await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummaryTotalConfirmation.submit()).click(); }; diff --git a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js index e82ea04b30..b04d872665 100644 --- a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js +++ b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js @@ -8,62 +8,62 @@ import HubPage from "../../../base_pages/hub.page.js"; describe("Component: Definition", () => { describe("Load the Survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_placeholder_based_on_first_item_in_list.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_placeholder_based_on_first_item_in_list.json"); }); - it("Given I am the first person in the list, When I get to the question page, Then I should see the default answer option", () => { + it("Given I am the first person in the list, When I get to the question page, Then I should see the default answer option", async () => { // Given - $(HubPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); // When - $(ListStatusInterstitial.submit()).click(); - $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); - $(FavouriteDrinkQuestion.submit()).click(); + await $(ListStatusInterstitial.submit()).click(); + await $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); + await $(FavouriteDrinkQuestion.submit()).click(); // Then - expect($(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Tea"); + await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Tea"); }); - it("Given I am not the first person in the list, When I get to the question page, Then I should see the correct answer option", () => { + it("Given I am not the first person in the list, When I get to the question page, Then I should see the correct answer option", async () => { // Given - $(HubPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("John"); - $(ListCollectorAddPage.lastName()).setValue("Doe"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); // When - $(ListStatusInterstitial.submit()).click(); - $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); - $(FavouriteDrinkQuestion.submit()).click(); - $(ListStatusQuestion.listStatus2Tea()).click(); - $(ListStatusQuestion.submit()).click(); - $(SummaryPage.submit()).click(); - $(HubPage.submit()).click(); - $(ListStatusInterstitial.submit()).click(); - $(FavouriteDrinkQuestion.answer()).setValue("Lemonade"); - $(FavouriteDrinkQuestion.submit()).click(); + await $(ListStatusInterstitial.submit()).click(); + await $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); + await $(FavouriteDrinkQuestion.submit()).click(); + await $(ListStatusQuestion.listStatus2Tea()).click(); + await $(ListStatusQuestion.submit()).click(); + await $(SummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(ListStatusInterstitial.submit()).click(); + await $(FavouriteDrinkQuestion.answer()).setValue("Lemonade"); + await $(FavouriteDrinkQuestion.submit()).click(); // Then - expect($(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Orange Juice"); + await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Orange Juice"); }); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js index bfefb4225f..81f5706091 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js @@ -8,70 +8,70 @@ import AgeBlockMonthYearRangePage from "../../../generated_pages/placeholder_dif import AgeTestMonthYearRangePage from "../../../generated_pages/placeholder_difference_in_years_month_year_range/age-test.page"; describe("Difference check (years)", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_difference_in_years.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_difference_in_years.json"); }); - it("Given a day, month and year answer is provided for a date question then the age in years should be calculated and displayed on the page ", () => { - $(AgeBlockYearPage.day()).setValue(1); - $(AgeBlockYearPage.month()).setValue(1); - $(AgeBlockYearPage.year()).setValue(1990); - $(AgeBlockYearPage.submit()).click(); - expect($(AgeTestYearPage.heading()).getText()).to.equal(`You are ${getYears("1990/01/01")} years old. Is this correct?`); + it("Given a day, month and year answer is provided for a date question then the age in years should be calculated and displayed on the page ", async () => { + await $(AgeBlockYearPage.day()).setValue(1); + await $(AgeBlockYearPage.month()).setValue(1); + await $(AgeBlockYearPage.year()).setValue(1990); + await $(AgeBlockYearPage.submit()).click(); + await expect(await $(AgeTestYearPage.heading()).getText()).to.equal(`You are ${getYears("1990/01/01")} years old. Is this correct?`); }); }); describe("Difference check (months and years)", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_difference_in_years_month_year.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_difference_in_years_month_year.json"); }); - it("Given a month and year answer is provided for a date question then the difference in years should be calculated and displayed on the page ", () => { - $(AgeBlockMonthYearPage.Month()).setValue(1); - $(AgeBlockMonthYearPage.Year()).setValue(1990); + it("Given a month and year answer is provided for a date question then the difference in years should be calculated and displayed on the page ", async () => { + await $(AgeBlockMonthYearPage.Month()).setValue(1); + await $(AgeBlockMonthYearPage.Year()).setValue(1990); - $(AgeBlockMonthYearPage.submit()).click(); + await $(AgeBlockMonthYearPage.submit()).click(); - expect($(AgeTestMonthYearPage.heading()).getText()).to.equal( + await expect(await $(AgeTestMonthYearPage.heading()).getText()).to.equal( `It has been ${getYears("1990/01/01")} years since you last went on holiday. Is this correct?` ); }); }); describe("Difference check (months and years range)", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_difference_in_years_month_year_range.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_difference_in_years_month_year_range.json"); }); - it("Given a month and year answers 'from' and 'to' are provided for a date question then the difference in years should be calculated and displayed on the page ", () => { - $(AgeBlockMonthYearRangePage.periodFromMonth()).setValue(1); - $(AgeBlockMonthYearRangePage.periodFromYear()).setValue(1990); - $(AgeBlockMonthYearRangePage.periodToMonth()).setValue(1); - $(AgeBlockMonthYearRangePage.periodToYear()).setValue(1991); + it("Given a month and year answers 'from' and 'to' are provided for a date question then the difference in years should be calculated and displayed on the page ", async () => { + await $(AgeBlockMonthYearRangePage.periodFromMonth()).setValue(1); + await $(AgeBlockMonthYearRangePage.periodFromYear()).setValue(1990); + await $(AgeBlockMonthYearRangePage.periodToMonth()).setValue(1); + await $(AgeBlockMonthYearRangePage.periodToYear()).setValue(1991); - $(AgeBlockMonthYearRangePage.submit()).click(); + await $(AgeBlockMonthYearRangePage.submit()).click(); - expect($(AgeTestMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); + await expect(await $(AgeTestMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); }); }); describe("Difference check (years range)", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_difference_in_years_range.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_difference_in_years_range.json"); }); - it("Given a day, month and year answers 'from' and 'to' are provided for a date question then the difference in years should be calculated and displayed on the page ", () => { - $(AgeBlockDayMonthYearRangePage.periodFromday()).setValue(1); - $(AgeBlockDayMonthYearRangePage.periodFrommonth()).setValue(1); - $(AgeBlockDayMonthYearRangePage.periodFromyear()).setValue(1990); + it("Given a day, month and year answers 'from' and 'to' are provided for a date question then the difference in years should be calculated and displayed on the page ", async () => { + await $(AgeBlockDayMonthYearRangePage.periodFromday()).setValue(1); + await $(AgeBlockDayMonthYearRangePage.periodFrommonth()).setValue(1); + await $(AgeBlockDayMonthYearRangePage.periodFromyear()).setValue(1990); - $(AgeBlockDayMonthYearRangePage.periodToday()).setValue(1); - $(AgeBlockDayMonthYearRangePage.periodTomonth()).setValue(1); - $(AgeBlockDayMonthYearRangePage.periodToyear()).setValue(1991); + await $(AgeBlockDayMonthYearRangePage.periodToday()).setValue(1); + await $(AgeBlockDayMonthYearRangePage.periodTomonth()).setValue(1); + await $(AgeBlockDayMonthYearRangePage.periodToyear()).setValue(1991); - $(AgeBlockDayMonthYearRangePage.submit()).click(); + await $(AgeBlockDayMonthYearRangePage.submit()).click(); - expect($(AgeTestDayMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); + await expect(await $(AgeTestDayMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js index 5b4615ed44..159471b013 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js @@ -4,39 +4,39 @@ import Block0Page from "../../../generated_pages/placeholder_transform_date_rang import RangeQuestionBlockPage from "../../../generated_pages/placeholder_transform_date_range_bounds/range-question-block.page"; describe("Date checks", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_transform_date_range_bounds.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_transform_date_range_bounds.json"); }); - it("Given a reference date is provided, when I get to the next page, then the placeholder contains a formatted date range based on the reference date", () => { - $(DateQuestionPage.day()).setValue(8); - $(DateQuestionPage.month()).setValue(9); - $(DateQuestionPage.year()).setValue(2021); + it("Given a reference date is provided, when I get to the next page, then the placeholder contains a formatted date range based on the reference date", async () => { + await $(DateQuestionPage.day()).setValue(8); + await $(DateQuestionPage.month()).setValue(9); + await $(DateQuestionPage.year()).setValue(2021); - $(DateQuestionPage.submit()).click(); + await $(DateQuestionPage.submit()).click(); - expect($(DaysQuestionBlockPage.questionText()).getText()).to.contain("Monday 30 August to Monday 13 September 2021"); - $(DaysQuestionBlockPage.submit()).click(); + await expect(await $(DaysQuestionBlockPage.questionText()).getText()).to.contain("Monday 30 August to Monday 13 September 2021"); + await $(DaysQuestionBlockPage.submit()).click(); }); - it("Given a reference date is provided, when I get to the next page, then the placeholder contains a formatted date range", () => { - $(DateQuestionPage.day()).setValue(15); - $(DateQuestionPage.month()).setValue(9); - $(DateQuestionPage.year()).setValue(2021); + it("Given a reference date is provided, when I get to the next page, then the placeholder contains a formatted date range", async () => { + await $(DateQuestionPage.day()).setValue(15); + await $(DateQuestionPage.month()).setValue(9); + await $(DateQuestionPage.year()).setValue(2021); - $(DateQuestionPage.submit()).click(); - $(DaysQuestionBlockPage.submit()).click(); + await $(DateQuestionPage.submit()).click(); + await $(DaysQuestionBlockPage.submit()).click(); - $(Block0Page.ref0day()).setValue(1); - $(Block0Page.ref0month()).setValue(5); - $(Block0Page.ref0year()).setValue(2019); + await $(Block0Page.ref0day()).setValue(1); + await $(Block0Page.ref0month()).setValue(5); + await $(Block0Page.ref0year()).setValue(2019); - $(Block0Page.ref1day()).setValue(19); - $(Block0Page.ref1month()).setValue(5); - $(Block0Page.ref1year()).setValue(2019); + await $(Block0Page.ref1day()).setValue(19); + await $(Block0Page.ref1month()).setValue(5); + await $(Block0Page.ref1year()).setValue(2019); - $(Block0Page.submit()).click(); + await $(Block0Page.submit()).click(); - expect($(RangeQuestionBlockPage.questionText()).getText()).to.contain("Wednesday 1 to Sunday 19 May 2019"); + await expect(await $(RangeQuestionBlockPage.questionText()).getText()).to.contain("Wednesday 1 to Sunday 19 May 2019"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js index 218a0f2b33..0cbadf3e55 100644 --- a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js @@ -3,31 +3,31 @@ import EmployeesTrainingBlockPage from "../../../generated_pages/placeholder_def import EmployeesNumberInterstitialPage from "../../../generated_pages/placeholder_default_value/employees-number-interstitial.page"; describe("Placeholder default value check", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_default_value.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_default_value.json"); }); - it("Given a question with default answer, When I do not enter any number and click submit, Then the interstitial page shows default employees number as 0", () => { - $(EmployeesNumberBlockPage.submit()).click(); - expect($("#main-content > p").getText()).to.contain("The total number of employees confirmed are 0"); + it("Given a question with default answer, When I do not enter any number and click submit, Then the interstitial page shows default employees number as 0", async () => { + await $(EmployeesNumberBlockPage.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 0"); }); - it("Given a question with default answer, When I enter a number of employee and click submit, Then the interstitial page shows me the employees number entered", () => { - $(EmployeesNumberBlockPage.employeesNo()).setValue("54"); - $(EmployeesNumberBlockPage.submit()).click(); - expect($("#main-content > p").getText()).to.contain("The total number of employees confirmed are 54"); + it("Given a question with default answer, When I enter a number of employee and click submit, Then the interstitial page shows me the employees number entered", async () => { + await $(EmployeesNumberBlockPage.employeesNo()).setValue("54"); + await $(EmployeesNumberBlockPage.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 54"); }); - it("Given a training budget question with default answer, When I do not enter any amount and click submit, Then the interstitial page shows default amount as 250.00", () => { - $(EmployeesNumberBlockPage.submit()).click(); - $(EmployeesNumberInterstitialPage.submit()).click(); - $(EmployeesTrainingBlockPage.submit()).click(); - expect($("#main-content > p").getText()).to.contain("The average training budget per employee is £250.00"); + it("Given a training budget question with default answer, When I do not enter any amount and click submit, Then the interstitial page shows default amount as 250.00", async () => { + await $(EmployeesNumberBlockPage.submit()).click(); + await $(EmployeesNumberInterstitialPage.submit()).click(); + await $(EmployeesTrainingBlockPage.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £250.00"); }); - it("Given a training budget question with default answer, When I enter an amount and click submit, Then the interstitial page shows amount entered", () => { - $(EmployeesNumberBlockPage.submit()).click(); - $(EmployeesNumberInterstitialPage.submit()).click(); - $(EmployeesTrainingBlockPage.employeesAvgTraining()).setValue("100"); - $(EmployeesTrainingBlockPage.submit()).click(); - expect($("#main-content > p").getText()).to.contain("The average training budget per employee is £100.00"); + it("Given a training budget question with default answer, When I enter an amount and click submit, Then the interstitial page shows amount entered", async () => { + await $(EmployeesNumberBlockPage.submit()).click(); + await $(EmployeesNumberInterstitialPage.submit()).click(); + await $(EmployeesTrainingBlockPage.employeesAvgTraining()).setValue("100"); + await $(EmployeesTrainingBlockPage.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £100.00"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js index 7492bd1e77..e6756457fe 100644 --- a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js @@ -3,18 +3,18 @@ import SubmitPage from "../../../generated_pages/placeholder_metadata/submit.pag describe("Placeholder metadata check", () => { describe("Given I launch placeholder metadata question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_metadata.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_metadata.json"); }); - it("When I see responding unit question, Then I see radio options with first option as metadata placeholder (ru_name)", () => { - expect($(MandatoryRadioPage.answerRuNameLabel()).getText()).to.equal("Apple"); + it("When I see responding unit question, Then I see radio options with first option as metadata placeholder (ru_name)", async () => { + await expect(await $(MandatoryRadioPage.answerRuNameLabel()).getText()).to.equal("Apple"); }); - it("When I answer responding unit question, Then I see confirmation page with my selected placeholder metadata option (ru_name)", () => { - $(MandatoryRadioPage.answerRuName()).click(); - $(MandatoryRadioPage.submit()).click(); + it("When I answer responding unit question, Then I see confirmation page with my selected placeholder metadata option (ru_name)", async () => { + await $(MandatoryRadioPage.answerRuName()).click(); + await $(MandatoryRadioPage.submit()).click(); - expect($(SubmitPage.mandatoryRadioAnswer()).getText()).to.equal("Apple"); - expect($(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + await expect(await $(SubmitPage.mandatoryRadioAnswer()).getText()).to.equal("Apple"); + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); }); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js index 28444cce99..c1d2c2c740 100644 --- a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js @@ -2,20 +2,22 @@ import MandatoryRadioPage from "../../../generated_pages/placeholder_option_labe import ConfirmationQuestionRadioBlockPage from "../../../generated_pages/placeholder_option_label_from_value/confirmation-question-radio-block.page"; describe("Option label value check", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_placeholder_option_label_from_value.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_placeholder_option_label_from_value.json"); }); - it("Given radio options are provided, when I select first answer (piped from metadata) and go to the next page, then the question title contains the label text of the answer I selected", () => { - expect($(MandatoryRadioPage.answerBusinessNamePipedLabel()).getText()).to.contain("Apple (piped)"); - $(MandatoryRadioPage.answerBusinessNamePiped()).click(); - $(MandatoryRadioPage.submit()).click(); - expect($(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Apple (piped)"); + it("Given radio options are provided, when I select first answer (piped from metadata) and go to the next page, then the question title contains the label text of the answer I selected", async () => { + await expect(await $(MandatoryRadioPage.answerBusinessNamePipedLabel()).getText()).to.contain("Apple (piped)"); + await $(MandatoryRadioPage.answerBusinessNamePiped()).click(); + await $(MandatoryRadioPage.submit()).scrollIntoView(); + await $(MandatoryRadioPage.submit()).click(); + await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Apple (piped)"); }); - it("Given radio options are provided, when I select an answer (static) and go to the next page, then the question title contains the label text of the answer I selected", () => { - $(MandatoryRadioPage.googleLtd()).click(); - $(MandatoryRadioPage.submit()).click(); - expect($(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Google LTD"); + it("Given radio options are provided, when I select an answer (static) and go to the next page, then the question title contains the label text of the answer I selected", async () => { + await $(MandatoryRadioPage.googleLtd()).click(); + await $(MandatoryRadioPage.submit()).scrollIntoView(); + await $(MandatoryRadioPage.submit()).click(); + await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Google LTD"); }); }); diff --git a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js index 881b57289f..cd403ef426 100644 --- a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js +++ b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js @@ -1,15 +1,17 @@ import MandatoryCheckboxPage from "../../../generated_pages/placeholder_playback_list/mandatory-checkbox.page"; describe("Feature: Playback Confirmation", () => { - beforeEach("Open the schema", () => { - browser.openQuestionnaire("test_placeholder_playback_list.json"); + beforeEach("Open the schema", async () => { + await browser.openQuestionnaire("test_placeholder_playback_list.json"); }); - it("When the user submits an answer, their answers should be shown on the confirmation screen", () => { - $(MandatoryCheckboxPage.cheese()).click(); - $(MandatoryCheckboxPage.ham()).click(); - $(MandatoryCheckboxPage.submit()).click(); + it("When the user submits an answer, their answers should be shown on the confirmation screen", async () => { + await $(MandatoryCheckboxPage.cheese()).click(); + await $(MandatoryCheckboxPage.ham()).click(); + await $(MandatoryCheckboxPage.submit()).click(); - expect($("#confirm-answers-question ul").getHTML()).to.contain("Cheese").to.contain("Ham"); + await expect(await $("#confirm-answers-question ul").getHTML()) + .to.contain("Cheese") + .to.contain("Ham"); }); }); diff --git a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js index e47b523f94..0d5318ba7d 100644 --- a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js +++ b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js @@ -4,35 +4,35 @@ import NameBlockPage from "../../../generated_pages/custom_question_summary/name import SubmitPage from "../../../generated_pages/custom_question_summary/submit.page.js"; describe("Summary Screen", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_custom_question_summary.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_custom_question_summary.json"); }); - it("Given a survey has question summary concatenations and has been completed when on the summary page then the correct response should be displayed formatted correctly", () => { + it("Given a survey has question summary concatenations and has been completed when on the summary page then the correct response should be displayed formatted correctly", async () => { completeAllQuestions(); - expect($(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("John Smith"); - expect($(SubmitPage.summaryRowState("address-question-concatenated-answer")).getText()).to.contain("Cardiff Road\nNewport\nNP10 8XG"); - expect($(SubmitPage.summaryRowState("age-question-concatenated-answer")).getText()).to.contain("7\nThis age is an estimate"); + await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("John Smith"); + await expect(await $(SubmitPage.summaryRowState("address-question-concatenated-answer")).getText()).to.contain("Cardiff Road\nNewport\nNP10 8XG"); + await expect(await $(SubmitPage.summaryRowState("age-question-concatenated-answer")).getText()).to.contain("7\nThis age is an estimate"); }); - it("Given no values are entered in a question with multiple answers and concatenation set, when on the summary screen then the correct response should be displayed", () => { - $(NameBlockPage.submit()).click(); - $(AddressBlockPage.submit()).click(); - $(AgeBlock.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("No answer provided"); + it("Given no values are entered in a question with multiple answers and concatenation set, when on the summary screen then the correct response should be displayed", async () => { + await $(NameBlockPage.submit()).click(); + await $(AddressBlockPage.submit()).click(); + await $(AgeBlock.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("No answer provided"); }); - function completeAllQuestions() { - $(NameBlockPage.first()).setValue("John"); - $(NameBlockPage.last()).setValue("Smith"); - $(NameBlockPage.submit()).click(); - $(AddressBlockPage.line1()).setValue("Cardiff Road"); - $(AddressBlockPage.townCity()).setValue("Newport"); - $(AddressBlockPage.postcode()).setValue("NP10 8XG"); - $(AddressBlockPage.submit()).click(); - $(AgeBlock.number()).setValue(7); - $(AgeBlock.singleCheckboxThisAgeIsAnEstimate()).click(); - $(AgeBlock.submit()).click(); + async function completeAllQuestions() { + await $(NameBlockPage.first()).setValue("John"); + await $(NameBlockPage.last()).setValue("Smith"); + await $(NameBlockPage.submit()).click(); + await $(AddressBlockPage.line1()).setValue("Cardiff Road"); + await $(AddressBlockPage.townCity()).setValue("Newport"); + await $(AddressBlockPage.postcode()).setValue("NP10 8XG"); + await $(AddressBlockPage.submit()).click(); + await $(AgeBlock.number()).setValue(7); + await $(AgeBlock.singleCheckboxThisAgeIsAnEstimate()).click(); + await $(AgeBlock.submit()).click(); } }); diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index e4b21120e5..8467e71d59 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -19,288 +19,288 @@ import VisitorsListCollectorRemovePage from "../../../generated_pages/repeating_ describe("Feature: Repeating Sections with Hub and Spoke", () => { describe("Given the user has added some members to the household and is on the Hub", () => { - before("Open survey and add household members", () => { - browser.openQuestionnaire("test_repeating_sections_with_hub_and_spoke.json"); + before("Open survey and add household members", async () => { + await browser.openQuestionnaire("test_repeating_sections_with_hub_and_spoke.json"); // Accept cookies, this is done due to headless window size where cookie banner // is pushing the submit button outside window - $(HubPage.acceptCookies()).click(); + await $(HubPage.acceptCookies()).click(); // Ensure we are on the Hub - expect(browser.getUrl()).to.contain(HubPage.url()); + await expect(await browser.getUrl()).to.contain(HubPage.url()); // Ensure the first section is not started - expect($(HubPage.summaryRowState("section")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section")).getText()).to.equal("Not started"); // Start first section to add household members - $(HubPage.summaryRowLink("section")).click(); + await $(HubPage.summaryRowLink("section")).click(); // Add a primary person - $(PrimaryPersonPage.yes()).click(); - $(PrimaryPersonPage.submit()).click(); - $(PrimaryPersonAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonPage.submit()).click(); + await $(PrimaryPersonPage.yes()).click(); + await $(PrimaryPersonPage.submit()).click(); + await $(PrimaryPersonAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonPage.submit()).click(); // Add other household members (First list collector) - $(FirstListCollectorPage.yes()).click(); - $(FirstListCollectorPage.submit()).click(); - $(FirstListCollectorAddPage.firstName()).setValue("Jean"); - $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - $(FirstListCollectorAddPage.submit()).click(); - - $(FirstListCollectorPage.yes()).click(); - $(FirstListCollectorPage.submit()).click(); - $(FirstListCollectorAddPage.firstName()).setValue("Samuel"); - $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - $(FirstListCollectorAddPage.submit()).click(); + await $(FirstListCollectorPage.yes()).click(); + await $(FirstListCollectorPage.submit()).click(); + await $(FirstListCollectorAddPage.firstName()).setValue("Jean"); + await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); + await $(FirstListCollectorAddPage.submit()).click(); + + await $(FirstListCollectorPage.yes()).click(); + await $(FirstListCollectorPage.submit()).click(); + await $(FirstListCollectorAddPage.firstName()).setValue("Samuel"); + await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); + await $(FirstListCollectorAddPage.submit()).click(); // Go to second list collector - $(FirstListCollectorPage.no()).click(); - $(FirstListCollectorPage.submit()).click(); - $(SecondListCollectorInterstitialPage.submit()).click(); + await $(FirstListCollectorPage.no()).click(); + await $(FirstListCollectorPage.submit()).click(); + await $(SecondListCollectorInterstitialPage.submit()).click(); // Add other household members (Second list collector) - $(SecondListCollectorPage.yes()).click(); - $(SecondListCollectorPage.submit()).click(); - $(SecondListCollectorAddPage.firstName()).setValue("John"); - $(SecondListCollectorAddPage.lastName()).setValue("Doe"); - $(SecondListCollectorAddPage.submit()).click(); + await $(SecondListCollectorPage.yes()).click(); + await $(SecondListCollectorPage.submit()).click(); + await $(SecondListCollectorAddPage.firstName()).setValue("John"); + await $(SecondListCollectorAddPage.lastName()).setValue("Doe"); + await $(SecondListCollectorAddPage.submit()).click(); // Go back to the Hub - $(SecondListCollectorPage.no()).click(); - $(SecondListCollectorPage.submit()).click(); - $(VisitorsListCollectorPage.no()).click(); - $(VisitorsListCollectorPage.submit()).click(); + await $(SecondListCollectorPage.no()).click(); + await $(SecondListCollectorPage.submit()).click(); + await $(VisitorsListCollectorPage.no()).click(); + await $(VisitorsListCollectorPage.submit()).click(); }); - beforeEach("Navigate to the Hub", () => browser.url(HubPage.url())); + beforeEach("Navigate to the Hub", async () => await browser.url(HubPage.url())); - it("Then a section for each household member should be displayed", () => { - expect(browser.getUrl()).to.contain(HubPage.url()); + it("Then a section for each household member should be displayed", async () => { + await expect(await browser.getUrl()).to.contain(HubPage.url()); - expect($(HubPage.summaryRowState("section")).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowTitle("personal-details-section-1")).getText()).to.equal("Marcus Twin"); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowTitle("personal-details-section-2")).getText()).to.equal("Jean Clemens"); - expect($(HubPage.summaryRowState("personal-details-section-3")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowTitle("personal-details-section-3")).getText()).to.equal("Samuel Clemens"); - expect($(HubPage.summaryRowState("personal-details-section-4")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowTitle("personal-details-section-4")).getText()).to.equal("John Doe"); + await expect(await $(HubPage.summaryRowState("section")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-1")).getText()).to.equal("Marcus Twin"); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-2")).getText()).to.equal("Jean Clemens"); + await expect(await $(HubPage.summaryRowState("personal-details-section-3")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-3")).getText()).to.equal("Samuel Clemens"); + await expect(await $(HubPage.summaryRowState("personal-details-section-4")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-4")).getText()).to.equal("John Doe"); - expect($(HubPage.summaryRowState("section-5")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("section-5")).isExisting()).to.be.false; }); - it("When the user starts a repeating section and clicks the Previous link on the first question, Then they should be taken back to the Hub", () => { - $(HubPage.summaryRowLink("personal-details-section-2")).click(); - $(ProxyPage.previous()).click(); + it("When the user starts a repeating section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { + await $(HubPage.summaryRowLink("personal-details-section-2")).click(); + await $(ProxyPage.previous()).click(); - expect(browser.getUrl()).to.contain(HubPage.url()); + await expect(await browser.getUrl()).to.contain(HubPage.url()); }); - it("When the user partially completes a repeating section, Then that section should be marked as 'Partially completed' on the Hub", () => { - $(HubPage.summaryRowLink("personal-details-section-1")).click(); - $(ProxyPage.yes()).click(); - $(ProxyPage.submit()).click(); + it("When the user partially completes a repeating section, Then that section should be marked as 'Partially completed' on the Hub", async () => { + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); + await $(ProxyPage.yes()).click(); + await $(ProxyPage.submit()).click(); - $(DateOfBirthPage.day()).setValue("01"); - $(DateOfBirthPage.month()).setValue("03"); - $(DateOfBirthPage.year()).setValue("2000"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("01"); + await $(DateOfBirthPage.month()).setValue("03"); + await $(DateOfBirthPage.year()).setValue("2000"); + await $(DateOfBirthPage.submit()).click(); - $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); + await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); - browser.url(HubPage.url()); + await browser.url(HubPage.url()); - expect(browser.getUrl()).to.contain(HubPage.url()); - expect($(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); }); - it("When the user continues with a partially completed repeating section, Then they are taken to the first incomplete block", () => { - $(HubPage.summaryRowLink("personal-details-section-1")).click(); + it("When the user continues with a partially completed repeating section, Then they are taken to the first incomplete block", async () => { + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - expect($(SexPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); + await expect(await $(SexPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); }); - it("When the user completes a repeating section, Then that section should be marked as 'Completed' on the Hub", () => { - $(HubPage.summaryRowLink("personal-details-section-2")).click(); - $(ProxyPage.yes()).click(); - $(ProxyPage.submit()).click(); + it("When the user completes a repeating section, Then that section should be marked as 'Completed' on the Hub", async () => { + await $(HubPage.summaryRowLink("personal-details-section-2")).click(); + await $(ProxyPage.yes()).click(); + await $(ProxyPage.submit()).click(); - $(DateOfBirthPage.day()).setValue("09"); - $(DateOfBirthPage.month()).setValue("09"); - $(DateOfBirthPage.year()).setValue("1995"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("09"); + await $(DateOfBirthPage.month()).setValue("09"); + await $(DateOfBirthPage.year()).setValue("1995"); + await $(DateOfBirthPage.submit()).click(); - $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); + await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); - $(SexPage.female()).click(); - $(SexPage.submit()).click(); + await $(SexPage.female()).click(); + await $(SexPage.submit()).click(); - $(PersonalDetailsSummaryPage.submit()).click(); + await $(PersonalDetailsSummaryPage.submit()).click(); - expect(browser.getUrl()).to.contain(HubPage.url()); - expect($(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); }); - it("When the user clicks 'View answers' for a completed repeating section, Then they are taken to the summary", () => { - $(HubPage.summaryRowLink("personal-details-section-2")).click(); - expect(browser.getUrl()).to.contain("/sections/personal-details-section"); + it("When the user clicks 'View answers' for a completed repeating section, Then they are taken to the summary", async () => { + await $(HubPage.summaryRowLink("personal-details-section-2")).click(); + await expect(await browser.getUrl()).to.contain("/sections/personal-details-section"); }); - it("When the user views the summary for a repeating section, Then the page title is shown", () => { - $(HubPage.summaryRowLink("personal-details-section-2")).click(); - expect(browser.getTitle()).to.equal("… - Hub & Spoke"); + it("When the user views the summary for a repeating section, Then the page title is shown", async () => { + await $(HubPage.summaryRowLink("personal-details-section-2")).click(); + await expect(await browser.getTitle()).to.equal("… - Hub & Spoke"); }); - it("When the user adds 2 visitors to the household then a section for each visitor should be display on the hub", () => { + it("When the user adds 2 visitors to the household then a section for each visitor should be display on the hub", async () => { // Ensure no other sections exist - expect($(HubPage.summaryRowState("personal-details-section-5")).isExisting()).to.be.false; - expect($(HubPage.summaryRowState("visitors-section-1")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("personal-details-section-5")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("visitors-section-1")).isExisting()).to.be.false; // Start section for first visitor - $(HubPage.summaryRowLink("section")).click(); + await $(HubPage.summaryRowLink("section")).click(); // Add first visitor - $(SectionSummaryPage.visitorListAddLink()).click(); - $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); - $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); - $(VisitorsListCollectorAddPage.submit()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/visitors-block"); + await $(SectionSummaryPage.visitorListAddLink()).click(); + await $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); + await $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); + await $(VisitorsListCollectorAddPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/visitors-block"); // Add second visitor - $(VisitorsListCollectorPage.yes()).click(); - $(VisitorsListCollectorPage.submit()).click(); - $(VisitorsListCollectorAddPage.firstName()).setValue("Yvonne"); - $(VisitorsListCollectorAddPage.lastName()).setValue("Yoe"); - $(VisitorsListCollectorAddPage.submit()).click(); + await $(VisitorsListCollectorPage.yes()).click(); + await $(VisitorsListCollectorPage.submit()).click(); + await $(VisitorsListCollectorAddPage.firstName()).setValue("Yvonne"); + await $(VisitorsListCollectorAddPage.lastName()).setValue("Yoe"); + await $(VisitorsListCollectorAddPage.submit()).click(); // Exit the visitors list collector - $(VisitorsListCollectorPage.no()).click(); - $(VisitorsListCollectorPage.submit()).click(); + await $(VisitorsListCollectorPage.no()).click(); + await $(VisitorsListCollectorPage.submit()).click(); - $(SectionSummaryPage.submit()).click(); + await $(SectionSummaryPage.submit()).click(); - expect($(HubPage.summaryRowState("visitors-section-1")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowTitle("visitors-section-1")).getText()).to.equal("Joe Public"); - expect($(HubPage.summaryRowState("visitors-section-2")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowTitle("visitors-section-2")).getText()).to.equal("Yvonne Yoe"); + await expect(await $(HubPage.summaryRowState("visitors-section-1")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowTitle("visitors-section-1")).getText()).to.equal("Joe Public"); + await expect(await $(HubPage.summaryRowState("visitors-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowTitle("visitors-section-2")).getText()).to.equal("Yvonne Yoe"); - expect($(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; }); - it("When the user clicks 'Continue' from the Hub, Then they should progress to the first incomplete section", () => { - $(HubPage.submit()).click(); - expect($(ConfirmDateOfBirthPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); + it("When the user clicks 'Continue' from the Hub, Then they should progress to the first incomplete section", async () => { + await $(HubPage.submit()).click(); + await expect(await $(ConfirmDateOfBirthPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); }); - it("When the user answers on their behalf, Then they are shown the non proxy question variant", () => { - $(HubPage.summaryRowLink("personal-details-section-4")).click(); - $(ProxyPage.noIMAnsweringForMyself()).click(); - $(ProxyPage.submit()).click(); + it("When the user answers on their behalf, Then they are shown the non proxy question variant", async () => { + await $(HubPage.summaryRowLink("personal-details-section-4")).click(); + await $(ProxyPage.noIMAnsweringForMyself()).click(); + await $(ProxyPage.submit()).click(); - $(DateOfBirthPage.day()).setValue("07"); - $(DateOfBirthPage.month()).setValue("07"); - $(DateOfBirthPage.year()).setValue("1970"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("07"); + await $(DateOfBirthPage.month()).setValue("07"); + await $(DateOfBirthPage.year()).setValue("1970"); + await $(DateOfBirthPage.submit()).click(); - $(ConfirmDateOfBirthPage.confirmDateOfBirthYesIAmAgeOld()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); + await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesIAmAgeOld()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); - expect($(SexPage.questionText()).getText()).to.equal("What is your sex?"); + await expect(await $(SexPage.questionText()).getText()).to.equal("What is your sex?"); }); - it("When the user answers on on behalf of someone else, Then they are shown the proxy question variant for the relevant repeating section", () => { - $(HubPage.summaryRowLink("personal-details-section-3")).click(); - $(ProxyPage.yes()).click(); - $(ProxyPage.submit()).click(); + it("When the user answers on on behalf of someone else, Then they are shown the proxy question variant for the relevant repeating section", async () => { + await $(HubPage.summaryRowLink("personal-details-section-3")).click(); + await $(ProxyPage.yes()).click(); + await $(ProxyPage.submit()).click(); - $(DateOfBirthPage.day()).setValue("11"); - $(DateOfBirthPage.month()).setValue("11"); - $(DateOfBirthPage.year()).setValue("1990"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("11"); + await $(DateOfBirthPage.month()).setValue("11"); + await $(DateOfBirthPage.year()).setValue("1990"); + await $(DateOfBirthPage.submit()).click(); - $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - $(ConfirmDateOfBirthPage.submit()).click(); - expect($(SexPage.questionText()).getText()).to.equal("What is Samuel Clemens’ sex?"); + await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); + await $(ConfirmDateOfBirthPage.submit()).click(); + await expect(await $(SexPage.questionText()).getText()).to.equal("What is Samuel Clemens’ sex?"); }); - it("When the user completes all sections, Then the Hub should be in the completed state", () => { + it("When the user completes all sections, Then the Hub should be in the completed state", async () => { // Complete remaining sections - $(HubPage.submit()).click(); - $(SexPage.male()).click(); - $(SexPage.submit()).click(); - $(PersonalDetailsSummaryPage.submit()).click(); - - $(HubPage.submit()).click(); - $(SexPage.submit()).click(); - $(PersonalDetailsSummaryPage.submit()).click(); - - $(HubPage.submit()).click(); - $(SexPage.female()).click(); - $(SexPage.submit()).click(); - $(PersonalDetailsSummaryPage.submit()).click(); - - $(HubPage.submit()).click(); - $(VisitorsDateOfBirthPage.day()).setValue("03"); - $(VisitorsDateOfBirthPage.month()).setValue("09"); - $(VisitorsDateOfBirthPage.year()).setValue("1975"); - $(VisitorsDateOfBirthPage.submit()).click(); - - $(HubPage.submit()).click(); - $(VisitorsDateOfBirthPage.day()).setValue("31"); - $(VisitorsDateOfBirthPage.month()).setValue("07"); - $(VisitorsDateOfBirthPage.year()).setValue("1999"); - $(VisitorsDateOfBirthPage.submit()).click(); - - expect($(HubPage.submit()).getText()).to.equal("Submit survey"); - expect($(HubPage.heading()).getText()).to.equal("Submit survey"); + await $(HubPage.submit()).click(); + await $(SexPage.male()).click(); + await $(SexPage.submit()).click(); + await $(PersonalDetailsSummaryPage.submit()).click(); + + await $(HubPage.submit()).click(); + await $(SexPage.submit()).click(); + await $(PersonalDetailsSummaryPage.submit()).click(); + + await $(HubPage.submit()).click(); + await $(SexPage.female()).click(); + await $(SexPage.submit()).click(); + await $(PersonalDetailsSummaryPage.submit()).click(); + + await $(HubPage.submit()).click(); + await $(VisitorsDateOfBirthPage.day()).setValue("03"); + await $(VisitorsDateOfBirthPage.month()).setValue("09"); + await $(VisitorsDateOfBirthPage.year()).setValue("1975"); + await $(VisitorsDateOfBirthPage.submit()).click(); + + await $(HubPage.submit()).click(); + await $(VisitorsDateOfBirthPage.day()).setValue("31"); + await $(VisitorsDateOfBirthPage.month()).setValue("07"); + await $(VisitorsDateOfBirthPage.year()).setValue("1999"); + await $(VisitorsDateOfBirthPage.submit()).click(); + + await expect(await $(HubPage.submit()).getText()).to.equal("Submit survey"); + await expect(await $(HubPage.heading()).getText()).to.equal("Submit survey"); }); - it("When the user adds a new visitor, Then the Hub should not be in the completed state", () => { - $(HubPage.summaryRowLink("section")).click(); + it("When the user adds a new visitor, Then the Hub should not be in the completed state", async () => { + await $(HubPage.summaryRowLink("section")).click(); // Add another visitor - $(SectionSummaryPage.visitorListAddLink()).click(); - $(VisitorsListCollectorAddPage.firstName()).setValue("Anna"); - $(VisitorsListCollectorAddPage.lastName()).setValue("Doe"); - $(VisitorsListCollectorAddPage.submit()).click(); + await $(SectionSummaryPage.visitorListAddLink()).click(); + await $(VisitorsListCollectorAddPage.firstName()).setValue("Anna"); + await $(VisitorsListCollectorAddPage.lastName()).setValue("Doe"); + await $(VisitorsListCollectorAddPage.submit()).click(); - $(VisitorsListCollectorPage.no()).click(); - $(VisitorsListCollectorPage.submit()).click(); + await $(VisitorsListCollectorPage.no()).click(); + await $(VisitorsListCollectorPage.submit()).click(); - $(SectionSummaryPage.submit()).click(); + await $(SectionSummaryPage.submit()).click(); // New visitor added to hub - expect($(HubPage.summaryRowState("visitors-section-3")).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; - expect($(HubPage.submit()).getText()).to.not.equal("Submit survey"); - expect($(HubPage.submit()).getText()).to.equal("Continue"); + await expect(await $(HubPage.submit()).getText()).to.not.equal("Submit survey"); + await expect(await $(HubPage.submit()).getText()).to.equal("Continue"); - expect($(HubPage.heading()).getText()).to.not.equal("Submit survey"); - expect($(HubPage.heading()).getText()).to.equal("Choose another section to complete"); + await expect(await $(HubPage.heading()).getText()).to.not.equal("Submit survey"); + await expect(await $(HubPage.heading()).getText()).to.equal("Choose another section to complete"); }); - it("When the user removes a visitor, Then their section is not longer displayed on he Hub", () => { + it("When the user removes a visitor, Then their section is not longer displayed on he Hub", async () => { // Ensure final householder exists - expect($(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; - $(HubPage.summaryRowLink("section")).click(); + await $(HubPage.summaryRowLink("section")).click(); // Remove final visitor - $(SectionSummaryPage.visitorListRemoveLink(3)).click(); + await $(SectionSummaryPage.visitorListRemoveLink(3)).click(); - $(VisitorsListCollectorRemovePage.yes()).click(); - $(VisitorsListCollectorPage.submit()).click(); - $(SectionSummaryPage.submit()).click(); + await $(VisitorsListCollectorRemovePage.yes()).click(); + await $(VisitorsListCollectorPage.submit()).click(); + await $(SectionSummaryPage.submit()).click(); // Ensure final householder no longer exists - expect($(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; }); - it("When the user submits, it should show the thank you page", () => { - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + it("When the user submits, it should show the thank you page", async () => { + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); }); }); diff --git a/tests/functional/spec/features/routing/all_in.spec.js b/tests/functional/spec/features/routing/all_in.spec.js index 6a52ba73e9..2453610bb8 100644 --- a/tests/functional/spec/features/routing/all_in.spec.js +++ b/tests/functional/spec/features/routing/all_in.spec.js @@ -5,26 +5,26 @@ import CountryInterstitialOtherPage from "../../../generated_pages/routing_check describe("Feature: Routing - ALL-IN Operator", () => { describe("Equals", () => { describe("Given I start the ALL-IN operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_checkbox_contains_all.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_checkbox_contains_all.json"); }); - it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.malta()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.malta()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); - it("When I do select India only, Then I should be routed to the correct answer interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + it("When I do select India only, Then I should be routed to the correct answer interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); - it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", () => { - $(CountryCheckboxPage.liechtenstein()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { + await $(CountryCheckboxPage.liechtenstein()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/and.spec.js b/tests/functional/spec/features/routing/and.spec.js index 40c88a7cdc..aece04f558 100644 --- a/tests/functional/spec/features/routing/and.spec.js +++ b/tests/functional/spec/features/routing/and.spec.js @@ -6,40 +6,40 @@ import IncorrectAnswerPage from "../../../generated_pages/routing_and/incorrect- describe("Feature: Routing - And Operator", () => { describe("Equals", () => { describe("Given I start the and operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_and.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_and.json"); }); - it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(123); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(321); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(123); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(321); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the incorrect page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(555); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(321); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the incorrect page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(555); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(321); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); - it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the incorrect page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(123); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(555); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the incorrect page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(123); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(555); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); - it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(555); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(444); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(555); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(444); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js index 2d92b8e5ce..bfb9d23e6c 100644 --- a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js +++ b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js @@ -2,31 +2,31 @@ import RouteComparison1Page from "../../../generated_pages/routing_answer_compar import RouteComparison2Page from "../../../generated_pages/routing_answer_comparison/route-comparison-2.page.js"; describe("Test routing skip", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_answer_comparison.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_answer_comparison.json"); }); - it("Given we start the routing test survey, When we enter a low number then a high number, Then, we should be routed to the fourth page", () => { - $(RouteComparison1Page.answer()).setValue(1); - $(RouteComparison1Page.submit()).click(); - $(RouteComparison2Page.answer()).setValue(2); - $(RouteComparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("This page should never be skipped"); + it("Given we start the routing test survey, When we enter a low number then a high number, Then, we should be routed to the fourth page", async () => { + await $(RouteComparison1Page.answer()).setValue(1); + await $(RouteComparison1Page.submit()).click(); + await $(RouteComparison2Page.answer()).setValue(2); + await $(RouteComparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("This page should never be skipped"); }); - it("Given we start the routing test survey, When we enter a high number then a low number, Then, we should be routed to the third page", () => { - $(RouteComparison1Page.answer()).setValue(1); - $(RouteComparison1Page.submit()).click(); - $(RouteComparison2Page.answer()).setValue(0); - $(RouteComparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); + it("Given we start the routing test survey, When we enter a high number then a low number, Then, we should be routed to the third page", async () => { + await $(RouteComparison1Page.answer()).setValue(1); + await $(RouteComparison1Page.submit()).click(); + await $(RouteComparison2Page.answer()).setValue(0); + await $(RouteComparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); }); - it("Given we start the routing test survey, When we enter an equal number on both questions, Then, we should be routed to the third page", () => { - $(RouteComparison1Page.answer()).setValue(1); - $(RouteComparison1Page.submit()).click(); - $(RouteComparison2Page.answer()).setValue(1); - $(RouteComparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); + it("Given we start the routing test survey, When we enter an equal number on both questions, Then, we should be routed to the third page", async () => { + await $(RouteComparison1Page.answer()).setValue(1); + await $(RouteComparison1Page.submit()).click(); + await $(RouteComparison2Page.answer()).setValue(1); + await $(RouteComparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); }); }); diff --git a/tests/functional/spec/features/routing/answer_not_on_path.spec.js b/tests/functional/spec/features/routing/answer_not_on_path.spec.js index faa52c93f0..6d679e39f2 100644 --- a/tests/functional/spec/features/routing/answer_not_on_path.spec.js +++ b/tests/functional/spec/features/routing/answer_not_on_path.spec.js @@ -5,33 +5,33 @@ import ValidPathPage from "../../../generated_pages/routing_not_affected_by_answ import ValidFinalInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-final-interstitial.page.js"; describe("Answers not on path are not considered when routing", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_not_affected_by_answers_not_on_path.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_not_affected_by_answers_not_on_path.json"); }); - it("Given the user enters an answer on the first path, when they return to the second path, they should be routed to the valid path interstitial", () => { - $(InitialChoicePage.goHereFirst()).click(); - $(InitialChoicePage.submit()).click(); + it("Given the user enters an answer on the first path, when they return to the second path, they should be routed to the valid path interstitial", async () => { + await $(InitialChoicePage.goHereFirst()).click(); + await $(InitialChoicePage.submit()).click(); - expect(browser.getUrl()).to.contain(InvalidPathPage.pageName); - $(InvalidPathPage.answer()).setValue(123); - $(InvalidPathPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(InvalidPathPage.pageName); + await $(InvalidPathPage.answer()).setValue(123); + await $(InvalidPathPage.submit()).click(); // We now have an answer in the store on the 'invalid' path - expect(browser.getUrl()).to.contain(InvalidPathInterstitialPage.pageName); - $(InvalidPathInterstitialPage.previous()).click(); - $(InvalidPathPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(InvalidPathInterstitialPage.pageName); + await $(InvalidPathInterstitialPage.previous()).click(); + await $(InvalidPathPage.previous()).click(); // Take the second route - $(InitialChoicePage.goHereSecond()).click(); - $(InitialChoicePage.submit()).click(); + await $(InitialChoicePage.goHereSecond()).click(); + await $(InitialChoicePage.submit()).click(); - $(ValidPathPage.answer()).setValue(321); - $(ValidPathPage.submit()).click(); + await $(ValidPathPage.answer()).setValue(321); + await $(ValidPathPage.submit()).click(); // We should be routed to the valid interstitial page since the invalid path answer should not be considered whilst routing. - expect(browser.getUrl()).to.contain(ValidFinalInterstitialPage.pageName); + await expect(await browser.getUrl()).to.contain(ValidFinalInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/answered_unanswered.spec.js b/tests/functional/spec/features/routing/answered_unanswered.spec.js index f1ecb87ffd..fba7b60e0a 100644 --- a/tests/functional/spec/features/routing/answered_unanswered.spec.js +++ b/tests/functional/spec/features/routing/answered_unanswered.spec.js @@ -12,85 +12,83 @@ import QuestionThreeUnansweredOrAnswerZero from "../../../generated_pages/routin describe("Test routing question answered/unanswered", () => { describe("Given I am on the first question", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_routing_answered_unanswered.json"); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_routing_answered_unanswered.json"); }); - it("When I select any answer and submit, Then I should see a page saying I have answered the first question", () => { - $(QuestionOne.ham()).click(); - $(QuestionOne.submit()).click(); - expect($(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); - expect(browser.getUrl()).to.contain(QuestionOneAnswered.pageName); - - $(QuestionOneAnswered.previous()).click(); - $(QuestionOne.cheese()).click(); - $(QuestionOne.submit()).click(); - expect($(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); - expect(browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + it("When I select any answer and submit, Then I should see a page saying I have answered the first question", async () => { + await $(QuestionOne.ham()).click(); + await $(QuestionOne.submit()).click(); + await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); + await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + + await $(QuestionOneAnswered.previous()).click(); + await $(QuestionOne.cheese()).click(); + await $(QuestionOne.submit()).click(); + await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); + await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); }); - it("When I don't select an answer and submit, Then I should see a page saying I have not answered the first question", () => { - $(QuestionOne.submit()).click(); - expect($(QuestionOneAnswered.heading()).getText()).to.contain("You did not answer the first question!"); - expect(browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + it("When I don't select an answer and submit, Then I should see a page saying I have not answered the first question", async () => { + await $(QuestionOne.submit()).click(); + await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You did not answer the first question!"); + await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); }); }); describe("Given I am on the second question", () => { - beforeEach("Load the questionnaire and get to the second question", () => { - browser.openQuestionnaire("test_routing_answered_unanswered.json"); - - $(QuestionOne.submit()).click(); - $(QuestionOneUnanswered.submit()).click(); + beforeEach("Load the questionnaire and get to the second question", async () => { + await browser.openQuestionnaire("test_routing_answered_unanswered.json"); + await $(QuestionOne.submit()).click(); + await $(QuestionOneUnanswered.submit()).click(); }); - it("When I select any answer and submit, Then I should see a page saying I have answered the second question", () => { - $(QuestionTwo.pizzaHut()).click(); - $(QuestionTwo.submit()).click(); - expect($(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); - expect(browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); - - $(QuestionOneAnswered.previous()).click(); - $(QuestionTwo.dominoS()).click(); - $(QuestionTwo.submit()).click(); - expect($(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); - expect(browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + it("When I select any answer and submit, Then I should see a page saying I have answered the second question", async () => { + await $(QuestionTwo.pizzaHut()).click(); + await $(QuestionTwo.submit()).click(); + await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); + await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + + await $(QuestionOneAnswered.previous()).click(); + await $(QuestionTwo.dominoS()).click(); + await $(QuestionTwo.submit()).click(); + await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); + await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); }); - it("When I don't select an answer and submit, Then I should see a page saying I have not answered the second question", () => { - $(QuestionTwo.submit()).click(); - expect($(QuestionTwoUnanswered.heading()).getText()).to.contain("You did not answer the second question!"); - expect(browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + it("When I don't select an answer and submit, Then I should see a page saying I have not answered the second question", async () => { + await $(QuestionTwo.submit()).click(); + await expect(await $(QuestionTwoUnanswered.heading()).getText()).to.contain("You did not answer the second question!"); + await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); }); }); describe("Given I am on the third question", () => { - beforeEach("Load the questionnaire and get to the third question", () => { - browser.openQuestionnaire("test_routing_answered_unanswered.json"); - - $(QuestionOne.submit()).click(); - $(QuestionOneUnanswered.submit()).click(); - $(QuestionTwo.submit()).click(); - $(QuestionTwoUnanswered.submit()).click(); + beforeEach("Load the questionnaire and get to the third question", async () => { + await browser.openQuestionnaire("test_routing_answered_unanswered.json"); + await $(QuestionOne.submit()).click(); + await $(QuestionOneUnanswered.submit()).click(); + await $(QuestionTwo.submit()).click(); + await $(QuestionTwoUnanswered.submit()).click(); }); - it("When I do not answer the question or answer `0` and submit, Then I should see a page saying I did not answer the question or that I chose `0`", () => { - $(QuestionThree.submit()).click(); - expect($(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); - expect(browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); + it("When I do not answer the question or answer `0` and submit, Then I should see a page saying I did not answer the question or that I chose `0`", async () => { + await $(QuestionThree.submit()).click(); + await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); + await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); - $(QuestionThreeUnansweredOrAnswerZero.previous()).click(); - $(QuestionThree.answer3()).setValue("0"); - $(QuestionThree.submit()).click(); - expect($(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); - expect(browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); + await $(QuestionThreeUnansweredOrAnswerZero.previous()).click(); + await $(QuestionThree.answer3()).setValue("0"); + await $(QuestionThree.submit()).click(); + await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); + await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); }); - it("When I enter an answer greater than 0 and submit, Then I should see a page saying I chose at least one", () => { - $(QuestionThree.answer3()).setValue("2"); - $(QuestionThree.submit()).click(); - expect($(QuestionTwoAnswered.heading()).getText()).to.contain("You chose at least 1 slice"); - expect(browser.getUrl()).to.contain(QuestionThreeAnsweredOrNotZero.pageName); + it("When I enter an answer greater than 0 and submit, Then I should see a page saying I chose at least one", async () => { + await $(QuestionThree.answer3()).setValue("2"); + await $(QuestionThree.submit()).click(); + await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You chose at least 1 slice"); + await expect(await browser.getUrl()).to.contain(QuestionThreeAnsweredOrNotZero.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/any_in.spec.js b/tests/functional/spec/features/routing/any_in.spec.js index fe1fa9ebf7..41e4167fda 100644 --- a/tests/functional/spec/features/routing/any_in.spec.js +++ b/tests/functional/spec/features/routing/any_in.spec.js @@ -5,26 +5,26 @@ import CountryInterstitialOtherPage from "../../../generated_pages/routing_check describe("Feature: Routing - ANY-IN Operator", () => { describe("Equals", () => { describe("Given I start the ANY-IN operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_checkbox_contains_any.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_checkbox_contains_any.json"); }); - it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.malta()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.malta()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); - it("When I do select India or Malta, Then I should be routed to the correct answer interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + it("When I do select India or Malta, Then I should be routed to the correct answer interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); - it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", () => { - $(CountryCheckboxPage.liechtenstein()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { + await $(CountryCheckboxPage.liechtenstein()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/checkbox_count.spec.js b/tests/functional/spec/features/routing/checkbox_count.spec.js index ed14704b33..3ba7397fbc 100644 --- a/tests/functional/spec/features/routing/checkbox_count.spec.js +++ b/tests/functional/spec/features/routing/checkbox_count.spec.js @@ -3,41 +3,41 @@ import CorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/c import IncorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/incorrect-answer.page"; describe("Test routing using count of checkboxes checked", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_checkbox_count.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_checkbox_count.json"); }); - it("Given a user selects 2 checkboxes, When they submit, Then they should be routed to the correct page", () => { - $(ToppingCheckboxPage.cheese()).click(); - $(ToppingCheckboxPage.ham()).click(); - $(ToppingCheckboxPage.submit()).click(); + it("Given a user selects 2 checkboxes, When they submit, Then they should be routed to the correct page", async () => { + await $(ToppingCheckboxPage.cheese()).click(); + await $(ToppingCheckboxPage.ham()).click(); + await $(ToppingCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); - expect($(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); }); - it("Given a user selects no checkboxes, When they submit, Then they should be routed to the incorrect page", () => { - $(ToppingCheckboxPage.submit()).click(); + it("Given a user selects no checkboxes, When they submit, Then they should be routed to the incorrect page", async () => { + await $(ToppingCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); - expect($(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); }); - it("Given a user selects 1 checkbox, When they submit, Then they should be routed to the incorrect page", () => { - $(ToppingCheckboxPage.cheese()).click(); - $(ToppingCheckboxPage.submit()).click(); + it("Given a user selects 1 checkbox, When they submit, Then they should be routed to the incorrect page", async () => { + await $(ToppingCheckboxPage.cheese()).click(); + await $(ToppingCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); - expect($(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); }); - it("Given a user selects 3 checkbox, When they submit, Then they should be routed to the correct page", () => { - $(ToppingCheckboxPage.cheese()).click(); - $(ToppingCheckboxPage.ham()).click(); - $(ToppingCheckboxPage.pineapple()).click(); - $(ToppingCheckboxPage.submit()).click(); + it("Given a user selects 3 checkbox, When they submit, Then they should be routed to the correct page", async () => { + await $(ToppingCheckboxPage.cheese()).click(); + await $(ToppingCheckboxPage.ham()).click(); + await $(ToppingCheckboxPage.pineapple()).click(); + await $(ToppingCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); - expect($(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); }); }); diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/features/routing/date.spec.js index 3d979b9204..0945cc2624 100644 --- a/tests/functional/spec/features/routing/date.spec.js +++ b/tests/functional/spec/features/routing/date.spec.js @@ -29,263 +29,262 @@ const yearTomorrow = tomorrow.getFullYear(); describe("Feature: Routing on a Date", () => { describe("Equals", () => { describe("Given I start date routing equals survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_equals.json"); - - $(DateEqualsComparisonQuestionPage.day()).setValue(31); - $(DateEqualsComparisonQuestionPage.month()).setValue(3); - $(DateEqualsComparisonQuestionPage.year()).setValue(2020); - $(DateEqualsComparisonQuestionPage.submit()).click(); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_equals.json"); + await $(DateEqualsComparisonQuestionPage.day()).setValue(31); + await $(DateEqualsComparisonQuestionPage.month()).setValue(3); + await $(DateEqualsComparisonQuestionPage.year()).setValue(2020); + await $(DateEqualsComparisonQuestionPage.submit()).click(); }); - it("When I enter the same date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(31); - $(DateEqualsQuestionPage.month()).setValue(3); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the same date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(31); + await $(DateEqualsQuestionPage.month()).setValue(3); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the yesterday date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(30); - $(DateEqualsQuestionPage.month()).setValue(3); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the yesterday date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(30); + await $(DateEqualsQuestionPage.month()).setValue(3); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the tomorrow date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(1); - $(DateEqualsQuestionPage.month()).setValue(4); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the tomorrow date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(1); + await $(DateEqualsQuestionPage.month()).setValue(4); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the last month date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(29); - $(DateEqualsQuestionPage.month()).setValue(2); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the last month date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(29); + await $(DateEqualsQuestionPage.month()).setValue(2); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the next month date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(30); - $(DateEqualsQuestionPage.month()).setValue(4); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the next month date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(30); + await $(DateEqualsQuestionPage.month()).setValue(4); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the last year date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(31); - $(DateEqualsQuestionPage.month()).setValue(3); - $(DateEqualsQuestionPage.year()).setValue(2019); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the last year date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(31); + await $(DateEqualsQuestionPage.month()).setValue(3); + await $(DateEqualsQuestionPage.year()).setValue(2019); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the next year date, Then I should be routed to the correct page", () => { - $(DateEqualsQuestionPage.day()).setValue(31); - $(DateEqualsQuestionPage.month()).setValue(3); - $(DateEqualsQuestionPage.year()).setValue(2021); - $(DateEqualsQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter the next year date, Then I should be routed to the correct page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(31); + await $(DateEqualsQuestionPage.month()).setValue(3); + await $(DateEqualsQuestionPage.year()).setValue(2021); + await $(DateEqualsQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter an incorrect date, Then I should be routed to the incorrect page", () => { - $(DateEqualsQuestionPage.day()).setValue(1); - $(DateEqualsQuestionPage.month()).setValue(3); - $(DateEqualsQuestionPage.year()).setValue(2020); - $(DateEqualsComparisonQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter an incorrect date, Then I should be routed to the incorrect page", async () => { + await $(DateEqualsQuestionPage.day()).setValue(1); + await $(DateEqualsQuestionPage.month()).setValue(3); + await $(DateEqualsQuestionPage.year()).setValue(2020); + await $(DateEqualsComparisonQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); }); }); describe("Not Equals", () => { describe("Given I start date routing not equals survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_not_equals.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_not_equals.json"); }); - it("When I enter a different date to February 2018, Then I should be routed to the correct page", () => { - $(DateNotEqualsQuestionPage.Month()).setValue(3); - $(DateNotEqualsQuestionPage.Year()).setValue(2018); - $(DateNotEqualsQuestionPage.submit()).click(); + it("When I enter a different date to February 2018, Then I should be routed to the correct page", async () => { + await $(DateNotEqualsQuestionPage.Month()).setValue(3); + await $(DateNotEqualsQuestionPage.Year()).setValue(2018); + await $(DateNotEqualsQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter February 2018, Then I should be routed to the incorrect page", () => { - $(DateNotEqualsQuestionPage.Month()).setValue(2); - $(DateNotEqualsQuestionPage.Year()).setValue(2018); - $(DateNotEqualsQuestionPage.submit()).click(); + it("When I enter February 2018, Then I should be routed to the incorrect page", async () => { + await $(DateNotEqualsQuestionPage.Month()).setValue(2); + await $(DateNotEqualsQuestionPage.Year()).setValue(2018); + await $(DateNotEqualsQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Greater Than", () => { describe("Given I start date routing greater than survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_greater_than.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_greater_than.json"); }); - it("When I enter a date greater than the 1st March 2017, Then I should be routed to the correct page", () => { - $(DateGreaterThanQuestionPage.day()).setValue(2); - $(DateGreaterThanQuestionPage.month()).setValue(3); - $(DateGreaterThanQuestionPage.year()).setValue(2017); - $(DateGreaterThanQuestionPage.submit()).click(); + it("When I enter a date greater than the 1st March 2017, Then I should be routed to the correct page", async () => { + await $(DateGreaterThanQuestionPage.day()).setValue(2); + await $(DateGreaterThanQuestionPage.month()).setValue(3); + await $(DateGreaterThanQuestionPage.year()).setValue(2017); + await $(DateGreaterThanQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter the 1st March 2017, Then I should be routed to the incorrect page", () => { - $(DateGreaterThanQuestionPage.day()).setValue(1); - $(DateGreaterThanQuestionPage.month()).setValue(3); - $(DateGreaterThanQuestionPage.year()).setValue(2017); - $(DateGreaterThanQuestionPage.submit()).click(); + it("When I enter the 1st March 2017, Then I should be routed to the incorrect page", async () => { + await $(DateGreaterThanQuestionPage.day()).setValue(1); + await $(DateGreaterThanQuestionPage.month()).setValue(3); + await $(DateGreaterThanQuestionPage.year()).setValue(2017); + await $(DateGreaterThanQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a date less than the 1st March 2017, Then I should be routed to the incorrect page", () => { - $(DateGreaterThanQuestionPage.day()).setValue(28); - $(DateGreaterThanQuestionPage.month()).setValue(2); - $(DateGreaterThanQuestionPage.year()).setValue(2017); - $(DateGreaterThanQuestionPage.submit()).click(); + it("When I enter a date less than the 1st March 2017, Then I should be routed to the incorrect page", async () => { + await $(DateGreaterThanQuestionPage.day()).setValue(28); + await $(DateGreaterThanQuestionPage.month()).setValue(2); + await $(DateGreaterThanQuestionPage.year()).setValue(2017); + await $(DateGreaterThanQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Greater Than Or Equals", () => { describe("Given I start date routing greater than or equals survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_greater_than_or_equals.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_greater_than_or_equals.json"); }); - it("When I enter a date greater than 2017, Then I should be routed to the correct page", () => { - $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2018); - $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + it("When I enter a date greater than 2017, Then I should be routed to the correct page", async () => { + await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2018); + await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 2017, Then I should be routed to the correct page", () => { - $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2017); - $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + it("When I enter 2017, Then I should be routed to the correct page", async () => { + await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2017); + await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a date less than March 2017, Then I should be routed to the incorrect page", () => { - $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2016); - $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + it("When I enter a date less than March 2017, Then I should be routed to the incorrect page", async () => { + await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2016); + await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); - const expectedUrl = browser.getUrl(); + const expectedUrl = await browser.getUrl(); - expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Less Than", () => { describe("Given I start date routing less than survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_less_than.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_less_than.json"); }); - it("When I enter a date less than today, Then I should be routed to the correct page", () => { - $(DateLessThanQuestionPage.day()).setValue(dayYesterday); - $(DateLessThanQuestionPage.month()).setValue(monthYesterday); - $(DateLessThanQuestionPage.year()).setValue(yearYesterday); - $(DateLessThanQuestionPage.submit()).click(); + it("When I enter a date less than today, Then I should be routed to the correct page", async () => { + await $(DateLessThanQuestionPage.day()).setValue(dayYesterday); + await $(DateLessThanQuestionPage.month()).setValue(monthYesterday); + await $(DateLessThanQuestionPage.year()).setValue(yearYesterday); + await $(DateLessThanQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(await browserUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a date equal to today, Then I should be routed to the incorrect page", () => { - $(DateLessThanQuestionPage.day()).setValue(dayToday); - $(DateLessThanQuestionPage.month()).setValue(monthToday); - $(DateLessThanQuestionPage.year()).setValue(yearToday); - $(DateLessThanQuestionPage.submit()).click(); + it("When I enter a date equal to today, Then I should be routed to the incorrect page", async () => { + await $(DateLessThanQuestionPage.day()).setValue(dayToday); + await $(DateLessThanQuestionPage.month()).setValue(monthToday); + await $(DateLessThanQuestionPage.year()).setValue(yearToday); + await $(DateLessThanQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); }); - it("When I enter a date greater than today, Then I should be routed to the incorrect page", () => { - $(DateLessThanQuestionPage.day()).setValue(dayTomorrow); - $(DateLessThanQuestionPage.month()).setValue(monthTomorrow); - $(DateLessThanQuestionPage.year()).setValue(yearTomorrow); - $(DateLessThanQuestionPage.submit()).click(); + it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { + await $(DateLessThanQuestionPage.day()).setValue(dayTomorrow); + await $(DateLessThanQuestionPage.month()).setValue(monthTomorrow); + await $(DateLessThanQuestionPage.year()).setValue(yearTomorrow); + await $(DateLessThanQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Less Than Or Equals", () => { describe("Given I start date routing less than or equals survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_date_less_than_or_equals.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_date_less_than_or_equals.json"); }); - it("When I enter a date less than today, Then I should be routed to the correct page", () => { - $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayYesterday); - $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthYesterday); - $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearYesterday); - $(DateLessThanOrEqualsQuestionPage.submit()).click(); + it("When I enter a date less than today, Then I should be routed to the correct page", async () => { + await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayYesterday); + await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthYesterday); + await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearYesterday); + await $(DateLessThanOrEqualsQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(browserUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a date equal to today, Then I should be routed to the correct page", () => { - $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayToday); - $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthToday); - $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearToday); - $(DateLessThanOrEqualsQuestionPage.submit()).click(); + it("When I enter a date equal to today, Then I should be routed to the correct page", async () => { + await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayToday); + await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthToday); + await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearToday); + await $(DateLessThanOrEqualsQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(await browserUrl).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a date greater than today, Then I should be routed to the incorrect page", () => { - $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayTomorrow); - $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthTomorrow); - $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearTomorrow); - $(DateLessThanOrEqualsQuestionPage.submit()).click(); + it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { + await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayTomorrow); + await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthTomorrow); + await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearTomorrow); + await $(DateLessThanOrEqualsQuestionPage.submit()).click(); - const browserUrl = browser.getUrl(); + const browserUrl = await browser.getUrl(); - expect(browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/in.spec.js b/tests/functional/spec/features/routing/in.spec.js index a0309a730b..10d14998c1 100644 --- a/tests/functional/spec/features/routing/in.spec.js +++ b/tests/functional/spec/features/routing/in.spec.js @@ -5,20 +5,20 @@ import CountryInterstitialOtherPage from "../../../generated_pages/routing_check describe("Feature: Routing - IN Operator", () => { describe("Equals", () => { describe("Given I start the IN operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_checkbox_contains_in.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_checkbox_contains_in.json"); }); - it("When I do select India, Then I should be routed to the the correct answer interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + it("When I do select India, Then I should be routed to the the correct answer interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); - it("When I do not select India, Then I should be routed to the the incorrect answer interstitial page", () => { - $(CountryCheckboxPage.liechtenstein()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + it("When I do not select India, Then I should be routed to the the incorrect answer interstitial page", async () => { + await $(CountryCheckboxPage.liechtenstein()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/not.spec.js b/tests/functional/spec/features/routing/not.spec.js index 53e2f717f1..771a06ebe0 100644 --- a/tests/functional/spec/features/routing/not.spec.js +++ b/tests/functional/spec/features/routing/not.spec.js @@ -5,20 +5,20 @@ import IndiaInterstitialPage from "../../../generated_pages/routing_not/country- describe("Feature: Routing - Not Operator", () => { describe("Equals", () => { describe("Given I start the not operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_not.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_not.json"); }); - it("When I do not select India, Then I should be routed to the not India interstitial page", () => { - $(CountryCheckboxPage.azerbaijan()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + it("When I do not select India, Then I should be routed to the not India interstitial page", async () => { + await $(CountryCheckboxPage.azerbaijan()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); - it("When I select India, Then I should be routed to the India interstitial page", () => { - $(CountryCheckboxPage.india()).click(); - $(CountryCheckboxPage.submit()).click(); - expect(browser.getUrl()).to.contain(IndiaInterstitialPage.pageName); + it("When I select India, Then I should be routed to the India interstitial page", async () => { + await $(CountryCheckboxPage.india()).click(); + await $(CountryCheckboxPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IndiaInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/number.spec.js b/tests/functional/spec/features/routing/number.spec.js index cb66532b8e..a932146467 100644 --- a/tests/functional/spec/features/routing/number.spec.js +++ b/tests/functional/spec/features/routing/number.spec.js @@ -5,154 +5,154 @@ import IncorrectAnswerPage from "../../../generated_pages/routing_number_equals/ describe("Feature: Routing on a Number", () => { describe("Equals", () => { describe("Given I start number routing equals survey", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_equals.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_equals.json"); }); - it("When I enter 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a number that isn't 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(555); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter a number that isn't 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(555); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Not Equals", () => { describe("Given I start number routing not equals survey", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_not_equals.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_not_equals.json"); }); - it("When I enter a number that isn't 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(987); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter a number that isn't 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(987); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Greater Than", () => { describe("Given I start number routing greater than survey", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_greater_than.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_greater_than.json"); }); - it("When I enter a number greater than 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(555); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(555); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); - it("When I enter a number less than 123, Then I should be routed to the incorrect page", () => { - $(IncorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(2); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { + await $(IncorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(2); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Less Than", () => { describe("Given I start number routing less than survey", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_less_than.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_less_than.json"); }); - it("When I enter a number less than 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(77); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(77); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); - it("When I enter a number greater than 123, Then I should be routed to the incorrect page", () => { - $(IncorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(765); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter a number greater than 123, Then I should be routed to the incorrect page", async () => { + await $(IncorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(765); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Greater Than or Equal", () => { describe("Given I have number routing with a greater than or equal", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_greater_than_or_equal.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_greater_than_or_equal.json"); }); - it("When I enter a number greater than 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(555); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(555); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 123, Then I should be routed to the correct page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the correct page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a number less than 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(2); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(2); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); describe("Less Than or Equal", () => { describe("Given I have number routing with a less than or equal", () => { - before(() => { - browser.openQuestionnaire("test_routing_number_less_than_or_equal.json"); + before(async () => { + await browser.openQuestionnaire("test_routing_number_less_than_or_equal.json"); }); - it("When I enter a number less than 123, Then I should be routed to the correct page", () => { - $(NumberQuestionPage.answer()).setValue(23); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { + await $(NumberQuestionPage.answer()).setValue(23); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter 123, Then I should be routed to the correct page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(123); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter 123, Then I should be routed to the correct page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(123); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I enter a number larger than 123, Then I should be routed to the incorrect page", () => { - $(CorrectAnswerPage.previous()).click(); - $(NumberQuestionPage.answer()).setValue(546); - $(NumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I enter a number larger than 123, Then I should be routed to the incorrect page", async () => { + await $(CorrectAnswerPage.previous()).click(); + await $(NumberQuestionPage.answer()).setValue(546); + await $(NumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/or.spec.js b/tests/functional/spec/features/routing/or.spec.js index 83ba0c193d..5d0d8d4598 100644 --- a/tests/functional/spec/features/routing/or.spec.js +++ b/tests/functional/spec/features/routing/or.spec.js @@ -6,40 +6,40 @@ import IncorrectAnswerPage from "../../../generated_pages/routing_or/incorrect-a describe("Feature: Routing - OR Operator", () => { describe("Equals", () => { describe("Given I start the or operator routing survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_or.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_or.json"); }); - it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(123); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(321); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(123); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(321); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the correct page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(555); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(321); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the correct page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(555); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(321); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the correct page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(123); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(555); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the correct page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(123); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(555); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); - it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", () => { - $(FirstNumberQuestionPage.answer1()).setValue(555); - $(FirstNumberQuestionPage.submit()).click(); - $(SecondNumberQuestionPage.answer2()).setValue(444); - $(SecondNumberQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { + await $(FirstNumberQuestionPage.answer1()).setValue(555); + await $(FirstNumberQuestionPage.submit()).click(); + await $(SecondNumberQuestionPage.answer2()).setValue(444); + await $(SecondNumberQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/removes_completed_block.spec.js b/tests/functional/spec/features/routing/removes_completed_block.spec.js index 899e3dfd57..4ec39fc0aa 100644 --- a/tests/functional/spec/features/routing/removes_completed_block.spec.js +++ b/tests/functional/spec/features/routing/removes_completed_block.spec.js @@ -4,13 +4,13 @@ import SubmitPage from "../../../generated_pages/confirmation_question/submit.pa describe("Feature: Routing incompletes block if routing backwards", () => { describe("Given I have a confirmation Question", () => { - before("Get to summary", () => { - browser.openQuestionnaire("test_confirmation_question.json"); - $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); - $(NumberOfEmployeesTotalBlockPage.submit()).click(); - $(ConfirmZeroEmployeesBlockPage.yes()).click(); - $(ConfirmZeroEmployeesBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + before("Get to summary", async () => { + await browser.openQuestionnaire("test_confirmation_question.json"); + await $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); + await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await $(ConfirmZeroEmployeesBlockPage.yes()).click(); + await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary.spec.js b/tests/functional/spec/features/section_summary/section_summary.spec.js index b2c0700ae9..8bb7228b6b 100644 --- a/tests/functional/spec/features/section_summary/section_summary.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary.spec.js @@ -11,157 +11,157 @@ import SubmitPage from "../../../generated_pages/section_summary/submit.page.js" describe("Section Summary", () => { describe("Given I start a Test Section Summary survey and complete to Section Summary", () => { - beforeEach(() => { - browser.openQuestionnaire("test_section_summary.json"); - $(InsuranceTypePage.both()).click(); - $(InsuranceTypePage.submit()).click(); - $(InsuranceAddressPage.submit()).click(); - $(ListedPage.submit()).click(); - expect($(PropertyDetailsSummaryPage.insuranceTypeAnswer()).getText()).to.contain("Both"); + beforeEach(async () => { + await browser.openQuestionnaire("test_section_summary.json"); + await $(InsuranceTypePage.both()).click(); + await $(InsuranceTypePage.submit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await $(ListedPage.submit()).click(); + await expect(await $(PropertyDetailsSummaryPage.insuranceTypeAnswer()).getText()).to.contain("Both"); }); - it("When I get to the section summary page, Then the submit button should read 'Continue'", () => { - expect($(PropertyDetailsSummaryPage.submit()).getText()).to.contain("Continue"); + it("When I get to the section summary page, Then the submit button should read 'Continue'", async () => { + await expect(await $(PropertyDetailsSummaryPage.submit()).getText()).to.contain("Continue"); }); - it("When I have selected an answer to edit and edit it, Then I should return to the section summary with new value displayed", () => { - $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); - $(InsuranceAddressPage.answer()).setValue("Test Address"); - $(InsuranceAddressPage.submit()).click(); - expect($(PropertyDetailsSummaryPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); + it("When I have selected an answer to edit and edit it, Then I should return to the section summary with new value displayed", async () => { + await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); + await $(InsuranceAddressPage.answer()).setValue("Test Address"); + await $(InsuranceAddressPage.submit()).click(); + await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); }); - it("When I select edit from the section summary and click previous on the question page, Then I should be taken back to the section summary", () => { - $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); - $(InsuranceAddressPage.previous()).click(); - expect(browser.getUrl()).to.contain(PropertyDetailsSummaryPage.url()); + it("When I select edit from the section summary and click previous on the question page, Then I should be taken back to the section summary", async () => { + await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); + await $(InsuranceAddressPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.url()); }); - it("When I continue on the section summary page, Then I should be taken to the next section", () => { - $(PropertyDetailsSummaryPage.submit()).click(); - expect(browser.getUrl()).to.contain(HouseType.pageName); + it("When I continue on the section summary page, Then I should be taken to the next section", async () => { + await $(PropertyDetailsSummaryPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HouseType.pageName); }); - it("When I select edit from Section Summary but change routing, Then I should step through the section and be returned to the Section Summary once all new questions have been answered", () => { - $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); - $(InsuranceTypePage.contents()).click(); - $(InsuranceTypePage.submit()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(AddressDurationPage.pageName); - $(AddressDurationPage.submit()).click(); - expect(browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); + it("When I select edit from Section Summary but change routing, Then I should step through the section and be returned to the Section Summary once all new questions have been answered", async () => { + await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); + await $(InsuranceTypePage.contents()).click(); + await $(InsuranceTypePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await $(AddressDurationPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); }); - it("When I select edit from Section Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", () => { - $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); - $(InsuranceTypePage.contents()).click(); - $(InsuranceTypePage.submit()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(AddressDurationPage.pageName); - $(AddressDurationPage.previous()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - expect(browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); + it("When I select edit from Section Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { + await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); + await $(InsuranceTypePage.contents()).click(); + await $(InsuranceTypePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await $(AddressDurationPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); }); }); describe("Given I start a Test Section Summary survey and complete to Final Summary", () => { - beforeEach(() => { - browser.openQuestionnaire("test_section_summary.json"); - $(InsuranceTypePage.both()).click(); - $(InsuranceTypePage.submit()).click(); - $(InsuranceAddressPage.submit()).click(); - $(ListedPage.submit()).click(); - $(PropertyDetailsSummaryPage.submit()).click(); - $(HouseType.submit()).click(); - $(HouseholdDetailsSummaryPage.submit()).click(); - $(NumberOfPeoplePage.answer()).setValue(3); - $(NumberOfPeoplePage.submit()).click(); - $(HouseholdCountSectionSummaryPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + beforeEach(async () => { + await browser.openQuestionnaire("test_section_summary.json"); + await $(InsuranceTypePage.both()).click(); + await $(InsuranceTypePage.submit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await $(ListedPage.submit()).click(); + await $(PropertyDetailsSummaryPage.submit()).click(); + await $(HouseType.submit()).click(); + await $(HouseholdDetailsSummaryPage.submit()).click(); + await $(NumberOfPeoplePage.answer()).setValue(3); + await $(NumberOfPeoplePage.submit()).click(); + await $(HouseholdCountSectionSummaryPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("When I select edit from Final Summary and don't change an answer, Then I should be taken to the Final Summary", () => { - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceAddressAnswerEdit()).click(); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + it("When I select edit from Final Summary and don't change an answer, Then I should be taken to the Final Summary", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceAddressAnswerEdit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("When I select edit from Final Summary and change an answer that doesn't affect completeness, Then I should be taken to the Final Summary", () => { - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceAddressAnswerEdit()).click(); - $(InsuranceAddressPage.answer()).setValue("Test Address"); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + it("When I select edit from Final Summary and change an answer that doesn't affect completeness, Then I should be taken to the Final Summary", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceAddressAnswerEdit()).click(); + await $(InsuranceAddressPage.answer()).setValue("Test Address"); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("When I select edit from Final Summary but change routing, Then I should step through the section and be returned to the Final Summary once all new questions have been answered", () => { - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceTypeAnswerEdit()).click(); - $(InsuranceTypePage.contents()).click(); - $(InsuranceTypePage.submit()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(AddressDurationPage.pageName); - $(AddressDurationPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I select edit from Final Summary but change routing, Then I should step through the section and be returned to the Final Summary once all new questions have been answered", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceTypeAnswerEdit()).click(); + await $(InsuranceTypePage.contents()).click(); + await $(InsuranceTypePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await $(AddressDurationPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I select edit from Final Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", () => { - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceTypeAnswerEdit()).click(); - $(InsuranceTypePage.contents()).click(); - $(InsuranceTypePage.submit()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain(AddressDurationPage.pageName); - $(AddressDurationPage.previous()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I select edit from Final Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceTypeAnswerEdit()).click(); + await $(InsuranceTypePage.contents()).click(); + await $(InsuranceTypePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await $(AddressDurationPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Then I should return to the question I originally edited", () => { - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceTypeAnswerEdit()).click(); - $(InsuranceTypePage.contents()).click(); - $(InsuranceTypePage.submit()).click(); - $(InsuranceAddressPage.previous()).click(); - expect(browser.getUrl()).to.contain(InsuranceTypePage.pageName); + it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Then I should return to the question I originally edited", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceTypeAnswerEdit()).click(); + await $(InsuranceTypePage.contents()).click(); + await $(InsuranceTypePage.submit()).click(); + await $(InsuranceAddressPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceTypePage.pageName); }); - it("When I change an answer, Then the final summary should display the updated value", () => { - $(SubmitPage.summaryShowAllButton()).click(); - expect($(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("No answer provided"); - $(SubmitPage.insuranceAddressAnswerEdit()).click(); - expect(browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - $(InsuranceAddressPage.answer()).setValue("Test Address"); - $(InsuranceAddressPage.submit()).click(); - $(SubmitPage.summaryShowAllButton()).click(); - expect($(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); + it("When I change an answer, Then the final summary should display the updated value", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); + await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("No answer provided"); + await $(SubmitPage.insuranceAddressAnswerEdit()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await $(InsuranceAddressPage.answer()).setValue("Test Address"); + await $(InsuranceAddressPage.submit()).click(); + await $(SubmitPage.summaryShowAllButton()).click(); + await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); }); }); describe("Given I start the Test Section Summary questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_section_summary.json"); - }); - it("When there is no title set in the sections summary, the section title is used for the section summary title", () => { - $(InsuranceTypePage.both()).click(); - $(InsuranceTypePage.submit()).click(); - $(InsuranceAddressPage.submit()).click(); - $(ListedPage.submit()).click(); - expect($(PropertyDetailsSummaryPage.heading()).getText()).to.contain("Property Details Section"); - }); - it("When there is a title set in the sections summary, it is used for the section summary title", () => { - $(PropertyDetailsSummaryPage.submit()).click(); - $(HouseType.semiDetached()).click(); - $(HouseType.submit()).click(); - expect($(HouseholdDetailsSummaryPage.heading()).getText()).to.contain("Household Summary - Semi-detached"); + before(async () => { + await browser.openQuestionnaire("test_section_summary.json"); + }); + it("When there is no title set in the sections summary, the section title is used for the section summary title", async () => { + await $(InsuranceTypePage.both()).click(); + await $(InsuranceTypePage.submit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await $(ListedPage.submit()).click(); + await expect(await $(PropertyDetailsSummaryPage.heading()).getText()).to.contain("Property Details Section"); + }); + it("When there is a title set in the sections summary, it is used for the section summary title", async () => { + await $(PropertyDetailsSummaryPage.submit()).click(); + await $(HouseType.semiDetached()).click(); + await $(HouseType.submit()).click(); + await expect(await $(HouseholdDetailsSummaryPage.heading()).getText()).to.contain("Household Summary - Semi-detached"); }); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js index 4efb211057..ec47750691 100644 --- a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js @@ -9,59 +9,61 @@ import HubPage from "../../../base_pages/hub.page.js"; describe("Feature: Repeating Section Summaries", () => { describe("Given the user has added some members to the household and is on the Hub", () => { - before("Open survey and add household members", () => { - browser.openQuestionnaire("test_repeating_section_summaries.json"); + before("Open survey and add household members", async () => { + await browser.openQuestionnaire("test_repeating_section_summaries.json"); + // Ensure the questionnaire fully loads + await browser.pause(100); // Ensure we are on the Hub - expect(browser.getUrl()).to.contain(HubPage.url()); + await expect(await browser.getUrl()).to.contain(HubPage.url()); // Start first section to add household members - $(HubPage.summaryRowLink("section")).click(); + await $(HubPage.summaryRowLink("section")).click(); // Add a primary person - $(PrimaryPersonPage.yes()).click(); - $(PrimaryPersonPage.submit()).click(); - $(PrimaryPersonAddPage.firstName()).setValue("Mark"); - $(PrimaryPersonAddPage.lastName()).setValue("Twain"); - $(PrimaryPersonPage.submit()).click(); + await $(PrimaryPersonPage.yes()).click(); + await $(PrimaryPersonPage.submit()).click(); + await $(PrimaryPersonAddPage.firstName()).setValue("Mark"); + await $(PrimaryPersonAddPage.lastName()).setValue("Twain"); + await $(PrimaryPersonPage.submit()).click(); // Add other household members - $(FirstListCollectorPage.yes()).click(); - $(FirstListCollectorPage.submit()).click(); - $(FirstListCollectorAddPage.firstName()).setValue("Jean"); - $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - $(FirstListCollectorAddPage.submit()).click(); + await $(FirstListCollectorPage.yes()).click(); + await $(FirstListCollectorPage.submit()).click(); + await $(FirstListCollectorAddPage.firstName()).setValue("Jean"); + await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); + await $(FirstListCollectorAddPage.submit()).click(); - $(FirstListCollectorPage.no()).click(); - $(FirstListCollectorPage.submit()).click(); + await $(FirstListCollectorPage.no()).click(); + await $(FirstListCollectorPage.submit()).click(); }); describe("When the user finishes a repeating section", () => { - before("Enter information for a repeating section", () => { - $(HubPage.summaryRowLink("personal-details-section-1")).click(); - $(ProxyPage.yes()).click(); - $(ProxyPage.submit()).click(); + before("Enter information for a repeating section", async () => { + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); + await $(ProxyPage.yes()).click(); + await $(ProxyPage.submit()).click(); - $(DateOfBirthPage.day()).setValue("30"); - $(DateOfBirthPage.month()).setValue("11"); - $(DateOfBirthPage.year()).setValue("1835"); - $(DateOfBirthPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue("30"); + await $(DateOfBirthPage.month()).setValue("11"); + await $(DateOfBirthPage.year()).setValue("1835"); + await $(DateOfBirthPage.submit()).click(); }); - beforeEach("Navigate to the Section Summary", () => { - browser.url(HubPage.url()); - $(HubPage.summaryRowLink("personal-details-section-1")).click(); + beforeEach("Navigate to the Section Summary", async () => { + await browser.url(HubPage.url()); + await $(HubPage.summaryRowLink("personal-details-section-1")).click(); }); - it("the title set in the repeating block is used for the section summary title", () => { - expect($(PersonalSummaryPage.heading()).getText()).to.contain("Mark Twain"); + it("the title set in the repeating block is used for the section summary title", async () => { + await expect(await $(PersonalSummaryPage.heading()).getText()).to.contain("Mark Twain"); }); - it("renders their name as part of the question title on the section summary", () => { - expect($(PersonalSummaryPage.dateOfBirthQuestion()).getText()).to.contain("Mark Twain’s"); + it("renders their name as part of the question title on the section summary", async () => { + await expect(await $(PersonalSummaryPage.dateOfBirthQuestion()).getText()).to.contain("Mark Twain’s"); }); - it("renders the correct date of birth answer", () => { - expect($(PersonalSummaryPage.dateOfBirthAnswer()).getText()).to.contain("30 November 1835"); + it("renders the correct date of birth answer", async () => { + await expect(await $(PersonalSummaryPage.dateOfBirthAnswer()).getText()).to.contain("30 November 1835"); }); }); }); diff --git a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js index 38a7c72f99..2d71b9377e 100644 --- a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js +++ b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js @@ -7,50 +7,50 @@ import accommodationSectionSummary from "../../../generated_pages/show_section_s import hubPage from "../../../base_pages/hub.page.js"; describe("Feature: Show section summary on completion", () => { - before("Launch survey", () => { - browser.openQuestionnaire("test_show_section_summary_on_completion.json"); + before("Launch survey", async () => { + await browser.openQuestionnaire("test_show_section_summary_on_completion.json"); }); describe("Given I am completing a section with the summary turned off for the forward journey", () => { - it("When I reach the end of that section, Then I go straight to the hub", () => { - $(employmentStatusBlockPage.workingAsAnEmployee()).click(); - $(employmentStatusBlockPage.submit()).click(); + it("When I reach the end of that section, Then I go straight to the hub", async () => { + await $(employmentStatusBlockPage.workingAsAnEmployee()).click(); + await $(employmentStatusBlockPage.submit()).click(); - expect(browser.getUrl()).to.contain(hubPage.url()); + await expect(await browser.getUrl()).to.contain(hubPage.url()); }); }); describe("Given I have completed a section with the summary turned off for the forward journey", () => { - it("When I return to a completed section from the hub, Then I am returned to that section summary", () => { - $(hubPage.summaryRowLink("employment-section")).click(); + it("When I return to a completed section from the hub, Then I am returned to that section summary", async () => { + await $(hubPage.summaryRowLink("employment-section")).click(); - expect(browser.getUrl()).to.contain(employmentSectionSummary.url()); + await expect(await browser.getUrl()).to.contain(employmentSectionSummary.url()); }); }); describe("Given I am completing a section with the summary turned on for the forward journey", () => { - before("Get to hub", () => { - browser.url(hubPage.url()); + before("Get to hub", async () => { + await browser.url(hubPage.url()); }); - it("When I reach the end of that section, Then I will be taken to the section summary to enable me to amend an answer", () => { - $(hubPage.summaryRowLink("accommodation-section")).click(); - $(proxyQuestionPage.noIMAnsweringForMyself()).click(); - $(proxyQuestionPage.submit()).click(); + it("When I reach the end of that section, Then I will be taken to the section summary to enable me to amend an answer", async () => { + await $(hubPage.summaryRowLink("accommodation-section")).click(); + await $(proxyQuestionPage.noIMAnsweringForMyself()).click(); + await $(proxyQuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(accommodationSectionSummary.url()); + await expect(await browser.getUrl()).to.contain(accommodationSectionSummary.url()); }); }); describe("Given I have completed a section with the summary turned on for the forward journey", () => { - before("Get to hub", () => { - browser.url(hubPage.url()); + before("Get to hub", async () => { + await browser.url(hubPage.url()); }); - it("When I return to a completed section from the hub, Then I am returned to the correct section summary", () => { - $(hubPage.summaryRowLink("accommodation-section")).click(); + it("When I return to a completed section from the hub, Then I am returned to the correct section summary", async () => { + await $(hubPage.summaryRowLink("accommodation-section")).click(); - expect(browser.getUrl()).to.contain(accommodationSectionSummary.url()); + await expect(await browser.getUrl()).to.contain(accommodationSectionSummary.url()); }); }); }); diff --git a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js index 8491d34257..324b6a596f 100644 --- a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js +++ b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js @@ -2,29 +2,29 @@ import Comparison1Page from "../../../generated_pages/skip_condition_answer_comp import Comparison2Page from "../../../generated_pages/skip_condition_answer_comparison/comparison-2.page.js"; describe("Test skip condition answer comparisons", () => { - beforeEach(() => { - browser.openQuestionnaire("test_skip_condition_answer_comparison.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_skip_condition_answer_comparison.json"); }); - it("Given we start the skip condition survey, when we enter the same answers, then the interstitial should show that the answers are the same", () => { - $(Comparison1Page.answer()).setValue(1); - $(Comparison1Page.submit()).click(); - $(Comparison2Page.answer()).setValue(1); - $(Comparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("Your second number was equal to your first number"); + it("Given we start the skip condition survey, when we enter the same answers, then the interstitial should show that the answers are the same", async () => { + await $(Comparison1Page.answer()).setValue(1); + await $(Comparison1Page.submit()).click(); + await $(Comparison2Page.answer()).setValue(1); + await $(Comparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("Your second number was equal to your first number"); }); - it("Given we start the skip condition survey, when we enter a high number then a low number, then the interstitial should show that the answers are low then high", () => { - $(Comparison1Page.answer()).setValue(3); - $(Comparison1Page.submit()).click(); - $(Comparison2Page.answer()).setValue(2); - $(Comparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("Your first answer was greater than your second number"); + it("Given we start the skip condition survey, when we enter a high number then a low number, then the interstitial should show that the answers are low then high", async () => { + await $(Comparison1Page.answer()).setValue(3); + await $(Comparison1Page.submit()).click(); + await $(Comparison2Page.answer()).setValue(2); + await $(Comparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("Your first answer was greater than your second number"); }); - it("Given we start the skip condition survey, when we enter a low number then a high number, then the interstitial should show that the answers are high then low", () => { - $(Comparison1Page.answer()).setValue(1); - $(Comparison1Page.submit()).click(); - $(Comparison2Page.answer()).setValue(2); - $(Comparison2Page.submit()).click(); - expect($("#main-content > p").getText()).to.contain("Your first answer was less than your second number"); + it("Given we start the skip condition survey, when we enter a low number then a high number, then the interstitial should show that the answers are high then low", async () => { + await $(Comparison1Page.answer()).setValue(1); + await $(Comparison1Page.submit()).click(); + await $(Comparison2Page.answer()).setValue(2); + await $(Comparison2Page.submit()).click(); + await expect(await $("#main-content > p").getText()).to.contain("Your first answer was less than your second number"); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page.spec.js b/tests/functional/spec/features/submit_page/submit_page.spec.js index 00ad357e17..1ab2c0ffe0 100644 --- a/tests/functional/spec/features/submit_page/submit_page.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page.spec.js @@ -3,21 +3,21 @@ import { SubmitPage } from "../../../base_pages/submit.page"; import { IntroductionPage } from "../../../base_pages/introduction.page"; describe("Given I launch a linear flow questionnaire without summary", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_submit_with_custom_submission_text.json"); - $(IntroductionPage.getStarted()).click(); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_submit_with_custom_submission_text.json"); + await $(IntroductionPage.getStarted()).click(); }); - it("When I complete the questionnaire, then I should be taken to the submit page without a summary", () => { - $(BreakfastPage.answer()).setValue("Bacon"); - $(BreakfastPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); - expect($(SubmitPage.summary()).isExisting()).to.be.false; + it("When I complete the questionnaire, then I should be taken to the submit page without a summary", async () => { + await $(BreakfastPage.answer()).setValue("Bacon"); + await $(BreakfastPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(await $(SubmitPage.summary()).isExisting()).to.be.false; }); - it("When I complete the questionnaire and submit the questionnaire, then the submission is successful", () => { - $(BreakfastPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); - $(SubmitPage.submit()).click(); + it("When I complete the questionnaire and submit the questionnaire, then the submission is successful", async () => { + await $(BreakfastPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await $(SubmitPage.submit()).click(); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js index a7d8247977..3e1cecedcc 100644 --- a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js @@ -10,35 +10,35 @@ import SubmitPage from "../../../generated_pages/section_summary/submit.page.js" describe("Collapsible Summary", () => { describe("Given I complete a questionnaire with collapsible summary enabled", () => { - beforeEach(() => { - browser.openQuestionnaire("test_section_summary.json"); - $(InsuranceTypePage.both()).click(); - $(InsuranceTypePage.submit()).click(); - $(InsuranceAddressPage.submit()).click(); - $(ListedPage.submit()).click(); - $(PropertyDetailsSummaryPage.submit()).click(); - $(HouseType.submit()).click(); - $(HouseholdDetailsSummaryPage.submit()).click(); - $(NumberOfPeoplePage.answer()).setValue(3); - $(NumberOfPeoplePage.submit()).click(); - $(HouseholdCountSectionSummaryPage.submit()).click(); + beforeEach(async () => { + await browser.openQuestionnaire("test_section_summary.json"); + await $(InsuranceTypePage.both()).click(); + await $(InsuranceTypePage.submit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await $(ListedPage.submit()).click(); + await $(PropertyDetailsSummaryPage.submit()).click(); + await $(HouseType.submit()).click(); + await $(HouseholdDetailsSummaryPage.submit()).click(); + await $(NumberOfPeoplePage.answer()).setValue(3); + await $(NumberOfPeoplePage.submit()).click(); + await $(HouseholdCountSectionSummaryPage.submit()).click(); }); - it("When I am on the submit page, Then a collapsed summary should be displayed with the group title and questions should not be displayed", () => { - expect($(SubmitPage.collapsibleSummary()).isDisplayed()).to.be.true; + it("When I am on the submit page, Then a collapsed summary should be displayed with the group title and questions should not be displayed", async () => { + await expect(await $(SubmitPage.collapsibleSummary()).isDisplayed()).to.be.true; - expect($(SubmitPage.collapsibleSummary()).getText()).to.contain("Property Details"); - expect($(SubmitPage.collapsibleSummary()).getText()).to.contain("House Details"); + await expect(await $(SubmitPage.collapsibleSummary()).getText()).to.contain("Property Details"); + await expect(await $(SubmitPage.collapsibleSummary()).getText()).to.contain("House Details"); - expect($(SubmitPage.insuranceAddressQuestion()).getText()).to.equal(""); - expect($(SubmitPage.numberOfPeopleQuestion()).getText()).to.equal(""); + await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).to.equal(""); + await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).to.equal(""); }); - it("When I click the Show all button, Then the summary should be expanded and questions should be displayed", () => { - $(SubmitPage.summaryShowAllButton()).click(); + it("When I click the Show all button, Then the summary should be expanded and questions should be displayed", async () => { + await $(SubmitPage.summaryShowAllButton()).click(); - expect($(SubmitPage.insuranceAddressQuestion()).getText()).to.contain("What is the address you would like to insure?"); - expect($(SubmitPage.numberOfPeopleQuestion()).getText()).to.contain("Title"); + await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).to.contain("What is the address you would like to insure?"); + await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).to.contain("Title"); }); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js index 43a2155a65..e91c30bdfd 100644 --- a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js @@ -2,16 +2,16 @@ import DessertBlockPage from "../../../generated_pages/submit_with_summary_custo import SubmitPage from "../../../generated_pages/submit_with_summary_custom_submission_text/submit.page.js"; describe("Summary Screen", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_submit_with_summary_custom_submission_text.json"); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_submit_with_summary_custom_submission_text.json"); }); - it("Given a questionnaire with a summary and custom submission content has been completed, then the correct submission content should be displayed", () => { - $(DessertBlockPage.dessert()).setValue("Crème Brûlée"); - $(DessertBlockPage.submit()).click(); - expect($(SubmitPage.heading()).getText()).to.contain("Submission title"); - expect($(SubmitPage.warning()).getText()).to.contain("Submission warning"); - expect($(SubmitPage.guidance()).getText()).to.contain("Submission guidance"); - expect($(SubmitPage.submit()).getText()).to.contain("Submission button"); + it("Given a questionnaire with a summary and custom submission content has been completed, then the correct submission content should be displayed", async () => { + await $(DessertBlockPage.dessert()).setValue("Crème Brûlée"); + await $(DessertBlockPage.submit()).click(); + await expect(await $(SubmitPage.heading()).getText()).to.contain("Submission title"); + await expect(await $(SubmitPage.warning()).getText()).to.contain("Submission warning"); + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Submission guidance"); + await expect(await $(SubmitPage.submit()).getText()).to.contain("Submission button"); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js index 103eb193be..2b0b5b087f 100644 --- a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js @@ -5,105 +5,105 @@ import RadioPage from "../../../generated_pages/submit_with_summary/radio.page.j import SubmitPage from "../../../generated_pages/submit_with_summary/submit.page.js"; describe("Submit Page with Summary", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_submit_with_summary.json"); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_submit_with_summary.json"); }); - it("Given a questionnaire with a summary has been completed when the submit page is displayed, then it should contain a summary of all answers", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed when the submit page is displayed, then it should contain a summary of all answers", async () => { + await completeAllQuestions(); - expect($(SubmitPage.radioAnswer()).getText()).to.contain("Bacon"); - expect($(SubmitPage.dessertGroupTitle()).getText()).to.contain("Dessert"); - expect($(SubmitPage.dessertAnswer()).getText()).to.contain("Crème Brûlée"); - expect($(SubmitPage.dessertConfirmationAnswer()).getText()).to.contain("Yes"); - expect($(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£1,234.00"); - expect($(SubmitPage.numbersUnitAnswer()).getText()).to.contain("123,456 km²"); - expect($(SubmitPage.numbersDecimalAnswer()).getText()).to.contain("123,456.78"); + await expect(await $(SubmitPage.radioAnswer()).getText()).to.contain("Bacon"); + await expect(await $(SubmitPage.dessertGroupTitle()).getText()).to.contain("Dessert"); + await expect(await $(SubmitPage.dessertAnswer()).getText()).to.contain("Crème Brûlée"); + await expect(await $(SubmitPage.dessertConfirmationAnswer()).getText()).to.contain("Yes"); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£1,234.00"); + await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).to.contain("123,456 km²"); + await expect(await $(SubmitPage.numbersDecimalAnswer()).getText()).to.contain("123,456.78"); }); - it("Given a questionnaire with a summary has been completed when the submit page is displayed then I should be able to submit the answers", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed when the submit page is displayed then I should be able to submit the answers", async () => { + await completeAllQuestions(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); - it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question", async () => { + await completeAllQuestions(); - $(SubmitPage.radioAnswerEdit()).click(); + await $(SubmitPage.radioAnswerEdit()).click(); - expect($(RadioPage.bacon()).isSelected()).to.be.true; + await expect(await $(RadioPage.bacon()).isSelected()).to.be.true; }); - it("Given a questionnaire with a summary has been completed and a summary page edit link is clicked, when I click previous, then it should return to the summary", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed and a summary page edit link is clicked, when I click previous, then it should return to the summary", async () => { + await completeAllQuestions(); - $(SubmitPage.radioAnswerEdit()).click(); - $(RadioPage.previous()).click(); + await $(SubmitPage.radioAnswerEdit()).click(); + await $(RadioPage.previous()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question then back to summary", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question then back to summary", async () => { + await completeAllQuestions(); - $(SubmitPage.radioAnswerEdit()).click(); - $(RadioPage.sausage()).click(); - $(RadioPage.submit()).click(); - expect($(SubmitPage.radioAnswer()).getText()).to.contain("Sausage"); + await $(SubmitPage.radioAnswerEdit()).click(); + await $(RadioPage.sausage()).click(); + await $(RadioPage.submit()).click(); + await expect(await $(SubmitPage.radioAnswer()).getText()).to.contain("Sausage"); }); - it("Given the edit link is used when a question is updated then the submit page summary should show the new answer", () => { - completeAllQuestions(); + it("Given the edit link is used when a question is updated then the submit page summary should show the new answer", async () => { + await completeAllQuestions(); - $(SubmitPage.numbersUnitAnswerEdit()).click(); - expect($(NumbersPage.unit()).isFocused()).to.be.true; - $(NumbersPage.unit()).setValue("654321"); - $(NumbersPage.submit()).click(); - expect($(SubmitPage.numbersUnitAnswer()).getText()).to.contain("654,321 km²"); + await $(SubmitPage.numbersUnitAnswerEdit()).click(); + await expect(await $(NumbersPage.unit()).isFocused()).to.be.true; + await $(NumbersPage.unit()).setValue("654321"); + await $(NumbersPage.submit()).click(); + await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).to.contain("654,321 km²"); }); - it("Given a number value of zero is entered when on the submit page then formatted 0 should be displayed on the summary", () => { - $(RadioPage.submit()).click(); - $(DessertPage.answer()).setValue("Cake"); - $(DessertPage.submit()).click(); - $(DessertConfirmationPage.yes()).click(); - $(DessertConfirmationPage.submit()).click(); - $(NumbersPage.currency()).setValue("0"); - $(NumbersPage.submit()).click(); - expect($(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£0.00"); + it("Given a number value of zero is entered when on the submit page then formatted 0 should be displayed on the summary", async () => { + await $(RadioPage.submit()).click(); + await $(DessertPage.answer()).setValue("Cake"); + await $(DessertPage.submit()).click(); + await $(DessertConfirmationPage.yes()).click(); + await $(DessertConfirmationPage.submit()).click(); + await $(NumbersPage.currency()).setValue("0"); + await $(NumbersPage.submit()).click(); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£0.00"); }); - it("Given no value is entered when on the submit page summary then the correct response should be displayed", () => { - $(RadioPage.submit()).click(); - $(DessertPage.answer()).setValue("Cake"); - $(DessertPage.submit()).click(); - $(DessertConfirmationPage.yes()).click(); - $(DessertConfirmationPage.submit()).click(); - $(NumbersPage.submit()).click(); - expect($(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("No answer provided"); + it("Given no value is entered when on the submit page summary then the correct response should be displayed", async () => { + await $(RadioPage.submit()).click(); + await $(DessertPage.answer()).setValue("Cake"); + await $(DessertPage.submit()).click(); + await $(DessertConfirmationPage.yes()).click(); + await $(DessertConfirmationPage.submit()).click(); + await $(NumbersPage.submit()).click(); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("No answer provided"); }); - it("Given a questionnaire with a summary has been completed, when submission content has not been set in the schema, then the default content should be displayed", () => { - completeAllQuestions(); + it("Given a questionnaire with a summary has been completed, when submission content has not been set in the schema, then the default content should be displayed", async () => { + await completeAllQuestions(); - expect($(SubmitPage.heading()).getText()).to.contain("Check your answers and submit"); - expect($(SubmitPage.submit()).getText()).to.contain("Submit answers"); + await expect(await $(SubmitPage.heading()).getText()).to.contain("Check your answers and submit"); + await expect(await $(SubmitPage.submit()).getText()).to.contain("Submit answers"); }); - function completeAllQuestions() { - $(RadioPage.bacon()).click(); - $(RadioPage.submit()).click(); - $(DessertPage.answer()).setValue("Crème Brûlée"); - $(DessertPage.submit()).click(); - $(DessertConfirmationPage.yes()).click(); - $(DessertConfirmationPage.submit()).click(); - $(NumbersPage.currency()).setValue("1234"); - $(NumbersPage.unit()).setValue("123456"); - $(NumbersPage.decimal()).setValue("123456.78"); - $(NumbersPage.submit()).click(); - - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + async function completeAllQuestions() { + await $(RadioPage.bacon()).click(); + await $(RadioPage.submit()).click(); + await $(DessertPage.answer()).setValue("Crème Brûlée"); + await $(DessertPage.submit()).click(); + await $(DessertConfirmationPage.yes()).click(); + await $(DessertConfirmationPage.submit()).click(); + await $(NumbersPage.currency()).setValue("1234"); + await $(NumbersPage.unit()).setValue("123456"); + await $(NumbersPage.decimal()).setValue("123456.78"); + await $(NumbersPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); } }); diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index fc808ed38a..a9598dc402 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -9,79 +9,79 @@ import NamePage from "../../generated_pages/submit_with_summary_return_to_answer describe("Summary Anchor Scrolling", () => { describe("Given I start a Test Section Summary survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_submit_with_summary_return_to_answer.json"); - $(NamePage.submit()).click(); - $(InsuranceTypePage.both()).click(); - $(InsuranceTypePage.submit()).click(); + beforeEach(async () => { + await browser.openQuestionnaire("test_submit_with_summary_return_to_answer.json"); + await $(NamePage.submit()).click(); + await $(InsuranceTypePage.both()).click(); + await $(InsuranceTypePage.submit()).click(); }); - it("When I have provided an answer and click through to the next question, Then the Previous link url shouldn't contain any anchors or reference to return_to or return_to_answer_id", () => { - expect($(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("#"); - expect($(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to"); - expect($(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to_answer_id"); + it("When I have provided an answer and click through to the next question, Then the Previous link url shouldn't contain any anchors or reference to return_to or return_to_answer_id", async () => { + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("#"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to_answer_id"); }); - it("When I reach the section summary page, Then the Change link url should contain return_to, return_to_answer_id query params", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - expect($(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( + it("When I reach the section summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( "insurance-address/?return_to=section-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2" ); }); - it("When I reach the section summary page, Then the Change link url for a concatenated answer should contain return_to, return_to_answer_id query params", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - expect($(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( + it("When I reach the section summary page, Then the Change link url for a concatenated answer should contain return_to, return_to_answer_id query params", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await expect(await $(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name" ); }); - it("When I edit an answer from the section summary page, Then the Previous link url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); - expect($(InsuranceAddressPage.previous()).getAttribute("href")).to.contain("property-details-section/#insurance-address-answer2"); + it("When I edit an answer from the section summary page, Then the Previous link url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).to.contain("property-details-section/#insurance-address-answer2"); }); - it("When I edit an answer from the section summary page and click the Previous link, Then the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); - $(InsuranceAddressPage.previous()).click(); - expect(browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); + it("When I edit an answer from the section summary page and click the Previous link, Then the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); + await $(InsuranceAddressPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); }); - it("When I edit an answer from the section summary page and click the Submit button, Then I am taken to the summary page and the browser url should contain an anchor referencing the answer id of the answer I am changing", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); - $(InsuranceAddressPage.submit()).click(); - expect(browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); + it("When I edit an answer from the section summary page and click the Submit button, Then I am taken to the summary page and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); + await $(InsuranceAddressPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); }); - it("When I am on the final summary page, Then the Change link url should contain return_to, return_to_answer_id query params", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - $(PropertyDetailsSummaryPage.submit()).click(); - $(HouseType.submit()).click(); - $(HouseholdDetailsSummaryPage.submit()).click(); - $(SubmitPage.summaryShowAllButton()).click(); - expect($(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( + it("When I am on the final summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await $(PropertyDetailsSummaryPage.submit()).click(); + await $(HouseType.submit()).click(); + await $(HouseholdDetailsSummaryPage.submit()).click(); + await $(SubmitPage.summaryShowAllButton()).click(); + await expect(await $(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( "?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2" ); }); - it("When I edit an answer from the final summary page, Then the browser url contains return_to, return_to_answer_id query params", () => { - $(InsuranceAddressPage.submit()).click(); - $(AddressDurationPage.submit()).click(); - $(PropertyDetailsSummaryPage.submit()).click(); - $(HouseType.submit()).click(); - $(HouseholdDetailsSummaryPage.submit()).click(); - $(SubmitPage.summaryShowAllButton()).click(); - $(SubmitPage.insuranceAddressAnswer2Edit()).click(); - expect(browser.getUrl()).to.contain("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); + it("When I edit an answer from the final summary page, Then the browser url contains return_to, return_to_answer_id query params", async () => { + await $(InsuranceAddressPage.submit()).click(); + await $(AddressDurationPage.submit()).click(); + await $(PropertyDetailsSummaryPage.submit()).click(); + await $(HouseType.submit()).click(); + await $(HouseholdDetailsSummaryPage.submit()).click(); + await $(SubmitPage.summaryShowAllButton()).click(); + await $(SubmitPage.insuranceAddressAnswer2Edit()).click(); + await expect(await browser.getUrl()).to.contain("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); }); }); }); diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index 5b645ba16a..6c3b60e5d6 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -5,39 +5,41 @@ import SetVolumeUnitsBlockPage from "../../generated_pages/unit_patterns/set-vol import SubmitPage from "../../generated_pages/unit_patterns/submit.page.js"; describe("Units", () => { - it("Given we do not set a language code and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", () => { - browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - $(SetLengthUnitsBlockPage.submit()).click(); - expect($(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("hours"); - expect($(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("years"); - $(SetDurationUnitsBlockPage.durationHour()).setValue(6); - $(SetDurationUnitsBlockPage.durationYear()).setValue(20); - $(SetDurationUnitsBlockPage.submit()).click(); - $(SetAreaUnitsBlockPage.submit()).click(); - $(SetVolumeUnitsBlockPage.submit()).click(); - expect($(SubmitPage.durationHour()).getText()).to.equal("6 hours"); - expect($(SubmitPage.durationYear()).getText()).to.equal("20 years"); + it("Given we do not set a language code and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { + await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); + await $(SetLengthUnitsBlockPage.submit()).click(); + await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("hours"); + await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("years"); + await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); + await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); + await $(SetDurationUnitsBlockPage.submit()).click(); + await $(SetAreaUnitsBlockPage.submit()).click(); + await $(SetVolumeUnitsBlockPage.submit()).click(); + await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 hours"); + await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 years"); }); - it("Given we set a language code for welsh and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", () => { - browser.openQuestionnaire("test_unit_patterns.json", { language: "cy" }); - $(SetLengthUnitsBlockPage.submit()).click(); - expect($(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("awr"); - expect($(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("flynedd"); - $(SetDurationUnitsBlockPage.durationHour()).setValue(6); - $(SetDurationUnitsBlockPage.durationYear()).setValue(20); - $(SetDurationUnitsBlockPage.submit()).click(); - $(SetAreaUnitsBlockPage.submit()).click(); - $(SetVolumeUnitsBlockPage.submit()).click(); - expect($(SubmitPage.durationHour()).getText()).to.equal("6 awr"); - expect($(SubmitPage.durationYear()).getText()).to.equal("20 mlynedd"); + it("Given we set a language code for welsh and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { + await browser.openQuestionnaire("test_unit_patterns.json", { language: "cy" }); + await $(SetLengthUnitsBlockPage.submit()).scrollIntoView(); + await $(SetLengthUnitsBlockPage.submit()).click(); + await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("awr"); + await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("flynedd"); + await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); + await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); + await $(SetDurationUnitsBlockPage.submit()).scrollIntoView(); + await $(SetDurationUnitsBlockPage.submit()).click(); + await $(SetAreaUnitsBlockPage.submit()).click(); + await $(SetVolumeUnitsBlockPage.submit()).click(); + await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 awr"); + await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 mlynedd"); }); - it("Given we open a questionnaire with unit labels, when the label is highlighted by the tooltip, then the long unit label should be displayed.", () => { - browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - expect($(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimeters"); - expect($(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("meters"); - expect($(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometers"); - expect($(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).to.equal("miles"); + it("Given we open a questionnaire with unit labels, when the label is highlighted by the tooltip, then the long unit label should be displayed.", async () => { + await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); + await expect(await $(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimeters"); + await expect(await $(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("meters"); + await expect(await $(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometers"); + await expect(await $(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).to.equal("miles"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js index 22bee8120f..df9ce26270 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js @@ -2,91 +2,91 @@ import DateRangePage from "../../../../generated_pages/date_validation_mm_yyyy_c import SubmitPage from "../../../../generated_pages/date_validation_mm_yyyy_combined/submit.page"; describe("Feature: Combined question level and single validation for MM-YYYY dates", () => { - before(() => { - browser.openQuestionnaire("test_date_validation_mm_yyyy_combined.json"); + before(async () => { + await browser.openQuestionnaire("test_date_validation_mm_yyyy_combined.json"); }); describe("Period Validation", () => { describe("Given I enter dates", () => { - it("When I enter a month but no year, Then I should see only a single invalid date error", () => { - $(DateRangePage.dateRangeFromYear()).setValue(2018); - - $(DateRangePage.dateRangeToMonth()).setValue(4); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); - expect($(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + it("When I enter a month but no year, Then I should see only a single invalid date error", async () => { + await $(DateRangePage.dateRangeFromYear()).setValue(2018); + + await $(DateRangePage.dateRangeToMonth()).setValue(4); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); - it("When I enter a year but no month, Then I should see only a single invalid date error", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(10); - $(DateRangePage.dateRangeFromYear()).setValue(""); + it("When I enter a year but no month, Then I should see only a single invalid date error", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(10); + await $(DateRangePage.dateRangeFromYear()).setValue(""); - $(DateRangePage.dateRangeToMonth()).setValue(4); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); - expect($(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + await $(DateRangePage.dateRangeToMonth()).setValue(4); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); - it("When I enter a year of 0, Then I should see only a single invalid date error", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(10); - $(DateRangePage.dateRangeFromYear()).setValue(0); + it("When I enter a year of 0, Then I should see only a single invalid date error", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(10); + await $(DateRangePage.dateRangeFromYear()).setValue(0); - $(DateRangePage.dateRangeToMonth()).setValue(4); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter the year in a valid format. For example, 2023."); - expect($(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + await $(DateRangePage.dateRangeToMonth()).setValue(4); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter the year in a valid format. For example, 2023."); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); - it("When I enter a single dates that are too early/late, Then I should see a single validation errors", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(10); - $(DateRangePage.dateRangeFromYear()).setValue(2016); + it("When I enter a single dates that are too early/late, Then I should see a single validation errors", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(10); + await $(DateRangePage.dateRangeFromYear()).setValue(2016); - $(DateRangePage.dateRangeToMonth()).setValue(6); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after November 2016"); - expect($(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before June 2017"); + await $(DateRangePage.dateRangeToMonth()).setValue(6); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after November 2016"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before June 2017"); }); - it("When I enter a range too large, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(12); - $(DateRangePage.dateRangeFromYear()).setValue(2016); + it("When I enter a range too large, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(12); + await $(DateRangePage.dateRangeFromYear()).setValue(2016); - $(DateRangePage.dateRangeToMonth()).setValue(5); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 months"); + await $(DateRangePage.dateRangeToMonth()).setValue(5); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 months"); }); - it("When I enter a range too small, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(12); - $(DateRangePage.dateRangeFromYear()).setValue(2016); + it("When I enter a range too small, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(12); + await $(DateRangePage.dateRangeFromYear()).setValue(2016); - $(DateRangePage.dateRangeToMonth()).setValue(1); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 months"); + await $(DateRangePage.dateRangeToMonth()).setValue(1); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 months"); }); - it("When I enter valid dates, Then I should see the summary page", () => { - $(DateRangePage.dateRangeFromMonth()).setValue(1); - $(DateRangePage.dateRangeFromYear()).setValue(2017); + it("When I enter valid dates, Then I should see the summary page", async () => { + await $(DateRangePage.dateRangeFromMonth()).setValue(1); + await $(DateRangePage.dateRangeFromYear()).setValue(2017); // Min range - $(DateRangePage.dateRangeToMonth()).setValue(3); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to March 2017"); + await $(DateRangePage.dateRangeToMonth()).setValue(3); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to March 2017"); // Max range - $(SubmitPage.dateRangeFromEdit()).click(); - $(DateRangePage.dateRangeToMonth()).setValue(4); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to April 2017"); + await $(SubmitPage.dateRangeFromEdit()).click(); + await $(DateRangePage.dateRangeToMonth()).setValue(4); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to April 2017"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js index eb0e665296..42c97a6adf 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js @@ -2,46 +2,46 @@ import DateRangePage from "../../../../generated_pages/date_validation_yyyy_comb import SubmitPage from "../../../../generated_pages/date_validation_yyyy_combined/submit.page"; describe("Feature: Combined question level and single validation for MM-YYYY dates", () => { - before(() => { - browser.openQuestionnaire("test_date_validation_yyyy_combined.json"); + before(async () => { + await browser.openQuestionnaire("test_date_validation_yyyy_combined.json"); }); describe("Period Validation", () => { describe("Given I enter dates", () => { - it("When I enter dates that are too early and too late, Then I should see two validation errors", () => { - $(DateRangePage.dateRangeFromYear()).setValue(2015); - $(DateRangePage.dateRangeToYear()).setValue(2021); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 2015"); - expect($(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 2021"); + it("When I enter dates that are too early and too late, Then I should see two validation errors", async () => { + await $(DateRangePage.dateRangeFromYear()).setValue(2015); + await $(DateRangePage.dateRangeToYear()).setValue(2021); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 2015"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 2021"); }); - it("When I enter a range too large, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromYear()).setValue(2016); - $(DateRangePage.dateRangeToYear()).setValue(2020); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 years"); + it("When I enter a range too large, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromYear()).setValue(2016); + await $(DateRangePage.dateRangeToYear()).setValue(2020); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 years"); }); - it("When I enter a range too small, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromYear()).setValue(2016); - $(DateRangePage.dateRangeToYear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 years"); + it("When I enter a range too small, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromYear()).setValue(2016); + await $(DateRangePage.dateRangeToYear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 years"); }); - it("When I enter valid dates, Then I should see the summary page", () => { - $(DateRangePage.dateRangeFromYear()).setValue(2016); + it("When I enter valid dates, Then I should see the summary page", async () => { + await $(DateRangePage.dateRangeFromYear()).setValue(2016); // Min range - $(DateRangePage.dateRangeToYear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2018"); + await $(DateRangePage.dateRangeToYear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2018"); // Max range - $(SubmitPage.dateRangeFromEdit()).click(); - $(DateRangePage.dateRangeToYear()).setValue(2019); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2019"); + await $(SubmitPage.dateRangeFromEdit()).click(); + await $(DateRangePage.dateRangeToYear()).setValue(2019); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2019"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js index 5470152fc6..704d551316 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js @@ -2,68 +2,68 @@ import DateRangePage from "../../../../generated_pages/date_validation_combined/ import SubmitPage from "../../../../generated_pages/date_validation_combined/submit.page"; describe("Feature: Combined question level and single validation for dates", () => { - before(() => { - browser.openQuestionnaire("test_date_validation_combined.json"); + before(async () => { + await browser.openQuestionnaire("test_date_validation_combined.json"); }); describe("Period Validation", () => { describe("Given I enter dates", () => { - it("When I enter a single dates that are too early/late, Then I should see a single validation errors", () => { - $(DateRangePage.dateRangeFromday()).setValue(12); - $(DateRangePage.dateRangeFrommonth()).setValue(12); - $(DateRangePage.dateRangeFromyear()).setValue(2016); + it("When I enter a single dates that are too early/late, Then I should see a single validation errors", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(12); + await $(DateRangePage.dateRangeFrommonth()).setValue(12); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(22); - $(DateRangePage.dateRangeTomonth()).setValue(2); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); - expect($(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 22 February 2017"); + await $(DateRangePage.dateRangeToday()).setValue(22); + await $(DateRangePage.dateRangeTomonth()).setValue(2); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 22 February 2017"); }); - it("When I enter a range too large, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(13); - $(DateRangePage.dateRangeFrommonth()).setValue(12); - $(DateRangePage.dateRangeFromyear()).setValue(2016); + it("When I enter a range too large, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(13); + await $(DateRangePage.dateRangeFrommonth()).setValue(12); + await $(DateRangePage.dateRangeFromyear()).setValue(2016); - $(DateRangePage.dateRangeToday()).setValue(21); - $(DateRangePage.dateRangeTomonth()).setValue(2); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 50 days"); + await $(DateRangePage.dateRangeToday()).setValue(21); + await $(DateRangePage.dateRangeTomonth()).setValue(2); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 50 days"); }); - it("When I enter a range too small, Then I should see a range validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2017); + it("When I enter a range too small, Then I should see a range validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2017); - $(DateRangePage.dateRangeToday()).setValue(10); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 10 days"); + await $(DateRangePage.dateRangeToday()).setValue(10); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 10 days"); }); - it("When I enter valid dates, Then I should see the summary page", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2017); + it("When I enter valid dates, Then I should see the summary page", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2017); // Min range - $(DateRangePage.dateRangeToday()).setValue(11); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 11 January 2017"); + await $(DateRangePage.dateRangeToday()).setValue(11); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 11 January 2017"); // Max range - $(SubmitPage.dateRangeFromEdit()).click(); - $(DateRangePage.dateRangeToday()).setValue(20); - $(DateRangePage.dateRangeTomonth()).setValue(2); - $(DateRangePage.dateRangeToyear()).setValue(2017); - $(DateRangePage.submit()).click(); - expect($(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 20 February 2017"); + await $(SubmitPage.dateRangeFromEdit()).click(); + await $(DateRangePage.dateRangeToday()).setValue(20); + await $(DateRangePage.dateRangeTomonth()).setValue(2); + await $(DateRangePage.dateRangeToyear()).setValue(2017); + await $(DateRangePage.submit()).click(); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 20 February 2017"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-range.spec.js b/tests/functional/spec/features/validation/date_validation/date-range.spec.js index b3cb191c77..ed7b385728 100644 --- a/tests/functional/spec/features/validation/date_validation/date-range.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-range.spec.js @@ -2,94 +2,94 @@ import DateRangePage from "../../../../generated_pages/date_validation_range/dat import SubmitPage from "../../../../generated_pages/date_validation_range/submit.page"; describe("Feature: Question level validation for date ranges", () => { - beforeEach(() => { - browser.openQuestionnaire("test_date_validation_range.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_date_validation_range.json"); }); describe("Period Validation", () => { describe("Given I enter a date period greater than the max period limit", () => { - it("When I continue, Then I should see a period validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should see a period validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(3); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 1 month, 20 days"); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(3); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 1 month, 20 days"); }); }); describe("Given I enter a date period less than the min period limit", () => { - it("When I continue, Then I should see a period validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should see a period validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 23 days"); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 23 days"); }); }); describe("Given I enter a date period within the set period limits", () => { - it("When I continue, Then I should be able to reach the summary", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should be able to reach the summary", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(2); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(2); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); }); describe("Date Range Validation", () => { describe('Given I enter a "to date" which is earlier than the "from date"', () => { - it("When I continue, Then I should see a validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(2); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should see a validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(2); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); }); }); describe('Given I enter matching dates for the "from" and "to" dates', () => { - it("When I continue, Then I should see a validation error", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should see a validation error", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(1); - $(DateRangePage.dateRangeTomonth()).setValue(1); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect($(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await $(DateRangePage.dateRangeToday()).setValue(1); + await $(DateRangePage.dateRangeTomonth()).setValue(1); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); }); }); describe("Given I enter a valid date range", () => { - it("When I continue, Then I should be able to reach the summary", () => { - $(DateRangePage.dateRangeFromday()).setValue(1); - $(DateRangePage.dateRangeFrommonth()).setValue(1); - $(DateRangePage.dateRangeFromyear()).setValue(2018); + it("When I continue, Then I should be able to reach the summary", async () => { + await $(DateRangePage.dateRangeFromday()).setValue(1); + await $(DateRangePage.dateRangeFrommonth()).setValue(1); + await $(DateRangePage.dateRangeFromyear()).setValue(2018); - $(DateRangePage.dateRangeToday()).setValue(3); - $(DateRangePage.dateRangeTomonth()).setValue(2); - $(DateRangePage.dateRangeToyear()).setValue(2018); - $(DateRangePage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(DateRangePage.dateRangeToday()).setValue(3); + await $(DateRangePage.dateRangeTomonth()).setValue(2); + await $(DateRangePage.dateRangeToyear()).setValue(2018); + await $(DateRangePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-single.spec.js b/tests/functional/spec/features/validation/date_validation/date-single.spec.js index 7c66aab5bd..dc95539046 100644 --- a/tests/functional/spec/features/validation/date_validation/date-single.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-single.spec.js @@ -3,75 +3,75 @@ import DatePeriodPage from "../../../../generated_pages/date_validation_single/d import SubmitPage from "../../../../generated_pages/date_validation_single/submit.page"; describe("Feature: Validation for single date periods", () => { - beforeEach(() => { - browser.openQuestionnaire("test_date_validation_single.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_date_validation_single.json"); completeFirstDatePage(); }); describe("Given I enter a date before the minimum offset meta date", () => { - it("When I continue, Then I should see a period validation error", () => { - $(DatePeriodPage.dateRangeFromday()).setValue(13); - $(DatePeriodPage.dateRangeFrommonth()).setValue(2); - $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - $(DatePeriodPage.submit()).click(); + it("When I continue, Then I should see a period validation error", async () => { + await $(DatePeriodPage.dateRangeFromday()).setValue(13); + await $(DatePeriodPage.dateRangeFrommonth()).setValue(2); + await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); + await $(DatePeriodPage.submit()).click(); - $(DatePeriodPage.dateRangeToday()).setValue(3); - $(DatePeriodPage.dateRangeTomonth()).setValue(3); - $(DatePeriodPage.dateRangeToyear()).setValue(2018); - $(DatePeriodPage.submit()).click(); - expect($(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); + await $(DatePeriodPage.dateRangeToday()).setValue(3); + await $(DatePeriodPage.dateRangeTomonth()).setValue(3); + await $(DatePeriodPage.dateRangeToyear()).setValue(2018); + await $(DatePeriodPage.submit()).click(); + await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); }); }); describe("Given I enter a date after the maximum offset value date", () => { - it("When I continue, Then I should see a period validation error", () => { - $(DatePeriodPage.dateRangeFromday()).setValue(13); - $(DatePeriodPage.dateRangeFrommonth()).setValue(7); - $(DatePeriodPage.dateRangeFromyear()).setValue(2017); - $(DatePeriodPage.submit()).click(); + it("When I continue, Then I should see a period validation error", async () => { + await $(DatePeriodPage.dateRangeFromday()).setValue(13); + await $(DatePeriodPage.dateRangeFrommonth()).setValue(7); + await $(DatePeriodPage.dateRangeFromyear()).setValue(2017); + await $(DatePeriodPage.submit()).click(); - $(DatePeriodPage.dateRangeToday()).setValue(3); - $(DatePeriodPage.dateRangeTomonth()).setValue(3); - $(DatePeriodPage.dateRangeToyear()).setValue(2018); - $(DatePeriodPage.submit()).click(); - expect($(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date before 2 July 2017"); + await $(DatePeriodPage.dateRangeToday()).setValue(3); + await $(DatePeriodPage.dateRangeTomonth()).setValue(3); + await $(DatePeriodPage.dateRangeToyear()).setValue(2018); + await $(DatePeriodPage.submit()).click(); + await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date before 2 July 2017"); }); }); describe("Given I enter a date before the minimum offset answer id date", () => { - it("When I continue, Then I should see a period validation error", () => { - $(DatePeriodPage.dateRangeFromday()).setValue(13); - $(DatePeriodPage.dateRangeFrommonth()).setValue(11); - $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - $(DatePeriodPage.submit()).click(); + it("When I continue, Then I should see a period validation error", async () => { + await $(DatePeriodPage.dateRangeFromday()).setValue(13); + await $(DatePeriodPage.dateRangeFrommonth()).setValue(11); + await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); + await $(DatePeriodPage.submit()).click(); - $(DatePeriodPage.dateRangeToday()).setValue(13); - $(DatePeriodPage.dateRangeTomonth()).setValue(1); - $(DatePeriodPage.dateRangeToyear()).setValue(2018); - $(DatePeriodPage.submit()).click(); - expect($(DatePeriodPage.errorNumber(2)).getText()).to.contain("Enter a date after 10 February 2018"); + await $(DatePeriodPage.dateRangeToday()).setValue(13); + await $(DatePeriodPage.dateRangeTomonth()).setValue(1); + await $(DatePeriodPage.dateRangeToyear()).setValue(2018); + await $(DatePeriodPage.submit()).click(); + await expect(await $(DatePeriodPage.errorNumber(2)).getText()).to.contain("Enter a date after 10 February 2018"); }); }); describe("Given I enter a date in between the minimum offset meta date and the maximum offset value date", () => { - it("When I continue, Then I should be able to reach the summary", () => { - $(DatePeriodPage.dateRangeFromday()).setValue(13); - $(DatePeriodPage.dateRangeFrommonth()).setValue(12); - $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - $(DatePeriodPage.submit()).click(); + it("When I continue, Then I should be able to reach the summary", async () => { + await $(DatePeriodPage.dateRangeFromday()).setValue(13); + await $(DatePeriodPage.dateRangeFrommonth()).setValue(12); + await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); + await $(DatePeriodPage.submit()).click(); - $(DatePeriodPage.dateRangeToday()).setValue(11); - $(DatePeriodPage.dateRangeTomonth()).setValue(2); - $(DatePeriodPage.dateRangeToyear()).setValue(2018); - $(DatePeriodPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(DatePeriodPage.dateRangeToday()).setValue(11); + await $(DatePeriodPage.dateRangeTomonth()).setValue(2); + await $(DatePeriodPage.dateRangeToyear()).setValue(2018); + await $(DatePeriodPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); - function completeFirstDatePage() { - $(DatePage.day()).setValue(1); - $(DatePage.month()).setValue(1); - $(DatePage.year()).setValue(2018); - $(DatePage.submit()).click(); + async function completeFirstDatePage() { + await $(DatePage.day()).setValue(1); + await $(DatePage.month()).setValue(1); + await $(DatePage.year()).setValue(2018); + await $(DatePage.submit()).click(); } }); diff --git a/tests/functional/spec/features/validation/sum/equal.spec.js b/tests/functional/spec/features/validation/sum/equal.spec.js index cda66d9cc7..559871e0bf 100644 --- a/tests/functional/spec/features/validation/sum/equal.spec.js +++ b/tests/functional/spec/features/validation/sum/equal.spec.js @@ -2,69 +2,69 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; -const answerAndSubmitBreakdownQuestion = (breakdown1, breakdown2, breakdown3, breakdown4) => { - $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); - $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); - $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); - $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); - $(BreakdownAnswerPage.submit()).click(); +const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { + await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); + await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); + await $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); + await $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); + await $(BreakdownAnswerPage.submit()).click(); }; describe("Feature: Sum of grouped answers equal to validation against total ", () => { - beforeEach(() => { - browser.openQuestionnaire("test_validation_sum_against_total_equal.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_equal.json"); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I completed a grouped answer validation question and I am on the summary", () => { - it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question with valid answers before I can get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question with valid answers before I can get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - $(SubmitPage.totalAnswerEdit()).click(); - $(TotalAnswerPage.total()).setValue("15"); - $(TotalAnswerPage.submit()).click(); + await $(SubmitPage.totalAnswerEdit()).click(); + await $(TotalAnswerPage.total()).setValue("15"); + await $(TotalAnswerPage.submit()).click(); - browser.url(SubmitPage.url()); - expect(browser.getUrl()).to.contain(BreakdownAnswerPage.pageName); + await browser.url(SubmitPage.url()); + await expect(await browser.getUrl()).to.contain(BreakdownAnswerPage.pageName); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); - answerAndSubmitBreakdownQuestion("6", "3", "3", "3"); + await answerAndSubmitBreakdownQuestion("6", "3", "3", "3"); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 5 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("5", "", "", ""); + it("When I continue and enter 5 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await answerAndSubmitBreakdownQuestion("5", "", "", ""); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js index aafc0ad04c..bfd4f73c26 100644 --- a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js @@ -3,49 +3,49 @@ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_agai import SubmitPage from "../../../../generated_pages/validation_sum_against_total_multiple/submit.page"; describe("Feature: Sum validation (Multi Rule Equals)", () => { - beforeEach(() => { - browser.openQuestionnaire("test_validation_sum_against_total_multiple.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_multiple.json"); }); describe("Given I start a grouped answer with multi rule validation survey and enter 10 into the total", () => { - it("When I continue and enter nothing, all zeros or 10 at breakdown level, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("10"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter nothing, all zeros or 10 at breakdown level, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("10"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.previous()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("0"); - $(BreakdownAnswerPage.breakdown2()).setValue("0"); - $(BreakdownAnswerPage.breakdown3()).setValue("0"); - $(BreakdownAnswerPage.breakdown4()).setValue("0"); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.previous()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("0"); + await $(BreakdownAnswerPage.breakdown2()).setValue("0"); + await $(BreakdownAnswerPage.breakdown3()).setValue("0"); + await $(BreakdownAnswerPage.breakdown4()).setValue("0"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - $(SubmitPage.previous()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("1"); - $(BreakdownAnswerPage.breakdown2()).setValue("2"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("4"); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.previous()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("1"); + await $(BreakdownAnswerPage.breakdown2()).setValue("2"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("4"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer with multi rule validation survey and enter 10 into the total", () => { - it("When I continue and enter less between 1 - 9 or greater than 10, Then it should error", () => { - $(TotalAnswerPage.total()).setValue("10"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("1"); - $(BreakdownAnswerPage.submit()).click(); + it("When I continue and enter less between 1 - 9 or greater than 10, Then it should error", async () => { + await $(TotalAnswerPage.total()).setValue("10"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("1"); + await $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); - $(BreakdownAnswerPage.breakdown2()).setValue("2"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("5"); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); + await $(BreakdownAnswerPage.breakdown2()).setValue("2"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("5"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js index 331718baa0..18c6ac2ce2 100644 --- a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js @@ -3,72 +3,72 @@ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_agai import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/submit.page"; describe("Feature: Sum of grouped answers validation (equal or less than) against total", () => { - beforeEach(() => { - browser.openQuestionnaire("test_validation_sum_against_total_equal_or_less_than.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_equal_or_less_than.json"); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("2"); - $(BreakdownAnswerPage.breakdown2()).setValue("2"); - $(BreakdownAnswerPage.breakdown3()).setValue("2"); - $(BreakdownAnswerPage.breakdown4()).setValue("2"); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("2"); + await $(BreakdownAnswerPage.breakdown2()).setValue("2"); + await $(BreakdownAnswerPage.breakdown3()).setValue("2"); + await $(BreakdownAnswerPage.breakdown4()).setValue("2"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("3"); - $(BreakdownAnswerPage.breakdown2()).setValue("3"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("3"); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("3"); + await $(BreakdownAnswerPage.breakdown2()).setValue("3"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("3"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("4"); - $(BreakdownAnswerPage.breakdown2()).setValue(""); - $(BreakdownAnswerPage.breakdown3()).setValue(""); - $(BreakdownAnswerPage.breakdown4()).setValue(""); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("4"); + await $(BreakdownAnswerPage.breakdown2()).setValue(""); + await $(BreakdownAnswerPage.breakdown3()).setValue(""); + await $(BreakdownAnswerPage.breakdown4()).setValue(""); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 4 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("4"); - $(BreakdownAnswerPage.breakdown2()).setValue("4"); - $(BreakdownAnswerPage.breakdown3()).setValue("4"); - $(BreakdownAnswerPage.breakdown4()).setValue("4"); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 12"); + it("When I continue and enter 4 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("4"); + await $(BreakdownAnswerPage.breakdown2()).setValue("4"); + await $(BreakdownAnswerPage.breakdown3()).setValue("4"); + await $(BreakdownAnswerPage.breakdown4()).setValue("4"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 12"); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("3"); - $(BreakdownAnswerPage.breakdown2()).setValue("3"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("3"); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 5"); + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("3"); + await $(BreakdownAnswerPage.breakdown2()).setValue("3"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("3"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 5"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js index 903f567557..4ba5dc0826 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js @@ -12,115 +12,115 @@ import ThankYouPage from "../../../../base_pages/thank-you.page"; const companyOverviewSectionID = "company-overview-section"; const breakdownSectionId = "breakdown-section"; -const answerAndSubmitTurnoverBreakdownQuestion = (breakdown1, breakdown2, breakdown3) => { - $(TurnoverBreakdownPage.turnoverBreakdown1()).setValue(breakdown1); - $(TurnoverBreakdownPage.turnoverBreakdown2()).setValue(breakdown2); - $(TurnoverBreakdownPage.turnoverBreakdown3()).setValue(breakdown3); - $(TurnoverBreakdownPage.submit()).click(); +const answerAndSubmitTurnoverBreakdownQuestion = async (breakdown1, breakdown2, breakdown3) => { + await $(TurnoverBreakdownPage.turnoverBreakdown1()).setValue(breakdown1); + await $(TurnoverBreakdownPage.turnoverBreakdown2()).setValue(breakdown2); + await $(TurnoverBreakdownPage.turnoverBreakdown3()).setValue(breakdown3); + await $(TurnoverBreakdownPage.submit()).click(); }; -const answerAndSubmitEmployeeBreakdownQuestion = (breakdown1, breakdown2) => { - $(EmployeesBreakdownPage.employeesBreakdown1()).setValue(breakdown1); - $(EmployeesBreakdownPage.employeesBreakdown2()).setValue(breakdown2); - $(EmployeesBreakdownPage.submit()).click(); +const answerAndSubmitEmployeeBreakdownQuestion = async (breakdown1, breakdown2) => { + await $(EmployeesBreakdownPage.employeesBreakdown1()).setValue(breakdown1); + await $(EmployeesBreakdownPage.employeesBreakdown2()).setValue(breakdown2); + await $(EmployeesBreakdownPage.submit()).click(); }; -const answerAndSubmitTotalTurnoverQuestion = (total) => { - $(TotalTurnoverPage.totalTurnover()).setValue(total); - $(TotalTurnoverPage.submit()).click(); +const answerAndSubmitTotalTurnoverQuestion = async (total) => { + await $(TotalTurnoverPage.totalTurnover()).setValue(total); + await $(TotalTurnoverPage.submit()).click(); }; -const answerAndSubmitTotalEmployeesQuestion = (total) => { - $(TotalEmployeesPage.totalEmployees()).setValue(total); - $(TotalEmployeesPage.submit()).click(); +const answerAndSubmitTotalEmployeesQuestion = async (total) => { + await $(TotalEmployeesPage.totalEmployees()).setValue(total); + await $(TotalEmployeesPage.submit()).click(); }; describe("Feature: Validation - Sum of grouped answers to equal total (Total in separate section)", () => { describe("Given I start a grouped answer validation with dependent sections and complete the total turnover and total employees questions", () => { - before(() => { - browser.openQuestionnaire("test_validation_sum_against_total_hub_with_dependent_section.json"); - answerAndSubmitTotalTurnoverQuestion(1000); - answerAndSubmitTotalEmployeesQuestion(10); - $(CompanySectionSummary.submit()).click(); + before(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_hub_with_dependent_section.json"); + await answerAndSubmitTotalTurnoverQuestion(1000); + await answerAndSubmitTotalEmployeesQuestion(10); + await $(CompanySectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(companyOverviewSectionID)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(companyOverviewSectionID)).getText()).to.equal("Completed"); }); - it("When I am on the hub, Then the breakdown section should be marked as 'Not Started'", () => { - expect($(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Not started"); + it("When I am on the hub, Then the breakdown section should be marked as 'Not Started'", async () => { + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Not started"); }); - it("When I start the breakdown section and enter an answer that is not equal to the total for the turnover question, Then I should see a validation error", () => { - $(HubPage.submit()).click(); - answerAndSubmitTurnoverBreakdownQuestion(1000, 250, 250); + it("When I start the breakdown section and enter an answer that is not equal to the total for the turnover question, Then I should see a validation error", async () => { + await $(HubPage.submit()).click(); + await answerAndSubmitTurnoverBreakdownQuestion(1000, 250, 250); - expect($(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); + await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); }); - it("When I start the breakdown section and enter answers that are equal the total, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", () => { - answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); - answerAndSubmitEmployeeBreakdownQuestion(5, 5); + it("When I start the breakdown section and enter answers that are equal the total, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { + await answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); + await answerAndSubmitEmployeeBreakdownQuestion(5, 5); - expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - $(BreakdownSectionSummary.submit()).click(); + await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); }); describe("Given I start a grouped answer validation with dependent sections and complete the overview and breakdown sections", () => { - before(() => { - browser.openQuestionnaire("test_validation_sum_against_total_hub_with_dependent_section.json"); + before(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_hub_with_dependent_section.json"); // Complete overview section - answerAndSubmitTotalTurnoverQuestion(1000); - answerAndSubmitTotalEmployeesQuestion(10); - $(CompanySectionSummary.submit()).click(); + await answerAndSubmitTotalTurnoverQuestion(1000); + await answerAndSubmitTotalEmployeesQuestion(10); + await $(CompanySectionSummary.submit()).click(); // Complete breakdown section - $(HubPage.submit()).click(); - answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); - answerAndSubmitEmployeeBreakdownQuestion(5, 5); - $(BreakdownSectionSummary.submit()).click(); + await $(HubPage.submit()).click(); + await answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); + await answerAndSubmitEmployeeBreakdownQuestion(5, 5); + await $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); - it("When I change my answer for the total turnover question, Then the breakdown section should be marked as 'Partially completed'", () => { - $(HubPage.summaryRowLink(companyOverviewSectionID)).click(); - $(CompanySectionSummary.totalTurnoverAnswerEdit()).click(); + it("When I change my answer for the total turnover question, Then the breakdown section should be marked as 'Partially completed'", async () => { + await $(HubPage.summaryRowLink(companyOverviewSectionID)).click(); + await $(CompanySectionSummary.totalTurnoverAnswerEdit()).click(); - answerAndSubmitTotalTurnoverQuestion(1500); - $(CompanySectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Partially completed"); + await answerAndSubmitTotalTurnoverQuestion(1500); + await $(CompanySectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Partially completed"); }); - it("When I click 'Continue with section' on the breakdown section, Then I should be taken to the turnover breakdown question and my previous answers should be prefilled", () => { - $(HubPage.summaryRowLink(breakdownSectionId)).click(); + it("When I click 'Continue with section' on the breakdown section, Then I should be taken to the turnover breakdown question and my previous answers should be prefilled", async () => { + await $(HubPage.summaryRowLink(breakdownSectionId)).click(); - expect($(TurnoverBreakdownPage.turnoverBreakdown1()).getValue()).to.equal("500.00"); - expect($(TurnoverBreakdownPage.turnoverBreakdown2()).getValue()).to.equal("250.00"); - expect($(TurnoverBreakdownPage.turnoverBreakdown3()).getValue()).to.equal("250.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown1()).getValue()).to.equal("500.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown2()).getValue()).to.equal("250.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown3()).getValue()).to.equal("250.00"); }); - it("When I submit the turnover breakdown question with no changes, Then I should see a validation error", () => { - $(TurnoverBreakdownPage.submit()).click(); + it("When I submit the turnover breakdown question with no changes, Then I should see a validation error", async () => { + await $(TurnoverBreakdownPage.submit()).click(); - expect($(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,500.00"); + await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,500.00"); }); - it("When I update my answers to equal the new total turnover, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", () => { - answerAndSubmitTurnoverBreakdownQuestion(500, 500, 500); + it("When I update my answers to equal the new total turnover, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { + await answerAndSubmitTurnoverBreakdownQuestion(500, 500, 500); - expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await $(BreakdownSectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); - it("When I submit the questionnaire, Then I should see the thank you page", () => { - $(HubPage.submit()).click(); - - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + it("When I submit the questionnaire, Then I should see the thank you page", async () => { + await $(HubPage.submit()).scrollIntoView(); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index ab70c37be4..22934a4ced 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -20,181 +20,182 @@ const repeatingSectionId = (repeatIndex) => { return `breakdown-section-${repeatIndex}`; }; -const addPersonToHousehold = (firstName, lastName) => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue(firstName); - $(ListCollectorAddPage.lastName()).setValue(lastName); - $(ListCollectorAddPage.submit()).click(); +const addPersonToHousehold = async (firstName, lastName) => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue(firstName); + await $(ListCollectorAddPage.lastName()).setValue(lastName); + await $(ListCollectorAddPage.submit()).click(); }; -const answerAndSubmitTotalSpendingQuestion = (total) => { - $(TotalSpendingPage.totalSpending()).setValue(total); - $(TotalSpendingPage.submit()).click(); +const answerAndSubmitTotalSpendingQuestion = async (total) => { + await $(TotalSpendingPage.totalSpending()).setValue(total); + await $(TotalSpendingPage.submit()).click(); }; -const answerAndSubmitEntertainmentSpendingQuestion = (total) => { - $(EntertainmentSpendingPage.entertainmentSpending()).setValue(total); - $(EntertainmentSpendingPage.submit()).click(); +const answerAndSubmitEntertainmentSpendingQuestion = async (total) => { + await $(EntertainmentSpendingPage.entertainmentSpending()).setValue(total); + await $(EntertainmentSpendingPage.submit()).click(); }; -const answerAndSubmitSpendingBreakdownQuestion = (breakdown1, breakdown2, breakdown3) => { - $(SpendingBreakdownPage.spendingBreakdown1()).setValue(breakdown1); - $(SpendingBreakdownPage.spendingBreakdown2()).setValue(breakdown2); - $(SpendingBreakdownPage.spendingBreakdown3()).setValue(breakdown3); - $(SpendingBreakdownPage.submit()).click(); +const answerAndSubmitSpendingBreakdownQuestion = async (breakdown1, breakdown2, breakdown3) => { + await $(SpendingBreakdownPage.spendingBreakdown1()).setValue(breakdown1); + await $(SpendingBreakdownPage.spendingBreakdown2()).setValue(breakdown2); + await $(SpendingBreakdownPage.spendingBreakdown3()).setValue(breakdown3); + await $(SpendingBreakdownPage.submit()).click(); }; -const assertSpendingBreakdownAnswer = (breakdown1, breakdown2, breakdown3) => { - expect($(SpendingBreakdownPage.spendingBreakdown1()).getValue()).to.equal(breakdown1); - expect($(SpendingBreakdownPage.spendingBreakdown2()).getValue()).to.equal(breakdown2); - expect($(SpendingBreakdownPage.spendingBreakdown3()).getValue()).to.equal(breakdown3); +const assertSpendingBreakdownAnswer = async (breakdown1, breakdown2, breakdown3) => { + await expect(await $(SpendingBreakdownPage.spendingBreakdown1()).getValue()).to.equal(breakdown1); + await expect(await $(SpendingBreakdownPage.spendingBreakdown2()).getValue()).to.equal(breakdown2); + await expect(await $(SpendingBreakdownPage.spendingBreakdown3()).getValue()).to.equal(breakdown3); }; -const answerAndSubmitEntertainmentBreakdownQuestion = (breakdown1, breakdown2, breakdown3) => { - $(EntertainmentBreakdownPage.secondSpendingBreakdown1()).setValue(breakdown1); - $(EntertainmentBreakdownPage.secondSpendingBreakdown2()).setValue(breakdown2); - $(EntertainmentBreakdownPage.secondSpendingBreakdown3()).setValue(breakdown3); - $(EntertainmentBreakdownPage.submit()).click(); +const answerAndSubmitEntertainmentBreakdownQuestion = async (breakdown1, breakdown2, breakdown3) => { + await $(EntertainmentBreakdownPage.secondSpendingBreakdown1()).setValue(breakdown1); + await $(EntertainmentBreakdownPage.secondSpendingBreakdown2()).setValue(breakdown2); + await $(EntertainmentBreakdownPage.secondSpendingBreakdown3()).setValue(breakdown3); + await $(EntertainmentBreakdownPage.submit()).click(); }; -const assertRepeatingSectionOnChange = (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { - it(`When I click 'Continue with section' on repeating section ${repeatIndex}, Then I should be taken to the spending breakdown question and my previous answers should be prefilled`, () => { - $(HubPage.summaryRowLink(repeatingSectionId(repeatIndex))).click(); +const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { + it(`When I click 'Continue with section' on repeating section ${repeatIndex}, Then I should be taken to the spending breakdown question and my previous answers should be prefilled`, async () => { + await $(HubPage.summaryRowLink(repeatingSectionId(repeatIndex))).click(); - assertSpendingBreakdownAnswer(currentBreakdown1, currentBreakdown2, currentBreakdown3); + await assertSpendingBreakdownAnswer(currentBreakdown1, currentBreakdown2, currentBreakdown3); }); - it("When I submit the spending breakdown question with no changes, Then I should see a validation error", () => { - $(SpendingBreakdownPage.submit()).click(); + it("When I submit the spending breakdown question with no changes, Then I should see a validation error", async () => { + await $(SpendingBreakdownPage.submit()).click(); - expect($(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain(`Enter answers that add up to £${newTotal}`); + await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain(`Enter answers that add up to £${newTotal}`); }); - it("When I update my answers to equal the new total spending, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", () => { - answerAndSubmitSpendingBreakdownQuestion(newTotal, 0, 0); + it("When I update my answers to equal the new total spending, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { + await answerAndSubmitSpendingBreakdownQuestion(newTotal, 0, 0); - expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).to.equal("Completed"); + await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await $(BreakdownSectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).to.equal("Completed"); }); }; describe("Feature: Validation - Sum of grouped answers to equal total (Repeating section) (Total in separate section)", () => { describe("Given I start a repeating grouped answer validation with dependent sections and add 2 householders and complete the household overview section", () => { - before(() => { - browser.openQuestionnaire("test_validation_sum_against_total_repeating_with_dependent_section.json"); + before(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_repeating_with_dependent_section.json"); // Add 2 householders - addPersonToHousehold("John", "Doe"); - addPersonToHousehold("Jane", "Doe"); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorSummaryPage.submit()).click(); + await addPersonToHousehold("John", "Doe"); + await addPersonToHousehold("Jane", "Doe"); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).scrollIntoView(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorSummaryPage.submit()).click(); // Complete household overview section - answerAndSubmitTotalSpendingQuestion(1000); - answerAndSubmitEntertainmentSpendingQuestion(500); - $(HouseholdOverviewSectionSummary.submit()).click(); + await answerAndSubmitTotalSpendingQuestion(1000); + await answerAndSubmitEntertainmentSpendingQuestion(500); + await $(HouseholdOverviewSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(householderSectionId)).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowState(householdOverviewSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(householderSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(householdOverviewSectionId)).getText()).to.equal("Completed"); }); - it("When I am on the hub, Then the two repeating breakdown sections should be marked as 'Not Started'", () => { - expect($(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Not started"); - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Not started"); + it("When I am on the hub, Then the two repeating breakdown sections should be marked as 'Not Started'", async () => { + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Not started"); }); - it("When I start a repeating section and don't skip the calculated question, and enter an answer that is not equal to the total for the spending question, Then I should see a validation error", () => { - $(HubPage.summaryRowLink(repeatingSectionId(1))).click(); - $(BreakdownDrivingPage.yes()).click(); - $(BreakdownDrivingPage.submit()).click(); + it("When I start a repeating section and don't skip the calculated question, and enter an answer that is not equal to the total for the spending question, Then I should see a validation error", async () => { + await $(HubPage.summaryRowLink(repeatingSectionId(1))).click(); + await $(BreakdownDrivingPage.yes()).click(); + await $(BreakdownDrivingPage.submit()).click(); - answerAndSubmitSpendingBreakdownQuestion(500, 500, 500); + await answerAndSubmitSpendingBreakdownQuestion(500, 500, 500); - expect($(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); + await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); }); - it("When I enter an answer that is equal to the total for the spending question, Then I should be able to get to the section summary and the repeating section should be marked as 'Completed'", () => { - answerAndSubmitSpendingBreakdownQuestion(500, 250, 250); - answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); + it("When I enter an answer that is equal to the total for the spending question, Then I should be able to get to the section summary and the repeating section should be marked as 'Completed'", async () => { + await answerAndSubmitSpendingBreakdownQuestion(500, 250, 250); + await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); - expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - $(BreakdownSectionSummary.submit()).click(); + await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); }); - it("When I start another repeating section and answer 'No' to the driving question, Then I should not have to answer the breakdown question and the section is marked as 'Completed'", () => { - $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); - $(BreakdownDrivingPage.no()).click(); - $(BreakdownDrivingPage.submit()).click(); + it("When I start another repeating section and answer 'No' to the driving question, Then I should not have to answer the breakdown question and the section is marked as 'Completed'", async () => { + await $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); + await $(BreakdownDrivingPage.no()).click(); + await $(BreakdownDrivingPage.submit()).click(); - expect(browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - $(BreakdownSectionSummary.submit()).click(); + await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); - it("When I change my answer for the total spending question, Then the first repeating section should be marked as 'Partially completed' and section repeating section should stay as 'Completed'", () => { - $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); - $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); + it("When I change my answer for the total spending question, Then the first repeating section should be marked as 'Partially completed' and section repeating section should stay as 'Completed'", async () => { + await $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); + await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); - answerAndSubmitTotalSpendingQuestion(1500); - $(HouseholdOverviewSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); + await answerAndSubmitTotalSpendingQuestion(1500); + await $(HouseholdOverviewSectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); // The 2nd repeating section skipped the breakdown question, therefore progress should updated for sections that have // calculated questions on the path. - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); assertRepeatingSectionOnChange(1, "500.00", "250.00", "250.00", "1,500.00"); - it("When I change my answer to the driving question to 'Yes' for the 2nd repeating section, Then I am able to answer the breakdown question and complete the section", () => { - $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); - $(BreakdownSectionSummary.breakdownDrivingAnswerEdit()).click(); - $(BreakdownDrivingPage.yes()).click(); - $(BreakdownDrivingPage.submit()).click(); + it("When I change my answer to the driving question to 'Yes' for the 2nd repeating section, Then I am able to answer the breakdown question and complete the section", async () => { + await $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); + await $(BreakdownSectionSummary.breakdownDrivingAnswerEdit()).click(); + await $(BreakdownDrivingPage.yes()).click(); + await $(BreakdownDrivingPage.submit()).click(); - answerAndSubmitSpendingBreakdownQuestion(1000, 500, 0); - answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); - $(BreakdownSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await answerAndSubmitSpendingBreakdownQuestion(1000, 500, 0); + await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); + await $(BreakdownSectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); - it("When I change my answer for the total spending question, Then both repeating section should be marked as 'Partially completed'", () => { - $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); - $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); + it("When I change my answer for the total spending question, Then both repeating section should be marked as 'Partially completed'", async () => { + await $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); + await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); - answerAndSubmitTotalSpendingQuestion(2500); - $(HouseholdOverviewSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); + await answerAndSubmitTotalSpendingQuestion(2500); + await $(HouseholdOverviewSectionSummary.submit()).click(); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); // The 2nd repeating section is now on the path, therefore, its status should have been updated. - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Partially completed"); }); assertRepeatingSectionOnChange(1, "1500.00", "0.00", "0.00", "2,500.00"); assertRepeatingSectionOnChange(2, "1000.00", "500.00", "0.00", "2,500.00"); - it("When I edit and resubmit the total spending question without changing the value, Then the repeating section's status should stay as 'Completed'", () => { - $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); - $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); + it("When I edit and resubmit the total spending question without changing the value, Then the repeating section's status should stay as 'Completed'", async () => { + await $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); + await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); - expect($(TotalSpendingPage.totalSpending()).getValue()).to.equal("2500.00"); - $(TotalSpendingPage.submit()).click(); - $(HouseholdOverviewSectionSummary.submit()).click(); + await expect(await $(TotalSpendingPage.totalSpending()).getValue()).to.equal("2500.00"); + await $(TotalSpendingPage.submit()).click(); + await $(HouseholdOverviewSectionSummary.submit()).click(); - expect($(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); - it("When I submit the questionnaire, Then I should see the thank you page", () => { - $(HubPage.submit()).click(); + it("When I submit the questionnaire, Then I should see the thank you page", async () => { + await $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/less_than.spec.js b/tests/functional/spec/features/validation/sum/less_than.spec.js index 1f662b7e9e..301cca6e9f 100644 --- a/tests/functional/spec/features/validation/sum/less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/less_than.spec.js @@ -3,59 +3,59 @@ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_agai import SubmitPage from "../../../../generated_pages/validation_sum_against_total_less_than/submit.page"; describe("Feature: Sum of grouped answers validation (less than) against total", () => { - beforeEach(() => { - browser.openQuestionnaire("test_validation_sum_against_total_less_than.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_less_than.json"); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("2"); - $(BreakdownAnswerPage.breakdown2()).setValue("2"); - $(BreakdownAnswerPage.breakdown3()).setValue("2"); - $(BreakdownAnswerPage.breakdown4()).setValue("2"); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("2"); + await $(BreakdownAnswerPage.breakdown2()).setValue("2"); + await $(BreakdownAnswerPage.breakdown3()).setValue("2"); + await $(BreakdownAnswerPage.breakdown4()).setValue("2"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("4"); - $(BreakdownAnswerPage.breakdown2()).setValue(""); - $(BreakdownAnswerPage.breakdown3()).setValue(""); - $(BreakdownAnswerPage.breakdown4()).setValue(""); - $(BreakdownAnswerPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("4"); + await $(BreakdownAnswerPage.breakdown2()).setValue(""); + await $(BreakdownAnswerPage.breakdown3()).setValue(""); + await $(BreakdownAnswerPage.breakdown4()).setValue(""); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("3"); - $(BreakdownAnswerPage.breakdown2()).setValue("3"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("3"); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £12.00"); + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("3"); + await $(BreakdownAnswerPage.breakdown2()).setValue("3"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("3"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £12.00"); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.breakdown1()).setValue("3"); - $(BreakdownAnswerPage.breakdown2()).setValue("3"); - $(BreakdownAnswerPage.breakdown3()).setValue("3"); - $(BreakdownAnswerPage.breakdown4()).setValue("3"); - $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £5.00"); + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.breakdown1()).setValue("3"); + await $(BreakdownAnswerPage.breakdown2()).setValue("3"); + await $(BreakdownAnswerPage.breakdown3()).setValue("3"); + await $(BreakdownAnswerPage.breakdown4()).setValue("3"); + await $(BreakdownAnswerPage.submit()).click(); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £5.00"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js index 2eaee37214..5fc2cedea0 100644 --- a/tests/functional/spec/features/validation/sum/value_source.spec.js +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -4,149 +4,149 @@ import TotalPlaybackPage from "../../../../generated_pages/validation_sum_agains import SecondBreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/second-breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; -const answerAndSubmitBreakdownQuestion = (breakdown1, breakdown2, breakdown3, breakdown4) => { - $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); - $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); - $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); - $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); - $(BreakdownAnswerPage.submit()).click(); +const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { + await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); + await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); + await $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); + await $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); + await $(BreakdownAnswerPage.submit()).click(); }; -const answerAndSubmitSecondBreakdownQuestion = (breakdown1, breakdown2, breakdown3, breakdown4) => { - $(SecondBreakdownAnswerPage.secondBreakdown1()).setValue(breakdown1); - $(SecondBreakdownAnswerPage.secondBreakdown2()).setValue(breakdown2); - $(SecondBreakdownAnswerPage.secondBreakdown3()).setValue(breakdown3); - $(SecondBreakdownAnswerPage.secondBreakdown4()).setValue(breakdown4); - $(SecondBreakdownAnswerPage.submit()).click(); +const answerAndSubmitSecondBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { + await $(SecondBreakdownAnswerPage.secondBreakdown1()).setValue(breakdown1); + await $(SecondBreakdownAnswerPage.secondBreakdown2()).setValue(breakdown2); + await $(SecondBreakdownAnswerPage.secondBreakdown3()).setValue(breakdown3); + await $(SecondBreakdownAnswerPage.secondBreakdown4()).setValue(breakdown4); + await $(SecondBreakdownAnswerPage.submit()).click(); }; -const answerBothBreakdownQuestions = (array1, array2) => { - answerAndSubmitBreakdownQuestion(array1[0], array1[1], array1[2], array1[3]); +const answerBothBreakdownQuestions = async (array1, array2) => { + await answerAndSubmitBreakdownQuestion(array1[0], array1[1], array1[2], array1[3]); - $(TotalPlaybackPage.submit()).click(); + await $(TotalPlaybackPage.submit()).click(); - answerAndSubmitSecondBreakdownQuestion(array2[0], array2[1], array2[2], array2[3]); + await answerAndSubmitSecondBreakdownQuestion(array2[0], array2[1], array2[2], array2[3]); }; describe("Feature: Sum of grouped answers equal to validation against value source ", () => { - beforeEach(() => { - browser.openQuestionnaire("test_validation_sum_against_value_source.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_value_source.json"); }); describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the total playback page", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the total playback page", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - expect(browser.getUrl()).to.contain(TotalPlaybackPage.pageName); + await expect(await browser.getUrl()).to.contain(TotalPlaybackPage.pageName); }); }); describe("Given I have a calculated summary value of 12", () => { - it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the summary page", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the summary page", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I completed both grouped answer validation questions and I am on the summary", () => { - it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the summary", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the summary", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - $(SubmitPage.totalAnswerEdit()).click(); - $(TotalAnswerPage.total()).setValue("15"); - $(TotalAnswerPage.submit()).click(); + await $(SubmitPage.totalAnswerEdit()).click(); + await $(TotalAnswerPage.total()).setValue("15"); + await $(TotalAnswerPage.submit()).click(); - $(BreakdownAnswerPage.submit()).click(); + await $(BreakdownAnswerPage.submit()).click(); - expect($(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); - answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); + await answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I completed both grouped answer validation questions and I am on the summary", () => { - it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question based on answer value source with valid answers before I can continue", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question based on answer value source with valid answers before I can continue", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - $(SubmitPage.totalAnswerEdit()).click(); - $(TotalAnswerPage.total()).setValue("15"); - $(TotalAnswerPage.submit()).click(); + await $(SubmitPage.totalAnswerEdit()).click(); + await $(TotalAnswerPage.total()).setValue("15"); + await $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("0", "3", "3", "3"); + await answerAndSubmitBreakdownQuestion("0", "3", "3", "3"); - expect($(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); - answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); + await answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I completed both grouped answer validation questions and I am on the summary", () => { - it("When I go back from the summary and change the first breakdown question answers so its total changes, Then I must reconfirm the second breakdown question based on calculated summary value source with valid answers before I can continue", () => { - $(TotalAnswerPage.total()).setValue("12"); - $(TotalAnswerPage.submit()).click(); + it("When I go back from the summary and change the first breakdown question answers so its total changes, Then I must reconfirm the second breakdown question based on calculated summary value source with valid answers before I can continue", async () => { + await $(TotalAnswerPage.total()).setValue("12"); + await $(TotalAnswerPage.submit()).click(); - answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - $(SubmitPage.breakdown1Edit()).click(); + await $(SubmitPage.breakdown1Edit()).click(); - answerAndSubmitBreakdownQuestion("6", "3", "2", "1"); + await answerAndSubmitBreakdownQuestion("6", "3", "2", "1"); - $(TotalPlaybackPage.submit()).click(); + await $(TotalPlaybackPage.submit()).click(); - $(SecondBreakdownAnswerPage.submit()).click(); + await $(SecondBreakdownAnswerPage.submit()).click(); - expect($(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; - expect($(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 9"); + await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 9"); - answerAndSubmitSecondBreakdownQuestion("5", "4", "0", "0"); + await answerAndSubmitSecondBreakdownQuestion("5", "4", "0", "0"); - expect($(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.false; + await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.false; - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); + it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); - answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); + await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - expect($(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); }); }); describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { - it("When I enter 3 in each breakdown field and continue to second breakdown and enter 3 in each field, Then I should see a validation error", () => { - $(TotalAnswerPage.total()).setValue("5"); - $(TotalAnswerPage.submit()).click(); + it("When I enter 3 in each breakdown field and continue to second breakdown and enter 3 in each field, Then I should see a validation error", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await $(TotalAnswerPage.submit()).click(); - answerBothBreakdownQuestions(["2", "1", "1", "1"], ["3", "3", "3", "3"]); + await answerBothBreakdownQuestions(["2", "1", "1", "1"], ["3", "3", "3", "3"]); - expect($(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 3"); + await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 3"); }); }); }); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index c9be417e64..3dc12c3792 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -5,39 +5,41 @@ import ThankYouPage from "../../../base_pages/thank-you.page"; import ViewSubmittedResponsePage from "../../../generated_pages/view_submitted_response/view-submitted-response.page.js"; describe("View Submitted Response", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_view_submitted_response.json"); - $(NameBlockPage.answer()).setValue("John Smith"); - $(NameBlockPage.submit()).click(); - $(AddressBlockPage.answer()).setValue("NP10 8XG"); - $(AddressBlockPage.submit()).click(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); - $(ThankYouPage.savePrintAnswersLink()).click(); - expect(browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_view_submitted_response.json"); + await $(NameBlockPage.answer()).setValue("John Smith"); + await $(NameBlockPage.submit()).click(); + await $(AddressBlockPage.answer()).setValue("NP10 8XG"); + await $(AddressBlockPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await $(ThankYouPage.savePrintAnswersLink()).click(); + await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); }); - it("Given I have completed a questionnaire with view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", () => { - expect($(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.false; - expect($(ViewSubmittedResponsePage.printButton()).isDisplayed()).to.be.true; - expect($(ViewSubmittedResponsePage.heading()).getText()).to.equal("Answers submitted for Apple"); - expect($(ViewSubmittedResponsePage.metadataTerm(1)).getText()).to.equal("Submitted on:"); - expect($(ViewSubmittedResponsePage.metadataTerm(2)).getText()).to.equal("Submission reference:"); - expect($(ViewSubmittedResponsePage.personalDetailsGroupTitle()).getText()).to.equal("Personal Details"); - expect($(ViewSubmittedResponsePage.nameQuestion()).getText()).to.equal("What is your name?"); - expect($(ViewSubmittedResponsePage.nameAnswer()).getText()).to.equal("John Smith"); - expect($(ViewSubmittedResponsePage.addressDetailsGroupTitle()).getText()).to.equal("Address Details"); - expect($(ViewSubmittedResponsePage.addressQuestion()).getText()).to.equal("What is your address?"); - expect($(ViewSubmittedResponsePage.addressAnswer()).getText()).to.equal("NP10 8XG"); + it("Given I have completed a questionnaire with view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { + await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.false; + await expect(await $(ViewSubmittedResponsePage.printButton()).isDisplayed()).to.be.true; + await expect(await $(ViewSubmittedResponsePage.heading()).getText()).to.equal("Answers submitted for Apple"); + await expect(await $(ViewSubmittedResponsePage.metadataTerm(1)).getText()).to.equal("Submitted on:"); + await expect(await $(ViewSubmittedResponsePage.metadataTerm(2)).getText()).to.equal("Submission reference:"); + await expect(await $(ViewSubmittedResponsePage.personalDetailsGroupTitle()).getText()).to.equal("Personal Details"); + await expect(await $(ViewSubmittedResponsePage.nameQuestion()).getText()).to.equal("What is your name?"); + await expect(await $(ViewSubmittedResponsePage.nameAnswer()).getText()).to.equal("John Smith"); + await expect(await $(ViewSubmittedResponsePage.addressDetailsGroupTitle()).getText()).to.equal("Address Details"); + await expect(await $(ViewSubmittedResponsePage.addressQuestion()).getText()).to.equal("What is your address?"); + await expect(await $(ViewSubmittedResponsePage.addressAnswer()).getText()).to.equal("NP10 8XG"); }); describe("Given I am on the view submitted response page and I submitted over 45 minutes ago", () => { - it("When I click the Download as PDF button, Then I should be redirected to a page informing me that I can no longer view or get a copy of my answers", () => { - browser.pause(40000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) - $(ViewSubmittedResponsePage.downloadButton()).click(); - expect($(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.true; - expect($(ViewSubmittedResponsePage.informationPanel()).getHTML()).to.contain("For security, you can no longer view or get a copy of your answers"); + it("When I click the Download as PDF button, Then I should be redirected to a page informing me that I can no longer view or get a copy of my answers", async () => { + await browser.pause(40000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) + await $(ViewSubmittedResponsePage.downloadButton()).click(); + await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.true; + await expect(await $(ViewSubmittedResponsePage.informationPanel()).getHTML()).to.contain( + "For security, you can no longer view or get a copy of your answers" + ); }); }); }); diff --git a/tests/functional/spec/feedback.spec.js b/tests/functional/spec/feedback.spec.js index 0295819b19..9f49629c4f 100644 --- a/tests/functional/spec/feedback.spec.js +++ b/tests/functional/spec/feedback.spec.js @@ -7,48 +7,48 @@ import ThankYouPage from "../base_pages/thank-you.page"; describe("Feedback", () => { describe("Given I launch and complete the test feedback survey", () => { - before(() => { - browser.openQuestionnaire("test_feedback.json"); - $(SchemaFeedbackPage.submit()).click(); - $(SubmitPage.submit()).click(); + before(async () => { + await browser.openQuestionnaire("test_feedback.json"); + await $(SchemaFeedbackPage.submit()).click(); + await $(SubmitPage.submit()).click(); }); - it("When I view the thank you page, Then I can see the feedback call to action", () => { - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.feedback()).getText()).to.contain("What do you think about this service?"); - expect($(ThankYouPage.feedbackLink()).getText()).to.equal("Give feedback"); - expect($(ThankYouPage.feedbackLink()).getAttribute("href")).to.contain("/submitted/feedback/send"); + it("When I view the thank you page, Then I can see the feedback call to action", async () => { + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.feedback()).getText()).to.contain("What do you think about this service?"); + await expect(await $(ThankYouPage.feedbackLink()).getText()).to.equal("Give feedback"); + await expect(await $(ThankYouPage.feedbackLink()).getAttribute("href")).to.contain("/submitted/feedback/send"); }); - it("When I try to submit without providing feedback, then I stay on the feedback page and get an error message", () => { - browser.url(FeedbackPage.url()); - expect(browser.getUrl()).to.contain(FeedbackPage.pageName); - expect($(FeedbackPage.feedbackTitle()).getText()).to.contain("Give feedback about this service"); - $(FeedbackPage.submit()).click(); - expect(browser.getUrl()).to.contain(FeedbackPage.pageName); - expect($(FeedbackPage.errorPanel()).isExisting()).to.be.true; - expect($(FeedbackPage.errorPanel()).getText()).to.contain( + it("When I try to submit without providing feedback, then I stay on the feedback page and get an error message", async () => { + await browser.url(FeedbackPage.url()); + await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); + await expect(await $(FeedbackPage.feedbackTitle()).getText()).to.contain("Give feedback about this service"); + await $(FeedbackPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); + await expect(await $(FeedbackPage.errorPanel()).isExisting()).to.be.true; + await expect(await $(FeedbackPage.errorPanel()).getText()).to.contain( "There are 2 problems with your feedback\nSelect what your feedback is about\nEnter your feedback" ); }); - it("When I enter valid feedback, Then I can submit the feedback page and get confirmation that the feedback has been sent", () => { - browser.url(FeedbackPage.url()); - $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); - $(FeedbackPage.feedbackText()).setValue("Well done!"); - $(FeedbackPage.submit()).click(); - expect(browser.getUrl()).to.contain(FeedbackSentPage.pageName); - expect($(FeedbackSentPage.feedbackThankYouText()).getText()).to.contain("Thank you for your feedback"); + it("When I enter valid feedback, Then I can submit the feedback page and get confirmation that the feedback has been sent", async () => { + await browser.url(FeedbackPage.url()); + await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); + await $(FeedbackPage.feedbackText()).setValue("Well done!"); + await $(FeedbackPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(FeedbackSentPage.pageName); + await expect(await $(FeedbackSentPage.feedbackThankYouText()).getText()).to.contain("Thank you for your feedback"); }); - it("When I click the done button on the feedback sent page, Then I am taken to the thank you page", () => { - browser.url(FeedbackPage.url()); - $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); - $(FeedbackPage.feedbackText()).setValue("Well done!"); - $(FeedbackPage.submit()).click(); - $(FeedbackSentPage.doneButton()).click(); - expect(browser.getUrl()).to.contain("thank-you"); - expect($(ThankYouPage.title()).getText()).to.contain("Thank you for completing the Feedback test schema"); + it("When I click the done button on the feedback sent page, Then I am taken to the thank you page", async () => { + await browser.url(FeedbackPage.url()); + await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); + await $(FeedbackPage.feedbackText()).setValue("Well done!"); + await $(FeedbackPage.submit()).click(); + await $(FeedbackSentPage.doneButton()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(await $(ThankYouPage.title()).getText()).to.contain("Thank you for completing the Feedback test schema"); }); }); }); diff --git a/tests/functional/spec/interstitial_definition.spec.js b/tests/functional/spec/interstitial_definition.spec.js index 9b09a8ed90..2c8346f241 100644 --- a/tests/functional/spec/interstitial_definition.spec.js +++ b/tests/functional/spec/interstitial_definition.spec.js @@ -2,26 +2,26 @@ import InterstitialDefinitionPage from "../generated_pages/interstitial_definiti describe("Component: Interstitial Definition", () => { describe("Given I launch the interstitial definition questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_interstitial_definition.json"); + before(async () => { + await browser.openQuestionnaire("test_interstitial_definition.json"); }); - it("When there is a definition on an interstitial, then the page is displayed correctly", () => { - expect($(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal(""); + it("When there is a definition on an interstitial, then the page is displayed correctly", async () => { + await expect(await $(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal(""); - expect($(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); + await expect(await $(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); }); - it("When I click on a definition title, the content is displayed for just that definition", () => { - $(InterstitialDefinitionPage.definitionTitle(1)).click(); + it("When I click on a definition title, the content is displayed for just that definition", async () => { + await $(InterstitialDefinitionPage.definitionTitle(1)).click(); - expect($(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal("In a way that accomplishes a desired aim or result"); + await expect(await $(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal("In a way that accomplishes a desired aim or result"); - expect($(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - expect($(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); + await expect(await $(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); }); }); }); diff --git a/tests/functional/spec/interviewer_note.spec.js b/tests/functional/spec/interviewer_note.spec.js index 0130e9a3b0..7d90687cd8 100644 --- a/tests/functional/spec/interviewer_note.spec.js +++ b/tests/functional/spec/interviewer_note.spec.js @@ -4,25 +4,25 @@ import FinalInterstitialPage from "../generated_pages/interviewer_note/final-int import InitialInterstitialPage from "../generated_pages/interviewer_note/initial-interstitial-block.page.js"; describe("Given I start a survey", () => { - before(() => { - browser.openQuestionnaire("test_interviewer_note.json"); + before(async () => { + await browser.openQuestionnaire("test_interviewer_note.json"); }); - it("When I view interstitial page and the interviewer_note is set to true then I should be able to see interviewer note", () => { - expect($(InitialInterstitialPage.questionText()).getText()).to.contain("Interviewer note"); + it("When I view interstitial page and the interviewer_note is set to true then I should be able to see interviewer note", async () => { + await expect(await $(InitialInterstitialPage.questionText()).getText()).to.contain("Interviewer note"); }); - it("When I view question page and the interviewer_note is set to true then I should be able to see interviewer note", () => { - $(InitialInterstitialPage.submit()).click(); - expect($(FavouriteTeamPage.questionText()).getText()).to.contain("Interviewer note"); + it("When I view question page and the interviewer_note is set to true then I should be able to see interviewer note", async () => { + await $(InitialInterstitialPage.submit()).click(); + await expect(await $(FavouriteTeamPage.questionText()).getText()).to.contain("Interviewer note"); }); - it("When I view question page and the interviewer_note is set to false then I should not be able to see interviewer note", () => { - $(FavouriteTeamPage.favouriteTeam()).setValue("TNS"); - $(FavouriteTeamPage.submit()).click(); - expect($(ConfirmPage.questionText()).getText()).to.not.contain("Interviewer note"); + it("When I view question page and the interviewer_note is set to false then I should not be able to see interviewer note", async () => { + await $(FavouriteTeamPage.favouriteTeam()).setValue("TNS"); + await $(FavouriteTeamPage.submit()).click(); + await expect(await $(ConfirmPage.questionText()).getText()).to.not.contain("Interviewer note"); }); - it("When I view interstitial page and the interviewer_note is not set then I should not be able to see interviewer note", () => { - $(ConfirmPage.yes()).click(); - $(ConfirmPage.submit()).click(); - expect($(FinalInterstitialPage.questionText()).getText()).to.not.contain("Interviewer note"); + it("When I view interstitial page and the interviewer_note is not set then I should not be able to see interviewer note", async () => { + await $(ConfirmPage.yes()).click(); + await $(ConfirmPage.submit()).click(); + await expect(await $(FinalInterstitialPage.questionText()).getText()).to.not.contain("Interviewer note"); }); }); diff --git a/tests/functional/spec/introduction.spec.js b/tests/functional/spec/introduction.spec.js index 54fbcbb713..33077210c9 100644 --- a/tests/functional/spec/introduction.spec.js +++ b/tests/functional/spec/introduction.spec.js @@ -2,27 +2,27 @@ import IntroductionPage from "../generated_pages/introduction/introduction.page" describe("Introduction page", () => { const introductionSchema = "test_introduction.json"; - beforeEach(() => { - browser.openQuestionnaire(introductionSchema); + beforeEach(async () => { + await browser.openQuestionnaire(introductionSchema); }); - it("Given I start a survey, When I view the introduction page, Then I should be able to see introduction information", () => { - browser.openQuestionnaire(introductionSchema); - expect($(IntroductionPage.useOfData()).getText()).to.contain("How we use your data"); - expect($(IntroductionPage.useOfInformation()).getText()).to.contain( + it("Given I start a survey, When I view the introduction page, Then I should be able to see introduction information", async () => { + await browser.openQuestionnaire(introductionSchema); + await expect(await $(IntroductionPage.useOfData()).getText()).to.contain("How we use your data"); + await expect(await $(IntroductionPage.useOfInformation()).getText()).to.contain( "Data should relate to all sites in England, Scotland and Wales unless otherwise stated." ); - expect($(IntroductionPage.legalResponse()).getText()).to.contain("Your response is legally required"); - expect($(IntroductionPage.legalBasis()).getText()).to.contain("Notice is given under section 999 of the Test Act 2000"); - expect($(IntroductionPage.introDescription()).getText()).to.contain( + await expect(await $(IntroductionPage.legalResponse()).getText()).to.contain("Your response is legally required"); + await expect(await $(IntroductionPage.legalBasis()).getText()).to.contain("Notice is given under section 999 of the Test Act 2000"); + await expect(await $(IntroductionPage.introDescription()).getText()).to.contain( "To take part, all you need to do is check that you have the information you need to answer the survey questions." ); }); - it("Given I start a survey with introduction guidance set, When I view the introduction page, Then I should be able to see introduction guidance", () => { - browser.openQuestionnaire(introductionSchema); - expect($(IntroductionPage.guidancePanel(1)).isDisplayed()).to.be.true; - expect($(IntroductionPage.guidancePanel(1)).getText()).to.contain("Coronavirus (COVID-19) guidance"); - expect($(IntroductionPage.guidancePanel(1)).getText()).to.contain( + it("Given I start a survey with introduction guidance set, When I view the introduction page, Then I should be able to see introduction guidance", async () => { + await browser.openQuestionnaire(introductionSchema); + await expect(await $(IntroductionPage.guidancePanel(1)).isDisplayed()).to.be.true; + await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain("Coronavirus (COVID-19) guidance"); + await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain( "Explain your figures in the comment section to minimise us contacting you and to help us tell an industry story" ); }); diff --git a/tests/functional/spec/language_code.spec.js b/tests/functional/spec/language_code.spec.js index 8e1a235478..a2772e16fc 100644 --- a/tests/functional/spec/language_code.spec.js +++ b/tests/functional/spec/language_code.spec.js @@ -96,151 +96,151 @@ const PLURAL_TEST_DATA_SETS = [ ]; describe("Language Code", () => { - it("Given a launch language of Welsh, I should see Welsh text", () => { - browser.openQuestionnaire("test_language.json", { + it("Given a launch language of Welsh, I should see Welsh text", async () => { + await browser.openQuestionnaire("test_language.json", { language: "cy", }); - $(HubPage.submit()).click(); - expect($(NamePage.questionText()).getText()).to.contain("Rhowch enw"); - - $(NamePage.firstName()).setValue("Catherine"); - $(NamePage.lastName()).setValue("Zeta-Jones"); - $(NamePage.submit()).click(); - - $(DobPage.day()).setValue(25); - $(DobPage.month()).setValue(9); - $(DobPage.year()).setValue(1969); - $(DobPage.submit()).click(); - - $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - $(NumberOfPeoplePage.submit()).click(); - $(ConfirmNumberOfPeoplePage.yes()).click(); - $(ConfirmNumberOfPeoplePage.submit()).click(); - - expect($(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); - expect($(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); - expect($(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); - expect($(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); - $(HubPage.submit()).click(); - - expect(browser.getUrl()).to.contain("thank-you"); + await $(HubPage.submit()).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + + await $(NamePage.firstName()).setValue("Catherine"); + await $(NamePage.lastName()).setValue("Zeta-Jones"); + await $(NamePage.submit()).click(); + + await $(DobPage.day()).setValue(25); + await $(DobPage.month()).setValue(9); + await $(DobPage.year()).setValue(1969); + await $(DobPage.submit()).click(); + + await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); + await $(NumberOfPeoplePage.submit()).click(); + await $(ConfirmNumberOfPeoplePage.yes()).click(); + await $(ConfirmNumberOfPeoplePage.submit()).click(); + + await expect(await $(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); + await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); + await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); + await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); + await $(HubPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain("thank-you"); }); - it("Given a launch language of English, I should see English text", () => { - browser.openQuestionnaire("test_language.json", { + it("Given a launch language of English, I should see English text", async () => { + await browser.openQuestionnaire("test_language.json", { language: "en", }); - $(HubPage.submit()).click(); - expect($(NamePage.questionText()).getText()).to.contain("Please enter a name"); - $(NamePage.firstName()).setValue("Catherine"); - $(NamePage.lastName()).setValue("Zeta-Jones"); - $(NamePage.submit()).click(); - - $(DobPage.day()).setValue(25); - $(DobPage.month()).setValue(9); - $(DobPage.year()).setValue(1969); - $(DobPage.submit()).click(); - - $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - $(NumberOfPeoplePage.submit()).click(); - $(ConfirmNumberOfPeoplePage.yes()).click(); - $(ConfirmNumberOfPeoplePage.submit()).click(); - - expect($(HubPage.heading()).getText()).to.contain("Submission title"); - expect($(HubPage.warning()).getText()).to.contain("Submission warning"); - expect($(HubPage.guidance()).getText()).to.contain("Submission guidance"); - expect($(HubPage.submit()).getText()).to.contain("Submission button"); - $(HubPage.submit()).click(); - - expect(browser.getUrl()).to.contain("thank-you"); + await $(HubPage.submit()).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await $(NamePage.firstName()).setValue("Catherine"); + await $(NamePage.lastName()).setValue("Zeta-Jones"); + await $(NamePage.submit()).click(); + + await $(DobPage.day()).setValue(25); + await $(DobPage.month()).setValue(9); + await $(DobPage.year()).setValue(1969); + await $(DobPage.submit()).click(); + + await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); + await $(NumberOfPeoplePage.submit()).click(); + await $(ConfirmNumberOfPeoplePage.yes()).click(); + await $(ConfirmNumberOfPeoplePage.submit()).click(); + + await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); + await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); + await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); + await $(HubPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain("thank-you"); }); - it("Given a launch language of English, When I select Cymraeg, Then the language should be switched to Welsh", () => { - browser.openQuestionnaire("test_language.json", { + it("Given a launch language of English, When I select Cymraeg, Then the language should be switched to Welsh", async () => { + await browser.openQuestionnaire("test_language.json", { language: "en", }); - $(HubPage.submit()).click(); - expect($(NamePage.questionText()).getText()).to.contain("Please enter a name"); - expect($("header").getText()).to.contain("Test Language Survey"); - $(NamePage.switchLanguage("cy")).click(); - expect($(NamePage.questionText()).getText()).to.contain("Rhowch enw"); - expect($("header").getText()).to.contain("Arolwg Iaith Prawf"); - $(NamePage.switchLanguage("en")).click(); - - $(NamePage.firstName()).setValue("Catherine"); - $(NamePage.lastName()).setValue("Zeta-Jones"); - $(NamePage.submit()).click(); - - $(DobPage.day()).setValue(25); - $(DobPage.month()).setValue(9); - $(DobPage.year()).setValue(1969); - $(DobPage.submit()).click(); - - $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - $(NumberOfPeoplePage.submit()).click(); - $(ConfirmNumberOfPeoplePage.yes()).click(); - $(ConfirmNumberOfPeoplePage.submit()).click(); - - expect($(HubPage.heading()).getText()).to.contain("Submission title"); - expect($(HubPage.warning()).getText()).to.contain("Submission warning"); - expect($(HubPage.guidance()).getText()).to.contain("Submission guidance"); - expect($(HubPage.submit()).getText()).to.contain("Submission button"); - $(HubPage.switchLanguage("cy")).click(); - expect($(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); - expect($(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); - expect($(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); - expect($(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); - $(HubPage.submit()).click(); - - expect(browser.getUrl()).to.contain("thank-you"); + await $(HubPage.submit()).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await expect(await $("header").getText()).to.contain("Test Language Survey"); + await $(NamePage.switchLanguage("cy")).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + await expect(await $("header").getText()).to.contain("Arolwg Iaith Prawf"); + await $(NamePage.switchLanguage("en")).click(); + + await $(NamePage.firstName()).setValue("Catherine"); + await $(NamePage.lastName()).setValue("Zeta-Jones"); + await $(NamePage.submit()).click(); + + await $(DobPage.day()).setValue(25); + await $(DobPage.month()).setValue(9); + await $(DobPage.year()).setValue(1969); + await $(DobPage.submit()).click(); + + await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); + await $(NumberOfPeoplePage.submit()).click(); + await $(ConfirmNumberOfPeoplePage.yes()).click(); + await $(ConfirmNumberOfPeoplePage.submit()).click(); + + await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); + await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); + await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); + await $(HubPage.switchLanguage("cy")).click(); + await expect(await $(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); + await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); + await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); + await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); + await $(HubPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain("thank-you"); }); - it("Given a launch language of Welsh, When I select English, Then the language should be switched to English", () => { - browser.openQuestionnaire("test_language.json", { + it("Given a launch language of Welsh, When I select English, Then the language should be switched to English", async () => { + await browser.openQuestionnaire("test_language.json", { language: "cy", }); - $(HubPage.submit()).click(); - expect($(NamePage.questionText()).getText()).to.contain("Rhowch enw"); - $(NamePage.switchLanguage("en")).click(); - expect($(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await $(HubPage.submit()).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + await $(NamePage.switchLanguage("en")).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); }); describe("Given a launch language of English and a question with plural forms, When I select switch languages, Then the plural forms are displayed correctly for the chosen language", () => { for (const dataSet of PLURAL_TEST_DATA_SETS) { const numberOfPeople = dataSet.count; - it(`Test plural count: ${numberOfPeople}`, () => { - browser.openQuestionnaire("test_language.json", { + it(`Test plural count: ${numberOfPeople}`, async () => { + await browser.openQuestionnaire("test_language.json", { language: "en", }); - $(HubPage.submit()).click(); - expect($(NamePage.questionText()).getText()).to.contain("Please enter a name"); - $(NamePage.firstName()).setValue("Catherine"); - $(NamePage.lastName()).setValue("Zeta-Jones"); - $(NamePage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await $(NamePage.firstName()).setValue("Catherine"); + await $(NamePage.lastName()).setValue("Zeta-Jones"); + await $(NamePage.submit()).click(); - $(DobPage.day()).setValue(25); - $(DobPage.month()).setValue(9); - $(DobPage.year()).setValue(1969); - $(DobPage.submit()).click(); + await $(DobPage.day()).setValue(25); + await $(DobPage.month()).setValue(9); + await $(DobPage.year()).setValue(1969); + await $(DobPage.submit()).click(); - $(NumberOfPeoplePage.numberOfPeople()).setValue(numberOfPeople); - $(NumberOfPeoplePage.submit()).click(); + await $(NumberOfPeoplePage.numberOfPeople()).setValue(numberOfPeople); + await $(NumberOfPeoplePage.submit()).click(); - expect($(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.en); - expect($(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.en); + await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.en); + await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.en); - $(ConfirmNumberOfPeoplePage.switchLanguage("cy")).click(); + await $(ConfirmNumberOfPeoplePage.switchLanguage("cy")).click(); - expect($(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.cy); - expect($(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.cy); + await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.cy); + await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.cy); - $(ConfirmNumberOfPeoplePage.yes()).click(); - $(ConfirmNumberOfPeoplePage.submit()).click(); + await $(ConfirmNumberOfPeoplePage.yes()).click(); + await $(ConfirmNumberOfPeoplePage.submit()).click(); }); } }); diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index b325a73f77..8894a68f82 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -22,211 +22,211 @@ import { SubmitPage } from "../base_pages/submit.page.js"; describe("List Collector", () => { describe("Given a normal journey through the list collector without variants", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector.json"); - }); - - it("The user is able to add members of the household", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Olivia"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Suzy"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - }); - - it("The collector shows all of the household members in the summary", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector.json"); + }); + + it("The user is able to add members of the household", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Olivia"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Suzy"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + }); + + it("The collector shows all of the household members in the summary", async () => { const peopleExpected = ["Marcus Twin", "Samuel Clemens", "Olivia Clemens", "Suzy Clemens"]; checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); }); - it("The questionnaire allows the name of a person to be changed", () => { - $(ListCollectorPage.listEditLink(1)).click(); - $(ListCollectorEditPage.firstName()).setValue("Mark"); - $(ListCollectorEditPage.lastName()).setValue("Twain"); - $(ListCollectorEditPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain"); + it("The questionnaire allows the name of a person to be changed", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + await $(ListCollectorEditPage.firstName()).setValue("Mark"); + await $(ListCollectorEditPage.lastName()).setValue("Twain"); + await $(ListCollectorEditPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain"); }); - it("The questionnaire allows me to remove the first person (Mark Twain) from the summary", () => { - $(ListCollectorPage.listRemoveLink(1)).click(); - $(ListCollectorRemovePage.yes()).click(); - $(ListCollectorRemovePage.submit()).click(); + it("The questionnaire allows me to remove the first person (Mark Twain) from the summary", async () => { + await $(ListCollectorPage.listRemoveLink(1)).click(); + await $(ListCollectorRemovePage.yes()).click(); + await $(ListCollectorRemovePage.submit()).click(); }); - it("The collector summary does not show Mark Twain anymore.", () => { - expect($(ListCollectorPage.listLabel(1)).getText()).to.not.have.string("Mark Twain"); - expect($(ListCollectorPage.listLabel(3)).getText()).to.equal("Suzy Clemens"); + it("The collector summary does not show Mark Twain anymore.", async () => { + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.not.have.string("Mark Twain"); + await expect(await $(ListCollectorPage.listLabel(3)).getText()).to.equal("Suzy Clemens"); }); - it("The questionnaire allows more people to be added", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - expect($(ListCollectorAddPage.questionText()).getText()).to.contain("What is the name of the person"); - $(ListCollectorAddPage.firstName()).setValue("Clara"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Jean"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + it("The questionnaire allows more people to be added", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(ListCollectorAddPage.questionText()).getText()).to.contain("What is the name of the person"); + await $(ListCollectorAddPage.firstName()).setValue("Clara"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Jean"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); }); - it("The user is returned to the list collector when the cancel link is clicked on the add page.", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Someone"); - $(ListCollectorAddPage.lastName()).setValue("Else"); - $(ListCollectorAddPage.cancelAndReturn()).click(); - expect(browser.getUrl()).to.contain(ListCollectorPage.pageName); + it("The user is returned to the list collector when the cancel link is clicked on the add page.", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Someone"); + await $(ListCollectorAddPage.lastName()).setValue("Else"); + await $(ListCollectorAddPage.cancelAndReturn()).click(); + await expect(await browser.getUrl()).to.contain(ListCollectorPage.pageName); }); - it("The user is returned to the list collector when the cancel link is clicked on the edit page.", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Someone"); - $(ListCollectorAddPage.lastName()).setValue("Else"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.listEditLink(1)).click(); - $(ListCollectorEditPage.cancelAndReturn()).click(); - expect(browser.getUrl()).to.contain(ListCollectorPage.pageName); + it("The user is returned to the list collector when the cancel link is clicked on the edit page.", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Someone"); + await $(ListCollectorAddPage.lastName()).setValue("Else"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.listEditLink(1)).click(); + await $(ListCollectorEditPage.cancelAndReturn()).click(); + await expect(await browser.getUrl()).to.contain(ListCollectorPage.pageName); }); - it("The collector shows everyone on the summary", () => { + it("The collector shows everyone on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); }); - it("When No is answered on the list collector the user sees an interstitial", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(NextInterstitialPage.pageName); - $(NextInterstitialPage.submit()).click(); + it("When No is answered on the list collector the user sees an interstitial", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(NextInterstitialPage.pageName); + await $(NextInterstitialPage.submit()).click(); }); - it("After the interstitial, the user should be on the second list collector page", () => { - expect(browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + it("After the interstitial, the user should be on the second list collector page", async () => { + await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); }); - it("The collector still shows the same list of people on the summary", () => { + it("The collector still shows the same list of people on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); }); - it("The collector allows the user to add another person to the same list", () => { - $(AnotherListCollectorPage.yes()).click(); - $(AnotherListCollectorPage.submit()).click(); - $(AnotherListCollectorAddPage.firstName()).setValue("Someone"); - $(AnotherListCollectorAddPage.lastName()).setValue("Else"); - $(AnotherListCollectorAddPage.submit()).click(); - expect($(AnotherListCollectorPage.listLabel(6)).getText()).to.equal("Someone Else"); + it("The collector allows the user to add another person to the same list", async () => { + await $(AnotherListCollectorPage.yes()).click(); + await $(AnotherListCollectorPage.submit()).click(); + await $(AnotherListCollectorAddPage.firstName()).setValue("Someone"); + await $(AnotherListCollectorAddPage.lastName()).setValue("Else"); + await $(AnotherListCollectorAddPage.submit()).click(); + await expect(await $(AnotherListCollectorPage.listLabel(6)).getText()).to.equal("Someone Else"); }); - it("The collector allows the user to remove a person again", () => { - $(AnotherListCollectorPage.listRemoveLink(5)).click(); - $(AnotherListCollectorRemovePage.yes()).click(); - $(AnotherListCollectorRemovePage.submit()).click(); + it("The collector allows the user to remove a person again", async () => { + await $(AnotherListCollectorPage.listRemoveLink(5)).click(); + await $(AnotherListCollectorRemovePage.yes()).click(); + await $(AnotherListCollectorRemovePage.submit()).click(); }); - it("The user is returned to the list collector when the previous link is clicked.", () => { - $(AnotherListCollectorPage.listRemoveLink(1)).click(); - $(AnotherListCollectorRemovePage.previous()).click(); - expect(browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); - $(AnotherListCollectorPage.listEditLink(1)).click(); - $(AnotherListCollectorEditPage.previous()).click(); - expect(browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); - $(AnotherListCollectorPage.yes()).click(); - $(AnotherListCollectorPage.submit()).click(); - $(AnotherListCollectorEditPage.previous()).click(); - expect(browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + it("The user is returned to the list collector when the previous link is clicked.", async () => { + await $(AnotherListCollectorPage.listRemoveLink(1)).click(); + await $(AnotherListCollectorRemovePage.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await $(AnotherListCollectorPage.listEditLink(1)).click(); + await $(AnotherListCollectorEditPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await $(AnotherListCollectorPage.yes()).click(); + await $(AnotherListCollectorPage.submit()).click(); + await $(AnotherListCollectorEditPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); }); - it("The questionnaire shows the confirmation page when no more people to add", () => { - $(AnotherListCollectorPage.no()).click(); - $(AnotherListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); + it("The questionnaire shows the confirmation page when no more people to add", async () => { + await $(AnotherListCollectorPage.no()).click(); + await $(AnotherListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); }); - it("The questionnaire allows submission", () => { - $(SummaryPage.submit()).click(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + it("The questionnaire allows submission", async () => { + await $(SummaryPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); }); describe("Given I start a list collector survey and complete to Section Summary", () => { - beforeEach(() => { - browser.openQuestionnaire("test_list_collector_list_summary.json"); - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(SectionSummaryListCollectorPage.yes()).click(); - $(SectionSummaryListCollectorPage.submit()).click(); - $(SectionSummaryListCollectorAddPage.firstName()).setValue("Samuel"); - $(SectionSummaryListCollectorAddPage.lastName()).setValue("Clemens"); - $(SectionSummaryListCollectorAddPage.submit()).click(); - $(SectionSummaryListCollectorPage.no()).click(); - $(SectionSummaryListCollectorPage.submit()).click(); - $(VisitorListCollectorPage.yes()).click(); - $(VisitorListCollectorPage.submit()).click(); - $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Olivia"); - $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Clemens"); - $(VisitorListCollectorAddPage.submit()).click(); - $(VisitorListCollectorPage.no()).click(); - $(VisitorListCollectorPage.submit()).click(); - }); - - it("The section summary should display contents of the list collector", () => { - expect($(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Marcus Twin (You)"); - expect($(PeopleListSectionSummaryPage.peopleListLabel(2)).getText()).to.contain("Samuel Clemens"); - expect($(PeopleListSectionSummaryPage.visitorsListLabel(1)).getText()).to.contain("Olivia Clemens"); - }); - - it("When the user adds an item to the list, They should return to the section summary and it should display the updated list", () => { - $(PeopleListSectionSummaryPage.visitorsListAddLink(1)).click(); - $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Joe"); - $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Bloggs"); - $(VisitorListCollectorAddPage.submit()).click(); - $(VisitorListCollectorPage.no()).click(); - $(VisitorListCollectorPage.submit()).click(); - expect($(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).to.contain("Joe Bloggs"); - }); - - it("When the user removes an item from the list, They should return to the section summary and it should display the updated list", () => { - $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); - $(SectionSummaryListCollectorRemovePage.yes()).click(); - $(SectionSummaryListCollectorRemovePage.submit()).click(); - expect($(PeopleListSectionSummaryPage.visitorsListLabel(2)).isExisting()).to.equal(false); - }); - - it("When the user updates the list, They should return to the section summary and it should display the updated list", () => { - $(PeopleListSectionSummaryPage.peopleListEditLink(1)).click(); - $(SectionSummaryListCollectorEditPage.firstName()).setValue("Mark"); - $(SectionSummaryListCollectorEditPage.lastName()).setValue("Twain"); - $(SectionSummaryListCollectorEditPage.submit()).click(); - expect($(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Mark Twain (You)"); - }); - - it("When the user removes an item from the list, They should see the individual response guidance", () => { - $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); - expect($(SectionSummaryListCollectorRemovePage.individualResponseGuidance()).isExisting()).to.equal(true); + beforeEach(async () => { + await browser.openQuestionnaire("test_list_collector_list_summary.json"); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(SectionSummaryListCollectorPage.yes()).click(); + await $(SectionSummaryListCollectorPage.submit()).click(); + await $(SectionSummaryListCollectorAddPage.firstName()).setValue("Samuel"); + await $(SectionSummaryListCollectorAddPage.lastName()).setValue("Clemens"); + await $(SectionSummaryListCollectorAddPage.submit()).click(); + await $(SectionSummaryListCollectorPage.no()).click(); + await $(SectionSummaryListCollectorPage.submit()).click(); + await $(VisitorListCollectorPage.yes()).click(); + await $(VisitorListCollectorPage.submit()).click(); + await $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Olivia"); + await $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Clemens"); + await $(VisitorListCollectorAddPage.submit()).click(); + await $(VisitorListCollectorPage.no()).click(); + await $(VisitorListCollectorPage.submit()).click(); + }); + + it("The section summary should display contents of the list collector", async () => { + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Marcus Twin (You)"); + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(2)).getText()).to.contain("Samuel Clemens"); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(1)).getText()).to.contain("Olivia Clemens"); + }); + + it("When the user adds an item to the list, They should return to the section summary and it should display the updated list", async () => { + await $(PeopleListSectionSummaryPage.visitorsListAddLink(1)).click(); + await $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Joe"); + await $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Bloggs"); + await $(VisitorListCollectorAddPage.submit()).click(); + await $(VisitorListCollectorPage.no()).click(); + await $(VisitorListCollectorPage.submit()).click(); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).to.contain("Joe Bloggs"); + }); + + it("When the user removes an item from the list, They should return to the section summary and it should display the updated list", async () => { + await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); + await $(SectionSummaryListCollectorRemovePage.yes()).click(); + await $(SectionSummaryListCollectorRemovePage.submit()).click(); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).isExisting()).to.equal(false); + }); + + it("When the user updates the list, They should return to the section summary and it should display the updated list", async () => { + await $(PeopleListSectionSummaryPage.peopleListEditLink(1)).click(); + await $(SectionSummaryListCollectorEditPage.firstName()).setValue("Mark"); + await $(SectionSummaryListCollectorEditPage.lastName()).setValue("Twain"); + await $(SectionSummaryListCollectorEditPage.submit()).click(); + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Mark Twain (You)"); + }); + + it("When the user removes an item from the list, They should see the individual response guidance", async () => { + await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); + await expect(await $(SectionSummaryListCollectorRemovePage.individualResponseGuidance()).isExisting()).to.equal(true); }); }); }); diff --git a/tests/functional/spec/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector_driving_question.spec.js index 3643b05d14..1280e2a020 100644 --- a/tests/functional/spec/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector_driving_question.spec.js @@ -7,55 +7,55 @@ import AnyoneElseLiveAtListCollectorRemovePage from "../generated_pages/list_col import SectionSummaryPage from "../generated_pages/list_collector_driving_question/section-summary.page.js"; describe("List Collector Driving Question", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_driving_question.json"); - $(HubPage.submit()).click(); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_driving_question.json"); + await $(HubPage.submit()).click(); }); describe("Given a happy journey through the list collector", () => { - it("The collector shows all of the household members in the summary", () => { - $(AnyoneUsuallyLiveAtPage.yes()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); - $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorPage.yes()).click(); - $(AnyoneElseLiveAtListCollectorPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Suzy"); - $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Clemens"); - $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorPage.no()).click(); - $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + it("The collector shows all of the household members in the summary", async () => { + await $(AnyoneUsuallyLiveAtPage.yes()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); + await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); + await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorPage.yes()).click(); + await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Suzy"); + await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Clemens"); + await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorPage.no()).click(); + await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); const peopleExpected = ["Marcus Twin", "Suzy Clemens"]; - checkPeopleInList(peopleExpected, SectionSummaryPage.peopleListLabel); + await checkPeopleInList(peopleExpected, SectionSummaryPage.peopleListLabel); }); }); describe("Given the user answers no to the driving question", () => { - it("The summary add link returns to the driving question", () => { - $(AnyoneUsuallyLiveAtPage.no()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - $(SectionSummaryPage.peopleListAddLink()).click(); - expect(browser.getUrl()).to.contain(AnyoneUsuallyLiveAtPage.url()); + it("The summary add link returns to the driving question", async () => { + await $(AnyoneUsuallyLiveAtPage.no()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await $(SectionSummaryPage.peopleListAddLink()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAtPage.url()); }); }); describe("Given the user answers yes to the driving question, adds someone and later removes them", () => { - it("The summary add link should return to the original list collector", () => { - $(AnyoneUsuallyLiveAtPage.yes()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); - $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); - $(AnyoneElseLiveAtListCollectorPage.no()).click(); - $(AnyoneElseLiveAtListCollectorPage.submit()).click(); - $(SectionSummaryPage.peopleListRemoveLink(1)).click(); - $(AnyoneElseLiveAtListCollectorRemovePage.yes()).click(); - $(AnyoneElseLiveAtListCollectorRemovePage.submit()).click(); - $(SectionSummaryPage.peopleListAddLink()).click(); - expect(browser.getUrl()).to.contain(AnyoneElseLiveAtListCollectorAddPage.pageName); + it("The summary add link should return to the original list collector", async () => { + await $(AnyoneUsuallyLiveAtPage.yes()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); + await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); + await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await $(AnyoneElseLiveAtListCollectorPage.no()).click(); + await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); + await $(AnyoneElseLiveAtListCollectorRemovePage.yes()).click(); + await $(AnyoneElseLiveAtListCollectorRemovePage.submit()).click(); + await $(SectionSummaryPage.peopleListAddLink()).click(); + await expect(await browser.getUrl()).to.contain(AnyoneElseLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js index bb65f3bc0d..c5b5a73087 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js @@ -9,63 +9,63 @@ import ListCollectorTemporaryAwayPage from "../generated_pages/list_collector_dr import ListCollectorTemporaryAwayAddPage from "../generated_pages/list_collector_driving_checkbox/list-collector-temporary-away-stay-add.page"; import SummaryPage from "../generated_pages/list_collector_driving_checkbox/section-summary.page"; -const beforeSetup = () => { - browser.openQuestionnaire("test_list_collector_driving_checkbox.json"); - $(HubPage.submit()).click(); +const beforeSetup = async () => { + await browser.openQuestionnaire("test_list_collector_driving_checkbox.json"); + await $(HubPage.submit()).click(); }; describe("List Collector Driving Checkbox Question", () => { before("Load the survey", beforeSetup); describe("Given a happy journey through the list collectors", () => { - it("All of the household members and visitors are shown in the summary", () => { - $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(AnyoneUsuallyLiveAtPage.familyMembersAndPartners()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Suzy"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.noIDoNotNeedToAddAPerson()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - $(ListCollectorTemporaryAwayPage.submit()).click(); + it("All of the household members and visitors are shown in the summary", async () => { + await $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(AnyoneUsuallyLiveAtPage.familyMembersAndPartners()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Suzy"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.noIDoNotNeedToAddAPerson()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); + await $(ListCollectorTemporaryAwayPage.submit()).click(); const householdMembersExpected = ["Marcus Twin (You)", "Suzy Clemens"]; - checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); describe("Given the primary person is removed", () => { - it("Then they aren't shown on the summary screen", () => { - $(SummaryPage.previous()).click(); - $(ListCollectorTemporaryAwayPage.previous()).click(); - $(ListCollectorPage.previous()).click(); - $(AnyoneUsuallyLiveAtPage.previous()).click(); - $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); + it("Then they aren't shown on the summary screen", async () => { + await $(SummaryPage.previous()).click(); + await $(ListCollectorTemporaryAwayPage.previous()).click(); + await $(ListCollectorPage.previous()).click(); + await $(AnyoneUsuallyLiveAtPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); const householdMembersExpected = ["Suzy Clemens"]; - checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); describe("Given the user chooses yes from the second list collector", () => { - it("Then they are taken to the correct list add screen", () => { - $(SummaryPage.previous()).click(); - $(ListCollectorTemporaryAwayPage.yesINeedToAddSomeone()).click(); - $(ListCollectorTemporaryAwayPage.submit()).click(); - $(ListCollectorTemporaryAwayAddPage.firstName()).setValue("Christopher"); - $(ListCollectorTemporaryAwayAddPage.lastName()).setValue("Pike"); - $(ListCollectorTemporaryAwayAddPage.submit()).click(); - $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - $(ListCollectorTemporaryAwayPage.submit()).click(); + it("Then they are taken to the correct list add screen", async () => { + await $(SummaryPage.previous()).click(); + await $(ListCollectorTemporaryAwayPage.yesINeedToAddSomeone()).click(); + await $(ListCollectorTemporaryAwayPage.submit()).click(); + await $(ListCollectorTemporaryAwayAddPage.firstName()).setValue("Christopher"); + await $(ListCollectorTemporaryAwayAddPage.lastName()).setValue("Pike"); + await $(ListCollectorTemporaryAwayAddPage.submit()).click(); + await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); + await $(ListCollectorTemporaryAwayPage.submit()).click(); const householdMembersExpected = ["Suzy Clemens", "Christopher Pike"]; - checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); }); @@ -73,31 +73,35 @@ describe("List Collector Driving Checkbox Question", () => { describe("Given the user says no one else lives in the house", () => { before("Load the survey", beforeSetup); - it("The user is asked if they need to add anyone that is temporarily away", () => { - $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); + it("The user is asked if they need to add anyone that is temporarily away", async () => { + await $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); - expect($(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal("You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?"); + await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( + "You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?" + ); }); }); describe("Given a person does not live in the house", () => { before("Load the survey", beforeSetup); - it("The user is asked whether they live there", () => { - $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - expect($(AnyoneUsuallyLiveAtPage.questionText()).getText()).to.equal("Do any of the following usually live at 12 Lovely Villas on 21 March?"); + it("The user is asked whether they live there", async () => { + await $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await expect(await $(AnyoneUsuallyLiveAtPage.questionText()).getText()).to.equal("Do any of the following usually live at 12 Lovely Villas on 21 March?"); - $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - expect($(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal("You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?"); + await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( + "You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?" + ); - $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); + await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); }); }); diff --git a/tests/functional/spec/list_collector_primary_person.spec.js b/tests/functional/spec/list_collector_primary_person.spec.js index baa56b8571..4b7647e564 100644 --- a/tests/functional/spec/list_collector_primary_person.spec.js +++ b/tests/functional/spec/list_collector_primary_person.spec.js @@ -10,117 +10,117 @@ import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_primary_p describe("Primary Person List Collector Survey", () => { describe("Given the user starts on the 'do you live here' question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_primary_person.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_primary_person.json"); }); - it.skip("When the user says they do not live there, and changes their answer to yes, then the user can't navigate to the list collector", () => { - $(PrimaryPersonListCollectorPage.noLabel()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.previous()).click(); - $(PrimaryPersonListCollectorPage.yesLabel()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - browser.url("questionnaire/list-collector"); - expect($(PrimaryPersonListCollectorPage.questionText()).getText()).to.contain("Do you live here"); + it.skip("When the user says they do not live there, and changes their answer to yes, then the user can't navigate to the list collector", async () => { + await $(PrimaryPersonListCollectorPage.noLabel()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.yesLabel()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await browser.url("questionnaire/list-collector"); + await expect(await $(PrimaryPersonListCollectorPage.questionText()).getText()).to.contain("Do you live here"); }); }); describe("Given the user starts on the 'do you live here' question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_primary_person.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_primary_person.json"); }); - it("When the user says that they do live there, then they are shown as the primary person", () => { - $(PrimaryPersonListCollectorPage.yesLabel()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + it("When the user says that they do live there, then they are shown as the primary person", async () => { + await $(PrimaryPersonListCollectorPage.yesLabel()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); - it("When the user adds another person, they are shown in the summary", () => { - $(ListCollectorPage.yesLabel()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + it("When the user adds another person, they are shown in the summary", async () => { + await $(ListCollectorPage.yesLabel()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); - it("When the user goes back and answers No, the primary person is not shown", () => { - $(ListCollectorPage.previous()).click(); - $(PrimaryPersonListCollectorPage.no()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(AnyoneUsuallyLiveAtPage.no()).click(); - $(AnyoneUsuallyLiveAtPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); + it("When the user goes back and answers No, the primary person is not shown", async () => { + await $(ListCollectorPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.no()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(AnyoneUsuallyLiveAtPage.no()).click(); + await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); }); - it("When the user adds the primary person again, then the primary person is first in the list", () => { - $(ListCollectorPage.previous()).click(); - $(AnyoneUsuallyLiveAtPage.previous()).click(); - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + it("When the user adds the primary person again, then the primary person is first in the list", async () => { + await $(ListCollectorPage.previous()).click(); + await $(AnyoneUsuallyLiveAtPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); - it("When the user views the summary, then it does not show the remove link for the primary person", () => { - expect($(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; - expect($(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; + it("When the user views the summary, then it does not show the remove link for the primary person", async () => { + await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; + await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; }); - it("When the user changes the primary person's name on the summary, then the name should be updated", () => { - $(ListCollectorPage.listEditLink(1)).click(); - $(ListCollectorEditPage.firstName()).setValue("Mark"); - $(ListCollectorEditPage.lastName()).setValue("Twain"); - $(ListCollectorEditPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); - expect($(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + it("When the user changes the primary person's name on the summary, then the name should be updated", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + await $(ListCollectorEditPage.firstName()).setValue("Mark"); + await $(ListCollectorEditPage.lastName()).setValue("Twain"); + await $(ListCollectorEditPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); - it("When the user views the summary, then it does not show the does anyone usually live here question", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect($("body").getText()).to.not.equal("usually live here"); + it("When the user views the summary, then it does not show the does anyone usually live here question", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect($("body").getText()).to.not.equal("usually live here"); }); - it("When the user attempts to submit, then they are shown the confirmation page", () => { - $(SectionSummaryPage.submit()).click(); - expect($(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, do you wish to submit"); + it("When the user attempts to submit, then they are shown the confirmation page", async () => { + await $(SectionSummaryPage.submit()).click(); + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, do you wish to submit"); }); - it("When the user submits, then they are allowed to submit the survey", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + it("When the user submits, then they are allowed to submit the survey", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); describe("Given the user starts on the 'do you live here' question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_primary_person.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_primary_person.json"); }); - it("When the user says they do not live there, then an empty list is displayed", () => { - $(PrimaryPersonListCollectorPage.no()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(AnyoneUsuallyLiveAtPage.no()).click(); - expect($(ListCollectorPage.listLabel(1)).isExisting()).to.be.false; + it("When the user says they do not live there, then an empty list is displayed", async () => { + await $(PrimaryPersonListCollectorPage.no()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(AnyoneUsuallyLiveAtPage.no()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).isExisting()).to.be.false; }); - it("When the user clicks on the add person button multiple times, then only one person is added", () => { - $(ListCollectorPage.previous()).click(); - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twain"); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); - expect($(ListCollectorPage.listLabel(2)).isExisting()).to.be.false; + it("When the user clicks on the add person button multiple times, then only one person is added", async () => { + await $(ListCollectorPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twain"); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).isExisting()).to.be.false; }); }); }); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index 6aa495fe9e..e76dc5e48b 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -7,289 +7,289 @@ import UkBasedPage from "../generated_pages/list_collector_section_summary/confi describe("List Collector Section Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_list_collector_section_summary.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_list_collector_section_summary.json"); }); - it("When I get to the section summary, Then the driving question should be visible.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).to.be.true; - expect($(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).to.contain("Yes"); + it("When I get to the section summary, Then the driving question should be visible.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).to.contain("Yes"); }); - it("When I add my own item, Then the item should be visible on the section summary and have correct values", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect($(SectionSummaryPage.companiesListLabel(1)).getText()).to.contain("Name of UK company or branch"); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - expect($(companiesListRowItem(1, 2)).getText()).to.contain("123"); - expect($(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); - expect($(companiesListRowItemAnchor(1)).getHTML()).to.contain("return_to=section-summary#company-or-branch-name"); - expect($(companiesListRowItemAnchor(2)).getHTML()).to.contain("return_to_answer_id=registration-number#registration-number"); - expect($(companiesListRowItemAnchor(3)).getHTML()).to.contain("return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio"); + it("When I add my own item, Then the item should be visible on the section summary and have correct values", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await $(SectionSummaryPage.companiesListLabel(1)).getText()).to.contain("Name of UK company or branch"); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); + await expect(await $(companiesListRowItemAnchor(1)).getHTML()).to.contain("return_to=section-summary#company-or-branch-name"); + await expect(await $(companiesListRowItemAnchor(2)).getHTML()).to.contain("return_to_answer_id=registration-number#registration-number"); + await expect(await $(companiesListRowItemAnchor(3)).getHTML()).to.contain("return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio"); }); - it("When I add multiple items, Then all the items should be visible on the section summary and have correct values", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", false); - anyMoreCompaniesYes(); - addCompany("Company C", "789", true); - anyMoreCompaniesNo(); - answerUkBasedQuestion(); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - expect($(companiesListRowItem(1, 2)).getText()).to.contain("123"); - expect($(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); - expect($(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); - expect($(companiesListRowItem(2, 2)).getText()).to.contain("456"); - expect($(companiesListRowItem(2, 3)).getText()).to.contain("No"); - expect($(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); - expect($(companiesListRowItem(3, 2)).getText()).to.contain("789"); - expect($(companiesListRowItem(3, 3)).getText()).to.contain("Yes"); + it("When I add multiple items, Then all the items should be visible on the section summary and have correct values", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", false); + await anyMoreCompaniesYes(); + await addCompany("Company C", "789", true); + await anyMoreCompaniesNo(); + await answerUkBasedQuestion(); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); + await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).to.contain("No"); + await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("789"); + await expect(await $(companiesListRowItem(3, 3)).getText()).to.contain("Yes"); }); - it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - removeFirstCompany(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($("body").getText()).to.not.have.string("Company A"); - expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await removeFirstCompany(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $("body").getText()).to.not.have.string("Company A"); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; }); - it("When I remove an item but the list collector is still on the path, Then the placeholder text should be visible on the section summary.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - removeFirstCompany(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($("body").getText()).to.contain("No UK company or branch added"); + it("When I remove an item but the list collector is still on the path, Then the placeholder text should be visible on the section summary.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await removeFirstCompany(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $("body").getText()).to.contain("No UK company or branch added"); }); - it("When I have multiple items in the list and I remove the first item, Then only the item that was not deleted should be visible on the section summary.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "234", true); - anyMoreCompaniesNo(); - removeFirstCompany(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($("body").getText()).to.not.have.string("Company A"); - expect($("body").getText()).to.contain("Company B"); + it("When I have multiple items in the list and I remove the first item, Then only the item that was not deleted should be visible on the section summary.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "234", true); + await anyMoreCompaniesNo(); + await removeFirstCompany(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $("body").getText()).to.not.have.string("Company A"); + await expect(await $("body").getText()).to.contain("Company B"); }); - it("When I add an item and relevant data and answer No on the additional items page, Then I should get to the section summary page.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + it("When I add an item and relevant data and answer No on the additional items page, Then I should get to the section summary page.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; }); - it("When I add an item and relevant data and answer Yes on the additional items page, Then I should be able to and add a new item and relevant data.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - expect($(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).isExisting()).to.be.true; - expect($(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).to.be.true; - expect($(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).to.be.true; - expect($(AnyCompaniesOrBranchesAddPage.heading()).getText()).to.contain( + it("When I add an item and relevant data and answer Yes on the additional items page, Then I should be able to and add a new item and relevant data.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).isExisting()).to.be.true; + await expect(await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).to.be.true; + await expect(await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).to.be.true; + await expect(await $(AnyCompaniesOrBranchesAddPage.heading()).getText()).to.contain( "Give details about the company or branch that undertakes general insurance business" ); }); - it("When I add an item and relevant data, Then I should be able to edit that item from the section summary page.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - $(SectionSummaryPage.companiesListEditLink(1)).click(); - expect(browser.getUrl()).to.contain("edit-company/?return_to=section-summary"); - expect($(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).to.equal("Company A"); + it("When I add an item and relevant data, Then I should be able to edit that item from the section summary page.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await $(SectionSummaryPage.companiesListEditLink(1)).click(); + await expect(await browser.getUrl()).to.contain("edit-company/?return_to=section-summary"); + await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).to.equal("Company A"); }); - it("When I edit an item after adding it, Then I should be redirected to the summary page", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - $(SectionSummaryPage.companiesListEditLink(1)).click(); - $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); - $(AnyCompaniesOrBranchesAddPage.submit()).click(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Changed Company"); + it("When I edit an item after adding it, Then I should be redirected to the summary page", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await $(SectionSummaryPage.companiesListEditLink(1)).click(); + await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); + await $(AnyCompaniesOrBranchesAddPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Changed Company"); }); - it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", () => { - drivingQuestionNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; - expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; - $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; - expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; - expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", async () => { + await drivingQuestionNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; }); - it("When I add an item and relevant data but change my answer to the driving question to No, Then I should see the original item on the summary if change the answer back to Yes.", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); - drivingQuestionNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; - expect($("body").getText()).to.not.have.string("No UK company or branch added"); - expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; - $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); - drivingQuestionYes(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - expect($(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - expect($(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; - expect($(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; - expect($(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + it("When I add an item and relevant data but change my answer to the driving question to No, Then I should see the original item on the summary if change the answer back to Yes.", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + await drivingQuestionNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + await expect(await $("body").getText()).to.not.have.string("No UK company or branch added"); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); + await drivingQuestionYes(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; }); - it("When I add another company from the summary page, Then I am asked if I want to add any more company before accessing the section summary", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesNo(); - $(SectionSummaryPage.companiesListAddLink()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - expect(browser.getUrl()).to.contain("?return_to=section-summary"); - addCompany("Company B", "456", true); - expect(browser.getUrl()).to.contain(AnyCompaniesOrBranchesPage.url()); - expect($("body").getText()).to.have.string("Company A"); - expect($("body").getText()).to.have.string("Company B"); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I add another company from the summary page, Then I am asked if I want to add any more company before accessing the section summary", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesNo(); + await $(SectionSummaryPage.companiesListAddLink()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await addCompany("Company B", "456", true); + await expect(await browser.getUrl()).to.contain(AnyCompaniesOrBranchesPage.url()); + await expect(await $("body").getText()).to.have.string("Company A"); + await expect(await $("body").getText()).to.have.string("Company B"); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I add three companies, Then I am prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesYes(); - addCompany("Company C", "789", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(UkBasedPage.url()); + it("When I add three companies, Then I am prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesYes(); + await addCompany("Company C", "789", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); }); - it("When I add less than 3 companies, Then I am not prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I add less than 3 companies, Then I am not prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I add more than 3 companies, Then I am not prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesYes(); - addCompany("Company C", "789", true); - anyMoreCompaniesYes(); - addCompany("Company D", "135", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I add more than 3 companies, Then I am not prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesYes(); + await addCompany("Company C", "789", true); + await anyMoreCompaniesYes(); + await addCompany("Company D", "135", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I add another company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - $(SectionSummaryPage.companiesListAddLink()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - expect(browser.getUrl()).to.contain("?return_to=section-summary"); - addCompany("Company C", "234", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(UkBasedPage.url()); - answerUkBasedQuestion(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I add another company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await $(SectionSummaryPage.companiesListAddLink()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await addCompany("Company C", "234", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await answerUkBasedQuestion(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I remove a company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesYes(); - addCompany("Company C", "234", true); - anyMoreCompaniesYes(); - addCompany("Company D", "345", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - removeFirstCompany(); - expect(browser.getUrl()).to.contain(UkBasedPage.url()); - answerUkBasedQuestion(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I remove a company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesYes(); + await addCompany("Company C", "234", true); + await anyMoreCompaniesYes(); + await addCompany("Company D", "345", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await removeFirstCompany(); + await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await answerUkBasedQuestion(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I remove a company from the summary page, and the amount then totals to 3, but the confirmation question has already been answered, Then I am not prompted with the confirmation question", () => { - drivingQuestionYes(); - addCompany("Company A", "123", true); - anyMoreCompaniesYes(); - addCompany("Company B", "456", true); - anyMoreCompaniesYes(); - addCompany("Company C", "234", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(UkBasedPage.url()); - answerUkBasedQuestion(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - $(SectionSummaryPage.companiesListAddLink()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - expect(browser.getUrl()).to.contain("?return_to=section-summary"); - addCompany("Company C", "234", true); - anyMoreCompaniesNo(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); - removeFirstCompany(); - expect(browser.getUrl()).to.contain(SectionSummaryPage.url()); + it("When I remove a company from the summary page, and the amount then totals to 3, but the confirmation question has already been answered, Then I am not prompted with the confirmation question", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesYes(); + await addCompany("Company C", "234", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await answerUkBasedQuestion(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await $(SectionSummaryPage.companiesListAddLink()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); + await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await addCompany("Company C", "234", true); + await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await removeFirstCompany(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); }); }); -const drivingQuestionYes = () => { - $(AnyCompaniesOrBranchesDrivingQuestionPage.yes()).click(); - $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); +const drivingQuestionYes = async () => { + await $(AnyCompaniesOrBranchesDrivingQuestionPage.yes()).click(); + await $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); }; -const drivingQuestionNo = () => { - $(AnyCompaniesOrBranchesDrivingQuestionPage.no()).click(); - $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); +const drivingQuestionNo = async () => { + await $(AnyCompaniesOrBranchesDrivingQuestionPage.no()).click(); + await $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); }; -const addCompany = (name, number, authorised) => { - $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue(name); - $(AnyCompaniesOrBranchesAddPage.registrationNumber()).setValue(number); +const addCompany = async (name, number, authorised) => { + await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue(name); + await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).setValue(number); if (authorised) { - $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).click(); + await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).click(); } else { - $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); + await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); } - $(AnyCompaniesOrBranchesAddPage.submit()).click(); + await $(AnyCompaniesOrBranchesAddPage.submit()).click(); }; -const anyMoreCompaniesYes = () => { - $(AnyCompaniesOrBranchesPage.yes()).click(); - $(AnyCompaniesOrBranchesPage.submit()).click(); +const anyMoreCompaniesYes = async () => { + await $(AnyCompaniesOrBranchesPage.yes()).click(); + await $(AnyCompaniesOrBranchesPage.submit()).click(); }; -const anyMoreCompaniesNo = () => { - $(AnyCompaniesOrBranchesPage.no()).click(); - $(AnyCompaniesOrBranchesPage.submit()).click(); +const anyMoreCompaniesNo = async () => { + await $(AnyCompaniesOrBranchesPage.no()).click(); + await $(AnyCompaniesOrBranchesPage.submit()).click(); }; -const removeFirstCompany = () => { - $(SectionSummaryPage.companiesListRemoveLink(1)).click(); - $(AnyCompaniesOrBranchesRemovePage.yes()).click(); - $(AnyCompaniesOrBranchesRemovePage.submit()).click(); +const removeFirstCompany = async () => { + await $(SectionSummaryPage.companiesListRemoveLink(1)).click(); + await $(AnyCompaniesOrBranchesRemovePage.yes()).click(); + await $(AnyCompaniesOrBranchesRemovePage.submit()).click(); }; -const answerUkBasedQuestion = () => { - $(UkBasedPage.yes()).click(); - $(UkBasedPage.submit()).click(); +const answerUkBasedQuestion = async () => { + await $(UkBasedPage.yes()).click(); + await $(UkBasedPage.submit()).click(); }; const companiesListRowItem = (row, index) => { diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector_variants.spec.js index 64238c73c7..7bc017d46c 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector_variants.spec.js @@ -9,94 +9,94 @@ import ThankYouPage from "../base_pages/thank-you.page.js"; describe("List Collector With Variants", () => { describe("Given that a person lives in house", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_variants.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_variants.json"); }); - it("The user is asked questions about whether they live there", () => { - $(YouLiveHerePage.yes()).click(); - $(YouLiveHerePage.submit()).click(); - expect($(ListCollectorPage.questionText()).getText()).to.equal("Does anyone else live at 1 Pleasant Lane?"); + it("The user is asked questions about whether they live there", async () => { + await $(YouLiveHerePage.yes()).click(); + await $(YouLiveHerePage.submit()).click(); + await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone else live at 1 Pleasant Lane?"); }); - it("The user is able to add members of the household", () => { - $(ListCollectorPage.anyoneElseYes()).click(); - $(ListCollectorPage.submit()).click(); - expect($(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + it("The user is able to add members of the household", async () => { + await $(ListCollectorPage.anyoneElseYes()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); }); - it("The user can see all household members in the summary", () => { + it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); }); - it("The questionnaire has the correct question text on the change and remove pages", () => { - $(ListCollectorPage.listEditLink(1)).click(); - expect($(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person?"); - $(ListCollectorEditPage.previous()).click(); - $(ListCollectorPage.listRemoveLink(1)).click(); - expect($(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person?"); - $(ListCollectorRemovePage.previous()).click(); + it("The questionnaire has the correct question text on the change and remove pages", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + await expect(await $(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person?"); + await $(ListCollectorEditPage.previous()).click(); + await $(ListCollectorPage.listRemoveLink(1)).click(); + await expect(await $(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person?"); + await $(ListCollectorRemovePage.previous()).click(); }); - it("The questionnaire shows the confirmation page when no more people to add", () => { - $(ListCollectorPage.anyoneElseNo()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + it("The questionnaire shows the confirmation page when no more people to add", async () => { + await $(ListCollectorPage.anyoneElseNo()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("The questionnaire allows submission", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + it("The questionnaire allows submission", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); }); describe("Given a person does not live in house", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_variants.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_variants.json"); }); - it("The user is asked questions about whether they live there", () => { - $(YouLiveHerePage.no()).click(); - $(YouLiveHerePage.submit()).click(); - expect($(ListCollectorPage.questionText()).getText()).to.equal("Does anyone live at 1 Pleasant Lane?"); + it("The user is asked questions about whether they live there", async () => { + await $(YouLiveHerePage.no()).click(); + await $(YouLiveHerePage.submit()).click(); + await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone live at 1 Pleasant Lane?"); }); - it("The user is able to add members of the household", () => { - $(ListCollectorPage.anyoneElseYes()).click(); - $(ListCollectorPage.submit()).click(); - expect($(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + it("The user is able to add members of the household", async () => { + await $(ListCollectorPage.anyoneElseYes()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); }); - it("The user can see all household members in the summary", () => { + it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); }); - it("The questionnaire has the correct question text on the change and remove pages", () => { - $(ListCollectorPage.listEditLink(1)).click(); - expect($(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); - $(ListCollectorEditPage.previous()).click(); - $(ListCollectorPage.listRemoveLink(1)).click(); - expect($(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person who isn’t you?"); - $(ListCollectorRemovePage.previous()).click(); + it("The questionnaire has the correct question text on the change and remove pages", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + await expect(await $(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); + await $(ListCollectorEditPage.previous()).click(); + await $(ListCollectorPage.listRemoveLink(1)).click(); + await expect(await $(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person who isn’t you?"); + await $(ListCollectorRemovePage.previous()).click(); }); - it("The questionnaire shows the confirmation page when no more people to add", () => { - $(ListCollectorPage.anyoneElseNo()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + it("The questionnaire shows the confirmation page when no more people to add", async () => { + await $(ListCollectorPage.anyoneElseNo()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("The questionnaire allows submission", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.url()); + it("The questionnaire allows submission", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.url()); }); }); }); diff --git a/tests/functional/spec/list_collector_variants_primary_person.spec.js b/tests/functional/spec/list_collector_variants_primary_person.spec.js index 79dfbc3403..9765ba63b2 100644 --- a/tests/functional/spec/list_collector_variants_primary_person.spec.js +++ b/tests/functional/spec/list_collector_variants_primary_person.spec.js @@ -8,114 +8,114 @@ import ThankYouPage from "../base_pages/thank-you.page.js"; describe("List collector with variants primary person", () => { describe("Given that person lives in house", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); - it("When the user is asked questions about whether they like variant, Then they are routed to section asking if they live in the house", () => { - $(VariantBlockPage.yes()).click(); - $(VariantBlockPage.submit()).click(); - expect($(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here? (variant)"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); + it("When the user is asked questions about whether they like variant, Then they are routed to section asking if they live in the house", async () => { + await $(VariantBlockPage.yes()).click(); + await $(VariantBlockPage.submit()).click(); + await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here? (variant)"); }); }); }); describe("Given the user starts on the 'Do you like variant' question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); }); - it("When the user says that they do live there, Then they are shown as the primary person", () => { - $(VariantBlockPage.yes()).click(); - $(VariantBlockPage.submit()).click(); - $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("John"); - $(ListCollectorAddPage.lastName()).setValue("Doe"); - $(ListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); + it("When the user says that they do live there, Then they are shown as the primary person", async () => { + await $(VariantBlockPage.yes()).click(); + await $(VariantBlockPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); }); - it("When the user adds another person, Then they are shown in the list collector summary", () => { - $(ListCollectorPage.yesLabel()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + it("When the user adds another person, Then they are shown in the list collector summary", async () => { + await $(ListCollectorPage.yesLabel()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); - it("When the user goes back and answers 'No' for 'Do you live here' question, Then the primary person is not shown", () => { - $(ListCollectorPage.previous()).click(); - $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); + it("When the user goes back and answers 'No' for 'Do you live here' question, Then the primary person is not shown", async () => { + await $(ListCollectorPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); }); - it("When the user adds another person, Then the user is able to add members of the household", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - expect($(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + it("When the user adds another person, Then the user is able to add members of the household", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); }); - it("When the user adds the primary person again, Then the primary person is first in the list", () => { - $(ListCollectorPage.previous()).click(); - $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Mark"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + it("When the user adds the primary person again, Then the primary person is first in the list", async () => { + await $(ListCollectorPage.previous()).click(); + await $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Mark"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); - it("When the user views the summary, Then it does not show the remove link for the primary person", () => { - expect($(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; - expect($(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; + it("When the user views the summary, Then it does not show the remove link for the primary person", async () => { + await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; + await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; }); - it("When the user changes the primary person's name on the summary, Then the name should be updated", () => { - $(ListCollectorPage.listEditLink(1)).click(); - $(EditPersonPage.firstName()).setValue("John"); - $(EditPersonPage.lastName()).setValue("Doe"); - $(EditPersonPage.submit()).click(); - expect($(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); - expect($(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + it("When the user changes the primary person's name on the summary, Then the name should be updated", async () => { + await $(ListCollectorPage.listEditLink(1)).click(); + await $(EditPersonPage.firstName()).setValue("John"); + await $(EditPersonPage.lastName()).setValue("Doe"); + await $(EditPersonPage.submit()).click(); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); - it("When the user answers 'no' to add any person, Then the questionnaire shows the confirmation page", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + it("When the user answers 'no' to add any person, Then the questionnaire shows the confirmation page", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); - it("When the user attempts to submit, Then they are shown the confirmation page", () => { - expect($(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + it("When the user attempts to submit, Then they are shown the confirmation page", async () => { + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); }); - it("When user updates the variant answer, Then it should come back to summary screen with updated answer", () => { - $(SubmitPage.variantAnswerEdit()).click(); - $(VariantBlockPage.no()).click(); - $(VariantBlockPage.submit()).click(); - expect($(SubmitPage.variantAnswer()).getText()).to.equal("No"); + it("When user updates the variant answer, Then it should come back to summary screen with updated answer", async () => { + await $(SubmitPage.variantAnswerEdit()).click(); + await $(VariantBlockPage.no()).click(); + await $(VariantBlockPage.submit()).click(); + await expect(await $(SubmitPage.variantAnswer()).getText()).to.equal("No"); }); - it("When the user submits, Then they are allowed to submit the survey", () => { - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + it("When the user submits, Then they are allowed to submit the survey", async () => { + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); }); describe("Given the user starts on the 'Do you like variant' question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); }); - it("When the user answers 'No' for variant question, Then they are routed to section asking if they live in the house", () => { - $(VariantBlockPage.no()).click(); - $(VariantBlockPage.submit()).click(); - expect($(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here?"); + it("When the user answers 'No' for variant question, Then they are routed to section asking if they live in the house", async () => { + await $(VariantBlockPage.no()).click(); + await $(VariantBlockPage.submit()).click(); + await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here?"); }); - it("When the user says they do not live there and anyone else, Then confirmation screen is displayed", () => { - $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); + it("When the user says they do not live there and anyone else, Then confirmation screen is displayed", async () => { + await $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); - expect($(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); }); }); diff --git a/tests/functional/spec/mobile_number.spec.js b/tests/functional/spec/mobile_number.spec.js index 786468dd23..494e8d015a 100644 --- a/tests/functional/spec/mobile_number.spec.js +++ b/tests/functional/spec/mobile_number.spec.js @@ -2,22 +2,22 @@ import MobileNumberBlockPage from "../generated_pages/mobile_number/mobile-numbe import submitPage from "../generated_pages/mobile_number/submit.page"; describe("Mobile number validation", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_mobile_number.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_mobile_number.json"); }); - it("Given I am asked to enter Mobile no, When I enter a valid mobile number with no prefix and submit, Then confirmation section is displayed with entered mobile number", () => { - $(MobileNumberBlockPage.mobileNumber()).setValue(7712345678); - $(MobileNumberBlockPage.submit()).click(); - expect($(submitPage.mobileNumberAnswer()).getText()).to.contain("7712345678"); + it("Given I am asked to enter Mobile no, When I enter a valid mobile number with no prefix and submit, Then confirmation section is displayed with entered mobile number", async () => { + await $(MobileNumberBlockPage.mobileNumber()).setValue(7712345678); + await $(MobileNumberBlockPage.submit()).click(); + await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("7712345678"); }); - it("Given I am asked to enter Mobile no, When I enter a valid mobile number with prefix (+44) and submit, Then confirmation section is displayed with entered mobile number", () => { - $(MobileNumberBlockPage.mobileNumber()).setValue("+447712345678"); - $(MobileNumberBlockPage.submit()).click(); - expect($(submitPage.mobileNumberAnswer()).getText()).to.contain("+447712345678"); + it("Given I am asked to enter Mobile no, When I enter a valid mobile number with prefix (+44) and submit, Then confirmation section is displayed with entered mobile number", async () => { + await $(MobileNumberBlockPage.mobileNumber()).setValue("+447712345678"); + await $(MobileNumberBlockPage.submit()).click(); + await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("+447712345678"); }); - it("Given I am asked to enter Mobile no, When I enter an invalid mobile number and submit, Then an error screen with invalid number information is displayed", () => { - $(MobileNumberBlockPage.mobileNumber()).setValue("12345678"); - $(MobileNumberBlockPage.submit()).click(); - expect($("body").getText()).to.contain("Enter a UK mobile number in a valid format"); + it("Given I am asked to enter Mobile no, When I enter an invalid mobile number and submit, Then an error screen with invalid number information is displayed", async () => { + await $(MobileNumberBlockPage.mobileNumber()).setValue("12345678"); + await $(MobileNumberBlockPage.submit()).click(); + await expect(await $("body").getText()).to.contain("Enter a UK mobile number in a valid format"); }); }); diff --git a/tests/functional/spec/multiple_answers.spec.js b/tests/functional/spec/multiple_answers.spec.js index d2803d3d40..582bfec62d 100644 --- a/tests/functional/spec/multiple_answers.spec.js +++ b/tests/functional/spec/multiple_answers.spec.js @@ -2,86 +2,86 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; import AgeBlock from "../generated_pages/multiple_answers/age-block.page"; import SubmitPage from "../generated_pages/multiple_answers/submit.page.js"; -function answerAllQuestions() { - $(AboutYou.textfield()).setValue("John Doe"); - $(AboutYou.dateday()).setValue("1"); - $(AboutYou.datemonth()).setValue("1"); - $(AboutYou.dateyear()).setValue("1995"); - $(AboutYou.checkboxBmw()).click(); - $(AboutYou.radioYes()).click(); - $(AboutYou.currency()).setValue("50000"); - $(AboutYou.monthYearDateMonth()).setValue("10"); - $(AboutYou.monthYearDateYear()).setValue("2021"); - $(AboutYou.dropdown()).selectByAttribute("value", "Silver"); - $(AboutYou.unit()).setValue("10000"); - $(AboutYou.durationMonths()).setValue("3"); - $(AboutYou.durationYears()).setValue("3"); - $(AboutYou.yearDateYear()).setValue("2019"); - $(AboutYou.number()).setValue("5"); - $(AboutYou.percentage()).setValue("3"); - $(AboutYou.mobileNumber()).setValue("07700900111"); - $(AboutYou.textarea()).setValue("Fuel type petrol"); - $(AboutYou.submit()).click(); +async function answerAllQuestions() { + await $(AboutYou.textfield()).setValue("John Doe"); + await $(AboutYou.dateday()).setValue("1"); + await $(AboutYou.datemonth()).setValue("1"); + await $(AboutYou.dateyear()).setValue("1995"); + await $(AboutYou.checkboxBmw()).click(); + await $(AboutYou.radioYes()).click(); + await $(AboutYou.currency()).setValue("50000"); + await $(AboutYou.monthYearDateMonth()).setValue("10"); + await $(AboutYou.monthYearDateYear()).setValue("2021"); + await $(AboutYou.dropdown()).selectByAttribute("value", "Silver"); + await $(AboutYou.unit()).setValue("10000"); + await $(AboutYou.durationMonths()).setValue("3"); + await $(AboutYou.durationYears()).setValue("3"); + await $(AboutYou.yearDateYear()).setValue("2019"); + await $(AboutYou.number()).setValue("5"); + await $(AboutYou.percentage()).setValue("3"); + await $(AboutYou.mobileNumber()).setValue("07700900111"); + await $(AboutYou.textarea()).setValue("Fuel type petrol"); + await $(AboutYou.submit()).click(); - $(AgeBlock.age()).setValue("10"); - $(AgeBlock.ageEstimateThisAgeIsAnEstimate()).click(); - $(AgeBlock.submit()).click(); + await $(AgeBlock.age()).setValue("10"); + await $(AgeBlock.ageEstimateThisAgeIsAnEstimate()).click(); + await $(AgeBlock.submit()).click(); } describe("Multiple Answers", () => { describe("Given I have completed a questionnaire that has multiple answers per question", () => { - beforeEach("Load the questionnaire and answer all questions", () => { - browser.openQuestionnaire("test_multiple_answers.json"); + beforeEach("Load the questionnaire and answer all questions", async () => { + await browser.openQuestionnaire("test_multiple_answers.json"); answerAllQuestions(); }); - it("When I am on the summary, Then all answers are displayed", () => { - expect($(SubmitPage.textfieldAnswer()).getText()).to.equal("John Doe"); - expect($(SubmitPage.dateAnswer()).getText()).to.equal("1 January 1995"); - expect($(SubmitPage.checkboxAnswer()).getText()).to.equal("BMW"); - expect($(SubmitPage.radioAnswer()).getText()).to.equal("Yes"); - expect($(SubmitPage.currencyAnswer()).getText()).to.equal("£50,000.00"); - expect($(SubmitPage.monthYearDateAnswer()).getText()).to.equal("October 2021"); - expect($(SubmitPage.dropdownAnswer()).getText()).to.equal("Silver"); - expect($(SubmitPage.unitAnswer()).getText()).to.equal("10,000 mi"); - expect($(SubmitPage.durationAnswer()).getText()).to.equal("3 years 3 months"); - expect($(SubmitPage.yearDateAnswer()).getText()).to.equal("2019"); - expect($(SubmitPage.numberAnswer()).getText()).to.equal("5"); - expect($(SubmitPage.percentageAnswer()).getText()).to.equal("3%"); - expect($(SubmitPage.mobileNumberAnswer()).getText()).to.equal("07700900111"); - expect($(SubmitPage.textareaAnswer()).getText()).to.equal("Fuel type petrol"); + it("When I am on the summary, Then all answers are displayed", async () => { + await expect(await $(SubmitPage.textfieldAnswer()).getText()).to.equal("John Doe"); + await expect(await $(SubmitPage.dateAnswer()).getText()).to.equal("1 January 1995"); + await expect(await $(SubmitPage.checkboxAnswer()).getText()).to.equal("BMW"); + await expect(await $(SubmitPage.radioAnswer()).getText()).to.equal("Yes"); + await expect(await $(SubmitPage.currencyAnswer()).getText()).to.equal("£50,000.00"); + await expect(await $(SubmitPage.monthYearDateAnswer()).getText()).to.equal("October 2021"); + await expect(await $(SubmitPage.dropdownAnswer()).getText()).to.equal("Silver"); + await expect(await $(SubmitPage.unitAnswer()).getText()).to.equal("10,000 mi"); + await expect(await $(SubmitPage.durationAnswer()).getText()).to.equal("3 years 3 months"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.equal("2019"); + await expect(await $(SubmitPage.numberAnswer()).getText()).to.equal("5"); + await expect(await $(SubmitPage.percentageAnswer()).getText()).to.equal("3%"); + await expect(await $(SubmitPage.mobileNumberAnswer()).getText()).to.equal("07700900111"); + await expect(await $(SubmitPage.textareaAnswer()).getText()).to.equal("Fuel type petrol"); - expect($(SubmitPage.ageAnswer()).getText()).to.equal("10"); - expect($(SubmitPage.ageEstimateAnswer()).getText()).to.equal("This age is an estimate"); + await expect(await $(SubmitPage.ageAnswer()).getText()).to.equal("10"); + await expect(await $(SubmitPage.ageEstimateAnswer()).getText()).to.equal("This age is an estimate"); }); - it("When I click 'Change' an answer, Then I should be taken to the correct page and the answer input should be focused", () => { - $(SubmitPage.currencyAnswerEdit()).click(); - expect(browser.getUrl()).to.contain(AboutYou.url()); - expect(browser.getUrl()).to.contain(AboutYou.currency()); - expect($(AboutYou.currency()).isFocused()).to.be.true; + it("When I click 'Change' an answer, Then I should be taken to the correct page and the answer input should be focused", async () => { + await $(SubmitPage.currencyAnswerEdit()).click(); + await expect(await browser.getUrl()).to.contain(AboutYou.url()); + await expect(await browser.getUrl()).to.contain(AboutYou.currency()); + await expect(await $(AboutYou.currency()).isFocused()).to.be.true; }); }); describe("Given I have launched a questionnaire that has multiple answers per question", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_multiple_answers.json"); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_multiple_answers.json"); }); - it("When I am on the question page, Then all answers should have a label/legend", () => { - expect($(AboutYou.dateLegend()).getText()).to.equal("What is your date of birth?"); - expect($(AboutYou.monthYearDateLegend()).getText()).to.equal("When would you like the car by?"); - expect($(AboutYou.radioLegend()).getText()).to.equal("Would you like the sports package?"); - expect($(AboutYou.durationLegend()).getText()).to.equal("How long have you had your licence?"); - expect($(AboutYou.checkboxLegend()).getText()).to.equal("What are your favourite car brands?"); - expect($(AboutYou.textfieldLabel()).getText()).to.equal("Your name"); - expect($(AboutYou.currencyLabel()).getText()).to.equal("What is your budget?"); - expect($(AboutYou.dropdownLabel()).getText()).to.equal("Select a colour"); - expect($(AboutYou.unitLabel()).getText()).to.equal("Max mileage"); - expect($(AboutYou.numberLabel()).getText()).to.equal("How many seats?"); - expect($(AboutYou.percentageLabel()).getText()).to.equal("Max CO2 emissions"); - expect($(AboutYou.mobileNumberLabel()).getText()).to.equal("What is your mobile number?"); - expect($(AboutYou.textareaLabel()).getText()).to.equal("Other comments"); + it("When I am on the question page, Then all answers should have a label/legend", async () => { + await expect(await $(AboutYou.dateLegend()).getText()).to.equal("What is your date of birth?"); + await expect(await $(AboutYou.monthYearDateLegend()).getText()).to.equal("When would you like the car by?"); + await expect(await $(AboutYou.radioLegend()).getText()).to.equal("Would you like the sports package?"); + await expect(await $(AboutYou.durationLegend()).getText()).to.equal("How long have you had your licence?"); + await expect(await $(AboutYou.checkboxLegend()).getText()).to.equal("What are your favourite car brands?"); + await expect(await $(AboutYou.textfieldLabel()).getText()).to.equal("Your name"); + await expect(await $(AboutYou.currencyLabel()).getText()).to.equal("What is your budget?"); + await expect(await $(AboutYou.dropdownLabel()).getText()).to.equal("Select a colour"); + await expect(await $(AboutYou.unitLabel()).getText()).to.equal("Max mileage"); + await expect(await $(AboutYou.numberLabel()).getText()).to.equal("How many seats?"); + await expect(await $(AboutYou.percentageLabel()).getText()).to.equal("Max CO2 emissions"); + await expect(await $(AboutYou.mobileNumberLabel()).getText()).to.equal("What is your mobile number?"); + await expect(await $(AboutYou.textareaLabel()).getText()).to.equal("Other comments"); }); }); }); diff --git a/tests/functional/spec/multiple_piping.spec.js b/tests/functional/spec/multiple_piping.spec.js index 74006d85d3..790fcae566 100644 --- a/tests/functional/spec/multiple_piping.spec.js +++ b/tests/functional/spec/multiple_piping.spec.js @@ -6,29 +6,29 @@ describe("Piping", () => { const pipingSchema = "test_multiple_piping.json"; describe("Multiple piping into question and answer", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire(pipingSchema); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire(pipingSchema); }); - it("Given I enter multiple fields in one question, When I navigate to the multiple piping answer, Then I should see all values piped into an answer", () => { - $(AddressPage.addressLine1()).setValue("1 The ONS"); - $(AddressPage.townCity()).setValue("Newport"); - $(AddressPage.postcode()).setValue("NP10 8XG"); - $(AddressPage.country()).setValue("Wales"); - $(AddressPage.submit()).click(); - $(TextfieldPage.firstText()).setValue("Fireman"); - $(TextfieldPage.secondText()).setValue("Sam"); - $(TextfieldPage.submit()).click(); - expect($(MultiplePipingPage.answerAddressLabel()).getText()).to.contain("1 The ONS, Newport, NP10 8XG, Wales"); + it("Given I enter multiple fields in one question, When I navigate to the multiple piping answer, Then I should see all values piped into an answer", async () => { + await $(AddressPage.addressLine1()).setValue("1 The ONS"); + await $(AddressPage.townCity()).setValue("Newport"); + await $(AddressPage.postcode()).setValue("NP10 8XG"); + await $(AddressPage.country()).setValue("Wales"); + await $(AddressPage.submit()).click(); + await $(TextfieldPage.firstText()).setValue("Fireman"); + await $(TextfieldPage.secondText()).setValue("Sam"); + await $(TextfieldPage.submit()).click(); + await expect(await $(MultiplePipingPage.answerAddressLabel()).getText()).to.contain("1 The ONS, Newport, NP10 8XG, Wales"); }); - it("Given I enter values in multiple questions, When I navigate to the multiple piping question, Then I should see both values piped into the question", () => { - $(AddressPage.addressLine1()).setValue("1 The ONS"); - $(AddressPage.submit()).click(); - $(TextfieldPage.firstText()).setValue("Fireman"); - $(TextfieldPage.secondText()).setValue("Sam"); - $(TextfieldPage.submit()).click(); - expect($(MultiplePipingPage.questionText()).getText()).to.contain("Does Fireman Sam live at 1 The ONS"); + it("Given I enter values in multiple questions, When I navigate to the multiple piping question, Then I should see both values piped into the question", async () => { + await $(AddressPage.addressLine1()).setValue("1 The ONS"); + await $(AddressPage.submit()).click(); + await $(TextfieldPage.firstText()).setValue("Fireman"); + await $(TextfieldPage.secondText()).setValue("Sam"); + await $(TextfieldPage.submit()).click(); + await expect(await $(MultiplePipingPage.questionText()).getText()).to.contain("Does Fireman Sam live at 1 The ONS"); }); }); }); diff --git a/tests/functional/spec/my_account_header_link.spec.js b/tests/functional/spec/my_account_header_link.spec.js index 48721d762c..e94c94f742 100644 --- a/tests/functional/spec/my_account_header_link.spec.js +++ b/tests/functional/spec/my_account_header_link.spec.js @@ -1,9 +1,10 @@ import IntroductionPage from "../generated_pages/introduction/introduction.page"; describe("My Account header link", () => { - it("Given I start a survey, When I visit a page then I should not see the My account button", () => { - browser.openQuestionnaire("test_introduction.json"); - expect(browser.getUrl()).to.contain("introduction"); - expect($(IntroductionPage.myAccountLink()).isExisting()).to.be.false; + it("Given I start a survey, When I visit a page then I should not see the My account button", async () => { + await browser.openQuestionnaire("test_introduction.json"); + await browser.pause(100); + await expect(await browser.getUrl()).to.contain("introduction"); + await expect(await $(IntroductionPage.myAccountLink()).isExisting()).to.be.false; }); }); diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index 6c442629f1..a1bddbcaee 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -4,95 +4,95 @@ import DetailAnswer from "../generated_pages/numbers/detail-answer-block.page"; import SubmitPage from "../generated_pages/numbers/submit.page"; describe("Number validation", () => { - before(() => { - browser.openQuestionnaire("test_numbers.json"); + before(async () => { + await browser.openQuestionnaire("test_numbers.json"); }); describe("Given I am completing the test numbers questionnaire,", () => { - it("When I am on the set minimum and maximum page, Then each field has a label", () => { - expect($(SetMinMax.setMinimumLabelDescription()).getText()).to.contain("This is a description of the minimum value"); - expect($(SetMinMax.setMaximumLabelDescription()).getText()).to.contain("This is a description of the maximum value"); + it("When I am on the set minimum and maximum page, Then each field has a label", async () => { + await expect(await $(SetMinMax.setMinimumLabelDescription()).getText()).to.contain("This is a description of the minimum value"); + await expect(await $(SetMinMax.setMaximumLabelDescription()).getText()).to.contain("This is a description of the maximum value"); }); - it("When I enter values outside of the set range, Then the correct error messages are displayed", () => { - $(SetMinMax.setMinimum()).setValue("10"); - $(SetMinMax.setMaximum()).setValue("1020"); - $(SetMinMax.submit()).click(); - - $(TestMinMax.testRange()).setValue("9"); - $(TestMinMax.testRangeExclusive()).setValue("10"); - $(TestMinMax.testMin()).setValue("0"); - $(TestMinMax.testMax()).setValue("12345"); - $(TestMinMax.testMinExclusive()).setValue("123"); - $(TestMinMax.testMaxExclusive()).setValue("12345"); - $(TestMinMax.testPercent()).setValue("101"); - $(TestMinMax.testDecimal()).setValue("5.4"); - $(TestMinMax.submit()).click(); - - expect($(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to 10"); - expect($(TestMinMax.errorNumber(2)).getText()).to.contain("Enter an answer more than 10"); - expect($(TestMinMax.errorNumber(3)).getText()).to.contain("Enter an answer more than or equal to 123"); - expect($(TestMinMax.errorNumber(4)).getText()).to.contain("Enter an answer less than or equal to 1,234"); - expect($(TestMinMax.errorNumber(5)).getText()).to.contain("Enter an answer more than 123"); - expect($(TestMinMax.errorNumber(6)).getText()).to.contain("Enter an answer less than 1,234"); - expect($(TestMinMax.errorNumber(7)).getText()).to.contain("Enter an answer less than or equal to 100"); - expect($(TestMinMax.errorNumber(8)).getText()).to.contain("Enter an answer more than or equal to £10.00"); + it("When I enter values outside of the set range, Then the correct error messages are displayed", async () => { + await $(SetMinMax.setMinimum()).setValue("10"); + await $(SetMinMax.setMaximum()).setValue("1020"); + await $(SetMinMax.submit()).click(); + + await $(TestMinMax.testRange()).setValue("9"); + await $(TestMinMax.testRangeExclusive()).setValue("10"); + await $(TestMinMax.testMin()).setValue("0"); + await $(TestMinMax.testMax()).setValue("12345"); + await $(TestMinMax.testMinExclusive()).setValue("123"); + await $(TestMinMax.testMaxExclusive()).setValue("12345"); + await $(TestMinMax.testPercent()).setValue("101"); + await $(TestMinMax.testDecimal()).setValue("5.4"); + await $(TestMinMax.submit()).click(); + + await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to 10"); + await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter an answer more than 10"); + await expect(await $(TestMinMax.errorNumber(3)).getText()).to.contain("Enter an answer more than or equal to 123"); + await expect(await $(TestMinMax.errorNumber(4)).getText()).to.contain("Enter an answer less than or equal to 1,234"); + await expect(await $(TestMinMax.errorNumber(5)).getText()).to.contain("Enter an answer more than 123"); + await expect(await $(TestMinMax.errorNumber(6)).getText()).to.contain("Enter an answer less than 1,234"); + await expect(await $(TestMinMax.errorNumber(7)).getText()).to.contain("Enter an answer less than or equal to 100"); + await expect(await $(TestMinMax.errorNumber(8)).getText()).to.contain("Enter an answer more than or equal to £10.00"); }); - it("When I enter values inside the set range but provide too many decimal places, Then the correct error messages are displayed", () => { - $(TestMinMax.testRange()).setValue("1020"); - $(TestMinMax.testRangeExclusive()).setValue("11"); - $(TestMinMax.testMin()).setValue("123"); - $(TestMinMax.testMax()).setValue("1019"); - $(TestMinMax.testMinExclusive()).setValue("124"); - $(TestMinMax.testMaxExclusive()).setValue("1233"); - $(TestMinMax.testPercent()).setValue("100"); - $(TestMinMax.testRange()).setValue("12.344"); - $(TestMinMax.testDecimal()).setValue("11.234"); - $(TestMinMax.submit()).click(); - - expect($(TestMinMax.errorNumber(1)).getText()).to.contain("Enter a number rounded to 2 decimal places"); - expect($(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 2 decimal places"); + it("When I enter values inside the set range but provide too many decimal places, Then the correct error messages are displayed", async () => { + await $(TestMinMax.testRange()).setValue("1020"); + await $(TestMinMax.testRangeExclusive()).setValue("11"); + await $(TestMinMax.testMin()).setValue("123"); + await $(TestMinMax.testMax()).setValue("1019"); + await $(TestMinMax.testMinExclusive()).setValue("124"); + await $(TestMinMax.testMaxExclusive()).setValue("1233"); + await $(TestMinMax.testPercent()).setValue("100"); + await $(TestMinMax.testRange()).setValue("12.344"); + await $(TestMinMax.testDecimal()).setValue("11.234"); + await $(TestMinMax.submit()).click(); + + await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter a number rounded to 2 decimal places"); + await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 2 decimal places"); }); - it("When I enter values inside the set range, Then I should be able to submit the survey", () => { - $(TestMinMax.testRange()).setValue("12"); - $(TestMinMax.testDecimal()).setValue("11.23"); - $(TestMinMax.testPercent()).setValue("99"); - $(TestMinMax.submit()).click(); - $(DetailAnswer.other()).click(); - $(DetailAnswer.otherDetail()).setValue("1020"); - $(DetailAnswer.submit()).click(); + it("When I enter values inside the set range, Then I should be able to submit the survey", async () => { + await $(TestMinMax.testRange()).setValue("12"); + await $(TestMinMax.testDecimal()).setValue("11.23"); + await $(TestMinMax.testPercent()).setValue("99"); + await $(TestMinMax.submit()).click(); + await $(DetailAnswer.other()).click(); + await $(DetailAnswer.otherDetail()).setValue("1020"); + await $(DetailAnswer.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", () => { - $(SubmitPage.setMaximumEdit()).click(); - $(SetMinMax.setMaximum()).setValue("1019"); - $(SetMinMax.submit()).click(); - $(TestMinMax.submit()).click(); - $(DetailAnswer.submit()).click(); + it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", async () => { + await $(SubmitPage.setMaximumEdit()).click(); + await $(SetMinMax.setMaximum()).setValue("1019"); + await $(SetMinMax.submit()).click(); + await $(TestMinMax.submit()).click(); + await $(DetailAnswer.submit()).click(); - expect($(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,019"); + await expect(await $(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,019"); - $(DetailAnswer.otherDetail()).setValue("1019"); - $(DetailAnswer.submit()).click(); + await $(DetailAnswer.otherDetail()).setValue("1019"); + await $(DetailAnswer.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I edit and change the minimum value, Then I must re-validate and submit any dependent answers again before I can return to the summary", () => { - $(SubmitPage.setMinimumEdit()).click(); - $(SetMinMax.setMinimum()).setValue("11"); - $(SetMinMax.submit()).click(); - $(TestMinMax.submit()).click(); + it("When I edit and change the minimum value, Then I must re-validate and submit any dependent answers again before I can return to the summary", async () => { + await $(SubmitPage.setMinimumEdit()).click(); + await $(SetMinMax.setMinimum()).setValue("11"); + await $(SetMinMax.submit()).click(); + await $(TestMinMax.submit()).click(); - expect($(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than 11"); + await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than 11"); - $(TestMinMax.testRangeExclusive()).setValue("12"); - $(TestMinMax.submit()).click(); + await $(TestMinMax.testRangeExclusive()).setValue("12"); + await $(TestMinMax.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/page_layout.spec.js b/tests/functional/spec/page_layout.spec.js index 8b9bc6c0f4..8b0045a1e2 100644 --- a/tests/functional/spec/page_layout.spec.js +++ b/tests/functional/spec/page_layout.spec.js @@ -1,10 +1,10 @@ import HubPage from "../base_pages/hub.page"; describe("Page Layout", () => { - it("Given a page in the questionnaire, When I visit the page, Then the page width should be as expected", () => { - browser.url(HubPage.url()); + it("Given a page in the questionnaire, When I visit the page, Then the page width should be as expected", async () => { + await browser.url(HubPage.url()); - const cssWidthSelector = $('div[class*="ons-col-"][class*="@m"]').getAttribute("class"); - expect(cssWidthSelector).to.contain("ons-col-8@m"); + const cssWidthSelector = await $('div[class*="ons-col-"][class*="@m"]').getAttribute("class"); + await expect(cssWidthSelector).to.contain("ons-col-8@m"); }); }); diff --git a/tests/functional/spec/percentage_decimal.spec.js b/tests/functional/spec/percentage_decimal.spec.js index 1de8800b3d..9eb757f4c7 100644 --- a/tests/functional/spec/percentage_decimal.spec.js +++ b/tests/functional/spec/percentage_decimal.spec.js @@ -3,23 +3,23 @@ import PercentageDecimalPage from "../generated_pages/percentage/block-decimal.p import SubmitPage from "../generated_pages/percentage/submit.page.js"; describe("Decimal places", () => { - it("Given an answer allows 3 decimal places, When I enter a value to 3 decimal places and return to edit the value, Then the answer should be displayed with 3 decimal places", () => { - browser.openQuestionnaire("test_percentage.json"); - $(PercentagePage.submit()).click(); - $(PercentageDecimalPage.decimal()).setValue("3.333"); - $(PercentageDecimalPage.submit()).click(); - $(SubmitPage.previous()).click(); - expect(browser.getUrl()).to.contain(PercentageDecimalPage.pageName); - expect($(PercentageDecimalPage.decimal()).getValue()).to.equal("3.333"); + it("Given an answer allows 3 decimal places, When I enter a value to 3 decimal places and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { + await browser.openQuestionnaire("test_percentage.json"); + await $(PercentagePage.submit()).click(); + await $(PercentageDecimalPage.decimal()).setValue("3.333"); + await $(PercentageDecimalPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); + await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.333"); }); - it("Given an answer allows 3 decimal places, When I enter a value to 1 decimal place and return to edit the value, Then the answer should be displayed with 3 decimal places", () => { - browser.openQuestionnaire("test_percentage.json"); - $(PercentagePage.submit()).click(); - $(PercentageDecimalPage.decimal()).setValue("3.3"); - $(PercentageDecimalPage.submit()).click(); - $(SubmitPage.previous()).click(); - expect(browser.getUrl()).to.contain(PercentageDecimalPage.pageName); - expect($(PercentageDecimalPage.decimal()).getValue()).to.equal("3.300"); + it("Given an answer allows 3 decimal places, When I enter a value to 1 decimal place and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { + await browser.openQuestionnaire("test_percentage.json"); + await $(PercentagePage.submit()).click(); + await $(PercentageDecimalPage.decimal()).setValue("3.3"); + await $(PercentageDecimalPage.submit()).click(); + await $(SubmitPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); + await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.300"); }); }); diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index 8849a52cf6..a93064b972 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -22,66 +22,66 @@ describe("Introduction preview questions", () => { const answerToMonth = "#answer-to-month"; const answerToYear = "#answer-to-year"; - function testPreview(schema, page) { - browser.openQuestionnaire(schema); - $(page.previewQuestions()).click(); - expect(browser.getUrl()).to.contain("questionnaire/preview"); + async function testPreview(schema, page) { + await browser.openQuestionnaire(schema); + await $(page.previewQuestions()).click(); + expect(await browser.getUrl()).to.contain("questionnaire/preview"); if (schema === "test_introduction.json") { - expect($(previewSectionTitle).getText()).to.equal("Main section"); + expect(await $(previewSectionTitle).getText()).to.equal("Main section"); } else { - $(showButton).click(); + await $(showButton).click(); } // :TODO: Add data attributes to elements below so we don't rely on tags or classes that are subject to DS changes - expect($(previewQuestion).$("h3").getText()).to.equal("Are you able to report for the calendar month 1 January 2017 to 1 February 2017?"); - expect($(previewQuestion).$(".ons-question__description").getText()).to.equal("Your return should relate to the calendar year 2021."); - expect($(previewQuestion).$$(".ons-panel__body")[0].getText()).to.equal("Please provide figures for the period in which you were trading."); - expect($(showButton).length).to.be.undefined; - expect($(printButton).isClickable()).to.be.true; - expect($(pdfButton).isClickable()).to.be.true; + expect(await $(previewQuestion).$("h3").getText()).to.equal("Are you able to report for the calendar month 1 January 2017 to 1 February 2017?"); + expect(await $(previewQuestion).$(".ons-question__description").getText()).to.equal("Your return should relate to the calendar year 2021."); + expect(await $(previewQuestion).$$(".ons-panel__body")[0].getText()).to.equal("Please provide figures for the period in which you were trading."); + expect(await $(showButton).length).to.be.undefined; + expect(await $(printButton).isClickable()).to.be.true; + expect(await $(pdfButton).isClickable()).to.be.true; // answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 // $(detailsHeading).click(); // expect($(previewQuestion).$("#answer-guidance--content div p").getText()).to.equal("For example select `yes` if you can report for this period"); - expect($(previewQuestion).$$("p")[2].getText()).to.equal("You can answer with one of the following options:"); - expect($(previewQuestion).$$("ul")[0].getText()).to.equal("Yes\nNo"); + expect(await $(previewQuestion).$$("p")[2].getText()).to.equal("You can answer with one of the following options:"); + expect(await $(previewQuestion).$$("ul")[0].getText()).to.equal("Yes\nNo"); } - it("Given I start a survey, When I view the preview page, Then all preview elements should be visible and any metadata piped answers are resolved", () => { - testPreview(introductionSchemaHub, IntroductionPageHub); - testPreview(introductionSchemaLinear, IntroductionPageLinear); + it("Given I start a survey, When I view the preview page, Then all preview elements should be visible and any metadata piped answers are resolved", async () => { + await testPreview(introductionSchemaHub, IntroductionPageHub); + await testPreview(introductionSchemaLinear, IntroductionPageLinear); }); - it("Given I complete some of a survey and the piped answers should be being populated, Then preview answers should still be showing placeholders", () => { - browser.openQuestionnaire(introductionSchemaLinear); - $(startSurveyButton).click(); - $(noRadio).click(); - $(submitButton).click(); - $(answerFromDay).setValue(5); - $(answerFromMonth).setValue(12); - $(answerFromYear).setValue(2016); - $(answerToDay).setValue(20); - $(answerToMonth).setValue(12); - $(answerToYear).setValue(2016); - $(submitButton).click(); - expect($("h1").getText()).to.equal("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); - browser.url("questionnaire/introduction/"); - $(IntroductionPageLinear.previewQuestions()).click(); - expect(browser.getUrl()).to.contain("questionnaire/preview"); - expect($(previewSectionTitle).getText()).to.equal("Main section"); - expect($$(previewQuestion)[2].$("h3").getText()).to.equal("Are you sure you are able to report for the calendar month {start_date} to {end_date}?"); + it("Given I complete some of a survey and the piped answers should be being populated, Then preview answers should still be showing placeholders", async () => { + await browser.openQuestionnaire(introductionSchemaLinear); + await $(startSurveyButton).click(); + await $(noRadio).click(); + await $(submitButton).click(); + await $(answerFromDay).setValue(5); + await $(answerFromMonth).setValue(12); + await $(answerFromYear).setValue(2016); + await $(answerToDay).setValue(20); + await $(answerToMonth).setValue(12); + await $(answerToYear).setValue(2016); + await $(submitButton).click(); + expect(await $("h1").getText()).to.equal("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); + await browser.url("questionnaire/introduction/"); + await $(IntroductionPageLinear.previewQuestions()).click(); + expect(await browser.getUrl()).to.contain("questionnaire/preview"); + expect(await $(previewSectionTitle).getText()).to.equal("Main section"); + expect(await $$(previewQuestion)[2].$("h3").getText()).to.equal("Are you sure you are able to report for the calendar month {start_date} to {end_date}?"); }); - it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", () => { - browser.openQuestionnaire(introductionSchemaHub); - $(IntroductionPageHub.previewQuestions()).click(); - expect(browser.getUrl()).to.contain("questionnaire/preview"); - expect($(printButton).isClickable()).to.be.true; - expect($(pdfButton).isClickable()).to.be.true; - expect($(showButton).getText()).to.equal("Show all"); - expect($(previewSummaryContent).isClickable()).to.be.false; - it("and if the twisty button is clicked, Then the twisty button should read 'Hide all' and the answers should be visible", () => { - $(showButton).click(); - expect($(showButton).getText()).to.equal("Hide all"); - expect($(previewSummaryContent).isClickable()).to.be.true; + it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", async () => { + await browser.openQuestionnaire(introductionSchemaHub); + await $(IntroductionPageHub.previewQuestions()).click(); + expect(await browser.getUrl()).to.contain("questionnaire/preview"); + expect(await $(printButton).isClickable()).to.be.true; + expect(await $(pdfButton).isClickable()).to.be.true; + expect(await $(showButton).getText()).to.equal("Show all"); + expect(await $(previewSummaryContent).isClickable()).to.be.false; + it("and if the twisty button is clicked, Then the twisty button should read 'Hide all' and the answers should be visible", async () => { + await $(showButton).click(); + expect(await $(showButton).getText()).to.equal("Hide all"); + expect(await $(previewSummaryContent).isClickable()).to.be.true; }); }); }); diff --git a/tests/functional/spec/question_definitions.spec.js b/tests/functional/spec/question_definitions.spec.js index 81a15e9238..08dec6560a 100644 --- a/tests/functional/spec/question_definitions.spec.js +++ b/tests/functional/spec/question_definitions.spec.js @@ -2,31 +2,31 @@ import DefinitionPage from "../generated_pages/question_definition/definition-bl describe("Component: Definition", () => { describe("Given I start a survey which contains question definition", () => { - beforeEach(() => { - browser.openQuestionnaire("test_question_definition.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_question_definition.json"); }); - it("When I click the title link, then the description should be visible", () => { - expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + it("When I click the title link, then the description should be visible", async () => { + await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); // When - $(DefinitionPage.definitionTitle("1")).click(); + await $(DefinitionPage.definitionTitle("1")).click(); // Then - expect($(DefinitionPage.definitionContent(1)).getText()).to.contain( + await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.contain( "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." ); }); - it("When I click the title link twice, then the description should not be visible", () => { - expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + it("When I click the title link twice, then the description should not be visible", async () => { + await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); // When - $(DefinitionPage.definitionTitle("1")).click(); - $(DefinitionPage.definitionTitle("1")).click(); + await $(DefinitionPage.definitionTitle("1")).click(); + await $(DefinitionPage.definitionTitle("1")).click(); // Then - expect($(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); }); }); }); diff --git a/tests/functional/spec/question_description.spec.js b/tests/functional/spec/question_description.spec.js index 58059f6ff2..1d103c8a75 100644 --- a/tests/functional/spec/question_description.spec.js +++ b/tests/functional/spec/question_description.spec.js @@ -1,8 +1,8 @@ import NameBlockPage from "../generated_pages/question_description/name-block.page.js"; describe("Question description", () => { - it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", () => { - browser.openQuestionnaire("test_question_description.json"); - expect($(NameBlockPage.questionTitle()).getHTML()).to.contain("

      Answer the question

      Go on

      "); + it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", async () => { + await browser.openQuestionnaire("test_question_description.json"); + await expect(await $(NameBlockPage.questionTitle()).getHTML()).to.contain("

      Answer the question

      Go on

      "); }); }); diff --git a/tests/functional/spec/question_variants.spec.js b/tests/functional/spec/question_variants.spec.js index aaba22dd10..f0b2976afb 100644 --- a/tests/functional/spec/question_variants.spec.js +++ b/tests/functional/spec/question_variants.spec.js @@ -9,53 +9,53 @@ import proxyBlock from "../generated_pages/variants_question/proxy-block.page.js import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; describe("QuestionVariants", () => { - beforeEach(() => { - browser.openQuestionnaire("test_variants_question.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_variants_question.json"); }); - it("Given I am completing the survey, then the correct questions are shown based on my previous answers", () => { - $(nameBlock.firstName()).setValue("Guido"); - $(nameBlock.lastName()).setValue("van Rossum"); - $(nameBlock.submit()).click(); + it("Given I am completing the survey, then the correct questions are shown based on my previous answers", async () => { + await $(nameBlock.firstName()).setValue("Guido"); + await $(nameBlock.lastName()).setValue("van Rossum"); + await $(nameBlock.submit()).click(); - expect($(proxyBlock.questionText()).getText()).to.contain("Are you Guido van Rossum?"); + await expect(await $(proxyBlock.questionText()).getText()).to.contain("Are you Guido van Rossum?"); - $(proxyBlock.noIAmAnsweringOnTheirBehalf()).click(); - $(proxyBlock.submit()).click(); + await $(proxyBlock.noIAmAnsweringOnTheirBehalf()).click(); + await $(proxyBlock.submit()).click(); - expect($(ageBlock.questionText()).getText()).to.contain("What age is Guido van Rossum"); + await expect(await $(ageBlock.questionText()).getText()).to.contain("What age is Guido van Rossum"); - $(ageBlock.age()).setValue(63); - $(ageBlock.submit()).click(); + await $(ageBlock.age()).setValue(63); + await $(ageBlock.submit()).click(); - expect($(ageConfirmationBlock.questionText()).getText()).to.contain("Guido van Rossum is over 16?"); + await expect(await $(ageConfirmationBlock.questionText()).getText()).to.contain("Guido van Rossum is over 16?"); - $(ageConfirmationBlock.ageConfirmYes()).click(); - $(ageConfirmationBlock.submit()).click(); + await $(ageConfirmationBlock.ageConfirmYes()).click(); + await $(ageConfirmationBlock.submit()).click(); - expect($(basicVariantsSummary.ageQuestion()).getText()).to.contain("What age is Guido van Rossum"); - expect($(basicVariantsSummary.ageAnswer()).getText()).to.contain("63"); + await expect(await $(basicVariantsSummary.ageQuestion()).getText()).to.contain("What age is Guido van Rossum"); + await expect(await $(basicVariantsSummary.ageAnswer()).getText()).to.contain("63"); - $(basicVariantsSummary.submit()).click(); + await $(basicVariantsSummary.submit()).click(); - $(currencyBlock.sterling()).click(); - $(currencyBlock.submit()).click(); + await $(currencyBlock.sterling()).click(); + await $(currencyBlock.submit()).click(); - expect($(firstNumberBlock.firstNumberLabel()).getText()).to.contain("First answer in GBP"); + await expect(await $(firstNumberBlock.firstNumberLabel()).getText()).to.contain("First answer in GBP"); - $(firstNumberBlock.firstNumber()).setValue(123); - $(firstNumberBlock.submit()).click(); + await $(firstNumberBlock.firstNumber()).setValue(123); + await $(firstNumberBlock.submit()).click(); - $(secondNumberBlock.secondNumber()).setValue(321); - $(secondNumberBlock.submit()).click(); + await $(secondNumberBlock.secondNumber()).setValue(321); + await $(secondNumberBlock.submit()).click(); - expect($(currencySectionSummary.currencyAnswer()).getText()).to.contain("Sterling"); - expect($(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("£"); + await expect(await $(currencySectionSummary.currencyAnswer()).getText()).to.contain("Sterling"); + await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("£"); - $(currencySectionSummary.currencyAnswerEdit()).click(); - $(currencyBlock.usDollars()).click(); - $(currencyBlock.submit()).click(); + await $(currencySectionSummary.currencyAnswerEdit()).click(); + await $(currencyBlock.usDollars()).click(); + await $(currencyBlock.submit()).click(); - expect($(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("$"); + await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("$"); }); }); diff --git a/tests/functional/spec/question_variants_first_item_in_list.spec.js b/tests/functional/spec/question_variants_first_item_in_list.spec.js index c015743d7e..c73782ccf1 100644 --- a/tests/functional/spec/question_variants_first_item_in_list.spec.js +++ b/tests/functional/spec/question_variants_first_item_in_list.spec.js @@ -4,36 +4,36 @@ import ListStatusQuestion from "../generated_pages/variants_first_item_in_list/l import HubPage from "../base_pages/hub.page.js"; describe("Question Variants First Item in List", () => { - it("Given I am the first person on the list, When the when rule is set, Then I should the correct question variant", () => { - browser.openQuestionnaire("test_variants_first_item_in_list.json"); - $(HubPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.submit()).click(); - expect($(ListStatusQuestion.questionText()).getText()).to.contain("You are the first person in the list"); + it("Given I am the first person on the list, When the when rule is set, Then I should the correct question variant", async () => { + await browser.openQuestionnaire("test_variants_first_item_in_list.json"); + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are the first person in the list"); }); - it("Given I am the second person on the list, When the when rule is set, Then I should the correct question variant", () => { - browser.openQuestionnaire("test_variants_first_item_in_list.json"); - $(HubPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("John"); - $(ListCollectorAddPage.lastName()).setValue("Doe"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HubPage.summaryRowLink("personal-details-section-2")).click(); - expect($(ListStatusQuestion.questionText()).getText()).to.contain("You are not the first person in the list"); + it("Given I am the second person on the list, When the when rule is set, Then I should the correct question variant", async () => { + await browser.openQuestionnaire("test_variants_first_item_in_list.json"); + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.summaryRowLink("personal-details-section-2")).click(); + await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are not the first person in the list"); }); }); diff --git a/tests/functional/spec/radio_checkbox_descriptions.spec.js b/tests/functional/spec/radio_checkbox_descriptions.spec.js index e5f3e5a6fb..e6dbb60d3d 100644 --- a/tests/functional/spec/radio_checkbox_descriptions.spec.js +++ b/tests/functional/spec/radio_checkbox_descriptions.spec.js @@ -3,22 +3,22 @@ import RadioBlockPage from "../generated_pages/radio_checkbox_descriptions/radio describe("Checkbox and Radio item descriptions", () => { describe("Given the user is presented with radio or checkbox options", () => { - before("Launch survey", () => { - browser.openQuestionnaire("test_radio_checkbox_descriptions.json"); + before("Launch survey", async () => { + await browser.openQuestionnaire("test_radio_checkbox_descriptions.json"); }); - it("When the schema defines a description for a checkbox option, then that description is displayed", () => { - expect($(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText()).to.contain( - "For example first use of alliances, partnerships, outsourcing or sub-contracting" - ); + it("When the schema defines a description for a checkbox option, then that description is displayed", async () => { + await expect( + await $(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText() + ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); }); - it("When the schema defines a description for a radio option, then that description is displayed", () => { - $(CheckboxBlockPage.newBusinessPracticesForOrganisingProcedures()).click(); - $(CheckboxBlockPage.submit()).click(); - expect($(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText()).to.contain( - "For example first use of alliances, partnerships, outsourcing or sub-contracting" - ); + it("When the schema defines a description for a radio option, then that description is displayed", async () => { + await $(CheckboxBlockPage.newBusinessPracticesForOrganisingProcedures()).click(); + await $(CheckboxBlockPage.submit()).click(); + await expect( + await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText() + ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); }); }); }); diff --git a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js index e4d0d6d04e..35739849c1 100644 --- a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js +++ b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js @@ -2,41 +2,41 @@ import RadioNonMandatoryPage from "../generated_pages/radio_optional_with_detail import SubmitPage from "../generated_pages/radio_optional_with_detail_answer_optional/submit.page"; describe("Checkbox and Radio item descriptions", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire("test_radio_optional_with_detail_answer_optional.json"); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire("test_radio_optional_with_detail_answer_optional.json"); }); describe("Given the user is presented with an optional radio answer with optional detail answer", () => { - it("When no answer is provided, Then the expected answer is displayed", () => { - $(RadioNonMandatoryPage.submit()).click(); - expect($(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); + it("When no answer is provided, Then the expected answer is displayed", async () => { + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); }); - it("When Toast is selected and no detail answer is provided, Then the expected answer is displayed", () => { - $(RadioNonMandatoryPage.toast()).click(); - $(RadioNonMandatoryPage.submit()).click(); - expect($(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); + it("When Toast is selected and no detail answer is provided, Then the expected answer is displayed", async () => { + await $(RadioNonMandatoryPage.toast()).click(); + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); }); - it("When Other is selected and no detail answer is provided, Then the expected answer is displayed", () => { - $(RadioNonMandatoryPage.other()).click(); - $(RadioNonMandatoryPage.submit()).click(); - expect($(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Other"); + it("When Other is selected and no detail answer is provided, Then the expected answer is displayed", async () => { + await $(RadioNonMandatoryPage.other()).click(); + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Other"); }); - it("When Other is selected and detail answer is provided, Then the expected answer is displayed", () => { - $(RadioNonMandatoryPage.other()).click(); - $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); - $(RadioNonMandatoryPage.submit()).click(); - expect($(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Eggs"); + it("When Other is selected and detail answer is provided, Then the expected answer is displayed", async () => { + await $(RadioNonMandatoryPage.other()).click(); + await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Eggs"); }); - it("When Other is selected and detail answer is provided and the answer is changed, Then the expected answer is displayed", () => { - $(RadioNonMandatoryPage.other()).click(); - $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); - $(RadioNonMandatoryPage.toast()).click(); - $(RadioNonMandatoryPage.submit()).click(); - expect($(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); + it("When Other is selected and detail answer is provided and the answer is changed, Then the expected answer is displayed", async () => { + await $(RadioNonMandatoryPage.other()).click(); + await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); + await $(RadioNonMandatoryPage.toast()).click(); + await $(RadioNonMandatoryPage.submit()).click(); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); }); }); }); diff --git a/tests/functional/spec/relationships-unrelated.spec.js b/tests/functional/spec/relationships-unrelated.spec.js index 1acd783484..6926b03d2e 100644 --- a/tests/functional/spec/relationships-unrelated.spec.js +++ b/tests/functional/spec/relationships-unrelated.spec.js @@ -8,87 +8,88 @@ describe("Unrelated Relationships", () => { const schema = "test_relationships_unrelated.json"; describe("Given I am completing the test_relationships_unrelated survey,", () => { - before("load the survey", () => { - browser.openQuestionnaire(schema); + before("load the survey", async () => { + await browser.openQuestionnaire(schema); }); describe("And I add six people", () => { - before("add people", () => { - addPerson("Andrew", "Austin"); - addPerson("Betty", "Burns"); - addPerson("Carla", "Clark"); - addPerson("Daniel", "Davis"); - addPerson("Eve", "Elliot"); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); + before("add people", async () => { + await addPerson("Andrew", "Austin"); + await addPerson("Betty", "Burns"); + await addPerson("Carla", "Clark"); + await addPerson("Daniel", "Davis"); + await addPerson("Eve", "Elliot"); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); }); - it("When I answer 'Unrelated' twice, Then I will be asked if anyone else is related with a list of the remaining people", () => { - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - expect($(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - expect($(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); - expect($(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); + it("When I answer 'Unrelated' twice, Then I will be asked if anyone else is related with a list of the remaining people", async () => { + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); }); - it("When I click previous, Then I will go back to the previous relationship", () => { - $(RelatedToAnyoneElsePage.previous()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Carla Clark is unrelated to Andrew Austin"); + it("When I click previous, Then I will go back to the previous relationship", async () => { + await $(RelatedToAnyoneElsePage.previous()).click(); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Carla Clark is unrelated to Andrew Austin"); }); - it("When I return to the 'related to anyone else' question and select 'Yes', Then I will be taken to the next relationship for the first person", () => { - $(RelationshipsPage.submit()).click(); - $(RelatedToAnyoneElsePage.yes()).click(); - $(RelatedToAnyoneElsePage.submit()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Andrew Austin, Daniel Davis is their"); + it("When I return to the 'related to anyone else' question and select 'Yes', Then I will be taken to the next relationship for the first person", async () => { + await $(RelationshipsPage.submit()).click(); + await $(RelatedToAnyoneElsePage.yes()).click(); + await $(RelatedToAnyoneElsePage.submit()).click(); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Andrew Austin, Daniel Davis is their"); }); - it("When I click previous, Then I will go back to the 'related to anyone else' question", () => { - $(RelationshipsPage.previous()).click(); - expect($(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - expect($(RelatedToAnyoneElsePage.yes()).isSelected()).to.be.true; + it("When I click previous, Then I will go back to the 'related to anyone else' question", async () => { + await $(RelationshipsPage.previous()).click(); + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.yes()).isSelected()).to.be.true; }); - it("When I select 'No' to the 'related to anyone else' question, Then I will be taken to the first relationship for the second person", () => { - $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).click(); - $(RelatedToAnyoneElsePage.submit()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Betty Burns, Carla Clark is their"); + it("When I select 'No' to the 'related to anyone else' question, Then I will be taken to the first relationship for the second person", async () => { + await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).click(); + await $(RelatedToAnyoneElsePage.submit()).click(); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Betty Burns, Carla Clark is their"); }); - it("When I click previous, Then I will go back to the 'related to anyone else' question for the first person", () => { - $(RelationshipsPage.previous()).click(); - expect($(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - expect($(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); - expect($(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); - expect($(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).isSelected()).to.be.true; + it("When I click previous, Then I will go back to the 'related to anyone else' question for the first person", async () => { + await $(RelationshipsPage.previous()).click(); + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); + await expect(await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).isSelected()).to.be.true; }); - it("When I click complete the remaining relationships, Then I will go to the relationships section complete page", () => { - $(RelatedToAnyoneElsePage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.unrelated()).click(); - $(RelationshipsPage.submit()).click(); - expect(browser.getUrl()).to.contain(RelationshipsInterstitialPage.pageName); + it("When I click complete the remaining relationships, Then I will go to the relationships section complete page", async () => { + await $(RelatedToAnyoneElsePage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.unrelated()).click(); + await $(RelationshipsPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RelationshipsInterstitialPage.pageName); }); }); - function addPerson(firstName, lastName) { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue(firstName); - $(ListCollectorAddPage.lastName()).setValue(lastName); - $(ListCollectorAddPage.submit()).click(); + async function addPerson(firstName, lastName) { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).scrollIntoView(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue(firstName); + await $(ListCollectorAddPage.lastName()).setValue(lastName); + await $(ListCollectorAddPage.submit()).click(); } }); }); diff --git a/tests/functional/spec/relationships.spec.js b/tests/functional/spec/relationships.spec.js index adaa47d276..c6669c0b6d 100644 --- a/tests/functional/spec/relationships.spec.js +++ b/tests/functional/spec/relationships.spec.js @@ -9,199 +9,202 @@ describe("Relationships", () => { const schema = "test_relationships.json"; describe("Given I am completing the test_relationships survey,", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire(schema); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire(schema); }); - it("When I have one household member, Then I will be not be asked about relationships", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); + it("When I have one household member, Then I will be not be asked about relationships", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); }); - it("When I add two household members, Then I will be asked about one relationship", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(RelationshipsPage.pageName); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsInterstitialPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); + it("When I add two household members, Then I will be asked about one relationship", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).scrollIntoView(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); }); describe("When I add three household members,", () => { - beforeEach("add three people", () => { - addThreePeople(); + beforeEach("add three people", async () => { + await addThreePeople(); }); - it("Then I will be asked about all relationships", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsInterstitialPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); + it("Then I will be asked about all relationships", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); }); - it("And go to the first relationship, Then the previous link should return to the list collector", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.previous()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/list-collector/"); + it("And go to the first relationship, Then the previous link should return to the list collector", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); }); - it("And go to the first relationship, Then the 'Brother or Sister' option should have the text 'Including half brother or half sister'", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect($(RelationshipsPage.brotherOrSisterLabelDescription()).getText()).to.contain("Including half brother or half sister"); + it("And go to the first relationship, Then the 'Brother or Sister' option should have the text 'Including half brother or half sister'", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(RelationshipsPage.brotherOrSisterLabelDescription()).getText()).to.contain("Including half brother or half sister"); }); - it("And go to the second relationship, Then the previous link should return to the first relationship", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.previous()).click(); - $(RelationshipsInterstitialPage.submit()).click(); - expect(browser.getUrl()).to.contain(RelationshipsPage.pageName); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Marcus"); + it("And go to the second relationship, Then the previous link should return to the first relationship", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.previous()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Marcus"); }); - it("And go to the section summary, Then the previous link should return to the last relationship Interstitial", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsInterstitialPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); - $(SectionSummaryPage.previous()).click(); - $(RelationshipsInterstitialPage.previous()).click(); - expect(browser.getUrl()).to.contain(RelationshipsPage.pageName); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Olivia"); + it("And go to the section summary, Then the previous link should return to the last relationship Interstitial", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); + await $(SectionSummaryPage.previous()).click(); + await $(RelationshipsInterstitialPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Olivia"); }); - it("When I add all relationships and return to the relationships, Then the relationships should be populated", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsInterstitialPage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); - $(SectionSummaryPage.previous()).click(); - $(RelationshipsInterstitialPage.previous()).click(); - expect($(RelationshipsPage.husbandOrWife()).isSelected()).to.be.true; - $(RelationshipsPage.previous()).click(); - expect($(RelationshipsPage.legallyRegisteredCivilPartner()).isSelected()).to.be.true; + it("When I add all relationships and return to the relationships, Then the relationships should be populated", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); + await $(SectionSummaryPage.previous()).click(); + await $(RelationshipsInterstitialPage.previous()).click(); + await expect(await $(RelationshipsPage.husbandOrWife()).isSelected()).to.be.true; + await $(RelationshipsPage.previous()).click(); + await expect(await $(RelationshipsPage.legallyRegisteredCivilPartner()).isSelected()).to.be.true; }); - it("And go to the first relationship, Then the person's name should be in the question title and playback text", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect($(ListCollectorPage.questionText()).getText()).to.contain("Marcus Twin"); - expect($(RelationshipsPage.playback()).getText()).to.contain("Marcus Twin"); + it("And go to the first relationship, Then the person's name should be in the question title and playback text", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(ListCollectorPage.questionText()).getText()).to.contain("Marcus Twin"); + await expect(await $(RelationshipsPage.playback()).getText()).to.contain("Marcus Twin"); }); - it("And go to the first relationship and submit without selecting an option, Then an error should be displayed", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.submit()).click(); - expect($(RelationshipsPage.error()).isDisplayed()).to.be.true; + it("And go to the first relationship and submit without selecting an option, Then an error should be displayed", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.submit()).click(); + await expect(await $(RelationshipsPage.error()).isDisplayed()).to.be.true; }); - it("And go to the first relationship and click 'Save and sign out', Then I should be signed out", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.saveSignOut()).click(); - expect(browser.getUrl()).to.not.contain("questionnaire"); + it("And go to the first relationship and click 'Save and sign out', Then I should be signed out", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.saveSignOut()).click(); + await expect(await browser.getUrl()).to.not.contain("questionnaire"); }); - it("And go to the first relationship, select a relationship and click 'Save and sign out', Then I should be signed out", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.saveSignOut()).click(); - expect(browser.getUrl()).to.not.contain("questionnaire"); + it("And go to the first relationship, select a relationship and click 'Save and sign out', Then I should be signed out", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.saveSignOut()).click(); + await expect(await browser.getUrl()).to.not.contain("questionnaire"); }); }); describe("When I have added one or more household members after answering the relationships question,", () => { - beforeEach("add three people and complete their relationships", () => { - addThreePeopleAndCompleteRelationships(); + beforeEach("add three people and complete their relationships", async () => { + await addThreePeopleAndCompleteRelationships(); }); - it("Then I delete one of the original household members I will not be asked for the original members relationships again", () => { - $(SectionSummaryPage.peopleListRemoveLink(1)).click(); - $(ListCollectorRemovePage.yes()).click(); - $(ListCollectorRemovePage.submit()).click(); - expect(browser.getUrl()).to.contain("/sections/section/"); + it("Then I delete one of the original household members I will not be asked for the original members relationships again", async () => { + await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); + await $(ListCollectorRemovePage.yes()).click(); + await $(ListCollectorRemovePage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/sections/section/"); }); - it("Then I add another household member I will be redirected to parent list collector", () => { - $(SectionSummaryPage.peopleListAddLink()).click(); - $(ListCollectorAddPage.firstName()).setValue("Tom"); - $(ListCollectorAddPage.lastName()).setValue("Bowden"); - $(ListCollectorAddPage.submit()).click(); - expect(browser.getUrl()).to.contain("/questionnaire/list-collector/"); + it("Then I add another household member I will be redirected to parent list collector", async () => { + await $(SectionSummaryPage.peopleListAddLink()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Tom"); + await $(ListCollectorAddPage.lastName()).setValue("Bowden"); + await $(ListCollectorAddPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); }); }); - function addThreePeopleAndCompleteRelationships() { - addThreePeople(); - - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.husbandOrWife()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsInterstitialPage.submit()).click(); + async function addThreePeopleAndCompleteRelationships() { + await addThreePeople(); + + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).scrollIntoView(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.husbandOrWife()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsInterstitialPage.submit()).click(); } - function addThreePeople() { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Olivia"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + async function addThreePeople() { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).scrollIntoView(); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Olivia"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); } }); }); diff --git a/tests/functional/spec/relationships_primary.spec.js b/tests/functional/spec/relationships_primary.spec.js index 3f61c79342..3306693cbd 100644 --- a/tests/functional/spec/relationships_primary.spec.js +++ b/tests/functional/spec/relationships_primary.spec.js @@ -8,79 +8,81 @@ describe("Relationships - Primary Person", () => { const schema = "test_relationships_primary.json"; describe("Given I am completing the test_relationships_primary survey", () => { - beforeEach(() => { - browser.openQuestionnaire(schema); + beforeEach(async () => { + await browser.openQuestionnaire(schema); }); - it("When I add household members, Then I will be asked my relationships as a primary person", () => { - addPrimaryAndTwoOthers(); + it("When I add household members, Then I will be asked my relationships as a primary person", async () => { + await addPrimaryAndTwoOthers(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("is your"); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is your"); }); - it("When I add household members, Then non-primary relationships will be asked as a non primary person", () => { - addPrimaryAndTwoOthers(); + it("When I add household members, Then non-primary relationships will be asked as a non primary person", async () => { + await addPrimaryAndTwoOthers(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.relationshipBrotherOrSister()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.relationshipSonOrDaughter()).click(); - $(RelationshipsPage.submit()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("is their"); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.relationshipBrotherOrSister()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.relationshipSonOrDaughter()).click(); + await $(RelationshipsPage.submit()).click(); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is their"); }); - it("When I add household members And add thir relationships And remove the primary person And add a new primary person then I will be asked for the relationships again", () => { - addPrimaryAndTwoOthersAndCompleteRelationships(); + it("When I add household members And add their relationships And remove the primary person And add a new primary person then I will be asked for the relationships again", async () => { + await addPrimaryAndTwoOthersAndCompleteRelationships(); - browser.url("/questionnaire/primary-person-list-collector"); + await browser.url("/questionnaire/primary-person-list-collector"); - $(PrimaryPersonListCollectorPage.no()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.no()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); - browser.url("/questionnaire/primary-person-list-collector"); + await browser.url("/questionnaire/primary-person-list-collector"); - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); - expect($(RelationshipsPage.questionText()).getText()).to.contain("Samuel Clemens is your"); + await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Samuel Clemens is your"); }); - function addPrimaryAndTwoOthersAndCompleteRelationships() { - addPrimaryAndTwoOthers(); + async function addPrimaryAndTwoOthersAndCompleteRelationships() { + await addPrimaryAndTwoOthers(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(RelationshipsPage.relationshipBrotherOrSister()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.relationshipSonOrDaughter()).click(); - $(RelationshipsPage.submit()).click(); - $(RelationshipsPage.relationshipBrotherOrSister()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).scrollIntoView(); + await $(ListCollectorPage.submit()).click(); + await $(RelationshipsPage.relationshipBrotherOrSister()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.relationshipSonOrDaughter()).click(); + await $(RelationshipsPage.submit()).click(); + await $(RelationshipsPage.relationshipBrotherOrSister()).click(); } - function addPrimaryAndTwoOthers() { - $(PrimaryPersonListCollectorPage.yes()).click(); - $(PrimaryPersonListCollectorPage.submit()).click(); - $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); - $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - $(PrimaryPersonListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Olivia"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); + async function addPrimaryAndTwoOthers() { + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).scrollIntoView(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Olivia"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); } }); }); diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 1bef146fda..77d7e5519e 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -20,358 +20,360 @@ import HubPage from "../base_pages/hub.page"; describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I answer 'No' to skipping the age question, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { - answerNoToSkipAgeQuestion(); + it("When I answer 'No' to skipping the age question, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", async () => { + await answerNoToSkipAgeQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); - answerAndSubmitAgeQuestion(); - answerAndSubmitReasonForNoConfirmationQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); + await answerAndSubmitAgeQuestion(); + await answerAndSubmitReasonForNoConfirmationQuestion(); - expectPersonalDetailsName(); - expectPersonalDetailsAge(); - expectReasonNoConfirmationAnswer(); + await expectPersonalDetailsName(); + await expectPersonalDetailsAge(); + await expectReasonNoConfirmationAnswer(); }); - it("When I answer 'Yes' to skipping the age question, Then in the Primary Person section I am only asked my name and why I didn't confirm skipping", () => { - answerYesToSkipAgeQuestion(); + it("When I answer 'Yes' to skipping the age question, Then in the Primary Person section I am only asked my name and why I didn't confirm skipping", async () => { + await answerYesToSkipAgeQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); - answerAndSubmitReasonForNoConfirmationQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); + await answerAndSubmitReasonForNoConfirmationQuestion(); - expectPersonalDetailsName(); - expectReasonNoConfirmationAnswer(); - expectPersonalDetailsAgeExistingFalse(); + await expectPersonalDetailsName(); + await expectReasonNoConfirmationAnswer(); + await expectPersonalDetailsAgeExistingFalse(); }); - it("When I answer 'Yes' to skipping the age question and 'Yes' to are you sure in skip question confirmation section, Then in the Primary Person section I am just asked my name", () => { - answerYesToSkipAgeQuestion(); + it("When I answer 'Yes' to skipping the age question and 'Yes' to are you sure in skip question confirmation section, Then in the Primary Person section I am just asked my name", async () => { + await answerYesToSkipAgeQuestion(); - selectConfirmationSectionAndAnswerSecurityQuestion(); - answerYesToSkipConfirmationQuestion(); + await selectConfirmationSectionAndAnswerSecurityQuestion(); + await answerYesToSkipConfirmationQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); - expectPersonalDetailsName(); - expectPersonalDetailsAgeExistingFalse(); - expectReasonNoConfirmationExistingFalse(); + await expectPersonalDetailsName(); + await expectPersonalDetailsAgeExistingFalse(); + await expectReasonNoConfirmationExistingFalse(); }); - it("When I answer 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section, Then in the Primary Person section I am only asked my name and age", () => { - answerYesToSkipAgeQuestion(); + it("When I answer 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section, Then in the Primary Person section I am only asked my name and age", async () => { + await answerYesToSkipAgeQuestion(); - selectConfirmationSectionAndAnswerSecurityQuestion(); - answerNoToSkipConfirmationQuestion(); + await selectConfirmationSectionAndAnswerSecurityQuestion(); + await answerNoToSkipConfirmationQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); - answerAndSubmitAgeQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); + await answerAndSubmitAgeQuestion(); - expectPersonalDetailsName(); - expectPersonalDetailsAge(); - expectReasonNoConfirmationExistingFalse(); + await expectPersonalDetailsName(); + await expectPersonalDetailsAge(); + await expectReasonNoConfirmationExistingFalse(); }); - it("When I answer 'No' to skipping the age question and populate the household, Then in each repeating section I am not asked their age", () => { - answerNoToSkipAgeQuestion(); + it("When I answer 'No' to skipping the age question and populate the household, Then in each repeating section I am not asked their age", async () => { + await answerNoToSkipAgeQuestion(); - addHouseholdMembers(); + await addHouseholdMembers(); - $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); - $(RepeatingSexPage.female()).click(); - $(RepeatingSexPage.submit()).click(); - $(RepeatingAgePage.answer()).setValue("45"); - $(RepeatingAgePage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("45"); + await $(RepeatingAgePage.submit()).click(); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); - $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); - $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); - $(RepeatingSexPage.male()).click(); - $(RepeatingSexPage.submit()).click(); - $(RepeatingAgePage.answer()).setValue("10"); - $(RepeatingAgePage.submit()).click(); + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); + await $(RepeatingSexPage.male()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("10"); + await $(RepeatingAgePage.submit()).click(); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); }); - it("When I answer 'Yes' to skipping the age question and populate the household, Then in each repeating section I am not asked their age", () => { - answerYesToSkipAgeQuestion(); + it("When I answer 'Yes' to skipping the age question and populate the household, Then in each repeating section I am not asked their age", async () => { + await answerYesToSkipAgeQuestion(); - addHouseholdMembers(); + await addHouseholdMembers(); - $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); - $(RepeatingSexPage.female()).click(); - $(RepeatingSexPage.submit()).click(); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; - $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); - $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); - $(RepeatingSexPage.male()).click(); - $(RepeatingAgePage.submit()).click(); + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); + await $(RepeatingSexPage.male()).click(); + await $(RepeatingAgePage.submit()).click(); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - expect($(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; }); }); describe("Given the routing and skipping section dependencies questionnaire", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", () => { - answerNoToSkipEnableQuestionAndYesToEnableSection(); + it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", async () => { + await answerNoToSkipEnableQuestionAndYesToEnableSection(); - expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.true; }); - it("When I answer 'No' to skipping the section question and 'No' to enable the section question, Then the household summary will not be visible on the hub", () => { - answerNoToSkipEnableQuestionAndNoToEnableSection(); + it("When I answer 'No' to skipping the section question and 'No' to enable the section question, Then the household summary will not be visible on the hub", async () => { + await answerNoToSkipEnableQuestionAndNoToEnableSection(); - expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; }); }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'No' to skipping the section question and 'Yes' to enable the section question", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I change my answer to skipping the section question to 'No', Then the household summary will not be visible on the hub", () => { - answerNoToSkipEnableQuestionAndYesToEnableSection(); - changeSkipEnableQuestionToYes(); + it("When I change my answer to skipping the section question to 'No', Then the household summary will not be visible on the hub", async () => { + await answerNoToSkipEnableQuestionAndYesToEnableSection(); + await changeSkipEnableQuestionToYes(); - expect($(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; }); }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question but 'No' to are you sure in skip question confirmation section", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I change my answer to skipping age to 'No', removing the 'are you sure' question from the path, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", () => { - answerYesToSkipAgeQuestion(); + it("When I change my answer to skipping age to 'No', removing the 'are you sure' question from the path, Then in the Primary Person section I am asked my name, age and why I didn't confirm skipping", async () => { + await answerYesToSkipAgeQuestion(); - selectConfirmationSectionAndAnswerSecurityQuestion(); - answerNoToSkipConfirmationQuestion(); + await selectConfirmationSectionAndAnswerSecurityQuestion(); + await answerNoToSkipConfirmationQuestion(); - editNoToSkipAgeQuestion(); + await editNoToSkipAgeQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); - answerAndSubmitAgeQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); + await answerAndSubmitAgeQuestion(); - $(ReasonNoConfirmationPage.iDidButItWasRemovedFromThePathAsIChangedMyAnswerToNoOnTheSkipQuestion()).click(); - $(ReasonNoConfirmationPage.submit()).click(); + await $(ReasonNoConfirmationPage.iDidButItWasRemovedFromThePathAsIChangedMyAnswerToNoOnTheSkipQuestion()).click(); + await $(ReasonNoConfirmationPage.submit()).click(); - expectPersonalDetailsName(); - expectPersonalDetailsAge(); - expect($(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( + await expectPersonalDetailsName(); + await expectPersonalDetailsAge(); + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( "I did, but it was removed from the path as I changed my answer to No on the skip question" ); }); }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and complete the Primary Person section", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I change my answer to skipping age to 'No', Then the Primary Person section status is changed to Partially completed", () => { - answerYesToSkipAgeQuestion(); - selectPrimaryPerson(); - answerAndSubmitNameQuestion(); - answerAndSubmitReasonForNoConfirmationQuestion(); - $(PrimaryPersonSummaryPage.submit()).click(); + it("When I change my answer to skipping age to 'No', Then the Primary Person section status is changed to Partially completed", async () => { + await answerYesToSkipAgeQuestion(); + await selectPrimaryPerson(); + await answerAndSubmitNameQuestion(); + await answerAndSubmitReasonForNoConfirmationQuestion(); + await $(PrimaryPersonSummaryPage.submit()).click(); - expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); - editNoToSkipAgeQuestion(); + await editNoToSkipAgeQuestion(); - expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Partially completed"); }); - it("When I change my answer back to skipping age to 'Yes', Then the Primary Person section status is changed back to Completed", () => { - editYesToSkipAgeQuestion(); + it("When I change my answer back to skipping age to 'Yes', Then the Primary Person section status is changed back to Completed", async () => { + await editYesToSkipAgeQuestion(); - expect($(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); }); }); describe("Given the routing and skipping section dependencies questionnaire and I answered 'Yes' to skipping the age question and add 2 household members but complete only one", () => { - before("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + before("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); }); - it("When I change my answer to skipping age to 'No', Then the completed household member status is changed to Partially completed and the other stays as not started", () => { - answerYesToSkipAgeQuestion(); - addHouseholdMembers(); - $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); - $(RepeatingSexPage.female()).click(); - $(RepeatingSexPage.submit()).click(); - $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + it("When I change my answer to skipping age to 'No', Then the completed household member status is changed to Partially completed and the other stays as not started", async () => { + await answerYesToSkipAgeQuestion(); + await addHouseholdMembers(); + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); - editNoToSkipAgeQuestion(); + await editNoToSkipAgeQuestion(); - expect($(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Partially completed"); - expect($(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); }); - it("When I change my answer back to skipping age to 'Yes', Then the Partially completed household member status is changed back to Completed and the other stays as not started", () => { - editYesToSkipAgeQuestion(); + it("When I change my answer back to skipping age to 'Yes', Then the Partially completed household member status is changed back to Completed and the other stays as not started", async () => { + await editYesToSkipAgeQuestion(); - expect($(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Completed"); - expect($(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); }); }); }); -const addHouseholdMembers = () => { - $(HubPage.summaryRowLink("household-section")).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Sarah"); - $(ListCollectorAddPage.lastName()).setValue("Smith"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Smith"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - $(HouseholdSectionSummaryPage.submit()).click(); +const addHouseholdMembers = async () => { + await $(HubPage.summaryRowLink("household-section")).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Sarah"); + await $(ListCollectorAddPage.lastName()).setValue("Smith"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Smith"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HouseholdSectionSummaryPage.submit()).click(); }; -const selectPrimaryPerson = () => { - $(HubPage.summaryRowLink("primary-person")).click(); +const selectPrimaryPerson = async () => { + await $(HubPage.summaryRowLink("primary-person")).click(); }; -const selectConfirmationSectionAndAnswerSecurityQuestion = () => { - $(HubPage.summaryRowLink("skip-confirmation-section")).click(); - $(SecurityPage.yes()).click(); - $(SecurityPage.submit()).click(); +const selectConfirmationSectionAndAnswerSecurityQuestion = async () => { + await $(HubPage.summaryRowLink("skip-confirmation-section")).click(); + await $(SecurityPage.yes()).click(); + await $(SecurityPage.submit()).click(); }; -const answerYesToSkipAgeQuestion = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipAgePage.yes()).click(); - $(SkipAgePage.submit()).click(); - $(SkipEnableSectionPage.no()).click(); - $(SkipEnableSectionPage.submit()).click(); - $(EnableSectionPage.yes()).click(); - $(EnableSectionPage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const answerYesToSkipAgeQuestion = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipAgePage.yes()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipEnableSectionPage.no()).click(); + await $(SkipEnableSectionPage.submit()).click(); + await $(EnableSectionPage.yes()).click(); + await $(EnableSectionPage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const editNoToSkipAgeQuestion = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); - $(SkipAgePage.no()).click(); - $(SkipAgePage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const editNoToSkipAgeQuestion = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); + await $(SkipAgePage.no()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const editYesToSkipAgeQuestion = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); - $(SkipAgePage.yes()).click(); - $(SkipAgePage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const editYesToSkipAgeQuestion = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); + await $(SkipAgePage.yes()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const answerNoToSkipAgeQuestion = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipAgePage.no()).click(); - $(SkipAgePage.submit()).click(); - $(SkipEnableSectionPage.no()).click(); - $(SkipEnableSectionPage.submit()).click(); - $(EnableSectionPage.yes()).click(); - $(EnableSectionPage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const answerNoToSkipAgeQuestion = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipAgePage.no()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipEnableSectionPage.no()).click(); + await $(SkipEnableSectionPage.submit()).click(); + await $(EnableSectionPage.yes()).click(); + await $(EnableSectionPage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const answerNoToSkipConfirmationQuestion = () => { - $(SkipConfirmationPage.no()).click(); - $(SkipConfirmationPage.submit()).click(); - $(SkipConfirmationSectionSummaryPage.submit()).click(); +const answerNoToSkipConfirmationQuestion = async () => { + await $(SkipConfirmationPage.no()).click(); + await $(SkipConfirmationPage.submit()).click(); + await $(SkipConfirmationSectionSummaryPage.submit()).click(); }; -const answerYesToSkipConfirmationQuestion = () => { - $(SkipConfirmationPage.yes()).click(); - $(SkipConfirmationPage.submit()).click(); - $(SkipConfirmationSectionSummaryPage.submit()).click(); +const answerYesToSkipConfirmationQuestion = async () => { + await $(SkipConfirmationPage.yes()).click(); + await $(SkipConfirmationPage.submit()).click(); + await $(SkipConfirmationSectionSummaryPage.submit()).click(); }; -const answerNoToSkipEnableQuestionAndYesToEnableSection = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipAgePage.no()).click(); - $(SkipAgePage.submit()).click(); - $(SkipEnableSectionPage.no()).click(); - $(SkipEnableSectionPage.submit()).click(); - $(EnableSectionPage.yes()).click(); - $(EnableSectionPage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const answerNoToSkipEnableQuestionAndYesToEnableSection = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipAgePage.no()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipEnableSectionPage.no()).click(); + await $(SkipEnableSectionPage.submit()).click(); + await $(EnableSectionPage.yes()).click(); + await $(EnableSectionPage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const answerNoToSkipEnableQuestionAndNoToEnableSection = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipAgePage.no()).click(); - $(SkipAgePage.submit()).click(); - $(SkipEnableSectionPage.no()).click(); - $(SkipEnableSectionPage.submit()).click(); - $(EnableSectionPage.no()).click(); - $(EnableSectionPage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const answerNoToSkipEnableQuestionAndNoToEnableSection = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipAgePage.no()).click(); + await $(SkipAgePage.submit()).click(); + await $(SkipEnableSectionPage.no()).click(); + await $(SkipEnableSectionPage.submit()).click(); + await $(EnableSectionPage.no()).click(); + await $(EnableSectionPage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const changeSkipEnableQuestionToYes = () => { - $(HubPage.summaryRowLink("skip-section")).click(); - $(SkipSectionSummaryPage.skipHouseholdSectionAnswerEdit()).click(); - $(SkipEnableSectionPage.yes()).click(); - $(SkipEnableSectionPage.submit()).click(); - $(SkipSectionSummaryPage.submit()).click(); +const changeSkipEnableQuestionToYes = async () => { + await $(HubPage.summaryRowLink("skip-section")).click(); + await $(SkipSectionSummaryPage.skipHouseholdSectionAnswerEdit()).click(); + await $(SkipEnableSectionPage.yes()).click(); + await $(SkipEnableSectionPage.submit()).click(); + await $(SkipSectionSummaryPage.submit()).click(); }; -const answerAndSubmitNameQuestion = () => { - $(NamePage.name()).setValue("John Smith"); - $(NamePage.submit()).click(); +const answerAndSubmitNameQuestion = async () => { + await $(NamePage.name()).setValue("John Smith"); + await $(NamePage.submit()).click(); }; -const answerAndSubmitAgeQuestion = () => { - $(AgePage.answer()).setValue("50"); - $(AgePage.submit()).click(); +const answerAndSubmitAgeQuestion = async () => { + await $(AgePage.answer()).setValue("50"); + await $(AgePage.submit()).click(); }; -const answerAndSubmitReasonForNoConfirmationQuestion = () => { - $(ReasonNoConfirmationPage.iDidNotVisitSection2SoConfirmationWasNotNeeded()).click(); - $(ReasonNoConfirmationPage.submit()).click(); +const answerAndSubmitReasonForNoConfirmationQuestion = async () => { + await $(ReasonNoConfirmationPage.iDidNotVisitSection2SoConfirmationWasNotNeeded()).click(); + await $(ReasonNoConfirmationPage.submit()).click(); }; -const expectPersonalDetailsName = () => { - expect($(PrimaryPersonSummaryPage.nameAnswer()).getText()).to.contain("John Smith"); +const expectPersonalDetailsName = async () => { + await expect(await $(PrimaryPersonSummaryPage.nameAnswer()).getText()).to.contain("John Smith"); }; -const expectPersonalDetailsAge = () => { - expect($(PrimaryPersonSummaryPage.ageAnswer()).getText()).to.contain("50"); +const expectPersonalDetailsAge = async () => { + await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).getText()).to.contain("50"); }; -const expectReasonNoConfirmationAnswer = () => { - expect($(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain("I did not visit section 2, so confirmation was not needed"); +const expectReasonNoConfirmationAnswer = async () => { + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( + "I did not visit section 2, so confirmation was not needed" + ); }; -const expectPersonalDetailsAgeExistingFalse = () => { - expect($(PrimaryPersonSummaryPage.ageAnswer()).isExisting()).to.be.false; +const expectPersonalDetailsAgeExistingFalse = async () => { + await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).isExisting()).to.be.false; }; -const expectReasonNoConfirmationExistingFalse = () => { - expect($(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).isExisting()).to.be.false; +const expectReasonNoConfirmationExistingFalse = async () => { + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).isExisting()).to.be.false; }; diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index d90c3e3fc8..9c27308dc9 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -10,114 +10,114 @@ import HubPage from "../base_pages/hub.page"; describe("Routing and skipping section dependencies based on calculated summaries", () => { describe("Given the section dependencies based on a calculated summary questionnaire", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_routing_and_skipping_section_dependencies_calculated_summary.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies_calculated_summary.json"); }); - it("When the calculated summary total has not been set, Then the dependent section should not be enabled", () => { - expect($(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - expect($(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - expect($(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + it("When the calculated summary total has not been set, Then the dependent section should not be enabled", async () => { + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; }); - it("When the calculated summary total is equal to £100, Then the dependent section should be enabled", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(25); - $(FirstQuestionBlockPage.eggs()).setValue(25); - $(FirstQuestionBlockPage.bread()).setValue(25); - $(FirstQuestionBlockPage.cheese()).setValue(25); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - expect($(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - expect($(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - expect($(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + it("When the calculated summary total is equal to £100, Then the dependent section should be enabled", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(25); + await $(FirstQuestionBlockPage.eggs()).setValue(25); + await $(FirstQuestionBlockPage.bread()).setValue(25); + await $(FirstQuestionBlockPage.cheese()).setValue(25); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; }); - it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is not met (total less than £10), then the dependent question should be displayed", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(1); - $(FirstQuestionBlockPage.eggs()).setValue(1); - $(FirstQuestionBlockPage.bread()).setValue(1); - $(FirstQuestionBlockPage.cheese()).setValue(1); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - $(HubPage.summaryRowLink("dependent-question-section")).click(); - expect(browser.getUrl()).to.contain(FruitPage.pageName); + it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is not met (total less than £10), then the dependent question should be displayed", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(1); + await $(FirstQuestionBlockPage.eggs()).setValue(1); + await $(FirstQuestionBlockPage.bread()).setValue(1); + await $(FirstQuestionBlockPage.cheese()).setValue(1); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await $(HubPage.summaryRowLink("dependent-question-section")).click(); + await expect(await browser.getUrl()).to.contain(FruitPage.pageName); }); - it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is met (total greater than £10), then the dependent question should not be displayed", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(5); - $(FirstQuestionBlockPage.eggs()).setValue(5); - $(FirstQuestionBlockPage.bread()).setValue(5); - $(FirstQuestionBlockPage.cheese()).setValue(5); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - $(HubPage.summaryRowLink("dependent-question-section")).click(); - expect(browser.getUrl()).to.contain(VegetablesPage.pageName); + it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is met (total greater than £10), then the dependent question should not be displayed", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(5); + await $(FirstQuestionBlockPage.eggs()).setValue(5); + await $(FirstQuestionBlockPage.bread()).setValue(5); + await $(FirstQuestionBlockPage.cheese()).setValue(5); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await $(HubPage.summaryRowLink("dependent-question-section")).click(); + await expect(await browser.getUrl()).to.contain(VegetablesPage.pageName); }); - it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is greater than £100, then we should be routed to the second question block", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(30); - $(FirstQuestionBlockPage.eggs()).setValue(30); - $(FirstQuestionBlockPage.bread()).setValue(30); - $(FirstQuestionBlockPage.cheese()).setValue(30); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - $(HubPage.summaryRowLink("dependent-question-section")).click(); - $(VegetablesPage.yes()).click(); - $(VegetablesPage.submit()).click(); - expect(browser.getUrl()).to.contain(SecondQuestionBlockPage.pageName); + it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is greater than £100, then we should be routed to the second question block", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(30); + await $(FirstQuestionBlockPage.eggs()).setValue(30); + await $(FirstQuestionBlockPage.bread()).setValue(30); + await $(FirstQuestionBlockPage.cheese()).setValue(30); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await $(HubPage.summaryRowLink("dependent-question-section")).click(); + await $(VegetablesPage.yes()).click(); + await $(VegetablesPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SecondQuestionBlockPage.pageName); }); - it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is less than £100, then we should be routed to the section summary", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(20); - $(FirstQuestionBlockPage.eggs()).setValue(20); - $(FirstQuestionBlockPage.bread()).setValue(20); - $(FirstQuestionBlockPage.cheese()).setValue(20); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - $(HubPage.summaryRowLink("dependent-question-section")).click(); - $(VegetablesPage.yes()).click(); - $(VegetablesPage.submit()).click(); - expect(browser.getUrl()).to.contain(DependentQuestionSectionSummaryPage.pageName); + it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is less than £100, then we should be routed to the section summary", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(20); + await $(FirstQuestionBlockPage.eggs()).setValue(20); + await $(FirstQuestionBlockPage.bread()).setValue(20); + await $(FirstQuestionBlockPage.cheese()).setValue(20); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await $(HubPage.summaryRowLink("dependent-question-section")).click(); + await $(VegetablesPage.yes()).click(); + await $(VegetablesPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DependentQuestionSectionSummaryPage.pageName); }); - it("When a question in another section has a dependency on a calculated summary total, and both sections are complete, and I go back and edit the calculated summary total, then the dependent section status should be in progress", () => { - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(FirstQuestionBlockPage.milk()).setValue(20); - $(FirstQuestionBlockPage.eggs()).setValue(20); - $(FirstQuestionBlockPage.bread()).setValue(20); - $(FirstQuestionBlockPage.cheese()).setValue(20); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - - $(HubPage.summaryRowLink("dependent-question-section")).click(); - $(VegetablesPage.yes()).click(); - $(VegetablesPage.submit()).click(); - $(DependentQuestionSectionSummaryPage.submit()).click(); - expect($(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Completed"); - - $(HubPage.summaryRowLink("calculated-summary-section")).click(); - $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); - $(FirstQuestionBlockPage.milk()).setValue(100); - $(FirstQuestionBlockPage.submit()).click(); - $(CurrencyTotalPlaybackPage.submit()).click(); - $(CalculatedSummarySectionSummaryPage.submit()).click(); - expect($(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); + it("When a question in another section has a dependency on a calculated summary total, and both sections are complete, and I go back and edit the calculated summary total, then the dependent section status should be in progress", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(20); + await $(FirstQuestionBlockPage.eggs()).setValue(20); + await $(FirstQuestionBlockPage.bread()).setValue(20); + await $(FirstQuestionBlockPage.cheese()).setValue(20); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await $(HubPage.summaryRowLink("dependent-question-section")).click(); + await $(VegetablesPage.yes()).click(); + await $(VegetablesPage.submit()).click(); + await $(DependentQuestionSectionSummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Completed"); + + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); + await $(FirstQuestionBlockPage.milk()).setValue(100); + await $(FirstQuestionBlockPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); }); }); }); diff --git a/tests/functional/spec/routing_checkbox_contains.spec.js b/tests/functional/spec/routing_checkbox_contains.spec.js index 62efa083a7..c48b5c29b3 100644 --- a/tests/functional/spec/routing_checkbox_contains.spec.js +++ b/tests/functional/spec/routing_checkbox_contains.spec.js @@ -4,77 +4,77 @@ import ContainsAnyPage from "../generated_pages/routing_checkbox_contains/countr import SubmitPage from "../generated_pages/routing_checkbox_contains/submit.page"; describe("Routing Checkbox Contains Condition.", () => { - beforeEach(() => { - browser.openQuestionnaire("test_routing_checkbox_contains.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_routing_checkbox_contains.json"); }); - it('Given a list of checkbox options, when I have don\'t select "Liechtenstein" and select the option "India" or the option "Azerbaijan" or both then I should be routed to the "contains any" condition page', () => { + it('Given a list of checkbox options, when I have don\'t select "Liechtenstein" and select the option "India" or the option "Azerbaijan" or both then I should be routed to the "contains any" condition page', async () => { // When - expect($(RoutingCheckboxContains.liechtenstein()).isSelected()).to.be.false; + await expect(await $(RoutingCheckboxContains.liechtenstein()).isSelected()).to.be.false; - $(RoutingCheckboxContains.india()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.india()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); // Or - $(ContainsAnyPage.previous()).click(); + await $(ContainsAnyPage.previous()).click(); // When - $(RoutingCheckboxContains.india()).click(); - $(RoutingCheckboxContains.azerbaijan()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.india()).click(); + await $(RoutingCheckboxContains.azerbaijan()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); // Or - $(ContainsAnyPage.previous()).click(); + await $(ContainsAnyPage.previous()).click(); // When - $(RoutingCheckboxContains.india()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.india()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); }); - it('Given a list of checkbox options, when I select the option "Malta" or the option "Liechtenstein" or both then I should be routed to the summary condition page', () => { + it('Given a list of checkbox options, when I select the option "Malta" or the option "Liechtenstein" or both then I should be routed to the summary condition page', async () => { // When - $(RoutingCheckboxContains.liechtenstein()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.liechtenstein()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); // Or - $(ContainsAnyPage.previous()).click(); + await $(ContainsAnyPage.previous()).click(); // When - $(RoutingCheckboxContains.liechtenstein()).click(); - $(RoutingCheckboxContains.malta()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.liechtenstein()).click(); + await $(RoutingCheckboxContains.malta()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); // Or - $(ContainsAnyPage.previous()).click(); + await $(ContainsAnyPage.previous()).click(); // When - $(RoutingCheckboxContains.liechtenstein()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.liechtenstein()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it('Given a list of checkbox options, when I select the options "India", "Azerbaijan" and "Liechtenstein" then I should be routed to the "contains all" condition page', () => { + it('Given a list of checkbox options, when I select the options "India", "Azerbaijan" and "Liechtenstein" then I should be routed to the "contains all" condition page', async () => { // When - $(RoutingCheckboxContains.india()).click(); - $(RoutingCheckboxContains.azerbaijan()).click(); - $(RoutingCheckboxContains.liechtenstein()).click(); - $(RoutingCheckboxContains.submit()).click(); + await $(RoutingCheckboxContains.india()).click(); + await $(RoutingCheckboxContains.azerbaijan()).click(); + await $(RoutingCheckboxContains.liechtenstein()).click(); + await $(RoutingCheckboxContains.submit()).click(); // Then - expect(browser.getUrl()).to.contain(ContainsAllPage.pageName); + await expect(await browser.getUrl()).to.contain(ContainsAllPage.pageName); }); }); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index a9f8b82cb0..9dc9474327 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -11,65 +11,65 @@ import { getRandomString } from "../jwt_helper"; describe("Save sign out / Exit", () => { const responseId = getRandomString(16); - it("Given I am on an introduction page, when I click the exit button, then I am redirected to sign out page and my session is cleared", () => { - browser.openQuestionnaire("test_introduction.json"); - $(IntroductionPage.exitButton()).click(); + it("Given I am on an introduction page, when I click the exit button, then I am redirected to sign out page and my session is cleared", async () => { + await browser.openQuestionnaire("test_introduction.json"); + await $(IntroductionPage.exitButton()).click(); - expect(browser.getUrl()).to.contain("/surveys/todo"); + await expect(await browser.getUrl()).to.contain("/surveys/todo"); - browser.back(); - expect($("body").getHTML()).to.contain("Sorry, you need to sign in again"); + await browser.back(); + await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); }); - it("Given I am completing a questionnaire, when I select save and sign out, then I am redirected to sign out page and my session is cleared", () => { - browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); - $(SetMinMax.setMinimum()).setValue("10"); - $(SetMinMax.setMaximum()).setValue("1020"); - $(SetMinMax.submit()).click(); - $(TestMinMax.saveSignOut()).click(); + it("Given I am completing a questionnaire, when I select save and sign out, then I am redirected to sign out page and my session is cleared", async () => { + await browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); + await $(SetMinMax.setMinimum()).setValue("10"); + await $(SetMinMax.setMaximum()).setValue("1020"); + await $(SetMinMax.submit()).click(); + await $(TestMinMax.saveSignOut()).click(); - expect(browser.getUrl()).to.contain("/surveys/todo"); + await expect(await browser.getUrl()).to.contain("/surveys/todo"); - browser.back(); - expect($("body").getHTML()).to.contain("Sorry, you need to sign in again"); + await browser.back(); + await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); }); - it("Given I have started a questionnaire, when I return to the questionnaire, then I am returned to the page I was on and can then complete the questionnaire", () => { - browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); + it("Given I have started a questionnaire, when I return to the questionnaire, then I am returned to the page I was on and can then complete the questionnaire", async () => { + await browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); - $(TestMinMax.testRange()).setValue("10"); - $(TestMinMax.testMin()).setValue("123"); - $(TestMinMax.testMax()).setValue("1000"); - $(TestMinMax.testPercent()).setValue("100"); - $(TestMinMax.submit()).click(); - $(DetailAnswer.answer1()).click(); - $(DetailAnswer.submit()).click(); + await $(TestMinMax.testRange()).setValue("10"); + await $(TestMinMax.testMin()).setValue("123"); + await $(TestMinMax.testMax()).setValue("1000"); + await $(TestMinMax.testPercent()).setValue("100"); + await $(TestMinMax.submit()).click(); + await $(DetailAnswer.answer1()).click(); + await $(DetailAnswer.submit()).click(); - $(SubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain("thank-you"); + await $(SubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain("thank-you"); }); - it("Given a business questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", () => { - browser.openQuestionnaire("test_introduction.json"); + it("Given a business questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { + await browser.openQuestionnaire("test_introduction.json"); - expect($(IntroductionPage.exitButton()).getText()).to.contain("Exit"); - $(IntroductionPage.getStarted()).click(); + await expect(await $(IntroductionPage.exitButton()).getText()).to.contain("Exit"); + await $(IntroductionPage.getStarted()).click(); - expect($(IntroInterstitialPage.saveSignOut()).getText()).to.contain("Save and exit survey"); - $(IntroInterstitialPage.submit()).click(); + await expect(await $(IntroInterstitialPage.saveSignOut()).getText()).to.contain("Save and exit survey"); + await $(IntroInterstitialPage.submit()).click(); - expect($(SubmitPage.saveSignOut()).getText()).to.contain("Save and exit survey"); - $(SubmitPage.submit()).click(); + await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and exit survey"); + await $(SubmitPage.submit()).click(); - expect($(IntroThankYouPagePage.exitButton()).isExisting()).to.be.false; + await expect(await $(IntroThankYouPagePage.exitButton()).isExisting()).to.be.false; }); - it("Given a Census questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", () => { - browser.openQuestionnaire("test_thank_you_census_household.json"); + it("Given a Census questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { + await browser.openQuestionnaire("test_thank_you_census_household.json"); - expect($(HouseHolderConfirmationPage.saveSignOut()).getText()).to.contain("Save and complete later"); - $(HouseHolderConfirmationPage.submit()).click(); + await expect(await $(HouseHolderConfirmationPage.saveSignOut()).getText()).to.contain("Save and complete later"); + await $(HouseHolderConfirmationPage.submit()).click(); - expect($(SubmitPage.saveSignOut()).getText()).to.contain("Save and complete later"); + await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and complete later"); }); }); diff --git a/tests/functional/spec/skip_condition_block.spec.js b/tests/functional/spec/skip_condition_block.spec.js index 720bbf9283..8906e9a0e1 100644 --- a/tests/functional/spec/skip_condition_block.spec.js +++ b/tests/functional/spec/skip_condition_block.spec.js @@ -6,20 +6,20 @@ describe("Skip Conditions - Block", () => { const schema = "test_skip_condition_block.json"; describe("Given I am completing the test skip condition block survey,", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire(schema); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire(schema); }); - it("When I choose to skip on the first page, Then I should see the summary page", () => { - $(QuestionPage.yes()).click(); - $(QuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I choose to skip on the first page, Then I should see the summary page", async () => { + await $(QuestionPage.yes()).click(); + await $(QuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I choose not to skip on the first page, Then I should see the should-skip page", () => { - $(QuestionPage.no()).click(); - $(QuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(SkipPage.pageName); + it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { + await $(QuestionPage.no()).click(); + await $(QuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_condition_group.spec.js b/tests/functional/spec/skip_condition_group.spec.js index 2847bb7d2e..dd35ec39aa 100644 --- a/tests/functional/spec/skip_condition_group.spec.js +++ b/tests/functional/spec/skip_condition_group.spec.js @@ -6,20 +6,20 @@ describe("Skip Conditions - Group", () => { const schema = "test_skip_condition_group.json"; describe("Given I am completing the test skip condition group survey,", () => { - beforeEach("load the survey", () => { - browser.openQuestionnaire(schema); + beforeEach("load the survey", async () => { + await browser.openQuestionnaire(schema); }); - it("When I choose to skip on the first page, Then I should see the summary page", () => { - $(QuestionPage.yes()).click(); - $(QuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("When I choose to skip on the first page, Then I should see the summary page", async () => { + await $(QuestionPage.yes()).click(); + await $(QuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("When I choose not to skip on the first page, Then I should see the should-skip page", () => { - $(QuestionPage.no()).click(); - $(QuestionPage.submit()).click(); - expect(browser.getUrl()).to.contain(SkipPage.pageName); + it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { + await $(QuestionPage.no()).click(); + await $(QuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_condition_list.spec.js b/tests/functional/spec/skip_condition_list.spec.js index 58b1d93209..9dd71278a5 100644 --- a/tests/functional/spec/skip_condition_list.spec.js +++ b/tests/functional/spec/skip_condition_list.spec.js @@ -6,62 +6,62 @@ import MoreThanTwoInterstitialPage from "../generated_pages/skip_condition_list/ describe("Feature: Routing on lists", () => { describe("Given I start skip condition list survey", () => { - beforeEach(() => { - browser.openQuestionnaire("test_skip_condition_list.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_skip_condition_list.json"); }); - it("When I don't add a person to the list, Then the less than two people skippable page should be shown", () => { - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); + it("When I don't add a person to the list, Then the less than two people skippable page should be shown", async () => { + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); }); - it("When I add one person to the list, Then the less than two people skippable page should be shown", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); + it("When I add one person to the list, Then the less than two people skippable page should be shown", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); }); - it("When I add two people to the list, Then the two people skippable page should be shown", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(TwoInterstitialPage.pageName); + it("When I add two people to the list, Then the two people skippable page should be shown", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(TwoInterstitialPage.pageName); }); - it("When I add three people to the list, Then the more than two people skippable page should be shown", () => { - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Marcus"); - $(ListCollectorAddPage.lastName()).setValue("Twin"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Samuel"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.yes()).click(); - $(ListCollectorPage.submit()).click(); - $(ListCollectorAddPage.firstName()).setValue("Olivia"); - $(ListCollectorAddPage.lastName()).setValue("Clemens"); - $(ListCollectorAddPage.submit()).click(); - $(ListCollectorPage.no()).click(); - $(ListCollectorPage.submit()).click(); - expect(browser.getUrl()).to.contain(MoreThanTwoInterstitialPage.pageName); + it("When I add three people to the list, Then the more than two people skippable page should be shown", async () => { + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Marcus"); + await $(ListCollectorAddPage.lastName()).setValue("Twin"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Samuel"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Olivia"); + await $(ListCollectorAddPage.lastName()).setValue("Clemens"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(MoreThanTwoInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_conditions_not_set.spec.js b/tests/functional/spec/skip_conditions_not_set.spec.js index a735460a18..34ff105ab3 100644 --- a/tests/functional/spec/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/skip_conditions_not_set.spec.js @@ -3,18 +3,18 @@ import DrinkPage from "../generated_pages/skip_condition_not_set/drink-block.pag import SubmitPage from "../generated_pages/skip_condition_not_set/submit.page"; describe("Skip Conditions - Not Set", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_skip_condition_not_set.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_skip_condition_not_set.json"); }); - it("Given I do not complete the first page, Then I should see the summary page", () => { - $(FoodPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("Given I do not complete the first page, Then I should see the summary page", async () => { + await $(FoodPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I complete the first page, Then I should see the drink page", () => { - $(FoodPage.bacon()).click(); - $(FoodPage.submit()).click(); - expect(browser.getUrl()).to.contain(DrinkPage.pageName); + it("Given I complete the first page, Then I should see the drink page", async () => { + await $(FoodPage.bacon()).click(); + await $(FoodPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/skip_conditions_set.spec.js b/tests/functional/spec/skip_conditions_set.spec.js index 61d579a927..65bfa0589c 100644 --- a/tests/functional/spec/skip_conditions_set.spec.js +++ b/tests/functional/spec/skip_conditions_set.spec.js @@ -3,18 +3,18 @@ import DrinkPage from "../generated_pages/skip_condition_set/drink-block.page"; import SubmitPage from "../generated_pages/skip_condition_set/submit.page"; describe("Skip Conditions - Set", () => { - beforeEach("Load the survey", () => { - browser.openQuestionnaire("test_skip_condition_set.json"); + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_skip_condition_set.json"); }); - it("Given I complete the first page, Then I should see the summary page", () => { - $(FoodPage.bacon()).click(); - $(FoodPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); + it("Given I complete the first page, Then I should see the summary page", async () => { + await $(FoodPage.bacon()).click(); + await $(FoodPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - it("Given I do not complete the first page, Then I should see the drink page", () => { - $(FoodPage.submit()).click(); - expect(browser.getUrl()).to.contain(DrinkPage.pageName); + it("Given I do not complete the first page, Then I should see the drink page", async () => { + await $(FoodPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/submit_with_custom_submission_text.spec.js b/tests/functional/spec/submit_with_custom_submission_text.spec.js index 3740db36e5..5153c61cac 100644 --- a/tests/functional/spec/submit_with_custom_submission_text.spec.js +++ b/tests/functional/spec/submit_with_custom_submission_text.spec.js @@ -3,16 +3,16 @@ import IntroductionPage from "../generated_pages/submit_with_custom_submission_t import { SubmitPage } from "../base_pages/submit.page.js"; describe("Submit with custom submission text", () => { - beforeEach("Load the questionnaire", () => { - browser.openQuestionnaire("test_submit_with_custom_submission_text.json"); + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_submit_with_custom_submission_text.json"); }); - it("Given a questionnaire with custom submission content has been started, when it is completed to the submit page, then the correct submission content should be displayed", () => { - $(IntroductionPage.getStarted()).click(); - $(BreakfastPage.answer()).setValue("Eggs"); - $(BreakfastPage.submit()).click(); - expect($(SubmitPage.heading()).getText()).to.contain("Submit your questionnaire"); - expect($(SubmitPage.warning()).getText()).to.contain("You cannot view your answers after submission"); - expect($(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, submit this to complete it"); + it("Given a questionnaire with custom submission content has been started, when it is completed to the submit page, then the correct submission content should be displayed", async () => { + await $(IntroductionPage.getStarted()).click(); + await $(BreakfastPage.answer()).setValue("Eggs"); + await $(BreakfastPage.submit()).click(); + await expect(await $(SubmitPage.heading()).getText()).to.contain("Submit your questionnaire"); + await expect(await $(SubmitPage.warning()).getText()).to.contain("You cannot view your answers after submission"); + await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, submit this to complete it"); }); }); diff --git a/tests/functional/spec/textarea.spec.js b/tests/functional/spec/textarea.spec.js index de77241300..126d8fce37 100644 --- a/tests/functional/spec/textarea.spec.js +++ b/tests/functional/spec/textarea.spec.js @@ -5,45 +5,45 @@ describe("Textarea", () => { const textareaSchema = "test_textarea.json"; const textareaLimit = `${TextareaBlock.answer()} + [data-charcount-singular]`; - beforeEach(() => { - browser.openQuestionnaire(textareaSchema); + beforeEach(async () => { + await browser.openQuestionnaire(textareaSchema); }); - it("Given a textarea option, a user should be able to click the label of the textarea to focus", () => { - $(TextareaBlock.answerLabel()).click(); - expect($(TextareaBlock.answer()).isFocused()).to.be.true; + it("Given a textarea option, a user should be able to click the label of the textarea to focus", async () => { + await $(TextareaBlock.answerLabel()).click(); + await expect(await $(TextareaBlock.answer()).isFocused()).to.be.true; }); - it('Given a textarea option, When no text is entered, Then the summary should display "No answer provided"', () => { - $(TextareaBlock.submit()).click(); - expect($(TextareaSummary.answer()).getText()).to.contain("No answer provided"); + it('Given a textarea option, When no text is entered, Then the summary should display "No answer provided"', async () => { + await $(TextareaBlock.submit()).click(); + await expect(await $(TextareaSummary.answer()).getText()).to.contain("No answer provided"); }); - it("Given a textarea option, When some text is entered, Then the summary should display the text", () => { - $(TextareaBlock.answer()).setValue("Some text"); - $(TextareaBlock.submit()).click(); - expect($(TextareaSummary.answer()).getText()).to.contain("Some text"); + it("Given a textarea option, When some text is entered, Then the summary should display the text", async () => { + await $(TextareaBlock.answer()).setValue("Some text"); + await $(TextareaBlock.submit()).click(); + await expect(await $(TextareaSummary.answer()).getText()).to.contain("Some text"); }); - it("Given a text entered in textarea , When user submits and revisits the textarea, Then the textarea must contain the text entered previously", () => { - $(TextareaBlock.answer()).setValue("'Twenty><&Five'"); - $(TextareaBlock.submit()).click(); - expect($(TextareaSummary.answer()).getText()).to.contain("'Twenty><&Five'"); - $(TextareaSummary.answerEdit()).click(); - $(TextareaBlock.answer()).getValue(); + it("Given a text entered in textarea , When user submits and revisits the textarea, Then the textarea must contain the text entered previously", async () => { + await $(TextareaBlock.answer()).setValue("'Twenty><&Five'"); + await $(TextareaBlock.submit()).click(); + await expect(await $(TextareaSummary.answer()).getText()).to.contain("'Twenty><&Five'"); + await $(TextareaSummary.answerEdit()).click(); + await $(TextareaBlock.answer()).getValue(); }); - it("Displays the number of characters remaining", () => { - expect($(textareaLimit).getText()).to.contain("20"); + it("Displays the number of characters remaining", async () => { + await expect(await $(textareaLimit).getText()).to.contain("20"); }); - it("Updates the number of characters remaining when the user adds content", () => { - $(TextareaBlock.answer()).setValue("Banjo"); - expect($(textareaLimit).getText()).to.contain("15"); + it("Updates the number of characters remaining when the user adds content", async () => { + await $(TextareaBlock.answer()).setValue("Banjo"); + await expect(await $(textareaLimit).getText()).to.contain("15"); }); - it("The user is unable to add more characters when the limit is reached", () => { - $(TextareaBlock.answer()).setValue("This sentence is over twenty characters long"); - expect($(textareaLimit).getText()).to.contain("0"); - $(TextareaBlock.answer()).getValue(); + it("The user is unable to add more characters when the limit is reached", async () => { + await $(TextareaBlock.answer()).setValue("This sentence is over twenty characters long"); + await expect(await $(textareaLimit).getText()).to.contain("0"); + await $(TextareaBlock.answer()).getValue(); }); }); diff --git a/tests/functional/spec/textfield.spec.js b/tests/functional/spec/textfield.spec.js index 4f9e8d58b2..f77b52d6ce 100644 --- a/tests/functional/spec/textfield.spec.js +++ b/tests/functional/spec/textfield.spec.js @@ -2,26 +2,26 @@ import TextFieldPage from "../generated_pages/textfield/name-block.page.js"; import SubmitPage from "../generated_pages/textfield/submit.page.js"; describe("Textfield", () => { - it("Given a textfield option, a user should be able to click the label of the textfield to focus", () => { - browser.openQuestionnaire("test_textfield.json"); - $(TextFieldPage.nameLabel()).click(); - expect($(TextFieldPage.name()).isFocused()).to.be.true; + it("Given a textfield option, a user should be able to click the label of the textfield to focus", async () => { + await browser.openQuestionnaire("test_textfield.json"); + await $(TextFieldPage.nameLabel()).click(); + await expect(await $(TextFieldPage.name()).isFocused()).to.be.true; }); - it("Given a text entered in textfield , When user submits and revisits the textfield, Then the textfield must contain the text entered previously", () => { - browser.openQuestionnaire("test_textfield.json"); - $(TextFieldPage.name()).setValue("'Twenty><&Five'"); - $(TextFieldPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.pageName); - expect($(SubmitPage.nameAnswer()).getText()).to.contain("Twenty><&Five'"); - $(SubmitPage.nameAnswerEdit()).click(); - $(TextFieldPage.name()).getValue(); + it("Given a text entered in textfield , When user submits and revisits the textfield, Then the textfield must contain the text entered previously", async () => { + await browser.openQuestionnaire("test_textfield.json"); + await $(TextFieldPage.name()).setValue("'Twenty><&Five'"); + await $(TextFieldPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Twenty><&Five'"); + await $(SubmitPage.nameAnswerEdit()).click(); + await $(TextFieldPage.name()).getValue(); }); - it("Given the string entered to the textfield is too long, When the user submits, then the correct error message is displayed", () => { - browser.openQuestionnaire("test_textfield.json"); - $(TextFieldPage.name()).setValue("This string is too long"); - $(TextFieldPage.submit()).click(); - expect($(TextFieldPage.errorNumber(1)).getText()).to.contain("You have entered too many characters. Enter up to 20 characters"); + it("Given the string entered to the textfield is too long, When the user submits, then the correct error message is displayed", async () => { + await browser.openQuestionnaire("test_textfield.json"); + await $(TextFieldPage.name()).setValue("This string is too long"); + await $(TextFieldPage.submit()).click(); + await expect(await $(TextFieldPage.errorNumber(1)).getText()).to.contain("You have entered too many characters. Enter up to 20 characters"); }); }); diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index 6c13cd4161..24807ef3a9 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -3,36 +3,36 @@ import MultipleSuggestionsPage from "../generated_pages/textfield_suggestions/mu import SubmitPage from "../generated_pages/textfield_suggestions/submit.page.js"; describe("Suggestions", () => { - it("Given I open a textfield with a suggestions url, when I have entered text, then it will show suggestions", () => { - browser.openQuestionnaire("test_textfield_suggestions.json"); - $(SuggestionsPage.country()).setValue("Uni"); + it("Given I open a textfield with a suggestions url, when I have entered text, then it will show suggestions", async () => { + await browser.openQuestionnaire("test_textfield_suggestions.json"); + await $(SuggestionsPage.country()).setValue("Uni"); $("#country-answer-listbox li").waitForDisplayed(); - expect($$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); + await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); }); }); describe("Suggestions", () => { - it("Given I open a textfield with a suggestions url that allows multiple suggestions, when I have entered text and picked suggestion from a list, then after typing more text it will show new suggestions", () => { - browser.openQuestionnaire("test_textfield_suggestions.json"); + it("Given I open a textfield with a suggestions url that allows multiple suggestions, when I have entered text and picked suggestion from a list, then after typing more text it will show new suggestions", async () => { + await browser.openQuestionnaire("test_textfield_suggestions.json"); const suggestionsList = $("#multiple-country-answer-listbox li"); const suggestionsOption = $("#multiple-country-answer-listbox__option--0"); - $(SuggestionsPage.country()).setValue("United States of America"); - $(SuggestionsPage.submit()).click(); - $(MultipleSuggestionsPage.multipleCountry()).click(); + await $(SuggestionsPage.country()).setValue("United States of America"); + await $(SuggestionsPage.submit()).click(); + await $(MultipleSuggestionsPage.multipleCountry()).click(); // Browser needs to pause before typing starts to allow for the autosuggest Javascript to initialise - browser.pause(500); - browser.keys("Ita"); - suggestionsList.waitForExist(); - suggestionsOption.click(); - $(MultipleSuggestionsPage.multipleCountry()).click(); + await browser.pause(500); + await browser.keys("Ita"); + await suggestionsList.waitForExist(); + await suggestionsOption.click(); + await $(MultipleSuggestionsPage.multipleCountry()).click(); // Browser needs to pause before typing starts to allow for the autosuggest Javascript to initialise - browser.pause(500); - browser.keys(" United"); - suggestionsList.waitForExist(); - expect($$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); - suggestionsOption.click(); - $(MultipleSuggestionsPage.submit()).click(); - expect(browser.getUrl()).to.contain(SubmitPage.url()); + await browser.pause(500); + await browser.keys(" United"); + await suggestionsList.waitForExist(); + await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); + await suggestionsOption.click(); + await $(MultipleSuggestionsPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); }); diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index ff0aee0b0a..de0b431a2d 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -7,64 +7,66 @@ import ThankYouSubmitPage from "../generated_pages/thank_you/submit.page"; describe("Thank You Social", () => { describe("Given I launch a social themed questionnaire", () => { - beforeEach(() => { - browser.openQuestionnaire("test_theme_social.json", { version: "v2", theme: "social" }); + beforeEach(async () => { + await browser.openQuestionnaire("test_theme_social.json", { version: "v2", theme: "social" }); }); - it("When I navigate to the thank you page, Then I should see social theme content", () => { - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Social Survey"); - expect($(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted"); - expect($(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); - expect($(ThankYouPage.metadata()).getHTML()).to.not.contain("Submission reference:"); + it("When I navigate to the thank you page, Then I should see social theme content", async () => { + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Social Survey"); + await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted"); + await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); + await expect(await $(ThankYouPage.metadata()).getHTML()).to.not.contain("Submission reference:"); }); }); }); describe("Thank You Default", () => { describe("Given I launch a default themed questionnaire", () => { - beforeEach(() => { - browser.openQuestionnaire("test_title.json"); + beforeEach(async () => { + await browser.openQuestionnaire("test_title.json"); }); - it("When I navigate to the thank you page, Then I should see default theme content", () => { - $(CheckboxPage.good()).click(); - $(SubmitPage.submit()).click(); - $(HubPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); - expect($(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Question Title Test"); - expect($(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted for"); - expect($(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); - expect($(ThankYouPage.metadata()).getHTML()).to.contain("Submission reference:"); + it("When I navigate to the thank you page, Then I should see default theme content", async () => { + await $(CheckboxPage.good()).click(); + await $(SubmitPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Question Title Test"); + await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted for"); + await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); + await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submission reference:"); }); }); }); describe("Thank You Default View Response Enabled", () => { describe("Given I launch a questionnaire where view response is enabled", () => { - beforeEach(() => { - browser.openQuestionnaire("test_thank_you.json"); - $(DidYouKnowPage.yes()).click(); - $(DidYouKnowPage.submit()).click(); - $(ThankYouSubmitPage.submit()).click(); - expect(browser.getUrl()).to.contain(ThankYouPage.pageName); + beforeEach(async () => { + await browser.openQuestionnaire("test_thank_you.json"); + await $(DidYouKnowPage.yes()).click(); + await $(DidYouKnowPage.submit()).click(); + await $(ThankYouSubmitPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); - it("When I navigate to the thank you page, and I have submitted less than 40 seconds ago, Then I should see the countdown timer and option to view my answers", () => { - expect($(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; - expect($(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Thank You"); - expect($(ThankYouPage.viewAnswersTitle()).getHTML()).to.contain("Get a copy of your answers"); - expect($(ThankYouPage.viewAnswersLink()).getText()).to.contain("save or print your answers"); - expect($(ThankYouPage.viewSubmittedCountdown()).getHTML()).to.contain("For security, your answers will only be available to view for another"); + it("When I navigate to the thank you page, and I have submitted less than 40 seconds ago, Then I should see the countdown timer and option to view my answers", async () => { + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Thank You"); + await expect(await $(ThankYouPage.viewAnswersTitle()).getHTML()).to.contain("Get a copy of your answers"); + await expect(await $(ThankYouPage.viewAnswersLink()).getText()).to.contain("save or print your answers"); + await expect(await $(ThankYouPage.viewSubmittedCountdown()).getHTML()).to.contain( + "For security, your answers will only be available to view for another" + ); }); - it("When I navigate to the thank you page, and I have submitted more than 40 seconds ago, Then I shouldn't see the option to view my answers", () => { - expect($(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; - browser.pause(46000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) - expect($(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.true; - expect($(ThankYouPage.viewSubmittedGuidance()).getHTML()).to.contain("For security, you can no longer view or get a copy of your answers"); + it("When I navigate to the thank you page, and I have submitted more than 40 seconds ago, Then I shouldn't see the option to view my answers", async () => { + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; + await browser.pause(46000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.true; + await expect(await $(ThankYouPage.viewSubmittedGuidance()).getHTML()).to.contain("For security, you can no longer view or get a copy of your answers"); }); }); }); diff --git a/tests/functional/spec/theme_beis.spec.js b/tests/functional/spec/theme_beis.spec.js index c95e5495bb..a90ad89b9f 100644 --- a/tests/functional/spec/theme_beis.spec.js +++ b/tests/functional/spec/theme_beis.spec.js @@ -2,13 +2,13 @@ import RadioPage from "../generated_pages/theme_beis/radio.page"; describe("Theme BEIS", () => { describe("Given I launch a BEIS themed questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_theme_beis.json"); + before(async () => { + await browser.openQuestionnaire("test_theme_beis.json"); }); - it("When I navigate to the radio page, Then I should see BEIS theme content", () => { - expect(browser.getUrl()).to.contain(RadioPage.pageName); - expect($("#beis-logo-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); + it("When I navigate to the radio page, Then I should see BEIS theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#beis-logo-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); }); }); }); diff --git a/tests/functional/spec/theme_beis_ni.spec.js b/tests/functional/spec/theme_beis_ni.spec.js index 7ebdd3debb..14948e00cf 100644 --- a/tests/functional/spec/theme_beis_ni.spec.js +++ b/tests/functional/spec/theme_beis_ni.spec.js @@ -2,14 +2,14 @@ import RadioPage from "../generated_pages/theme_beis_ni/radio.page"; describe("Theme BEIS-NI", () => { describe("Given I launch a BEIS-NI themed questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_theme_beis_ni.json"); + before(async () => { + await browser.openQuestionnaire("test_theme_beis_ni.json"); }); - it("When I navigate to the radio page, Then I should see BEIS-NI theme content", () => { - expect(browser.getUrl()).to.contain(RadioPage.pageName); - expect($("#beis-logo-mobile-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); - expect($("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + it("When I navigate to the radio page, Then I should see BEIS-NI theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#beis-logo-mobile-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); + await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_northernireland.spec.js b/tests/functional/spec/theme_northernireland.spec.js index 06c037eab8..81e1a1a7a4 100644 --- a/tests/functional/spec/theme_northernireland.spec.js +++ b/tests/functional/spec/theme_northernireland.spec.js @@ -2,13 +2,13 @@ import RadioPage from "../generated_pages/theme_northernireland/radio.page"; describe("Theme Northern Ireland", () => { describe("Given I launch a Northern Ireland themed questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_theme_northernireland.json"); + before(async () => { + await browser.openQuestionnaire("test_theme_northernireland.json"); }); - it("When I navigate to the radio page, Then I should see Northern Ireland theme content", () => { - expect(browser.getUrl()).to.contain(RadioPage.pageName); - expect($("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + it("When I navigate to the radio page, Then I should see Northern Ireland theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_orr.spec.js b/tests/functional/spec/theme_orr.spec.js index a75346323b..15f5ad86e1 100644 --- a/tests/functional/spec/theme_orr.spec.js +++ b/tests/functional/spec/theme_orr.spec.js @@ -2,13 +2,13 @@ import RadioPage from "../generated_pages/theme_orr/radio.page"; describe("Theme Rail and Road", () => { describe("Given I launch a Rail and Road themed questionnaire", () => { - before(() => { - browser.openQuestionnaire("test_theme_orr.json"); + before(async () => { + await browser.openQuestionnaire("test_theme_orr.json"); }); - it("When I navigate to the radio page, Then I should see Rail and Road theme content", () => { - expect(browser.getUrl()).to.contain(RadioPage.pageName); - expect($("#orr-logo-mobile-alt").getHTML()).to.contain("Office of Rail and Road logo"); + it("When I navigate to the radio page, Then I should see Rail and Road theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#orr-logo-mobile-alt").getHTML()).to.contain("Office of Rail and Road logo"); }); }); }); diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index 386a81daea..ec634787e6 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -2,11 +2,11 @@ import { TimeoutModalPage } from "../../base_pages/timeout-modal.page.js"; class TestCase { testCaseExpired(page) { - it("When the timeout modal is displayed, and I do not extend my session, Then I will be redirected to the session expired page", () => { - this.checkTimeoutModal(); - browser.pause(65000); // We are waiting for the session to expire - expect(browser.getUrl()).to.contain("/session-expired"); - expect($("body").getHTML()) + it("When the timeout modal is displayed, and I do not extend my session, Then I will be redirected to the session expired page", async () => { + await this.checkTimeoutModal(); + await browser.pause(65000); // We are waiting for the session to expire + await expect(await browser.getUrl()).to.contain("/session-expired"); + await expect(await $("body").getHTML()) .to.include( "Sorry, you need to sign in again", "This is because you have either:", @@ -19,31 +19,31 @@ class TestCase { } testCaseExtended(page) { - it("When the timeout modal is displayed, and I click the “Continue survey” button, Then my session will be extended", () => { - this.checkTimeoutModal(); - $(TimeoutModalPage.submit()).click(); - expect($(TimeoutModalPage.timer()).getText()).to.equal(""); - browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired - browser.refresh(); - expect(browser.getUrl()).to.contain(page.pageName); - expect($("body").getHTML()).to.not.include("Sorry, you need to sign in again"); + it("When the timeout modal is displayed, and I click the “Continue survey” button, Then my session will be extended", async () => { + await this.checkTimeoutModal(); + await $(TimeoutModalPage.submit()).click(); + await expect(await $(TimeoutModalPage.timer()).getText()).to.equal(""); + await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired + await browser.refresh(); + await expect(await browser.getUrl()).to.contain(await page.pageName); + await expect(await $("body").getHTML()).to.not.include("Sorry, you need to sign in again"); }).timeout(140000); } testCaseExtendedNewWindow(page) { - it("When the timeout modal is displayed, but I open a new window and then focus back on the timeout modal window, Then my session will be extended", () => { - this.checkTimeoutModal(); - browser.newWindow(""); - browser.switchWindow(page.pageName); - browser.refresh(); - browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired - expect(browser.getUrl()).to.contain(page.pageName); + it("When the timeout modal is displayed, but I open a new window and then focus back on the timeout modal window, Then my session will be extended", async () => { + await this.checkTimeoutModal(); + await browser.newWindow(""); + await browser.switchWindow(await page.pageName); + await browser.refresh(); + await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired + await expect(await browser.getUrl()).to.contain(await page.pageName); }).timeout(140000); } - checkTimeoutModal() { - $(TimeoutModalPage.timer()).waitForDisplayed({ timeout: 70000 }); - expect($(TimeoutModalPage.timer()).getText()).to.equal( + async checkTimeoutModal() { + await $(TimeoutModalPage.timer()).waitForDisplayed({ timeout: 70000 }); + await expect(await $(TimeoutModalPage.timer()).getText()).to.equal( "To protect your information, your progress will be saved and you will be signed out in 59 seconds." ); } diff --git a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js index f64cf124e6..52395bb078 100644 --- a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_expired.spec.js @@ -3,8 +3,8 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Expired", () => { describe("Given I am completing the survey,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); }); TimeoutModalTestCase.testCaseExpired(TimeoutInterstitialPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js index a96fcc7428..4dff85da36 100644 --- a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js @@ -5,10 +5,10 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); - $(TimeoutInterstitialPage.submit()).click(); - $(TimeoutSubmitPage.submit()).click(); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); + await $(TimeoutInterstitialPage.submit()).click(); + await $(TimeoutSubmitPage.submit()).click(); }); TimeoutModalTestCase.testCaseExpired(ThankYouPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js index 4348c33cf3..14795ea524 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_extended.spec.js @@ -3,8 +3,8 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Expired", () => { describe("Given I am completing the survey,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); }); TimeoutModalTestCase.testCaseExtended(TimeoutInterstitialPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js index 2279cf22c6..d97b013a12 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js @@ -5,10 +5,10 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); - $(TimeoutInterstitialPage.submit()).click(); - $(TimeoutSubmitPage.submit()).click(); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); + await $(TimeoutInterstitialPage.submit()).click(); + await $(TimeoutSubmitPage.submit()).click(); }); TimeoutModalTestCase.testCaseExtended(ThankYouPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js index 3cf469800f..eedd00ddc4 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_extended_new_window.spec.js @@ -3,8 +3,8 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Expired", () => { describe("Given I am completing the survey,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); }); TimeoutModalTestCase.testCaseExtendedNewWindow(TimeoutInterstitialPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js index bf670da745..3af53a896d 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js @@ -5,10 +5,10 @@ import { TimeoutModalTestCase } from "../timeout_modal.js"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { - before(() => { - browser.openQuestionnaire("test_timeout_modal.json"); - $(TimeoutInterstitialPage.submit()).click(); - $(TimeoutSubmitPage.submit()).click(); + before(async () => { + await browser.openQuestionnaire("test_timeout_modal.json"); + await $(TimeoutInterstitialPage.submit()).click(); + await $(TimeoutSubmitPage.submit()).click(); }); TimeoutModalTestCase.testCaseExtendedNewWindow(ThankYouPage); }); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 8b371a6ad6..6bfaa0d92a 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -1,4 +1,4 @@ -exports.config = { +export const config = { // // ==================== // Runner Configuration @@ -16,14 +16,14 @@ exports.config = { // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working // directory is where your package.json resides, so `wdio` will be called from there. // - specs: ["./tests/functional/spec/**/*.js"], + specs: ["./spec/**/*.js"], suites: { - timeout_modal_expired: ["./tests/functional/spec/timeout/timeout_modal_expired/*.js"], - timeout_modal_extended: ["./tests/functional/spec/timeout/timeout_modal_extended/*.js"], - timeout_modal_extended_new_window: ["./tests/functional/spec/timeout/timeout_modal_extended_new_window/*.js"], - components: ["./tests/functional/spec/components/**/*.js"], - features: ["./tests/functional/spec/features/**/*.js"], - general: ["./tests/functional/spec/*.spec.js"], + timeout_modal_expired: ["./spec/timeout/timeout_modal_expired/*.js"], + timeout_modal_extended: ["./spec/timeout/timeout_modal_extended/*.js"], + timeout_modal_extended_new_window: ["./spec/timeout/timeout_modal_extended_new_window/*.js"], + components: ["./spec/components/**/*.js"], + features: ["./spec/features/**/*.js"], + general: ["./spec/*.spec.js"], }, // Patterns to exclude. exclude: [ diff --git a/yarn.lock b/yarn.lock index 45bdb34848..0a1d1aab59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,27 +4,27 @@ "@ampproject/remapping@^2.1.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" - integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== +"@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": + version "7.20.14" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz" + integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== "@babel/core@^7.17.5": version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz" integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== dependencies: "@ampproject/remapping" "^2.1.0" @@ -43,672 +43,679 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" - integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== +"@babel/generator@^7.17.9", "@babel/generator@^7.20.7": + version "7.20.14" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz" + integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.20.7" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" - source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" - integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== +"@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" - integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - -"@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" - integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": + version "7.20.12" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz" + integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.20.5" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz" + integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.2.1" + +"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" - -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" - integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" - integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== - -"@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-wrap-function" "^7.16.8" - "@babel/types" "^7.16.8" - -"@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-simple-access@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" - integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== - dependencies: - "@babel/helper-function-name" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz" + integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== + dependencies: + "@babel/types" "^7.20.7" + +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.17.7", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" "@babel/helpers@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" - integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + version "7.20.13" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz" + integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.13" + "@babel/types" "^7.20.7" -"@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.16.7", "@babel/parser@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" - integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== +"@babel/parser@^7.17.9", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": + version "7.20.15" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz" + integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" - integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" - integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" "@babel/plugin-proposal-async-generator-functions@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" - integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-class-static-block@^7.16.7": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz#164e8fd25f0d80fa48c5a4d1438a6629325ad83c" - integrity sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz" + integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.6" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-namespace-from@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" - integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" - integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" - integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" - integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.17.0" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" - integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== +"@babel/plugin-proposal-optional-chaining@^7.16.7", "@babel/plugin-proposal-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz" + integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.16.11": - version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" - integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.10" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-private-property-in-object@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" - integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== + version "7.20.5" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz" + integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" - integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== dependencies: "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-arrow-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" - integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + version "7.20.15" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz" + integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz" + integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.16.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" - integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz" + integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-duplicate-keys@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" - integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-for-of@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + version "7.18.8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-amd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" - integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== + version "7.20.11" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== dependencies: - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" - integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== + version "7.20.11" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz" + integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== dependencies: - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" "@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" - integrity sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw== + version "7.20.11" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" - integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== dependencies: - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" - integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== + version "7.20.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-new-target@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" - integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== +"@babel/plugin-transform-parameters@^7.16.7", "@babel/plugin-transform-parameters@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz" + integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-regenerator@^7.16.7": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" - integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== + version "7.20.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== dependencies: - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" - integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-runtime@^7.17.0": version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" + resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz" integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== dependencies: "@babel/helper-module-imports" "^7.16.7" @@ -719,59 +726,59 @@ semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-template-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typeof-symbol@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" - integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== + version "7.18.10" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.16.11": version "7.16.11" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz" integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== dependencies: "@babel/compat-data" "^7.16.8" @@ -851,7 +858,7 @@ "@babel/preset-modules@^0.1.5": version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -862,7 +869,7 @@ "@babel/register@^7.17.0": version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.17.7.tgz#5eef3e0f4afc07e25e847720e7b987ae33f08d0b" + resolved "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz" integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== dependencies: clone-deep "^4.0.1" @@ -871,64 +878,70 @@ pirates "^4.0.5" source-map-support "^0.5.16" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/runtime@^7.17.2", "@babel/runtime@^7.8.4": version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz" integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" - integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.9" - "@babel/types" "^7.17.0" +"@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.17.9", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": + version "7.20.13" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@eslint/eslintrc@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.2.tgz#4989b9e8c0216747ee7cca314ae73791bb281aae" - integrity sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg== + version "1.4.1" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.1" - globals "^13.9.0" + espree "^9.4.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" - minimatch "^3.0.4" + minimatch "^3.1.2" strip-json-comments "^3.1.1" "@humanwhocodes/config-array@^0.9.2": version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" @@ -937,471 +950,362 @@ "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/expect-utils@^29.4.3": + version "29.4.3" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.3.tgz" + integrity sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ== + dependencies: + jest-get-type "^29.4.3" + +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/types@^29.4.3": + version "29.4.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz" + integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA== dependencies: + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" "@jridgewell/gen-mapping@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.0.tgz#b30fd66b5426bb7f44bb6043ab127b20ee404063" - integrity sha512-YH+BnkvuCiPR+MUOY6JIArdTIGrRtsxnLaIxPRy4CpGJ/V6OO6Gq/1J+FJEc4j5e5h6Bcy3/K7prlMrm93BJoA== + version "0.1.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== dependencies: - "@jridgewell/set-array" "1.0.0" + "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz#4ac237f4dabc8dd93330386907b97591801f7352" - integrity sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw== +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/set-array@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.0.0.tgz#6a8e62049ab198c5f7daf8047e71947ef46c11c8" - integrity sha512-LcqVnHCjOAj8BTCtjpwYZCMTn4yArusbdObCVRUYvBHhrR5fVLVyENG+UVWM4T4H/ufv7NiBLdprllxWs/5PaQ== +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + version "0.3.17" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sinclair/typebox@^0.25.16": + version "0.25.23" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz" + integrity sha512-VEB8ygeP42CFLWyAJhN5OklpxUliqdNEUcXb4xZ/CINqtYGTjL5ukluKdKzQ0iWdUxyQ7B0539PAUhHKrCNWSQ== -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== +"@sindresorhus/is@^5.2.0": + version "5.3.0" + resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz" + integrity sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw== + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== dependencies: - defer-to-connect "^2.0.0" + defer-to-connect "^2.0.1" "@testim/chrome-version@^1.1.3": version "1.1.3" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.3.tgz#fbb68696899d7b8c1b9b891eded9c04fe2cd5529" + resolved "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.3.tgz" integrity sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A== -"@types/aria-query@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.0.tgz#df2d64b5cc73cca0d75e2a7793d6b5c199c2f7b2" - integrity sha512-P+dkdFu0n08PDIvw+9nT9ByQnd+Udc8DaWPb9HKfaPwCvWvQpC5XaMRx2xLWECm9x1VKNps6vEAlirjA6+uNrQ== - -"@types/cacheable-request@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" - integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/diff@^5.0.0": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.2.tgz#dd565e0086ccf8bc6522c6ebafd8a3125c91c12b" - integrity sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg== - -"@types/easy-table@^0.0.33": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/easy-table/-/easy-table-0.0.33.tgz#b1f7ec29014ec24906b4f28d8368e2e99b399313" - integrity sha512-/vvqcJPmZUfQwCgemL0/34G7bIQnCuvgls379ygRlcC1FqNqk3n+VZ15dAO51yl6JNDoWd8vsk+kT8zfZ1VZSw== - -"@types/ejs@^3.0.5": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.0.tgz#ab8109208106b5e764e5a6c92b2ba1c625b73020" - integrity sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA== - -"@types/fibers@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/fibers/-/fibers-3.1.1.tgz#b714d357eebf6aec0bc5d70512e573b89bc84f20" - integrity sha512-yHoUi46uika0snoTpNcVqUSvgbRndaIps4TUCotrXjtc0DHDoPQckmyXEZ2bX3e4mpJmyEW3hRhCwQa/ISCPaA== - -"@types/fs-extra@^9.0.4": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== - dependencies: - "@types/node" "*" - -"@types/http-cache-semantics@*": +"@types/http-cache-semantics@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/inquirer@^8.1.2": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" - integrity sha512-wKW3SKIUMmltbykg4I5JzCVzUhkuD9trD6efAmYgN2MrSntY0SMRQzEnD3mkyJ/rv9NLbTC7g3hKKE86YwEDLw== - dependencies: - "@types/through" "*" - rxjs "^7.2.0" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" -"@types/json-buffer@~3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" - integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== - "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/keyv@*": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - -"@types/lodash.flattendeep@^4.4.6": - version "4.4.7" - resolved "https://registry.yarnpkg.com/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.7.tgz#0ce3dccbe006826d58e9824b27df4b00ed3e90e6" - integrity sha512-1h6GW/AeZw/Wej6uxrqgmdTDZX1yFS39lRsXYkg+3kWvOWWrlGCI6H7lXxlUHOzxDT4QeYGmgPpQ3BX9XevzOg== - dependencies: - "@types/lodash" "*" - -"@types/lodash.pickby@^4.6.6": - version "4.6.7" - resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.7.tgz#fd089a5a7f8cbe7294ae5c90ea5ecd9f4cae4d2c" - integrity sha512-4ebXRusuLflfscbD0PUX4eVknDHD9Yf+uMtBIvA/hrnTqeAzbuHuDjvnYriLjUrI9YrhCPVKUf4wkRSXJQ6gig== - dependencies: - "@types/lodash" "*" - -"@types/lodash.union@^4.6.6": - version "4.6.7" - resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.7.tgz#ceace5ed9f3610652ba4a72e0e0afb2a0eec7a4d" - integrity sha512-6HXM6tsnHJzKgJE0gA/LhTGf/7AbjUk759WZ1MziVm+OBNAATHhdgj+a3KVE8g76GCLAnN4ZEQQG1EGgtBIABA== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.182" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" - integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== - -"@types/mocha@^9.0.0": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== - -"@types/node@*": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" - integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== - -"@types/node@^17.0.4": - version "17.0.29" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" - integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== - -"@types/object-inspect@^1.8.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" - integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== - -"@types/puppeteer@^5.4.0": - version "5.4.6" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.6.tgz#afc438e41dcbc27ca1ba0235ea464a372db2b21c" - integrity sha512-98Kghehs7+/GD9b56qryhqdqVCXUTbetTv3PlvDnmFRTHQH0j9DIp1f7rkAW3BAj4U3yoeSEQnKgdW8bDq0Y0Q== - dependencies: - "@types/node" "*" +"@types/mocha@^10.0.0": + version "10.0.1" + resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz" + integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== -"@types/recursive-readdir@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz#330f5ec0b73e8aeaf267a6e056884e393f3543a3" - integrity sha512-Xd+Ptc4/F2ueInqy5yK2FI5FxtwwbX2+VZpcg+9oYsFJVen8qQKGapCr+Bi5wQtHU1cTXT8s+07lo/nKPgu8Gg== - dependencies: - "@types/node" "*" +"@types/node@*", "@types/node@^18.0.0": + version "18.13.0" + resolved "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz" + integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== -"@types/responselike@*", "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" +"@types/normalize-package-data@^2.4.1": + version "2.4.1" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/stack-utils@^2.0.0": version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/stream-buffers@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.4.tgz#bf128182da7bc62722ca0ddf5458a9c65f76e648" - integrity sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w== - dependencies: - "@types/node" "*" - -"@types/supports-color@^8.1.0": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.1.tgz#1b44b1b096479273adf7f93c75fc4ecc40a61ee4" - integrity sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw== +"@types/which@^2.0.1": + version "2.0.2" + resolved "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz" + integrity sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw== -"@types/through@*": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== +"@types/ws@^8.5.3": + version "8.5.4" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" -"@types/tmp@^0.2.0": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.3.tgz#908bfb113419fd6a42273674c00994d40902c165" - integrity sha512-dDZH/tXzwjutnuk4UacGgFRwV+JSLaXL1ikvidfJprkb7L9Nx1njcRHHmi3Dsvt7pgqqTEeucQuOrWHPFgzVHA== - -"@types/ua-parser-js@^0.7.33": - version "0.7.36" - resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz#9bd0b47f26b5a3151be21ba4ce9f5fa457c5f190" - integrity sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ== - -"@types/which@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf" - integrity sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA== - "@types/yargs-parser@*": version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== +"@types/yargs@^17.0.8": + version "17.0.22" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== dependencies: "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" + resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz" integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== dependencies: "@types/node" "*" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -"@wdio/cli@^7.16.16": - version "7.19.6" - resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-7.19.6.tgz#0b9054fba7f4161185ef80f20f259c9a45c3493a" - integrity sha512-lcHos0zyIQumdt+uzCbzVwJuzMyCI/Tv7nt6CcaJtIkpuut5WZ9H7V6IM3j1UYV0SEqz58KU+VPB82qazEP+YQ== - dependencies: - "@types/ejs" "^3.0.5" - "@types/fs-extra" "^9.0.4" - "@types/inquirer" "^8.1.2" - "@types/lodash.flattendeep" "^4.4.6" - "@types/lodash.pickby" "^4.6.6" - "@types/lodash.union" "^4.6.6" - "@types/node" "^17.0.4" - "@types/recursive-readdir" "^2.2.0" - "@wdio/config" "7.19.5" - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" +"@wdio/cli@^8.3.10": + version "8.3.10" + resolved "https://registry.npmjs.org/@wdio/cli/-/cli-8.3.10.tgz" + integrity sha512-Zlr/qhzZLL7tWJB28mJ/p2KcOlb2FPfJLocUq3uqnGmTsbTdjSp0h0tVBYE6BjmwL7pKLlE+g9/fYFkjiRQMTA== + dependencies: + "@types/node" "^18.0.0" + "@wdio/config" "8.3.2" + "@wdio/globals" "8.3.10" + "@wdio/logger" "8.1.0" + "@wdio/protocols" "8.3.5" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" async-exit-hook "^2.0.1" - chalk "^4.0.0" - chokidar "^3.0.0" - cli-spinners "^2.1.0" - ejs "^3.0.1" - fs-extra "^10.0.0" - inquirer "8.2.2" + chalk "^5.0.1" + chokidar "^3.5.3" + cli-spinners "^2.6.1" + ejs "^3.1.8" + execa "^7.0.0" + import-meta-resolve "^2.1.0" + inquirer "9.1.2" lodash.flattendeep "^4.4.0" lodash.pickby "^4.6.0" lodash.union "^4.6.0" - mkdirp "^1.0.4" + mkdirp "^2.0.0" + read-pkg-up "9.1.0" recursive-readdir "^2.2.2" - webdriverio "7.19.5" - yargs "^17.0.0" + webdriverio "8.3.10" + yargs "^17.5.1" yarn-install "^1.0.0" -"@wdio/config@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-7.19.5.tgz#aa8158d648e1ffb28a7e53474d5ce171066e82f7" - integrity sha512-GyG0SSUjw9RyDgEwculgwiWyQ0eEeFAgaKTAa4RHC6ZgHHTgfyxzkWqBmNLzHfiB6GSR2DyZDcDsPT7ZAHkiEg== - dependencies: - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" - deepmerge "^4.0.0" - glob "^7.1.2" - -"@wdio/local-runner@^7.16.16": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-7.19.5.tgz#1a2a519e0e8eb7fd5197bda7651ffbc813a2c1e0" - integrity sha512-VZr726za6JHY+14SqEEq/VolKIsC2QgMH9BhX3sw0yWUsRxWy4M7STl2fiQWgJbW0bc2Z74TRLrx7TYfncIPpw== - dependencies: - "@types/stream-buffers" "^3.0.3" - "@wdio/logger" "7.19.0" - "@wdio/repl" "7.19.5" - "@wdio/runner" "7.19.5" - "@wdio/types" "7.19.5" +"@wdio/config@8.3.2": + version "8.3.2" + resolved "https://registry.npmjs.org/@wdio/config/-/config-8.3.2.tgz" + integrity sha512-lLZh53MorYRxAr08x2o6B1npRgc5ZmGEGdzH2neILnVcs/x3fQV9uFKpC9sKtMmU8B0G0oMUSoIaj8GfqygcNQ== + dependencies: + "@wdio/logger" "8.1.0" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" + decamelize "^6.0.0" + deepmerge-ts "^4.2.2" + glob "^8.0.3" + import-meta-resolve "^2.1.0" + read-pkg-up "^9.1.0" + +"@wdio/globals@8.3.10": + version "8.3.10" + resolved "https://registry.npmjs.org/@wdio/globals/-/globals-8.3.10.tgz" + integrity sha512-ZlvCqOWZjkbDFxtskAaON/pH56RzSAfRTNPLMzIK7j3xnZT2d5Oi0te0Oo0Hs13sEoTEaqSzwJjBVIHueOk3Dw== + optionalDependencies: + expect-webdriverio "^4.0.1" + webdriverio "8.3.10" + +"@wdio/globals@^8.2.4": + version "8.3.9" + resolved "https://registry.npmjs.org/@wdio/globals/-/globals-8.3.9.tgz" + integrity sha512-Y4cB2l92NO2WqgkRQE/oLwrvCAT/pPSvmFXKvk0avsHbTPHnukaf0gfB0ku0EKbsyaC9glwPE/PEVrXSC8Hsiw== + optionalDependencies: + expect-webdriverio "^4.0.1" + webdriverio "8.3.9" + +"@wdio/local-runner@^8.3.10": + version "8.3.10" + resolved "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.3.10.tgz" + integrity sha512-5FZCu+AgcWVGaLpWV7nHchzECFlVdVS3OF2GjhPz3srTPh0NAQkvH0+WXtpXxgNgeURIcaBUx/NxqquMyuc7DQ== + dependencies: + "@types/node" "^18.0.0" + "@wdio/logger" "8.1.0" + "@wdio/repl" "8.1.0" + "@wdio/runner" "8.3.10" + "@wdio/types" "8.3.0" async-exit-hook "^2.0.1" - split2 "^4.0.0" + split2 "^4.1.0" stream-buffers "^3.0.2" -"@wdio/logger@7.19.0", "@wdio/logger@^7.5.3": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-7.19.0.tgz#23697a4b4aaea56c3bd477a0393af2a5c175fc85" - integrity sha512-xR7SN/kGei1QJD1aagzxs3KMuzNxdT/7LYYx+lt6BII49+fqL/SO+5X0FDCZD0Ds93AuQvvz9eGyzrBI2FFXmQ== +"@wdio/logger@8.1.0", "@wdio/logger@^8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@wdio/logger/-/logger-8.1.0.tgz" + integrity sha512-QRC5b7FF4JIYUCqggnVA0sZ80TwIUFN9JyBSbuGuMxaSLSLujSo7WfuSrnQXVvsRbnJ16wWwJWYigfLkxOW86Q== dependencies: - chalk "^4.0.0" + chalk "^5.1.2" loglevel "^1.6.0" loglevel-plugin-prefix "^0.8.4" strip-ansi "^6.0.0" -"@wdio/mocha-framework@^7.16.15": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-7.19.5.tgz#33f1965a52e8974ffc45f7ad2dc7ec642e7b6191" - integrity sha512-dZ2Xhl1LX/KOz/kAQ0bYcdsfr+rpMeLimwk2t9hPxtLt+xRYQNuJ8MnUyetJ7OqWsqtV/txe+JFxnfjuDg/HXg== +"@wdio/mocha-framework@^8.3.0": + version "8.3.0" + resolved "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.3.0.tgz" + integrity sha512-19BVsTe0DBlArNy12HTYAEFAepUXvU67JceKUOpNcYIr8lpk6ZqPVZ8tm4GiBTDvt7FSt8u5Iw2BsnsrcbQGuA== + dependencies: + "@types/mocha" "^10.0.0" + "@types/node" "^18.0.0" + "@wdio/logger" "8.1.0" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" + mocha "^10.0.0" + +"@wdio/protocols@8.3.5": + version "8.3.5" + resolved "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.3.5.tgz" + integrity sha512-/zWz+ok6gIB1/L/VEOCoD8nCB8inNF+rsVOYps3FgNbrliQ782YLfA1uAVJTw3U6CaO+7zZ8aJw82EswpoxWjg== + +"@wdio/repl@8.1.0": + version "8.1.0" + resolved "https://registry.npmjs.org/@wdio/repl/-/repl-8.1.0.tgz" + integrity sha512-96G4TzbYnRf95+GURo15FYt6iTYq85nbWU6YQedLRAV15RfSp4foKTbAnq++bKKMALNL6gdzTc+HGhQr3Q0sQg== dependencies: - "@types/mocha" "^9.0.0" - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" - expect-webdriverio "^3.0.0" - mocha "^9.0.0" - -"@wdio/protocols@7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-7.19.0.tgz#cd753752c64b9c1dd7ace05398c1d11c46af41ab" - integrity sha512-ji74rQag6v+INSNd0J8eAh2rpH5vOXgeiP5Qr32K6PWU6HzYWuAFH2x4srXsH0JawHCdTK2OQAOYrLmMb44hug== - -"@wdio/repl@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-7.19.5.tgz#e99d1e24c52ac34b7434d21c94419c46eabcfbf8" - integrity sha512-Rej9SrH2DIPQ2342o4ixF48fJYsaeU4rtz+R7wYv6J1RZaNQvINdO21HoODfJ7DIDltVWzvLhCOe4CgzGubRGA== - dependencies: - "@wdio/utils" "7.19.5" - -"@wdio/reporter@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.19.5.tgz#2099bd46df6ed4af753d9f56f4bb6c8f66131c54" - integrity sha512-1h2IM+xF1Oc483HZElhOVgKrgSxI8erMQJ8fTgPo17aeWQFU2rl5HsdY49+LRU5YEmSHNm3HbNn8fs1nkpSnIA== - dependencies: - "@types/diff" "^5.0.0" - "@types/node" "^17.0.4" - "@types/object-inspect" "^1.8.0" - "@types/supports-color" "^8.1.0" - "@types/tmp" "^0.2.0" - "@wdio/types" "7.19.5" - diff "^5.0.0" - fs-extra "^10.0.0" - object-inspect "^1.10.3" - supports-color "8.1.1" + "@types/node" "^18.0.0" -"@wdio/runner@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-7.19.5.tgz#c9ca9574264143070c27ff73c02739be7bcb19c8" - integrity sha512-9eKoTqT/Ht8RPlVfqBcDTvhluP/nwyHOvqw0+zHUoJHTtLFCsZNcAXnBgAuozIrHFgyX5e4LMQLvY0voWDmqCQ== +"@wdio/reporter@8.3.0": + version "8.3.0" + resolved "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.3.0.tgz" + integrity sha512-SCI6YXMDOs+rIDXUqaQYncv3SAcMPU9nQKGCLWjtZXsYHYlLZMT/PaFy7lWyebsObiRAKJYayXOqG7ymS6hDzA== dependencies: - "@wdio/config" "7.19.5" - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" - deepmerge "^4.0.0" + "@types/node" "^18.0.0" + "@wdio/logger" "8.1.0" + "@wdio/types" "8.3.0" + diff "^5.0.0" + object-inspect "^1.12.0" + supports-color "9.3.1" + +"@wdio/runner@8.3.10": + version "8.3.10" + resolved "https://registry.npmjs.org/@wdio/runner/-/runner-8.3.10.tgz" + integrity sha512-jdbcbbC0oMB79WQO1wEmLb6q1msiF7RAUnLoj7TSLrBr/yVqSuURIHpde5fo4d0GdYoPuVIS0VF06bw6o7QhTA== + dependencies: + "@types/node" "^18.0.0" + "@wdio/config" "8.3.2" + "@wdio/globals" "8.3.10" + "@wdio/logger" "8.1.0" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" + deepmerge-ts "^4.2.2" + expect-webdriverio "^4.0.1" gaze "^1.1.2" - webdriver "7.19.5" - webdriverio "7.19.5" + webdriver "8.3.8" + webdriverio "8.3.10" -"@wdio/spec-reporter@^7.16.14": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-7.19.5.tgz#55d5c1fc99c7b3b2595889abdb332909e1062c65" - integrity sha512-ovkjhOowpSJKblEIC5Si+pOmr0/fUqo+bNkxZwYDNSzIwPyxJnED17WSNYw+JAzSp2PgfNwjELoQfMNCDPQvAQ== +"@wdio/spec-reporter@^8.3.0": + version "8.3.0" + resolved "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.3.0.tgz" + integrity sha512-2K+qMNM5rYMCJdw5M3H6XfXUkto/jLnE2GgAI7VoTo6mZVOGGCRRPVtCa7NR/asWF+P0iBl9PJbPFv2/TzV59A== dependencies: - "@types/easy-table" "^0.0.33" - "@wdio/reporter" "7.19.5" - "@wdio/types" "7.19.5" - chalk "^4.0.0" - easy-table "^1.1.1" + "@wdio/reporter" "8.3.0" + "@wdio/types" "8.3.0" + chalk "^5.1.2" + easy-table "^1.2.0" pretty-ms "^7.0.0" -"@wdio/sync@^7.16.16": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/sync/-/sync-7.19.5.tgz#c1b750a350158dfcbe6e5d5aefef77eaed6e1d96" - integrity sha512-lawVJupeXmQGKT4MAAYF6vMw8Fr7e9BVRdoE9DiQMVY25ghmQqAO+gyE8LH7xJW5qBSJqXyWdoSiOdKWbS/2tg== - dependencies: - "@types/fibers" "^3.1.0" - "@types/puppeteer" "^5.4.0" - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" - fibers "^5.0.0" - webdriverio "7.19.5" - -"@wdio/types@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.19.5.tgz#e05790f61dfab54ee6683ac799cb5f96615d1d0f" - integrity sha512-S1lC0pmtEO7NVH/2nM1c7NHbkgxLZH3VVG/z6ym3Bbxdtcqi2LMsEvvawMAU/fmhyiIkMsGZCO8vxG9cRw4z4A== - dependencies: - "@types/node" "^17.0.4" - got "^11.8.1" - -"@wdio/utils@7.19.5": - version "7.19.5" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-7.19.5.tgz#9c2d17e1ae3d21824409806f80faec1d7c78baff" - integrity sha512-UvZK8PvY50aUEg5CYSDkkT2NsDA0+Eo7QWjreA3L5ff50jaFrYnpVOyfX4PjWhErH8gZtYSwep+DSgldCUInGQ== - dependencies: - "@wdio/logger" "7.19.0" - "@wdio/types" "7.19.5" +"@wdio/types@8.3.0": + version "8.3.0" + resolved "https://registry.npmjs.org/@wdio/types/-/types-8.3.0.tgz" + integrity sha512-TTs3ETVOJtooTIY/u2+feeBnMBx2Hb4SEItN31r0pFncn37rnIZXE/buywLNf5wvZW9ukxoCup5hCnohOR27eQ== + dependencies: + "@types/node" "^18.0.0" + +"@wdio/utils@8.3.0": + version "8.3.0" + resolved "https://registry.npmjs.org/@wdio/utils/-/utils-8.3.0.tgz" + integrity sha512-BbgzxAu4AN99l9hwaRUvkvBJLeIxON7F6ts+vq4LASRiGVRErrwYGeO9H3ffYgpCAaYSvXnw2GtOf2SQGaPoLA== + dependencies: + "@wdio/logger" "8.1.0" + "@wdio/types" "8.3.0" + import-meta-resolve "^2.2.0" p-iteration "^1.1.8" -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.7.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.8.0: + version "8.8.2" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== agent-base@6: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" @@ -1409,7 +1313,7 @@ aggregate-error@^3.0.0: ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1419,71 +1323,78 @@ ajv@^6.10.0, ajv@^6.12.4: ansi-colors@4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.3.0: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== ansi-styles@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" - integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== +ansi-styles@^6.0.0, ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" archiver-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + resolved "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz" integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== dependencies: glob "^7.1.4" @@ -1499,7 +1410,7 @@ archiver-utils@^2.1.0: archiver@^5.0.0: version "5.3.1" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" + resolved "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz" integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== dependencies: archiver-utils "^2.1.0" @@ -1512,38 +1423,40 @@ archiver@^5.0.0: argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" - integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== + version "5.1.3" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + version "3.1.6" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + version "1.3.1" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" asn1.js@^5.0.1: version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" @@ -1553,111 +1466,113 @@ asn1.js@^5.0.1: assertion-error@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-exit-hook@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" + resolved "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz" integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== async@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" - integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + version "3.2.4" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== axios@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3" - integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw== + version "1.3.3" + resolved "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz" + integrity sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + version "0.3.3" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + version "0.5.3" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz" integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base64url@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.0.3, bl@^4.1.0: +bl@^4.0.3: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" readable-stream "^3.4.0" +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^4.0.0: version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -1665,47 +1580,46 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-stdout@1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.17.5, browserslist@^4.20.2: - version "4.20.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" - integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== dependencies: - caniuse-lite "^1.0.30001332" - electron-to-chromium "^1.4.118" - escalade "^3.1.1" - node-releases "^2.0.3" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.2.1, buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1713,7 +1627,7 @@ buffer@^5.2.1, buffer@^5.5.0: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -1721,8 +1635,8 @@ buffer@^6.0.3: cac@^3.0.3: version "3.0.4" - resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" - integrity sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8= + resolved "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz" + integrity sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w== dependencies: camelcase-keys "^3.0.0" chalk "^1.1.3" @@ -1732,27 +1646,27 @@ cac@^3.0.3: suffix "^0.1.0" text-table "^0.2.0" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.1: + version "10.2.7" + resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.7.tgz" + integrity sha512-I4SA6mKgDxcxVbSt/UmIkb9Ny8qSkg6ReBHtAAXnZHk7KOSx5g3DTiAOaYzcHCs6oOdHn+bip9T48E6tMvK9hw== + dependencies: + "@types/http-cache-semantics" "^4.0.1" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.2" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1760,35 +1674,35 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase-keys@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-3.0.0.tgz#fc0c6c360363f7377e3793b9a16bccf1070c1ca4" - integrity sha1-/AxsNgNj9zd+N5O5oWvM8QcMHKQ= + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz" + integrity sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ== dependencies: camelcase "^3.0.0" map-obj "^1.0.0" camelcase@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + resolved "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg== camelcase@^6.0.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001332: - version "1.0.30001332" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" - integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== +caniuse-lite@^1.0.30001449: + version "1.0.30001452" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz" + integrity sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w== chai@^4.3.6: version "4.3.6" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" + resolved "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz" integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== dependencies: assertion-error "^1.1.0" @@ -1801,8 +1715,8 @@ chai@^4.3.6: chalk@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -1812,34 +1726,39 @@ chalk@^1.1.3: chalk@^2.0.0, chalk@^2.0.1: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.0, chalk@^5.0.1, chalk@^5.1.2: + version "5.2.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== check-error@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -chokidar@3.5.3, chokidar@^3.0.0, chokidar@^3.5.0: +chokidar@3.5.3, chokidar@^3.5.0, chokidar@^3.5.3: version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" @@ -1854,13 +1773,13 @@ chokidar@3.5.3, chokidar@^3.0.0, chokidar@^3.5.0: chownr@^1.1.1: version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== chrome-launcher@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.0.tgz#5144a57aba0cf2f4cbe61dccefdde024fb3ca7fc" - integrity sha512-ZQqX5kb9H0+jy1OqLnWampfocrtSZaGl7Ny3F9GRha85o4odbL8x55paUzh51UC7cEmZ5obp3H2Mm70uC2PpRA== + version "0.15.1" + resolved "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.1.tgz" + integrity sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg== dependencies: "@types/node" "*" escape-string-regexp "^4.0.0" @@ -1869,7 +1788,7 @@ chrome-launcher@^0.15.0: chromedriver@^110.0.0: version "110.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-110.0.0.tgz#d00a1a2976592d933faa8e9839e97692922834a4" + resolved "https://registry.npmjs.org/chromedriver/-/chromedriver-110.0.0.tgz" integrity sha512-Le6q8xrA/3fAt+g8qiN0YjsYxINIhQMC6wj9X3W5L77uN4NspEzklDrqYNwBcEVn7PcAEJ73nLlS7mTyZRspHA== dependencies: "@testim/chrome-version" "^1.1.3" @@ -1880,26 +1799,38 @@ chromedriver@^110.0.0: proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.1.0, cli-spinners@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.6.1: + version "2.7.0" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" @@ -1907,109 +1838,103 @@ cli-truncate@^2.1.0: cli-truncate@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== dependencies: slice-ansi "^5.0.0" string-width "^5.0.0" -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz" + integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== cliui@^7.0.2: version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== - dependencies: - mimic-response "^1.0.0" - clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.16: - version "2.0.16" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^8.3.0: version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== compare-versions@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" - integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== - -compress-brotli@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" - integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ== - dependencies: - "@types/json-buffer" "~3.0.0" - json-buffer "~3.0.1" + version "5.0.3" + resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== compress-commons@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" + resolved "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz" integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== dependencies: buffer-crc32 "^0.2.13" @@ -2019,37 +1944,34 @@ compress-commons@^4.1.0: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== core-js-compat@^3.20.2, core-js-compat@^3.21.0: - version "3.22.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.2.tgz#eec621eb276518efcf718d0a6d9d042c3d0cad48" - integrity sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw== + version "3.28.0" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz" + integrity sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg== dependencies: - browserslist "^4.20.2" - semver "7.0.0" + browserslist "^4.21.5" core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== crc-32@^1.2.0: version "1.2.2" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== crc32-stream@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" + resolved "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz" integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== dependencies: crc-32 "^1.2.0" @@ -2057,22 +1979,22 @@ crc32-stream@^4.0.2: cross-fetch@3.1.5: version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: node-fetch "2.6.7" cross-spawn@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz" + integrity sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA== dependencies: lru-cache "^4.0.1" which "^1.2.9" cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -2081,255 +2003,314 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: css-shorthand-properties@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" + resolved "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz" integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== css-value@^0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" - integrity sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo= + resolved "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz" + integrity sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q== debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debug@4.3.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - debug@^2.6.8, debug@^2.6.9: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.2.7: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" decamelize@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decamelize@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz" + integrity sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA== + decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" deep-eql@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== dependencies: type-detect "^4.0.0" +deep-equal@^2.0.5: + version "2.2.0" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz" + integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + dependencies: + call-bind "^1.0.2" + es-get-iterator "^1.1.2" + get-intrinsic "^1.1.3" + is-arguments "^1.1.1" + is-array-buffer "^3.0.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +deepmerge-ts@^4.2.2: + version "4.3.0" + resolved "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-4.3.0.tgz" + integrity sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw== defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + version "1.0.4" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0: +defer-to-connect@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-properties@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.2.0" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -devtools-protocol@0.0.981744: - version "0.0.981744" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.981744.tgz#9960da0370284577d46c28979a0b32651022bacf" - integrity sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg== - -devtools-protocol@^0.0.982423: - version "0.0.982423" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.982423.tgz#39ac3791d4c5b90ebb416d4384663b7b0cc44154" - integrity sha512-FnVW2nDbjGNw1uD/JRC+9U5768W7e1TfUwqbDTcSsAu1jXFjITSX8w3rkW5FEpHRMPPGpvNSmO1pOpqByiWscA== - -devtools@7.19.5: - version "7.19.5" - resolved "https://registry.yarnpkg.com/devtools/-/devtools-7.19.5.tgz#6a310e4d52b526fc89110dee5ec174df51ee8984" - integrity sha512-O62fArKiAT7fxTgn3GagEFZPpKF2Kfx/HkzGmJVIlBktrN4bJ+DAwZh6d27U9hE6qraMbVPEw+tW6T3RWoj0Sw== - dependencies: - "@types/node" "^17.0.4" - "@types/ua-parser-js" "^0.7.33" - "@wdio/config" "7.19.5" - "@wdio/logger" "7.19.0" - "@wdio/protocols" "7.19.0" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" +devtools-protocol@0.0.1082910: + version "0.0.1082910" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1082910.tgz" + integrity sha512-RqoZ2GmqaNxyx+99L/RemY5CkwG9D0WEfOKxekwCRXOGrDCep62ngezEJUVMq6rISYQ+085fJnWDQqGHlxVNww== + +devtools-protocol@^0.0.1103684: + version "0.0.1103684" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1103684.tgz" + integrity sha512-44Qr4zFQkzW8r4WdDOSuQoxIzPDczY/K1RDfyxzEsiG2nSzbTojDW3becX5+HrDw3gENG8jY6ffbHZ2/Ix5LSA== + +devtools@8.3.8: + version "8.3.8" + resolved "https://registry.npmjs.org/devtools/-/devtools-8.3.8.tgz" + integrity sha512-zMulliftMVMUiMRp/YYoCFA3DebzdFij7gkIHlj1yq+gHqwsPSd6pNO+CyZVa0CJhH5uHOs3FAsbIpXeR/woew== + dependencies: + "@types/node" "^18.0.0" + "@wdio/config" "8.3.2" + "@wdio/logger" "8.1.0" + "@wdio/protocols" "8.3.5" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" chrome-launcher "^0.15.0" - edge-paths "^2.1.0" - puppeteer-core "^13.1.3" + edge-paths "^3.0.5" + import-meta-resolve "^2.1.0" + puppeteer-core "19.6.3" query-selector-shadow-dom "^1.0.0" ua-parser-js "^1.0.1" - uuid "^8.0.0" + uuid "^9.0.0" + which "^3.0.0" -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== -diff@5.0.0, diff@^5.0.0: +diff@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +diff@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -easy-table@^1.1.1: +easy-table@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb" + resolved "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz" integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww== dependencies: ansi-regex "^5.0.1" optionalDependencies: wcwidth "^1.0.1" -edge-paths@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-2.2.1.tgz#d2d91513225c06514aeac9843bfce546abbf4391" - integrity sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw== +edge-paths@^3.0.5: + version "3.0.5" + resolved "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz" + integrity sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg== dependencies: - "@types/which" "^1.3.2" + "@types/which" "^2.0.1" which "^2.0.2" -ejs@^3.0.1: - version "3.1.7" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" - integrity sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw== +ejs@^3.1.8: + version "3.1.8" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz" + integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.118: - version "1.4.123" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.123.tgz#de88ea7fd29d7c868e63c88f129e91494bcf3266" - integrity sha512-0pHGE53WkYoFbsgwYcVKEpWa6jbzlvkohIEA2CUoZ9b5KC+w/zlMiQHvW/4IBcOh7YoEFqRNavgTk02TBoUTUw== +electron-to-chromium@^1.4.284: + version "1.4.295" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz" + integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.1, es-abstract@^1.19.2: - version "1.19.5" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.5.tgz#a2cb01eb87f724e815b278b0dd0d00f36ca9a7f1" - integrity sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== dependencies: + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function-bind "^1.1.1" - get-intrinsic "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" + is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.0" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-get-iterator@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" es-shim-unscopables@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== dependencies: has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -2338,32 +2319,37 @@ es-to-primitive@^1.2.1: es6-promise@^4.2.8: version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== escalade@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + eslint-cli@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/eslint-cli/-/eslint-cli-1.1.1.tgz#ae6979edd8ee6e78c6d413b525f4052cb2a94cfd" + resolved "https://registry.npmjs.org/eslint-cli/-/eslint-cli-1.1.1.tgz" integrity sha512-Gu+fYzt7M+jIb5szUHLl5Ex0vFY7zErbi78D7ZaaLunvVTxHRvbOlfzmJlIUWsV5WDM4qyu9TD7WnGgDaDgaMA== dependencies: chalk "^2.0.1" @@ -2372,33 +2358,33 @@ eslint-cli@^1.1.1: eslint-config-standard@^14.1.1: version "14.1.1" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" + resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz" integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + version "0.3.7" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + version "2.7.4" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" eslint-plugin-chai-friendly@^0.7.2: version "0.7.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz#0ebfbb2c1244f5de2997f3963d155758234f2b0f" + resolved "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz" integrity sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg== eslint-plugin-es@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + resolved "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz" integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== dependencies: eslint-utils "^2.0.0" @@ -2406,7 +2392,7 @@ eslint-plugin-es@^3.0.0: eslint-plugin-import@^2.25.4: version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz" integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== dependencies: array-includes "^3.1.4" @@ -2425,7 +2411,7 @@ eslint-plugin-import@^2.25.4: eslint-plugin-json@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz#251108ba1681c332e0a442ef9513bd293619de67" + resolved "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz" integrity sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g== dependencies: lodash "^4.17.21" @@ -2433,7 +2419,7 @@ eslint-plugin-json@^3.1.0: eslint-plugin-node@^11.1.0: version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + resolved "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz" integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== dependencies: eslint-plugin-es "^3.0.0" @@ -2445,17 +2431,17 @@ eslint-plugin-node@^11.1.0: eslint-plugin-promise@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz#017652c07c9816413a41e11c30adc42c3d55ff18" + resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz" integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw== eslint-plugin-standard@^4.0.1: version "4.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz#0c3bf3a67e853f8bbbc580fb4945fbf16f41b7c5" + resolved "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz" integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== eslint-scope@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" @@ -2463,36 +2449,36 @@ eslint-scope@^7.1.1: eslint-utils@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-utils@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint-visitor-keys@^3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.10.0: version "8.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.14.0.tgz#62741f159d9eb4a79695b28ec4989fcdec623239" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz" integrity sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw== dependencies: "@eslint/eslintrc" "^1.2.2" @@ -2531,42 +2517,42 @@ eslint@^8.10.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.3.1: - version "9.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" - integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== +espree@^9.3.1, espree@^9.4.0: + version "9.4.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== dependencies: - acorn "^8.7.0" - acorn-jsx "^5.3.1" + acorn "^8.8.0" + acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" esquery@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== execa@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -2579,27 +2565,46 @@ execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -expect-webdriverio@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-3.2.1.tgz#7db8f450784b7e9fa231798f9761bffd70586d3a" - integrity sha512-bN3FGCKvddmtlt1A8SW3A1HK9lUbNybrvwUEDu5nEjJMwC8oTnE+9kDogA/6aEN1kRX9cX3tKB8WdyqbGPYnoA== +execa@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-7.0.0.tgz" + integrity sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g== dependencies: - expect "^27.0.2" - jest-matcher-utils "^27.0.2" + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" -expect@^27.0.2: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect-webdriverio@^4.0.1: + version "4.1.2" + resolved "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.1.2.tgz" + integrity sha512-punU19F1Nq0m9H7f5+A/OtT/LljA+d04sVB9nHIFDYbumdDBOy3I23EFzCsSZMvMdda5r3stKmKalTX4SG2yhA== + dependencies: + expect "^29.4.0" + jest-matcher-utils "^29.4.0" + optionalDependencies: + "@wdio/globals" "^8.2.4" + webdriverio "^8.2.4" + +expect@^29.4.0: + version "29.4.3" + resolved "https://registry.npmjs.org/expect/-/expect-29.4.3.tgz" + integrity sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.4.3" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.4.3" + jest-message-util "^29.4.3" + jest-util "^29.4.3" external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2608,7 +2613,7 @@ external-editor@^3.0.3: extract-zip@2.0.1, extract-zip@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: debug "^4.1.1" @@ -2619,69 +2624,63 @@ extract-zip@2.0.1, extract-zip@^2.0.1: fast-deep-equal@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz" + integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fd-slicer@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== dependencies: pend "~1.2.0" -fibers@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/fibers/-/fibers-5.0.1.tgz#bb9b02aa022685185d21aed227363e456d87660d" - integrity sha512-VMC7Frt87Oo0AOJ6EcPFbi+tZmkQ4tD85aatwyWL6I9cYMJmm2e+pXUJsfGZ36U7MffXtjou2XIiWJMtHriErw== - dependencies: - detect-libc "^1.0.3" - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== +figures@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz" + integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg== dependencies: - escape-string-regexp "^1.0.5" + escape-string-regexp "^5.0.0" + is-unicode-supported "^1.2.0" file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" filelist@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" - integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q== + version "1.0.4" + resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: minimatch "^5.0.1" fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-cache-dir@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" @@ -2690,7 +2689,7 @@ find-cache-dir@^2.0.0: find-up@5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -2698,37 +2697,30 @@ find-up@5.0.0: find-up@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" + integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" + locate-path "^7.1.0" + path-exists "^5.0.0" flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -2736,22 +2728,34 @@ flat-cache@^3.0.4: flat@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + version "3.2.7" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.15.0: version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -2760,94 +2764,99 @@ form-data@^4.0.0: fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== dependencies: - at-least-node "^1.0.0" graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gaze@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz" integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== dependencies: globule "^1.0.0" gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-func-name@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== dependencies: function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" + has-symbols "^1.0.3" get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" @@ -2855,21 +2864,21 @@ get-symbol-description@^1.0.0: glob-parent@^6.0.1: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob@7.2.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -2879,9 +2888,32 @@ glob@7.2.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@~7.1.1: version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -2893,139 +2925,152 @@ glob@~7.1.1: globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.13.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" - integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== +globals@^13.19.0, globals@^13.6.0: + version "13.20.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globule@^1.0.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.3.tgz#811919eeac1ab7344e905f2e3be80a13447973c2" - integrity sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg== + version "1.3.4" + resolved "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz" + integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== dependencies: glob "~7.1.1" - lodash "~4.17.10" + lodash "^4.17.21" minimatch "~3.0.2" -got@^11.0.2, got@^11.8.1: - version "11.8.5" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" - integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@^12.1.0: + version "12.5.3" + resolved "https://registry.npmjs.org/got/-/got-12.5.3.tgz" + integrity sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.1" decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== grapheme-splitter@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - has-ansi@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" + integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== dependencies: get-intrinsic "^1.1.1" -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" he@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -http-cache-semantics@^4.0.0: +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +http-cache-semantics@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +http2-wrapper@^2.1.10: + version "2.2.0" + resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz" + integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== dependencies: quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-proxy-agent@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" + resolve-alpn "^1.2.0" -https-proxy-agent@^5.0.1: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -3033,293 +3078,375 @@ https-proxy-agent@^5.0.1: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz" + integrity sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ== + iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.1, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + version "5.2.4" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" +import-meta-resolve@^2.1.0, import-meta-resolve@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-2.2.1.tgz" + integrity sha512-C6lLL7EJPY44kBvA80gq4uMsVFw5x3oSKfuMl1cuZ2RkI5+UJqQXgn+6hlUew0y4ig7Ypt4CObAAIzU53Nfpuw== + imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + resolved "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz" + integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@8.2.2: - version "8.2.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" - integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== +inquirer@9.1.2: + version "9.1.2" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz" + integrity sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg== dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" + ansi-escapes "^5.0.0" + chalk "^5.0.1" + cli-cursor "^4.0.0" + cli-width "^4.0.0" external-editor "^3.0.3" - figures "^3.0.0" + figures "^5.0.0" lodash "^4.17.21" mute-stream "0.0.8" - ora "^5.4.1" + ora "^6.1.2" run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" + rxjs "^7.5.6" + string-width "^5.1.2" + strip-ansi "^7.0.1" through "^2.3.6" + wrap-ansi "^8.0.1" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" ip-regex@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.8.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-docker@^2.0.0: version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== is-negative-zero@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-plain-obj@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== dependencies: call-bind "^1.0.2" is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + is-url@^1.2.4: version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== is-utf8@^0.2.0: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== is-weakref@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" is2@^2.0.6: - version "2.0.7" - resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.7.tgz#d084e10cab3bd45d6c9dfde7a48599fcbb93fcac" - integrity sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA== + version "2.0.9" + resolved "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz" + integrity sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g== dependencies: deep-is "^0.1.3" ip-regex "^4.1.0" is-url "^1.2.4" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== jake@^10.8.5: version "10.8.5" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz" integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== dependencies: async "^3.2.3" @@ -3327,110 +3454,127 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.3.tgz" + integrity sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.4.3" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-matcher-utils@^27.0.2, jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.4.0, jest-matcher-utils@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz" + integrity sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.4.3" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.3.tgz" + integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.4.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.4.3" slash "^3.0.0" stack-utils "^2.0.3" +jest-util@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz" + integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q== + dependencies: + "@jest/types" "^29.4.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== jsesc@~0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== -json-buffer@3.0.1, json-buffer@~3.0.1: +json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-web-key@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/json-web-key/-/json-web-key-0.4.0.tgz#a8e7268d1741c3a87c51c5070ea7ea988e9a78b7" + resolved "https://registry.npmjs.org/json-web-key/-/json-web-key-0.4.0.tgz" integrity sha512-4MwQAsadU3y7ZTfhwup/2lYJUEB7mAOMOjfNvuHGJkgkvXaBaiWlFsuWNFpWkTG23ZlMm56TBB3BI9cVQTxjzA== dependencies: asn1.js "^5.0.1" json5@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" - integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== + version "3.2.0" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -3439,37 +3583,36 @@ jsonfile@^6.0.1: jsrsasign@^10.5.25: version "10.5.25" - resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.5.25.tgz#8eb3f943718d73f2dd3d85f587f241a5316b835a" + resolved "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.5.25.tgz" integrity sha512-N7zxHaCwYvFlXsybq4p4RxRwn4AbEq3cEiyjbCrWmwA7g8aS4LTKDJ9AJmsXxwtYesYx0imJ+ITtkyyxLCgeIg== -keyv@^4.0.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2" - integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw== +keyv@^4.5.2: + version "4.5.2" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== dependencies: - compress-brotli "^1.3.8" json-buffer "3.0.1" kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -ky@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.30.0.tgz#a3d293e4f6c4604a9a4694eceb6ce30e73d27d64" - integrity sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog== +ky@^0.33.0: + version "0.33.2" + resolved "https://registry.npmjs.org/ky/-/ky-0.33.2.tgz" + integrity sha512-f6oS2rKUcPu5FzdqCDbFpmzis/JlqFZw8uIHm/jf8Kc3vtnW+VDhuashOAKyBZv8bFiZFZUMNxTC0JtahEvujA== lazystream@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + resolved "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz" integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== dependencies: readable-stream "^2.0.5" levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -3477,7 +3620,7 @@ levn@^0.4.1: lighthouse-logger@^1.0.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz#ba6303e739307c4eee18f08249524e7dafd510db" + resolved "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz" integrity sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA== dependencies: debug "^2.6.9" @@ -3485,12 +3628,17 @@ lighthouse-logger@^1.0.0: lilconfig@2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + lint-staged@^12.3.5: version "12.4.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.4.1.tgz#63fa27bfc8a33515f6902f63f6670864f1fb233c" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz" integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== dependencies: cli-truncate "^3.1.0" @@ -3510,7 +3658,7 @@ lint-staged@^12.3.5: listr2@^4.0.1: version "4.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" + resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz" integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== dependencies: cli-truncate "^2.1.0" @@ -3523,13 +3671,13 @@ listr2@^4.0.1: wrap-ansi "^7.0.0" livereload-js@^3.3.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.4.0.tgz#2083158125f16fb5207141bbadb9bdc26cfdb1ef" - integrity sha512-F/pz9ZZP+R+arY94cECTZco7PXgBXyL+KVWUPZq8AQE9TOu14GV6fYeKOviv02JCvFa4Oi3Rs1hYEpfeajc+ow== + version "3.4.1" + resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz" + integrity sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g== livereload@^0.9.3: version "0.9.3" - resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.3.tgz#a714816375ed52471408bede8b49b2ee6a0c55b1" + resolved "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz" integrity sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw== dependencies: chokidar "^3.5.0" @@ -3539,8 +3687,8 @@ livereload@^0.9.3: load-json-file@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" + integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -3548,112 +3696,107 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" path-exists "^3.0.0" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.defaults@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== lodash.difference@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" - integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= + resolved "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" + integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== lodash.flatten@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== lodash.flattendeep@^4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= + resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz" + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== lodash.isplainobject@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== -lodash.merge@^4.6.1, lodash.merge@^4.6.2: +lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.pickby@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" - integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= + resolved "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" + integrity sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q== lodash.union@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= + resolved "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz" + integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== lodash.zip@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" - integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= + resolved "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz" + integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== -lodash@^4.17.21, lodash@~4.17.10: +lodash@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.1.0, log-symbols@^4.1.0: +log-symbols@4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + log-update@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" @@ -3663,42 +3806,56 @@ log-update@^4.0.0: loglevel-plugin-prefix@^0.8.4: version "0.8.4" - resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" + resolved "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz" integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== loglevel@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" - integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== + version "1.8.1" + resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz" + integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== long@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" - integrity sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w== + version "5.2.1" + resolved "https://registry.npmjs.org/long/-/long-5.2.1.tgz" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== + version "2.3.6" + resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== lru-cache@^4.0.1: version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -3706,22 +3863,22 @@ make-dir@^2.0.0, make-dir@^2.1.0: map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== marky@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.4.tgz#d02bb4c08be2366687c778ecd2a328971ce23d7f" - integrity sha512-zd2/GiSn6U3/jeFVZ0J9CA1LzQ8RfIVvXkb/U0swFHF/zT+dVohTAWjmo2DcIuofmIIIROlwTbd+shSeXmxr0w== + version "1.2.5" + resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz" + integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== micromatch@^4.0.4: version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -3729,161 +3886,163 @@ micromatch@^4.0.4: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + minimalistic-assert@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.0, minimatch@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== +minimatch@^5.0.1, minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^6.1.4: + version "6.2.0" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz" + integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== dependencies: brace-expansion "^2.0.1" minimatch@~3.0.2: version "3.0.8" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp-classic@^0.5.2: version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^2.0.0: + version "2.1.3" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz" + integrity sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw== -mocha@^9.0.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" - integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== +mocha@^10.0.0: + version "10.2.0" + resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" - debug "4.3.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.2.0" - growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "4.2.1" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.1" + nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - workerpool "6.2.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: +ms@2.1.2, ms@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1: +ms@2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nanoid@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== node-fetch@2.6.7: version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" node-forge@^1.2.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-jose@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.2.0.tgz#b64f3225ad6bec328509a420800de597ba2bf3ed" + resolved "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz" integrity sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw== dependencies: base64url "^3.0.1" @@ -3896,14 +4055,14 @@ node-jose@^2.2.0: process "^0.11.10" uuid "^9.0.0" -node-releases@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" - integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== normalize-package-data@^2.3.2: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -3911,69 +4070,101 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.2: + version "3.0.3" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz" + integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -object-inspect@^1.10.3, object-inspect@^1.12.0, object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" object-keys "^1.1.1" object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + version "1.1.6" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + optionator@^0.9.1: version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -3985,165 +4176,173 @@ optionator@^0.9.1: "opts@>= 1.2.0": version "2.0.2" - resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1" + resolved "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz" integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg== -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" +ora@^6.1.2: + version "6.1.2" + resolved "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz" + integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw== + dependencies: + bl "^5.0.0" + chalk "^5.0.0" + cli-cursor "^4.0.0" + cli-spinners "^2.6.1" + is-interactive "^2.0.0" + is-unicode-supported "^1.1.0" + log-symbols "^5.1.0" + strip-ansi "^7.0.1" wcwidth "^1.0.1" os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== p-iteration@^1.1.8: version "1.1.8" - resolved "https://registry.yarnpkg.com/p-iteration/-/p-iteration-1.1.8.tgz#14df726d55af368beba81bcc92a26bb1b48e714a" + resolved "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.8.tgz" integrity sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== dependencies: - p-limit "^1.1.0" + yocto-queue "^1.0.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + p-map@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" - integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== + version "2.1.0" + resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" + integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== dependencies: error-ex "^1.2.0" +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parse-ms@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== path-exists@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" + integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -4151,194 +4350,199 @@ path-type@^1.0.0: pathval@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== pend@~1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pidtree@^0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz" integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== pify@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pinkie-promise@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== pirates@^4.0.5: version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pkg-dir@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pkg-dir@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== dependencies: find-up "^3.0.0" prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^2.5.1: version "2.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz" integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.4.3: + version "29.4.3" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz" + integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" pretty-ms@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz" integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== dependencies: parse-ms "^2.1.0" process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.10: version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -progress@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== proxy-from-env@1.1.0, proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== pseudomap@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -puppeteer-core@^13.1.3: - version "13.6.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-13.6.0.tgz#1626efbe789c19000a4c15309dbe55db5e936724" - integrity sha512-n692xT9uOTxbFKcCRkfOT2Go5LL0YBCHrSpBdbNsjLhcxO5yuhj2/4jgAIK9bT1blY17Pb4I35eBSuDzJ54ERw== +puppeteer-core@19.6.3: + version "19.6.3" + resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.6.3.tgz" + integrity sha512-8MbhioSlkDaHkmolpQf9Z7ui7jplFfOFTnN8d5kPsCazRRTNIH6/bVxPskn0v5Gh9oqOBlknw0eHH0/OBQAxpQ== dependencies: cross-fetch "3.1.5" debug "4.3.4" - devtools-protocol "0.0.981744" + devtools-protocol "0.0.1082910" extract-zip "2.0.1" - https-proxy-agent "5.0.0" - pkg-dir "4.2.0" - progress "2.0.3" + https-proxy-agent "5.0.1" proxy-from-env "1.1.0" rimraf "3.0.2" tar-fs "2.1.1" unbzip2-stream "1.4.3" - ws "8.5.0" + ws "8.11.0" query-selector-shadow-dom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz#8fa7459a4620f094457640e74e953a9dbe61a38e" - integrity sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg== + version "1.0.1" + resolved "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz" + integrity sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw== quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +read-pkg-up@9.1.0, read-pkg-up@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz" + integrity sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg== + dependencies: + find-up "^6.3.0" + read-pkg "^7.1.0" + type-fest "^2.5.0" read-pkg-up@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" + integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== dependencies: find-up "^1.0.0" read-pkg "^1.0.0" read-pkg@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz" + integrity sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg== + dependencies: + "@types/normalize-package-data" "^2.4.1" + normalize-package-data "^3.0.2" + parse-json "^5.2.0" + type-fest "^2.0.0" + readable-stream@^2.0.0, readable-stream@^2.0.5: version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" @@ -4349,9 +4553,9 @@ readable-stream@^2.0.0, readable-stream@^2.0.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" @@ -4359,239 +4563,262 @@ readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable util-deprecate "^1.0.1" readdir-glob@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" - integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA== + version "1.1.2" + resolved "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz" + integrity sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA== dependencies: - minimatch "^3.0.4" + minimatch "^5.1.0" readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + version "2.2.3" + resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== dependencies: - minimatch "3.0.4" + minimatch "^3.0.5" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + version "0.13.11" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.0.0, regexpp@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== +regexpu-core@^5.2.1: + version "5.3.0" + resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.0.tgz" + integrity sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ== dependencies: + "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== - -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== dependencies: jsesc "~0.5.0" require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve-alpn@^1.0.0: +resolve-alpn@^1.2.0: version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.3: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.3: + version "1.22.1" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== dependencies: - lowercase-keys "^2.0.0" + lowercase-keys "^3.0.0" resq@^1.9.1: - version "1.10.2" - resolved "https://registry.yarnpkg.com/resq/-/resq-1.10.2.tgz#cedf4f20d53f6e574b1e12afbda446ad9576c193" - integrity sha512-HmgVS3j+FLrEDBTDYysPdPVF9/hioDMJ/otOiQDKqk77YfZeeLOj0qi34yObumcud1gBpk+wpBTEg4kMicD++A== + version "1.11.0" + resolved "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz" + integrity sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw== dependencies: fast-deep-equal "^2.0.1" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + rfdc@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== rgb2hex@0.2.5: version "0.2.5" - resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.5.tgz#f82230cd3ab1364fa73c99be3a691ed688f8dbdc" + resolved "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz" integrity sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw== rimraf@3.0.2, rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -rxjs@^7.2.0, rxjs@^7.5.5: - version "7.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" - integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== +rxjs@^7.5.5, rxjs@^7.5.6: + version "7.8.0" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" -safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== "semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.4: + version "7.3.8" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + serialize-error@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz" integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== dependencies: type-fest "^0.20.2" serialize-javascript@6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" @@ -4600,7 +4827,7 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -4609,7 +4836,7 @@ slice-ansi@^4.0.0: slice-ansi@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== dependencies: ansi-styles "^6.0.0" @@ -4617,25 +4844,20 @@ slice-ansi@^5.0.0: source-map-support@^0.5.16: version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - source-map@^0.6.0: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" @@ -4643,186 +4865,193 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + version "3.0.12" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz" + integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== -split2@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" - integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== - dependencies: - readable-stream "^3.0.0" - -split2@^4.0.0: +split2@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" + resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + stream-buffers@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" + resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz" integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== string-argv@^0.3.1: version "0.3.1" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.0: +string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== dependencies: ansi-regex "^6.0.1" strip-bom@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== suffix@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.1.tgz#cc58231646a0ef1102f79478ef3a9248fd9c842f" - integrity sha1-zFgjFkag7xEC95R47zqSSP2chC8= + resolved "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz" + integrity sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA== supports-color@8.1.1: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" +supports-color@9.3.1, supports-color@^9.2.1: + version "9.3.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.3.1.tgz" + integrity sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q== + supports-color@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^9.2.1: - version "9.2.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.2.tgz#502acaf82f2b7ee78eb7c83dcac0f89694e5a7bb" - integrity sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA== - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== tar-fs@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== dependencies: chownr "^1.1.1" @@ -4832,7 +5061,7 @@ tar-fs@2.1.1: tar-stream@^2.1.4, tar-stream@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: bl "^4.0.3" @@ -4841,9 +5070,9 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tcp-port-used@^1.0.1: +tcp-port-used@^1.0.1, tcp-port-used@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" + resolved "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz" integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== dependencies: debug "4.3.1" @@ -4851,41 +5080,41 @@ tcp-port-used@^1.0.1: text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== through@^2.3.6, through@^2.3.8: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== tsconfig-paths@^3.14.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" @@ -4894,45 +5123,64 @@ tsconfig-paths@^3.14.1: strip-bom "^3.0.0" tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.5.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-fest@^2.0.0, type-fest@^2.5.0: + version "2.19.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typescript@^4.7.4: version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz" integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== ua-parser-js@^1.0.1: version "1.0.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz" integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ== -unbox-primitive@^1.0.1: +unbox-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: call-bind "^1.0.2" @@ -4942,7 +5190,7 @@ unbox-primitive@^1.0.1: unbzip2-stream@1.4.3: version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== dependencies: buffer "^5.2.1" @@ -4950,62 +5198,70 @@ unbzip2-stream@1.4.3: unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== universalify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -uuid@^8.0.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz" integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -5013,7 +5269,7 @@ validate-npm-package-license@^3.0.1: vscode-json-languageservice@^4.1.6: version "4.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" + resolved "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz" integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== dependencies: jsonc-parser "^3.0.0" @@ -5023,106 +5279,137 @@ vscode-json-languageservice@^4.1.6: vscode-uri "^3.0.3" vscode-languageserver-textdocument@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" - integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== + version "1.0.8" + resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz" + integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== + version "3.17.3" + resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz" + integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== vscode-nls@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" - integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== + version "5.2.0" + resolved "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-uri@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" - integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + version "3.0.7" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz" + integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" -wdio-chromedriver-service@^7.2.8: - version "7.3.2" - resolved "https://registry.yarnpkg.com/wdio-chromedriver-service/-/wdio-chromedriver-service-7.3.2.tgz#569053df4ceaf6ce9e43bebcdd540197f516e313" - integrity sha512-4M3OqFzBSC4FdbqkfwOrUMeroMEuyIFCHfcUebkU6tJ1w5GenWO61YweJ8NKlhPZx9nkO8223+20MpvBjv+fTg== - dependencies: - "@wdio/logger" "^7.5.3" - fs-extra "^9.1.0" - split2 "^3.2.2" - tcp-port-used "^1.0.1" - -webdriver@7.19.5: - version "7.19.5" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-7.19.5.tgz#485db210517d44e2e572a5a53a3294afee87ca8e" - integrity sha512-kdOC6w2LSZO2zQ+sNQjHRP4CVQzo1C+g2EDz7g3czOQv2Oo8WqkVlMT9hIBiMLHQfh896gFh776GUCDnEd9emg== - dependencies: - "@types/node" "^17.0.4" - "@wdio/config" "7.19.5" - "@wdio/logger" "7.19.0" - "@wdio/protocols" "7.19.0" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" - got "^11.0.2" - ky "^0.30.0" - lodash.merge "^4.6.1" - -webdriverio@7.19.5, webdriverio@^7.17.4: - version "7.19.5" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-7.19.5.tgz#ba5edc75da6cea93da03bbe67b60e5d435b92269" - integrity sha512-FDq6ULiXFhAzIxQ+NI/GprYoouT5GUDhdXNJkMHYT6wBSC0shEtbpu8+ciAB7lqbsD/D9pADePtj+18+gEG0zA== - dependencies: - "@types/aria-query" "^5.0.0" - "@types/node" "^17.0.4" - "@wdio/config" "7.19.5" - "@wdio/logger" "7.19.0" - "@wdio/protocols" "7.19.0" - "@wdio/repl" "7.19.5" - "@wdio/types" "7.19.5" - "@wdio/utils" "7.19.5" +wdio-chromedriver-service@^8.1.1: + version "8.1.1" + resolved "https://registry.npmjs.org/wdio-chromedriver-service/-/wdio-chromedriver-service-8.1.1.tgz" + integrity sha512-pN3GiOkTIMnalfq4PJAHdX95pDp1orHnTY8W1fIbd6ok81ba97UjerTgS7lUDRUh1p0MAm35Ww0uc0/9wzB7SA== + dependencies: + "@wdio/logger" "^8.1.0" + fs-extra "^11.1.0" + split2 "^4.1.0" + tcp-port-used "^1.0.2" + +webdriver@8.3.8: + version "8.3.8" + resolved "https://registry.npmjs.org/webdriver/-/webdriver-8.3.8.tgz" + integrity sha512-85ga5IMX8btQbLTQhJrt6HoXNDK0msEV2Ff1E2UYnfgmK8IGvqtZWxz8LM3fKrnjS8NNgxpYB4y81ixfnzRRFA== + dependencies: + "@types/node" "^18.0.0" + "@types/ws" "^8.5.3" + "@wdio/config" "8.3.2" + "@wdio/logger" "8.1.0" + "@wdio/protocols" "8.3.5" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" + deepmerge-ts "^4.2.2" + got "^12.1.0" + ky "^0.33.0" + ws "^8.8.0" + +webdriverio@8.3.10: + version "8.3.10" + resolved "https://registry.npmjs.org/webdriverio/-/webdriverio-8.3.10.tgz" + integrity sha512-kfbrPiqIZzJ6ZhSQmvId6LMzWxQ7eVnZxTVP1OvuuIQ26BoHbxs05hNJSdgm6utWj7f+34VeWiu0e+w5kZyEyA== + dependencies: + "@types/node" "^18.0.0" + "@wdio/config" "8.3.2" + "@wdio/logger" "8.1.0" + "@wdio/protocols" "8.3.5" + "@wdio/repl" "8.1.0" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" archiver "^5.0.0" aria-query "^5.0.0" css-shorthand-properties "^1.1.1" css-value "^0.0.1" - devtools "7.19.5" - devtools-protocol "^0.0.982423" - fs-extra "^10.0.0" + devtools "8.3.8" + devtools-protocol "^0.0.1103684" grapheme-splitter "^1.0.2" + import-meta-resolve "^2.1.0" + is-plain-obj "^4.1.0" + lodash.clonedeep "^4.5.0" + lodash.zip "^4.2.0" + minimatch "^6.1.4" + puppeteer-core "19.6.3" + query-selector-shadow-dom "^1.0.0" + resq "^1.9.1" + rgb2hex "0.2.5" + serialize-error "^8.0.0" + webdriver "8.3.8" + +webdriverio@8.3.9, webdriverio@^8.2.4: + version "8.3.9" + resolved "https://registry.npmjs.org/webdriverio/-/webdriverio-8.3.9.tgz" + integrity sha512-r1fkBs06zqt9UGAlGVK/bGY+mfF/4SBDGTzKQSh+osqSDAtu/heWE2Xwn9CTbbzLlK1HhBVftDhQYEFOmfSvDg== + dependencies: + "@types/node" "^18.0.0" + "@wdio/config" "8.3.2" + "@wdio/logger" "8.1.0" + "@wdio/protocols" "8.3.5" + "@wdio/repl" "8.1.0" + "@wdio/types" "8.3.0" + "@wdio/utils" "8.3.0" + archiver "^5.0.0" + aria-query "^5.0.0" + css-shorthand-properties "^1.1.1" + css-value "^0.0.1" + devtools "8.3.8" + devtools-protocol "^0.0.1103684" + grapheme-splitter "^1.0.2" + import-meta-resolve "^2.1.0" + is-plain-obj "^4.1.0" lodash.clonedeep "^4.5.0" - lodash.isobject "^3.0.2" - lodash.isplainobject "^4.0.6" lodash.zip "^4.2.0" - minimatch "^5.0.0" - puppeteer-core "^13.1.3" + minimatch "^6.1.4" + puppeteer-core "19.6.3" query-selector-shadow-dom "^1.0.0" resq "^1.9.1" rgb2hex "0.2.5" serialize-error "^8.0.0" - webdriver "7.19.5" + webdriver "8.3.8" webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -5131,33 +5418,62 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which@2.0.2, which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== dependencies: - isexe "^2.0.0" + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" which@^1.2.9: version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/which/-/which-3.0.0.tgz" + integrity sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ== + dependencies: + isexe "^2.0.0" + word-wrap@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -5166,61 +5482,80 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.0.1: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@8.11.0: + version "8.11.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== ws@^7.4.3: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== + version "7.5.9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.8.0: + version "8.12.1" + resolved "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz" + integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.2: version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@20.2.4: +yargs-parser@20.2.4, yargs-parser@^20.2.2: version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -5230,7 +5565,7 @@ yargs-unparser@2.0.0: yargs@16.2.0: version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -5241,23 +5576,23 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.0.0: - version "17.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284" - integrity sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g== +yargs@^17.5.1: + version "17.7.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz" + integrity sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^21.0.0" + yargs-parser "^21.1.1" yarn-install@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/yarn-install/-/yarn-install-1.0.0.tgz#57f45050b82efd57182b3973c54aa05cb5d25230" - integrity sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA= + resolved "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz" + integrity sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg== dependencies: cac "^3.0.3" chalk "^1.1.3" @@ -5265,20 +5600,25 @@ yarn-install@^1.0.0: yauzl@^2.10.0: version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + zip-stream@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" + resolved "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz" integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== dependencies: archiver-utils "^2.1.0" From 7cb54f666b824fa31407f507abfab6939ce321ba Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:14:03 +0000 Subject: [PATCH 187/567] Schemas v3.44.4 (#1057) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 60eabaa921..afdc3a6206 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.44.3 +v3.44.4 From 3a78906defd8d15d834e0d48d5ad99ef16960b98 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:23:55 +0000 Subject: [PATCH 188/567] Revert answers ids change in benchmark business schema (#1058) --- schemas/test/en/test_benchmark_business.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 7be13f12fc..9f89e73c68 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -206,7 +206,7 @@ "type": "DateRange", "answers": [ { - "id": "answer-from", + "id": "answerfrom", "type": "Date", "mandatory": true, "label": "Period from", @@ -222,7 +222,7 @@ } }, { - "id": "answer-to", + "id": "answerto", "type": "Date", "mandatory": true, "label": "Period to", From 8c53d91216a1508d5d00d03c822d2227325dfb4f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 15 Mar 2023 13:18:17 +0000 Subject: [PATCH 189/567] Schemas v3.45.0 (#1060) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index afdc3a6206..b188161172 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.44.4 +v3.45.0 From 2a33f81dda9a8f46fc99227513083475c5f8c737 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:55:35 +0000 Subject: [PATCH 190/567] Replace BEIS with DBT-DSIT theme (#1062) --- app/helpers/template_helpers.py | 8 +-- app/survey_config/__init__.py | 8 +-- app/survey_config/business_config.py | 19 +++--- app/survey_config/survey_type.py | 4 +- ...eme_beis.json => test_theme_dbt_dsit.json} | 8 +-- ...is_ni.json => test_theme_dbt_dsit_ni.json} | 8 +-- templates/assets/images/beis-logo.svg | 10 --- templates/assets/images/beis-mobile-logo.svg | 11 --- templates/assets/images/dbt-logo-stacked.svg | 39 +++++++++++ templates/assets/images/dsit-logo-stacked.svg | 12 ++++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 68 ++++++++++--------- tests/functional/spec/theme_beis.spec.js | 14 ---- tests/functional/spec/theme_beis_ni.spec.js | 15 ---- tests/functional/spec/theme_dbt_dsit.spec.js | 15 ++++ .../functional/spec/theme_dbt_dsit_ni.spec.js | 16 +++++ 16 files changed, 147 insertions(+), 110 deletions(-) rename schemas/test/en/{test_theme_beis.json => test_theme_dbt_dsit.json} (90%) rename schemas/test/en/{test_theme_beis_ni.json => test_theme_dbt_dsit_ni.json} (89%) delete mode 100644 templates/assets/images/beis-logo.svg delete mode 100644 templates/assets/images/beis-mobile-logo.svg create mode 100644 templates/assets/images/dbt-logo-stacked.svg create mode 100644 templates/assets/images/dsit-logo-stacked.svg delete mode 100644 tests/functional/spec/theme_beis.spec.js delete mode 100644 tests/functional/spec/theme_beis_ni.spec.js create mode 100644 tests/functional/spec/theme_dbt_dsit.spec.js create mode 100644 tests/functional/spec/theme_dbt_dsit_ni.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 9dcec3de5c..8679c97fd3 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -14,11 +14,11 @@ from app.questionnaire import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( - BEISBusinessSurveyConfig, - BEISNIBusinessSurveyConfig, BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, + DBTDSITBusinessSurveyConfig, + DBTDSITNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -182,8 +182,8 @@ def survey_config_mapping( SurveyType.HEALTH: SocialSurveyConfig, SurveyType.SOCIAL: SocialSurveyConfig, SurveyType.NORTHERN_IRELAND: NIBusinessSurveyConfig, - SurveyType.BEIS: BEISBusinessSurveyConfig, - SurveyType.BEIS_NI: BEISNIBusinessSurveyConfig, + SurveyType.DBT_DSIT: DBTDSITBusinessSurveyConfig, + SurveyType.DBT_DSIT_NI: DBTDSITNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, SurveyType.CENSUS: ( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 75a25c7a0f..9c6cd726ce 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -1,7 +1,7 @@ from .business_config import ( - BEISBusinessSurveyConfig, - BEISNIBusinessSurveyConfig, BusinessSurveyConfig, + DBTDSITBusinessSurveyConfig, + DBTDSITNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) @@ -22,8 +22,8 @@ "WelshCensusSurveyConfig", "BusinessSurveyConfig", "NIBusinessSurveyConfig", - "BEISBusinessSurveyConfig", - "BEISNIBusinessSurveyConfig", + "DBTDSITBusinessSurveyConfig", + "DBTDSITNIBusinessSurveyConfig", "ORRBusinessSurveyConfig", "Link", ] diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 10a3b6c1ac..71d05970e6 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -130,18 +130,19 @@ class NIBusinessSurveyConfig(BusinessSurveyConfig): @dataclass -class BEISBusinessSurveyConfig(BusinessSurveyConfig): - masthead_logo: str = read_file("./templates/assets/images/beis-logo.svg") - masthead_logo_mobile: str = read_file( - "./templates/assets/images/beis-mobile-logo.svg" - ) +class DBTDSITBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/dbt-logo-stacked.svg" + ) + read_file("./templates/assets/images/dsit-logo-stacked.svg") @dataclass -class BEISNIBusinessSurveyConfig(BusinessSurveyConfig): - masthead_logo: str = read_file( - "./templates/assets/images/beis-mobile-logo.svg" - ) + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") +class DBTDSITNIBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = ( + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/dsit-logo-stacked.svg") + + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") + ) @dataclass diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index 30f645a336..cb70d4a93b 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -7,8 +7,8 @@ class SurveyType(Enum): DEFAULT = "default" HEALTH = "health" NORTHERN_IRELAND = "northernireland" - BEIS = "beis" - BEIS_NI = "beis-ni" + DBT_DSIT = "dbt-dsit" + DBT_DSIT_NI = "dbt-dsit-ni" ORR = "orr" CENSUS = "census" CENSUS_NISRA = "census-nisra" diff --git a/schemas/test/en/test_theme_beis.json b/schemas/test/en/test_theme_dbt_dsit.json similarity index 90% rename from schemas/test/en/test_theme_beis.json rename to schemas/test/en/test_theme_dbt_dsit.json index 87ff1364ed..0395f918d4 100644 --- a/schemas/test/en/test_theme_beis.json +++ b/schemas/test/en/test_theme_dbt_dsit.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Test Department for Business, Energy and Industrial Strategy", - "theme": "beis", - "description": "A questionnaire to demo the BEIS survey theme", + "title": "Test Department for Business and Trade and Department for Science, Innovation and Technology", + "theme": "dbt-dsit", + "description": "A questionnaire to demo the DBT-DSIT survey theme", "metadata": [ { "name": "user_id", @@ -67,7 +67,7 @@ } ], "id": "group", - "title": "BEIS Theme Test" + "title": "DBT-DSIT Theme Test" } ] } diff --git a/schemas/test/en/test_theme_beis_ni.json b/schemas/test/en/test_theme_dbt_dsit_ni.json similarity index 89% rename from schemas/test/en/test_theme_beis_ni.json rename to schemas/test/en/test_theme_dbt_dsit_ni.json index 3c0e688d27..8a5cf9c642 100644 --- a/schemas/test/en/test_theme_beis_ni.json +++ b/schemas/test/en/test_theme_dbt_dsit_ni.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Test NI Department for Business, Energy and Industrial Strategy", - "theme": "beis-ni", - "description": "A questionnaire to demo the BEIS-NI survey theme", + "title": "Test NI Department for Business and Trade and Department for Science, Innovation and Technology", + "theme": "dbt-dsit-ni", + "description": "A questionnaire to demo the DBT-DSIT-NI survey theme", "metadata": [ { "name": "user_id", @@ -67,7 +67,7 @@ } ], "id": "group", - "title": "BEIS-NI Theme Test" + "title": "DBT-DSIT-NI Theme Test" } ] } diff --git a/templates/assets/images/beis-logo.svg b/templates/assets/images/beis-logo.svg deleted file mode 100644 index 619462dcfd..0000000000 --- a/templates/assets/images/beis-logo.svg +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/templates/assets/images/beis-mobile-logo.svg b/templates/assets/images/beis-mobile-logo.svg deleted file mode 100644 index ffb2816967..0000000000 --- a/templates/assets/images/beis-mobile-logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/templates/assets/images/dbt-logo-stacked.svg b/templates/assets/images/dbt-logo-stacked.svg new file mode 100644 index 0000000000..96d351601e --- /dev/null +++ b/templates/assets/images/dbt-logo-stacked.svg @@ -0,0 +1,39 @@ + + Department for Business and Trade logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/assets/images/dsit-logo-stacked.svg b/templates/assets/images/dsit-logo-stacked.svg new file mode 100644 index 0000000000..a5f4c76fb2 --- /dev/null +++ b/templates/assets/images/dsit-logo-stacked.svg @@ -0,0 +1,12 @@ + + Department for Science, Innovation and Technology logo + + + + + + + + + diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 1a42f020dc..4aa1d24a1a 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,5 +1,5 @@ {% extends 'layouts/_base.html' %} -{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "beis", "beis-ni", "orr"] %} +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt-dsit", "dbt-dsit-ni", "orr"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index a3df6d8360..585b7c4a2a 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -15,11 +15,11 @@ read_file, ) from app.survey_config import ( - BEISBusinessSurveyConfig, - BEISNIBusinessSurveyConfig, BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, + DBTDSITBusinessSurveyConfig, + DBTDSITNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -233,41 +233,45 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): ( None, None, - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), [ "ONS Surveys", - read_file("./templates/assets/images/beis-logo.svg"), - read_file("./templates/assets/images/beis-mobile-logo.svg"), + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/dsit-logo-stacked.svg"), + None, ], ), ( - SurveyType.BEIS, + SurveyType.DBT_DSIT, "Test", - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), [ "Test", - read_file("./templates/assets/images/beis-logo.svg"), - read_file("./templates/assets/images/beis-mobile-logo.svg"), + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/dsit-logo-stacked.svg"), + None, ], ), ( None, None, - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), [ "ONS Surveys", - read_file("./templates/assets/images/beis-mobile-logo.svg") + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/dsit-logo-stacked.svg") + read_file("./templates/assets/images/finance-ni-logo-stacked.svg"), None, ], ), ( - SurveyType.BEIS_NI, + SurveyType.DBT_DSIT_NI, "Test", - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), [ "Test", - read_file("./templates/assets/images/beis-mobile-logo.svg") + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/dsit-logo-stacked.svg") + read_file("./templates/assets/images/finance-ni-logo-stacked.svg"), None, ], @@ -427,12 +431,12 @@ def test_service_links_context( f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), ( - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), @@ -506,12 +510,12 @@ def test_sign_out_button_text_context( f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), ( - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), @@ -565,12 +569,12 @@ def test_cookie_settings_url_context( ACCOUNT_SERVICE_BASE_URL, ), ( - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), "en", ACCOUNT_SERVICE_BASE_URL, ), ( - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), "en", ACCOUNT_SERVICE_BASE_URL, ), @@ -616,8 +620,8 @@ def test_cookie_domain_context( BusinessSurveyConfig(), SocialSurveyConfig(), NIBusinessSurveyConfig(), - BEISBusinessSurveyConfig(), - BEISNIBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), ORRBusinessSurveyConfig(), ], ) @@ -693,11 +697,11 @@ def test_account_service_my_todo_url_context( f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), ( - BEISBusinessSurveyConfig(), + DBTDSITBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), ( - BEISNIBusinessSurveyConfig(), + DBTDSITNIBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), ( @@ -733,8 +737,8 @@ def test_account_service_log_out_url_context( (SurveyType.HEALTH, "en", SocialSurveyConfig), (SurveyType.SOCIAL, "en", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NIBusinessSurveyConfig), - (SurveyType.BEIS, "en", BEISBusinessSurveyConfig), - (SurveyType.BEIS_NI, "en", BEISNIBusinessSurveyConfig), + (SurveyType.DBT_DSIT, "en", DBTDSITBusinessSurveyConfig), + (SurveyType.DBT_DSIT_NI, "en", DBTDSITNIBusinessSurveyConfig), (SurveyType.ORR, "en", ORRBusinessSurveyConfig), (SurveyType.CENSUS, "en", CensusSurveyConfig), (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), @@ -841,8 +845,8 @@ def test_context_set_from_app_config(app): (SurveyType.SOCIAL, "en", None), (SurveyType.SOCIAL, "cy", None), (SurveyType.NORTHERN_IRELAND, "en", None), - (SurveyType.BEIS, "en", None), - (SurveyType.BEIS_NI, "en", None), + (SurveyType.DBT_DSIT, "en", None), + (SurveyType.DBT_DSIT_NI, "en", None), (SurveyType.ORR, "en", None), (SurveyType.CENSUS, "en", "census"), (SurveyType.CENSUS, "cy", "census"), @@ -870,8 +874,8 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.SOCIAL, "en", "ONS Surveys"), (SurveyType.SOCIAL, "cy", "ONS Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Surveys"), - (SurveyType.BEIS, "en", "ONS Surveys"), - (SurveyType.BEIS_NI, "en", "ONS Surveys"), + (SurveyType.DBT_DSIT, "en", "ONS Surveys"), + (SurveyType.DBT_DSIT_NI, "en", "ONS Surveys"), (SurveyType.ORR, "en", "ONS Surveys"), (SurveyType.CENSUS, "en", "ONS Surveys"), (SurveyType.CENSUS, "cy", "ONS Surveys"), @@ -938,13 +942,13 @@ def test_use_default_survey_title_in_context_when_no_cookie( [{"survey_id": "001"}], ), ( - SurveyType.BEIS, + SurveyType.DBT_DSIT, "en", QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), ( - SurveyType.BEIS_NI, + SurveyType.DBT_DSIT_NI, "en", QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], diff --git a/tests/functional/spec/theme_beis.spec.js b/tests/functional/spec/theme_beis.spec.js deleted file mode 100644 index a90ad89b9f..0000000000 --- a/tests/functional/spec/theme_beis.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import RadioPage from "../generated_pages/theme_beis/radio.page"; - -describe("Theme BEIS", () => { - describe("Given I launch a BEIS themed questionnaire", () => { - before(async () => { - await browser.openQuestionnaire("test_theme_beis.json"); - }); - - it("When I navigate to the radio page, Then I should see BEIS theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#beis-logo-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); - }); - }); -}); diff --git a/tests/functional/spec/theme_beis_ni.spec.js b/tests/functional/spec/theme_beis_ni.spec.js deleted file mode 100644 index 14948e00cf..0000000000 --- a/tests/functional/spec/theme_beis_ni.spec.js +++ /dev/null @@ -1,15 +0,0 @@ -import RadioPage from "../generated_pages/theme_beis_ni/radio.page"; - -describe("Theme BEIS-NI", () => { - describe("Given I launch a BEIS-NI themed questionnaire", () => { - before(async () => { - await browser.openQuestionnaire("test_theme_beis_ni.json"); - }); - - it("When I navigate to the radio page, Then I should see BEIS-NI theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#beis-logo-mobile-alt").getHTML()).to.contain("Department for Business, Energy and Industrial Strategy"); - await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); - }); - }); -}); diff --git a/tests/functional/spec/theme_dbt_dsit.spec.js b/tests/functional/spec/theme_dbt_dsit.spec.js new file mode 100644 index 0000000000..7f129e36ae --- /dev/null +++ b/tests/functional/spec/theme_dbt_dsit.spec.js @@ -0,0 +1,15 @@ +import RadioPage from "../generated_pages/theme_dbt_dsit/radio.page"; + +describe("Theme DBT-DSIT", () => { + describe("Given I launch a DBT-DSIT themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_dbt_dsit.json"); + }); + + it("When I navigate to the radio page, Then I should see DBT-DSIT theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade logo"); + await expect(await $("#dsit-logo-alt").getHTML()).to.contain("Department for Science, Innovation and Technology logo"); + }); + }); +}); diff --git a/tests/functional/spec/theme_dbt_dsit_ni.spec.js b/tests/functional/spec/theme_dbt_dsit_ni.spec.js new file mode 100644 index 0000000000..80e78bb78c --- /dev/null +++ b/tests/functional/spec/theme_dbt_dsit_ni.spec.js @@ -0,0 +1,16 @@ +import RadioPage from "../generated_pages/theme_dbt_dsit_ni/radio.page"; + +describe("Theme DBT-DSIT-NI", () => { + describe("Given I launch a DBT-DSIT-NI themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_dbt_dsit_ni.json"); + }); + + it("When I navigate to the radio page, Then I should see DBT-DSIT-NI theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade logo"); + await expect(await $("#dsit-logo-alt").getHTML()).to.contain("Department for Science, Innovation and Technology logo"); + await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + }); + }); +}); From 0c0bad34c690ba5f502289679afaff6028db241c Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:52:45 +0000 Subject: [PATCH 191/567] Schemas v3.46.0 (#1065) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b188161172..89f17c0d66 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.45.0 +v3.46.0 From 8eed2ed97ee3ce105f267d1f52500c0e8b5dcc74 Mon Sep 17 00:00:00 2001 From: Amit Sinha Date: Wed, 22 Mar 2023 09:42:09 +0000 Subject: [PATCH 192/567] Bug: Cross-section when rule dependencies not working as expected fix (#1061) * Bug: Cross-section when rule dependencies not working as expected fix * update * Adding functional tests * update --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/questionnaire/questionnaire_schema.py | 2 +- ...ing_and_skipping_section_dependencies.json | 84 +++++++++++++ tests/app/questionnaire/test_path_finder.py | 9 +- ..._and_skipping_section_dependencies.spec.js | 114 +++++++++++++++++- 4 files changed, 205 insertions(+), 4 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 754f5680ef..afac80fb64 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -846,7 +846,7 @@ def _get_error_messages(self) -> dict: def _populate_when_rules_section_dependencies(self) -> None: for section in self.get_sections(): when_rules = self.get_values_for_key(section, "when") - rules: Union[Mapping, list] = next(when_rules, []) + rules: list = list(when_rules) if rules_section_dependencies := self._get_rules_section_dependencies( section["id"], rules diff --git a/schemas/test/en/test_routing_and_skipping_section_dependencies.json b/schemas/test/en/test_routing_and_skipping_section_dependencies.json index 6bdf9d4611..0987ace23f 100644 --- a/schemas/test/en/test_routing_and_skipping_section_dependencies.json +++ b/schemas/test/en/test_routing_and_skipping_section_dependencies.json @@ -614,6 +614,90 @@ ] } } + }, + { + "id": "repeating-is-dependent", + "question": { + "answers": [ + { + "id": "repeating-is-dependent-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + }, + { + "label": "Prefer not to say", + "value": "Prefer not to say" + } + ], + "type": "Radio" + } + ], + "id": "repeating-is-dependent-question", + "title": "Is this person dependent on you?", + "type": "General" + }, + "type": "Question" + }, + { + "id": "repeating-is-smoker", + "question": { + "answers": [ + { + "id": "repeating-is-smoker-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + }, + { + "label": "Prefer not to say", + "value": "Prefer not to say" + } + ], + "type": "Radio" + } + ], + "id": "repeating-is-smoker-question", + "title": "Is this person smoke or use nicotine products?", + "type": "General" + }, + "type": "Question", + "skip_conditions": { + "when": { + "or": [ + { + "==": [ + { + "source": "answers", + "identifier": "skip-age-answer" + }, + "Yes" + ] + }, + { + "<=": [ + { + "source": "answers", + "identifier": "repeating-age-answer" + }, + 18 + ] + } + ] + } + } } ] } diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index 2e194714c1..f155904304 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -557,13 +557,18 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( # Answering 'Yes' to the skip age question # means in all repeating sections you won't be asked their age "Yes", - ["repeating-sex"], + ["repeating-sex", "repeating-is-dependent"], ), ( # Answering 'No' to the skip age question # means in all repeating sections you will be asked their age "No", - ["repeating-sex", "repeating-age"], + [ + "repeating-sex", + "repeating-age", + "repeating-is-dependent", + "repeating-is-smoker", + ], ), ), ) diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 77d7e5519e..127af94bf1 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -15,6 +15,8 @@ import EnableSectionPage from "../generated_pages/routing_and_skipping_section_d import SkipConfirmationPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation.page"; import SkipConfirmationSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; import SkipSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-section-summary.page"; +import RepeatingIsDependentPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-is-dependent.page"; +import RepeatingIsSmokerPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-is-smoker.page"; import HubPage from "../base_pages/hub.page"; @@ -88,6 +90,10 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingSexPage.submit()).click(); await $(RepeatingAgePage.answer()).setValue("45"); await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.no()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + await $(RepeatingIsSmokerPage.no()).click(); + await $(RepeatingIsSmokerPage.submit()).click(); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); @@ -98,6 +104,8 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingSexPage.submit()).click(); await $(RepeatingAgePage.answer()).setValue("10"); await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); @@ -111,13 +119,17 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); await $(RepeatingSexPage.submit()).click(); + await $(RepeatingIsDependentPage.no()).click(); + await $(RepeatingIsDependentPage.submit()).click(); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingAgePage.submit()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; @@ -217,6 +229,8 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); await $(RepeatingSexPage.submit()).click(); + await $(RepeatingIsDependentPage.no()).click(); + await $(RepeatingIsDependentPage.submit()).click(); await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); await editNoToSkipAgeQuestion(); @@ -232,6 +246,104 @@ describe("Routing and skipping section dependencies", () => { await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); }); }); + + describe("Given the routing and skipping section dependencies questionnaire", () => { + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_routing_and_skipping_section_dependencies.json"); + }); + + it("When I answer 'No' to skipping the age question and populate the household with Repeating Age > 18, Then in each repeating section I am asked if they are smoker", async () => { + await answerNoToSkipAgeQuestion(); + + await addHouseholdMembers(); + + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("45"); + await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.no()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + await $(RepeatingIsSmokerPage.no()).click(); + await $(RepeatingIsSmokerPage.submit()).click(); + + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).to.contain("No"); + + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); + await $(RepeatingSexPage.male()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("19"); + await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + await $(RepeatingIsSmokerPage.no()).click(); + await $(RepeatingIsSmokerPage.submit()).click(); + + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("19"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).to.contain("No"); + }); + + it("When I answer 'No' to skipping the age question and populate the household with Repeating Age < 18, Then in each repeating section I am not asked if they are smoker", async () => { + await answerNoToSkipAgeQuestion(); + + await addHouseholdMembers(); + + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("15"); + await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("15"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); + await $(RepeatingSexPage.male()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingAgePage.answer()).setValue("10"); + await $(RepeatingAgePage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + }); + + it("When I answer 'Yes' to skipping the age question and populate the household, Then in each repeating section I am not asked if they are smoker", async () => { + await answerYesToSkipAgeQuestion(); + + await addHouseholdMembers(); + + await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); + await $(RepeatingSexPage.female()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingIsDependentPage.no()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + + await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); + await $(RepeatingSexPage.male()).click(); + await $(RepeatingSexPage.submit()).click(); + await $(RepeatingIsDependentPage.yes()).click(); + await $(RepeatingIsDependentPage.submit()).click(); + + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + }); + }); }); const addHouseholdMembers = async () => { From 64096aa4a0c9251a2fe1f406a079fb832cd00933 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:51:16 +0000 Subject: [PATCH 193/567] Add DBT theme (#1064) --- app/helpers/template_helpers.py | 4 + app/survey_config/__init__.py | 4 + app/survey_config/business_config.py | 12 +++ app/survey_config/survey_type.py | 2 + schemas/test/en/test_theme_dbt.json | 75 +++++++++++++++ schemas/test/en/test_theme_dbt_ni.json | 75 +++++++++++++++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 102 +++++++++++++++++++++ tests/functional/spec/theme_dbt.spec.js | 14 +++ tests/functional/spec/theme_dbt_ni.spec.js | 15 +++ 10 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 schemas/test/en/test_theme_dbt.json create mode 100644 schemas/test/en/test_theme_dbt_ni.json create mode 100644 tests/functional/spec/theme_dbt.spec.js create mode 100644 tests/functional/spec/theme_dbt_ni.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 8679c97fd3..6c7ef517f5 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -17,8 +17,10 @@ BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, + DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, + DBTNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -182,6 +184,8 @@ def survey_config_mapping( SurveyType.HEALTH: SocialSurveyConfig, SurveyType.SOCIAL: SocialSurveyConfig, SurveyType.NORTHERN_IRELAND: NIBusinessSurveyConfig, + SurveyType.DBT: DBTBusinessSurveyConfig, + SurveyType.DBT_NI: DBTNIBusinessSurveyConfig, SurveyType.DBT_DSIT: DBTDSITBusinessSurveyConfig, SurveyType.DBT_DSIT_NI: DBTDSITNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 9c6cd726ce..c0f4f9b481 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -1,7 +1,9 @@ from .business_config import ( BusinessSurveyConfig, + DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, + DBTNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) @@ -22,6 +24,8 @@ "WelshCensusSurveyConfig", "BusinessSurveyConfig", "NIBusinessSurveyConfig", + "DBTBusinessSurveyConfig", + "DBTNIBusinessSurveyConfig", "DBTDSITBusinessSurveyConfig", "DBTDSITNIBusinessSurveyConfig", "ORRBusinessSurveyConfig", diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 71d05970e6..74e75b68b8 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -145,6 +145,18 @@ class DBTDSITNIBusinessSurveyConfig(BusinessSurveyConfig): ) +@dataclass +class DBTBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file("./templates/assets/images/dbt-logo-stacked.svg") + + +@dataclass +class DBTNIBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/dbt-logo-stacked.svg" + ) + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") + + @dataclass class ORRBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo: str = read_file("./templates/assets/images/orr-logo.svg") diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index cb70d4a93b..01098e2470 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -7,6 +7,8 @@ class SurveyType(Enum): DEFAULT = "default" HEALTH = "health" NORTHERN_IRELAND = "northernireland" + DBT = "dbt" + DBT_NI = "dbt-ni" DBT_DSIT = "dbt-dsit" DBT_DSIT_NI = "dbt-dsit-ni" ORR = "orr" diff --git a/schemas/test/en/test_theme_dbt.json b/schemas/test/en/test_theme_dbt.json new file mode 100644 index 0000000000..be7981734c --- /dev/null +++ b/schemas/test/en/test_theme_dbt.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Department for Business and Trade", + "theme": "dbt", + "description": "A questionnaire to demo the DBT survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "DBT Theme Test" + } + ] + } + ] +} diff --git a/schemas/test/en/test_theme_dbt_ni.json b/schemas/test/en/test_theme_dbt_ni.json new file mode 100644 index 0000000000..b8cf5716d0 --- /dev/null +++ b/schemas/test/en/test_theme_dbt_ni.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test NI Department for Business and Trade", + "theme": "dbt-ni", + "description": "A questionnaire to demo the DBT-NI survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "DBT-NI Theme Test" + } + ] + } + ] +} diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 4aa1d24a1a..4dd183d764 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,5 +1,5 @@ {% extends 'layouts/_base.html' %} -{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt-dsit", "dbt-dsit-ni", "orr"] %} +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 585b7c4a2a..6bea2382fa 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -18,8 +18,10 @@ BusinessSurveyConfig, CensusNISRASurveyConfig, CensusSurveyConfig, + DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, + DBTNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -276,6 +278,48 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, ], ), + ( + None, + None, + DBTBusinessSurveyConfig(), + [ + "ONS Surveys", + read_file("./templates/assets/images/dbt-logo-stacked.svg"), + None, + ], + ), + ( + SurveyType.DBT, + "Test", + DBTBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/dbt-logo-stacked.svg"), + None, + ], + ), + ( + None, + None, + DBTNIBusinessSurveyConfig(), + [ + "ONS Surveys", + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/finance-ni-logo-stacked.svg"), + None, + ], + ), + ( + SurveyType.DBT_NI, + "Test", + DBTNIBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/dbt-logo-stacked.svg") + + read_file("./templates/assets/images/finance-ni-logo-stacked.svg"), + None, + ], + ), ( None, None, @@ -440,6 +484,16 @@ def test_service_links_context( "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), + ( + DBTBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), + ( + DBTNIBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), ( ORRBusinessSurveyConfig(), "en", @@ -519,6 +573,16 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), + ( + DBTBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), + ( + DBTNIBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), ( ORRBusinessSurveyConfig(), True, @@ -578,6 +642,16 @@ def test_cookie_settings_url_context( "en", ACCOUNT_SERVICE_BASE_URL, ), + ( + DBTBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), + ( + DBTNIBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), ( ORRBusinessSurveyConfig(), "en", @@ -620,6 +694,8 @@ def test_cookie_domain_context( BusinessSurveyConfig(), SocialSurveyConfig(), NIBusinessSurveyConfig(), + DBTBusinessSurveyConfig(), + DBTNIBusinessSurveyConfig(), DBTDSITBusinessSurveyConfig(), DBTDSITNIBusinessSurveyConfig(), ORRBusinessSurveyConfig(), @@ -704,6 +780,14 @@ def test_account_service_my_todo_url_context( DBTDSITNIBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), + ( + DBTBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), + ( + DBTNIBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), ( ORRBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", @@ -737,6 +821,8 @@ def test_account_service_log_out_url_context( (SurveyType.HEALTH, "en", SocialSurveyConfig), (SurveyType.SOCIAL, "en", SocialSurveyConfig), (SurveyType.NORTHERN_IRELAND, "en", NIBusinessSurveyConfig), + (SurveyType.DBT, "en", DBTBusinessSurveyConfig), + (SurveyType.DBT_NI, "en", DBTNIBusinessSurveyConfig), (SurveyType.DBT_DSIT, "en", DBTDSITBusinessSurveyConfig), (SurveyType.DBT_DSIT_NI, "en", DBTDSITNIBusinessSurveyConfig), (SurveyType.ORR, "en", ORRBusinessSurveyConfig), @@ -845,6 +931,8 @@ def test_context_set_from_app_config(app): (SurveyType.SOCIAL, "en", None), (SurveyType.SOCIAL, "cy", None), (SurveyType.NORTHERN_IRELAND, "en", None), + (SurveyType.DBT, "en", None), + (SurveyType.DBT_NI, "en", None), (SurveyType.DBT_DSIT, "en", None), (SurveyType.DBT_DSIT_NI, "en", None), (SurveyType.ORR, "en", None), @@ -874,6 +962,8 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.SOCIAL, "en", "ONS Surveys"), (SurveyType.SOCIAL, "cy", "ONS Surveys"), (SurveyType.NORTHERN_IRELAND, "en", "ONS Surveys"), + (SurveyType.DBT, "en", "ONS Surveys"), + (SurveyType.DBT_NI, "en", "ONS Surveys"), (SurveyType.DBT_DSIT, "en", "ONS Surveys"), (SurveyType.DBT_DSIT_NI, "en", "ONS Surveys"), (SurveyType.ORR, "en", "ONS Surveys"), @@ -953,6 +1043,18 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), + ( + SurveyType.DBT, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), + ( + SurveyType.DBT_NI, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), ( SurveyType.ORR, "en", diff --git a/tests/functional/spec/theme_dbt.spec.js b/tests/functional/spec/theme_dbt.spec.js new file mode 100644 index 0000000000..cab192f328 --- /dev/null +++ b/tests/functional/spec/theme_dbt.spec.js @@ -0,0 +1,14 @@ +import RadioPage from "../generated_pages/theme_dbt/radio.page"; + +describe("Theme DBT", () => { + describe("Given I launch a DBT themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_dbt.json"); + }); + + it("When I navigate to the radio page, Then I should see DBT theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade"); + }); + }); +}); diff --git a/tests/functional/spec/theme_dbt_ni.spec.js b/tests/functional/spec/theme_dbt_ni.spec.js new file mode 100644 index 0000000000..3cb462124f --- /dev/null +++ b/tests/functional/spec/theme_dbt_ni.spec.js @@ -0,0 +1,15 @@ +import RadioPage from "../generated_pages/theme_dbt_ni/radio.page"; + +describe("Theme DBT-NI", () => { + describe("Given I launch a DBT-NI themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_dbt_ni.json"); + }); + + it("When I navigate to the radio page, Then I should see DBT-NI theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade"); + await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + }); + }); +}); From 428d5854aea5328bcb219eb63cececb2511565ec Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 22 Mar 2023 15:02:47 +0000 Subject: [PATCH 194/567] Update schemas version to 3.46.1 (#1066) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 89f17c0d66..a44ebcfaa8 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.46.0 +v3.46.1 From 108782006c4e3955e70dc40443373f464462c292 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 23 Mar 2023 09:53:22 +0000 Subject: [PATCH 195/567] Update DS version to 62.1 (#1063) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 33eba4acf9..58d83ee7ce 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -62.0.1 +62.1.0 From 0fdc3aeb9c1ee0ba8bc49ea13570a6b4117fddfc Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 24 Mar 2023 10:43:37 +0000 Subject: [PATCH 196/567] Schemas v3.46.2 (#1069) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a44ebcfaa8..a93b18ba1b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.46.1 +v3.46.2 From 48137ba4f06a79fa2ca5471119e928eb2e61fefc Mon Sep 17 00:00:00 2001 From: Hajara Iyal Date: Mon, 27 Mar 2023 14:32:55 +0100 Subject: [PATCH 197/567] Update action checkout to v3 (#1067) * Update action checkout to v3 * Change on.push.branch to test change * Revert on.push.branches back to main --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 84693d3aba..668e93eecd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: docker-push: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Write app version run: printf "$GITHUB_SHA" > .application-version - name: Build From e963db6b2d6ddbfda41e39393598a5ce025fbb16 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:33:06 +0100 Subject: [PATCH 198/567] Schemas v3.47.0 (#1071) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a93b18ba1b..b4a01b5478 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.46.2 +v3.47.0 From d48e9f77d7ba29629e2572c3893ad40e74b39e72 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 29 Mar 2023 10:04:48 +0100 Subject: [PATCH 199/567] Schemas v3.48.0 (#1075) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b4a01b5478..a2effd12b9 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.47.0 +v3.48.0 From 31397cdd14a477f383243bb6622253b3dc44584c Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:08:24 +0100 Subject: [PATCH 200/567] Calculated Summary only show answers on the path (#1001) --- app/forms/questionnaire_form.py | 57 +- app/questionnaire/dependencies.py | 54 ++ app/questionnaire/path_finder.py | 12 +- app/questionnaire/placeholder_parser.py | 106 ++- app/questionnaire/placeholder_renderer.py | 72 ++- app/questionnaire/questionnaire_schema.py | 107 +++- app/questionnaire/router.py | 11 +- app/questionnaire/rules/rule_evaluator.py | 12 +- app/questionnaire/value_source_resolver.py | 46 +- app/questionnaire/variants.py | 10 + app/submitter/convert_payload_0_0_1.py | 4 +- app/submitter/converter.py | 2 + app/submitter/converter_v2.py | 6 +- app/translations/cy/LC_MESSAGES/messages.po | 1 - app/utilities/mappings.py | 33 + .../contexts/calculated_summary_context.py | 94 +-- app/views/contexts/context.py | 5 +- app/views/contexts/list_context.py | 2 +- app/views/contexts/section_preview_context.py | 2 +- app/views/contexts/summary/block.py | 8 +- app/views/contexts/summary/group.py | 20 +- .../contexts/summary/list_collector_block.py | 5 +- app/views/handlers/block.py | 1 + app/views/handlers/calculated_summary.py | 20 +- app/views/handlers/confirm_email.py | 1 + app/views/handlers/content.py | 4 +- app/views/handlers/feedback.py | 1 + app/views/handlers/individual_response.py | 4 +- app/views/handlers/question.py | 49 +- schemas/test/en/test_calculated_summary.json | 95 +-- ...ed_summary_cross_section_dependencies.json | 355 +++++++++++ schemas/test/en/test_introduction.json | 6 +- schemas/test/en/test_introduction_hub.json | 6 +- .../test/en/test_new_calculated_summary.json | 116 +--- ...ed_summary_cross_section_dependencies.json | 375 +++++++++++ ..._cross_section_dependencies_repeating.json | 603 ++++++++++++++++++ ...alculated_summary_dependent_questions.json | 1 - ..._calculated_summary_repeating_section.json | 101 +-- ...ction_dependencies_calculated_summary.json | 58 +- ...n_dependencies_new_calculated_summary.json | 62 +- tests/app/forms/field_handlers/conftest.py | 3 + tests/app/forms/test_field_factory.py | 3 + tests/app/forms/test_questionnaire_form.py | 79 ++- tests/app/questionnaire/conftest.py | 4 + .../rules/test_rule_evaluator.py | 3 +- .../test_dynamic_answer_options.py | 4 +- .../questionnaire/test_placeholder_parser.py | 353 +++++++--- .../test_placeholder_renderer.py | 99 ++- .../test_questionnaire_schema.py | 46 +- tests/app/questionnaire/test_schema_utils.py | 11 + .../test_value_source_resolver.py | 3 +- .../submitter/test_convert_payload_0_0_1.py | 18 + .../submitter/test_convert_payload_0_0_3.py | 25 + tests/app/views/contexts/conftest.py | 18 + .../app/views/contexts/summary/test_block.py | 2 + .../views/contexts/summary/test_question.py | 4 +- .../test_calculated_summary_context.py | 64 +- .../views/contexts/test_preview_context.py | 2 +- .../contexts/test_section_summary_context.py | 3 + .../spec/features/calculated_summary.spec.js | 7 + .../features/calculated_summary_test_case.js | 193 ++++-- ...lculated_summary_repeating_section.spec.js | 195 ++++-- tests/functional/spec/preview.spec.js | 4 +- ...on_dependencies_calculated_summary.spec.js | 75 +++ .../test_questionnaire_calculated_summary.py | 100 ++- 65 files changed, 3091 insertions(+), 754 deletions(-) create mode 100644 app/questionnaire/dependencies.py create mode 100644 app/utilities/mappings.py create mode 100644 schemas/test/en/test_calculated_summary_cross_section_dependencies.json create mode 100644 schemas/test/en/test_new_calculated_summary_cross_section_dependencies.json create mode 100644 schemas/test/en/test_new_calculated_summary_cross_section_dependencies_repeating.json diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 30e37641e0..e43c9a829b 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -12,15 +12,20 @@ from werkzeug.datastructures import ImmutableMultiDict, MultiDict from wtforms import validators -from app.data_models import AnswerStore, AnswerValueTypes, ListStore +from app.data_models import AnswerStore, AnswerValueTypes, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy from app.forms import error_messages from app.forms.field_handlers import DateHandler, FieldHandler, get_field_handler from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType +from app.questionnaire.dependencies import ( + get_block_ids_for_calculated_summary_dependencies, +) +from app.questionnaire.path_finder import PathFinder from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver +from app.utilities.mappings import get_flattened_mapping_values logger = logging.getLogger(__name__) @@ -44,6 +49,7 @@ def __init__( metadata: Optional[MetadataProxy], response_metadata: Mapping[str, Any], location: Union[None, Location, RelationshipLocation], + progress_store: ProgressStore, **kwargs: Union[MultiDict[str, Any], Mapping[str, Any], None], ): self.schema = schema @@ -56,6 +62,7 @@ def __init__( self.question_errors: dict[str, str] = {} self.options_with_detail_answer: dict = {} self.question_title = self.question.get("title", "") + self.progress_store = progress_store self.value_source_resolver = ValueSourceResolver( answer_store=self.answer_store, schema=self.schema, @@ -64,6 +71,7 @@ def __init__( list_store=self.list_store, location=self.location, list_item_id=self.location.list_item_id if self.location else None, + progress_store=self.progress_store, ) super().__init__(**kwargs) @@ -308,6 +316,7 @@ def _get_period_range_for_single_date( location=self.location, list_item_id=list_item_id, escape_answer_values=False, + progress_store=self.progress_store, ) rule_evaluator = RuleEvaluator( @@ -317,6 +326,7 @@ def _get_period_range_for_single_date( metadata=self.metadata, response_metadata=self.response_metadata, location=self.location, + progress_store=self.progress_store, ) handler = DateHandler( @@ -440,15 +450,39 @@ def _option_value_in_data( def get_answer_fields( question: QuestionSchemaType, - data: Union[None, MultiDict[str, Any], Mapping[str, Any]], + data: MultiDict[str, Any] | Mapping[str, Any] | None, schema: QuestionnaireSchema, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping[str, Any], - location: Union[Location, RelationshipLocation, None], + location: Location | RelationshipLocation | None, + progress_store: ProgressStore, ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None + + routing_path_block_ids: dict[tuple, list[str]] = {} + + if location and progress_store: + routing_path_block_ids = get_block_ids_for_calculated_summary_dependencies( + schema=schema, + location=location, + progress_store=progress_store, + path_finder=PathFinder( + schema=schema, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=metadata, + response_metadata=response_metadata, + ), + data=question, + ) + + block_ids = None + if routing_path_block_ids: + block_ids = get_flattened_mapping_values(routing_path_block_ids) + value_source_resolver = ValueSourceResolver( answer_store=answer_store, list_store=list_store, @@ -458,6 +492,9 @@ def get_answer_fields( list_item_id=list_item_id, escape_answer_values=False, response_metadata=response_metadata, + routing_path_block_ids=block_ids, + assess_routing_path=False, + progress_store=progress_store, ) rule_evaluator = RuleEvaluator( @@ -467,6 +504,7 @@ def get_answer_fields( metadata=metadata, response_metadata=response_metadata, location=location, + progress_store=progress_store, ) answer_fields = {} @@ -559,11 +597,12 @@ def generate_form( question_schema: QuestionSchemaType, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping[str, Any], - location: Union[None, Location, RelationshipLocation] = None, - data: Optional[dict[str, Any]] = None, - form_data: Optional[MultiDict[str, Any]] = None, + progress_store: ProgressStore, + location: Location | RelationshipLocation | None = None, + data: dict[str, Any] | None = None, + form_data: MultiDict[str, Any] | None = None, ) -> QuestionnaireForm: class DynamicForm(QuestionnaireForm): pass @@ -581,6 +620,7 @@ class DynamicForm(QuestionnaireForm): metadata, response_metadata, location, + progress_store=progress_store, ) for answer_id, field in answer_fields.items(): @@ -596,4 +636,5 @@ class DynamicForm(QuestionnaireForm): location, data=data, formdata=form_data, + progress_store=progress_store, ) diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py new file mode 100644 index 0000000000..76492c1402 --- /dev/null +++ b/app/questionnaire/dependencies.py @@ -0,0 +1,54 @@ +from typing import TYPE_CHECKING, Mapping, Sequence + +from werkzeug.datastructures import MultiDict + +from app.data_models import ProgressStore +from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire.questionnaire_schema import get_sources_for_type_from_data +from app.questionnaire.relationship_location import RelationshipLocation +from app.utilities.mappings import get_flattened_mapping_values + +if TYPE_CHECKING: + from app.questionnaire.path_finder import PathFinder # pragma: no cover + + +def get_block_ids_for_calculated_summary_dependencies( + schema: QuestionnaireSchema, + location: Location | RelationshipLocation, + progress_store: ProgressStore, + path_finder: "PathFinder", + data: MultiDict | Mapping | Sequence, + sections_to_ignore: list | None = None, +) -> dict[tuple, list[str]]: + block_ids_by_section: dict[tuple, list[str]] = {} + + sections_to_ignore = sections_to_ignore or [] + dependent_sections = schema.calculated_summary_section_dependencies_by_block[ + location.section_id + ] + + if block_id := location.block_id: + dependents = dependent_sections[block_id] + else: + dependents = get_flattened_mapping_values(dependent_sections) + + if dependents and not get_sources_for_type_from_data( + source_type="calculated_summary", + data=data, + ignore_keys=["when"], + ): + return block_ids_by_section + + for section in dependents: + # Dependent sections other than the current section cannot be a repeating section + list_item_id = location.list_item_id if section == location.section_id else None + key = (section, list_item_id) + + if key in sections_to_ignore: + continue + + if key in progress_store.started_section_keys(): + routing_path = path_finder.routing_path(*key) + block_ids_by_section[key] = routing_path.block_ids + + return block_ids_by_section diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 7b3ef524f4..0cc2d8fff2 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -193,6 +193,7 @@ def _evaluate_routing_rules( self.response_metadata, location=this_location, routing_path_block_ids=routing_path_block_ids, + progress_store=self.progress_store, ) for rule in routing_rules: rule_valid = ( @@ -238,11 +239,12 @@ def evaluate_skip_conditions( ) when_rule_evaluator = RuleEvaluator( - self.schema, - self.answer_store, - self.list_store, - self.metadata, - self.response_metadata, + schema=self.schema, + answer_store=self.answer_store, + list_store=self.list_store, + metadata=self.metadata, + response_metadata=self.response_metadata, + progress_store=self.progress_store, location=this_location, routing_path_block_ids=routing_path_block_ids, ) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 0f5aac3a9e..a2964e01b8 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -1,18 +1,15 @@ from decimal import Decimal -from typing import ( - TYPE_CHECKING, - Any, - Mapping, - MutableMapping, - Optional, - Sequence, - Union, -) +from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Sequence, Union +from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import path_finder as pf +from app.questionnaire.dependencies import ( + get_block_ids_for_calculated_summary_dependencies, +) from app.questionnaire.placeholder_transforms import PlaceholderTransforms from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.value_source_resolver import ( @@ -20,6 +17,7 @@ ValueSourceResolver, ValueSourceTypes, ) +from app.utilities.mappings import get_flattened_mapping_values if TYPE_CHECKING: from app.questionnaire.placeholder_renderer import ( @@ -40,50 +38,88 @@ def __init__( language: str, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping, schema: QuestionnaireSchema, renderer: "PlaceholderRenderer", - list_item_id: Optional[str] = None, - location: Union[Location, RelationshipLocation, None] = None, - placeholder_preview_mode: Optional[bool] = False, + progress_store: ProgressStore, + list_item_id: str | None = None, + location: Location | RelationshipLocation | None = None, + placeholder_preview_mode: bool | None = False, ): + self._transformer = PlaceholderTransforms(language, schema, renderer) + self._placeholder_map: MutableMapping[ + str, Union[ValueSourceEscapedTypes, ValueSourceTypes, None] + ] = {} self._answer_store = answer_store self._list_store = list_store self._metadata = metadata self._response_metadata = response_metadata - self._schema = schema self._list_item_id = list_item_id + self._schema = schema self._location = location - self._transformer = PlaceholderTransforms(language, schema, renderer) - self._placeholder_map: MutableMapping[ - str, Union[ValueSourceEscapedTypes, ValueSourceTypes, None] - ] = {} + self._progress_store = progress_store self._placeholder_preview_mode = placeholder_preview_mode - self._value_source_resolver = ValueSourceResolver( + self._path_finder = pf.PathFinder( + schema=self._schema, answer_store=self._answer_store, list_store=self._list_store, + progress_store=self._progress_store, metadata=self._metadata, - schema=self._schema, - location=self._location, - list_item_id=self._list_item_id, - escape_answer_values=True, response_metadata=self._response_metadata, - use_default_answer=True, ) + self._value_source_resolver = self._get_value_source_resolver() + self._routing_path_block_ids_by_section_key: dict = {} + def __call__( self, placeholder_list: Sequence[Mapping] ) -> MutableMapping[str, Union[ValueSourceEscapedTypes, ValueSourceTypes]]: placeholder_list = QuestionnaireSchema.get_mutable_deepcopy(placeholder_list) + + sections_to_ignore = list(self._routing_path_block_ids_by_section_key) + + if routing_path_block_ids_map := self._get_routing_path_block_ids( + sections_to_ignore=sections_to_ignore, data=placeholder_list + ): + self._routing_path_block_ids_by_section_key.update( + routing_path_block_ids_map + ) + + routing_path_block_ids = get_flattened_mapping_values( + routing_path_block_ids_map + ) + self._value_source_resolver = self._get_value_source_resolver( + routing_path_block_ids + ) + for placeholder in placeholder_list: - if placeholder["placeholder"] not in self._placeholder_map: - self._placeholder_map[ - placeholder["placeholder"] - ] = self._parse_placeholder(placeholder) + # :TODO: Caching of placeholder values will need to be revisited once validation is added to ensure that placeholders are globally unique + # if placeholder["placeholder"] not in self._placeholder_map: + self._placeholder_map[placeholder["placeholder"]] = self._parse_placeholder( + placeholder + ) return self._placeholder_map + def _get_value_source_resolver( + self, routing_path_block_ids: set[str] | None = None + ) -> ValueSourceResolver: + return ValueSourceResolver( + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + schema=self._schema, + location=self._location, + list_item_id=self._list_item_id, + escape_answer_values=True, + response_metadata=self._response_metadata, + use_default_answer=True, + assess_routing_path=False, + routing_path_block_ids=routing_path_block_ids, + progress_store=self._progress_store, + ) + def _parse_placeholder(self, placeholder: Mapping) -> Any: if self._placeholder_preview_mode and not self._all_value_sources_metadata( placeholder @@ -140,6 +176,20 @@ def _resolve_value_source_list( values.append(value) return values + def _get_routing_path_block_ids( + self, data: Mapping | Sequence, sections_to_ignore: list | None = None + ) -> dict[tuple, list[str]] | None: + if self._location: + return get_block_ids_for_calculated_summary_dependencies( + schema=self._schema, + location=self._location, + progress_store=self._progress_store, + sections_to_ignore=sections_to_ignore, + data=data, + path_finder=self._path_finder, + ) + return {} + def _all_value_sources_metadata(self, placeholder: Mapping) -> bool: sources = self._schema.get_values_for_key(placeholder, key="source") return all(source == "metadata" for source in sources) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 67cad82565..1b2faa66b4 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -1,7 +1,8 @@ -from typing import Any, Mapping, MutableMapping, Optional, Union +from typing import Any, Mapping, MutableMapping from jsonpointer import resolve_pointer, set_pointer +from app.data_models import ProgressStore from app.data_models.answer import AnswerValueTypes from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -24,11 +25,12 @@ def __init__( language: str, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping, schema: QuestionnaireSchema, - location: Union[None, Location, RelationshipLocation] = None, - placeholder_preview_mode: Optional[bool] = False, + progress_store: ProgressStore, + location: Location | RelationshipLocation | None = None, + placeholder_preview_mode: bool | None = False, ): self._placeholder_preview_mode = placeholder_preview_mode self._language = language @@ -38,20 +40,23 @@ def __init__( self._response_metadata = response_metadata self._schema = schema self._location = location + self._progress_store = progress_store def render_pointer( self, + *, dict_to_render: Mapping[str, Any], pointer_to_render: str, - list_item_id: Optional[str], + list_item_id: str | None, + placeholder_parser: PlaceholderParser, ) -> str: pointer_data = resolve_pointer(dict_to_render, pointer_to_render) - return self.render_placeholder(pointer_data, list_item_id) + return self.render_placeholder(pointer_data, list_item_id, placeholder_parser) def get_plural_count( self, schema_partial: Mapping[str, str] - ) -> Optional[AnswerValueTypes]: + ) -> AnswerValueTypes | None: source = schema_partial["source"] source_id = schema_partial["identifier"] @@ -66,20 +71,23 @@ def get_plural_count( def render_placeholder( self, placeholder_data: MutableMapping[str, Any], - list_item_id: Optional[str], + list_item_id: str | None, + placeholder_parser: PlaceholderParser | None = None, ) -> str: - placeholder_parser = PlaceholderParser( - language=self._language, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - schema=self._schema, - list_item_id=list_item_id, - location=self._location, - renderer=self, - placeholder_preview_mode=self._placeholder_preview_mode, - ) + if not placeholder_parser: + placeholder_parser = PlaceholderParser( + language=self._language, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + list_item_id=list_item_id, + location=self._location, + renderer=self, + progress_store=self._progress_store, + placeholder_preview_mode=self._placeholder_preview_mode, + ) placeholder_data = QuestionnaireSchema.get_mutable_deepcopy(placeholder_data) @@ -106,7 +114,10 @@ def render_placeholder( return formatted_placeholder_data def render( - self, data_to_render: Mapping[str, Any], list_item_id: Optional[str] + self, + *, + data_to_render: Mapping[str, Any], + list_item_id: str | None, ) -> dict[str, Any]: """ Transform the current schema json to a fully rendered dictionary @@ -116,9 +127,26 @@ def render( ] = QuestionnaireSchema.get_mutable_deepcopy(data_to_render) pointers = find_pointers_containing(data_to_render_mutable, "placeholders") + placeholder_parser = PlaceholderParser( + language=self._language, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + list_item_id=list_item_id, + location=self._location, + renderer=self, + placeholder_preview_mode=self._placeholder_preview_mode, + progress_store=self._progress_store, + ) + for pointer in pointers: rendered_text = self.render_pointer( - data_to_render_mutable, pointer, list_item_id + dict_to_render=data_to_render_mutable, + pointer_to_render=pointer, + list_item_id=list_item_id, + placeholder_parser=placeholder_parser, ) set_pointer(data_to_render_mutable, pointer, rendered_text) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index afac80fb64..a65084eb80 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -5,12 +5,13 @@ from typing import Any, Generator, Iterable, Mapping, Optional, Sequence, Union from flask_babel import force_locale -from werkzeug.datastructures import ImmutableDict +from werkzeug.datastructures import ImmutableDict, MultiDict from app.data_models.answer import Answer from app.forms import error_messages from app.questionnaire.rules.operator import OPERATION_MAPPING from app.utilities.make_immutable import make_immutable +from app.utilities.mappings import get_mappings_with_key DEFAULT_LANGUAGE_CODE = "en" @@ -57,6 +58,9 @@ def __init__( set ) self._when_rules_section_dependencies_by_section: dict[str, set[str]] = {} + self.calculated_summary_section_dependencies_by_block: dict[ + str, dict[str, set[str]] + ] = defaultdict(lambda: defaultdict(set)) self._when_rules_section_dependencies_by_answer: dict[ str, set[str] ] = defaultdict(set) @@ -73,6 +77,7 @@ def __init__( # Post schema parsing. self._populate_answer_dependencies() self._populate_when_rules_section_dependencies() + self._populate_calculated_summary_section_dependencies() @cached_property def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: @@ -238,7 +243,7 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: def _populate_answer_dependencies(self) -> None: for block in self.get_blocks(): if block["type"] == "CalculatedSummary": - answer_ids_for_block = self.get_calculated_summary_answer_ids(block) + answer_ids_for_block = get_calculated_summary_answer_ids(block) self._update_answer_dependencies_for_calculated_summary( answer_ids_for_block, block["id"] ) @@ -307,9 +312,7 @@ def _update_answer_dependencies_for_answer( def _update_answer_dependencies_for_dynamic_options( self, dynamic_options_values: Mapping, *, block_id: str, answer_id: str ) -> None: - value_sources = self._get_dictionaries_with_key( - "source", dynamic_options_values - ) + value_sources = get_mappings_with_key("source", dynamic_options_values) for value_source in value_sources: self._update_answer_dependencies_for_value_source( value_source, block_id=block_id, answer_id=answer_id @@ -325,7 +328,7 @@ def _update_answer_dependencies_for_value_source( if value_source["source"] == "calculated_summary": identifier = value_source["identifier"] if calculated_summary_block := self.get_block(identifier): - answer_ids_for_block = self.get_calculated_summary_answer_ids( + answer_ids_for_block = get_calculated_summary_answer_ids( calculated_summary_block ) @@ -778,7 +781,7 @@ def has_operator(rule: Any) -> bool: ) def get_values_for_key( - self, block: Mapping, key: str, ignore_keys: Optional[list[str]] = None + self, block: Mapping, key: str, ignore_keys: list[str] | None = None ) -> Generator: ignore_keys = ignore_keys or [] for k, v in block.items(): @@ -795,18 +798,6 @@ def get_values_for_key( except AttributeError: continue - def _get_dictionaries_with_key( - self, key: str, dictionary: Mapping - ) -> Generator[Mapping, None, None]: - if key in dictionary: - yield dictionary - - for value in dictionary.values(): - if isinstance(value, Sequence): - for element in value: - if isinstance(element, Mapping): - yield from self._get_dictionaries_with_key(key, element) - def _get_parent_section_id_for_block(self, block_id: str) -> str: parent_block_id = self._parent_id_map[block_id] group_id = self._parent_id_map[parent_block_id] @@ -875,7 +866,7 @@ def _get_rules_section_dependencies( answer_id_list.append(identifier) elif source == "calculated_summary" and identifier: calculated_summary_block = self.get_block(identifier) - calculated_summary_answer_ids = self.get_calculated_summary_answer_ids( + calculated_summary_answer_ids = get_calculated_summary_answer_ids( calculated_summary_block # type: ignore ) answer_id_list.extend(iter(calculated_summary_answer_ids)) @@ -896,17 +887,51 @@ def _get_rules_section_dependencies( return rules_section_dependencies - def get_calculated_summary_answer_ids( - self, calculated_summary_block: Mapping[str, Any] - ) -> list[str]: - if calculated_summary_block["calculation"].get("answers_to_calculate"): - return calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + def _populate_calculated_summary_section_dependencies(self) -> None: + for section in self.get_sections(): + for block in self.get_blocks_for_section(section): + sources = get_mappings_with_key("source", block, ignore_keys=["when"]) + + calculated_summary_sources = [ + source + for source in sources + if source["source"] == "calculated_summary" + ] + + section_dependencies = ( + self._get_calculated_summary_section_dependencies( + sources=calculated_summary_sources, + ) + ) - values = self._get_dictionaries_with_key( - "source", calculated_summary_block["calculation"]["operation"] - ) + self.calculated_summary_section_dependencies_by_block[section["id"]][ + block["id"] + ].update(section_dependencies) + + def _get_calculated_summary_section_dependencies( + self, + sources: list[Mapping], + ) -> set[str]: + # Type ignore: Added to this method as the block will exist at this point + section_dependencies: set[str] = set() + + for source in sources: + answer_id_list: list = [] + identifier: str = source["identifier"] + + calculated_summary_block = self.get_block(identifier) # type: ignore + calculated_summary_answer_ids = get_calculated_summary_answer_ids( + calculated_summary_block # type: ignore + ) + answer_id_list.extend(calculated_summary_answer_ids) - return [value["identifier"] for value in values if value["source"] == "answers"] + for answer_id in answer_id_list: + block = self.get_block_for_answer_id(answer_id) # type: ignore + section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore + + section_dependencies.add(section_id) # type: ignore + + return section_dependencies def get_summary_item_for_list_for_section( self, *, section_id: str, list_name: str @@ -935,3 +960,27 @@ def get_item_anchor(self, section_id: str, list_name: str) -> Optional[str]: for item in summary.get("items", []): if item["for_list"] == list_name and item.get("item_anchor_answer_id"): return f"#{str(item['item_anchor_answer_id'])}" + + +def get_sources_for_type_from_data( + *, + source_type: str, + data: MultiDict | Mapping | Sequence, + ignore_keys: list, +) -> list | None: + sources = get_mappings_with_key("source", data, ignore_keys=ignore_keys) + + return [source for source in sources if source["source"] == source_type] + + +def get_calculated_summary_answer_ids( + calculated_summary_block: Mapping[str, Any] +) -> list[str]: + if calculated_summary_block["calculation"].get("answers_to_calculate"): + return calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + + values = get_mappings_with_key( + "source", calculated_summary_block["calculation"]["operation"] + ) + + return [value["identifier"] for value in values if value["source"] == "answers"] diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 739ec34e54..a2a91bc9ca 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -392,11 +392,12 @@ def _is_section_enabled(self, section: Mapping) -> bool: ) when_rule_evaluator = RuleEvaluator( - self._schema, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, + schema=self._schema, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + progress_store=self._progress_store, location=None, routing_path_block_ids=routing_path_block_ids, ) diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 64b7607741..02046a1d07 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -3,7 +3,7 @@ from decimal import Decimal from typing import Generator, Iterable, Mapping, Optional, Sequence, Union -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer @@ -26,10 +26,11 @@ class RuleEvaluator: schema: QuestionnaireSchema answer_store: AnswerStore list_store: ListStore - metadata: Optional[MetadataProxy] + metadata: MetadataProxy | None response_metadata: Mapping - location: Union[None, Location, RelationshipLocation] - routing_path_block_ids: Optional[list] = None + location: Location | RelationshipLocation | None + progress_store: ProgressStore + routing_path_block_ids: Iterable | None = None language: str = DEFAULT_LANGUAGE_CODE # pylint: disable=attribute-defined-outside-init @@ -45,7 +46,9 @@ def __post_init__(self) -> None: list_item_id=list_item_id, routing_path_block_ids=self.routing_path_block_ids, use_default_answer=True, + progress_store=self.progress_store, ) + renderer: PlaceholderRenderer = PlaceholderRenderer( language=self.language, answer_store=self.answer_store, @@ -54,6 +57,7 @@ def __post_init__(self) -> None: response_metadata=self.response_metadata, schema=self.schema, location=self.location, + progress_store=self.progress_store, ) self.operations = Operations( language=self.language, schema=self.schema, renderer=renderer diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 45ac31d9c2..8cc2a7de37 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -4,6 +4,7 @@ from markupsafe import Markup +from app.data_models import ProgressStore from app.data_models.answer import AnswerValueTypes, escape_answer_value from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListModel, ListStore @@ -25,14 +26,16 @@ class ValueSourceResolver: answer_store: AnswerStore list_store: ListStore - metadata: Optional[MetadataProxy] + metadata: MetadataProxy | None response_metadata: Mapping schema: QuestionnaireSchema - location: Union[None, Location, RelationshipLocation] - list_item_id: Optional[str] - routing_path_block_ids: Optional[list] = None + location: Location | RelationshipLocation | None + list_item_id: str | None + progress_store: ProgressStore + routing_path_block_ids: Iterable[str] | None = None use_default_answer: bool = False escape_answer_values: bool = False + assess_routing_path: bool = True def _is_answer_on_path(self, answer_id: str) -> bool: if self.routing_path_block_ids: @@ -42,9 +45,18 @@ def _is_answer_on_path(self, answer_id: str) -> bool: return True def _get_answer_value( - self, answer_id: str, list_item_id: Optional[str] - ) -> Optional[AnswerValueTypes]: - if not self._is_answer_on_path(answer_id): + self, + answer_id: str, + list_item_id: str | None, + assess_routing_path: bool | None = None, + ) -> AnswerValueTypes | None: + assess_routing_path = ( + assess_routing_path + if assess_routing_path is not None + else self.assess_routing_path + ) + + if assess_routing_path and not self._is_answer_on_path(answer_id): return None if answer := self.answer_store.get_answer(answer_id, list_item_id): @@ -120,7 +132,7 @@ def _resolve_list_value_source( return list(list_model) def _resolve_calculated_summary_value_source( - self, value_source: Mapping + self, value_source: Mapping, *, assess_routing_path: bool ) -> IntOrDecimal: """Calculates the value for the 'calculation' used by the provided Calculated Summary. @@ -132,21 +144,27 @@ def _resolve_calculated_summary_value_source( operator = self.get_calculation_operator(calculation["calculation_type"]) list_item_id = self._resolve_list_item_id_for_value_source(value_source) values = [ - self._get_answer_value(answer_id=answer_id, list_item_id=list_item_id) + self._get_answer_value( + answer_id=answer_id, + list_item_id=list_item_id, + assess_routing_path=assess_routing_path, + ) for answer_id in calculation["answers_to_calculate"] ] return operator([value for value in values if value]) # type: ignore - evaluate_calculated_summary = rule_evaluator.RuleEvaluator( + evaluator = rule_evaluator.RuleEvaluator( self.schema, self.answer_store, self.list_store, self.metadata, self.response_metadata, location=self.location, + routing_path_block_ids=self.routing_path_block_ids, + progress_store=self.progress_store, ) - return evaluate_calculated_summary.evaluate(calculation["operation"]) # type: ignore + return evaluator.evaluate(calculation["operation"]) # type: ignore @staticmethod def get_calculation_operator( @@ -162,6 +180,8 @@ def resolve( ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes]: source = value_source["source"] + # We always need to assess the routing path for calculated summary value sources + # as they may contain answers that are not on the path if source == "answers": return self._resolve_answer_value_source(value_source) @@ -183,4 +203,6 @@ def resolve( return self.response_metadata.get(value_source.get("identifier")) if source == "calculated_summary": - return self._resolve_calculated_summary_value_source(value_source) + return self._resolve_calculated_summary_value_source( + value_source, assess_routing_path=True + ) diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index 3e4c23dfc5..13b0cf7416 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -13,6 +13,7 @@ def choose_variant( variants_key, single_key, current_location, + progress_store, ): if block.get(single_key): return block[single_key] @@ -26,6 +27,7 @@ def choose_variant( metadata, response_metadata, location=current_location, + progress_store=progress_store, ) if when_rule_evaluator.evaluate(when_rules): @@ -40,6 +42,7 @@ def choose_question_to_display( answer_store, list_store, current_location, + progress_store, ): return choose_variant( block, @@ -51,6 +54,7 @@ def choose_question_to_display( variants_key="question_variants", single_key="question", current_location=current_location, + progress_store=progress_store, ) @@ -62,6 +66,7 @@ def choose_content_to_display( answer_store, list_store, current_location, + progress_store, ): return choose_variant( block, @@ -73,6 +78,7 @@ def choose_content_to_display( variants_key="content_variants", single_key="content", current_location=current_location, + progress_store=progress_store, ) @@ -84,6 +90,7 @@ def transform_variants( answer_store, list_store, current_location, + progress_store, ) -> ImmutableDict: output_block = dict(block) if "question_variants" in block: @@ -95,6 +102,7 @@ def transform_variants( answer_store, list_store, current_location, + progress_store=progress_store, ) output_block.pop("question_variants", None) output_block.pop("question", None) @@ -110,6 +118,7 @@ def transform_variants( answer_store, list_store, current_location, + progress_store=progress_store, ) output_block.pop("content_variants", None) output_block.pop("content", None) @@ -128,6 +137,7 @@ def transform_variants( answer_store, list_store, current_location, + progress_store=progress_store, ) return ImmutableDict(output_block) diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index 90796e7efd..84716ce423 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone from typing import Any, Mapping, Optional, Union -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import AnswerValueTypes, ListAnswer from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema @@ -22,6 +22,7 @@ def convert_answers_to_payload_0_0_1( list_store: ListStore, schema: QuestionnaireSchema, full_routing_path: RoutingPath, + progress_store: ProgressStore, ) -> OrderedDict[str, Any]: """ Convert answers into the data format below @@ -63,6 +64,7 @@ def convert_answers_to_payload_0_0_1( answer_store, list_store, current_location=current_location, + progress_store=progress_store, ) for answer_id, answer in schema.get_answers_for_question_by_id( question diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 84bacd55b1..9064212524 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -69,6 +69,7 @@ def convert_answers( response_metadata = questionnaire_store.response_metadata answer_store = questionnaire_store.answer_store list_store = questionnaire_store.list_store + progress_store = questionnaire_store.progress_store survey_id = schema.json["survey_id"] @@ -95,6 +96,7 @@ def convert_answers( routing_path=routing_path, metadata=metadata, response_metadata=response_metadata, + progress_store=progress_store, ) return payload | optional_properties diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index b75e788ca7..77d1edbfed 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -4,7 +4,7 @@ from structlog import get_logger from app.authentication.auth_payload_version import AuthPayloadVersion -from app.data_models import AnswerStore, ListStore, QuestionnaireStore +from app.data_models import AnswerStore, ListStore, ProgressStore, QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, @@ -55,6 +55,7 @@ def convert_answers_v2( response_metadata = questionnaire_store.response_metadata answer_store = questionnaire_store.answer_store list_store = questionnaire_store.list_store + progress_store = questionnaire_store.progress_store survey_id = schema.json["survey_id"] @@ -85,6 +86,7 @@ def convert_answers_v2( routing_path=routing_path, metadata=metadata, response_metadata=response_metadata, + progress_store=progress_store, ) logger.info("converted answer ready for submission") @@ -113,6 +115,7 @@ def get_payload_data( routing_path: RoutingPath, metadata: MetadataProxy, response_metadata: Mapping, + progress_store: ProgressStore, ) -> Union[OrderedDict[str, Any], dict[str, Union[list[Any]]]]: if schema.json["data_version"] == "0.0.1": return convert_answers_to_payload_0_0_1( @@ -122,6 +125,7 @@ def get_payload_data( list_store=list_store, schema=schema, full_routing_path=routing_path, + progress_store=progress_store, ) if schema.json["data_version"] == "0.0.3": diff --git a/app/translations/cy/LC_MESSAGES/messages.po b/app/translations/cy/LC_MESSAGES/messages.po index dcc21e7d68..bf05e48f08 100644 --- a/app/translations/cy/LC_MESSAGES/messages.po +++ b/app/translations/cy/LC_MESSAGES/messages.po @@ -1386,4 +1386,3 @@ msgstr "Newid" #: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "Newid manylion ar gyfer {item_name}" - diff --git a/app/utilities/mappings.py b/app/utilities/mappings.py new file mode 100644 index 0000000000..63dc6eba28 --- /dev/null +++ b/app/utilities/mappings.py @@ -0,0 +1,33 @@ +from typing import Generator, Iterable, Mapping, Sequence + + +def get_flattened_mapping_values( + map_to_flatten: Mapping[tuple, Iterable[str]] | Mapping[str, Iterable[str]] +) -> set[str]: + return {x for v in map_to_flatten.values() for x in v} + + +def get_mappings_with_key( # noqa: C901 pylint: disable=too-complex + key: str, data: Mapping | Sequence, ignore_keys: list[str] | None = None +) -> Generator[Mapping, None, None]: + ignore_keys = ignore_keys or [] + + def _handle_sequence(value: Sequence) -> Generator[Mapping, None, None]: + for element in value: + if isinstance(element, Mapping): + yield from get_mappings_with_key(key, element, ignore_keys) + + if isinstance(data, Sequence): + yield from _handle_sequence(data) + + if isinstance(data, Mapping): + if key not in ignore_keys and key in data: + yield data + + for k, v in data.items(): + if k in ignore_keys: + continue + if isinstance(v, Mapping): + yield from get_mappings_with_key(key, v, ignore_keys) + if isinstance(v, Sequence): + yield from _handle_sequence(v) diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index a4e516f3b0..7ec250bc57 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,9 +1,11 @@ from copy import deepcopy from decimal import Decimal -from typing import Any, Callable, Iterable, Mapping, Tuple, Union +from typing import Any, Callable, Iterable, Mapping, Optional, Tuple, Union from werkzeug.datastructures import ImmutableDict +from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models.metadata_proxy import MetadataProxy from app.jinja_filters import ( format_number, format_percentage, @@ -11,7 +13,11 @@ get_formatted_currency, ) from app.questionnaire import Location -from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.questionnaire.questionnaire_schema import ( + QuestionnaireSchema, + get_calculated_summary_answer_ids, +) +from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.schema_utils import get_answer_ids_in_block from app.questionnaire.value_source_resolver import ValueSourceResolver @@ -21,23 +27,45 @@ class CalculatedSummaryContext(Context): + def __init__( + self, + language: str, + schema: QuestionnaireSchema, + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: Optional[MetadataProxy], + response_metadata: Mapping, + routing_path: RoutingPath, + current_location: Location, + ) -> None: + super().__init__( + language, + schema, + answer_store, + list_store, + progress_store, + metadata, + response_metadata, + ) + self.routing_path = routing_path + self.current_location = current_location + def build_groups_for_section( self, section: Mapping[str, Any], return_to_block_id: str, - current_location: Location, - ) -> list[dict[str, Group]]: - routing_path = self._router.routing_path(section["id"]) + ) -> list[Mapping[str, Group]]: return [ Group( group_schema=group, - routing_path=routing_path, + routing_path=self.routing_path, answer_store=self._answer_store, list_store=self._list_store, metadata=self._metadata, response_metadata=self._response_metadata, schema=self._schema, - location=current_location, + location=self.current_location, language=self._language, progress_store=self._progress_store, return_to="calculated-summary", @@ -46,25 +74,23 @@ def build_groups_for_section( for group in section["groups"] ] - def build_view_context_for_calculated_summary( - self, current_location: Location - ) -> dict[str, dict[str, Any]]: + def build_view_context_for_calculated_summary(self) -> dict[str, dict[str, Any]]: # type ignores added as block will exist at this point - block_id: str = current_location.block_id # type: ignore + block_id: str = self.current_location.block_id # type: ignore block: ImmutableDict = self._schema.get_block(block_id) # type: ignore calculated_section: dict[str, Any] = self._build_calculated_summary_section( - block, current_location + block ) calculation = block["calculation"] groups = self.build_groups_for_section( - calculated_section, block_id, current_location + calculated_section, + block_id, ) formatted_total = self._get_formatted_total( - groups or [], - current_location=current_location, + groups=groups or [], calculation=ValueSourceResolver.get_calculation_operator( calculation["calculation_type"] ) @@ -89,11 +115,11 @@ def build_view_context_for_calculated_summary( } def _build_calculated_summary_section( - self, rendered_block: ImmutableDict[str, Any], current_location: Location + self, rendered_block: ImmutableDict[str, Any] ) -> dict[str, Any]: """Build up the list of blocks only including blocks / questions / answers which are relevant to the summary""" # type ignores added as block will exist at this point - block_id: str = current_location.block_id # type: ignore + block_id: str = self.current_location.block_id # type: ignore group: ImmutableDict = self._schema.get_group_for_block_id(block_id) # type: ignore # type ignores it is not valid to not have a section at this point section_id: str = self._schema.get_section_id_for_block_id(block_id) # type: ignore @@ -102,9 +128,7 @@ def _build_calculated_summary_section( if rendered_block["calculation"].get("answers_to_calculate"): answers_to_calculate = rendered_block["calculation"]["answers_to_calculate"] else: - answers_to_calculate = self._schema.get_calculated_summary_answer_ids( - rendered_block - ) + answers_to_calculate = get_calculated_summary_answer_ids(rendered_block) blocks_to_calculate: list = [ self._schema.get_block_for_answer_id(answer_id) @@ -118,7 +142,7 @@ def _build_calculated_summary_section( for block in unique_blocks: if QuestionnaireSchema.is_question_block_type(block["type"]): transformed_block = self._remove_unwanted_questions_answers( - block, answers_to_calculate, current_location=current_location + block, answers_to_calculate ) if set(get_answer_ids_in_block(transformed_block)) & set( answers_to_calculate @@ -128,10 +152,7 @@ def _build_calculated_summary_section( return {"id": section_id, "groups": [{"id": group["id"], "blocks": blocks}]} def _remove_unwanted_questions_answers( - self, - block: Mapping[str, Any], - answer_ids_to_keep: Iterable[str], - current_location: Location, + self, block: Mapping[str, Any], answer_ids_to_keep: Iterable[str] ) -> dict[str, Any]: """ Evaluates questions in a block and removes any questions not containing a relevant answer @@ -143,7 +164,8 @@ def _remove_unwanted_questions_answers( self._response_metadata, self._answer_store, self._list_store, - current_location=current_location, + self.current_location, + self._progress_store, ) transformed_block = deepcopy(transformed_block) transformed_block = QuestionnaireSchema.get_mutable_deepcopy(transformed_block) @@ -168,14 +190,9 @@ def _remove_unwanted_questions_answers( return transformed_block def _get_formatted_total( - self, - groups: list, - current_location: Location, - calculation: Union[Callable, ImmutableDict], + self, groups: list, calculation: Callable | ImmutableDict ) -> str: - answer_format, values_to_calculate = self._get_answer_format( - groups, current_location - ) + answer_format, values_to_calculate = self._get_answer_format(groups) if isinstance(calculation, ImmutableDict): evaluate_calculated_summary = RuleEvaluator( @@ -184,7 +201,9 @@ def _get_formatted_total( self._list_store, self._metadata, self._response_metadata, - location=current_location, + routing_path_block_ids=self.routing_path.block_ids, + location=self.current_location, + progress_store=self._progress_store, ) calculated_total: Union[int, float, Decimal] = evaluate_calculated_summary.evaluate(calculation) # type: ignore @@ -193,9 +212,7 @@ def _get_formatted_total( return self._format_total(answer_format, calculated_total) - def _get_answer_format( - self, groups: list, current_location: Location - ) -> Tuple[dict[str, Any], list]: + def _get_answer_format(self, groups: list) -> Tuple[dict[str, Any], list]: values_to_calculate: list = [] answer_format: dict = {"type": None} for group in groups: @@ -207,7 +224,8 @@ def _get_answer_format( self._response_metadata, self._answer_store, self._list_store, - current_location=current_location, + current_location=self.current_location, + progress_store=self._progress_store, ) for answer in question["answers"]: if not answer_format["type"]: diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index 4ba0301f6a..8c7d0d82ca 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Mapping, Optional +from typing import Mapping from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -18,7 +18,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping, ) -> None: self._language = language @@ -46,5 +46,6 @@ def __init__( metadata=self._metadata, response_metadata=self._response_metadata, schema=self._schema, + progress_store=self._progress_store, placeholder_preview_mode=self._placeholder_preview_mode, ) diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index 8fa21e64ca..aa654f3fb9 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -103,7 +103,7 @@ def _get_item_title( is_primary: bool, ) -> str: rendered_summary: dict[str, Any] = self._placeholder_renderer.render( - summary_definition, list_item_id + data_to_render=summary_definition, list_item_id=list_item_id ) if is_primary: rendered_summary["item_title"] += lazy_gettext(" (You)") diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py index 495a6149a9..7cfa58e99b 100644 --- a/app/views/contexts/section_preview_context.py +++ b/app/views/contexts/section_preview_context.py @@ -36,7 +36,7 @@ def __call__(self) -> dict: def _build_preview(self) -> list[dict]: # Type ignore: The section has to exist at this point - section = self._placeholder_renderer.render(self._schema.get_section(self._section_id), None) # type: ignore + section = self._placeholder_renderer.render(data_to_render=self._schema.get_section(self._section_id), list_item_id=None) # type: ignore groups = [ PreviewGroup(group_schema=group).serialize() for group in section["groups"] diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index fc83cd4994..194833beca 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -1,6 +1,6 @@ from typing import Any, Mapping, Optional -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -22,6 +22,7 @@ def __init__( location: Location, return_to: Optional[str], return_to_block_id: Optional[str] = None, + progress_store: ProgressStore, ) -> None: self.id = block_schema["id"] self.title = block_schema.get("title") @@ -34,6 +35,7 @@ def __init__( metadata=metadata, response_metadata=response_metadata, location=location, + progress_store=progress_store, ) self._value_source_resolver = ValueSourceResolver( @@ -45,6 +47,7 @@ def __init__( location=location, list_item_id=location.list_item_id if location else None, use_default_answer=True, + progress_store=progress_store, ) self.question = self.get_question( @@ -57,6 +60,7 @@ def __init__( location=location, return_to=return_to, return_to_block_id=return_to_block_id, + progress_store=progress_store, ) def get_question( @@ -71,6 +75,7 @@ def get_question( location: Location, return_to: Optional[str], return_to_block_id: Optional[str], + progress_store: ProgressStore, ) -> dict[str, Question]: """Taking question variants into account, return the question which was displayed to the user""" @@ -84,6 +89,7 @@ def get_question( variants_key="question_variants", single_key="question", current_location=location, + progress_store=progress_store, ) return Question( variant, diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 735425464e..3ee22c9a56 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -20,20 +20,21 @@ def __init__( routing_path: RoutingPath, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: Mapping, schema: QuestionnaireSchema, location: Location, language: str, progress_store: ProgressStore, - return_to: Optional[str], - return_to_block_id: Optional[str] = None, + return_to: str | None, + return_to_block_id: str | None = None, ) -> None: self.id = group_schema["id"] self.title = group_schema.get("title") self.location = location self.placeholder_text = None self.links: dict[str, Link] = {} + self.blocks = self._build_blocks_and_links( group_schema=group_schema, routing_path=routing_path, @@ -42,20 +43,22 @@ def __init__( metadata=metadata, response_metadata=response_metadata, schema=schema, - location=location, + location=self.location, return_to=return_to, progress_store=progress_store, language=language, return_to_block_id=return_to_block_id, ) + self.placeholder_renderer = PlaceholderRenderer( language=language, answer_store=answer_store, list_store=list_store, - location=location, + location=self.location, metadata=metadata, response_metadata=response_metadata, schema=schema, + progress_store=progress_store, ) # pylint: disable=too-many-locals @@ -96,6 +99,7 @@ def _build_blocks_and_links( location=location, return_to=return_to, return_to_block_id=return_to_block_id, + progress_store=progress_store, ).serialize() ] ) @@ -138,14 +142,14 @@ def _build_blocks_and_links( return blocks - def serialize(self) -> dict[str, Any]: + def serialize(self) -> Mapping[str, Any]: return self.placeholder_renderer.render( - { + data_to_render={ "id": self.id, "title": self.title, "blocks": self.blocks, "links": self.links, "placeholder_text": self.placeholder_text, }, - self.location.list_item_id, + list_item_id=self.location.list_item_id if self.location else None, ) diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 3aad850ebf..93557624b2 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -35,6 +35,8 @@ def __init__( metadata=metadata, response_metadata=response_metadata, schema=schema, + progress_store=progress_store, + location=location, ) self._list_store = list_store self._schema = schema @@ -82,7 +84,7 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: ) rendered_summary = self._placeholder_renderer.render( - summary, self._location.list_item_id + data_to_render=summary, list_item_id=self._location.list_item_id ) section_id = self._section["id"] @@ -189,6 +191,7 @@ def _get_related_answers( ), return_to="section-summary", return_to_block_id=None, + progress_store=self._progress_store, ).serialize() for block in blocks ] diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 723a5c3764..91e3ef016a 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -73,6 +73,7 @@ def placeholder_renderer(self): response_metadata=self._questionnaire_store.response_metadata, schema=self._schema, location=self._current_location, + progress_store=self._questionnaire_store.progress_store, ) @cached_property diff --git a/app/views/handlers/calculated_summary.py b/app/views/handlers/calculated_summary.py index 693b581311..37b5cbb2a0 100644 --- a/app/views/handlers/calculated_summary.py +++ b/app/views/handlers/calculated_summary.py @@ -5,17 +5,17 @@ class CalculatedSummary(Content): def get_context(self): calculated_summary_context = CalculatedSummaryContext( - self._language, - self._schema, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, - self._questionnaire_store.progress_store, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, - ) - context = calculated_summary_context.build_view_context_for_calculated_summary( - self._current_location + language=self._language, + schema=self._schema, + answer_store=self._questionnaire_store.answer_store, + list_store=self._questionnaire_store.list_store, + progress_store=self._questionnaire_store.progress_store, + metadata=self._questionnaire_store.metadata, + response_metadata=self._questionnaire_store.response_metadata, + current_location=self._current_location, + routing_path=self._routing_path, ) + context = calculated_summary_context.build_view_context_for_calculated_summary() if not self.page_title: self.page_title = context["summary"]["calculated_question"]["title"] diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index da0e7b5f68..c08330feeb 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -78,6 +78,7 @@ def form(self): response_metadata=self._questionnaire_store.metadata, data=None, form_data=self._form_data, + progress_store=self._questionnaire_store.progress_store, ) @cached_property diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index 3c97870e1a..c69895759f 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -16,6 +16,7 @@ def rendered_block(self): self._questionnaire_store.answer_store, self._questionnaire_store.list_store, self._current_location, + self._questionnaire_store.progress_store, ) content_page_title = transformed_block.get( @@ -23,7 +24,8 @@ def rendered_block(self): ) or self._get_content_title(transformed_block) self._set_page_title(content_page_title) return self.placeholder_renderer.render( - transformed_block, self._current_location.list_item_id + data_to_render=transformed_block, + list_item_id=self._current_location.list_item_id, ) def get_context(self): diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 39e20e56d2..b9d73f0011 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -71,6 +71,7 @@ def form(self) -> QuestionnaireForm: response_metadata=self._questionnaire_store.response_metadata, data=None, form_data=self._form_data, + progress_store=self._questionnaire_store.progress_store, ) def get_context(self) -> Mapping[str, Union[str, bool, dict]]: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index fa9ed38a1d..645286626d 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -170,6 +170,7 @@ def placeholder_renderer(self): response_metadata=self._questionnaire_store.response_metadata, schema=self._schema, location=None, + progress_store=self._questionnaire_store.progress_store, ) @cached_property @@ -198,6 +199,7 @@ def form(self): response_metadata=self._questionnaire_store.response_metadata, data=self._answers, form_data=self._form_data, + progress_store=self._questionnaire_store.progress_store, ) def get_context(self): @@ -296,7 +298,7 @@ def _get_previous_location_url(self): def _render_block(self): return self.placeholder_renderer.render( - self.block_definition, self._list_item_id + data_to_render=self.block_definition, list_item_id=self._list_item_id ) def _update_section_status(self, status): diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index c41df3451e..181023907d 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -22,28 +22,31 @@ def _has_redirect_to_list_add_action(answer_action): @cached_property 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, + schema=self._schema, + question_schema=question_json, + answer_store=self._questionnaire_store.answer_store, + list_store=self._questionnaire_store.list_store, + metadata=self._questionnaire_store.metadata, + response_metadata=self._questionnaire_store.response_metadata, + location=self._current_location, form_data=self._form_data, + progress_store=self._questionnaire_store.progress_store, ) 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, + schema=self._schema, + question_schema=question_json, + answer_store=self._questionnaire_store.answer_store, + list_store=self._questionnaire_store.list_store, + metadata=self._questionnaire_store.metadata, + response_metadata=self._questionnaire_store.response_metadata, + location=self._current_location, data=answers, + progress_store=self._questionnaire_store.progress_store, ) @cached_property @@ -66,6 +69,7 @@ def rendered_block(self): self._questionnaire_store.answer_store, self._questionnaire_store.list_store, self._current_location, + self._questionnaire_store.progress_store, ) page_title = transformed_block.get("page_title") or self._get_safe_page_title( transformed_block["question"]["title"] @@ -73,7 +77,8 @@ def rendered_block(self): self._set_page_title(page_title) rendered_question = self.placeholder_renderer.render( - transformed_block["question"], self._current_location.list_item_id + data_to_render=transformed_block["question"], + list_item_id=self._current_location.list_item_id, ) return { **transformed_block, @@ -83,13 +88,13 @@ def rendered_block(self): @cached_property def list_context(self): return ListContext( - self._language, - self._schema, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, - self._questionnaire_store.progress_store, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, + language=self._language, + schema=self._schema, + answer_store=self._questionnaire_store.answer_store, + list_store=self._questionnaire_store.list_store, + progress_store=self._questionnaire_store.progress_store, + metadata=self._questionnaire_store.metadata, + response_metadata=self._questionnaire_store.response_metadata, ) def get_next_location_url(self): diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 38e8483bdc..71753ae08d 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -271,34 +271,8 @@ }, { "type": "CalculatedSummary", - "id": "currency-total-playback-skipped-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", - "calculation": { - "calculation_type": "sum", - "answers_to_calculate": [ - "first-number-answer", - "second-number-answer", - "second-number-answer-also-in-total", - "third-number-answer" - ], - "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "No" - ] - } - } - }, - { - "type": "CalculatedSummary", - "id": "currency-total-playback-with-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", "calculation": { "calculation_type": "sum", "answers_to_calculate": [ @@ -310,17 +284,6 @@ "fourth-and-a-half-number-answer-also-in-total" ], "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "Yes" - ] - } } }, { @@ -363,17 +326,17 @@ { "list": [ { - "text": "Total currency values (if Q4 not skipped): {total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "currency_total", "transforms": [ { "transform": "format_currency", "arguments": { "number": { "source": "calculated_summary", - "identifier": "currency-total-playback-with-fourth" + "identifier": "currency-total-playback" } } } @@ -382,29 +345,10 @@ ] }, { - "text": "Total currency values (if Q4 skipped)): {total}", + "text": "Total unit values: {unit_total}", "placeholders": [ { - "placeholder": "total", - "transforms": [ - { - "transform": "format_currency", - "arguments": { - "number": { - "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" - } - } - } - ] - } - ] - }, - { - "text": "Total unit values: {total}", - "placeholders": [ - { - "placeholder": "total", + "placeholder": "unit_total", "transforms": [ { "transform": "format_number", @@ -420,10 +364,10 @@ ] }, { - "text": "Total percentage values: {total}", + "text": "Total percentage values: {percentage_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "percentage_total", "transforms": [ { "transform": "format_number", @@ -439,10 +383,10 @@ ] }, { - "text": "Total number values: {total}", + "text": "Total number values: {number_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "number_total", "transforms": [ { "transform": "format_number", @@ -465,17 +409,6 @@ { "type": "Question", "id": "set-min-max-block", - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "No" - ] - } - }, "question": { "answers": [ { @@ -489,7 +422,7 @@ "minimum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } }, @@ -504,7 +437,7 @@ "maximum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } } @@ -515,7 +448,7 @@ { "placeholder": "calculated_summary_answer", "value": { - "identifier": "currency-total-playback-skipped-fourth", + "identifier": "currency-total-playback", "source": "calculated_summary" } } diff --git a/schemas/test/en/test_calculated_summary_cross_section_dependencies.json b/schemas/test/en/test_calculated_summary_cross_section_dependencies.json new file mode 100644 index 0000000000..5d8192d724 --- /dev/null +++ b/schemas/test/en/test_calculated_summary_cross_section_dependencies.json @@ -0,0 +1,355 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary Cross Section Dependencies", + "theme": "default", + "description": "A questionnaire to demo resolution of calculated summary values across sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "questions-section", + "title": "Questions", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "radio", + "title": "Questions", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-first-block-question", + "title": "Skip First Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-first-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-and-a-half-number-block", + "question": { + "id": "first-and-a-half-number-question-also-in-total", + "title": "First Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "first-and-a-half-number-answer-also-in-total", + "label": "First answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question-also-in-total", + "title": "Second Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-1", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": [ + "first-number-answer", + "first-and-a-half-number-answer-also-in-total", + "second-number-answer-also-in-total" + ], + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Calculated Summary", + "summary": { "show_on_completion": true }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": ["third-number-answer", "third-number-answer-also-in-total"], + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Which answer did you give to question 4 and a half?", + "mandatory": false, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "answer_value_1", + "value": { + "identifier": "first-and-a-half-number-answer-also-in-total", + "source": "answers" + } + } + ], + "text": "{answer_value_1} - first and a half answer" + }, + "value": "{answer_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_1", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_1} - calculated summary answer (previous section)" + }, + "value": "{calc_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_2", + "value": { + "identifier": "currency-total-playback-2", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_2} - calculated summary answer (current section)" + }, + "value": "{calc_value_2}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "third_answer_value", + "value": { + "identifier": "third-number-answer", + "source": "answers" + } + } + ], + "text": "{third_answer_value} - third answer" + }, + "value": "{third_answer_value}" + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ], + "id": "calculated-summary" + } + ] + } + ] +} diff --git a/schemas/test/en/test_introduction.json b/schemas/test/en/test_introduction.json index fe88af00f0..130b68eab8 100644 --- a/schemas/test/en/test_introduction.json +++ b/schemas/test/en/test_introduction.json @@ -422,10 +422,10 @@ ], "id": "report-radio-second-question", "title": { - "text": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "text": "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", "placeholders": [ { - "placeholder": "start_date", + "placeholder": "calendar_start_date", "transforms": [ { "transform": "first_non_empty_item", @@ -450,7 +450,7 @@ ] }, { - "placeholder": "end_date", + "placeholder": "calendar_end_date", "transforms": [ { "transform": "first_non_empty_item", diff --git a/schemas/test/en/test_introduction_hub.json b/schemas/test/en/test_introduction_hub.json index 59fb42325e..d0e761c80c 100644 --- a/schemas/test/en/test_introduction_hub.json +++ b/schemas/test/en/test_introduction_hub.json @@ -333,10 +333,10 @@ ], "id": "report-radio-second-question", "title": { - "text": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "text": "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", "placeholders": [ { - "placeholder": "start_date", + "placeholder": "calendar_start_date", "transforms": [ { "transform": "first_non_empty_item", @@ -361,7 +361,7 @@ ] }, { - "placeholder": "end_date", + "placeholder": "calendar_end_date", "transforms": [ { "transform": "first_non_empty_item", diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index 5d247cfde3..36afe23f8a 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -160,8 +160,8 @@ "when": { "==": [ { - "source": "answers", - "identifier": "skip-fourth-block-answer" + "identifier": "skip-fourth-block-answer", + "source": "answers" }, "Yes" ] @@ -190,8 +190,8 @@ "when": { "==": [ { - "source": "answers", - "identifier": "skip-fourth-block-answer" + "identifier": "skip-fourth-block-answer", + "source": "answers" }, "Yes" ] @@ -271,47 +271,8 @@ }, { "type": "CalculatedSummary", - "id": "currency-total-playback-skipped-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", - "calculation": { - "operation": { - "+": [ - { - "source": "answers", - "identifier": "first-number-answer" - }, - { - "source": "answers", - "identifier": "second-number-answer" - }, - { - "source": "answers", - "identifier": "second-number-answer-also-in-total" - }, - { - "source": "answers", - "identifier": "third-number-answer" - } - ] - }, - "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "No" - ] - } - } - }, - { - "type": "CalculatedSummary", - "id": "currency-total-playback-with-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", "calculation": { "operation": { "+": [ @@ -342,17 +303,6 @@ ] }, "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "Yes" - ] - } } }, { @@ -425,17 +375,17 @@ { "list": [ { - "text": "Total currency values (if Q4 not skipped): {total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "currency_total", "transforms": [ { "transform": "format_currency", "arguments": { "number": { "source": "calculated_summary", - "identifier": "currency-total-playback-with-fourth" + "identifier": "currency-total-playback" } } } @@ -444,29 +394,10 @@ ] }, { - "text": "Total currency values (if Q4 skipped)): {total}", + "text": "Total unit values: {unit_total}", "placeholders": [ { - "placeholder": "total", - "transforms": [ - { - "transform": "format_currency", - "arguments": { - "number": { - "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" - } - } - } - ] - } - ] - }, - { - "text": "Total unit values: {total}", - "placeholders": [ - { - "placeholder": "total", + "placeholder": "unit_total", "transforms": [ { "transform": "format_number", @@ -482,10 +413,10 @@ ] }, { - "text": "Total percentage values: {total}", + "text": "Total percentage values: {percentage_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "percentage_total", "transforms": [ { "transform": "format_number", @@ -501,10 +432,10 @@ ] }, { - "text": "Total number values: {total}", + "text": "Total number values: {number_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "number_total", "transforms": [ { "transform": "format_number", @@ -527,17 +458,6 @@ { "type": "Question", "id": "set-min-max-block", - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "No" - ] - } - }, "question": { "answers": [ { @@ -551,7 +471,7 @@ "minimum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } }, @@ -566,7 +486,7 @@ "maximum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } } @@ -577,7 +497,7 @@ { "placeholder": "calculated_summary_answer", "value": { - "identifier": "currency-total-playback-skipped-fourth", + "identifier": "currency-total-playback", "source": "calculated_summary" } } diff --git a/schemas/test/en/test_new_calculated_summary_cross_section_dependencies.json b/schemas/test/en/test_new_calculated_summary_cross_section_dependencies.json new file mode 100644 index 0000000000..d79882b3d1 --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_cross_section_dependencies.json @@ -0,0 +1,375 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary Cross Section Dependencies", + "theme": "default", + "description": "A questionnaire to demo resolution of calculated summary values across sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "questions-section", + "title": "Questions", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "radio", + "title": "Questions", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-first-block-question", + "title": "Skip First Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-first-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-and-a-half-number-block", + "question": { + "id": "first-and-a-half-number-question-also-in-total", + "title": "First Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "first-and-a-half-number-answer-also-in-total", + "label": "First answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question-also-in-total", + "title": "Second Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-1", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "first-and-a-half-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Calculated Summary", + "summary": { "show_on_completion": true }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "third-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Which answer did you give to question 4 and a half?", + "mandatory": true, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "answer_value_1", + "value": { + "identifier": "first-and-a-half-number-answer-also-in-total", + "source": "answers" + } + } + ], + "text": "{answer_value_1} - first and a half answer" + }, + "value": "{answer_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_1", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_1} - calculated summary answer (previous section)" + }, + "value": "{calc_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_2", + "value": { + "identifier": "currency-total-playback-2", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_2} - calculated summary answer (current section)" + }, + "value": "{calc_value_2}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "third_answer_value", + "value": { + "identifier": "third-number-answer", + "source": "answers" + } + } + ], + "text": "{third_answer_value} - third answer" + }, + "value": "{third_answer_value}" + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ], + "id": "calculated-summary" + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_cross_section_dependencies_repeating.json b/schemas/test/en/test_new_calculated_summary_cross_section_dependencies_repeating.json new file mode 100644 index 0000000000..5bfb54b9a2 --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_cross_section_dependencies_repeating.json @@ -0,0 +1,603 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary Cross Section Dependencies", + "theme": "default", + "description": "A questionnaire to demo resolution of calculated summary values across sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section", + "title": "Household", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": "Do you live here?", + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "questions-section", + "title": "Questions", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "radio", + "title": "Questions", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-first-block-question", + "title": "Skip First Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-first-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-and-a-half-number-block", + "question": { + "id": "first-and-a-half-number-question-also-in-total", + "title": "First Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "first-and-a-half-number-answer-also-in-total", + "label": "First answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question-also-in-total", + "title": "Second Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-1", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "first-and-a-half-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Calculated Summary", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "third-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Which answer did you give to question 4 and a half?", + "mandatory": false, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "answer_value_1", + "value": { + "identifier": "first-and-a-half-number-answer-also-in-total", + "source": "answers" + } + } + ], + "text": "{answer_value_1} - first and a half answer" + }, + "value": "{answer_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_1", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_1} - calculated summary answer (previous section)" + }, + "value": "{calc_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_2", + "value": { + "identifier": "currency-total-playback-2", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_2} - calculated summary answer (current section)" + }, + "value": "{calc_value_2}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "third_answer_value", + "value": { + "identifier": "third-number-answer", + "source": "answers" + } + } + ], + "text": "{third_answer_value} - third answer" + }, + "value": "{third_answer_value}" + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ], + "id": "calculated-summary" + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_dependent_questions.json b/schemas/test/en/test_new_calculated_summary_dependent_questions.json index b23550329e..b55565346d 100644 --- a/schemas/test/en/test_new_calculated_summary_dependent_questions.json +++ b/schemas/test/en/test_new_calculated_summary_dependent_questions.json @@ -133,7 +133,6 @@ ] } }, - { "id": "calculated-summary-block", "type": "CalculatedSummary", diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json index d61b7d57a0..f66c7626c1 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_section.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -228,7 +228,9 @@ { "id": "personal-details-section", "title": "Personal Details", - "summary": { "show_on_completion": true }, + "summary": { + "show_on_completion": true + }, "repeat": { "for_list": "people", "title": { @@ -497,47 +499,8 @@ }, { "type": "CalculatedSummary", - "id": "currency-total-playback-skipped-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (Skipped Fourth)", - "calculation": { - "operation": { - "+": [ - { - "source": "answers", - "identifier": "first-number-answer" - }, - { - "source": "answers", - "identifier": "second-number-answer" - }, - { - "source": "answers", - "identifier": "second-number-answer-also-in-total" - }, - { - "source": "answers", - "identifier": "third-number-answer" - } - ] - }, - "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "No" - ] - } - } - }, - { - "type": "CalculatedSummary", - "id": "currency-total-playback-with-fourth", - "title": "We calculate the total of currency values entered to be %(total)s. Is this correct? (With Fourth)", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", "calculation": { "operation": { "+": [ @@ -568,17 +531,6 @@ ] }, "title": "Grand total of previous values" - }, - "skip_conditions": { - "when": { - "==": [ - { - "identifier": "skip-fourth-block-answer", - "source": "answers" - }, - "Yes" - ] - } } }, { @@ -717,36 +669,17 @@ { "list": [ { - "text": "Total currency values (if Q4 not skipped): {total}", - "placeholders": [ - { - "placeholder": "total", - "transforms": [ - { - "transform": "format_currency", - "arguments": { - "number": { - "source": "calculated_summary", - "identifier": "currency-total-playback-with-fourth" - } - } - } - ] - } - ] - }, - { - "text": "Total currency values (if Q4 skipped)): {total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "currency_total", "transforms": [ { "transform": "format_currency", "arguments": { "number": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } } @@ -755,10 +688,10 @@ ] }, { - "text": "Total unit values: {total}", + "text": "Total unit values: {unit_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "unit_total", "transforms": [ { "transform": "format_number", @@ -774,10 +707,10 @@ ] }, { - "text": "Total percentage values: {total}", + "text": "Total percentage values: {percentage_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "percentage_total", "transforms": [ { "transform": "format_number", @@ -793,10 +726,10 @@ ] }, { - "text": "Total number values: {total}", + "text": "Total number values: {number_total}", "placeholders": [ { - "placeholder": "total", + "placeholder": "number_total", "transforms": [ { "transform": "format_number", @@ -843,7 +776,7 @@ "minimum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } }, @@ -858,7 +791,7 @@ "maximum": { "value": { "source": "calculated_summary", - "identifier": "currency-total-playback-skipped-fourth" + "identifier": "currency-total-playback" } } } @@ -869,7 +802,7 @@ { "placeholder": "calculated_summary_answer", "value": { - "identifier": "currency-total-playback-skipped-fourth", + "identifier": "currency-total-playback", "source": "calculated_summary" } } diff --git a/schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json b/schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json index 8d7ba0aecb..adf631db12 100644 --- a/schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json +++ b/schemas/test/en/test_routing_and_skipping_section_dependencies_calculated_summary.json @@ -79,13 +79,69 @@ ] } }, + { + "type": "Question", + "id": "skip-butter-block", + "question": { + "type": "General", + "id": "skip-butter-block-question", + "title": "Skip optional question about butter so that it doesn’t appear in the Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-butter-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-butter-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "butter-block", + "question": { + "id": "butter-question", + "title": "How much do you spend on butter?", + "type": "General", + "answers": [ + { + "id": "butter-answer", + "label": "Butter", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, { "type": "CalculatedSummary", "id": "currency-total-playback", "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", "calculation": { "calculation_type": "sum", - "answers_to_calculate": ["milk-answer", "eggs-answer", "bread-answer", "cheese-answer"], + "answers_to_calculate": ["milk-answer", "eggs-answer", "bread-answer", "cheese-answer", "butter-answer"], "title": "Grand total of previous values" } } diff --git a/schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json b/schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json index f1c53305e4..4de0f7aeb3 100644 --- a/schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json +++ b/schemas/test/en/test_routing_and_skipping_section_dependencies_new_calculated_summary.json @@ -40,7 +40,7 @@ "id": "first-question", "title": "How much do you spend on the following items?", "description": [ - "If the total is equal to £100 a new section will appear on the hub and if it is greater than or equal to £100 a dependent question will appear in the dependent question section" + "If the total is equal to £100 a new section will appear on the hub and if it is less than or equal to £10 a dependent question will appear in the dependent question section" ], "type": "General", "answers": [ @@ -79,6 +79,62 @@ ] } }, + { + "type": "Question", + "id": "skip-butter-block", + "question": { + "type": "General", + "id": "skip-butter-block-question", + "title": "Skip optional question about butter so that it doesn’t appear in the Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-butter-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-butter-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "butter-block", + "question": { + "id": "butter-question", + "title": "How much do you spend on butter?", + "type": "General", + "answers": [ + { + "id": "butter-answer", + "label": "Butter", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, { "type": "CalculatedSummary", "id": "currency-total-playback", @@ -101,6 +157,10 @@ { "source": "answers", "identifier": "cheese-answer" + }, + { + "source": "answers", + "identifier": "butter-answer" } ] }, diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 2e807dcb26..098c1385b1 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -1,5 +1,6 @@ import pytest +from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.forms.field_handlers.select_handlers import Choice, ChoiceWithDetailAnswer @@ -32,6 +33,7 @@ def rule_evaluator(): response_metadata=get_mock_response_metadata(), schema=get_mock_schema(), location=None, + progress_store=ProgressStore(), ) return evaluator @@ -48,6 +50,7 @@ def value_source_resolver(): location=None, list_item_id=None, escape_answer_values=False, + progress_store=ProgressStore(), ) return resolver diff --git a/tests/app/forms/test_field_factory.py b/tests/app/forms/test_field_factory.py index bab122042a..13fd7a0d3b 100644 --- a/tests/app/forms/test_field_factory.py +++ b/tests/app/forms/test_field_factory.py @@ -1,5 +1,6 @@ import pytest +from app.data_models import ProgressStore from app.forms import error_messages from app.forms.field_handlers import get_field_handler from app.questionnaire import QuestionnaireSchema @@ -43,6 +44,7 @@ def test_invalid_field_type_raises_on_invalid(answer_store, list_store): location=None, list_item_id=None, escape_answer_values=False, + progress_store=ProgressStore(), ) rule_evaluator = RuleEvaluator( @@ -52,6 +54,7 @@ def test_invalid_field_type_raises_on_invalid(answer_store, list_store): response_metadata=response_metadata, schema=schema, location=None, + progress_store=ProgressStore(), ) # Given diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 7440b4d93a..6c3623d570 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -4,7 +4,7 @@ import pytest from werkzeug.datastructures import MultiDict -from app.data_models import ListStore +from app.data_models import ListStore, ProgressStore from app.data_models.answer_store import Answer, AnswerStore from app.forms import error_messages from app.forms.questionnaire_form import generate_form @@ -13,7 +13,7 @@ ResponseRequired, format_message_with_title, ) -from app.questionnaire import QuestionnaireSchema +from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.utilities.schema import load_schema_from_name from tests.app.questionnaire.conftest import get_metadata @@ -40,6 +40,7 @@ def test_form_ids_match_block_answer_ids(app, answer_store, list_store): list_store, metadata=get_metadata(), response_metadata={}, + progress_store=ProgressStore(), ) for answer_id in schema.get_answer_ids_for_block("name-block"): @@ -68,6 +69,7 @@ def test_form_date_range_populates_data(app, answer_store, list_store): "date-range-from-answer": "2016-03-01", "date-range-to-answer": "2016-03-31", } + form = generate_form( schema, question_schema, @@ -76,6 +78,7 @@ def test_form_date_range_populates_data(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) assert form.data == expected_form_data @@ -103,6 +106,7 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list "date-range-from-answer": "2016-12-25", "date-range-to-answer": "2016-12-25", } + form = generate_form( schema, question_schema, @@ -111,6 +115,7 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -154,6 +159,7 @@ def test_date_range_to_precedes_from_raises_question_error( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -188,6 +194,7 @@ def test_date_range_too_large_period_raises_question_error( "date-range-from": "2016-12-25", "date-range-to": "2017-12-24", } + form = generate_form( schema, question_schema, @@ -196,6 +203,7 @@ def test_date_range_too_large_period_raises_question_error( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -229,6 +237,7 @@ def test_date_range_too_small_period_raises_question_error( "date-range-from": "2016-12-25", "date-range-to": "2016-12-26", } + form = generate_form( schema, question_schema, @@ -237,6 +246,7 @@ def test_date_range_too_small_period_raises_question_error( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -268,6 +278,7 @@ def test_date_range_valid_period(app, answer_store, list_store): "date-range-from": "2016-12-25", "date-range-to": "2017-01-26", } + form = generate_form( schema, question_schema, @@ -276,6 +287,7 @@ def test_date_range_valid_period(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -313,6 +325,7 @@ def test_date_combined_single_validation(app, answer_store, list_store): "date-range-from": "2017-01-01", "date-range-to": "2017-03-14", } + form = generate_form( schema, question_schema, @@ -321,6 +334,7 @@ def test_date_combined_single_validation(app, answer_store, list_store): metadata, response_metadata, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -363,6 +377,7 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) "date-range-from": "2017-01-01", "date-range-to": "2017-01-10", } + form = generate_form( schema, question_schema, @@ -371,6 +386,7 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -411,6 +427,7 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) "date-range-from": "2017-01-01", "date-range-to": "2017-02-21", } + form = generate_form( schema, question_schema, @@ -419,6 +436,7 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) metadata, response_metadata, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -457,6 +475,7 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): "date-range-from": "2016-11", "date-range-to": "2017-06", } + form = generate_form( schema, question_schema, @@ -465,6 +484,7 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): metadata, response_metadata, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -507,6 +527,7 @@ def test_date_mm_yyyy_combined_range_too_small_validation( "date-range-from": "2017-01", "date-range-to": "2017-02", } + form = generate_form( schema, question_schema, @@ -515,6 +536,7 @@ def test_date_mm_yyyy_combined_range_too_small_validation( metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -555,6 +577,7 @@ def test_date_mm_yyyy_combined_range_too_large_validation( "date-range-from": "2017-01", "date-range-to": "2017-05", } + form = generate_form( schema, question_schema, @@ -563,6 +586,7 @@ def test_date_mm_yyyy_combined_range_too_large_validation( metadata, response_metadata, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -594,6 +618,7 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): "date-range-from": "2015", "date-range-to": "2021", } + form = generate_form( schema, question_schema, @@ -602,6 +627,7 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -637,6 +663,7 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s "date-range-from": "2016", "date-range-to": "2017", } + form = generate_form( schema, question_schema, @@ -645,6 +672,7 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -676,6 +704,7 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s "date-range-from": "2016", "date-range-to": "2020", } + form = generate_form( schema, question_schema, @@ -684,6 +713,7 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -724,6 +754,7 @@ def test_date_raises_ValueError_when_any_date_range_parts_are_falsy( metadata, response_metadata, form_data=form_data, + progress_store=ProgressStore(), ) with pytest.raises(ValueError): @@ -776,6 +807,7 @@ def test_bespoke_message_for_date_validation_range( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) mocker.patch( @@ -833,6 +865,7 @@ def test_invalid_minimum_period_limit_and_single_date_periods( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) with pytest.raises(Exception) as exc: @@ -893,6 +926,7 @@ def test_invalid_maximum_period_limit_and_single_date_periods( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) with pytest.raises(Exception) as exc: @@ -954,6 +988,7 @@ def test_period_limits_minimum_not_set_and_single_date_periods( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) with mocker.patch( @@ -1019,6 +1054,7 @@ def test_invalid_date_range_and_single_date_periods( metadata=get_metadata(metadata), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) with pytest.raises(Exception) as exc: @@ -1064,6 +1100,7 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) with pytest.raises(Exception) as exc: @@ -1101,6 +1138,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) mocker.patch( @@ -1291,6 +1329,14 @@ def test_calculated_field( question_schema = schema.get_block(block).get("question") + location = Location( + section_id="default-section", + block_id=block, + list_item_id=None, + ) + + metadata = get_metadata() + form_data = MultiDict(breakdowns) form = generate_form( @@ -1298,9 +1344,11 @@ def test_calculated_field( question_schema, answer_store, list_store, - metadata=get_metadata(), + location=location, + metadata=metadata, response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1355,6 +1403,7 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1391,6 +1440,7 @@ def test_multi_calculation(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1407,6 +1457,7 @@ def test_multi_calculation(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1423,6 +1474,7 @@ def test_multi_calculation(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1456,6 +1508,7 @@ def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_st metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate() @@ -1475,6 +1528,7 @@ def test_form_errors_are_correctly_mapped(app, answer_store, list_store): list_store, metadata=get_metadata(), response_metadata={}, + progress_store=ProgressStore(), ) form.validate() @@ -1498,6 +1552,7 @@ def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store list_store, metadata=get_metadata(), response_metadata={}, + progress_store=ProgressStore(), ) form.validate() @@ -1534,6 +1589,7 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"mandatory-checkbox-answer": "Your choice"}), + progress_store=ProgressStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") @@ -1548,6 +1604,7 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( metadata=get_metadata(), response_metadata={}, data={"mandatory-checkbox-answer": "Ham"}, + progress_store=ProgressStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") @@ -1572,6 +1629,7 @@ def test_answer_with_detail_answer_errors_are_correctly_mapped( metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"radio-mandatory-answer": "Other"}), + progress_store=ProgressStore(), ) form.validate() @@ -1603,6 +1661,7 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"set-minimum": "-1"}), + progress_store=ProgressStore(), ) form.validate() @@ -1628,6 +1687,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), + progress_store=ProgressStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1658,8 +1718,12 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( metadata=get_metadata(), response_metadata={}, schema=schema, + progress_store=ProgressStore(), + location=Location(section_id="mutually-exclusive-checkbox-section"), + ) + rendered_schema = renderer.render( + data_to_render=question_schema, list_item_id=None ) - rendered_schema = renderer.render(question_schema, None) form = generate_form( schema, @@ -1669,6 +1733,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), + progress_store=ProgressStore(), ) form.validate_mutually_exclusive_question(question_schema) error = form.question_errors["mutually-exclusive-checkbox-question"] @@ -1704,6 +1769,7 @@ def test_mutually_exclusive_question_raises_error_when_both_answered( metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1725,6 +1791,7 @@ def test_date_range_form(app, answer_store, list_store): list_store, metadata=get_metadata(), response_metadata={}, + progress_store=ProgressStore(), ) assert hasattr(form, "date-range-from-answer") @@ -1761,6 +1828,7 @@ def test_date_range_form_with_data(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) assert hasattr(form, "date-range-from-answer") @@ -1799,6 +1867,7 @@ def test_form_for_radio_other_not_selected(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) assert hasattr(form, "radio-mandatory-answer") @@ -1820,6 +1889,7 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): "other-answer-mandatory": "Other text field value", } ) + form = generate_form( schema, question_schema, @@ -1828,6 +1898,7 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): metadata=get_metadata(), response_metadata={}, form_data=form_data, + progress_store=ProgressStore(), ) other_text_field = getattr(form, "other-answer-mandatory") diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 03771dc58f..c2738adda8 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -69,6 +69,7 @@ def parser(answer_store, location, mock_schema, mock_renderer): schema=mock_schema, location=location, renderer=mock_renderer, + progress_store=ProgressStore(), ) @@ -1005,6 +1006,8 @@ def placeholder_renderer(option_label_from_value_schema): metadata=get_metadata({"trad_as": "ESSENTIAL SERVICES LTD"}), response_metadata={}, schema=option_label_from_value_schema, + progress_store=ProgressStore(), + location=Location(section_id="checkbox-section"), ) return renderer @@ -1018,6 +1021,7 @@ def mock_renderer(mock_schema): metadata=get_metadata(), response_metadata={}, schema=mock_schema, + progress_store=ProgressStore(), ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 0e04c3ac81..b2875a4686 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -5,7 +5,7 @@ from freezegun import freeze_time from mock import Mock -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import Answer from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema @@ -60,6 +60,7 @@ def get_rule_evaluator( list_store=list_store, location=location, routing_path_block_ids=routing_path_block_ids, + progress_store=ProgressStore(), ) diff --git a/tests/app/questionnaire/test_dynamic_answer_options.py b/tests/app/questionnaire/test_dynamic_answer_options.py index 0154079dd2..dd3ff1d7cf 100644 --- a/tests/app/questionnaire/test_dynamic_answer_options.py +++ b/tests/app/questionnaire/test_dynamic_answer_options.py @@ -1,7 +1,7 @@ # pylint: disable=redefined-outer-name import pytest -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer_store import Answer from app.questionnaire.dynamic_answer_options import DynamicAnswerOptions from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -17,6 +17,7 @@ def rule_evaluator(mock_schema, response_metadata): response_metadata=response_metadata, schema=mock_schema, location=None, + progress_store=ProgressStore(), ) return evaluator @@ -34,6 +35,7 @@ def value_source_resolver(mock_schema, response_metadata): list_item_id=None, routing_path_block_ids=None, use_default_answer=True, + progress_store=ProgressStore(), ) return resolver diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index e096700911..c57f6be309 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1,9 +1,12 @@ +# pylint: disable=too-many-lines from mock import Mock +from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.questionnaire import Location from app.questionnaire.placeholder_parser import PlaceholderParser -from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.utilities.schema import load_schema_from_name from tests.app.questionnaire.conftest import get_metadata @@ -15,7 +18,7 @@ def test_parse_placeholders(placeholder_list, parser): assert placeholders["first_name"] == "Joe" -def test_metadata_placeholder(mock_renderer): +def test_metadata_placeholder(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "period", @@ -27,21 +30,27 @@ def test_metadata_placeholder(mock_renderer): ] period_str = "Aug 2018" + + metadata = get_metadata({"period_str": period_str}) parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata({"period_str": period_str}), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert period_str == placeholders["period"] -def test_previous_answer_transform_placeholder(mock_renderer): +def test_previous_answer_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "total_turnover", @@ -71,15 +80,17 @@ def test_previous_answer_transform_placeholder(mock_renderer): list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["total_turnover"] == "£1,000.00" -def test_metadata_transform_placeholder(mock_renderer): +def test_metadata_transform_placeholder(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "start_date", @@ -98,21 +109,27 @@ def test_metadata_transform_placeholder(mock_renderer): } ] + metadata = get_metadata({"ref_p_start_date": "2019-02-11"}) + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["start_date"] == "Monday 11 February 2019" -def test_response_metadata_transform_placeholder(mock_renderer): +def test_response_metadata_transform_placeholder( + mock_renderer, mock_schema, mock_location +): # This test should use ISO format dates when they become supported placeholder_list = [ { @@ -132,21 +149,28 @@ def test_response_metadata_transform_placeholder(mock_renderer): } ] + metadata = get_metadata({"ref_p_start_date": "2019-02-11"}) + response_metadata = {"started_at": "2019-02-11"} + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), - response_metadata={"started_at": "2019-02-11"}, - schema=QuestionnaireSchema({}), + metadata=metadata, + response_metadata=response_metadata, + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["start_date"] == "Monday 11 February 2019" -def test_multiple_answer_transform_placeholder(mock_renderer): +def test_multiple_answer_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "persons_name", @@ -165,19 +189,23 @@ def test_multiple_answer_transform_placeholder(mock_renderer): } ] + answer_store = AnswerStore( + [ + {"answer_id": "first-name", "value": "Joe"}, + {"answer_id": "last-name", "value": "Bloggs"}, + ] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [ - {"answer_id": "first-name", "value": "Joe"}, - {"answer_id": "last-name", "value": "Bloggs"}, - ] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -185,7 +213,9 @@ def test_multiple_answer_transform_placeholder(mock_renderer): assert placeholders["persons_name"] == "Joe Bloggs" -def test_first_non_empty_item_transform_placeholder(mock_renderer): +def test_first_non_empty_item_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "company_name", @@ -203,14 +233,18 @@ def test_first_non_empty_item_transform_placeholder(mock_renderer): } ] + metadata = get_metadata({"trad_as": None, "ru_name": "ru_name"}) + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata({"trad_as": None, "ru_name": "ru_name"}), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -218,7 +252,9 @@ def test_first_non_empty_item_transform_placeholder(mock_renderer): assert placeholders["company_name"] == "ru_name" -def test_format_list_answer_transform_placeholder(mock_renderer): +def test_format_list_answer_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "toppings", @@ -236,16 +272,20 @@ def test_format_list_answer_transform_placeholder(mock_renderer): } ] + answer_store = AnswerStore( + [{"answer_id": "checkbox-answer", "value": ["Ham", "Cheese"]}] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [{"answer_id": "checkbox-answer", "value": ["Ham", "Cheese"]}] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -253,7 +293,7 @@ def test_format_list_answer_transform_placeholder(mock_renderer): assert placeholders["toppings"] == "
      • Ham
      • Cheese
      " -def test_placeholder_parser_escapes_answers(mock_renderer): +def test_placeholder_parser_escapes_answers(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "crisps", @@ -271,21 +311,25 @@ def test_placeholder_parser_escapes_answers(mock_renderer): } ] + answer_store = AnswerStore( + [ + { + "answer_id": "checkbox-answer", + "value": ["Cheese & Onion", "Salt & Vinegar", "><'"], + } + ] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [ - { - "answer_id": "checkbox-answer", - "value": ["Cheese & Onion", "Salt & Vinegar", "><'"], - } - ] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -296,7 +340,9 @@ def test_placeholder_parser_escapes_answers(mock_renderer): ) -def test_multiple_metadata_transform_placeholder(mock_renderer): +def test_multiple_metadata_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "start_date", @@ -322,14 +368,18 @@ def test_multiple_metadata_transform_placeholder(mock_renderer): } ] + metadata = get_metadata({"ref_p_start_date": "2019-02-11"}) + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata({"ref_p_start_date": "2019-02-11"}), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -337,7 +387,9 @@ def test_multiple_metadata_transform_placeholder(mock_renderer): assert placeholders["start_date"] == "11/02/2019" -def test_multiple_metadata_list_transform_placeholder(mock_renderer): +def test_multiple_metadata_list_transform_placeholder( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "dates", @@ -356,23 +408,27 @@ def test_multiple_metadata_list_transform_placeholder(mock_renderer): } ] + metadata = get_metadata( + {"ref_p_start_date": "2019-02-11", "ref_p_end_date": "2019-10-11"} + ) + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), list_store=ListStore(), - metadata=get_metadata( - {"ref_p_start_date": "2019-02-11", "ref_p_end_date": "2019-10-11"} - ), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["dates"] == "2019-02-11 2019-10-11" -def test_checkbox_transform_placeholder(mock_renderer): +def test_checkbox_transform_placeholder(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "toppings", @@ -390,18 +446,22 @@ def test_checkbox_transform_placeholder(mock_renderer): } ] + answer_store = AnswerStore( + [ + {"answer_id": "checkbox-answer", "value": ["Ham", "Cheese"]}, + ] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [ - {"answer_id": "checkbox-answer", "value": ["Ham", "Cheese"]}, - ] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -409,7 +469,7 @@ def test_checkbox_transform_placeholder(mock_renderer): assert placeholders["toppings"] == "Ham, Cheese" -def test_mixed_transform_placeholder(mock_renderer): +def test_mixed_transform_placeholder(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "age", @@ -431,23 +491,28 @@ def test_mixed_transform_placeholder(mock_renderer): } ] + answer_store = AnswerStore( + [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] + ) + metadata = get_metadata({"second-date": "2019-02-02"}) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] - ), + answer_store=answer_store, list_store=ListStore(), - metadata=get_metadata({"second-date": "2019-02-02"}), + metadata=metadata, response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["age"] == "20 years" -def test_mixed_transform_placeholder_value(mock_renderer): +def test_mixed_transform_placeholder_value(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "age", @@ -466,23 +531,27 @@ def test_mixed_transform_placeholder_value(mock_renderer): } ] + answer_store = AnswerStore( + [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [{"answer_id": "date-of-birth-answer", "value": "1999-01-01"}] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["age"] == "20 years" -def test_list_source_count(mock_renderer): +def test_list_source_count(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "number_of_people", @@ -500,15 +569,17 @@ def test_list_source_count(mock_renderer): list_store=list_store, metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["number_of_people"] == 2 -def test_list_source_count_in_transform(mock_renderer): +def test_list_source_count_in_transform(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "number_of_people", @@ -537,15 +608,17 @@ def test_list_source_count_in_transform(mock_renderer): list_store=list_store, metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["number_of_people"] == 2 -def test_chain_transform_placeholder(mock_renderer): +def test_chain_transform_placeholder(mock_renderer, mock_schema, mock_location): placeholder_list = [ { "placeholder": "persons_name", @@ -568,26 +641,32 @@ def test_chain_transform_placeholder(mock_renderer): } ] + answer_store = AnswerStore( + [ + {"answer_id": "first-name", "value": "Joe"}, + {"answer_id": "last-name", "value": "Bloggs"}, + ] + ) + parser = PlaceholderParser( language="en", - answer_store=AnswerStore( - [ - {"answer_id": "first-name", "value": "Joe"}, - {"answer_id": "last-name", "value": "Bloggs"}, - ] - ), + answer_store=answer_store, list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) assert placeholders["persons_name"] == "Joe Bloggs’" -def test_placeholder_resolves_answer_value_based_on_first_item_in_list(mock_renderer): +def test_placeholder_resolves_answer_value_based_on_first_item_in_list( + mock_renderer, mock_schema, mock_location +): placeholder_list = [ { "placeholder": "answer", @@ -621,8 +700,10 @@ def test_placeholder_resolves_answer_value_based_on_first_item_in_list(mock_rend list_store=list_store, metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -630,7 +711,7 @@ def test_placeholder_resolves_answer_value_based_on_first_item_in_list(mock_rend def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( - mock_renderer, + mock_renderer, mock_schema, mock_location ): placeholder_list = [ { @@ -651,8 +732,10 @@ def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( list_store=list_store, metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -660,7 +743,9 @@ def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( assert str(placeholders["first_person_list_item_id"]) == list_store["people"].first -def test_placeholder_resolves_same_name_items(mock_renderer): +def test_placeholder_resolves_same_name_items( + mock_renderer, mock_schema, mock_location +): list_store = ListStore( [ { @@ -687,9 +772,11 @@ def test_placeholder_resolves_same_name_items(mock_renderer): list_store=list_store, metadata=get_metadata(), response_metadata={}, - schema=QuestionnaireSchema({}), + schema=mock_schema, renderer=mock_renderer, list_item_id="abc123", + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_list) @@ -697,7 +784,9 @@ def test_placeholder_resolves_same_name_items(mock_renderer): assert placeholders["answer"] == ["abc123", "fgh789"] -def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer): +def test_placeholder_resolves_name_is_duplicate_chain( + mock_schema, mock_renderer, mock_location +): list_store = ListStore( [ { @@ -792,6 +881,8 @@ def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer schema=mock_schema, list_item_id="abc123", renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_transforms) @@ -807,6 +898,8 @@ def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer schema=mock_schema, list_item_id="cde456", renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_transforms) @@ -814,7 +907,9 @@ def test_placeholder_resolves_name_is_duplicate_chain(mock_schema, mock_renderer assert placeholders["persons_name"] == "Marie Smith" -def test_placeholder_resolves_list_has_items_chain(mock_schema, mock_renderer): +def test_placeholder_resolves_list_has_items_chain( + mock_schema, mock_renderer, mock_location +): list_store = ListStore( [ { @@ -905,6 +1000,8 @@ def test_placeholder_resolves_list_has_items_chain(mock_schema, mock_renderer): schema=mock_schema, list_item_id="abc123", renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_transforms) @@ -920,6 +1017,8 @@ def test_placeholder_resolves_list_has_items_chain(mock_schema, mock_renderer): schema=mock_schema, list_item_id="cde456", renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, ) placeholders = parser(placeholder_transforms) @@ -941,6 +1040,9 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render ], } ] + + location = Location(section_id="default-section") + parser = PlaceholderParser( language="en", answer_store=AnswerStore(), @@ -949,7 +1051,88 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render response_metadata={}, schema=default_placeholder_value_schema, renderer=mock_renderer, + progress_store=ProgressStore(), + location=location, ) placeholders = parser(placeholder_list) assert placeholders["answer_employee"] == "0" + + +def test_placeholder_parser_calculated_summary_dependencies_cache( + mocker, mock_renderer +): + schema = load_schema_from_name("test_calculated_summary") + + path_finder = mocker.patch("app.questionnaire.path_finder.PathFinder.routing_path") + + placeholder_list_1 = [ + { + "placeholder": "percentage-total-playback", + "value": { + "source": "calculated_summary", + "identifier": "percentage-total-playback", + }, + }, + ] + + placeholder_list_2 = [ + { + "placeholder": "unit-total-playback", + "value": { + "source": "calculated_summary", + "identifier": "unit-total-playback", + }, + }, + ] + + progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": [ + "second-number-answer-unit-total", + "third-and-a-half-number-answer-unit-total", + "unit-total-playback", + "fifth-percent-answer", + "sixth-percent-answer", + "percentage-total-playback", + ], + "status": "COMPLETED", + }, + ] + ) + + answer_store = AnswerStore( + [ + {"answer_id": "second-number-answer-unit-total", "value": 1}, + {"answer_id": "third-and-a-half-number-answer-unit-total", "value": 10}, + {"answer_id": "fifth-percent-answer", "value": 2}, + {"answer_id": "sixth-percent-answer", "value": 20}, + ] + ) + + location = Location( + section_id="default-section", + block_id="calculated-summary-total-confirmation", + ) + + placeholder_parser = PlaceholderParser( + language="en", + answer_store=answer_store, + list_store=ListStore(), + metadata=get_metadata(), + response_metadata={}, + schema=schema, + renderer=mock_renderer, + progress_store=progress_store, + location=location, + ) + + placeholder_1 = placeholder_parser(placeholder_list=placeholder_list_1) + assert placeholder_1["percentage-total-playback"] == 22 + assert path_finder.called == 1 + + placeholder_2 = placeholder_parser(placeholder_list=placeholder_list_2) + assert placeholder_2["unit-total-playback"] == 11 + assert path_finder.called == 1 diff --git a/tests/app/questionnaire/test_placeholder_renderer.py b/tests/app/questionnaire/test_placeholder_renderer.py index 1fc86caaed..755ac799b8 100644 --- a/tests/app/questionnaire/test_placeholder_renderer.py +++ b/tests/app/questionnaire/test_placeholder_renderer.py @@ -1,7 +1,11 @@ import pytest +from mock import MagicMock +from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.questionnaire import Location +from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.placeholder_renderer import PlaceholderRenderer @@ -9,7 +13,9 @@ def test_correct_pointers(placholder_transform_pointers): assert placholder_transform_pointers[0] == "/answers/0/options/0/label" -def test_renders_pointer(placholder_transform_question_json, mocker): +def test_renders_pointer( + placholder_transform_question_json, mock_schema, mock_renderer, mock_location +): mock_transform = { "transform": "calculate_date_difference", "arguments": { @@ -33,17 +39,29 @@ def test_renders_pointer(placholder_transform_question_json, mocker): {"answer_id": "date-of-birth-answer", "value": "1991-01-01"}, ] ) - renderer = get_placeholder_render(mocker=mocker, answer_store=answer_store) + + renderer = get_placeholder_render(answer_store=answer_store) rendered = renderer.render_pointer( - placholder_transform_question_json, - "/answers/0/options/0/label", + dict_to_render=placholder_transform_question_json, + pointer_to_render="/answers/0/options/0/label", list_item_id=None, + placeholder_parser=PlaceholderParser( + language="en", + answer_store=answer_store, + list_store=ListStore(), + metadata=None, + response_metadata={}, + schema=mock_schema, + location=mock_location, + renderer=mock_renderer, + progress_store=ProgressStore(), + ), ) assert rendered == "Hal Abelson’s age is 28 years. Is this correct?" -def test_renders_json(placholder_transform_question_json, mocker): +def test_renders_json(placholder_transform_question_json): mock_transform = { "transform": "calculate_date_difference", "arguments": { @@ -67,14 +85,14 @@ def test_renders_json(placholder_transform_question_json, mocker): ] ) - renderer = get_placeholder_render(mocker=mocker, answer_store=answer_store) - rendered_schema = renderer.render(json_to_render, list_item_id=None) + renderer = get_placeholder_render(answer_store=answer_store) + rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label = rendered_schema["answers"][0]["options"][0]["label"] assert rendered_label == "Alfred Aho’s age is 33 years. Is this correct?" -def test_renders_json_uses_language(placholder_transform_question_json, mocker): +def test_renders_json_uses_language(placholder_transform_question_json): mock_transform = { "transform": "calculate_date_difference", "arguments": { @@ -97,34 +115,62 @@ def test_renders_json_uses_language(placholder_transform_question_json, mocker): {"answer_id": "date-of-birth-answer", "value": "1986-01-01"}, ] ) - renderer = get_placeholder_render( - mocker=mocker, language="cy", answer_store=answer_store - ) - rendered_schema = renderer.render(json_to_render, list_item_id=None) + + renderer = get_placeholder_render(language="cy", answer_store=answer_store) + rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label = rendered_schema["answers"][0]["options"][0]["label"] assert rendered_label == "Alfred Aho age is 33 years. Is this correct?" -def test_errors_on_invalid_pointer(placholder_transform_question_json, mocker): - renderer = get_placeholder_render(mocker=mocker) +def test_errors_on_invalid_pointer(placholder_transform_question_json, mock_schema): + renderer = get_placeholder_render() with pytest.raises(ValueError): renderer.render_pointer( - placholder_transform_question_json, "/title", list_item_id=None + dict_to_render=placholder_transform_question_json, + pointer_to_render="/title", + list_item_id=None, + placeholder_parser=PlaceholderParser( + language="en", + answer_store=AnswerStore(), + list_store=ListStore(), + metadata=None, + response_metadata={}, + schema=mock_schema, + location=None, + renderer=renderer, + progress_store=ProgressStore(), + ), ) -def test_errors_on_invalid_json(mocker): - renderer = get_placeholder_render(mocker=mocker) +def test_errors_on_invalid_json(mock_schema): + renderer = get_placeholder_render() with pytest.raises(ValueError): dict_to_render = {"invalid": {"no": "placeholders", "in": "this"}} - renderer.render_pointer(dict_to_render, "/invalid", list_item_id=None) + renderer.render_pointer( + dict_to_render=dict_to_render, + pointer_to_render="/invalid", + list_item_id=None, + placeholder_parser=PlaceholderParser( + language="en", + answer_store=AnswerStore(), + list_store=ListStore(), + metadata=None, + response_metadata={}, + schema=mock_schema, + location=None, + renderer=renderer, + progress_store=ProgressStore(), + ), + ) -def test_renders_text_plural_from_answers(mocker): +def test_renders_text_plural_from_answers(): answer_store = AnswerStore([{"answer_id": "number-of-people", "value": 1}]) - renderer = get_placeholder_render(mocker=mocker, answer_store=answer_store) + + renderer = get_placeholder_render(answer_store=answer_store) rendered_text = renderer.render_placeholder( { "text_plural": { @@ -147,8 +193,8 @@ def test_renders_text_plural_from_answers(mocker): assert rendered_text == "Yes, 1 person lives here" -def test_renders_text_plural_from_list(mocker): - renderer = get_placeholder_render(mocker=mocker) +def test_renders_text_plural_from_list(): + renderer = get_placeholder_render() rendered_text = renderer.render_placeholder( { @@ -176,9 +222,9 @@ def test_renders_text_plural_from_list(mocker): assert rendered_text == "Yes, 0 people live here" -def test_renders_text_plural_from_metadata(mocker): +def test_renders_text_plural_from_metadata(): metadata = {"some_value": 100} - renderer = get_placeholder_render(mocker=mocker, metadata=metadata) + renderer = get_placeholder_render(metadata=metadata) rendered_text = renderer.render_placeholder( { @@ -209,7 +255,6 @@ def get_placeholder_render( list_store=ListStore(), metadata=None, response_metadata=None, - mocker, ): renderer = PlaceholderRenderer( language=language, @@ -217,6 +262,8 @@ def get_placeholder_render( list_store=list_store, metadata=metadata or {}, response_metadata=response_metadata or {}, - schema=mocker.Mock(), + schema=MagicMock(), + progress_store=ProgressStore(), + location=Location(section_id="default-section"), ) return renderer diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 6dbcf02a8f..98b923bc09 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -494,13 +494,7 @@ def test_answer_dependencies_for_calculated_summary( "first-number-answer": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-skipped-fourth", - for_list=None, - answer_id=None, - ), - AnswerDependent( - section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="currency-total-playback", for_list=None, answer_id=None, ), @@ -514,13 +508,7 @@ def test_answer_dependencies_for_calculated_summary( "second-number-answer": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-skipped-fourth", - for_list=None, - answer_id=None, - ), - AnswerDependent( - section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="currency-total-playback", for_list=None, answer_id=None, ), @@ -534,33 +522,35 @@ def test_answer_dependencies_for_calculated_summary( "second-number-answer-also-in-total": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-skipped-fourth", + block_id="currency-total-playback", for_list=None, answer_id=None, ), AnswerDependent( section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="set-min-max-block", for_list=None, answer_id=None, ), + }, + "third-number-answer": { AnswerDependent( section_id="default-section", - block_id="set-min-max-block", + block_id="currency-total-playback", for_list=None, answer_id=None, ), - }, - "third-number-answer": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-skipped-fourth", + block_id="set-min-max-block", for_list=None, answer_id=None, ), + }, + "fourth-number-answer": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="currency-total-playback", for_list=None, answer_id=None, ), @@ -571,21 +561,19 @@ def test_answer_dependencies_for_calculated_summary( answer_id=None, ), }, - "fourth-number-answer": { + "fourth-and-a-half-number-answer-also-in-total": { AnswerDependent( section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="currency-total-playback", for_list=None, answer_id=None, - ) - }, - "fourth-and-a-half-number-answer-also-in-total": { + ), AnswerDependent( section_id="default-section", - block_id="currency-total-playback-with-fourth", + block_id="set-min-max-block", for_list=None, answer_id=None, - ) + ), }, "second-number-answer-unit-total": { AnswerDependent( @@ -770,6 +758,7 @@ def test_when_rules_section_dependencies_calculated_summary( "eggs-answer": {"dependent-enabled-section", "dependent-question-section"}, "bread-answer": {"dependent-enabled-section", "dependent-question-section"}, "cheese-answer": {"dependent-enabled-section", "dependent-question-section"}, + "butter-answer": {"dependent-enabled-section", "dependent-question-section"}, } == schema.when_rules_section_dependencies_by_answer @@ -783,6 +772,7 @@ def test_when_rules_section_dependencies_new_calculated_summary( "eggs-answer": {"dependent-enabled-section", "dependent-question-section"}, "bread-answer": {"dependent-enabled-section", "dependent-question-section"}, "cheese-answer": {"dependent-enabled-section", "dependent-question-section"}, + "butter-answer": {"dependent-enabled-section", "dependent-question-section"}, } == schema.when_rules_section_dependencies_by_answer diff --git a/tests/app/questionnaire/test_schema_utils.py b/tests/app/questionnaire/test_schema_utils.py index b1d43af8d0..4d27d1fa8b 100644 --- a/tests/app/questionnaire/test_schema_utils.py +++ b/tests/app/questionnaire/test_schema_utils.py @@ -1,3 +1,4 @@ +from app.data_models import ProgressStore from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.questionnaire.location import Location @@ -33,6 +34,7 @@ def test_transform_variants_with_question_variants(question_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) compare_transformed_block(block, transformed_block, "Question 1, No") @@ -47,6 +49,7 @@ def test_transform_variants_with_question_variants(question_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) compare_transformed_block(block, transformed_block, "Question 1, Yes") @@ -70,6 +73,7 @@ def test_transform_variants_with_content(content_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert transformed_block != block @@ -94,6 +98,7 @@ def test_transform_variants_with_no_variants(question_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert transformed_block == block @@ -117,6 +122,7 @@ def test_transform_variants_list_collector(list_collector_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) compare_transformed_block( @@ -139,6 +145,7 @@ def test_transform_variants_list_collector(list_collector_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) compare_transformed_block( @@ -170,6 +177,7 @@ def test_choose_content_to_display(content_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert content_to_display[0]["title"] == "You are over 16" @@ -184,6 +192,7 @@ def test_choose_content_to_display(content_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert content_to_display[0]["title"] == "You are ageless" @@ -207,6 +216,7 @@ def test_choose_question_to_display(question_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert question_to_display["title"] == "Question 1, Yes" @@ -221,6 +231,7 @@ def test_choose_question_to_display(question_variant_schema): answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), + progress_store=ProgressStore(), ) assert question_to_display["title"] == "Question 1, No" diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 10563f4ed8..38ff197d53 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -4,7 +4,7 @@ from mock import Mock from app.authentication.auth_payload_version import AuthPayloadVersion -from app.data_models import AnswerStore, ListStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import Answer, AnswerDict from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire import Location, QuestionnaireSchema @@ -65,6 +65,7 @@ def get_value_source_resolver( routing_path_block_ids=routing_path_block_ids, use_default_answer=use_default_answer, escape_answer_values=escape_answer_values, + progress_store=ProgressStore(), ) diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 9a5e12037c..6c14a3a4a2 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -59,6 +59,7 @@ def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): list_store=questionnaire_store.list_store, schema=QuestionnaireSchema(questionnaire), full_routing_path=full_routing_path, + progress_store=questionnaire_store.progress_store, ) assert answer_object["002"] == "2016-03-30" assert len(answer_object) == 1 @@ -97,6 +98,7 @@ def test_answer_with_zero(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert data_payload["003"] == "0" @@ -135,6 +137,7 @@ def test_answer_with_float(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Check the converter correctly @@ -176,6 +179,7 @@ def test_answer_with_string(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Check the converter correctly @@ -217,6 +221,7 @@ def test_answer_without_qcode(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert not data_payload @@ -283,6 +288,7 @@ def test_converter_checkboxes_with_q_codes(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -361,6 +367,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value( full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -437,6 +444,7 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(v full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -508,6 +516,7 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -561,6 +570,7 @@ def test_converter_q_codes_for_empty_strings(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -628,6 +638,7 @@ def test_radio_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -673,6 +684,7 @@ def test_number_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -717,6 +729,7 @@ def test_percentage_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -761,6 +774,7 @@ def test_textarea_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -805,6 +819,7 @@ def test_currency_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -860,6 +875,7 @@ def test_dropdown_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -911,6 +927,7 @@ def test_date_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -956,6 +973,7 @@ def test_unit_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 1f4b2ad4ca..23cfcc7a4d 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -89,6 +89,7 @@ def test_convert_answers_v2_to_payload_0_0_3(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -145,6 +146,7 @@ def test_convert_payload_0_0_3_multiple_answers(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -196,6 +198,7 @@ def test_radio_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -237,6 +240,7 @@ def test_number_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -278,6 +282,7 @@ def test_percentage_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -321,6 +326,7 @@ def test_textarea_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -362,6 +368,7 @@ def test_currency_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -413,6 +420,7 @@ def test_dropdown_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -460,6 +468,7 @@ def test_date_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -507,6 +516,7 @@ def test_month_year_date_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -549,6 +559,7 @@ def test_unit_answer(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) assert len(data_payload["answers"]) == 1 @@ -604,6 +615,7 @@ def test_primary_person_list_item_conversion(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data_dict = json_loads(json_dumps(output["answers"])) @@ -658,6 +670,7 @@ def test_list_item_conversion(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) del answer_objects[-1] @@ -707,6 +720,7 @@ def test_list_item_conversion_empty_list(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Answers not on the routing path @@ -752,6 +766,7 @@ def test_default_answers_not_present_when_not_answered(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -809,6 +824,7 @@ def test_list_structure_in_payload_is_as_expected(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -863,6 +879,7 @@ def test_primary_person_not_in_payload_when_not_answered(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -939,6 +956,7 @@ def test_relationships_in_payload(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1015,6 +1033,7 @@ def test_no_relationships_in_payload(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1108,6 +1127,7 @@ def test_unrelated_block_answers_in_payload(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1218,6 +1238,7 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1318,6 +1339,7 @@ def test_relationship_answers_not_on_path_in_payload(version): routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1382,6 +1404,7 @@ def test_answers_codes_only_present_for_answered_questions(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -1419,6 +1442,7 @@ def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(ve full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then @@ -1454,6 +1478,7 @@ def test_no_answers_codes_in_payload_when_no_questions_answered(version): full_routing_path, questionnaire_store.metadata, questionnaire_store.response_metadata, + questionnaire_store.progress_store, ) # Then diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 025cfe3cf1..35bfffb073 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -349,6 +349,24 @@ def test_calculated_summary_answers(): return AnswerStore(answers) +@pytest.fixture +def test_calculated_summary_answers_skipped_fourth(): + answers = [ + {"value": 1, "answer_id": "first-number-answer"}, + {"value": 2, "answer_id": "second-number-answer"}, + {"value": 3, "answer_id": "second-number-answer-unit-total"}, + {"value": 4, "answer_id": "second-number-answer-also-in-total"}, + {"value": 5, "answer_id": "third-number-answer"}, + {"value": 6, "answer_id": "third-and-a-half-number-answer-unit-total"}, + {"value": "Yes", "answer_id": "skip-fourth-block-answer"}, + {"value": 9, "answer_id": "fifth-percent-answer"}, + {"value": 10, "answer_id": "fifth-number-answer"}, + {"value": 11, "answer_id": "sixth-percent-answer"}, + {"value": 12, "answer_id": "sixth-number-answer"}, + ] + return AnswerStore(answers) + + @pytest.fixture def test_section_summary_schema(): return load_schema_from_name("test_section_summary") diff --git a/tests/app/views/contexts/summary/test_block.py b/tests/app/views/contexts/summary/test_block.py index a955e0fbab..d290c8ac67 100644 --- a/tests/app/views/contexts/summary/test_block.py +++ b/tests/app/views/contexts/summary/test_block.py @@ -1,3 +1,4 @@ +from app.data_models import ProgressStore from app.questionnaire.location import Location from app.views.contexts.summary.block import Block @@ -29,6 +30,7 @@ def test_create_block(mocker): schema=mocker.MagicMock(), location=location, return_to="final-summary", + progress_store=ProgressStore(), ) # Then diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index 8f21774458..cf89714c80 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -2,7 +2,7 @@ import pytest from mock import MagicMock -from app.data_models import Answer +from app.data_models import Answer, ProgressStore from app.data_models.answer_store import AnswerStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -18,6 +18,7 @@ def get_rule_evaluator(answer_store, list_store, schema, response_metadata=None) metadata={}, response_metadata=response_metadata or {}, location=None, + progress_store=ProgressStore(), ) @@ -32,6 +33,7 @@ def get_value_source_resolver(answer_store, list_store, schema, response_metadat list_item_id=None, routing_path_block_ids=None, use_default_answer=True, + progress_store=ProgressStore(), ) diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 0a5d0597d9..58514c64b1 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -1,6 +1,7 @@ import pytest -from app.questionnaire.location import Location +from app.questionnaire import Location +from app.questionnaire.routing_path import RoutingPath from app.views.contexts.calculated_summary_context import CalculatedSummaryContext from tests.app.views.contexts import assert_summary_context @@ -8,25 +9,27 @@ # pylint: disable=too-many-locals @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "block_id, locale, language, title, value, total_blocks, return_to_answer_id", + "block_id, locale, language, title, value, total_blocks, return_to_answer_id, skip_fourth", ( ( - "currency-total-playback-with-fourth", + "currency-total-playback", "en_GB", "en", - "We calculate the total of currency values entered to be £27.00. Is this correct? (With Fourth)", + "We calculate the total of currency values entered to be £27.00. Is this correct?", "£27.00", 5, "first-number-answer", + False, ), ( - "currency-total-playback-skipped-fourth", + "currency-total-playback", "en_GB", "en", - "We calculate the total of currency values entered to be £12.00. Is this correct? (Skipped Fourth)", + "We calculate the total of currency values entered to be £12.00. Is this correct?", "£12.00", 3, "first-number-answer", + True, ), ( "unit-total-playback", @@ -36,6 +39,7 @@ "9 cm", 2, "second-number-answer-unit-total", + False, ), ( "percentage-total-playback", @@ -45,6 +49,7 @@ "20%", 2, "fifth-percent-answer", + False, ), ( "number-total-playback", @@ -54,6 +59,7 @@ "22", 2, "fifth-number-answer", + False, ), ), ) @@ -66,31 +72,57 @@ def test_build_view_context_for_currency_calculated_summary( total_blocks, test_calculated_summary_schema, test_calculated_summary_answers, + test_calculated_summary_answers_skipped_fourth, list_store, progress_store, mocker, return_to_answer_id, + skip_fourth, ): mocker.patch( "app.jinja_filters.flask_babel.get_locale", mocker.MagicMock(return_value=locale), ) - current_location = Location(section_id="default-section", block_id=block_id) + block_ids = ( + [ + "first-number-block", + "second-number-block", + "third-number-block", + "third-and-a-half-number-block", + "skip-fourth-block", + "fifth-number-block", + "sixth-number-block", + ] + if skip_fourth + else [ + "first-number-block", + "second-number-block", + "third-number-block", + "third-and-a-half-number-block", + "skip-fourth-block", + "fourth-number-block", + "fourth-and-a-half-number-block", + "fifth-number-block", + "sixth-number-block", + ] + ) calculated_summary_context = CalculatedSummaryContext( - language, - test_calculated_summary_schema, - test_calculated_summary_answers, - list_store, - progress_store, - metadata={}, + language=language, + schema=test_calculated_summary_schema, + answer_store=test_calculated_summary_answers_skipped_fourth + if skip_fourth + else test_calculated_summary_answers, + list_store=list_store, + progress_store=progress_store, + metadata=None, response_metadata={}, + routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), + current_location=Location(section_id="default-section", block_id=block_id), ) - context = calculated_summary_context.build_view_context_for_calculated_summary( - current_location - ) + context = calculated_summary_context.build_view_context_for_calculated_summary() assert "summary" in context assert_summary_context(context) diff --git a/tests/app/views/contexts/test_preview_context.py b/tests/app/views/contexts/test_preview_context.py index 524ce75768..bf0b3d3413 100644 --- a/tests/app/views/contexts/test_preview_context.py +++ b/tests/app/views/contexts/test_preview_context.py @@ -142,7 +142,7 @@ def test_build_preview_context( ], "guidance": None, "id": "report-radio-second", - "title": "Are you sure you are able to report for the calendar month {start_date} to {end_date}?", + "title": "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", "type": "General", } }, diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 6542df3329..9d1d358236 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -150,6 +150,7 @@ def test_context_for_section_list_summary(people_answer_store): section_id="section", ), ) + context = summary_context() expected = { "summary": { @@ -956,6 +957,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): section_id="section", ), ) + context = summary_context() list_items = context["summary"]["custom_summary"][0]["list"]["list_items"] @@ -993,6 +995,7 @@ def test_primary_links_for_section_summary(people_answer_store): section_id="section", ), ) + context = summary_context() list_items = context["summary"]["custom_summary"][0]["list"]["list_items"] diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary.spec.js index 334a367c1d..5926b120d4 100644 --- a/tests/functional/spec/features/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary.spec.js @@ -7,4 +7,11 @@ describe("Feature: Calculated Summary", () => { describe("Given I have a Calculated Summary with the new format", () => { CalculatedSummaryTestCase.testCase("test_new_calculated_summary.json"); }); + + describe("Given I have a Calculated Summary", () => { + CalculatedSummaryTestCase.testCrossSectionDependencies("test_calculated_summary_cross_section_dependencies.json"); + }); + describe("Given I have a Calculated Summary with the new format", () => { + CalculatedSummaryTestCase.testCrossSectionDependencies("test_new_calculated_summary_cross_section_dependencies.json"); + }); }); diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary_test_case.js index 9b886404e2..fd088e7564 100644 --- a/tests/functional/spec/features/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary_test_case.js @@ -1,4 +1,4 @@ -import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page"; +import CurrencyTotalPlaybackPage from "../../generated_pages/calculated_summary/currency-total-playback.page"; import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page"; import NumberTotalPlaybackPage from "../../generated_pages/calculated_summary/number-total-playback.page"; import ThirdNumberBlockPage from "../../generated_pages/calculated_summary/third-number-block.page"; @@ -7,7 +7,6 @@ import FourthAndAHalfNumberBlockPage from "../../generated_pages/calculated_summ import SixthNumberBlockPage from "../../generated_pages/calculated_summary/sixth-number-block.page"; import FifthNumberBlockPage from "../../generated_pages/calculated_summary/fifth-number-block.page"; import SkipFourthBlockPage from "../../generated_pages/calculated_summary/skip-fourth-block.page"; -import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/calculated_summary/currency-total-playback-skipped-fourth.page"; import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page"; import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page"; import SetMinMaxBlockPage from "../../generated_pages/calculated_summary/set-min-max-block.page"; @@ -16,6 +15,18 @@ import ThirdAndAHalfNumberBlockPage from "../../generated_pages/calculated_summa import ThankYouPage from "../../base_pages/thank-you.page"; import FirstNumberBlockPage from "../../generated_pages/calculated_summary/first-number-block.page"; import SecondNumberBlockPage from "../../generated_pages/calculated_summary/second-number-block.page"; +import HubPage from "../../base_pages/hub.page"; +import SkipFirstNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/skip-first-block.page"; +import FirstNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/first-number-block.page"; +import FirstAndAHalfNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/first-and-a-half-number-block.page"; +import SecondNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/second-number-block.page"; +import CalculatedSummarySectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-1.page"; +import CalculatedSummarySectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-2.page"; +import ThirdNumberBlockPageSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/third-number-block.page"; +import SectionSummarySectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/questions-section-summary.page"; +import SectionSummarySectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; +import DependencyQuestionSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; +import MinMaxSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; class TestCase { testCase(schema) { @@ -53,7 +64,7 @@ class TestCase { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", async () => { @@ -62,29 +73,29 @@ class TestCase { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £20.71. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).to.contain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Second answer label also in currency total (optional)" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).to.contain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Fourth answer label also in total (optional)" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); // Answers not included in calculation should not be shown await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; @@ -94,50 +105,50 @@ class TestCase { }); it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( - "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer" ); }); it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.fourthNumberAnswerEdit()).click(); await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await $(FourthNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £30.71. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await $(FourthAndAHalfNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £28.37. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + await $(CurrencyTotalPlaybackPage.previous()).click(); await $(SixthNumberBlockPage.previous()).click(); await $(FifthNumberBlockPage.previous()).click(); await $(FourthAndAHalfNumberBlockPage.previous()).click(); @@ -151,18 +162,18 @@ class TestCase { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; - await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; + await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.36. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" ); @@ -257,11 +268,11 @@ class TestCase { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -283,11 +294,11 @@ class TestCase { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -310,11 +321,11 @@ class TestCase { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -327,11 +338,101 @@ class TestCase { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); + it("Given I am on a page with a placeholder containing a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the placeholder should display the updated value", async () => { + await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); + await $(SkipFourthBlockPage.no()).click(); + await $(SkipFourthBlockPage.submit()).click(); + await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); + await browser.url(CalculatedSummaryTotalConfirmation.url()); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryTotalConfirmation.pageName); + const content = await $("h1 + ul").getText(); + const textsToAssert = ["Total currency values: £25.92", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; + + textsToAssert.forEach((text) => expect(content).to.contain(text)); + await browser.url(SubmitPage.url()); + }); + + it("Given I am on a page with a dependent question based on a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the question should display the updated value", async () => { + await $(SubmitPage.setMinimumAnswerEdit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).to.contain( + "Set minimum and maximum values based on your calculated summary total of £25.92" + ); + await $(SetMinMaxBlockPage.submit()).click(); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £25.92"); + await $(SetMinMaxBlockPage.setMinimum()).setValue(30.0); + await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); + await $(SetMinMaxBlockPage.submit()).click(); + }); + it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { await $(SubmitPage.submit()).click(); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); } + + testCrossSectionDependencies(schema) { + before("Get to the question containing calculated summary values with cross section dependencies", async () => { + await browser.openQuestionnaire(schema); + await $(HubPage.submit()).click(); + await $(SkipFirstNumberBlockPageSectionOne.no()).click(); + await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); + await $(FirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); + await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); + await $(SecondNumberBlockPageSectionOne.submit()).click(); + await $(CalculatedSummarySectionOne.submit()).click(); + await $(SectionSummarySectionOne.submit()).click(); + await $(HubPage.submit()).click(); + await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.submit()).click(); + await $(CalculatedSummarySectionTwo.submit()).click(); + }); + + it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { + await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + "60 - calculated summary answer (previous section)" + ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + "40 - calculated summary answer (current section)" + ); + }); + + it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { + await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); + await $(DependencyQuestionSectionTwo.submit()).click(); + await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); + await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); + await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); + await $(MinMaxSectionTwo.submit()).click(); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); + await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); + await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); + await $(MinMaxSectionTwo.submit()).click(); + }); + + it("Given I remove answers from the path for a calculated summary in a previous section by changing an answer, When I return to the question with the calculated summary value source, Then the value displayed should be correct", async () => { + await $(SectionSummarySectionTwo.submit()).click(); + await $(HubPage.summaryRowLink("questions-section")).click(); + await $(SectionSummarySectionOne.skipFirstBlockAnswerEdit()).click(); + await $(SkipFirstNumberBlockPageSectionOne.yes()).click(); + await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await $(SectionSummarySectionOne.submit()).click(); + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); + await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + "30 - calculated summary answer (previous section)" + ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + "40 - calculated summary answer (current section)" + ); + }); + } } export const CalculatedSummaryTestCase = new TestCase(); diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js index d2e9e6d006..8391e5602d 100644 --- a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js @@ -7,9 +7,8 @@ import FourthNumberBlockPage from "../../generated_pages/new_calculated_summary_ import FourthAndAHalfNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fourth-and-a-half-number-block.page.js"; import FifthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fifth-number-block.page.js"; import SixthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/sixth-number-block.page.js"; -import CurrencyTotalPlaybackPageWithFourth from "../../generated_pages/calculated_summary/currency-total-playback-with-fourth.page.js"; +import CurrencyTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/currency-total-playback.page.js"; import SetMinMaxBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/set-min-max-block.page.js"; -import CurrencyTotalPlaybackPageSkippedFourth from "../../generated_pages/new_calculated_summary_repeating_section/currency-total-playback-skipped-fourth.page.js"; import UnitTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/unit-total-playback.page.js"; import PercentageTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/percentage-total-playback.page.js"; import NumberTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/number-total-playback.page.js"; @@ -23,6 +22,17 @@ import PrimaryPersonListCollectorPage from "../../generated_pages/new_calculated import PrimaryPersonListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector-add.page.js"; import ListCollectorPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector.page"; import ListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector-add.page"; +import SkipFirstNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/skip-first-block.page"; +import FirstNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-number-block.page"; +import FirstAndAHalfNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-and-a-half-number-block.page"; +import SecondNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/second-number-block.page"; +import CalculatedSummarySectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-1.page"; +import CalculatedSummarySectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-2.page"; +import ThirdNumberBlockPageSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/third-number-block.page"; +import SectionSummarySectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/questions-section-summary.page"; +import SectionSummarySectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; +import DependencyQuestionSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; +import MinMaxSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { @@ -42,7 +52,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); + await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", async () => { @@ -51,29 +61,29 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £20.71. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswer()).getText()).to.contain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswer()).getText()).to.contain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).to.contain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Second answer label also in currency total (optional)" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswer()).getText()).to.contain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer()).getText()).to.contain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).to.contain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( "Fourth answer label also in total (optional)" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); // Answers not included in calculation should not be shown await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; @@ -83,50 +93,50 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { - await expect(await $(CurrencyTotalPlaybackPageWithFourth.firstNumberAnswerEdit()).getAttribute("href")).to.contain( - "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback-with-fourth#first-number-answer" + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer" ); }); it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.thirdNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain("currency-total-playback-with-fourth/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswerEdit()).click(); + await $(CurrencyTotalPlaybackPage.fourthNumberAnswerEdit()).click(); await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await $(FourthNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £30.71. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); + await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await $(FourthAndAHalfNumberBlockPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageWithFourth.pageName); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £28.37. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { - await $(CurrencyTotalPlaybackPageWithFourth.previous()).click(); + await $(CurrencyTotalPlaybackPage.previous()).click(); await $(SixthNumberBlockPage.previous()).click(); await $(FifthNumberBlockPage.previous()).click(); await $(FourthAndAHalfNumberBlockPage.previous()).click(); @@ -140,18 +150,18 @@ describe("Feature: Calculated Summary Repeating Section", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthNumberAnswer())).to.be.empty; - await expect(await $$(CurrencyTotalPlaybackPageWithFourth.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; + await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.36. Is this correct?" ); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(CurrencyTotalPlaybackPageWithFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" ); @@ -223,13 +233,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SecondCurrencyTotalPlaybackPage.submit()).click(); const content = $("h1 + ul").getText(); - const textsToAssert = [ - "Total currency values (if Q4 not skipped): £28.37", - "Total currency values (if Q4 skipped)): £9.36", - "Total unit values: 1,467", - "Total percentage values: 79", - "Total number values: 124.58", - ]; + const textsToAssert = ["Total currency values: £9.36", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; textsToAssert.forEach(async (text) => await expect(content).to.contain(text)); }); @@ -264,11 +268,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -292,11 +296,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -321,11 +325,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FifthNumberBlockPage.submit()).click(); await $(SixthNumberBlockPage.submit()).click(); - await expect(await $(CurrencyTotalPlaybackPageSkippedFourth.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?" ); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); @@ -347,7 +351,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); }); - describe("Given I have a Calculated Summary in a Repeating Section", () => { + describe("Given I have a Calculated Summary in a Repeating Section", async () => { before("Get to Final Summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); await $(HubPage.submit()).click(); @@ -397,14 +401,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(HubPage.pageName); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPageSkippedFourth.pageName); - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(BreakdownPage.submit()).click(); - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await $(SetMinMaxBlockPage.submit()).click(); @@ -414,6 +411,78 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); }); }); + + describe("Given I have a Calculated Summary in a Repeating Section with a Dependency based on a calculated summary in another section", () => { + before("Get to the Dependent question page", async () => { + await browser.openQuestionnaire("test_new_calculated_summary_cross_section_dependencies_repeating.json"); + await $(HubPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); + + await $(SkipFirstNumberBlockPageSectionOne.no()).click(); + await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); + await $(FirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); + await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); + await $(SecondNumberBlockPageSectionOne.submit()).click(); + await $(CalculatedSummarySectionOne.submit()).click(); + await $(SectionSummarySectionOne.submit()).click(); + await $(HubPage.submit()).click(); + await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.submit()).click(); + await $(CalculatedSummarySectionTwo.submit()).click(); + }); + + it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { + await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + "60 - calculated summary answer (previous section)" + ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + "40 - calculated summary answer (current section)" + ); + }); + + it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { + await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); + await $(DependencyQuestionSectionTwo.submit()).click(); + await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); + await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); + await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); + await $(MinMaxSectionTwo.submit()).click(); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); + await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); + await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); + await $(MinMaxSectionTwo.submit()).click(); + }); + + it("Given I remove answers from the path for a calculated summary in a previous section by changing an answer, When I return to the question with the calculated summary value source, Then the value displayed should be correct", async () => { + await $(SectionSummarySectionTwo.submit()).click(); + await $(HubPage.summaryRowLink("questions-section")).click(); + await $(SectionSummarySectionOne.skipFirstBlockAnswerEdit()).click(); + await $(SkipFirstNumberBlockPageSectionOne.yes()).click(); + await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await $(SectionSummarySectionOne.submit()).click(); + await $(HubPage.summaryRowLink("calculated-summary-section-1")).click(); + await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); + await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + "30 - calculated summary answer (previous section)" + ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + "40 - calculated summary answer (current section)" + ); + }); + }); }); const getToFirstCalculatedSummary = async () => { @@ -448,7 +517,7 @@ const getToFirstCalculatedSummary = async () => { }; const getToSubmitPage = async () => { - await $(CurrencyTotalPlaybackPageSkippedFourth.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); await $(UnitTotalPlaybackPage.submit()).click(); await $(PercentageTotalPlaybackPage.submit()).click(); await $(NumberTotalPlaybackPage.submit()).click(); diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index a93064b972..e62d6610e7 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -67,7 +67,9 @@ describe("Introduction preview questions", () => { await $(IntroductionPageLinear.previewQuestions()).click(); expect(await browser.getUrl()).to.contain("questionnaire/preview"); expect(await $(previewSectionTitle).getText()).to.equal("Main section"); - expect(await $$(previewQuestion)[2].$("h3").getText()).to.equal("Are you sure you are able to report for the calendar month {start_date} to {end_date}?"); + expect(await $$(previewQuestion)[2].$("h3").getText()).to.equal( + "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?" + ); }); it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", async () => { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 9c27308dc9..bd4926efe6 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -5,6 +5,8 @@ import FirstQuestionBlockPage from "../generated_pages/routing_and_skipping_sect import FruitPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; import SecondQuestionBlockPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; import VegetablesPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; +import SkipQuestionPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/skip-butter-block.page"; +import ButterPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/butter-block.page"; import HubPage from "../base_pages/hub.page"; @@ -27,6 +29,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(25); await $(FirstQuestionBlockPage.cheese()).setValue(25); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -42,6 +46,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(1); await $(FirstQuestionBlockPage.cheese()).setValue(1); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -56,6 +62,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(5); await $(FirstQuestionBlockPage.cheese()).setValue(5); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -70,6 +78,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(30); await $(FirstQuestionBlockPage.cheese()).setValue(30); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -86,6 +96,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(20); await $(FirstQuestionBlockPage.cheese()).setValue(20); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -102,6 +114,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.bread()).setValue(20); await $(FirstQuestionBlockPage.cheese()).setValue(20); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); @@ -115,9 +129,70 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); await $(FirstQuestionBlockPage.milk()).setValue(100); await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); await $(CurrencyTotalPlaybackPage.submit()).click(); await $(CalculatedSummarySectionSummaryPage.submit()).click(); await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); }); + + it("When the calculated summary total is less than £100 but additional answers on the path are opened up as a result of editing an answer, Then the dependent section should be enabled", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(10); + await $(FirstQuestionBlockPage.eggs()).setValue(10); + await $(FirstQuestionBlockPage.bread()).setValue(10); + await $(FirstQuestionBlockPage.cheese()).setValue(10); + await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); + await $(SkipQuestionPage.no()).click(); + await $(SkipQuestionPage.submit()).click(); + await $(ButterPage.butter()).setValue(60); + await $(ButterPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + }); + + it("When the calculated summary total is equal to £100 but answers on the path are remove as a result of an answer edit, Then the dependent section should be enabled", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(FirstQuestionBlockPage.milk()).setValue(10); + await $(FirstQuestionBlockPage.eggs()).setValue(10); + await $(FirstQuestionBlockPage.bread()).setValue(10); + await $(FirstQuestionBlockPage.cheese()).setValue(10); + await $(FirstQuestionBlockPage.submit()).click(); + await $(SkipQuestionPage.no()).click(); + await $(SkipQuestionPage.submit()).click(); + await $(ButterPage.butter()).setValue(60); + await $(ButterPage.submit()).click(); + await $(CurrencyTotalPlaybackPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); + await $(SkipQuestionPage.yes()).click(); + await $(SkipQuestionPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + }); }); }); diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index b357c0741d..c0d4ac7155 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -48,7 +48,6 @@ def _complete_calculated_summary_path_no_skip(self): def test_calculated_summary(self): self.launchSurvey("test_calculated_summary") self._complete_calculated_summary_path_with_skip() - self.assertInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £80.00" ) @@ -56,7 +55,6 @@ def test_calculated_summary(self): def test_calculated_summary_no_skip(self): self.launchSurvey("test_calculated_summary") self._complete_calculated_summary_path_no_skip() - self.assertNotInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £180.00" ) @@ -64,7 +62,6 @@ def test_calculated_summary_no_skip(self): def test_new_calculated_summary(self): self.launchSurvey("test_new_calculated_summary") self._complete_calculated_summary_path_with_skip() - self.assertInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £80.00" ) @@ -72,7 +69,6 @@ def test_new_calculated_summary(self): def test_new_calculated_summary_no_skip(self): self.launchSurvey("test_new_calculated_summary") self._complete_calculated_summary_path_no_skip() - self.assertNotInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £180.00" ) @@ -83,7 +79,6 @@ def test_new_calculated_summary_repeating_section(self): self.post() self._complete_calculated_summary_path_with_skip() - self.assertInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £80.00" ) @@ -94,7 +89,100 @@ def test_new_calculated_summary_no_skip_repeating_section(self): self.post() self._complete_calculated_summary_path_no_skip() - self.assertNotInBody("Skipped Fourth") self.assertInBody( "We calculate the total of currency values entered to be £180.00" ) + + def test_calculated_summary_value_sources_across_sections(self): + self.launchSurvey("test_calculated_summary_cross_section_dependencies") + + # Complete the first section + self.post() + self.post({"skip-first-block-answer": "No"}) + self.post({"first-number-answer": "10"}) + self.post({"first-and-a-half-number-answer-also-in-total": "20"}) + self.post({"second-number-answer-also-in-total": "30"}) + self.assertInBody( + "We calculate the total of currency values entered to be £60.00" + ) + self.post() + self.post() + self.post() + + # Complete the second section + self.post( + { + "third-number-answer": "20", + "third-number-answer-also-in-total": "20", + } + ) + self.assertInBody( + "We calculate the total of currency values entered to be £40.00" + ) + + # Check calculated summary value sources are displayed correctly for both the current and previous + # sections + self.post() + self.assertInBody("60 - calculated summary answer (previous section)") + self.assertInBody("40 - calculated summary answer (current section)") + self.post() + + self.assertInBody( + "Set minimum and maximum values based on your calculated summary total of £60" + ) + self.post( + { + "set-minimum-answer": "40", + "set-maximum-answer": "70", + } + ) + self.assertInBody("Enter an answer more than or equal to £60.00") + + def test_calculated_summary_value_sources_across_sections_repeating(self): + self.launchSurvey( + "test_new_calculated_summary_cross_section_dependencies_repeating" + ) + + # Add household members + self._add_list_items() + + # Complete the first section + self.post({"skip-first-block-answer": "No"}) + self.post({"first-number-answer": "10"}) + self.post({"first-and-a-half-number-answer-also-in-total": "20"}) + self.post({"second-number-answer-also-in-total": "30"}) + self.assertInBody( + "We calculate the total of currency values entered to be £60.00" + ) + self.post() + self.post() + self.post() + + # Complete the second section + self.post( + { + "third-number-answer": "20", + "third-number-answer-also-in-total": "20", + } + ) + self.assertInBody( + "We calculate the total of currency values entered to be £40.00" + ) + + # Check calculated summary value sources are displayed correctly for both the current and previous + # sections + self.post() + self.assertInBody("60 - calculated summary answer (previous section)") + self.assertInBody("40 - calculated summary answer (current section)") + self.post() + + self.assertInBody( + "Set minimum and maximum values based on your calculated summary total of £60" + ) + self.post( + { + "set-minimum-answer": "40", + "set-maximum-answer": "70", + } + ) + self.assertInBody("Enter an answer more than or equal to £60.00") From e06e43caf8729cdba0114a373dd55f1d1a6415a0 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:47:33 +0100 Subject: [PATCH 201/567] Fix language changing (#1076) --- app/utilities/schema.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index a84e01f4a2..c1e21f6bdc 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -22,7 +22,11 @@ SCHEMA_DIR = "schemas" LANGUAGE_CODES = ("en", "cy") -LANGUAGES_MAP = {"test_language": [["en", "cy"]]} +LANGUAGES_MAP = { + "test_language": [["en", "cy"]], + "cris_0001": [["en", "cy"]], + "phm_0001": [["en", "cy"]], +} SCHEMA_REQUEST_MAX_BACKOFF = 0.2 SCHEMA_REQUEST_MAX_RETRIES = 2 # Totals no. of request should be 3. The initial request + SCHEMA_REQUEST_MAX_RETRIES From bbfdd3b3e5e7008882ffd322dab2f0926996e70f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:27:14 +0100 Subject: [PATCH 202/567] Schemas v3.49.0 (#1081) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a2effd12b9..d921158034 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.48.0 +v3.49.0 From f95b0a13e49e990d2fc478784e5a292fcaec99e5 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 5 Apr 2023 12:52:29 +0100 Subject: [PATCH 203/567] Update ChromeDriver to 111 (#1082) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 09115a0402..591411bee7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/mocha-framework": "^8.3.0", "@wdio/spec-reporter": "^8.3.0", "chai": "^4.3.6", - "chromedriver": "^110.0.0", + "chromedriver": "^111.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 0a1d1aab59..fad76959a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,10 +1786,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^110.0.0: - version "110.0.0" - resolved "https://registry.npmjs.org/chromedriver/-/chromedriver-110.0.0.tgz" - integrity sha512-Le6q8xrA/3fAt+g8qiN0YjsYxINIhQMC6wj9X3W5L77uN4NspEzklDrqYNwBcEVn7PcAEJ73nLlS7mTyZRspHA== +chromedriver@^111.0.0: + version "111.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-111.0.0.tgz#ef704dc44524ce5c1eced283d523b7aefd59ee19" + integrity sha512-XavNYNBBfKIrT8Oi/ywJ0DoOOU+jHF5bQWTkqStFsAXvfCV9VzYN3J+TGAvZdrpXeoixqPRGUxQ2yZhD2iowdQ== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From 16011e6810ce5e1a31c03877ca52b1539a270491 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 5 Apr 2023 17:00:55 +0100 Subject: [PATCH 204/567] Confirmation page after signout (#1070) Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/routes/session.py | 21 ++- app/translations/messages.pot | 144 ++++++++++-------- templates/errors/401.html | 6 +- templates/errors/403.html | 6 +- templates/errors/404.html | 6 +- templates/errors/500.html | 8 +- templates/errors/_base.html | 2 + templates/errors/submission-failed.html | 6 +- templates/layouts/configs/_header.html | 3 +- templates/signed-out.html | 23 ++- .../hub_and_spoke/hub_and_spoke.spec.js | 2 +- tests/functional/spec/save_sign_out.spec.js | 23 ++- tests/integration/routes/test_errors.py | 8 +- tests/integration/routes/test_session.py | 11 +- .../session/test_sign_out_and_exit.py | 14 +- 15 files changed, 182 insertions(+), 101 deletions(-) diff --git a/app/routes/session.py b/app/routes/session.py index d3d803dc14..8231cbe87c 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -162,9 +162,11 @@ def get_sign_out(): Signs the user out of eQ and redirects to the log out url. """ survey_config = get_survey_config() - log_out_url = ( - survey_config.account_service_todo_url if "todo" in request.args else None - ) + log_out_url = None + if "internal_redirect" in request.args: + log_out_url = url_for("session.get_signed_out") + elif "todo" in request.args: + log_out_url = survey_config.account_service_todo_url if not log_out_url: log_out_url = survey_config.account_service_log_out_url @@ -178,7 +180,18 @@ def get_sign_out(): @session_blueprint.route("/signed-out", methods=["GET"]) def get_signed_out(): - return render_template(template="signed-out") + if not cookie_session: + return redirect(url_for("session.get_session_expired")) + + survey_config = get_survey_config() + redirect_url = ( + survey_config.account_service_todo_url + or survey_config.account_service_log_out_url + ) + return render_template( + template="signed-out", + redirect_url=redirect_url, + ) def get_runner_claims(decrypted_token): diff --git a/app/translations/messages.pot b/app/translations/messages.pot index fc21a30b16..b707c68161 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-03-03 11:01+0000\n" +"POT-Creation-Date: 2023-04-05 10:55+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -219,19 +219,19 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:46 +#: app/helpers/template_helpers.py:48 msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:112 +#: app/helpers/template_helpers.py:114 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:143 +#: app/helpers/template_helpers.py:145 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:168 +#: app/helpers/template_helpers.py:170 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -540,19 +540,19 @@ msgstr "" msgid "Confirm your email address" msgstr "" -#: app/views/handlers/confirm_email.py:88 +#: app/views/handlers/confirm_email.py:89 msgid "Is this email address correct?" msgstr "" -#: app/views/handlers/confirm_email.py:101 #: app/views/handlers/confirm_email.py:102 -#: app/views/handlers/individual_response.py:878 +#: app/views/handlers/confirm_email.py:103 +#: app/views/handlers/individual_response.py:880 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:128 +#: app/views/handlers/confirm_email.py:129 #: app/views/handlers/confirmation_email.py:67 -#: app/views/handlers/feedback.py:83 app/views/handlers/question.py:165 +#: app/views/handlers/feedback.py:84 app/views/handlers/question.py:170 msgid "Error: {page_title}" msgstr "" @@ -564,35 +564,35 @@ msgstr "" msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:139 +#: app/views/handlers/feedback.py:140 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:145 app/views/handlers/feedback.py:169 +#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:170 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:148 app/views/handlers/feedback.py:149 +#: app/views/handlers/feedback.py:149 app/views/handlers/feedback.py:150 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:150 +#: app/views/handlers/feedback.py:151 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:155 app/views/handlers/feedback.py:156 +#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:157 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:159 app/views/handlers/feedback.py:162 +#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:163 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:177 app/views/handlers/feedback.py:187 +#: app/views/handlers/feedback.py:178 app/views/handlers/feedback.py:188 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:178 +#: app/views/handlers/feedback.py:179 msgid "Do not include confidential information, such as your contact details" msgstr "" @@ -600,136 +600,136 @@ msgstr "" msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:252 +#: app/views/handlers/individual_response.py:254 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:322 +#: app/views/handlers/individual_response.py:324 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:345 +#: app/views/handlers/individual_response.py:347 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:347 +#: app/views/handlers/individual_response.py:349 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:355 +#: app/views/handlers/individual_response.py:357 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:357 +#: app/views/handlers/individual_response.py:359 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:366 +#: app/views/handlers/individual_response.py:368 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:368 +#: app/views/handlers/individual_response.py:370 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:371 +#: app/views/handlers/individual_response.py:373 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:417 +#: app/views/handlers/individual_response.py:419 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:448 +#: app/views/handlers/individual_response.py:450 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:463 +#: app/views/handlers/individual_response.py:465 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:469 +#: app/views/handlers/individual_response.py:471 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:473 +#: app/views/handlers/individual_response.py:475 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:479 +#: app/views/handlers/individual_response.py:481 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:521 +#: app/views/handlers/individual_response.py:523 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:586 +#: app/views/handlers/individual_response.py:588 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:594 +#: app/views/handlers/individual_response.py:596 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:601 +#: app/views/handlers/individual_response.py:603 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:614 +#: app/views/handlers/individual_response.py:616 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:620 +#: app/views/handlers/individual_response.py:622 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:653 +#: app/views/handlers/individual_response.py:655 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:708 -#: app/views/handlers/individual_response.py:746 +#: app/views/handlers/individual_response.py:710 +#: app/views/handlers/individual_response.py:748 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:712 +#: app/views/handlers/individual_response.py:714 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:770 +#: app/views/handlers/individual_response.py:772 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:782 +#: app/views/handlers/individual_response.py:784 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:783 +#: app/views/handlers/individual_response.py:785 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:816 +#: app/views/handlers/individual_response.py:818 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:865 +#: app/views/handlers/individual_response.py:867 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:874 +#: app/views/handlers/individual_response.py:876 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:912 +#: app/views/handlers/individual_response.py:914 msgid "Confirm mobile number" msgstr "" @@ -876,7 +876,7 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:23 templates/partials/question.html:25 +#: templates/interstitial.html:23 templates/partials/question.html:24 msgid "If you can’t answer questions for this person" msgstr "" @@ -912,16 +912,22 @@ msgstr "" msgid "Close this window to continue with your current survey" msgstr "" -#: templates/signed-out.html:3 +#: templates/signed-out.html:5 msgid "Signed out" msgstr "" -#: templates/signed-out.html:6 -msgid "Your survey answers have been saved. You are now signed out" +#: templates/signed-out.html:16 +msgid "

      Your progress has been saved

      " msgstr "" -#: templates/signed-out.html:9 -msgid "Return to your account" +#: templates/signed-out.html:23 +msgid "" +"To find further information or resume the survey, return " +"to My Account." +msgstr "" + +#: templates/signed-out.html:29 +msgid "To resume the survey, re-enter your access code." msgstr "" #: templates/thank-you.html:6 @@ -1030,12 +1036,24 @@ msgid "" "code." msgstr "" +#: templates/errors/401.html:20 templates/errors/403.html:12 +#: templates/errors/404.html:13 templates/errors/500.html:12 +#: templates/errors/submission-failed.html:14 +msgid "Business surveys" +msgstr "" + #: templates/errors/401.html:21 msgid "" "If you are completing a business survey, you need to sign back in to your account." msgstr "" +#: templates/errors/401.html:22 templates/errors/403.html:14 +#: templates/errors/404.html:15 templates/errors/500.html:14 +#: templates/errors/submission-failed.html:16 +msgid "All other surveys" +msgstr "" + #: templates/errors/401.html:23 msgid "" "If you started your survey using an access code, you need to " msgstr "" -#: templates/partials/preview-question.html:46 -#: templates/partials/question.html:69 +#: templates/partials/preview-question.html:35 +#: templates/partials/question.html:68 msgid "Or" msgstr "" -#: templates/partials/preview-question.html:75 +#: templates/partials/preview-question.html:56 msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:60 +#: templates/partials/question.html:59 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:61 +#: templates/partials/question.html:60 msgid "cleared" msgstr "" -#: templates/partials/question.html:64 +#: templates/partials/question.html:63 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:65 templates/partials/question.html:73 +#: templates/partials/question.html:64 templates/partials/question.html:72 msgid "deselected" msgstr "" diff --git a/templates/errors/401.html b/templates/errors/401.html index f9e349005b..a94af63ac4 100644 --- a/templates/errors/401.html +++ b/templates/errors/401.html @@ -13,13 +13,13 @@

      {{ _("Sorry, you need to sign in again") }}

      {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %}

      {{ _("You will need to sign back in to access your account").format(url = business_logout_url) }}

      - {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL %} + {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %}

      {{ _("To access this page you need to re-enter your access code.").format(url = other_logout_url) }}

      {% else %} -

      Business surveys

      +

      {{ _("Business surveys") }}

      {{ _("If you are completing a business survey, you need to sign back in to your account.").format(url = business_logout_url) }}

      -

      All other surveys

      +

      {{ _("All other surveys") }}

      {{ _("If you started your survey using an access code, you need to re-enter your code.").format(url = other_logout_url) }}

      {% endif %} {% endblock %} diff --git a/templates/errors/403.html b/templates/errors/403.html index 14c7c44f7b..857a3025c3 100644 --- a/templates/errors/403.html +++ b/templates/errors/403.html @@ -6,12 +6,12 @@

      {{ _("Sorry, there is a problem") }}

      {{ _("You may need to update your browser to a newer version.") }}

      {{ _("If the problem still occurs, try using a different browser or device.") }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} + {% if survey_type in SURVEY_TYPES_ALL %}

      {{ _("For further help, please contact us.").format(url=contact_us_url) }}

      {% else %} -

      Business surveys

      +

      {{ _("Business surveys") }}

      {{ _("If you are completing a business survey and you need further help, please contact us.").format(url=business_contact_us_url) }}

      -

      All other surveys

      +

      {{ _("All other surveys") }}

      {{ _("If you started your survey using an access code and you need further help, please contact us.").format(url=other_contact_us_url) }}

      {% endif %} {% endblock %} diff --git a/templates/errors/404.html b/templates/errors/404.html index 47806411a9..f7d4a357e1 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -6,13 +6,13 @@

      {{ _("Page not found") }}

      {{ _("If you entered a web address, check it is correct.") }}

      {{ _("If you pasted the web address, check you copied the whole address.") }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} + {% if survey_type in SURVEY_TYPES_ALL %}

      {{ _("If the web address is correct or you selected a link or button, contact us for more help.").format(url=contact_us_url) }}

      {% else %}

      {{ _("If the web address is correct or you selected a link or button, please see the following help links.") }}

      -

      Business surveys

      +

      {{ _("Business surveys") }}

      {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

      -

      All other surveys

      +

      {{ _("All other surveys") }}

      {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

      {% endif %} {% endblock %} diff --git a/templates/errors/500.html b/templates/errors/500.html index 6ac3b682b1..14cad74421 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -6,12 +6,12 @@

      {{ _("Sorry, there is a problem with this service") }}

      {{ _("Try again later.") }}

      {{ _("If you have started a survey, your answers have been saved.") }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} -

      {{ _("Contact us if you need to speak to someone about your survey.").format(url = contact_us_url) }}

      + {% if survey_type and survey_type in SURVEY_TYPES_ALL %} +

      {{ _("Contact us if you need to speak to someone about your survey.").format(url = contact_us_url) }}

      {% else %} -

      Business surveys

      +

      {{ _("Business surveys") }}

      {{ _("If you are completing a business survey and you need to speak to someone about your survey, please contact us.").format(url=business_contact_us_url) }}

      -

      All other surveys

      +

      {{ _("All other surveys") }}

      {{ _("If you started your survey using an access code and you need to speak to someone about your survey, please contact us.").format(url=other_contact_us_url) }}

      {% endif %} {% endblock %} diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 4dd183d764..41cdd3d9f6 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -3,3 +3,5 @@ {% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} +{% set SURVEY_TYPES_HEALTH = ["health"] %} +{% set SURVEY_TYPES_ALL = SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT + SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} diff --git a/templates/errors/submission-failed.html b/templates/errors/submission-failed.html index 77b1695561..8371ceb4d8 100644 --- a/templates/errors/submission-failed.html +++ b/templates/errors/submission-failed.html @@ -7,13 +7,13 @@ {% block main %}

      {{ _("Sorry, there is a problem") }}

      {{ _("You can try to submit your survey again").format(url=url_for('questionnaire.get_questionnaire')) }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT or survey_type in SURVEY_TYPES_SOCIAL) %} + {% if survey_type and (survey_type in SURVEY_TYPES_ALL) %}

      {{ _("If this problem keeps happening, please contact us for help.").format(url=contact_us_url) }}

      {% else %}

      {{ _("If this problem keeps happening, please see the following help links.") }}

      -

      Business surveys

      +

      {{ _("Business surveys") }}

      {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

      -

      All other surveys

      +

      {{ _("All other surveys") }}

      {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

      {% endif %} {% endblock %} diff --git a/templates/layouts/configs/_header.html b/templates/layouts/configs/_header.html index 3935fadcf6..fd9ac1bb21 100644 --- a/templates/layouts/configs/_header.html +++ b/templates/layouts/configs/_header.html @@ -1,12 +1,13 @@ {% if current_user.is_authenticated and not hide_sign_out_button %} - {% set sign_out_url = url_for('session.get_sign_out', todo=true) %} {% if save_on_signout %} + {% set sign_out_url = url_for('session.get_sign_out', internal_redirect=true) %} {% set header_button_text = sign_out_button_text %} {% set header_button_data_qa = "btn-save-sign-out" %} {% set header_button_data_ga_action = "Save and sign out" %} {% set header_button_data_ga_label = "Save and sign out button click" %} {% else %} + {% set sign_out_url = url_for('session.get_sign_out', todo=true) %} {% set header_button_text = _("Exit") %} {% set header_button_data_qa = "btn-exit" %} {% set header_button_data_ga_action = "Exit" %} diff --git a/templates/signed-out.html b/templates/signed-out.html index 0597dabb1f..7833a751a6 100644 --- a/templates/signed-out.html +++ b/templates/signed-out.html @@ -1,11 +1,26 @@ -{% extends 'layouts/_base.html' %} +{% extends 'errors/_base.html' %} + +{% from "components/panel/_macro.njk" import onsPanel %} {% set page_title = _("Signed out") %} {% block main %} -

      {{ _("Your survey answers have been saved. You are now signed out") }}.

      - {% if account_service_log_out_url %} - {{ _("Return to your account") }} + {{ + onsPanel({ + "id": '', + "type": 'success', + "iconType": 'check', + "iconSize": 'xl', + "classes": "ons-u-mb-xl", + "body": _("

      Your progress has been saved

      ") + }) + }} + + {% if survey_type in SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT %} +

      {{ _("To find further information or resume the survey, return to My Account.").format(url = redirect_url) }}

      + {% elif survey_type in SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} +

      {{ _("To resume the survey, re-enter your access code.").format(url = redirect_url) }}

      {% endif %} + {% endblock %} diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 2fc6e8c0b2..4afeab4596 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -40,7 +40,7 @@ describe("Feature: Hub and Spoke", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain("/surveys/todo"); + await expect(expectedUrl).to.contain("/signed-out"); }); it("When a user views the Hub, Then the page title should be Choose another section to complete", async () => { diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index 9dc9474327..fe122f3389 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -21,14 +21,14 @@ describe("Save sign out / Exit", () => { await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); }); - it("Given I am completing a questionnaire, when I select save and sign out, then I am redirected to sign out page and my session is cleared", async () => { + it("Given I am completing a questionnaire, when I select save and sign out, then I am redirected to the signed out page", async () => { await browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); await $(SetMinMax.setMinimum()).setValue("10"); await $(SetMinMax.setMaximum()).setValue("1020"); await $(SetMinMax.submit()).click(); await $(TestMinMax.saveSignOut()).click(); - await expect(await browser.getUrl()).to.contain("/surveys/todo"); + await expect(await browser.getUrl()).to.contain("/signed-out"); await browser.back(); await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); @@ -49,6 +49,25 @@ describe("Save sign out / Exit", () => { await expect(await browser.getUrl()).to.contain("thank-you"); }); + it("Given a I have started a social questionnaire, when I select save and sign out, then I am redirected to the signed out page and the correct access code link is shown", async () => { + await browser.openQuestionnaire("test_theme_social.json", { version: "v2", theme: "social" }); + await $(SubmitPage.saveSignOut()).click(); + await expect(await browser.getUrl()).to.contain("/signed-out"); + await expect(await $("body").getHTML()).to.contain("Your progress has been saved"); + await expect(await $("body").getHTML()).to.contain("To resume the survey,"); + await expect(await $("body").getHTML()).to.contain("/en/start"); + }); + + it("Given a I have started a business questionnaire, when I select save and sign out, then I am redirected to the signed out page and the correct access code link is shown", async () => { + await browser.openQuestionnaire("test_introduction.json"); + await $(IntroductionPage.getStarted()).click(); + await $(IntroInterstitialPage.saveSignOut()).click(); + await expect(await browser.getUrl()).to.contain("/signed-out"); + await expect(await $("body").getHTML()).to.contain("Your progress has been saved"); + await expect(await $("body").getHTML()).to.contain("To find further information or resume the survey,"); + await expect(await $("body").getHTML()).to.contain("/surveys/todo"); + }); + it("Given a business questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { await browser.openQuestionnaire("test_introduction.json"); diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 33604dae66..db4414e8cb 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -65,7 +65,7 @@ def test_errors_500_with_payload(self): # gets called with patch( "app.routes.questionnaire.get_block_handler", - side_effect=Exception("You broked it"), + side_effect=Exception("You broke it"), ): self.post({"answer": "5000000"}) self.assertStatusCode(500) @@ -80,12 +80,12 @@ def test_errors_500_exception_during_error_handling(self): # gets called with patch( "app.routes.questionnaire.get_block_handler", - side_effect=Exception("You broked it"), + side_effect=Exception("You broke it"), ): # Another exception occurs during exception handling with patch( "app.routes.errors.log_exception", - side_effect=Exception("You broked it again"), + side_effect=Exception("You broke it again"), ): self.post({"answer": "5000000"}) @@ -319,7 +319,7 @@ def test_500_theme_census_cookie_exists(self): # When with patch( "app.routes.questionnaire.get_block_handler", - side_effect=Exception("You broked it"), + side_effect=Exception("You broke it"), ): self.post({"answer": "test"}) diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index f60852e834..64379e468c 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -3,11 +3,15 @@ from freezegun import freeze_time +from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.utilities.json import json_loads from tests.integration.integration_test_case import IntegrationTestCase TIME_TO_FREEZE = datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc) EQ_SESSION_TIMEOUT_SECONDS = 45 * 60 +BUSINESS_URL = ACCOUNT_SERVICE_BASE_URL +SOCIAL_URL = ACCOUNT_SERVICE_BASE_URL_SOCIAL class TestSession(IntegrationTestCase): @@ -30,10 +34,10 @@ def test_session_expired(self): self.get("/session-expired") self.assertInBody("Sorry, you need to sign in again") self.assertInBody( - '

      If you are completing a business survey, you need to sign back in to your account.

      ' + f'

      If you are completing a business survey, you need to sign back in to your account.

      ' ) self.assertInBody( - '

      If you started your survey using an access code, you need to re-enter your code.

      ' + f'

      If you started your survey using an access code, you need to re-enter your code.

      ' ) def test_session_jti_token_expired(self): @@ -45,7 +49,8 @@ def test_head_request_on_session_expired(self): self.assertStatusOK() def test_head_request_on_session_signed_out(self): - self.head("/signed-out") + self.launchSurvey("test_introduction") + self.get("/signed-out") self.assertStatusOK() @freeze_time(TIME_TO_FREEZE) diff --git a/tests/integration/session/test_sign_out_and_exit.py b/tests/integration/session/test_sign_out_and_exit.py index 93fdf51a3c..abc35a002d 100644 --- a/tests/integration/session/test_sign_out_and_exit.py +++ b/tests/integration/session/test_sign_out_and_exit.py @@ -3,6 +3,7 @@ SIGN_OUT_URL_PATH = "/sign-out" SIGNED_OUT_URL_PATH = "/signed-out" +SESSION_EXPIRED_PATH = "/session-expired" ACCOUNT_SERVICE_BASE_URL = "http://localhost" ACCOUNT_SERVICE_LOG_OUT_URL_PATH = "/sign-in/logout" ACCOUNT_SERVICE_LOG_OUT_URL = ( @@ -16,12 +17,14 @@ class TestSaveAndSignOut(IntegrationTestCase): def test_sign_out_button_link(self): self.launchSurvey("test_textfield") - self.assertEqual("/sign-out?todo=True", self.getSignOutButton()["href"]) + self.assertEqual( + "/sign-out?internal_redirect=True", self.getSignOutButton()["href"] + ) def test_sign_out_url(self): self.launchSurvey("test_textfield") self.saveAndSignOut() - self.assertInRedirect("/surveys/todo") + self.assertInRedirect(SIGNED_OUT_URL_PATH) def test_sign_out_button_text(self): self.launchSurvey("test_textfield") @@ -36,6 +39,11 @@ def test_no_session_cookie_redirects_to_default_account_service_log_out_url(self self.get(SIGN_OUT_URL_PATH, follow_redirects=False) self.assertInRedirect(DEFAULT_ACCOUNT_SERVICE_LOG_OUT_URL) + def test_no_session_cookie_signed_out_redirects_to_session_expiry(self): + self.deleteCookie() + self.get(SIGNED_OUT_URL_PATH, follow_redirects=False) + self.assertInRedirect(SESSION_EXPIRED_PATH) + # Test the behaviour when using Hub/No Hub def test_redirects_to_account_service_log_out_url_using_base_url_from_claims(self): for schema in ["test_textfield", "test_hub_and_spoke"]: @@ -95,7 +103,7 @@ def setUp(self): def test_sign_out_url(self): self.launchSurvey(schema_name="test_individual_response") self.saveAndSignOut() - self.assertInRedirect("census.gov.uk") + self.assertInRedirect(SIGNED_OUT_URL_PATH) def test_sign_out_button_text(self): self.launchSurvey(schema_name="test_individual_response") From 62863e4b18222d35f98b55c1c17ebbc8fa10c5af Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 6 Apr 2023 09:31:10 +0100 Subject: [PATCH 205/567] Schemas v3.50.0 (#1084) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d921158034..0c8cc2343f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.49.0 +v3.50.0 From 1bc1af2cd053b98f0fa494d65c8b1d4e1d14569d Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 6 Apr 2023 09:43:34 +0100 Subject: [PATCH 206/567] Upload latest static translations (#1083) --- app/translations/cy/LC_MESSAGES/messages.po | 270 +++++++++++--------- 1 file changed, 154 insertions(+), 116 deletions(-) diff --git a/app/translations/cy/LC_MESSAGES/messages.po b/app/translations/cy/LC_MESSAGES/messages.po index bf05e48f08..1fd474d2c2 100644 --- a/app/translations/cy/LC_MESSAGES/messages.po +++ b/app/translations/cy/LC_MESSAGES/messages.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: phm\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-01-31 13:07+0000\n" -"PO-Revision-Date: 2023-03-03 11:11\n" +"POT-Creation-Date: 2023-04-05 10:55+0100\n" +"PO-Revision-Date: 2023-04-05 16:08\n" "Last-Translator: \n" "Language-Team: Welsh\n" "MIME-Version: 1.0\n" @@ -18,7 +18,7 @@ msgstr "" "X-Crowdin-File-ID: 1\n" "Language: cy_GB\n" -#: app/forms/validators.py:376 app/jinja_filters.py:115 +#: app/forms/validators.py:377 app/jinja_filters.py:115 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" @@ -29,7 +29,7 @@ msgstr[3] "%(num)s blynedd" msgstr[4] "%(num)s blynedd" msgstr[5] "%(num)s o flynyddoedd" -#: app/forms/validators.py:380 app/jinja_filters.py:123 +#: app/forms/validators.py:381 app/jinja_filters.py:123 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" @@ -50,15 +50,15 @@ msgstr "%(date)s am %(time)s" msgid "%(from_date)s to %(to_date)s" msgstr "%(from_date)s i %(to_date)s" -#: app/jinja_filters.py:600 templates/partials/summary/list-summary.html:9 +#: app/jinja_filters.py:598 templates/partials/summary/list-summary.html:9 msgid "Remove" msgstr "Dileu" -#: app/jinja_filters.py:601 templates/partials/summary/list-summary.html:10 +#: app/jinja_filters.py:599 templates/partials/summary/list-summary.html:10 msgid "Remove {item_name}" msgstr "Dileu{item_name}" -#: app/jinja_filters.py:701 +#: app/jinja_filters.py:699 msgid "No answer provided" msgstr "Dim ateb wedi'i roi" @@ -213,7 +213,7 @@ msgstr "Dileu ateb" msgid "Enter the year in a valid format. For example, 2023." msgstr "Rhowch y flwyddyn mewn fformat dilys. Er enghraifft, 2023." -#: app/forms/validators.py:387 +#: app/forms/validators.py:388 #, python-format msgid "%(num)s day" msgid_plural "%(num)s days" @@ -224,27 +224,27 @@ msgstr[3] "%(num)s diwrnod" msgstr[4] "%(num)s diwrnod" msgstr[5] "%(num)s diwrnod" -#: app/forms/fields/select_field_with_detail_answer.py:36 +#: app/forms/fields/select_field_with_detail_answer.py:39 msgid "Not a valid choice." msgstr "Nid yw'n ddewis dilys." -#: app/helpers/template_helpers.py:46 +#: app/helpers/template_helpers.py:48 msgid "ONS Surveys" msgstr "Arolygon SYG" -#: app/helpers/template_helpers.py:107 +#: app/helpers/template_helpers.py:114 msgid "Menu" msgstr "Dewislen" -#: app/helpers/template_helpers.py:138 +#: app/helpers/template_helpers.py:145 msgid "The following links open in a new tab" msgstr "Mae'r dolenni canlynol yn agor mewn tab newydd" -#: app/helpers/template_helpers.py:163 +#: app/helpers/template_helpers.py:170 msgid "Make sure you leave this page or close your browser if using a shared device" msgstr "Cofiwch adael y dudalen hon neu gau eich porwr os ydych chi'n defnyddio dyfais sy'n cael ei rhannu" -#: app/questionnaire/placeholder_transforms.py:164 +#: app/questionnaire/placeholder_transforms.py:160 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "{number_of_years} blwyddyn" @@ -254,7 +254,7 @@ msgstr[3] "{number_of_years} blynedd" msgstr[4] "{number_of_years} blynedd" msgstr[5] "{number_of_years} o flynyddoedd" -#: app/questionnaire/placeholder_transforms.py:170 +#: app/questionnaire/placeholder_transforms.py:166 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "{number_of_months} mis" @@ -264,7 +264,7 @@ msgstr[3] "{number_of_months} mis" msgstr[4] "{number_of_months} mis" msgstr[5] "{number_of_months} mis" -#: app/questionnaire/placeholder_transforms.py:175 +#: app/questionnaire/placeholder_transforms.py:171 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "{number_of_days} diwrnod" @@ -329,8 +329,8 @@ msgid "An individual access code has been sent by text" msgstr "Mae cod mynediad unigol wedi cael ei anfon drwy neges destun" #: app/survey_config/business_config.py:59 -#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:90 -#: app/survey_config/census_config.py:156 +#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:89 +#: app/survey_config/census_config.py:154 msgid "Help" msgstr "Help" @@ -348,8 +348,8 @@ msgid "What we do" msgstr "Beth rydym yn ei wneud" #: app/survey_config/business_config.py:92 -#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:98 -#: app/survey_config/census_config.py:164 +#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:97 +#: app/survey_config/census_config.py:162 #: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "Cysylltu â ni" @@ -360,15 +360,15 @@ msgid "Accessibility" msgstr "Hygyrchedd" #: app/survey_config/business_config.py:107 -#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:118 -#: app/survey_config/census_config.py:173 +#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:117 +#: app/survey_config/census_config.py:171 #: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "Cwcis" #: app/survey_config/business_config.py:109 -#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:124 -#: app/survey_config/census_config.py:179 +#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:123 +#: app/survey_config/census_config.py:177 #: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "Preifatrwydd a diogelu data" @@ -381,29 +381,29 @@ msgstr "Cyfrifiad 2021" msgid "Save and complete later" msgstr "Cadw a chwblhau yn nes ymlaen" -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:104 +#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:103 msgid "Languages" msgstr "Ieithoedd" -#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:108 +#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:107 msgid "BSL and audio videos" msgstr "Fideos sain ac Iaith Arwyddion Prydain" -#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:120 -#: app/survey_config/census_config.py:175 +#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:119 +#: app/survey_config/census_config.py:173 msgid "Accessibility statement" msgstr "Datganiad hygyrchedd" -#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:128 -#: app/survey_config/census_config.py:183 +#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:127 +#: app/survey_config/census_config.py:181 msgid "Terms and conditions" msgstr "Telerau ac amodau" -#: app/survey_config/census_config.py:144 +#: app/survey_config/census_config.py:143 msgid "Crown copyright and database rights 2021 NIMA MOU577.501." msgstr "Hawlfraint y goron a hawliau cronfa ddata 2021 NIMA MOU577.501." -#: app/survey_config/census_config.py:147 app/survey_config/survey_config.py:20 +#: app/survey_config/census_config.py:146 app/survey_config/survey_config.py:20 msgid "Use of address data is subject to the terms and conditions." msgstr "Mae defnyddio data cyfeiriadau yn ddarostyngedig i’r telerau ac amodau." @@ -415,63 +415,63 @@ msgstr "Hawlfraint y goron a hawliau cronfa ddata 2020 OS 100019153." msgid "Save and exit survey" msgstr "Cadw a gadael yr arolwg" -#: app/views/contexts/hub_context.py:14 +#: app/views/contexts/hub_context.py:15 msgid "Completed" msgstr "Cwblhawyd" -#: app/views/contexts/hub_context.py:16 +#: app/views/contexts/hub_context.py:17 msgid "View answers" msgstr "Gweld yr atebion" -#: app/views/contexts/hub_context.py:17 +#: app/views/contexts/hub_context.py:18 msgid "View answers for {section_name}" msgstr "Gweld yr atebion ar gyfer {section_name}" -#: app/views/contexts/hub_context.py:21 +#: app/views/contexts/hub_context.py:22 msgid "Partially completed" msgstr "Wedi’i chwblhau’n rhannol" -#: app/views/contexts/hub_context.py:23 +#: app/views/contexts/hub_context.py:24 msgid "Continue with section" msgstr "Parhau â'r adran" -#: app/views/contexts/hub_context.py:24 +#: app/views/contexts/hub_context.py:25 msgid "Continue with {section_name} section" msgstr "Parhau ag adran {section_name}" -#: app/views/contexts/hub_context.py:28 +#: app/views/contexts/hub_context.py:29 msgid "Not started" msgstr "Heb ddechrau" -#: app/views/contexts/hub_context.py:30 +#: app/views/contexts/hub_context.py:31 msgid "Start section" msgstr "Dechrau'r adran" -#: app/views/contexts/hub_context.py:31 +#: app/views/contexts/hub_context.py:32 msgid "Start {section_name} section" msgstr "Dechrau adran {section_name}" -#: app/views/contexts/hub_context.py:35 +#: app/views/contexts/hub_context.py:36 msgid "Separate census requested" msgstr "Cais am gyfrifiad ar wahân wedi'i wneud" -#: app/views/contexts/hub_context.py:37 app/views/contexts/hub_context.py:38 +#: app/views/contexts/hub_context.py:38 app/views/contexts/hub_context.py:39 msgid "Change or resend" msgstr "Newid neu ailanfon" -#: app/views/contexts/hub_context.py:48 app/views/contexts/hub_context.py:49 +#: app/views/contexts/hub_context.py:51 app/views/contexts/hub_context.py:52 msgid "Submit survey" msgstr "Cyflwyno'r arolwg" -#: app/views/contexts/hub_context.py:53 +#: app/views/contexts/hub_context.py:56 msgid "You must submit this survey to complete it" msgstr "Mae'n rhaid i chi gyflwyno'r arolwg hwn er mwyn ei gwblhau" -#: app/views/contexts/hub_context.py:60 +#: app/views/contexts/hub_context.py:63 msgid "Choose another section to complete" msgstr "Dewiswch adran arall i'w chwblhau" -#: app/views/contexts/hub_context.py:61 templates/confirm-email.html:25 +#: app/views/contexts/hub_context.py:64 templates/confirm-email.html:25 #: templates/individual_response/confirmation-post.html:21 #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 @@ -479,19 +479,23 @@ msgstr "Dewiswch adran arall i'w chwblhau" msgid "Continue" msgstr "Parhau" -#: app/views/contexts/list_context.py:104 +#: app/views/contexts/list_context.py:109 msgid " (You)" msgstr " (Chi)" -#: app/views/contexts/submission_metadata_context.py:13 +#: app/views/contexts/preview_context.py:66 +msgid "Preview survey questions" +msgstr "Rhagolwg o gwestiynau'r arolwg" + +#: app/views/contexts/submission_metadata_context.py:14 msgid "Submitted on:" msgstr "Cyflwynwyd ar:" -#: app/views/contexts/submission_metadata_context.py:16 +#: app/views/contexts/submission_metadata_context.py:17 msgid "{date} at {time}" msgstr "{date} am {time}" -#: app/views/contexts/submission_metadata_context.py:25 +#: app/views/contexts/submission_metadata_context.py:26 msgid "Submission reference:" msgstr "Cyfeirnod cyflwyno:" @@ -507,35 +511,43 @@ msgstr "Cyflwyno atebion" msgid "Please submit this survey to complete it" msgstr "Cyflwynwch yr arolwg hwn i'w gwblhau" -#: app/views/contexts/thank_you_context.py:28 -msgid "Your answers have been submitted." -msgstr "Mae eich atebion wedi cael eu cyflwyno." - -#: app/views/contexts/thank_you_context.py:30 +#: app/views/contexts/thank_you_context.py:29 msgid "Your answers have been submitted for {company_name} ({trading_name})" msgstr "Mae eich atebion wedi cael eu cyflwyno ar gyfer {company_name} ({trading_name})" -#: app/views/contexts/thank_you_context.py:37 +#: app/views/contexts/thank_you_context.py:36 msgid "Your answers have been submitted for {company_name}" msgstr "Mae eich atebion wedi cael eu cyflwyno ar gyfer {company_name}" -#: app/views/contexts/view_submitted_response_context.py:37 -msgid "Answers submitted." -msgstr "Atebion wedi'u cyflwyno." +#: app/views/contexts/thank_you_context.py:40 +msgid "Your answers have been submitted." +msgstr "Mae eich atebion wedi cael eu cyflwyno." -#: app/views/contexts/view_submitted_response_context.py:39 +#: app/views/contexts/view_submitted_response_context.py:33 msgid "Answers submitted for {ru_name} ({trad_as})" msgstr "Atebion wedi'u cyflwyno ar gyfer {ru_name} ({trad_as})" -#: app/views/contexts/view_submitted_response_context.py:43 +#: app/views/contexts/view_submitted_response_context.py:37 msgid "Answers submitted for {ru_name}" msgstr "Atebion wedi'u cyflwyno ar gyfer {ru_name}" +#: app/views/contexts/view_submitted_response_context.py:41 +msgid "Answers submitted." +msgstr "Atebion wedi'u cyflwyno." + +#: app/views/contexts/preview/preview_question.py:31 +msgid "You can answer with the following options:" +msgstr "Gallwch ateb gyda'r opsiynau canlynol:" + +#: app/views/contexts/preview/preview_question.py:35 +msgid "You can answer with one of the following options:" +msgstr "Gallwch ateb gydag un o'r opsiynau canlynol:" + #: app/views/handlers/confirm_email.py:38 msgid "Yes, send the confirmation email" msgstr "Ydy, anfonwch yr e-bost cadarnhau" -#: app/views/handlers/confirm_email.py:69 +#: app/views/handlers/confirm_email.py:68 msgid "Confirm your email address" msgstr "Cadarnhewch eich cyfeiriad e-bost" @@ -545,17 +557,17 @@ msgstr "Ydy'r cyfeiriad e-bost hwn yn gywir?" #: app/views/handlers/confirm_email.py:102 #: app/views/handlers/confirm_email.py:103 -#: app/views/handlers/individual_response.py:878 +#: app/views/handlers/individual_response.py:880 msgid "No, I need to change it" msgstr "Nac ydy, mae angen i mi ei newid" #: app/views/handlers/confirm_email.py:129 -#: app/views/handlers/confirmation_email.py:68 -#: app/views/handlers/feedback.py:83 app/views/handlers/question.py:165 +#: app/views/handlers/confirmation_email.py:67 +#: app/views/handlers/feedback.py:84 app/views/handlers/question.py:170 msgid "Error: {page_title}" msgstr "Gwall: {page_title}" -#: app/views/handlers/confirmation_email.py:46 +#: app/views/handlers/confirmation_email.py:45 msgid "Confirmation email" msgstr "E-bost cadarnhau" @@ -599,128 +611,128 @@ msgstr "Peidiwch â chynnwys gwybodaeth gyfrinachol, fel eich manylion cyswllt" msgid "Person {list_item_position}" msgstr "Person {list_item_position}" -#: app/views/handlers/individual_response.py:252 +#: app/views/handlers/individual_response.py:254 msgid "Cannot answer questions for others in your household" msgstr "Methu ateb cwestiynau ar ran pobl eraill yn eich cartref" -#: app/views/handlers/individual_response.py:322 +#: app/views/handlers/individual_response.py:324 msgid "How would you like {person_name} to receive a separate census?" msgstr "Sut hoffech chi i {person_name} gael cyfrifiad ar wahân?" -#: app/views/handlers/individual_response.py:345 +#: app/views/handlers/individual_response.py:347 msgid "Text message" msgstr "Neges destun" -#: app/views/handlers/individual_response.py:347 +#: app/views/handlers/individual_response.py:349 msgid "We will need their mobile number for this" msgstr "Bydd angen rhif ffôn symudol yr unigolyn arnom ar gyfer hyn" -#: app/views/handlers/individual_response.py:355 +#: app/views/handlers/individual_response.py:357 msgid "Post" msgstr "Post" -#: app/views/handlers/individual_response.py:357 +#: app/views/handlers/individual_response.py:359 msgid "We can only send this to an unnamed resident at the registered household address" msgstr "Dim ond at breswylydd dienw yng nghyfeiriad cofrestredig y cartref y gallwn anfon hwn" -#: app/views/handlers/individual_response.py:366 +#: app/views/handlers/individual_response.py:368 msgid "It is no longer possible to receive an access code by post." msgstr "Ni ellir cael cod myneidad drwy'r post mwyach." -#: app/views/handlers/individual_response.py:368 +#: app/views/handlers/individual_response.py:370 msgid "Select how to send access code." msgstr "Dewiswch sut i anfon y cod mynediad." -#: app/views/handlers/individual_response.py:371 +#: app/views/handlers/individual_response.py:373 msgid "For someone to complete a separate census, we need to send them an individual access code." msgstr "Er mwyn i rywun allu cwblhau cyfrifiad ar wahân, bydd angen i ni anfon cod mynediad unigol ato." -#: app/views/handlers/individual_response.py:417 +#: app/views/handlers/individual_response.py:419 msgid "Send individual access code" msgstr "Anfon cod mynediad unigol" -#: app/views/handlers/individual_response.py:448 +#: app/views/handlers/individual_response.py:450 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "Sut hoffech chi ateb cwestiynau {person_name_possessive}?" -#: app/views/handlers/individual_response.py:463 +#: app/views/handlers/individual_response.py:465 msgid "I would like to request a separate census for them to complete" msgstr "Hoffwn i wneud cais am gyfrifiad ar wahân i’r unigolyn ei gwblhau" -#: app/views/handlers/individual_response.py:469 +#: app/views/handlers/individual_response.py:471 msgid "I will ask them to answer their own questions" msgstr "Byddaf yn gofyn i’r unigolyn ateb ei gwestiynau ei hun" -#: app/views/handlers/individual_response.py:473 +#: app/views/handlers/individual_response.py:475 msgid "They will need the household access code from the letter we sent you" msgstr "Bydd angen cod mynediad y cartref ar yr unigolyn o’r llythyr y gwnaethom ei anfon atoch" -#: app/views/handlers/individual_response.py:479 +#: app/views/handlers/individual_response.py:481 msgid "I will answer for {person_name}" msgstr "Byddaf yn ateb ar ran {person_name}" -#: app/views/handlers/individual_response.py:521 +#: app/views/handlers/individual_response.py:523 msgid "How to answer questions" msgstr "Sut i ateb cwestiynau" -#: app/views/handlers/individual_response.py:586 +#: app/views/handlers/individual_response.py:588 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "Ydych chi am anfon cod mynediad unigol at {person_name} drwy’r post?" -#: app/views/handlers/individual_response.py:594 +#: app/views/handlers/individual_response.py:596 msgid "A letter with an individual access code will be sent to your registered household address" msgstr "Bydd llythyr â chod mynediad unigol yn cael ei anfon i gyfeiriad cofrestredig eich cartref" -#: app/views/handlers/individual_response.py:601 +#: app/views/handlers/individual_response.py:603 msgid "The letter will be addressed to Individual Resident instead of the name provided" msgstr "Bydd y llythyr wedi'i gyfeirio at y Preswylydd Unigol yn hytrach na'r enw a gafodd ei roi" -#: app/views/handlers/individual_response.py:614 +#: app/views/handlers/individual_response.py:616 msgid "Yes, send the access code by post" msgstr "Ydw, anfonwch y cod mynediad drwy'r post" -#: app/views/handlers/individual_response.py:620 +#: app/views/handlers/individual_response.py:622 msgid "No, send it another way" msgstr "Nac ydw, anfonwch y cod mynediad mewn ffordd arall" -#: app/views/handlers/individual_response.py:653 +#: app/views/handlers/individual_response.py:655 msgid "Confirm address" msgstr "Cadarnhau'r cyfeiriad" -#: app/views/handlers/individual_response.py:708 -#: app/views/handlers/individual_response.py:746 +#: app/views/handlers/individual_response.py:710 +#: app/views/handlers/individual_response.py:748 msgid "Separate Census" msgstr "Cyfrifiad ar wahân" -#: app/views/handlers/individual_response.py:712 +#: app/views/handlers/individual_response.py:714 msgid "Who do you need to request a separate census for?" msgstr "Ar gyfer pwy y mae angen i chi wneud cais am gyfrifiad ar wahân?" -#: app/views/handlers/individual_response.py:770 +#: app/views/handlers/individual_response.py:772 msgid "What is {person_name_possessive} mobile number?" msgstr "Beth yw rhif ffôn symudol {person_name_possessive}?" -#: app/views/handlers/individual_response.py:782 +#: app/views/handlers/individual_response.py:784 msgid "UK mobile number" msgstr "Rhif ffôn symudol yn y Deyrnas Unedig" -#: app/views/handlers/individual_response.py:783 +#: app/views/handlers/individual_response.py:785 msgid "This will not be stored and only used once to send the access code" msgstr "Ni chaiff ei storio a bydd ond yn cael ei ddefnyddio unwaith i anfon y cod mynediad" -#: app/views/handlers/individual_response.py:816 +#: app/views/handlers/individual_response.py:818 msgid "Mobile number" msgstr "Rhif ffôn symudol" -#: app/views/handlers/individual_response.py:865 +#: app/views/handlers/individual_response.py:867 msgid "Is this mobile number correct?" msgstr "Ydy’r rhif ffôn symudol hwn yn gywir?" -#: app/views/handlers/individual_response.py:874 +#: app/views/handlers/individual_response.py:876 msgid "Yes, send the text" msgstr "Ydy, anfonwch y neges destun" -#: app/views/handlers/individual_response.py:912 +#: app/views/handlers/individual_response.py:914 msgid "Confirm mobile number" msgstr "Cadarnhau'r rhif ffôn symudol" @@ -855,7 +867,7 @@ msgstr "Anfon adborth" msgid "If you can’t answer someone else’s questions" msgstr "Os na allwch chi ateb cwestiynau rhywun arall" -#: templates/interstitial.html:23 templates/partials/question.html:25 +#: templates/interstitial.html:23 templates/partials/question.html:24 msgid "If you can’t answer questions for this person" msgstr "Os na allwch chi ateb cwestiynau ar ran yr unigolyn hwn" @@ -867,7 +879,11 @@ msgstr "Cadw a pharhau" msgid "Introduction" msgstr "Cyflwyniad" -#: templates/introduction.html:23 +#: templates/introduction.html:24 +msgid "View the questions you will be asked in this survey" +msgstr "Gweld y cwestiynau a ofynnir i chi yn yr arolwg hwn" + +#: templates/introduction.html:29 msgid "Your response is legally required" msgstr "Mae eich ymateb yn ofynnol yn gyfreithiol" @@ -887,17 +903,21 @@ msgstr "Yn anffodus, dim ond un arolwg y gallwch chi ei gwblhau ar y tro" msgid "Close this window to continue with your current survey" msgstr "Caewch y ffenestr hon i barhau â'ch arolwg presennol" -#: templates/signed-out.html:3 +#: templates/signed-out.html:5 msgid "Signed out" msgstr "Wedi allgofnodi" -#: templates/signed-out.html:6 -msgid "Your survey answers have been saved. You are now signed out" -msgstr "Mae eich atebion i'r arolwg wedi'u cadw. Rydych chi nawr wedi'ch allgofnodi" +#: templates/signed-out.html:16 +msgid "

      Your progress has been saved

      " +msgstr "

      Mae eich cynnydd wedi'i gadw

      " + +#: templates/signed-out.html:23 +msgid "To find further information or resume the survey, return to My Account." +msgstr "I ddod o hyd i ragor o wybodaeth neu i ailddechrau'r arolwg, dychwelwch i Fy Nghyfrif." -#: templates/signed-out.html:9 -msgid "Return to your account" -msgstr "Dychwelyd i'ch cyfrif" +#: templates/signed-out.html:29 +msgid "To resume the survey, re-enter your access code." +msgstr "I ailddechrau'r arolwg, ail-nodwch eich cod mynediad." #: templates/thank-you.html:6 msgid "We’ve received your answers" @@ -999,10 +1019,22 @@ msgstr "Bydd angen i chi fewngofnodi eto i gael mynediad i'c msgid "To access this page you need to re-enter your access code." msgstr "I fynd i'r dudalen hon, bydd angen i chi roi eich cod mynediad eto." +#: templates/errors/401.html:20 templates/errors/403.html:12 +#: templates/errors/404.html:13 templates/errors/500.html:12 +#: templates/errors/submission-failed.html:14 +msgid "Business surveys" +msgstr "Arolygon busnes" + #: templates/errors/401.html:21 msgid "If you are completing a business survey, you need to sign back in to your account." msgstr "Os ydych chi'n cwblhau arolwg busnes, bydd angen i chi fewngofnodi eto i'ch cyfrif." +#: templates/errors/401.html:22 templates/errors/403.html:14 +#: templates/errors/404.html:15 templates/errors/500.html:14 +#: templates/errors/submission-failed.html:16 +msgid "All other surveys" +msgstr "Pob arolwg arall" + #: templates/errors/401.html:23 msgid "If you started your survey using an access code, you need to re-enter your code." msgstr "Os gwnaethoch chi ddechrau eich arolwg gan ddefnyddio cod mynediad, bydd angen i chi roi eich cod eto." @@ -1205,26 +1237,31 @@ msgstr "Dyma'r cwestiwn a gafodd ei weld ddiwethaf yn yr adran hon" msgid "You can also go back to the start of the section" msgstr "Gallwch chi hefyd fynd yn ôl i ddechrau'r adran" -#: templates/partials/question.html:60 +#: templates/partials/preview-question.html:35 +#: templates/partials/question.html:68 +msgid "Or" +msgstr "Neu" + +#: templates/partials/preview-question.html:56 +msgid "{max_characters} characters can be added." +msgstr "Mae modd ychwanegu {max_characters} nod." + +#: templates/partials/question.html:59 msgid "Selecting this will clear your answer" msgstr "Bydd dewis hwn yn clirio eich ateb" -#: templates/partials/question.html:61 +#: templates/partials/question.html:60 msgid "cleared" msgstr "wedi'i glirio" -#: templates/partials/question.html:64 +#: templates/partials/question.html:63 msgid "Selecting this will deselect any selected options" msgstr "Bydd dewis hwn yn dad-ddewis unrhyw opsiynau sydd wedi'u dewis" -#: templates/partials/question.html:65 templates/partials/question.html:73 +#: templates/partials/question.html:64 templates/partials/question.html:72 msgid "deselected" msgstr "dad-ddewiswyd" -#: templates/partials/question.html:69 -msgid "Or" -msgstr "Neu" - #: templates/partials/answers/address.html:9 msgid "Address line 1" msgstr "Llinell cyfeiriad 1" @@ -1386,3 +1423,4 @@ msgstr "Newid" #: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "Newid manylion ar gyfer {item_name}" + From 2fdad05303856d0a8fbb29815831afb34d427823 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:44:48 +0100 Subject: [PATCH 207/567] Schemas v3.50.1 (#1086) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0c8cc2343f..0f8f57d9eb 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.50.0 +v3.50.1 From 984e1e3c12e8a220c6c1e1c23eb6e200bfbd95ef Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 12 Apr 2023 12:52:35 +0100 Subject: [PATCH 208/567] Fix signed-out confirmation in other languages (#1087) --- app/helpers/language_helper.py | 5 +++++ app/helpers/template_helpers.py | 2 +- app/routes/session.py | 2 ++ app/setup.py | 8 +------- tests/app/helpers/test_template_helpers.py | 9 +++++++++ tests/integration/routes/test_cookie.py | 3 ++- tests/integration/routes/test_questionnaire_language.py | 9 +++++++++ 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 006bf5b7f3..1d18dfa46a 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -38,6 +38,7 @@ def handle_language(metadata: Optional[MetadataProxy] = None) -> None: if schema.json["title"] != cookie_session.get("title"): cookie_session["title"] = schema.json["title"] + cookie_session["language_code"] = request_language session_store.session_data.language_code = request_language session_store.save() @@ -49,6 +50,10 @@ def get_languages_context(current_language: str) -> Optional[dict[str, list[dict for language in allowed_languages: context.append(_get_language_context(language, current_language)) return {"languages": context} + + if (language := cookie_session.get("language_code")) and language in LANGUAGE_TEXT: + return {"languages": [_get_language_context(language, language)]} + return None diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 6c7ef517f5..05473809ce 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -209,9 +209,9 @@ def get_survey_config( ) -> SurveyConfig: # The fallback to assigning SURVEY_TYPE to theme is only being added until # business feedback on the differentiation between theme and SURVEY_TYPE. + language = language or get_locale().language if metadata := get_metadata(current_user): - language = language or get_locale().language schema = load_schema_from_metadata(metadata=metadata, language_code=language) survey_theme = theme or get_survey_type() diff --git a/app/routes/session.py b/app/routes/session.py index 8231cbe87c..d8a7e12656 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -123,6 +123,8 @@ def login(): "account_service_log_out_url" ) + cookie_session["language_code"] = metadata.language_code + return redirect(url_for("questionnaire.get_questionnaire")) diff --git a/app/setup.py b/app/setup.py index 451f5edce5..a4f9a6d64f 100644 --- a/app/setup.py +++ b/app/setup.py @@ -24,7 +24,6 @@ from app.authentication.cookie_session import SHA256SecureCookieSessionInterface from app.authentication.user_id_generator import UserIDGenerator from app.cloud_tasks import CloudTaskPublisher, LogCloudTaskPublisher -from app.globals import get_session_store from app.helpers import get_span_and_trace from app.jinja_filters import blueprint as filter_blueprint from app.keys import KEY_PURPOSE_SUBMISSION @@ -480,12 +479,7 @@ def get_minimized_asset(filename): def get_locale(): - session = get_session_store() - - if session and (session_data := session.session_data): - return session_data.language_code - - return None + return cookie_session.get("language_code") def get_timezone(): diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 6bea2382fa..8039d8ad31 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -1116,3 +1116,12 @@ def test_include_csrf_token(app: Flask, include_csrf_token: bool): ).context["include_csrf_token"] assert result == include_csrf_token + + +def test_get_survey_config_language_retrieved_from_cookie(app: Flask): + with app.app_context(): + cookie_session["language_code"] = "cy" + cookie_session["theme"] = SurveyType.SOCIAL + result = get_survey_config() + + assert result.account_service_log_out_url == f"{ACCOUNT_SERVICE_BASE_URL}/cy/start/" diff --git a/tests/integration/routes/test_cookie.py b/tests/integration/routes/test_cookie.py index 053732379f..d22a6af4b3 100644 --- a/tests/integration/routes/test_cookie.py +++ b/tests/integration/routes/test_cookie.py @@ -15,7 +15,8 @@ def test_cookie_contents(self): self.assertIsNotNone(cookie.get("survey_id")) self.assertIsNotNone(cookie.get("user_ik")) self.assertIsNotNone(cookie.get("account_service_base_url")) - self.assertEqual(len(cookie), 9) + self.assertIsNotNone(cookie.get("language_code")) + self.assertEqual(len(cookie), 10) self.assertIsNone(cookie.get("user_id")) self.assertIsNone(cookie.get("_permanent")) diff --git a/tests/integration/routes/test_questionnaire_language.py b/tests/integration/routes/test_questionnaire_language.py index 89ba9f1210..6e570d982b 100644 --- a/tests/integration/routes/test_questionnaire_language.py +++ b/tests/integration/routes/test_questionnaire_language.py @@ -207,3 +207,12 @@ def test_last_viewed_guidance_is_displayed_after_language_switch(self): # Switch the language to welsh and check that the last viewed guidance is still being displayed (in welsh) self.get(f"{self.last_url}&language_code=cy") self.assertInBody("Dyma'r cwestiwn a gafodd ei weld ddiwethaf yn yr adran hon") + + def test_sign_out_cy_survey(self): + # When: load a cy survey + self.launchSurvey("test_language", language_code="cy") + # Then: sign out + self.get(self.getSignOutButton()["href"], follow_redirects=True) + # Check the text and logos are in Welsh + self.assertInBody("Mae eich cynnydd wedi'i gadw") + self.assertInBody("Swyddfa Ystadegau Gwladol") From f5b0cc17952b8fd495b8113cac4c2330165bf7ec Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:35:38 +0100 Subject: [PATCH 209/567] Schema Release v3.51.0 (#1088) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0f8f57d9eb..100a28e0e5 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.50.1 +v3.51.0 From cad85bdbe7ca6a4d973e4001e498099179bcf7b2 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 18 Apr 2023 10:58:47 +0100 Subject: [PATCH 210/567] Update cookie banner to be multilingual (#1090) --- app/translations/messages.pot | 137 ++++++++++++++++++-- babel.cfg | 1 + templates/layouts/_base.html | 1 + templates/layouts/_questionnaire.html | 1 - tests/functional/base_pages/base.page.js | 2 +- tests/functional/spec/cookie_banner.spec.js | 27 ++++ 6 files changed, 157 insertions(+), 12 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index b707c68161..dd769cb1f3 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-04-05 10:55+0100\n" +"POT-Creation-Date: 2023-04-14 12:54+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -50,6 +50,8 @@ msgid "Remove {item_name}" msgstr "" #: app/jinja_filters.py:699 +#: templates/partials/summary/collapsible-summary.html:36 +#: templates/partials/summary/summary.html:20 msgid "No answer provided" msgstr "" @@ -857,7 +859,8 @@ msgstr "" msgid "Done" msgstr "" -#: templates/feedback.html:14 templates/view-submitted-response.html:16 +#: templates/feedback.html:14 templates/preview.html:15 +#: templates/view-submitted-response.html:16 msgid "Back" msgstr "" @@ -880,7 +883,7 @@ msgstr "" msgid "If you can’t answer questions for this person" msgstr "" -#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:27 +#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:26 msgid "Save and continue" msgstr "" @@ -912,6 +915,53 @@ msgstr "" msgid "Close this window to continue with your current survey" msgstr "" +#: templates/preview.html:36 +msgid "Preview of the questions in this survey" +msgstr "" + +#: templates/preview.html:41 +msgid "To answer these questions you need to start survey" +msgstr "" + +#: templates/preview.html:43 +msgid "" +"You may not have to answer all of these questions. The questions you see " +"will depend on the answers you provide." +msgstr "" + +#: templates/preview.html:48 +msgid "Print questions" +msgstr "" + +#: templates/preview.html:60 +msgid "Save questions as PDF" +msgstr "" + +#: templates/partials/introduction/preview.html:29 +#: templates/partials/summary/collapsible-summary.html:14 +#: templates/preview.html:81 +msgid "Show" +msgstr "" + +#: templates/layouts/_base.html:74 +#: templates/partials/introduction/preview.html:30 +#: templates/partials/summary/collapsible-summary.html:15 +#: templates/preview.html:82 +msgid "Hide" +msgstr "" + +#: templates/partials/introduction/preview.html:47 +#: templates/partials/summary/collapsible-summary.html:59 +#: templates/preview.html:102 +msgid "Show all" +msgstr "" + +#: templates/partials/introduction/preview.html:48 +#: templates/partials/summary/collapsible-summary.html:60 +#: templates/preview.html:103 +msgid "Hide all" +msgstr "" + #: templates/signed-out.html:5 msgid "Signed out" msgstr "" @@ -920,13 +970,13 @@ msgstr "" msgid "

      Your progress has been saved

      " msgstr "" -#: templates/signed-out.html:23 +#: templates/signed-out.html:21 msgid "" "To find further information or resume the survey, return " "to My Account." msgstr "" -#: templates/signed-out.html:29 +#: templates/signed-out.html:23 msgid "To resume the survey, re-enter your access code." msgstr "" @@ -972,19 +1022,19 @@ msgid "" "your answers for your records." msgstr "" -#: templates/thank-you.html:71 +#: templates/layouts/_base.html:157 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/thank-you.html:72 +#: templates/layouts/_base.html:158 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/thank-you.html:73 +#: templates/layouts/_base.html:159 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/thank-you.html:74 +#: templates/layouts/_base.html:160 templates/thank-you.html:74 msgid "seconds" msgstr "" @@ -1234,10 +1284,70 @@ msgid "" "href='{contact_us_url}'>contact us" msgstr "" -#: templates/layouts/_questionnaire.html:40 +#: templates/layouts/_base.html:20 +msgid "Previous" +msgstr "" + +#: templates/layouts/_base.html:69 +msgid "Tell us whether you accept cookies" +msgstr "" + +#: templates/layouts/_base.html:70 +msgid "" +"We use cookies to collect information" +" about how you use {cookie_domain}. We use this information to make the " +"website work as well as possible and improve our services." +msgstr "" + +#: templates/layouts/_base.html:71 +msgid "" +"You’ve accepted all cookies. You can change your cookie preferences at any " +"time." +msgstr "" + +#: templates/layouts/_base.html:72 +msgid "Accept all cookies" +msgstr "" + +#: templates/layouts/_base.html:73 +msgid "Set cookie preferences" +msgstr "" + +#: templates/layouts/_base.html:130 +msgid "Skip to main content" +msgstr "" + +#: templates/layouts/_base.html:152 +msgid "You will be signed out soon" +msgstr "" + +#: templates/layouts/_base.html:153 +msgid "It appears you have been inactive for a while." +msgstr "" + +#: templates/layouts/_base.html:154 +msgid "" +"To protect your information, your progress will be saved and you will be " +"signed out in" +msgstr "" + +#: templates/layouts/_base.html:155 +msgid "You are being signed out" +msgstr "" + +#: templates/layouts/_base.html:156 +msgid "Continue survey" +msgstr "" + +#: templates/layouts/_questionnaire.html:39 msgid "Choose another section and return to this later" msgstr "" +#: templates/layouts/configs/_header.html:11 +msgid "Exit" +msgstr "" + #: templates/macros/helpers.html:13 msgid "Interviewer note:" msgstr "" @@ -1481,10 +1591,17 @@ msgstr "" msgid "Start survey" msgstr "" +#: templates/partials/summary/collapsible-summary.html:37 #: templates/partials/summary/list-summary.html:7 +#: templates/partials/summary/summary.html:21 msgid "Change" msgstr "" +#: templates/partials/summary/collapsible-summary.html:38 +#: templates/partials/summary/summary.html:22 +msgid "Change your answer for:" +msgstr "" + #: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "" diff --git a/babel.cfg b/babel.cfg index feab9c5b77..41a54f889d 100644 --- a/babel.cfg +++ b/babel.cfg @@ -2,3 +2,4 @@ [ignore: templates/components/**] [ignore: templates/layout/**] [jinja2: templates/**.html] +extensions=jinja2.ext.do,jinja2.ext.i18n diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 8268ae156d..3e21578aaa 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -72,6 +72,7 @@ "primaryButtonText": _('Accept all cookies'), "secondaryButtonText": _('Set cookie preferences'), "confirmationButtonText": _('Hide'), + 'lang': language_code }) }} {% endif %} diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index b42af9dfa1..9d61efeb53 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -1,6 +1,5 @@ {% extends 'layouts/_base.html' %} {% from "components/button/_macro.njk" import onsButton %} -{% from "components/cookies-banner/_macro.njk" import onsCookiesBanner %} {% set block = content.block %} {% set form = content.form %} diff --git a/tests/functional/base_pages/base.page.js b/tests/functional/base_pages/base.page.js index 15f9e3925c..89b1bfb23e 100644 --- a/tests/functional/base_pages/base.page.js +++ b/tests/functional/base_pages/base.page.js @@ -20,7 +20,7 @@ export default class BasePage { } acceptCookies() { - return ".ons-js-accept-cookies"; + return '[data-button="accept"]'; } submit() { diff --git a/tests/functional/spec/cookie_banner.spec.js b/tests/functional/spec/cookie_banner.spec.js index d4a9525dc2..66d3f7d7fe 100644 --- a/tests/functional/spec/cookie_banner.spec.js +++ b/tests/functional/spec/cookie_banner.spec.js @@ -1,4 +1,5 @@ import InitialPage from "../generated_pages/checkbox/mandatory-checkbox.page"; +import HubPage from "../base_pages/hub.page.js"; describe("Given I am not authenticated and have no cookie,", () => { it("When I visit a page in runner, Then the cookie banner shouldn‘t be displayed", async () => { @@ -30,3 +31,29 @@ describe("Given I start a survey,", () => { await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; }); }); + +describe("Given I start a survey with multiple languages,", () => { + beforeEach(async () => { + await browser.deleteAllCookies(); + }); + it("When I open the page in english, Then the cookie banner should be displayed in english", async () => { + await browser.openQuestionnaire("test_language.json", { + language: "en", + }); + await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Accept additional cookies"); + }); + it("When I open the page in welsh, Then the cookie banner should be displayed in welsh", async () => { + await browser.openQuestionnaire("test_language.json", { + language: "cy", + }); + await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Derbyn cwcis ychwanegol"); + }); + it("When I open the page in english, Then change the language to welsh the cookie banner should be displayed in welsh", async () => { + await browser.openQuestionnaire("test_language.json", { + language: "en", + }); + await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Accept additional cookies"); + await $(HubPage.switchLanguage("cy")).click(); + await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Derbyn cwcis ychwanegol"); + }); +}); From 7c723f3464d314a63e92eb8eee3b264e50f672de Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 24 Apr 2023 14:29:41 +0100 Subject: [PATCH 211/567] Update static content translations (#1093) --- app/translations/cy/LC_MESSAGES/messages.po | 131 ++++++++++++++++++-- app/translations/messages.pot | 2 +- 2 files changed, 120 insertions(+), 13 deletions(-) diff --git a/app/translations/cy/LC_MESSAGES/messages.po b/app/translations/cy/LC_MESSAGES/messages.po index 1fd474d2c2..30cf80b23f 100644 --- a/app/translations/cy/LC_MESSAGES/messages.po +++ b/app/translations/cy/LC_MESSAGES/messages.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: phm\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-04-05 10:55+0100\n" -"PO-Revision-Date: 2023-04-05 16:08\n" +"POT-Creation-Date: 2023-04-14 12:54+0100\n" +"PO-Revision-Date: 2023-04-19 07:32\n" "Last-Translator: \n" "Language-Team: Welsh\n" "MIME-Version: 1.0\n" @@ -59,6 +59,8 @@ msgid "Remove {item_name}" msgstr "Dileu{item_name}" #: app/jinja_filters.py:699 +#: templates/partials/summary/collapsible-summary.html:36 +#: templates/partials/summary/summary.html:20 msgid "No answer provided" msgstr "Dim ateb wedi'i roi" @@ -844,7 +846,8 @@ msgstr "Bydd eich sylwadau yn ein helpu ni i wella ein harolygon. Ni allwn ymate msgid "Done" msgstr "Cwblhawyd" -#: templates/feedback.html:14 templates/view-submitted-response.html:16 +#: templates/feedback.html:14 templates/preview.html:15 +#: templates/view-submitted-response.html:16 msgid "Back" msgstr "Yn ôl" @@ -853,7 +856,7 @@ msgstr "Yn ôl" msgid "There is a problem with your feedback" msgid_plural "There are %(num)s problems with your feedback" msgstr[0] "" -msgstr[1] "Mae problem gyda'ch ateb" +msgstr[1] "Mae problem gyda'ch adborth" msgstr[2] "Mae %(num)s broblem gyda'ch adborth" msgstr[3] "Mae %(num)s problem gyda'ch adborth" msgstr[4] "Mae %(num)s phroblem gyda'ch adborth" @@ -871,7 +874,7 @@ msgstr "Os na allwch chi ateb cwestiynau rhywun arall" msgid "If you can’t answer questions for this person" msgstr "Os na allwch chi ateb cwestiynau ar ran yr unigolyn hwn" -#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:27 +#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:26 msgid "Save and continue" msgstr "Cadw a pharhau" @@ -903,6 +906,51 @@ msgstr "Yn anffodus, dim ond un arolwg y gallwch chi ei gwblhau ar y tro" msgid "Close this window to continue with your current survey" msgstr "Caewch y ffenestr hon i barhau â'ch arolwg presennol" +#: templates/preview.html:36 +msgid "Preview of the questions in this survey" +msgstr "Rhagolwg o'r cwestiynau yn yr arolwg hwn" + +#: templates/preview.html:41 +msgid "To answer these questions you need to start survey" +msgstr "I ateb y cwestiynau hyn mae angen i chi dechrau arolwg" + +#: templates/preview.html:43 +msgid "You may not have to answer all of these questions. The questions you see will depend on the answers you provide." +msgstr "Efallai na fydd yn rhaid i chi ateb pob un o'r cwestiynau hyn. Bydd y cwestiynau a welwch yn dibynnu ar yr atebion a roddwch." + +#: templates/preview.html:48 +msgid "Print questions" +msgstr "Argraffu cwestiynau" + +#: templates/preview.html:60 +msgid "Save questions as PDF" +msgstr "Cadw cwestiynau fel PDF" + +#: templates/partials/introduction/preview.html:29 +#: templates/partials/summary/collapsible-summary.html:14 +#: templates/preview.html:81 +msgid "Show" +msgstr "Dangos" + +#: templates/layouts/_base.html:74 +#: templates/partials/introduction/preview.html:30 +#: templates/partials/summary/collapsible-summary.html:15 +#: templates/preview.html:82 +msgid "Hide" +msgstr "Cuddio" + +#: templates/partials/introduction/preview.html:47 +#: templates/partials/summary/collapsible-summary.html:59 +#: templates/preview.html:102 +msgid "Show all" +msgstr "Dangos popeth" + +#: templates/partials/introduction/preview.html:48 +#: templates/partials/summary/collapsible-summary.html:60 +#: templates/preview.html:103 +msgid "Hide all" +msgstr "Cuddio popeth" + #: templates/signed-out.html:5 msgid "Signed out" msgstr "Wedi allgofnodi" @@ -911,11 +959,11 @@ msgstr "Wedi allgofnodi" msgid "

      Your progress has been saved

      " msgstr "

      Mae eich cynnydd wedi'i gadw

      " -#: templates/signed-out.html:23 +#: templates/signed-out.html:21 msgid "To find further information or resume the survey, return to My Account." msgstr "I ddod o hyd i ragor o wybodaeth neu i ailddechrau'r arolwg, dychwelwch i Fy Nghyfrif." -#: templates/signed-out.html:29 +#: templates/signed-out.html:23 msgid "To resume the survey, re-enter your access code." msgstr "I ailddechrau'r arolwg, ail-nodwch eich cod mynediad." @@ -959,19 +1007,19 @@ msgstr "Cael copi o'ch atebion" msgid "You can save or print your answers for your records." msgstr "Gallwch gadw neu argraffu eich atebion ar gyfer eich cofnodion." -#: templates/thank-you.html:71 +#: templates/layouts/_base.html:157 templates/thank-you.html:71 msgid "minute" msgstr "munud" -#: templates/thank-you.html:72 +#: templates/layouts/_base.html:158 templates/thank-you.html:72 msgid "minutes" msgstr "munudau" -#: templates/thank-you.html:73 +#: templates/layouts/_base.html:159 templates/thank-you.html:73 msgid "second" msgstr "eiliad" -#: templates/thank-you.html:74 +#: templates/layouts/_base.html:160 templates/thank-you.html:74 msgid "seconds" msgstr "eiliadau" @@ -1183,10 +1231,62 @@ msgstr "Gwneud cais am gyfrifiad ar wahân" msgid "To request a census in a different format or for further help, please contact us" msgstr "I wneud cais am gyfrifiad mewn fformat gwahanol neu am fwy o gymorth, cysylltwch â ni" -#: templates/layouts/_questionnaire.html:40 +#: templates/layouts/_base.html:20 +msgid "Previous" +msgstr "Blaenorol" + +#: templates/layouts/_base.html:69 +msgid "Tell us whether you accept cookies" +msgstr "Dywedwch wrthym a ydych chi'n derbyn cwcis" + +#: templates/layouts/_base.html:70 +msgid "We use cookies to collect information about how you use {cookie_domain}. We use this information to make the website work as well as possible and improve our services." +msgstr "Rydym ni'n defnyddio cwcis i gasglu gwybodaeth am y ffordd rydych chi'n defnyddio {cookie_domain}. Rydym ni'n defnyddio'r wybodaeth hon i sicrhau bod y wefan yn gweithio cystal â phosibl ac i wella ein gwasanaethau." + +#: templates/layouts/_base.html:71 +msgid "You’ve accepted all cookies. You can change your cookie preferences at any time." +msgstr "Rydych chi wedi derbyn yr holl gwcis. Gallwch chi newid eich dewisiadau o ran cwcis ar unrhyw adeg." + +#: templates/layouts/_base.html:72 +msgid "Accept all cookies" +msgstr "Derbyn yr holl gwcis" + +#: templates/layouts/_base.html:73 +msgid "Set cookie preferences" +msgstr "Gosod dewisiadau o ran cwcis" + +#: templates/layouts/_base.html:130 +msgid "Skip to main content" +msgstr "Neidio i’r prif gynnwys" + +#: templates/layouts/_base.html:152 +msgid "You will be signed out soon" +msgstr "Byddwch yn cael eich allgofnodi yn fuan" + +#: templates/layouts/_base.html:153 +msgid "It appears you have been inactive for a while." +msgstr "Mae'n ymddangos eich bod wedi bod yn segur ers tro." + +#: templates/layouts/_base.html:154 +msgid "To protect your information, your progress will be saved and you will be signed out in" +msgstr "I ddiogelu eich gwybodaeth, bydd eich cynnydd yn cael ei gadw a byddwch yn cael eich allgofnodi" + +#: templates/layouts/_base.html:155 +msgid "You are being signed out" +msgstr "Rydych chi'n cael eich allgofnodi" + +#: templates/layouts/_base.html:156 +msgid "Continue survey" +msgstr "Parhau â'r arolwg" + +#: templates/layouts/_questionnaire.html:39 msgid "Choose another section and return to this later" msgstr "Dewiswch adran arall a dod yn ôl yn nes ymlaen" +#: templates/layouts/configs/_header.html:11 +msgid "Exit" +msgstr "Ymadael" + #: templates/macros/helpers.html:13 msgid "Interviewer note:" msgstr "Nodyn i’r cyfwelydd:" @@ -1416,10 +1516,17 @@ msgstr "{x} o nodau yn ormod" msgid "Start survey" msgstr "Dechrau'r arolwg" +#: templates/partials/summary/collapsible-summary.html:37 #: templates/partials/summary/list-summary.html:7 +#: templates/partials/summary/summary.html:21 msgid "Change" msgstr "Newid" +#: templates/partials/summary/collapsible-summary.html:38 +#: templates/partials/summary/summary.html:22 +msgid "Change your answer for:" +msgstr "Newidiwch eich ateb ar gyfer:" + #: templates/partials/summary/list-summary.html:8 msgid "Change details for {item_name}" msgstr "Newid manylion ar gyfer {item_name}" diff --git a/app/translations/messages.pot b/app/translations/messages.pot index dd769cb1f3..4e1a813244 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-04-14 12:54+0100\n" +"POT-Creation-Date: 2023-04-21 14:37+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" From 55ad4aea4155a6c37cb2b8fe5798801ba6faeb78 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 25 Apr 2023 09:42:58 +0100 Subject: [PATCH 212/567] Refactor onsQuestion component usage (#1059) --- .design-system-version | 2 +- app/jinja_filters.py | 2 +- app/translations/messages.pot | 61 ++++++-------- .../test/en/test_array_type_definitions.json | 80 +++++++++++++++++++ schemas/test/en/test_benchmark_business.json | 14 +--- .../test/en/test_interstitial_definition.json | 10 --- schemas/test/en/test_question_definition.json | 29 +------ templates/partials/answer-guidance.html | 11 --- templates/partials/contents.html | 2 +- templates/partials/definition.html | 13 +-- .../individual-response-guidance.html | 10 --- templates/partials/introduction/preview.html | 10 --- templates/partials/preview-question.html | 1 - templates/partials/question-definition.html | 7 -- templates/partials/question.html | 60 ++++++++------ .../partials/summary/collapsible-summary.html | 12 +-- templates/preview.html | 12 +-- templates/view-submitted-response.html | 2 +- tests/functional/generate_pages.py | 6 +- .../spec/interstitial_definition.spec.js | 16 ++-- .../spec/question_definitions.spec.js | 14 ++-- .../question_definitions_array_type.spec.js | 32 ++++++++ ...est_questionnaire_array_type_definition.py | 28 +++++++ .../test_questionnaire_interstitial.py | 4 - .../test_questionnaire_question_definition.py | 13 --- 25 files changed, 230 insertions(+), 221 deletions(-) create mode 100644 schemas/test/en/test_array_type_definitions.json delete mode 100644 templates/partials/question-definition.html create mode 100644 tests/functional/spec/question_definitions_array_type.spec.js create mode 100644 tests/integration/questionnaire/test_questionnaire_array_type_definition.py diff --git a/.design-system-version b/.design-system-version index 58d83ee7ce..1a5e111a58 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -62.1.0 +62.2.0 diff --git a/app/jinja_filters.py b/app/jinja_filters.py index a52d6d4248..3a9faeaa2f 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -216,7 +216,7 @@ def setAttributes( @blueprint.app_template_filter() def should_wrap_with_fieldset(question: dict[str, list]) -> bool: # Logic for when to wrap with a fieldset comes from - # https://ons-design-system.netlify.app/patterns/question/ + # https://service-manual.ons.gov.uk/design-system/components/fieldset if question["type"] == "DateRange": return False diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 4e1a813244..c56f4bb583 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -50,7 +50,7 @@ msgid "Remove {item_name}" msgstr "" #: app/jinja_filters.py:699 -#: templates/partials/summary/collapsible-summary.html:36 +#: templates/partials/summary/collapsible-summary.html:26 #: templates/partials/summary/summary.html:20 msgid "No answer provided" msgstr "" @@ -879,7 +879,7 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:23 templates/partials/question.html:24 +#: templates/interstitial.html:23 templates/partials/question.html:45 msgid "If you can’t answer questions for this person" msgstr "" @@ -937,28 +937,15 @@ msgstr "" msgid "Save questions as PDF" msgstr "" -#: templates/partials/introduction/preview.html:29 -#: templates/partials/summary/collapsible-summary.html:14 -#: templates/preview.html:81 -msgid "Show" -msgstr "" - -#: templates/layouts/_base.html:74 -#: templates/partials/introduction/preview.html:30 -#: templates/partials/summary/collapsible-summary.html:15 -#: templates/preview.html:82 -msgid "Hide" -msgstr "" - -#: templates/partials/introduction/preview.html:47 -#: templates/partials/summary/collapsible-summary.html:59 -#: templates/preview.html:102 +#: templates/partials/introduction/preview.html:37 +#: templates/partials/summary/collapsible-summary.html:49 +#: templates/preview.html:92 msgid "Show all" msgstr "" -#: templates/partials/introduction/preview.html:48 -#: templates/partials/summary/collapsible-summary.html:60 -#: templates/preview.html:103 +#: templates/partials/introduction/preview.html:38 +#: templates/partials/summary/collapsible-summary.html:50 +#: templates/preview.html:93 msgid "Hide all" msgstr "" @@ -1314,6 +1301,10 @@ msgstr "" msgid "Set cookie preferences" msgstr "" +#: templates/layouts/_base.html:74 +msgid "Hide" +msgstr "" + #: templates/layouts/_base.html:130 msgid "Skip to main content" msgstr "" @@ -1352,12 +1343,6 @@ msgstr "" msgid "Interviewer note:" msgstr "" -#: templates/partials/answer-guidance.html:18 -#: templates/partials/definition.html:18 -#: templates/partials/individual-response-guidance.html:13 -msgid "Hide this" -msgstr "" - #: templates/partials/email-form.html:25 msgid "Email address" msgstr "" @@ -1382,13 +1367,13 @@ msgstr "" msgid "Give feedback" msgstr "" -#: templates/partials/individual-response-guidance.html:24 +#: templates/partials/individual-response-guidance.html:14 msgid "" "You can share your household access code with the people you " "live with so they can complete their own sections." msgstr "" -#: templates/partials/individual-response-guidance.html:25 +#: templates/partials/individual-response-guidance.html:15 msgid "" "If this is not possible, there are other ways each person " "can complete their own census." @@ -1404,28 +1389,28 @@ msgid "" " of the section" msgstr "" -#: templates/partials/preview-question.html:35 -#: templates/partials/question.html:68 +#: templates/partials/preview-question.html:34 +#: templates/partials/question.html:81 msgid "Or" msgstr "" -#: templates/partials/preview-question.html:56 +#: templates/partials/preview-question.html:55 msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:59 +#: templates/partials/question.html:72 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:60 +#: templates/partials/question.html:73 msgid "cleared" msgstr "" -#: templates/partials/question.html:63 +#: templates/partials/question.html:76 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:64 templates/partials/question.html:72 +#: templates/partials/question.html:77 templates/partials/question.html:85 msgid "deselected" msgstr "" @@ -1591,13 +1576,13 @@ msgstr "" msgid "Start survey" msgstr "" -#: templates/partials/summary/collapsible-summary.html:37 +#: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/list-summary.html:7 #: templates/partials/summary/summary.html:21 msgid "Change" msgstr "" -#: templates/partials/summary/collapsible-summary.html:38 +#: templates/partials/summary/collapsible-summary.html:28 #: templates/partials/summary/summary.html:22 msgid "Change your answer for:" msgstr "" diff --git a/schemas/test/en/test_array_type_definitions.json b/schemas/test/en/test_array_type_definitions.json new file mode 100644 index 0000000000..6c7c7ec1b2 --- /dev/null +++ b/schemas/test/en/test_array_type_definitions.json @@ -0,0 +1,80 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Other input fields", + "theme": "default", + "description": "A questionnaire to test definitions (array type).", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "id": "definition-group", + "blocks": [ + { + "type": "Question", + "id": "definition-block", + "question": { + "id": "question", + "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", + "type": "General", + "definitions": [ + { + "title": "What is a photovoltaic system?", + "contents": [ + { + "description": "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky." + } + ] + } + ], + "answers": [ + { + "type": "Radio", + "id": "radio-mandatory-answer", + "mandatory": false, + "options": [ + { + "label": "Yes, I do connect a battery", + "value": "Yes, I do connect a battery" + }, + { + "label": "No, I don’t connect a battery", + "value": "No, I don’t connect a battery" + } + ] + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 9f89e73c68..7b78c7e793 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -275,16 +275,10 @@ } ] }, - "definitions": [ - { - "title": "What is work in progress?", - "contents": [ - { - "description": "This refers to goods and services that have been partially completed (e.g. a solicitor working on a legal case over a period of time and being paid at the end of the contract for the services provided i.e. unbilled work)." - } - ] - } - ], + "definition": { + "title": "What is work in progress?", + "content": "This refers to goods and services that have been partially completed (e.g. a solicitor working on a legal case over a period of time and being paid at the end of the contract for the services provided i.e. unbilled work)." + }, "type": "General", "answers": [ { diff --git a/schemas/test/en/test_interstitial_definition.json b/schemas/test/en/test_interstitial_definition.json index 4c9eed4472..27ae0408ee 100644 --- a/schemas/test/en/test_interstitial_definition.json +++ b/schemas/test/en/test_interstitial_definition.json @@ -53,16 +53,6 @@ ] } }, - { - "definition": { - "title": "Questionnaire", - "contents": [ - { - "description": "A set of printed or written questions with a choice of answers, devised for the purposes of a survey or statistical study" - } - ] - } - }, { "description": "You can now continue." } diff --git a/schemas/test/en/test_question_definition.json b/schemas/test/en/test_question_definition.json index edd1576229..708c9a82e1 100644 --- a/schemas/test/en/test_question_definition.json +++ b/schemas/test/en/test_question_definition.json @@ -43,31 +43,10 @@ "id": "question", "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", "type": "General", - "definitions": [ - { - "title": "What is a photovoltaic system?", - "contents": [ - { - "description": "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky." - } - ] - }, - { - "title": "Why use LiFePO4 batteries?", - "contents": [ - { - "title": "3 Benefits of LifePO4 batteries." - }, - { - "list": [ - "LifePO4 batteries have a life span 10 times longer than that of traditional lead acid batteries. This dramatically reduces the need for battery changes.", - "Lithium iron phosphate batteries operate with much lower resistance and consequently recharge at a faster rate.", - "LifeP04 lightweight batteries are lighter than lead acid batteries, usually weighing about 1/4 less." - ] - } - ] - } - ], + "definition": { + "title": "What is a photovoltaic system?", + "content": "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky." + }, "answers": [ { "type": "Radio", diff --git a/templates/partials/answer-guidance.html b/templates/partials/answer-guidance.html index 110a9876da..f9286bac10 100644 --- a/templates/partials/answer-guidance.html +++ b/templates/partials/answer-guidance.html @@ -13,17 +13,6 @@ }, "contentAttributes": { "data-qa": "answer-guidance-" ~ answer.id ~ "-content" - }, - "button": { - "close": _("Hide this"), - "contextSuffix": "content", - "attributes": { - "data-ga": "click", - "data-ga-category": "Answer guidance", - "data-ga-action": "Close panel", - "data-ga-label": _(answer_guidance.schema_item.show_guidance), - "data-qa": "answer-guidance-" ~ answer.id ~ "-button" - } } }) %}
      diff --git a/templates/partials/contents.html b/templates/partials/contents.html index 23148d2f46..3aede16413 100644 --- a/templates/partials/contents.html +++ b/templates/partials/contents.html @@ -4,7 +4,7 @@ {% for item in contents %} {%- if item.definition -%} {% set definition = item.definition %} - {% set definition_id = 'definition-' ~ definition_count.value %} + {% set definition_id = 'definition' %} {% set category = 'Definition' %} {%- include 'partials/definition.html' -%} {% set definition_count.value = definition_count.value + 1 %} diff --git a/templates/partials/definition.html b/templates/partials/definition.html index f758221b0d..3b00c4fca6 100644 --- a/templates/partials/definition.html +++ b/templates/partials/definition.html @@ -12,18 +12,7 @@ "data-qa": definition_id ~ "-title" }, "contentAttributes": { - "data-qa": definition_id ~ "-content" - }, - "button": { - "close": _("Hide this"), - "contextSuffix": "content", - "attributes": { - "data-ga": "click", - "data-ga-category": category, - "data-ga-action": "Close panel", - "data-ga-label": definition.title, - "data-qa": definition_id ~ "-button" - } + "data-qa": definition_id ~ "-content" } }) %} {% set contents = definition.contents %} diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index 0a43fa04b4..6c2a1a81d6 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -8,16 +8,6 @@ "data-ga-category": "definition", "data-ga-action": "Open panel", "data-ga-label": title - }, - "button": { - "close": _("Hide this"), - "contextSuffix": "content", - "attributes": { - "data-ga": "click", - "data-ga-category": "definition", - "data-ga-action": "Close panel", - "data-ga-label": title - } } }) %}
      diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index 117711e408..5eb1c8837a 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -24,16 +24,6 @@

      {{ intro.title }}

      "data-ga-category": "Preview Survey", "data-ga-action": "Open panel", "data-ga-label": question.question - }, - "button": { - "open": _('Show'), - "close": _('Hide'), - "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Open panel", - "data-ga-label": question.question - } } } %} diff --git a/templates/partials/preview-question.html b/templates/partials/preview-question.html index 0d79df5a80..0d6b2d2a3b 100644 --- a/templates/partials/preview-question.html +++ b/templates/partials/preview-question.html @@ -24,7 +24,6 @@

      {{ question.title }}

      "id": "question-guidance-" ~ question.id, "classes": "ons-u-mb-m" }) %} - {% set contents = question.guidance.contents %} {% include 'partials/contents.html' %} {% endcall %} {% endif %} diff --git a/templates/partials/question-definition.html b/templates/partials/question-definition.html deleted file mode 100644 index 8cb5e658a0..0000000000 --- a/templates/partials/question-definition.html +++ /dev/null @@ -1,7 +0,0 @@ -{% from "components/details/_macro.njk" import onsDetails %} - -{% for definition in question.definitions %} - {% set definition_id = "question-definition-" ~ loop.index %} - {% set category = 'Question definition' %} - {%- include 'partials/definition.html' -%} -{% endfor %} diff --git a/templates/partials/question.html b/templates/partials/question.html index 83234fcb6e..af8eb0ce79 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -13,29 +13,43 @@ {% set question_instruction = format_paragraphs(question.instruction) %} {% set question_error = form.question_errors[question.id] %} -{%- set question_definition -%} - {%- if question.definitions -%} - {%- include 'partials/question-definition.html' -%} - {%- endif -%} -{%- endset -%} +{%- if question.definition -%} + {% set definition_id = "question-definition" %} + {% set question_definition = { + "title": question.definition.title, + "id": definition_id, + "content": question.definition.content, + "headingAttributes": { + "data-ga": "click", + "data-ga-category": "Question definition", + "data-ga-action": "Open panel", + "data-ga-label": question.definition.title, + "data-qa": definition_id ~ "-title" + }, + "contentAttributes": { + "data-qa": definition_id ~ "-content" + } + } %} +{% elif question.definitions %} + {%- set question_definitions -%} + {% for definition in question.definitions %} + {% set definition_id = "question-definition" %} + {% set category = 'Question definition' %} + {%- include 'partials/definition.html' -%} + {% endfor %} + {%- endset -%} +{%- endif -%} {% set individual_response_guidance %} - {%- if show_individual_response_guidance == True -%} - {% set title = _("If you can’t answer questions for this person") %} - {% include 'partials/individual-response-guidance.html' %} - {%- endif -%} + {%- if show_individual_response_guidance == True -%} + {% set title = _("If you can’t answer questions for this person") %} + {% include 'partials/individual-response-guidance.html' %} + {%- endif -%} {% endset %} -{% set question_warning %} - {%- if question.warning -%} - {% call onsPanel({ - "id": "question-warning-" ~ question.id, - "type": "warn" - }) %} -

      {{ question.warning }}

      - {% endcall %} - {% endif %} -{% endset %} +{%- if question.warning -%} + {% set question_warning = {"body": question.warning} %} +{% endif %} {% set question_guidance %} {%- if question.guidance -%} @@ -44,7 +58,6 @@ "id": "question-guidance-" ~ question.id, "classes": "ons-u-mb-m" }) %} - {% set contents = question.guidance.contents %} {% include 'partials/contents.html' %} {% endcall %} {% endif %} @@ -88,6 +101,8 @@ "title": title, "description": question_description, "instruction": question_instruction, + "warning": question_warning, + "definition": question_definition, "legendIsQuestionTitle": should_wrap_with_fieldset(question) }) %} {%- if content.list and content.list.list_items -%} @@ -96,9 +111,8 @@ {% include 'partials/summary/list-summary.html' %}
      {% endif %} - {{ question_warning }} {{ individual_response_guidance }} - {{ question_definition }} + {{ question_definitions }} {{ question_guidance }} {% if question_error %} @@ -111,7 +125,7 @@ "data-ga-action": question.type, "data-ga-label": question.id } - }%} + } %} {% call onsError(config) %} {{ question_answers }} {% endcall %} diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index 8ee344d198..723de179ca 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -9,17 +9,7 @@ {%- set item = { "title": group.title, "id": group.id, - "content": "", - "button": { - "open": _('Show'), - "close": _('Hide'), - "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Open panel", - "data-ga-label": group.title - } - } + "content": "" } -%} {%- set summary %} diff --git a/templates/preview.html b/templates/preview.html index d5309be46b..0aaf290f6f 100644 --- a/templates/preview.html +++ b/templates/preview.html @@ -76,17 +76,7 @@

      {{ _("Preview of the questions in this survey") }}

      {%- for section in content.preview.sections if section.blocks -%} {%- set item = { "title": section.title, - "id": section.id, - "button": { - "open": _('Show'), - "close": _('Hide'), - "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Open panel", - "data-ga-label": section.title - } - } + "id": section.id } -%} {%- set block_previews = preview_blocks_for_sections(blocks=section["blocks"]) -%} diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index d9bb9150e6..c227f3e4a2 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -21,7 +21,7 @@ } } ] -}%} +} %} {% block main %}

      diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 8e93a640e9..7473435ccd 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -85,13 +85,13 @@ """ DEFINITION_TITLE_GETTER = Template( - r""" definitionTitle(definitionIndex) { return `[data-qa='${definitionId}-${definitionIndex}-title']`; } + r""" definitionTitle() { return `[data-qa='${definitionId}-title']`; } """ ) DEFINITION_CONTENT_GETTER = Template( - r""" definitionContent(definitionIndex) { return `[data-qa='${definitionId}-${definitionIndex}-content']`; } + r""" definitionContent() { return `[data-qa='${definitionId}-content']`; } """ ) @@ -465,7 +465,7 @@ def process_answer(answer, page_spec, long_names, page_name): def process_question(question, page_spec, num_questions, page_name): long_names = long_names_required(question, num_questions) - if "definitions" in question: + if ("definition" in question) or ("definitions" in question): context = {"definitionId": "question-definition"} process_definition(context, page_spec) diff --git a/tests/functional/spec/interstitial_definition.spec.js b/tests/functional/spec/interstitial_definition.spec.js index 2c8346f241..8cde0becf8 100644 --- a/tests/functional/spec/interstitial_definition.spec.js +++ b/tests/functional/spec/interstitial_definition.spec.js @@ -7,21 +7,15 @@ describe("Component: Interstitial Definition", () => { }); it("When there is a definition on an interstitial, then the page is displayed correctly", async () => { - await expect(await $(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal(""); - - await expect(await $(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); + await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).to.equal(""); }); it("When I click on a definition title, the content is displayed for just that definition", async () => { - await $(InterstitialDefinitionPage.definitionTitle(1)).click(); - - await expect(await $(InterstitialDefinitionPage.definitionTitle(1)).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent(1)).getText()).to.equal("In a way that accomplishes a desired aim or result"); + await $(InterstitialDefinitionPage.definitionTitle()).click(); - await expect(await $(InterstitialDefinitionPage.definitionTitle(2)).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent(2)).getText()).to.equal(""); + await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).to.be.true; + await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).to.equal("In a way that accomplishes a desired aim or result"); }); }); }); diff --git a/tests/functional/spec/question_definitions.spec.js b/tests/functional/spec/question_definitions.spec.js index 08dec6560a..ee925326d7 100644 --- a/tests/functional/spec/question_definitions.spec.js +++ b/tests/functional/spec/question_definitions.spec.js @@ -7,26 +7,26 @@ describe("Component: Definition", () => { }); it("When I click the title link, then the description should be visible", async () => { - await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); // When - await $(DefinitionPage.definitionTitle("1")).click(); + await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.contain( + await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." ); }); it("When I click the title link twice, then the description should not be visible", async () => { - await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); // When - await $(DefinitionPage.definitionTitle("1")).click(); - await $(DefinitionPage.definitionTitle("1")).click(); + await $(DefinitionPage.definitionTitle()).click(); + await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent(1)).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); }); }); }); diff --git a/tests/functional/spec/question_definitions_array_type.spec.js b/tests/functional/spec/question_definitions_array_type.spec.js new file mode 100644 index 0000000000..b7528f4e21 --- /dev/null +++ b/tests/functional/spec/question_definitions_array_type.spec.js @@ -0,0 +1,32 @@ +import DefinitionPage from "../generated_pages/question_definition/definition-block.page"; + +describe("Component: Definition", () => { + describe("Given I start a survey which contains question definition", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_array_type_definitions.json"); + }); + + it("When I click the title link, then the description should be visible", async () => { + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + + // When + await $(DefinitionPage.definitionTitle()).click(); + + // Then + await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( + "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." + ); + }); + + it("When I click the title link twice, then the description should not be visible", async () => { + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + + // When + await $(DefinitionPage.definitionTitle()).click(); + await $(DefinitionPage.definitionTitle()).click(); + + // Then + await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + }); + }); +}); diff --git a/tests/integration/questionnaire/test_questionnaire_array_type_definition.py b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py new file mode 100644 index 0000000000..eea36010b4 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py @@ -0,0 +1,28 @@ +from tests.integration.integration_test_case import IntegrationTestCase +from tests.integration.questionnaire import SUBMIT_URL_PATH, THANK_YOU_URL_PATH + + +class TestQuestionnaireQuestionDefinition(IntegrationTestCase): + def test_question_definition(self): + # Given I launch a questionnaire with definitions + self.launchSurvey("test_array_type_definitions") + + # When I start the survey I am presented with the definitions title and content correctly + self.assertInBody( + "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?" + ) + + self.assertInBody("What is a photovoltaic system?") + self.assertInBody( + "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, " + "which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. " + "The mount may be fixed, or use a solar tracker to follow the sun across the sky." + ) + + # When we continue we go to the summary page + self.post() + self.assertInUrl(SUBMIT_URL_PATH) + + # And Submit my answers + self.post() + self.assertInUrl(THANK_YOU_URL_PATH) diff --git a/tests/integration/questionnaire/test_questionnaire_interstitial.py b/tests/integration/questionnaire/test_questionnaire_interstitial.py index 461414291c..510c3a26e2 100644 --- a/tests/integration/questionnaire/test_questionnaire_interstitial.py +++ b/tests/integration/questionnaire/test_questionnaire_interstitial.py @@ -25,11 +25,7 @@ def test_interstitial_can_continue_and_submit(self): def test_interstitial_definition(self): self.launchSurvey("test_interstitial_definition") self.assertInBody("Successfully") - self.assertInBody("Questionnaire") self.assertInBody("In a way that accomplishes a desired aim or result") - self.assertInBody( - "A set of printed or written questions with a choice of answers, devised for the purposes of a survey or statistical study" - ) def test_interstitial_content_variant_definition(self): self.launchSurvey("test_interstitial_definition") diff --git a/tests/integration/questionnaire/test_questionnaire_question_definition.py b/tests/integration/questionnaire/test_questionnaire_question_definition.py index 0a2f0ddce2..640e18d602 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_definition.py +++ b/tests/integration/questionnaire/test_questionnaire_question_definition.py @@ -19,19 +19,6 @@ def test_question_definition(self): "The mount may be fixed, or use a solar tracker to follow the sun across the sky." ) - self.assertInBody("Why use LiFePO4 batteries?") - self.assertInBody("3 Benefits of LifePO4 batteries.") - self.assertInBody( - "LifePO4 batteries have a life span 10 times longer than that of traditional lead acid batteries. " - "This dramatically reduces the need for battery changes." - ) - self.assertInBody( - "Lithium iron phosphate batteries operate with much lower resistance and consequently recharge at a faster rate." - ) - self.assertInBody( - "LifeP04 lightweight batteries are lighter than lead acid batteries, usually weighing about 1/4 less." - ) - # When we continue we go to the summary page self.post() self.assertInUrl(SUBMIT_URL_PATH) From c1231126389abc289012aec4b1c20e9f1d3cead9 Mon Sep 17 00:00:00 2001 From: Kyle Lawson_ONS <108286538+kylelawsonAND@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:15:14 +0100 Subject: [PATCH 213/567] Add type hints to routes module (#1080) * Typing added to schema.py and utilities/schema.py * Type ignores added where necessary in utilities/schema.py * Type ignores for flush.py and related files * Type hinting added shcema_helpers.py * Removing duplicate decorator and unnecessary call for QuestionnaireStore in dump.py * Fixing type hinting and defaults for language_code in schema.py + starting type hinting in individual_response.py * Adding type hinting to both files individual_response.py. * Type ignoring ListStore.__getitem__ accessor with a str | None from handlers/individual_response.py where it will be str when invoked. * Type hinting added to questionnaire.py and related classes/methods in globals.py, location.py and section.py * Type hinting added to schema.py * Type hinting added to errors.py and affected members + bugfix to change made to schema.py * Type hinting added to errors.py and template_helpers.py * Fix language changing (#1076) * Schemas v3.49.0 (#1081) * Update ChromeDriver to 111 (#1082) * Confirmation page after signout (#1070) Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> * Schemas v3.50.0 (#1084) * Upload latest static translations (#1083) * Schemas v3.50.1 (#1086) * Fixing styling issues * Fixing styling issues (mypy + pylint) * Running black formatter * Running isort * Fixing mypy errors. * Rerunning black * Removing unused function in globals.py * Fix signed-out confirmation in other languages (#1087) * Adding routes to MyPy.ini, starting app/views/handlers/individual_response.py + changing response_metadata to MutableMapping. * Continued type hinting in individual_response.py + enforcing a str for PlaceholderRenderer as str is provided elsewhere except IndividualResponseHandler where it implicitly gives a str instead of str | None * Type hinting attribute response_metadata: MutableMapping[str, Any] throughout * Fixing MyPy errors for routes/individual_response.py and views/handlers/individual_response.py * Schema Release v3.51.0 (#1088) * Fixing MyPy errors for routes/errors.py * Fixing MyPy errors for routes/session.py and associated members * Fixing MyPy errors for routes/questionnaire.py and associated members * Running make format * Fixing make format breaking type ignores * kwarg enforcement on Router * Adding missing Router kwarg usage + make format * kwarg usage of method in flush.py * Removing enforcement, but keeping usage of, kwarg on Router * make format * Update cookie banner to be multilingual (#1090) * Addressing comments in errors.py * Addressing comments in flush.py * using get_locale for language in individual_response.py * Explicit TypeAlias hinting, BadRequest handling instead of subtype handling and removing redundant type ignore. * Mapping[str, Any] -> Mapping (same for MutableMapping) * Updating python type hinting guide * Addressing type hint comments * Update static content translations (#1093) * Refactor onsQuestion component usage (#1059) * Format and resolving merging conflicts. --------- Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: petechd <53475968+petechd@users.noreply.github.com> Co-authored-by: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> --- app/authentication/authenticator.py | 8 +- app/authentication/user.py | 4 +- app/data_models/questionnaire_store.py | 8 +- app/forms/questionnaire_form.py | 10 +- app/globals.py | 11 +- app/helpers/language_helper.py | 7 +- app/helpers/schema_helpers.py | 21 ++- app/helpers/template_helpers.py | 16 +- app/questionnaire/location.py | 8 +- app/questionnaire/placeholder_parser.py | 2 +- app/questionnaire/placeholder_renderer.py | 2 +- app/questionnaire/relationship_location.py | 8 +- app/questionnaire/router.py | 4 +- app/questionnaire/rules/rule_evaluator.py | 4 +- app/questionnaire/value_source_resolver.py | 6 +- app/questionnaire/variants.py | 4 +- app/routes/dump.py | 66 +++---- app/routes/errors.py | 69 +++++--- app/routes/flush.py | 76 +++++---- app/routes/individual_response.py | 78 ++++++--- app/routes/questionnaire.py | 119 +++++++++---- app/routes/schema.py | 6 +- app/routes/session.py | 39 +++-- app/submitter/convert_payload_0_0_1.py | 4 +- app/submitter/convert_payload_0_0_3.py | 4 +- app/submitter/converter.py | 6 +- app/submitter/converter_v2.py | 8 +- app/utilities/metadata_parser.py | 4 +- app/utilities/metadata_parser_v2.py | 13 +- app/utilities/schema.py | 48 ++++-- .../contexts/calculated_summary_context.py | 13 +- app/views/contexts/context.py | 16 +- app/views/contexts/preview_context.py | 4 +- app/views/contexts/section_preview_context.py | 4 +- app/views/contexts/section_summary_context.py | 4 +- app/views/contexts/summary/block.py | 4 +- app/views/contexts/summary/group.py | 6 +- .../contexts/summary/list_collector_block.py | 4 +- app/views/handlers/block_factory.py | 28 +-- app/views/handlers/feedback.py | 6 +- app/views/handlers/individual_response.py | 161 ++++++++++-------- app/views/handlers/section.py | 37 ++-- doc/python-type-hinting.md | 59 ++++++- mypy.ini | 5 + 44 files changed, 625 insertions(+), 389 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 0d508d56e5..4a30e20b1c 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta, timezone -from typing import Any, Mapping, Optional, Union +from typing import Any, Mapping, MutableMapping, Optional from uuid import uuid4 from blinker import ANY @@ -132,7 +132,7 @@ def _create_session_data_from_metadata(metadata: Mapping[str, Any]) -> SessionDa ) -def store_session(metadata: dict[str, Any]) -> None: +def store_session(metadata: MutableMapping) -> None: """ Store new session and metadata :param metadata: metadata parsed from jwt token @@ -161,12 +161,12 @@ def store_session(metadata: dict[str, Any]) -> None: logger.info("user authenticated") -def decrypt_token(encrypted_token: str) -> dict[str, Union[str, list, int]]: +def decrypt_token(encrypted_token: str | None) -> dict[str, Any]: if not encrypted_token: raise NoTokenException("Please provide a token") logger.debug("decrypting token") - decrypted_token: dict[str, Union[str, list, int]] = decrypt( + decrypted_token: dict[str, Any] = decrypt( token=encrypted_token, key_store=current_app.eq["key_store"], # type: ignore key_purpose=KEY_PURPOSE_AUTHENTICATION, diff --git a/app/authentication/user.py b/app/authentication/user.py index 59b5309728..b32af75709 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -1,10 +1,8 @@ -from typing import Optional - from flask_login import UserMixin class User(UserMixin): - def __init__(self, user_id: Optional[str], user_ik: Optional[str]) -> None: + def __init__(self, user_id: str | None, user_ik: str | None) -> None: if user_id and user_ik: self.user_id = user_id self.user_ik = user_ik diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 1c4953d069..f4352112c7 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import TYPE_CHECKING, Any, Mapping, Optional +from typing import TYPE_CHECKING, MutableMapping, Optional from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -26,12 +26,12 @@ def __init__( if version is None: version = self.get_latest_version_number() self.version = version - self._metadata: dict[str, Any] = {} + self._metadata: MutableMapping = {} # self.metadata is a read-only view over self._metadata self.metadata: Optional[MetadataProxy] = ( MetadataProxy.from_dict(self._metadata) if self._metadata else None ) - self.response_metadata: Mapping[str, Any] = {} + self.response_metadata: MutableMapping = {} self.list_store = ListStore() self.answer_store = AnswerStore() self.progress_store = ProgressStore() @@ -53,7 +53,7 @@ def __init__( def get_latest_version_number(self) -> int: return self.LATEST_VERSION - def set_metadata(self, to_set: dict[str, Any]) -> QuestionnaireStore: + def set_metadata(self, to_set: MutableMapping) -> QuestionnaireStore: """ Set metadata. This should only be used where absolutely necessary. Metadata should normally be read only. diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index e43c9a829b..674214f509 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -5,7 +5,7 @@ from collections.abc import Callable from datetime import datetime, timedelta, timezone from decimal import Decimal -from typing import Any, Mapping, Optional, Sequence, Union +from typing import Any, Mapping, MutableMapping, Optional, Sequence, Union from dateutil.relativedelta import relativedelta from flask_wtf import FlaskForm @@ -47,10 +47,10 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping[str, Any], + response_metadata: MutableMapping, location: Union[None, Location, RelationshipLocation], progress_store: ProgressStore, - **kwargs: Union[MultiDict[str, Any], Mapping[str, Any], None], + **kwargs: Union[MultiDict, Mapping, None], ): self.schema = schema self.question = question_schema @@ -455,7 +455,7 @@ def get_answer_fields( answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, - response_metadata: Mapping[str, Any], + response_metadata: MutableMapping, location: Location | RelationshipLocation | None, progress_store: ProgressStore, ) -> dict[str, FieldHandler]: @@ -598,7 +598,7 @@ def generate_form( answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, - response_metadata: Mapping[str, Any], + response_metadata: MutableMapping, progress_store: ProgressStore, location: Location | RelationshipLocation | None = None, data: dict[str, Any] | None = None, diff --git a/app/globals.py b/app/globals.py index a5987cbaf9..1323d60928 100644 --- a/app/globals.py +++ b/app/globals.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta, timezone -from typing import Optional from flask import current_app, g from flask import session as cookie_session @@ -7,7 +6,6 @@ from app.authentication.user import User from app.data_models import QuestionnaireStore -from app.data_models.answer_store import AnswerStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore @@ -33,7 +31,7 @@ def get_questionnaire_store(user_id: str, user_ik: str) -> QuestionnaireStore: return store -def get_session_store() -> Optional[SessionStore]: +def get_session_store() -> SessionStore | None: if USER_IK not in cookie_session or EQ_SESSION_ID not in cookie_session: return None @@ -92,7 +90,7 @@ def create_session_store( ) -def get_metadata(user: User) -> Optional[MetadataProxy]: +def get_metadata(user: User) -> MetadataProxy | None: if user.is_anonymous: logger.debug("anonymous user requesting metadata get instance") return None @@ -101,11 +99,6 @@ def get_metadata(user: User) -> Optional[MetadataProxy]: return questionnaire_store.metadata -def get_answer_store(user: User) -> AnswerStore: - questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) - return questionnaire_store.answer_store - - def get_view_submitted_response_expiration_time(submitted_at: datetime) -> datetime: view_submitted_expiry_seconds = current_app.config[ "VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS" diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 1d18dfa46a..e35e0756d7 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -1,4 +1,3 @@ -from typing import Optional, Union from urllib.parse import urlencode from flask import g, request @@ -16,7 +15,7 @@ } -def handle_language(metadata: Optional[MetadataProxy] = None) -> None: +def handle_language(metadata: MetadataProxy | None = None) -> None: session_store = get_session_store() if session_store and session_store.session_data: @@ -43,7 +42,7 @@ def handle_language(metadata: Optional[MetadataProxy] = None) -> None: session_store.save() -def get_languages_context(current_language: str) -> Optional[dict[str, list[dict]]]: +def get_languages_context(current_language: str) -> dict[str, list[dict]] | None: context = [] allowed_languages = g.get("allowed_languages") if allowed_languages and len(allowed_languages) > 1: @@ -59,7 +58,7 @@ def get_languages_context(current_language: str) -> Optional[dict[str, list[dict def _get_language_context( language_code: str, current_language: str -) -> dict[str, Union[str, bool]]: +) -> dict[str, str | bool]: return { "ISOCode": language_code, "url": _get_query_string_with_language(language_code), diff --git a/app/helpers/schema_helpers.py b/app/helpers/schema_helpers.py index f163afeaf5..0f995b808f 100644 --- a/app/helpers/schema_helpers.py +++ b/app/helpers/schema_helpers.py @@ -1,14 +1,20 @@ from functools import wraps -from typing import Any, Callable +from typing import Callable, Concatenate, ParamSpec, TypeVar from flask_login import current_user from werkzeug.exceptions import Unauthorized from app.globals import get_metadata, get_session_store +from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.utilities.schema import load_schema_from_metadata +T = TypeVar("T") +P = ParamSpec("P") -def with_schema(function: Callable) -> Any: + +def with_schema( + function: Callable[Concatenate[QuestionnaireSchema, P], T] +) -> Callable[P, T]: """Adds the survey schema as the first argument to the function being wrapped. Use on flask request handlers or methods called by flask request handlers. @@ -23,16 +29,19 @@ def get_block(routing_path, schema, *args): """ @wraps(function) - def wrapped_function(*args: Any, **kwargs: Any) -> Any: + def wrapped_function(*args: P.args, **kwargs: P.kwargs) -> T: session_store = get_session_store() - if not session_store or not session_store.session_data: + if ( + not session_store + or not session_store.session_data + or not (metadata := get_metadata(current_user)) + ): raise Unauthorized - metadata = get_metadata(current_user) language_code = session_store.session_data.language_code schema = load_schema_from_metadata( - metadata=metadata, language_code=language_code # type: ignore + metadata=metadata, language_code=language_code ) return function(schema, *args, **kwargs) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 05473809ce..10f82a2be9 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -1,5 +1,5 @@ from functools import cached_property, lru_cache -from typing import Any, Mapping, Optional, Type, Union +from typing import Any, Type from flask import current_app from flask import render_template as flask_render_template @@ -98,7 +98,7 @@ def context(self) -> dict[str, Any]: @property def service_links_context( self, - ) -> Optional[dict[str, Union[dict[str, str], list[dict]]]]: + ) -> dict[str, dict[str, str] | list[dict]] | None: ru_ref = ( metadata["ru_ref"] if (metadata := get_metadata(current_user)) else None ) @@ -165,7 +165,7 @@ def footer_context(self) -> dict[str, Any]: return context @cached_property - def _footer_warning(self) -> Optional[str]: + def _footer_warning(self) -> str | None: if self._is_post_submission: footer_warning: str = lazy_gettext( "Make sure you leave this page or close your browser if using a shared device" @@ -202,10 +202,10 @@ def survey_config_mapping( def get_survey_config( *, - base_url: Optional[str] = None, - theme: Optional[SurveyType] = None, - language: Optional[str] = None, - schema: Optional[QuestionnaireSchema] = None, + base_url: str | None = None, + theme: SurveyType | None = None, + language: str | None = None, + schema: QuestionnaireSchema | None = None, ) -> SurveyConfig: # The fallback to assigning SURVEY_TYPE to theme is only being added until # business feedback on the differentiation between theme and SURVEY_TYPE. @@ -228,7 +228,7 @@ def get_survey_config( ) -def render_template(template: str, **kwargs: Union[None, str, Mapping]) -> str: +def render_template(template: str, **kwargs: Any) -> str: session_expires_at = None language = get_locale().language if session_store := get_session_store(): diff --git a/app/questionnaire/location.py b/app/questionnaire/location.py index a3f293f7d4..e9cfbf0ee1 100644 --- a/app/questionnaire/location.py +++ b/app/questionnaire/location.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Mapping, Optional +from typing import Mapping from flask import url_for @@ -24,9 +24,9 @@ class Location: """ section_id: str - block_id: Optional[str] = None - list_name: Optional[str] = None - list_item_id: Optional[str] = None + block_id: str | None = None + list_name: str | None = None + list_item_id: str | None = None def __hash__(self): return hash(frozenset(self.__dict__.values())) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index a2964e01b8..bb4e2eba83 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -39,7 +39,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, renderer: "PlaceholderRenderer", progress_store: ProgressStore, diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 1b2faa66b4..aa6106f89c 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -26,7 +26,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, progress_store: ProgressStore, location: Location | RelationshipLocation | None = None, diff --git a/app/questionnaire/relationship_location.py b/app/questionnaire/relationship_location.py index da15643dd4..e42c918f56 100644 --- a/app/questionnaire/relationship_location.py +++ b/app/questionnaire/relationship_location.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Mapping, Optional +from typing import Mapping from flask import url_for @@ -10,9 +10,9 @@ class RelationshipLocation: section_id: str block_id: str - list_name: str - list_item_id: str - to_list_item_id: Optional[str] = None + list_name: str | None + list_item_id: str | None + to_list_item_id: str | None = None def for_json(self) -> Mapping: attributes = vars(self) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index a2a91bc9ca..d701c9ddf2 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -1,4 +1,4 @@ -from typing import Generator, Mapping, Optional +from typing import Generator, Mapping, MutableMapping, Optional from flask import url_for @@ -20,7 +20,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, ): self._schema = schema self._answer_store = answer_store diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 02046a1d07..8173521a82 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from datetime import date from decimal import Decimal -from typing import Generator, Iterable, Mapping, Optional, Sequence, Union +from typing import Generator, Iterable, MutableMapping, Optional, Sequence, Union from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy @@ -27,7 +27,7 @@ class RuleEvaluator: answer_store: AnswerStore list_store: ListStore metadata: MetadataProxy | None - response_metadata: Mapping + response_metadata: MutableMapping location: Location | RelationshipLocation | None progress_store: ProgressStore routing_path_block_ids: Iterable | None = None diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 8cc2a7de37..79e3606810 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from decimal import Decimal -from typing import Any, Callable, Iterable, Mapping, Optional, Union +from typing import Any, Callable, Iterable, Mapping, MutableMapping, Optional, Union from markupsafe import Markup @@ -27,7 +27,7 @@ class ValueSourceResolver: answer_store: AnswerStore list_store: ListStore metadata: MetadataProxy | None - response_metadata: Mapping + response_metadata: MutableMapping[str, Any] schema: QuestionnaireSchema location: Location | RelationshipLocation | None list_item_id: str | None @@ -200,7 +200,7 @@ def resolve( return self.list_item_id if source == "response_metadata": - return self.response_metadata.get(value_source.get("identifier")) + return self.response_metadata.get(value_source["identifier"]) if source == "calculated_summary": return self._resolve_calculated_summary_value_source( diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index 13b0cf7416..c0be5ee7b1 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -1,3 +1,5 @@ +from typing import MutableMapping + from werkzeug.datastructures import ImmutableDict from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -86,7 +88,7 @@ def transform_variants( block, schema, metadata, - response_metadata, + response_metadata: MutableMapping, answer_store, list_store, current_location, diff --git a/app/routes/dump.py b/app/routes/dump.py index 81e9d0462e..c22765dd50 100644 --- a/app/routes/dump.py +++ b/app/routes/dump.py @@ -1,38 +1,23 @@ -from functools import wraps - -from flask import Blueprint, g -from flask_babel import get_locale +from flask import Blueprint from flask_login import current_user, login_required from app.authentication.roles import role_required -from app.globals import get_metadata, get_questionnaire_store +from app.data_models import QuestionnaireStore +from app.globals import get_questionnaire_store +from app.helpers.schema_helpers import with_schema from app.helpers.session_helpers import with_questionnaire_store +from app.questionnaire import QuestionnaireSchema from app.questionnaire.router import Router from app.utilities.json import json_dumps -from app.utilities.schema import load_schema_from_metadata from app.views.handlers.submission import SubmissionHandler dump_blueprint = Blueprint("dump", __name__) -def requires_schema(func): - @wraps(func) - def wrapper(*args, **kwargs): - # pylint: disable=assigning-non-slot - metadata = get_metadata(current_user) - g.schema = load_schema_from_metadata( - metadata=metadata, language_code=get_locale().language - ) - result = func(g.schema, *args, **kwargs) - return result - - return wrapper - - @dump_blueprint.route("/dump/debug", methods=["GET"]) @login_required @role_required("dumper") -def dump_debug(): +def dump_debug() -> str: questionnaire_store = get_questionnaire_store( current_user.user_id, current_user.user_ik ) @@ -43,15 +28,17 @@ def dump_debug(): @login_required @role_required("dumper") @with_questionnaire_store -@requires_schema -def dump_routing(schema, questionnaire_store): +@with_schema +def dump_routing( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> tuple[str, int]: router = Router( - schema, - questionnaire_store.answer_store, - questionnaire_store.list_store, - questionnaire_store.progress_store, - questionnaire_store.metadata, - questionnaire_store.response_metadata, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, ) response = [ @@ -70,21 +57,20 @@ def dump_routing(schema, questionnaire_store): @login_required @role_required("dumper") @with_questionnaire_store -@requires_schema -def dump_submission(schema, questionnaire_store): +@with_schema +def dump_submission( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> tuple[str, int]: router = Router( - schema, - questionnaire_store.answer_store, - questionnaire_store.list_store, - questionnaire_store.progress_store, - questionnaire_store.metadata, - questionnaire_store.response_metadata, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, ) routing_path = router.full_routing_path() - questionnaire_store = get_questionnaire_store( - current_user.user_id, current_user.user_ik - ) submission_handler = SubmissionHandler(schema, questionnaire_store, routing_path) diff --git a/app/routes/errors.py b/app/routes/errors.py index b5b011a38e..2d8f8d2b6c 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -1,4 +1,4 @@ -from typing import Tuple +from typing import Any from flask import Blueprint, request from flask.helpers import url_for @@ -7,7 +7,13 @@ from flask_wtf.csrf import CSRFError from sdc.crypto.exceptions import InvalidTokenException from structlog import contextvars, get_logger -from werkzeug.exceptions import BadRequestKeyError +from werkzeug.exceptions import ( + BadRequest, + Forbidden, + MethodNotAllowed, + NotFound, + Unauthorized, +) from app.authentication.no_questionnaire_state_exception import ( NoQuestionnaireStateException, @@ -34,7 +40,7 @@ errors_blueprint = Blueprint("errors", __name__) -def log_exception(exception, status_code): +def log_exception(exception: Exception, status_code: int) -> None: if metadata := get_metadata(current_user): contextvars.bind_contextvars(tx_id=metadata.tx_id) @@ -48,7 +54,9 @@ def log_exception(exception, status_code): ) -def _render_error_page(status_code, template=None, **kwargs): +def _render_error_page( + status_code: int, template: str | int | None = None, **kwargs: Any +) -> tuple[str, int]: handle_language() business_survey_config = get_survey_config(theme=SurveyType.BUSINESS) other_survey_config = get_survey_config( @@ -75,8 +83,10 @@ def _render_error_page(status_code, template=None, **kwargs): @errors_blueprint.app_errorhandler(400) -@errors_blueprint.app_errorhandler(BadRequestKeyError) -def bad_request(exception=None): +@errors_blueprint.app_errorhandler(BadRequest) +def bad_request( + exception: BadRequest, +) -> tuple[str, int]: log_exception(exception, 400) return _render_error_page(400, template="500") @@ -85,38 +95,46 @@ def bad_request(exception=None): @errors_blueprint.app_errorhandler(CSRFError) @errors_blueprint.app_errorhandler(NoTokenException) @errors_blueprint.app_errorhandler(NoQuestionnaireStateException) -def unauthorized(exception=None) -> Tuple[str, int]: +@errors_blueprint.app_errorhandler(Unauthorized) +def unauthorized( + exception: CSRFError + | NoTokenException + | NoQuestionnaireStateException + | Unauthorized, +) -> tuple[str, int]: log_exception(exception, 401) return _render_error_page(401, template="401") @errors_blueprint.app_errorhandler(PreviouslySubmittedException) -def previously_submitted(exception=None): +def previously_submitted( + exception: PreviouslySubmittedException, +) -> tuple[str, int]: log_exception(exception, 401) return _render_error_page(401, "previously-submitted") @errors_blueprint.app_errorhandler(InvalidTokenException) -def forbidden(exception=None): +def forbidden(exception: InvalidTokenException) -> tuple[str, int]: log_exception(exception, 403) return _render_error_page(403) @errors_blueprint.app_errorhandler(405) -def method_not_allowed(exception=None): +def method_not_allowed(exception: MethodNotAllowed) -> tuple[str, int]: log_exception(exception, 405) return _render_error_page(405, template="404") @errors_blueprint.app_errorhandler(403) @errors_blueprint.app_errorhandler(404) -def http_exception(exception): +def http_exception(exception: NotFound | Forbidden) -> tuple[str, int]: log_exception(exception, exception.code) return _render_error_page(exception.code) @errors_blueprint.app_errorhandler(Exception) -def internal_server_error(exception=None): +def internal_server_error(exception: Exception) -> tuple[str, int]: try: log_exception(exception, 500) return _render_error_page(500) @@ -130,7 +148,9 @@ def internal_server_error(exception=None): @errors_blueprint.app_errorhandler(IndividualResponseLimitReached) -def too_many_individual_response_requests(exception=None): +def too_many_individual_response_requests( + exception: IndividualResponseLimitReached, +) -> tuple[str, int]: log_exception(exception, 429) title = lazy_gettext( "You have reached the maximum number of individual access codes" @@ -149,7 +169,9 @@ def too_many_individual_response_requests(exception=None): @errors_blueprint.app_errorhandler(FeedbackLimitReached) -def too_many_feedback_requests(exception=None): +def too_many_feedback_requests( + exception: FeedbackLimitReached, +) -> tuple[str, int]: log_exception(exception, 429) title = lazy_gettext( "You have reached the maximum number of times for submitting feedback" @@ -168,13 +190,17 @@ def too_many_feedback_requests(exception=None): @errors_blueprint.app_errorhandler(SubmissionFailedException) -def submission_failed(exception=None): +def submission_failed( + exception: SubmissionFailedException, +) -> tuple[str, int]: log_exception(exception, 500) return _render_error_page(500, template="submission-failed") @errors_blueprint.app_errorhandler(IndividualResponseFulfilmentRequestPublicationFailed) -def individual_response_fulfilment_request_publication_failed(exception): +def individual_response_fulfilment_request_publication_failed( + exception: IndividualResponseFulfilmentRequestPublicationFailed, +) -> tuple[str, int]: log_exception(exception, 500) if "mobile_number" in request.args: @@ -189,8 +215,9 @@ def individual_response_fulfilment_request_publication_failed(exception): title = lazy_gettext("Sorry, there was a problem sending the access code") retry_url = url_for( blueprint_method, - list_item_id=request.view_args["list_item_id"], - **request.args, + # Type ignore: Request will be not None as this function will only run when a request handle raises and exception + list_item_id=request.view_args["list_item_id"], # type: ignore + **request.args, # type: ignore ) retry_message = lazy_gettext( "You can try to request a new access code again." @@ -211,7 +238,9 @@ def individual_response_fulfilment_request_publication_failed(exception): @errors_blueprint.app_errorhandler(ConfirmationEmailFulfilmentRequestPublicationFailed) -def confirmation_email_fulfilment_request_publication_failed(exception): +def confirmation_email_fulfilment_request_publication_failed( + exception: ConfirmationEmailFulfilmentRequestPublicationFailed, +) -> tuple[str, int]: log_exception(exception, 500) title = lazy_gettext("Sorry, there was a problem sending the confirmation email") @@ -234,7 +263,7 @@ def confirmation_email_fulfilment_request_publication_failed(exception): @errors_blueprint.app_errorhandler(FeedbackUploadFailed) -def feedback_upload_failed(exception): +def feedback_upload_failed(exception: FeedbackUploadFailed) -> tuple[str, int]: log_exception(exception, 500) title = lazy_gettext("Sorry, there is a problem") retry_message = lazy_gettext( diff --git a/app/routes/flush.py b/app/routes/flush.py index 924439c2e3..ed613e0050 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -1,19 +1,23 @@ from datetime import datetime, timezone +from typing import Iterable, TypeAlias from flask import Blueprint, Response, current_app, request, session from sdc.crypto.decrypter import decrypt from sdc.crypto.encrypter import encrypt +from sdc.crypto.key_store import KeyStore from structlog import contextvars, get_logger from app.authentication.auth_payload_version import AuthPayloadVersion from app.authentication.user import User +from app.authentication.user_id_generator import UserIDGenerator from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy -from app.globals import get_answer_store, get_metadata, get_questionnaire_store +from app.globals import get_metadata, get_questionnaire_store from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SUBMISSION from app.questionnaire import QuestionnaireSchema from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath +from app.submitter import GCSSubmitter, LogSubmitter, RabbitMQSubmitter from app.submitter.converter import convert_answers from app.submitter.converter_v2 import convert_answers_v2 from app.submitter.submission_failed import SubmissionFailedException @@ -25,9 +29,11 @@ logger = get_logger() +Submitter: TypeAlias = GCSSubmitter | LogSubmitter | RabbitMQSubmitter + @flush_blueprint.route("/flush", methods=["POST"]) -def flush_data(): +def flush_data() -> Response: if session: session.clear() @@ -38,7 +44,7 @@ def flush_data(): decrypted_token = decrypt( token=encrypted_token, - key_store=current_app.eq["key_store"], + key_store=_get_keystore(), key_purpose=KEY_PURPOSE_AUTHENTICATION, leeway=current_app.config["EQ_JWT_LEEWAY_IN_SECONDS"], ) @@ -56,42 +62,43 @@ def flush_data(): return Response(status=403) -def _submit_data(user): - answer_store = get_answer_store(user) +def _submit_data(user: User) -> bool: + questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) - if answer_store: - questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) - answer_store = questionnaire_store.answer_store - metadata = questionnaire_store.metadata - response_metadata = questionnaire_store.response_metadata - progress_store = questionnaire_store.progress_store - list_store = questionnaire_store.list_store + if questionnaire_store and questionnaire_store.answer_store: + # Type ignore: The presence of an answer_store implicitly verifies that there must be metadata populated and thus can safely be used non-optionally. + metadata: MetadataProxy = questionnaire_store.metadata # type: ignore submitted_at = datetime.now(timezone.utc) schema = load_schema_from_metadata( - metadata=metadata, language_code=metadata.language_code + metadata=metadata, language_code=metadata.language_code # type: ignore ) router = Router( - schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, ) full_routing_path = router.full_routing_path() message: str = _get_converted_answers_message( - full_routing_path, metadata, questionnaire_store, schema, submitted_at + full_routing_path=full_routing_path, + metadata=metadata, + questionnaire_store=questionnaire_store, + schema=schema, + submitted_at=submitted_at, ) - encrypted_message = encrypt( - message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION - ) + encrypted_message = encrypt(message, _get_keystore(), KEY_PURPOSE_SUBMISSION) additional_metadata = get_receipting_metadata(metadata) - sent = current_app.eq["submitter"].send_message( + # Type ignore: Instance attribute 'eq' is a dict with key "submitter" with value of type GCSSubmitter, LogSubmitter or RabbitMQSubmitter + submitter: Submitter = current_app.eq["submitter"] # type: ignore + + sent = submitter.send_message( encrypted_message, tx_id=metadata.tx_id, case_id=metadata.case_id, @@ -110,7 +117,7 @@ def _submit_data(user): def _get_converted_answers_message( - full_routing_path: RoutingPath, + full_routing_path: Iterable[RoutingPath], metadata: MetadataProxy, questionnaire_store: QuestionnaireStore, schema: QuestionnaireSchema, @@ -129,17 +136,24 @@ def _get_converted_answers_message( ) return json_dumps( answer_converter( - schema, - questionnaire_store, - full_routing_path, - submitted_at, + schema=schema, + questionnaire_store=questionnaire_store, + routing_path=full_routing_path, + submitted_at=submitted_at, flushed=True, ) ) -def _get_user(response_id): - id_generator = current_app.eq["id_generator"] +def _get_user(response_id: str) -> User: + # Type ignore: Instance attribute 'eq' is a dict with key "id_generator" with value of type UserIDGenerator + id_generator: UserIDGenerator = current_app.eq["id_generator"] # type: ignore user_id = id_generator.generate_id(response_id) user_ik = id_generator.generate_ik(response_id) return User(user_id, user_ik) + + +def _get_keystore() -> KeyStore: + # Type ignore: Instance attribute 'eq' is a dict with key "key_store" with value of type KeyStore + key_store: KeyStore = current_app.eq["key_store"] # type: ignore + return key_store diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index 9cc39d9049..f835f64c56 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -4,16 +4,20 @@ from itsdangerous import BadSignature from structlog import contextvars, get_logger from werkzeug.exceptions import BadRequest +from werkzeug.wrappers.response import Response from app.authentication.no_questionnaire_state_exception import ( NoQuestionnaireStateException, ) -from app.globals import get_metadata, get_questionnaire_store, get_session_store +from app.data_models import QuestionnaireStore +from app.data_models.metadata_proxy import MetadataProxy +from app.globals import get_metadata, get_questionnaire_store from app.helpers import url_safe_serializer from app.helpers.language_helper import handle_language from app.helpers.schema_helpers import with_schema from app.helpers.session_helpers import with_questionnaire_store from app.helpers.template_helpers import render_template +from app.questionnaire import QuestionnaireSchema from app.utilities.schema import load_schema_from_metadata from app.views.handlers.individual_response import ( IndividualResponseChangeHandler, @@ -35,7 +39,7 @@ @login_required @individual_response_blueprint.before_request -def before_individual_response_request(): +def before_individual_response_request() -> Response | None: if request.method == "OPTIONS": return None @@ -65,6 +69,7 @@ def before_individual_response_request(): "individual-response request", method=request.method, url_path=request.full_path ) + # Ensures langauge is set in the SessionStore handle_language(metadata) # pylint: disable=assigning-non-slot @@ -77,9 +82,11 @@ def before_individual_response_request(): @individual_response_blueprint.route("/", methods=["GET"]) @with_questionnaire_store @with_schema -def request_individual_response(schema, questionnaire_store): - language_code = get_session_store().session_data.language_code - list_item_id = request.args.get("list_item_id") +def request_individual_response( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> str: + language_code: str = get_locale().language + list_item_id: str | None = request.args.get("list_item_id") individual_response_handler = IndividualResponseHandler( schema=schema, @@ -96,8 +103,12 @@ def request_individual_response(schema, questionnaire_store): @individual_response_blueprint.route("//how", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def individual_response_how(schema, questionnaire_store, list_item_id): - language_code = get_session_store().session_data.language_code +def individual_response_how( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_item_id: str, +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseHowHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -116,8 +127,12 @@ def individual_response_how(schema, questionnaire_store, list_item_id): @individual_response_blueprint.route("//change", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def individual_response_change(schema, questionnaire_store, list_item_id): - language_code = get_session_store().session_data.language_code +def individual_response_change( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_item_id: str, +) -> Response | str | None: + language_code: str = get_locale().language individual_response_handler = IndividualResponseChangeHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -138,8 +153,12 @@ def individual_response_change(schema, questionnaire_store, list_item_id): ) @with_questionnaire_store @with_schema -def individual_response_post_address_confirm(schema, questionnaire_store, list_item_id): - language_code = get_session_store().session_data.language_code +def individual_response_post_address_confirm( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_item_id: str, +) -> Response | str: + language_code: str = get_locale().language try: individual_response_handler = IndividualResponsePostAddressConfirmHandler( schema=schema, @@ -161,8 +180,10 @@ def individual_response_post_address_confirm(schema, questionnaire_store, list_i @individual_response_blueprint.route("/post/confirmation", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def individual_response_post_address_confirmation(schema, questionnaire_store): - language_code = get_session_store().session_data.language_code +def individual_response_post_address_confirmation( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -175,7 +196,8 @@ def individual_response_post_address_confirmation(schema, questionnaire_store): if request.method == "POST": return redirect(url_for("questionnaire.get_questionnaire")) - metadata = questionnaire_store.metadata + # Type ignore: @with_schema guarantees that metadata is present via QuestionnaireStore + metadata: MetadataProxy = questionnaire_store.metadata # type: ignore return render_template( template="individual_response/confirmation-post", @@ -189,8 +211,10 @@ def individual_response_post_address_confirmation(schema, questionnaire_store): @individual_response_blueprint.route("/who", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def individual_response_who(schema, questionnaire_store): - language_code = get_session_store().session_data.language_code +def individual_response_who( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseWhoHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -210,8 +234,12 @@ def individual_response_who(schema, questionnaire_store): ) @with_questionnaire_store @with_schema -def individual_response_text_message(schema, questionnaire_store, list_item_id): - language_code = get_session_store().session_data.language_code +def individual_response_text_message( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_item_id: str, +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseTextHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -232,8 +260,12 @@ def individual_response_text_message(schema, questionnaire_store, list_item_id): ) @with_questionnaire_store @with_schema -def individual_response_text_message_confirm(schema, questionnaire_store, list_item_id): - language_code = get_session_store().session_data.language_code +def individual_response_text_message_confirm( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_item_id: str, +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseTextConfirmHandler( schema=schema, questionnaire_store=questionnaire_store, @@ -252,8 +284,10 @@ def individual_response_text_message_confirm(schema, questionnaire_store, list_i @individual_response_blueprint.route("/text/confirmation", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def individual_response_text_message_confirmation(schema, questionnaire_store): - language_code = get_session_store().session_data.language_code +def individual_response_text_message_confirmation( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> Response | str: + language_code: str = get_locale().language individual_response_handler = IndividualResponseHandler( schema=schema, questionnaire_store=questionnaire_store, diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 52c05f1cb1..7f54dfce8d 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Union +from typing import Mapping import flask_babel from flask import Blueprint, g, redirect, request, send_file @@ -16,7 +16,7 @@ from app.authentication.no_questionnaire_state_exception import ( NoQuestionnaireStateException, ) -from app.data_models import QuestionnaireStore +from app.data_models import QuestionnaireStore, SessionStore from app.globals import ( get_metadata, get_questionnaire_store, @@ -70,7 +70,7 @@ @questionnaire_blueprint.before_request @login_required -def before_questionnaire_request(): +def before_questionnaire_request() -> Response | None: if request.method == "OPTIONS": return None @@ -115,7 +115,7 @@ def before_questionnaire_request(): @post_submission_blueprint.before_request @login_required -def before_post_submission_request(): +def before_post_submission_request() -> None: if request.method == "OPTIONS": return None @@ -153,14 +153,16 @@ def before_post_submission_request(): @questionnaire_blueprint.route("/", methods=["GET", "POST"]) @with_questionnaire_store @with_schema -def get_questionnaire(schema, questionnaire_store): +def get_questionnaire( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> Response | str: router = Router( - schema, - questionnaire_store.answer_store, - questionnaire_store.list_store, - questionnaire_store.progress_store, - questionnaire_store.metadata, - questionnaire_store.response_metadata, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, ) if not router.can_access_hub(): @@ -203,7 +205,7 @@ def get_questionnaire(schema, questionnaire_store): @with_schema def submit_questionnaire( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore -) -> Union[Response, str]: +) -> Response | str: try: submit_questionnaire_handler = SubmitQuestionnaireHandler( schema, questionnaire_store, flask_babel.get_locale().language @@ -232,7 +234,9 @@ def submit_questionnaire( @questionnaire_blueprint.route("/preview", methods=["GET"]) @with_questionnaire_store @with_schema -def get_preview(schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore): +def get_preview( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> str: try: preview_context = PreviewContext( language=flask_babel.get_locale().language, @@ -265,7 +269,12 @@ def get_preview(schema: QuestionnaireSchema, questionnaire_store: QuestionnaireS ) @with_questionnaire_store @with_schema -def get_section(schema, questionnaire_store, section_id, list_item_id=None): +def get_section( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + section_id: str, + list_item_id: str | None = None, +) -> Response | str: try: section_handler = SectionHandler( schema=schema, @@ -301,7 +310,13 @@ def get_section(schema, questionnaire_store, section_id, list_item_id=None): ) @with_questionnaire_store @with_schema -def block(schema, questionnaire_store, block_id, list_name=None, list_item_id=None): +def block( + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + block_id: str, + list_name: str | None = None, + list_item_id: str | None = None, +) -> Response | str: try: block_handler = get_block_handler( schema=schema, @@ -350,13 +365,13 @@ def block(schema, questionnaire_store, block_id, list_name=None, list_item_id=No @with_questionnaire_store @with_schema def relationships( - schema, - questionnaire_store, - list_name=None, - list_item_id=None, - to_list_item_id=None, - block_id="relationships", -): + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + list_name: str | None = None, + list_item_id: str | None = None, + to_list_item_id: str | None = None, + block_id: str = "relationships", +) -> Response | str: try: block_handler = get_block_handler( schema=schema, @@ -397,8 +412,13 @@ def relationships( @with_questionnaire_store @with_session_store @with_schema -def get_thank_you(schema, session_store, questionnaire_store): - thank_you = ThankYou(schema, session_store, questionnaire_store.submitted_at) +def get_thank_you( + schema: QuestionnaireSchema, + session_store: SessionStore, + questionnaire_store: QuestionnaireStore, +) -> Response | str: + # Type ignore: Endpoint is only called upon submission + thank_you = ThankYou(schema, session_store, questionnaire_store.submitted_at) # type: ignore if request.method == "POST": confirmation_email = thank_you.confirmation_email @@ -420,9 +440,12 @@ def get_thank_you(schema, session_store, questionnaire_store): error_message=str(confirmation_email.form.errors["email"][0]), ) + # Type ignore: Session data exists at point of submission show_feedback_call_to_action = Feedback.is_enabled( schema - ) and not Feedback.is_limit_reached(session_store.session_data) + ) and not Feedback.is_limit_reached( + session_store.session_data # type: ignore + ) return render_template( template=thank_you.template, @@ -438,7 +461,9 @@ def get_thank_you(schema, session_store, questionnaire_store): @post_submission_blueprint.route("view-response/", methods=["GET"]) @with_questionnaire_store @with_schema -def get_view_submitted_response(schema, questionnaire_store): +def get_view_submitted_response( + schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore +) -> str: try: view_submitted_response = ViewSubmittedResponse( schema, @@ -514,7 +539,9 @@ def get_view_submitted_response_pdf( @post_submission_blueprint.route("confirmation-email/send", methods=["GET", "POST"]) @with_schema @with_session_store -def send_confirmation_email(session_store, schema): +def send_confirmation_email( + session_store: SessionStore, schema: QuestionnaireSchema +) -> Response | str: try: confirmation_email = ConfirmationEmail( session_store, schema, serialised_email=request.args.get("email") @@ -550,7 +577,11 @@ def send_confirmation_email(session_store, schema): @with_questionnaire_store @with_schema @with_session_store -def confirm_confirmation_email(session_store, schema, questionnaire_store): +def confirm_confirmation_email( + session_store: SessionStore, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, +) -> Response | str: try: confirm_email = ConfirmEmail( questionnaire_store, @@ -577,8 +608,11 @@ def confirm_confirmation_email(session_store, schema, questionnaire_store): @post_submission_blueprint.route("confirmation-email/sent", methods=["GET"]) @with_schema @with_session_store -def get_confirmation_email_sent(session_store, schema): - if not session_store.session_data.confirmation_email_count: +def get_confirmation_email_sent( + session_store: SessionStore, schema: QuestionnaireSchema +) -> str: + # Type ignore: Session data exists for routes decorated with @login_required, which this is + if not session_store.session_data.confirmation_email_count: # type: ignore raise NotFound try: @@ -587,11 +621,13 @@ def get_confirmation_email_sent(session_store, schema): raise BadRequest from exc show_send_another_email_guidance = not ConfirmationEmail.is_limit_reached( - session_store.session_data + session_store.session_data # type: ignore ) show_feedback_call_to_action = Feedback.is_enabled( schema - ) and not Feedback.is_limit_reached(session_store.session_data) + ) and not Feedback.is_limit_reached( + session_store.session_data # type: ignore + ) return render_template( template="confirmation-email-sent", @@ -612,7 +648,11 @@ def get_confirmation_email_sent(session_store, schema): @with_questionnaire_store @with_session_store @with_schema -def send_feedback(schema, session_store, questionnaire_store): +def send_feedback( + schema: QuestionnaireSchema, + session_store: SessionStore, + questionnaire_store: QuestionnaireStore, +) -> Response | str: try: feedback = Feedback( questionnaire_store, schema, session_store, form_data=request.form @@ -633,8 +673,9 @@ def send_feedback(schema, session_store, questionnaire_store): @post_submission_blueprint.route("feedback/sent", methods=["GET"]) @with_session_store -def get_feedback_sent(session_store): - if not session_store.session_data.feedback_count: +def get_feedback_sent(session_store: SessionStore) -> str: + # Type ignore: Session data exists for routes decorated with @login_required, which this is + if not session_store.session_data.feedback_count: # type: ignore raise NotFound return render_template( @@ -646,7 +687,13 @@ def get_feedback_sent(session_store): ) -def _render_page(template, context, previous_location_url, schema, page_title): +def _render_page( + template: str, + context: Mapping, + previous_location_url: str, + schema: QuestionnaireSchema, + page_title: str, +) -> str: session_timeout = get_session_timeout_in_seconds(schema) return render_template( diff --git a/app/routes/schema.py b/app/routes/schema.py index f6de20da95..27dfe5bba9 100644 --- a/app/routes/schema.py +++ b/app/routes/schema.py @@ -1,4 +1,4 @@ -from flask import Blueprint, jsonify +from flask import Blueprint, Response, jsonify from app.utilities.schema import get_schema_list, load_schema_from_name @@ -6,7 +6,7 @@ @schema_blueprint.route("/schemas/", methods=["GET"]) -def get_schema_json_from_name(schema_name): +def get_schema_json_from_name(schema_name: str) -> Response | tuple[str, int]: try: schema = load_schema_from_name(schema_name) return jsonify(schema.json) @@ -15,5 +15,5 @@ def get_schema_json_from_name(schema_name): @schema_blueprint.route("/schemas", methods=["GET"]) -def list_schemas(): +def list_schemas() -> Response: return jsonify(get_schema_list()) diff --git a/app/routes/session.py b/app/routes/session.py index d8a7e12656..56c2653a69 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -1,4 +1,5 @@ from datetime import datetime, timezone +from typing import Any, Iterable, Mapping from flask import Blueprint, g, jsonify, redirect, request from flask import session as cookie_session @@ -8,6 +9,7 @@ from sdc.crypto.exceptions import InvalidTokenException from structlog import contextvars, get_logger from werkzeug.exceptions import Unauthorized +from werkzeug.wrappers.response import Response from app.authentication.auth_payload_version import AuthPayloadVersion from app.authentication.authenticator import decrypt_token, store_session @@ -19,6 +21,7 @@ get_survey_config, render_template, ) +from app.questionnaire import QuestionnaireSchema from app.routes.errors import _render_error_page from app.utilities.metadata_parser import validate_runner_claims from app.utilities.metadata_parser_v2 import ( @@ -33,24 +36,24 @@ @session_blueprint.after_request -def add_cache_control(response): +def add_cache_control(response: Response) -> Response: response.cache_control.no_cache = True return response @session_blueprint.route("/session", methods=["HEAD"]) -def login_head(): +def login_head() -> tuple[str, int]: return "", 204 -def set_schema_context_in_cookie(schema) -> None: +def set_schema_context_in_cookie(schema: QuestionnaireSchema) -> None: for key in [*DATA_LAYER_KEYS, "theme"]: if value := schema.json.get(key): cookie_session[key] = value @session_blueprint.route("/session", methods=["GET", "POST"]) -def login(): +def login() -> Response: """ Initial url processing - expects a token parameter and then will authenticate this token. Once authenticated it will be placed in the users session @@ -128,15 +131,17 @@ def login(): return redirect(url_for("questionnaire.get_questionnaire")) -def validate_jti(decrypted_token): - expires_at = datetime.fromtimestamp(decrypted_token["exp"], tz=timezone.utc) +def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: + # Type ignore: decrypted_token["exp"] will return a valid timestamp with compatible typing + expires_at = datetime.fromtimestamp(decrypted_token["exp"], tz=timezone.utc) # type: ignore jwt_expired = expires_at < datetime.now(tz=timezone.utc) if jwt_expired: raise Unauthorized jti_claim = decrypted_token.get("jti") try: - use_jti_claim(jti_claim, expires_at) + # Type ignore: decrypted_token.get("jti") will return a valid JTI with compatible typing + use_jti_claim(jti_claim, expires_at) # type: ignore except JtiTokenUsed as e: raise Unauthorized from e except (TypeError, ValueError) as e: @@ -144,7 +149,7 @@ def validate_jti(decrypted_token): @session_blueprint.route("/session-expired", methods=["GET"]) -def get_session_expired(): +def get_session_expired() -> tuple[str, int]: # Check for GET as we don't want to log out for HEAD requests if request.method == "GET": logout_user() @@ -154,12 +159,13 @@ def get_session_expired(): @session_blueprint.route("/session-expiry", methods=["GET", "PATCH"]) @login_required -def session_expiry(): - return jsonify(expires_at=get_session_store().expiration_time.isoformat()) +def session_expiry() -> Response: + # Type ignore: @login_required endpoint will ensure a session store exists + return jsonify(expires_at=get_session_store().expiration_time.isoformat()) # type: ignore @session_blueprint.route("/sign-out", methods=["GET"]) -def get_sign_out(): +def get_sign_out() -> Response: """ Signs the user out of eQ and redirects to the log out url. """ @@ -177,11 +183,12 @@ def get_sign_out(): if request.method == "GET": logout_user() - return redirect(log_out_url) + # Type ignore: Logic above to set log_out_url ensures it is not None + return redirect(log_out_url) # type: ignore @session_blueprint.route("/signed-out", methods=["GET"]) -def get_signed_out(): +def get_signed_out() -> Response | str: if not cookie_session: return redirect(url_for("session.get_session_expired")) @@ -196,7 +203,7 @@ def get_signed_out(): ) -def get_runner_claims(decrypted_token): +def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: try: if version := decrypted_token.get("version"): if version == AuthPayloadVersion.V2.value: @@ -209,7 +216,9 @@ def get_runner_claims(decrypted_token): raise InvalidTokenException("Invalid runner claims") from e -def get_questionnaire_claims(decrypted_token, schema_metadata): +def get_questionnaire_claims( + decrypted_token: Mapping, schema_metadata: Iterable[Mapping[str, str]] +) -> dict: try: if decrypted_token.get("version") == AuthPayloadVersion.V2.value: claims = decrypted_token.get("survey_metadata", {}).get("data", {}) diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index 84716ce423..65510248f5 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -1,6 +1,6 @@ from collections import OrderedDict from datetime import datetime, timezone -from typing import Any, Mapping, Optional, Union +from typing import Any, Iterable, Mapping, Optional, Union from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import AnswerValueTypes, ListAnswer @@ -21,7 +21,7 @@ def convert_answers_to_payload_0_0_1( answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, - full_routing_path: RoutingPath, + full_routing_path: Iterable[RoutingPath], progress_store: ProgressStore, ) -> OrderedDict[str, Any]: """ diff --git a/app/submitter/convert_payload_0_0_3.py b/app/submitter/convert_payload_0_0_3.py index 20d58160e3..8e9393eeef 100644 --- a/app/submitter/convert_payload_0_0_3.py +++ b/app/submitter/convert_payload_0_0_3.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional +from typing import Iterable, Mapping, Optional from app.data_models import Answer, ListStore from app.data_models.answer_store import AnswerStore @@ -14,7 +14,7 @@ def convert_answers_to_payload_0_0_3( answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, - full_routing_path: RoutingPath, + full_routing_path: Iterable[RoutingPath], ) -> list[Answer]: """ Convert answers into the data format below diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 9064212524..72987bb999 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Mapping, Optional, Union +from typing import Any, Iterable, Mapping, MutableMapping, Optional, Union from structlog import get_logger @@ -20,7 +20,7 @@ def convert_answers( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, - routing_path: RoutingPath, + routing_path: Iterable[RoutingPath], submitted_at: datetime, flushed: bool = False, ) -> dict[str, Any]: @@ -132,7 +132,7 @@ def build_metadata(metadata: MetadataProxy) -> MetadataType: def get_optional_payload_properties( - metadata: MetadataProxy, response_metadata: Mapping + metadata: MetadataProxy, response_metadata: MutableMapping ) -> MetadataType: payload = {} diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index 77d1edbfed..b4c6aed5a9 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Iterable, Mapping, Optional, OrderedDict, Union +from typing import Any, Iterable, Mapping, MutableMapping, Optional, OrderedDict, Union from structlog import get_logger @@ -31,7 +31,7 @@ def __str__(self) -> str: def convert_answers_v2( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, - routing_path: RoutingPath, + routing_path: Iterable[RoutingPath], submitted_at: datetime, flushed: bool = False, ) -> dict[str, Any]: @@ -95,7 +95,7 @@ def convert_answers_v2( def get_optional_payload_properties( - metadata: MetadataProxy, response_metadata: Mapping + metadata: MetadataProxy, response_metadata: MutableMapping ) -> MetadataType: payload = {} @@ -112,7 +112,7 @@ def get_payload_data( answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, - routing_path: RoutingPath, + routing_path: Iterable[RoutingPath], metadata: MetadataProxy, response_metadata: Mapping, progress_store: ProgressStore, diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index 172e0107e5..537adae896 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -1,6 +1,6 @@ import functools from datetime import datetime, timezone -from typing import Dict +from typing import Mapping from marshmallow import ( EXCLUDE, @@ -144,7 +144,7 @@ def update_response_id( ) -def validate_runner_claims(claims: Dict): +def validate_runner_claims(claims: Mapping) -> dict: """Validate claims required for runner to function""" runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) return runner_metadata_schema.load(claims) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 2a4dfb7cc4..366f997435 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -1,6 +1,6 @@ import functools from datetime import datetime, timezone -from typing import Dict +from typing import Callable, Iterable, Mapping from marshmallow import ( EXCLUDE, @@ -20,8 +20,7 @@ logger = get_logger() - -VALIDATORS = { +VALIDATORS: Mapping[str, Callable] = { "date": functools.partial(DateString, format="%Y-%m-%d", required=True), "uuid": functools.partial(UUIDString, required=True), "boolean": functools.partial(fields.Boolean, required=True), @@ -110,8 +109,10 @@ def validate_schema_name_is_set(self, data, **kwargs): def validate_questionnaire_claims( - claims, questionnaire_specific_metadata, unknown=EXCLUDE -): + claims: Mapping, + questionnaire_specific_metadata: Iterable[Mapping], + unknown=EXCLUDE, +) -> dict: """Validate any survey specific claims required for a questionnaire""" dynamic_fields = {} @@ -146,7 +147,7 @@ def validate_questionnaire_claims( return questionnaire_metadata_schema.load(claims) -def validate_runner_claims_v2(claims: Dict): +def validate_runner_claims_v2(claims: Mapping) -> dict: """Validate claims required for runner to function""" runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) return runner_metadata_schema.load(claims) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index c1e21f6bdc..994f700423 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -3,7 +3,7 @@ from functools import lru_cache from glob import glob from pathlib import Path -from typing import Mapping, Optional +from typing import Any import requests from requests import RequestException @@ -59,7 +59,7 @@ def get_schema_list(language_code: str = DEFAULT_LANGUAGE_CODE) -> dict[str, lis @lru_cache(maxsize=None) -def get_schema_path(language_code, schema_name): +def get_schema_path(language_code: str, schema_name: str) -> str | None: for schemas_by_language in get_schema_path_map(include_test_schemas=True).values(): schema_path = schemas_by_language.get(language_code, {}).get(schema_name) if schema_path: @@ -67,7 +67,9 @@ def get_schema_path(language_code, schema_name): @lru_cache(maxsize=None) -def get_schema_path_map(include_test_schemas: Optional[bool] = False) -> Mapping: +def get_schema_path_map( + include_test_schemas: bool = False, +) -> dict[str, dict[str, dict[str, str]]]: schemas = {} for survey_type in os.listdir(SCHEMA_DIR): if not include_test_schemas and survey_type == "test": @@ -86,7 +88,7 @@ def get_schema_path_map(include_test_schemas: Optional[bool] = False) -> Mapping return schemas -def _schema_exists(language_code, schema_name): +def _schema_exists(language_code: str, schema_name: str) -> bool: schema_path_map = get_schema_path_map(include_test_schemas=True) return any( True @@ -96,17 +98,18 @@ def _schema_exists(language_code, schema_name): ) -def get_allowed_languages(schema_name, launch_language): - for language_combination in LANGUAGES_MAP.get(schema_name, []): - if launch_language in language_combination: - return language_combination +def get_allowed_languages(schema_name: str | None, launch_language: str): + if schema_name: + for language_combination in LANGUAGES_MAP.get(schema_name, []): + if launch_language in language_combination: + return language_combination return [DEFAULT_LANGUAGE_CODE] def load_schema_from_metadata( - metadata: MetadataProxy, *, language_code: str + metadata: MetadataProxy, *, language_code: str | None ) -> QuestionnaireSchema: - if metadata and (schema_url := metadata.schema_url): + if schema_url := metadata.schema_url: # :TODO: Remove before production uses schema_url # This is temporary and is only for development/integration purposes. # This should not be used in production. @@ -129,27 +132,32 @@ def load_schema_from_metadata( return schema return load_schema_from_name( - metadata.schema_name, + # Type ignore: Metadata is validated to have either schema_name or schema_url populated. + # This code runs only if schema_url was not present, thus schema_name is present (not None). + metadata.schema_name, # type: ignore language_code=language_code, ) -def load_schema_from_name(schema_name, language_code=DEFAULT_LANGUAGE_CODE): +def load_schema_from_name( + schema_name: str, language_code: str | None = DEFAULT_LANGUAGE_CODE +) -> QuestionnaireSchema: + language_code = language_code or DEFAULT_LANGUAGE_CODE return _load_schema_from_name(schema_name, language_code) @lru_cache(maxsize=None) -def _load_schema_from_name(schema_name, language_code): +def _load_schema_from_name(schema_name: str, language_code: str) -> QuestionnaireSchema: schema_json = _load_schema_file(schema_name, language_code) return QuestionnaireSchema(schema_json, language_code) -def get_schema_name_from_params(eq_id, form_type): +def get_schema_name_from_params(eq_id, form_type) -> str: return f"{eq_id}_{form_type}" -def _load_schema_file(schema_name, language_code): +def _load_schema_file(schema_name: str, language_code: str) -> Any: """ Load a schema, optionally for a specified language. :param schema_name: The name of the schema e.g. census_household @@ -182,12 +190,15 @@ def _load_schema_file(schema_name, language_code): schema_path=schema_path, ) - with open(schema_path, encoding="utf8") as json_file: + # Type ignore: Existence of the file is checked prior to call for the path + with open(schema_path, encoding="utf8") as json_file: # type: ignore return json_load(json_file) @lru_cache(maxsize=None) -def load_schema_from_url(schema_url, language_code): +def load_schema_from_url( + schema_url: str, language_code: str | None +) -> QuestionnaireSchema: language_code = language_code or DEFAULT_LANGUAGE_CODE pid = os.getpid() logger.info( @@ -206,7 +217,8 @@ def load_schema_from_url(schema_url, language_code): status_forcelist=SCHEMA_REQUEST_RETRY_STATUS_CODES, ) # Codes to retry according to Google Docs https://cloud.google.com/storage/docs/retry-strategy#client-libraries - retries.BACKOFF_MAX = SCHEMA_REQUEST_MAX_BACKOFF + # Type ignore: MyPy does not recognise BACKOFF_MAX however it is a property, albeit deprecated + retries.BACKOFF_MAX = SCHEMA_REQUEST_MAX_BACKOFF # type: ignore session.mount("http://", HTTPAdapter(max_retries=retries)) session.mount("https://", HTTPAdapter(max_retries=retries)) diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 7ec250bc57..9ede3a4611 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,6 +1,15 @@ from copy import deepcopy from decimal import Decimal -from typing import Any, Callable, Iterable, Mapping, Optional, Tuple, Union +from typing import ( + Any, + Callable, + Iterable, + Mapping, + MutableMapping, + Optional, + Tuple, + Union, +) from werkzeug.datastructures import ImmutableDict @@ -35,7 +44,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, routing_path: RoutingPath, current_location: Location, ) -> None: diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index 8c7d0d82ca..9495e08c45 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Mapping +from typing import MutableMapping from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -19,7 +19,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: MetadataProxy | None, - response_metadata: Mapping, + response_metadata: MutableMapping, ) -> None: self._language = language self._schema = schema @@ -31,12 +31,12 @@ def __init__( self._placeholder_preview_mode = self._schema.preview_enabled self._router = Router( - self._schema, - self._answer_store, - self._list_store, - self._progress_store, - self._metadata, - self._response_metadata, + schema=self._schema, + answer_store=self._answer_store, + list_store=self._list_store, + progress_store=self._progress_store, + metadata=self._metadata, + response_metadata=self._response_metadata, ) self._placeholder_renderer = PlaceholderRenderer( diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py index f458488483..7e173250c6 100644 --- a/app/views/contexts/preview_context.py +++ b/app/views/contexts/preview_context.py @@ -1,4 +1,4 @@ -from typing import Generator, Mapping, Optional, Union +from typing import Generator, MutableMapping, Optional, Union from flask_babel import lazy_gettext @@ -22,7 +22,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping[str, Union[str, int, list]], + response_metadata: MutableMapping[str, Union[str, int, list]], ): if not schema.preview_enabled: raise PreviewNotEnabledException diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py index 7cfa58e99b..325897752c 100644 --- a/app/views/contexts/section_preview_context.py +++ b/app/views/contexts/section_preview_context.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional, Union +from typing import MutableMapping, Optional from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy @@ -17,7 +17,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping[str, Union[str, int, list]], + response_metadata: MutableMapping, section_id: str, ): super().__init__( diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 01d9ce1242..ad99d9ab40 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, Optional, Union +from typing import Any, Generator, Iterable, Mapping, MutableMapping, Optional, Union from werkzeug.datastructures import ImmutableDict @@ -24,7 +24,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, routing_path: RoutingPath, current_location: Location, ) -> None: diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 194833beca..43981c7ecd 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -1,4 +1,4 @@ -from typing import Any, Mapping, Optional +from typing import Any, Mapping, MutableMapping, Optional from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy @@ -17,7 +17,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, return_to: Optional[str], diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 3ee22c9a56..8af2ef5ec2 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,4 +1,4 @@ -from typing import Any, Mapping, Optional +from typing import Any, Mapping, MutableMapping, Optional from werkzeug.datastructures import ImmutableDict @@ -21,7 +21,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, language: str, @@ -70,7 +70,7 @@ def _build_blocks_and_links( answer_store: AnswerStore, list_store: ListStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, return_to: Optional[str], diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 93557624b2..d31d026e3c 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Any, Mapping, Optional +from typing import Any, Mapping, MutableMapping, Optional from flask import url_for from werkzeug.datastructures import ImmutableDict @@ -22,7 +22,7 @@ def __init__( list_store: ListStore, progress_store: ProgressStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, language: str, diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 4aed203139..91c0a3d6c3 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -1,3 +1,7 @@ +from werkzeug.datastructures import ImmutableMultiDict, MultiDict + +from app.data_models import QuestionnaireStore +from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException, Location from app.questionnaire.relationship_location import RelationshipLocation from app.views.handlers.calculated_summary import CalculatedSummary @@ -30,15 +34,15 @@ def get_block_handler( - schema, - block_id, - list_item_id, - questionnaire_store, - language, - list_name=None, - to_list_item_id=None, - request_args=None, - form_data=None, + schema: QuestionnaireSchema, + block_id: str, + list_item_id: str | None, + questionnaire_store: QuestionnaireStore, + language: str | None, + list_name: str | None = None, + to_list_item_id: str | None = None, + request_args: MultiDict[str, str] | None = None, + form_data: ImmutableMultiDict[str, str] | None = None, ): block = schema.get_block(block_id) @@ -63,7 +67,8 @@ def get_block_handler( if to_list_item_id or block_type == "UnrelatedQuestion": location = RelationshipLocation( - section_id=section_id, + # Type ignore: Block is fetched from schema so must have a corresponding section + section_id=section_id, # type: ignore block_id=block_id, list_item_id=list_item_id, to_list_item_id=to_list_item_id, @@ -71,7 +76,8 @@ def get_block_handler( ) else: location = Location( - section_id=section_id, + # Type ignore: Block is fetched from schema so must have a corresponding section + section_id=section_id, # type: ignore block_id=block_id, list_name=list_name, list_item_id=list_item_id, diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index b9d73f0011..b13b163754 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -1,6 +1,6 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Mapping, Optional, Union +from typing import Any, Mapping, MutableMapping, Optional, Union from flask import current_app from flask_babel import gettext, lazy_gettext @@ -268,7 +268,7 @@ class FeedbackPayload: def __init__( self, metadata: MetadataProxy, - response_metadata: Mapping[str, Union[str, int, list]], + response_metadata: MutableMapping[str, Union[str, int, list]], schema: QuestionnaireSchema, case_id: Optional[str], submission_language_code: Optional[str], @@ -339,7 +339,7 @@ class FeedbackPayloadV2: def __init__( self, metadata: MetadataProxy, - response_metadata: Mapping[str, Union[str, int, list]], + response_metadata: MutableMapping[str, Union[str, int, list]], schema: QuestionnaireSchema, case_id: Optional[str], submission_language_code: Optional[str], diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 645286626d..c4872aaef2 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -1,21 +1,25 @@ from datetime import datetime, timezone from functools import cached_property -from typing import List, Mapping, Optional +from typing import Any, Mapping, Optional from uuid import uuid4 from flask import current_app, redirect from flask.helpers import url_for -from flask_babel import lazy_gettext +from flask_babel import LazyString, lazy_gettext from itsdangerous import BadSignature +from werkzeug.datastructures import ImmutableMultiDict from werkzeug.exceptions import BadRequest, NotFound +from werkzeug.wrappers.response import Response -from app.data_models import CompletionStatus, FulfilmentRequest +from app.data_models import CompletionStatus, FulfilmentRequest, QuestionnaireStore +from app.data_models.list_store import ListModel from app.data_models.metadata_proxy import MetadataProxy -from app.forms.questionnaire_form import generate_form +from app.forms.questionnaire_form import QuestionnaireForm, generate_form from app.forms.validators import sanitise_mobile_number from app.helpers import url_safe_serializer from app.helpers.template_helpers import render_template from app.publisher.exceptions import PublicationFailed +from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.router import Router from app.views.contexts.question import build_question_context @@ -39,7 +43,7 @@ class IndividualResponsePostalDeadlinePast(Exception): class IndividualResponseHandler: @staticmethod - def _person_name_transforms(list_name) -> List[Mapping]: + def _person_name_transforms(list_name: str) -> list[Mapping]: return [ { "transform": "contains", @@ -64,7 +68,7 @@ def _person_name_transforms(list_name) -> List[Mapping]: ] @staticmethod - def _person_name_placeholder(list_name) -> List[Mapping]: + def _person_name_placeholder(list_name: str) -> list[Mapping]: return [ { "placeholder": "person_name", @@ -75,8 +79,10 @@ def _person_name_placeholder(list_name) -> List[Mapping]: ] @staticmethod - def _person_name_placeholder_possessive(list_name) -> List[Mapping]: - name_transforms = IndividualResponseHandler._person_name_transforms(list_name) + def _person_name_placeholder_possessive(list_name: str) -> list[Mapping]: + name_transforms: list[ + Mapping + ] = IndividualResponseHandler._person_name_transforms(list_name) return [ { "placeholder": "person_name_possessive", @@ -93,7 +99,7 @@ def _person_name_placeholder_possessive(list_name) -> List[Mapping]: ] @cached_property - def has_postal_deadline_passed(self): + def has_postal_deadline_passed(self) -> bool: individual_response_postal_deadline = current_app.config[ "EQ_INDIVIDUAL_RESPONSE_POSTAL_DEADLINE" ] @@ -101,19 +107,19 @@ def has_postal_deadline_passed(self): def __init__( self, - schema, - questionnaire_store, - language, - request_args, - form_data, - list_item_id=None, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + language: str, + request_args: dict[str, str] | None, + form_data: ImmutableMultiDict[str, str], + list_item_id: str | None = None, ): self._schema = schema self._questionnaire_store = questionnaire_store self._language = language - self._request_args = request_args or {} + self._request_args: dict[str, str] = request_args or {} self._form_data = form_data - self._answers = None + self._answers: dict[str, Any] | None = None self._list_item_id = list_item_id self._list_name = self._schema.get_individual_response_list() @@ -125,16 +131,18 @@ def __init__( raise NotFound @cached_property - def _list_model(self): - return self._questionnaire_store.list_store[self._list_name] + def _list_model(self) -> ListModel: + # Type ignore: Current usages of this cached property occur when List Name will exist and be not None + return self._questionnaire_store.list_store[self._list_name] # type: ignore @cached_property - def _list_item_position(self): + def _list_item_position(self) -> int: + # Type ignore: Current usages of this cached property occur when List Name and List Item ID exist and be not None return self._questionnaire_store.list_store.list_item_position( - self._list_name, self._list_item_id + self._list_name, self._list_item_id # type: ignore ) - def page_title(self, page_title): + def page_title(self, page_title: str) -> str: if self._list_item_id: page_title += ": " + lazy_gettext( "Person {list_item_position}".format( # pylint: disable=consider-using-f-string @@ -143,7 +151,7 @@ def page_title(self, page_title): ) return page_title - def _is_location_valid(self): + def _is_location_valid(self) -> bool: if not self._list_model: return False @@ -157,13 +165,14 @@ def _is_location_valid(self): return True @cached_property - def rendered_block(self) -> Mapping: + def rendered_block(self) -> Mapping[str, Any]: return self._render_block() @cached_property - def placeholder_renderer(self): + def placeholder_renderer(self) -> PlaceholderRenderer: return PlaceholderRenderer( - language=self._language, + # Type ignore: Language is defaulted via handle_language in the individual_response blueprint before_request which triggers this + language=self._language, # type: ignore answer_store=self._questionnaire_store.answer_store, list_store=self._questionnaire_store.list_store, metadata=self._questionnaire_store.metadata, @@ -174,7 +183,7 @@ def placeholder_renderer(self): ) @cached_property - def router(self): + def router(self) -> Router: return Router( schema=self._schema, answer_store=self._questionnaire_store.answer_store, @@ -185,11 +194,11 @@ def router(self): ) @cached_property - def individual_section_id(self): + def individual_section_id(self) -> str | None: return self._schema.get_individual_response_individual_section_id() @cached_property - def form(self): + def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=self.rendered_block["question"], @@ -205,14 +214,17 @@ def form(self): def get_context(self): return build_question_context(self.rendered_block, self.form) - def _publish_fulfilment_request(self, mobile_number=None): + def _publish_fulfilment_request(self, mobile_number=None) -> None: self._check_individual_response_count() topic_id = current_app.config["EQ_FULFILMENT_TOPIC_ID"] fulfilment_request = IndividualResponseFulfilmentRequest( - self._metadata, mobile_number + # Type ignore: _metadata will exist at point of publish + self._metadata, # type: ignore + mobile_number, ) try: - return current_app.eq["publisher"].publish( + # Type ignore: Instance attribute 'eq' is a dict with key "publisher" with value of abstract type Publisher + return current_app.eq["publisher"].publish( # type: ignore topic_id, message=fulfilment_request.message, fulfilment_request_transaction_id=fulfilment_request.transaction_id, @@ -220,7 +232,7 @@ def _publish_fulfilment_request(self, mobile_number=None): except PublicationFailed as exc: raise IndividualResponseFulfilmentRequestPublicationFailed from exc - def _check_individual_response_count(self): + def _check_individual_response_count(self) -> None: if ( self._questionnaire_store.response_metadata.get( "individual_response_count", 0 @@ -231,20 +243,21 @@ def _check_individual_response_count(self): "Individual response limit has been reached" ) - def _update_individual_response_count(self): + def _update_individual_response_count(self) -> None: response_metadata = self._questionnaire_store.response_metadata + # Type ignore: if response_metadata.get("individual_response_count"): response_metadata["individual_response_count"] += 1 else: response_metadata["individual_response_count"] = 1 - def _update_questionnaire_store_on_publish(self): + def _update_questionnaire_store_on_publish(self) -> None: self._update_section_status(CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED) self._update_individual_response_count() self._questionnaire_store.save() - def handle_get(self): + def handle_get(self) -> str: return render_template( template="individual_response/interstitial", language=self._language, @@ -255,8 +268,9 @@ def handle_get(self): ), ) - def _get_next_location_url(self): - list_model = self._questionnaire_store.list_store[self._list_name] + def _get_next_location_url(self) -> str: + # Type ignore: Current usages of this method occur when List Name exists and is not None + list_model = self._questionnaire_store.list_store[self._list_name] # type: ignore if self._list_item_id: return url_for( @@ -274,18 +288,20 @@ def _get_next_location_url(self): return url_for(".individual_response_who", journey="hub") - def _get_previous_location_url(self): + def _get_previous_location_url(self) -> str: if self._request_args.get("journey") == "remove-person": return url_for( "questionnaire.block", list_name=self._list_name, list_item_id=self._list_item_id, - block_id=self._schema.get_remove_block_id_for_list(self._list_name), + # Type ignore: Current usages of this method occur when List Name exists and is not None + block_id=self._schema.get_remove_block_id_for_list(self._list_name), # type: ignore ) if self._list_item_id: individual_section_first_block_id = ( - self._schema.get_first_block_id_for_section(self.individual_section_id) + # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None + self._schema.get_first_block_id_for_section(self.individual_section_id) # type: ignore ) return url_for( "questionnaire.block", @@ -296,24 +312,27 @@ def _get_previous_location_url(self): return url_for("questionnaire.get_questionnaire") - def _render_block(self): + def _render_block(self) -> dict[str, Any]: return self.placeholder_renderer.render( data_to_render=self.block_definition, list_item_id=self._list_item_id ) - def _update_section_status(self, status): + def _update_section_status(self, status: str) -> None: self._questionnaire_store.progress_store.update_section_status( - status, self.individual_section_id, self._list_item_id + # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None + status, + self.individual_section_id, # type: ignore + self._list_item_id, ) @property - def block_definition(self): # pragma: no cover + def block_definition(self) -> Mapping[str, Any]: # pragma: no cover raise NotImplementedError class IndividualResponseHowHandler(IndividualResponseHandler): @cached_property - def block_definition(self) -> Mapping: + def block_definition(self) -> Mapping[str, Any]: return { "type": "IndividualResponse", "id": "individual-response", @@ -325,7 +344,8 @@ def block_definition(self) -> Mapping: "How would you like {person_name} to receive a separate census?" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - self._list_name + # Type ignore: List name will exist and be not None at this point + self._list_name # type: ignore ), }, "description": self._build_question_description(), @@ -341,7 +361,7 @@ def block_definition(self) -> Mapping: }, } - def _build_handler_answer_options(self): + def _build_handler_answer_options(self) -> list[dict[str, str | LazyString]]: handler_options = [ { "label": lazy_gettext("Text message"), @@ -363,7 +383,7 @@ def _build_handler_answer_options(self): ) return handler_options - def _build_question_description(self): + def _build_question_description(self) -> list[LazyString]: description = ( lazy_gettext("It is no longer possible to receive an access code by post.") if self.has_postal_deadline_passed @@ -377,11 +397,11 @@ def _build_question_description(self): ] @cached_property - def selected_option(self): + def selected_option(self) -> str: answer_id = self.rendered_block["question"]["answers"][0]["id"] return self.form.get_data(answer_id) - def handle_get(self): + def handle_get(self) -> str: if self._request_args.get("journey") == "hub": if len(self._list_model.non_primary_people) == 1: previous_location_url = url_for( @@ -419,7 +439,7 @@ def handle_get(self): page_title=self.page_title(lazy_gettext("Send individual access code")), ) - def handle_post(self): + def handle_post(self) -> Response: if self.selected_option == "Post": return redirect( url_for( @@ -439,7 +459,7 @@ def handle_post(self): class IndividualResponseChangeHandler(IndividualResponseHandler): @cached_property - def block_definition(self) -> Mapping: + def block_definition(self) -> Mapping[str, Any]: return { "type": "IndividualResponse", "id": "individual-response-change", @@ -451,7 +471,8 @@ def block_definition(self) -> Mapping: "How would you like to answer {person_name_possessive} questions?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( - self._list_name + # Type ignore: List name will exist and be not None at this point + self._list_name # type: ignore ), }, "answers": [ @@ -482,7 +503,8 @@ def block_definition(self) -> Mapping: "I will answer for {person_name}" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - self._list_name + # Type ignore: List name will exist and be not None at this point + self._list_name # type: ignore ), }, "value": "I will answer for {person_name}", @@ -510,7 +532,7 @@ def selected_option(self): answer_id = self.rendered_block["question"]["answers"][0]["id"] return self.form.get_data(answer_id) - def handle_get(self): + def handle_get(self) -> str: self._answers = { "individual-response-change-answer": self.request_separate_census_option } @@ -523,7 +545,7 @@ def handle_get(self): page_title=self.page_title(lazy_gettext("How to answer questions")), ) - def handle_post(self): + def handle_post(self) -> Response | None: if self.selected_option == self.request_separate_census_option: return redirect( url_for( @@ -540,7 +562,8 @@ def handle_post(self): if self.selected_option == self.cancel_go_to_section_option: self._update_section_completeness() individual_section_first_block_id = ( - self._schema.get_first_block_id_for_section(self.individual_section_id) + # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None + self._schema.get_first_block_id_for_section(self.individual_section_id) # type: ignore ) return redirect( url_for( @@ -589,7 +612,8 @@ def block_definition(self) -> Mapping: "Do you want to send an individual access code for {person_name} by post?" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - self._list_name + # Type ignore: List name will exist and be not None at this point + self._list_name # type: ignore ), }, "description": [ @@ -640,7 +664,7 @@ def confirm_option(self): def selected_option(self): return self.form.get_data(self.answer_id) - def handle_get(self): + def handle_get(self) -> str: previous_location_url = url_for( "individual_response.individual_response_how", list_item_id=self._list_item_id, @@ -655,7 +679,7 @@ def handle_get(self): page_title=self.page_title(lazy_gettext("Confirm address")), ) - def handle_post(self): + def handle_post(self) -> Response: if self.selected_option == self.confirm_option: self._publish_fulfilment_request() self._update_questionnaire_store_on_publish() @@ -733,7 +757,7 @@ def selected_option(self): answer_id = self.rendered_block["question"]["answers"][0]["id"] return self.form.get_data(answer_id) - def handle_get(self): + def handle_get(self) -> str: if len(self.non_primary_people_names) > 1: previous_location_url = url_for( "individual_response.request_individual_response", @@ -750,7 +774,7 @@ def handle_get(self): raise NotFound - def handle_post(self): + def handle_post(self) -> Response: return redirect( url_for( ".individual_response_how", @@ -773,7 +797,8 @@ def block_definition(self) -> Mapping: "What is {person_name_possessive} mobile number?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( - self._list_name + # Type ignore: List name will exist and be not None at this point + self._list_name # type: ignore ), }, "answers": [ @@ -798,7 +823,7 @@ def answer_id(self): def mobile_number(self): return self.form.get_data(self.answer_id) - def handle_get(self): + def handle_get(self) -> str: if "mobile_number" in self._request_args: mobile_number = url_safe_serializer().loads( self._request_args["mobile_number"] @@ -818,7 +843,7 @@ def handle_get(self): page_title=self.page_title(lazy_gettext("Mobile number")), ) - def handle_post(self): + def handle_post(self) -> Response: mobile_number = url_safe_serializer().dumps(self.mobile_number) return redirect( @@ -898,7 +923,7 @@ def confirm_option(self): def selected_option(self): return self.form.get_data(self.answer_id) - def handle_get(self): + def handle_get(self) -> str: previous_location_url = url_for( "individual_response.individual_response_text_message", list_item_id=self._list_item_id, @@ -914,7 +939,7 @@ def handle_get(self): page_title=self.page_title(lazy_gettext("Confirm mobile number")), ) - def handle_post(self): + def handle_post(self) -> Response: if self.selected_option == self.confirm_option: self._publish_fulfilment_request(self.mobile_number) self._update_questionnaire_store_on_publish() diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index 8e2bff1107..0715ec9d9b 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -1,22 +1,33 @@ +from typing import Mapping + +from app.data_models import QuestionnaireStore +from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException, Location from app.questionnaire.router import Router from app.views.contexts import SectionSummaryContext class SectionHandler: - def __init__(self, schema, questionnaire_store, section_id, list_item_id, language): + def __init__( + self, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + section_id: str, + list_item_id: str | None, + language: str, + ): self._schema = schema self._questionnaire_store = questionnaire_store self._section_id = section_id self._list_item_id = list_item_id self._language = language self._router = Router( - schema, - questionnaire_store.answer_store, - questionnaire_store.list_store, - questionnaire_store.progress_store, - questionnaire_store.metadata, - questionnaire_store.response_metadata, + schema=schema, + answer_store=questionnaire_store.answer_store, + list_store=questionnaire_store.list_store, + progress_store=questionnaire_store.progress_store, + metadata=questionnaire_store.metadata, + response_metadata=questionnaire_store.response_metadata, ) if not self._is_valid_location(): raise InvalidLocationException(f"location {self._section_id} is not valid") @@ -31,7 +42,7 @@ def __init__(self, schema, questionnaire_store, section_id, list_item_id, langua section_id=self._section_id, list_item_id=self._list_item_id ) - def get_context(self): + def get_context(self) -> Mapping: section_summary_context = SectionSummaryContext( self._language, self._schema, @@ -45,19 +56,19 @@ def get_context(self): ) return section_summary_context() - def get_next_location_url(self): + def get_next_location_url(self) -> str: return self._router.get_next_location_url_for_end_of_section() - def get_previous_location_url(self): + def get_previous_location_url(self) -> str: return self._router.get_last_location_in_section(self._routing_path).url() - def get_resume_url(self): + def get_resume_url(self) -> str: return self._router.get_section_resume_url(self._routing_path) - def can_display_summary(self): + def can_display_summary(self) -> bool: return self._router.can_display_section_summary( self._section_id, self._list_item_id ) - def _is_valid_location(self): + def _is_valid_location(self) -> bool: return self._section_id in self._router.enabled_section_ids diff --git a/doc/python-type-hinting.md b/doc/python-type-hinting.md index f6132ff9df..0f423aee1f 100644 --- a/doc/python-type-hinting.md +++ b/doc/python-type-hinting.md @@ -1,6 +1,7 @@ # Python Type Hinting -As a team we have committed to adding type hints throughout the Python code. This document defines our approach to type hinting, to ensure consistency in the codebase and avoid repeating discussions about how we apply typing. +As a team we have committed to adding type hints throughout the Python code. This document defines our approach to type hinting, to ensure consistency in the +codebase and avoid repeating discussions about how we apply typing. ## Variables @@ -30,12 +31,44 @@ https://www.python.org/dev/peps/pep-0585/ ## Generic types -Specify at least the top-level type parameters for all generic types: +Specify at least the top-level type parameters for generic types: ```python def get_objects_matching(ids: Sequence[str]) -> dict[int, dict] ``` +If the typing used for a generic type would be Any or indeterministic, do not specify it: + +``` +items: list[Any] = ["demo", 2, true] # Incorrect +items: list[str | int | bool] = ["demo", 2, true] # Incorrect +items: list = ["demo", 2, true] # Correct +``` + +This same ruling applies for key-val types such as Mapping. + +If the key type and value types are known, they may be specified: + +``` +known_types_dict: dict[str, str] = {"name" = "demo"} +``` + +If the key type is known, and the value types are deterministic, use TypedDict: + +``` +from typing import TypedDict + +class Movie(TypedDict): + name: str + year: int +``` + +If the key type is known but the value types are indeterministic or the key type is not known, do not declare the types: + +``` +json_data: dict = json.loads(stringified_json) +``` + ## Optional arguments For arguments that can be a single type or None, use the shorthand `|` instead of using the older `Optional` keyword: @@ -57,16 +90,20 @@ def test(self, var: None | int | str) -> None: ```python def increment_values(self, values: Sequence[int]) -> list[int]: - return [value + 1 for value in values] + + +return [value + 1 for value in values] ``` ## Self Type -To annotate methods that return an instance of their class, use the `Self` type is as it is bound to it's encapsulating class. In the example below, the type checker will correctly infer the type of `Circle().set_scale(0.5)` to be `Circle`: +To annotate methods that return an instance of their class, use the `Self` type is as it is bound to it's encapsulating class. In the example below, the type +checker will correctly infer the type of `Circle().set_scale(0.5)` to be `Circle`: ```python from typing import Self + class Shape: def set_scale(self, scale: float) -> Self: self.scale = scale @@ -84,16 +121,20 @@ https://peps.python.org/pep-0673/ ## Type Alias -Use the special annotation `TypeAlias` to declare type aliases more explicitly so type checkers are able to distinguish between type aliases and ordinary assignments: +Use the special annotation `TypeAlias` to declare type aliases more explicitly so type checkers are able to distinguish between type aliases and ordinary +assignments: ```python MyType: TypeAlias = "ClassName" + + def foo() -> MyType: ... ``` ## Type Ignore -To mark portions of the program that should not be covered by type hinting, use `# type: ignore` on the specific line. When used in a line by itself at the top of a file, it silences all errors in the file: +To mark portions of the program that should not be covered by type hinting, use `# type: ignore` on the specific line. When used in a line by itself at the top +of a file, it silences all errors in the file: ```python # type: ignore @@ -118,13 +159,17 @@ E.g. a basic logging function decorator: T = TypeVar('T') P = ParamSpec('P') + def add_logging(f: Callable[P, T]) -> Callable[P, T]: '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: logging.info(f'{f.__name__} was called') return f(*args, **kwargs) + return inner + @add_logging def add_two(x: float, y: float) -> float: '''Add two numbers together.''' @@ -138,6 +183,7 @@ Use `TypeVar` when the type returned by a function is the same as the type which ```python T = TypeVar('T') + def increment_value(self, value: T) -> T: return value + 1 ``` @@ -147,6 +193,7 @@ def increment_value(self, value: T) -> T: ```python T = TypeVar('T', int, float) + def increment_value(self, value: T) -> T: return value + 1 ``` diff --git a/mypy.ini b/mypy.ini index 3e085e0701..4192e70fd6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -150,3 +150,8 @@ no_implicit_optional = True disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True + +[mypy-app.routes.*] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True From a7f39efbddf15765d8fed6ab0ecacebee4fbe654 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Wed, 26 Apr 2023 11:01:33 +0100 Subject: [PATCH 214/567] Add type hints to questionnaire module (#1085) * type hint routing_path * type hint location and questionnaire_store_updater * type hint questionnaire variants * remaining type hints for questionnaire * Update existing hints in questionnaire module * update variant test json and types in edited files * fix linting error * fix isort error * run black * fix the type-ignore that black broke * Fix type ignore comments and more top level params * Fix missing import * Address most PR comments * Remove [str, Any] occurences in questionnaire module * Add the more specific hints * Remove a missed [Any] * Fix remaining [str, Any] cases * Fix imports * Fix the merge errors --- app/data_models/answer.py | 15 +- app/forms/questionnaire_form.py | 2 +- app/questionnaire/dependencies.py | 8 +- app/questionnaire/dynamic_answer_options.py | 4 +- app/questionnaire/location.py | 10 +- app/questionnaire/path_finder.py | 69 ++++--- app/questionnaire/placeholder_parser.py | 22 +- app/questionnaire/placeholder_renderer.py | 21 +- app/questionnaire/placeholder_transforms.py | 24 +-- app/questionnaire/plural_forms.py | 5 +- app/questionnaire/questionnaire_schema.py | 192 +++++++++--------- .../questionnaire_store_updater.py | 80 ++++---- app/questionnaire/relationship_location.py | 6 +- app/questionnaire/relationship_router.py | 38 ++-- app/questionnaire/router.py | 54 ++--- app/questionnaire/routing_path.py | 25 ++- app/questionnaire/schema_utils.py | 18 +- app/questionnaire/value_source_resolver.py | 29 ++- app/questionnaire/variants.py | 90 ++++---- app/routes/flush.py | 2 +- app/submitter/convert_payload_0_0_1.py | 11 +- app/submitter/convert_payload_0_0_3.py | 4 +- app/submitter/converter.py | 6 +- app/submitter/converter_v2.py | 22 +- .../contexts/calculated_summary_context.py | 11 +- app/views/contexts/summary/block.py | 2 +- mypy.ini | 37 +--- tests/app/questionnaire/conftest.py | 6 +- tests/app/questionnaire/test_schema_utils.py | 6 +- 29 files changed, 419 insertions(+), 400 deletions(-) diff --git a/app/data_models/answer.py b/app/data_models/answer.py index fa75ef50c2..24295cd950 100644 --- a/app/data_models/answer.py +++ b/app/data_models/answer.py @@ -8,16 +8,14 @@ DictAnswer = dict[str, Union[int, str]] ListAnswer = list[str] +ListDictAnswer = list[DictAnswer] DictAnswerEscaped = dict[str, Union[int, Markup]] ListAnswerEscaped = list[Markup] +ListDictAnswerEscaped = list[DictAnswerEscaped] -AnswerValueTypes = Union[str, int, Decimal, DictAnswer, ListAnswer] +AnswerValueTypes = Union[str, int, Decimal, DictAnswer, ListAnswer, ListDictAnswer] AnswerValueEscapedTypes = Union[ - Markup, - int, - Decimal, - DictAnswerEscaped, - ListAnswerEscaped, + Markup, int, Decimal, DictAnswerEscaped, ListAnswerEscaped, ListDictAnswerEscaped ] @@ -61,6 +59,11 @@ def escape_answer_value(value: DictAnswer) -> DictAnswerEscaped: ... # pragma: no cover +@overload +def escape_answer_value(value: ListDictAnswer) -> ListDictAnswerEscaped: + ... # pragma: no cover + + @overload def escape_answer_value(value: str) -> Markup: ... # pragma: no cover diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 674214f509..0ea61d0245 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -461,7 +461,7 @@ def get_answer_fields( ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None - routing_path_block_ids: dict[tuple, list[str]] = {} + routing_path_block_ids: dict[tuple, tuple[str, ...]] = {} if location and progress_store: routing_path_block_ids = get_block_ids_for_calculated_summary_dependencies( diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index 76492c1402..1fc380ea7a 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import TYPE_CHECKING, Mapping, Sequence from werkzeug.datastructures import MultiDict @@ -16,11 +18,11 @@ def get_block_ids_for_calculated_summary_dependencies( schema: QuestionnaireSchema, location: Location | RelationshipLocation, progress_store: ProgressStore, - path_finder: "PathFinder", + path_finder: PathFinder, data: MultiDict | Mapping | Sequence, sections_to_ignore: list | None = None, -) -> dict[tuple, list[str]]: - block_ids_by_section: dict[tuple, list[str]] = {} +) -> dict[tuple, tuple[str, ...]]: + block_ids_by_section: dict[tuple, tuple[str, ...]] = {} sections_to_ignore = sections_to_ignore or [] dependent_sections = schema.calculated_summary_section_dependencies_by_block[ diff --git a/app/questionnaire/dynamic_answer_options.py b/app/questionnaire/dynamic_answer_options.py index a1aa86ac01..0ee21e60b5 100644 --- a/app/questionnaire/dynamic_answer_options.py +++ b/app/questionnaire/dynamic_answer_options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Any, Mapping, Union +from typing import Mapping, Union from app.questionnaire.rules.operator import Operator from app.questionnaire.rules.rule_evaluator import RuleEvaluator, RuleEvaluatorTypes @@ -12,7 +12,7 @@ @dataclass class DynamicAnswerOptions: - dynamic_options_schema: Mapping[str, Any] + dynamic_options_schema: Mapping rule_evaluator: RuleEvaluator value_source_resolver: ValueSourceResolver diff --git a/app/questionnaire/location.py b/app/questionnaire/location.py index e9cfbf0ee1..19d498660e 100644 --- a/app/questionnaire/location.py +++ b/app/questionnaire/location.py @@ -1,13 +1,13 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Mapping +from typing import Any, Mapping from flask import url_for class InvalidLocationException(Exception): - def __init__(self, value): + def __init__(self, value: str): super().__init__() self.value = value @@ -28,11 +28,11 @@ class Location: list_name: str | None = None list_item_id: str | None = None - def __hash__(self): + def __hash__(self) -> int: return hash(frozenset(self.__dict__.values())) @classmethod - def from_dict(cls, location_dict: Mapping): + def from_dict(cls, location_dict: Mapping[str, str]) -> Location: section_id = location_dict["section_id"] block_id = location_dict["block_id"] list_item_id = location_dict.get("list_item_id") @@ -44,7 +44,7 @@ def from_dict(cls, location_dict: Mapping): list_item_id=list_item_id, ) - def url(self, **kwargs) -> str: + def url(self, **kwargs: Any) -> str: """ Return the survey runner url that this location represents Any additional keyword arguments are parsed as query strings. diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 0cc2d8fff2..47583b3fe4 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional +from typing import Iterable, Mapping, MutableMapping, Sequence from werkzeug.datastructures import ImmutableDict @@ -9,7 +9,7 @@ from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath -from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.questionnaire.rules.rule_evaluator import RuleEvaluator, RuleEvaluatorTypes class PathFinder: @@ -19,8 +19,8 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Optional[MetadataProxy], - response_metadata: Mapping, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, ): self.answer_store = answer_store self.metadata = metadata @@ -30,7 +30,7 @@ def __init__( self.list_store = list_store def routing_path( - self, section_id: str, list_item_id: Optional[str] = None + self, section_id: str, list_item_id: str | None = None ) -> RoutingPath: """ Visits all the blocks in a section and returns a path given a list of answers. @@ -62,7 +62,7 @@ def routing_path( def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: """NB: At present when rules block dependencies does not fully support repeating sections. - It is supported when the section is dependent i.e the current section is repeating and building the routing path for sections that are not, + It is supported when the section is dependent i.e. the current section is repeating and building the routing path for sections that are not, It isn't supported if it needs to build the path for repeating sections""" return [ block_id @@ -79,10 +79,10 @@ def _get_not_skipped_blocks_in_section( routing_path_block_ids: list[str], section: ImmutableDict, when_rules_block_dependencies: list[str], - ) -> Optional[list[Mapping]]: + ) -> list[dict] | None: # :TODO: Fix group skipping in its own section. Routing path will be empty and therefore not checked if section: - not_skipped_blocks: list[Mapping] = [] + not_skipped_blocks: list[dict] = [] for group in section["groups"]: if "skip_conditions" in group: skip_conditions = group.get("skip_conditions") @@ -98,7 +98,9 @@ def _get_not_skipped_blocks_in_section( return not_skipped_blocks @staticmethod - def _block_index_for_block_id(blocks, block_id): + def _block_index_for_block_id( + blocks: Iterable[Mapping], block_id: str + ) -> int | None: return next( (index for (index, block) in enumerate(blocks) if block["id"] == block_id), None, @@ -106,7 +108,7 @@ def _block_index_for_block_id(blocks, block_id): def _build_routing_path_block_ids( self, - blocks: list[Mapping], + blocks: Sequence[Mapping], current_location: Location, when_rules_block_dependencies: list[str], ) -> list[str]: @@ -147,9 +149,10 @@ def _build_routing_path_block_ids( routing_path_block_ids.append(block_id) # If routing rules exist then a rule must match (i.e. default goto) - routing_rules = block.get("routing_rules") + routing_rules: Iterable[Mapping] | None = block.get("routing_rules") if routing_rules: - block_index = self._evaluate_routing_rules( + # Type ignore: block_index will always be non-null when evaluate is called + block_index = self._evaluate_routing_rules( # type: ignore this_location, blocks, routing_rules, @@ -173,13 +176,13 @@ def _build_routing_path_block_ids( def _evaluate_routing_rules( self, - this_location, - blocks, - routing_rules, - block_index, - routing_path_block_ids, - when_rules_block_dependencies, - ): + this_location: Location, + blocks: Iterable[Mapping], + routing_rules: Iterable[Mapping], + block_index: int, + routing_path_block_ids: list[str], + when_rules_block_dependencies: list[str], + ) -> int | None: if when_rules_block_dependencies: routing_path_block_ids = ( when_rules_block_dependencies + routing_path_block_ids @@ -225,11 +228,11 @@ def _evaluate_routing_rules( def evaluate_skip_conditions( self, - this_location, - routing_path_block_ids, - skip_conditions, - when_rules_block_dependencies, - ): + this_location: Location, + routing_path_block_ids: list[str], + skip_conditions: ImmutableDict[str, dict] | None, + when_rules_block_dependencies: list[str], + ) -> RuleEvaluatorTypes: if not skip_conditions: return False @@ -251,20 +254,22 @@ def evaluate_skip_conditions( return when_rule_evaluator.evaluate(skip_conditions["when"]) - def _get_next_block_id(self, rule): + def _get_next_block_id(self, rule: Mapping) -> str: if "group" in rule: - return self.schema.get_first_block_id_for_group(rule["group"]) - return rule["block"] + # Type ignore: by this point the block for the rule will exist + return self.schema.get_first_block_id_for_group(rule["group"]) # type: ignore + # Type ignore: the rules block will be a string + return rule["block"] # type: ignore def _remove_current_blocks_answers_for_backwards_routing( - self, rule: dict, this_location: Location + self, rule: Mapping, this_location: Location ) -> None: if block_id := this_location.block_id: answer_ids_for_current_block = self.schema.get_answer_ids_for_block( block_id ) if "when" in rule: - self._remove_block_anwers_for_backward_routing_according_to_when_rule( + self._remove_block_answers_for_backward_routing_according_to_when_rule( rule["when"], answer_ids_for_current_block ) @@ -273,8 +278,8 @@ def _remove_current_blocks_answers_for_backwards_routing( CompletionStatus.IN_PROGRESS, this_location.section_id ) - def _remove_block_anwers_for_backward_routing_according_to_when_rule( - self, rules: dict, answer_ids_for_current_block: list[str] + def _remove_block_answers_for_backward_routing_according_to_when_rule( + self, rules: Mapping, answer_ids_for_current_block: list[str] ) -> None: operands = self.schema.get_operands(rules) @@ -286,6 +291,6 @@ def _remove_block_anwers_for_backward_routing_according_to_when_rule( self.answer_store.remove_answer(rule["identifier"]) if QuestionnaireSchema.has_operator(rule): - return self._remove_block_anwers_for_backward_routing_according_to_when_rule( + return self._remove_block_answers_for_backward_routing_according_to_when_rule( rule, answer_ids_for_current_block ) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index bb4e2eba83..f196f9ef1e 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from decimal import Decimal -from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Sequence, Union +from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Sequence, TypeAlias from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore @@ -24,7 +26,7 @@ PlaceholderRenderer, # pragma: no cover ) -TransformedValueTypes = Union[None, str, int, Decimal, bool] +TransformedValueTypes: TypeAlias = None | str | int | Decimal | bool class PlaceholderParser: @@ -41,7 +43,7 @@ def __init__( metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, - renderer: "PlaceholderRenderer", + renderer: PlaceholderRenderer, progress_store: ProgressStore, list_item_id: str | None = None, location: Location | RelationshipLocation | None = None, @@ -49,7 +51,7 @@ def __init__( ): self._transformer = PlaceholderTransforms(language, schema, renderer) self._placeholder_map: MutableMapping[ - str, Union[ValueSourceEscapedTypes, ValueSourceTypes, None] + str, ValueSourceEscapedTypes | ValueSourceTypes | None ] = {} self._answer_store = answer_store self._list_store = list_store @@ -75,7 +77,7 @@ def __init__( def __call__( self, placeholder_list: Sequence[Mapping] - ) -> MutableMapping[str, Union[ValueSourceEscapedTypes, ValueSourceTypes]]: + ) -> MutableMapping[str, ValueSourceEscapedTypes | ValueSourceTypes]: placeholder_list = QuestionnaireSchema.get_mutable_deepcopy(placeholder_list) sections_to_ignore = list(self._routing_path_block_ids_by_section_key) @@ -137,12 +139,10 @@ def _parse_transforms( transformed_value: TransformedValueTypes = None for transform in transform_list: - transform_args: MutableMapping[str, Any] = {} + transform_args: MutableMapping = {} for arg_key, arg_value in transform["arguments"].items(): - resolved_value: Union[ - ValueSourceEscapedTypes, ValueSourceTypes, TransformedValueTypes - ] + resolved_value: ValueSourceEscapedTypes | ValueSourceTypes | TransformedValueTypes if isinstance(arg_value, list): resolved_value = self._resolve_value_source_list(arg_value) @@ -177,8 +177,8 @@ def _resolve_value_source_list( return values def _get_routing_path_block_ids( - self, data: Mapping | Sequence, sections_to_ignore: list | None = None - ) -> dict[tuple, list[str]] | None: + self, data: Sequence[Mapping], sections_to_ignore: list | None = None + ) -> dict[tuple, tuple[str, ...]] | None: if self._location: return get_block_ids_for_calculated_summary_dependencies( schema=self._schema, diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index aa6106f89c..c41063da70 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -1,4 +1,4 @@ -from typing import Any, Mapping, MutableMapping +from typing import Mapping, MutableMapping from jsonpointer import resolve_pointer, set_pointer @@ -45,7 +45,7 @@ def __init__( def render_pointer( self, *, - dict_to_render: Mapping[str, Any], + dict_to_render: Mapping, pointer_to_render: str, list_item_id: str | None, placeholder_parser: PlaceholderParser, @@ -70,7 +70,7 @@ def get_plural_count( def render_placeholder( self, - placeholder_data: MutableMapping[str, Any], + placeholder_data: MutableMapping, list_item_id: str | None, placeholder_parser: PlaceholderParser | None = None, ) -> str: @@ -93,8 +93,9 @@ def render_placeholder( if "text_plural" in placeholder_data: plural_schema: Mapping[str, dict] = placeholder_data["text_plural"] - count = ( - 0 + # Type ignore: For a valid schema the plural count will return an integer + count: int = ( + 0 # type: ignore if self._placeholder_preview_mode else self.get_plural_count(plural_schema["count"]) ) @@ -116,15 +117,15 @@ def render_placeholder( def render( self, *, - data_to_render: Mapping[str, Any], + data_to_render: Mapping, list_item_id: str | None, - ) -> dict[str, Any]: + ) -> dict: """ Transform the current schema json to a fully rendered dictionary """ - data_to_render_mutable: dict[ - str, Any - ] = QuestionnaireSchema.get_mutable_deepcopy(data_to_render) + data_to_render_mutable: dict = QuestionnaireSchema.get_mutable_deepcopy( + data_to_render + ) pointers = find_pointers_containing(data_to_render_mutable, "placeholders") placeholder_parser = PlaceholderParser( diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index d8e0b0d98a..5cdd6a45d8 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -1,6 +1,6 @@ from datetime import date, datetime, timezone from decimal import Decimal -from typing import TYPE_CHECKING, Optional, Sequence, Sized, Union +from typing import TYPE_CHECKING, Sequence, Sized from urllib.parse import quote from babel import units @@ -42,7 +42,7 @@ def __init__( input_date_format = "%Y-%m-%d" def format_currency( - self, number: Optional[Union[float, str]] = None, currency: str = "GBP" + self, number: float | str | None = None, currency: str = "GBP" ) -> str: formatted_currency: str = format_currency(number, currency, locale=self.locale) return formatted_currency @@ -95,8 +95,8 @@ def telephone_number_link(self, telephone_number: str) -> str: def email_link( self, email_address: str, - email_subject: Optional[str] = None, - email_subject_append: Optional[str] = None, + email_subject: str | None = None, + email_subject_append: str | None = None, ) -> str: href = f"mailto:{email_address}" if email_subject: @@ -123,7 +123,7 @@ def format_possessive(self, string_to_format: str) -> str: return string_to_format - def format_number(self, number: Union[int, Decimal, str]) -> str: + def format_number(self, number: int | Decimal | str) -> str: if number or number == 0: formatted_decimal: str = format_decimal(number, locale=self.locale) return formatted_decimal @@ -131,14 +131,14 @@ def format_number(self, number: Union[int, Decimal, str]) -> str: return "" @staticmethod - def format_percentage(value: Union[int, Decimal, str]) -> str: + def format_percentage(value: int | Decimal | str) -> str: return f"{value}%" def format_unit( self, unit: str, - value: Union[int, Decimal, str], - unit_length: Optional[str] = None, + value: int | Decimal | str, + unit_length: str | None = None, ) -> str: length = unit_length or "short" formatted_unit: str = units.format_unit( @@ -238,11 +238,11 @@ def format_date_range(self, date_range: tuple[str, str]) -> str: return f"{start_date_formatted} to {end_date_formatted}" @staticmethod - def add(lhs: Union[int, Decimal], rhs: Union[int, Decimal]) -> Union[int, Decimal]: + def add(lhs: int | Decimal, rhs: int | Decimal) -> int | Decimal: return lhs + rhs def format_ordinal( - self, number_to_format: int, determiner: Optional[str] = None + self, number_to_format: int, determiner: str | None = None ) -> str: indicator = self.get_ordinal_indicator(number_to_format) @@ -325,7 +325,7 @@ def _create_hyperlink(href: str, link_text: str) -> str: return f'{link_text}' @staticmethod - def list_item_count(list_to_count: Optional[Sized]) -> int: + def list_item_count(list_to_count: Sized | None) -> int: return len(list_to_count or []) def option_label_from_value(self, value: str, answer_id: str) -> str: @@ -336,5 +336,5 @@ def option_label_from_value(self, value: str, answer_id: str) -> str: return self.ops_helper.get_option_label_from_value(value, answer_id) @staticmethod - def conditional_trad_as(trad_as: Optional[str]) -> str: + def conditional_trad_as(trad_as: str | None) -> str: return f" ({trad_as})" if trad_as else "" diff --git a/app/questionnaire/plural_forms.py b/app/questionnaire/plural_forms.py index 1956f3bf85..8fecb25d93 100644 --- a/app/questionnaire/plural_forms.py +++ b/app/questionnaire/plural_forms.py @@ -3,7 +3,7 @@ from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE -def get_plural_form_key(count, language=DEFAULT_LANGUAGE_CODE): +def get_plural_form_key(count: int, language: str = DEFAULT_LANGUAGE_CODE) -> str: mappings = { "en": {"one": "n is 1"}, "cy": { @@ -23,5 +23,6 @@ def get_plural_form_key(count, language=DEFAULT_LANGUAGE_CODE): } plural_rule = PluralRule(mappings[language]) + plural_form: str = plural_rule(count) - return plural_rule(count) + return plural_form diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index a65084eb80..e1cf2a01f6 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -2,7 +2,7 @@ from copy import deepcopy from dataclasses import dataclass from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, Optional, Sequence, Union +from typing import Any, Generator, Iterable, Mapping, Sequence from flask_babel import force_locale from werkzeug.datastructures import ImmutableDict, MultiDict @@ -24,7 +24,7 @@ RELATIONSHIP_CHILDREN = ["UnrelatedQuestion"] -QuestionSchemaType = Mapping[str, Any] +QuestionSchemaType = Mapping class InvalidSchemaConfigurationException(Exception): @@ -44,8 +44,8 @@ class AnswerDependent: section_id: str block_id: str - for_list: Optional[str] = None - answer_id: Optional[str] = None + for_list: str | None = None + answer_id: str | None = None class QuestionnaireSchema: # pylint: disable=too-many-public-methods @@ -106,18 +106,18 @@ def json(self) -> Any: return self.serialize(self._questionnaire_json) @cached_property - def survey(self) -> Optional[str]: - survey: Optional[str] = self.json.get("survey") + def survey(self) -> str | None: + survey: str | None = self.json.get("survey") return survey @cached_property - def form_type(self) -> Optional[str]: - form_type: Optional[str] = self.json.get("form_type") + def form_type(self) -> str | None: + form_type: str | None = self.json.get("form_type") return form_type @cached_property - def region_code(self) -> Optional[str]: - region_code: Optional[str] = self.json.get("region_code") + def region_code(self) -> str | None: + region_code: str | None = self.json.get("region_code") return region_code @cached_property @@ -143,13 +143,13 @@ def get_mutable_deepcopy(cls, data: Any) -> Any: return deepcopy(data) @cached_property - def _flow(self) -> ImmutableDict[str, Any]: + def _flow(self) -> ImmutableDict: questionnaire_flow: ImmutableDict = self.json["questionnaire_flow"] return questionnaire_flow @cached_property - def flow_options(self) -> ImmutableDict[str, Any]: - options: ImmutableDict[str, Any] = self._flow["options"] + def flow_options(self) -> ImmutableDict: + options: ImmutableDict = self._flow["options"] return options @cached_property @@ -167,7 +167,10 @@ def is_view_submitted_response_enabled(self) -> bool: return is_enabled def _get_sections_by_id(self) -> dict[str, ImmutableDict]: - return {section["id"]: section for section in self.json.get("sections", [])} + return { + section["id"]: section + for section in self.json.get("sections", ImmutableDict({})) + } def _get_groups_by_id(self) -> dict[str, ImmutableDict]: groups_by_id: dict[str, ImmutableDict] = {} @@ -267,7 +270,7 @@ def _populate_answer_dependencies(self) -> None: ) def _update_answer_dependencies_for_calculated_summary( - self, calculated_summary_answer_ids: list[str], block_id: str + self, calculated_summary_answer_ids: Iterable[str], block_id: str ) -> None: for answer_id in calculated_summary_answer_ids: self._answer_dependencies_map[answer_id] |= { @@ -275,7 +278,7 @@ def _update_answer_dependencies_for_calculated_summary( } def _update_answer_dependencies_for_calculations( - self, calculations: tuple[ImmutableDict[str, Any]], *, block_id: str + self, calculations: tuple[ImmutableDict, ...], *, block_id: str ) -> None: for calculation in calculations: if source_answer_id := calculation.get("answer_id"): @@ -310,7 +313,11 @@ def _update_answer_dependencies_for_answer( ) def _update_answer_dependencies_for_dynamic_options( - self, dynamic_options_values: Mapping, *, block_id: str, answer_id: str + self, + dynamic_options_values: Mapping[str, Mapping], + *, + block_id: str, + answer_id: str, ) -> None: value_sources = get_mappings_with_key("source", dynamic_options_values) for value_source in value_sources: @@ -319,7 +326,11 @@ def _update_answer_dependencies_for_dynamic_options( ) def _update_answer_dependencies_for_value_source( - self, value_source: Mapping, *, block_id: str, answer_id: Optional[str] = None + self, + value_source: Mapping, + *, + block_id: str, + answer_id: str | None = None, ) -> None: if value_source["source"] == "answers": self._answer_dependencies_map[value_source["identifier"]] |= { @@ -338,7 +349,7 @@ def _update_answer_dependencies_for_value_source( } def _get_answer_dependent_for_block_id( - self, *, block_id: str, answer_id: Optional[str] = None + self, *, block_id: str, answer_id: str | None = None ) -> AnswerDependent: section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore for_list = self.get_repeating_list_for_section(section_id) @@ -350,23 +361,23 @@ def _get_answer_dependent_for_block_id( answer_id=answer_id, ) - def _get_flattened_questions(self) -> list[ImmutableDict[str, Any]]: + def _get_flattened_questions(self) -> list[ImmutableDict]: return [ question for questions in self._questions_by_id.values() for question in questions ] - def get_section_ids_required_for_hub(self) -> list[str]: - return self.flow_options.get("required_completed_sections", []) + def get_section_ids_required_for_hub(self) -> tuple[str, ...]: + return self.flow_options.get("required_completed_sections", tuple()) - def get_summary_options(self) -> ImmutableDict[str, Any]: - return self.flow_options.get("summary", {}) + def get_summary_options(self) -> ImmutableDict[str, bool]: + return self.flow_options.get("summary", ImmutableDict({})) def get_sections(self) -> Iterable[ImmutableDict]: return self._sections_by_id.values() - def get_section(self, section_id: str) -> Optional[ImmutableDict]: + def get_section(self, section_id: str) -> ImmutableDict | None: return self._sections_by_id.get(section_id) def get_section_ids_dependent_on_list(self, list_name: str) -> list[str]: @@ -385,7 +396,9 @@ def get_post_submission(self) -> ImmutableDict: schema: ImmutableDict = self.json.get("post_submission", ImmutableDict({})) return schema - def _is_list_name_in_rule(self, when_rule: Mapping, list_name: str) -> bool: + def _is_list_name_in_rule( + self, when_rule: Mapping[str, list], list_name: str + ) -> bool: if not QuestionnaireSchema.has_operator(when_rule): return False @@ -407,9 +420,9 @@ def _is_list_name_in_rule(self, when_rule: Mapping, list_name: str) -> bool: return False @staticmethod - def get_operands(rules: Mapping) -> list: + def get_operands(rules: Mapping) -> Sequence: operator = next(iter(rules)) - operands: list = rules[operator] + operands: Sequence = rules[operator] return operands def _section_ids_associated_to_list_name(self, list_name: str) -> list[str]: @@ -433,7 +446,7 @@ def get_blocks_for_section( @classmethod def get_driving_question_for_list( cls, section: Mapping, list_name: str - ) -> Optional[ImmutableDict]: + ) -> ImmutableDict | None: for block in cls.get_blocks_for_section(section): if ( block["type"] == "ListCollectorDrivingQuestion" @@ -441,16 +454,14 @@ def get_driving_question_for_list( ): return block - def get_remove_block_id_for_list(self, list_name: str) -> Optional[str]: + def get_remove_block_id_for_list(self, list_name: str) -> str | None: for block in self.get_blocks(): if block["type"] == "ListCollector" and block["for_list"] == list_name: remove_block_id: str = block["remove_block"]["id"] return remove_block_id - def get_individual_response_list(self) -> Optional[str]: - list_name: Optional[str] = self.json.get("individual_response", {}).get( - "for_list" - ) + def get_individual_response_list(self) -> str | None: + list_name: str | None = self.json.get("individual_response", {}).get("for_list") return list_name def get_individual_response_show_on_hub(self) -> bool: @@ -459,56 +470,54 @@ def get_individual_response_show_on_hub(self) -> bool: ) return show_on_hub - def get_individual_response_individual_section_id(self) -> Optional[str]: - section_id: Optional[str] = self._questionnaire_json.get( + def get_individual_response_individual_section_id(self) -> str | None: + section_id: str | None = self._questionnaire_json.get( "individual_response", {} ).get("individual_section_id") return section_id - def get_title_for_section(self, section_id: str) -> Optional[str]: + def get_title_for_section(self, section_id: str) -> str | None: if section := self.get_section(section_id): return section.get("title") - def get_show_on_hub_for_section(self, section_id: str) -> Optional[bool]: + def get_show_on_hub_for_section(self, section_id: str) -> bool | None: if section := self.get_section(section_id): return section.get("show_on_hub", True) - def get_summary_for_section(self, section_id: str) -> Optional[ImmutableDict]: + def get_summary_for_section(self, section_id: str) -> ImmutableDict | None: if section := self.get_section(section_id): return section.get("summary") - def get_summary_title_for_section(self, section_id: str) -> Optional[str]: + def get_summary_title_for_section(self, section_id: str) -> str | None: if summary := self.get_summary_for_section(section_id): return summary.get("title") - def show_summary_on_completion_for_section(self, section_id: str) -> Optional[bool]: + def show_summary_on_completion_for_section(self, section_id: str) -> bool | None: if summary := self.get_summary_for_section(section_id): return summary.get("show_on_completion", False) - def get_repeat_for_section(self, section_id: str) -> Optional[ImmutableDict]: + def get_repeat_for_section(self, section_id: str) -> ImmutableDict | None: if section := self.get_section(section_id): return section.get("repeat") - def get_repeating_list_for_section(self, section_id: str) -> Optional[str]: + def get_repeating_list_for_section(self, section_id: str) -> str | None: if repeat := self.get_repeat_for_section(section_id): return repeat.get("for_list") - def get_repeating_title_for_section( - self, section_id: str - ) -> Optional[ImmutableDict]: + def get_repeating_title_for_section(self, section_id: str) -> ImmutableDict | None: if repeat := self.get_repeat_for_section(section_id): title: ImmutableDict = repeat["title"] return title - def get_repeating_page_title_for_section(self, section_id: str) -> Optional[str]: + def get_repeating_page_title_for_section(self, section_id: str) -> str | None: if repeat := self.get_repeat_for_section(section_id): return repeat.get("page_title") - def get_custom_page_title_for_section(self, section_id: str) -> Optional[str]: + def get_custom_page_title_for_section(self, section_id: str) -> str | None: if summary := self.get_summary_for_section(section_id): return summary.get("page_title") - def get_section_for_block_id(self, block_id: str) -> Optional[ImmutableDict]: + def get_section_for_block_id(self, block_id: str) -> ImmutableDict | None: block = self.get_block(block_id) if ( @@ -522,7 +531,7 @@ def get_section_for_block_id(self, block_id: str) -> Optional[ImmutableDict]: return self.get_section(section_id) - def get_section_id_for_block_id(self, block_id: str) -> Optional[str]: + def get_section_id_for_block_id(self, block_id: str) -> str | None: if section := self.get_section_for_block_id(block_id): section_id: str = section["id"] return section_id @@ -530,19 +539,19 @@ def get_section_id_for_block_id(self, block_id: str) -> Optional[str]: def get_groups(self) -> Iterable[ImmutableDict]: return self._groups_by_id.values() - def get_group(self, group_id: str) -> Optional[ImmutableDict]: + def get_group(self, group_id: str) -> ImmutableDict | None: return self._groups_by_id.get(group_id) - def get_group_for_block_id(self, block_id: str) -> Optional[ImmutableDict]: + def get_group_for_block_id(self, block_id: str) -> ImmutableDict | None: return self._group_for_block(block_id) - def get_first_block_id_for_group(self, group_id: str) -> Optional[str]: + def get_first_block_id_for_group(self, group_id: str) -> str | None: group = self.get_group(group_id) if group: block_id: str = group["blocks"][0]["id"] return block_id - def get_first_block_id_for_section(self, section_id: str) -> Optional[str]: + def get_first_block_id_for_section(self, section_id: str) -> str | None: section = self.get_section(section_id) if section: group_id: str = section["groups"][0]["id"] @@ -551,24 +560,24 @@ def get_first_block_id_for_section(self, section_id: str) -> Optional[str]: def get_blocks(self) -> Iterable[ImmutableDict]: return self._blocks_by_id.values() - def get_block(self, block_id: str) -> Optional[ImmutableDict]: + def get_block(self, block_id: str) -> ImmutableDict | None: return self._blocks_by_id.get(block_id) def is_block_valid(self, block_id: str) -> bool: return bool(self.get_block(block_id)) - def get_block_for_answer_id(self, answer_id: str) -> Optional[ImmutableDict]: + def get_block_for_answer_id(self, answer_id: str) -> ImmutableDict | None: return self._block_for_answer(answer_id) - def is_block_in_repeating_section(self, block_id: str) -> Optional[bool]: + def is_block_in_repeating_section(self, block_id: str) -> bool | None: if section_id := self.get_section_id_for_block_id(block_id=block_id): return bool(self.get_repeating_list_for_section(section_id)) - def is_answer_in_list_collector_block(self, answer_id: str) -> Optional[bool]: + def is_answer_in_list_collector_block(self, answer_id: str) -> bool | None: if block := self.get_block_for_answer_id(answer_id): return self.is_list_block_type(block["type"]) - def is_answer_in_repeating_section(self, answer_id: str) -> Optional[bool]: + def is_answer_in_repeating_section(self, answer_id: str) -> bool | None: if block := self.get_block_for_answer_id(answer_id): return self.is_block_in_repeating_section(block_id=block["id"]) @@ -588,7 +597,7 @@ def get_answers_by_answer_id(self, answer_id: str) -> list[ImmutableDict]: answers: list[ImmutableDict] = self._answers_by_id.get(answer_id, []) return answers - def get_default_answer(self, answer_id: str) -> Optional[Answer]: + def get_default_answer(self, answer_id: str) -> Answer | None: if answer_schemas := self.get_answers_by_answer_id(answer_id): first_answer_schema = answer_schemas[0] try: @@ -599,7 +608,7 @@ def get_default_answer(self, answer_id: str) -> Optional[Answer]: def get_add_block_for_list_collector( self, list_collector_id: str - ) -> Optional[ImmutableDict]: + ) -> ImmutableDict | None: add_block_map = { "ListCollector": "add_block", "PrimaryPersonListCollector": "add_or_edit_block", @@ -610,16 +619,14 @@ def get_add_block_for_list_collector( ] return add_block - def get_answer_ids_for_list_items( - self, list_collector_id: str - ) -> Optional[list[str]]: + def get_answer_ids_for_list_items(self, list_collector_id: str) -> list[str] | None: """ Get answer ids used to add items to a list. """ if add_block := self.get_add_block_for_list_collector(list_collector_id): return self.get_answer_ids_for_block(add_block["id"]) - def get_questions(self, question_id: str) -> Optional[list[ImmutableDict]]: + def get_questions(self, question_id: str) -> list[ImmutableDict] | None: """Return a list of questions matching some question id This includes all questions inside variants """ @@ -640,7 +647,7 @@ def get_list_collectors_for_list( @staticmethod def get_list_collector_for_list( section: Mapping, for_list: str, primary: bool = False - ) -> Optional[ImmutableDict]: + ) -> ImmutableDict | None: try: return next( QuestionnaireSchema.get_list_collectors_for_list( @@ -653,8 +660,8 @@ def get_list_collector_for_list( @classmethod def get_answers_for_question_by_id( cls, question: QuestionSchemaType - ) -> dict[str, dict[str, Any]]: - answers: dict[str, dict[str, Any]] = {} + ) -> dict[str, dict]: + answers: dict[str, dict] = {} for answer in question.get("answers", {}): answers[answer["id"]] = answer @@ -693,7 +700,7 @@ def get_relationship_collectors(self) -> list[ImmutableDict]: def get_relationship_collectors_by_list_name( self, list_name: str - ) -> Optional[list[ImmutableDict]]: + ) -> list[ImmutableDict] | None: relationship_collectors = self.get_relationship_collectors() if relationship_collectors: return [ @@ -704,7 +711,7 @@ def get_relationship_collectors_by_list_name( def get_unrelated_block_no_answer_values( self, unrelated_answer_id: str - ) -> Optional[list[str]]: + ) -> list[str] | None: if unrelated_answers := self.get_answers_by_answer_id(unrelated_answer_id): return [ option["value"] @@ -714,7 +721,7 @@ def get_unrelated_block_no_answer_values( ] @staticmethod - def get_single_string_value(schema_object: Union[Mapping, str]) -> str: + def get_single_string_value(schema_object: Mapping | str) -> str: """ Resolves an identifying string value for the schema_object. If text_plural the `other` form is returned. :return: string value @@ -785,18 +792,17 @@ def get_values_for_key( ) -> Generator: ignore_keys = ignore_keys or [] for k, v in block.items(): - try: - if k in ignore_keys: - continue - if k == key: - yield v - if isinstance(v, dict): - yield from self.get_values_for_key(v, key, ignore_keys) - elif isinstance(v, (list, tuple)): - for d in v: - yield from self.get_values_for_key(d, key, ignore_keys) - except AttributeError: + if k in ignore_keys: continue + if k == key: + yield v + if isinstance(v, dict): + yield from self.get_values_for_key(v, key, ignore_keys) + elif isinstance(v, (list, tuple)): + for d in v: + # in the case of a when_rule "==": {dict, "Yes"} d could be a string + if isinstance(d, dict): + yield from self.get_values_for_key(d, key, ignore_keys) def _get_parent_section_id_for_block(self, block_id: str) -> str: parent_block_id = self._parent_id_map[block_id] @@ -804,7 +810,7 @@ def _get_parent_section_id_for_block(self, block_id: str) -> str: section_id = self._parent_id_map[group_id] return section_id - def _block_for_answer(self, answer_id: str) -> Optional[ImmutableDict]: + def _block_for_answer(self, answer_id: str) -> ImmutableDict | None: question_id = self._parent_id_map[answer_id] block_id = self._parent_id_map[question_id] parent_block_id = self._parent_id_map[block_id] @@ -815,7 +821,7 @@ def _block_for_answer(self, answer_id: str) -> Optional[ImmutableDict]: return self.get_block(block_id) - def _group_for_block(self, block_id: str) -> Optional[ImmutableDict]: + def _group_for_block(self, block_id: str) -> ImmutableDict | None: block = self.get_block(block_id) parent_id = self._parent_id_map[block_id] if block and block["type"] in LIST_COLLECTOR_CHILDREN: @@ -847,7 +853,7 @@ def _populate_when_rules_section_dependencies(self) -> None: ] = rules_section_dependencies def _get_rules_section_dependencies( - self, current_section_id: str, rules: Union[Mapping, Sequence] + self, current_section_id: str, rules: Mapping | Sequence ) -> set[str]: rules_section_dependencies: set[str] = set() @@ -859,8 +865,8 @@ def _get_rules_section_dependencies( continue answer_id_list: list = [] - identifier: Optional[str] = rule.get("identifier") - source: Optional[str] = rule.get("source") + identifier: str | None = rule.get("identifier") + source: str | None = rule.get("source") if source == "answers" and identifier: answer_id_list.append(identifier) @@ -935,7 +941,7 @@ def _get_calculated_summary_section_dependencies( def get_summary_item_for_list_for_section( self, *, section_id: str, list_name: str - ) -> Optional[ImmutableDict]: + ) -> ImmutableDict | None: if summary := self.get_summary_for_section(section_id): for item in summary.get("items", []): if item.get("for_list") == list_name: @@ -943,19 +949,19 @@ def get_summary_item_for_list_for_section( def get_related_answers_for_list_for_section( self, *, section_id: str, list_name: str - ) -> Optional[tuple[ImmutableDict]]: + ) -> tuple[ImmutableDict] | None: if item := self.get_summary_item_for_list_for_section( section_id=section_id, list_name=list_name ): return item.get("related_answers") - def get_item_label(self, section_id: str, list_name: str) -> Optional[str]: + def get_item_label(self, section_id: str, list_name: str) -> str | None: if summary := self.get_summary_for_section(section_id): for item in summary.get("items", []): if item["for_list"] == list_name and item.get("item_label"): return str(item["item_label"]) - def get_item_anchor(self, section_id: str, list_name: str) -> Optional[str]: + def get_item_anchor(self, section_id: str, list_name: str) -> str | None: if summary := self.get_summary_for_section(section_id): for item in summary.get("items", []): if item["for_list"] == list_name and item.get("item_anchor_answer_id"): @@ -973,9 +979,7 @@ def get_sources_for_type_from_data( return [source for source in sources if source["source"] == source_type] -def get_calculated_summary_answer_ids( - calculated_summary_block: Mapping[str, Any] -) -> list[str]: +def get_calculated_summary_answer_ids(calculated_summary_block: Mapping) -> list[str]: if calculated_summary_block["calculation"].get("answers_to_calculate"): return calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 4699871ffb..8b117da257 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,10 +1,13 @@ from collections import defaultdict, namedtuple from itertools import combinations -from typing import Any, Dict, Iterable, List, Mapping, Optional, Tuple, Union +from typing import Iterable, Mapping + +from werkzeug.datastructures import ImmutableDict from app.data_models import AnswerValueTypes, QuestionnaireStore from app.data_models.answer_store import Answer from app.data_models.progress_store import CompletionStatus, SectionKeyType +from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent @@ -16,7 +19,7 @@ class QuestionnaireStoreUpdater: """Component responsible for any actions that need to happen as a result of updating the questionnaire_store""" - EMPTY_ANSWER_VALUES: Tuple = (None, [], "", {}) + EMPTY_ANSWER_VALUES: tuple[None, list, str, dict] = (None, [], "", {}) def __init__( self, @@ -24,7 +27,7 @@ def __init__( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, router: Router, - current_question: Mapping[str, Any], + current_question: Mapping | None, ): self._current_location = current_location self._current_question = current_question or {} @@ -40,11 +43,11 @@ def __init__( ] = defaultdict(set) self.dependent_sections: set[DependentSection] = set() - def save(self): + def save(self) -> None: if self.is_dirty(): self._questionnaire_store.save() - def is_dirty(self): + def is_dirty(self) -> bool: if ( self._answer_store.is_dirty or self._list_store.is_dirty @@ -55,11 +58,12 @@ def is_dirty(self): def update_relationships_answer( self, - relationship_store, - relationships_answer_id, - ): + relationship_store: RelationshipStore, + relationships_answer_id: str, + ) -> None: self._answer_store.add_or_update( - Answer(relationships_answer_id, relationship_store.serialize()) + # Type ignore: serialize returns a list of typed dicts, so it is a valid answer type + Answer(relationships_answer_id, relationship_store.serialize()) # type: ignore ) def remove_completed_relationship_locations_for_list_name( @@ -106,21 +110,27 @@ def update_relationship_question_completeness(self, list_name: str) -> None: location = Location(section_id, collector["id"]) self.add_completed_location(location) - def _get_relationship_collectors_by_list_name(self, list_name: str): + def _get_relationship_collectors_by_list_name( + self, list_name: str + ) -> list[ImmutableDict] | None: return self._schema.get_relationship_collectors_by_list_name(list_name) - def _get_relationships_in_answer_store(self, relationship_answer_id: str): + def _get_relationships_in_answer_store( + self, relationship_answer_id: str + ) -> list[RelationshipDict]: return self._answer_store.get_answer(relationship_answer_id).value # type: ignore - def remove_answers(self, answer_ids: List, list_item_id: Optional[str] = None): + def remove_answers( + self, answer_ids: list[str], list_item_id: str | None = None + ) -> None: for answer_id in answer_ids: self._answer_store.remove_answer(answer_id, list_item_id=list_item_id) - def add_primary_person(self, list_name): + def add_primary_person(self, list_name: str) -> str: self.remove_completed_relationship_locations_for_list_name(list_name) - if self._list_store[list_name].primary_person: - return self._list_store[list_name].primary_person + if primary_person := self._list_store[list_name].primary_person: + return primary_person # If a primary person was initially answered negatively, then changed to positive, # the location must be removed from the progress store. @@ -128,12 +138,12 @@ def add_primary_person(self, list_name): return self._list_store.add_list_item(list_name, primary_person=True) - def add_list_item(self, list_name): + def add_list_item(self, list_name: str) -> str: new_list_item_id = self._list_store.add_list_item(list_name) self.remove_completed_relationship_locations_for_list_name(list_name) return new_list_item_id - def remove_primary_person(self, list_name: str): + def remove_primary_person(self, list_name: str) -> None: """Remove the primary person and all of their answers. Any context for the primary person will be removed """ @@ -141,7 +151,7 @@ def remove_primary_person(self, list_name: str): if list_item_id: self.remove_list_item_and_answers(list_name, list_item_id) - def remove_list_item_and_answers(self, list_name: str, list_item_id: str): + def remove_list_item_and_answers(self, list_name: str, list_item_id: str) -> None: """Remove answers from the answer store and update the list store to remove it. Any related relationship answers are re-evaluated for completeness. """ @@ -160,7 +170,7 @@ def remove_list_item_and_answers(self, list_name: str, list_item_id: str): def get_relationship_answers_for_list_name( self, list_name: str - ) -> Union[List[Answer], None]: + ) -> list[Answer] | None: associated_relationship_collectors = ( self._get_relationship_collectors_by_list_name(list_name) ) @@ -175,13 +185,13 @@ def get_relationship_answers_for_list_name( return self._answer_store.get_answers_by_answer_id(relationship_answer_ids) def update_same_name_items( - self, list_name: str, same_name_answer_ids: Optional[List[str]] - ): + self, list_name: str, same_name_answer_ids: list[str] | None + ) -> None: if not same_name_answer_ids: return same_name_items = set() - people_names: Dict[str, list] = {} + people_names: dict[str, str] = {} list_model = self._questionnaire_store.list_store[list_name] @@ -195,12 +205,12 @@ def update_same_name_items( if matching_list_item_id := people_names.get(current_list_item_name): same_name_items |= {current_list_item_id, matching_list_item_id} else: - people_names[current_list_item_name] = current_list_item_id # type: ignore + people_names[current_list_item_name] = current_list_item_id list_model.same_name_items = list(same_name_items) # type: ignore def remove_relationship_answers_for_list_item_id( - self, list_item_id: str, answers: List + self, list_item_id: str, answers: list ) -> None: for answer in answers: answers_to_keep = [ @@ -211,18 +221,18 @@ def remove_relationship_answers_for_list_item_id( answer.value = answers_to_keep self._answer_store.add_or_update(answer) - def add_completed_location(self, location: Optional[Location] = None): + def add_completed_location(self, location: Location | None = None) -> None: if not self._progress_store.is_routing_backwards: location = location or self._current_location self._progress_store.add_completed_location(location) - def remove_completed_location(self, location: Optional[Location] = None) -> bool: + def remove_completed_location(self, location: Location | None = None) -> bool: location = location or self._current_location return self._progress_store.remove_completed_location(location) def update_section_status( - self, is_complete: bool, section_id: str, list_item_id: Optional[str] = None - ): + self, is_complete: bool, section_id: str, list_item_id: str | None = None + ) -> None: status = ( CompletionStatus.COMPLETED if is_complete else CompletionStatus.IN_PROGRESS ) @@ -231,7 +241,7 @@ def update_section_status( def _update_answer( self, answer_id: str, - list_item_id: Optional[str], + list_item_id: str | None, answer_value: AnswerValueTypes, ) -> bool: answer_value_to_store = ( @@ -287,10 +297,10 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: ].add(dependency.block_id) def _get_list_item_ids_for_dependency( - self, dependency: AnswerDependent, is_repeating_answer: Optional[bool] = False - ) -> Union[list[str], list[None]]: + self, dependency: AnswerDependent, is_repeating_answer: bool | None = False + ) -> list[str] | list[None]: if dependency.for_list: - list_item_ids: Union[list[str], list[None]] + list_item_ids: list[str] | list[None] if is_repeating_answer: # If the source answer is repeating, then we must be in the current repeating section. @@ -322,7 +332,7 @@ def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: self.dependent_sections.add(DependentSection(section_id, None, None)) def update_answers( - self, form_data: Mapping[str, Any], list_item_id: Optional[str] = None + self, form_data: Mapping, list_item_id: str | None = None ) -> None: list_item_id = list_item_id or self._current_location.list_item_id answer_ids_for_question = self._schema.get_answer_ids_for_question( @@ -401,5 +411,7 @@ def _remove_dependent_blocks_and_capture_dependent_sections(self) -> None: DependentSection(section_id, list_item_id, False) ) - def started_section_keys(self, section_ids: Optional[Iterable[str]] = None): + def started_section_keys( + self, section_ids: Iterable[str] | None = None + ) -> list[SectionKeyType]: return self._progress_store.started_section_keys(section_ids) diff --git a/app/questionnaire/relationship_location.py b/app/questionnaire/relationship_location.py index e42c918f56..4151641ea0 100644 --- a/app/questionnaire/relationship_location.py +++ b/app/questionnaire/relationship_location.py @@ -1,7 +1,5 @@ -from __future__ import annotations - from dataclasses import dataclass -from typing import Mapping +from typing import Any, Mapping from flask import url_for @@ -18,7 +16,7 @@ def for_json(self) -> Mapping: attributes = vars(self) return {k: v for k, v in attributes.items() if v is not None} - def url(self, **kwargs) -> str: + def url(self, **kwargs: Any) -> str: if self.to_list_item_id: return url_for( "questionnaire.relationships", diff --git a/app/questionnaire/relationship_router.py b/app/questionnaire/relationship_router.py index c725f895e2..f0667f58b0 100644 --- a/app/questionnaire/relationship_router.py +++ b/app/questionnaire/relationship_router.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from app.data_models.answer_store import AnswerStore from app.data_models.relationship_store import RelationshipStore from app.questionnaire.relationship_location import RelationshipLocation @@ -16,9 +14,9 @@ def __init__( list_name: str, list_item_ids: list[str], relationships_block_id: str, - unrelated_block_id: Optional[str] = None, - unrelated_answer_id: Optional[str] = None, - unrelated_no_answer_values: Optional[List[str]] = None, + unrelated_block_id: str | None = None, + unrelated_answer_id: str | None = None, + unrelated_no_answer_values: list[str] | None = None, ): self.answer_store = answer_store self.relationship_store = relationship_store @@ -28,33 +26,37 @@ def __init__( self.relationships_block_id = relationships_block_id self.unrelated_block_id = unrelated_block_id self.unrelated_answer_id = unrelated_answer_id - self.unrelated_no_answer_values = unrelated_no_answer_values + self.unrelated_no_answer_values = unrelated_no_answer_values or [] self.path = self._relationships_routing_path() - def can_access_location(self, location): + def can_access_location(self, location: RelationshipLocation) -> bool: return location in self.path - def get_first_location(self): + def get_first_location(self) -> RelationshipLocation: return self.path[0] - def get_last_location(self): + def get_last_location(self) -> RelationshipLocation: return self.path[-1] - def get_next_location(self, location): + def get_next_location( + self, location: RelationshipLocation + ) -> RelationshipLocation | None: try: location_index = self.path.index(location) return self.path[location_index + 1] except IndexError: return None - def get_previous_location(self, location): + def get_previous_location( + self, location: RelationshipLocation + ) -> RelationshipLocation | None: location_index = self.path.index(location) if not location_index: return None return self.path[location_index - 1] - def _relationships_routing_path(self): - path = [] + def _relationships_routing_path(self) -> list[RelationshipLocation]: + path: list[RelationshipLocation] = [] for from_index, from_list_item_id in enumerate(self.list_item_ids): path += self._individual_relationships_routing_path( from_list_item_id=from_list_item_id, @@ -64,9 +66,9 @@ def _relationships_routing_path(self): return path def _individual_relationships_routing_path( - self, from_list_item_id, to_list_item_ids - ): - path = [] + self, from_list_item_id: str, to_list_item_ids: list[str] + ) -> list[RelationshipLocation]: + path: list[RelationshipLocation] = [] number_of_unrelated_relationships = 0 number_of_relationships_left = len(to_list_item_ids) unrelated_block_in_path = False @@ -87,7 +89,9 @@ def _individual_relationships_routing_path( ) unrelated_block_in_path = True unrelated_answer = self.answer_store.get_answer( - self.unrelated_answer_id, from_list_item_id + # Type ignore: unrelated_answer_id will exist if the unrelated_block_id does + self.unrelated_answer_id, # type: ignore + from_list_item_id, ) if ( unrelated_answer diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index d701c9ddf2..8fa1692329 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -1,4 +1,4 @@ -from typing import Generator, Mapping, MutableMapping, Optional +from typing import Generator, Mapping, MutableMapping from flask import url_for @@ -19,7 +19,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, ): self._schema = schema @@ -64,7 +64,7 @@ def get_first_incomplete_location_in_questionnaire_url(self) -> str: return self.get_next_location_url_for_end_of_section() - def get_last_location_in_questionnaire_url(self) -> Optional[str]: + def get_last_location_in_questionnaire_url(self) -> str | None: section_key = self._get_last_complete_section_key() if section_key: routing_path = self.routing_path(*section_key) @@ -102,14 +102,14 @@ def can_access_hub(self) -> bool: ) def can_display_section_summary( - self, section_id: str, list_item_id: Optional[str] = None + self, section_id: str, list_item_id: str | None = None ) -> bool: return bool( self._schema.get_summary_for_section(section_id) ) and self._progress_store.is_section_complete(section_id, list_item_id) def routing_path( - self, section_id: str, list_item_id: Optional[str] = None + self, section_id: str, list_item_id: str | None = None ) -> RoutingPath: return self._path_finder.routing_path(section_id, list_item_id) @@ -117,9 +117,9 @@ def get_next_location_url( self, location: Location, routing_path: RoutingPath, - return_to: Optional[str] = None, - return_to_answer_id: Optional[str] = None, - return_to_block_id: Optional[str] = None, + return_to: str | None = None, + return_to_answer_id: str | None = None, + return_to_block_id: str | None = None, ) -> str: """ Get the next location in the section. If the section is complete, determine where to go next, @@ -167,10 +167,10 @@ def get_previous_location_url( self, location: Location, routing_path: RoutingPath, - return_to: Optional[str] = None, - return_to_answer_id: Optional[str] = None, - return_to_block_id: Optional[str] = None, - ) -> Optional[str]: + return_to: str | None = None, + return_to_answer_id: str | None = None, + return_to_block_id: str | None = None, + ) -> str | None: """ Returns the previous 'location' to visit given a set of user answers or returns to the summary if the `return_to` var is set and the section is complete. @@ -184,7 +184,8 @@ def get_previous_location_url( ): return return_to_url - block_id_index = routing_path.index(location.block_id) + # Type ignore: the location will have a block id at this point + block_id_index = routing_path.index(location.block_id) # type: ignore if block_id_index != 0: previous_block_id = routing_path[block_id_index - 1] @@ -214,12 +215,12 @@ def get_previous_location_url( def _get_return_to_location_url( self, location: Location, - return_to: Optional[str], + return_to: str | None, routing_path: RoutingPath, - is_section_complete: Optional[bool] = None, - return_to_answer_id: Optional[str] = None, - return_to_block_id: Optional[str] = None, - ) -> Optional[str]: + is_section_complete: bool | None = None, + return_to_answer_id: str | None = None, + return_to_block_id: str | None = None, + ) -> str | None: if not return_to: return None @@ -316,7 +317,7 @@ def full_routing_path(self) -> list[RoutingPath]: return full_routing_path def is_block_complete( - self, *, block_id: str, section_id: str, list_item_id: str + self, *, block_id: str, section_id: str, list_item_id: str | None ) -> bool: return block_id in self._progress_store.get_completed_block_ids( section_id, list_item_id @@ -324,7 +325,7 @@ def is_block_complete( def _get_first_incomplete_location_in_section( self, routing_path: RoutingPath - ) -> Optional[Location]: + ) -> Location | None: for block_id in routing_path: if not self.is_block_complete( block_id=block_id, @@ -342,7 +343,7 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: """ The allowable path is the completed path plus the next location """ - allowable_path = [] + allowable_path: list[str] = [] if routing_path: for block_id in routing_path: @@ -371,12 +372,12 @@ def get_enabled_section_keys( section_key = (section_id, None) yield section_key - def _get_first_incomplete_section_key(self) -> Optional[tuple[str, Optional[str]]]: + def _get_first_incomplete_section_key(self) -> tuple[str, str | None] | None: for section_id, list_item_id in self.get_enabled_section_keys(): if not self._progress_store.is_section_complete(section_id, list_item_id): return section_id, list_item_id - def _get_last_complete_section_key(self) -> Optional[tuple[str, Optional[str]]]: + def _get_last_complete_section_key(self) -> tuple[str, str | None] | None: for section_id, list_item_id in list(self.get_enabled_section_keys())[::-1]: if self._progress_store.is_section_complete(section_id, list_item_id): return section_id, list_item_id @@ -406,9 +407,10 @@ def _is_section_enabled(self, section: Mapping) -> bool: @staticmethod def get_next_block_url( - location: Location, routing_path: RoutingPath, **kwargs: Optional[str] + location: Location, routing_path: RoutingPath, **kwargs: str | None ) -> str: - next_block_id = routing_path[routing_path.index(location.block_id) + 1] + # Type ignore: the location will have a block + next_block_id = routing_path[routing_path.index(location.block_id) + 1] # type: ignore return url_for( "questionnaire.block", block_id=next_block_id, @@ -420,7 +422,7 @@ def get_next_block_url( @staticmethod def _get_section_url( - location: Location, return_to_answer_id: Optional[str] = None + location: Location, return_to_answer_id: str | None = None ) -> str: return url_for( "questionnaire.get_section", diff --git a/app/questionnaire/routing_path.py b/app/questionnaire/routing_path.py index cc0583c545..f6ab522010 100644 --- a/app/questionnaire/routing_path.py +++ b/app/questionnaire/routing_path.py @@ -1,25 +1,34 @@ +from typing import Iterator, SupportsIndex + + class RoutingPath: """Holds a list of block_ids and has section_id, list_item_id and list_name attributes""" - def __init__(self, block_ids, section_id, list_item_id=None, list_name=None): + def __init__( + self, + block_ids: list[str], + section_id: str, + list_item_id: str | None = None, + list_name: str | None = None, + ): self.block_ids = tuple(block_ids) self.section_id = section_id self.list_item_id = list_item_id self.list_name = list_name - def __len__(self): + def __len__(self) -> int: return len(self.block_ids) - def __getitem__(self, index): + def __getitem__(self, index: int) -> str: return self.block_ids[index] - def __iter__(self): + def __iter__(self) -> Iterator[str]: return iter(self.block_ids) - def __reversed__(self): + def __reversed__(self) -> Iterator[str]: return reversed(self.block_ids) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, RoutingPath): return ( self.block_ids == other.block_ids @@ -33,5 +42,5 @@ def __eq__(self, other): return self.block_ids == other - def index(self, *args): - return self.block_ids.index(*args) + def index(self, value: str, *args: SupportsIndex) -> int: + return self.block_ids.index(value, *args) diff --git a/app/questionnaire/schema_utils.py b/app/questionnaire/schema_utils.py index 985ba02015..6f09c4e7f0 100644 --- a/app/questionnaire/schema_utils.py +++ b/app/questionnaire/schema_utils.py @@ -1,4 +1,11 @@ -def find_pointers_containing(input_data, search_key, pointer=None): +from typing import Generator, Iterable, Mapping + + +def find_pointers_containing( + input_data: Mapping | Iterable[Mapping], + search_key: str, + pointer: str | None = None, +) -> Generator[str, None, None]: """ Recursive function which lists pointers which contain a search key @@ -13,7 +20,7 @@ def find_pointers_containing(input_data, search_key, pointer=None): for k, v in input_data.items(): if (isinstance(v, dict)) and search_key in v: yield pointer + "/" + k if pointer else "/" + k - else: + elif isinstance(v, (list, tuple, dict)): yield from find_pointers_containing( v, search_key, pointer + "/" + k if pointer else "/" + k ) @@ -22,10 +29,7 @@ def find_pointers_containing(input_data, search_key, pointer=None): yield from find_pointers_containing(item, search_key, f"{pointer}/{index}") -def get_answer_ids_in_block(block): +def get_answer_ids_in_block(block: Mapping) -> list[str]: question = block["question"] - answer_ids = [] - for answer in question["answers"]: - answer_ids.append(answer["id"]) - + answer_ids = [answer["id"] for answer in question["answers"]] return answer_ids diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 79e3606810..819d9f3ce8 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from decimal import Decimal -from typing import Any, Callable, Iterable, Mapping, MutableMapping, Optional, Union +from typing import Callable, Iterable, Mapping, MutableMapping from markupsafe import Markup @@ -14,12 +14,9 @@ from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules import rule_evaluator -ValueSourceTypes = Union[None, str, int, Decimal, list] -ValueSourceEscapedTypes = Union[ - Markup, - list[Markup], -] -IntOrDecimal = Union[int, Decimal] +ValueSourceTypes = None | str | int | Decimal | list +ValueSourceEscapedTypes = Markup | list[Markup] +IntOrDecimal = int | Decimal @dataclass @@ -27,7 +24,7 @@ class ValueSourceResolver: answer_store: AnswerStore list_store: ListStore metadata: MetadataProxy | None - response_metadata: MutableMapping[str, Any] + response_metadata: MutableMapping schema: QuestionnaireSchema location: Location | RelationshipLocation | None list_item_id: str | None @@ -69,8 +66,8 @@ def _get_answer_value( def _resolve_list_item_id_for_value_source( self, value_source: Mapping - ) -> Optional[str]: - list_item_id: Optional[str] = None + ) -> str | None: + list_item_id: str | None = None if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": @@ -99,7 +96,7 @@ def _resolve_list_item_id_for_value_source( def _resolve_answer_value_source( self, value_source: Mapping - ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes]: + ) -> ValueSourceEscapedTypes | ValueSourceTypes: list_item_id = self._resolve_list_item_id_for_value_source(value_source) answer_id = value_source["identifier"] @@ -119,14 +116,12 @@ def _resolve_answer_value_source( return answer_value - def _resolve_list_value_source( - self, value_source: Mapping - ) -> Union[int, str, list]: + def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: identifier = value_source["identifier"] list_model: ListModel = self.list_store[identifier] if selector := value_source.get("selector"): - value: Union[str, list, int] = getattr(list_model, selector) + value: str | list | int = getattr(list_model, selector) return value return list(list_model) @@ -138,7 +133,7 @@ def _resolve_calculated_summary_value_source( The caller is responsible for ensuring the provided Calculated Summary and its answers are on the path. """ - calculated_summary_block: Mapping[str, Any] = self.schema.get_block(value_source["identifier"]) # type: ignore + calculated_summary_block: Mapping = self.schema.get_block(value_source["identifier"]) # type: ignore calculation = calculated_summary_block["calculation"] if calculation.get("answers_to_calculate"): operator = self.get_calculation_operator(calculation["calculation_type"]) @@ -177,7 +172,7 @@ def get_calculation_operator( def resolve( self, value_source: Mapping - ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes]: + ) -> ValueSourceEscapedTypes | ValueSourceTypes: source = value_source["source"] # We always need to assess the routing path for calculated summary value sources diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index c0be5ee7b1..92a2c7f62f 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -1,24 +1,33 @@ -from typing import MutableMapping +from typing import Mapping, MutableMapping from werkzeug.datastructures import ImmutableDict +from app.data_models.answer_store import AnswerStore +from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.progress_store import ProgressStore +from app.questionnaire import Location +from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator -def choose_variant( - block, - schema, - metadata, - response_metadata, - answer_store, - list_store, - variants_key, - single_key, - current_location, - progress_store, -): +# Type ignore: validation should ensure the variant exists when this is called +def choose_variant( # type: ignore + block: Mapping, + schema: QuestionnaireSchema, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + answer_store: AnswerStore, + list_store: ListStore, + variants_key: str, + single_key: str, + current_location: Location | RelationshipLocation, + progress_store: ProgressStore, +) -> dict: if block.get(single_key): - return block[single_key] + # Type ignore: the key passed in will be for a dictionary + return block[single_key] # type: ignore for variant in block.get(variants_key, []): when_rules = variant["when"] @@ -33,19 +42,20 @@ def choose_variant( ) if when_rule_evaluator.evaluate(when_rules): - return variant[single_key] + # Type ignore: question/content key is for a dictionary + return variant[single_key] # type: ignore def choose_question_to_display( - block, - schema, - metadata, - response_metadata, - answer_store, - list_store, - current_location, - progress_store, -): + block: ImmutableDict, + schema: QuestionnaireSchema, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + answer_store: AnswerStore, + list_store: ListStore, + current_location: Location | RelationshipLocation, + progress_store: ProgressStore, +) -> dict: return choose_variant( block, schema, @@ -61,15 +71,15 @@ def choose_question_to_display( def choose_content_to_display( - block, - schema, - metadata, - response_metadata, - answer_store, - list_store, - current_location, - progress_store, -): + block: ImmutableDict, + schema: QuestionnaireSchema, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + answer_store: AnswerStore, + list_store: ListStore, + current_location: Location | RelationshipLocation, + progress_store: ProgressStore, +) -> dict: return choose_variant( block, schema, @@ -85,14 +95,14 @@ def choose_content_to_display( def transform_variants( - block, - schema, - metadata, + block: ImmutableDict, + schema: QuestionnaireSchema, + metadata: MetadataProxy | None, response_metadata: MutableMapping, - answer_store, - list_store, - current_location, - progress_store, + answer_store: AnswerStore, + list_store: ListStore, + current_location: Location | RelationshipLocation, + progress_store: ProgressStore, ) -> ImmutableDict: output_block = dict(block) if "question_variants" in block: diff --git a/app/routes/flush.py b/app/routes/flush.py index ed613e0050..2ff8897a86 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -138,7 +138,7 @@ def _get_converted_answers_message( answer_converter( schema=schema, questionnaire_store=questionnaire_store, - routing_path=full_routing_path, + full_routing_path=full_routing_path, submitted_at=submitted_at, flushed=True, ) diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index 65510248f5..4e1aeada96 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -1,6 +1,8 @@ from collections import OrderedDict from datetime import datetime, timezone -from typing import Any, Iterable, Mapping, Optional, Union +from typing import Any, Iterable, Mapping, MutableMapping + +from werkzeug.datastructures import ImmutableDict from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import AnswerValueTypes, ListAnswer @@ -10,7 +12,7 @@ from app.questionnaire.routing_path import RoutingPath from app.questionnaire.variants import choose_question_to_display -MetadataType = Mapping[str, Union[str, int, list]] +MetadataType = MutableMapping[str, str | int | list] # pylint: disable=too-many-locals,too-many-nested-blocks @@ -37,6 +39,7 @@ def convert_answers_to_payload_0_0_1( :param list_store: list store :param schema: QuestionnaireSchema class with populated schema json :param full_routing_path: a list of section routing paths followed in the questionnaire + :param progress_store: progress store :return: data in a formatted form """ data = OrderedDict() @@ -50,7 +53,7 @@ def convert_answers_to_payload_0_0_1( for answer_in_block in answers_in_block: answer_schema = None - block = schema.get_block_for_answer_id(answer_in_block.answer_id) + block: ImmutableDict = schema.get_block_for_answer_id(answer_in_block.answer_id) # type: ignore current_location = Location( block_id=block_id, section_id=routing_path.section_id, @@ -151,7 +154,7 @@ def _get_checkbox_answer_data( return checkbox_answer_data -def _encode_value(value: AnswerValueTypes) -> Optional[str]: +def _encode_value(value: AnswerValueTypes) -> str | None: if isinstance(value, str): if value == "": return None diff --git a/app/submitter/convert_payload_0_0_3.py b/app/submitter/convert_payload_0_0_3.py index 8e9393eeef..a15708e017 100644 --- a/app/submitter/convert_payload_0_0_3.py +++ b/app/submitter/convert_payload_0_0_3.py @@ -1,4 +1,4 @@ -from typing import Iterable, Mapping, Optional +from typing import Iterable, Mapping from app.data_models import Answer, ListStore from app.data_models.answer_store import AnswerStore @@ -109,7 +109,7 @@ def add_relationships_unrelated_answers( section_id: str, relationships_block: Mapping, answers_payload: AnswerStore, -) -> Optional[RelationshipStore]: +) -> RelationshipStore | None: relationships_answer_id = schema.get_first_answer_id_for_block( relationships_block["id"] ) diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 72987bb999..e03c35a656 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -20,7 +20,7 @@ def convert_answers( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, - routing_path: Iterable[RoutingPath], + full_routing_path: Iterable[RoutingPath], submitted_at: datetime, flushed: bool = False, ) -> dict[str, Any]: @@ -56,7 +56,7 @@ def convert_answers( Args: schema: QuestionnaireSchema instance with populated schema json questionnaire_store: EncryptedQuestionnaireStorage instance for accessing current questionnaire data - routing_path: The full routing path followed by the user when answering the questionnaire + full_routing_path: The full routing path followed by the user when answering the questionnaire submitted_at: The date and time of submission flushed: True when system submits the users answers, False when submitted by user. Returns: @@ -93,7 +93,7 @@ def convert_answers( answer_store=answer_store, list_store=list_store, schema=schema, - routing_path=routing_path, + full_routing_path=full_routing_path, metadata=metadata, response_metadata=response_metadata, progress_store=progress_store, diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index b4c6aed5a9..c2e88745b2 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Iterable, Mapping, MutableMapping, Optional, OrderedDict, Union +from typing import Any, Iterable, Mapping, MutableMapping, OrderedDict from structlog import get_logger @@ -16,7 +16,7 @@ logger = get_logger() -MetadataType = Mapping[str, Optional[Union[str, list]]] +MetadataType = Mapping[str, str | list | None] class DataVersionError(Exception): @@ -31,7 +31,7 @@ def __str__(self) -> str: def convert_answers_v2( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, - routing_path: Iterable[RoutingPath], + full_routing_path: Iterable[RoutingPath], submitted_at: datetime, flushed: bool = False, ) -> dict[str, Any]: @@ -42,7 +42,7 @@ def convert_answers_v2( Args: schema: QuestionnaireSchema instance with populated schema json questionnaire_store: EncryptedQuestionnaireStorage instance for accessing current questionnaire data - routing_path: The full routing path followed by the user when answering the questionnaire + full_routing_path: The full routing path followed by the user when answering the questionnaire submitted_at: The date and time of submission flushed: True when system submits the users answers, False when submitted by user. Returns: @@ -83,7 +83,7 @@ def convert_answers_v2( answer_store=answer_store, list_store=list_store, schema=schema, - routing_path=routing_path, + full_routing_path=full_routing_path, metadata=metadata, response_metadata=response_metadata, progress_store=progress_store, @@ -112,11 +112,11 @@ def get_payload_data( answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, - routing_path: Iterable[RoutingPath], + full_routing_path: Iterable[RoutingPath], metadata: MetadataProxy, - response_metadata: Mapping, + response_metadata: MutableMapping, progress_store: ProgressStore, -) -> Union[OrderedDict[str, Any], dict[str, Union[list[Any]]]]: +) -> OrderedDict[str, Any] | dict[str, list[Any]]: if schema.json["data_version"] == "0.0.1": return convert_answers_to_payload_0_0_1( metadata=metadata, @@ -124,7 +124,7 @@ def get_payload_data( answer_store=answer_store, list_store=list_store, schema=schema, - full_routing_path=routing_path, + full_routing_path=full_routing_path, progress_store=progress_store, ) @@ -133,10 +133,10 @@ def get_payload_data( answer_store=answer_store, list_store=list_store, schema=schema, - full_routing_path=routing_path, + full_routing_path=full_routing_path, ) - data: dict[str, Union[list[Any]]] = { + data: dict[str, list[Any]] = { "answers": answers, "lists": list_store.serialize(), } diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 9ede3a4611..c1a15dadd3 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -139,8 +139,9 @@ def _build_calculated_summary_section( else: answers_to_calculate = get_calculated_summary_answer_ids(rendered_block) - blocks_to_calculate: list = [ - self._schema.get_block_for_answer_id(answer_id) + blocks_to_calculate: list[ImmutableDict] = [ + # Type ignore: the answer blocks will always exist at this point + self._schema.get_block_for_answer_id(answer_id) # type: ignore for answer_id in answers_to_calculate ] @@ -161,12 +162,12 @@ def _build_calculated_summary_section( return {"id": section_id, "groups": [{"id": group["id"], "blocks": blocks}]} def _remove_unwanted_questions_answers( - self, block: Mapping[str, Any], answer_ids_to_keep: Iterable[str] - ) -> dict[str, Any]: + self, block: ImmutableDict, answer_ids_to_keep: Iterable[str] + ) -> dict: """ Evaluates questions in a block and removes any questions not containing a relevant answer """ - transformed_block: dict[str, Any] = transform_variants( + transformed_block: ImmutableDict = transform_variants( block, self._schema, self._metadata, diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 43981c7ecd..99677a2b4f 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -70,7 +70,7 @@ def get_question( answer_store: AnswerStore, list_store: ListStore, metadata: Optional[MetadataProxy], - response_metadata: Mapping, + response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, return_to: Optional[str], diff --git a/mypy.ini b/mypy.ini index 4192e70fd6..ebbd7085f8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -16,22 +16,7 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.questionnaire.placeholder_parser] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.questionnaire.placeholder_renderer] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.questionnaire.placeholder_transforms] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.questionnaire.questionnaire_schema] +[mypy-app.questionnaire.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True @@ -61,21 +46,11 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.questionnaire.value_source_resolver] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - [mypy-app.libs.utils] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.questionnaire.routing.*] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - [mypy-app.forms.field_handlers.*] disallow_untyped_defs = True warn_return_any = True @@ -131,16 +106,6 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.questionnaire.router] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.questionnaire.dynamic_answer_options] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - [mypy-app.secrets] disallow_untyped_defs = True warn_return_any = True diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index c2738adda8..d745f5af4c 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -756,7 +756,7 @@ def content_variant_schema(): "title": "Block 1", "content_variants": [ { - "content": [{"title": "You are over 16"}], + "content": {"title": "You are over 16"}, "when": { ">": [ { @@ -768,7 +768,7 @@ def content_variant_schema(): }, }, { - "content": [{"title": "You are under 16"}], + "content": {"title": "You are under 16"}, "when": { "<=": [ { @@ -780,7 +780,7 @@ def content_variant_schema(): }, }, { - "content": [{"title": "You are ageless"}], + "content": {"title": "You are ageless"}, "when": { "==": [ { diff --git a/tests/app/questionnaire/test_schema_utils.py b/tests/app/questionnaire/test_schema_utils.py index 4d27d1fa8b..2fd44b5dc5 100644 --- a/tests/app/questionnaire/test_schema_utils.py +++ b/tests/app/questionnaire/test_schema_utils.py @@ -78,7 +78,7 @@ def test_transform_variants_with_content(content_variant_schema): assert transformed_block != block assert "content_variants" not in transformed_block - assert transformed_block["content"][0]["title"] == "You are over 16" + assert transformed_block["content"]["title"] == "You are over 16" def test_transform_variants_with_no_variants(question_schema): @@ -180,7 +180,7 @@ def test_choose_content_to_display(content_variant_schema): progress_store=ProgressStore(), ) - assert content_to_display[0]["title"] == "You are over 16" + assert content_to_display["title"] == "You are over 16" answer_store = AnswerStore({}) @@ -195,7 +195,7 @@ def test_choose_content_to_display(content_variant_schema): progress_store=ProgressStore(), ) - assert content_to_display[0]["title"] == "You are ageless" + assert content_to_display["title"] == "You are ageless" def test_choose_question_to_display(question_variant_schema): From 568bf19771f760ee295de03014417b70aaac1da9 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:55:07 +0100 Subject: [PATCH 215/567] Update schema version to v3.52.0 (#1095) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 100a28e0e5..4f203efe99 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.51.0 +v3.52.0 From f7208c84637c22e433a829a6ff23baaf08e446cc Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 3 May 2023 13:38:58 +0100 Subject: [PATCH 216/567] Update to chromedriver v112 (#1097) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 591411bee7..f4e26d4f46 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/mocha-framework": "^8.3.0", "@wdio/spec-reporter": "^8.3.0", "chai": "^4.3.6", - "chromedriver": "^111.0.0", + "chromedriver": "^112.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index fad76959a7..e07600b0ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,10 +1786,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^111.0.0: - version "111.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-111.0.0.tgz#ef704dc44524ce5c1eced283d523b7aefd59ee19" - integrity sha512-XavNYNBBfKIrT8Oi/ywJ0DoOOU+jHF5bQWTkqStFsAXvfCV9VzYN3J+TGAvZdrpXeoixqPRGUxQ2yZhD2iowdQ== +chromedriver@^112.0.0: + version "112.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-112.0.1.tgz#b1d23720613d7afa17cde6d5057e765109e07e69" + integrity sha512-ieQzvellbtPY4MUrFzzayC1bZa/HoBsGXejUQJhAPWcYALxtkjUZNUYWbojMjIzf8iIhVda9VvdXiRKqdlN7ow== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From f4fd625d0f8bff8fd3603e7dc18e33a3152b5756 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 3 May 2023 13:55:01 +0100 Subject: [PATCH 217/567] Update MAX_NUMBERS to 15 digits (#1089) Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/settings.py | 2 +- schemas/test/en/test_currency.json | 91 ++++++++++++++++++- schemas/test/en/test_numbers.json | 8 +- schemas/test/en/test_unit_patterns.json | 11 +++ tests/app/forms/test_questionnaire_form.py | 7 +- tests/app/test_jinja_filters.py | 10 +- tests/functional/spec/numbers.spec.js | 4 +- .../test_questionnaire_html_escaping.py | 2 +- tests/integration/routes/test_thank_you.py | 9 +- 9 files changed, 123 insertions(+), 21 deletions(-) diff --git a/app/settings.py b/app/settings.py index d5e212698f..675c8f1bd2 100644 --- a/app/settings.py +++ b/app/settings.py @@ -126,7 +126,7 @@ def utcoffset_or_fail(date_value, key): EQ_SESSION_ID = "eq-session-id" EQ_LIST_ITEM_ID_LENGTH = 6 -MAX_NUMBER = 9999999999 +MAX_NUMBER = 999_999_999_999_999 CONFIRMATION_EMAIL_LIMIT = int(os.getenv("CONFIRMATION_EMAIL_LIMIT", "10")) diff --git a/schemas/test/en/test_currency.json b/schemas/test/en/test_currency.json index de84a6f6c8..9f26654f51 100644 --- a/schemas/test/en/test_currency.json +++ b/schemas/test/en/test_currency.json @@ -8,8 +8,6 @@ "theme": "default", "description": "A questionnaire to test currency input type", "messages": { - "NUMBER_TOO_LARGE": "Number is too large", - "NUMBER_TOO_SMALL": "Number cannot be less than zero", "INVALID_DECIMAL": "Please enter a number to %(max)d decimal places" }, "metadata": [ @@ -52,7 +50,7 @@ "question": { "answers": [ { - "id": "answer", + "id": "answer-gbp", "label": "How much did you spend?", "mandatory": false, "type": "Currency", @@ -93,10 +91,93 @@ "maximum": { "value": 1000000 } + }, + { + "id": "answer-gbp-max-range", + "label": "How much did you spend? (Max range)", + "mandatory": false, + "type": "Currency", + "currency": "GBP" + } + ], + "id": "currency-question", + "title": "Currency Input Test Positve", + "type": "General" + } + }, + { + "type": "Question", + "id": "negative-block", + "question": { + "answers": [ + { + "id": "negative-answer-gbp", + "label": "How much did you spend?", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": 10000 + }, + "minimum": { + "value": -1000000 + } + }, + { + "id": "negative-answer-usd", + "label": "How much did you spend?", + "mandatory": false, + "type": "Currency", + "currency": "USD", + "decimal_places": 2, + "maximum": { + "value": 10000 + }, + "minimum": { + "value": -1000000 + } + }, + { + "id": "negative-answer-eur", + "label": "How much did you spend?", + "mandatory": false, + "type": "Currency", + "currency": "EUR", + "decimal_places": 2, + "maximum": { + "value": 10000 + }, + "minimum": { + "value": -1000000 + } + }, + { + "id": "negative-answer-jpy", + "label": "How much did you spend?", + "mandatory": false, + "type": "Currency", + "currency": "JPY", + "maximum": { + "value": 1000000 + }, + "minimum": { + "value": -1000000 + } + }, + { + "id": "answer-gbp-min-range", + "label": "How much did you spend? (Min range)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "minimum": { + "value": -99999999999999 + } } ], - "id": "question", - "title": "Currency Input Test", + "id": "negative-currency-question", + "title": "Currency Input Test Including Negative Values", "type": "General" } } diff --git a/schemas/test/en/test_numbers.json b/schemas/test/en/test_numbers.json index 1746e25300..723cad7a2b 100644 --- a/schemas/test/en/test_numbers.json +++ b/schemas/test/en/test_numbers.json @@ -48,7 +48,7 @@ "type": "Number", "decimal_places": 2, "minimum": { - "value": 0 + "value": -1000 }, "maximum": { "value": 1000 @@ -187,11 +187,11 @@ }, { "id": "test-min", - "label": "Min Test (123 to 9,999,999,999)", + "label": "Min Test (-123 to 999,999,999,999,999)", "mandatory": false, "type": "Number", "minimum": { - "value": 123 + "value": -123 } }, { @@ -205,7 +205,7 @@ }, { "id": "test-min-exclusive", - "label": "Min Exclusive Test (124 to 9,999,999,999 - 123 Exclusive)", + "label": "Min Exclusive Test (124 to 999,999,999,999,999 - 123 Exclusive)", "mandatory": false, "type": "Number", "minimum": { diff --git a/schemas/test/en/test_unit_patterns.json b/schemas/test/en/test_unit_patterns.json index fe44283bd6..d54a8bca90 100644 --- a/schemas/test/en/test_unit_patterns.json +++ b/schemas/test/en/test_unit_patterns.json @@ -83,6 +83,17 @@ "maximum": { "value": 1000 } + }, + { + "id": "min-max-miles", + "label": "Length in Miles - Min Max Range", + "mandatory": false, + "type": "Unit", + "unit": "length-mile", + "unit_length": "short", + "minimum": { + "value": -99999999999999 + } } ], "id": "set-length-units-question", diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 6c3623d570..d61f2e2a69 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1660,13 +1660,16 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): list_store, metadata=get_metadata(), response_metadata={}, - form_data=MultiDict({"set-minimum": "-1"}), + form_data=MultiDict({"set-minimum": "-10001"}), progress_store=ProgressStore(), ) form.validate() answer_errors = form.answer_errors("set-minimum") - assert schema.error_messages["NUMBER_TOO_SMALL"] % {"min": "0"} in answer_errors + assert ( + schema.error_messages["NUMBER_TOO_SMALL"] % {"min": "-1,000"} + in answer_errors + ) def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index f219bac98d..f114076848 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -237,12 +237,18 @@ def test_format_duration(duration, formatted_duration, app): @pytest.mark.parametrize( "answer, width", ( - ({}, 10), + ({}, 15), ({"maximum": {"value": 1}}, 1), ({"maximum": {"value": 123456}}, 6), ({"maximum": {"value": 12345678901}}, 15), ({"minimum": {"value": -123456}, "maximum": {"value": 1234}}, 7), ({"decimal_places": 2, "maximum": {"value": 123456}}, 8), + ({"maximum": {"value": 999_999_999_999_999}}, 15), + ({"decimal_places": 5, "maximum": {"value": 999_999_999_999_999}}, 20), + ({"decimal_places": 6, "maximum": {"value": 999_999_999_999_999}}, 30), + ({"minimum": {"value": -99_999_999_999_999}}, 15), + ({"decimal_places": 5, "minimum": {"value": -99_999_999_999_999}}, 20), + ({"decimal_places": 6, "minimum": {"value": -99_999_999_999_999}}, 30), ( {"maximum": {"value": 123456789012345678901123456789012345678901234567890}}, None, @@ -598,7 +604,7 @@ def test_get_formatted_address(address_fields, formatted_address): @pytest.mark.parametrize( "max_value, expected_width", [ - (None, 10), + (None, 15), (1, 1), (123123123123, 15), ], diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index a1bddbcaee..46b634cad2 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -20,7 +20,7 @@ describe("Number validation", () => { await $(TestMinMax.testRange()).setValue("9"); await $(TestMinMax.testRangeExclusive()).setValue("10"); - await $(TestMinMax.testMin()).setValue("0"); + await $(TestMinMax.testMin()).setValue("-124"); await $(TestMinMax.testMax()).setValue("12345"); await $(TestMinMax.testMinExclusive()).setValue("123"); await $(TestMinMax.testMaxExclusive()).setValue("12345"); @@ -30,7 +30,7 @@ describe("Number validation", () => { await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to 10"); await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter an answer more than 10"); - await expect(await $(TestMinMax.errorNumber(3)).getText()).to.contain("Enter an answer more than or equal to 123"); + await expect(await $(TestMinMax.errorNumber(3)).getText()).to.contain("Enter an answer more than or equal to -123"); await expect(await $(TestMinMax.errorNumber(4)).getText()).to.contain("Enter an answer less than or equal to 1,234"); await expect(await $(TestMinMax.errorNumber(5)).getText()).to.contain("Enter an answer more than 123"); await expect(await $(TestMinMax.errorNumber(6)).getText()).to.contain("Enter an answer less than 1,234"); diff --git a/tests/integration/questionnaire/test_questionnaire_html_escaping.py b/tests/integration/questionnaire/test_questionnaire_html_escaping.py index f893cc6af1..790bd133a3 100644 --- a/tests/integration/questionnaire/test_questionnaire_html_escaping.py +++ b/tests/integration/questionnaire/test_questionnaire_html_escaping.py @@ -35,7 +35,7 @@ def test_quotes_in_detail_answer(self): def test_quotes_in_numeric_answers(self): testdata = [ ("test_numbers", "set-minimum"), - ("test_currency", "answer"), + ("test_currency", "answer-gbp"), ("test_percentage", "answer"), ("test_unit_patterns", "centimetres"), ("test_dates", "date-range-from-answer-day"), diff --git a/tests/integration/routes/test_thank_you.py b/tests/integration/routes/test_thank_you.py index 3f373c8bc3..44a48f9800 100644 --- a/tests/integration/routes/test_thank_you.py +++ b/tests/integration/routes/test_thank_you.py @@ -9,16 +9,17 @@ def test_thank_you_page_no_sign_out(self): # We fill in our answers form_data = { - "answer": "12", + "answer-gbp": "12", "answer-usd": "345", "answer-eur": "67.89", "answer-jpy": "0", } - # We submit the form + # We submit the form and answers for the first page + self.post(form_data) + self.post() + # We submit the form and answers for the second page self.post(form_data) - - # Submit answers self.post() # check we're on the thank you page and there's no sign out button From d564e64682447204e6f8acb745ad50425fc795a3 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 3 May 2023 14:45:03 +0100 Subject: [PATCH 218/567] Update summaries to allow support for repeating sections (#1091) --- app/jinja_filters.py | 6 +- app/translations/messages.pot | 125 ++-- app/views/contexts/section_summary_context.py | 17 +- .../contexts/submit_questionnaire_context.py | 1 + app/views/contexts/summary/group.py | 18 +- app/views/contexts/summary_context.py | 123 +++- .../view_submitted_response_context.py | 1 + .../test_list_collector_section_summary.json | 3 + ...submitted_response_repeating_sections.json | 664 ++++++++++++++++++ templates/partials/summary/summary.html | 17 +- tests/app/test_jinja_filters.py | 8 +- .../views/contexts/test_summary_context.py | 469 +++++++++++++ .../view_submitted_response.spec.js | 108 +++ .../list_collector_section_summary.spec.js | 66 +- 14 files changed, 1508 insertions(+), 118 deletions(-) create mode 100644 schemas/test/en/test_view_submitted_response_repeating_sections.json create mode 100644 tests/app/views/contexts/test_summary_context.py diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 3a9faeaa2f..29d7e18b20 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -573,6 +573,8 @@ def map_summary_item_config( edit_link_text: str, edit_link_aria_label: str, calculated_question: Optional[dict[str, list]], + remove_link_text: str | None = None, + remove_link_aria_label: str | None = None, icon: Optional[str] = None, ) -> list[Union[dict[str, list], SummaryRow]]: rows: list[Union[dict[str, list], SummaryRow]] = [] @@ -595,8 +597,8 @@ def map_summary_item_config( icon=icon, edit_link_text=edit_link_text, edit_link_aria_label=edit_link_aria_label, - remove_link_text=flask_babel.lazy_gettext("Remove"), - remove_link_aria_label=flask_babel.lazy_gettext("Remove {item_name}"), + remove_link_text=remove_link_text, + remove_link_aria_label=remove_link_aria_label, related_answers=block.get("related_answers"), item_label=block.get("item_label"), item_anchor=block.get("item_anchor"), diff --git a/app/translations/messages.pot b/app/translations/messages.pot index c56f4bb583..9fcab41dd0 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-04-21 14:37+0100\n" +"POT-Creation-Date: 2023-04-27 09:02+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,17 +41,9 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:598 templates/partials/summary/list-summary.html:9 -msgid "Remove" -msgstr "" - -#: app/jinja_filters.py:599 templates/partials/summary/list-summary.html:10 -msgid "Remove {item_name}" -msgstr "" - -#: app/jinja_filters.py:699 +#: app/jinja_filters.py:701 #: templates/partials/summary/collapsible-summary.html:26 -#: templates/partials/summary/summary.html:20 +#: templates/partials/summary/summary.html:22 msgid "No answer provided" msgstr "" @@ -257,63 +249,63 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:135 +#: app/routes/errors.py:155 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:138 +#: app/routes/errors.py:158 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:154 +#: app/routes/errors.py:176 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:157 +#: app/routes/errors.py:179 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:189 +#: app/routes/errors.py:215 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:195 +#: app/routes/errors.py:222 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:198 app/routes/errors.py:221 app/routes/errors.py:243 +#: app/routes/errors.py:225 app/routes/errors.py:250 app/routes/errors.py:272 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:217 +#: app/routes/errors.py:246 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:218 +#: app/routes/errors.py:247 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:239 templates/errors/403.html:3 +#: app/routes/errors.py:268 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:240 +#: app/routes/errors.py:269 msgid "You can try to submit your feedback again." msgstr "" -#: app/routes/individual_response.py:184 +#: app/routes/individual_response.py:206 msgid "An individual access code has been sent by post" msgstr "" -#: app/routes/individual_response.py:278 +#: app/routes/individual_response.py:312 msgid "An individual access code has been sent by text" msgstr "" @@ -548,7 +540,7 @@ msgstr "" #: app/views/handlers/confirm_email.py:102 #: app/views/handlers/confirm_email.py:103 -#: app/views/handlers/individual_response.py:880 +#: app/views/handlers/individual_response.py:905 msgid "No, I need to change it" msgstr "" @@ -598,140 +590,140 @@ msgstr "" msgid "Do not include confidential information, such as your contact details" msgstr "" -#: app/views/handlers/individual_response.py:140 +#: app/views/handlers/individual_response.py:148 msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:254 +#: app/views/handlers/individual_response.py:267 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:324 +#: app/views/handlers/individual_response.py:343 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:347 +#: app/views/handlers/individual_response.py:367 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:349 +#: app/views/handlers/individual_response.py:369 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:357 +#: app/views/handlers/individual_response.py:377 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:359 +#: app/views/handlers/individual_response.py:379 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:368 +#: app/views/handlers/individual_response.py:388 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:370 +#: app/views/handlers/individual_response.py:390 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:373 +#: app/views/handlers/individual_response.py:393 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:419 +#: app/views/handlers/individual_response.py:439 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:450 +#: app/views/handlers/individual_response.py:470 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:465 +#: app/views/handlers/individual_response.py:486 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:471 +#: app/views/handlers/individual_response.py:492 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:475 +#: app/views/handlers/individual_response.py:496 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:481 +#: app/views/handlers/individual_response.py:502 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:523 +#: app/views/handlers/individual_response.py:545 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:588 +#: app/views/handlers/individual_response.py:611 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:596 +#: app/views/handlers/individual_response.py:620 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:603 +#: app/views/handlers/individual_response.py:627 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:616 +#: app/views/handlers/individual_response.py:640 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:622 +#: app/views/handlers/individual_response.py:646 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:655 +#: app/views/handlers/individual_response.py:679 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:710 -#: app/views/handlers/individual_response.py:748 +#: app/views/handlers/individual_response.py:734 +#: app/views/handlers/individual_response.py:772 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:714 +#: app/views/handlers/individual_response.py:738 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:772 +#: app/views/handlers/individual_response.py:796 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:784 +#: app/views/handlers/individual_response.py:809 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:785 +#: app/views/handlers/individual_response.py:810 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:818 +#: app/views/handlers/individual_response.py:843 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:867 +#: app/views/handlers/individual_response.py:892 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:876 +#: app/views/handlers/individual_response.py:901 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:914 +#: app/views/handlers/individual_response.py:939 msgid "Confirm mobile number" msgstr "" @@ -1578,12 +1570,12 @@ msgstr "" #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/list-summary.html:7 -#: templates/partials/summary/summary.html:21 +#: templates/partials/summary/summary.html:25 msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:28 -#: templates/partials/summary/summary.html:22 +#: templates/partials/summary/summary.html:26 msgid "Change your answer for:" msgstr "" @@ -1591,3 +1583,16 @@ msgstr "" msgid "Change details for {item_name}" msgstr "" +#: templates/partials/summary/list-summary.html:9 +#: templates/partials/summary/summary.html:23 +msgid "Remove" +msgstr "" + +#: templates/partials/summary/list-summary.html:10 +msgid "Remove {item_name}" +msgstr "" + +#: templates/partials/summary/summary.html:24 +msgid "Remove your answer for:" +msgstr "" + diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index ad99d9ab40..7925880edd 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -41,10 +41,12 @@ def __init__( self.current_location = current_location def __call__( - self, return_to: Optional[str] = "section-summary" + self, + return_to: Optional[str] = "section-summary", + view_submitted_response: bool = False, ) -> Mapping[str, Any]: - summary = self._build_summary(return_to) - title_for_location = self._title_for_location() + summary = self.build_summary(return_to, view_submitted_response) + title_for_location = self.title_for_location() title = ( self._placeholder_renderer.render_placeholder( title_for_location, self.current_location.list_item_id @@ -91,7 +93,11 @@ def get_page_title(self, title_for_location: Union[Mapping, str]) -> str: page_title = page_title.format(list_item_position=list_item_position) return page_title - def _build_summary(self, return_to: Optional[str]) -> dict[str, Any]: + def build_summary( + self, + return_to: str | None, + view_submitted_response: bool, + ) -> dict: """ Build a summary context for a particular location. @@ -131,6 +137,7 @@ def _build_summary(self, return_to: Optional[str]) -> dict[str, Any]: progress_store=self._progress_store, return_to=return_to, return_to_block_id=None, + view_submitted_response=view_submitted_response, ).serialize() for group in refactored_groups ], @@ -138,7 +145,7 @@ def _build_summary(self, return_to: Optional[str]) -> dict[str, Any]: return groups - def _title_for_location(self) -> Union[str, dict]: + def title_for_location(self) -> Union[str, dict]: section_id = self.current_location.section_id return ( # Type ignore: section id should exist at this point diff --git a/app/views/contexts/submit_questionnaire_context.py b/app/views/contexts/submit_questionnaire_context.py index 4d7a6ac1ad..736bc8ccb6 100644 --- a/app/views/contexts/submit_questionnaire_context.py +++ b/app/views/contexts/submit_questionnaire_context.py @@ -39,6 +39,7 @@ def __call__(self) -> dict[str, Union[str, dict]]: progress_store=self._progress_store, metadata=self._metadata, response_metadata=self._response_metadata, + view_submitted_response=False, ) context["summary"] = summary_context( answers_are_editable=True, return_to="final-summary" diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 8af2ef5ec2..d1a73c276c 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -28,8 +28,10 @@ def __init__( progress_store: ProgressStore, return_to: str | None, return_to_block_id: str | None = None, + view_submitted_response: bool | None = False, ) -> None: self.id = group_schema["id"] + self.title = group_schema.get("title") self.location = location self.placeholder_text = None @@ -48,6 +50,7 @@ def __init__( progress_store=progress_store, language=language, return_to_block_id=return_to_block_id, + view_submitted_response=view_submitted_response, ) self.placeholder_renderer = PlaceholderRenderer( @@ -77,6 +80,7 @@ def _build_blocks_and_links( progress_store: ProgressStore, language: str, return_to_block_id: Optional[str], + view_submitted_response: bool | None = False, ) -> list[dict[str, Block]]: blocks = [] @@ -131,12 +135,14 @@ def _build_blocks_and_links( summary_item ) blocks.extend([list_summary_element]) - self.links["add_link"] = Link( - target="_self", - text=list_summary_element["add_link_text"], - url=list_summary_element["add_link"], - attributes={"data-qa": "add-item-link"}, - ) + + if not view_submitted_response: + self.links["add_link"] = Link( + target="_self", + text=list_summary_element["add_link_text"], + url=list_summary_element["add_link"], + attributes={"data-qa": "add-item-link"}, + ) self.placeholder_text = list_summary_element["empty_list_text"] diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index ed7db2bcb5..829cc98578 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,14 +1,37 @@ -from typing import Any, Generator, Mapping, Optional, Union - -from werkzeug.datastructures import ImmutableDict +from typing import Generator, MutableMapping, Optional, Union from app.questionnaire.location import Location +from ...data_models import AnswerStore, ListStore, ProgressStore +from ...data_models.metadata_proxy import MetadataProxy +from ...questionnaire import QuestionnaireSchema from .context import Context from .section_summary_context import SectionSummaryContext class SummaryContext(Context): + def __init__( + self, + language: str, + schema: QuestionnaireSchema, + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + view_submitted_response: bool, + ) -> None: + super().__init__( + language, + schema, + answer_store, + list_store, + progress_store, + metadata, + response_metadata, + ) + self.view_submitted_response = view_submitted_response + def __call__( self, answers_are_editable: bool = False, return_to: Optional[str] = None ) -> dict[str, Union[str, list, bool]]: @@ -16,36 +39,82 @@ def __call__( summary_options = self._schema.get_summary_options() collapsible = summary_options.get("collapsible", False) + refactored_groups = set_unique_group_ids_and_titles(groups) + return { - "groups": groups, + "groups": refactored_groups, "answers_are_editable": answers_are_editable, "collapsible": collapsible, "summary_type": "Summary", + "view_submitted_response": self.view_submitted_response, } def _build_all_groups( self, return_to: Optional[str] - ) -> Generator[ImmutableDict[str, Any], None, None]: - """NB: Does not support repeating sections""" - + ) -> Generator[dict, None, None]: for section_id in self._router.enabled_section_ids: - location = Location(section_id=section_id) - section_summary_context = SectionSummaryContext( - language=self._language, - schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - current_location=location, - routing_path=self._router.routing_path(section_id), - ) - section: Mapping = self._schema.get_section(section_id) or {} - if section.get("summary", {}).get("items"): - break - - for group in section_summary_context(return_to=return_to)["summary"][ - "groups" - ]: - yield group + if repeat := self._schema.get_repeat_for_section(section_id): + for_repeat = self._list_store[repeat["for_list"]] + + if for_repeat.count > 0: + for item in for_repeat.items: + yield from self.build_summary_item( + section_id=section_id, + return_to=return_to, + list_item_id=item, + list_name=for_repeat.name, + ) + else: + yield from self.build_summary_item( + section_id=section_id, return_to=return_to + ) + + def build_summary_item( + self, + section_id: str, + return_to: str | None, + list_name: str | None = None, + list_item_id: str | None = None, + ) -> list[dict]: + location = Location( + section_id=section_id, list_name=list_name, list_item_id=list_item_id + ) + section_summary_context = SectionSummaryContext( + language=self._language, + schema=self._schema, + answer_store=self._answer_store, + list_store=self._list_store, + progress_store=self._progress_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + current_location=location, + routing_path=self._router.routing_path( + section_id, list_item_id=list_item_id + ), + ) + + summary = section_summary_context( + view_submitted_response=self.view_submitted_response, return_to=return_to + )["summary"] + groups: list = summary.get("groups", []) + + for group in groups: + group["section_title"] = summary["title"] + break + + return groups + + +def set_unique_group_ids_and_titles(groups: list[dict]) -> list[dict]: + checked_ids = set() + id_value = 0 + + for group in groups: + group_id = group["id"] + if group_id in checked_ids: + id_value += 1 + group["id"] = f"{group_id}-{id_value}" + + checked_ids.add(group_id) + + return groups diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index 512503353e..740983f18e 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -63,6 +63,7 @@ def build_view_submitted_response_context( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + view_submitted_response=True, ) context["summary"] = summary_context() context["pdf_url"] = url_for("post_submission.get_view_submitted_response_pdf") diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index 1a1046aeeb..2791afd7dc 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -71,6 +71,9 @@ } } }, + "post_submission": { + "view_response": true + }, "sections": [ { "id": "section-companies", diff --git a/schemas/test/en/test_view_submitted_response_repeating_sections.json b/schemas/test/en/test_view_submitted_response_repeating_sections.json new file mode 100644 index 0000000000..b0a015ccd5 --- /dev/null +++ b/schemas/test/en/test_view_submitted_response_repeating_sections.json @@ -0,0 +1,664 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test View Submitted Response Repeating Sections", + "theme": "default", + "description": "A questionnaire to test the summary pages with repeating section on the view submitted response page", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "post_submission": { + "view_response": true + }, + "sections": [ + { + "title": "Personal Details Section", + "id": "name-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "name", + "question": { + "answers": [ + { + "id": "name-answer", + "label": "Full name", + "max_length": 20, + "mandatory": false, + "type": "TextField" + } + ], + "id": "name-question", + "title": "What is your name?", + "type": "General" + } + } + ], + "id": "personal-details-group", + "title": "Personal Details" + }, + { + "blocks": [ + { + "type": "Question", + "id": "address", + "question": { + "answers": [ + { + "id": "address-answer", + "label": "Postcode", + "max_length": 20, + "mandatory": false, + "type": "TextField" + } + ], + "id": "address-question", + "title": "What is your address?", + "type": "General" + } + } + ], + "id": "address-details-group", + "title": "Address Details" + } + ] + }, + { + "id": "section", + "title": "Household Section", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": "Do you live here?", + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "questions-section", + "title": "Questions Section", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "radio", + "title": "Questions Group", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-first-block-question", + "title": "Skip First Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-first-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-and-a-half-number-block", + "question": { + "id": "first-and-a-half-number-question-also-in-total", + "title": "First Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "first-and-a-half-number-answer-also-in-total", + "label": "First answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question-also-in-total", + "title": "Second Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-1", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "first-and-a-half-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Calculated Summary Section", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "title": "Calculated Summary Group", + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "third-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Which answer did you give to question 4 and a half?", + "mandatory": false, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "answer_value_1", + "value": { + "identifier": "first-and-a-half-number-answer-also-in-total", + "source": "answers" + } + } + ], + "text": "{answer_value_1} - first and a half answer" + }, + "value": "{answer_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_1", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_1} - calculated summary answer (previous section)" + }, + "value": "{calc_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_2", + "value": { + "identifier": "currency-total-playback-2", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_2} - calculated summary answer (current section)" + }, + "value": "{calc_value_2}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "third_answer_value", + "value": { + "identifier": "third-number-answer", + "source": "answers" + } + } + ], + "text": "{third_answer_value} - third answer" + }, + "value": "{third_answer_value}" + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + ">=": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback-2" + }, + 50 + ] + } + }, + "type": "Question", + "id": "skippable-block", + "question": { + "answers": [ + { + "id": "skippable-answer", + "mandatory": true, + "type": "Currency", + "label": "Capital expenditure", + "decimal_places": 0, + "currency": "GBP" + } + ], + "id": "skippable-question", + "title": { + "text": "How much did {person_name} spend on fruit?", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + }, + "type": "General" + } + } + ], + "id": "calculated-summary" + } + ] + } + ] +} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index af9f0177a5..cdd3a9df92 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -3,23 +3,28 @@ {%- else -%} {% from "components/summary/_macro.njk" import onsSummary %} - {% set summaryGroups = [] %} + {% set summary_groups = [] %} + {% set summary_types = content.summary.summary_type %} + {% set view_submitted_response = content.summary.view_submitted_response %} {%- for group in content.summary.groups if group.blocks -%} - {% do summaryGroups.append + {% do summary_groups.append ( { + "summaryTitle": group.section_title, "groups": [ { "groupTitle": group.title if group.title else None, "id": group.id if group.id else None, "rows": map_summary_item_config( group=group, - summary_type=content.summary.summary_type, + summary_type=summary_types, answers_are_editable=content.summary.answers_are_editable, no_answer_provided=_("No answer provided"), - edit_link_text=_("Change"), - edit_link_aria_label=_("Change your answer for:"), + remove_link_text=_("Remove") if not view_submitted_response else "", + remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", + edit_link_text=_("Change") if not view_submitted_response else "", + edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", calculated_question=content.summary.calculated_question, icon=None ), @@ -35,7 +40,7 @@ {{ onsSummary({ - "summaries": summaryGroups + "summaries": summary_groups }) }} {%- endif -%} diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index f114076848..d76c7c55fa 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -800,11 +800,7 @@ def test_summary_item_config_with_list_collector(): { "ariaLabel": "Remove Company A", "attributes": {"data-qa": "list-item-remove-1-link"}, - "text": { - "_args": ["Remove"], - "_func": {}, - "_kwargs": {}, - }, + "text": "Remove", "url": "remove_link_url", }, ], @@ -933,6 +929,8 @@ def test_summary_item_config_with_list_collector(): summary_type="SectionSummary", answers_are_editable=True, no_answer_provided="No answer Provided", + remove_link_aria_label="Remove Company A", + remove_link_text="Remove", edit_link_text="Change", edit_link_aria_label="Change your answer for:", calculated_question={}, diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py new file mode 100644 index 0000000000..1fc223e8b7 --- /dev/null +++ b/tests/app/views/contexts/test_summary_context.py @@ -0,0 +1,469 @@ +import pytest +from markupsafe import Markup + +from app.data_models import AnswerStore, ListStore, ProgressStore +from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.utilities.schema import load_schema_from_name +from app.views.contexts.summary_context import SummaryContext + + +@pytest.mark.usefixtures("app") +def test_context_for_summary(): + schema = load_schema_from_name("test_view_submitted_response_repeating_sections") + + list_store = ListStore( + [{"items": ["jufPpX", "fjWZET"], "name": "people", "primary_person": "jufPpX"}] + ) + + answer_store = AnswerStore( + [ + {"answer_id": "name-answer", "value": "John"}, + {"answer_id": "address-answer", "value": "1 Street"}, + {"answer_id": "you-live-here", "value": "Yes"}, + {"answer_id": "first-name", "value": "James", "list_item_id": "jufPpX"}, + {"answer_id": "last-name", "value": "Bond", "list_item_id": "jufPpX"}, + {"answer_id": "first-name", "value": "Jane", "list_item_id": "fjWZET"}, + {"answer_id": "last-name", "value": "Doe", "list_item_id": "fjWZET"}, + {"answer_id": "anyone-else", "value": "No"}, + {"answer_id": "skip-first-block-answer", "value": "Yes"}, + {"answer_id": "second-number-answer-also-in-total", "value": 1}, + {"answer_id": "third-number-answer", "value": 2, "list_item_id": "jufPpX"}, + { + "answer_id": "third-number-answer-also-in-total", + "value": 2, + "list_item_id": "jufPpX", + }, + { + "answer_id": "checkbox-answer", + "value": ["{calc_value_2}"], + "list_item_id": "jufPpX", + }, + {"answer_id": "third-number-answer", "value": 1, "list_item_id": "fjWZET"}, + { + "answer_id": "third-number-answer-also-in-total", + "value": 1, + "list_item_id": "fjWZET", + }, + { + "answer_id": "checkbox-answer", + "value": ["{calc_value_1}"], + "list_item_id": "fjWZET", + }, + ] + ) + + progress_store = ProgressStore( + [ + { + "section_id": "name-section", + "block_ids": ["name", "address"], + "status": "COMPLETED", + }, + { + "section_id": "section", + "block_ids": ["primary-person-list-collector", "list-collector"], + "status": "COMPLETED", + }, + { + "section_id": "questions-section", + "block_ids": [ + "skip-first-block", + "second-number-block", + "currency-total-playback-1", + ], + "status": "COMPLETED", + }, + { + "section_id": "calculated-summary-section", + "block_ids": [ + "third-number-block", + "currency-total-playback-2", + "mutually-exclusive-checkbox", + ], + "status": "COMPLETED", + "list_item_id": "jufPpX", + }, + { + "section_id": "calculated-summary-section", + "block_ids": [ + "third-number-block", + "currency-total-playback-2", + "mutually-exclusive-checkbox", + ], + "status": "COMPLETED", + "list_item_id": "fjWZET", + }, + ] + ) + + summary_context = SummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=None, + response_metadata={}, + view_submitted_response=False, + ) + context = summary_context() + expected = { + "answers_are_editable": False, + "collapsible": False, + "groups": [ + { + "blocks": [ + { + "id": "name", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "name-answer", + "label": "Full name", + "link": "/questionnaire/name/#name-answer", + "type": "textfield", + "unit": None, + "unit_length": None, + "value": Markup("John"), + } + ], + "id": "name-question", + "number": None, + "title": "What is your name?", + "type": "General", + }, + "title": None, + } + ], + "id": "personal-details-group-0", + "links": {}, + "placeholder_text": None, + "section_title": "Personal Details Section", + "title": "Personal Details", + }, + { + "blocks": [ + { + "id": "address", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "address-answer", + "label": "Postcode", + "link": "/questionnaire/address/#address-answer", + "type": "textfield", + "unit": None, + "unit_length": None, + "value": Markup("1 Street"), + } + ], + "id": "address-question", + "number": None, + "title": "What is your address?", + "type": "General", + }, + "title": None, + } + ], + "id": "address-details-group-0", + "links": {}, + "placeholder_text": None, + "title": "Address Details", + }, + { + "blocks": [], + "id": "group-0", + "links": {}, + "placeholder_text": None, + "title": None, + "section_title": "Household Section", + }, + { + "blocks": [], + "id": "group-1", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [], + "id": "group-2", + "links": {}, + "placeholder_text": None, + "title": "List", + }, + { + "blocks": [ + { + "id": "skip-first-block", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "skip-first-block-answer", + "label": None, + "link": "/questionnaire/skip-first-block/#skip-first-block-answer", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + } + ], + "id": "skip-first-block-question", + "number": None, + "title": "Skip First Block so it doesn’t " + "appear in Total?", + "type": "General", + }, + "title": None, + }, + { + "id": "second-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "second-number-answer-also-in-total", + "label": "Second answer " + "label also in " + "total (optional)", + "link": "/questionnaire/second-number-block/#second-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + } + ], + "id": "second-number-question-also-in-total", + "number": None, + "title": "Second Number Additional " "Question Title", + "type": "General", + }, + "title": None, + }, + ], + "id": "radio-0", + "links": {}, + "placeholder_text": None, + "title": "Questions Group", + "section_title": "Questions Section", + }, + { + "blocks": [ + { + "id": "third-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "third-number-answer", + "label": "Third answer in " "currency label", + "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 2, + }, + { + "currency": "GBP", + "id": "third-number-answer-also-in-total", + "label": "Third answer " + "label also in " + "currency total " + "(optional)", + "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 2, + }, + ], + "id": "third-number-question", + "number": None, + "title": "Third Number Question Title", + "type": "General", + }, + "title": None, + }, + { + "id": "mutually-exclusive-checkbox", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "checkbox-answer", + "label": None, + "link": "/questionnaire/people/jufPpX/mutually-exclusive-checkbox/#checkbox-answer", + "type": "checkbox", + "unit": None, + "unit_length": None, + "value": [ + { + "detail_answer_value": None, + "label": "4 - " + "calculated " + "summary " + "answer " + "(current " + "section)", + } + ], + } + ], + "id": "mutually-exclusive-checkbox-question", + "number": None, + "title": "Which answer did you give to " + "question 4 and a half?", + "type": "MutuallyExclusive", + }, + "title": None, + }, + { + "id": "skippable-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "skippable-answer", + "label": "Capital " "expenditure", + "link": "/questionnaire/people/jufPpX/skippable-block/#skippable-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": None, + } + ], + "id": "skippable-question", + "number": None, + "title": "How much did James Bond spend " "on fruit?", + "type": "General", + }, + "title": None, + }, + ], + "id": "calculated-summary-0", + "links": {}, + "placeholder_text": None, + "title": "Calculated Summary Group", + "section_title": "James Bond", + }, + { + "blocks": [ + { + "id": "third-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "third-number-answer", + "label": "Third answer in " "currency label", + "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + }, + { + "currency": "GBP", + "id": "third-number-answer-also-in-total", + "label": "Third answer " + "label also in " + "currency total " + "(optional)", + "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + }, + ], + "id": "third-number-question", + "number": None, + "title": "Third Number Question Title", + "type": "General", + }, + "title": None, + }, + { + "id": "mutually-exclusive-checkbox", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "checkbox-answer", + "label": None, + "link": "/questionnaire/people/fjWZET/mutually-exclusive-checkbox/#checkbox-answer", + "type": "checkbox", + "unit": None, + "unit_length": None, + "value": [ + { + "detail_answer_value": None, + "label": "1 - " + "calculated " + "summary " + "answer " + "(previous " + "section)", + } + ], + } + ], + "id": "mutually-exclusive-checkbox-question", + "number": None, + "title": "Which answer did you give to " + "question 4 and a half?", + "type": "MutuallyExclusive", + }, + "title": None, + }, + { + "id": "skippable-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "skippable-answer", + "label": "Capital " "expenditure", + "link": "/questionnaire/people/fjWZET/skippable-block/#skippable-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": None, + } + ], + "id": "skippable-question", + "number": None, + "title": "How much did Jane Doe spend " "on fruit?", + "type": "General", + }, + "title": None, + }, + ], + "id": "calculated-summary-0-1", + "links": {}, + "placeholder_text": None, + "title": "Calculated Summary Group", + "section_title": "Jane Doe", + }, + ], + "summary_type": "Summary", + "view_submitted_response": False, + } + + assert context == expected diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 3dc12c3792..6ae9cc47f5 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -3,6 +3,23 @@ import NameBlockPage from "../../../generated_pages/view_submitted_response/name import SubmitPage from "../../../generated_pages/view_submitted_response/submit.page.js"; import ThankYouPage from "../../../base_pages/thank-you.page"; import ViewSubmittedResponsePage from "../../../generated_pages/view_submitted_response/view-submitted-response.page.js"; +import ViewSubmittedResponseRepeatingPage from "../../../generated_pages/view_submitted_response_repeating_sections/view-submitted-response.page.js"; +import HubPage from "../../../base_pages/hub.page"; +import PrimaryPersonListCollectorPage from "../../../generated_pages/view_submitted_response_repeating_sections/primary-person-list-collector.page"; +import PrimaryPersonListCollectorAddPage from "../../../generated_pages/view_submitted_response_repeating_sections/primary-person-list-collector-add.page"; +import ListCollectorPage from "../../../generated_pages/view_submitted_response_repeating_sections/list-collector.page"; +import SkipFirstNumberBlockPageSectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/skip-first-block.page"; +import FirstNumberBlockPageSectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/first-number-block.page"; +import FirstAndAHalfNumberBlockPageSectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/first-and-a-half-number-block.page"; +import SecondNumberBlockPageSectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/second-number-block.page"; +import CalculatedSummarySectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/currency-total-playback-1.page"; +import SectionSummarySectionOne from "../../../generated_pages/view_submitted_response_repeating_sections/questions-section-summary.page"; +import ThirdNumberBlockPageSectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/third-number-block.page"; +import CalculatedSummarySectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/currency-total-playback-2.page"; +import DependencyQuestionSectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/mutually-exclusive-checkbox.page"; +import SkippableBlockSectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/skippable-block.page"; +import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; +import ListCollectorAddPage from "../../../generated_pages/view_submitted_response_repeating_sections//list-collector-add.page"; describe("View Submitted Response", () => { beforeEach("Load the questionnaire", async () => { @@ -43,3 +60,94 @@ describe("View Submitted Response", () => { }); }); }); + +const firstGroup = 'div[id="calculated-summary-0"]'; +const secondGroup = 'div[id="calculated-summary-0-1"]'; +const groupTitle = 'h3[class="ons-summary__group-title"]'; +const repeatingSectionAnswer = '[data-qa="checkbox-answer"]'; +const skippableRepeatingSectionAnswer = '[data-qa="skippable-answer"]'; + +describe("View Submitted Response Summary Page With Repeating Sections", () => { + beforeEach("Load the questionnaire", async () => { + await browser.openQuestionnaire("test_view_submitted_response_repeating_sections.json"); + await $(HubPage.submit()).click(); + + await $(NameBlockPage.answer()).setValue("John Smith"); + await $(NameBlockPage.submit()).click(); + await $(AddressBlockPage.answer()).setValue("NP10 8XG"); + await $(AddressBlockPage.submit()).click(); + + await $(HubPage.submit()).click(); + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HubPage.submit()).click(); + + await $(SkipFirstNumberBlockPageSectionOne.no()).click(); + await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); + await $(FirstNumberBlockPageSectionOne.submit()).click(); + await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); + await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); + await $(SecondNumberBlockPageSectionOne.submit()).click(); + await $(CalculatedSummarySectionOne.submit()).click(); + await $(SectionSummarySectionOne.submit()).click(); + await $(HubPage.submit()).click(); + await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); + await $(ThirdNumberBlockPageSectionTwo.submit()).click(); + await $(CalculatedSummarySectionTwo.submit()).click(); + await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2()).click(); + await $(DependencyQuestionSectionTwo.submit()).click(); + await $(SkippableBlockSectionTwo.skippable()).setValue(100); + await $(SkippableBlockSectionTwo.submit()).click(); + await $(SectionSummarySectionTwo.submit()).click(); + await $(HubPage.submit()).click(); + await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(40); + await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(40); + await $(ThirdNumberBlockPageSectionTwo.submit()).click(); + await $(CalculatedSummarySectionTwo.submit()).click(); + await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2()).click(); + await $(DependencyQuestionSectionTwo.submit()).click(); + await $(SectionSummarySectionTwo.submit()).click(); + + await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await $(ThankYouPage.savePrintAnswersLink()).click(); + await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); + }); + + it("Given I have completed a questionnaire with a repeating section and view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { + await expect(await $(ViewSubmittedResponseRepeatingPage.informationPanel()).isDisplayed()).to.be.false; + await expect(await $(ViewSubmittedResponseRepeatingPage.printButton()).isDisplayed()).to.be.true; + await expect(await $(ViewSubmittedResponseRepeatingPage.heading()).getText()).to.equal("Answers submitted for Apple"); + await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(1)).getText()).to.equal("Submitted on:"); + await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(2)).getText()).to.equal("Submission reference:"); + await expect(await $(ViewSubmittedResponseRepeatingPage.personalDetailsGroupTitle()).getText()).to.equal("Personal Details"); + await expect(await $(ViewSubmittedResponseRepeatingPage.nameQuestion()).getText()).to.equal("What is your name?"); + await expect(await $(ViewSubmittedResponseRepeatingPage.nameAnswer()).getText()).to.equal("John Smith"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressDetailsGroupTitle()).getText()).to.equal("Address Details"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressQuestion()).getText()).to.equal("What is your address?"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressAnswer()).getText()).to.equal("NP10 8XG"); + await expect(await $("body").getHTML()).to.contain("Marcus Twin"); + await expect(await $(firstGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); + await expect(await $(firstGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("40 - calculated summary answer (current section)"); + await expect(await $("body").getHTML()).to.contain("How much did Marcus Twin spend on fruit?"); + await expect(await $(firstGroup).$$(skippableRepeatingSectionAnswer)[0].getText()).to.equal("£100.00"); + await expect(await $("body").getHTML()).to.contain("John Doe"); + await expect(await $(secondGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); + await expect(await $(secondGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("80 - calculated summary answer (current section)"); + await expect(await $("body").getHTML()).to.not.contain("How much did John Doe spend on fruit?"); + }); +}); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index e76dc5e48b..0c9ff489ff 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -3,9 +3,15 @@ import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_sectio import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-add.page.js"; import AnyCompaniesOrBranchesRemovePage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-remove.page.js"; import SectionSummaryPage from "../generated_pages/list_collector_section_summary/section-companies-summary.page"; +import SectionSummaryTwoPage from "../generated_pages/list_collector_section_summary/section-household-summary.page"; import UkBasedPage from "../generated_pages/list_collector_section_summary/confirmation-checkbox.page"; +import ListCollectorPage from "../generated_pages/list_collector_section_summary/list-collector.page"; +import HouseholderCheckboxPage from "../generated_pages/list_collector_section_summary/householder-checkbox.page"; +import SubmitPage from "../generated_pages/list_collector_section_summary/submit.page"; +import ThankYouPage from "../base_pages/thank-you.page"; +import ViewSubmittedResponsePage from "../generated_pages/list_collector_section_summary/view-submitted-response.page"; -describe("List Collector Section Summary Items", () => { +describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { beforeEach(async () => { await browser.openQuestionnaire("test_list_collector_section_summary.json"); @@ -227,7 +233,8 @@ describe("List Collector Section Summary Items", () => { await answerUkBasedQuestion(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); }); - it("When I remove a company from the summary page, and the amount then totals to 3, but the confirmation question has already been answered, Then I am not prompted with the confirmation question", async () => { + + it("When I get to the summary page, Then the summary should be displayed as expected with change links", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesYes(); @@ -238,14 +245,59 @@ describe("List Collector Section Summary Items", () => { await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); await answerUkBasedQuestion(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await $(SectionSummaryPage.companiesListAddLink()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await $(SectionSummaryPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HouseholderCheckboxPage.no()).click(); + await $(HouseholderCheckboxPage.submit()).click(); + await $(SectionSummaryTwoPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Change"); + await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).to.contain("Change"); + await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("234"); + await expect(await $(companiesListRowItem(3, 3)).getText()).to.contain("Change"); + await expect(await $(SubmitPage.householderCheckboxAnswer()).getText()).to.contain("No"); + await expect(await $("body").getHTML()).to.contain("Add another UK company or branch"); + await expect(await $("body").getHTML()).to.contain("Remove"); + }); + + it("When I get to the view submitted response page, Then the summary should be displayed as expected without any change or remove links", async () => { + await drivingQuestionYes(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesYes(); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); + await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await answerUkBasedQuestion(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await removeFirstCompany(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await $(SectionSummaryPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(HouseholderCheckboxPage.no()).click(); + await $(HouseholderCheckboxPage.submit()).click(); + await $(SectionSummaryTwoPage.submit()).click(); + await $(SubmitPage.submit()).click(); + await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await $(ThankYouPage.savePrintAnswersLink()).click(); + + await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); + await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); + await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); + await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("234"); + await expect(await $("body").getHTML()).to.not.contain("Change"); + await expect(await $("body").getHTML()).to.not.contain("Remove"); + await expect(await $("body").getHTML()).to.not.contain("Add another UK company or branch"); }); }); }); From 5d152dd2c964e6d2306b3217cb462a30e716c840 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 3 May 2023 14:59:12 +0100 Subject: [PATCH 219/567] Schemas v3.53.0 (#1098) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 4f203efe99..dddd714f95 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.52.0 +v3.53.0 From aa137124ebc901537caf96f80fadbfda5876fdef Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 5 May 2023 11:23:22 +0100 Subject: [PATCH 220/567] Schemas v3.54.0 (#1101) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index dddd714f95..33659e8fda 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.53.0 +v3.54.0 From 114d9c483cf348abb1f13647b439117a1978b091 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 10 May 2023 10:33:11 +0100 Subject: [PATCH 221/567] Update question definition (#1099) --- schemas/test/en/test_benchmark_business.json | 6 +++- schemas/test/en/test_question_definition.json | 6 +++- ... test_question_definition_array_type.json} | 0 templates/partials/question.html | 30 +++++++++++-------- .../question_definitions_array_type.spec.js | 2 +- ...est_questionnaire_array_type_definition.py | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) rename schemas/test/en/{test_array_type_definitions.json => test_question_definition_array_type.json} (100%) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 7b78c7e793..63ab6312e7 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -277,7 +277,11 @@ }, "definition": { "title": "What is work in progress?", - "content": "This refers to goods and services that have been partially completed (e.g. a solicitor working on a legal case over a period of time and being paid at the end of the contract for the services provided i.e. unbilled work)." + "contents": [ + { + "description": "This refers to goods and services that have been partially completed (e.g. a solicitor working on a legal case over a period of time and being paid at the end of the contract for the services provided i.e. unbilled work)." + } + ] }, "type": "General", "answers": [ diff --git a/schemas/test/en/test_question_definition.json b/schemas/test/en/test_question_definition.json index 708c9a82e1..b7d118cae1 100644 --- a/schemas/test/en/test_question_definition.json +++ b/schemas/test/en/test_question_definition.json @@ -45,7 +45,11 @@ "type": "General", "definition": { "title": "What is a photovoltaic system?", - "content": "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky." + "contents": [ + { + "description": "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power. PV installations may be ground-mounted, rooftop mounted or wall mounted. The mount may be fixed, or use a solar tracker to follow the sun across the sky." + } + ] }, "answers": [ { diff --git a/schemas/test/en/test_array_type_definitions.json b/schemas/test/en/test_question_definition_array_type.json similarity index 100% rename from schemas/test/en/test_array_type_definitions.json rename to schemas/test/en/test_question_definition_array_type.json diff --git a/templates/partials/question.html b/templates/partials/question.html index af8eb0ce79..9b5aab2d3e 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -15,20 +15,24 @@ {%- if question.definition -%} {% set definition_id = "question-definition" %} + {% set definition_content %} + {% set contents = question.definition.contents %} + {% include 'partials/contents.html' %} + {% endset %} {% set question_definition = { - "title": question.definition.title, - "id": definition_id, - "content": question.definition.content, - "headingAttributes": { - "data-ga": "click", - "data-ga-category": "Question definition", - "data-ga-action": "Open panel", - "data-ga-label": question.definition.title, - "data-qa": definition_id ~ "-title" - }, - "contentAttributes": { - "data-qa": definition_id ~ "-content" - } + "title": question.definition.title, + "id": definition_id, + "content": definition_content, + "headingAttributes": { + "data-ga": "click", + "data-ga-category": "Question definition", + "data-ga-action": "Open panel", + "data-ga-label": question.definition.title, + "data-qa": definition_id ~ "-title" + }, + "contentAttributes": { + "data-qa": definition_id ~ "-content" + } } %} {% elif question.definitions %} {%- set question_definitions -%} diff --git a/tests/functional/spec/question_definitions_array_type.spec.js b/tests/functional/spec/question_definitions_array_type.spec.js index b7528f4e21..51e32785f2 100644 --- a/tests/functional/spec/question_definitions_array_type.spec.js +++ b/tests/functional/spec/question_definitions_array_type.spec.js @@ -3,7 +3,7 @@ import DefinitionPage from "../generated_pages/question_definition/definition-bl describe("Component: Definition", () => { describe("Given I start a survey which contains question definition", () => { beforeEach(async () => { - await browser.openQuestionnaire("test_array_type_definitions.json"); + await browser.openQuestionnaire("test_question_definition_array_type.json"); }); it("When I click the title link, then the description should be visible", async () => { diff --git a/tests/integration/questionnaire/test_questionnaire_array_type_definition.py b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py index eea36010b4..5b0e5b9d13 100644 --- a/tests/integration/questionnaire/test_questionnaire_array_type_definition.py +++ b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py @@ -5,7 +5,7 @@ class TestQuestionnaireQuestionDefinition(IntegrationTestCase): def test_question_definition(self): # Given I launch a questionnaire with definitions - self.launchSurvey("test_array_type_definitions") + self.launchSurvey("test_question_definition_array_type") # When I start the survey I am presented with the definitions title and content correctly self.assertInBody( From 9f0742805eb0dc4dacc7c947805c3de923cb5099 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 10 May 2023 14:12:03 +0100 Subject: [PATCH 222/567] Schemas v3.55.0 (#1103) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 33659e8fda..304e303931 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.54.0 +v3.55.0 From e8155e383be0517b1706efdfebb5a339134889a1 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 15 May 2023 11:58:38 +0100 Subject: [PATCH 223/567] Fix rendering on Summary pages (#1102) --- .design-system-version | 2 +- .../contexts/calculated_summary_context.py | 4 +- app/views/contexts/section_summary_context.py | 18 +- app/views/contexts/summary_context.py | 63 +- .../test/en/test_view_submitted_response.json | 1 + .../partials/summary/collapsible-summary.html | 70 +- templates/partials/summary/summary.html | 65 +- templates/sectionsummary.html | 46 +- tests/app/views/contexts/__init__.py | 25 +- .../test_calculated_summary_context.py | 8 +- .../contexts/test_section_summary_context.py | 822 +++++++++--------- .../test_submitted_response_context.py | 20 +- .../views/contexts/test_summary_context.py | 621 +++++++------ 13 files changed, 880 insertions(+), 885 deletions(-) diff --git a/.design-system-version b/.design-system-version index 1a5e111a58..2f9886f839 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -62.2.0 +62.2.1 diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index c1a15dadd3..50fc424435 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -110,9 +110,11 @@ def build_view_context_for_calculated_summary(self) -> dict[str, dict[str, Any]] collapsible = block.get("collapsible") or False block_title = block["title"] + sections = [{"id": self.current_location.section_id, "groups": groups}] + return { "summary": { - "groups": groups, + "sections": sections, "answers_are_editable": True, "calculated_question": self._get_calculated_question( calculation, formatted_total diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 7925880edd..30bebc684d 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -57,16 +57,28 @@ def __call__( page_title = self.get_page_title(title_for_location) - return { + summary_context = { "summary": { "title": title, "page_title": page_title, "summary_type": "SectionSummary", "answers_are_editable": True, - **summary, - } + "collapsible": summary.get("collapsible"), + }, } + if custom_summary := summary.get("custom_summary"): + summary_context["summary"]["custom_summary"] = custom_summary + elif groups := summary.get("groups"): + summary_context["summary"]["sections"] = [ + { + "title": title, + "groups": groups, + } + ] + + return summary_context + @cached_property def section(self) -> ImmutableDict: # Type ignore: The section has to exist at this point diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index 829cc98578..117f6a1f06 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,4 +1,4 @@ -from typing import Generator, MutableMapping, Optional, Union +from typing import MutableMapping from app.questionnaire.location import Location @@ -31,43 +31,39 @@ def __init__( response_metadata, ) self.view_submitted_response = view_submitted_response + self.summaries: list[dict] = [] def __call__( - self, answers_are_editable: bool = False, return_to: Optional[str] = None - ) -> dict[str, Union[str, list, bool]]: - groups = list(self._build_all_groups(return_to)) + self, answers_are_editable: bool = False, return_to: str | None = None + ) -> dict[str, dict | str | list | bool]: + self._build_all_groups(return_to) summary_options = self._schema.get_summary_options() collapsible = summary_options.get("collapsible", False) - - refactored_groups = set_unique_group_ids_and_titles(groups) + self.set_unique_group_ids() return { - "groups": refactored_groups, + "sections": self.summaries, "answers_are_editable": answers_are_editable, "collapsible": collapsible, "summary_type": "Summary", "view_submitted_response": self.view_submitted_response, } - def _build_all_groups( - self, return_to: Optional[str] - ) -> Generator[dict, None, None]: + def _build_all_groups(self, return_to: str | None) -> None: for section_id in self._router.enabled_section_ids: if repeat := self._schema.get_repeat_for_section(section_id): for_repeat = self._list_store[repeat["for_list"]] if for_repeat.count > 0: for item in for_repeat.items: - yield from self.build_summary_item( + self.build_summary_item( section_id=section_id, return_to=return_to, list_item_id=item, list_name=for_repeat.name, ) else: - yield from self.build_summary_item( - section_id=section_id, return_to=return_to - ) + self.build_summary_item(section_id=section_id, return_to=return_to) def build_summary_item( self, @@ -75,7 +71,7 @@ def build_summary_item( return_to: str | None, list_name: str | None = None, list_item_id: str | None = None, - ) -> list[dict]: + ) -> None: location = Location( section_id=section_id, list_name=list_name, list_item_id=list_item_id ) @@ -96,25 +92,20 @@ def build_summary_item( summary = section_summary_context( view_submitted_response=self.view_submitted_response, return_to=return_to )["summary"] - groups: list = summary.get("groups", []) - - for group in groups: - group["section_title"] = summary["title"] - break - - return groups - - -def set_unique_group_ids_and_titles(groups: list[dict]) -> list[dict]: - checked_ids = set() - id_value = 0 - - for group in groups: - group_id = group["id"] - if group_id in checked_ids: - id_value += 1 - group["id"] = f"{group_id}-{id_value}" - - checked_ids.add(group_id) - return groups + for section in summary.get("sections", []): + if any(group["blocks"] for group in section["groups"]): + self.summaries.extend(summary["sections"]) + + def set_unique_group_ids(self) -> None: + checked_ids = set() + id_value = 0 + + for section in self.summaries: + if groups := section.get("groups"): + for group in groups: + group_id = group["id"] + if group_id in checked_ids: + id_value += 1 + group["id"] = f"{group_id}-{id_value}" + checked_ids.add(group_id) diff --git a/schemas/test/en/test_view_submitted_response.json b/schemas/test/en/test_view_submitted_response.json index adc19efb98..66a5d4c298 100644 --- a/schemas/test/en/test_view_submitted_response.json +++ b/schemas/test/en/test_view_submitted_response.json @@ -35,6 +35,7 @@ "sections": [ { "id": "name-section", + "title": "Name Section", "groups": [ { "blocks": [ diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index 723de179ca..efff1abdf9 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -3,44 +3,46 @@ {%- set itemList = [] -%} -{%- for group in content.summary.groups -%} - {%- if group["blocks"] | length -%} - {%- if group.title -%} - {%- set item = { - "title": group.title, - "id": group.id, - "content": "" - } -%} +{%- for section in content.summary.sections -%} + {%- for group in section.groups -%} + {%- if group["blocks"] | length -%} + {%- if group.title -%} + {%- set item = { + "title": group.title, + "id": group.id, + "content": "" + } -%} - {%- set summary %} - {{ onsSummary({ - "classes": "summary--no-bottom-border", - "summaries": [ - { - "groups": [ - { - "rows": map_summary_item_config( - group=group, - summary_type=content.summary.summary_type, - answers_are_editable=content.summary.answers_are_editable, - no_answer_provided=_("No answer provided"), - edit_link_text=_("Change"), - edit_link_aria_label=_("Change your answer for:"), - calculated_question=content.summary.calculated_question, - icon=None - ) - } - ] - } - ] - }) }} - {%- endset -%} + {%- set summary %} + {{ onsSummary({ + "classes": "summary--no-bottom-border", + "summaries": [ + { + "groups": [ + { + "rows": map_summary_item_config( + group=group, + summary_type=content.summary.summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + edit_link_text=_("Change"), + edit_link_aria_label=_("Change your answer for:"), + calculated_question=content.summary.calculated_question, + icon=None + ) + } + ] + } + ] + }) }} + {%- endset -%} - {%- do item | setAttribute("content", item.content + summary) -%} + {%- do item | setAttribute("content", item.content + summary) -%} - {%- do itemList.append(item) -%} + {%- do itemList.append(item) -%} + {%- endif -%} {%- endif -%} - {%- endif -%} + {%- endfor -%} {%- endfor -%} {{ onsAccordion({ diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index cdd3a9df92..03702f8a17 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -3,36 +3,47 @@ {%- else -%} {% from "components/summary/_macro.njk" import onsSummary %} - {% set summary_groups = [] %} - {% set summary_types = content.summary.summary_type %} + {% set summary_sections = [] %} + {% set summary_type = content.summary.summary_type %} {% set view_submitted_response = content.summary.view_submitted_response %} - {%- for group in content.summary.groups if group.blocks -%} - {% do summary_groups.append + + {%- for section in content.summary.sections -%} + {% set summary_groups = [] %} + {%- if section.groups -%} + {%- for group in section.groups -%} + {%- if group.blocks -%} + {% do summary_groups.append + ( + { + "groupTitle": group.title if group.title else None, + "id": group.id if group.id else None, + "rows": map_summary_item_config( + group=group, + summary_type=summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + remove_link_text=_("Remove") if not view_submitted_response else "", + remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", + edit_link_text=_("Change") if not view_submitted_response else "", + edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", + calculated_question=content.summary.calculated_question, + icon=None + ), + "classes": "ons-u-mt-m" if loop.index > 1 else "", + "placeholderText": group.placeholder_text, + "summaryLink": group.links.add_link, + } + ) + %} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + {% do summary_sections.append ( { - "summaryTitle": group.section_title, - "groups": [ - { - "groupTitle": group.title if group.title else None, - "id": group.id if group.id else None, - "rows": map_summary_item_config( - group=group, - summary_type=summary_types, - answers_are_editable=content.summary.answers_are_editable, - no_answer_provided=_("No answer provided"), - remove_link_text=_("Remove") if not view_submitted_response else "", - remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", - edit_link_text=_("Change") if not view_submitted_response else "", - edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", - calculated_question=content.summary.calculated_question, - icon=None - ), - "classes": "ons-u-mt-m" if loop.index > 1 else "", - "placeholderText": group.placeholder_text, - "summaryLink": group.links.add_link, - } - ] + "summaryTitle": section.title if summary_type == "Summary", + "groups": summary_groups } ) %} @@ -40,7 +51,7 @@ {{ onsSummary({ - "summaries": summary_groups + "summaries": summary_sections }) }} {%- endif -%} diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index fee6de6d15..11745adee0 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -17,31 +17,29 @@ }) }} {% block form_content %} + {% if content.summary.custom_summary %} +

      {{content.summary.title}}

      + {% for summary in content.summary.custom_summary %} + {% if summary.type == 'List' %} + {% set add_link = summary.add_link %} + {% set add_link_text = summary.add_link_text %} + {% set empty_list_text = summary.empty_list_text %} + {% set list_title = summary.title %} - {% if content.summary.groups %} - {% set group = content.summary.groups %} -

      {{content.summary.title}}

      -
      - {% include 'partials/summary/summary.html' %} -
      - {% elif content.summary.custom_summary %} -

      {{content.summary.title}}

      - {% for summary in content.summary.custom_summary %} - {% if summary.type == 'List' %} - {% set add_link = summary.add_link %} - {% set add_link_text = summary.add_link_text %} - {% set empty_list_text = summary.empty_list_text %} - {% set list_title = summary.title %} - -
      - {% if summary.list %} - {% set list = summary.list %} - {% include 'partials/summary/list-summary.html' %} - {% endif %} -
      - {% endif %} - {% endfor %} - {% endif %} +
      + {% if summary.list %} + {% set list = summary.list %} + {% include 'partials/summary/list-summary.html' %} + {% endif %} +
      + {% endif %} + {% endfor %} + {%- else -%} +

      {{content.summary.title}}

      +
      + {% include 'partials/summary/summary.html' %} +
      + {% endif %} {% endblock %} {% block submit_button %} diff --git a/tests/app/views/contexts/__init__.py b/tests/app/views/contexts/__init__.py index 9ae3029b5c..e929f99c7a 100644 --- a/tests/app/views/contexts/__init__.py +++ b/tests/app/views/contexts/__init__.py @@ -1,21 +1,22 @@ def assert_summary_context(context): summary_context = context["summary"] - for key_value in ("groups", "answers_are_editable", "summary_type"): + for key_value in ("sections", "answers_are_editable", "summary_type"): assert ( key_value in summary_context ), f"Key value {key_value} missing from context['summary']" - for group in summary_context["groups"]: - assert "id" in group - assert "blocks" in group - for block in group["blocks"]: - assert "question" in block - assert "title" in block["question"] - assert "answers" in block["question"] - for answer in block["question"]["answers"]: - assert "id" in answer - assert "value" in answer - assert "type" in answer + for section in summary_context["sections"]: + for group in section["groups"]: + assert "id" in group + assert "blocks" in group + for block in group["blocks"]: + assert "question" in block + assert "title" in block["question"] + assert "answers" in block["question"] + for answer in block["question"]["answers"]: + assert "id" in answer + assert "value" in answer + assert "type" in answer def assert_preview_context(context): diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 58514c64b1..9811e82762 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -132,16 +132,16 @@ def test_build_view_context_for_currency_calculated_summary( assert context_summary["title"] == title assert "calculated_question" in context_summary - assert len(context_summary["groups"][0]["blocks"]) == total_blocks + assert len(context_summary["sections"][0]["groups"][0]["blocks"]) == total_blocks assert ( context_summary["calculated_question"]["title"] == "Grand total of previous values" ) assert context_summary["calculated_question"]["answers"][0]["value"] == value - answer_change_link = context_summary["groups"][0]["blocks"][0]["question"][ - "answers" - ][0]["link"] + answer_change_link = context_summary["sections"][0]["groups"][0]["blocks"][0][ + "question" + ]["answers"][0]["link"] assert "return_to=calculated-summary" in answer_change_link assert f"return_to_answer_id={return_to_answer_id}" in answer_change_link assert f"return_to_block_id={block_id}" in answer_change_link diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 9d1d358236..863cee7942 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -184,9 +184,7 @@ def test_context_for_section_list_summary(people_answer_store): }, "list_name": "people", "related_answers": None, - "title": "Household members staying overnight " - "on 13 October 2019 at 70 Abingdon " - "Road, Goathill", + "title": "Household members staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", "type": "List", }, { @@ -209,9 +207,7 @@ def test_context_for_section_list_summary(people_answer_store): }, "list_name": "visitors", "related_answers": None, - "title": "Visitors staying overnight on 13 " - "October 2019 at 70 Abingdon Road, " - "Goathill", + "title": "Visitors staying overnight on 13 October 2019 at 70 Abingdon Road, Goathill", "type": "List", }, ], @@ -224,6 +220,7 @@ def test_context_for_section_list_summary(people_answer_store): assert context == expected +# pylint: disable=line-too-long @pytest.mark.usefixtures("app") def test_context_for_section_summary_with_list_summary(companies_answer_store): schema = load_schema_from_name("test_list_collector_section_summary") @@ -238,7 +235,7 @@ def test_context_for_section_summary_with_list_summary(companies_answer_store): ] ), progress_store=ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location(section_id="section-companies"), routing_path=RoutingPath( @@ -253,147 +250,148 @@ def test_context_for_section_summary_with_list_summary(companies_answer_store): "summary": { "answers_are_editable": True, "collapsible": False, - "groups": [ - { - "blocks": [], - "id": "group-companies-0", - "links": {}, - "placeholder_text": None, - "title": None, - }, + "sections": [ { - "blocks": [ + "groups": [ { - "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", - "add_link_text": "Add another UK company or branch", - "empty_list_text": "No UK company or branch added", - "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK company or branch", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", - "item_title": "company a", - "list_item_id": "PlwgoG", - "primary_person": False, - "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", - }, - { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", - "item_title": "company b", - "list_item_id": "UHPLbX", - "primary_person": False, - "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], }, - ], - }, - "list_name": "companies", - "related_answers": { - "PlwgoG": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 123, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - "UHPLbX": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 456, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "No", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) }, - "title": "Companies or UK branches", - "type": "List", - } + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, ], - "id": "group-companies-1", - "links": { - "add_link": Link( - text="Add another UK company or branch", - url="/questionnaire/companies/add-company/?return_to=section-summary", - target="_self", - attributes={"data-qa": "add-item-link"}, - ) - }, - "placeholder_text": "No UK company or branch added", - "title": None, - }, - { - "blocks": [], - "id": "group-companies-2", - "links": {}, - "placeholder_text": None, - "title": None, - }, + "title": "General insurance business", + } ], "page_title": "General insurance business", "summary_type": "SectionSummary", @@ -420,7 +418,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( ] ), progress_store=ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location(section_id="section-companies"), routing_path=RoutingPath( @@ -435,147 +433,148 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( "summary": { "answers_are_editable": True, "collapsible": False, - "groups": [ - { - "blocks": [], - "id": "group-companies-0", - "links": {}, - "placeholder_text": None, - "title": None, - }, + "sections": [ { - "blocks": [ + "groups": [ { - "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", - "add_link_text": "Add another UK company or branch", - "empty_list_text": "No UK company or branch added", - "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK or non-UK company or branch", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", - "item_title": "company a", - "list_item_id": "PlwgoG", - "primary_person": False, - "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", - }, - { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", - "item_title": "company b", - "list_item_id": "UHPLbX", - "primary_person": False, - "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK or non-UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], }, - ], - }, - "list_name": "companies", - "related_answers": { - "PlwgoG": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "UK Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 123, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - "UHPLbX": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "UK Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 456, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "UK Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "No", - }, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "UK Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) }, - "title": "Companies or UK branches", - "type": "List", - } + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, ], - "id": "group-companies-1", - "links": { - "add_link": Link( - text="Add another UK company or branch", - url="/questionnaire/companies/add-company/?return_to=section-summary", - target="_self", - attributes={"data-qa": "add-item-link"}, - ) - }, - "placeholder_text": "No UK company or branch added", - "title": None, - }, - { - "blocks": [], - "id": "group-companies-2", - "links": {}, - "placeholder_text": None, - "title": None, - }, + "title": "General insurance business", + } ], "page_title": "General insurance business", "summary_type": "SectionSummary", @@ -602,7 +601,7 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( ] ), progress_store=ProgressStore(), - metadata={}, + metadata=None, response_metadata={}, current_location=Location(section_id="section-companies"), routing_path=RoutingPath( @@ -617,147 +616,148 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( "summary": { "answers_are_editable": True, "collapsible": False, - "groups": [ + "sections": [ { - "blocks": [], - "id": "group-companies-0", - "links": {}, - "placeholder_text": None, - "title": None, - }, - { - "blocks": [ + "groups": [ { - "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", - "add_link_text": "Add another UK company or branch", - "empty_list_text": "No UK company or branch added", - "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK or non-UK company or branch", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", - "item_title": "company a", - "list_item_id": "PlwgoG", - "primary_person": False, - "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", - }, - { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", - "item_title": "company b", - "list_item_id": "UHPLbX", - "primary_person": False, - "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + "blocks": [], + "id": "group-companies-0", + "links": {}, + "placeholder_text": None, + "title": None, + }, + { + "blocks": [ + { + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "item_anchor": "#company-or-branch-name", + "item_label": "Name of UK or non-UK company or branch", + "list": { + "editable": True, + "list_items": [ + { + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "item_title": "company a", + "list_item_id": "PlwgoG", + "primary_person": False, + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + }, + { + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "item_title": "company b", + "list_item_id": "UHPLbX", + "primary_person": False, + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + }, + ], }, - ], - }, - "list_name": "companies", - "related_answers": { - "PlwgoG": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Non-UK Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 123, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this non-UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, + "list_name": "companies", + "related_answers": { + "PlwgoG": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Non-UK Registration number", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 123, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - "UHPLbX": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Non-UK Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=regist" - "ration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 456, + "title": None, + } + ], + "UHPLbX": [ + { + "id": "edit-company", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "registration-number", + "label": "Non-UK Registration number", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "type": "number", + "unit": None, + "unit_length": None, + "value": 456, + }, + { + "currency": None, + "id": "authorised-insurer-radio", + "label": "Is this non-UK company or branch an authorised insurer?", + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "No", + }, + }, + ], + "id": "edit-question-companies", + "number": None, + "title": "What is the name of the company?", + "type": "General", }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this non-UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=author" - "ised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "No", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], + "title": None, + } + ], + }, + "title": "Companies or UK branches", + "type": "List", + } + ], + "id": "group-companies-1", + "links": { + "add_link": Link( + text="Add another UK company or branch", + url="/questionnaire/companies/add-company/?return_to=section-summary", + target="_self", + attributes={"data-qa": "add-item-link"}, + ) }, - "title": "Companies or UK branches", - "type": "List", - } + "placeholder_text": "No UK company or branch added", + "title": None, + }, + { + "blocks": [], + "id": "group-companies-2", + "links": {}, + "placeholder_text": None, + "title": None, + }, ], - "id": "group-companies-1", - "links": { - "add_link": Link( - text="Add another UK company or branch", - url="/questionnaire/companies/add-company/?return_to=section-summary", - target="_self", - attributes={"data-qa": "add-item-link"}, - ) - }, - "placeholder_text": "No UK company or branch added", - "title": None, - }, - { - "blocks": [], - "id": "group-companies-2", - "links": {}, - "placeholder_text": None, - "title": None, - }, + "title": "General insurance business", + } ], "page_title": "General insurance business", "summary_type": "SectionSummary", diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index d5639b9d5b..10676bd021 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -34,23 +34,27 @@ def test_build_view_submitted_response_context_summary(app: Flask): assert context["summary"]["collapsible"] is False assert context["view_submitted_response"]["expired"] is False assert ( - context["summary"]["groups"][0]["blocks"][0]["question"]["answers"][0][ - "value" - ] + context["summary"]["sections"][0]["groups"][0]["blocks"][0]["question"][ + "answers" + ][0]["value"] == "John Smith" ) assert ( - context["summary"]["groups"][0]["blocks"][0]["question"]["title"] + context["summary"]["sections"][0]["groups"][0]["blocks"][0]["question"][ + "title" + ] == "What is your name?" ) assert ( - context["summary"]["groups"][1]["blocks"][0]["question"]["answers"][0][ - "value" - ] + context["summary"]["sections"][0]["groups"][1]["blocks"][0]["question"][ + "answers" + ][0]["value"] == "NP10 8XG" ) assert ( - context["summary"]["groups"][1]["blocks"][0]["question"]["title"] + context["summary"]["sections"][0]["groups"][1]["blocks"][0]["question"][ + "title" + ] == "What is your address?" ) diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index 1fc223e8b7..96f7ff96ac 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -110,356 +110,329 @@ def test_context_for_summary(): expected = { "answers_are_editable": False, "collapsible": False, - "groups": [ + "sections": [ { - "blocks": [ + "groups": [ { - "id": "name", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "name-answer", - "label": "Full name", - "link": "/questionnaire/name/#name-answer", - "type": "textfield", - "unit": None, - "unit_length": None, - "value": Markup("John"), - } - ], - "id": "name-question", - "number": None, - "title": "What is your name?", - "type": "General", - }, - "title": None, - } - ], - "id": "personal-details-group-0", - "links": {}, - "placeholder_text": None, - "section_title": "Personal Details Section", - "title": "Personal Details", - }, - { - "blocks": [ + "blocks": [ + { + "id": "name", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "name-answer", + "label": "Full name", + "link": "/questionnaire/name/#name-answer", + "type": "textfield", + "unit": None, + "unit_length": None, + "value": Markup("John"), + } + ], + "id": "name-question", + "number": None, + "title": "What is your name?", + "type": "General", + }, + "title": None, + } + ], + "id": "personal-details-group-0", + "links": {}, + "placeholder_text": None, + "title": "Personal Details", + }, { - "id": "address", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "address-answer", - "label": "Postcode", - "link": "/questionnaire/address/#address-answer", - "type": "textfield", - "unit": None, - "unit_length": None, - "value": Markup("1 Street"), - } - ], - "id": "address-question", - "number": None, - "title": "What is your address?", - "type": "General", - }, - "title": None, - } + "blocks": [ + { + "id": "address", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "address-answer", + "label": "Postcode", + "link": "/questionnaire/address/#address-answer", + "type": "textfield", + "unit": None, + "unit_length": None, + "value": Markup("1 Street"), + } + ], + "id": "address-question", + "number": None, + "title": "What is your address?", + "type": "General", + }, + "title": None, + } + ], + "id": "address-details-group-0", + "links": {}, + "placeholder_text": None, + "title": "Address Details", + }, ], - "id": "address-details-group-0", - "links": {}, - "placeholder_text": None, - "title": "Address Details", + "title": "Personal Details Section", }, { - "blocks": [], - "id": "group-0", - "links": {}, - "placeholder_text": None, - "title": None, - "section_title": "Household Section", - }, - { - "blocks": [], - "id": "group-1", - "links": {}, - "placeholder_text": None, - "title": None, - }, - { - "blocks": [], - "id": "group-2", - "links": {}, - "placeholder_text": None, - "title": "List", - }, - { - "blocks": [ - { - "id": "skip-first-block", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "skip-first-block-answer", - "label": None, - "link": "/questionnaire/skip-first-block/#skip-first-block-answer", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, - } - ], - "id": "skip-first-block-question", - "number": None, - "title": "Skip First Block so it doesn’t " - "appear in Total?", - "type": "General", - }, - "title": None, - }, + "groups": [ { - "id": "second-number-block", - "number": None, - "question": { - "answers": [ - { - "currency": "GBP", - "id": "second-number-answer-also-in-total", - "label": "Second answer " - "label also in " - "total (optional)", - "link": "/questionnaire/second-number-block/#second-number-answer-also-in-total", - "type": "currency", - "unit": None, - "unit_length": None, - "value": 1, - } - ], - "id": "second-number-question-also-in-total", - "number": None, - "title": "Second Number Additional " "Question Title", - "type": "General", - }, - "title": None, - }, + "blocks": [ + { + "id": "skip-first-block", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "skip-first-block-answer", + "label": None, + "link": "/questionnaire/skip-first-block/#skip-first-block-answer", + "type": "radio", + "unit": None, + "unit_length": None, + "value": { + "detail_answer_value": None, + "label": "Yes", + }, + } + ], + "id": "skip-first-block-question", + "number": None, + "title": "Skip First Block so it doesn’t appear in Total?", + "type": "General", + }, + "title": None, + }, + { + "id": "second-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "link": "/questionnaire/second-number-block/#second-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + } + ], + "id": "second-number-question-also-in-total", + "number": None, + "title": "Second Number Additional Question Title", + "type": "General", + }, + "title": None, + }, + ], + "id": "radio-0", + "links": {}, + "placeholder_text": None, + "title": "Questions Group", + } ], - "id": "radio-0", - "links": {}, - "placeholder_text": None, - "title": "Questions Group", - "section_title": "Questions Section", + "title": "Questions Section", }, { - "blocks": [ + "groups": [ { - "id": "third-number-block", - "number": None, - "question": { - "answers": [ - { - "currency": "GBP", - "id": "third-number-answer", - "label": "Third answer in " "currency label", - "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer", - "type": "currency", - "unit": None, - "unit_length": None, - "value": 2, + "blocks": [ + { + "id": "third-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "third-number-answer", + "label": "Third answer in currency label", + "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 2, + }, + { + "currency": "GBP", + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 2, + }, + ], + "id": "third-number-question", + "number": None, + "title": "Third Number Question Title", + "type": "General", }, - { - "currency": "GBP", - "id": "third-number-answer-also-in-total", - "label": "Third answer " - "label also in " - "currency total " - "(optional)", - "link": "/questionnaire/people/jufPpX/third-number-block/#third-number-answer-also-in-total", - "type": "currency", - "unit": None, - "unit_length": None, - "value": 2, + "title": None, + }, + { + "id": "mutually-exclusive-checkbox", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "checkbox-answer", + "label": None, + "link": "/questionnaire/people/jufPpX/mutually-exclusive-checkbox/#checkbox-answer", + "type": "checkbox", + "unit": None, + "unit_length": None, + "value": [ + { + "detail_answer_value": None, + "label": "4 - calculated summary answer (current section)", + } + ], + } + ], + "id": "mutually-exclusive-checkbox-question", + "number": None, + "title": "Which answer did you give to question 4 and a half?", + "type": "MutuallyExclusive", }, - ], - "id": "third-number-question", - "number": None, - "title": "Third Number Question Title", - "type": "General", - }, - "title": None, - }, - { - "id": "mutually-exclusive-checkbox", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "checkbox-answer", - "label": None, - "link": "/questionnaire/people/jufPpX/mutually-exclusive-checkbox/#checkbox-answer", - "type": "checkbox", - "unit": None, - "unit_length": None, - "value": [ + "title": None, + }, + { + "id": "skippable-block", + "number": None, + "question": { + "answers": [ { - "detail_answer_value": None, - "label": "4 - " - "calculated " - "summary " - "answer " - "(current " - "section)", + "currency": "GBP", + "id": "skippable-answer", + "label": "Capital expenditure", + "link": "/questionnaire/people/jufPpX/skippable-block/#skippable-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": None, } ], - } - ], - "id": "mutually-exclusive-checkbox-question", - "number": None, - "title": "Which answer did you give to " - "question 4 and a half?", - "type": "MutuallyExclusive", - }, - "title": None, - }, - { - "id": "skippable-block", - "number": None, - "question": { - "answers": [ - { - "currency": "GBP", - "id": "skippable-answer", - "label": "Capital " "expenditure", - "link": "/questionnaire/people/jufPpX/skippable-block/#skippable-answer", - "type": "currency", - "unit": None, - "unit_length": None, - "value": None, - } - ], - "id": "skippable-question", - "number": None, - "title": "How much did James Bond spend " "on fruit?", - "type": "General", - }, - "title": None, - }, + "id": "skippable-question", + "number": None, + "title": "How much did James Bond spend on fruit?", + "type": "General", + }, + "title": None, + }, + ], + "id": "calculated-summary-0", + "links": {}, + "placeholder_text": None, + "title": "Calculated Summary Group", + } ], - "id": "calculated-summary-0", - "links": {}, - "placeholder_text": None, - "title": "Calculated Summary Group", - "section_title": "James Bond", + "title": "James Bond", }, { - "blocks": [ + "groups": [ { - "id": "third-number-block", - "number": None, - "question": { - "answers": [ - { - "currency": "GBP", - "id": "third-number-answer", - "label": "Third answer in " "currency label", - "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer", - "type": "currency", - "unit": None, - "unit_length": None, - "value": 1, + "blocks": [ + { + "id": "third-number-block", + "number": None, + "question": { + "answers": [ + { + "currency": "GBP", + "id": "third-number-answer", + "label": "Third answer in currency label", + "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + }, + { + "currency": "GBP", + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer-also-in-total", + "type": "currency", + "unit": None, + "unit_length": None, + "value": 1, + }, + ], + "id": "third-number-question", + "number": None, + "title": "Third Number Question Title", + "type": "General", }, - { - "currency": "GBP", - "id": "third-number-answer-also-in-total", - "label": "Third answer " - "label also in " - "currency total " - "(optional)", - "link": "/questionnaire/people/fjWZET/third-number-block/#third-number-answer-also-in-total", - "type": "currency", - "unit": None, - "unit_length": None, - "value": 1, + "title": None, + }, + { + "id": "mutually-exclusive-checkbox", + "number": None, + "question": { + "answers": [ + { + "currency": None, + "id": "checkbox-answer", + "label": None, + "link": "/questionnaire/people/fjWZET/mutually-exclusive-checkbox/#checkbox-answer", + "type": "checkbox", + "unit": None, + "unit_length": None, + "value": [ + { + "detail_answer_value": None, + "label": "1 - calculated summary answer (previous section)", + } + ], + } + ], + "id": "mutually-exclusive-checkbox-question", + "number": None, + "title": "Which answer did you give to question 4 and a half?", + "type": "MutuallyExclusive", }, - ], - "id": "third-number-question", - "number": None, - "title": "Third Number Question Title", - "type": "General", - }, - "title": None, - }, - { - "id": "mutually-exclusive-checkbox", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "checkbox-answer", - "label": None, - "link": "/questionnaire/people/fjWZET/mutually-exclusive-checkbox/#checkbox-answer", - "type": "checkbox", - "unit": None, - "unit_length": None, - "value": [ + "title": None, + }, + { + "id": "skippable-block", + "number": None, + "question": { + "answers": [ { - "detail_answer_value": None, - "label": "1 - " - "calculated " - "summary " - "answer " - "(previous " - "section)", + "currency": "GBP", + "id": "skippable-answer", + "label": "Capital expenditure", + "link": "/questionnaire/people/fjWZET/skippable-block/#skippable-answer", + "type": "currency", + "unit": None, + "unit_length": None, + "value": None, } ], - } - ], - "id": "mutually-exclusive-checkbox-question", - "number": None, - "title": "Which answer did you give to " - "question 4 and a half?", - "type": "MutuallyExclusive", - }, - "title": None, - }, - { - "id": "skippable-block", - "number": None, - "question": { - "answers": [ - { - "currency": "GBP", - "id": "skippable-answer", - "label": "Capital " "expenditure", - "link": "/questionnaire/people/fjWZET/skippable-block/#skippable-answer", - "type": "currency", - "unit": None, - "unit_length": None, - "value": None, - } - ], - "id": "skippable-question", - "number": None, - "title": "How much did Jane Doe spend " "on fruit?", - "type": "General", - }, - "title": None, - }, + "id": "skippable-question", + "number": None, + "title": "How much did Jane Doe spend on fruit?", + "type": "General", + }, + "title": None, + }, + ], + "id": "calculated-summary-0-1", + "links": {}, + "placeholder_text": None, + "title": "Calculated Summary Group", + } ], - "id": "calculated-summary-0-1", - "links": {}, - "placeholder_text": None, - "title": "Calculated Summary Group", - "section_title": "Jane Doe", + "title": "Jane Doe", }, ], "summary_type": "Summary", From 0938f8f858bd812a6b1e5d111025e6cc0f1518a4 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 15 May 2023 12:30:58 +0100 Subject: [PATCH 224/567] Add dynamic answers list source (#1079) --- app/forms/questionnaire_form.py | 38 +- app/questionnaire/placeholder_renderer.py | 88 +++- app/questionnaire/questionnaire_schema.py | 58 ++- .../questionnaire_store_updater.py | 43 +- app/questionnaire/schema_utils.py | 11 +- app/submitter/convert_payload_0_0_3.py | 34 +- app/views/contexts/summary/block.py | 8 + app/views/contexts/summary/group.py | 1 + .../contexts/summary/list_collector_block.py | 1 + app/views/contexts/summary/question.py | 78 ++- app/views/handlers/block.py | 2 + app/views/handlers/list_action.py | 2 + app/views/handlers/list_add_question.py | 5 +- app/views/handlers/list_remove_question.py | 4 +- .../handlers/primary_person_list_collector.py | 6 +- app/views/handlers/primary_person_question.py | 2 + app/views/handlers/question.py | 48 +- .../en/test_dynamic_answers_list_source.json | 486 ++++++++++++++++++ tests/app/questionnaire/conftest.py | 72 +++ .../test_placeholder_renderer.py | 100 ++++ .../test_questionnaire_store_updater.py | 182 +++++++ .../submitter/test_convert_payload_0_0_3.py | 52 ++ .../app/views/contexts/summary/test_block.py | 1 + .../views/contexts/summary/test_question.py | 302 ++++++++++- .../test_question_with_dynamic_answers.py | 113 ++++ tests/functional/generate_pages.py | 14 +- .../dynamic_answers_list_value_source.spec.js | 201 ++++++++ 27 files changed, 1850 insertions(+), 102 deletions(-) create mode 100644 schemas/test/en/test_dynamic_answers_list_source.json create mode 100644 tests/app/views/handlers/test_question_with_dynamic_answers.py create mode 100644 tests/functional/spec/features/dynamic_answers_list_value_source.spec.js diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 0ea61d0245..9acd9d5ed2 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -483,19 +483,20 @@ def get_answer_fields( if routing_path_block_ids: block_ids = get_flattened_mapping_values(routing_path_block_ids) - value_source_resolver = ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - schema=schema, - location=location, - list_item_id=list_item_id, - escape_answer_values=False, - response_metadata=response_metadata, - routing_path_block_ids=block_ids, - assess_routing_path=False, - progress_store=progress_store, - ) + def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResolver: + return ValueSourceResolver( + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + schema=schema, + location=location, + list_item_id=list_item, + escape_answer_values=False, + response_metadata=response_metadata, + routing_path_block_ids=block_ids, + assess_routing_path=False, + progress_store=progress_store, + ) rule_evaluator = RuleEvaluator( schema=schema, @@ -509,7 +510,14 @@ def get_answer_fields( answer_fields = {} question_title = question.get("title") - for answer in question.get("answers", []): + + value_source_resolved_for_location = _get_value_source_resolver(list_item_id) + for answer in question["answers"]: + if "list_item_id" in answer: + value_source_resolver = _get_value_source_resolver(answer["list_item_id"]) + else: + value_source_resolver = value_source_resolved_for_location + for option in answer.get("options", []): if "detail_answer" in option: if data: @@ -581,7 +589,7 @@ def _clear_detail_answer_field( """ Clears the detail answer field if the parent option is not selected """ - for answer in question_schema["answers"]: + for answer in question_schema.get("answers", []): for option in answer.get("options", []): if "detail_answer" in option and option["value"] not in form_data.getlist( answer["id"] diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index c41063da70..1309c52a56 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -1,4 +1,5 @@ -from typing import Mapping, MutableMapping +from copy import deepcopy +from typing import Any, Mapping, MutableMapping from jsonpointer import resolve_pointer, set_pointer @@ -123,9 +124,12 @@ def render( """ Transform the current schema json to a fully rendered dictionary """ - data_to_render_mutable: dict = QuestionnaireSchema.get_mutable_deepcopy( - data_to_render - ) + data_to_render_mutable: dict[ + str, Any + ] = QuestionnaireSchema.get_mutable_deepcopy(data_to_render) + + self._handle_and_resolve_dynamic_answers(data_to_render_mutable) + pointers = find_pointers_containing(data_to_render_mutable, "placeholders") placeholder_parser = PlaceholderParser( @@ -150,5 +154,79 @@ def render( placeholder_parser=placeholder_parser, ) set_pointer(data_to_render_mutable, pointer, rendered_text) - return data_to_render_mutable + + def _handle_and_resolve_dynamic_answers( + self, data_to_render_mutable: dict[str, Any] + ) -> None: + pointers = find_pointers_containing(data_to_render_mutable, "dynamic_answers") + + for pointer in pointers: + data = resolve_pointer(data_to_render_mutable, pointer) + dynamic_answers = data["dynamic_answers"] + + if dynamic_answers["values"]["source"] == "list": + self.resolve_dynamic_answers_ids(dynamic_answers) + self.resolve_dynamic_answers(dynamic_answers) + + updated_value = { + "answers": dynamic_answers["answers"] + data.get("answers", []), + "dynamic_answers": dynamic_answers, + } + + del updated_value["dynamic_answers"]["answers"] + + if pointer: + set_pointer(data_to_render_mutable, pointer, updated_value) + else: + data_to_render_mutable |= updated_value + + def resolve_dynamic_answers_ids( + self, + dynamic_answers: dict, + ) -> None: + list_name = dynamic_answers["values"]["identifier"] + list_items = self._list_store[list_name].items + + resolved_dynamic_answers = [] + + for dynamic_answer in dynamic_answers["answers"]: + for item in list_items: + resolved_dynamic_answer = deepcopy(dynamic_answer) + resolved_dynamic_answer["original_answer_id"] = dynamic_answer["id"] + resolved_dynamic_answer["id"] = f"{dynamic_answer['id']}-{item}" + resolved_dynamic_answer["list_item_id"] = item + + resolved_dynamic_answers.append(resolved_dynamic_answer) + + dynamic_answers["answers"] = resolved_dynamic_answers + + def resolve_dynamic_answers( + self, + dynamic_answers: dict, + ) -> None: + for answer in dynamic_answers["answers"]: + placeholder_parser = PlaceholderParser( + language=self._language, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + list_item_id=answer["list_item_id"], + location=self._location, + renderer=self, + placeholder_preview_mode=self._placeholder_preview_mode, + progress_store=self._progress_store, + ) + + pointers = find_pointers_containing(answer, "placeholders") + + for pointer in pointers: + rendered_text = self.render_pointer( + dict_to_render=answer, + pointer_to_render=pointer, + list_item_id=answer["list_item_id"], + placeholder_parser=placeholder_parser, + ) + set_pointer(answer, pointer, rendered_text) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index e1cf2a01f6..17ec2348de 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -10,6 +10,7 @@ from app.data_models.answer import Answer from app.forms import error_messages from app.questionnaire.rules.operator import OPERATION_MAPPING +from app.questionnaire.schema_utils import get_answers_from_question from app.utilities.make_immutable import make_immutable from app.utilities.mappings import get_mappings_with_key @@ -73,6 +74,7 @@ def __init__( self._blocks_by_id = self._get_blocks_by_id() self._questions_by_id = self._get_questions_by_id() self._answers_by_id = self._get_answers_by_id() + self._dynamic_answer_ids: set[None] = set() # Post schema parsing. self._populate_answer_dependencies() @@ -229,7 +231,8 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: for question in self._get_flattened_questions(): question_id = question["id"] - for answer in question["answers"]: + + for answer in get_answers_from_question(question): answer_id = answer["id"] self._parent_id_map[answer_id] = question_id @@ -259,6 +262,19 @@ def _populate_answer_dependencies(self) -> None: ) continue + if dynamic_answers := question.get("dynamic_answers"): + for answer in dynamic_answers["answers"]: + value_source = dynamic_answers["values"] + self._update_answer_dependencies_for_value_source( + value_source, block_id=block["id"], answer_id=answer["id"] + ) + + self._dynamic_answer_ids.add(answer["id"]) + + self._update_answer_dependencies_for_answer( + answer, block_id=block["id"] + ) + for answer in question.get("answers", []): self._update_answer_dependencies_for_answer( answer, block_id=block["id"] @@ -334,7 +350,9 @@ def _update_answer_dependencies_for_value_source( ) -> None: if value_source["source"] == "answers": self._answer_dependencies_map[value_source["identifier"]] |= { - self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore + self._get_answer_dependent_for_block_id( + block_id=block_id, answer_id=answer_id + ) } if value_source["source"] == "calculated_summary": identifier = value_source["identifier"] @@ -345,14 +363,38 @@ def _update_answer_dependencies_for_value_source( for answer_id_for_block in answer_ids_for_block: self._answer_dependencies_map[answer_id_for_block] |= { - self._get_answer_dependent_for_block_id(block_id=block_id, answer_id=answer_id) # type: ignore + self._get_answer_dependent_for_block_id( + block_id=block_id, answer_id=answer_id + ) } + if value_source["source"] == "list": + section = self.get_section_for_block_id(block_id) + list_collector = self.get_list_collector_for_list( + # Returns of methods are optional, but they always exist at this point, same with optional returns below + section=section, # type: ignore + for_list=value_source["identifier"], # type: ignore + ) + add_block_question = self.get_add_block_for_list_collector( # type: ignore + list_collector["id"] # type: ignore + )["question"] + answer_ids_for_block = list( + self.get_answers_for_question_by_id(add_block_question) + ) + for block_answer_id in answer_ids_for_block: + self._answer_dependencies_map[block_answer_id] |= { + self._get_answer_dependent_for_block_id(block_id=block_id, for_list=value_source["identifier"]) # type: ignore + } def _get_answer_dependent_for_block_id( - self, *, block_id: str, answer_id: str | None = None + self, + *, + block_id: str, + answer_id: str | None = None, + for_list: str | None = None, ) -> AnswerDependent: section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore - for_list = self.get_repeating_list_for_section(section_id) + if not for_list: + for_list = self.get_repeating_list_for_section(section_id) return AnswerDependent( block_id=block_id, @@ -581,6 +623,9 @@ def is_answer_in_repeating_section(self, answer_id: str) -> bool | None: if block := self.get_block_for_answer_id(answer_id): return self.is_block_in_repeating_section(block_id=block["id"]) + def is_answer_dynamic(self, answer_id: str) -> bool: + return answer_id in self._dynamic_answer_ids + def is_repeating_answer( self, answer_id: str, @@ -588,6 +633,7 @@ def is_repeating_answer( return bool( self.is_answer_in_list_collector_block(answer_id) or self.is_answer_in_repeating_section(answer_id) + or self.is_answer_dynamic(answer_id) ) def get_answers_by_answer_id(self, answer_id: str) -> list[ImmutableDict]: @@ -663,7 +709,7 @@ def get_answers_for_question_by_id( ) -> dict[str, dict]: answers: dict[str, dict] = {} - for answer in question.get("answers", {}): + for answer in get_answers_from_question(question): answers[answer["id"]] = answer for option in answer.get("options", {}): if "detail_answer" in option: diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 8b117da257..72d748cbff 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,6 +1,6 @@ from collections import defaultdict, namedtuple from itertools import combinations -from typing import Iterable, Mapping +from typing import Any, Iterable, Mapping from werkzeug.datastructures import ImmutableDict @@ -168,6 +168,17 @@ def remove_list_item_and_answers(self, list_name: str, list_item_id: str) -> Non self._progress_store.remove_progress_for_list_item_id(list_item_id=list_item_id) + for list_collector in self._schema.get_list_collectors_for_list( + for_list=list_name, + section=self._schema.get_section(self._current_location.section_id), # type: ignore + # type ignore Section and answer_id below must exist at this point + ): + block = self._schema.get_add_block_for_list_collector(list_collector["id"]) + answer_ids = self._schema.get_answer_ids_for_block(block["id"]) # type: ignore + # type ignore non-optional return, always exists + for answer_id in answer_ids: + self._capture_block_dependencies_for_answer(answer_id) + def get_relationship_answers_for_list_name( self, list_name: str ) -> list[Answer] | None: @@ -332,28 +343,30 @@ def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: self.dependent_sections.add(DependentSection(section_id, None, None)) def update_answers( - self, form_data: Mapping, list_item_id: str | None = None + self, form_data: Mapping[str, Any], list_item_id: str | None = None ) -> None: list_item_id = list_item_id or self._current_location.list_item_id - answer_ids_for_question = self._schema.get_answer_ids_for_question( + answers_by_answer_id = self._schema.get_answers_for_question_by_id( self._current_question ) for answer_id, answer_value in form_data.items(): - if answer_id not in answer_ids_for_question: + if answer_id not in answers_by_answer_id: continue - answer_updated = self._update_answer(answer_id, list_item_id, answer_value) + resolved_answer = answers_by_answer_id[answer_id] + answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id + list_item_id_to_use = resolved_answer.get("list_item_id") or list_item_id + + answer_updated = self._update_answer( + answer_id_to_use, list_item_id_to_use, answer_value + ) if answer_updated: - self._capture_section_dependencies_for_answer(answer_id) - self._capture_block_dependencies_for_answer(answer_id) + self._capture_section_dependencies_for_answer(answer_id_to_use) + self._capture_block_dependencies_for_answer(answer_id_to_use) def update_progress_for_dependent_sections(self) -> None: - """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. - Section progress is not updated for the current location as it is handled by `handle_post` on block handlers. - """ - - self._remove_dependent_blocks_and_capture_dependent_sections() + """Updates the progress to IN_PROGRESS. Section progress is not updated for the current location as it is handled by `handle_post` on block handlers.""" for section in self.dependent_sections: if ( @@ -376,8 +389,10 @@ def update_progress_for_dependent_sections(self) -> None: list_item_id=section.list_item_id, ) - def _remove_dependent_blocks_and_capture_dependent_sections(self) -> None: - """Removes dependent blocks from the progress store.""" + def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: + """Removes dependent blocks from the progress store. + This must be called before updating section progress (update_progress_for_dependent_sections) and section dependencies (_update_section_completeness) + """ for ( section_key, diff --git a/app/questionnaire/schema_utils.py b/app/questionnaire/schema_utils.py index 6f09c4e7f0..28d7830ae2 100644 --- a/app/questionnaire/schema_utils.py +++ b/app/questionnaire/schema_utils.py @@ -29,7 +29,16 @@ def find_pointers_containing( yield from find_pointers_containing(item, search_key, f"{pointer}/{index}") +def get_answers_from_question(question: Mapping) -> list: + static_answers = question.get("answers", []) + dynamic_answers = question.get("dynamic_answers", {}).get("answers", []) + return [*dynamic_answers, *static_answers] + + def get_answer_ids_in_block(block: Mapping) -> list[str]: question = block["question"] - answer_ids = [answer["id"] for answer in question["answers"]] + answer_ids = [] + for answer in get_answers_from_question(question): + answer_ids.append(answer["id"]) + return answer_ids diff --git a/app/submitter/convert_payload_0_0_3.py b/app/submitter/convert_payload_0_0_3.py index a15708e017..fa6ccfe62f 100644 --- a/app/submitter/convert_payload_0_0_3.py +++ b/app/submitter/convert_payload_0_0_3.py @@ -70,8 +70,23 @@ def convert_answers_to_payload_0_0_3( ) answer_ids = schema.get_answer_ids_for_block(block_id) + static_answer_ids = [ + answer_id + for answer_id in answer_ids + if not schema.is_answer_dynamic(answer_id) + ] + has_dynamic_answers = len(static_answer_ids) != len(answer_ids) + + if answer_ids and has_dynamic_answers: + resolve_dynamic_answers( + question=schema.get_all_questions_for_block(block)[0], + answer_store=answer_store, + answers_payload=answers_payload, + list_store=list_store, + ) + answers_in_block = answer_store.get_answers_by_answer_id( - answer_ids, list_item_id=routing_path.list_item_id + static_answer_ids, list_item_id=routing_path.list_item_id ) for answer_in_block in answers_in_block: answers_payload.add_or_update(answer_in_block) @@ -145,3 +160,20 @@ def add_relationships_unrelated_answers( ) ): answers_payload.add_or_update(unrelated_answer) + + +def resolve_dynamic_answers( + question: Mapping, + answer_store: AnswerStore, + answers_payload: AnswerStore, + list_store: ListStore, +) -> None: + dynamic_answers = question["dynamic_answers"] + values = dynamic_answers["values"] + for answer in dynamic_answers["answers"]: + if values["source"] == "list": + for list_item_id in list_store[values["identifier"]].items: + if extracted_answer := answer_store.get_answer( + answer["id"], list_item_id=list_item_id + ): + answers_payload.add_or_update(extracted_answer) diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 99677a2b4f..13f271a0c4 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -23,6 +23,7 @@ def __init__( return_to: Optional[str], return_to_block_id: Optional[str] = None, progress_store: ProgressStore, + language: str, ) -> None: self.id = block_schema["id"] self.title = block_schema.get("title") @@ -61,6 +62,7 @@ def __init__( return_to=return_to, return_to_block_id=return_to_block_id, progress_store=progress_store, + language=language, ) def get_question( @@ -76,6 +78,7 @@ def get_question( return_to: Optional[str], return_to_block_id: Optional[str], progress_store: ProgressStore, + language: str, ) -> dict[str, Question]: """Taking question variants into account, return the question which was displayed to the user""" @@ -94,6 +97,8 @@ def get_question( return Question( variant, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=schema, rule_evaluator=self._rule_evaluator, value_source_resolver=self._value_source_resolver, @@ -101,6 +106,9 @@ def get_question( block_id=self.id, return_to=return_to, return_to_block_id=return_to_block_id, + metadata=metadata, + response_metadata=response_metadata, + language=language, ).serialize() def serialize(self) -> dict[str, Any]: diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index d1a73c276c..9b4a15fae5 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -104,6 +104,7 @@ def _build_blocks_and_links( return_to=return_to, return_to_block_id=return_to_block_id, progress_store=progress_store, + language=language, ).serialize() ] ) diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index d31d026e3c..93a4737f68 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -192,6 +192,7 @@ def _get_related_answers( return_to="section-summary", return_to_block_id=None, progress_store=self._progress_store, + language=self._language, ).serialize() for block in blocks ] diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index de26e19e53..0f16e95e0d 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -1,12 +1,15 @@ -from typing import Any, Mapping, Optional +from typing import Any, Mapping, MutableMapping, Optional from flask import url_for from markupsafe import Markup, escape +from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import AnswerValueEscapedTypes, escape_answer_value +from app.data_models.metadata_proxy import MetadataProxy from app.forms.field_handlers.select_handlers import DynamicAnswerOptions from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType +from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.views.contexts.summary.answer import ( @@ -16,12 +19,15 @@ ) +# pylint: disable=too-many-locals class Question: def __init__( self, question_schema: QuestionSchemaType, *, answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, schema: QuestionnaireSchema, rule_evaluator: RuleEvaluator, value_source_resolver: ValueSourceResolver, @@ -29,12 +35,18 @@ def __init__( block_id: str, return_to: Optional[str], return_to_block_id: Optional[str] = None, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + language: str, ) -> None: self.list_item_id = location.list_item_id if location else None self.id = question_schema["id"] self.type = question_schema["type"] self.schema = schema - self.answer_schemas = iter(question_schema["answers"]) + self.answer_schemas = iter(question_schema.get("answers", [])) + self.answer_store = answer_store + self.list_store = list_store + self.progress_store = progress_store self.summary = question_schema.get("summary") self.title = ( question_schema.get("title") or question_schema["answers"][0]["label"] @@ -51,13 +63,16 @@ def __init__( list_name=location.list_name if location else None, return_to=return_to, return_to_block_id=return_to_block_id, + metadata=metadata, + response_metadata=response_metadata, + language=language, ) def get_answer( - self, answer_store: AnswerStore, answer_id: str + self, answer_store: AnswerStore, answer_id: str, list_item_id: str | None = None ) -> Optional[AnswerValueEscapedTypes]: answer = answer_store.get_answer( - answer_id, self.list_item_id + answer_id, list_item_id or self.list_item_id ) or self.schema.get_default_answer(answer_id) return escape_answer_value(answer.value) if answer else None @@ -68,9 +83,12 @@ def _build_answers( answer_store: AnswerStore, question_schema: QuestionSchemaType, block_id: str, - list_name: Optional[str], - return_to: Optional[str], - return_to_block_id: Optional[str], + list_name: str | None, + return_to: str | None, + return_to_block_id: str | None, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + language: str, ) -> list[dict[str, Any]]: if self.summary: answer_id = f"{self.id}-concatenated-answer" @@ -95,9 +113,17 @@ def _build_answers( ] summary_answers = [] - for answer_schema in self.answer_schemas: - answer_value: Optional[AnswerValueEscapedTypes] = self.get_answer( - answer_store, answer_schema["id"] + + for answer_schema in self._get_resolved_answers( + question_schema=question_schema, + language=language, + metadata=metadata, + response_metadata=response_metadata, + ): + list_item_id = answer_schema.get("list_item_id") + answer_id = answer_schema.get("original_answer_id") or answer_schema["id"] + answer_value = self.get_answer( + answer_store, answer_id, list_item_id=list_item_id ) answer = self._build_answer( answer_store, question_schema, answer_schema, answer_value @@ -108,7 +134,7 @@ def _build_answers( answer_value=answer, block_id=block_id, list_name=list_name, - list_item_id=self.list_item_id, + list_item_id=list_item_id or self.list_item_id, return_to=return_to, return_to_block_id=return_to_block_id, ).serialize() @@ -252,6 +278,34 @@ def _build_dropdown_answer( if answer == option["value"]: return option["label"] + def _get_resolved_answers( + self, + *, + question_schema: QuestionSchemaType, + language: str, + metadata: MetadataProxy | None = None, + response_metadata: MutableMapping, + ) -> Any: + resolved_question = ImmutableDict({"answers": self.answer_schemas}) + + if "dynamic_answers" in question_schema: + placeholder_renderer = PlaceholderRenderer( + answer_store=self.answer_store, + list_store=self.list_store, + progress_store=self.progress_store, + schema=self.schema, + language=language, + metadata=metadata, + response_metadata=response_metadata, + ) + + resolved_question = ImmutableDict( + placeholder_renderer.render( + data_to_render=question_schema, list_item_id=self.list_item_id + ) + ) + return resolved_question["answers"] + def serialize(self) -> dict[str, Any]: return { "id": self.id, diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 91e3ef016a..6019bc762e 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -113,7 +113,9 @@ def get_next_location_url(self): def handle_post(self): self._set_started_at_metadata() self.questionnaire_store_updater.add_completed_location() + self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self._update_section_completeness() + self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() def _get_routing_path(self): diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index 79e4e4ec2e..010defc49e 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -92,6 +92,8 @@ def handle_post(self): self.current_location.section_id, self.current_location.list_item_id ) + self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() + self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() def _get_location_url( diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index 4e0fe60745..0ccae5827e 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -33,9 +33,8 @@ def handle_post(self): # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data, list_item_id) - self.evaluate_and_update_section_status_on_list_change( - self.parent_block["for_list"] - ) + self.capture_dependent_sections_for_list(self.parent_block["for_list"]) + return super().handle_post() def _resolve_custom_page_title_vars(self) -> MutableMapping: diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index cc35814826..c9742638ca 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -28,9 +28,7 @@ def handle_post(self): self.questionnaire_store_updater.remove_list_item_and_answers( list_name, self._current_location.list_item_id ) - self.evaluate_and_update_section_status_on_list_change( - self.parent_block["for_list"] - ) + self.capture_dependent_sections_for_list(self.parent_block["for_list"]) return super().handle_post() diff --git a/app/views/handlers/primary_person_list_collector.py b/app/views/handlers/primary_person_list_collector.py index bb986421f6..84123fa2fe 100644 --- a/app/views/handlers/primary_person_list_collector.py +++ b/app/views/handlers/primary_person_list_collector.py @@ -34,7 +34,9 @@ def handle_post(self): self.questionnaire_store_updater.add_primary_person(list_name) ) - self.evaluate_and_update_section_status_on_list_change(list_name) + self.capture_dependent_sections_for_list(list_name) + self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() + self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() else: self.questionnaire_store_updater.remove_primary_person(list_name) @@ -47,5 +49,5 @@ def handle_post(self): # `super().handle_post()`. The section status will eventually get # determined correctly when the parent class' `update_section_status` # method is called. - self.evaluate_and_update_section_status_on_list_change(list_name) + self.capture_dependent_sections_for_list(list_name) super().handle_post() diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index 1544458ff4..13079791fd 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -51,5 +51,7 @@ def handle_post(self): location=self.parent_location ) + self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self._update_section_completeness(location=self.parent_location) + self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 181023907d..4299047179 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -7,7 +7,10 @@ from app.forms.questionnaire_form import generate_form from app.helpers import get_address_lookup_api_auth_token from app.questionnaire.location import Location -from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater +from app.questionnaire.questionnaire_store_updater import ( + DependentSection, + QuestionnaireStoreUpdater, +) from app.questionnaire.variants import transform_variants from app.views.contexts import ListContext from app.views.contexts.question import build_question_context @@ -114,11 +117,23 @@ def get_next_location_url(self): ) def _get_answers_for_question(self, question_json) -> dict[str, Any]: - answer_ids = self._schema.get_answer_ids_for_question(question_json) - answers = self._questionnaire_store.answer_store.get_answers_by_answer_id( - answer_ids=answer_ids, list_item_id=self._current_location.list_item_id + answers_by_answer_id = self._schema.get_answers_for_question_by_id( + question_json ) - return {answer.answer_id: answer.value for answer in answers if answer} + answer_value_by_answer_id = {} + + for answer_id, resolved_answer in answers_by_answer_id.items(): + list_item_id = ( + resolved_answer.get("list_item_id") + or self._current_location.list_item_id + ) + answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id + if answer := self._questionnaire_store.answer_store.get_answer( + answer_id=answer_id_to_use, list_item_id=list_item_id + ): + answer_value_by_answer_id[answer_id] = answer.value + + return answer_value_by_answer_id def _get_list_add_question_url(self, params): block_id = params["block_id"] @@ -195,7 +210,6 @@ def handle_post(self): # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) - self.questionnaire_store_updater.update_progress_for_dependent_sections() if self.questionnaire_store_updater.is_dirty(): self._routing_path = self.router.routing_path( section_id=self._current_location.section_id, @@ -210,22 +224,20 @@ def get_return_to_hub_url(self): ): return url_for(".get_questionnaire") - def evaluate_and_update_section_status_on_list_change(self, list_name): + def capture_dependent_sections_for_list(self, list_name): section_ids = self._schema.get_section_ids_dependent_on_list(list_name) section_ids.append(self.current_location.section_id) - section_keys_to_evaluate = ( - self.questionnaire_store_updater.started_section_keys( - section_ids=section_ids - ) + section_keys_to_add = self.questionnaire_store_updater.started_section_keys( + section_ids=section_ids ) - - for section_id, list_item_id in section_keys_to_evaluate: - path = self.router.routing_path(section_id, list_item_id) - self.questionnaire_store_updater.update_section_status( - is_complete=self.router.is_path_complete(path), - section_id=section_id, - list_item_id=list_item_id, + for section_id, list_item_id in section_keys_to_add: + self.questionnaire_store_updater.dependent_sections.add( + DependentSection( + section_id=section_id, + list_item_id=list_item_id, + is_complete=None, + ) ) def clear_radio_answers(self): diff --git a/schemas/test/en/test_dynamic_answers_list_source.json b/schemas/test/en/test_dynamic_answers_list_source.json new file mode 100644 index 0000000000..4c2ebf94ef --- /dev/null +++ b/schemas/test/en/test_dynamic_answers_list_source.json @@ -0,0 +1,486 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Dynamic Answers List Source", + "theme": "default", + "description": "A questionnaire to demo dynamic answers list source.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": {} + }, + "sections": [ + { + "id": "section", + "title": "List Collector Section", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "supermarkets", + "title": "Household members", + "add_link_text": "Add another supermarket", + "empty_list_text": "There are no supermarkets" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-supermarket", + "for_list": "supermarkets", + "question": { + "type": "General", + "id": "any-supermarket-question", + "title": "Do you need to add any supermarkets?", + "answers": [ + { + "type": "Radio", + "id": "any-supermarket-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-supermarket", + "list_name": "supermarkets" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-supermarket-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "supermarkets", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add any more supermarkets?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-supermarket", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other supermarkets?", + "question": { + "guidance": { + "contents": [ + { + "description": "Maximum spending value will be used for each supermarket’s max spending validation and placeholders." + } + ] + }, + "id": "add-question", + "type": "General", + "title": "Which supermarkets do you use for your weekly shopping?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + }, + { + "id": "set-maximum", + "description": "Maximum amount of spending at this supermarket, should be between 1001 and 10000", + "label": "Maximum Spending", + "mandatory": true, + "type": "Number", + "decimal_places": 2, + "minimum": { + "value": 1001 + }, + "maximum": { + "value": 10000 + } + } + ] + } + }, + "edit_block": { + "id": "edit-supermarket", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "guidance": { + "contents": [ + { + "description": "Maximum spending value will be used for each supermarket’s max spending validation and placeholders." + } + ] + }, + "id": "edit-question", + "type": "General", + "title": "What is the name of the supermarket?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + }, + { + "id": "set-maximum", + "description": "Maximum amount of spending at this supermarket", + "label": "Maximum amount of spending", + "mandatory": true, + "type": "Number", + "decimal_places": 2, + "minimum": { + "value": 1001 + }, + "maximum": { + "value": 10000 + } + } + ] + } + }, + "remove_block": { + "id": "remove-supermarket", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this supermarket?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this supermarket?", + "warning": "All of the information about this supermarket will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Supermarkets", + "item_title": { + "text": "{supermarket_name}", + "placeholders": [ + { + "placeholder": "supermarket_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "supermarket-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-answer", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "Percentage of shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "percentage-of-shopping", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 0 + }, + { + "id": "days-a-week", + "label": { + "text": "How many days a week you shop at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "mandatory": false, + "type": "Number", + "decimal_places": 0, + "minimum": { + "value": 1 + }, + "maximum": { + "value": 7 + } + } + ] + }, + "answers": [ + { + "id": "based-checkbox-answer", + "label": "Are supermarkets UK or non UK based?", + "instruction": "Select any answers that apply", + "mandatory": false, + "options": [ + { + "label": "UK based supermarkets", + "value": "UK based supermarkets" + }, + { + "label": "Non UK based supermarkets", + "value": "Non UK based supermarkets" + } + ], + "type": "Checkbox" + } + ], + "id": "dynamic-answer-question", + "title": "What percent of your shopping do you do at each of the following supermarket?", + "type": "General" + } + }, + { + "type": "Question", + "id": "minimum-spending", + "question": { + "guidance": { + "contents": [ + { + "description": "This value will be used for all supermarkets minimum spending validation and placeholders." + } + ] + }, + "answers": [ + { + "id": "set-minimum", + "label": "Minimum Spending", + "description": "Minium amount of spending at all supermarkets", + "mandatory": true, + "type": "Number", + "decimal_places": 2, + "minimum": { + "value": 0 + }, + "maximum": { + "value": 1000 + } + } + ], + "id": "minimum-spending-question", + "title": "What is your minimum amount of spending?", + "type": "General" + } + }, + { + "type": "Question", + "id": "dynamic-answer-only", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "guidance": { + "contents": [ + { + "description": "Answers are validated against values piped from previous questions, maximum from repeated question for each supermarket, minimum from non-repeated question. Answer label’s placeholders are resolved from these as well." + } + ] + }, + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "How much do you spend at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "amount-of-shopping", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "description": { + "text": "The answers must be less than or equal {max_value} and greater than or equal {min_value}", + "placeholders": [ + { + "placeholder": "min_value", + "value": { + "source": "answers", + "identifier": "set-minimum" + } + }, + { + "placeholder": "max_value", + "value": { + "source": "answers", + "identifier": "set-maximum" + } + } + ] + }, + "maximum": { + "value": { + "source": "answers", + "identifier": "set-maximum" + } + }, + "minimum": { + "value": { + "source": "answers", + "identifier": "set-minimum" + } + } + } + ] + }, + "id": "dynamic-answer-only-question", + "title": "How much do you spend at each of the following supermarket?", + "type": "General" + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index d745f5af4c..58ed5d4bae 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1290,3 +1290,75 @@ def section_dependencies_new_calculated_summary_schema(): return load_schema_from_name( "test_routing_and_skipping_section_dependencies_new_calculated_summary" ) + + +@pytest.fixture +@pytest.mark.usefixtures("app", "gb_locale") +def placeholder_transform_question_dynamic_answers_json(): + return { + "dynamic_answers": { + "values": {"source": "list", "identifier": "supermarkets"}, + "answers": [ + { + "label": { + "text": "Percentage of shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name", + }, + } + ], + }, + "id": "percentage-of-shopping", + "mandatory": False, + "type": "Percentage", + "maximum": {"value": 100}, + "decimal_places": 0, + } + ], + }, + "answers": [], + "id": "dynamic-answer-question", + "title": "What percent of your shopping do you do at each of the following supermarket?", + "type": "General", + } + + +@pytest.fixture +@pytest.mark.usefixtures("app", "gb_locale") +def placeholder_transform_question_dynamic_answers_pointer_json(): + return { + "question": { + "dynamic_answers": { + "values": {"source": "list", "identifier": "supermarkets"}, + "answers": [ + { + "label": { + "text": "Percentage of shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name", + }, + } + ], + }, + "id": "percentage-of-shopping", + "mandatory": False, + "type": "Percentage", + "maximum": {"value": 100}, + "decimal_places": 0, + } + ], + }, + "answers": [], + "id": "dynamic-answer-question", + "title": "What percent of your shopping do you do at each of the following supermarket?", + "type": "General", + } + } diff --git a/tests/app/questionnaire/test_placeholder_renderer.py b/tests/app/questionnaire/test_placeholder_renderer.py index 755ac799b8..b5257ae6b9 100644 --- a/tests/app/questionnaire/test_placeholder_renderer.py +++ b/tests/app/questionnaire/test_placeholder_renderer.py @@ -7,6 +7,7 @@ from app.questionnaire import Location from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.utilities.schema import load_schema_from_name def test_correct_pointers(placholder_transform_pointers): @@ -248,6 +249,105 @@ def test_renders_text_plural_from_metadata(): assert rendered_text == "Yes, 100 people live here" +def test_renders_json_dynamic_answers( + placeholder_transform_question_dynamic_answers_json, +): + json_to_render = placeholder_transform_question_dynamic_answers_json + + answer_store = AnswerStore( + [ + {"answer_id": "any-supermarket-answer", "value": "Yes"}, + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "yWKpNY", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "vtbSnC", + }, + {"answer_id": "list-collector-answer", "value": "No"}, + ] + ) + + list_store = ListStore([{"items": ["yWKpNY", "vtbSnC"], "name": "supermarkets"}]) + + renderer = get_placeholder_render_dynamic_answers( + answer_store=answer_store, list_store=list_store + ) + rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) + rendered_label_first = rendered_schema["answers"][0]["label"] + rendered_id_first = rendered_schema["answers"][0]["id"] + rendered_label_second = rendered_schema["answers"][1]["label"] + rendered_id_second = rendered_schema["answers"][1]["id"] + + assert rendered_label_first == "Percentage of shopping at Tesco" + assert rendered_id_first == "percentage-of-shopping-yWKpNY" + assert rendered_label_second == "Percentage of shopping at Aldi" + assert rendered_id_second == "percentage-of-shopping-vtbSnC" + + +def test_renders_json_dynamic_answers_pointer( + placeholder_transform_question_dynamic_answers_pointer_json, +): + json_to_render = placeholder_transform_question_dynamic_answers_pointer_json + + answer_store = AnswerStore( + [ + {"answer_id": "any-supermarket-answer", "value": "Yes"}, + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "yWKpNY", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "vtbSnC", + }, + {"answer_id": "list-collector-answer", "value": "No"}, + ] + ) + + list_store = ListStore([{"items": ["yWKpNY", "vtbSnC"], "name": "supermarkets"}]) + + renderer = get_placeholder_render_dynamic_answers( + answer_store=answer_store, list_store=list_store + ) + rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) + rendered_label_first = rendered_schema["question"]["answers"][0]["label"] + rendered_id_first = rendered_schema["question"]["answers"][0]["id"] + rendered_label_second = rendered_schema["question"]["answers"][1]["label"] + rendered_id_second = rendered_schema["question"]["answers"][1]["id"] + + assert rendered_label_first == "Percentage of shopping at Tesco" + assert rendered_id_first == "percentage-of-shopping-yWKpNY" + assert rendered_label_second == "Percentage of shopping at Aldi" + assert rendered_id_second == "percentage-of-shopping-vtbSnC" + + +def get_placeholder_render_dynamic_answers( + *, + language="en", + answer_store=AnswerStore(), + list_store=ListStore(), + progress_store=ProgressStore(), + metadata=None, + response_metadata=None, +): + schema = load_schema_from_name("test_dynamic_answers_list_source") + return PlaceholderRenderer( + language=language, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=metadata or {}, + response_metadata=response_metadata or {}, + schema=schema, + ) + + def get_placeholder_render( *, language="en", diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index a658370323..03b09a3b77 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -26,6 +26,7 @@ def test_save_answers_with_form_data( answer_value = "1000" mock_empty_schema.get_answer_ids_for_question.return_value = [answer_id] + mock_empty_schema.get_answers_for_question_by_id.return_value = {answer_id: {}} form_data = {answer_id: answer_value} @@ -49,6 +50,96 @@ def test_save_answers_with_form_data( } +def test_update_dynamic_answers( + mock_location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, +): + mock_empty_schema.get_answer_ids_for_question.return_value = [ + "percentage-of-shopping-vhECeh" + ] + mock_empty_schema.answer_dependencies = { + "supermarket-name": { + AnswerDependent( + section_id="section", + block_id="dynamic-answer", + for_list="supermarkets", + answer_id="percentage-of-shopping", + ) + } + } + + mock_empty_schema.get_answers_for_question_by_id.return_value = { + "percentage-of-shopping": {} + } + + mock_questionnaire_store.answer_store = AnswerStore( + [ + {"answer_id": "any-supermarket-answer", "value": "Yes"}, + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "tUJzGV", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "vhECeh", + }, + {"answer_id": "list-collector-answer", "value": "No"}, + { + "answer_id": "percentage-of-shopping", + "value": 12, + "list_item_id": "tUJzGV", + }, + ] + ) + + form_data = {"percentage-of-shopping": 21} + + current_question = mock_empty_schema.get_block(mock_location.block_id)["question"] + + questionnaire_store_updater = QuestionnaireStoreUpdater( + mock_location, + mock_empty_schema, + mock_questionnaire_store, + mock_router, + current_question, + ) + questionnaire_store_updater._list_store = ( # pylint: disable=protected-access + ListStore([{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}]) + ) + questionnaire_store_updater.update_answers(form_data, list_item_id="vhECeh") + + assert mock_questionnaire_store.answer_store == AnswerStore( + [ + {"answer_id": "any-supermarket-answer", "value": "Yes"}, + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "tUJzGV", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "vhECeh", + }, + {"answer_id": "list-collector-answer", "value": "No"}, + { + "answer_id": "percentage-of-shopping", + "value": 12, + "list_item_id": "tUJzGV", + }, + { + "answer_id": "percentage-of-shopping", + "value": 21, + "list_item_id": "vhECeh", + }, + ] + ) + + def test_save_empty_answer_removes_existing_answer( mock_empty_schema, mock_empty_answer_store, mock_questionnaire_store, mock_router ): @@ -65,6 +156,8 @@ def test_save_empty_answer_removes_existing_answer( mock_empty_schema.get_answer_ids_for_question.return_value = [answer_id] + mock_empty_schema.get_answers_for_question_by_id.return_value = {answer_id: {}} + form_data = MultiDict({answer_id: answer_value}) current_question = mock_empty_schema.get_block(location.block_id)["question"] @@ -111,6 +204,7 @@ def test_default_answers_are_not_saved( mock_empty_schema.get_answers_by_answer_id.return_value = [ {"default": default_value} ] + mock_empty_schema.get_answers_for_question_by_id.return_value = {answer_id: {}} # No answer given so will use schema defined default form_data = MultiDict({answer_id: None}) @@ -144,6 +238,11 @@ def test_empty_answers( checkbox_answer_id, radio_answer_id, ] + mock_empty_schema.get_answers_for_question_by_id.return_value = { + string_answer_id: {}, + checkbox_answer_id: {}, + radio_answer_id: {}, + } form_data = { string_answer_id: "", @@ -515,6 +614,10 @@ def test_update_answers_captures_answer_dependencies( mock_schema.get_answer_ids_for_question.return_value = [answer_id] mock_schema.answer_dependencies = answer_dependencies mock_schema.is_answer_in_repeating_section.return_value = is_repeating + mock_schema.get_answers_for_question_by_id.return_value = { + "total-employees-answer": {}, + "total-turnover-answer": {}, + } mock_empty_answer_store.add_or_update.return_value = answer_updated form_data = MultiDict({answer_id: "some-value"}) @@ -598,6 +701,10 @@ def test_update_answers_with_answer_dependents( ) }, } + mock_schema.get_answers_for_question_by_id.return_value = { + "first-answer": {}, + "second-answer": {}, + } form_data = MultiDict({"first-answer": updated_answer_value}) @@ -683,6 +790,10 @@ def test_update_repeating_answers_with_answer_dependents( ) }, } + mock_schema.get_answers_for_question_by_id.return_value = { + "first-answer": {}, + "second-answer": {}, + } form_data = MultiDict({"first-answer": "answer updated"}) @@ -751,6 +862,10 @@ def test_answer_id_section_dependents( "first-answer": {"section-2"} } mock_router.is_path_complete.return_value = is_path_complete + mock_schema.get_answers_for_question_by_id.return_value = { + "first-answer": {}, + "second-answer": {}, + } answer_store = AnswerStore( [ @@ -845,6 +960,10 @@ def test_answer_id_section_dependents_repeating( mock_schema.when_rules_section_dependencies_by_answer = { "first-answer": {"section-2"} } + mock_schema.get_answers_for_question_by_id.return_value = { + "first-answer": {}, + "second-answer": {}, + } answer_store = AnswerStore( [ @@ -1008,6 +1127,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update ) # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then @@ -1053,6 +1173,7 @@ def test_dependent_sections_current_section_status_not_updated(mocker): ) # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then @@ -1094,6 +1215,7 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): questionnaire_store_updater.update_section_status = mocker.Mock() # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then @@ -1141,6 +1263,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): ) # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then @@ -1209,6 +1332,7 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta ) # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then @@ -1229,3 +1353,61 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta section_id=section_id, list_item_id="item-2", is_complete=False ), } + + +@pytest.mark.parametrize( + "dependent_section_status", + [CompletionStatus.IN_PROGRESS, CompletionStatus.COMPLETED], +) +def test_dependent_sections_added_dependant_block_removed( + dependent_section_status, mock_router +): + # Given + current_location = Location( + section_id="company-summary-section", block_id="total-turnover-block" + ) + progress_store = ProgressStore( + [ + { + "section_id": "company-summary-section", + "block_ids": ["total-turnover-block", "total-employees-block"], + "status": "COMPLETED", + }, + { + "section_id": "breakdown-section", + "block_ids": [ + "turnover-breakdown-block", + ], + "status": dependent_section_status, + }, + ], + ) + questionnaire_store_updater = get_questionnaire_store_updater( + current_location=current_location, + progress_store=progress_store, + router=mock_router, + ) + dependent_section_key = ("breakdown-section", None) + dependent_block_id = "turnover-breakdown-block" + + questionnaire_store_updater.dependent_block_id_by_section_key = { + dependent_section_key: {dependent_block_id} + } + + assert dependent_block_id in progress_store.get_completed_block_ids( + *dependent_section_key + ) + assert questionnaire_store_updater.dependent_sections == set() + + # When + questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() + + # Then + assert dependent_block_id not in progress_store.get_completed_block_ids( + *dependent_section_key + ) + assert questionnaire_store_updater.dependent_sections == { + DependentSection( + section_id="breakdown-section", list_item_id=None, is_complete=False + ) + } diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 23cfcc7a4d..aadf9d8862 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -1483,3 +1483,55 @@ def test_no_answers_codes_in_payload_when_no_questions_answered(version): # Then assert "answer_codes" not in data_payload + + +@pytest.mark.parametrize( + "version", + (AuthPayloadVersion.V2, None), +) +def test_payload_dynamic_answers(version): + questionnaire_store = get_questionnaire_store(version) + + full_routing_path = [ + RoutingPath( + ["any-supermarket", "list-collector", "dynamic-answer"], + section_id="section", + ) + ] + + questionnaire_store.answer_store = AnswerStore( + [ + Answer("any-supermarket-answer", "Yes", None).to_dict(), + Answer("supermarket-name", "Tesco", "tUJzGV").to_dict(), + Answer("supermarket-name", "Aldi", "vhECeh").to_dict(), + Answer("list-collector-answer", "No", None).to_dict(), + Answer("percentage-of-shopping", 12, "tUJzGV").to_dict(), + Answer("percentage-of-shopping", 21, "vhECeh").to_dict(), + ] + ) + + questionnaire_store.list_store = ListStore( + [{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}] + ) + + schema = load_schema_from_name("test_dynamic_answers_list_source") + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + questionnaire_store.progress_store, + ) + + # Then + assert ( + Answer(answer_id="percentage-of-shopping", value=12, list_item_id="tUJzGV") + in data_payload["answers"] + ) + assert ( + Answer(answer_id="percentage-of-shopping", value=21, list_item_id="vhECeh") + in data_payload["answers"] + ) diff --git a/tests/app/views/contexts/summary/test_block.py b/tests/app/views/contexts/summary/test_block.py index d290c8ac67..90beccc047 100644 --- a/tests/app/views/contexts/summary/test_block.py +++ b/tests/app/views/contexts/summary/test_block.py @@ -31,6 +31,7 @@ def test_create_block(mocker): location=location, return_to="final-summary", progress_store=ProgressStore(), + language="en", ) # Then diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index cf89714c80..364f28262c 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -2,11 +2,12 @@ import pytest from mock import MagicMock -from app.data_models import Answer, ProgressStore +from app.data_models import Answer, ListStore, ProgressStore from app.data_models.answer_store import AnswerStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver +from app.utilities.schema import load_schema_from_name from app.views.contexts.summary.question import Question @@ -128,11 +129,13 @@ def address_questionnaire_schema(concatenation_type): ) -def address_question(answer_store, list_store, schema): +def address_question(answer_store, list_store, progress_store, schema): question_schema = schema.get_questions("what-is-your-address-question")[0] return Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), location=None, @@ -141,6 +144,9 @@ def address_question(answer_store, list_store, schema): value_source_resolver=get_value_source_resolver( answer_store, list_store, schema ), + language="en", + metadata={}, + response_metadata={}, ) @@ -165,6 +171,7 @@ def test_create_question( expected_len, answer_store, list_store, + progress_store, mock_schema, ): # Given @@ -179,6 +186,8 @@ def test_create_question( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -187,6 +196,9 @@ def test_create_question( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -204,7 +216,11 @@ def test_create_question( ), ) def test_concatenate_textfield_answers( - concatenation_type, concatenation_character, list_store, answer_store + concatenation_type, + concatenation_character, + list_store, + progress_store, + answer_store, ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -216,7 +232,7 @@ def test_concatenate_textfield_answers( ): answer_store.add_or_update(answer) - question = address_question(answer_store, list_store, schema) + question = address_question(answer_store, list_store, progress_store, schema) # Then assert ( question.answers[0]["value"] @@ -234,7 +250,11 @@ def test_concatenate_textfield_answers( ), ) def test_concatenate_textfield_answers_default( - concatenation_type, concatenation_character, list_store, answer_store + concatenation_type, + concatenation_character, + list_store, + answer_store, + progress_store, ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -246,7 +266,7 @@ def test_concatenate_textfield_answers_default( answer_store.add_or_update(answer) # When - question = address_question(answer_store, list_store, schema) + question = address_question(answer_store, list_store, progress_store, schema) # Then assert ( @@ -265,7 +285,12 @@ def test_concatenate_textfield_answers_default( ), ) def test_concatenate_number_and_checkbox_answers( - concatenation_type, concatenation_character, list_store, answer_store, mock_schema + concatenation_type, + concatenation_character, + list_store, + answer_store, + progress_store, + mock_schema, ): # Given answer_store.add_or_update(Answer(answer_id="age", value=7)) @@ -303,6 +328,8 @@ def test_concatenate_number_and_checkbox_answers( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -311,6 +338,9 @@ def test_concatenate_number_and_checkbox_answers( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -322,7 +352,9 @@ def test_concatenate_number_and_checkbox_answers( @pytest.mark.usefixtures("app") -def test_merge_date_range_answers(answer_store, list_store, mock_schema): +def test_merge_date_range_answers( + answer_store, list_store, progress_store, mock_schema +): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value="13/02/2016")) answer_store.add_or_update(Answer(answer_id="answer_2", value="13/09/2016")) @@ -341,6 +373,8 @@ def test_merge_date_range_answers(answer_store, list_store, mock_schema): question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -349,6 +383,9 @@ def test_merge_date_range_answers(answer_store, list_store, mock_schema): location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -358,7 +395,9 @@ def test_merge_date_range_answers(answer_store, list_store, mock_schema): @pytest.mark.usefixtures("app") -def test_merge_multiple_date_range_answers(answer_store, list_store, mock_schema): +def test_merge_multiple_date_range_answers( + answer_store, list_store, progress_store, mock_schema +): # Given for answer in ( Answer(answer_id="answer_1", value="13/02/2016"), @@ -384,6 +423,8 @@ def test_merge_multiple_date_range_answers(answer_store, list_store, mock_schema question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -392,6 +433,9 @@ def test_merge_multiple_date_range_answers(answer_store, list_store, mock_schema location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -403,7 +447,9 @@ def test_merge_multiple_date_range_answers(answer_store, list_store, mock_schema @pytest.mark.usefixtures("app") -def test_create_question_with_multiple_answers(answer_store, list_store, mock_schema): +def test_create_question_with_multiple_answers( + answer_store, list_store, progress_store, mock_schema +): # Given for answer in ( Answer(answer_id="answer_1", value="Han"), @@ -425,6 +471,8 @@ def test_create_question_with_multiple_answers(answer_store, list_store, mock_sc question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -433,6 +481,9 @@ def test_create_question_with_multiple_answers(answer_store, list_store, mock_sc location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -442,7 +493,7 @@ def test_create_question_with_multiple_answers(answer_store, list_store, mock_sc @pytest.mark.usefixtures("app") -def test_checkbox_button_options(answer_store, list_store, mock_schema): +def test_checkbox_button_options(answer_store, list_store, progress_store, mock_schema): # Given answer_store.add_or_update( Answer(answer_id="answer_1", value=["Light Side", "Dark Side"]) @@ -469,6 +520,8 @@ def test_checkbox_button_options(answer_store, list_store, mock_schema): question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -477,6 +530,9 @@ def test_checkbox_button_options(answer_store, list_store, mock_schema): location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -486,7 +542,9 @@ def test_checkbox_button_options(answer_store, list_store, mock_schema): @pytest.mark.usefixtures("app") -def test_checkbox_button_detail_answer_empty(answer_store, list_store, mock_schema): +def test_checkbox_button_detail_answer_empty( + answer_store, list_store, progress_store, mock_schema +): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value=["other", ""])) @@ -515,6 +573,8 @@ def test_checkbox_button_detail_answer_empty(answer_store, list_store, mock_sche question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -523,6 +583,9 @@ def test_checkbox_button_detail_answer_empty(answer_store, list_store, mock_sche location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -576,6 +639,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( expected_value, answer_store, list_store, + progress_store, mock_schema, ): # Given @@ -600,6 +664,8 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -608,6 +674,9 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -616,7 +685,9 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( @pytest.mark.usefixtures("app") -def test_checkbox_button_other_option_text(answer_store, list_store, mock_schema): +def test_checkbox_button_other_option_text( + answer_store, list_store, progress_store, mock_schema +): # Given answer_store.add_or_update( Answer(answer_id="answer_1", value=["Light Side", "other"]) @@ -648,6 +719,8 @@ def test_checkbox_button_other_option_text(answer_store, list_store, mock_schema question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -656,6 +729,9 @@ def test_checkbox_button_other_option_text(answer_store, list_store, mock_schema location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -702,7 +778,14 @@ def test_checkbox_button_other_option_text(answer_store, list_store, mock_schema ), ) def test_radio_answer_with_detail_answers_returns_correct_value( - answer_type, options, answers, expected, answer_store, list_store, mock_schema + answer_type, + options, + answers, + expected, + answer_store, + list_store, + progress_store, + mock_schema, ): # Given for answer in answers: @@ -726,6 +809,8 @@ def test_radio_answer_with_detail_answers_returns_correct_value( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -734,6 +819,9 @@ def test_radio_answer_with_detail_answers_returns_correct_value( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -774,7 +862,14 @@ def test_radio_answer_with_detail_answers_returns_correct_value( ), ) def test_answer_types_selected_option_label( - answer_type, options, answers, expected, answer_store, list_store, mock_schema + answer_type, + options, + answers, + expected, + answer_store, + list_store, + progress_store, + mock_schema, ): for answer in answers: answer_store.add_or_update(answer) @@ -797,6 +892,8 @@ def test_answer_types_selected_option_label( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), value_source_resolver=get_value_source_resolver( @@ -805,6 +902,9 @@ def test_answer_types_selected_option_label( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -813,7 +913,7 @@ def test_answer_types_selected_option_label( @pytest.mark.usefixtures("app") def test_dynamic_checkbox_answer_options( - answer_store, list_store, mock_schema, dynamic_answer_options_schema + answer_store, list_store, progress_store, mock_schema, dynamic_answer_options_schema ): # Given answer_schema = { @@ -839,6 +939,8 @@ def test_dynamic_checkbox_answer_options( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator( answer_store, list_store, mock_schema, response_metadata @@ -849,6 +951,9 @@ def test_dynamic_checkbox_answer_options( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -890,6 +995,7 @@ def test_dynamic_checkbox_answer_options( def test_dynamic_answer_options( answer_type, list_store, + progress_store, answer_store_value, expected, dynamic_answer_options_schema, @@ -911,6 +1017,8 @@ def test_dynamic_answer_options( question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=mock_schema, rule_evaluator=get_rule_evaluator( answer_store, list_store, mock_schema, response_metadata @@ -921,6 +1029,9 @@ def test_dynamic_answer_options( location=None, block_id="house-type", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then @@ -963,7 +1074,7 @@ def test_dynamic_answer_options( ), ), ) -def test_get_answer(answer_schema, answer_store, expected, list_store): +def test_get_answer(answer_schema, answer_store, expected, list_store, progress_store): schema = address_questionnaire_schema("Newline") # Given @@ -973,6 +1084,8 @@ def test_get_answer(answer_schema, answer_store, expected, list_store): question = Question( question_schema, answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, schema=schema, rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), value_source_resolver=get_value_source_resolver( @@ -981,7 +1094,162 @@ def test_get_answer(answer_schema, answer_store, expected, list_store): location=None, block_id="address-group", return_to=None, + language="en", + metadata={}, + response_metadata={}, ) # Then assert question.get_answer(answer_store, "building") == expected + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize( + "answer_store, list_store, expected", + ( + ( + AnswerStore( + [ + {"answer_id": "any-supermarket-answer", "value": "Yes"}, + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "awTNTI", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "FMOByU", + }, + {"answer_id": "list-collector-answer", "value": "No"}, + { + "answer_id": "based-checkbox-answer", + "value": ["Non UK based supermarkets"], + }, + { + "answer_id": "percentage-of-shopping", + "value": 12, + "list_item_id": "awTNTI", + }, + { + "answer_id": "percentage-of-shopping", + "value": 21, + "list_item_id": "FMOByU", + }, + ], + ), + ListStore([{"items": ["awTNTI", "FMOByU"], "name": "supermarkets"}]), + [ + { + "currency": None, + "id": "percentage-of-shopping-awTNTI", + "label": "Percentage of shopping at Tesco", + "link": "/questionnaire/group/?list_item_id=awTNTI#percentage-of-shopping-awTNTI", + "type": "percentage", + "unit": None, + "unit_length": None, + "value": 12, + }, + { + "currency": None, + "id": "percentage-of-shopping-FMOByU", + "label": "Percentage of shopping at Aldi", + "link": "/questionnaire/group/?list_item_id=FMOByU#percentage-of-shopping-FMOByU", + "type": "percentage", + "unit": None, + "unit_length": None, + "value": 21, + }, + { + "currency": None, + "id": "based-checkbox-answer", + "label": "Are supermarkets UK or non UK based?", + "link": "/questionnaire/group/#based-checkbox-answer", + "type": "checkbox", + "unit": None, + "unit_length": None, + "value": [ + { + "detail_answer_value": None, + "label": "Non UK based supermarkets", + } + ], + }, + ], + ), + ), +) +def test_dynamic_answers(answer_store, list_store, progress_store, expected): + schema = load_schema_from_name("test_dynamic_answers_list_source", "en") + + # Given + question_schema = { + "dynamic_answers": { + "values": {"source": "list", "identifier": "supermarkets"}, + "answers": [ + { + "label": { + "text": "Percentage of shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name", + }, + } + ], + }, + "id": "percentage-of-shopping", + "mandatory": False, + "type": "Percentage", + "maximum": {"value": 100}, + "decimal_places": 0, + } + ], + }, + "answers": [ + { + "id": "based-checkbox-answer", + "label": "Are supermarkets UK or non UK based?", + "instruction": "Select any answers that apply", + "mandatory": False, + "options": [ + { + "label": "UK based supermarkets", + "value": "UK based supermarkets", + }, + { + "label": "Non UK based supermarkets", + "value": "Non UK based supermarkets", + }, + ], + "type": "Checkbox", + } + ], + "id": "dynamic-answer-question", + "title": "What percent of your shopping do you do at each of the following supermarket?", + "type": "General", + } + + # When + question = Question( + question_schema, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + schema=schema, + rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), + value_source_resolver=get_value_source_resolver( + answer_store, list_store, schema + ), + location=None, + block_id="group", + return_to=None, + language="en", + metadata={}, + response_metadata={}, + ) + + # Then + assert question.answers == expected diff --git a/tests/app/views/handlers/test_question_with_dynamic_answers.py b/tests/app/views/handlers/test_question_with_dynamic_answers.py new file mode 100644 index 0000000000..4e771b7788 --- /dev/null +++ b/tests/app/views/handlers/test_question_with_dynamic_answers.py @@ -0,0 +1,113 @@ +from datetime import datetime, timezone + +import pytest +from freezegun import freeze_time + +from app.data_models import AnswerStore, ListStore, QuestionnaireStore +from app.questionnaire import Location +from app.utilities.schema import load_schema_from_name +from app.views.handlers.question import Question + +from .conftest import set_storage_data + + +@pytest.mark.usefixtures("app") +@freeze_time("2022-06-01T15:34:54+00:00") +def test_question_with_dynamic_answers(storage, language, mocker): + submitted_at = datetime.now(timezone.utc) + set_storage_data(storage, submitted_at=submitted_at) + + questionnaire_store = QuestionnaireStore(storage) + questionnaire_store.answer_store = AnswerStore( + [ + { + "answer_id": "supermarket-name", + "value": "Tesco", + "list_item_id": "tUJzGV", + }, + { + "answer_id": "supermarket-name", + "value": "Aldi", + "list_item_id": "vhECeh", + }, + ] + ) + questionnaire_store.list_store = ListStore( + [{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}] + ) + schema = load_schema_from_name("test_dynamic_answers_list_source") + + mocker.patch( + "app.views.handlers.question.Question.is_location_valid", + return_value=True, + ) + question = Question( + current_location=Location(section_id="section", block_id="dynamic-answer"), + form_data=None, + language=language, + questionnaire_store=questionnaire_store, + request_args=mocker.MagicMock(), + schema=schema, + ) + + form = question.form + question.handle_post() + + assert form.question["answers"] == [ + { + "decimal_places": 0, + "id": "percentage-of-shopping-tUJzGV", + "label": "Percentage of shopping at Tesco", + "list_item_id": "tUJzGV", + "mandatory": False, + "maximum": {"value": 100}, + "original_answer_id": "percentage-of-shopping", + "type": "Percentage", + }, + { + "decimal_places": 0, + "id": "percentage-of-shopping-vhECeh", + "label": "Percentage of shopping at Aldi", + "list_item_id": "vhECeh", + "mandatory": False, + "maximum": {"value": 100}, + "original_answer_id": "percentage-of-shopping", + "type": "Percentage", + }, + { + "decimal_places": 0, + "id": "days-a-week-tUJzGV", + "label": "How many days a week you shop at Tesco", + "list_item_id": "tUJzGV", + "mandatory": False, + "maximum": {"value": 7}, + "minimum": {"value": 1}, + "original_answer_id": "days-a-week", + "type": "Number", + }, + { + "decimal_places": 0, + "id": "days-a-week-vhECeh", + "label": "How many days a week you shop at Aldi", + "list_item_id": "vhECeh", + "mandatory": False, + "maximum": {"value": 7}, + "minimum": {"value": 1}, + "original_answer_id": "days-a-week", + "type": "Number", + }, + { + "id": "based-checkbox-answer", + "instruction": "Select any answers that apply", + "label": "Are supermarkets UK or non UK based?", + "mandatory": False, + "options": [ + {"label": "UK based supermarkets", "value": "UK based supermarkets"}, + { + "label": "Non UK based supermarkets", + "value": "Non UK based supermarkets", + }, + ], + "type": "Checkbox", + }, + ] diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 7473435ccd..b20998f7df 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -472,11 +472,15 @@ def process_question(question, page_spec, num_questions, page_name): for answer in question.get("answers", []): process_answer(answer, page_spec, long_names, page_name) - question_or_answer_id = ( - question["id"] - if question["type"] in ["DateRange", "MutuallyExclusive"] - else question["answers"][0]["id"] - ) + try: + question_or_answer_id = ( + question["id"] + if question["type"] in ["DateRange", "MutuallyExclusive"] + else question.get("answers", [])[0].get("id", None) + ) + except IndexError: + question_or_answer_id = None + question_name = generate_pascal_case_from_id(question["id"]).replace(page_name, "") question_context = { "questionName": camel_case(question_name), diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js new file mode 100644 index 0000000000..de81a2f98a --- /dev/null +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -0,0 +1,201 @@ +import DriverPage from "../../generated_pages/dynamic_answers_list_source/any-supermarket.page"; +import DynamicAnswerPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer.page"; +import DynamicAnswerOnlyPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer-only.page"; +import ListCollectorPage from "../../generated_pages/dynamic_answers_list_source/list-collector.page"; +import ListCollectorAddPage from "../../generated_pages/dynamic_answers_list_source/list-collector-add.page"; +import ListCollectorRemovePage from "../../generated_pages/dynamic_answers_list_source/list-collector-remove.page"; +import SetMinimumPage from "../../generated_pages/dynamic_answers_list_source/minimum-spending.page"; +import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/section-summary.page"; + +describe("Dynamic answers list value source", () => { + const labels = 'label[class="ons-label"]'; + const percentageInputs = 'input[class="ons-input ons-input--text ons-input-type__input ons-input-number--w-3"]'; + const numberInputs = 'input[class="ons-input ons-input--text ons-input-type__input ons-input-number--w-1"]'; + const group = 'div[id="group-2"]'; + const summaryTitles = 'dt[class="ons-summary__item-title"]'; + const summaryValues = 'dd[class="ons-summary__values"]'; + const summaryActions = 'dd[class="ons-summary__actions"]'; + beforeEach("Load the survey", async () => { + await browser.openQuestionnaire("test_dynamic_answers_list_source.json"); + }); + + it("Given list items have been added, When the dynamic answers are displayed, Then the correct answers should be visible", async () => { + await addTwoSupermarkets(); + await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $$(labels).length).to.equal(4); + }); + it("Given list items have been added, When additional items are added using add link, Then the correct dynamic answers are displayed", async () => { + await $(DriverPage.yes()).click(); + await $(DriverPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(labels).length).to.equal(2); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.supermarketsListAddLink()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $$(labels).length).to.equal(4); + }); + it("Given list items have been added and the dynamic answers are submitted, When the summary is displayed, Then the correct answers should be visible and have correct values", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $$(numberInputs)[0].setValue(3); + await $$(numberInputs)[1].setValue(7); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(group).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(group).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(group).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(group).$$(summaryTitles).length).to.equal(8); + await expect(await $(group).$$(summaryValues).length).to.equal(8); + }); + it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are revisited, Then they should be visible and have correct values", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.previous()).click(); + await $(DynamicAnswerOnlyPage.previous()).click(); + await $(SetMinimumPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(percentageInputs)[0].getValue()).to.equal("12"); + await expect(await $$(percentageInputs)[1].getValue()).to.equal("21"); + await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); + }); + it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with different values, Then they should be displayed correctly on summary", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.previous()).click(); + await $(DynamicAnswerOnlyPage.previous()).click(); + await $(SetMinimumPage.previous()).click(); + await $$(percentageInputs)[0].setValue(21); + await $$(percentageInputs)[1].setValue(12); + await $(DynamicAnswerPage.submit()).click(); + await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("12%"); + }); + it("Given list items have been added and the dynamic answers are submitted, When the summary edit answer link is used for dynamic answer, Then the focus is on correct answer option", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(group).$$(summaryActions)[0].$("a").click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(percentageInputs)[0].isFocused()).to.be.true; + await $(DynamicAnswerPage.submit()).click(); + await $(group).$$(summaryActions)[1].$("a").click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(percentageInputs)[1].isFocused()).to.be.true; + }); + it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with answers updated, Then they should be displayed correctly on summary", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(group).$$(summaryActions)[0].$("a").click(); + await $$(percentageInputs)[0].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); + }); + it("Given list items have been added and the dynamic answers are submitted, When the list items are removed and answers updated, Then they should be displayed correctly on summary", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); + await $(ListCollectorRemovePage.yes()).click(); + await $(ListCollectorRemovePage.submit()).click(); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(group).$$(summaryTitles).length).to.equal(5); + await expect(await $(group).$$(summaryValues).length).to.equal(5); + }); + it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No', Then after changing answer to 'Yes' all answers should re-appear on summary", async () => { + await addTwoSupermarkets(); + await $$(percentageInputs)[0].setValue(12); + await $$(percentageInputs)[1].setValue(21); + await $$(numberInputs)[0].setValue(3); + await $$(numberInputs)[1].setValue(7); + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); + await $(DriverPage.no()).click(); + await $(DriverPage.submit()).click(); + await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.false; + await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.false; + await expect(await $(group).isExisting()).to.be.false; + await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); + await $(DriverPage.yes()).click(); + await $(DriverPage.submit()).click(); + await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.true; + await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.true; + await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(group).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(group).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(group).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(group).$$(summaryTitles).length).to.equal(8); + await expect(await $(group).$$(summaryValues).length).to.equal(8); + }); +}); + +async function addTwoSupermarkets() { + await $(DriverPage.yes()).click(); + await $(DriverPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); +} From 08b030406a1312fa3ed40463645febad749d07b8 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 18 May 2023 10:52:36 +0100 Subject: [PATCH 225/567] Make response expiry date mandatory (#1104) --- app/data_models/metadata_proxy.py | 2 +- app/data_models/questionnaire_store.py | 6 ++---- app/utilities/metadata_parser.py | 2 +- app/utilities/metadata_parser_v2.py | 2 +- tests/app/data_model/conftest.py | 6 +++++- tests/app/data_model/test_metadata_proxy.py | 6 ++++++ tests/app/parser/conftest.py | 10 ++++++++++ tests/app/submitter/conftest.py | 3 +++ tests/app/views/handlers/conftest.py | 6 ++++++ .../test_individual_response_fulfilment_request.py | 2 ++ .../handlers/test_question_with_dynamic_answers.py | 2 ++ tests/functional/jwt_helper.js | 5 +++++ tests/integration/create_token.py | 2 ++ tests/integration/routes/test_errors.py | 2 ++ tests/integration/routes/test_jwt_authentication.py | 2 ++ tests/integration/test_flush_data.py | 2 ++ 16 files changed, 52 insertions(+), 8 deletions(-) diff --git a/app/data_models/metadata_proxy.py b/app/data_models/metadata_proxy.py index 3eed9a85f5..28e12dee3d 100644 --- a/app/data_models/metadata_proxy.py +++ b/app/data_models/metadata_proxy.py @@ -49,11 +49,11 @@ class MetadataProxy: case_id: str collection_exercise_sid: str response_id: str + response_expires_at: datetime survey_metadata: Optional[SurveyMetadata] = None schema_url: Optional[str] = None schema_name: Optional[str] = None language_code: Optional[str] = None - response_expires_at: Optional[datetime] = None channel: Optional[str] = None region_code: Optional[str] = None version: Optional[AuthPayloadVersion] = None diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index f4352112c7..1848a3d4b2 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -93,12 +93,10 @@ def save(self) -> None: collection_exercise_sid = ( self.collection_exercise_sid or self._metadata["collection_exercise_sid"] ) - response_expires_at = self._metadata.get("response_expires_at") + response_expires_at = self._metadata["response_expires_at"] self._storage.save( data=data, collection_exercise_sid=collection_exercise_sid, submitted_at=self.submitted_at, - expires_at=parse_iso_8601_datetime(response_expires_at) - if response_expires_at - else None, + expires_at=parse_iso_8601_datetime(response_expires_at), ) diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index 537adae896..f0d2681aa1 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -65,7 +65,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): ) # type:ignore case_type = VALIDATORS["string"](required=False) # type:ignore response_expires_at = VALIDATORS["iso_8601_date_string"]( - required=False, + required=True, validate=lambda x: parse_iso_8601_datetime(x) > datetime.now(tz=timezone.utc), ) # type:ignore diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 366f997435..51fa8eaa8d 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -89,7 +89,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): required=False, validate=validate.Length(min=1) ) # type:ignore response_expires_at = VALIDATORS["iso_8601_date_string"]( - required=False, + required=True, validate=lambda x: parse_iso_8601_datetime(x) > datetime.now(tz=timezone.utc), ) # type:ignore region_code = VALIDATORS["string"]( diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index 50905ab2f7..4d71f6623d 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -6,6 +6,7 @@ from app.data_models.progress_store import CompletionStatus from app.data_models.session_store import SessionStore from app.storage import storage_encryption +from tests.app.parser.conftest import get_response_expires_at @pytest.fixture @@ -81,7 +82,10 @@ def store_to_serialize(answer_store): @pytest.fixture def basic_input(): return { - "METADATA": {"test": True}, + "METADATA": { + "test": True, + "response_expires_at": get_response_expires_at(), + }, "ANSWERS": [{"answer_id": "test", "value": "test"}], "LISTS": [], "PROGRESS": [ diff --git a/tests/app/data_model/test_metadata_proxy.py b/tests/app/data_model/test_metadata_proxy.py index 73804f5860..79dce605c7 100644 --- a/tests/app/data_model/test_metadata_proxy.py +++ b/tests/app/data_model/test_metadata_proxy.py @@ -12,6 +12,7 @@ "tx_id": "tx_id", "collection_exercise_sid": "collection_exercise_sid", "case_id": "case_id", + "response_expires_at": "2023-04-24T10:46:32+00:00", } METADATA_V2 = { @@ -22,6 +23,7 @@ "tx_id": "tx_id", "collection_exercise_sid": "collection_exercise_sid", "case_id": "case_id", + "response_expires_at": "2023-04-24T10:46:32+00:00", "survey_metadata": { "data": { "ru_ref": "432423423423", @@ -46,6 +48,10 @@ MetadataProxy.from_dict(METADATA_V2)["schema_name"], METADATA_V2["schema_name"], ), + ( + MetadataProxy.from_dict(METADATA_V2)["response_expires_at"], + METADATA_V2["response_expires_at"], + ), (MetadataProxy.from_dict(METADATA_V1)["non_existing"], None), (MetadataProxy.from_dict(METADATA_V2)["non_existing"], None), ), diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index d408e09103..ab7bbc6e26 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -1,5 +1,6 @@ # pylint: disable=redefined-outer-name import uuid +from datetime import datetime, timedelta, timezone import pytest @@ -37,6 +38,7 @@ def fake_metadata_runner(): "response_id": str(uuid.uuid4()), "account_service_url": "https://ras.ons.gov.uk", "case_id": str(uuid.uuid4()), + "response_expires_at": get_response_expires_at(), } @@ -48,6 +50,7 @@ def fake_business_metadata_runner(): metadata["eq_id"] = "mbs" metadata["form_type"] = "0253" + metadata["response_expires_at"] = get_response_expires_at() return metadata @@ -67,6 +70,7 @@ def fake_metadata_full(): "return_by": "2016-07-07", "case_ref": "1000000000000001", "case_id": str(uuid.uuid4()), + "response_expires_at": get_response_expires_at(), } return dict(fake_metadata_runner(), **fake_questionnaire_claims) @@ -84,6 +88,7 @@ def fake_metadata_runner_v2(): "case_id": str(uuid.uuid4()), "version": AuthPayloadVersion.V2.value, "survey_metadata": {"data": {"key": "value"}}, + "response_expires_at": get_response_expires_at(), } @@ -103,6 +108,7 @@ def fake_metadata_full_v2_business(): "case_ref": "1000000000000001", "ru_ref": "123456789", "form_type": "I", + "response_expires_at": get_response_expires_at(), } metadata = fake_metadata_runner_v2() @@ -140,3 +146,7 @@ def fake_questionnaire_metadata_requirements_full(): {"name": "ref_p_end_date", "type": "string"}, {"name": "account_service_url", "type": "url", "optional": True}, ] + + +def get_response_expires_at() -> str: + return (datetime.now(tz=timezone.utc) + timedelta(days=1)).isoformat() diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 67a15ef3e1..c92d9da656 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -15,6 +15,7 @@ from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter import RabbitMQSubmitter +from tests.app.parser.conftest import get_response_expires_at METADATA_V1 = MetadataProxy.from_dict( { @@ -39,6 +40,7 @@ "display_address": "68 Abingdon Road, Goathill", "case_ref": "1000000000000001", "jti": str(uuid.uuid4()), + "response_expires_at": get_response_expires_at(), } ) @@ -69,6 +71,7 @@ "region_code": "GB-ENG", "channel": "RH", "jti": str(uuid.uuid4()), + "response_expires_at": get_response_expires_at(), } ) diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index 003f9e973a..6fde601054 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -11,6 +11,7 @@ from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore from app.questionnaire import QuestionnaireSchema +from tests.app.parser.conftest import get_response_expires_at time_to_freeze = datetime.now(timezone.utc).replace(second=0, microsecond=0) tx_id = str(uuid.uuid4()) @@ -39,6 +40,7 @@ channel = "H" case_ref = "1000000000000001" region_code = "GB_WLS" +response_expires_at = get_response_expires_at() @pytest.fixture @@ -126,6 +128,7 @@ def metadata(): "region_code": region_code, "case_id": case_id, "language_code": language_code, + "response_expires_at": response_expires_at, } ) @@ -143,6 +146,7 @@ def metadata_v2(): "channel": channel, "region_code": region_code, "account_service_url": "account_service_url", + "response_expires_at": get_response_expires_at(), "survey_metadata": { "data": { "period_id": period_id, @@ -210,6 +214,7 @@ def mock_questionnaire_store(mocker): "schema_name": schema_name, "account_service_url": "account_service_url", "response_id": "response_id", + "response_expires_at": get_response_expires_at(), } ) return questionnaire_store @@ -231,6 +236,7 @@ def mock_questionnaire_store_v2(mocker): "channel": channel, "region_code": region_code, "account_service_url": "account_service_url", + "response_expires_at": get_response_expires_at(), "survey_metadata": { "data": { "period_id": period_id, diff --git a/tests/app/views/handlers/test_individual_response_fulfilment_request.py b/tests/app/views/handlers/test_individual_response_fulfilment_request.py index ae295f8d3f..ff9f6c8007 100644 --- a/tests/app/views/handlers/test_individual_response_fulfilment_request.py +++ b/tests/app/views/handlers/test_individual_response_fulfilment_request.py @@ -14,6 +14,7 @@ GB_WLS_REGION_CODE, IndividualResponseFulfilmentRequest, ) +from tests.app.parser.conftest import get_response_expires_at DUMMY_MOBILE_NUMBER = "07700900258" @@ -27,6 +28,7 @@ def test_sms_fulfilment_request_payload(): response_id="response_id", account_service_url="account_service_url", collection_exercise_sid="collection_exercise_sid", + response_expires_at=get_response_expires_at(), ) fulfilment_request = IndividualResponseFulfilmentRequest( diff --git a/tests/app/views/handlers/test_question_with_dynamic_answers.py b/tests/app/views/handlers/test_question_with_dynamic_answers.py index 4e771b7788..22beb7ffbc 100644 --- a/tests/app/views/handlers/test_question_with_dynamic_answers.py +++ b/tests/app/views/handlers/test_question_with_dynamic_answers.py @@ -8,6 +8,7 @@ from app.utilities.schema import load_schema_from_name from app.views.handlers.question import Question +from ...parser.conftest import get_response_expires_at from .conftest import set_storage_data @@ -35,6 +36,7 @@ def test_question_with_dynamic_answers(storage, language, mocker): questionnaire_store.list_store = ListStore( [{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}] ) + questionnaire_store.set_metadata({"response_expires_at": get_response_expires_at()}) schema = load_schema_from_name("test_dynamic_answers_list_source") mocker.patch( diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index 678b7d8d8d..75f458fda9 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -90,6 +90,9 @@ export function generateToken( const iat = KJUR.jws.IntDate.get("now"); const exp = KJUR.jws.IntDate.get("now") + 1800; const caseId = uuidv4(); + const currentDate = new Date(); + currentDate.setUTCDate(currentDate.getUTCDate() + 1); + const isoDate = currentDate.toISOString(); if (version === "v2") { payload = { @@ -106,6 +109,7 @@ export function generateToken( account_service_url: "http://localhost:8000", survey_metadata: getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr), version: "v2", + response_expires_at: isoDate, }; } else { payload = { @@ -131,6 +135,7 @@ export function generateToken( region_code: regionCode, language_code: languageCode, account_service_url: "http://localhost:8000", + response_expires_at: isoDate, }; } diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 1c88bf92bb..c611ad9918 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -5,6 +5,7 @@ from app.authentication.auth_payload_version import AuthPayloadVersion from app.keys import KEY_PURPOSE_AUTHENTICATION +from tests.app.parser.conftest import get_response_expires_at ACCOUNT_SERVICE_URL = "http://upstream.url" @@ -88,6 +89,7 @@ def _get_payload_with_params( payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now payload_vars["jti"] = str(uuid4()) payload_vars["case_id"] = str(uuid4()) + payload_vars["response_expires_at"] = get_response_expires_at() for key, value in extra_payload.items(): payload_vars[key] = value diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index db4414e8cb..4d5d2502ac 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -6,6 +6,7 @@ ACCOUNT_SERVICE_BASE_URL_SOCIAL, ONS_URL, ) +from tests.app.parser.conftest import get_response_expires_at from tests.integration.create_token import ACCOUNT_SERVICE_URL from tests.integration.integration_test_case import IntegrationTestCase @@ -31,6 +32,7 @@ class TestErrors(IntegrationTestCase): # pylint: disable=too-many-public-method "language_code": "en", "account_service_url": "http://correct.place", "roles": [], + "response_expires_at": get_response_expires_at(), } def test_errors_404(self): diff --git a/tests/integration/routes/test_jwt_authentication.py b/tests/integration/routes/test_jwt_authentication.py index ff15a28325..0d1a9eff39 100644 --- a/tests/integration/routes/test_jwt_authentication.py +++ b/tests/integration/routes/test_jwt_authentication.py @@ -13,6 +13,7 @@ TEST_DO_NOT_USE_SR_PUBLIC_KEY, TEST_DO_NOT_USE_UPSTREAM_PRIVATE_KEY, ) +from tests.app.parser.conftest import get_response_expires_at from tests.integration.app_context_test_case import AppContextTestCase from tests.integration.integration_test_case import ( EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID, @@ -85,6 +86,7 @@ def create_payload(): "ru_name": "Test", "return_by": "2016-09-09", "account_service_url": "http://upstream.url/", + "response_expires_at": get_response_expires_at(), } diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 3ca7de5d95..9fa232c81e 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -3,6 +3,7 @@ from mock import patch +from tests.app.parser.conftest import get_response_expires_at from tests.integration.integration_test_case import IntegrationTestCase @@ -179,6 +180,7 @@ def test_flush_data_successful_v2( "lists": [], }, "started_at": "2023-02-07T11:42:32.380784+00:00", + "response_expires_at": get_response_expires_at(), } self.launchSurveyV2("test_textfield") form_data = {"name-answer": "Joe Bloggs"} From 18d09f497485e5f5e56e5ab3f528b80a8974dc3f Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 18 May 2023 15:40:32 +0100 Subject: [PATCH 226/567] Bind additional contexts to flush requests (#1108) --- app/routes/flush.py | 9 +++++- tests/integration/test_flush_data.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/app/routes/flush.py b/app/routes/flush.py index 2ff8897a86..28cb2093b2 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -55,7 +55,14 @@ def flush_data() -> Response: user = _get_user(decrypted_token["response_id"]) if metadata := get_metadata(user): - contextvars.bind_contextvars(tx_id=metadata.tx_id) + contextvars.bind_contextvars( + tx_id=metadata.tx_id, + ce_id=metadata.collection_exercise_sid, + ) + if schema_name := metadata.schema_name: + contextvars.bind_contextvars(schema_name=schema_name) + if schema_url := metadata.schema_url: + contextvars.bind_contextvars(schema_url=schema_url) if _submit_data(user): return Response(status=200) return Response(status=404) diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 9fa232c81e..121c9ad9e1 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -1,11 +1,15 @@ import time import uuid +from httmock import HTTMock, urlmatch from mock import patch +from app.utilities.schema import get_schema_path_map from tests.app.parser.conftest import get_response_expires_at from tests.integration.integration_test_case import IntegrationTestCase +SCHEMA_PATH_MAP = get_schema_path_map(include_test_schemas=True) + class TestFlushData(IntegrationTestCase): def setUp(self): @@ -30,6 +34,14 @@ def tearDown(self): super().tearDown() + @staticmethod + @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") + def schema_url_mock(_url, _request): + schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textfield"] + + with open(schema_path, encoding="utf8") as json_data: + return json_data.read() + def test_flush_data_successful(self): self.post( url="/flush?token=" @@ -193,3 +205,38 @@ def test_flush_data_successful_v2( self.assertStatusOK() mock_convert_answers_v2.assert_called_once() mock_convert_answers.assert_not_called() + + def test_flush_logs_output(self): + with self.assertLogs() as logs: + self.post( + url=f"/flush?token={self.token_generator.create_token(schema_name='test_textfield', payload=self.get_payload())}" + ) + + flush_log = logs.output[5] + + self.assertIn("successfully flushed answers", flush_log) + self.assertIn("tx_id", flush_log) + self.assertIn("ce_id", flush_log) + self.assertIn("schema_name", flush_log) + self.assertNotIn("schema_url", flush_log) + + def test_flush_logs_output_schema_url(self): + schema_url = "http://eq-survey-register.url/my-test-schema" + token = self.token_generator.create_token_with_schema_url( + "test_textfield", schema_url + ) + with HTTMock(self.schema_url_mock): + self.get(url=f"/session?token={token}") + self.assertStatusOK() + with self.assertLogs() as logs: + self.post( + url=f"/flush?token={self.token_generator.create_token_with_schema_url('test_textfield', schema_url, payload=self.get_payload())}" + ) + + flush_log = logs.output[6] + + self.assertIn("successfully flushed answers", flush_log) + self.assertIn("tx_id", flush_log) + self.assertIn("ce_id", flush_log) + self.assertIn("schema_name", flush_log) + self.assertIn("schema_url", flush_log) From cee23dc0833e88ab6d85eb46ef9974878db09f7f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 23 May 2023 10:16:52 +0100 Subject: [PATCH 227/567] Schemas v3.56.0 (#1110) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 304e303931..436ebfe052 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.55.0 +v3.56.0 From 36fb6c7bb45d04c9cdf363a6169d4c6d258e4039 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 25 May 2023 13:35:41 +0100 Subject: [PATCH 228/567] Schemas v3.57.0 (#1113) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 436ebfe052..5af1c91d2a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.56.0 +v3.57.0 From fdc0f756efcaaf9e92a4b41f4763475d06cbc70f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 26 May 2023 08:02:18 +0100 Subject: [PATCH 229/567] Schemas v3.57.1 (#1115) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5af1c91d2a..6337654f6b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.57.0 +v3.57.1 From c2439d82a0a1884054bcda846c61878d5834ab3e Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 26 May 2023 12:12:03 +0100 Subject: [PATCH 230/567] Schemas v3.58.0 (#1116) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6337654f6b..6d07408b02 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.57.1 +v3.58.0 From cc12488abd2b2fa636005a99ba4c8b0987e8e2cd Mon Sep 17 00:00:00 2001 From: Guilhem <122792081+ONS-Guilhem-Forey@users.noreply.github.com> Date: Fri, 26 May 2023 14:16:47 +0100 Subject: [PATCH 231/567] Implement "progress" value source (#1044) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.c --- Pipfile | 3 +- Pipfile.lock | 1734 +++++++++-------- app/data_models/progress_store.py | 33 +- app/jinja_filters.py | 17 +- app/publisher/publisher.py | 3 +- app/questionnaire/dependencies.py | 3 +- app/questionnaire/path_finder.py | 12 +- app/questionnaire/placeholder_parser.py | 4 +- app/questionnaire/placeholder_transforms.py | 10 +- app/questionnaire/questionnaire_schema.py | 241 ++- .../questionnaire_store_updater.py | 190 +- app/questionnaire/router.py | 7 +- app/questionnaire/rules/rule_evaluator.py | 2 +- app/questionnaire/value_source_resolver.py | 89 +- app/survey_config/business_config.py | 2 +- app/survey_config/social_survey_config.py | 2 +- app/survey_config/survey_config.py | 2 +- app/translations/messages.pot | 16 +- app/utilities/mappings.py | 6 +- .../contexts/calculated_summary_context.py | 4 +- app/views/contexts/summary/answer.py | 2 +- app/views/handlers/calculated_summary.py | 7 + app/views/handlers/individual_response.py | 2 +- app/views/handlers/list_action.py | 1 - app/views/handlers/question.py | 3 + ...block_value_source_repeating_sections.json | 392 ++++ ...ction_value_source_repeating_sections.json | 392 ++++ .../en/test_progress_value_source_blocks.json | 210 ++ ...ess_value_source_blocks_cross_section.json | 224 +++ ...gress_value_source_calculated_summary.json | 517 +++++ ...ue_source_calculated_summary_extended.json | 1129 +++++++++++ ...peating_sections_chained_dependencies.json | 490 +++++ ...ress_value_source_section_enabled_hub.json | 110 ++ ...ue_source_section_enabled_hub_complex.json | 251 +++ ...s_value_source_section_enabled_no_hub.json | 114 ++ tests/app/data_model/test_progress_store.py | 2 +- .../data_model/test_questionnaire_store.py | 10 +- tests/app/questionnaire/conftest.py | 19 + .../rules/test_rule_evaluator.py | 43 +- .../questionnaire/test_placeholder_parser.py | 3 +- .../test_questionnaire_schema.py | 88 + .../test_questionnaire_store_updater.py | 151 +- .../test_value_source_resolver.py | 13 +- .../views/contexts/summary/test_question.py | 2 +- .../progress/progress_value_source_blocks.js | 193 ++ .../progress_value_source_repeating.js | 199 ++ tests/integration/integration_test_case.py | 4 + ...stionnaire_progress_value_source_blocks.py | 213 ++ ...rogress_value_source_calculated_summary.py | 417 ++++ ...ress_value_source_in_repeating_sections.py | 533 +++++ ...e_progress_value_source_section_enabled.py | 176 ++ .../integration/test_application_variables.py | 6 +- 52 files changed, 7268 insertions(+), 1028 deletions(-) create mode 100644 schemas/test/en/test_progress_block_value_source_repeating_sections.json create mode 100644 schemas/test/en/test_progress_section_value_source_repeating_sections.json create mode 100644 schemas/test/en/test_progress_value_source_blocks.json create mode 100644 schemas/test/en/test_progress_value_source_blocks_cross_section.json create mode 100644 schemas/test/en/test_progress_value_source_calculated_summary.json create mode 100644 schemas/test/en/test_progress_value_source_calculated_summary_extended.json create mode 100644 schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json create mode 100644 schemas/test/en/test_progress_value_source_section_enabled_hub.json create mode 100644 schemas/test/en/test_progress_value_source_section_enabled_hub_complex.json create mode 100644 schemas/test/en/test_progress_value_source_section_enabled_no_hub.json create mode 100644 tests/functional/spec/features/progress/progress_value_source_blocks.js create mode 100644 tests/functional/spec/features/progress/progress_value_source_repeating.js create mode 100644 tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py create mode 100644 tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py create mode 100644 tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py create mode 100644 tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py diff --git a/Pipfile b/Pipfile index aa1c5a8c06..33b9d009cf 100644 --- a/Pipfile +++ b/Pipfile @@ -38,7 +38,7 @@ pytest-mock = "*" [packages] colorama = "*" -flask = "*" +flask = "==2.2.2" flask-babel = "*" flask-login = "*" flask-wtf = "*" @@ -72,6 +72,7 @@ google-cloud-tasks = "*" simplejson = "*" markupsafe = "*" pdfkit = "*" +ordered-set = "*" [requires] python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index 53ccf6d636..7f8c8b727d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "65c2887dc528a01d63be44fc18d26e792c56501ddfb961c4cfb01a7ce7c925b7" + "sha256": "6048e514736937d357347a60fbe097ce97d92ec7b8b5dc1b1c8050d4d106d61b" }, "pipfile-spec": 6, "requires": { @@ -21,40 +21,40 @@ "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version <= '3.11.2'", "version": "==4.0.2" }, "babel": { "hashes": [ - "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe", - "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6" + "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610", + "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455" ], - "markers": "python_version >= '3.6'", - "version": "==2.11.0" + "markers": "python_version >= '3.7'", + "version": "==2.12.1" }, "blinker": { "hashes": [ - "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36", - "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462" + "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", + "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" ], "index": "pypi", - "version": "==1.5" + "version": "==1.6.2" }, "boto3": { "hashes": [ - "sha256:9e23ec51e1fe162ecb5efee41bd9346d90ebbb8ff094ef8088223305d48ffaec", - "sha256:9fc94b3078f5047c1fc40529fa12eb82a2432bb1dca0ee37b1c54f902f46191f" + "sha256:6648aff15d19927cd26db47eb56362ccd313a1ddbd7aaa3235ef05d05d398252", + "sha256:fe8248b80c4f0fdaed8b8779467c4431a5e52177e02ccd137d51ec51194ebb00" ], "index": "pypi", - "version": "==1.26.65" + "version": "==1.26.125" }, "botocore": { "hashes": [ - "sha256:77fff109e1bdbf030d8400ccab9d28454c03c7be62c1696a239b21792b0873df", - "sha256:8710f53af0e20f08f36a3bf434d18bc7ceba5d9835495b02aedbedd35df5de9a" + "sha256:3005a7ffee083315e69938acdf1bfeaf9e21fe1fe1643d6573ee817721f4ffcd", + "sha256:ac87b63e9aa4231cd28941945024a0c4470c184c60334ebe5e1cae3544c785ed" ], "markers": "python_version >= '3.7'", - "version": "==1.29.72" + "version": "==1.29.125" }, "brotli": { "hashes": [ @@ -230,97 +230,84 @@ }, "charset-normalizer": { "hashes": [ - "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b", - "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42", - "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d", - "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b", - "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a", - "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59", - "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154", - "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1", - "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c", - "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a", - "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d", - "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6", - "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b", - "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b", - "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783", - "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5", - "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918", - "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555", - "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639", - "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786", - "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e", - "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed", - "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820", - "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8", - "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3", - "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541", - "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14", - "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be", - "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e", - "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76", - "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b", - "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c", - "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b", - "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3", - "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc", - "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6", - "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59", - "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4", - "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d", - "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d", - "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3", - "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a", - "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea", - "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6", - "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e", - "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603", - "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24", - "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a", - "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58", - "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678", - "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a", - "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c", - "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6", - "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18", - "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174", - "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317", - "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f", - "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc", - "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837", - "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41", - "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c", - "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579", - "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753", - "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8", - "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291", - "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087", - "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866", - "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3", - "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d", - "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1", - "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca", - "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e", - "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db", - "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72", - "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d", - "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc", - "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539", - "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d", - "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af", - "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b", - "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602", - "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f", - "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478", - "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c", - "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e", - "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479", - "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7", - "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==3.0.1" + "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", + "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", + "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", + "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", + "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", + "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", + "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", + "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", + "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", + "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", + "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", + "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", + "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", + "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", + "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", + "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", + "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", + "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", + "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", + "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", + "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", + "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", + "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", + "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", + "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", + "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", + "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", + "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", + "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", + "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", + "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", + "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", + "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", + "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", + "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", + "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", + "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", + "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", + "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", + "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", + "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", + "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", + "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", + "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", + "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", + "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", + "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", + "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", + "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", + "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", + "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", + "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", + "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", + "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", + "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", + "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", + "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", + "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", + "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", + "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", + "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", + "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", + "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", + "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", + "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", + "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", + "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", + "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", + "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", + "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", + "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", + "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", + "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", + "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", + "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.1.0" }, "click": { "hashes": [ @@ -348,32 +335,28 @@ }, "cryptography": { "hashes": [ - "sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4", - "sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f", - "sha256:4789d1e3e257965e960232345002262ede4d094d1a19f4d3b52e48d4d8f3b885", - "sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502", - "sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41", - "sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965", - "sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e", - "sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc", - "sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad", - "sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505", - "sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388", - "sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6", - "sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2", - "sha256:c5caeb8188c24888c90b5108a441c106f7faa4c4c075a2bcae438c6e8ca73cef", - "sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac", - "sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695", - "sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6", - "sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336", - "sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0", - "sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c", - "sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106", - "sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a", - "sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8" - ], - "index": "pypi", - "version": "==39.0.1" + "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", + "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", + "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", + "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", + "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", + "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", + "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", + "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", + "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", + "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", + "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", + "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", + "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", + "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", + "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", + "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", + "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", + "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", + "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" + ], + "markers": "python_version >= '3.6'", + "version": "==40.0.2" }, "deprecated": { "hashes": [ @@ -388,16 +371,16 @@ "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9", "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46" ], - "markers": "python_version >= '3.7' and python_version < '4'", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==2.3.0" }, "email-validator": { "hashes": [ - "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda", - "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2" + "sha256:1ff6e86044200c56ae23595695c54e9614f4a9551e0e393614f764860b3d7900", + "sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c" ], "index": "pypi", - "version": "==1.3.1" + "version": "==2.0.0.post2" }, "flask": { "hashes": [ @@ -409,11 +392,11 @@ }, "flask-babel": { "hashes": [ - "sha256:ceb8c82039954a6b29da33ec5deb84878b78069d1ea628b21cac3f8233e9189c", - "sha256:d408cace25514bea8b92e898fd7e55877fbac79b71bc230e266ff515408eba38" + "sha256:be015772c5d7f046f3b99c508dcf618636eb93d21b713b356db79f3e79f69f39", + "sha256:deb3ee272d5adf97f5974ed09ab501243d63e7fb4a047501a00de4bd4aca4830" ], "index": "pypi", - "version": "==3.0.1" + "version": "==3.1.0" }, "flask-compress": { "hashes": [ @@ -519,11 +502,11 @@ }, "google-auth": { "hashes": [ - "sha256:5045648c821fb72384cdc0e82cc326df195f113a33049d9b62b74589243d2acc", - "sha256:ed7057a101af1146f0554a769930ac9de506aeca4fd5af6543ebe791851a9fbd" + "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", + "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.16.0" + "version": "==2.17.3" }, "google-cloud-core": { "hashes": [ @@ -535,35 +518,35 @@ }, "google-cloud-datastore": { "hashes": [ - "sha256:3d11652cb98cfb77147a61a74adefdcd3018c5e06f1ed723ec32c55e1dc9597d", - "sha256:8a4b2d5b9dcaad1af8be79ad6ebd00386f3f907685f1ec5c2456f072584203b0" + "sha256:4c2f0e8825482a8998c8c5b672c86206a6d988e449f251bb9bf17dae9a056c2e", + "sha256:788c512ea6d63012e711e0371a0e425ec852990051aca5c9c9a73804c448b1af" ], "index": "pypi", - "version": "==2.13.2" + "version": "==2.15.1" }, "google-cloud-pubsub": { "hashes": [ - "sha256:a035d63ef209a28edd54c8c1fcf66cc25b207ed6edb1259a973b909c723ecf80", - "sha256:e2714f07b750458beaf5b07b670ea7b6058ee155c021c987d0b8e6a40bf3446f" + "sha256:587da7d535ca858ceeed7036205355e5a6dd3e44ea4abc96f4e50d1abfd8843b", + "sha256:e41ec9635cc68dbd238f572f295b3d0bae6340c66ba31a70dfb890950ab33c2b" ], "index": "pypi", - "version": "==2.14.0" + "version": "==2.16.0" }, "google-cloud-storage": { "hashes": [ - "sha256:1ac2d58d2d693cb1341ebc48659a3527be778d9e2d8989697a2746025928ff17", - "sha256:f78a63525e72dd46406b255bbdf858a22c43d6bad8dc5bdeb7851a42967e95a1" + "sha256:248e210c13bc109909160248af546a91cb2dabaf3d7ebbf04def9dd49f02dbb6", + "sha256:4388da1ff5bda6d729f26dbcaf1bfa020a2a52a7b91f0a8123edbda51660802c" ], "index": "pypi", - "version": "==2.7.0" + "version": "==2.8.0" }, "google-cloud-tasks": { "hashes": [ - "sha256:56109cfd9f65864dd548f6248e48dee9919d6bfe658f8065ab18b11b84e7171f", - "sha256:da4463e739403d53b653713337ea29cf9381b7012bbb946a18e5c6a8b50f6940" + "sha256:616f2c32945cf68e812375a32f8a0f1d9cc39f4a39de03942593af6d13af7c20", + "sha256:99b845055c7a1b27b73349429f2d6f6a0eb9f409b4cdeec89df99e16bd197833" ], "index": "pypi", - "version": "==2.12.1" + "version": "==2.13.1" }, "google-crc32c": { "hashes": [ @@ -641,19 +624,19 @@ }, "google-resumable-media": { "hashes": [ - "sha256:15b8a2e75df42dc6502d1306db0bce2647ba6013f9cd03b6e17368c0886ee90a", - "sha256:831e86fd78d302c1a034730a0c6e5369dd11d37bad73fa69ca8998460d5bae8d" + "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", + "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" ], "markers": "python_version >= '3.7'", - "version": "==2.4.1" + "version": "==2.5.0" }, "googleapis-common-protos": { "hashes": [ - "sha256:c727251ec025947d545184ba17e3578840fc3a24a0516a020479edab660457df", - "sha256:ca3befcd4580dab6ad49356b46bf165bb68ff4b32389f028f1abd7c10ab9519a" + "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", + "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" ], "markers": "python_version >= '3.7'", - "version": "==1.58.0" + "version": "==1.59.0" }, "greenlet": { "hashes": [ @@ -731,62 +714,62 @@ }, "grpcio": { "hashes": [ - "sha256:094e64236253590d9d4075665c77b329d707b6fca864dd62b144255e199b4f87", - "sha256:0dc5354e38e5adf2498312f7241b14c7ce3484eefa0082db4297189dcbe272e6", - "sha256:0e1a9e1b4a23808f1132aa35f968cd8e659f60af3ffd6fb00bcf9a65e7db279f", - "sha256:0fb93051331acbb75b49a2a0fd9239c6ba9528f6bdc1dd400ad1cb66cf864292", - "sha256:16c71740640ba3a882f50b01bf58154681d44b51f09a5728180a8fdc66c67bd5", - "sha256:172405ca6bdfedd6054c74c62085946e45ad4d9cec9f3c42b4c9a02546c4c7e9", - "sha256:17ec9b13cec4a286b9e606b48191e560ca2f3bbdf3986f91e480a95d1582e1a7", - "sha256:22b011674090594f1f3245960ced7386f6af35485a38901f8afee8ad01541dbd", - "sha256:24ac1154c4b2ab4a0c5326a76161547e70664cd2c39ba75f00fc8a2170964ea2", - "sha256:257478300735ce3c98d65a930bbda3db172bd4e00968ba743e6a1154ea6edf10", - "sha256:29cb97d41a4ead83b7bcad23bdb25bdd170b1e2cba16db6d3acbb090bc2de43c", - "sha256:2b170eaf51518275c9b6b22ccb59450537c5a8555326fd96ff7391b5dd75303c", - "sha256:31bb6bc7ff145e2771c9baf612f4b9ebbc9605ccdc5f3ff3d5553de7fc0e0d79", - "sha256:3c2b3842dcf870912da31a503454a33a697392f60c5e2697c91d133130c2c85d", - "sha256:3f9b0023c2c92bebd1be72cdfca23004ea748be1813a66d684d49d67d836adde", - "sha256:471d39d3370ca923a316d49c8aac66356cea708a11e647e3bdc3d0b5de4f0a40", - "sha256:49d680356a975d9c66a678eb2dde192d5dc427a7994fb977363634e781614f7c", - "sha256:4c4423ea38a7825b8fed8934d6d9aeebdf646c97e3c608c3b0bcf23616f33877", - "sha256:506b9b7a4cede87d7219bfb31014d7b471cfc77157da9e820a737ec1ea4b0663", - "sha256:538d981818e49b6ed1e9c8d5e5adf29f71c4e334e7d459bf47e9b7abb3c30e09", - "sha256:59dffade859f157bcc55243714d57b286da6ae16469bf1ac0614d281b5f49b67", - "sha256:5a6ebcdef0ef12005d56d38be30f5156d1cb3373b52e96f147f4a24b0ddb3a9d", - "sha256:5dca372268c6ab6372d37d6b9f9343e7e5b4bc09779f819f9470cd88b2ece3c3", - "sha256:6df3b63538c362312bc5fa95fb965069c65c3ea91d7ce78ad9c47cab57226f54", - "sha256:6f0b89967ee11f2b654c23b27086d88ad7bf08c0b3c2a280362f28c3698b2896", - "sha256:75e29a90dc319f0ad4d87ba6d20083615a00d8276b51512e04ad7452b5c23b04", - "sha256:7942b32a291421460d6a07883033e392167d30724aa84987e6956cd15f1a21b9", - "sha256:9235dcd5144a83f9ca6f431bd0eccc46b90e2c22fe27b7f7d77cabb2fb515595", - "sha256:97d67983189e2e45550eac194d6234fc38b8c3b5396c153821f2d906ed46e0ce", - "sha256:9ff42c5620b4e4530609e11afefa4a62ca91fa0abb045a8957e509ef84e54d30", - "sha256:a8a0b77e992c64880e6efbe0086fe54dfc0bbd56f72a92d9e48264dcd2a3db98", - "sha256:aacb54f7789ede5cbf1d007637f792d3e87f1c9841f57dd51abf89337d1b8472", - "sha256:bc59f7ba87972ab236f8669d8ca7400f02a0eadf273ca00e02af64d588046f02", - "sha256:cc2bece1737b44d878cc1510ea04469a8073dbbcdd762175168937ae4742dfb3", - "sha256:cd3baccea2bc5c38aeb14e5b00167bd4e2373a373a5e4d8d850bd193edad150c", - "sha256:dad6533411d033b77f5369eafe87af8583178efd4039c41d7515d3336c53b4f1", - "sha256:e223a9793522680beae44671b9ed8f6d25bbe5ddf8887e66aebad5e0686049ef", - "sha256:e473525c28251558337b5c1ad3fa969511e42304524a4e404065e165b084c9e4", - "sha256:e4ef09f8997c4be5f3504cefa6b5c6cc3cf648274ce3cede84d4342a35d76db6", - "sha256:e6dfc2b6567b1c261739b43d9c59d201c1b89e017afd9e684d85aa7a186c9f7a", - "sha256:eacad297ea60c72dd280d3353d93fb1dcca952ec11de6bb3c49d12a572ba31dd", - "sha256:f1158bccbb919da42544a4d3af5d9296a3358539ffa01018307337365a9a0c64", - "sha256:f1fec3abaf274cdb85bf3878167cfde5ad4a4d97c68421afda95174de85ba813", - "sha256:f96ace1540223f26fbe7c4ebbf8a98e3929a6aa0290c8033d12526847b291c0f", - "sha256:fbdbe9a849854fe484c00823f45b7baab159bdd4a46075302281998cb8719df5" - ], - "index": "pypi", - "version": "==1.51.1" + "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", + "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", + "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", + "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", + "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", + "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", + "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", + "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", + "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", + "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", + "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", + "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", + "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", + "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", + "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", + "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", + "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", + "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", + "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", + "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", + "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", + "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", + "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", + "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", + "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", + "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", + "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", + "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", + "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", + "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", + "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", + "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", + "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", + "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", + "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", + "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", + "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", + "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", + "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", + "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", + "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", + "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", + "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", + "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", + "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" + ], + "index": "pypi", + "version": "==1.54.0" }, "grpcio-status": { "hashes": [ - "sha256:a52cbdc4b18f325bfc13d319ae7c7ae7a0fee07f3d9a005504d6097896d7a495", - "sha256:ac2617a3095935ebd785e2228958f24b10a0d527a0c9eb5a0863c784f648a816" + "sha256:96968314e0c8576b2b631be3917c665964c8018900cb980d58a736fbff828578", + "sha256:b50305d52c0df6169493cca5f2e39b9b4d773b3f30d4a7a6b6dd7c18cb89007c" ], "markers": "python_version >= '3.6'", - "version": "==1.51.1" + "version": "==1.54.0" }, "gunicorn": { "hashes": [ @@ -930,13 +913,21 @@ "index": "pypi", "version": "==3.19.0" }, + "ordered-set": { + "hashes": [ + "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", + "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" + ], + "index": "pypi", + "version": "==4.1.0" + }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], "markers": "python_version >= '3.7'", - "version": "==23.0" + "version": "==23.1" }, "pdfkit": { "hashes": [ @@ -965,59 +956,38 @@ }, "protobuf": { "hashes": [ - "sha256:1f22ac0ca65bb70a876060d96d914dae09ac98d114294f77584b0d2644fa9c30", - "sha256:237216c3326d46808a9f7c26fd1bd4b20015fb6867dc5d263a493ef9a539293b", - "sha256:27f4d15021da6d2b706ddc3860fac0a5ddaba34ab679dc182b60a8bb4e1121cc", - "sha256:299ea899484ee6f44604deb71f424234f654606b983cb496ea2a53e3c63ab791", - "sha256:3d164928ff0727d97022957c2b849250ca0e64777ee31efd7d6de2e07c494717", - "sha256:6ab80df09e3208f742c98443b6166bcb70d65f52cfeb67357d52032ea1ae9bec", - "sha256:78a28c9fa223998472886c77042e9b9afb6fe4242bd2a2a5aced88e3f4422aa7", - "sha256:7cd532c4566d0e6feafecc1059d04c7915aec8e182d1cf7adee8b24ef1e2e6ab", - "sha256:89f9149e4a0169cddfc44c74f230d7743002e3aa0b9472d8c28f0388102fc4c2", - "sha256:a53fd3f03e578553623272dc46ac2f189de23862e68565e83dde203d41b76fc5", - "sha256:b135410244ebe777db80298297a97fbb4c862c881b4403b71bac9d4107d61fd1", - "sha256:b98d0148f84e3a3c569e19f52103ca1feacdac0d2df8d6533cf983d1fda28462", - "sha256:d1736130bce8cf131ac7957fa26880ca19227d4ad68b4888b3be0dea1f95df97", - "sha256:f45460f9ee70a0ec1b6694c6e4e348ad2019275680bd68a1d9314b8c7e01e574" + "sha256:13233ee2b9d3bd9a5f216c1fa2c321cd564b93d8f2e4f521a85b585447747997", + "sha256:23452f2fdea754a8251d0fc88c0317735ae47217e0d27bf330a30eec2848811a", + "sha256:52f0a78141078077cfe15fe333ac3e3a077420b9a3f5d1bf9b5fe9d286b4d881", + "sha256:70659847ee57a5262a65954538088a1d72dfc3e9882695cab9f0c54ffe71663b", + "sha256:7760730063329d42a9d4c4573b804289b738d4931e363ffbe684716b796bde51", + "sha256:7cf56e31907c532e460bb62010a513408e6cdf5b03fb2611e4b67ed398ad046d", + "sha256:8b54f56d13ae4a3ec140076c9d937221f887c8f64954673d46f63751209e839a", + "sha256:d14fc1a41d1a1909998e8aff7e80d2a7ae14772c4a70e4bf7db8a36690b54425", + "sha256:d4b66266965598ff4c291416be429cef7989d8fae88b55b62095a2331511b3fa", + "sha256:e0e630d8e6a79f48c557cd1835865b593d0547dce221c66ed1b827de59c66c97", + "sha256:ecae944c6c2ce50dda6bf76ef5496196aeb1b85acb95df5843cd812615ec4b61", + "sha256:f08aa300b67f1c012100d8eb62d47129e53d1150f4469fd78a29fa3cb68c66f2", + "sha256:f2f4710543abec186aee332d6852ef5ae7ce2e9e807a3da570f36de5a732d88e" ], "markers": "python_version >= '3.7'", - "version": "==4.21.12" + "version": "==4.22.3" }, "pyasn1": { "hashes": [ - "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", - "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", - "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", - "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", - "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", - "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", - "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", - "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", - "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", - "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", - "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", - "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", - "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" - ], - "version": "==0.4.8" + "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", + "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.5.0" }, "pyasn1-modules": { "hashes": [ - "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8", - "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199", - "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811", - "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed", - "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4", - "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", - "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74", - "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb", - "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45", - "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd", - "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0", - "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d", - "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405" - ], - "version": "==0.2.8" + "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", + "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.3.0" }, "pycparser": { "hashes": [ @@ -1090,10 +1060,10 @@ }, "pytz": { "hashes": [ - "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0", - "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a" + "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588", + "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb" ], - "version": "==2022.7.1" + "version": "==2023.3" }, "pyyaml": { "hashes": [ @@ -1143,19 +1113,19 @@ }, "redis": { "hashes": [ - "sha256:a010f6cb7378065040a02839c3f75c7e0fb37a87116fb4a95be82a95552776c7", - "sha256:e6206448e2f8a432871d07d432c13ed6c2abcf6b74edb436c99752b1371be387" + "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2", + "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893" ], "index": "pypi", - "version": "==4.4.2" + "version": "==4.5.4" }, "requests": { "hashes": [ - "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", - "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", + "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" ], "index": "pypi", - "version": "==2.28.2" + "version": "==2.29.0" }, "rsa": { "hashes": [ @@ -1183,90 +1153,102 @@ }, "setuptools": { "hashes": [ - "sha256:95f00380ef2ffa41d9bba85d95b27689d923c93dfbafed4aecd7cf988a25e012", - "sha256:bb6d8e508de562768f2027902929f8523932fcd1fb784e6d573d2cafac995a48" + "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", + "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" ], "markers": "python_version >= '3.7'", - "version": "==67.3.2" + "version": "==67.7.2" }, "simplejson": { "hashes": [ - "sha256:04a4b9a297cccbc9e1d66fe652fbffd55b36d6579c43132e821d315957302194", - "sha256:063db62a9251e61ea0c17e49c3e7bed465bfcc5359655abcb8c0bc6130a4e0d4", - "sha256:070ab073ce72f1624107dfd6d095c87ac32aafe7ba54a5c5055a3dd83cb06e51", - "sha256:099bbd3b5b4ea83159a980348cd481a34984dee5fe1b9fac31a9137158f46960", - "sha256:0baf8c60efef74944ed4adb034d14bcf737731576f0e4c3c56fb875ea256af69", - "sha256:0e7c3fae6c9540064e06a653780b4f263675cd69ca6841345029fee3e27e9bb5", - "sha256:141782a0a25c1792627575b37b4951583358ccc7137623aa45947f8425ee8d96", - "sha256:14b35fb90083218e59df5dba733c7086655f2938f3fcabe36ad849623941d660", - "sha256:169c2c7446ef33439c304a6aa5b7b5a2dbc938c9c2dd882dd3f2553f9518ebf6", - "sha256:16cc750d19852fa5ebafd55da86fa357f87991e07b4e2afb37a5975dfdde0153", - "sha256:1907d49d70c75530976119c13785db91168d2599288debaca7d25da9cd2f3747", - "sha256:1b79e2607ac5ba98381c2e068727acc1e4dd385a6d216914c0613f8f568a06a5", - "sha256:1e49c84df6e71e3c23169d3df481565dd607cbee4aa1e0af15c493cccad7c745", - "sha256:23fce984045804194f513a2739dcd82be350198470d5ade5058da019a48cf3f8", - "sha256:24823364fee93bab141621b3a2e10612e31be7ca58788bf9b2cd2b1ce37ab07d", - "sha256:290bbcdcbb37af3f7e43378f592ab7a9168fca640da6af63d42cdb535f96bbf2", - "sha256:2a1b3222bc8f6ac91b5ebe3263111c7dc4dc4b01c52f0153f5bb1f3ef3bf0023", - "sha256:2b0f6de11f5ce4b80f51bc49d08b898602e190547f8efe4e44af8ae3cda7779d", - "sha256:2be75f4cb9951efeb2616e16f944ee4f9a09768475a3f5c40a6ac4dc5ee68dfd", - "sha256:2c7ee643ee93684bf76196e2d84a2090c6df8f01737a016e869b579593827b6e", - "sha256:37bdef13412c0bc338db2993a38f3911d5bd2a0ba8d00b3bc66d1063edd7c33e", - "sha256:3bab9ea49ff477c926c5787f79ec47cf51c7ffb15c9d8dd0f09e728807d44f4b", - "sha256:44d6c52d4f5c0c087a6e88a92bf9f94234321d21be32c6471ba39856e304bbe3", - "sha256:4b8d4d958c5ab3489d1174917a7fad82da642560c39ce559a624e63deaaa36b1", - "sha256:4de9fed1166aeedee44150fa83bc059aca6b612940281f8b5a39374781f16196", - "sha256:502d86fbfe914263642479b87ed61af3b27b9e039df77acd2416cfccfc892e68", - "sha256:508342d7227ed66beecfbba7a38b46e1a713faeb034216f43f03ec5c175e0622", - "sha256:50f4b6d52f3a2d1cffd11834a1fe7f9516f0e3f20cbe78027aa88ff990fad7d6", - "sha256:52465a5578cfc2c5e374a574df14dfb75e04c6cb6a100b7abc8bf6c89bea8f5e", - "sha256:55aa983575b0aef143845f5bfbb35075475eccaebf7d4b30f4037a2fe8414666", - "sha256:55df3dfd8777bf134e1078d2f195352432a77f23ccb90b92b08218123d56adc9", - "sha256:56f186d44a9f625b5e5d9ba4b9551e263604000a7df60cb373b3e789ca603b2a", - "sha256:5780e3929435a8d39671537174f8ce0ccafb4f6e0c748ffe139916ffbdca39d3", - "sha256:59a629240cfbc5b4f390a8578dca74ae77ab617de971862acb946822d2eb1b11", - "sha256:5b009342e712026ffabe8a471d5b4a4ff2a038687387e74eae601574c04dae33", - "sha256:62628ea5df8c830d00a7417d5ecd949a1b24a8d0a5063a2a77f7ec7522110a0f", - "sha256:694332fd6fd10fe8868c2508583220d1a1a7be9ff049dab5bd6b9aedfb9edc50", - "sha256:6a49665169c18f27a0fc10935466332ee7406ee14ced8dc0a1b4d465547299aa", - "sha256:6b997739fdbc9b7030ff490fc8e5f8c144b8ec80f3605eff643983672bb8cfde", - "sha256:6bd81d10cb3384f64242316da8a2b2f88618776bc1ef38bcc79f1afe8ad36616", - "sha256:6c4c56c5abb82e22877b913186e5c0fd7d9eef0c930719e28fa451d3f11defb4", - "sha256:6fe1173b4146641c872bafa6f9a21f3a2012f502d54fbb523a76e6320024fae9", - "sha256:75eb555dc349d0cbe2c95ea2be665b306c6ac6d5b64e3a3920af9b805ecdb5f7", - "sha256:7c26fe63755ecc59c502ddde8e58ce8b765bf4fdd3f5858d2b7c8ab28bc2a9c8", - "sha256:7e73d9d6af3c29b60a92e28b3144d951110f59a3d05fc402c3f6c5248b883400", - "sha256:7ff65b475091084e5bdb7f26e9c555956be7355b573ce494fa96f9f8e34541ac", - "sha256:8209c40279ed9b2cd5fbe2d617a29a074e90ea97fce7c07a0128a01cb3e8afc5", - "sha256:88f59a07873dc1f06fd9e6712dd71286f1b297a066ad2fd9110ad080d3cb011c", - "sha256:96ade36640734b54176c4765d00a60767bd7fae5b7a5b3574accc055ac18e34c", - "sha256:9cf299fbb7d476676dfea372a3262654af98694bd1df35b060ce0fe1b68087f1", - "sha256:a2960b95f3ba822d077d1afa7e1fea9799cfb2990028cf010e666f64195ecb5a", - "sha256:a80bd9a3db88a76a401155c64e3499376c702307c2206cb381cc2a8dd9cc4f1f", - "sha256:aad323e92cb1bd3b1db6f57c007dca964d13c52247ad844203ce381e94066601", - "sha256:ab5bdf0b8d07f7fd603b2d0c1982412cd9f8ade997088ddced251f7e656c7fd4", - "sha256:b0352428b35da859a98770949e7353866ae65463026f1c8e4c89a6395d4b5fd7", - "sha256:b2c4e8b65987f3c6529149495d28e23efe213e94dc3659176c4ab22d18a9ee4a", - "sha256:bcd9eac304a133ee4af58e68c5ded4c5ba663d3ee4602e8613359b776a1f8c8f", - "sha256:c3b696770b504f881f271f97b94a687487ec1ef20bfbd5f20d92bbab7a85952d", - "sha256:c4514675f6571da8190fea52a110bca686fa844972e8b2b3bc07ace9e632ee4f", - "sha256:c98fddc374468158778a8afb3fd7296412a2b2fc34cebba64212ac3e018e7382", - "sha256:cde5a3ff5e0bd5d6da676314dfae86c9e99bff77bca03d30223c9718a58f9e83", - "sha256:cf7168b2046db0eceb83d8ed2ee31c0847ce18b2d8baf3e93de9560f3921a8c3", - "sha256:d774782159347d66563cd7ac18b9dd37010438a825160cde4818caa18110a746", - "sha256:d990ea42ba908cb57a3df97d283aa26c1822f10a0a60e250b54ee21cd08c48d0", - "sha256:e762e9d8556fa9f3a99f8a278eeba50a35b5f554b82deeb282ddbdd85816e638", - "sha256:e8a4750e8db92109e6f1f7783a7faae4254d6d5dc28a41ff7eff7d2265f0586b", - "sha256:eb81cfef0c0039010f0212f4e5eb6909641b8a54c761584054ac97fd7bd0c21a", - "sha256:ebb53837c5ffcb6100646018565d3f1afed6f4b185b14b2c9cbccf874fe40157", - "sha256:efa70fd9b6c7b57b048ecadb909683acd535cddebc5b22f3c05ba3b369739caf", - "sha256:f73bae5e315adf7bc8cb7f0a13a1e9e33bead42e8ce174be83ac9ecc2513c86a", - "sha256:f89f078114cacedb9a3392615cc099cf02a51efa7507f90e2006bf7ec38c880d", - "sha256:f9f72d2b539512f382a48cc9ad6cea2d3a572e71e92c40e03d2140041eeaa233", - "sha256:fc8df5831b645e96a318ea51a66ce6e2bb869eebc3fa9a860bbf67aecd270055" - ], - "index": "pypi", - "version": "==3.18.3" + "sha256:081ea6305b3b5e84ae7417e7f45956db5ea3872ec497a584ec86c3260cda049e", + "sha256:08be5a241fdf67a8e05ac7edbd49b07b638ebe4846b560673e196b2a25c94b92", + "sha256:0c16ec6a67a5f66ab004190829eeede01c633936375edcad7cbf06d3241e5865", + "sha256:0ccb2c1877bc9b25bc4f4687169caa925ffda605d7569c40e8e95186e9a5e58b", + "sha256:17a963e8dd4d81061cc05b627677c1f6a12e81345111fbdc5708c9f088d752c9", + "sha256:199a0bcd792811c252d71e3eabb3d4a132b3e85e43ebd93bfd053d5b59a7e78b", + "sha256:1cb19eacb77adc5a9720244d8d0b5507421d117c7ed4f2f9461424a1829e0ceb", + "sha256:203412745fed916fc04566ecef3f2b6c872b52f1e7fb3a6a84451b800fb508c1", + "sha256:2098811cd241429c08b7fc5c9e41fcc3f59f27c2e8d1da2ccdcf6c8e340ab507", + "sha256:22b867205cd258050c2625325fdd9a65f917a5aff22a23387e245ecae4098e78", + "sha256:23fbb7b46d44ed7cbcda689295862851105c7594ae5875dce2a70eeaa498ff86", + "sha256:2541fdb7467ef9bfad1f55b6c52e8ea52b3ce4a0027d37aff094190a955daa9d", + "sha256:3231100edee292da78948fa0a77dee4e5a94a0a60bcba9ed7a9dc77f4d4bb11e", + "sha256:344a5093b71c1b370968d0fbd14d55c9413cb6f0355fdefeb4a322d602d21776", + "sha256:37724c634f93e5caaca04458f267836eb9505d897ab3947b52f33b191bf344f3", + "sha256:3844305bc33d52c4975da07f75b480e17af3558c0d13085eaa6cc2f32882ccf7", + "sha256:390f4a8ca61d90bcf806c3ad644e05fa5890f5b9a72abdd4ca8430cdc1e386fa", + "sha256:3a4480e348000d89cf501b5606415f4d328484bbb431146c2971123d49fd8430", + "sha256:3b652579c21af73879d99c8072c31476788c8c26b5565687fd9db154070d852a", + "sha256:3e0902c278243d6f7223ba3e6c5738614c971fd9a887fff8feaa8dcf7249c8d4", + "sha256:412e58997a30c5deb8cab5858b8e2e5b40ca007079f7010ee74565cc13d19665", + "sha256:44cdb4e544134f305b033ad79ae5c6b9a32e7c58b46d9f55a64e2a883fbbba01", + "sha256:46133bc7dd45c9953e6ee4852e3de3d5a9a4a03b068bd238935a5c72f0a1ce34", + "sha256:46e89f58e4bed107626edce1cf098da3664a336d01fc78fddcfb1f397f553d44", + "sha256:4710806eb75e87919b858af0cba4ffedc01b463edc3982ded7b55143f39e41e1", + "sha256:476c8033abed7b1fd8db62a7600bf18501ce701c1a71179e4ce04ac92c1c5c3c", + "sha256:48600a6e0032bed17c20319d91775f1797d39953ccfd68c27f83c8d7fc3b32cb", + "sha256:4d3025e7e9ddb48813aec2974e1a7e68e63eac911dd5e0a9568775de107ac79a", + "sha256:547ea86ca408a6735335c881a2e6208851027f5bfd678d8f2c92a0f02c7e7330", + "sha256:54fca2b26bcd1c403146fd9461d1da76199442297160721b1d63def2a1b17799", + "sha256:5673d27806085d2a413b3be5f85fad6fca4b7ffd31cfe510bbe65eea52fff571", + "sha256:58ee5e24d6863b22194020eb62673cf8cc69945fcad6b283919490f6e359f7c5", + "sha256:5ca922c61d87b4c38f37aa706520328ffe22d7ac1553ef1cadc73f053a673553", + "sha256:5db86bb82034e055257c8e45228ca3dbce85e38d7bfa84fa7b2838e032a3219c", + "sha256:6277f60848a7d8319d27d2be767a7546bc965535b28070e310b3a9af90604a4c", + "sha256:6424d8229ba62e5dbbc377908cfee9b2edf25abd63b855c21f12ac596cd18e41", + "sha256:65dafe413b15e8895ad42e49210b74a955c9ae65564952b0243a18fb35b986cc", + "sha256:66389b6b6ee46a94a493a933a26008a1bae0cfadeca176933e7ff6556c0ce998", + "sha256:66d780047c31ff316ee305c3f7550f352d87257c756413632303fc59fef19eac", + "sha256:69a8b10a4f81548bc1e06ded0c4a6c9042c0be0d947c53c1ed89703f7e613950", + "sha256:6a561320485017ddfc21bd2ed5de2d70184f754f1c9b1947c55f8e2b0163a268", + "sha256:6aa7ca03f25b23b01629b1c7f78e1cd826a66bfb8809f8977a3635be2ec48f1a", + "sha256:6b79642a599740603ca86cf9df54f57a2013c47e1dd4dd2ae4769af0a6816900", + "sha256:6e7c70f19405e5f99168077b785fe15fcb5f9b3c0b70b0b5c2757ce294922c8c", + "sha256:70128fb92932524c89f373e17221cf9535d7d0c63794955cc3cd5868e19f5d38", + "sha256:73d0904c2471f317386d4ae5c665b16b5c50ab4f3ee7fd3d3b7651e564ad74b1", + "sha256:74bf802debe68627227ddb665c067eb8c73aa68b2476369237adf55c1161b728", + "sha256:79c748aa61fd8098d0472e776743de20fae2686edb80a24f0f6593a77f74fe86", + "sha256:79d46e7e33c3a4ef853a1307b2032cfb7220e1a079d0c65488fbd7118f44935a", + "sha256:7e78d79b10aa92f40f54178ada2b635c960d24fc6141856b926d82f67e56d169", + "sha256:8090e75653ea7db75bc21fa5f7bcf5f7bdf64ea258cbbac45c7065f6324f1b50", + "sha256:87b190e6ceec286219bd6b6f13547ca433f977d4600b4e81739e9ac23b5b9ba9", + "sha256:889328873c35cb0b2b4c83cbb83ec52efee5a05e75002e2c0c46c4e42790e83c", + "sha256:8f8d179393e6f0cf6c7c950576892ea6acbcea0a320838c61968ac7046f59228", + "sha256:919bc5aa4d8094cf8f1371ea9119e5d952f741dc4162810ab714aec948a23fe5", + "sha256:926957b278de22797bfc2f004b15297013843b595b3cd7ecd9e37ccb5fad0b72", + "sha256:93f5ac30607157a0b2579af59a065bcfaa7fadeb4875bf927a8f8b6739c8d910", + "sha256:96ade243fb6f3b57e7bd3b71e90c190cd0f93ec5dce6bf38734a73a2e5fa274f", + "sha256:9f14ecca970d825df0d29d5c6736ff27999ee7bdf5510e807f7ad8845f7760ce", + "sha256:a755f7bfc8adcb94887710dc70cc12a69a454120c6adcc6f251c3f7b46ee6aac", + "sha256:a79b439a6a77649bb8e2f2644e6c9cc0adb720fc55bed63546edea86e1d5c6c8", + "sha256:aa9d614a612ad02492f704fbac636f666fa89295a5d22b4facf2d665fc3b5ea9", + "sha256:ad071cd84a636195f35fa71de2186d717db775f94f985232775794d09f8d9061", + "sha256:b0e9a5e66969f7a47dc500e3dba8edc3b45d4eb31efb855c8647700a3493dd8a", + "sha256:b438e5eaa474365f4faaeeef1ec3e8d5b4e7030706e3e3d6b5bee6049732e0e6", + "sha256:b46aaf0332a8a9c965310058cf3487d705bf672641d2c43a835625b326689cf4", + "sha256:c39fa911e4302eb79c804b221ddec775c3da08833c0a9120041dd322789824de", + "sha256:ca56a6c8c8236d6fe19abb67ef08d76f3c3f46712c49a3b6a5352b6e43e8855f", + "sha256:cb502cde018e93e75dc8fc7bb2d93477ce4f3ac10369f48866c61b5e031db1fd", + "sha256:cd4d50a27b065447c9c399f0bf0a993bd0e6308db8bbbfbc3ea03b41c145775a", + "sha256:d125e754d26c0298715bdc3f8a03a0658ecbe72330be247f4b328d229d8cf67f", + "sha256:d300773b93eed82f6da138fd1d081dc96fbe53d96000a85e41460fe07c8d8b33", + "sha256:d396b610e77b0c438846607cd56418bfc194973b9886550a98fd6724e8c6cfec", + "sha256:d61482b5d18181e6bb4810b4a6a24c63a490c3a20e9fbd7876639653e2b30a1a", + "sha256:d9f2c27f18a0b94107d57294aab3d06d6046ea843ed4a45cae8bd45756749f3a", + "sha256:dc2b3f06430cbd4fac0dae5b2974d2bf14f71b415fb6de017f498950da8159b1", + "sha256:dc935d8322ba9bc7b84f99f40f111809b0473df167bf5b93b89fb719d2c4892b", + "sha256:e333c5b62e93949f5ac27e6758ba53ef6ee4f93e36cc977fe2e3df85c02f6dc4", + "sha256:e765b1f47293dedf77946f0427e03ee45def2862edacd8868c6cf9ab97c8afbd", + "sha256:ed18728b90758d171f0c66c475c24a443ede815cf3f1a91e907b0db0ebc6e508", + "sha256:eff87c68058374e45225089e4538c26329a13499bc0104b52b77f8428eed36b2", + "sha256:f05d05d99fce5537d8f7a0af6417a9afa9af3a6c4bb1ba7359c53b6257625fcb", + "sha256:f253edf694ce836631b350d758d00a8c4011243d58318fbfbe0dd54a6a839ab4", + "sha256:f41915a4e1f059dfad614b187bc06021fefb5fc5255bfe63abf8247d2f7a646a", + "sha256:f96def94576f857abf58e031ce881b5a3fc25cbec64b2bc4824824a8a4367af9" + ], + "index": "pypi", + "version": "==3.19.1" }, "six": { "hashes": [ @@ -1278,11 +1260,11 @@ }, "structlog": { "hashes": [ - "sha256:b403f344f902b220648fa9f286a23c0cc5439a5844d271fec40562dbadbc70ad", - "sha256:e7509391f215e4afb88b1b80fa3ea074be57a5a17d794bd436a5c949da023333" + "sha256:270d681dd7d163c11ba500bc914b2472d2b50a8ef00faa999ded5ff83a2f906b", + "sha256:79b9e68e48b54e373441e130fa447944e6f87a05b35de23138e475c05d0f7e0e" ], "index": "pypi", - "version": "==22.3.0" + "version": "==23.1.0" }, "ua-parser": { "hashes": [ @@ -1294,11 +1276,11 @@ }, "urllib3": { "hashes": [ - "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", - "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" + "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", + "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.14" + "version": "==1.26.15" }, "uwsgi": { "hashes": [ @@ -1309,81 +1291,92 @@ }, "werkzeug": { "hashes": [ - "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe", - "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612" + "sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a", + "sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091" ], - "markers": "python_version >= '3.7'", - "version": "==2.2.3" + "markers": "python_version >= '3.8'", + "version": "==2.3.3" }, "wrapt": { "hashes": [ - "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", - "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", - "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", - "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", - "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", - "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", - "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", - "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", - "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", - "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", - "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", - "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", - "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", - "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", - "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", - "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", - "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", - "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", - "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", - "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", - "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", - "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", - "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", - "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", - "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", - "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", - "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", - "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", - "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", - "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", - "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", - "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", - "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", - "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", - "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", - "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", - "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", - "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", - "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", - "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", - "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", - "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", - "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", - "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", - "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", - "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", - "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", - "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", - "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", - "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", - "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", - "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", - "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", - "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", - "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", - "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", - "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", - "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", - "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", - "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", - "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", - "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", - "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", - "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" + "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", + "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", + "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", + "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", + "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", + "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", + "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", + "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", + "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", + "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", + "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", + "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", + "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", + "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", + "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", + "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", + "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", + "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", + "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", + "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", + "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", + "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", + "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", + "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", + "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", + "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", + "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", + "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", + "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", + "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", + "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", + "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", + "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", + "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", + "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", + "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", + "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", + "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", + "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", + "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", + "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", + "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", + "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", + "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", + "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", + "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", + "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", + "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", + "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", + "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", + "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", + "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", + "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", + "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", + "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", + "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", + "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", + "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", + "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", + "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", + "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", + "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", + "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", + "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", + "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", + "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", + "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", + "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", + "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", + "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", + "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", + "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", + "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", + "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", + "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.14.1" + "version": "==1.15.0" }, "wtforms": { "hashes": [ @@ -1402,126 +1395,120 @@ }, "zope.interface": { "hashes": [ - "sha256:008b0b65c05993bb08912f644d140530e775cf1c62a072bf9340c2249e613c32", - "sha256:0217a9615531c83aeedb12e126611b1b1a3175013bbafe57c702ce40000eb9a0", - "sha256:0fb497c6b088818e3395e302e426850f8236d8d9f4ef5b2836feae812a8f699c", - "sha256:17ebf6e0b1d07ed009738016abf0d0a0f80388e009d0ac6e0ead26fc162b3b9c", - "sha256:311196634bb9333aa06f00fc94f59d3a9fddd2305c2c425d86e406ddc6f2260d", - "sha256:3218ab1a7748327e08ef83cca63eea7cf20ea7e2ebcb2522072896e5e2fceedf", - "sha256:404d1e284eda9e233c90128697c71acffd55e183d70628aa0bbb0e7a3084ed8b", - "sha256:4087e253bd3bbbc3e615ecd0b6dd03c4e6a1e46d152d3be6d2ad08fbad742dcc", - "sha256:40f4065745e2c2fa0dff0e7ccd7c166a8ac9748974f960cd39f63d2c19f9231f", - "sha256:5334e2ef60d3d9439c08baedaf8b84dc9bb9522d0dacbc10572ef5609ef8db6d", - "sha256:604cdba8f1983d0ab78edc29aa71c8df0ada06fb147cea436dc37093a0100a4e", - "sha256:6373d7eb813a143cb7795d3e42bd8ed857c82a90571567e681e1b3841a390d16", - "sha256:655796a906fa3ca67273011c9805c1e1baa047781fca80feeb710328cdbed87f", - "sha256:65c3c06afee96c654e590e046c4a24559e65b0a87dbff256cd4bd6f77e1a33f9", - "sha256:696f3d5493eae7359887da55c2afa05acc3db5fc625c49529e84bd9992313296", - "sha256:6e972493cdfe4ad0411fd9abfab7d4d800a7317a93928217f1a5de2bb0f0d87a", - "sha256:7579960be23d1fddecb53898035a0d112ac858c3554018ce615cefc03024e46d", - "sha256:765d703096ca47aa5d93044bf701b00bbce4d903a95b41fff7c3796e747b1f1d", - "sha256:7e66f60b0067a10dd289b29dceabd3d0e6d68be1504fc9d0bc209cf07f56d189", - "sha256:8a2ffadefd0e7206adc86e492ccc60395f7edb5680adedf17a7ee4205c530df4", - "sha256:959697ef2757406bff71467a09d940ca364e724c534efbf3786e86eee8591452", - "sha256:9d783213fab61832dbb10d385a319cb0e45451088abd45f95b5bb88ed0acca1a", - "sha256:a16025df73d24795a0bde05504911d306307c24a64187752685ff6ea23897cb0", - "sha256:a2ad597c8c9e038a5912ac3cf166f82926feff2f6e0dabdab956768de0a258f5", - "sha256:bfee1f3ff62143819499e348f5b8a7f3aa0259f9aca5e0ddae7391d059dce671", - "sha256:d169ccd0756c15bbb2f1acc012f5aab279dffc334d733ca0d9362c5beaebe88e", - "sha256:d514c269d1f9f5cd05ddfed15298d6c418129f3f064765295659798349c43e6f", - "sha256:d692374b578360d36568dd05efb8a5a67ab6d1878c29c582e37ddba80e66c396", - "sha256:dbaeb9cf0ea0b3bc4b36fae54a016933d64c6d52a94810a63c00f440ecb37dd7", - "sha256:dc26c8d44472e035d59d6f1177eb712888447f5799743da9c398b0339ed90b1b", - "sha256:e1574980b48c8c74f83578d1e77e701f8439a5d93f36a5a0af31337467c08fcf", - "sha256:e74a578172525c20d7223eac5f8ad187f10940dac06e40113d62f14f3adb1e8f", - "sha256:e945de62917acbf853ab968d8916290548df18dd62c739d862f359ecd25842a6", - "sha256:f0980d44b8aded808bec5059018d64692f0127f10510eca71f2f0ace8fb11188", - "sha256:f98d4bd7bbb15ca701d19b93263cc5edfd480c3475d163f137385f49e5b3a3a7", - "sha256:fb68d212efd057596dee9e6582daded9f8ef776538afdf5feceb3059df2d2e7b" + "sha256:042f2381118b093714081fd82c98e3b189b68db38ee7d35b63c327c470ef8373", + "sha256:0ec9653825f837fbddc4e4b603d90269b501486c11800d7c761eee7ce46d1bbb", + "sha256:12175ca6b4db7621aedd7c30aa7cfa0a2d65ea3a0105393e05482d7a2d367446", + "sha256:1592f68ae11e557b9ff2bc96ac8fc30b187e77c45a3c9cd876e3368c53dc5ba8", + "sha256:23ac41d52fd15dd8be77e3257bc51bbb82469cf7f5e9a30b75e903e21439d16c", + "sha256:424d23b97fa1542d7be882eae0c0fc3d6827784105264a8169a26ce16db260d8", + "sha256:4407b1435572e3e1610797c9203ad2753666c62883b921318c5403fb7139dec2", + "sha256:48f4d38cf4b462e75fac78b6f11ad47b06b1c568eb59896db5b6ec1094eb467f", + "sha256:4c3d7dfd897a588ec27e391edbe3dd320a03684457470415870254e714126b1f", + "sha256:5171eb073474a5038321409a630904fd61f12dd1856dd7e9d19cd6fe092cbbc5", + "sha256:5a158846d0fca0a908c1afb281ddba88744d403f2550dc34405c3691769cdd85", + "sha256:6ee934f023f875ec2cfd2b05a937bd817efcc6c4c3f55c5778cbf78e58362ddc", + "sha256:790c1d9d8f9c92819c31ea660cd43c3d5451df1df61e2e814a6f99cebb292788", + "sha256:809fe3bf1a91393abc7e92d607976bbb8586512913a79f2bf7d7ec15bd8ea518", + "sha256:87b690bbee9876163210fd3f500ee59f5803e4a6607d1b1238833b8885ebd410", + "sha256:89086c9d3490a0f265a3c4b794037a84541ff5ffa28bb9c24cc9f66566968464", + "sha256:99856d6c98a326abbcc2363827e16bd6044f70f2ef42f453c0bd5440c4ce24e5", + "sha256:aab584725afd10c710b8f1e6e208dbee2d0ad009f57d674cb9d1b3964037275d", + "sha256:af169ba897692e9cd984a81cb0f02e46dacdc07d6cf9fd5c91e81f8efaf93d52", + "sha256:b39b8711578dcfd45fc0140993403b8a81e879ec25d53189f3faa1f006087dca", + "sha256:b3f543ae9d3408549a9900720f18c0194ac0fe810cecda2a584fd4dca2eb3bb8", + "sha256:d0583b75f2e70ec93f100931660328965bb9ff65ae54695fb3fa0a1255daa6f2", + "sha256:dfbbbf0809a3606046a41f8561c3eada9db811be94138f42d9135a5c47e75f6f", + "sha256:e538f2d4a6ffb6edfb303ce70ae7e88629ac6e5581870e66c306d9ad7b564a58", + "sha256:eba51599370c87088d8882ab74f637de0c4f04a6d08a312dce49368ba9ed5c2a", + "sha256:ee4b43f35f5dc15e1fec55ccb53c130adb1d11e8ad8263d68b1284b66a04190d", + "sha256:f2363e5fd81afb650085c6686f2ee3706975c54f331b426800b53531191fdf28", + "sha256:f299c020c6679cb389814a3b81200fe55d428012c5e76da7e722491f5d205990", + "sha256:f72f23bab1848edb7472309e9898603141644faec9fd57a823ea6b4d1c4c8995", + "sha256:fa90bac61c9dc3e1a563e5babb3fd2c0c1c80567e815442ddbe561eadc803b30" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==5.5.2" + "markers": "python_version >= '3.7'", + "version": "==6.0" } }, "develop": { "astroid": { "hashes": [ - "sha256:0e0e3709d64fbffd3037e4ff403580550f14471fd3eaae9fa11cc9a5c7901153", - "sha256:a3cf9f02c53dd259144a7e8f3ccd75d67c9a8c716ef183e0c1f291bc5d7bb3cf" + "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347", + "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.14.2" + "version": "==2.15.4" }, "async-timeout": { "hashes": [ "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version <= '3.11.2'", "version": "==4.0.2" }, "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.1.0" }, "beautifulsoup4": { "hashes": [ - "sha256:0e79446b10b3ecb499c1556f7e228a53e64a2bfcebd455f370d8927cb5b59e39", - "sha256:bc4bdda6717de5a2987436fb8d72f45dc90dd856bdfd512a1314ce90349a0106" + "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", + "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" ], "index": "pypi", - "version": "==4.11.2" + "version": "==4.12.2" }, "black": { "hashes": [ - "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd", - "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555", - "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481", - "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468", - "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9", - "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a", - "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958", - "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580", - "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26", - "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32", - "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8", - "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753", - "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b", - "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074", - "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651", - "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24", - "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6", - "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad", - "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac", - "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221", - "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06", - "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27", - "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648", - "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739", - "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104" - ], - "index": "pypi", - "version": "==23.1.0" + "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", + "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", + "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", + "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", + "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", + "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", + "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", + "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", + "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", + "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", + "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", + "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", + "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", + "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", + "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", + "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", + "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", + "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", + "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", + "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", + "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", + "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", + "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", + "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", + "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" + ], + "index": "pypi", + "version": "==23.3.0" }, "boto3": { "hashes": [ - "sha256:9e23ec51e1fe162ecb5efee41bd9346d90ebbb8ff094ef8088223305d48ffaec", - "sha256:9fc94b3078f5047c1fc40529fa12eb82a2432bb1dca0ee37b1c54f902f46191f" + "sha256:6648aff15d19927cd26db47eb56362ccd313a1ddbd7aaa3235ef05d05d398252", + "sha256:fe8248b80c4f0fdaed8b8779467c4431a5e52177e02ccd137d51ec51194ebb00" ], "index": "pypi", - "version": "==1.26.65" + "version": "==1.26.125" }, "botocore": { "hashes": [ - "sha256:77fff109e1bdbf030d8400ccab9d28454c03c7be62c1696a239b21792b0873df", - "sha256:8710f53af0e20f08f36a3bf434d18bc7ceba5d9835495b02aedbedd35df5de9a" + "sha256:3005a7ffee083315e69938acdf1bfeaf9e21fe1fe1643d6573ee817721f4ffcd", + "sha256:ac87b63e9aa4231cd28941945024a0c4470c184c60334ebe5e1cae3544c785ed" ], "markers": "python_version >= '3.7'", - "version": "==1.29.72" + "version": "==1.29.125" }, "certifi": { "hashes": [ @@ -1602,97 +1589,84 @@ }, "charset-normalizer": { "hashes": [ - "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b", - "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42", - "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d", - "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b", - "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a", - "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59", - "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154", - "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1", - "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c", - "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a", - "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d", - "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6", - "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b", - "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b", - "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783", - "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5", - "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918", - "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555", - "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639", - "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786", - "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e", - "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed", - "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820", - "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8", - "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3", - "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541", - "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14", - "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be", - "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e", - "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76", - "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b", - "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c", - "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b", - "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3", - "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc", - "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6", - "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59", - "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4", - "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d", - "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d", - "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3", - "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a", - "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea", - "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6", - "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e", - "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603", - "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24", - "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a", - "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58", - "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678", - "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a", - "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c", - "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6", - "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18", - "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174", - "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317", - "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f", - "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc", - "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837", - "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41", - "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c", - "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579", - "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753", - "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8", - "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291", - "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087", - "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866", - "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3", - "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d", - "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1", - "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca", - "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e", - "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db", - "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72", - "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d", - "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc", - "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539", - "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d", - "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af", - "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b", - "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602", - "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f", - "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478", - "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c", - "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e", - "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479", - "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7", - "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==3.0.1" + "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", + "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", + "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", + "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", + "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", + "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", + "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", + "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", + "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", + "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", + "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", + "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", + "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", + "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", + "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", + "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", + "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", + "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", + "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", + "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", + "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", + "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", + "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", + "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", + "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", + "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", + "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", + "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", + "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", + "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", + "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", + "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", + "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", + "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", + "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", + "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", + "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", + "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", + "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", + "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", + "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", + "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", + "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", + "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", + "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", + "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", + "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", + "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", + "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", + "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", + "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", + "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", + "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", + "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", + "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", + "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", + "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", + "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", + "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", + "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", + "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", + "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", + "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", + "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", + "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", + "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", + "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", + "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", + "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", + "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", + "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", + "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", + "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", + "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", + "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.1.0" }, "click": { "hashes": [ @@ -1707,89 +1681,85 @@ "toml" ], "hashes": [ - "sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab", - "sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851", - "sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265", - "sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0", - "sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a", - "sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5", - "sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6", - "sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311", - "sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada", - "sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f", - "sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8", - "sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc", - "sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73", - "sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf", - "sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e", - "sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352", - "sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c", - "sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c", - "sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c", - "sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda", - "sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d", - "sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0", - "sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3", - "sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d", - "sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038", - "sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c", - "sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8", - "sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa", - "sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09", - "sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b", - "sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c", - "sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a", - "sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52", - "sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3", - "sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146", - "sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a", - "sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f", - "sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4", - "sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c", - "sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75", - "sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040", - "sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063", - "sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050", - "sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7", - "sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222", - "sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912", - "sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801", - "sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d", - "sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06", - "sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8", - "sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2" + "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", + "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", + "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", + "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", + "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", + "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", + "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", + "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", + "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", + "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", + "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", + "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", + "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", + "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", + "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", + "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", + "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", + "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", + "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", + "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", + "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", + "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", + "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", + "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", + "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", + "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", + "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", + "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", + "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", + "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", + "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", + "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", + "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", + "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", + "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", + "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", + "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", + "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", + "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", + "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", + "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", + "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", + "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", + "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", + "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", + "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", + "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", + "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", + "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", + "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", + "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" ], "markers": "python_version >= '3.7'", - "version": "==7.1.0" + "version": "==7.2.5" }, "cryptography": { "hashes": [ - "sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4", - "sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f", - "sha256:4789d1e3e257965e960232345002262ede4d094d1a19f4d3b52e48d4d8f3b885", - "sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502", - "sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41", - "sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965", - "sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e", - "sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc", - "sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad", - "sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505", - "sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388", - "sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6", - "sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2", - "sha256:c5caeb8188c24888c90b5108a441c106f7faa4c4c075a2bcae438c6e8ca73cef", - "sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac", - "sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695", - "sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6", - "sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336", - "sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0", - "sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c", - "sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106", - "sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a", - "sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8" - ], - "index": "pypi", - "version": "==39.0.1" + "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", + "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", + "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", + "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", + "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", + "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", + "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", + "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", + "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", + "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", + "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", + "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", + "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", + "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", + "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", + "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", + "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", + "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", + "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" + ], + "markers": "python_version >= '3.6'", + "version": "==40.0.2" }, "dill": { "hashes": [ @@ -1801,11 +1771,11 @@ }, "exceptiongroup": { "hashes": [ - "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e", - "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23" + "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", + "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" ], "markers": "python_version < '3.11'", - "version": "==1.1.0" + "version": "==1.1.1" }, "execnet": { "hashes": [ @@ -1817,11 +1787,11 @@ }, "fakeredis": { "hashes": [ - "sha256:854d758794dab9953be16b9a0f7fbd4bbd6b6964db7a9684e163291c1342ece6", - "sha256:e2f7a88dad23be1191ad6212008e170d75c2d63dde979c2694be8cbfd917428e" + "sha256:69a504328a89e5e5f2d05a4236b570fb45244c96997c5002c8c6a0503b95f289", + "sha256:e0fef512b8ec49679d373456aa4698a4103005ecd7ca0b13170a2c1d3af949c5" ], "index": "pypi", - "version": "==2.7.1" + "version": "==2.11.2" }, "flake8": { "hashes": [ @@ -2057,51 +2027,51 @@ }, "mock": { "hashes": [ - "sha256:c41cfb1e99ba5d341fbcc5308836e7d7c9786d302f995b2c271ce2144dece9eb", - "sha256:e3ea505c03babf7977fd21674a69ad328053d414f05e6433c30d8fa14a534a6b" + "sha256:06f18d7d65b44428202b145a9a36e99c2ee00d1eb992df0caf881d4664377891", + "sha256:0e0bc5ba78b8db3667ad636d964eb963dc97a59f04c6f6214c5f0e4a8f726c56" ], "index": "pypi", - "version": "==5.0.1" + "version": "==5.0.2" }, "moto": { "hashes": [ - "sha256:1b361ece638c74a657325378a259276f368aafce2f8be84f8143e69fa93ce8ec", - "sha256:63431733d2a02c7bd652ad71ec1da442a0e0d580cbac5eeb50d440a2ce066eac" + "sha256:c3ecc2dda1a7b3a3c46655490bc6a4660b7bb47e31eaed7bbd54adeb01f8471f", + "sha256:df5b52eff70bf125ee03ea72c4e01f2daff243796f984a534c3dee92a0b93522" ], "index": "pypi", - "version": "==4.1.2" + "version": "==4.1.8" }, "mypy": { "hashes": [ - "sha256:01b1b9e1ed40544ef486fa8ac022232ccc57109f379611633ede8e71630d07d2", - "sha256:0ab090d9240d6b4e99e1fa998c2d0aa5b29fc0fb06bd30e7ad6183c95fa07593", - "sha256:14d776869a3e6c89c17eb943100f7868f677703c8a4e00b3803918f86aafbc52", - "sha256:1ace23f6bb4aec4604b86c4843276e8fa548d667dbbd0cb83a3ae14b18b2db6c", - "sha256:2efa963bdddb27cb4a0d42545cd137a8d2b883bd181bbc4525b568ef6eca258f", - "sha256:2f6ac8c87e046dc18c7d1d7f6653a66787a4555085b056fe2d599f1f1a2a2d21", - "sha256:3ae4c7a99e5153496243146a3baf33b9beff714464ca386b5f62daad601d87af", - "sha256:3cfad08f16a9c6611e6143485a93de0e1e13f48cfb90bcad7d5fde1c0cec3d36", - "sha256:4e5175026618c178dfba6188228b845b64131034ab3ba52acaffa8f6c361f805", - "sha256:50979d5efff8d4135d9db293c6cb2c42260e70fb010cbc697b1311a4d7a39ddb", - "sha256:5cd187d92b6939617f1168a4fe68f68add749902c010e66fe574c165c742ed88", - "sha256:5cfca124f0ac6707747544c127880893ad72a656e136adc935c8600740b21ff5", - "sha256:5e398652d005a198a7f3c132426b33c6b85d98aa7dc852137a2a3be8890c4072", - "sha256:67cced7f15654710386e5c10b96608f1ee3d5c94ca1da5a2aad5889793a824c1", - "sha256:7306edca1c6f1b5fa0bc9aa645e6ac8393014fa82d0fa180d0ebc990ebe15964", - "sha256:7cc2c01dfc5a3cbddfa6c13f530ef3b95292f926329929001d45e124342cd6b7", - "sha256:87edfaf344c9401942883fad030909116aa77b0fa7e6e8e1c5407e14549afe9a", - "sha256:8845125d0b7c57838a10fd8925b0f5f709d0e08568ce587cc862aacce453e3dd", - "sha256:92024447a339400ea00ac228369cd242e988dd775640755fa4ac0c126e49bb74", - "sha256:a86b794e8a56ada65c573183756eac8ac5b8d3d59daf9d5ebd72ecdbb7867a43", - "sha256:bb2782a036d9eb6b5a6efcdda0986774bf798beef86a62da86cb73e2a10b423d", - "sha256:be78077064d016bc1b639c2cbcc5be945b47b4261a4f4b7d8923f6c69c5c9457", - "sha256:c7cf862aef988b5fbaa17764ad1d21b4831436701c7d2b653156a9497d92c83c", - "sha256:e0626db16705ab9f7fa6c249c017c887baf20738ce7f9129da162bb3075fc1af", - "sha256:f34495079c8d9da05b183f9f7daec2878280c2ad7cc81da686ef0b484cea2ecf", - "sha256:fe523fcbd52c05040c7bee370d66fee8373c5972171e4fbc323153433198592d" + "sha256:023fe9e618182ca6317ae89833ba422c411469156b690fde6a315ad10695a521", + "sha256:031fc69c9a7e12bcc5660b74122ed84b3f1c505e762cc4296884096c6d8ee140", + "sha256:2de7babe398cb7a85ac7f1fd5c42f396c215ab3eff731b4d761d68d0f6a80f48", + "sha256:2e93a8a553e0394b26c4ca683923b85a69f7ccdc0139e6acd1354cc884fe0128", + "sha256:390bc685ec209ada4e9d35068ac6988c60160b2b703072d2850457b62499e336", + "sha256:3a2d219775a120581a0ae8ca392b31f238d452729adbcb6892fa89688cb8306a", + "sha256:3efde4af6f2d3ccf58ae825495dbb8d74abd6d176ee686ce2ab19bd025273f41", + "sha256:4a99fe1768925e4a139aace8f3fb66db3576ee1c30b9c0f70f744ead7e329c9f", + "sha256:4b41412df69ec06ab141808d12e0bf2823717b1c363bd77b4c0820feaa37249e", + "sha256:4c8d8c6b80aa4a1689f2a179d31d86ae1367ea4a12855cc13aa3ba24bb36b2d8", + "sha256:4d19f1a239d59f10fdc31263d48b7937c585810288376671eaf75380b074f238", + "sha256:4e4a682b3f2489d218751981639cffc4e281d548f9d517addfd5a2917ac78119", + "sha256:695c45cea7e8abb6f088a34a6034b1d273122e5530aeebb9c09626cea6dca4cb", + "sha256:701189408b460a2ff42b984e6bd45c3f41f0ac9f5f58b8873bbedc511900086d", + "sha256:70894c5345bea98321a2fe84df35f43ee7bb0feec117a71420c60459fc3e1eed", + "sha256:8293a216e902ac12779eb7a08f2bc39ec6c878d7c6025aa59464e0c4c16f7eb9", + "sha256:8d26b513225ffd3eacece727f4387bdce6469192ef029ca9dd469940158bc89e", + "sha256:a197ad3a774f8e74f21e428f0de7f60ad26a8d23437b69638aac2764d1e06a6a", + "sha256:bea55fc25b96c53affab852ad94bf111a3083bc1d8b0c76a61dd101d8a388cf5", + "sha256:c9a084bce1061e55cdc0493a2ad890375af359c766b8ac311ac8120d3a472950", + "sha256:d0e9464a0af6715852267bf29c9553e4555b61f5904a4fc538547a4d67617937", + "sha256:d8e9187bfcd5ffedbe87403195e1fc340189a68463903c39e2b63307c9fa0394", + "sha256:eaeaa0888b7f3ccb7bcd40b50497ca30923dba14f385bde4af78fac713d6d6f6", + "sha256:f46af8d162f3d470d8ffc997aaf7a269996d205f9d746124a179d3abe05ac602", + "sha256:f70a40410d774ae23fcb4afbbeca652905a04de7948eaf0b1789c8d1426b72d1", + "sha256:fe91be1c51c90e2afe6827601ca14353bbf3953f343c2129fa1e247d55fd95ba" ], "index": "pypi", - "version": "==1.0.0" + "version": "==1.2.0" }, "mypy-extensions": { "hashes": [ @@ -2113,19 +2083,19 @@ }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], "markers": "python_version >= '3.7'", - "version": "==23.0" + "version": "==23.1" }, "pathspec": { "hashes": [ - "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229", - "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc" + "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687", + "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293" ], "markers": "python_version >= '3.7'", - "version": "==0.11.0" + "version": "==0.11.1" }, "pep8": { "hashes": [ @@ -2137,11 +2107,11 @@ }, "platformdirs": { "hashes": [ - "sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9", - "sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567" + "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", + "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" ], "markers": "python_version >= '3.7'", - "version": "==3.0.0" + "version": "==3.5.0" }, "pluggy": { "hashes": [ @@ -2176,11 +2146,11 @@ }, "pylint": { "hashes": [ - "sha256:bad9d7c36037f6043a1e848a43004dfd5ea5ceb05815d713ba56ca4503a9fe37", - "sha256:ffe7fa536bb38ba35006a7c8a6d2efbfdd3d95bbf21199cad31f76b1c50aaf30" + "sha256:761907349e699f8afdcd56c4fe02f3021ab5b3a0fc26d19a9bfdc66c7d0d5cd5", + "sha256:a6cbb4c6e96eab4a3c7de7c6383c512478f58f88d95764507d84c899d656a89a" ], "index": "pypi", - "version": "==2.16.1" + "version": "==2.17.3" }, "pylint-mccabe": { "hashes": [ @@ -2232,11 +2202,11 @@ }, "pytest": { "hashes": [ - "sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5", - "sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42" + "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", + "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" ], "index": "pypi", - "version": "==7.2.1" + "version": "==7.3.1" }, "pytest-cov": { "hashes": [ @@ -2264,19 +2234,19 @@ }, "pytest-sugar": { "hashes": [ - "sha256:30e5225ed2b3cc988a8a672f8bda0fc37bcd92d62e9273937f061112b3f2186d", - "sha256:c4793495f3c32e114f0f5416290946c316eb96ad5a3684dcdadda9267e59b2b8" + "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062", + "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46" ], "index": "pypi", - "version": "==0.9.6" + "version": "==0.9.7" }, "pytest-xdist": { "hashes": [ - "sha256:40fdb8f3544921c5dfcd486ac080ce22870e71d82ced6d2e78fa97c2addd480c", - "sha256:70a76f191d8a1d2d6be69fc440cdf85f3e4c03c08b520fd5dc5d338d6cf07d89" + "sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727", + "sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9" ], "index": "pypi", - "version": "==3.1.0" + "version": "==3.2.1" }, "python-dateutil": { "hashes": [ @@ -2286,29 +2256,75 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.2" }, + "pyyaml": { + "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "index": "pypi", + "version": "==6.0" + }, "redis": { "hashes": [ - "sha256:a010f6cb7378065040a02839c3f75c7e0fb37a87116fb4a95be82a95552776c7", - "sha256:e6206448e2f8a432871d07d432c13ed6c2abcf6b74edb436c99752b1371be387" + "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2", + "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893" ], "index": "pypi", - "version": "==4.4.2" + "version": "==4.5.4" }, "requests": { "hashes": [ - "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa", - "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf" + "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", + "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" ], "index": "pypi", - "version": "==2.28.2" + "version": "==2.29.0" }, "responses": { "hashes": [ - "sha256:396acb2a13d25297789a5866b4881cf4e46ffd49cc26c43ab1117f40b973102e", - "sha256:dcf294d204d14c436fddcc74caefdbc5764795a40ff4e6a7740ed8ddbf3294be" + "sha256:8a3a5915713483bf353b6f4079ba8b2a29029d1d1090a503c70b0dc5d9d0c7bd", + "sha256:c4d9aa9fc888188f0c673eff79a8dadbe2e75b7fe879dc80a221a06e0a68138f" ], "index": "pypi", - "version": "==0.22.0" + "version": "==0.23.1" }, "s3transfer": { "hashes": [ @@ -2335,27 +2351,19 @@ }, "soupsieve": { "hashes": [ - "sha256:49e5368c2cda80ee7e84da9dbe3e110b70a4575f196efb74e51b94549d921955", - "sha256:e28dba9ca6c7c00173e34e4ba57448f0688bb681b7c5e8bf4971daafc093d69a" + "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8", + "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea" ], "markers": "python_version >= '3.7'", - "version": "==2.4" + "version": "==2.4.1" }, "termcolor": { "hashes": [ - "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7", - "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a" + "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", + "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" ], "markers": "python_version >= '3.7'", - "version": "==2.2.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" + "version": "==2.3.0" }, "tomli": { "hashes": [ @@ -2367,72 +2375,65 @@ }, "tomlkit": { "hashes": [ - "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b", - "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73" + "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171", + "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3" ], - "markers": "python_version >= '3.6'", - "version": "==0.11.6" + "markers": "python_version >= '3.7'", + "version": "==0.11.8" }, "types-pyopenssl": { "hashes": [ - "sha256:6ca54d593f8b946f9570f9ed7457c41da3b518feff5e344851941a6209bea62b", - "sha256:847ab17a16475a882dc29898648a6a35ad0d3e11a5bba5aa8ab2f3435a8647cb" + "sha256:20b80971b86240e8432a1832bd8124cea49c3088c7bfc77dfd23be27ffe4a517", + "sha256:b050641aeff6dfebf231ad719bdac12d53b8ee818d4afb67b886333484629957" ], - "version": "==23.0.0.3" + "version": "==23.1.0.2" }, "types-python-dateutil": { "hashes": [ - "sha256:4a6f4cc19ce4ba1a08670871e297bf3802f55d4f129e6aa2443f540b6cf803d2", - "sha256:cfb7d31021c6bce6f3362c69af6e3abb48fe3e08854f02487e844ff910deec2a" + "sha256:355b2cb82b31e556fd18e7b074de7c350c680ab80608f0cc55ba6770d986d67d", + "sha256:fe5b545e678ec13e3ddc83a0eee1545c1b5e2fba4cfc39b276ab6f4e7604a923" ], "index": "pypi", - "version": "==2.8.19.6" + "version": "==2.8.19.12" }, "types-pyyaml": { "hashes": [ - "sha256:ade6e328a5a3df816c47c912c2e1e946ae2bace90744aa73111ee6834b03a314", - "sha256:de3bacfc4e0772d9b1baf007c37354f3c34c8952e90307d5155b6de0fc183a67" + "sha256:5aed5aa66bd2d2e158f75dda22b059570ede988559f030cf294871d3b647e3e8", + "sha256:c51b1bd6d99ddf0aa2884a7a328810ebf70a4262c292195d3f4f9a0005f9eeb6" ], "index": "pypi", - "version": "==6.0.12.4" + "version": "==6.0.12.9" }, "types-redis": { "hashes": [ - "sha256:57f8b3706afe47ef36496d70a97a3783560e6cb19e157be12985dbb31de1d853", - "sha256:8b40d6bf3a54352d4cb2aa7d01294c572a39d40a9d289b96bdf490b51d3a42d2" + "sha256:2db530f54facec3149147bfe61d5ac24f5fe4e871823d95a601cd2c1d775d8a0", + "sha256:bf04192f415b2b42ecefd70bb4b91eb0352e48f2716a213e038e35c096a639c2" ], "index": "pypi", - "version": "==4.4.0.6" + "version": "==4.5.4.1" }, "types-requests": { "hashes": [ - "sha256:19622ace35a5da1838ee9cad0df4a50c7e3a420f8a37e8357ce870fed492fa81", - "sha256:b4adf81c6bcd2f9fa0f6ab8669e96fad7f04429d6e22c486b2b3382d729ca364" + "sha256:4cf6e323e856c779fbe8815bb977a5bf5d6c5034713e4c17ff2a9a20610f5b27", + "sha256:c86f4a955d943d2457120dbe719df24ef0924e11177164d10a0373cf311d7b4d" ], "index": "pypi", - "version": "==2.28.11.11" + "version": "==2.29.0.0" }, "types-simplejson": { "hashes": [ - "sha256:857adb13190abd65d0d103be965152e79a6842c5663e01e8681bde1895713f52", - "sha256:f8a8428f753574fa3b7eb290756776f0fb6b4ad9cae72bf8c0c9534eeda6398c" + "sha256:38982991785db84c78f9012f5b9a03069ec3be020f468fb8fcd950b23e3a3341", + "sha256:64aff3d151104e0458bfc9d64ffb78efd0157ff740a9f00904a30a5465f7c013" ], "index": "pypi", - "version": "==3.18.0.0" - }, - "types-toml": { - "hashes": [ - "sha256:306b1bb8b5bbc5f1b60387dbcc4b489e79f8490ce20e93af5f422a68b470d94b", - "sha256:c8748dd225b28eb80ce712e2d7d61b57599815e7b48d07ef53df51ed148fa6b1" - ], - "version": "==0.10.8.4" + "version": "==3.19.0.0" }, "types-urllib3": { "hashes": [ - "sha256:35586727cbd7751acccf2c0f34a88baffc092f435ab62458f10776466590f2d5", - "sha256:a6c23c41bd03e542eaee5423a018f833077b51c4bf9ceb5aa544e12b812d5604" + "sha256:3ba3d3a8ee46e0d5512c6bd0594da4f10b2584b47a470f8422044a2ab462f1df", + "sha256:a1557355ce8d350a555d142589f3001903757d2d36c18a66f588d9659bbc917d" ], - "version": "==1.26.25.6" + "version": "==1.26.25.12" }, "typing-extensions": { "hashes": [ @@ -2444,89 +2445,100 @@ }, "urllib3": { "hashes": [ - "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", - "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" + "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", + "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.14" + "version": "==1.26.15" }, "werkzeug": { "hashes": [ - "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe", - "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612" + "sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a", + "sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091" ], - "markers": "python_version >= '3.7'", - "version": "==2.2.3" + "markers": "python_version >= '3.8'", + "version": "==2.3.3" }, "wrapt": { "hashes": [ - "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", - "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", - "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", - "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", - "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", - "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", - "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", - "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", - "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", - "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", - "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", - "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", - "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", - "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", - "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", - "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", - "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", - "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", - "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", - "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", - "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", - "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", - "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", - "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", - "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", - "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", - "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", - "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", - "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", - "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", - "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", - "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", - "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", - "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", - "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", - "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", - "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", - "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", - "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", - "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", - "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", - "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", - "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", - "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", - "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", - "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", - "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", - "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", - "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", - "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", - "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", - "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", - "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", - "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", - "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", - "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", - "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", - "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", - "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", - "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", - "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", - "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", - "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", - "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" + "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", + "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", + "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", + "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", + "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", + "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", + "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", + "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", + "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", + "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", + "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", + "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", + "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", + "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", + "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", + "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", + "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", + "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", + "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", + "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", + "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", + "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", + "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", + "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", + "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", + "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", + "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", + "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", + "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", + "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", + "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", + "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", + "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", + "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", + "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", + "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", + "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", + "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", + "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", + "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", + "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", + "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", + "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", + "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", + "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", + "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", + "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", + "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", + "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", + "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", + "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", + "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", + "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", + "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", + "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", + "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", + "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", + "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", + "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", + "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", + "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", + "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", + "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", + "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", + "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", + "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", + "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", + "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", + "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", + "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", + "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", + "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", + "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", + "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", + "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.14.1" + "version": "==1.15.0" }, "xmltodict": { "hashes": [ diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index 29eef569a6..ae5a286d6a 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -111,16 +111,16 @@ def section_keys( def update_section_status( self, section_status: str, section_id: str, list_item_id: Optional[str] = None - ) -> None: + ) -> bool: + updated = False section_key = (section_id, list_item_id) if section_key in self._progress: - self._progress[section_key].status = section_status - self._is_dirty = True + if self._progress[section_key].status != section_status: + updated = True + self._progress[section_key].status = section_status + self._is_dirty = True - elif ( - section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED - and section_key not in self._progress - ): + elif section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: self._progress[section_key] = Progress( section_id=section_id, list_item_id=list_item_id, @@ -129,6 +129,8 @@ def update_section_status( ) self._is_dirty = True + return updated + def get_section_status( self, section_id: str, list_item_id: Optional[str] = None ) -> str: @@ -138,8 +140,19 @@ def get_section_status( return CompletionStatus.NOT_STARTED + def get_block_status( + self, *, block_id: str, section_id: str, list_item_id: str | None = None + ) -> str: + section_blocks = self.get_completed_block_ids( + section_id=section_id, list_item_id=list_item_id + ) + if block_id in section_blocks: + return CompletionStatus.COMPLETED + + return CompletionStatus.NOT_STARTED + def get_completed_block_ids( - self, section_id: str, list_item_id: Optional[str] = None + self, *, section_id: str, list_item_id: str | None = None ) -> list[str]: section_key = (section_id, list_item_id) if section_key in self._progress: @@ -151,7 +164,9 @@ def add_completed_location(self, location: Location) -> None: section_id = location.section_id list_item_id = location.list_item_id - completed_block_ids = self.get_completed_block_ids(section_id, list_item_id) + completed_block_ids = self.get_completed_block_ids( + section_id=section_id, list_item_id=list_item_id + ) if location.block_id not in completed_block_ids: completed_block_ids.append(location.block_id) # type: ignore diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 29d7e18b20..8bfe24b0f1 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -2,7 +2,7 @@ import re from datetime import datetime from decimal import Decimal -from typing import Any, Callable, Mapping, Optional, Union +from typing import Any, Callable, Literal, Mapping, Optional, TypeAlias, Union import flask import flask_babel @@ -18,6 +18,7 @@ blueprint = flask.Blueprint("filters", __name__) FormType = Mapping[str, Mapping[str, Any]] AnswerType = Mapping[str, Any] +UnitLengthType: TypeAlias = Literal["short", "long", "narrow"] def mark_safe(context: nodes.EvalContext, value: str) -> Union[Markup, str]: @@ -69,7 +70,9 @@ def format_percentage(value: Union[int, float, Decimal]) -> str: def format_unit( - unit: str, value: Union[int, float, Decimal], length: str = "short" + unit: str, + value: int | float | Decimal, + length: UnitLengthType = "short", ) -> str: formatted_unit: str = units.format_unit( value=value, @@ -80,7 +83,7 @@ def format_unit( return formatted_unit -def format_unit_input_label(unit: str, unit_length: str = "short") -> str: +def format_unit_input_label(unit: str, unit_length: UnitLengthType = "short") -> str: """ This function is used to only get the unit of measurement text. If the unit_length is long then only the plural form of the word is returned (e.g., Hours, Years, etc). @@ -97,8 +100,9 @@ def format_unit_input_label(unit: str, unit_length: str = "short") -> str: locale=flask_babel.get_locale(), ).replace("2 ", "") else: + # Type ignore: We pass an empty string as the value so that we just return the unit label unit_label = units.format_unit( - value="", + value="", # type: ignore measurement_unit=unit, length=unit_length, locale=flask_babel.get_locale(), @@ -183,7 +187,10 @@ def get_format_date_range(start_date: Markup, end_date: Markup) -> Markup: @blueprint.app_context_processor def format_unit_processor() -> ( - dict[str, Callable[[str, Union[int, Decimal], str], str]] + dict[ + str, + Callable[[str, int | float | Decimal, UnitLengthType], str], + ] ): return {"format_unit": format_unit} diff --git a/app/publisher/publisher.py b/app/publisher/publisher.py index 11abdf72c5..6b909d0c47 100644 --- a/app/publisher/publisher.py +++ b/app/publisher/publisher.py @@ -2,6 +2,7 @@ import google.auth from google.cloud.pubsub import PublisherClient +from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1.futures import Future from structlog import get_logger @@ -21,7 +22,7 @@ def __init__(self): self._client = PublisherClient() _, self._project_id = google.auth.default() - def _publish(self, topic_id, message): + def _publish(self, topic_id, message) -> "publisher.futures.Future": logger.info("publishing message", topic_id=topic_id) # pylint: disable=no-member topic_path = self._client.topic_path(self._project_id, topic_id) diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index 1fc380ea7a..c79ecc1009 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Mapping, Sequence +from ordered_set import OrderedSet from werkzeug.datastructures import MultiDict from app.data_models import ProgressStore @@ -30,7 +31,7 @@ def get_block_ids_for_calculated_summary_dependencies( ] if block_id := location.block_id: - dependents = dependent_sections[block_id] + dependents = OrderedSet(dependent_sections[block_id]) else: dependents = get_flattened_mapping_values(dependent_sections) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 47583b3fe4..f09a19a247 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -44,7 +44,7 @@ def routing_path( if section: when_rules_block_dependencies = self.get_when_rules_block_dependencies( - section["id"] + section_id ) blocks = self._get_not_skipped_blocks_in_section( current_location, @@ -64,11 +64,13 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: """NB: At present when rules block dependencies does not fully support repeating sections. It is supported when the section is dependent i.e. the current section is repeating and building the routing path for sections that are not, It isn't supported if it needs to build the path for repeating sections""" + dependencies_for_section = ( + self.schema.get_all_when_rules_section_dependencies_for_section(section_id) + ) + return [ block_id - for dependent_section in self.schema.when_rules_section_dependencies_by_section.get( - section_id, {} - ) + for dependent_section in dependencies_for_section for block_id in self.routing_path(dependent_section) if (dependent_section, None) in self.progress_store.started_section_keys() ] @@ -194,9 +196,9 @@ def _evaluate_routing_rules( self.list_store, self.metadata, self.response_metadata, + progress_store=self.progress_store, location=this_location, routing_path_block_ids=routing_path_block_ids, - progress_store=self.progress_store, ) for rule in routing_rules: rule_valid = ( diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index f196f9ef1e..8c2ef0d43d 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -3,6 +3,8 @@ from decimal import Decimal from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Sequence, TypeAlias +from ordered_set import OrderedSet + from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -105,7 +107,7 @@ def __call__( return self._placeholder_map def _get_value_source_resolver( - self, routing_path_block_ids: set[str] | None = None + self, routing_path_block_ids: OrderedSet[str] | None = None ) -> ValueSourceResolver: return ValueSourceResolver( answer_store=self._answer_store, diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 5cdd6a45d8..7b2c660a71 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -1,6 +1,6 @@ from datetime import date, datetime, timezone from decimal import Decimal -from typing import TYPE_CHECKING, Sequence, Sized +from typing import TYPE_CHECKING, Literal, Sequence, Sized from urllib.parse import quote from babel import units @@ -41,9 +41,7 @@ def __init__( input_date_format = "%Y-%m-%d" - def format_currency( - self, number: float | str | None = None, currency: str = "GBP" - ) -> str: + def format_currency(self, number: Decimal | float, currency: str = "GBP") -> str: formatted_currency: str = format_currency(number, currency, locale=self.locale) return formatted_currency @@ -137,8 +135,8 @@ def format_percentage(value: int | Decimal | str) -> str: def format_unit( self, unit: str, - value: int | Decimal | str, - unit_length: str | None = None, + value: int | Decimal, + unit_length: Literal["short", "long", "narrow"] | None = None, ) -> str: length = unit_length or "short" formatted_unit: str = units.format_unit( diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 17ec2348de..2f57e8874f 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -1,10 +1,12 @@ +# pylint: disable=too-many-lines from collections import defaultdict from copy import deepcopy from dataclasses import dataclass from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, Sequence +from typing import Any, Generator, Iterable, Mapping, Sequence, TypeAlias from flask_babel import force_locale +from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict, MultiDict from app.data_models.answer import Answer @@ -12,7 +14,7 @@ from app.questionnaire.rules.operator import OPERATION_MAPPING from app.questionnaire.schema_utils import get_answers_from_question from app.utilities.make_immutable import make_immutable -from app.utilities.mappings import get_mappings_with_key +from app.utilities.mappings import get_flattened_mapping_values, get_mappings_with_key DEFAULT_LANGUAGE_CODE = "en" @@ -27,6 +29,8 @@ QuestionSchemaType = Mapping +DependencyDictType: TypeAlias = dict[str, OrderedSet[str]] + class InvalidSchemaConfigurationException(Exception): pass @@ -59,9 +63,19 @@ def __init__( set ) self._when_rules_section_dependencies_by_section: dict[str, set[str]] = {} + self._when_rules_section_dependencies_by_section_for_progress_value_source: defaultdict[ + str, OrderedSet[str] + ] = defaultdict( + OrderedSet + ) + self._when_rules_block_dependencies_by_section_for_progress_value_source: defaultdict[ + str, DependencyDictType + ] = defaultdict( + lambda: defaultdict(OrderedSet) + ) self.calculated_summary_section_dependencies_by_block: dict[ - str, dict[str, set[str]] - ] = defaultdict(lambda: defaultdict(set)) + str, DependencyDictType + ] = defaultdict(lambda: defaultdict(OrderedSet)) self._when_rules_section_dependencies_by_answer: dict[ str, set[str] ] = defaultdict(set) @@ -91,6 +105,56 @@ def when_rules_section_dependencies_by_section( ) -> ImmutableDict[str, set[str]]: return ImmutableDict(self._when_rules_section_dependencies_by_section) + @cached_property + def when_rules_section_dependencies_for_progress( + self, + ) -> ImmutableDict[str, set[str]]: + """ + This method flips the dependencies that were captured for progress value sources so that they can be + evaluated properly for when rules, this is because for when rules we need to check for dependencies in + previous sections, whereas for progress we are checking for dependent blocks/sections in "future" sections + """ + when_rules_section_dependencies_for_progress = defaultdict(set) + for ( + section, + dependent, + ) in ( + self._when_rules_block_dependencies_by_section_for_progress_value_source.items() + ): + section_dependents = get_flattened_mapping_values(dependent) + for dependent_section in section_dependents: + when_rules_section_dependencies_for_progress[dependent_section].add( + section + ) + + for ( + section, + dependents, + ) in ( + self._when_rules_section_dependencies_by_section_for_progress_value_source.items() + ): + for dependent_section in dependents: + when_rules_section_dependencies_for_progress[dependent_section].add( + section + ) + return ImmutableDict(when_rules_section_dependencies_for_progress) + + @cached_property + def when_rules_section_dependencies_by_section_for_progress_value_source( + self, + ) -> ImmutableDict[str, OrderedSet[str]]: + return ImmutableDict( + self._when_rules_section_dependencies_by_section_for_progress_value_source + ) + + @cached_property + def when_rules_block_dependencies_by_section_for_progress_value_source( + self, + ) -> ImmutableDict[str, DependencyDictType]: + return ImmutableDict( + self._when_rules_block_dependencies_by_section_for_progress_value_source + ) + @cached_property def when_rules_section_dependencies_by_answer(self) -> ImmutableDict[str, set[str]]: return ImmutableDict(self._when_rules_section_dependencies_by_answer) @@ -168,6 +232,20 @@ def is_view_submitted_response_enabled(self) -> bool: is_enabled: bool = schema.get("view_response", False) return is_enabled + def get_all_when_rules_section_dependencies_for_section( + self, section_id: str + ) -> set[str]: + all_section_dependencies = self.when_rules_section_dependencies_by_section.get( + section_id, set() + ) + + if progress_dependencies := self.when_rules_section_dependencies_for_progress.get( + section_id + ): + all_section_dependencies.update(progress_dependencies) + + return all_section_dependencies + def _get_sections_by_id(self) -> dict[str, ImmutableDict]: return { section["id"]: section @@ -419,6 +497,9 @@ def get_summary_options(self) -> ImmutableDict[str, bool]: def get_sections(self) -> Iterable[ImmutableDict]: return self._sections_by_id.values() + def get_section_ids(self) -> Iterable[str]: + return self._sections_by_id.keys() + def get_section(self, section_id: str) -> ImmutableDict | None: return self._sections_by_id.get(section_id) @@ -887,21 +968,115 @@ def _get_error_messages(self) -> dict: return messages def _populate_when_rules_section_dependencies(self) -> None: + """ + Populates section dependencies for when rules, including when rules containing + progress value sources. + Progress section dependencies by section are directly populated in this method. + Progress section dependencies by block are populated in the + `self._populate_block_dependencies_for_progress_value_source` called here. + """ + progress_section_dependencies = ( + self._when_rules_section_dependencies_by_section_for_progress_value_source + ) + for section in self.get_sections(): when_rules = self.get_values_for_key(section, "when") rules: list = list(when_rules) - if rules_section_dependencies := self._get_rules_section_dependencies( - section["id"], rules - ): + ( + rules_section_dependencies, + rule_section_dependencies_for_progress_value_source, + rule_block_dependencies_for_progress_value_source, + ) = self._get_rules_section_dependencies(section["id"], rules) + + if rules_section_dependencies: self._when_rules_section_dependencies_by_section[ section["id"] ] = rules_section_dependencies + for ( + key, + values, + ) in rule_section_dependencies_for_progress_value_source.items(): + progress_section_dependencies[key].update(values) + + self._populate_block_dependencies_for_progress_value_source( + rule_block_dependencies_for_progress_value_source + ) + + def _populate_block_dependencies_for_progress_value_source( + self, + rule_block_dependencies_for_progress_value_source: dict[ + str, DependencyDictType + ], + ) -> None: + """ + Populates section dependencies for progress value sources at the block level + """ + dependencies = ( + self._when_rules_block_dependencies_by_section_for_progress_value_source + ) + for ( + dependent_section, + section_dependencies_by_block, + ) in rule_block_dependencies_for_progress_value_source.items(): + for block_id, section_ids in section_dependencies_by_block.items(): + dependencies[dependent_section][block_id].update(section_ids) + + def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( + self, current_section_id: str, rule: Mapping + ) -> tuple[set[str], dict[str, dict[str, OrderedSet[str] | DependencyDictType]]]: + """ + For a given rule, returns a set of dependent answer ids and any dependent sections for progress value sources. + Progress dependencies are keyed both by section and by block e.g. + sections: {"section-1": {"section-2"}} + blocks: {"section-1": {"block-1": {"section-2"}}} + """ + answer_id_list: set[str] = set() + dependencies_ids_for_progress_value_source: dict[ + str, dict[str, OrderedSet[str] | DependencyDictType] + ] = { + "sections": {}, + "blocks": {}, + } + identifier: str | None = rule.get("identifier") + source: str | None = rule.get("source") + selector: str | None = rule.get("selector") + + if source == "answers" and identifier: + answer_id_list.add(identifier) + elif source == "calculated_summary" and identifier: + calculated_summary_block = self.get_block(identifier) + # Type Ignore: Calculated summary block will exist at this point + calculated_summary_answer_ids = get_calculated_summary_answer_ids( + calculated_summary_block # type: ignore + ) + answer_id_list.update(calculated_summary_answer_ids) + elif source == "progress" and identifier: + if selector == "section" and identifier != current_section_id: + # Type ignore: Added as this will be a set rather than a dict at this point + dependencies_ids_for_progress_value_source["sections"][identifier] = OrderedSet([current_section_id]) # type: ignore + elif selector == "block" and ( + section_id := self.get_section_id_for_block_id(identifier) + ): + # Type ignore: The identifier key will return a list + if section_id != current_section_id: + dependencies_ids_for_progress_value_source["blocks"][section_id] = { + identifier: OrderedSet() + } + dependencies_ids_for_progress_value_source["blocks"][section_id][identifier].add(current_section_id) # type: ignore + + return answer_id_list, dependencies_ids_for_progress_value_source + def _get_rules_section_dependencies( self, current_section_id: str, rules: Mapping | Sequence - ) -> set[str]: - rules_section_dependencies: set[str] = set() + ) -> tuple[set[str], DependencyDictType, dict[str, DependencyDictType]]: + """ + Returns a set of sections ids that the current sections depends on. + """ + section_dependencies: set[str] = set() + section_dependencies_for_progress_value_source: dict = {} + block_dependencies_for_progress_value_source: dict = {} if isinstance(rules, Mapping) and QuestionnaireSchema.has_operator(rules): rules = self.get_operands(rules) @@ -910,18 +1085,21 @@ def _get_rules_section_dependencies( if not isinstance(rule, Mapping): continue - answer_id_list: list = [] - identifier: str | None = rule.get("identifier") - source: str | None = rule.get("source") - - if source == "answers" and identifier: - answer_id_list.append(identifier) - elif source == "calculated_summary" and identifier: - calculated_summary_block = self.get_block(identifier) - calculated_summary_answer_ids = get_calculated_summary_answer_ids( - calculated_summary_block # type: ignore - ) - answer_id_list.extend(iter(calculated_summary_answer_ids)) + [ + answer_id_list, + dependencies_for_progress_value_source, + ] = self._get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( + current_section_id, rule + ) + + section_dependencies_for_progress_value_source.update( + dependencies_for_progress_value_source["sections"] + ) + block_dependencies_for_progress_value_source.update( + dependencies_for_progress_value_source["blocks"] + ) + + # Type Ignore: Added to this method as the block will exist at this point for answer_id in answer_id_list: block = self.get_block_for_answer_id(answer_id) # type: ignore section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore @@ -930,14 +1108,27 @@ def _get_rules_section_dependencies( self._when_rules_section_dependencies_by_answer[answer_id].add( current_section_id ) - rules_section_dependencies.add(section_id) # type: ignore + section_dependencies.add(section_id) # type: ignore if QuestionnaireSchema.has_operator(rule): - rules_section_dependencies.update( - self._get_rules_section_dependencies(current_section_id, rule) + ( + nested_section_dependencies, + nested_section_dependencies_for_progress_value_source, + nested_block_dependencies_for_progress_value_source, + ) = self._get_rules_section_dependencies(current_section_id, rule) + section_dependencies.update(nested_section_dependencies) + section_dependencies_for_progress_value_source |= ( + nested_section_dependencies_for_progress_value_source + ) + block_dependencies_for_progress_value_source |= ( + nested_block_dependencies_for_progress_value_source ) - return rules_section_dependencies + return ( + section_dependencies, + section_dependencies_for_progress_value_source, + block_dependencies_for_progress_value_source, + ) def _populate_calculated_summary_section_dependencies(self) -> None: for section in self.get_sections(): diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 72d748cbff..2e9b80fa41 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -2,6 +2,7 @@ from itertools import combinations from typing import Any, Iterable, Mapping +from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict from app.data_models import AnswerValueTypes, QuestionnaireStore @@ -48,13 +49,13 @@ def save(self) -> None: self._questionnaire_store.save() def is_dirty(self) -> bool: - if ( - self._answer_store.is_dirty - or self._list_store.is_dirty - or self._progress_store.is_dirty - ): - return True - return False + return bool( + ( + self._answer_store.is_dirty + or self._list_store.is_dirty + or self._progress_store.is_dirty + ) + ) def update_relationships_answer( self, @@ -69,16 +70,15 @@ def update_relationships_answer( def remove_completed_relationship_locations_for_list_name( self, list_name: str ) -> None: - target_relationship_collectors = self._get_relationship_collectors_by_list_name( + if target_relationship_collectors := self._get_relationship_collectors_by_list_name( list_name - ) - if target_relationship_collectors: + ): for target in target_relationship_collectors: block_id = target["id"] section_id = self._schema.get_section_for_block_id(block_id)["id"] # type: ignore self.remove_completed_location(Location(section_id, block_id)) - def update_relationship_question_completeness(self, list_name: str) -> None: + def _update_relationship_question_completeness(self, list_name: str) -> None: relationship_collectors = self._get_relationship_collectors_by_list_name( list_name ) @@ -92,11 +92,9 @@ def update_relationship_question_completeness(self, list_name: str) -> None: relationship_answer_id = self._schema.get_first_answer_id_for_block( collector["id"] ) - relationship_answers = self._get_relationships_in_answer_store( + if relationship_answers := self._get_relationships_in_answer_store( relationship_answer_id - ) - - if relationship_answers: + ): pairs = { (answer["list_item_id"], answer["to_list_item_id"]) for answer in relationship_answers @@ -147,8 +145,7 @@ def remove_primary_person(self, list_name: str) -> None: """Remove the primary person and all of their answers. Any context for the primary person will be removed """ - list_item_id = self._list_store[list_name].primary_person - if list_item_id: + if list_item_id := self._list_store[list_name].primary_person: self.remove_list_item_and_answers(list_name, list_item_id) def remove_list_item_and_answers(self, list_name: str, list_item_id: str) -> None: @@ -161,10 +158,9 @@ def remove_list_item_and_answers(self, list_name: str, list_item_id: str) -> Non list_item_id=list_item_id ) - answers = self.get_relationship_answers_for_list_name(list_name) - if answers: - self.remove_relationship_answers_for_list_item_id(list_item_id, answers) - self.update_relationship_question_completeness(list_name) + if answers := self.get_relationship_answers_for_list_name(list_name): + self._remove_relationship_answers_for_list_item_id(list_item_id, answers) + self._update_relationship_question_completeness(list_name) self._progress_store.remove_progress_for_list_item_id(list_item_id=list_item_id) @@ -220,7 +216,7 @@ def update_same_name_items( list_model.same_name_items = list(same_name_items) # type: ignore - def remove_relationship_answers_for_list_item_id( + def _remove_relationship_answers_for_list_item_id( self, list_item_id: str, answers: list ) -> None: for answer in answers: @@ -243,11 +239,13 @@ def remove_completed_location(self, location: Location | None = None) -> bool: def update_section_status( self, is_complete: bool, section_id: str, list_item_id: str | None = None - ) -> None: + ) -> bool: status = ( CompletionStatus.COMPLETED if is_complete else CompletionStatus.IN_PROGRESS ) - self._progress_store.update_section_status(status, section_id, list_item_id) + return self._progress_store.update_section_status( + status, section_id, list_item_id + ) def _update_answer( self, @@ -342,6 +340,44 @@ def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: else: self.dependent_sections.add(DependentSection(section_id, None, None)) + def _capture_section_dependencies_progress_value_source_for_current_section( + self, + ) -> None: + """ + Captures a unique list of section ids that are dependents of the current section, for progress value sources. + """ + dependent_sections: Iterable = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( + self._current_location.section_id, set() + ) + self._update_section_dependencies(dependent_sections) + + def _capture_section_dependencies_progress_value_source_for_current_block( + self, + ) -> None: + """ + Captures a unique list of section ids that are dependents of the current block, for progress value sources. + """ + # Type ignore: Added as block_id will exist at this point + dependent_sections: Iterable = self._schema.when_rules_block_dependencies_by_section_for_progress_value_source.get( + self._current_location.section_id, {} + ).get( + self._current_location.block_id, set() # type: ignore + ) + + self._update_section_dependencies(dependent_sections) + + def _update_section_dependencies(self, dependent_sections: Iterable) -> None: + for section_id in dependent_sections: + if repeating_list := self._schema.get_repeating_list_for_section( + section_id + ): + for list_item_id in self._list_store[repeating_list].items: + self.dependent_sections.add( + DependentSection(section_id, list_item_id, None) + ) + else: + self.dependent_sections.add(DependentSection(section_id, None, None)) + def update_answers( self, form_data: Mapping[str, Any], list_item_id: str | None = None ) -> None: @@ -358,41 +394,103 @@ def update_answers( answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id list_item_id_to_use = resolved_answer.get("list_item_id") or list_item_id - answer_updated = self._update_answer( - answer_id_to_use, list_item_id_to_use, answer_value - ) - if answer_updated: + if self._update_answer(answer_id_to_use, list_item_id_to_use, answer_value): self._capture_section_dependencies_for_answer(answer_id_to_use) self._capture_block_dependencies_for_answer(answer_id_to_use) + self.capture_progress_section_dependencies() + + def capture_progress_section_dependencies(self) -> None: + self._capture_section_dependencies_progress_value_source_for_current_block() + self._capture_section_dependencies_progress_value_source_for_current_section() def update_progress_for_dependent_sections(self) -> None: - """Updates the progress to IN_PROGRESS. Section progress is not updated for the current location as it is handled by `handle_post` on block handlers.""" + """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. + Section progress is not updated for the current location as it is handled by `handle_post` on block handlers. + """ + evaluated_dependents: list[tuple] = [] + + chronological_dependents = self.get_chronological_section_dependents() - for section in self.dependent_sections: + for section in chronological_dependents: if ( section.section_id, section.list_item_id, ) not in self.started_section_keys(): continue - is_path_complete = section.is_complete - if is_path_complete is None: - is_path_complete = self._router.is_path_complete( - self._router.routing_path( - section.section_id, list_item_id=section.list_item_id - ) + if ( + section.section_id, + section.list_item_id, + ) not in evaluated_dependents: + self._evaluate_dependents( + dependent_section=section, evaluated_dependents=evaluated_dependents + ) + evaluated_dependents.append((section.section_id, section.list_item_id)) + + def _evaluate_dependents( + self, + *, + dependent_section: DependentSection, + evaluated_dependents: list[tuple], + ) -> None: + is_path_complete = dependent_section.is_complete + if is_path_complete is None: + is_path_complete = self._router.is_path_complete( + self._router.routing_path( + dependent_section.section_id, + list_item_id=dependent_section.list_item_id, ) + ) - self.update_section_status( - is_complete=is_path_complete, - section_id=section.section_id, - list_item_id=section.list_item_id, + if self.update_section_status( + is_complete=is_path_complete, + section_id=dependent_section.section_id, + list_item_id=dependent_section.list_item_id, + ): + dependents_of_dependent: OrderedSet = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( + dependent_section.section_id, OrderedSet() ) + for dependent_section_id in dependents_of_dependent: + if repeating_list := self._schema.get_repeating_list_for_section( + dependent_section_id + ): + for item_id in self._list_store[repeating_list].items: + if ( + dependent_section_id, + item_id, + ) not in evaluated_dependents: + self._evaluate_dependent_of_dependents( + dependent_section_id=dependent_section_id, + list_item_id=item_id, + evaluated_dependents=evaluated_dependents, + ) + elif ( + dependent_section_id, + dependent_section.list_item_id, + ) not in evaluated_dependents: + self._evaluate_dependent_of_dependents( + dependent_section_id=dependent_section_id, + evaluated_dependents=evaluated_dependents, + ) + + def _evaluate_dependent_of_dependents( + self, + dependent_section_id: str, + evaluated_dependents: list[tuple], + list_item_id: str | None = None, + ) -> None: + self._evaluate_dependents( + dependent_section=DependentSection( + section_id=dependent_section_id, + list_item_id=list_item_id, + is_complete=None, + ), + evaluated_dependents=evaluated_dependents, + ) + evaluated_dependents.append((dependent_section_id, list_item_id)) def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: - """Removes dependent blocks from the progress store. - This must be called before updating section progress (update_progress_for_dependent_sections) and section dependencies (_update_section_completeness) - """ + """Removes dependent blocks from the progress store.""" for ( section_key, @@ -430,3 +528,9 @@ def started_section_keys( self, section_ids: Iterable[str] | None = None ) -> list[SectionKeyType]: return self._progress_store.started_section_keys(section_ids) + + def get_chronological_section_dependents(self) -> list: + sections = list(self._schema.get_section_ids()) + return sorted( + self.dependent_sections, key=lambda x: sections.index(x.section_id) + ) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 8fa1692329..b5ab65e7d5 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -320,7 +320,7 @@ def is_block_complete( self, *, block_id: str, section_id: str, list_item_id: str | None ) -> bool: return block_id in self._progress_store.get_completed_block_ids( - section_id, list_item_id + section_id=section_id, list_item_id=list_item_id ) def _get_first_incomplete_location_in_section( @@ -387,9 +387,10 @@ def _is_section_enabled(self, section: Mapping) -> bool: return True enabled = section["enabled"] + section_id = section["id"] routing_path_block_ids = self._path_finder.get_when_rules_block_dependencies( - section["id"] + section_id ) when_rule_evaluator = RuleEvaluator( @@ -399,7 +400,7 @@ def _is_section_enabled(self, section: Mapping) -> bool: metadata=self._metadata, response_metadata=self._response_metadata, progress_store=self._progress_store, - location=None, + location=Location(section_id=section_id), routing_path_block_ids=routing_path_block_ids, ) diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 8173521a82..241d3fa6bf 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -45,8 +45,8 @@ def __post_init__(self) -> None: location=self.location, list_item_id=list_item_id, routing_path_block_ids=self.routing_path_block_ids, - use_default_answer=True, progress_store=self.progress_store, + use_default_answer=True, ) renderer: PlaceholderRenderer = PlaceholderRenderer( diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 819d9f3ce8..ed2be5bfd8 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -37,10 +37,15 @@ class ValueSourceResolver: def _is_answer_on_path(self, answer_id: str) -> bool: if self.routing_path_block_ids: block = self.schema.get_block_for_answer_id(answer_id) - return block is not None and block["id"] in self.routing_path_block_ids - + return block is not None and self._is_block_on_path(block["id"]) return True + def _is_block_on_path(self, block_id: str) -> bool: + return ( + self.routing_path_block_ids is not None + and block_id in self.routing_path_block_ids + ) + def _get_answer_value( self, answer_id: str, @@ -116,6 +121,36 @@ def _resolve_answer_value_source( return answer_value + def _resolve_progress_value_source( + self, value_source: Mapping + ) -> ValueSourceEscapedTypes | ValueSourceTypes | None: + identifier = value_source["identifier"] + selector = value_source["selector"] + if selector == "section": + # List item id is set to None here as we do not support checking progress value sources for + # repeating sections + return self.progress_store.get_section_status( + section_id=identifier, list_item_id=None + ) + + if selector == "block": + if not self.location: + raise ValueError("location is required to resolve block progress") + + if not self._is_block_on_path(identifier): + return None + + # Type ignore: Section id will exist at this point + section_id_for_block: str = self.schema.get_section_id_for_block_id(identifier) # type: ignore + + return self.progress_store.get_block_status( + block_id=identifier, + section_id=section_id_for_block, + list_item_id=self.location.list_item_id + if self.location.section_id == section_id_for_block + else None, + ) + def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: identifier = value_source["identifier"] list_model: ListModel = self.list_store[identifier] @@ -154,13 +189,26 @@ def _resolve_calculated_summary_value_source( self.list_store, self.metadata, self.response_metadata, + progress_store=self.progress_store, location=self.location, routing_path_block_ids=self.routing_path_block_ids, - progress_store=self.progress_store, ) return evaluator.evaluate(calculation["operation"]) # type: ignore + def _resolve_metadata_source(self, value_source: Mapping) -> str | None: + if not self.metadata: + raise NoMetadataException + identifier = value_source["identifier"] + return self.metadata[identifier] + + def _resolve_location_source(self, value_source: Mapping) -> str | None: + if value_source.get("identifier") == "list_item_id": + return self.list_item_id + + def _resolve_response_metadata_source(self, value_source: Mapping) -> str | None: + return self.response_metadata.get(value_source.get("identifier")) + @staticmethod def get_calculation_operator( calculation_type: str, @@ -175,29 +223,18 @@ def resolve( ) -> ValueSourceEscapedTypes | ValueSourceTypes: source = value_source["source"] - # We always need to assess the routing path for calculated summary value sources - # as they may contain answers that are not on the path - if source == "answers": - return self._resolve_answer_value_source(value_source) - - if source == "list": - return self._resolve_list_value_source(value_source) - - if source == "metadata": - if not self.metadata: - raise NoMetadataException - identifier = value_source["identifier"] - return self.metadata[identifier] - - if source == "location" and value_source.get("identifier") == "list_item_id": - # This does not use the location object because - # routes such as individual response does not have the concept of location. - return self.list_item_id - - if source == "response_metadata": - return self.response_metadata.get(value_source["identifier"]) - if source == "calculated_summary": return self._resolve_calculated_summary_value_source( - value_source, assess_routing_path=True + value_source=value_source, assess_routing_path=True ) + + resolve_method_mapping = { + "answers": self._resolve_answer_value_source, + "list": self._resolve_list_value_source, + "metadata": self._resolve_metadata_source, + "location": self._resolve_location_source, + "response_metadata": self._resolve_response_metadata_source, + "progress": self._resolve_progress_value_source, + } + + return resolve_method_mapping[source](value_source) diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 74e75b68b8..c9803a33f8 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -16,7 +16,7 @@ class BusinessSurveyConfig(SurveyConfig): footer_links: Iterable[MutableMapping] = field(default_factory=list) footer_legal_links: Iterable[Mapping] = field(default_factory=list) - def __post_init__(self): + def __post_init__(self) -> None: self.base_url = self._stripped_base_url super().__post_init__() diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 4b21e7cb97..30bd716b82 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -17,7 +17,7 @@ class SocialSurveyConfig( footer_links: Iterable[MutableMapping] = field(default_factory=list) footer_legal_links: Iterable[Mapping] = field(default_factory=list) - def __post_init__(self): + def __post_init__(self) -> None: super().__post_init__() upstream_base_url = f"{self.base_url}/{self.language_code}" diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index b945911400..c9cd640d84 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -40,7 +40,7 @@ class SurveyConfig: privacy_and_data_protection_url: str = field(init=False) language_code: Optional[str] = None - def __post_init__(self): + def __post_init__(self) -> None: self.contact_us_url: str = f"{self.base_url}/contact-us/" self.cookie_settings_url: str = f"{self.base_url}/cookies/" self.cookie_domain: str = self.cookie_settings_url.split("://")[-1].split("/")[ diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 9fcab41dd0..e53387de23 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -15,28 +15,28 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.11.0\n" +"Generated-By: Babel 2.12.1\n" -#: app/forms/validators.py:377 app/jinja_filters.py:115 +#: app/forms/validators.py:377 app/jinja_filters.py:119 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:381 app/jinja_filters.py:123 +#: app/forms/validators.py:381 app/jinja_filters.py:127 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" msgstr[0] "" msgstr[1] "" -#: app/jinja_filters.py:165 +#: app/jinja_filters.py:169 #, python-format msgid "%(date)s at %(time)s" msgstr "" -#: app/jinja_filters.py:179 +#: app/jinja_filters.py:183 #, python-format msgid "%(from_date)s to %(to_date)s" msgstr "" @@ -231,19 +231,19 @@ msgid "" "browser if using a shared device" msgstr "" -#: app/questionnaire/placeholder_transforms.py:160 +#: app/questionnaire/placeholder_transforms.py:158 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:166 +#: app/questionnaire/placeholder_transforms.py:164 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:171 +#: app/questionnaire/placeholder_transforms.py:169 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "" diff --git a/app/utilities/mappings.py b/app/utilities/mappings.py index 63dc6eba28..7069a25155 100644 --- a/app/utilities/mappings.py +++ b/app/utilities/mappings.py @@ -1,10 +1,12 @@ from typing import Generator, Iterable, Mapping, Sequence +from ordered_set import OrderedSet + def get_flattened_mapping_values( map_to_flatten: Mapping[tuple, Iterable[str]] | Mapping[str, Iterable[str]] -) -> set[str]: - return {x for v in map_to_flatten.values() for x in v} +) -> OrderedSet[str]: + return OrderedSet([x for v in map_to_flatten.values() for x in v]) def get_mappings_with_key( # noqa: C901 pylint: disable=too-complex diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 50fc424435..bbe379ed59 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -4,6 +4,7 @@ Any, Callable, Iterable, + Literal, Mapping, MutableMapping, Optional, @@ -254,7 +255,8 @@ def _get_answer_format(self, groups: list) -> Tuple[dict[str, Any], list]: @staticmethod def _format_total( - answer_format: Mapping[str, str], total: Union[int, float, Decimal] + answer_format: Mapping[str, Literal["short", "long", "narrow"]], + total: int | float | Decimal, ) -> str: if answer_format["type"] == "currency": return get_formatted_currency(total, answer_format["currency"]) diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index 86fe3e0e21..2c275f3434 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -61,7 +61,7 @@ def _build_link( return_to_block_id: Optional[str], ) -> str: return url_for( - "questionnaire.block", + endpoint="questionnaire.block", list_name=list_name, block_id=block_id, list_item_id=list_item_id, diff --git a/app/views/handlers/calculated_summary.py b/app/views/handlers/calculated_summary.py index 37b5cbb2a0..325ada18e1 100644 --- a/app/views/handlers/calculated_summary.py +++ b/app/views/handlers/calculated_summary.py @@ -21,3 +21,10 @@ def get_context(self): self.page_title = context["summary"]["calculated_question"]["title"] return context + + def handle_post(self): + # We prematurely set the current as complete, so that dependent sections can be updated accordingly + self.questionnaire_store_updater.add_completed_location() + # Then we update dependent sections + self.questionnaire_store_updater.capture_progress_section_dependencies() + return super().handle_post() diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index c4872aaef2..69aa7ce2e4 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -577,7 +577,7 @@ def handle_post(self) -> Response | None: def _update_section_completeness(self): if not self._questionnaire_store.progress_store.get_completed_block_ids( - self.individual_section_id, self._list_item_id + section_id=self.individual_section_id, list_item_id=self._list_item_id ): status = CompletionStatus.NOT_STARTED else: diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index 010defc49e..5083de11c4 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -91,7 +91,6 @@ def handle_post(self): self._routing_path = self.router.routing_path( self.current_location.section_id, self.current_location.list_item_id ) - self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 4299047179..483da976f7 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -211,6 +211,9 @@ def handle_post(self): # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) if self.questionnaire_store_updater.is_dirty(): + # We prematurely complete the block, as we need it completed to build the routing path + # In order to support progress value source references of the previous block + self.questionnaire_store_updater.add_completed_location() self._routing_path = self.router.routing_path( section_id=self._current_location.section_id, list_item_id=self._current_location.list_item_id, diff --git a/schemas/test/en/test_progress_block_value_source_repeating_sections.json b/schemas/test/en/test_progress_block_value_source_repeating_sections.json new file mode 100644 index 0000000000..605565a303 --- /dev/null +++ b/schemas/test/en/test_progress_block_value_source_repeating_sections.json @@ -0,0 +1,392 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "survey_id": "139", + "theme": "default", + "title": "Progress Value Source Repeating Sections Test", + "data_version": "0.0.3", + "description": "Progress Value Source Repeating Sections Test", + "navigation": { + "visible": true + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "List collector + random question", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "question-block", + "question": { + "id": "question", + "title": "Question", + "description": ["The next question is used as a dependency in the repeating sections."], + "type": "General", + "answers": [ + { + "id": "answer", + "mandatory": false, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "random-question-enabler-block", + "question": { + "id": "random-question-enabler-question", + "title": "Random question enabler", + "description": [ + "Answering this question will enable the random question in the repeated section coming after the list collector." + ], + "type": "General", + "answers": [ + { + "id": "random-question-enabler-answer", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Questions", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "dob-group", + "title": "Date of birth", + "blocks": [ + { + "type": "Question", + "id": "dob-block", + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": false, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "other-question-block", + "question": { + "id": "other-question", + "answers": [ + { + "id": "other-answer", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Random question about {person_name_possessive}" + }, + "description": ["Shows because the random question was completed in section 1"], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "random-question-enabler-block" + }, + "COMPLETED" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_section_value_source_repeating_sections.json b/schemas/test/en/test_progress_section_value_source_repeating_sections.json new file mode 100644 index 0000000000..4f7c41b55c --- /dev/null +++ b/schemas/test/en/test_progress_section_value_source_repeating_sections.json @@ -0,0 +1,392 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "survey_id": "139", + "theme": "default", + "title": "Progress Value Source Repeating Sections Test", + "data_version": "0.0.3", + "description": "Progress Value Source Repeating Sections Test", + "navigation": { + "visible": true + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "List collector + random question", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "question-block", + "question": { + "id": "question", + "title": "Question", + "description": ["The next question is used as a dependency in the repeating sections."], + "type": "General", + "answers": [ + { + "id": "answer", + "mandatory": false, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "random-question-enabler-block", + "question": { + "id": "random-question-enabler-question", + "title": "Random question enabler", + "description": [ + "Answering this question will enable the random question in the repeated section coming after the list collector." + ], + "type": "General", + "answers": [ + { + "id": "random-question-enabler-answer", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Questions", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "dob-group", + "title": "Date of birth", + "blocks": [ + { + "type": "Question", + "id": "dob-block", + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": false, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "other-question-block", + "question": { + "id": "other-question", + "answers": [ + { + "id": "other-answer", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Random question about {person_name_possessive}" + }, + "description": ["Shows because the random question was completed in section 1"], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-1" + }, + "COMPLETED" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_blocks.json b/schemas/test/en/test_progress_value_source_blocks.json new file mode 100644 index 0000000000..3a10d2a382 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_blocks.json @@ -0,0 +1,210 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Test progress value source", + "theme": "default", + "description": "A test survey for testing progres value source referencing blocks", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section-1", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s1-b1", + "question": { + "id": "s1-b1-q1", + "title": "Section 1 Question 1", + "description": [ + "If you answer 0, then the second question will be skipped because of a routing rule, as well as the fourth question because of a skip condition referencing the progress of question 2, as well as the 6th question because of a routing rule referencing the progress of question 4.", + "So only questions 3, 5, 7 will be displayed.", + "Otherwise, questions 2, 4 and 6 can also display." + ], + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "s1-b1-q1-a1" + }, + 0 + ] + }, + "block": "s1-b3" + }, + { + "block": "s1-b2" + } + ] + }, + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b2-q1", + "title": "Section 1 Question 2", + "description": ["Showing this question because question 1 value is not 0"], + "type": "General", + "answers": [ + { + "id": "s1-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b3", + "question": { + "id": "s1-b3-q1", + "title": "Section 1 Question 3", + "type": "General", + "answers": [ + { + "id": "s1-b3-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b4", + "question": { + "id": "s1-b4-q1", + "title": "Section 1 Question 4", + "type": "General", + "answers": [ + { + "id": "s1-b4-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [{ "source": "progress", "selector": "block", "identifier": "s1-b2" }, "COMPLETED"] + } + } + }, + { + "type": "Question", + "id": "s1-b5", + "question": { + "id": "s1-b5-q1", + "title": "Section 1 Question 5", + "type": "General", + "answers": [ + { + "id": "s1-b5-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "progress", + "selector": "block", + "identifier": "s1-b4" + }, + "COMPLETED" + ] + }, + "block": "s1-b6" + }, + { + "block": "s1-b7" + } + ] + }, + { + "type": "Question", + "id": "s1-b6", + "question": { + "id": "s1-b6-q1", + "title": "Section 1 Question 6", + "description": ["Showing this question because question 4 was completed"], + "type": "General", + "answers": [ + { + "id": "s1-b6-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b7", + "question": { + "id": "s1-b7-q1", + "title": "Section 1 Question 7", + "type": "General", + "answers": [ + { + "id": "s1-b7-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-1" + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_blocks_cross_section.json b/schemas/test/en/test_progress_value_source_blocks_cross_section.json new file mode 100644 index 0000000000..d8cb40da44 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_blocks_cross_section.json @@ -0,0 +1,224 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Test progress value source", + "theme": "default", + "description": "A test survey for testing progres value source referencing blocks", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Section One", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s1-b1", + "question": { + "id": "s1-b1-q1", + "title": "Section 1 Question 1", + "description": [ + "If you answer 0, then the second question will be skipped because of a routing rule, as well as the fourth question because of a skip condition referencing the progress of question 2, as well as the 6th question in the Second Section because of a routing rule referencing the progress of question 4.", + "So only question 3 in Section One, and questions 5 and 7 in Section Two will be displayed.", + "Otherwise, questions 2 and 4 in Section Ona and question 6 in Section Two can also display." + ], + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "s1-b1-q1-a1" + }, + 0 + ] + }, + "block": "s1-b3" + }, + { + "block": "s1-b2" + } + ] + }, + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b2-q1", + "title": "Section 1 Question 2", + "description": ["Showing this question because question 1 value is not 0"], + "type": "General", + "answers": [ + { + "id": "s1-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b3", + "question": { + "id": "s1-b3-q1", + "title": "Section 1 Question 3", + "type": "General", + "answers": [ + { + "id": "s1-b3-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b4", + "question": { + "id": "s1-b4-q1", + "title": "Section 1 Question 4", + "type": "General", + "answers": [ + { + "id": "s1-b4-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [{ "source": "progress", "selector": "block", "identifier": "s1-b2" }, "COMPLETED"] + } + } + } + ], + "id": "group-1" + } + ] + }, + { + "id": "section-2", + "title": "Section Two", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s2-b5", + "question": { + "id": "s2-b5-q1", + "title": "Section 2 Question 5", + "type": "General", + "answers": [ + { + "id": "s2-b5-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "progress", + "selector": "block", + "identifier": "s1-b4" + }, + "COMPLETED" + ] + }, + "block": "s2-b6" + }, + { + "block": "s2-b7" + } + ] + }, + { + "type": "Question", + "id": "s2-b6", + "question": { + "id": "s2-b6-q1", + "title": "Section 2 Question 6", + "description": ["Showing this question because question 4 in Section One was completed"], + "type": "General", + "answers": [ + { + "id": "s2-b6-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s2-b7", + "question": { + "id": "s2-b7-q1", + "title": "Section 2 Question 7", + "type": "General", + "answers": [ + { + "id": "s2-b7-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-2" + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_calculated_summary.json b/schemas/test/en/test_progress_value_source_calculated_summary.json new file mode 100644 index 0000000000..b8e3ff32ba --- /dev/null +++ b/schemas/test/en/test_progress_value_source_calculated_summary.json @@ -0,0 +1,517 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "survey_id": "139", + "theme": "default", + "title": "Progress Value Source Repeating Sections Test", + "data_version": "0.0.3", + "description": "Progress Value Source Repeating Sections Test", + "navigation": { + "visible": true + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Calculated Summary", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "group-1", + "title": "Calculated Summary group", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-block", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Skippable random question + List collector", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "calculated-summary-block" + }, + "COMPLETED" + ] + } + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Repeating section", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "dob-group", + "title": "Date of birth", + "blocks": [ + { + "type": "Question", + "id": "dob-block", + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": false, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "other-question-block", + "question": { + "id": "other-question", + "answers": [ + { + "id": "other-answer", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Random question about {person_name_possessive}" + }, + "description": ["Shows because the calculated summary was completed in section 1"], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "calculated-summary-block" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "other-question-block-2", + "question": { + "id": "other-question-2", + "answers": [ + { + "id": "other-answer-2", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Another random question about {person_name_possessive}" + }, + "description": ["Shows because block 2 of this repeating section was completed."], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "other-question-block" + }, + "COMPLETED" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_calculated_summary_extended.json b/schemas/test/en/test_progress_value_source_calculated_summary_extended.json new file mode 100644 index 0000000000..9a11b9b5f9 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_calculated_summary_extended.json @@ -0,0 +1,1129 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "survey_id": "139", + "theme": "default", + "title": "Progress Value Source Caluclated Summary (Extended)", + "data_version": "0.0.3", + "description": "An extended version of the Progress Value Source Calculated Summary schema intended to test chained dependency evaluation for progress value sources where multiple sections have progress value source dependencies on one another", + "navigation": { + "visible": true + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["introduction-section"] + } + }, + "sections": [ + { + "id": "introduction-section", + "title": "Guidance", + "show_on_hub": false, + "groups": [ + { + "blocks": [ + { + "id": "interstitial", + "content": { + "title": "Guidance for completing this test schema", + "contents": [ + { + "description": "This schema was created in order to ensure that dependencies based on a progress value sources captured in order." + }, + { + "description": "It is also being used to test progress value sources with chained dependents. For example, In this schema, Sections 7, 8, 9 and 10 are all dependent on Section 2, and Sections 11 and 12 are dependent on Section 9 and 10." + }, + { + "description": "So we can use this schema to test journeys and ensure that all dependent sections are updated. For example if we had not started Section 2 yet, but Sections 8, 9 and 10 are all Complete, and sections 11 and 12 are Partially Completed. Given the dependencies in this schema, completing Section 2 would mean that the status of Sections 8, 9 and 10 would change to Partially Complete and Sections 11 and 12 to Complete." + } + ] + }, + "type": "Interstitial" + } + ], + "id": "introduction-group", + "title": "Test Schema Guidance" + } + ] + }, + { + "id": "section-1", + "title": "Calculated Summary", + "groups": [ + { + "id": "group-1", + "title": "Calculated Summary group", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-block", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Skippable random question + List collector", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "calculated-summary-block" + }, + "COMPLETED" + ] + } + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Repeating section", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "dob-group", + "title": "Date of birth", + "blocks": [ + { + "type": "Question", + "id": "dob-block", + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": false, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "other-question-block", + "question": { + "id": "other-question", + "answers": [ + { + "id": "other-answer", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Random question about {person_name_possessive}" + }, + "description": ["Shows because the calculated summary was completed in section 1"], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "calculated-summary-block" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "other-question-block-2", + "question": { + "id": "other-question-2", + "answers": [ + { + "id": "other-answer-2", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Another random question about {person_name_possessive}" + }, + "description": ["Shows because block 2 of this repeating section was completed."], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "other-question-block" + }, + "COMPLETED" + ] + } + } + } + ] + } + ] + }, + { + "id": "section-4", + "title": "Section 4 (Dependent on Section 1)", + "groups": [ + { + "id": "group-4", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s4-b1", + "question": { + "id": "s4-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s4-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-1" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s4-b2", + "question": { + "id": "s4-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s4-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-5", + "title": "Section 5 (Dependent on Calc Summary Block Section 1)", + "groups": [ + { + "id": "group-5", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s5-b1", + "question": { + "id": "s5-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s5-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "block", + "identifier": "calculated-summary-block" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s5-b2", + "question": { + "id": "s5-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s5-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-6", + "title": "Section 6 (Dependent on Section 4)", + "groups": [ + { + "id": "group-6", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s6-b1", + "question": { + "id": "s6-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s6-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-4" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s6-b2", + "question": { + "id": "s6-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s6-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-7", + "title": "Section 7 (Dependent on Section 5)", + "groups": [ + { + "id": "group-7", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s7-b1", + "question": { + "id": "s7-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s7-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-5" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s7-b2", + "question": { + "id": "s7-b2-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s7-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s7-b3", + "question": { + "id": "s7-b3-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s7-b3-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-8", + "title": "Section 8 (Dependent on Section 7 and Section 2)", + "groups": [ + { + "id": "group-8", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s8-b1", + "question": { + "id": "s8-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s8-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-7" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s8-b2", + "question": { + "id": "s8-b2-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s8-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s8-b3", + "question": { + "id": "s8-b3-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s8-b3-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-9", + "title": "Section 9 (Dependent on Section 2)", + "groups": [ + { + "id": "group-9", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s9-b1", + "question": { + "id": "s9-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s9-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s9-b2", + "question": { + "id": "s9-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s9-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-10", + "title": "Section 10 (Dependent on Section 2)", + "groups": [ + { + "id": "group-10", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s10-b1", + "question": { + "id": "s10-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s10-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s10-b2", + "question": { + "id": "s10-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s10-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-11", + "title": "Section 11 (Dependent on Section 10)", + "groups": [ + { + "id": "group-11", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s11-b1", + "question": { + "id": "s11-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s11-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-10" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s11-b2", + "question": { + "id": "s11-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s11-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-12", + "title": "Section 12 (Dependent on Section 9)", + "groups": [ + { + "id": "group-12", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s12-b1", + "question": { + "id": "s12-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s12-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-9" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s12-b2", + "question": { + "id": "s12-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s12-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json b/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json new file mode 100644 index 0000000000..5e53c28755 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json @@ -0,0 +1,490 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "survey_id": "139", + "theme": "default", + "title": "Progress Value Source Repeating Sections With Chained Dependencies Test", + "data_version": "0.0.3", + "description": "Progress Value Source Repeating Sections Test", + "navigation": { + "visible": true + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Section 1", + "groups": [ + { + "id": "group-1", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b1-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Section 2 (Dependent on Section 1)", + "groups": [ + { + "id": "group-2", + "title": "List", + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Skippable random question", + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-1" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "s2-b2", + "question": { + "id": "s2-b2-q1", + "title": "Random question", + "type": "General", + "answers": [ + { + "id": "s2-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Section 3 (Depends on Section 2)", + "groups": [ + { + "id": "group", + "title": "Second List Collector", + "blocks": [ + { + "id": "second-list-collector", + "type": "ListCollector", + "for_list": "second-people", + "question": { + "id": "second-confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "second-anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "second-add-person", + "type": "ListAddQuestion", + "question": { + "id": "second-add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "second-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "second-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "second-edit-person", + "type": "ListEditQuestion", + "question": { + "id": "second-edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "second-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "second-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "second-remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "second-remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "second-remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "second-first-name" + }, + { + "source": "answers", + "identifier": "second-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "second-question-block", + "question": { + "id": "second-question", + "title": "Question", + "type": "General", + "answers": [ + { + "id": "second-answer", + "mandatory": false, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + }, + { + "type": "Question", + "id": "second-random-question-enabler-block", + "question": { + "id": "second-random-question-enabler-question", + "title": "Random question enabler", + "description": [ + "Answering this question will enable the random question in the repeated section coming after the list collector." + ], + "type": "General", + "answers": [ + { + "id": "second-random-question-enabler-answer", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-4", + "title": "Section 4 - Repeat (Depends on section 3)", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "second-people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "second-first-name" + }, + { + "source": "answers", + "identifier": "second-last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "second-dob-group", + "title": "Date of birth", + "blocks": [ + { + "type": "Question", + "id": "second-dob-block", + "question": { + "answers": [ + { + "id": "second-date-of-birth-answer", + "mandatory": false, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "second-date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "second-first-name" + }, + { + "source": "answers", + "identifier": "second-last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + } + }, + { + "type": "Question", + "id": "second-other-question-block", + "question": { + "id": "second-other-question", + "answers": [ + { + "id": "second-other-answer", + "mandatory": true, + "label": "Anything", + "type": "Number" + } + ], + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "second-first-name" + }, + { + "source": "answers", + "identifier": "second-last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "Random question about {person_name_possessive}" + }, + "description": ["Shows because section 2 was completed"], + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_section_enabled_hub.json b/schemas/test/en/test_progress_value_source_section_enabled_hub.json new file mode 100644 index 0000000000..e3babd5cb2 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_section_enabled_hub.json @@ -0,0 +1,110 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Test progress value source", + "theme": "default", + "description": "A test survey for testing progress value source section enabled in a hub flow", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Section 1", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s1-b1", + "question": { + "id": "s1-b1-q1", + "title": "Section 1 Question 1", + "description": ["Always shows"], + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b2-q1", + "title": "Section 1 Question 2", + "type": "General", + "answers": [ + { + "id": "s1-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-1" + } + ] + }, + { + "id": "section-2", + "title": "Section 2", + "enabled": { + "when": { + "==": [{ "source": "progress", "selector": "section", "identifier": "section-1" }, "COMPLETED"] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Section 2 Question 1", + "description": ["This question always shows"], + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-2" + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_section_enabled_hub_complex.json b/schemas/test/en/test_progress_value_source_section_enabled_hub_complex.json new file mode 100644 index 0000000000..4d647dbdb6 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_section_enabled_hub_complex.json @@ -0,0 +1,251 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Test progress value source", + "theme": "default", + "description": "A test survey for testing progress value source section enabled in a hub flow, with a mixture of skip conditions and section enabled conditions, and a mix of block and section references", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Section 1", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s1-b1", + "question": { + "id": "s1-b1-q1", + "title": "Section 1 Question 1", + "description": ["Always shows. The next question in the section also shows when the answer is not 0"], + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "!=": [ + { + "identifier": "s1-b1-q1-a1", + "source": "answers" + }, + 0 + ] + }, + "block": "s1-b2" + }, + { + "section": "End" + } + ] + }, + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b2-q1", + "title": "Section 1 Question 2", + "type": "General", + "description": ["Shows if the answer to the previous question is not 0"], + "answers": [ + { + "id": "s1-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-1" + } + ] + }, + { + "id": "section-2", + "title": "Section 2", + "enabled": { + "when": { + "==": [{ "source": "progress", "selector": "section", "identifier": "section-1" }, "COMPLETED"] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Section 2 Question 1", + "description": ["This question always shows. The next question in the section also shows when the answer is not 0"], + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s2-b2", + "question": { + "id": "s2-b2-q1", + "title": "Section 2 Question 2", + "type": "General", + "description": ["Always shows"], + "answers": [ + { + "id": "s2-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + }, + "routing_rules": [ + { + "when": { + "!=": [ + { + "identifier": "s2-b1-q1-a1", + "source": "answers" + }, + 0 + ] + }, + "block": "s2-b3" + }, + { + "section": "End" + } + ] + }, + { + "type": "Question", + "id": "s2-b3", + "question": { + "id": "s2-b3-q1", + "title": "Section 2 Question 3", + "type": "General", + "description": ["Shows if the answer to the Section 2 Question 1 is not 0"], + "answers": [ + { + "id": "s2-b3-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-2" + } + ] + }, + { + "id": "section-3", + "title": "Section 3", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s3-b1", + "question": { + "id": "s3-b1-q1", + "title": "Section 3 Question 1", + "description": ["Always shows"], + "type": "General", + "answers": [ + { + "id": "s3-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-3" + } + ] + }, + { + "id": "section-4", + "title": "Section 4", + "enabled": { + "when": { + "and": [ + { + "==": [{ "source": "progress", "selector": "block", "identifier": "s2-b2" }, "COMPLETED"] + }, + { + "==": [{ "source": "progress", "selector": "section", "identifier": "section-2" }, "COMPLETED"] + } + ] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s4-b1", + "question": { + "id": "s4-b1-q1", + "title": "Section 4 Question 1", + "description": ["This section shows if section 2 block 2 is completed, as well as section 2"], + "type": "General", + "answers": [ + { + "id": "s4-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-4" + } + ] + } + ] +} diff --git a/schemas/test/en/test_progress_value_source_section_enabled_no_hub.json b/schemas/test/en/test_progress_value_source_section_enabled_no_hub.json new file mode 100644 index 0000000000..a9f88839d2 --- /dev/null +++ b/schemas/test/en/test_progress_value_source_section_enabled_no_hub.json @@ -0,0 +1,114 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "001", + "title": "Test progress value source", + "theme": "default", + "description": "A test survey for testing progress value source section enabled in a linear flow", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section-1", + "title": "Section 1", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s1-b1", + "question": { + "id": "s1-b1-q1", + "title": "Section 1 Question 1", + "description": ["Always shows"], + "type": "General", + "answers": [ + { + "id": "s1-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "s1-b2", + "question": { + "id": "s1-b2-q1", + "title": "Section 1 Question 2", + "type": "General", + "answers": [ + { + "id": "s1-b2-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-1" + } + ] + }, + { + "id": "section-2", + "title": "Section 2", + "enabled": { + "when": { + "==": [{ "source": "progress", "selector": "section", "identifier": "section-1" }, "COMPLETED"] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "s2-b1", + "question": { + "id": "s2-b1-q1", + "title": "Section 2 Question 1", + "description": ["This question always shows"], + "type": "General", + "answers": [ + { + "id": "s2-b1-q1-a1", + "mandatory": true, + "label": "Enter any number", + "type": "Number" + } + ] + } + } + ], + "id": "group-2" + } + ] + } + ] +} diff --git a/tests/app/data_model/test_progress_store.py b/tests/app/data_model/test_progress_store.py index 020bd38ffa..72b218d61f 100644 --- a/tests/app/data_model/test_progress_store.py +++ b/tests/app/data_model/test_progress_store.py @@ -68,7 +68,7 @@ def test_deserialisation(): store = ProgressStore(in_progress_sections) assert store.get_section_status(section_id="s1") == CompletionStatus.IN_PROGRESS - assert store.get_completed_block_ids("s1") == ["one", "two"] + assert store.get_completed_block_ids(section_id="s1") == ["one", "two"] assert ( store.get_section_status(section_id="s2", list_item_id="abc123") diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index e416ac9dbb..f9d686770b 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -29,11 +29,17 @@ def test_questionnaire_store_json_loads( expected_completed_block_ids = basic_input["PROGRESS"][0]["block_ids"][0] assert ( - len(store.progress_store.get_completed_block_ids("a-test-section", "abc123")) + len( + store.progress_store.get_completed_block_ids( + section_id="a-test-section", list_item_id="abc123" + ) + ) == 1 ) assert ( - store.progress_store.get_completed_block_ids("a-test-section", "abc123")[0] + store.progress_store.get_completed_block_ids( + section_id="a-test-section", list_item_id="abc123" + )[0] == expected_completed_block_ids ) diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 58ed5d4bae..876b236b79 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1292,6 +1292,25 @@ def section_dependencies_new_calculated_summary_schema(): ) +@pytest.fixture +def progress_block_dependencies_schema(): + return load_schema_from_name("test_progress_value_source_calculated_summary") + + +@pytest.fixture +def progress_section_dependencies_schema(): + return load_schema_from_name( + "test_progress_value_source_section_enabled_hub_complex" + ) + + +@pytest.fixture +def progress_dependencies_schema(): + return load_schema_from_name( + "test_progress_value_source_calculated_summary_extended" + ) + + @pytest.fixture @pytest.mark.usefixtures("app", "gb_locale") def placeholder_transform_question_dynamic_answers_json(): diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index b2875a4686..981d788c30 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -12,6 +12,7 @@ from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.operator import Operator from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.utilities.schema import load_schema_from_name from tests.app.questionnaire.conftest import get_metadata from tests.app.questionnaire.test_value_source_resolver import get_list_items @@ -45,6 +46,7 @@ def get_rule_evaluator( section_id="test-section", block_id="test-block" ), routing_path_block_ids: Optional[list] = None, + progress: ProgressStore = ProgressStore(), ): if not schema: schema = get_mock_schema() @@ -60,7 +62,7 @@ def get_rule_evaluator( list_store=list_store, location=location, routing_path_block_ids=routing_path_block_ids, - progress_store=ProgressStore(), + progress_store=progress, ) @@ -255,6 +257,45 @@ def test_metadata_source(metadata_value, expected_result): ) +@pytest.mark.parametrize( + "identifier, selector, expected_result", + [("s1-b1", "block", True), ("s1-b2", "block", True), ("s1-b3", "block", False)], +) +def test_progress_source(identifier, selector, expected_result): + schema = load_schema_from_name("test_progress_value_source_blocks") + in_progress_sections = [ + { + "section_id": "section-1", + "list_item_id": None, + "status": "COMPLETED", + "block_ids": ["s1-b1", "s1-b2"], + }, + ] + + rule_evaluator = get_rule_evaluator( + schema=schema, + location=Location(section_id="section-1", block_id="s1-b3", list_item_id=None), + progress=ProgressStore(in_progress_sections), + routing_path_block_ids=["s1-b1", "s1-b2", "s1-b3"], + ) + + assert ( + rule_evaluator.evaluate( + rule={ + Operator.EQUAL: [ + { + "source": "progress", + "selector": selector, + "identifier": identifier, + }, + "COMPLETED", + ] + }, + ) + is expected_result + ) + + @pytest.mark.parametrize( "response_metadata_value, expected_result", [ diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index c57f6be309..6974b969c1 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-lines from mock import Mock from app.data_models import ProgressStore @@ -9,6 +8,8 @@ from app.utilities.schema import load_schema_from_name from tests.app.questionnaire.conftest import get_metadata +# pylint: disable=too-many-lines + def test_parse_placeholders(placeholder_list, parser): placeholders = parser(placeholder_list) diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 98b923bc09..43b78f273f 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -1,6 +1,7 @@ from collections import abc import pytest +from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema @@ -776,6 +777,67 @@ def test_when_rules_section_dependencies_new_calculated_summary( } == schema.when_rules_section_dependencies_by_answer +def test_progress_block_dependencies( + progress_block_dependencies_schema, +): + schema = progress_block_dependencies_schema + + assert { + "section-1": {"calculated-summary-block": {"section-2", "section-3"}} + } == schema.when_rules_block_dependencies_by_section_for_progress_value_source + + +def test_progress_section_dependencies( + progress_section_dependencies_schema, +): + schema = progress_section_dependencies_schema + + assert { + "section-1": {"section-2"}, + "section-2": {"section-4"}, + } == schema.when_rules_section_dependencies_by_section_for_progress_value_source + + +def test_progress_block_and_section_dependencies_are_ordered( + progress_dependencies_schema, +): + schema = progress_dependencies_schema + + assert ( + ImmutableDict( + { + "section-1": OrderedSet(["section-4"]), + "section-2": OrderedSet( + ["section-7", "section-8", "section-9", "section-10"] + ), + "section-4": OrderedSet(["section-6"]), + "section-5": OrderedSet(["section-7"]), + "section-7": OrderedSet(["section-8"]), + "section-9": OrderedSet(["section-12"]), + "section-10": OrderedSet(["section-11"]), + } + ) + == schema.when_rules_section_dependencies_by_section_for_progress_value_source + ) + + assert ( + ImmutableDict( + { + "section-1": { + "calculated-summary-block": OrderedSet( + [ + "section-2", + "section-3", + "section-5", + ] + ) + } + } + ) + == schema.when_rules_block_dependencies_by_section_for_progress_value_source + ) + + @pytest.mark.parametrize( "rule, expected_result", ( @@ -802,3 +864,29 @@ def test_when_rules_section_dependencies_new_calculated_summary( def test_has_operator_returns_correct_value(rule, expected_result): result = QuestionnaireSchema.has_operator(rule) assert result == expected_result + + +def test_progress_dependencies_for_when_rules( + progress_dependencies_schema, +): + """ + Asserts that the dependencies captured by + schema.when_rules_section_dependencies_by_section_for_progress_value_source and + schema.when_rules_section_dependencies_by_block_for_progress_value_source are flipped + correctly so that progress dependencies can be evaluated with our normal when rules + """ + schema = progress_dependencies_schema + + assert { + "section-10": {"section-2"}, + "section-11": {"section-10"}, + "section-12": {"section-9"}, + "section-2": {"section-1"}, + "section-3": {"section-1"}, + "section-4": {"section-1"}, + "section-5": {"section-1"}, + "section-6": {"section-4"}, + "section-7": {"section-5", "section-2"}, + "section-8": {"section-7", "section-2"}, + "section-9": {"section-2"}, + } == schema.when_rules_section_dependencies_for_progress diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index 03b09a3b77..4b900f9e4e 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -1,5 +1,9 @@ +from collections import defaultdict + import pytest from mock import MagicMock, Mock +from mock.mock import call +from ordered_set import OrderedSet from werkzeug.datastructures import MultiDict from app.data_models import QuestionnaireStore @@ -446,7 +450,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( ) assert ( - questionnaire_store_updater.update_relationship_question_completeness( + questionnaire_store_updater._update_relationship_question_completeness( # pylint: disable=protected-access "test-relationship-collector" ) is None @@ -861,6 +865,7 @@ def test_answer_id_section_dependents( mock_schema.when_rules_section_dependencies_by_answer = { "first-answer": {"section-2"} } + mock_schema.get_section_ids.return_value = ["section-1", "section-2"] mock_router.is_path_complete.return_value = is_path_complete mock_schema.get_answers_for_question_by_id.return_value = { "first-answer": {}, @@ -960,6 +965,7 @@ def test_answer_id_section_dependents_repeating( mock_schema.when_rules_section_dependencies_by_answer = { "first-answer": {"section-2"} } + mock_schema.get_section_ids.return_value = ["section-1", "section-2"] mock_schema.get_answers_for_question_by_id.return_value = { "first-answer": {}, "second-answer": {}, @@ -1088,11 +1094,11 @@ def get_questionnaire_store_updater( [CompletionStatus.IN_PROGRESS, CompletionStatus.COMPLETED], ) def test_dependent_sections_completed_dependant_blocks_removed_and_status_updated( - dependent_section_status, mock_router + mocker, dependent_section_status, mock_router ): # Given current_location = Location( - section_id="company-summary-section", block_id="total-turnover-block" + section_id="company-summary-section", block_id="breakdown-section" ) progress_store = ProgressStore( [ @@ -1110,6 +1116,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update }, ], ) + questionnaire_store_updater = get_questionnaire_store_updater( current_location=current_location, progress_store=progress_store, @@ -1123,19 +1130,29 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update } assert dependent_block_id in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) + mocker.patch( + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + return_value=[ + DependentSection( + section_id="breakdown-section", list_item_id=None, is_complete=False + ) + ], + ) # When questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) assert ( - progress_store.get_section_status(*dependent_section_key) + progress_store.get_section_status( + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + ) == CompletionStatus.IN_PROGRESS ) @@ -1169,7 +1186,7 @@ def test_dependent_sections_current_section_status_not_updated(mocker): questionnaire_store_updater.update_section_status = mocker.Mock() assert dependent_block_id in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) # When @@ -1178,7 +1195,7 @@ def test_dependent_sections_current_section_status_not_updated(mocker): # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) # Status for current section is handled separately by handle post. assert questionnaire_store_updater.update_section_status.call_count == 0 @@ -1259,7 +1276,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): questionnaire_store_updater.update_section_status = mocker.Mock() assert dependent_block_id not in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) # When @@ -1275,7 +1292,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): [CompletionStatus.IN_PROGRESS, CompletionStatus.COMPLETED], ) def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_status_updated( - dependent_section_status, mock_router + mocker, dependent_section_status, mock_router ): # Given current_location = Location( @@ -1331,6 +1348,14 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta ) ) + mocker.patch( + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + return_value=[ + DependentSection( + section_id="breakdown-section", list_item_id=None, is_complete=None + ) + ], + ) # When questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() questionnaire_store_updater.update_progress_for_dependent_sections() @@ -1339,7 +1364,7 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta for list_item in list_store["some-list"]: section_id, list_item_id = "breakdown-section", list_item assert "turnover-breakdown-block" not in progress_store.get_completed_block_ids( - section_id, list_item_id + section_id=section_id, list_item_id=list_item_id ) assert ( progress_store.get_section_status(section_id, list_item_id) @@ -1395,7 +1420,7 @@ def test_dependent_sections_added_dependant_block_removed( } assert dependent_block_id in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) assert questionnaire_store_updater.dependent_sections == set() @@ -1404,10 +1429,110 @@ def test_dependent_sections_added_dependant_block_removed( # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - *dependent_section_key + section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) assert questionnaire_store_updater.dependent_sections == { DependentSection( section_id="breakdown-section", list_item_id=None, is_complete=False ) } + + +@pytest.mark.parametrize( + "status_unchanged_section_ids, expected_routing_path_calls", + [ + ( + # s1.s1 -> s1.s2 -> s2.s3 -> s3.s4 -> s3.s5 -> s3.s7 -> s2.s6 + [], + [ + call("section-1", list_item_id=None), + call("section-2", list_item_id=None), + call("section-3", list_item_id=None), + call("section-4", list_item_id=None), + call("section-5", list_item_id=None), + call("section-7", list_item_id=None), + call("section-6", list_item_id=None), + ], + ), + ( + # s1 -> s1.s2 -> s1.s3 -> s3.s4 -> s3.s5 -> s3.s7 + ["section-2"], + [ + call("section-1", list_item_id=None), + call("section-2", list_item_id=None), + call("section-3", list_item_id=None), + call("section-4", list_item_id=None), + call("section-5", list_item_id=None), + call("section-7", list_item_id=None), + ], + ), + ( + # s1 -> s1.s2 -> s2.s3 -> s2.s4 -> s2.s5 -> s2.s6 + ["section-3"], + [ + call("section-1", list_item_id=None), + call("section-2", list_item_id=None), + call("section-3", list_item_id=None), + call("section-4", list_item_id=None), + call("section-5", list_item_id=None), + call("section-6", list_item_id=None), + ], + ), + ], +) +def test_questionnaire_store_updater_dependency_capture( + mocker, + mock_router, + mock_schema, + status_unchanged_section_ids, + expected_routing_path_calls, +): + """ + This test is intended to ensure that the order in which dependencies are captured and evaluated is correct. + We should only call the routing path for a given section once and need to ensure that only the necessary paths are evaluated + i.e. only sections in which the status has changed should be evaluated. + """ + current_location = Location(section_id="section-1", block_id="block-2") + mock_dependencies = defaultdict(OrderedSet) | { + "section-1": OrderedSet(["section-2", "section-3"]), + "section-2": OrderedSet(["section-3", "section-4", "section-5", "section-6"]), + "section-3": OrderedSet(["section-4", "section-5", "section-7"]), + } + mock_schema.when_rules_section_dependencies_by_section_for_progress_value_source = ( + mock_dependencies + ) + mock_schema.get_repeating_list_for_section.return_value = False + mock_router.is_path_complete.return_value = ( + False # This will result in new status being IN=PROGRESS + ) + progress_store = ProgressStore( + [ + { + "section_id": f"section-{idx}", + "block_ids": ["block-1", "block-2"], + "status": CompletionStatus.IN_PROGRESS + if f"section-{idx}" in status_unchanged_section_ids + else CompletionStatus.COMPLETED, + } + for idx in range(1, 8) + ], + ) + questionnaire_store_updater = get_questionnaire_store_updater( + current_location=current_location, + progress_store=progress_store, + router=mock_router, + schema=mock_schema, + ) + mocker.patch( + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + return_value=[ + DependentSection( + section_id="section-1", list_item_id=None, is_complete=None + ), + DependentSection( + section_id="section-2", list_item_id=None, is_complete=None + ), + ], + ) + questionnaire_store_updater.update_progress_for_dependent_sections() + assert mock_router.routing_path.call_args_list == expected_routing_path_calls diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 38ff197d53..57957aa7dd 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -46,6 +46,7 @@ def get_value_source_resolver( routing_path_block_ids: Optional[list] = None, use_default_answer=False, escape_answer_values=False, + progress_store: ProgressStore | None = None, ): if not schema: schema = get_mock_schema() @@ -65,7 +66,7 @@ def get_value_source_resolver( routing_path_block_ids=routing_path_block_ids, use_default_answer=use_default_answer, escape_answer_values=escape_answer_values, - progress_store=ProgressStore(), + progress_store=progress_store, ) @@ -685,3 +686,13 @@ def test_answer_value_with_selector_can_be_escaped(): ) == ESCAPED_CONTENT ) + + +def test_progress_values_source_throws_if_no_location_given(): + value_source_resolver = get_value_source_resolver( + progress_store=ProgressStore(), location=None + ) + with pytest.raises(ValueError): + value_source_resolver.resolve( + {"source": "progress", "selector": "block", "identifier": "a-block"} + ) diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index 364f28262c..a5389a70e2 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -184,7 +184,7 @@ def test_create_question( # When question = Question( - question_schema, + question_schema=question_schema, answer_store=answer_store, list_store=list_store, progress_store=progress_store, diff --git a/tests/functional/spec/features/progress/progress_value_source_blocks.js b/tests/functional/spec/features/progress/progress_value_source_blocks.js new file mode 100644 index 0000000000..c7219c932c --- /dev/null +++ b/tests/functional/spec/features/progress/progress_value_source_blocks.js @@ -0,0 +1,193 @@ +import FirstQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b1.page"; +import SecondQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b2.page"; +import ThirdQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b3.page"; +import ThirdQuestionSectionTwoPage from "../../../generated_pages/progress_value_source_section_enabled_no_hub/s2-b1.page"; +import FourthQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b4.page"; +import FifthQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b5.page"; +import SixthQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b6.page"; +import SeventhQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b7.page"; +import SubmitPage from "../../../generated_pages/progress_value_source_blocks/submit.page"; +import HubPage from "../../../base_pages/hub.page"; + +describe("Feature: Routing based on progress value sources using block identifiers", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_progress_value_source_blocks.json"); + }); + + describe("Given I have routing based on the completeness of a block", () => { + it("When the block being evaluated is incomplete (Q2), Then the dependent question (Q4) should not be on the path or displayed on the summary", async () => { + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); + await $(ThirdQuestionPage.q1A1()).setValue("1"); + await $(ThirdQuestionPage.submit()).click(); + + await $(FifthQuestionPage.q1A1()).setValue("2"); + await $(FifthQuestionPage.submit()).click(); + + await $(SeventhQuestionPage.q1A1()).setValue("3"); + await $(SeventhQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $("body").getText()).to.not.have.string("Section 1 Question 2"); + await expect(await $("body").getText()).to.not.have.string("Section 1 Question 4"); + }); + }); + + describe("Given I have routing based on the completeness of a block", () => { + it("When the blocks being evaluated are complete (Q2 + Q5), Then the dependent questions (Q4 + Q6) should be on the path and displayed on the summary", async () => { + await $(FirstQuestionPage.q1A1()).setValue("1"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await $(SecondQuestionPage.q1A1()).setValue("1"); + await $(SecondQuestionPage.submit()).click(); + + await $(ThirdQuestionPage.q1A1()).setValue("2"); + await $(ThirdQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await $(FourthQuestionPage.q1A1()).setValue("3"); + await $(FourthQuestionPage.submit()).click(); + + await $(FifthQuestionPage.q1A1()).setValue("4"); + await $(FifthQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await $(SixthQuestionPage.q1A1()).setValue("5"); + await $(SixthQuestionPage.submit()).click(); + + await $(SeventhQuestionPage.q1A1()).setValue("6"); + await $(SeventhQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); + await expect(await $("body").getText()).to.have.string("Section 1 Question 6"); + }); + }); + + describe("Given I have routing based on the completeness of a block", () => { + it("When an answer is changed so that the block being evaluated is completed, Then the dependent questions (Q4 + Q6) should be on the path and displayed on the summary", async () => { + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); + await $(ThirdQuestionPage.q1A1()).setValue("1"); + await $(ThirdQuestionPage.submit()).click(); + + await $(FifthQuestionPage.q1A1()).setValue("2"); + await $(FifthQuestionPage.submit()).click(); + + await $(SeventhQuestionPage.q1A1()).setValue("3"); + await $(SeventhQuestionPage.submit()).click(); + + await $(SubmitPage.s1B1Q1A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); + await $(FirstQuestionPage.q1A1()).setValue("1"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await $(SecondQuestionPage.q1A1()).setValue("1"); + await $(SecondQuestionPage.submit()).click(); + + await $(ThirdQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await $(FourthQuestionPage.q1A1()).setValue("3"); + await $(FourthQuestionPage.submit()).click(); + + await $(FifthQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await $(SixthQuestionPage.q1A1()).setValue("3"); + await $(SixthQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); + await expect(await $("body").getText()).to.have.string("Section 1 Question 6"); + }); + }); + + describe("Given I have routing based on the completeness of a block", () => { + it("When an answer is removed form the path block being evaluated is no longer completed, Then the dependent questions (Q4 + Q6) should not be on the path and not be displayed on the summary", async () => { + await $(FirstQuestionPage.q1A1()).setValue("1"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await $(SecondQuestionPage.q1A1()).setValue("1"); + await $(SecondQuestionPage.submit()).click(); + + await $(ThirdQuestionPage.q1A1()).setValue("2"); + await $(ThirdQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await $(FourthQuestionPage.q1A1()).setValue("3"); + await $(FourthQuestionPage.submit()).click(); + + await $(FifthQuestionPage.q1A1()).setValue("4"); + await $(FifthQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await $(SixthQuestionPage.q1A1()).setValue("5"); + await $(SixthQuestionPage.submit()).click(); + + await $(SeventhQuestionPage.q1A1()).setValue("6"); + await $(SeventhQuestionPage.submit()).click(); + + await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await $(SubmitPage.s1B1Q1A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + + await expect(await $("body").getText()).to.not.have.string("Section 1 Question 4"); + await expect(await $("body").getText()).to.not.have.string("Section 1 Question 6"); + }); + }); +}); + +describe("Feature: Section enabled based on progress value sources using block identifiers (no hub)", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_progress_value_source_section_enabled_no_hub.json"); + }); + + describe("Given I have a section enabled based on the completeness of a block", () => { + it("When the block being evaluated is complete, Then the dependent section should be enabled", async () => { + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + await $(SecondQuestionPage.q1A1()).setValue("1"); + await $(SecondQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ThirdQuestionSectionTwoPage.pageName); + }); + }); +}); + +describe("Feature: Section enabled based on progress value sources using section identifiers", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_progress_value_source_section_enabled_hub.json"); + }); + + describe("Given I have a section enabled based on the completeness of another section", () => { + it("When the section being evaluated is complete, Then the dependent section should be enabled", async () => { + await $(HubPage.submit()).click(); + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + await $(SecondQuestionPage.q1A1()).setValue("1"); + await $(SecondQuestionPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Not started"); + }); + }); + + describe("Given I have a section enabled based on the completeness of another section", () => { + it("When the section being evaluated is incomplete, Then the dependent section should not be enabled", async () => { + await $(HubPage.submit()).click(); + await $(FirstQuestionPage.q1A1()).setValue("0"); + await $(FirstQuestionPage.submit()).click(); + await browser.url(HubPage.url()); + + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + await expect(await $("body").getText()).to.not.have.string("Section 2"); + }); + }); +}); diff --git a/tests/functional/spec/features/progress/progress_value_source_repeating.js b/tests/functional/spec/features/progress/progress_value_source_repeating.js new file mode 100644 index 0000000000..51b544c022 --- /dev/null +++ b/tests/functional/spec/features/progress/progress_value_source_repeating.js @@ -0,0 +1,199 @@ +import HubPage from "../../../base_pages/hub.page"; +import ListCollectorPage from "../../../generated_pages/new_calculated_summary_repeating_section/list-collector.page"; +import ListCollectorAddPage from "../../../generated_pages/new_calculated_summary_repeating_section/list-collector-add.page"; +import QuestionBlockPage from "../../../generated_pages/progress_block_value_source_repeating_sections/question-block.page"; +import DOBQuestionBlockPage from "../../../generated_pages/progress_block_value_source_repeating_sections/dob-block.page"; +import RandomQuestionEnablerBlockPage from "../../../generated_pages/progress_block_value_source_repeating_sections/random-question-enabler-block.page"; +import SectionTwoSummaryPage from "../../../generated_pages/progress_block_value_source_repeating_sections/section-2-summary.page"; +import SectionThreeSummaryPage from "../../../generated_pages/progress_value_source_calculated_summary/section-3-summary.page"; +import OtherQuestionBlockPage from "../../../generated_pages/progress_block_value_source_repeating_sections/other-question-block.page"; +import FirstNumberBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/first-number-block.page"; +import SecondNumberBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/second-number-block.page"; +import SectionTwoQuestionBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/s2-b1.page"; +import CalculatedSummaryBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/calculated-summary-block.page"; + +describe("Feature: Routing rules based on progress value sources in repeating sections", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_progress_block_value_source_repeating_sections.json"); + }); + + describe("Given I have routing in a repeating section based on the completeness of a block", () => { + it("When the block is incomplete, then I should not see the dependent question in the repeating section", async () => { + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(QuestionBlockPage.pageName); + + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + + await $(HubPage.summaryRowLink("section-2-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SectionTwoSummaryPage.pageName); + }); + }); + + describe("Given I have routing in a repeating section based on the completeness of a block", () => { + it("When the block is complete, then I should see the dependent question in the repeating section", async () => { + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(QuestionBlockPage.submit()).click(); + await $(RandomQuestionEnablerBlockPage.randomQuestionEnabler()).setValue(1); + await $(RandomQuestionEnablerBlockPage.submit()).click(); + + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + + await $(HubPage.summaryRowLink("section-2-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); + }); + }); + + describe("Given I have routing in a repeating section based on the completeness of a block", () => { + it("When the status of the block changes from incomplete to complete, then the dependent question should be on the path in the repeating sections", async () => { + await $(HubPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("Joe"); + await $(ListCollectorAddPage.lastName()).setValue("Bloggs"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + + await $(HubPage.summaryRowLink("section-2-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await $(SectionTwoSummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + + await $(HubPage.submit()).click(); + await $(QuestionBlockPage.submit()).click(); + await $(RandomQuestionEnablerBlockPage.randomQuestionEnabler()).setValue(1); + await $(RandomQuestionEnablerBlockPage.submit()).click(); + + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + }); + }); +}); + +describe("Feature: Routing rules based on progress value sources in repeating sections", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_progress_value_source_calculated_summary.json"); + }); + + describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { + it("When the calculated summary block is incomplete, then I should not see the dependent question in the repeating section", async () => { + await $(HubPage.submit()).click(); + await $(FirstNumberBlockPage.firstNumber()).setValue(1); + await $(FirstNumberBlockPage.submit()).click(); + await browser.url(HubPage.url()); + + await $(HubPage.summaryRowLink("section-2")).click(); + + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + + await $(HubPage.summaryRowLink("section-3-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await $(SectionThreeSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Completed"); + }); + }); + + describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { + it("When the calculated summary block is incomplete but is updated so that it is completed, then I should see the dependency should be updated in the repeating section", async () => { + await $(HubPage.submit()).click(); + await $(FirstNumberBlockPage.firstNumber()).setValue(1); + await $(FirstNumberBlockPage.submit()).click(); + await browser.url(HubPage.url()); + + await $(HubPage.summaryRowLink("section-2")).click(); + + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + + await $(HubPage.summaryRowLink("section-3-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await $(SectionThreeSummaryPage.submit()).click(); + + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Completed"); + + await $(HubPage.summaryRowLink("section-1")).click(); + await $(SecondNumberBlockPage.secondNumber()).setValue(2); + await $(SecondNumberBlockPage.submit()).click(); + await $(CalculatedSummaryBlockPage.submit()).click(); + await browser.url(HubPage.url()); + + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Partially completed"); + }); + }); + + describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { + it("When the calculated summary block is complete, then I should see the dependent question in the repeating section", async () => { + await $(HubPage.submit()).click(); + await $(FirstNumberBlockPage.firstNumber()).setValue(1); + await $(FirstNumberBlockPage.submit()).click(); + await $(SecondNumberBlockPage.secondNumber()).setValue(2); + await $(SecondNumberBlockPage.submit()).click(); + await $(CalculatedSummaryBlockPage.submit()).click(); + await browser.url(HubPage.url()); + + await $(HubPage.summaryRowLink("section-2")).click(); + + await $(SectionTwoQuestionBlockPage.q1A1()).setValue(1); + await $(SectionTwoQuestionBlockPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + + await $(HubPage.summaryRowLink("section-3-1")).click(); + await $(DOBQuestionBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); + }); + }); +}); diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 44f0f08709..3f1b6a7aa6 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -329,6 +329,10 @@ def getHtmlSoup(self): """ return BeautifulSoup(self.getResponseData(), "html.parser") + @staticmethod + def row_selector(row_number): + return f".ons-summary__item:nth-of-type({row_number})" + # Extra Helper Assertions def assertInHead(self, content): self.assertInSelector(content, "head") diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py new file mode 100644 index 0000000000..b167e50887 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py @@ -0,0 +1,213 @@ +from tests.integration.integration_test_case import IntegrationTestCase +from tests.integration.questionnaire import SUBMIT_URL_PATH + + +class TestQuestionnaireProgressValueSourceBlocks(IntegrationTestCase): + def go_to_section(self, section_id): + self.get(f"/questionnaire/sections/{section_id}/") + + def test_skip_condition_block_not_complete(self): + """ + Test that a block is skipped if the progress value source is not complete + """ + + self.launchSurvey("test_progress_value_source_blocks") + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 0}) + + # Routes to block 3 because answer to block 1 is 0 + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + # Block 4 is skipped because block 2 is not complete + self.assertInBody("Section 1 Question 5") + + def test_routing_condition_block_not_complete(self): + """ + Test that routes to proper block if the progress value source is not complete + """ + + self.launchSurvey("test_progress_value_source_blocks") + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 0}) + + # Routes to block 3 because answer to block 1 is 0 + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + # Block 4 is skipped because block 2 is not complete + self.assertInBody("Section 1 Question 5") + self.post({"s1-b5-q1-a1": 1}) + + # Routes to block 7 because answer to block 2 is not complete + self.assertInBody("Section 1 Question 7") + self.post({"s1-b7-q1-a1": 1}) + + def test_block_value_source_dependencies_updated(self): + """ + Test that the block value source dependencies are updated when a dependent block progress changes + """ + + self.launchSurvey("test_progress_value_source_blocks") + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 0}) + + # Routes to block 3 because answer to block 1 is 0 + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + # Block 4 is skipped because block 2 is not complete + self.assertInBody("Section 1 Question 5") + self.post({"s1-b5-q1-a1": 1}) + + # Routes to block 7 because answer to block 2 is not complete + self.assertInBody("Section 1 Question 7") + self.post({"s1-b7-q1-a1": 1}) + + self.assertInBody("Check your answers and submit") + + # Change block 1 answer to 1 + self.get( + "/questionnaire/s1-b1/?return_to=final-summary&return_to_answer_id=s1-b1-q1-a1#s1-b1-q1-a1" + ) + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + # Routes to block 2 because answer to block 1 is now 1 + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + # bBock 4 is not skipped because answer to block 1 is now 1 + self.assertInBody("Section 1 Question 4") + self.post({"s1-b4-q1-a1": 1}) + + # Routes to block 5 because answer to block 1 is now 1 + self.assertInBody("Section 1 Question 5") + self.post({"s1-b5-q1-a1": 1}) + + # Routes to block 6 because answer to block 1 is now 1 + self.assertInBody("Section 1 Question 6") + self.post({"s1-b6-q1-a1": 1}) + + # Redirects to the hub + self.assertInUrl(SUBMIT_URL_PATH) + + # Question 2, 4 and 6 are now visible because block 2 is complete (dependencies updated) + + self.assertInSelector("Section 1 Question 2", self.row_selector(2)) + self.assertInSelector("1", self.row_selector(2)) + self.assertInSelector("Change", self.row_selector(2)) + + self.assertInSelector("Section 1 Question 4", self.row_selector(4)) + self.assertInSelector("1", self.row_selector(4)) + self.assertInSelector("Change", self.row_selector(4)) + + self.assertInSelector("Section 1 Question 6", self.row_selector(6)) + self.assertInSelector("1", self.row_selector(6)) + self.assertInSelector("Change", self.row_selector(6)) + + def test_block_value_source_dependencies_removed_from_path(self): + """ + Test that the block value source dependencies are updated when a dependent block progress changes and gets removed from path + """ + + self.launchSurvey("test_progress_value_source_blocks") + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + self.assertInBody("Section 1 Question 4") + self.post({"s1-b4-q1-a1": 1}) + + self.assertInBody("Section 1 Question 5") + self.post({"s1-b5-q1-a1": 1}) + + self.assertInBody("Section 1 Question 6") + self.post({"s1-b6-q1-a1": 1}) + + self.assertInBody("Section 1 Question 7") + self.post({"s1-b7-q1-a1": 1}) + + self.assertInBody("Check your answers and submit") + + # Change block 1 answer to 0 + self.get( + "/questionnaire/s1-b1/?return_to=final-summary&return_to_answer_id=s1-b1-q1-a1#s1-b1-q1-a1" + ) + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 0}) + + # Redirects to the hub + self.assertInUrl(SUBMIT_URL_PATH) + + # Questions 2, 4 and 6 are not visible because they aren't on the path anymore although they've been answered earlier + self.assertNotInBody("Section 1 Question 2") + self.assertNotInBody("Section 1 Question 4") + self.assertNotInBody("Section 1 Question 6") + + def test_block_value_source_cross_section_dependencies_removed_from_path(self): + """ + Test that the block value source dependencies are updated when a dependent block progress changes and gets removed from path + """ + + self.launchSurvey("test_progress_value_source_blocks_cross_section") + + self.post() + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + self.assertInBody("Section 1 Question 3") + self.post({"s1-b3-q1-a1": 1}) + + self.assertInBody("Section 1 Question 4") + self.post({"s1-b4-q1-a1": 1}) + + self.post() + self.post() + + self.assertInBody("Section 2 Question 5") + self.post({"s2-b5-q1-a1": 1}) + + self.assertInBody("Section 2 Question 6") + self.post({"s2-b6-q1-a1": 1}) + + self.assertInBody("Section 2 Question 7") + self.post({"s2-b7-q1-a1": 1}) + + self.post() + self.assertEqualUrl("/questionnaire/") + + # Change block 1 answer to 0 + self.get("/questionnaire/s1-b1/") + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 0}) + + # Questions 2, 4 in Section 1 are not visible because they aren't on the path anymore + self.assertEqualUrl("/questionnaire/sections/section-1/") + self.assertNotInBody("Section 1 Question 2") + self.assertNotInBody("Section 1 Question 4") + self.post() + + # Redirects to the hub + self.assertEqualUrl("/questionnaire/") + + # Question 6 in Section 2 is not visible because it is not on the path anymore + self.go_to_section("section-2") + self.assertEqualUrl("/questionnaire/sections/section-2/") + self.assertNotInBody("Section 2 Question 6") diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py new file mode 100644 index 0000000000..3027aea765 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py @@ -0,0 +1,417 @@ +from tests.integration.integration_test_case import IntegrationTestCase + + +class TestQuestionnaireProgressValueSource(IntegrationTestCase): + def john_doe_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-3-1-link"})[ + "href" + ] + + def james_bond_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-3-2-link"})[ + "href" + ] + + def section_one_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-1-link"})[ + "href" + ] + + def add_person(self, first_name, last_name): + self.assertEqualUrl("/questionnaire/people/add-person/") + self.post({"first-name": first_name, "last-name": last_name}) + + def assert_section_status(self, section_index, status, other_labels=None): + self.assertInSelector(status, self.row_selector(section_index)) + if other_labels and len(other_labels) > 0: + for other_label in other_labels: + self.assertInSelector(other_label, self.row_selector(section_index)) + + def answer_dob(self, payload=None): + if not payload: + payload = { + "date-of-birth-answer-year": "1998", + "date-of-birth-answer-month": "12", + "date-of-birth-answer-day": 12, + } + self.post(payload) + + def go_to_section(self, section_id): + self.get(f"/questionnaire/sections/{section_id}/") + + def go_to_hub(self): + self.get("/questionnaire/") + + # pylint: disable=locally-disabled, too-many-statements + def test_happy_path(self): + self.launchSurvey("test_progress_value_source_calculated_summary") + + self.assertInBody("Choose another section to complete") + self.assertInBody("Calculated Summary") + self.assertInBody("Skippable random question + List collector") + + # 1. Complete the first section + self.go_to_section("section-1") + self.assertInUrl("/questionnaire/first-number-block/") + self.assertInBody("First Number Question Title") + self.post({"first-number-answer": 1}) + + self.assertInUrl("/questionnaire/second-number-block") + self.assertInBody("Second Number Question Title") + self.post({"second-number-answer": 1}) + + # 2. Should be on the calculated summary page + self.assertEqualUrl("/questionnaire/calculated-summary-block/") + + self.assertInSelector("First answer label", self.row_selector(1)) + self.assertInSelector("£1.00", self.row_selector(1)) + self.assertInSelector("Change", self.row_selector(1)) + + self.assertInSelector("Second answer label", self.row_selector(2)) + self.assertInSelector("£1.00", self.row_selector(2)) + self.assertInSelector("Change", self.row_selector(2)) + + self.post() + self.post() + + # 3. Should be on the hub page + self.assertEqualUrl("/questionnaire/") + self.assertInBody("Choose another section to complete") + + # 4. 1st section should be marked as complete + self.assert_section_status(1, "Completed", ["View answers"]) + + # 5. Complete the second section + # 6. Random question shows + self.go_to_section("section-2") + self.assertInUrl("/questionnaire/s2-b1/") + self.assertInBody("Skippable random question") + self.post({"s2-b1-q1-a1": 1}) + + # 7. Add two people + self.assertEqualUrl("/questionnaire/list-collector/") + self.post({"anyone-else": "Yes"}) + self.add_person("John", "Doe") + + self.assertEqualUrl("/questionnaire/list-collector/") + self.post({"anyone-else": "Yes"}) + self.add_person("James", "Bond") + + self.post({"anyone-else": "No"}) + + # 8. Two new sections should be available on the hub + self.assert_section_status(3, "Not started", ["John Doe"]) + self.assert_section_status(4, "Not started", ["James Bond"]) + + # 9. Complete the John Doe section, random question shows + self.get(self.john_doe_link()) + + self.assertInBody("John Doe") + + self.answer_dob() + + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + + self.assertInBody("Another random question about") + self.post({"other-answer-2": 1}) + + self.assertInUrl("/questionnaire/sections/section-3/") + + self.post() + + # 10. John Doe section should be marked as complete + self.assert_section_status(3, "Completed", ["John Doe"]) + self.assert_section_status(4, "Not started", ["James Bond"]) + + # 11. Complete the James Bond section, random question shows + self.get(self.james_bond_link()) + + self.assertInBody("James Bond") + + self.answer_dob() + + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + + self.assertInBody("Another random question about") + self.post({"other-answer-2": 1}) + + self.assertInUrl("/questionnaire/sections/section-3/") + + self.post() + + # 12. James Bond section should be marked as complete + self.assert_section_status(4, "Completed", ["James Bond"]) + + # pylint: disable=locally-disabled, too-many-statements + def test_calculated_summary_first_incomplete_then_complete(self): + self.launchSurvey("test_progress_value_source_calculated_summary") + + # 1. Start completing the first section + self.go_to_section("section-1") + self.post({"first-number-answer": 1}) + + self.post({"second-number-answer": 1}) + + self.assertEqualUrl("/questionnaire/calculated-summary-block/") + + # 2. Go back to the hub BEFORE completing the section + self.go_to_hub() + + # 3. Section 1 should show as partially complete + self.assert_section_status(1, "Partially completed", ["Continue with section"]) + + # 4. Complete the second section + self.go_to_section("section-2") + + self.assertNotInBody("Skippable random question") + self.assertEqualUrl("/questionnaire/list-collector/") + + self.post({"anyone-else": "Yes"}) + self.add_person("John", "Doe") + self.post({"anyone-else": "Yes"}) + self.add_person("James", "Bond") + self.post({"anyone-else": "No"}) + + # 5. Section 2 should show as complete + self.assert_section_status(2, "Completed") + + # 6. Complete the John Doe section. Random question DOES NOT show because section 1 is not complete + self.get(self.john_doe_link()) + + self.answer_dob() + + self.assertNotInBody("Random question about") + self.post() + + self.assertEqualUrl("/questionnaire/") + + # 7. On the hub, John Doe section shows as completted + self.assert_section_status(3, "Completed", ["John Doe"]) + + # 8. Complete the James Bond section. Random question DOES NOT show because section 1 is not complete + self.get(self.james_bond_link()) + + self.assertInBody("James Bond") + + self.answer_dob() + + self.assertNotInBody("Random question about") + self.post() + + self.assertEqualUrl("/questionnaire/") + + # 9. On the hub, James Bond section shows as completed + self.assert_section_status(4, "Completed", ["James Bond"]) + + # 10. Go back to calculated summary (section 1) and complete it + self.get("/questionnaire/calculated-summary-block/?resume=True") + self.assertInBody("We calculate the total of currency values entered to be") + self.post() + self.post() + + # 11. Dependent sections should have been updated to partially completed + self.assert_section_status(1, "Completed") + self.assert_section_status( + 2, "Partially completed", ["Skippable random question + List collector"] + ) + self.assert_section_status(3, "Partially completed", ["John Doe"]) + self.assert_section_status(4, "Partially completed", ["James Bond"]) + + # 12. Go back to section 2 and complete it + # Random question SHOWS because section 1 is now completed + self.go_to_section("section-2") + self.assertInBody("Skippable random question") + self.post({"s2-b1-q1-a1": 1}) + + # 13. Section 2 shows as completed on the hub + self.assertEqualUrl("/questionnaire/") + self.assertInBody("Choose another section to complete") + + self.assert_section_status(2, "Completed") + + # 14. Go back to John Doe section and complete it + # Random questions show because section 1 is now complete + self.get(self.john_doe_link()) + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + self.assertInBody("Another random question about") + self.post({"other-answer-2": 1}) + self.post() + + self.assertEqualUrl("/questionnaire/") + + # 15. Go back to James Bond section and complete it + # Random questions show because section 1 is now complete + self.get(self.james_bond_link()) + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + self.assertInBody("Another random question about") + self.post({"other-answer-2": 1}) + self.post() + + self.assertEqualUrl("/questionnaire/") + + # 16. All sections should show as completed on the hub + self.assert_section_status(3, "Completed", ["John Doe"]) + self.assert_section_status(4, "Completed", ["James Bond"]) + + def test_happy_path_then_make_calculated_summary_incomplete(self): + self.launchSurvey("test_progress_value_source_calculated_summary") + + # 1. Complete section 1 + self.go_to_section("section-1") + self.post({"first-number-answer": 1}) + + self.post({"second-number-answer": 1}) + + self.post() + self.post() + + # 2. Complete section 2 and add two people + self.go_to_section("section-2") + self.post({"s2-b1-q1-a1": 1}) + + self.post({"anyone-else": "Yes"}) + self.add_person("John", "Doe") + + self.post({"anyone-else": "Yes"}) + self.add_person("James", "Bond") + + self.post({"anyone-else": "No"}) + + # 3. Complete John Doe section + self.get(self.john_doe_link()) + + self.answer_dob() + + self.post({"other-answer": 1}) + + self.post({"other-answer-2": 1}) + + self.post() + + # 4. Complete James Bond section + self.get(self.james_bond_link()) + + self.answer_dob() + + self.post({"other-answer": 1}) + + self.post({"other-answer-2": 1}) + + self.post() + + # END OF HAPPY PATH + + # 5. Go back to calculated summary and make it incomplete + self.get("/questionnaire/calculated-summary-block/") + # Edit first answer + first_answer_link = self.getHtmlSoup().find( + "a", {"data-qa": "first-number-answer-edit"} + )["href"] + self.get(first_answer_link) + self.post({"first-number-answer": 2}) + + # Don't complete the calculated summary, go back to the hub + self.go_to_hub() + + # 6. Section 1 should show as partially completed on the hub + # Other sections should show as completed + self.assert_section_status(1, "Partially completed") + self.assert_section_status(2, "Completed") + self.assert_section_status(3, "Completed") + self.assert_section_status(4, "Completed") + + self.get("/questionnaire/sections/section-2/") + # No random question + self.assertInBody("Does anyone else live here?") + + self.go_to_hub() + + self.get(self.john_doe_link()) + self.assertNotInBody("Random question about") + + self.go_to_hub() + + self.get(self.james_bond_link()) + self.assertNotInBody("Random question about") + + def test_progress_value_source_with_backward_chained_dependencies(self): + self.launchSurvey("test_progress_value_source_calculated_summary_extended") + self.post() + + # 1. Complete section 7 + self.go_to_section("section-7") + self.post({"s7-b3-q1-a1": 1}) + + # Check the section is complete + self.assertEqualUrl("/questionnaire/") + self.assert_section_status(6, "Completed") + + # 2. Complete section 5, will change the status of section 7 to partially completed + self.go_to_section("section-5") + self.post({"s5-b2-q1-a1": 1}) + + # Check the section is complete + self.assertEqualUrl("/questionnaire/") + self.assert_section_status(4, "Completed") + self.assert_section_status(6, "Partially completed") + + # 2. Complete section 1, this will change the status of section 5 to partially completed + # and section 7 should once again be complete + self.go_to_section("section-1") + self.post({"first-number-answer": 1}) + + self.post({"second-number-answer": 1}) + + self.post() + + # Check the section is complete + self.assertEqualUrl("/questionnaire/") + self.assert_section_status(1, "Completed") + self.assert_section_status(4, "Partially completed") + self.assert_section_status(6, "Completed") + + def test_progress_value_source_with_chained_dependencies(self): + self.launchSurvey("test_progress_value_source_calculated_summary_extended") + self.post() + + # 1. Complete section 8, 9, 10, 11 and 12 + self.go_to_section("section-12") + self.post({"s12-b2-q1-a1": 1}) + + self.go_to_section("section-11") + self.post({"s11-b2-q1-a1": 1}) + + self.go_to_section("section-8") + self.post({"s8-b3-q1-a1": 1}) + + self.go_to_section("section-9") + self.post({"s9-b2-q1-a1": 1}) + + self.go_to_section("section-10") + self.post({"s10-b2-q1-a1": 1}) + + # Check that sections 8, 9 and 10 are complete, and 11 and 12 are partially complete + self.assertEqualUrl("/questionnaire/") + self.assert_section_status(7, "Completed") + self.assert_section_status(8, "Completed") + self.assert_section_status(9, "Completed") + self.assert_section_status(10, "Partially completed") + self.assert_section_status(11, "Partially completed") + + # 2. Update the second section, this should make sections + self.go_to_section("section-2") + self.post({"s2-b1-q1-a1": 1}) + self.post({"anyone-else": "No"}) + + # Check that section 11 and 12 are complete, and 8, 9 and 10 are partially complete + self.assertEqualUrl("/questionnaire/") + self.assert_section_status(2, "Completed") + self.assert_section_status(7, "Partially completed") + self.assert_section_status(8, "Partially completed") + self.assert_section_status(9, "Partially completed") + self.assert_section_status(10, "Completed") + self.assert_section_status(11, "Completed") diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py new file mode 100644 index 0000000000..db12b2c901 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py @@ -0,0 +1,533 @@ +from tests.integration.integration_test_case import IntegrationTestCase + + +class TestQuestionnaireProgressValueSourceInRepeatingSections(IntegrationTestCase): + def answer_dob(self, payload=None): + if not payload: + payload = { + "date-of-birth-answer-year": "1998", + "date-of-birth-answer-month": "12", + "date-of-birth-answer-day": 12, + } + self.post(payload) + + def answer_dob_second_repeat(self, payload=None): + if not payload: + payload = { + "second-date-of-birth-answer-year": "1998", + "second-date-of-birth-answer-month": "12", + "second-date-of-birth-answer-day": 12, + } + self.post(payload) + + def john_doe_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-2-1-link"})[ + "href" + ] + + def james_bond_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-2-2-link"})[ + "href" + ] + + def jane_doe_link(self): + return self.getHtmlSoup().find("a", {"data-qa": "hub-row-section-4-1-link"})[ + "href" + ] + + def add_person(self, first_name, last_name): + self.assertInBody("What is the name of the person?") + self.post({"first-name": first_name, "last-name": last_name}) + + def add_person_second_list_collector(self, first_name, last_name): + self.assertInBody("What is the name of the person?") + self.post({"second-first-name": first_name, "second-last-name": last_name}) + + def assert_section_status(self, section_index, status, other_labels=None): + self.assertInSelector(status, self.row_selector(section_index)) + if other_labels and len(other_labels): + for other_label in other_labels: + self.assertInSelector(other_label, self.row_selector(section_index)) + + def go_to_section(self, section_id): + self.get(f"/questionnaire/sections/{section_id}/") + + def go_to_hub(self): + self.get("/questionnaire/") + + def test_disable_block_in_repeating_section_if_block_source_progress_not_completed( + self, + ): + """ + Test that a block inside a repeating section is disabled if the progress value source + from a block in another section is not completed + """ + + self.launchSurvey("test_progress_block_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. First section shows as not started + self.assertInSelector("List collector + random question", self.row_selector(1)) + self.assert_section_status(1, "Not started") + + # 2. Start completing section 1 and add 2 people + # Don't answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + # 3. Assert random question is there + self.assertInBody("Random question enabler") + + # 4. Go back to the hub and leave random question block incomplete + self.go_to_hub() + + # 5. The two repeating sections should show as "not started" + self.assertInBody("Choose another section to complete") + self.assert_section_status(2, "Not started", ["John Doe"]) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 5. Complete John Doe section + # Random question doesn't show + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # Assert random question not there + self.assertNotInBody("Random question about") + + def test_disable_block_in_repeating_section_if_section_source_progress_not_completed( + self, + ): + """ + Test that a block inside a repeating section is disabled if the progress value source + from a block in another section is not completed + """ + + self.launchSurvey("test_progress_section_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. First section shows as not started + self.assertInSelector("List collector + random question", self.row_selector(1)) + self.assert_section_status(1, "Not started") + + # 2. Start completing section 1 and add 2 people + # Don't answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + # 3. Assert random question is there + self.assertInBody("Random question enabler") + + # 4. Go back to the hub and leave random question block incomplete + self.go_to_hub() + + # 5. The two repeating sections should show as "not started" + self.assertInBody("Choose another section to complete") + self.assert_section_status(2, "Not started", ["John Doe"]) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 5. Complete John Doe section + # Random question doesn't show + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # Assert random question not there + self.assertNotInBody("Random question about") + + def test_enable_block_in_repeating_section_if_block_source_progress_is_completed( + self, + ): + """ + Test that a block inside a repeating section is enabled if the progress value source + from a block in another section is completeted + """ + + self.launchSurvey("test_progress_block_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. Complete 1st section and add 2 people + # And answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + self.assertInBody("Random question enabler") + self.post({"random-question-enabler-answer": 1}) + + # Go back to the hub + self.go_to_hub() + self.assertInBody("Choose another section to complete") + + # Assert first section completed + self.assert_section_status(1, "Completed", ["List collector + random question"]) + + # 2. Complete John Doe section + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # 3. Assert random question shows up + self.assertInBody("Random question") + + def test_enable_block_in_repeating_section_if_section_source_progress_is_completed( + self, + ): + """ + Test that a block inside a repeating section is enabled if the progress value source + from a block in another section is completeted + """ + self.launchSurvey("test_progress_section_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. Complete 1st section and add 2 people + # And answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + self.assertInBody("Random question enabler") + self.post({"random-question-enabler-answer": 1}) + + self.post() + + # Go back to the hub + self.go_to_hub() + self.assertInBody("Choose another section to complete") + + # Assert first section completed + self.assert_section_status(1, "Completed", ["List collector + random question"]) + + # 2. Complete John Doe section + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # 3. Assert random question shows up + self.assertInBody("Random question") + + # pylint: disable=locally-disabled, too-many-statements + def test_block_progress_dependencies_updated_in_repeating_sections(self): + """ + Test that dependency blocks inside repeating sections are updated properly + """ + + self.launchSurvey("test_progress_block_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. Complete 1st section and add 2 people + # Don't answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + self.assertInBody("Random question enabler") + + # 2. Go back to the hub and leave random question block incomplete + self.go_to_hub() + + # 3. Repeating sections show as "Not started" + self.assertInBody("Choose another section to complete") + self.assert_section_status(2, "Not started", ["John Doe"]) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 4. Complete John Doe section + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # 5. Assert random question not there + self.assertNotInBody("Random question about") + + # 6. Go back to section 1 and complete random question + self.go_to_section("section-1") + self.assertInBody("Random question enabler") + self.post({"random-question-enabler-answer": 1}) + + # 7. Go back to the hub + self.go_to_hub() + + # 8. Assert sections 1 is completed and repeating sections are partially completed + self.assertInBody("Choose another section to complete") + self.assert_section_status(1, "Completed", ["List collector + random question"]) + self.assert_section_status( + 2, "Partially completed", ["John Doe", "Continue with section"] + ) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 9. Go back to John Doe section + self.get(self.john_doe_link()) + + # 10. Assert prompts for random question + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + + # 12. Assert it goes to the section summary + self.assertInUrl("questionnaire/sections/section-2") + + # 13. Assert answer was provided + self.assertInSelector("Random question about", self.row_selector(2)) + self.assertNotInSelector("No answer provided", self.row_selector(2)) + + # 14. Go back to summary + self.go_to_hub() + + # 15. Assert John Doe section is completed + self.assert_section_status(2, "Completed", ["John Doe"]) + + # 15. Edit James Bond section + self.get(self.james_bond_link()) + self.assertInBody("James Bond") + self.answer_dob() + + # 16. Assert random question shows up + self.assertInBody("Random question about") + + def test_section_progress_dependencies_updated_in_repeating_sections(self): + """ + Test that dependency blocks inside repeating sections are updated properly + """ + + self.launchSurvey("test_progress_section_value_source_repeating_sections") + + self.assertInBody("Choose another section to complete") + + # 1. Complete 1st section and add 2 people + # Don't answer the random question enabler + self.go_to_section("section-1") + self.assertInBody("Does anyone else live here?") + self.post({"anyone-else": "Yes"}) + + self.add_person("John", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("John Doe", self.row_selector(1)) + self.post({"anyone-else": "Yes"}) + + self.add_person("James", "Bond") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("James Bond", self.row_selector(2)) + self.post({"anyone-else": "No"}) + + self.post() + + self.assertInBody("Random question enabler") + + # 2. Go back to the hub and leave random question block incomplete + self.go_to_hub() + + # 3. Repeating sections show as "Not started" + self.assertInBody("Choose another section to complete") + self.assert_section_status(2, "Not started", ["John Doe"]) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 4. Complete John Doe section + self.get(self.john_doe_link()) + self.assertInBody("John Doe") + self.answer_dob() + + # 5. Assert random question not there + self.assertNotInBody("Random question about") + + # 6. Go back to section 1 and complete random question + self.go_to_section("section-1") + self.assertInBody("Random question enabler") + self.post({"random-question-enabler-answer": 1}) + + # 7. Go back to the hub + self.go_to_hub() + + # 8. Assert sections 1 is completed and repeating sections are partially completed + self.assertInBody("Choose another section to complete") + self.assert_section_status(1, "Completed", ["List collector + random question"]) + self.assert_section_status( + 2, "Partially completed", ["John Doe", "Continue with section"] + ) + self.assert_section_status(3, "Not started", ["James Bond"]) + + # 9. Go back to John Doe section + self.get(self.john_doe_link()) + + # 10. Assert prompts for random question + self.assertInBody("Random question about") + self.post({"other-answer": 1}) + + # 12. Assert it goes to the section summary + self.assertInUrl("questionnaire/sections/section-2") + + # 13. Assert answer was provided + self.assertInSelector("Random question about", self.row_selector(2)) + self.assertNotInSelector("No answer provided", self.row_selector(2)) + + # 14. Go back to summary + self.go_to_hub() + + # 15. Assert John Doe section is completed + self.assert_section_status(2, "Completed", ["John Doe"]) + + # 15. Edit James Bond section + self.get(self.james_bond_link()) + self.assertInBody("James Bond") + self.answer_dob() + + # 16. Assert random question shows up + self.assertInBody("Random question about") + + def test_section_progress_dependencies_updated_in_repeating_sections_with_chained_dependencies( + self, + ): + """ + Test that dependency blocks inside repeating sections are updated properly when there are chained dependencies + """ + self.launchSurvey( + "test_progress_value_source_repeating_sections_chained_dependencies" + ) + + self.assertInBody("Choose another section to complete") + + # 1. Complete 3rd section and add 2 people + # And answer the random question enabler + self.go_to_section("section-3") + self.assertInBody("Does anyone else live here?") + self.post({"second-anyone-else": "Yes"}) + + self.add_person_second_list_collector("Jane", "Doe") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("Jane Doe", self.row_selector(1)) + self.post({"second-anyone-else": "Yes"}) + + self.add_person_second_list_collector("Marie", "Clare") + + self.assertInBody("Does anyone else live here?") + self.assertInSelector("Marie Clare", self.row_selector(2)) + self.post({"second-anyone-else": "No"}) + + self.post() + + self.assertInBody("Random question enabler") + self.post({"second-random-question-enabler-answer": 1}) + + self.post() + + # Go back to the hub + self.go_to_hub() + + # 2. The two repeating sections should show as "not started" + self.assertInBody("Choose another section to complete") + self.assert_section_status(4, "Not started", ["Jane Doe"]) + self.assert_section_status(5, "Not started", ["Marie Clare"]) + + # 3. Complete Jane Doe section + self.get(self.jane_doe_link()) + self.assertInBody("Jane Doe") + self.answer_dob_second_repeat() + self.post({"second-other-answer": 1}) + + # Go back to the hub + self.go_to_hub() + + # 4. The Jane Doe section should now be Complete + self.assertInBody("Choose another section to complete") + self.assert_section_status(4, "Completed", ["Jane Doe"]) + self.assert_section_status(5, "Not started", ["Marie Clare"]) + + # 5. Complete section 2 + self.go_to_section("section-2") + self.post({"s2-b2-q1-a1": 1}) + + # 6. Section-2 and should be complete but the Jane Doe section should be partially completed + self.assertInBody("Choose another section to complete") + self.assert_section_status(2, "Completed") + self.assert_section_status(4, "Partially completed", ["Jane Doe"]) + self.assert_section_status(5, "Not started", ["Marie Clare"]) + + # 5. Complete section 1 + self.go_to_section("section-1") + self.post({"s1-b1-q1-a1": 1}) + + # 6. Section 1 and the Jane Doe section should now be but complete + # but Section-2 should be partially completed + self.assertInBody("Choose another section to complete") + self.assert_section_status(1, "Completed") + self.assert_section_status(2, "Partially completed") + self.assert_section_status(4, "Completed", ["Jane Doe"]) + self.assert_section_status(5, "Not started", ["Marie Clare"]) diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py new file mode 100644 index 0000000000..d528cfc9a7 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py @@ -0,0 +1,176 @@ +from tests.integration.integration_test_case import IntegrationTestCase + + +class TestQuestionnaireProgressValueSource(IntegrationTestCase): + def assert_section_status(self, section_index, status, other_labels=None): + self.assertInSelector(status, self.row_selector(section_index)) + if other_labels and len(other_labels) > 0: + for other_label in other_labels: + self.assertInSelector(other_label, self.row_selector(section_index)) + + def go_to_section(self, section_id): + self.get(f"/questionnaire/sections/{section_id}/") + + def go_to_hub(self): + self.get("/questionnaire/") + + def test_enable_section_by_progress_linear_flow(self): + """ + Test that a section is enabled by progress value source + In a linear flow with no hub + """ + + self.launchSurvey("test_progress_value_source_section_enabled_no_hub") + + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + self.assertInBody("Section 2 Question 1") + self.post({"s2-b1-q1-a1": 1}) + + def test_enable_section_by_progress_hub_flow(self): + """ + Test that a section is enabled by progress value source + In a hub flow + """ + + self.launchSurvey("test_progress_value_source_section_enabled_hub") + + # 1. Only section 1 shows on the hub + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 1") + self.assertNotInBody("Section 2") + + # 2. Complete section 1 + self.go_to_section("section-1") + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInUrl("/questionnaire/s1-b2") + self.post({"s1-b2-q1-a1": 1}) + + # 3. Assert section 1 completed on the hub + self.assertEqualUrl("/questionnaire/") + + self.assert_section_status(1, "Completed", ["View answers"]) + + # 4. Assert section 2 is now available on the hub + self.assert_section_status(2, "Not started", ["Start section"]) + + # 5. Complete section 2 + self.go_to_section("section-2") + self.assertInUrl("/questionnaire/s2-b1") + self.post({"s2-b1-q1-a1": 1}) + + self.assertEqualUrl("/questionnaire/") + + # 6. Assert section 2 completed on the hub + self.assert_section_status(1, "Completed", ["View answers"]) + self.assert_section_status(2, "Completed", ["View answers"]) + + self.assertInBody("Submit survey") + + def test_value_source_dependency_enable_section_by_progress_hub_flow(self): + """ + Test that dependencies that rely on a section's progress + are updated when the section progress changes + """ + + self.launchSurvey("test_progress_value_source_section_enabled_hub") + + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 1") + self.assertNotInBody("Section 2") + + # 1. Start section 1 + self.go_to_section("section-1") + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInBody("Section 1 Question 2") + + # 2. Leave section 1 incomplete + self.get("/questionnaire/") + + # 3. Assert that section 2 is not enabled + self.assertNotInBody("Section 2") + + # 4. Assert section 1 is in progress + self.assert_section_status(1, "Partially completed", ["Continue with section"]) + + # 5. Go back to section 1 and complete it + self.get("/questionnaire/sections/section-1/?resume=True") + + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + self.assertEqualUrl("/questionnaire/") + + # 6. Assert section 1 completed on the hub + self.assert_section_status(1, "Completed", ["View answers"]) + + # 7. Assert that section 2 is now enabled + self.assert_section_status(2, "Not started", ["Start section"]) + + def test_enable_section_by_progress_hub_complex_happy_path(self): + self.launchSurvey("test_progress_value_source_section_enabled_hub_complex") + + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 1") + self.assertInBody("Section 3") + self.assertNotInBody("Section 2") + + # 1. Complete section 1 + self.go_to_section("section-1") + self.assertInBody("Section 1 Question 1") + self.post({"s1-b1-q1-a1": 1}) + + self.assertInBody("Section 1 Question 2") + self.post({"s1-b2-q1-a1": 1}) + + # 2. Complete section 2 with all the questions + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 2") + self.go_to_section("section-2") + + self.assertInBody("Section 2 Question 1") + self.post({"s2-b1-q1-a1": 1}) + + self.assertInBody("Section 2 Question 2") + self.post({"s2-b2-q1-a1": 0}) + + self.assertInBody("Section 2 Question 3") + self.post({"s2-b3-q1-a1": 0}) + + # 3. Assert section 3 is on the path + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 3") + self.go_to_section("section-3") + self.assertInUrl("/questionnaire/s3-b1") + + # 4. Complete section 3 + self.assertInBody("Section 3 Question 1") + self.post({"s3-b1-q1-a1": 0}) + + # 5. Complete section 4 + self.assertInBody("Choose another section to complete") + self.assertInBody("Section 4") + self.go_to_section("section-4") + self.assertInUrl("/questionnaire/s4-b1") + + self.assertInBody("Section 4 Question 1") + self.post({"s4-b1-q1-a1": 0}) + + # 6. Assert all sections have been completed + + for sel in ( + self.row_selector(1), + self.row_selector(2), + self.row_selector(3), + self.row_selector(4), + ): + self.assertInSelector("Completed", sel) + self.assertInSelector("View answers", sel) diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 0acc0ec6f7..8c8a228ef7 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -21,7 +21,7 @@ def test_google_analytics_code_and_credentials_are_present(self): self.get("/dump/debug") actual = json_loads(self.getResponseData()) self._client.set_cookie( - "localhost", key="ons_cookie_policy", value="'usage':true" + domain="localhost", key="ons_cookie_policy", value="'usage':true" ) self.get("/questionnaire/feedback/") self.assertStatusOK() @@ -38,7 +38,7 @@ def test_google_analytics_data_layer_has_no_null_fields(self): self.get("/dump/debug") actual = json_loads(self.getResponseData()) self._client.set_cookie( - "localhost", key="ons_cookie_policy", value="'usage':true" + domain="localhost", key="ons_cookie_policy", value="'usage':true" ) self.get("/questionnaire/name-block/") self.assertStatusOK() @@ -53,7 +53,7 @@ def test_google_analytics_data_layer_is_set_to_nisra_false(self): self.get("/dump/debug") actual = json_loads(self.getResponseData()) self._client.set_cookie( - "localhost", key="ons_cookie_policy", value="'usage':true" + domain="localhost", key="ons_cookie_policy", value="'usage':true" ) self.get("/questionnaire/individual-confirmation/") self.assertStatusOK() From 82b2abee888216147784d6ae287b530fa9b2094d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 30 May 2023 13:51:40 +0100 Subject: [PATCH 232/567] Fix handling of invalid values in form numerical inputs (#1111) --- .../fields/decimal_field_with_separator.py | 7 +-- .../fields/integer_field_with_separator.py | 7 +-- app/forms/validators.py | 44 +++++-------------- app/helpers/form_helpers.py | 33 ++++++++++++++ tests/app/forms/test_custom_fields.py | 41 +++++++++++++++++ .../forms/validation/test_number_validator.py | 18 ++++---- tests/functional/spec/error_messages.spec.js | 18 ++++++++ 7 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 app/helpers/form_helpers.py diff --git a/app/forms/fields/decimal_field_with_separator.py b/app/forms/fields/decimal_field_with_separator.py index 666540a56a..ea202f0b50 100644 --- a/app/forms/fields/decimal_field_with_separator.py +++ b/app/forms/fields/decimal_field_with_separator.py @@ -1,9 +1,8 @@ from decimal import Decimal, InvalidOperation -from babel import numbers from wtforms import DecimalField -from app.settings import DEFAULT_LOCALE +from app.helpers.form_helpers import sanitise_number class DecimalFieldWithSeparator(DecimalField): @@ -23,8 +22,6 @@ def __init__(self, **kwargs): def process_formdata(self, valuelist): if valuelist: try: - self.data = Decimal( - valuelist[0].replace(numbers.get_group_symbol(DEFAULT_LOCALE), "") - ) + self.data = Decimal(sanitise_number(valuelist[0])) except (ValueError, TypeError, InvalidOperation): pass diff --git a/app/forms/fields/integer_field_with_separator.py b/app/forms/fields/integer_field_with_separator.py index 7a3098a8c7..d218ab29b3 100644 --- a/app/forms/fields/integer_field_with_separator.py +++ b/app/forms/fields/integer_field_with_separator.py @@ -1,7 +1,6 @@ -from babel import numbers from wtforms import IntegerField -from app.settings import DEFAULT_LOCALE +from app.helpers.form_helpers import sanitise_number class IntegerFieldWithSeparator(IntegerField): @@ -21,8 +20,6 @@ def __init__(self, **kwargs): def process_formdata(self, valuelist): if valuelist: try: - self.data = int( - valuelist[0].replace(numbers.get_group_symbol(DEFAULT_LOCALE), "") - ) + self.data = int(sanitise_number(valuelist[0])) except ValueError: pass diff --git a/app/forms/validators.py b/app/forms/validators.py index 9366b54126..1739808770 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -1,5 +1,6 @@ from __future__ import annotations +import math import re from datetime import datetime, timezone from decimal import Decimal, InvalidOperation @@ -19,10 +20,14 @@ DecimalFieldWithSeparator, IntegerFieldWithSeparator, ) -from app.jinja_filters import format_number, get_formatted_currency +from app.helpers.form_helpers import ( + format_message_with_title, + format_playback_value, + sanitise_mobile_number, + sanitise_number, +) from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.rules.utils import parse_datetime -from app.utilities import safe_content if TYPE_CHECKING: from app.forms.questionnaire_form import QuestionnaireForm # pragma: no cover @@ -49,15 +54,12 @@ def __call__( field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], ) -> None: try: - Decimal( - field.raw_data[0].replace( - numbers.get_group_symbol(flask_babel.get_locale()), "" - ) - ) + # number is sanitised to guard against inputs like `,NaN_` etc + number = Decimal(sanitise_number(number=field.raw_data[0])) except (ValueError, TypeError, InvalidOperation, AttributeError) as exc: raise validators.StopValidation(self.message) from exc - if "e" in field.raw_data[0].lower(): + if "e" in field.raw_data[0].lower() or math.isnan(number): raise validators.StopValidation(self.message) @@ -126,6 +128,7 @@ def __call__( field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], ) -> None: value: Union[int, Decimal] = field.data + if value is not None: error_message = self.validate_minimum(value) or self.validate_maximum(value) if error_message: @@ -179,11 +182,7 @@ def __init__(self, max_decimals: int = 0, messages: OptionalMessage = None): def __call__( self, form: "QuestionnaireForm", field: DecimalFieldWithSeparator ) -> None: - data = ( - field.raw_data[0] - .replace(numbers.get_group_symbol(flask_babel.get_locale()), "") - .replace(" ", "") - ) + data = sanitise_number(field.raw_data[0]) decimal_symbol = numbers.get_decimal_symbol(flask_babel.get_locale()) if data and decimal_symbol in data: if self.max_decimals == 0: @@ -450,20 +449,6 @@ def _is_valid( raise NotImplementedError(f"Condition '{condition}' is not implemented") -def format_playback_value( - value: Union[float, Decimal], currency: Optional[str] = None -) -> str: - if currency: - return get_formatted_currency(value, currency) - - formatted_number: str = format_number(value) - return formatted_number - - -def format_message_with_title(error_message: str, question_title: str) -> str: - return error_message % {"question_title": safe_content(question_title)} - - class MutuallyExclusiveCheck: def __init__(self, question_title: str, messages: OptionalMessage = None): self.messages = {**error_messages, **(messages or {})} @@ -492,11 +477,6 @@ def __call__( raise validators.ValidationError(message) -def sanitise_mobile_number(data: str) -> str: - data = re.sub(r"[\s.,\t\-{}\[\]()/]", "", data) - return re.sub(r"^(0{1,2}44|\+44|0)", "", data) - - class MobileNumberCheck: def __init__(self, message: OptionalMessage = None): self.message = message or error_messages["INVALID_MOBILE_NUMBER"] diff --git a/app/helpers/form_helpers.py b/app/helpers/form_helpers.py new file mode 100644 index 0000000000..52d773c879 --- /dev/null +++ b/app/helpers/form_helpers.py @@ -0,0 +1,33 @@ +import re +from decimal import Decimal + +import flask_babel +from babel import numbers + +from app.jinja_filters import format_number, get_formatted_currency +from app.utilities import safe_content + + +def sanitise_number(number: str) -> str: + return ( + number.replace(numbers.get_group_symbol(flask_babel.get_locale()), "") + .replace("_", "") + .replace(" ", "") + ) + + +def sanitise_mobile_number(data: str) -> str: + data = re.sub(r"[\s.,\t\-{}\[\]()/]", "", data) + return re.sub(r"^(0{1,2}44|\+44|0)", "", data) + + +def format_playback_value(value: float | Decimal, currency: str | None = None) -> str: + if currency: + return get_formatted_currency(value, currency) + + formatted_number: str = format_number(value) + return formatted_number + + +def format_message_with_title(error_message: str, question_title: str) -> str: + return error_message % {"question_title": safe_content(question_title)} diff --git a/tests/app/forms/test_custom_fields.py b/tests/app/forms/test_custom_fields.py index a1d5454371..ef79081356 100644 --- a/tests/app/forms/test_custom_fields.py +++ b/tests/app/forms/test_custom_fields.py @@ -1,3 +1,5 @@ +from decimal import Decimal + import pytest from wtforms.fields import Field @@ -21,6 +23,7 @@ def test_text_area_supports_maxlength_property(mock_form): assert text_area.maxlength == 20 +@pytest.mark.usefixtures("gb_locale") def test_integer_field(mock_form): integer_field = IntegerFieldWithSeparator(_form=mock_form, name="aName") assert isinstance(integer_field, Field) @@ -31,6 +34,44 @@ def test_integer_field(mock_form): pytest.fail("Exceptions should not thrown by CustomIntegerField") +@pytest.mark.usefixtures("gb_locale") +@pytest.mark.parametrize( + "number_input, result", + [ + ("_110", 110), + ("1_10", 110), + ("1__10", 110), + ("1_1,0", 110), + ("_1_1,0,0", 1100), + ("1.10", None), + ], +) +def test_integer_field_inputs(mock_form, number_input, result): + integer_field = IntegerFieldWithSeparator(_form=mock_form, name="aName") + integer_field.process_formdata([number_input]) + + assert integer_field.data == result + + +@pytest.mark.usefixtures("gb_locale") +@pytest.mark.parametrize( + "number_input, result", + [ + ("1_1,0", Decimal("110")), + ("1.10", Decimal("1.1")), + ("_1.1_0", Decimal("1.1")), + ("_1.1_0,0", Decimal("1.1")), + ("_1._1,0,0", Decimal("1.1")), + ], +) +def test_decimal_field_inputs(mock_form, number_input, result): + decimal_field = DecimalFieldWithSeparator(_form=mock_form, name="aName") + decimal_field.process_formdata([number_input]) + + assert decimal_field.data == result + + +@pytest.mark.usefixtures("gb_locale") def test_decimal_field(mock_form): decimal_field = DecimalFieldWithSeparator(_form=mock_form, name="aName") assert isinstance(decimal_field, Field) diff --git a/tests/app/forms/validation/test_number_validator.py b/tests/app/forms/validation/test_number_validator.py index 81d45fddde..ddc61ecf14 100644 --- a/tests/app/forms/validation/test_number_validator.py +++ b/tests/app/forms/validation/test_number_validator.py @@ -1,3 +1,5 @@ +from decimal import Decimal + import pytest from wtforms.validators import StopValidation, ValidationError @@ -7,12 +9,7 @@ @pytest.mark.parametrize( "value", - ( - [None], - [""], - ["a"], - ["2E2"], - ), + ([None], [""], ["a"], ["2E2"], ["NaN"], [",NaN_"]), ) @pytest.mark.usefixtures("gb_locale") def test_number_validator_raises_StopValidation( @@ -49,12 +46,13 @@ def test_decimal_validator_raises_StopValidation( @pytest.mark.parametrize( "value", ( - ["0"], - ["10"], - ["-10"], + "0", + "10", + "-10", ), ) @pytest.mark.usefixtures("gb_locale") def test_number_validator(number_check, value, mock_form, mock_field): - mock_field.raw_data = value + mock_field.raw_data = [value] + mock_field.data = Decimal(value) number_check(mock_form, mock_field) diff --git a/tests/functional/spec/error_messages.spec.js b/tests/functional/spec/error_messages.spec.js index c363f9433e..ac1d4a9fde 100644 --- a/tests/functional/spec/error_messages.spec.js +++ b/tests/functional/spec/error_messages.spec.js @@ -1,4 +1,5 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; +import BlockPage from "../generated_pages/percentage/block.page"; async function answerAllButOne() { await $(AboutYou.textfield()).setValue("John Doe"); @@ -135,3 +136,20 @@ describe("Error Messages", () => { await expect(await $(AboutYou.textarea()).isFocused()).to.be.true; }); }); +describe("Error Message NaN value", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_percentage.json"); + }); + it("Given a NaN value was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { + await $(BlockPage.answer()).setValue("NaN"); + await $(BlockPage.submit()).click(); + await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); + await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); + }); + it("Given a NaN value with separators was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { + await $(BlockPage.answer()).setValue(",NaN_"); + await $(BlockPage.submit()).click(); + await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); + await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); + }); +}); From f4046195902e80654f6eed5e20782bb78a697f31 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:34:57 +0100 Subject: [PATCH 233/567] Update Chromedriver to version 113 (#1118) * update chromedriver * temporarily comment out line, to be fixed separately --- package.json | 2 +- tests/functional/spec/textfield_suggestions.spec.js | 3 ++- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f4e26d4f46..5e7eb261de 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/mocha-framework": "^8.3.0", "@wdio/spec-reporter": "^8.3.0", "chai": "^4.3.6", - "chromedriver": "^112.0.0", + "chromedriver": "^113.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index 24807ef3a9..80cd8fd70a 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -31,7 +31,8 @@ describe("Suggestions", () => { await browser.keys(" United"); await suggestionsList.waitForExist(); await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); - await suggestionsOption.click(); + // TODO there is an issue with the load-time of the auto-suggest dropdown causing this test to fail. Uncomment when this has been resolved. + // await suggestionsOption.click(); await $(MultipleSuggestionsPage.submit()).click(); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); diff --git a/yarn.lock b/yarn.lock index e07600b0ed..724845cce5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,10 +1786,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^112.0.0: - version "112.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-112.0.1.tgz#b1d23720613d7afa17cde6d5057e765109e07e69" - integrity sha512-ieQzvellbtPY4MUrFzzayC1bZa/HoBsGXejUQJhAPWcYALxtkjUZNUYWbojMjIzf8iIhVda9VvdXiRKqdlN7ow== +chromedriver@^113.0.0: + version "113.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-113.0.0.tgz#d4855f156ee51cea4282e04aadd29fa154e44dbb" + integrity sha512-UnQlt2kPicYXVNHPzy9HfcWvEbKJjjKAEaatdcnP/lCIRwuSoZFVLH0HVDAGdbraXp3dNVhfE2Qx7gw8TnHnPw== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From 12fb8ff328e97457f3c7acba17953e5b84da0dad Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:52:43 +0100 Subject: [PATCH 234/567] Feat/Grand calculated summary (#1107) * initial ideas * Implementation closer to calculated summaries * better test schemas and routing evaluation * get routing working * dont need section id * make routing better, start adding tests * start fixing test schemas * Two further schema fixes * Fix remaining schema issue * Add integration test * Add functional test * Functional tests for routing and answers not on the path * Changing an answer updates GCS progress * Add schema for overlapping answers * Functionally test skip-question in grand calculated summary * Add tests for GCS routing * Fix errors caused by merge * type hints, comments, remove some duplication * fix formatting * Update translation templates * remove accidental update * Fix some type hints * Use validator branch and update test description * refactor GCS answer format * Fix lint and test errors * Address PR comments * improve context and coverage of test schemas * Fix invalid schema and add invalid routing test * Fix line length in test * test schema linting * Fix routing bug * Make schema easier to follow * Linting * partial fix to routing * Add addtional routing test case * Remove arg from new test case * Routing to next incomplete block for summaries * linting error * remove arg from router tests * PR comments * PR Comment * Amend comment and improve dependencies function * Handle merge errors * fix gcs routing within same section * Add test for GCS with progress value source * Remove backtick * update chromedriver * Revert "update chromedriver" This reverts commit a2e3698b7a3f437b76e08472f973a4aef8d273d9. * Fix schema labels * Revert validator branch to latest --- app/jinja_filters.py | 18 +- app/questionnaire/questionnaire_schema.py | 95 +++- app/questionnaire/router.py | 170 ++++++- app/questionnaire/value_source_resolver.py | 14 +- app/translations/messages.pot | 56 +-- app/views/contexts/__init__.py | 2 + .../contexts/calculated_summary_context.py | 131 ++++-- .../grand_calculated_summary_context.py | 124 ++++++ app/views/contexts/section_summary_context.py | 4 +- .../summary/calculated_summary_block.py | 83 ++++ app/views/contexts/summary/group.py | 55 ++- .../contexts/summary/list_collector_block.py | 9 +- app/views/handlers/block.py | 8 + app/views/handlers/block_factory.py | 6 +- ...ated_summary.py => calculation_summary.py} | 21 +- .../en/test_grand_calculated_summary.json | 294 ++++++++++++ ...ed_summary_cross_section_dependencies.json | 341 ++++++++++++++ ..._calculated_summary_multiple_sections.json | 348 +++++++++++++++ ...alculated_summary_overlapping_answers.json | 329 ++++++++++++++ templates/calculatedsummary.html | 35 +- templates/grandcalculatedsummary.html | 5 + templates/layouts/_calculatedsummary.html | 35 ++ tests/app/questionnaire/conftest.py | 25 +- tests/app/questionnaire/test_router.py | 418 +++++++++++++++++- tests/app/views/contexts/__init__.py | 10 +- tests/app/views/contexts/conftest.py | 20 + .../test_calculated_summary_context.py | 89 +++- .../test_grand_calculated_summary_context.py | 111 +++++ .../grand-calculated-summary.page.js | 17 + tests/functional/generate_pages.py | 19 + ...summary_cross_section_dependencies.spec.js | 120 +++++ ...lculated_summary_multiple_sections.spec.js | 154 +++++++ ...ulated_summary_overlapping_answers.spec.js | 130 ++++++ ..._questionnaire_grand_calculated_summary.py | 163 +++++++ 34 files changed, 3276 insertions(+), 183 deletions(-) create mode 100644 app/views/contexts/grand_calculated_summary_context.py create mode 100644 app/views/contexts/summary/calculated_summary_block.py rename app/views/handlers/{calculated_summary.py => calculation_summary.py} (65%) create mode 100644 schemas/test/en/test_grand_calculated_summary.json create mode 100644 schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json create mode 100644 schemas/test/en/test_grand_calculated_summary_multiple_sections.json create mode 100644 schemas/test/en/test_grand_calculated_summary_overlapping_answers.json create mode 100644 templates/grandcalculatedsummary.html create mode 100644 templates/layouts/_calculatedsummary.html create mode 100644 tests/app/views/contexts/test_grand_calculated_summary_context.py create mode 100644 tests/functional/base_pages/grand-calculated-summary.page.js create mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js create mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js create mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js create mode 100644 tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 8bfe24b0f1..834a036502 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -12,6 +12,7 @@ from markupsafe import Markup, escape from wtforms import SelectFieldBase +from app.questionnaire.questionnaire_schema import is_summary_with_calculation from app.questionnaire.rules.utils import parse_datetime from app.settings import MAX_NUMBER @@ -469,7 +470,7 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche ( multiple_answers or answer_type == "relationship" - or summary_type == "CalculatedSummary" + or is_summary_with_calculation(summary_type) ) and "label" in answer and answer["label"] @@ -553,7 +554,7 @@ def __init__( multiple_answers = len(question["answers"]) > 1 - if summary_type == "CalculatedSummary" and not answers_are_editable: + if is_summary_with_calculation(summary_type) and not answers_are_editable: self.total = True for answer in question["answers"]: @@ -598,6 +599,17 @@ def map_summary_item_config( edit_link_aria_label, ) ) + elif block.get("calculated_summary"): + rows.append( + SummaryRow( + block["calculated_summary"], + summary_type, + answers_are_editable, + no_answer_provided, + edit_link_text, + edit_link_aria_label, + ) + ) else: list_collector_rows = map_list_collector_config( list_items=block["list"]["list_items"], @@ -613,7 +625,7 @@ def map_summary_item_config( rows.extend(list_collector_rows) - if summary_type == "CalculatedSummary": + if is_summary_with_calculation(summary_type): rows.append(SummaryRow(calculated_question, summary_type, False, "", "", "")) return rows diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 2f57e8874f..2eb751424d 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -326,11 +326,8 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: def _populate_answer_dependencies(self) -> None: for block in self.get_blocks(): - if block["type"] == "CalculatedSummary": - answer_ids_for_block = get_calculated_summary_answer_ids(block) - self._update_answer_dependencies_for_calculated_summary( - answer_ids_for_block, block["id"] - ) + if block["type"] in {"CalculatedSummary", "GrandCalculatedSummary"}: + self._update_answer_dependencies_for_summary(block) continue for question in self.get_all_questions_for_block(block): @@ -363,14 +360,44 @@ def _populate_answer_dependencies(self) -> None: option["detail_answer"], block_id=block["id"] ) - def _update_answer_dependencies_for_calculated_summary( - self, calculated_summary_answer_ids: Iterable[str], block_id: str + def _update_answer_dependencies_for_summary(self, block: ImmutableDict) -> None: + if block["type"] == "CalculatedSummary": + self._update_answer_dependencies_for_calculated_summary_dependency( + calculated_summary_block=block, dependent_block=block + ) + elif block["type"] == "GrandCalculatedSummary": + self._update_answer_dependencies_for_grand_calculated_summary(block) + + def _update_answer_dependencies_for_calculated_summary_dependency( + self, *, calculated_summary_block: ImmutableDict, dependent_block: ImmutableDict ) -> None: + """ + update all calculated summary answers to be dependencies of the dependent block + """ + calculated_summary_answer_ids = get_calculated_summary_answer_ids( + calculated_summary_block + ) for answer_id in calculated_summary_answer_ids: self._answer_dependencies_map[answer_id] |= { - self._get_answer_dependent_for_block_id(block_id=block_id) + self._get_answer_dependent_for_block_id(block_id=dependent_block["id"]) } + def _update_answer_dependencies_for_grand_calculated_summary( + self, grand_calculated_summary_block: ImmutableDict + ) -> None: + grand_calculated_summary_calculated_summary_ids = ( + get_calculation_block_ids_for_grand_calculated_summary( + grand_calculated_summary_block + ) + ) + for calculated_summary_id in grand_calculated_summary_calculated_summary_ids: + # Type ignore: safe to assume block exists + calculated_summary_block: ImmutableDict = self.get_block(calculated_summary_id) # type: ignore + self._update_answer_dependencies_for_calculated_summary_dependency( + calculated_summary_block=calculated_summary_block, + dependent_block=grand_calculated_summary_block, + ) + def _update_answer_dependencies_for_calculations( self, calculations: tuple[ImmutableDict, ...], *, block_id: str ) -> None: @@ -408,7 +435,7 @@ def _update_answer_dependencies_for_answer( def _update_answer_dependencies_for_dynamic_options( self, - dynamic_options_values: Mapping[str, Mapping], + dynamic_options_values: Mapping, *, block_id: str, answer_id: str, @@ -806,6 +833,23 @@ def get_first_answer_id_for_block(self, block_id: str) -> str: answer_ids = self.get_answer_ids_for_block(block_id) return answer_ids[0] + def get_answer_format_for_calculated_summary( + self, calculated_summary_block_id: str + ) -> dict: + """ + Given a calculated summary block id, find the format of the total by using the first answer + """ + # Type ignore: the block will exist for any valid calculated summary id + calculated_summary_block: ImmutableDict = self.get_block(calculated_summary_block_id) # type: ignore + first_answer_id = get_calculated_summary_answer_ids(calculated_summary_block)[0] + first_answer = self.get_answers_by_answer_id(first_answer_id)[0] + return { + "type": first_answer["type"].lower(), + "unit": first_answer.get("unit"), + "unit_length": first_answer.get("unit_length"), + "currency": first_answer.get("currency"), + } + def get_answer_ids_for_block(self, block_id: str) -> list[str]: block = self.get_block(block_id) @@ -1205,23 +1249,44 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None: return f"#{str(item['item_anchor_answer_id'])}" +def is_summary_with_calculation(summary_type: str) -> bool: + return summary_type in {"GrandCalculatedSummary", "CalculatedSummary"} + + def get_sources_for_type_from_data( *, source_type: str, data: MultiDict | Mapping | Sequence, - ignore_keys: list, -) -> list | None: + ignore_keys: list | None = None, +) -> list: sources = get_mappings_with_key("source", data, ignore_keys=ignore_keys) return [source for source in sources if source["source"] == source_type] +def get_identifiers_from_calculation_block( + *, calculation_block: Mapping, source_type: str +) -> list[str]: + values = get_sources_for_type_from_data( + source_type=source_type, data=calculation_block["calculation"]["operation"] + ) + + return [value["identifier"] for value in values] + + def get_calculated_summary_answer_ids(calculated_summary_block: Mapping) -> list[str]: if calculated_summary_block["calculation"].get("answers_to_calculate"): - return calculated_summary_block["calculation"]["answers_to_calculate"] # type: ignore + return list(calculated_summary_block["calculation"]["answers_to_calculate"]) - values = get_mappings_with_key( - "source", calculated_summary_block["calculation"]["operation"] + return get_identifiers_from_calculation_block( + calculation_block=calculated_summary_block, source_type="answers" ) - return [value["identifier"] for value in values if value["source"] == "answers"] + +def get_calculation_block_ids_for_grand_calculated_summary( + grand_calculated_summary_block: Mapping, +) -> list[str]: + return get_identifiers_from_calculation_block( + calculation_block=grand_calculated_summary_block, + source_type="calculated_summary", + ) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index b5ab65e7d5..23a672c86d 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -133,6 +133,7 @@ def get_next_location_url( location, return_to, routing_path, + is_for_previous=False, is_section_complete=is_section_complete, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, @@ -179,6 +180,7 @@ def get_previous_location_url( location, return_to, routing_path, + is_for_previous=True, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, ): @@ -217,6 +219,7 @@ def _get_return_to_location_url( location: Location, return_to: str | None, routing_path: RoutingPath, + is_for_previous: bool, is_section_complete: bool | None = None, return_to_answer_id: str | None = None, return_to_block_id: str | None = None, @@ -224,22 +227,29 @@ def _get_return_to_location_url( if not return_to: return None - if return_to == "calculated-summary" and self.can_access_location( - Location( - block_id=return_to_block_id, - section_id=location.section_id, - list_item_id=location.list_item_id, - ), - routing_path, + if return_to == "grand-calculated-summary" and ( + url := self._get_return_to_for_grand_calculated_summary( + return_to=return_to, + return_to_block_id=return_to_block_id, + location=location, + routing_path=routing_path, + is_for_previous=is_for_previous, + return_to_answer_id=return_to_answer_id, + ) ): - return url_for( - "questionnaire.block", - block_id=return_to_block_id, - list_name=location.list_name, - list_item_id=location.list_item_id, + return url + + if return_to.startswith("calculated-summary") and ( + url := self._get_return_to_for_calculated_summary( return_to=return_to, - _anchor=return_to_answer_id, + return_to_block_id=return_to_block_id, + location=location, + routing_path=routing_path, + is_for_previous=is_for_previous, + return_to_answer_id=return_to_answer_id, ) + ): + return url if is_section_complete is None: is_section_complete = self._progress_store.is_section_complete( @@ -258,6 +268,140 @@ def _get_return_to_location_url( "questionnaire.submit_questionnaire", _anchor=return_to_answer_id ) + def _get_return_to_for_grand_calculated_summary( + self, + *, + return_to: str | None, + return_to_block_id: str | None, + location: Location, + routing_path: RoutingPath, + is_for_previous: bool, + return_to_answer_id: str | None = None, + ) -> str | None: + """ + Builds the return url for a grand calculated summary, + and accounts for it possibly being in a different section to the calculated summaries it references + """ + if not (return_to_block_id and self._schema.is_block_valid(return_to_block_id)): + return None + + # Type ignore: if the block is valid, then we'll be able to find a section for it + grand_calculated_summary_section: str = self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore + if grand_calculated_summary_section != location.section_id: + # the grand calculated summary is in a different section which will have a different routing path + # but don't go to it unless the current section is complete + if not self._progress_store.is_section_complete(location.section_id): + return self._get_return_url_for_inaccessible_location( + is_for_previous=is_for_previous, + return_to_block_id=return_to_block_id, + return_to=return_to, + routing_path=routing_path, + ) + + routing_path = self._path_finder.routing_path( + section_id=grand_calculated_summary_section + ) + if self.can_access_location( + # grand calculated summaries do not yet support repeating sections, when they do, this will need to make use of list item id as well + Location( + block_id=return_to_block_id, + section_id=grand_calculated_summary_section, + ), + routing_path, + ): + return url_for( + "questionnaire.block", + block_id=return_to_block_id, + return_to=return_to, + _anchor=return_to_answer_id, + ) + return self._get_return_url_for_inaccessible_location( + is_for_previous=is_for_previous, + return_to_block_id=return_to_block_id, + return_to=return_to, + routing_path=routing_path, + ) + + def _get_return_to_for_calculated_summary( + self, + *, + return_to: str, + return_to_block_id: str | None, + location: Location, + routing_path: RoutingPath, + is_for_previous: bool, + return_to_answer_id: str | None = None, + ) -> str | None: + """ + The return url for a calculated summary varies based on whether it's standalone or part of a grand calculated summary + + If the user goes from GrandCalculatedSummary -> CalculatedSummary -> Question, then return_to_block_ids needs to be a list + so that both the calculated summary id and the grand calculated summary ids are stored. + """ + block_id = None + remaining: list[str] = [] + # for a calculated summary this might have multiple items, e.g. a calculated summary to go to and then a grand calculated one + if return_to_block_id: + # the first item is the block id to route to (e.g. a calculated summary to go back to first) + # anything remaining forms where to go next (e.g. a grand calculated summary) + block_id, *remaining = return_to_block_id.split(",") + + if self.can_access_location( + Location( + block_id=block_id, + section_id=location.section_id, + list_item_id=location.list_item_id, + ), + routing_path, + ): + # if the next location is valid, the new url is that location, and the new 'return to block id' is just what remains + return_to_block_id = ",".join(remaining) if remaining else None + # if return_to is a list, return all but the first item, but if it's a single item then leave as is + return_to = return_to[return_to.find(",") + 1 :] + + return url_for( + "questionnaire.block", + block_id=block_id, + list_name=location.list_name, + list_item_id=location.list_item_id, + return_to=return_to, + return_to_block_id=return_to_block_id, + _anchor=return_to_answer_id, + ) + + return self._get_return_url_for_inaccessible_location( + is_for_previous=is_for_previous, + return_to_block_id=return_to_block_id, + return_to=return_to, + routing_path=routing_path, + ) + + def _get_return_url_for_inaccessible_location( + self, + *, + is_for_previous: bool, + return_to_block_id: str | None, + return_to: str | None, + routing_path: RoutingPath, + ) -> str | None: + """ + Routes to the next incomplete block in the section and preserves return to parameters + but only when routing forwards, returns None in the case of the previous link + """ + if ( + not is_for_previous + and return_to_block_id + and ( + next_incomplete_location := self._get_first_incomplete_location_in_section( + routing_path + ) + ) + ): + return next_incomplete_location.url( + return_to=return_to, + return_to_block_id=return_to_block_id, + ) + def get_next_location_url_for_end_of_section(self) -> str: if self._schema.is_flow_hub and self.can_access_hub(): return url_for("questionnaire.get_questionnaire") diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index ed2be5bfd8..adfc8f2384 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -3,6 +3,7 @@ from typing import Callable, Iterable, Mapping, MutableMapping from markupsafe import Markup +from werkzeug.datastructures import ImmutableDict from app.data_models import ProgressStore from app.data_models.answer import AnswerValueTypes, escape_answer_value @@ -163,12 +164,19 @@ def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: def _resolve_calculated_summary_value_source( self, value_source: Mapping, *, assess_routing_path: bool - ) -> IntOrDecimal: + ) -> IntOrDecimal | None: """Calculates the value for the 'calculation' used by the provided Calculated Summary. - The caller is responsible for ensuring the provided Calculated Summary and its answers are on the path. + The caller is responsible for ensuring the provided Calculated Summary and its answers are on the path, + or providing routing_path_block_ids when initialising the value source resolver. """ - calculated_summary_block: Mapping = self.schema.get_block(value_source["identifier"]) # type: ignore + calculated_summary_block: ImmutableDict = self.schema.get_block(value_source["identifier"]) # type: ignore + + if self.routing_path_block_ids and not self._is_block_on_path( + calculated_summary_block["id"] + ): + return None + calculation = calculated_summary_block["calculation"] if calculation.get("answers_to_calculate"): operator = self.get_calculation_operator(calculation["calculation_type"]) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index e53387de23..85ac35f5e2 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-04-27 09:02+0100\n" +"POT-Creation-Date: 2023-05-18 10:40+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,9 +41,9 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:701 -#: templates/partials/summary/collapsible-summary.html:26 -#: templates/partials/summary/summary.html:22 +#: app/jinja_filters.py:716 +#: templates/partials/summary/collapsible-summary.html:27 +#: templates/partials/summary/summary.html:25 msgid "No answer provided" msgstr "" @@ -456,7 +456,7 @@ msgstr "" #: templates/individual_response/confirmation-post.html:21 #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/sectionsummary.html:10 templates/sectionsummary.html:50 +#: templates/sectionsummary.html:10 templates/sectionsummary.html:48 msgid "Continue" msgstr "" @@ -546,7 +546,7 @@ msgstr "" #: app/views/handlers/confirm_email.py:129 #: app/views/handlers/confirmation_email.py:67 -#: app/views/handlers/feedback.py:84 app/views/handlers/question.py:170 +#: app/views/handlers/feedback.py:84 app/views/handlers/question.py:185 msgid "Error: {page_title}" msgstr "" @@ -735,14 +735,6 @@ msgstr "" msgid "View Submitted Response" msgstr "" -#: templates/calculatedsummary.html:13 -msgid "Please review your answers and confirm these are correct" -msgstr "" - -#: templates/calculatedsummary.html:24 -msgid "Yes, I confirm these are correct" -msgstr "" - #: templates/census-thank-you.html:9 templates/confirmation-email.html:8 msgid "There is a problem with this page" msgstr "" @@ -871,7 +863,7 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:23 templates/partials/question.html:45 +#: templates/interstitial.html:23 templates/partials/question.html:49 msgid "If you can’t answer questions for this person" msgstr "" @@ -930,13 +922,13 @@ msgid "Save questions as PDF" msgstr "" #: templates/partials/introduction/preview.html:37 -#: templates/partials/summary/collapsible-summary.html:49 +#: templates/partials/summary/collapsible-summary.html:51 #: templates/preview.html:92 msgid "Show all" msgstr "" #: templates/partials/introduction/preview.html:38 -#: templates/partials/summary/collapsible-summary.html:50 +#: templates/partials/summary/collapsible-summary.html:52 #: templates/preview.html:93 msgid "Hide all" msgstr "" @@ -1323,6 +1315,14 @@ msgstr "" msgid "Continue survey" msgstr "" +#: templates/layouts/_calculatedsummary.html:14 +msgid "Please review your answers and confirm these are correct" +msgstr "" + +#: templates/layouts/_calculatedsummary.html:25 +msgid "Yes, I confirm these are correct" +msgstr "" + #: templates/layouts/_questionnaire.html:39 msgid "Choose another section and return to this later" msgstr "" @@ -1382,7 +1382,7 @@ msgid "" msgstr "" #: templates/partials/preview-question.html:34 -#: templates/partials/question.html:81 +#: templates/partials/question.html:85 msgid "Or" msgstr "" @@ -1390,19 +1390,19 @@ msgstr "" msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:72 +#: templates/partials/question.html:76 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:73 +#: templates/partials/question.html:77 msgid "cleared" msgstr "" -#: templates/partials/question.html:76 +#: templates/partials/question.html:80 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:77 templates/partials/question.html:85 +#: templates/partials/question.html:81 templates/partials/question.html:89 msgid "deselected" msgstr "" @@ -1568,14 +1568,14 @@ msgstr "" msgid "Start survey" msgstr "" -#: templates/partials/summary/collapsible-summary.html:27 +#: templates/partials/summary/collapsible-summary.html:28 #: templates/partials/summary/list-summary.html:7 -#: templates/partials/summary/summary.html:25 +#: templates/partials/summary/summary.html:28 msgid "Change" msgstr "" -#: templates/partials/summary/collapsible-summary.html:28 -#: templates/partials/summary/summary.html:26 +#: templates/partials/summary/collapsible-summary.html:29 +#: templates/partials/summary/summary.html:29 msgid "Change your answer for:" msgstr "" @@ -1584,7 +1584,7 @@ msgid "Change details for {item_name}" msgstr "" #: templates/partials/summary/list-summary.html:9 -#: templates/partials/summary/summary.html:23 +#: templates/partials/summary/summary.html:26 msgid "Remove" msgstr "" @@ -1592,7 +1592,7 @@ msgstr "" msgid "Remove {item_name}" msgstr "" -#: templates/partials/summary/summary.html:24 +#: templates/partials/summary/summary.html:27 msgid "Remove your answer for:" msgstr "" diff --git a/app/views/contexts/__init__.py b/app/views/contexts/__init__.py index 6477a330bc..1acb951d53 100644 --- a/app/views/contexts/__init__.py +++ b/app/views/contexts/__init__.py @@ -1,5 +1,6 @@ from .calculated_summary_context import CalculatedSummaryContext from .context import Context +from .grand_calculated_summary_context import GrandCalculatedSummaryContext from .hub_context import HubContext from .list_context import ListContext from .section_summary_context import SectionSummaryContext @@ -7,6 +8,7 @@ __all__ = [ "CalculatedSummaryContext", + "GrandCalculatedSummaryContext", "Context", "SubmitQuestionnaireContext", "HubContext", diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index bbe379ed59..b82822a919 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,16 +1,4 @@ -from copy import deepcopy -from decimal import Decimal -from typing import ( - Any, - Callable, - Iterable, - Literal, - Mapping, - MutableMapping, - Optional, - Tuple, - Union, -) +from typing import Callable, Iterable, Literal, Mapping, MutableMapping, Tuple from werkzeug.datastructures import ImmutableDict @@ -33,6 +21,7 @@ from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_question_to_display, transform_variants from app.views.contexts.context import Context +from app.views.contexts.summary.calculated_summary_block import NumericType from app.views.contexts.summary.group import Group @@ -44,10 +33,12 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, routing_path: RoutingPath, current_location: Location, + return_to: str | None = None, + return_to_block_id: str | None = None, ) -> None: super().__init__( language, @@ -58,18 +49,30 @@ def __init__( metadata, response_metadata, ) - self.routing_path = routing_path + self.routing_path_block_ids = routing_path.block_ids self.current_location = current_location + self.return_to = return_to + self.return_to_block_id = return_to_block_id def build_groups_for_section( self, - section: Mapping[str, Any], + *, + section: Mapping, return_to_block_id: str, - ) -> list[Mapping[str, Group]]: + routing_path_block_ids: Iterable[str], + ) -> list[Mapping]: + """ + If the calculated summary is being edited from a grand calculated summary + the details of the grand calculated summary to return to needs to be passed down to the calculated summary answer links + """ + return_to = "calculated-summary" + if self.return_to == "grand-calculated-summary": + return_to_block_id += f",{self.return_to_block_id}" + return_to += ",grand-calculated-summary" return [ Group( group_schema=group, - routing_path=self.routing_path, + routing_path_block_ids=routing_path_block_ids, answer_store=self._answer_store, list_store=self._list_store, metadata=self._metadata, @@ -78,25 +81,25 @@ def build_groups_for_section( location=self.current_location, language=self._language, progress_store=self._progress_store, - return_to="calculated-summary", + return_to=return_to, return_to_block_id=return_to_block_id, + summary_type="CalculatedSummary", ).serialize() for group in section["groups"] ] - def build_view_context_for_calculated_summary(self) -> dict[str, dict[str, Any]]: + def build_view_context(self) -> dict[str, dict]: # type ignores added as block will exist at this point block_id: str = self.current_location.block_id # type: ignore block: ImmutableDict = self._schema.get_block(block_id) # type: ignore - calculated_section: dict[str, Any] = self._build_calculated_summary_section( - block - ) + calculated_section: dict = self._build_calculated_summary_section(block) calculation = block["calculation"] groups = self.build_groups_for_section( - calculated_section, - block_id, + section=calculated_section, + return_to_block_id=block_id, + routing_path_block_ids=self.routing_path_block_ids, ) formatted_total = self._get_formatted_total( @@ -108,6 +111,23 @@ def build_view_context_for_calculated_summary(self) -> dict[str, dict[str, Any]] else calculation["operation"], ) + return self._build_formatted_summary( + block=block, + groups=groups, + calculation=calculation, + formatted_total=formatted_total, + summary_type="CalculatedSummary", + ) + + def _build_formatted_summary( + self, + *, + block: ImmutableDict, + groups: Iterable[Mapping], + calculation: ImmutableDict, + formatted_total: str, + summary_type: str, + ) -> dict[str, dict]: collapsible = block.get("collapsible") or False block_title = block["title"] @@ -122,13 +142,11 @@ def build_view_context_for_calculated_summary(self) -> dict[str, dict[str, Any]] ), "title": block_title % {"total": formatted_total}, "collapsible": collapsible, - "summary_type": "CalculatedSummary", + "summary_type": summary_type, } } - def _build_calculated_summary_section( - self, rendered_block: ImmutableDict[str, Any] - ) -> dict[str, Any]: + def _build_calculated_summary_section(self, rendered_block: ImmutableDict) -> dict: """Build up the list of blocks only including blocks / questions / answers which are relevant to the summary""" # type ignores added as block will exist at this point block_id: str = self.current_location.block_id # type: ignore @@ -170,7 +188,7 @@ def _remove_unwanted_questions_answers( """ Evaluates questions in a block and removes any questions not containing a relevant answer """ - transformed_block: ImmutableDict = transform_variants( + block_to_transform: ImmutableDict = transform_variants( block, self._schema, self._metadata, @@ -180,8 +198,9 @@ def _remove_unwanted_questions_answers( self.current_location, self._progress_store, ) - transformed_block = deepcopy(transformed_block) - transformed_block = QuestionnaireSchema.get_mutable_deepcopy(transformed_block) + transformed_block: dict = QuestionnaireSchema.get_mutable_deepcopy( + block_to_transform + ) block_question = transformed_block["question"] matching_answers = [] @@ -202,30 +221,45 @@ def _remove_unwanted_questions_answers( return transformed_block + def _get_evaluated_total( + self, + *, + calculation: ImmutableDict, + routing_path_block_ids: Iterable[str], + ) -> NumericType: + """ + For a calculation in the new style and the list of involved block ids (possibly across sections) evaluate the total + """ + evaluate_calculated_summary = RuleEvaluator( + self._schema, + self._answer_store, + self._list_store, + self._metadata, + self._response_metadata, + routing_path_block_ids=routing_path_block_ids, + location=self.current_location, + progress_store=self._progress_store, + ) + # Type ignore: in the case of a calculated summation it will always be a numeric type + calculated_total: NumericType = evaluate_calculated_summary.evaluate(calculation) # type: ignore + return calculated_total + def _get_formatted_total( self, groups: list, calculation: Callable | ImmutableDict ) -> str: answer_format, values_to_calculate = self._get_answer_format(groups) if isinstance(calculation, ImmutableDict): - evaluate_calculated_summary = RuleEvaluator( - self._schema, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, - routing_path_block_ids=self.routing_path.block_ids, - location=self.current_location, - progress_store=self._progress_store, + calculated_total = self._get_evaluated_total( + calculation=calculation, + routing_path_block_ids=self.routing_path_block_ids, ) - - calculated_total: Union[int, float, Decimal] = evaluate_calculated_summary.evaluate(calculation) # type: ignore else: calculated_total = calculation(values_to_calculate) - return self._format_total(answer_format, calculated_total) + return self._format_total(answer_format=answer_format, total=calculated_total) - def _get_answer_format(self, groups: list) -> Tuple[dict[str, Any], list]: + def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: values_to_calculate: list = [] answer_format: dict = {"type": None} for group in groups: @@ -255,8 +289,9 @@ def _get_answer_format(self, groups: list) -> Tuple[dict[str, Any], list]: @staticmethod def _format_total( + *, answer_format: Mapping[str, Literal["short", "long", "narrow"]], - total: int | float | Decimal, + total: NumericType, ) -> str: if answer_format["type"] == "currency": return get_formatted_currency(total, answer_format["currency"]) @@ -275,9 +310,9 @@ def _format_total( @staticmethod def _get_calculated_question( - calculation_question: ImmutableDict[str, Any], + calculation_question: ImmutableDict, formatted_total: str, - ) -> dict[str, Any]: + ) -> dict: calculation_title = calculation_question["title"] return { diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py new file mode 100644 index 0000000000..467ba9f040 --- /dev/null +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -0,0 +1,124 @@ +from typing import Iterable, Mapping + +from werkzeug.datastructures import ImmutableDict + +from app.questionnaire.questionnaire_schema import ( + get_calculation_block_ids_for_grand_calculated_summary, +) +from app.views.contexts.calculated_summary_context import CalculatedSummaryContext +from app.views.contexts.summary.group import Group + + +class GrandCalculatedSummaryContext(CalculatedSummaryContext): + def _build_grand_calculated_summary_section( + self, rendered_block: ImmutableDict + ) -> dict[str, str | list]: + """ + Build list of calculated summary blocks that the grand calculated summary will be adding up + """ + # Type ignore: the block, group and section will all exist at this point + calculated_summary_group: ImmutableDict = self._schema.get_group_for_block_id(self.current_location.block_id) # type: ignore + + calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( + rendered_block + ) + blocks_to_calculate = [ + self._schema.get_block(block_id) for block_id in calculated_summary_ids + ] + + return { + "id": self.current_location.section_id, + "groups": [ + {"id": calculated_summary_group["id"], "blocks": blocks_to_calculate} + ], + } + + def _blocks_on_routing_path( + self, calculated_summary_ids: Iterable[str] + ) -> list[str]: + """ + Find all blocks on the routing path for each of the calculated summaries + """ + # Type ignore: each block must have a section id + section_ids: set[str] = {self._schema.get_section_id_for_block_id(block_id) for block_id in calculated_summary_ids} # type: ignore + # find any sections involved in the grand calculated summary (but only if they have started, to avoid evaluating the path if not necessary) + started_sections = [ + key for key, _ in self._progress_store.started_section_keys(section_ids) + ] + routing_path_block_ids: list[str] = [] + + for section_id in started_sections: + if section_id == self.current_location.section_id: + routing_path_block_ids.extend(self.routing_path_block_ids) + else: + routing_path_block_ids.extend( + # repeating calculated summaries are not supported at the moment, so no list item is needed + self._router.routing_path(section_id).block_ids + ) + + return routing_path_block_ids + + def build_groups_for_section( + self, + *, + section: Mapping, + return_to_block_id: str, + routing_path_block_ids: Iterable[str], + ) -> list[Mapping]: + return [ + Group( + group_schema=group, + routing_path_block_ids=routing_path_block_ids, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=self.current_location, + language=self._language, + progress_store=self._progress_store, + return_to="grand-calculated-summary", + return_to_block_id=return_to_block_id, + summary_type="GrandCalculatedSummary", + ).serialize() + for group in section["groups"] + ] + + def build_view_context(self) -> dict[str, dict]: + """ + Build summary section with formatted total and change links for each calculated summary + """ + # Type ignore: Block will exist at this point + block: ImmutableDict = self._schema.get_block(self.current_location.block_id) # type: ignore + + calculation = block["calculation"] + calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( + block + ) + routing_path_block_ids = self._blocks_on_routing_path(calculated_summary_ids) + + calculated_section = self._build_grand_calculated_summary_section(block) + + groups = self.build_groups_for_section( + section=calculated_section, + return_to_block_id=block["id"], + routing_path_block_ids=routing_path_block_ids, + ) + total = self._get_evaluated_total( + calculation=calculation["operation"], + routing_path_block_ids=routing_path_block_ids, + ) + + # validator ensures all calculated summaries are of the same type, so the first can be used for the format + answer_format = self._schema.get_answer_format_for_calculated_summary( + calculated_summary_ids[0] + ) + formatted_total = self._format_total(answer_format=answer_format, total=total) + + return self._build_formatted_summary( + block=block, + groups=groups, + calculation=calculation, + formatted_total=formatted_total, + summary_type="GrandCalculatedSummary", + ) diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 30bebc684d..06c4fc392e 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -138,7 +138,7 @@ def build_summary( "groups": [ Group( group_schema=group, - routing_path=self.routing_path, + routing_path_block_ids=self.routing_path.block_ids, answer_store=self._answer_store, list_store=self._list_store, metadata=self._metadata, @@ -172,7 +172,7 @@ def _custom_summary_elements( for summary_element in section_summary: if summary_element["type"] == "List": list_collector_block = ListCollectorBlock( - routing_path=self.routing_path, + routing_path_block_ids=self.routing_path.block_ids, answer_store=self._answer_store, list_store=self._list_store, progress_store=self._progress_store, diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py new file mode 100644 index 0000000000..8ba998fe74 --- /dev/null +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -0,0 +1,83 @@ +from decimal import Decimal +from typing import Iterable, Mapping, MutableMapping, TypeAlias + +from flask import url_for + +from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models.metadata_proxy import MetadataProxy +from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire.rules.rule_evaluator import RuleEvaluator + +NumericType: TypeAlias = int | float | Decimal + + +class CalculatedSummaryBlock: + def __init__( + self, + block_schema: Mapping, + *, + answer_store: AnswerStore, + list_store: ListStore, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + schema: QuestionnaireSchema, + location: Location, + return_to: str | None, + return_to_block_id: str | None = None, + progress_store: ProgressStore, + routing_path_block_ids: Iterable[str], + ) -> None: + """ + A Calculated summary block that is rendered as part of a grand calculated summary + """ + + self.id = block_schema["id"] + self.title = block_schema["calculation"]["title"] + self._return_to = return_to + self._return_to_block_id = return_to_block_id + self._block_schema = block_schema + self._schema = schema + + self._rule_evaluator = RuleEvaluator( + schema=schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, + location=location, + progress_store=progress_store, + routing_path_block_ids=routing_path_block_ids, + ) + + # Type ignore: for a calculated summary the resolved answer would only ever be one of these 3 + calculated_total: NumericType = self._rule_evaluator.evaluate(block_schema["calculation"]["operation"]) # type: ignore + answer_format = self._schema.get_answer_format_for_calculated_summary(self.id) + self.answers = [ + { + "id": self.id, + "label": self.title, + "value": calculated_total, + "link": self._build_link(), + **answer_format, + } + ] + + def _build_link(self) -> str: + return url_for( + "questionnaire.block", + block_id=self.id, + return_to=self._return_to, + return_to_answer_id=self.id, + return_to_block_id=self._return_to_block_id, + _anchor=self.id, + ) + + def _calculated_summary(self) -> dict: + return {"id": self.id, "title": self.title, "answers": self.answers} + + def serialize(self) -> dict: + return { + "id": self.id, + "title": self.title, + "calculated_summary": self._calculated_summary(), + } diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 9b4a15fae5..01be23b42c 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,4 +1,4 @@ -from typing import Any, Mapping, MutableMapping, Optional +from typing import Iterable, Mapping, MutableMapping from werkzeug.datastructures import ImmutableDict @@ -6,9 +6,9 @@ from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer -from app.questionnaire.routing_path import RoutingPath from app.survey_config.link import Link from app.views.contexts.summary.block import Block +from app.views.contexts.summary.calculated_summary_block import CalculatedSummaryBlock from app.views.contexts.summary.list_collector_block import ListCollectorBlock @@ -16,8 +16,8 @@ class Group: def __init__( self, *, - group_schema: Mapping[str, Any], - routing_path: RoutingPath, + group_schema: Mapping, + routing_path_block_ids: Iterable[str], answer_store: AnswerStore, list_store: ListStore, metadata: MetadataProxy | None, @@ -28,6 +28,7 @@ def __init__( progress_store: ProgressStore, return_to: str | None, return_to_block_id: str | None = None, + summary_type: str | None = None, view_submitted_response: bool | None = False, ) -> None: self.id = group_schema["id"] @@ -39,7 +40,7 @@ def __init__( self.blocks = self._build_blocks_and_links( group_schema=group_schema, - routing_path=routing_path, + routing_path_block_ids=routing_path_block_ids, answer_store=answer_store, list_store=list_store, metadata=metadata, @@ -51,6 +52,7 @@ def __init__( language=language, return_to_block_id=return_to_block_id, view_submitted_response=view_submitted_response, + summary_type=summary_type, ) self.placeholder_renderer = PlaceholderRenderer( @@ -68,24 +70,25 @@ def __init__( def _build_blocks_and_links( self, *, - group_schema: Mapping[str, Any], - routing_path: RoutingPath, + group_schema: Mapping, + routing_path_block_ids: Iterable[str], answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, location: Location, - return_to: Optional[str], + return_to: str | None, progress_store: ProgressStore, language: str, - return_to_block_id: Optional[str], + return_to_block_id: str | None, view_submitted_response: bool | None = False, + summary_type: str | None = None, ) -> list[dict[str, Block]]: blocks = [] for block in group_schema["blocks"]: - if block["id"] not in routing_path: + if block["id"] not in routing_path_block_ids: continue if block["type"] in [ "Question", @@ -108,20 +111,38 @@ def _build_blocks_and_links( ).serialize() ] ) + # check the summary_type as opposed to the block type + # otherwise this gets called on section summaries as well + elif summary_type == "GrandCalculatedSummary": + blocks.extend( + [ + CalculatedSummaryBlock( + block, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, + schema=schema, + location=location, + return_to=return_to, + return_to_block_id=return_to_block_id, + progress_store=progress_store, + routing_path_block_ids=routing_path_block_ids, + ).serialize() + ] + ) elif block["type"] == "ListCollector": - section: Optional[ImmutableDict] = schema.get_section( - location.section_id - ) + section: ImmutableDict | None = schema.get_section(location.section_id) - summary_item: Optional[ImmutableDict] + summary_item: ImmutableDict | None if summary_item := schema.get_summary_item_for_list_for_section( # Type ignore: section id will not be optional at this point section_id=section["id"], # type: ignore list_name=block["for_list"], ): list_collector_block = ListCollectorBlock( - routing_path=routing_path, + routing_path_block_ids=routing_path_block_ids, answer_store=answer_store, list_store=list_store, progress_store=progress_store, @@ -149,7 +170,7 @@ def _build_blocks_and_links( return blocks - def serialize(self) -> Mapping[str, Any]: + def serialize(self) -> Mapping: return self.placeholder_renderer.render( data_to_render={ "id": self.id, diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 93a4737f68..2a6a980757 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Any, Mapping, MutableMapping, Optional +from typing import Any, Iterable, Mapping, MutableMapping, Optional from flask import url_for from werkzeug.datastructures import ImmutableDict @@ -9,7 +9,6 @@ from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer -from app.questionnaire.routing_path import RoutingPath from app.views.contexts.list_context import ListContext from app.views.contexts.summary.block import Block @@ -17,7 +16,7 @@ class ListCollectorBlock: def __init__( self, - routing_path: RoutingPath, + routing_path_block_ids: Iterable[str], answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, @@ -47,7 +46,7 @@ def __init__( self._answer_store = answer_store self._metadata = metadata self._response_metadata = response_metadata - self._routing_path = routing_path + self._routing_path_block_ids = routing_path_block_ids self._progress_store = progress_store # pylint: disable=too-many-locals @@ -74,7 +73,7 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: list_collector_blocks_on_path = [ list_collector_block for list_collector_block in list_collector_blocks - if list_collector_block["id"] in self._routing_path.block_ids + if list_collector_block["id"] in self._routing_path_block_ids ] list_collector_block = ( diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 6019bc762e..1495831744 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -53,6 +53,14 @@ def __init__( def current_location(self): return self._current_location + @property + def return_to(self): + return self._return_to + + @property + def return_to_block_id(self): + return self._return_to_block_id + @cached_property def questionnaire_store_updater(self): return QuestionnaireStoreUpdater( diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 91c0a3d6c3..7961432d15 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -4,7 +4,10 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException, Location from app.questionnaire.relationship_location import RelationshipLocation -from app.views.handlers.calculated_summary import CalculatedSummary +from app.views.handlers.calculation_summary import ( + CalculatedSummary, + GrandCalculatedSummary, +) from app.views.handlers.content import Content from app.views.handlers.list_add_question import ListAddQuestion from app.views.handlers.list_collector import ListCollector @@ -30,6 +33,7 @@ "Introduction": Content, "Interstitial": Content, "CalculatedSummary": CalculatedSummary, + "GrandCalculatedSummary": GrandCalculatedSummary, } diff --git a/app/views/handlers/calculated_summary.py b/app/views/handlers/calculation_summary.py similarity index 65% rename from app/views/handlers/calculated_summary.py rename to app/views/handlers/calculation_summary.py index 325ada18e1..852ce7a3b2 100644 --- a/app/views/handlers/calculated_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -1,10 +1,15 @@ +from typing import Type + +from app.views.contexts import GrandCalculatedSummaryContext from app.views.contexts.calculated_summary_context import CalculatedSummaryContext from app.views.handlers.content import Content -class CalculatedSummary(Content): +class _SummaryWithCalculation(Content): + summary_class: Type[CalculatedSummaryContext] | Type[GrandCalculatedSummaryContext] + def get_context(self): - calculated_summary_context = CalculatedSummaryContext( + summary_context = self.summary_class( language=self._language, schema=self._schema, answer_store=self._questionnaire_store.answer_store, @@ -14,8 +19,10 @@ def get_context(self): response_metadata=self._questionnaire_store.response_metadata, current_location=self._current_location, routing_path=self._routing_path, + return_to=self.return_to, + return_to_block_id=self.return_to_block_id, ) - context = calculated_summary_context.build_view_context_for_calculated_summary() + context = summary_context.build_view_context() if not self.page_title: self.page_title = context["summary"]["calculated_question"]["title"] @@ -28,3 +35,11 @@ def handle_post(self): # Then we update dependent sections self.questionnaire_store_updater.capture_progress_section_dependencies() return super().handle_post() + + +class CalculatedSummary(_SummaryWithCalculation): + summary_class = CalculatedSummaryContext + + +class GrandCalculatedSummary(_SummaryWithCalculation): + summary_class = GrandCalculatedSummaryContext diff --git a/schemas/test/en/test_grand_calculated_summary.json b/schemas/test/en/test_grand_calculated_summary.json new file mode 100644 index 0000000000..e2271d03b9 --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary.json @@ -0,0 +1,294 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Simple Grand Calculated Summary demo", + "theme": "default", + "description": "A schema to showcase Grand Calculated Summary.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section-1", + "title": "Commuting", + "groups": [ + { + "id": "group", + "title": "Commuting", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "How much do you walk per week?", + "type": "General", + "answers": [ + { + "id": "q1-a1", + "label": "Weekly distance travelled on foot", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q1-a2", + "label": "Number of walks per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "How much do you drive per week?", + "type": "General", + "answers": [ + { + "id": "q2-a1", + "label": "Weekly distance travelled by car", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q2-a2", + "label": "Number of car journeys per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "distance-calculated-summary-1", + "title": "We calculate the total of distance travelled by foot and car to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q2-a1" + } + ] + }, + "title": "Calculated distance on foot and driving" + } + }, + { + "type": "CalculatedSummary", + "id": "number-calculated-summary-1", + "title": "We calculate the total number of journeys on foot and in a car to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + }, + "title": "Calculated journeys on foot and driving" + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Alternative Transport", + "groups": [ + { + "id": "transport-group", + "title": "Alternative Transport", + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "How much do you cycle per week?", + "type": "General", + "answers": [ + { + "id": "q3-a1", + "label": "Weekly distance travelled by bike", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q3-a2", + "label": "Number of bicycle journeys per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "How much do you voi per week?", + "type": "General", + "answers": [ + { + "id": "q4-a1", + "label": "Weekly distance travelled on a Voi", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q4-a2", + "label": "Number of scooter trips per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "distance-calculated-summary-2", + "title": "We calculate the total of distance travelled by bike and voi to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q4-a1" + } + ] + }, + "title": "Calculated weekly distance on bike and scooter" + } + }, + { + "type": "CalculatedSummary", + "id": "number-calculated-summary-2", + "title": "We calculate the total number of journeys on bike and on a voi to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a2" + }, + { + "source": "answers", + "identifier": "q4-a2" + } + ] + }, + "title": "Calculated journeys on bike and scooter" + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Grand calculated summaries", + "groups": [ + { + "id": "summary-group", + "title": "Grand calculated summary group", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "distance-grand-calculated-summary", + "title": "We calculate the grand total weekly distance travelled to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "distance-calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "distance-calculated-summary-2" + } + ] + }, + "title": "Grand calculated summary of distance travelled" + } + }, + { + "type": "GrandCalculatedSummary", + "id": "number-grand-calculated-summary", + "title": "We calculate the grand total journeys per week to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "number-calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "number-calculated-summary-2" + } + ] + }, + "title": "Grand calculated summary of journeys" + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json b/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json new file mode 100644 index 0000000000..b0754d04b6 --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json @@ -0,0 +1,341 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Grand Calculated Summary Cross Section Dependencies", + "theme": "default", + "description": "A questionnaire to demo resolution of grand calculated summary values across sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "questions-section", + "title": "Household bills", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "radio", + "title": "Questions", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-question-1", + "title": "Are you a student?", + "guidance": { + "contents": [ + { + "description": "If you answer yes, then the question about council tax will be skipped and not included in total monthly expenditure." + } + ] + }, + "answers": [ + { + "type": "Radio", + "id": "skip-answer-1", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-answer-1", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block-part-a", + "question": { + "id": "question-1-a", + "title": "How much do you pay monthly for council tax?", + "type": "General", + "answers": [ + { + "id": "first-number-answer-a", + "label": "Council tax (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "question-2", + "title": "How much are your monthly gas, water and electricity bills?", + "type": "General", + "answers": [ + { + "id": "second-number-answer-a", + "label": "Electricity Bill", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-number-answer-b", + "label": "Gas Bill", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-number-answer-c", + "label": "Water Bill", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-section-1", + "title": "We calculate your total monthly expenditure on household bills to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "second-number-answer-a" + }, + { + "source": "answers", + "identifier": "second-number-answer-b" + }, + { + "source": "answers", + "identifier": "second-number-answer-c" + }, + { + "source": "answers", + "identifier": "first-number-answer-a" + } + ] + }, + "title": "Monthly expenditure on household bills" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Other outgoing costs", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "calculated-summary", + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "How much do you spend on internet and television?", + "type": "General", + "guidance": { + "contents": [ + { + "description": "If you enter a value for the TV licence, it will unlock an additional question about premium channels." + } + ] + }, + "answers": [ + { + "id": "third-number-answer-part-a", + "label": "Internet bill", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-part-b", + "label": "TV licence (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "third-number-answer-part-b", + "source": "answers" + }, + null + ] + } + }, + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "How much do you spend per month on premium television channels?", + "type": "General", + "answers": [ + { + "id": "fourth-number-answer", + "label": "TV channel subscription fees", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "skip-calculated-summary", + "question": { + "type": "General", + "id": "skip-question-2", + "title": "Skip the calculated summary of other outgoing costs so it isn’t included in the grand calculated summary?", + "answers": [ + { + "type": "Radio", + "id": "skip-answer-2", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-answer-2", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "CalculatedSummary", + "id": "currency-question-3", + "title": "We calculate the total monthly spending on internet and TV to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "third-number-answer-part-a" + }, + { + "source": "answers", + "identifier": "third-number-answer-part-b" + }, + { + "source": "answers", + "identifier": "fourth-number-answer" + } + ] + }, + "title": "Total monthly spending on internet and TV" + } + } + ] + } + ] + }, + { + "id": "grand-calculated-summary-section", + "title": "Grand Calculated Summary", + "groups": [ + { + "id": "grand-calculated-summary", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "currency-all", + "title": "The grand calculated summary is calculated to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "currency-section-1" + }, + { + "source": "calculated_summary", + "identifier": "currency-question-3" + } + ] + }, + "title": "Grand total monthly expenditure" + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_grand_calculated_summary_multiple_sections.json b/schemas/test/en/test_grand_calculated_summary_multiple_sections.json new file mode 100644 index 0000000000..8b7700df1d --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary_multiple_sections.json @@ -0,0 +1,348 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Grand Calculated Summary cross section demo", + "theme": "default", + "description": "A schema to showcase grand calculated summaries across multiple sections and groups", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Section 1", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "group-1", + "title": "Group title for questions about food", + "blocks": [ + { + "type": "Question", + "id": "block-1", + "question": { + "type": "General", + "id": "question-1", + "title": "How much do you spend per week on fruit and veg? (Question 1, Group 1, Section 1)", + "answers": [ + { + "id": "q1-a1", + "label": "Money spent on fruit", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q1-a2", + "label": "Money spent on veg", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "Question", + "id": "block-2", + "question": { + "type": "General", + "id": "question-2", + "title": "How much do you spend per week on other food? (Question 2, Group 1, Section 1)", + "answers": [ + { + "id": "q2-a1", + "label": "Money spent on bread", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q2-a2", + "label": "Money spent on not bread", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-1", + "title": "Calculated Summary for Question 1 is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total food expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + } + } + } + ] + }, + { + "id": "group-2", + "title": "Group title for clothing questions", + "blocks": [ + { + "type": "Question", + "id": "block-3", + "question": { + "type": "General", + "id": "question-3", + "title": "How much do you spend per week on clothes? (Question 3, Group 2, Section 1)", + "answers": [ + { + "id": "q3-a1", + "label": "Money spent on jumpers", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q3-a2", + "label": "Money spent on hats", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-2", + "title": "Calculated summary for question 3 is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total clothes expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q3-a2" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-3", + "title": "Calculated summary for section 1 is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total food and clothes expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + }, + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q3-a2" + } + ] + } + } + }, + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-1", + "title": "Grand Calculated Summary which should match the previous calculated summary is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Grand calculated summary", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Section 2", + "groups": [ + { + "id": "group-3", + "title": "Group title for questions about games", + "blocks": [ + { + "type": "Question", + "id": "block-4", + "question": { + "type": "General", + "id": "question-4", + "title": "How much do you spend per week on games? (Question 4, section 2, group 3)", + "guidance": { + "contents": [ + { + "description": "Note:" + }, + { + "list": [ + "The grand calculated summary section after this will only show if the total spending on games is not zero", + "You should test that if you use the change links on the grand calculated summary to come back here and set both answers to 0, that you are not routed to the grand calculated summary when you press continue on the calculated summary, but instead, taken to the Hub.", + "If you use the change links on the grand calculated summary to edit these answer values to a non-zero sum, pressing continue twice should take you back to the grand calculated summary" + ] + } + ] + }, + "answers": [ + { + "id": "q4-a1", + "label": "Video games", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q4-a2", + "label": "Board games", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-4", + "title": "Calculated Summary for Question 4 is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total games expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q4-a1" + }, + { + "source": "answers", + "identifier": "q4-a2" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Grand calculated summary section which only shows if the previous calculated summary is non zero", + "enabled": { + "when": { + "!=": [ + 0, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + } + ] + } + }, + "groups": [ + { + "id": "group-4", + "title": "Group title for the grand calculated summary of both sections", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-2", + "title": "Grand Calculated Summary for section 1 and 2 is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total food clothes and games expenditure", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json new file mode 100644 index 0000000000..78bd81e4b0 --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json @@ -0,0 +1,329 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Grand Calculated Summary with overlapping answers", + "theme": "default", + "description": "A schema to showcase grand calculated summaries which include multiple calculated summaries using the same answers.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["introduction-section"] + } + }, + "sections": [ + { + "id": "introduction-section", + "title": "Introduction", + "show_on_hub": false, + "groups": [ + { + "id": "introduction-group", + "title": "Introduction", + "blocks": [ + { + "type": "Introduction", + "id": "introduction-block", + "primary_content": [ + { + "id": "about", + "contents": [ + { + "title": "About", + "list": [ + "This survey tests that when you re-use answers between calculated summaries, the grand calculated summary still resolves to the correct value" + ] + }, + { + "title": "How to test this schema", + "list": [ + "Ensure that the grand calculated summary section does not show unless all dependent calculated summaries in section-1 have been confirmed.", + "Your answer to the third question, may unlock an additional calculated summary which re-use your answers to the first two questions", + "If you do not select to buy extra food, verify no additional calculated summary occurs, and that the grand calculated summary is correct", + "If you choose to buy any food items twice, verify that they are included twice in the grand calculated summary, one for each calculated summary", + "Verify that if you have the extra calculated summary, and change the cost of bread for example using either of the calculated summary change links which include it that you are routed to each calculated summary first, and only then the grand calculated summary" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "section-1", + "title": "Weekly shop", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "group-1", + "title": "Weekly shopping", + "blocks": [ + { + "type": "Question", + "id": "block-1", + "question": { + "type": "General", + "id": "question-1", + "title": "How much do you spend on the following in a typical weekly shop?", + "answers": [ + { + "id": "q1-a1", + "label": "Money on milk", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q1-a2", + "label": "Money on eggs", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "Question", + "id": "block-2", + "question": { + "type": "General", + "id": "question-2", + "title": "How much do you spend on these items in a typical week?", + "answers": [ + { + "id": "q2-a1", + "label": "Money on bread", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q2-a2", + "label": "Money on cheese", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-1", + "title": "Total of milk and bread is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "milk + bread", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q2-a1" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-2", + "title": "Total of eggs and cheese is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "eggs + cheese", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + } + } + }, + { + "type": "Question", + "id": "block-3", + "question": { + "type": "General", + "id": "question-3", + "title": "Do you want to buy extra of anything this week?", + "guidance": { + "contents": [ + { + "description": "If you select the first option, all your answers so far will be reused in a new calculated summary for extra shopping. If you select the second option, only your answers for bread and cheese will be reused." + } + ] + }, + "answers": [ + { + "type": "Radio", + "id": "radio-extra", + "mandatory": true, + "options": [ + { + "label": "Yes, I am going to buy two of everything", + "value": "Yes, I am going to buy two of everything" + }, + { + "label": "Yes, extra bread and cheese", + "value": "Yes, extra bread and cheese" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "!=": [ + { + "source": "answers", + "identifier": "radio-extra" + }, + "Yes, I am going to buy two of everything" + ] + } + }, + "type": "CalculatedSummary", + "id": "calculated-summary-3", + "title": "Total extra items purchased is calculated to be %(total)s. Is this correct? This reuses your answers to question 1 and 2", + "calculation": { + "title": "(extra) milk + eggs + bread + cheese", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + } + } + }, + { + "skip_conditions": { + "when": { + "!=": [ + { + "source": "answers", + "identifier": "radio-extra" + }, + "Yes, extra bread and cheese" + ] + } + }, + "type": "CalculatedSummary", + "id": "calculated-summary-4", + "title": "Total extra items cost is calculated to be %(total)s. Is this correct? This is reusing your bread and cheese answers", + "calculation": { + "title": "(extra) bread + cheese", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Grand calculated summary", + "enabled": { + "when": { + "==": [{ "source": "progress", "selector": "section", "identifier": "section-1" }, "COMPLETED"] + } + }, + "groups": [ + { + "id": "group-2", + "title": "Grand calculated summary", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-shopping", + "title": "Grand Calculated Summary of purchases this week comes to %(total)s. Is this correct?.", + "calculation": { + "title": "Weekly shopping cost", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-3" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/templates/calculatedsummary.html b/templates/calculatedsummary.html index 674524fea9..bdb0f96511 100644 --- a/templates/calculatedsummary.html +++ b/templates/calculatedsummary.html @@ -1,34 +1,5 @@ -{% extends 'layouts/_questionnaire.html' %} -{% from "components/button/_macro.njk" import onsButton %} -{% from "components/panel/_macro.njk" import onsPanel %} +{% extends 'layouts/_calculatedsummary.html' %} -{% set save_on_signout = true %} - -{% block form_content %} -

      {{content.summary.title}}

      - - {% call onsPanel({ - "classes": "ons-u-mb-l" - }) %} -

      {{ _("Please review your answers and confirm these are correct") }}

      - {% endcall %} - -
      - {% include 'partials/summary/summary.html' %} -
      -{% endblock -%} - -{% block submit_button %} - {{ - onsButton({ - "text": _("Yes, I confirm these are correct"), - "variants": 'timer', - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Confirm", - "data-ga-label": "Confirm button click" - } - }) - }} +{% block form_title %} +

      {{ content.summary.title }}

      {% endblock %} diff --git a/templates/grandcalculatedsummary.html b/templates/grandcalculatedsummary.html new file mode 100644 index 0000000000..875870974a --- /dev/null +++ b/templates/grandcalculatedsummary.html @@ -0,0 +1,5 @@ +{% extends 'layouts/_calculatedsummary.html' %} + +{% block form_title %} +

      {{ content.summary.title }}

      +{% endblock %} diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html new file mode 100644 index 0000000000..b618e817b3 --- /dev/null +++ b/templates/layouts/_calculatedsummary.html @@ -0,0 +1,35 @@ +{% extends 'layouts/_questionnaire.html' %} +{% from "components/button/_macro.njk" import onsButton %} +{% from "components/panel/_macro.njk" import onsPanel %} + +{% set save_on_signout = true %} + +{% block form_content %} + {% block form_title %} + {% endblock %} + + {% call onsPanel({ + "classes": "ons-u-mb-l" + }) %} +

      {{ _("Please review your answers and confirm these are correct") }}

      + {% endcall %} + +
      + {% include 'partials/summary/summary.html' %} +
      +{% endblock -%} + +{% block submit_button %} + {{ + onsButton({ + "text": _("Yes, I confirm these are correct"), + "variants": 'timer', + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Confirm", + "data-ga-label": "Confirm button click" + } + }) + }} +{% endblock %} diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 876b236b79..5e92c71cd0 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -6,7 +6,7 @@ from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import ProgressStore +from app.data_models.progress_store import CompletionStatus, ProgressStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.placeholder_parser import PlaceholderParser @@ -1311,6 +1311,29 @@ def progress_dependencies_schema(): ) +@pytest.fixture +def grand_calculated_summary_schema(): + return load_schema_from_name("test_grand_calculated_summary") + + +@pytest.fixture +def grand_calculated_summary_progress_store(): + return ProgressStore( + [ + { + "section_id": "section-1", + "block_ids": [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + "status": CompletionStatus.COMPLETED, + } + ] + ) + + @pytest.fixture @pytest.mark.usefixtures("app", "gb_locale") def placeholder_transform_question_dynamic_answers_json(): diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 2772e5ad09..7c1ec52e6f 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -510,7 +510,25 @@ def test_section_summary_on_completion_false(self): ("test_calculated_summary",), ) def test_return_to_calculated_summary(self, schema): + """ + This tests that when you hit continue on an edited answer for a calculated summary + and all dependent answers for that calculated summary are complete, you are routed to the calculated summary + """ self.schema = load_schema_from_name(schema) + # for the purposes of this test, assume the routing path consists only of the first two blocks and the calculated summary + # and that those two blocks are complete - this will be a sufficient condition to return to the calculated summary + self.progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": [ + "first-number-block", + "second-number-block", + ], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) current_location = Location( section_id="default-section", block_id="second-number-block" @@ -518,8 +536,9 @@ def test_return_to_calculated_summary(self, schema): routing_path = RoutingPath( [ + "first-number-block", "second-number-block", - "currency-total-playback-skipped-fourth", + "currency-total-playback", ], section_id="default-section", ) @@ -529,11 +548,11 @@ def test_return_to_calculated_summary(self, schema): routing_path, return_to_answer_id="first-number-answer", return_to="calculated-summary", - return_to_block_id="currency-total-playback-skipped-fourth", + return_to_block_id="currency-total-playback", ) expected_location = Location( section_id="default-section", - block_id="currency-total-playback-skipped-fourth", + block_id="currency-total-playback", ) expected_location_url = url_for( @@ -541,8 +560,7 @@ def test_return_to_calculated_summary(self, schema): list_item_id=expected_location.list_item_id, block_id=expected_location.block_id, return_to="calculated-summary", - return_to_answer_id="first-number-answer", - return_to_block_id="currency-total-playback-skipped-fourth", + _anchor="first-number-answer", ) assert expected_location_url == next_location_url @@ -556,10 +574,24 @@ def test_return_to_calculated_summary(self, schema): ), ) def test_return_to_calculated_summary_not_on_allowable_path(self, schema): + """ + This tests that if you try to return to a calculated summary before all its dependencies have been answered + then you are instead routed to the first incomplete block of the section + """ self.schema = load_schema_from_name(schema) + self.progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": ["block-3"], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) current_location = Location(section_id="default-section", block_id="block-3") + # block 3 is complete, and block 4 is not, so block 4 should be routed to before the calculated summary routing_path = RoutingPath( [ "block-3", @@ -587,7 +619,6 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): list_item_id=expected_location.list_item_id, block_id=expected_location.block_id, return_to="calculated-summary", - return_to_answer_id="answer-3", return_to_block_id="calculated-summary-block", ) @@ -613,6 +644,15 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( self, schema, return_to_block_id, expected_url ): self.schema = load_schema_from_name(schema) + self.progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": ["fifth-number-block"], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) current_location = Location( section_id="default-section", block_id="fifth-number-block" @@ -638,6 +678,15 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( ) def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schema): self.schema = load_schema_from_name(schema) + self.progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": ["fifth-number-block"], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) current_location = Location( section_id="default-section", block_id="fifth-number-block" @@ -661,6 +710,196 @@ def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schem == next_location_url ) + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_answer( + self, grand_calculated_summary_progress_store, grand_calculated_summary_schema + ): + """ + If going from GCS -> CS -> answer -> CS -> GCS this tests going from CS -> GCS having just come from an answer + """ + self.schema = grand_calculated_summary_schema + self.progress_store = grand_calculated_summary_progress_store + + current_location = Location( + section_id="section-1", block_id="first-number-block" + ) + + routing_path = RoutingPath( + ["distance-calculated-summary-1"], + section_id="section-1", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="distance-calculated-summary-1", + return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + ) + + expected_previous_url = url_for( + "questionnaire.block", + return_to="grand-calculated-summary", + block_id="distance-calculated-summary-1", + return_to_block_id="distance-grand-calculated-summary", + _anchor="distance-calculated-summary-1", + ) + + assert expected_previous_url == next_location_url + + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_calculated_summary( + self, grand_calculated_summary_progress_store, grand_calculated_summary_schema + ): + """ + If going from GCS -> CS -> GCS this tests going from CS -> GCS having just come from the grand calculated summary + """ + self.schema = grand_calculated_summary_schema + self.progress_store = grand_calculated_summary_progress_store + + current_location = Location( + section_id="section-1", block_id="distance-calculated-summary-1" + ) + + routing_path = RoutingPath( + ["distance-calculated-summary-1"], + section_id="section-1", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="grand-calculated-summary", + return_to_answer_id="distance-calculated-summary-1", + return_to_block_id="distance-grand-calculated-summary", + ) + + expected_previous_url = url_for( + "questionnaire.block", + return_to="grand-calculated-summary", + block_id="distance-grand-calculated-summary", + _anchor="distance-calculated-summary-1", + ) + + assert expected_previous_url == next_location_url + + @pytest.mark.parametrize( + "return_to_block_id", + ("grand-calculated-summary-1", "grand-calculated-summary-2"), + ) + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_incomplete_section( + self, return_to_block_id + ): + """ + This tests that if you try to return to a grand calculated summary from an incomplete section + (or the same section but before the dependencies of the grand calculated summary are complete) + you are routed to the next block in the incomplete section rather than the grand calculated summary + """ + self.schema = load_schema_from_name( + "test_grand_calculated_summary_multiple_sections" + ) + # calculated summary 3 is not complete yet + self.progress_store = ProgressStore( + [ + { + "section_id": "section-1", + "block_ids": [ + "block-1", + "block-2", + "calculated-summary-1", + "block-3", + "calculated-summary-2", + ], + "status": "IN_PROGRESS", + } + ] + ) + + current_location = Location(section_id="section-1", block_id="block-2") + routing_path = RoutingPath( + [ + "block-1", + "block-2", + "calculated-summary-1", + "block-3", + "calculated-summary-2", + "calculated-summary-3", + "grand-calculated-summary-1", + ], + section_id="section-1", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="grand-calculated-summary", + return_to_answer_id="calculated-summary-1", + return_to_block_id=return_to_block_id, + ) + + # because calculated summary 3 isn't done, should go there before jumping to the grand calculated summary + # test from grand-calculated-summary-1 which is in the same section, and grand-calculated-summary-2 which is in another + expected_next_url = url_for( + "questionnaire.block", + return_to="grand-calculated-summary", + return_to_block_id=return_to_block_id, + block_id="calculated-summary-3", + ) + + assert expected_next_url == next_location_url + + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary_from_incomplete_section( + self, grand_calculated_summary_schema + ): + """ + This tests that if you try to return to a calculated summary section from an incomplete section + you are routed to the next block in the incomplete section rather than the calculated summary + """ + self.schema = grand_calculated_summary_schema + # second-number block not complete yet + self.progress_store = ProgressStore( + [ + { + "section_id": "section-1", + "block_ids": [ + "first-number-block", + "distance-calculated-summary-1", + ], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) + + current_location = Location( + section_id="section-1", block_id="first-number-block" + ) + routing_path = RoutingPath( + [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + section_id="section-1", + ) + # the test is being done as part of a two-step return to but its identical functionally + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="first-number-block", + return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + ) + + # should take you to the second-number-block before going back to the calculated summary + expected_next_url = url_for( + "questionnaire.block", + return_to="calculated-summary,grand-calculated-summary", + return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + block_id="second-number-block", + ) + + assert expected_next_url == next_location_url + class TestRouterNextLocationLinearFlow(RouterTestCase): @pytest.mark.usefixtures("app") @@ -826,6 +1065,173 @@ def test_return_to_calculated_summary(self): assert expected_location_url == previous_location_url + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_answer_incomplete_section( + self, grand_calculated_summary_schema + ): + """ + This tests that if you are on a calculated summary, and your return_to_block_id is another calculated summary that you cannot reach yet + if you click previous, then you are taken to the previous block in the section + (rather than the first incomplete block of the section which is what next location would return) + """ + self.schema = grand_calculated_summary_schema + # trying to go to number-calculated-summary-1 but distance-calculated-summary-1 which comes before is not complete yet + self.progress_store = ProgressStore( + [ + { + "section_id": "section-1", + "block_ids": [ + "first-number-block", + "second-number-block", + "number-calculated-summary-1", + ], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) + + current_location = Location( + section_id="section-1", block_id="second-number-block" + ) + + routing_path = RoutingPath( + [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + section_id="section-1", + ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="second-number-block", + return_to_block_id="number-calculated-summary-1,number-grand-calculated-summary", + ) + # return to can't go to the distance calculated summary, so go to previous block with return params preserved + expected_previous_url = url_for( + "questionnaire.block", + return_to="calculated-summary,grand-calculated-summary", + return_to_block_id="number-calculated-summary-1,number-grand-calculated-summary", + block_id="first-number-block", + _anchor="second-number-block", + ) + + assert expected_previous_url == previous_location_url + + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_calculated_summary_incomplete_section( + self, grand_calculated_summary_schema + ): + """ + This tests that if you are on a calculated summary, and your return_to_block_id is a grand calculated summary + if you click previous, then you are taken to the previous block in the section + (rather than the first incomplete block of the section which is what next location would return) + """ + self.schema = grand_calculated_summary_schema + # number calculated summary is not complete, so the section is not complete + self.progress_store = ProgressStore( + [ + { + "section_id": "section-1", + "block_ids": [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + ], + "status": CompletionStatus.IN_PROGRESS, + } + ] + ) + + current_location = Location( + section_id="section-1", block_id="distance-calculated-summary-1" + ) + + routing_path = RoutingPath( + [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + section_id="section-1", + ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_to="grand-calculated-summary", + return_to_answer_id="distance-calculated-summary-1", + return_to_block_id="distance-grand-calculated-summary", + ) + # return to can't go to the grand calculated summary, so routing is just to the previous block in the section with return params preserved + expected_previous_url = url_for( + "questionnaire.block", + return_to="grand-calculated-summary", + return_to_block_id="distance-grand-calculated-summary", + block_id="second-number-block", + _anchor="distance-calculated-summary-1", + ) + + assert expected_previous_url == previous_location_url + + @pytest.mark.parametrize( + "return_to, current_block, return_to_block_id, expected_url", + [ + ( + "grand-calculated-summary", + "distance-calculated-summary-1", + "invalid-block", + "/questionnaire/second-number-block/?return_to=grand-calculated-summary&return_to_block_id=invalid-block#distance-calculated-summary-1", + ), + ( + "calculated-summary,invalid", + "second-number-block", + "invalid-1,invalid-2", + "/questionnaire/first-number-block/?return_to=calculated-summary,invalid&return_to_block_id=invalid-1,invalid-2#distance-calculated-summary-1", + ), + ( + "invalid", + "distance-calculated-summary-1", + "first-number-block", + "/questionnaire/second-number-block/?return_to=invalid&return_to_block_id=first-number-block#distance-calculated-summary-1", + ), + ], + ) + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_invalid_url( + self, + return_to, + current_block, + return_to_block_id, + expected_url, + grand_calculated_summary_schema, + ): + self.schema = grand_calculated_summary_schema + + current_location = Location(section_id="section-1", block_id=current_block) + + routing_path = RoutingPath( + [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + section_id="section-1", + ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_to=return_to, + return_to_answer_id="distance-calculated-summary-1", + return_to_block_id=return_to_block_id, + ) + + assert expected_url == previous_location_url + @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") diff --git a/tests/app/views/contexts/__init__.py b/tests/app/views/contexts/__init__.py index e929f99c7a..38e5cfe793 100644 --- a/tests/app/views/contexts/__init__.py +++ b/tests/app/views/contexts/__init__.py @@ -1,4 +1,4 @@ -def assert_summary_context(context): +def assert_summary_context(context, summary_item_type="question"): summary_context = context["summary"] for key_value in ("sections", "answers_are_editable", "summary_type"): assert ( @@ -10,10 +10,10 @@ def assert_summary_context(context): assert "id" in group assert "blocks" in group for block in group["blocks"]: - assert "question" in block - assert "title" in block["question"] - assert "answers" in block["question"] - for answer in block["question"]["answers"]: + assert summary_item_type in block + assert "title" in block[summary_item_type] + assert "answers" in block[summary_item_type] + for answer in block[summary_item_type]["answers"]: assert "id" in answer assert "value" in answer assert "type" in answer diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 35bfffb073..d10dd27c78 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -329,6 +329,11 @@ def test_calculated_summary_schema(): return load_schema_from_name("test_calculated_summary") +@pytest.fixture +def test_grand_calculated_summary_schema(): + return load_schema_from_name("test_grand_calculated_summary") + + @pytest.fixture def test_calculated_summary_answers(): answers = [ @@ -349,6 +354,21 @@ def test_calculated_summary_answers(): return AnswerStore(answers) +@pytest.fixture +def test_grand_calculated_summary_answers(): + answers = [ + {"value": 10, "answer_id": "q1-a1"}, + {"value": 1, "answer_id": "q1-a2"}, + {"value": 20, "answer_id": "q2-a1"}, + {"value": 2, "answer_id": "q2-a2"}, + {"value": 30, "answer_id": "q3-a1"}, + {"value": 3, "answer_id": "q3-a2"}, + {"value": 40, "answer_id": "q4-a1"}, + {"value": 4, "answer_id": "q4-a2"}, + ] + return AnswerStore(answers) + + @pytest.fixture def test_calculated_summary_answers_skipped_fourth(): answers = [ diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 9811e82762..8e9e4f4dab 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -122,7 +122,7 @@ def test_build_view_context_for_currency_calculated_summary( current_location=Location(section_id="default-section", block_id=block_id), ) - context = calculated_summary_context.build_view_context_for_calculated_summary() + context = calculated_summary_context.build_view_context() assert "summary" in context assert_summary_context(context) @@ -145,3 +145,90 @@ def test_build_view_context_for_currency_calculated_summary( assert "return_to=calculated-summary" in answer_change_link assert f"return_to_answer_id={return_to_answer_id}" in answer_change_link assert f"return_to_block_id={block_id}" in answer_change_link + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize( + "block_id, return_to_answer_id, return_to, return_to_block_id", + ( + ( + "distance-calculated-summary-1", + "q1-a1", + "grand-calculated-summary", + "distance-grand-calculated-summary", + ), + ( + "number-calculated-summary-1", + "q1-a2", + "grand-calculated-summary", + "number-grand-calculated-summary", + ), + ( + "distance-calculated-summary-2", + "q3-a1", + "grand-calculated-summary", + "distance-grand-calculated-summary", + ), + ( + "number-calculated-summary-2", + "q3-a2", + "grand-calculated-summary", + "number-grand-calculated-summary", + ), + ), +) +def test_build_view_context_for_return_to_calculated_summary( + test_grand_calculated_summary_schema, + test_grand_calculated_summary_answers, + list_store, + progress_store, + mocker, + block_id, + return_to_answer_id, + return_to, + return_to_block_id, +): + """ + Tests the change answer links for a calculated summary that has been reached by a change link on a grand calculated summary + """ + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.MagicMock(return_value="en_GB"), + ) + + block_ids = [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + "third-number-block", + "fourth-number-block", + "distance-calculated-summary-2", + "number-calculated-summary-2", + ] + + calculated_summary_context = CalculatedSummaryContext( + language="en", + schema=test_grand_calculated_summary_schema, + answer_store=test_grand_calculated_summary_answers, + list_store=list_store, + progress_store=progress_store, + metadata=None, + response_metadata={}, + routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), + current_location=Location(section_id="default-section", block_id=block_id), + return_to=return_to, + return_to_block_id=return_to_block_id, + ) + + context = calculated_summary_context.build_view_context() + assert "summary" in context + assert_summary_context(context) + context_summary = context["summary"] + + answer_change_link = context_summary["sections"][0]["groups"][0]["blocks"][0][ + "question" + ]["answers"][0]["link"] + assert f"return_to=calculated-summary,{return_to}" in answer_change_link + assert f"return_to_answer_id={return_to_answer_id}" in answer_change_link + assert f"return_to_block_id={block_id},{return_to_block_id}" in answer_change_link diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py new file mode 100644 index 0000000000..3c53d30390 --- /dev/null +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -0,0 +1,111 @@ +import pytest + +from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.questionnaire import Location +from app.questionnaire.routing_path import RoutingPath +from app.views.contexts.grand_calculated_summary_context import ( + GrandCalculatedSummaryContext, +) +from tests.app.views.contexts import assert_summary_context + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize( + "block_id, title, value, return_to_answer_id", + ( + ( + "distance-grand-calculated-summary", + "We calculate the grand total weekly distance travelled to be 100 mi. Is this correct?", + "100 mi", + "distance-calculated-summary-1", + ), + ( + "number-grand-calculated-summary", + "We calculate the grand total journeys per week to be 10. Is this correct?", + "10", + "number-calculated-summary-1", + ), + ), +) +def test_build_view_context_for_grand_calculated_summary( + block_id, + title, + value, + test_grand_calculated_summary_schema, + test_grand_calculated_summary_answers, + list_store, + mocker, + return_to_answer_id, +): + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.MagicMock(return_value="en_GB"), + ) + + block_ids = [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + "third-number-block", + "fourth-number-block", + "distance-calculated-summary-2", + "number-calculated-summary-2", + ] + + grand_calculated_summary_context = GrandCalculatedSummaryContext( + language="en", + schema=test_grand_calculated_summary_schema, + answer_store=test_grand_calculated_summary_answers, + list_store=list_store, + progress_store=ProgressStore( + in_progress_sections=[ + { + "section_id": "section-1", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + }, + { + "section_id": "section-2", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "third-number-block", + "fourth-number-block", + "distance-calculated-summary-2", + "number-calculated-summary-2", + ], + }, + ] + ), + metadata=None, + response_metadata={}, + routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), + current_location=Location(section_id="default-section", block_id=block_id), + return_to=None, + return_to_block_id=None, + ) + + context = grand_calculated_summary_context.build_view_context() + + assert "summary" in context + assert_summary_context(context, "calculated_summary") + assert len(context["summary"]) == 6 + context_summary = context["summary"] + assert context_summary.get("title") == title + + assert "calculated_question" in context_summary + assert context_summary["calculated_question"]["answers"][0]["value"] == value + + calculated_summary_change_link = context_summary["sections"][0]["groups"][0][ + "blocks" + ][0]["calculated_summary"]["answers"][0]["link"] + assert "return_to=grand-calculated-summary" in calculated_summary_change_link + assert ( + f"return_to_answer_id={return_to_answer_id}" in calculated_summary_change_link + ) + assert f"return_to_block_id={block_id}" in calculated_summary_change_link diff --git a/tests/functional/base_pages/grand-calculated-summary.page.js b/tests/functional/base_pages/grand-calculated-summary.page.js new file mode 100644 index 0000000000..4f1c9e3312 --- /dev/null +++ b/tests/functional/base_pages/grand-calculated-summary.page.js @@ -0,0 +1,17 @@ +import BasePage from "./base.page"; + +class GrandCalculatedSummaryPage extends BasePage { + grandCalculatedSummaryTitle() { + return '[data-qa="grand-calculated-summary-title"]'; + } + + grandCalculatedSummaryQuestion() { + return "[data-qa=grand-calculated-summary-question]"; + } + + grandCalculatedSummaryAnswer() { + return "[data-qa=grand-calculated-summary-answer]"; + } +} + +export default GrandCalculatedSummaryPage; diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index b20998f7df..0b5910ae9b 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -848,6 +848,10 @@ def process_block( base_page = "CalculatedSummaryPage" base_page_file = "calculated-summary.page" + if block["type"] == "GrandCalculatedSummary": + base_page = "GrandCalculatedSummaryPage" + base_page_file = "grand-calculated-summary.page" + if block["type"] == "Introduction": base_page = "IntroductionPageBase" base_page_file = "introduction.page" @@ -893,6 +897,21 @@ def process_block( process_calculated_summary(calculated_summary_answer_ids, page_spec) + elif block["type"] == "GrandCalculatedSummary": + values = _get_dictionaries_with_key( + "source", block["calculation"]["operation"] + ) + + calculated_summary_ids = [ + value["identifier"] + for value in values + if value["source"] == "calculated_summary" + ] + + # each calculated summary in a grand calculated summary is constructed such that it will have a single "answer" linking back to it + # so the processing for calculated summaries can be directly reused. + process_calculated_summary(calculated_summary_ids, page_spec) + elif block["type"] == "Interstitial": has_definition = False if "content_variants" in block: diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js new file mode 100644 index 0000000000..13dd580147 --- /dev/null +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -0,0 +1,120 @@ +import SkipFirstBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/skip-first-block.page"; +import SecondNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/second-number-block.page"; +import HubPage from "../../../base_pages/hub.page"; +import CurrencySection1Page from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/currency-section-1.page"; +import QuestionsSectionSummaryPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/questions-section-summary.page"; +import ThirdNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/third-number-block.page"; +import SkipCalculatedSummaryPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/skip-calculated-summary.page"; +import CalculatedSummarySectionSummaryPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; +import CurrencyQuestion3Page from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/currency-question-3.page"; +import CurrencyAllPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/currency-all.page"; +import FirstNumberBlockPartAPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/first-number-block-part-a.page"; +import FourthNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/fourth-number-block.page"; + +describe("Feature: Grand Calculated Summary", () => { + describe("Given I have a Grand Calculated Summary", () => { + before("Getting to the second calculated summary", async () => { + await browser.openQuestionnaire("test_grand_calculated_summary_cross_section_dependencies.json"); + await $(HubPage.submit()).click(); + await $(SkipFirstBlockPage.no()).click(); + await $(SkipFirstBlockPage.submit()).click(); + await $(FirstNumberBlockPartAPage.firstNumberA()).setValue(300); + await $(FirstNumberBlockPartAPage.submit()).click(); + await $(SecondNumberBlockPage.secondNumberA()).setValue(10); + await $(SecondNumberBlockPage.secondNumberB()).setValue(5); + await $(SecondNumberBlockPage.secondNumberC()).setValue(15); + await $(SecondNumberBlockPage.submit()).click(); + await $(CurrencySection1Page.submit()).click(); + await $(QuestionsSectionSummaryPage.submit()).click(); + // section 2 + await $(HubPage.submit()).click(); + await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(70); + await $(ThirdNumberBlockPage.submit()).click(); + }); + it("Given I don't skip the second calculated summary, it is included in the grand calculated summary", async () => { + await $(SkipCalculatedSummaryPage.no()).click(); + await $(SkipCalculatedSummaryPage.submit()).click(); + await $(CurrencyQuestion3Page.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£330.00"); + await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).to.contain("£70.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £400.00. Is this correct?" + ); + await $(CurrencyAllPage.submit()).click(); + }); + it("Given I go back and skip the second calculated summary, it is not included in the grand calculated summary", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); + await $(SkipCalculatedSummaryPage.yes()).click(); + await $(SkipCalculatedSummaryPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + // Currently the grand calculated summary remains 'Completed' because none of the answers have changed + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); + await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £330.00. Is this correct?" + ); + await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; + }); + it("Given I confirm the grand calculated summary, then edit an answer for question 3, the grand calculated summary updates to be incomplete, because this is a dependency", async () => { + await $(CurrencyAllPage.submit()).click(); + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(130); + await $(ThirdNumberBlockPage.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + // Although the calculated summary is not on the path, the answer is still a grand calculated summary dependency, so it updates progress + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); + await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £330.00. Is this correct?" + ); + await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; + await $(CurrencyAllPage.submit()).click(); + }); + it("Given I change my response to include the calculated summary, the grand calculated summary updates to re-include it", async () => { + await $(HubPage.summaryRowLink("calculated-summary-section")).click(); + await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); + await $(SkipCalculatedSummaryPage.no()).click(); + await $(SkipCalculatedSummaryPage.submit()).click(); + await $(CurrencyQuestion3Page.submit()).click(); + await $(CalculatedSummarySectionSummaryPage.submit()).click(); + // Currently, the grand calculated summary does not return to in-progress, because none of the answers it depends on have changed + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); + await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £460.00. Is this correct?" + ); + }); + it("Given I provide an answer to question 3b from the grand calculated summary, this opens up an additional question, and when I press continue I am taken to this question first, then the calculated summary, and then the grand calculated summary", async () => { + await $(CurrencyAllPage.currencyQuestion3Edit()).click(); + await $(CurrencyQuestion3Page.thirdNumberAnswerPartBEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumberPartB()).setValue(10); + await $(ThirdNumberBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(FourthNumberBlockPage.pageName); + await $(FourthNumberBlockPage.fourthNumber()).setValue(1); + await $(FourthNumberBlockPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + await $(CurrencyQuestion3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CurrencyAllPage.pageName); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £471.00. Is this correct?" + ); + await $(CurrencyAllPage.submit()).click(); + }); + it("Given I go back to section one and skip the first block, it is not included in the first calculated summary and consequently not included in the grand calculated summary", async () => { + await $(HubPage.summaryRowLink("questions-section")).click(); + await $(QuestionsSectionSummaryPage.skipAnswer1Edit()).click(); + await $(SkipFirstBlockPage.yes()).click(); + await $(SkipFirstBlockPage.submit()).click(); + await $(QuestionsSectionSummaryPage.submit()).click(); + await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); + await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£30.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "The grand calculated summary is calculated to be £171.00. Is this correct?" + ); + }); + }); +}); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js new file mode 100644 index 0000000000..d9a3ed2a9c --- /dev/null +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js @@ -0,0 +1,154 @@ +import HubPage from "../../../base_pages/hub.page"; +import Block1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-1.page"; +import Block2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-2.page"; +import CalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-1.page"; +import Block3Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-3.page"; +import Block4Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-4.page"; +import CalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-2.page"; +import CalculatedSummary3Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-3.page"; +import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-4.page"; +import GrandCalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/grand-calculated-summary-1.page"; +import GrandCalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/grand-calculated-summary-2.page"; +import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_multiple_sections/section-1-summary.page"; + +describe("Feature: Grand Calculated Summary", () => { + describe("Given I have a Grand Calculated Summary across multiple sections", () => { + before("Reaching the grand calculated summary section", async () => { + await browser.openQuestionnaire("test_grand_calculated_summary_multiple_sections.json"); + await $(HubPage.submit()).click(); + + // complete 2 questions in section 1 + await $(Block1Page.q1A1()).setValue(10); + await $(Block1Page.q1A2()).setValue(20); + await $(Block1Page.submit()).click(); + await $(Block2Page.q2A1()).setValue(30); + await $(Block2Page.q2A2()).setValue(40); + await $(Block2Page.submit()).click(); + await $(CalculatedSummary1Page.submit()).click(); + + // and the one for section 2 + await $(Block3Page.q3A1()).setValue(100); + await $(Block3Page.q3A2()).setValue(200); + await $(Block3Page.submit()).click(); + await $(CalculatedSummary2Page.submit()).click(); + await $(CalculatedSummary3Page.submit()).click(); + await $(GrandCalculatedSummary1Page.submit()).click(); + await $(Section1SummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(Block4Page.q4A1()).setValue(5); + await $(Block4Page.q4A2()).setValue(10); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.submit()).click(); + await $(HubPage.submit()).click(); + }); + + it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for section 1 and 2 is calculated to be £415.00. Is this correct?" + ); + await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + + await $(CalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + }); + + it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for Question 4 is calculated to be £15.00. Is this correct?" + ); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block4Page.pageName); + + await $(Block4Page.q4A1()).setValue(50); + await $(Block4Page.submit()).click(); + + // first taken back to the calculated summary which has updated + await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for Question 4 is calculated to be £60.00. Is this correct?" + ); + await $(CalculatedSummary4Page.submit()).click(); + + // then taken back to the grand calculated summary which has also been updated correctly + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for section 1 and 2 is calculated to be £460.00. Is this correct?" + ); + }); + + it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { + await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for Question 1 is calculated to be £100.00. Is this correct?" + ); + + // change first answer + await $(CalculatedSummary1Page.q1A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block1Page.pageName); + await $(Block1Page.q1A1()).setValue(100); + await $(Block1Page.submit()).click(); + + // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing + await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for Question 1 is calculated to be £190.00. Is this correct?" + ); + + // change another answer + await $(CalculatedSummary1Page.q2A2Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block2Page.pageName); + await $(Block2Page.q2A2()).setValue(400); + await $(Block2Page.submit()).click(); + + // back at updated calculated summary + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for Question 1 is calculated to be £550.00. Is this correct?" + ); + + // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary + await $(CalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummary3Page.pageName); + await $(CalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary1Page.pageName); + await $(GrandCalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for section 1 and 2 is calculated to be £910.00. Is this correct?" + ); + }); + + it("Given I edit an answer included in a grand calculated summary, both the calculated and grand calculated summary sections should return to partially completed.", async () => { + await $(GrandCalculatedSummary2Page.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Completed"); + + // Now edit an answer from section 2 and go back to the hub + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await $(Block4Page.q4A1()).setValue(1); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.previous()).click(); + await $(Block4Page.previous()).click(); + + // calculated summary section should be in progress + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + // TODO: grand calculated summary should not show, but this requires progress source, until this is implemented, it should at least show as in progress + await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Partially completed"); + }); + + it("Given I set both answers to block 4 to zero which removes the Grand Calculated Summary from the path, I am routed back to the Hub after the calculated summary", async () => { + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await $(Block4Page.q4A1()).setValue(0); + await $(Block4Page.q4A2()).setValue(0); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.submit()).click(); + // should be back at Hub, and grand calculated summary section not present + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + }); + }); +}); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js new file mode 100644 index 0000000000..c466bad957 --- /dev/null +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -0,0 +1,130 @@ +import HubPage from "../../../base_pages/hub.page"; +import IntroductionBlockPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/introduction-block.page"; +import Block1Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/block-1.page"; +import Block2Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/block-2.page"; +import CalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/calculated-summary-1.page"; +import CalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/calculated-summary-2.page"; +import Block3Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/block-3.page"; +import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/calculated-summary-4.page"; +import GrandCalculatedSummaryShoppingPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/grand-calculated-summary-shopping.page"; +import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/section-1-summary.page"; + +describe("Feature: Grand Calculated Summary", () => { + describe("Given I have a Grand Calculated Summary with overlapping answers", () => { + before("completing the survey", async () => { + await browser.openQuestionnaire("test_grand_calculated_summary_overlapping_answers.json"); + await $(IntroductionBlockPage.submit()).click(); + + // grand calculated summary should not be enabled until section-1 complete + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + + await $(HubPage.submit()).click(); + await $(Block1Page.q1A1()).setValue(100); + await $(Block1Page.q1A2()).setValue(200); + await $(Block1Page.submit()).click(); + await $(Block2Page.q2A1()).setValue(10); + await $(Block2Page.q2A2()).setValue(20); + await $(Block2Page.submit()).click(); + await $(CalculatedSummary1Page.submit()).click(); + await $(CalculatedSummary2Page.submit()).click(); + await $(Block3Page.yesExtraBreadAndCheese()).click(); + await $(Block3Page.submit()).click(); + await $(CalculatedSummary4Page.submit()).click(); + await $(Section1SummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?" + ); + await $(GrandCalculatedSummaryShoppingPage.submit()).click(); + }); + + it("Given I edit an answer that is only used in a single calculated summary, I am routed back to the calculated summary and then the grand calculated summary", async () => { + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummaryShoppingPage.calculatedSummary2Edit()).click(); + await $(CalculatedSummary2Page.q1A2Edit()).click(); + await $(Block1Page.q1A2()).setValue(300); + await $(Block1Page.submit()).click(); + + // taken back to calculated summary + await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); + await $(CalculatedSummary2Page.submit()).click(); + + // then grand calculated summary + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?" + ); + }); + + it("Given I edit an answer that is used in two calculated summaries, if I edit it from the first calculated summary change link, I taken through each block between the question and the second calculated summary before returning to the grand calculated summary", async () => { + await $(GrandCalculatedSummaryShoppingPage.calculatedSummary2Edit()).click(); + await $(CalculatedSummary2Page.q2A2Edit()).click(); + await $(Block2Page.q2A2()).setValue(400); + await $(Block2Page.submit()).click(); + + // taken back to the FIRST calculated summary which uses it + await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( + "Total of eggs and cheese is calculated to be £700.00. Is this correct?" + ); + await $(CalculatedSummary2Page.submit()).click(); + + // taken back to the SECOND calculated summary which uses it + await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Total extra items cost is calculated to be £410.00. Is this correct?" + ); + await $(CalculatedSummary4Page.submit()).click(); + + // then grand calculated summary + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary of purchases this week comes to £1,220.00. Is this correct?" + ); + }); + + it("Given I edit an answer that is used in two calculated summaries, if I edit it from the second calculated summary change link, I taken through each block between the question and the second calculated summary before returning to the grand calculated summary", async () => { + await $(GrandCalculatedSummaryShoppingPage.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q2A2Edit()).click(); + await $(Block2Page.q2A2()).setValue(500); + await $(Block2Page.submit()).click(); + + // taken back to the FIRST calculated summary which uses it + await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( + "Total of eggs and cheese is calculated to be £800.00. Is this correct?" + ); + await $(CalculatedSummary2Page.submit()).click(); + + // taken back to the SECOND calculated summary which uses it + await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Total extra items cost is calculated to be £510.00. Is this correct?" + ); + await $(CalculatedSummary4Page.submit()).click(); + + // then grand calculated summary + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?" + ); + await $(GrandCalculatedSummaryShoppingPage.submit()).click(); + }); + + it("Given I change an answer and return to the Hub before all calculated summaries are confirmed, the grand calculated summary section becomes inaccessible", async () => { + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummaryShoppingPage.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q2A2Edit()).click(); + await $(Block2Page.q2A2()).setValue(100); + await $(Block2Page.submit()).click(); + + // confirm one of the calculated summaries but return to the hub instead of confirming the other + await $(CalculatedSummary2Page.submit()).click(); + await browser.url(HubPage.url()); + + // calculated summary 4 is not confirmed so GCS doesn't show + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + }); + }); +}); diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py new file mode 100644 index 0000000000..3750564cc3 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -0,0 +1,163 @@ +from . import QuestionnaireTestCase + + +class TestQuestionnaireGrandCalculatedSummary(QuestionnaireTestCase): + BASE_URL = "/questionnaire/" + + def test_grand_calculated_summary(self): + self.launchSurvey("test_grand_calculated_summary") + # section-1 two types of unit questions + self.post({"q1-a1": 20, "q1-a2": 5}) + self.post({"q2-a1": 100, "q2-a2": 3}) + self.post() + self.post() + # section-2 two more of each question type + self.post({"q3-a1": 40, "q3-a2": 2}) + self.post({"q4-a1": 10, "q4-a2": 3}) + self.post() + self.post() + # check the two grand calculated summaries + self.assertInBody( + "We calculate the grand total weekly distance travelled to be 170 mi. Is this correct?" + ) + self.post() + self.assertInBody( + "We calculate the grand total journeys per week to be 13. Is this correct?" + ) + + def test_grand_calculated_summary_multiple_sections(self): + self.launchSurvey("test_grand_calculated_summary_multiple_sections") + # section 1 + self.post() + self.post({"q1-a1": 10, "q1-a2": 20}) + self.post({"q2-a1": 30, "q2-a2": 40}) + self.post() + self.post({"q3-a1": 50, "q3-a2": 60}) + self.post() + # confirm calculated and grand calculated summary + self.assertInBody( + "Calculated summary for section 1 is calculated to be £210.00. Is this correct?" + ) + self.post() + self.assertInBody( + "Grand Calculated Summary which should match the previous calculated summary is calculated to be £210.00. Is this correct?" + ) + self.post() + self.post() + # section 2 + self.post() + self.post({"q4-a1": 100, "q4-a2": 200}) + self.post() + # grand calculated summary section with calculated summaries from multiple sections + self.post() + self.assertInBody( + "Grand Calculated Summary for section 1 and 2 is calculated to be £510.00. Is this correct?" + ) + + def _complete_upto_grand_calculated_summary_cross_section_dependencies(self): + """ + Completes first two sections of the schema testing grand calculated summaries + depending on calculated summaries in other sections + """ + # Complete the first section + self.post() + self.post({"skip-answer-1": "Yes"}) + self.post({"second-number-answer-a": "30", "second-number-answer-b": "60"}) + self.assertInBody( + "We calculate your total monthly expenditure on household bills to be £90.00. Is this correct?" + ) + self.post() + self.post() + # Complete the second section + self.post() + self.post({"third-number-answer-part-a": "70"}) + + def test_grand_calculated_summary_cross_section_dependencies_with_skip(self): + self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self._complete_upto_grand_calculated_summary_cross_section_dependencies() + + # skip the calculated summary and go straight to section summary + self.post({"skip-answer-2": "Yes"}) + self.post() + + # grand calculated summary which doesn't include skipped calculated summary + self.post() + self.assertInBody( + "The grand calculated summary is calculated to be £90.00. Is this correct?" + ) + + def test_grand_calculated_summary_cross_section_dependencies_no_skip(self): + self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self._complete_upto_grand_calculated_summary_cross_section_dependencies() + + # don't skip calculated summary, confirm it, and go to section summary + self.post({"skip-answer-2": "No"}) + self.post() + self.post() + + # grand calculated summary will now include the previous calculated summary + self.post() + self.assertInBody( + "The grand calculated summary is calculated to be £160.00. Is this correct?" + ) + + def test_grand_calculated_summary_cross_section_dependencies_extra_question(self): + self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self._complete_upto_grand_calculated_summary_cross_section_dependencies() + + # edit question to unlock the extra one + self.previous() + self.post( + {"third-number-answer-part-a": "70", "third-number-answer-part-b": "20"} + ) + self.post({"fourth-number-answer": "40"}) + self.post({"skip-answer-2": "No"}) + self.post() + self.post() + + # grand calculated summary will now include the extra question answer + self.post() + self.assertInBody( + "The grand calculated summary is calculated to be £220.00. Is this correct?" + ) + + def _complete_upto_grand_calculated_summary_overlapping_answers( + self, radio_answer: str + ): + self.post() + self.post() + self.post({"q1-a1": "100", "q1-a2": "200"}) + self.post({"q2-a1": "10", "q2-a2": "20"}) + self.post() + self.post() + self.post({"radio-extra": radio_answer}) + if radio_answer != "No": + # in the no overlap case, the calculated summary is skipped entirely + self.post() + self.post() + self.post() + + def test_grand_calculated_summary_overlapping_answers_full_overlap(self): + self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self._complete_upto_grand_calculated_summary_overlapping_answers( + "Yes, I am going to buy two of everything" + ) + self.assertInBody( + "Grand Calculated Summary of purchases this week comes to £660.00. Is this correct?" + ) + + def test_grand_calculated_summary_overlapping_answers_partial_overlap(self): + self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self._complete_upto_grand_calculated_summary_overlapping_answers( + "Yes, extra bread and cheese" + ) + self.assertInBody( + "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?" + ) + + def test_grand_calculated_summary_overlapping_answers_no_overlap(self): + self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self._complete_upto_grand_calculated_summary_overlapping_answers("No") + self.assertInBody( + "Grand Calculated Summary of purchases this week comes to £330.00. Is this correct?" + ) From d5f71732c774f053c6732b29a2395c8cc0f1ccee Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 6 Jun 2023 15:49:22 +0100 Subject: [PATCH 235/567] Fix dynamic answers functional test (#1121) --- tests/functional/generate_pages.py | 18 ++ .../dynamic_answers_list_value_source.spec.js | 215 +++++++++--------- 2 files changed, 122 insertions(+), 111 deletions(-) diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 0b5910ae9b..d55a82741e 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -180,6 +180,14 @@ """ ) +QUESTION_LABELS_GETTER = r""" labels() { return `.ons-label`; } + +""" + +QUESTION_INPUTS_GETTER = r""" inputs() { return `[data-qa="input-text"]`; } + +""" + DYNAMIC_ANSWER_GETTER = Template( r""" answerByIndex(answerIndex) { return `#${answerId}-${answerIndex}`; @@ -222,6 +230,12 @@ """ ) +SUMMARY_GROUP_GETTER = Template( + r""" ${group_id_camel}Content(groupNumber) { return `#${group_id_without_number}-` + groupNumber; } + +""" +) + SUMMARY_QUESTION_GETTER = Template( r""" ${questionName}() { return `[data-qa=${questionId}]`; } @@ -491,6 +505,8 @@ def process_question(question, page_spec, num_questions, page_name): page_spec.write(QUESTION_TITLE.substitute(question_context)) page_spec.write(ANSWER_NUMBERED_ERROR_LIST_GETTER) page_spec.write(ANSWER_SINGLE_ERROR_LINK_GETTER) + page_spec.write(QUESTION_LABELS_GETTER) + page_spec.write(QUESTION_INPUTS_GETTER) def process_calculated_summary(answers, page_spec): @@ -657,8 +673,10 @@ def write_summary_spec( group_context = { "group_id_camel": camel_case(generate_pascal_case_from_id(group["id"])), "group_id": f'{group["id"]}-0', + "group_id_without_number": f'{group["id"]}', } page_spec.write(SUMMARY_TITLE_GETTER.substitute(group_context)) + page_spec.write(SUMMARY_GROUP_GETTER.substitute(group_context)) def long_names_required(question, num_questions): diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index de81a2f98a..c024ca0667 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -8,22 +8,19 @@ import SetMinimumPage from "../../generated_pages/dynamic_answers_list_source/mi import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/section-summary.page"; describe("Dynamic answers list value source", () => { - const labels = 'label[class="ons-label"]'; - const percentageInputs = 'input[class="ons-input ons-input--text ons-input-type__input ons-input-number--w-3"]'; - const numberInputs = 'input[class="ons-input ons-input--text ons-input-type__input ons-input-number--w-1"]'; - const group = 'div[id="group-2"]'; - const summaryTitles = 'dt[class="ons-summary__item-title"]'; - const summaryValues = 'dd[class="ons-summary__values"]'; - const summaryActions = 'dd[class="ons-summary__actions"]'; + const summaryTitles = ".ons-summary__item-title"; + const summaryValues = ".ons-summary__values"; + const summaryActions = ".ons-summary__actions"; + const timeout = 2000; beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_dynamic_answers_list_source.json"); }); it("Given list items have been added, When the dynamic answers are displayed, Then the correct answers should be visible", async () => { - await addTwoSupermarkets(); - await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $$(labels).length).to.equal(4); + await addTwoSupermarkets(timeout); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); }); it("Given list items have been added, When additional items are added using add link, Then the correct dynamic answers are displayed", async () => { await $(DriverPage.yes()).click(); @@ -33,113 +30,102 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); - await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(labels).length).to.equal(2); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await $(DynamicAnswerPage.labels()).waitForExist({ timeout: timeout }); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); + await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); - await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $$(labels).length).to.equal(4); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); }); it("Given list items have been added and the dynamic answers are submitted, When the summary is displayed, Then the correct answers should be visible and have correct values", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $$(numberInputs)[0].setValue(3); - await $$(numberInputs)[1].setValue(7); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); - await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(group).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(group).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(group).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(group).$$(summaryTitles).length).to.equal(8); - await expect(await $(group).$$(summaryValues).length).to.equal(8); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await $$(DynamicAnswerPage.inputs())[2].setValue(3); + await $$(DynamicAnswerPage.inputs())[3].setValue(7); + await setMinimumAndGetSectionSummary(timeout); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(8); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(8); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are revisited, Then they should be visible and have correct values", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(percentageInputs)[0].getValue()).to.equal("12"); - await expect(await $$(percentageInputs)[1].getValue()).to.equal("21"); - await expect(await $$(labels)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(labels)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); + await $(DynamicAnswerPage.labels()).waitForExist({ timeout: timeout }); + await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).to.equal("12"); + await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).to.equal("21"); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with different values, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); - await $$(percentageInputs)[0].setValue(21); - await $$(percentageInputs)[1].setValue(12); + await $$(DynamicAnswerPage.inputs())[0].waitForExist({ timeout: timeout }); + await $$(DynamicAnswerPage.inputs())[0].setValue(21); + await $$(DynamicAnswerPage.inputs())[1].setValue(12); await $(DynamicAnswerPage.submit()).click(); - await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("12%"); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); }); it("Given list items have been added and the dynamic answers are submitted, When the summary edit answer link is used for dynamic answer, Then the focus is on correct answer option", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); - await $(group).$$(summaryActions)[0].$("a").click(); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await setMinimumAndGetSectionSummary(timeout); + await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[0].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(percentageInputs)[0].isFocused()).to.be.true; + await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).to.be.true; await $(DynamicAnswerPage.submit()).click(); - await $(group).$$(summaryActions)[1].$("a").click(); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[1].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(percentageInputs)[1].isFocused()).to.be.true; + await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).to.be.true; }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with answers updated, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await setMinimumAndGetSectionSummary(timeout); + await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[0].$("a").click(); + await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); - await $(group).$$(summaryActions)[0].$("a").click(); - await $$(percentageInputs)[0].setValue(21); - await $(DynamicAnswerPage.submit()).click(); - await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); }); it("Given list items have been added and the dynamic answers are submitted, When the list items are removed and answers updated, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await setMinimumAndGetSectionSummary(timeout); + await $(SectionSummaryPage.supermarketsListRemoveLink(1)).waitForExist({ timeout: timeout }); await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await $(ListCollectorRemovePage.submit()).click(); @@ -148,44 +134,42 @@ describe("Dynamic answers list value source", () => { await $(SetMinimumPage.submit()).click(); await $(DynamicAnswerOnlyPage.submit()).click(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); - await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(group).$$(summaryTitles).length).to.equal(5); - await expect(await $(group).$$(summaryValues).length).to.equal(5); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(5); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(5); }); it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No', Then after changing answer to 'Yes' all answers should re-appear on summary", async () => { - await addTwoSupermarkets(); - await $$(percentageInputs)[0].setValue(12); - await $$(percentageInputs)[1].setValue(21); - await $$(numberInputs)[0].setValue(3); - await $$(numberInputs)[1].setValue(7); - await $(DynamicAnswerPage.submit()).click(); - await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await addTwoSupermarkets(timeout); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await $$(DynamicAnswerPage.inputs())[2].setValue(3); + await $$(DynamicAnswerPage.inputs())[3].setValue(7); + await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); await $(DriverPage.submit()).click(); await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.false; await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.false; - await expect(await $(group).isExisting()).to.be.false; await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await $(DriverPage.submit()).click(); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.true; await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.true; - await expect(await $(group).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(group).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(group).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(group).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(group).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(group).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(group).$$(summaryTitles).length).to.equal(8); - await expect(await $(group).$$(summaryValues).length).to.equal(8); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(8); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(8); }); }); -async function addTwoSupermarkets() { +async function addTwoSupermarkets(timeout) { await $(DriverPage.yes()).click(); await $(DriverPage.submit()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); @@ -198,4 +182,13 @@ async function addTwoSupermarkets() { await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); +} + +async function setMinimumAndGetSectionSummary(timeout) { + await $(DynamicAnswerPage.submit()).click(); + await $(SetMinimumPage.setMinimum()).setValue(2); + await $(SetMinimumPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); } From ab1967248c042ee13602dcbe963083b2c699b0c4 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:09:45 +0100 Subject: [PATCH 236/567] Schemas v3.59.0 (#1130) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6d07408b02..e968ff7420 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.58.0 +v3.59.0 From a2986c0332d8ec996b00c2fd720954aa2f92a099 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 8 Jun 2023 13:45:19 +0100 Subject: [PATCH 237/567] Schemas v3.60.0 (#1133) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e968ff7420..ed4d98134d 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.59.0 +v3.60.0 From 090e6529ca77b124c1c73172b86dc2b09fb17855 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:16:29 +0100 Subject: [PATCH 238/567] Update to chromedriver v114 (#1134) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5e7eb261de..589f4068c3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@wdio/mocha-framework": "^8.3.0", "@wdio/spec-reporter": "^8.3.0", "chai": "^4.3.6", - "chromedriver": "^113.0.0", + "chromedriver": "^114.0.0", "eslint": "^8.10.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^14.1.1", diff --git a/yarn.lock b/yarn.lock index 724845cce5..ebd0b67798 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,10 +1786,10 @@ chrome-launcher@^0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromedriver@^113.0.0: - version "113.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-113.0.0.tgz#d4855f156ee51cea4282e04aadd29fa154e44dbb" - integrity sha512-UnQlt2kPicYXVNHPzy9HfcWvEbKJjjKAEaatdcnP/lCIRwuSoZFVLH0HVDAGdbraXp3dNVhfE2Qx7gw8TnHnPw== +chromedriver@^114.0.0: + version "114.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-114.0.1.tgz#6b781922e74e2f6520cbd3e363dfabc41c4347f5" + integrity sha512-Srkyt7xv+RL9aSNVkmARm0tAfw84fIBKge9c1MCTiHfW0tjuNFdhKVlgD0TmPmwSKOeFJrTdd1Flf2hGWWKsUw== dependencies: "@testim/chrome-version" "^1.1.3" axios "^1.2.1" From 1dcf278e9943acab6fea31361120f71a92bc97be Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 12 Jun 2023 12:31:22 +0100 Subject: [PATCH 239/567] Add DESNZ theme (#1131) --- app/helpers/template_helpers.py | 4 + app/survey_config/__init__.py | 4 + app/survey_config/business_config.py | 12 ++ app/survey_config/survey_type.py | 2 + schemas/test/en/test_theme_desnz.json | 75 +++++++++++ schemas/test/en/test_theme_desnz_ni.json | 75 +++++++++++ .../assets/images/desnz-logo-stacked.svg | 116 ++++++++++++++++++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 73 +++++++++++ tests/functional/spec/theme_desnz.spec.js | 14 +++ tests/functional/spec/theme_desnz_ni.spec.js | 15 +++ 11 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 schemas/test/en/test_theme_desnz.json create mode 100644 schemas/test/en/test_theme_desnz_ni.json create mode 100644 templates/assets/images/desnz-logo-stacked.svg create mode 100644 tests/functional/spec/theme_desnz.spec.js create mode 100644 tests/functional/spec/theme_desnz_ni.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 10f82a2be9..4a5e8ec402 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -21,6 +21,8 @@ DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, DBTNIBusinessSurveyConfig, + DESNZBusinessSurveyConfig, + DESNZNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -188,6 +190,8 @@ def survey_config_mapping( SurveyType.DBT_NI: DBTNIBusinessSurveyConfig, SurveyType.DBT_DSIT: DBTDSITBusinessSurveyConfig, SurveyType.DBT_DSIT_NI: DBTDSITNIBusinessSurveyConfig, + SurveyType.DESNZ: DESNZBusinessSurveyConfig, + SurveyType.DESNZ_NI: DESNZNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, SurveyType.CENSUS: ( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index c0f4f9b481..36393c2636 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -4,6 +4,8 @@ DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, DBTNIBusinessSurveyConfig, + DESNZBusinessSurveyConfig, + DESNZNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) @@ -29,5 +31,7 @@ "DBTDSITBusinessSurveyConfig", "DBTDSITNIBusinessSurveyConfig", "ORRBusinessSurveyConfig", + "DESNZBusinessSurveyConfig", + "DESNZNIBusinessSurveyConfig", "Link", ] diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index c9803a33f8..8019845eea 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -157,6 +157,18 @@ class DBTNIBusinessSurveyConfig(BusinessSurveyConfig): ) + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") +@dataclass +class DESNZBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file("./templates/assets/images/desnz-logo-stacked.svg") + + +@dataclass +class DESNZNIBusinessSurveyConfig(BusinessSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/desnz-logo-stacked.svg" + ) + read_file("./templates/assets/images/finance-ni-logo-stacked.svg") + + @dataclass class ORRBusinessSurveyConfig(BusinessSurveyConfig): masthead_logo: str = read_file("./templates/assets/images/orr-logo.svg") diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index 01098e2470..ae3b3d978a 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -12,5 +12,7 @@ class SurveyType(Enum): DBT_DSIT = "dbt-dsit" DBT_DSIT_NI = "dbt-dsit-ni" ORR = "orr" + DESNZ = "desnz" + DESNZ_NI = "desnz-ni" CENSUS = "census" CENSUS_NISRA = "census-nisra" diff --git a/schemas/test/en/test_theme_desnz.json b/schemas/test/en/test_theme_desnz.json new file mode 100644 index 0000000000..c1aa83755f --- /dev/null +++ b/schemas/test/en/test_theme_desnz.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test Department for Energy Security and Net Zero", + "theme": "desnz", + "description": "A questionnaire to demo the DESNZ survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "DESNZ Theme Test" + } + ] + } + ] +} diff --git a/schemas/test/en/test_theme_desnz_ni.json b/schemas/test/en/test_theme_desnz_ni.json new file mode 100644 index 0000000000..06751903f5 --- /dev/null +++ b/schemas/test/en/test_theme_desnz_ni.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test NI Department for Energy Security and Net Zero", + "theme": "desnz-ni", + "description": "A questionnaire to demo the DESNZ-NI survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "DESNZ-NI Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/desnz-logo-stacked.svg b/templates/assets/images/desnz-logo-stacked.svg new file mode 100644 index 0000000000..ea62005183 --- /dev/null +++ b/templates/assets/images/desnz-logo-stacked.svg @@ -0,0 +1,116 @@ + + Department for Energy Security and Net Zero + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 41cdd3d9f6..06a5a47cc0 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,6 +1,6 @@ {% extends 'layouts/_base.html' %} -{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr"] %} +{% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr", "desnz", "desnz-ni"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} {% set SURVEY_TYPES_HEALTH = ["health"] %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 8039d8ad31..b7449148d8 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -22,6 +22,8 @@ DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, DBTNIBusinessSurveyConfig, + DESNZBusinessSurveyConfig, + DESNZNIBusinessSurveyConfig, NIBusinessSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, @@ -340,6 +342,27 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): read_file("./templates/assets/images/orr-mobile-logo.svg"), ], ), + ( + SurveyType.DESNZ, + "Test", + DESNZBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/desnz-logo-stacked.svg"), + None, + ], + ), + ( + SurveyType.DESNZ, + "Test", + DESNZNIBusinessSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/desnz-logo-stacked.svg") + + read_file("./templates/assets/images/finance-ni-logo-stacked.svg"), + None, + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): @@ -499,6 +522,16 @@ def test_service_links_context( "en", f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", ), + ( + DESNZBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), + ( + DESNZNIBusinessSurveyConfig(), + "en", + f"{ACCOUNT_SERVICE_BASE_URL}/contact-us/", + ), ( SocialSurveyConfig(), "en", @@ -588,6 +621,16 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", ), + ( + DESNZBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), + ( + DESNZNIBusinessSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL}/cookies/", + ), ( SocialSurveyConfig(), True, @@ -657,6 +700,16 @@ def test_cookie_settings_url_context( "en", ACCOUNT_SERVICE_BASE_URL, ), + ( + DESNZBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), + ( + DESNZNIBusinessSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL, + ), ( SocialSurveyConfig(), "en", @@ -792,6 +845,14 @@ def test_account_service_my_todo_url_context( ORRBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), + ( + DESNZBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), + ( + DESNZNIBusinessSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", + ), ( SocialSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", @@ -1061,6 +1122,18 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), + ( + SurveyType.DESNZ, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), + ( + SurveyType.DESNZ_NI, + "en", + QuestionnaireSchema({"survey_id": "001"}), + [{"survey_id": "001"}], + ), ( SurveyType.CENSUS, "en", diff --git a/tests/functional/spec/theme_desnz.spec.js b/tests/functional/spec/theme_desnz.spec.js new file mode 100644 index 0000000000..e67af59233 --- /dev/null +++ b/tests/functional/spec/theme_desnz.spec.js @@ -0,0 +1,14 @@ +import RadioPage from "../generated_pages/theme_desnz/radio.page"; + +describe("Theme DESNZ", () => { + describe("Given I launch a DESNZ themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_desnz.json"); + }); + + it("When I navigate to the radio page, Then I should see DESNZ theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#desnz-logo-alt").getHTML()).to.contain("Department for Energy Security and Net Zero"); + }); + }); +}); diff --git a/tests/functional/spec/theme_desnz_ni.spec.js b/tests/functional/spec/theme_desnz_ni.spec.js new file mode 100644 index 0000000000..7896f46ef5 --- /dev/null +++ b/tests/functional/spec/theme_desnz_ni.spec.js @@ -0,0 +1,15 @@ +import RadioPage from "../generated_pages/theme_desnz_ni/radio.page"; + +describe("Theme DESNZ-NI", () => { + describe("Given I launch a DESNZ-NI themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_desnz_ni.json"); + }); + + it("When I navigate to the radio page, Then I should see DESNZ-NI theme content", async () => { + await expect(await browser.getUrl()).to.contain(RadioPage.pageName); + await expect(await $("#desnz-logo-alt").getHTML()).to.contain("Department for Energy Security and Net Zero"); + await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + }); + }); +}); From d04325cdaa17ccde913098ecc86375341943da09 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:33:47 +0100 Subject: [PATCH 240/567] Schemas v3.61.0 (#1139) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ed4d98134d..0fac7be0d1 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.60.0 +v3.61.0 From f5d1b7df3f2e379ad122cd10476f0b169578edbf Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:57:22 +0100 Subject: [PATCH 241/567] Add schema changes for validator format_unit using calculated summary source validation fix (#1142) --- schemas/test/en/test_calculated_summary.json | 44 ++++++++++++++++++- .../test/en/test_new_calculated_summary.json | 44 ++++++++++++++++++- .../features/calculated_summary_test_case.js | 9 +++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 71753ae08d..199a993cff 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -345,7 +345,7 @@ ] }, { - "text": "Total unit values: {unit_total}", + "text": "Total unformatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -364,7 +364,28 @@ ] }, { - "text": "Total percentage values: {percentage_total}", + "text": "Total formatted unit values: {unit_total}", + "placeholders": [ + { + "placeholder": "unit_total", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "calculated_summary", + "identifier": "unit-total-playback" + }, + "unit": "length-centimeter", + "unit_length": "short" + } + } + ] + } + ] + }, + { + "text": "Total unformatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -382,6 +403,25 @@ } ] }, + { + "text": "Total formatted percentage values: {percentage_total}", + "placeholders": [ + { + "placeholder": "percentage_total", + "transforms": [ + { + "transform": "format_percentage", + "arguments": { + "value": { + "source": "calculated_summary", + "identifier": "percentage-total-playback" + } + } + } + ] + } + ] + }, { "text": "Total number values: {number_total}", "placeholders": [ diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index 36afe23f8a..98b77580d7 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -394,7 +394,7 @@ ] }, { - "text": "Total unit values: {unit_total}", + "text": "Total unformatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -413,7 +413,28 @@ ] }, { - "text": "Total percentage values: {percentage_total}", + "text": "Total formatted unit values: {unit_total}", + "placeholders": [ + { + "placeholder": "unit_total", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "calculated_summary", + "identifier": "unit-total-playback" + }, + "unit": "length-centimeter", + "unit_length": "short" + } + } + ] + } + ] + }, + { + "text": "Total unformatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -431,6 +452,25 @@ } ] }, + { + "text": "Total formatted percentage values: {percentage_total}", + "placeholders": [ + { + "placeholder": "percentage_total", + "transforms": [ + { + "transform": "format_percentage", + "arguments": { + "value": { + "source": "calculated_summary", + "identifier": "percentage-total-playback" + } + } + } + ] + } + ] + }, { "text": "Total number values: {number_total}", "placeholders": [ diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary_test_case.js index fd088e7564..d4ee91e1af 100644 --- a/tests/functional/spec/features/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary_test_case.js @@ -346,7 +346,14 @@ class TestCase { await browser.url(CalculatedSummaryTotalConfirmation.url()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryTotalConfirmation.pageName); const content = await $("h1 + ul").getText(); - const textsToAssert = ["Total currency values: £25.92", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; + const textsToAssert = [ + "Total currency values: £25.92", + "Total unformatted unit values: 1,467", + "Total formatted unit values: 1,467 cm", + "Total unformatted percentage values: 79", + "Total formatted percentage values: 79%", + "Total number values: 124.58", + ]; textsToAssert.forEach((text) => expect(content).to.contain(text)); await browser.url(SubmitPage.url()); From c0bb6bbd9226f1c838ded343276a1006d1bef5b9 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 20 Jun 2023 15:40:30 +0100 Subject: [PATCH 242/567] Schemas v3.62.0 (#1144) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0fac7be0d1..5cd3892448 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.61.0 +v3.62.0 From 004c882c423a464b7b7c831674c044a94f2a1c9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:18:17 +0100 Subject: [PATCH 243/567] Bump cryptography from 40.0.2 to 41.0.0 (#1124) --- Pipfile.lock | 369 +++++++++++++++++++++++++++------------------------ 1 file changed, 193 insertions(+), 176 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 7f8c8b727d..3af7ed83cf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -50,11 +50,11 @@ }, "botocore": { "hashes": [ - "sha256:3005a7ffee083315e69938acdf1bfeaf9e21fe1fe1643d6573ee817721f4ffcd", - "sha256:ac87b63e9aa4231cd28941945024a0c4470c184c60334ebe5e1cae3544c785ed" + "sha256:77f7793cb36074eb84d606a23ad6e1d57c20f7a2eeab7d9136d3e63c584e0504", + "sha256:ac57003292f18206ee942eafc381ecd9a3420a3844d6b7e1c1b0f4b88b28263b" ], "markers": "python_version >= '3.7'", - "version": "==1.29.125" + "version": "==1.29.146" }, "brotli": { "hashes": [ @@ -145,19 +145,19 @@ }, "cachetools": { "hashes": [ - "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14", - "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4" + "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590", + "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b" ], - "markers": "python_version ~= '3.7'", - "version": "==5.3.0" + "markers": "python_version >= '3.7'", + "version": "==5.3.1" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cffi": { "hashes": [ @@ -306,7 +306,7 @@ "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==3.1.0" }, "click": { @@ -335,43 +335,43 @@ }, "cryptography": { "hashes": [ - "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", - "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", - "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", - "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", - "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", - "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", - "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", - "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", - "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", - "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", - "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", - "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", - "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", - "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", - "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", - "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", - "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", - "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", - "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" - ], - "markers": "python_version >= '3.6'", - "version": "==40.0.2" + "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", + "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", + "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", + "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", + "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", + "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", + "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", + "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", + "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", + "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", + "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", + "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", + "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", + "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", + "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", + "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", + "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", + "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", + "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" + ], + "index": "pypi", + "version": "==41.0.0" }, "deprecated": { "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" + "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", + "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" + "version": "==1.2.14" }, "dnspython": { "hashes": [ "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9", "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==2.3.0" }, "email-validator": { @@ -491,7 +491,7 @@ }, "google-api-core": { "extras": [ - "grpc" + ], "hashes": [ "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", @@ -502,11 +502,11 @@ }, "google-auth": { "hashes": [ - "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc", - "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f" + "sha256:a9cfa88b3e16196845e64a3658eb953992129d13ac7337b064c6546f77c17183", + "sha256:ea165e014c7cbd496558796b627c271aa8c18b4cba79dc1cc962b24c5efdfb85" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==2.17.3" + "markers": "python_version >= '3.6'", + "version": "==2.19.1" }, "google-cloud-core": { "hashes": [ @@ -844,10 +844,10 @@ }, "jwcrypto": { "hashes": [ - "sha256:80a35e9ed1b3b2c43ce03d92c5d48e6d0b6647e2aa2618e4963448923d78a37b" + "sha256:2c1dc51cf8e38ddf324795dfe9426dee9dd46caf47f535ccbc18781fba810b8d" ], "markers": "python_version >= '3.6'", - "version": "==1.4.2" + "version": "==1.5.0" }, "markupsafe": { "hashes": [ @@ -956,22 +956,22 @@ }, "protobuf": { "hashes": [ - "sha256:13233ee2b9d3bd9a5f216c1fa2c321cd564b93d8f2e4f521a85b585447747997", - "sha256:23452f2fdea754a8251d0fc88c0317735ae47217e0d27bf330a30eec2848811a", - "sha256:52f0a78141078077cfe15fe333ac3e3a077420b9a3f5d1bf9b5fe9d286b4d881", - "sha256:70659847ee57a5262a65954538088a1d72dfc3e9882695cab9f0c54ffe71663b", - "sha256:7760730063329d42a9d4c4573b804289b738d4931e363ffbe684716b796bde51", - "sha256:7cf56e31907c532e460bb62010a513408e6cdf5b03fb2611e4b67ed398ad046d", - "sha256:8b54f56d13ae4a3ec140076c9d937221f887c8f64954673d46f63751209e839a", - "sha256:d14fc1a41d1a1909998e8aff7e80d2a7ae14772c4a70e4bf7db8a36690b54425", - "sha256:d4b66266965598ff4c291416be429cef7989d8fae88b55b62095a2331511b3fa", - "sha256:e0e630d8e6a79f48c557cd1835865b593d0547dce221c66ed1b827de59c66c97", - "sha256:ecae944c6c2ce50dda6bf76ef5496196aeb1b85acb95df5843cd812615ec4b61", - "sha256:f08aa300b67f1c012100d8eb62d47129e53d1150f4469fd78a29fa3cb68c66f2", - "sha256:f2f4710543abec186aee332d6852ef5ae7ce2e9e807a3da570f36de5a732d88e" + "sha256:09310bce43353b46d73ba7e3bca78273b9bc50349509b9698e64d288c6372c2a", + "sha256:20874e7ca4436f683b64ebdbee2129a5a2c301579a67d1a7dda2cdf62fb7f5f7", + "sha256:25e3370eda26469b58b602e29dff069cfaae8eaa0ef4550039cc5ef8dc004511", + "sha256:281342ea5eb631c86697e1e048cb7e73b8a4e85f3299a128c116f05f5c668f8f", + "sha256:384dd44cb4c43f2ccddd3645389a23ae61aeb8cfa15ca3a0f60e7c3ea09b28b3", + "sha256:54a533b971288af3b9926e53850c7eb186886c0c84e61daa8444385a4720297f", + "sha256:6c081863c379bb1741be8f8193e893511312b1d7329b4a75445d1ea9955be69e", + "sha256:86df87016d290143c7ce3be3ad52d055714ebaebb57cc659c387e76cfacd81aa", + "sha256:8da6070310d634c99c0db7df48f10da495cc283fd9e9234877f0cd182d43ab7f", + "sha256:b2cfab63a230b39ae603834718db74ac11e52bccaaf19bf20f5cce1a84cf76df", + "sha256:c52cfcbfba8eb791255edd675c1fe6056f723bf832fa67f0442218f8817c076e", + "sha256:ce744938406de1e64b91410f473736e815f28c3b71201302612a68bf01517fea", + "sha256:efabbbbac1ab519a514579ba9ec52f006c28ae19d97915951f69fa70da2c9e91" ], "markers": "python_version >= '3.7'", - "version": "==4.22.3" + "version": "==4.23.2" }, "pyasn1": { "hashes": [ @@ -1132,16 +1132,16 @@ "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" ], - "markers": "python_version >= '3.6'", + "markers": "python_version >= '3.6' and python_version < '4'", "version": "==4.9" }, "s3transfer": { "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", + "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" ], "markers": "python_version >= '3.7'", - "version": "==0.6.0" + "version": "==0.6.1" }, "sdc-cryptography": { "hashes": [ @@ -1153,11 +1153,11 @@ }, "setuptools": { "hashes": [ - "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b", - "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990" + "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", + "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" ], "markers": "python_version >= '3.7'", - "version": "==67.7.2" + "version": "==67.8.0" }, "simplejson": { "hashes": [ @@ -1276,11 +1276,11 @@ }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", + "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "version": "==1.26.16" }, "uwsgi": { "hashes": [ @@ -1291,11 +1291,11 @@ }, "werkzeug": { "hashes": [ - "sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a", - "sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091" + "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76", + "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f" ], "markers": "python_version >= '3.8'", - "version": "==2.3.3" + "version": "==2.3.4" }, "wrapt": { "hashes": [ @@ -1433,11 +1433,11 @@ "develop": { "astroid": { "hashes": [ - "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347", - "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8" + "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324", + "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.4" + "version": "==2.15.5" }, "async-timeout": { "hashes": [ @@ -1494,6 +1494,14 @@ "index": "pypi", "version": "==23.3.0" }, + "blinker": { + "hashes": [ + "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", + "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + ], + "index": "pypi", + "version": "==1.6.2" + }, "boto3": { "hashes": [ "sha256:6648aff15d19927cd26db47eb56362ccd313a1ddbd7aaa3235ef05d05d398252", @@ -1504,19 +1512,19 @@ }, "botocore": { "hashes": [ - "sha256:3005a7ffee083315e69938acdf1bfeaf9e21fe1fe1643d6573ee817721f4ffcd", - "sha256:ac87b63e9aa4231cd28941945024a0c4470c184c60334ebe5e1cae3544c785ed" + "sha256:77f7793cb36074eb84d606a23ad6e1d57c20f7a2eeab7d9136d3e63c584e0504", + "sha256:ac57003292f18206ee942eafc381ecd9a3420a3844d6b7e1c1b0f4b88b28263b" ], "markers": "python_version >= '3.7'", - "version": "==1.29.125" + "version": "==1.29.146" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cffi": { "hashes": [ @@ -1665,7 +1673,7 @@ "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" ], - "markers": "python_full_version >= '3.7.0'", + "markers": "python_version >= '3.7'", "version": "==3.1.0" }, "click": { @@ -1681,85 +1689,94 @@ "toml" ], "hashes": [ - "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3", - "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a", - "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813", - "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0", - "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a", - "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd", - "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139", - "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b", - "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252", - "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790", - "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045", - "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce", - "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200", - "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718", - "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b", - "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f", - "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5", - "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade", - "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5", - "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a", - "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8", - "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33", - "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e", - "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c", - "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3", - "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969", - "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068", - "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2", - "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771", - "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed", - "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212", - "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614", - "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88", - "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3", - "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c", - "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84", - "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11", - "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1", - "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1", - "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e", - "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1", - "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd", - "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47", - "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a", - "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c", - "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31", - "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5", - "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6", - "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303", - "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5", - "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47" + "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f", + "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2", + "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a", + "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a", + "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01", + "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6", + "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7", + "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f", + "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02", + "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c", + "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063", + "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a", + "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5", + "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959", + "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97", + "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6", + "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f", + "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9", + "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5", + "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f", + "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562", + "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe", + "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9", + "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f", + "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb", + "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb", + "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1", + "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb", + "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250", + "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e", + "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511", + "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5", + "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59", + "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2", + "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d", + "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3", + "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4", + "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de", + "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9", + "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833", + "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0", + "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9", + "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d", + "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050", + "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d", + "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6", + "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353", + "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb", + "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e", + "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8", + "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495", + "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2", + "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd", + "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27", + "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1", + "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818", + "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4", + "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e", + "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850", + "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3" ], "markers": "python_version >= '3.7'", - "version": "==7.2.5" + "version": "==7.2.7" }, "cryptography": { "hashes": [ - "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", - "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", - "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", - "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", - "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", - "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", - "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", - "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", - "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", - "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", - "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", - "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", - "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", - "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", - "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", - "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", - "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", - "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", - "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" - ], - "markers": "python_version >= '3.6'", - "version": "==40.0.2" + "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", + "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", + "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", + "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", + "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", + "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", + "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", + "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", + "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", + "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", + "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", + "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", + "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", + "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", + "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", + "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", + "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", + "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", + "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" + ], + "index": "pypi", + "version": "==41.0.0" }, "dill": { "hashes": [ @@ -2107,11 +2124,11 @@ }, "platformdirs": { "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" + "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", + "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" ], "markers": "python_version >= '3.7'", - "version": "==3.5.0" + "version": "==3.5.1" }, "pluggy": { "hashes": [ @@ -2328,11 +2345,11 @@ }, "s3transfer": { "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", + "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" ], "markers": "python_version >= '3.7'", - "version": "==0.6.0" + "version": "==0.6.1" }, "six": { "hashes": [ @@ -2383,10 +2400,10 @@ }, "types-pyopenssl": { "hashes": [ - "sha256:20b80971b86240e8432a1832bd8124cea49c3088c7bfc77dfd23be27ffe4a517", - "sha256:b050641aeff6dfebf231ad719bdac12d53b8ee818d4afb67b886333484629957" + "sha256:43e307e8dfb3a7a8208a19874ca060305f460c529d4eaca8a2669ea89499f244", + "sha256:ba803a99440b0c2e9ab4e197084aeefc55bdfe8a580d367b2aa4210810a21240" ], - "version": "==23.1.0.2" + "version": "==23.2.0.0" }, "types-python-dateutil": { "hashes": [ @@ -2430,34 +2447,34 @@ }, "types-urllib3": { "hashes": [ - "sha256:3ba3d3a8ee46e0d5512c6bd0594da4f10b2584b47a470f8422044a2ab462f1df", - "sha256:a1557355ce8d350a555d142589f3001903757d2d36c18a66f588d9659bbc917d" + "sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5", + "sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c" ], - "version": "==1.26.25.12" + "version": "==1.26.25.13" }, "typing-extensions": { "hashes": [ - "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb", - "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4" + "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", + "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" ], "markers": "python_version >= '3.7'", - "version": "==4.5.0" + "version": "==4.6.3" }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", + "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "version": "==1.26.16" }, "werkzeug": { "hashes": [ - "sha256:4866679a0722de00796a74086238bb3b98d90f423f05de039abb09315487254a", - "sha256:a987caf1092edc7523edb139edb20c70571c4a8d5eed02e0b547b4739174d091" + "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76", + "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f" ], "markers": "python_version >= '3.8'", - "version": "==2.3.3" + "version": "==2.3.4" }, "wrapt": { "hashes": [ From d2dc26a15d8e8ec3a4310f46e85f582d070e36e5 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 26 Jun 2023 13:37:58 +0100 Subject: [PATCH 244/567] Fix mass-metric-ton units handling (#1146) --- app/jinja_filters.py | 24 ++++++++++++------------ tests/app/test_jinja_filters.py | 5 +++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 834a036502..5f711d60f0 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -75,12 +75,17 @@ def format_unit( value: int | float | Decimal, length: UnitLengthType = "short", ) -> str: + # mass-metric-ton no longer supported for en_GB and related locales, but still present in business schema and allowed in validator, + # until removed from schema we substitute mass-tonne for mass-metric-ton before format unit + measurement_unit = "mass-tonne" if unit == "mass-metric-ton" else unit + formatted_unit: str = units.format_unit( value=value, - measurement_unit=unit, + measurement_unit=measurement_unit, length=length, locale=flask_babel.get_locale(), ) + return formatted_unit @@ -93,20 +98,15 @@ def format_unit_input_label(unit: str, unit_length: UnitLengthType = "short") -> :param (str) unit_length length of unit text, can be one of short/long/narrow """ unit_label: str + if unit_length == "long": - unit_label = units.format_unit( - value=2, - measurement_unit=unit, - length=unit_length, - locale=flask_babel.get_locale(), - ).replace("2 ", "") + unit_label = format_unit(value=2, unit=unit, length=unit_length).replace( + "2 ", "" + ) else: # Type ignore: We pass an empty string as the value so that we just return the unit label - unit_label = units.format_unit( - value="", # type: ignore - measurement_unit=unit, - length=unit_length, - locale=flask_babel.get_locale(), + unit_label = format_unit( + value="", unit=unit, length=unit_length # type: ignore ).strip() return unit_label diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index d76c7c55fa..18473b779a 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -153,6 +153,9 @@ def test_format_percentage(percentage, formatted_percentage): ("duration-year", 100, "short", "100 bl", "cy"), ("duration-hour", 100, "long", "100 awr", "cy"), ("duration-year", 100, "long", "100 mlynedd", "cy"), + ("mass-metric-ton", 100, "long", "100 tonnes", "en_GB"), + ("mass-metric-ton", 1, "long", "1 tonne", "en_GB"), + ("mass-metric-ton", 100, "short", "100 t", "en_GB"), ), ) def test_format_unit(unit, value, length, formatted_unit, language, mocker): @@ -204,6 +207,8 @@ def test_format_unit(unit, value, length, formatted_unit, language, mocker): ("duration-hour", "long", "awr", "cy"), ("duration-year", "short", "bl", "cy"), ("duration-year", "long", "flynedd", "cy"), + ("mass-metric-ton", "long", "tonnes", "en_GB"), + ("mass-metric-ton", "short", "t", "en_GB"), ), ) def test_format_unit_input_label(unit, length, formatted_unit, language, mocker): From deb64a7ad74100df961c687ff8359954950070a6 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:10:35 +0100 Subject: [PATCH 245/567] Fix unit label hover title for metric ton (#1150) --- app/setup.py | 7 ++++++- schemas/test/en/test_unit_patterns.json | 19 +++++++++++++++++++ tests/functional/spec/features/units.spec.js | 19 ++++++++++++++++--- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/setup.py b/app/setup.py index a4f9a6d64f..81f9df690c 100644 --- a/app/setup.py +++ b/app/setup.py @@ -36,6 +36,7 @@ from app.routes.schema import schema_blueprint from app.routes.session import session_blueprint from app.secrets import SecretStore, validate_required_secrets +from app.settings import DEFAULT_LOCALE from app.storage import Datastore, Dynamodb, Redis from app.submitter import ( GCSFeedbackSubmitter, @@ -479,7 +480,11 @@ def get_minimized_asset(filename): def get_locale(): - return cookie_session.get("language_code") + return ( + DEFAULT_LOCALE + if cookie_session.get("language_code") == "en" + else cookie_session.get("language_code") + ) def get_timezone(): diff --git a/schemas/test/en/test_unit_patterns.json b/schemas/test/en/test_unit_patterns.json index d54a8bca90..f3d6ed684f 100644 --- a/schemas/test/en/test_unit_patterns.json +++ b/schemas/test/en/test_unit_patterns.json @@ -243,6 +243,25 @@ "title": "Volume Units", "type": "General" } + }, + { + "type": "Question", + "id": "set-weight-units-block", + "question": { + "answers": [ + { + "id": "mass-metric-ton", + "label": "Mass tonnes", + "mandatory": false, + "type": "Unit", + "unit": "mass-metric-ton", + "unit_length": "short" + } + ], + "id": "set-weight-unit-questions", + "title": "Weight Units", + "type": "General" + } } ], "id": "test" diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index 6c3b60e5d6..0873bbf8fb 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -2,6 +2,7 @@ import SetLengthUnitsBlockPage from "../../generated_pages/unit_patterns/set-len import SetDurationUnitsBlockPage from "../../generated_pages/unit_patterns/set-duration-units-block.page.js"; import SetAreaUnitsBlockPage from "../../generated_pages/unit_patterns/set-area-units-block.page.js"; import SetVolumeUnitsBlockPage from "../../generated_pages/unit_patterns/set-volume-units-block.page.js"; +import SetWeightUnitsBlockPage from "../../generated_pages/unit_patterns/set-weight-units-block.page.js"; import SubmitPage from "../../generated_pages/unit_patterns/submit.page.js"; describe("Units", () => { @@ -15,6 +16,7 @@ describe("Units", () => { await $(SetDurationUnitsBlockPage.submit()).click(); await $(SetAreaUnitsBlockPage.submit()).click(); await $(SetVolumeUnitsBlockPage.submit()).click(); + await $(SetWeightUnitsBlockPage.submit()).click(); await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 hours"); await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 years"); }); @@ -31,15 +33,26 @@ describe("Units", () => { await $(SetDurationUnitsBlockPage.submit()).click(); await $(SetAreaUnitsBlockPage.submit()).click(); await $(SetVolumeUnitsBlockPage.submit()).click(); + await $(SetWeightUnitsBlockPage.submit()).click(); await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 awr"); await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 mlynedd"); }); it("Given we open a questionnaire with unit labels, when the label is highlighted by the tooltip, then the long unit label should be displayed.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - await expect(await $(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimeters"); - await expect(await $(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("meters"); - await expect(await $(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometers"); + await expect(await $(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimetres"); + await expect(await $(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("metres"); + await expect(await $(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometres"); await expect(await $(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).to.equal("miles"); }); + + it("Given we open a questionnaire with unit labels, when the weight unit label is highlighted by the tooltip, then the correct unit label should be displayed.", async () => { + await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); + await $(SetLengthUnitsBlockPage.submit()).click(); + await $(SetDurationUnitsBlockPage.submit()).click(); + await $(SetAreaUnitsBlockPage.submit()).click(); + await $(SetVolumeUnitsBlockPage.submit()).click(); + await expect(await $("body").getText()).to.have.string("tonnes"); + await expect(await $("body").getText()).to.not.have.string("metric tons"); + }); }); From f11fa2cb5d1bcd0766f41846a15b2689e856a416 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:42:27 +0100 Subject: [PATCH 246/567] Fix list collector placeholders not resolving when preview enabled (#1151) --- app/views/contexts/context.py | 3 ++- app/views/contexts/preview_context.py | 1 + app/views/contexts/section_preview_context.py | 1 + schemas/test/en/test_list_collector_list_summary.json | 5 +++++ tests/functional/spec/list_collector.spec.js | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index 9495e08c45..a0033abd94 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -20,6 +20,7 @@ def __init__( progress_store: ProgressStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, + placeholder_preview_mode: bool = False, ) -> None: self._language = language self._schema = schema @@ -28,7 +29,7 @@ def __init__( self._progress_store = progress_store self._metadata = metadata self._response_metadata = response_metadata - self._placeholder_preview_mode = self._schema.preview_enabled + self._placeholder_preview_mode = placeholder_preview_mode self._router = Router( schema=self._schema, diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py index 7e173250c6..057cbe73d6 100644 --- a/app/views/contexts/preview_context.py +++ b/app/views/contexts/preview_context.py @@ -35,6 +35,7 @@ def __init__( progress_store, metadata, response_metadata, + placeholder_preview_mode=True, ) def __call__(self) -> dict[str, Union[str, list, bool]]: diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py index 325897752c..5a139ff376 100644 --- a/app/views/contexts/section_preview_context.py +++ b/app/views/contexts/section_preview_context.py @@ -28,6 +28,7 @@ def __init__( progress_store, metadata, response_metadata, + placeholder_preview_mode=True, ) self._section_id = section_id diff --git a/schemas/test/en/test_list_collector_list_summary.json b/schemas/test/en/test_list_collector_list_summary.json index a7637c7bf7..82aae3b14d 100644 --- a/schemas/test/en/test_list_collector_list_summary.json +++ b/schemas/test/en/test_list_collector_list_summary.json @@ -5,6 +5,7 @@ "data_version": "0.0.3", "survey_id": "0", "title": "Test ListCollector", + "preview_questions": true, "theme": "default", "description": "A questionnaire to test ListCollector", "metadata": [ @@ -111,6 +112,10 @@ "id": "group", "title": "Questions", "blocks": [ + { + "id": "introduction", + "type": "Introduction" + }, { "id": "primary-person-list-collector", "type": "PrimaryPersonListCollector", diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index 8894a68f82..a3ea2ab759 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -19,6 +19,7 @@ import VisitorListCollectorPage from "../generated_pages/list_collector_list_sum import VisitorListCollectorAddPage from "../generated_pages/list_collector_list_summary/visitor-list-collector-add.page.js"; import PeopleListSectionSummaryPage from "../generated_pages/list_collector_list_summary/section-summary.page.js"; import { SubmitPage } from "../base_pages/submit.page.js"; +import IntroductionPage from "../generated_pages/list_collector_list_summary/introduction.page.js"; describe("List Collector", () => { describe("Given a normal journey through the list collector without variants", () => { @@ -172,6 +173,7 @@ describe("List Collector", () => { describe("Given I start a list collector survey and complete to Section Summary", () => { beforeEach(async () => { await browser.openQuestionnaire("test_list_collector_list_summary.json"); + await $(IntroductionPage.submit()).click(); await $(PrimaryPersonListCollectorPage.yes()).click(); await $(PrimaryPersonListCollectorPage.submit()).click(); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); From fa46ce75c790b66d331ba66f47b4b6ce659ec5ef Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 5 Jul 2023 09:13:13 +0100 Subject: [PATCH 247/567] Schemas v3.63.0 (#1155) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5cd3892448..7b705cd7be 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.62.0 +v3.63.0 From 4d8f8f564f273939ad01dd8faec36b497164c1df Mon Sep 17 00:00:00 2001 From: Kyle Lawson_ONS <108286538+kylelawsonAND@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:09:47 +0100 Subject: [PATCH 248/567] Bug Fix: None language code defaults to "en" (#1159) * Defaulting cookie_session language code to DEFAULT_LOCALE if none is provided in claims. --- app/routes/session.py | 3 ++- tests/integration/create_token.py | 8 +++++++ .../test_questionnaire_locale.py | 21 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/integration/questionnaire/test_questionnaire_locale.py diff --git a/app/routes/session.py b/app/routes/session.py index 56c2653a69..3a51b380b3 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -22,6 +22,7 @@ render_template, ) from app.questionnaire import QuestionnaireSchema +from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.routes.errors import _render_error_page from app.utilities.metadata_parser import validate_runner_claims from app.utilities.metadata_parser_v2 import ( @@ -126,7 +127,7 @@ def login() -> Response: "account_service_log_out_url" ) - cookie_session["language_code"] = metadata.language_code + cookie_session["language_code"] = metadata.language_code or DEFAULT_LANGUAGE_CODE return redirect(url_for("questionnaire.get_questionnaire")) diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index c611ad9918..8d5ae365ef 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -163,5 +163,13 @@ def create_token_with_schema_url(self, schema_name, schema_url, **extra_payload) return self.generate_token(payload_vars) + def create_token_with_none_language_code(self, schema_name, **extra_payload): + payload_vars = self._get_payload_with_params( + schema_name=schema_name, **extra_payload + ) + del payload_vars["language_code"] + + return self.generate_token(payload_vars) + def generate_token(self, payload): return encrypt(payload, self._key_store, KEY_PURPOSE_AUTHENTICATION) diff --git a/tests/integration/questionnaire/test_questionnaire_locale.py b/tests/integration/questionnaire/test_questionnaire_locale.py new file mode 100644 index 0000000000..aa8075bc40 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_locale.py @@ -0,0 +1,21 @@ +from tests.integration.integration_test_case import IntegrationTestCase + + +class TestSession(IntegrationTestCase): + def test_none_language_code_defaults_to_en(self): + # Given a questionnaire with an answer of type mass-metric-tonnes launches with no language code in the runner claims + token = self.token_generator.create_token_with_none_language_code( + "test_unit_patterns" + ) + self.get(url=f"/session?token={token}") + + # Skip to the mass-metric-tonnes answer + self.post() + self.post() + self.post() + self.post() + + # Then the cookie_session[language_code] will have defaulted to DEFAULT_LANGUAGE_CODE (en), and the tooltip will show "tonnes" not "metric tons" + self.assertInBody("Weight Units") + self.assertInSelector("tonnes", "[id='mass-metric-ton-type']") + self.assertNotInSelector("metric tons", "[id='mass-metric-ton-type']") From 0d53ddf9095de17aca2a406e6888c7305a22b3fd Mon Sep 17 00:00:00 2001 From: Michael Donaghey_ONS <111740109+mgdon650@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:04:46 +0100 Subject: [PATCH 249/567] Squashed commit of the following: (#1132) commit af4ec68194eef3e763e787a96eed6fbe88ce12c9 Author: Michael Donaghey Date: Thu Jul 6 17:11:51 2023 +0100 clean up commit 865dcd4ba8f0be1803107f931ac5e75fab4b8731 Author: Michael Donaghey Date: Thu Jul 6 15:54:29 2023 +0100 clean up commit f92a74d2c9c4c7f382e614a5144a53d09e74ef61 Merge: 46ee670b3 fa46ce75c Author: Michael Donaghey Date: Wed Jul 5 15:56:23 2023 +0100 merging main commit 46ee670b31aa4f657f6314e560982ca32d2ce177 Author: Michael Donaghey Date: Wed Jul 5 15:53:29 2023 +0100 resolving review comments commit c46876ea147fb2e0bb6aa46e3f6e124a21982adf Author: Michael Donaghey Date: Mon Jun 26 15:17:30 2023 +0100 linting and small changes commit ec2c39c5e506ba98cffcacd95dcea877f7d1df91 Merge: 4efd5e697 d2dc26a15 Author: Michael Donaghey Date: Mon Jun 26 15:10:39 2023 +0100 merging main commit 4efd5e697609a2b4e5b7a1f43dfc00fdcf3d63b3 Author: Michael Donaghey Date: Mon Jun 26 15:06:39 2023 +0100 resolving comments commit 4e62982571721ab7506077e9fdd17c1490da0aaf Merge: bf199318f 16bd27d03 Author: Michael Donaghey Date: Thu Jun 22 15:42:10 2023 +0100 Merge remote-tracking branch 'origin/mick-type-hinting' into mick-type-hinting commit bf199318f84fbd01653a1cde33b29faa99e2da52 Author: Michael Donaghey Date: Thu Jun 22 15:32:41 2023 +0100 linting commit 16bd27d0340132d3cfedb2598cd535884dcb6d2b Merge: 10e3c1707 c0bb6bbd9 Author: Michael Donaghey_ONS <111740109+mgdon650@users.noreply.github.com> Date: Thu Jun 22 14:52:00 2023 +0100 Merge branch 'main' into mick-type-hinting commit 03f0d655f46f9afef05ef4c6c89c5abca2cee755 Merge: 10e3c1707 c0bb6bbd9 Author: Michael Donaghey Date: Thu Jun 22 14:49:33 2023 +0100 Merge branch 'main' into mick-type-hinting commit 10e3c170785d400aef4bf8ca5eaa5c1ab1a045bd Author: Michael Donaghey Date: Thu Jun 22 14:48:20 2023 +0100 additional changes. still to merge main commit 3f042bd925874738d6a8799ef8e17a104169692a Author: Michael Donaghey Date: Wed Jun 21 13:51:07 2023 +0100 updated some more errors commit c50e64971efeb2a4919332e1c69395d6b0a249d1 Author: Michael Donaghey Date: Wed Jun 14 14:51:43 2023 +0100 linting and additional type hints following merge commit 08b16e5711cb6679f5f1b755cd0d37482d9ee3eb Merge: 417f7fafd d04325cda Author: Michael Donaghey Date: Wed Jun 14 14:11:21 2023 +0100 merging main commit 417f7fafd3c6191b60816d31aa5ae31d97998dfb Author: Michael Donaghey Date: Wed Jun 14 13:52:51 2023 +0100 resolve comments. add hints to relationships commit 3bbc02087d6d39ff482538fbb4bcb360b05af487 Author: Michael Donaghey Date: Thu Jun 8 09:26:58 2023 +0100 Type hinting for app/views/handlers --- app/data_models/list_store.py | 10 +- app/data_models/progress_store.py | 5 +- app/forms/questionnaire_form.py | 20 +-- app/questionnaire/dependencies.py | 6 +- app/questionnaire/path_finder.py | 11 +- app/questionnaire/placeholder_parser.py | 6 +- app/questionnaire/placeholder_renderer.py | 6 +- .../questionnaire_store_updater.py | 7 +- app/questionnaire/router.py | 24 ++-- app/questionnaire/rules/rule_evaluator.py | 6 +- app/questionnaire/value_source_resolver.py | 6 +- app/questionnaire/variants.py | 11 +- app/utilities/types.py | 6 + .../contexts/calculated_summary_context.py | 4 +- app/views/contexts/confirm_email_context.py | 4 +- app/views/contexts/list_context.py | 2 +- app/views/contexts/question.py | 8 +- app/views/contexts/section_summary_context.py | 4 +- app/views/contexts/summary/block.py | 7 +- .../summary/calculated_summary_block.py | 5 +- app/views/contexts/summary/group.py | 7 +- .../contexts/summary/list_collector_block.py | 3 +- app/views/contexts/summary/question.py | 5 +- app/views/contexts/thank_you_context.py | 12 +- app/views/handlers/__init__.py | 8 +- app/views/handlers/block.py | 44 ++++--- app/views/handlers/block_factory.py | 4 +- app/views/handlers/calculation_summary.py | 4 +- app/views/handlers/confirm_email.py | 51 +++++--- app/views/handlers/confirmation_email.py | 24 ++-- app/views/handlers/content.py | 15 ++- app/views/handlers/individual_response.py | 119 +++++++++++------- app/views/handlers/list_action.py | 48 +++---- app/views/handlers/list_add_question.py | 13 +- app/views/handlers/list_collector.py | 12 +- app/views/handlers/list_edit_question.py | 7 +- app/views/handlers/list_remove_question.py | 26 ++-- .../handlers/primary_person_list_collector.py | 10 +- app/views/handlers/primary_person_question.py | 23 ++-- app/views/handlers/question.py | 52 ++++---- .../relationships/relationship_collector.py | 28 +++-- .../relationships/relationship_question.py | 63 ++++++---- .../relationships/unrelated_question.py | 36 +++--- app/views/handlers/submission.py | 35 ++++-- app/views/handlers/submit_questionnaire.py | 5 +- app/views/handlers/thank_you.py | 14 ++- mypy.ini | 28 +---- 47 files changed, 486 insertions(+), 368 deletions(-) create mode 100644 app/utilities/types.py diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 7ecf36de7f..ecfdcd22ec 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -3,7 +3,7 @@ import random from functools import cached_property from string import ascii_letters -from typing import Iterable, Iterator, Optional, TypedDict +from typing import Iterable, Iterator, Optional, TypedDict, overload from structlog import get_logger @@ -44,7 +44,15 @@ def __eq__(self, other: object) -> bool: def __iter__(self) -> Iterator[str]: yield from self.items + @overload def __getitem__(self, list_item_index: int) -> str: + ... # pragma: no cover + + @overload + def __getitem__(self, list_item_index: slice) -> list[str]: + ... # pragma: no cover + + def __getitem__(self, list_item_index: slice | int) -> str | list[str]: return self.items[list_item_index] def __len__(self) -> int: diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index ae5a286d6a..8c317703c0 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -3,6 +3,7 @@ from app.data_models.progress import Progress, ProgressDictType from app.questionnaire.location import Location +from app.utilities.types import LocationType SectionKeyType = tuple[str, Optional[str]] @@ -160,7 +161,7 @@ def get_completed_block_ids( return [] - def add_completed_location(self, location: Location) -> None: + def add_completed_location(self, location: LocationType) -> None: section_id = location.section_id list_item_id = location.list_item_id @@ -185,7 +186,7 @@ def add_completed_location(self, location: Location) -> None: self._is_dirty = True - def remove_completed_location(self, location: Location) -> bool: + def remove_completed_location(self, location: LocationType) -> bool: section_key = (location.section_id, location.list_item_id) if ( section_key in self._progress diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 9acd9d5ed2..ef7922ddf0 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -26,6 +26,7 @@ from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.utilities.mappings import get_flattened_mapping_values +from app.utilities.types import LocationType logger = logging.getLogger(__name__) @@ -456,7 +457,7 @@ def get_answer_fields( list_store: ListStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, - location: Location | RelationshipLocation | None, + location: LocationType | None, progress_store: ProgressStore, ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None @@ -584,20 +585,23 @@ def _get_error_id(id_: str) -> str: def _clear_detail_answer_field( - form_data: MultiDict, question_schema: QuestionSchemaType + form_data: ImmutableMultiDict | MultiDict, question_schema: QuestionSchemaType ) -> MultiDict[str, Any]: """ Clears the detail answer field if the parent option is not selected """ + mutable_form_data = ( + MultiDict(form_data) if isinstance(form_data, ImmutableMultiDict) else form_data + ) + for answer in question_schema.get("answers", []): for option in answer.get("options", []): if "detail_answer" in option and option["value"] not in form_data.getlist( answer["id"] ): - if isinstance(form_data, ImmutableMultiDict): - form_data = MultiDict(form_data) - form_data[option["detail_answer"]["id"]] = "" - return form_data + mutable_form_data[option["detail_answer"]["id"]] = "" + + return mutable_form_data def generate_form( @@ -608,9 +612,9 @@ def generate_form( metadata: MetadataProxy | None, response_metadata: MutableMapping, progress_store: ProgressStore, - location: Location | RelationshipLocation | None = None, + location: LocationType | None = None, data: dict[str, Any] | None = None, - form_data: MultiDict[str, Any] | None = None, + form_data: MultiDict | None = None, ) -> QuestionnaireForm: class DynamicForm(QuestionnaireForm): pass diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index c79ecc1009..b3d9ad5d1a 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -6,10 +6,10 @@ from werkzeug.datastructures import MultiDict from app.data_models import ProgressStore -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.questionnaire_schema import get_sources_for_type_from_data -from app.questionnaire.relationship_location import RelationshipLocation from app.utilities.mappings import get_flattened_mapping_values +from app.utilities.types import LocationType if TYPE_CHECKING: from app.questionnaire.path_finder import PathFinder # pragma: no cover @@ -17,7 +17,7 @@ def get_block_ids_for_calculated_summary_dependencies( schema: QuestionnaireSchema, - location: Location | RelationshipLocation, + location: LocationType, progress_store: ProgressStore, path_finder: PathFinder, data: MultiDict | Mapping | Sequence, diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index f09a19a247..89c50b18df 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -10,6 +10,7 @@ from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator, RuleEvaluatorTypes +from app.utilities.types import LocationType class PathFinder: @@ -77,7 +78,7 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: def _get_not_skipped_blocks_in_section( self, - location: Location, + current_location: LocationType, routing_path_block_ids: list[str], section: ImmutableDict, when_rules_block_dependencies: list[str], @@ -89,7 +90,7 @@ def _get_not_skipped_blocks_in_section( if "skip_conditions" in group: skip_conditions = group.get("skip_conditions") if self.evaluate_skip_conditions( - location, + current_location, routing_path_block_ids, skip_conditions, when_rules_block_dependencies, @@ -111,7 +112,7 @@ def _block_index_for_block_id( def _build_routing_path_block_ids( self, blocks: Sequence[Mapping], - current_location: Location, + current_location: LocationType, when_rules_block_dependencies: list[str], ) -> list[str]: # Keep going unless we've hit the last block @@ -230,7 +231,7 @@ def _evaluate_routing_rules( def evaluate_skip_conditions( self, - this_location: Location, + current_location: LocationType, routing_path_block_ids: list[str], skip_conditions: ImmutableDict[str, dict] | None, when_rules_block_dependencies: list[str], @@ -250,7 +251,7 @@ def evaluate_skip_conditions( metadata=self.metadata, response_metadata=self.response_metadata, progress_store=self.progress_store, - location=this_location, + location=current_location, routing_path_block_ids=routing_path_block_ids, ) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 8c2ef0d43d..72e54a49b3 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -9,19 +9,19 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire import path_finder as pf from app.questionnaire.dependencies import ( get_block_ids_for_calculated_summary_dependencies, ) from app.questionnaire.placeholder_transforms import PlaceholderTransforms -from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.value_source_resolver import ( ValueSourceEscapedTypes, ValueSourceResolver, ValueSourceTypes, ) from app.utilities.mappings import get_flattened_mapping_values +from app.utilities.types import LocationType if TYPE_CHECKING: from app.questionnaire.placeholder_renderer import ( @@ -48,7 +48,7 @@ def __init__( renderer: PlaceholderRenderer, progress_store: ProgressStore, list_item_id: str | None = None, - location: Location | RelationshipLocation | None = None, + location: LocationType | None = None, placeholder_preview_mode: bool | None = False, ): self._transformer = PlaceholderTransforms(language, schema, renderer) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 1309c52a56..f9fbd21ec9 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -8,11 +8,11 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.plural_forms import get_plural_form_key -from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.schema_utils import find_pointers_containing +from app.utilities.types import LocationType class PlaceholderRenderer: @@ -30,7 +30,7 @@ def __init__( response_metadata: MutableMapping, schema: QuestionnaireSchema, progress_store: ProgressStore, - location: Location | RelationshipLocation | None = None, + location: LocationType | None = None, placeholder_preview_mode: bool | None = False, ): self._placeholder_preview_mode = placeholder_preview_mode diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 2e9b80fa41..52143136c2 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -13,6 +13,7 @@ from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent from app.questionnaire.router import Router +from app.utilities.types import LocationType DependentSection = namedtuple("DependentSection", "section_id list_item_id is_complete") @@ -24,7 +25,7 @@ class QuestionnaireStoreUpdater: def __init__( self, - current_location: Location, + current_location: LocationType, schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, router: Router, @@ -228,12 +229,12 @@ def _remove_relationship_answers_for_list_item_id( answer.value = answers_to_keep self._answer_store.add_or_update(answer) - def add_completed_location(self, location: Location | None = None) -> None: + def add_completed_location(self, location: LocationType | None = None) -> None: if not self._progress_store.is_routing_backwards: location = location or self._current_location self._progress_store.add_completed_location(location) - def remove_completed_location(self, location: Location | None = None) -> bool: + def remove_completed_location(self, location: LocationType | None = None) -> bool: location = location or self._current_location return self._progress_store.remove_completed_location(location) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 23a672c86d..685c5fbd2e 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -10,6 +10,7 @@ from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.utilities.types import LocationType class Router: @@ -74,7 +75,7 @@ def is_list_item_in_list_store(self, list_item_id: str, list_name: str) -> bool: return list_item_id in self._list_store[list_name] def can_access_location( - self, location: Location, routing_path: RoutingPath + self, location: LocationType, routing_path: RoutingPath ) -> bool: """ Checks whether the location is valid and accessible. @@ -115,7 +116,7 @@ def routing_path( def get_next_location_url( self, - location: Location, + location: LocationType, routing_path: RoutingPath, return_to: str | None = None, return_to_answer_id: str | None = None, @@ -158,7 +159,9 @@ def get_next_location_url( return_to_block_id=return_to_block_id, ) - def _get_next_location_url_for_complete_section(self, location: Location) -> str: + def _get_next_location_url_for_complete_section( + self, location: LocationType + ) -> str: if self._schema.show_summary_on_completion_for_section(location.section_id): return self._get_section_url(location) @@ -166,7 +169,7 @@ def _get_next_location_url_for_complete_section(self, location: Location) -> str def get_previous_location_url( self, - location: Location, + location: LocationType, routing_path: RoutingPath, return_to: str | None = None, return_to_answer_id: str | None = None, @@ -216,7 +219,7 @@ def get_previous_location_url( def _get_return_to_location_url( self, - location: Location, + location: LocationType, return_to: str | None, routing_path: RoutingPath, is_for_previous: bool, @@ -273,7 +276,7 @@ def _get_return_to_for_grand_calculated_summary( *, return_to: str | None, return_to_block_id: str | None, - location: Location, + location: LocationType, routing_path: RoutingPath, is_for_previous: bool, return_to_answer_id: str | None = None, @@ -327,7 +330,7 @@ def _get_return_to_for_calculated_summary( *, return_to: str, return_to_block_id: str | None, - location: Location, + location: LocationType, routing_path: RoutingPath, is_for_previous: bool, return_to_answer_id: str | None = None, @@ -552,7 +555,9 @@ def _is_section_enabled(self, section: Mapping) -> bool: @staticmethod def get_next_block_url( - location: Location, routing_path: RoutingPath, **kwargs: str | None + location: LocationType, + routing_path: RoutingPath, + **kwargs: str | None, ) -> str: # Type ignore: the location will have a block next_block_id = routing_path[routing_path.index(location.block_id) + 1] # type: ignore @@ -567,7 +572,8 @@ def get_next_block_url( @staticmethod def _get_section_url( - location: Location, return_to_answer_id: str | None = None + location: LocationType, + return_to_answer_id: str | None = None, ) -> str: return url_for( "questionnaire.get_section", diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 241d3fa6bf..940a82ff2e 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -5,16 +5,16 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE -from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.operations import Operations from app.questionnaire.rules.operator import Operator from app.questionnaire.value_source_resolver import ( ValueSourceResolver, ValueSourceTypes, ) +from app.utilities.types import LocationType RuleEvaluatorTypes = Union[ bool, Optional[date], list[str], list[date], int, float, Decimal @@ -28,7 +28,7 @@ class RuleEvaluator: list_store: ListStore metadata: MetadataProxy | None response_metadata: MutableMapping - location: Location | RelationshipLocation | None + location: LocationType | None progress_store: ProgressStore routing_path_block_ids: Iterable | None = None language: str = DEFAULT_LANGUAGE_CODE diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index adfc8f2384..faa83774e1 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -10,10 +10,10 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListModel, ListStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException -from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules import rule_evaluator +from app.utilities.types import LocationType ValueSourceTypes = None | str | int | Decimal | list ValueSourceEscapedTypes = Markup | list[Markup] @@ -27,7 +27,7 @@ class ValueSourceResolver: metadata: MetadataProxy | None response_metadata: MutableMapping schema: QuestionnaireSchema - location: Location | RelationshipLocation | None + location: LocationType | None list_item_id: str | None progress_store: ProgressStore routing_path_block_ids: Iterable[str] | None = None diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index 92a2c7f62f..1cdfaac3ed 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -6,10 +6,9 @@ from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore -from app.questionnaire import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema -from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.utilities.types import LocationType # Type ignore: validation should ensure the variant exists when this is called @@ -22,7 +21,7 @@ def choose_variant( # type: ignore list_store: ListStore, variants_key: str, single_key: str, - current_location: Location | RelationshipLocation, + current_location: LocationType, progress_store: ProgressStore, ) -> dict: if block.get(single_key): @@ -53,7 +52,7 @@ def choose_question_to_display( response_metadata: MutableMapping, answer_store: AnswerStore, list_store: ListStore, - current_location: Location | RelationshipLocation, + current_location: LocationType, progress_store: ProgressStore, ) -> dict: return choose_variant( @@ -77,7 +76,7 @@ def choose_content_to_display( response_metadata: MutableMapping, answer_store: AnswerStore, list_store: ListStore, - current_location: Location | RelationshipLocation, + current_location: LocationType, progress_store: ProgressStore, ) -> dict: return choose_variant( @@ -101,7 +100,7 @@ def transform_variants( response_metadata: MutableMapping, answer_store: AnswerStore, list_store: ListStore, - current_location: Location | RelationshipLocation, + current_location: LocationType, progress_store: ProgressStore, ) -> ImmutableDict: output_block = dict(block) diff --git a/app/utilities/types.py b/app/utilities/types.py new file mode 100644 index 0000000000..9217bb6207 --- /dev/null +++ b/app/utilities/types.py @@ -0,0 +1,6 @@ +from typing import TypeAlias + +from app.questionnaire.location import Location +from app.questionnaire.relationship_location import RelationshipLocation + +LocationType: TypeAlias = Location | RelationshipLocation diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index b82822a919..f74f45d1c6 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -10,7 +10,6 @@ format_unit, get_formatted_currency, ) -from app.questionnaire import Location from app.questionnaire.questionnaire_schema import ( QuestionnaireSchema, get_calculated_summary_answer_ids, @@ -20,6 +19,7 @@ from app.questionnaire.schema_utils import get_answer_ids_in_block from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_question_to_display, transform_variants +from app.utilities.types import LocationType from app.views.contexts.context import Context from app.views.contexts.summary.calculated_summary_block import NumericType from app.views.contexts.summary.group import Group @@ -36,7 +36,7 @@ def __init__( metadata: MetadataProxy | None, response_metadata: MutableMapping, routing_path: RoutingPath, - current_location: Location, + current_location: LocationType, return_to: str | None = None, return_to_block_id: str | None = None, ) -> None: diff --git a/app/views/contexts/confirm_email_context.py b/app/views/contexts/confirm_email_context.py index 9ab59a4af5..489ae5cafe 100644 --- a/app/views/contexts/confirm_email_context.py +++ b/app/views/contexts/confirm_email_context.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Mapping from flask import url_for @@ -9,7 +9,7 @@ def build_confirm_email_context( question_schema: QuestionSchemaType, form: QuestionnaireForm -) -> dict[str, Union[str, bool, dict]]: +) -> dict[str, Mapping]: block = {"question": question_schema} context = build_question_context(block, form) context["hide_sign_out_button"] = False diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index aa654f3fb9..7130b16786 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -17,7 +17,7 @@ def __call__( remove_block_id: Optional[str] = None, primary_person_edit_block_id: Optional[str] = None, for_list_item_ids: Optional[Sequence[str]] = None, - ) -> dict[str, Any]: + ) -> dict[str, dict]: list_items = ( list( self._build_list_items_context( diff --git a/app/views/contexts/question.py b/app/views/contexts/question.py index 5baae46543..0be2ad4c4f 100644 --- a/app/views/contexts/question.py +++ b/app/views/contexts/question.py @@ -1,15 +1,13 @@ -from typing import Any - from app.forms.questionnaire_form import QuestionnaireForm from app.questionnaire import QuestionSchemaType def build_question_context( rendered_block: dict[str, QuestionSchemaType], form: QuestionnaireForm -) -> dict[str, Any]: +) -> dict: question = rendered_block["question"] - context = { + context: dict[str, dict] = { "block": rendered_block, "form": { "errors": form.errors, @@ -20,7 +18,7 @@ def build_question_context( }, } - answer_ids = [] + answer_ids: list[str] = [] for answer in question["answers"]: answer_ids.append(answer["id"]) diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 06c4fc392e..135a514c75 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -5,11 +5,11 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.questionnaire import QuestionnaireSchema -from app.questionnaire.location import Location from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content from ...data_models.metadata_proxy import MetadataProxy +from ...utilities.types import LocationType from .context import Context from .summary import Group from .summary.list_collector_block import ListCollectorBlock @@ -26,7 +26,7 @@ def __init__( metadata: Optional[MetadataProxy], response_metadata: MutableMapping, routing_path: RoutingPath, - current_location: Location, + current_location: LocationType, ) -> None: super().__init__( language, diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 13f271a0c4..2a373cd292 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -2,10 +2,11 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_variant +from app.utilities.types import LocationType from app.views.contexts.summary.question import Question @@ -19,7 +20,7 @@ def __init__( metadata: Optional[MetadataProxy], response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, return_to: Optional[str], return_to_block_id: Optional[str] = None, progress_store: ProgressStore, @@ -74,7 +75,7 @@ def get_question( metadata: Optional[MetadataProxy], response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, return_to: Optional[str], return_to_block_id: Optional[str], progress_store: ProgressStore, diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py index 8ba998fe74..07808dae50 100644 --- a/app/views/contexts/summary/calculated_summary_block.py +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -5,8 +5,9 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.utilities.types import LocationType NumericType: TypeAlias = int | float | Decimal @@ -21,7 +22,7 @@ def __init__( metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, return_to: str | None, return_to_block_id: str | None = None, progress_store: ProgressStore, diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 01be23b42c..19a5f9aeff 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -4,9 +4,10 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.survey_config.link import Link +from app.utilities.types import LocationType from app.views.contexts.summary.block import Block from app.views.contexts.summary.calculated_summary_block import CalculatedSummaryBlock from app.views.contexts.summary.list_collector_block import ListCollectorBlock @@ -23,7 +24,7 @@ def __init__( metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, language: str, progress_store: ProgressStore, return_to: str | None, @@ -77,7 +78,7 @@ def _build_blocks_and_links( metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, return_to: str | None, progress_store: ProgressStore, language: str, diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 2a6a980757..7f5905f6af 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -9,6 +9,7 @@ from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.utilities.types import LocationType from app.views.contexts.list_context import ListContext from app.views.contexts.summary.block import Block @@ -23,7 +24,7 @@ def __init__( metadata: Optional[MetadataProxy], response_metadata: MutableMapping, schema: QuestionnaireSchema, - location: Location, + location: LocationType, language: str, ) -> None: self._location = location diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 0f16e95e0d..ac9724b8b7 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -8,10 +8,11 @@ from app.data_models.answer import AnswerValueEscapedTypes, escape_answer_value from app.data_models.metadata_proxy import MetadataProxy from app.forms.field_handlers.select_handlers import DynamicAnswerOptions -from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType +from app.questionnaire import QuestionnaireSchema, QuestionSchemaType from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver +from app.utilities.types import LocationType from app.views.contexts.summary.answer import ( Answer, InferredAnswerValueTypes, @@ -31,7 +32,7 @@ def __init__( schema: QuestionnaireSchema, rule_evaluator: RuleEvaluator, value_source_resolver: ValueSourceResolver, - location: Location, + location: LocationType, block_id: str, return_to: Optional[str], return_to_block_id: Optional[str] = None, diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index bf7feb2dc3..6fffb9ab82 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Optional, Union +from typing import Any, Optional from flask import url_for from flask_babel import lazy_gettext @@ -39,7 +39,9 @@ def build_thank_you_context( else: submission_text = lazy_gettext("Your answers have been submitted.") context_metadata = build_submission_metadata_context( - survey_type, submitted_at, metadata.tx_id + survey_type, + submitted_at, + metadata.tx_id, ) return { "hide_sign_out_button": True, @@ -54,8 +56,8 @@ def build_thank_you_context( def build_view_submitted_response_context( schema: QuestionnaireSchema, submitted_at: datetime -) -> dict[str, Union[bool, str]]: - view_submitted_response: dict[str, Union[bool, str]] = { +) -> dict[str, bool | str]: + view_submitted_response: dict[str, bool | str] = { "enabled": schema.is_view_submitted_response_enabled } @@ -78,7 +80,7 @@ def build_census_thank_you_context( metadata: MetadataProxy, confirmation_email_form: Optional[EmailForm], form_type: str, -) -> dict[str, Union[bool, str, None]]: +) -> dict[str, bool | str | None]: context = { "display_address": metadata["display_address"], "form_type": form_type, diff --git a/app/views/handlers/__init__.py b/app/views/handlers/__init__.py index 13e41d3c8d..493419cb39 100644 --- a/app/views/handlers/__init__.py +++ b/app/views/handlers/__init__.py @@ -1,5 +1,3 @@ -from typing import Optional, Union - from flask import url_for from app.data_models import QuestionnaireStore @@ -19,11 +17,11 @@ def individual_response_url( - individual_response_for_list: str, + individual_response_for_list: str | None, list_item_id: str, questionnaire_store: QuestionnaireStore, - journey: Optional[str] = None, -) -> Union[str, None]: + journey: str | None = None, +) -> str | None: if individual_response_for_list: if ( list_item_id diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 1495831744..ecad1ba18d 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -1,9 +1,9 @@ from datetime import datetime, timezone from functools import cached_property -from typing import MutableMapping, Optional, Union +from typing import Mapping, MutableMapping, Optional, Union from structlog import get_logger -from werkzeug.datastructures import ImmutableDict +from werkzeug.datastructures import ImmutableDict, ImmutableMultiDict from app.data_models import QuestionnaireStore from app.questionnaire.location import InvalidLocationException, Location @@ -12,7 +12,9 @@ from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.router import Router +from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content +from app.utilities.types import LocationType logger = get_logger() @@ -23,9 +25,9 @@ def __init__( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, language: str, - current_location: Union[Location, RelationshipLocation], + current_location: LocationType, request_args: MutableMapping, - form_data: MutableMapping, + form_data: ImmutableMultiDict, ): self._schema = schema self._questionnaire_store = questionnaire_store @@ -50,19 +52,19 @@ def __init__( ) @property - def current_location(self): + def current_location(self) -> LocationType: return self._current_location @property - def return_to(self): + def return_to(self) -> str | None: return self._return_to @property - def return_to_block_id(self): + def return_to_block_id(self) -> str | None: return self._return_to_block_id @cached_property - def questionnaire_store_updater(self): + def questionnaire_store_updater(self) -> QuestionnaireStoreUpdater: return QuestionnaireStoreUpdater( self._current_location, self._schema, @@ -72,7 +74,7 @@ def questionnaire_store_updater(self): ) @cached_property - def placeholder_renderer(self): + def placeholder_renderer(self) -> PlaceholderRenderer: return PlaceholderRenderer( self._language, answer_store=self._questionnaire_store.answer_store, @@ -85,7 +87,7 @@ def placeholder_renderer(self): ) @cached_property - def router(self): + def router(self) -> Router: return Router( schema=self._schema, answer_store=self._questionnaire_store.answer_store, @@ -95,12 +97,12 @@ def router(self): response_metadata=self._questionnaire_store.response_metadata, ) - def is_location_valid(self): + def is_location_valid(self) -> bool: return self.router.can_access_location( self._current_location, self._routing_path ) - def get_previous_location_url(self): + def get_previous_location_url(self) -> str | None: return self.router.get_previous_location_url( self._current_location, self._routing_path, @@ -109,7 +111,7 @@ def get_previous_location_url(self): self._return_to_block_id, ) - def get_next_location_url(self): + def get_next_location_url(self) -> str: return self.router.get_next_location_url( self._current_location, self._routing_path, @@ -118,7 +120,7 @@ def get_next_location_url(self): self._return_to_block_id, ) - def handle_post(self): + def handle_post(self) -> None: self._set_started_at_metadata() self.questionnaire_store_updater.add_completed_location() self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() @@ -126,7 +128,7 @@ def handle_post(self): self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() - def _get_routing_path(self): + def _get_routing_path(self) -> RoutingPath: return self.router.routing_path( section_id=self._current_location.section_id, list_item_id=self._current_location.list_item_id, @@ -134,7 +136,7 @@ def _get_routing_path(self): def _update_section_completeness( self, location: Optional[Union[Location, RelationshipLocation]] = None - ): + ) -> None: location = location or self._current_location self.questionnaire_store_updater.update_section_status( @@ -143,24 +145,26 @@ def _update_section_completeness( list_item_id=location.list_item_id, ) - def _set_started_at_metadata(self): + def _set_started_at_metadata(self) -> str | None: response_metadata = self._questionnaire_store.response_metadata if not response_metadata.get("started_at"): started_at = datetime.now(timezone.utc).isoformat() logger.info("Survey started", started_at=started_at) response_metadata["started_at"] = started_at - def _get_safe_page_title(self, page_title): + def _get_safe_page_title(self, page_title: Mapping | str) -> str: page_title = self._schema.get_single_string_value(page_title) return safe_content(page_title) def _resolve_custom_page_title_vars(self) -> MutableMapping: + # Type ignore: list_item_id and list_name are populated at this stage list_item_position = self._questionnaire_store.list_store.list_item_position( - self.current_location.list_name, self.current_location.list_item_id + self.current_location.list_name, # type: ignore + self.current_location.list_item_id, # type: ignore ) return {"list_item_position": list_item_position} - def _set_page_title(self, page_title: Optional[str]): + def _set_page_title(self, page_title: str | None) -> str | None: if not page_title: return None diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 7961432d15..6c3a4c03ff 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -1,3 +1,5 @@ +from typing import Any + from werkzeug.datastructures import ImmutableMultiDict, MultiDict from app.data_models import QuestionnaireStore @@ -47,7 +49,7 @@ def get_block_handler( to_list_item_id: str | None = None, request_args: MultiDict[str, str] | None = None, form_data: ImmutableMultiDict[str, str] | None = None, -): +) -> Any: block = schema.get_block(block_id) if not block: diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 852ce7a3b2..54df21bb41 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -8,7 +8,7 @@ class _SummaryWithCalculation(Content): summary_class: Type[CalculatedSummaryContext] | Type[GrandCalculatedSummaryContext] - def get_context(self): + def get_context(self) -> dict[str, dict]: summary_context = self.summary_class( language=self._language, schema=self._schema, @@ -29,7 +29,7 @@ def get_context(self): return context - def handle_post(self): + def handle_post(self) -> None: # We prematurely set the current as complete, so that dependent sections can be updated accordingly self.questionnaire_store_updater.add_completed_location() # Then we update dependent sections diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index c08330feeb..aa6f2ea45d 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -4,10 +4,16 @@ from flask import current_app, url_for from flask_babel import gettext, lazy_gettext +from google.cloud.tasks_v2.types.task import Task from itsdangerous import BadSignature from markupsafe import escape +from werkzeug.datastructures import MultiDict from werkzeug.exceptions import BadRequest +from app.cloud_tasks.cloud_task_publishers import ( + CloudTaskPublisher, + LogCloudTaskPublisher, +) from app.cloud_tasks.exceptions import CloudTaskCreationFailed from app.data_models import ( FulfilmentRequest, @@ -16,7 +22,7 @@ SessionStore, ) from app.data_models.metadata_proxy import MetadataProxy -from app.forms.questionnaire_form import generate_form +from app.forms.questionnaire_form import QuestionnaireForm, generate_form from app.helpers import url_safe_serializer from app.questionnaire import QuestionnaireSchema, QuestionSchemaType from app.settings import ( @@ -35,7 +41,7 @@ class ConfirmationEmailFulfilmentRequestPublicationFailed(Exception): pass -CONFIRM_EMAIL_YES_VALUE = lazy_gettext("Yes, send the confirmation email") +CONFIRM_EMAIL_YES_VALUE: str = lazy_gettext("Yes, send the confirmation email") class ConfirmEmail: @@ -44,8 +50,8 @@ def __init__( questionnaire_store: QuestionnaireStore, schema: QuestionnaireSchema, session_store: SessionStore, - serialized_email, - form_data: Mapping, + serialized_email: str, + form_data: MultiDict, ): if not ConfirmationEmail.is_enabled(schema): raise ConfirmationEmailNotEnabled @@ -65,18 +71,17 @@ def __init__( self._session_store = session_store self._form_data = form_data self._email = email - self.page_title = lazy_gettext("Confirm your email address") + self.page_title: str = lazy_gettext("Confirm your email address") @cached_property - def form(self): + def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=self.question_schema, answer_store=self._questionnaire_store.answer_store, list_store=self._questionnaire_store.list_store, metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.metadata, - data=None, + response_metadata=self._questionnaire_store.response_metadata, form_data=self._form_data, progress_store=self._questionnaire_store.progress_store, ) @@ -108,13 +113,13 @@ def question_schema(self) -> QuestionSchemaType: } @cached_property - def is_email_correct(self): + def is_email_correct(self) -> bool: return self._form_data.get("confirm-email") == CONFIRM_EMAIL_YES_VALUE - def get_context(self): + def get_context(self) -> dict: return build_confirm_email_context(self.question_schema, self.form) - def get_next_location_url(self): + def get_next_location_url(self) -> str: if self.is_email_correct: return url_for( ".get_confirmation_email_sent", @@ -122,29 +127,37 @@ def get_next_location_url(self): ) return url_for(".send_confirmation_email", email=self._serialized_email) - def get_page_title(self): + def get_page_title(self) -> str: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors: - return gettext("Error: {page_title}").format(page_title=self.page_title) + formatted_errors: str = gettext("Error: {page_title}").format( + page_title=self.page_title + ) + return formatted_errors return self.page_title - def handle_post(self): + def handle_post(self) -> None: if self.is_email_correct: self._publish_fulfilment_request() - self._session_store.session_data.confirmation_email_count += 1 + # Type ignore: session data would be populated at this point + self._session_store.session_data.confirmation_email_count += 1 # type: ignore self._session_store.save() - def _publish_fulfilment_request(self): + def _publish_fulfilment_request(self) -> Task | None: fulfilment_request = ConfirmationEmailFulfilmentRequest( self._email, - self._session_store.session_data, - self._questionnaire_store.metadata, + # Type ignore: session data would be populated at this point + self._session_store.session_data, # type: ignore + # Type ignore: metadata will be populated as we reach this stage + self._questionnaire_store.metadata, # type: ignore self._schema, ) try: - return current_app.eq["cloud_tasks"].create_task( + # Type ignore: mypy not aware of eq attribute but it is a cloud task publisher + cloud_task_publisher: CloudTaskPublisher | LogCloudTaskPublisher = current_app.eq["cloud_tasks"] # type: ignore + return cloud_task_publisher.create_task( body=fulfilment_request.message, queue_name=EQ_SUBMISSION_CONFIRMATION_QUEUE, function_name=EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME, diff --git a/app/views/handlers/confirmation_email.py b/app/views/handlers/confirmation_email.py index 46112bd095..c2fb4713e9 100644 --- a/app/views/handlers/confirmation_email.py +++ b/app/views/handlers/confirmation_email.py @@ -1,8 +1,7 @@ from functools import cached_property -from typing import Optional from flask import current_app -from flask_babel import gettext, lazy_gettext +from flask_babel import LazyString, gettext, lazy_gettext from itsdangerous import BadSignature from werkzeug.exceptions import BadRequest @@ -26,12 +25,13 @@ def __init__( self, session_store: SessionStore, schema: QuestionnaireSchema, - page_title: Optional[str] = None, - serialised_email: Optional[str] = None, + page_title: str | None = None, + serialised_email: str | None = None, ): if not self.is_enabled(schema): raise ConfirmationEmailNotEnabled + # Type ignore: session_data is populated at login therefore won't be None for confirmation email if self.is_limit_reached(session_store.session_data): # type: ignore raise ConfirmationEmailLimitReached @@ -41,11 +41,11 @@ def __init__( self._serialised_email = serialised_email @property - def page_title(self): + def page_title(self) -> str: return self._page_title or lazy_gettext("Confirmation email") @cached_property - def form(self): + def form(self) -> EmailForm: if self._serialised_email: try: email = url_safe_serializer().loads(self._serialised_email) @@ -54,13 +54,13 @@ def form(self): return EmailForm(email=email) return EmailForm() - def get_context(self): + def get_context(self) -> dict[str, bool | str]: return build_confirmation_email_form_context(self.form) - def get_url_safe_serialized_email(self): + def get_url_safe_serialized_email(self) -> str | bytes: return url_safe_serializer().dumps(self.form.email.data) - def get_page_title(self): + def get_page_title(self) -> str | LazyString | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors: @@ -69,10 +69,8 @@ def get_page_title(self): @staticmethod def is_limit_reached(session_data: SessionData) -> bool: - return ( - session_data.confirmation_email_count - >= current_app.config["CONFIRMATION_EMAIL_LIMIT"] - ) + # Type ignore: confirmation_email_count already declared an int + return session_data.confirmation_email_count >= current_app.config["CONFIRMATION_EMAIL_LIMIT"] # type: ignore @staticmethod def is_enabled(schema: QuestionnaireSchema) -> bool: diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index c69895759f..ab536629c5 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -1,5 +1,7 @@ from functools import cached_property +from werkzeug.datastructures import ImmutableDict + from app.questionnaire.variants import transform_variants from app.views.handlers import individual_response_url from app.views.handlers.block import BlockHandler @@ -7,7 +9,7 @@ class Content(BlockHandler): @cached_property - def rendered_block(self): + def rendered_block(self) -> dict: transformed_block = transform_variants( self.block, self._schema, @@ -28,24 +30,25 @@ def rendered_block(self): list_item_id=self._current_location.list_item_id, ) - def get_context(self): + def get_context(self) -> dict: return { "block": self.rendered_block, + # Type ignore: if block is first in individual response, these two items won't be none "individual_response_url": individual_response_url( - self._schema.get_individual_response_list(), - self._current_location.list_item_id, + self._schema.get_individual_response_list(), # type: ignore + self._current_location.list_item_id, # type: ignore self._questionnaire_store, ) if self._is_block_first_block_in_individual_response() else None, } - def _get_content_title(self, transformed_block): + def _get_content_title(self, transformed_block: ImmutableDict) -> str | None: content = transformed_block.get("content") if content: return self._get_safe_page_title(content["title"]) - def _is_block_first_block_in_individual_response(self): + def _is_block_first_block_in_individual_response(self) -> bool: individual_section_id = ( self._schema.get_individual_response_individual_section_id() ) diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 69aa7ce2e4..86bc863e0e 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -7,7 +7,7 @@ from flask.helpers import url_for from flask_babel import LazyString, lazy_gettext from itsdangerous import BadSignature -from werkzeug.datastructures import ImmutableMultiDict +from werkzeug.datastructures import ImmutableMultiDict, MultiDict from werkzeug.exceptions import BadRequest, NotFound from werkzeug.wrappers.response import Response @@ -100,7 +100,7 @@ def _person_name_placeholder_possessive(list_name: str) -> list[Mapping]: @cached_property def has_postal_deadline_passed(self) -> bool: - individual_response_postal_deadline = current_app.config[ + individual_response_postal_deadline: datetime = current_app.config[ "EQ_INDIVIDUAL_RESPONSE_POSTAL_DEADLINE" ] return individual_response_postal_deadline < datetime.now(timezone.utc) @@ -121,7 +121,8 @@ def __init__( self._form_data = form_data self._answers: dict[str, Any] | None = None self._list_item_id = list_item_id - self._list_name = self._schema.get_individual_response_list() + # Type ignore: in individual_response for_list is required which is where list_name comes from + self._list_name: str = self._schema.get_individual_response_list() # type: ignore self._metadata = self._questionnaire_store.metadata @@ -132,8 +133,7 @@ def __init__( @cached_property def _list_model(self) -> ListModel: - # Type ignore: Current usages of this cached property occur when List Name will exist and be not None - return self._questionnaire_store.list_store[self._list_name] # type: ignore + return self._questionnaire_store.list_store[self._list_name] @cached_property def _list_item_position(self) -> int: @@ -165,7 +165,7 @@ def _is_location_valid(self) -> bool: return True @cached_property - def rendered_block(self) -> Mapping[str, Any]: + def rendered_block(self) -> dict: return self._render_block() @cached_property @@ -194,8 +194,9 @@ def router(self) -> Router: ) @cached_property - def individual_section_id(self) -> str | None: - return self._schema.get_individual_response_individual_section_id() + def individual_section_id(self) -> str: + # Type ignore: In an individual response handler this will not be none + return self._schema.get_individual_response_individual_section_id() # type: ignore @cached_property def form(self) -> QuestionnaireForm: @@ -211,10 +212,10 @@ def form(self) -> QuestionnaireForm: progress_store=self._questionnaire_store.progress_store, ) - def get_context(self): + def get_context(self) -> dict: return build_question_context(self.rendered_block, self.form) - def _publish_fulfilment_request(self, mobile_number=None) -> None: + def _publish_fulfilment_request(self, mobile_number: str | None = None) -> None: self._check_individual_response_count() topic_id = current_app.config["EQ_FULFILMENT_TOPIC_ID"] fulfilment_request = IndividualResponseFulfilmentRequest( @@ -516,20 +517,29 @@ def block_definition(self) -> Mapping[str, Any]: } @cached_property - def request_separate_census_option(self): - return self.rendered_block["question"]["answers"][0]["options"][0]["value"] + def request_separate_census_option(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["options"][0][ + "value" + ] + return value @cached_property - def cancel_go_to_hub_option(self): - return self.rendered_block["question"]["answers"][0]["options"][1]["value"] + def cancel_go_to_hub_option(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["options"][1][ + "value" + ] + return value @cached_property - def cancel_go_to_section_option(self): - return self.rendered_block["question"]["answers"][0]["options"][2]["value"] + def cancel_go_to_section_option(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["options"][2][ + "value" + ] + return value @cached_property - def selected_option(self): - answer_id = self.rendered_block["question"]["answers"][0]["id"] + def selected_option(self) -> str: + answer_id: str = self.rendered_block["question"]["answers"][0]["id"] return self.form.get_data(answer_id) def handle_get(self) -> str: @@ -562,8 +572,7 @@ def handle_post(self) -> Response | None: if self.selected_option == self.cancel_go_to_section_option: self._update_section_completeness() individual_section_first_block_id = ( - # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None - self._schema.get_first_block_id_for_section(self.individual_section_id) # type: ignore + self._schema.get_first_block_id_for_section(self.individual_section_id) ) return redirect( url_for( @@ -575,7 +584,7 @@ def handle_post(self) -> Response | None: ) ) - def _update_section_completeness(self): + def _update_section_completeness(self) -> None: if not self._questionnaire_store.progress_store.get_completed_block_ids( section_id=self.individual_section_id, list_item_id=self._list_item_id ): @@ -595,7 +604,7 @@ def _update_section_completeness(self): class IndividualResponsePostAddressConfirmHandler(IndividualResponseHandler): - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any): if self.has_postal_deadline_passed: raise IndividualResponsePostalDeadlinePast super().__init__(**kwargs) @@ -653,15 +662,19 @@ def block_definition(self) -> Mapping: } @cached_property - def answer_id(self): - return self.rendered_block["question"]["answers"][0]["id"] + def answer_id(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["id"] + return value @cached_property - def confirm_option(self): - return self.rendered_block["question"]["answers"][0]["options"][0]["value"] + def confirm_option(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["options"][0][ + "value" + ] + return value @cached_property - def selected_option(self): + def selected_option(self) -> str: return self.form.get_data(self.answer_id) def handle_get(self) -> str: @@ -701,8 +714,15 @@ def handle_post(self) -> Response: class IndividualResponseWhoHandler(IndividualResponseHandler): - def __init__(self, schema, questionnaire_store, language, request_args, form_data): - self._list_name = schema.get_individual_response_list() + def __init__( + self, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + language: str, + request_args: dict[str, str], + form_data: ImmutableMultiDict[str, str], + ): + self._list_name: str = schema.get_individual_response_list() # type: ignore list_model = questionnaire_store.list_store[self._list_name] self.non_primary_people_names = {} @@ -715,7 +735,8 @@ def __init__(self, schema, questionnaire_store, language, request_args, form_dat name_answers = questionnaire_store.answer_store.get_answers_by_answer_id( name_answer_ids, list_item_id=list_item_id ) - name = " ".join(name_answer.value for name_answer in name_answers) + # Type ignore: AnswerValues can be any type, however name_answers in this context will always be strings + name = " ".join(name_answer.value for name_answer in name_answers) # type: ignore self.non_primary_people_names[list_item_id] = name super().__init__( @@ -753,7 +774,7 @@ def block_definition(self) -> Mapping: } @cached_property - def selected_option(self): + def selected_option(self) -> str: answer_id = self.rendered_block["question"]["answers"][0]["id"] return self.form.get_data(answer_id) @@ -816,12 +837,14 @@ def block_definition(self) -> Mapping: } @cached_property - def answer_id(self): - return self.rendered_block["question"]["answers"][0]["id"] + def answer_id(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["id"] + return value @cached_property - def mobile_number(self): - return self.form.get_data(self.answer_id) + def mobile_number(self) -> str: + value: str = self.form.get_data(self.answer_id) + return value def handle_get(self) -> str: if "mobile_number" in self._request_args: @@ -859,12 +882,12 @@ def handle_post(self) -> Response: class IndividualResponseTextConfirmHandler(IndividualResponseHandler): def __init__( self, - schema, - questionnaire_store, - language, - request_args, - form_data, - list_item_id, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + language: str, + request_args: MultiDict[str, str], + form_data: ImmutableMultiDict[str, str], + list_item_id: str, ): try: self.mobile_number = url_safe_serializer().loads( @@ -912,15 +935,19 @@ def block_definition(self) -> Mapping: } @cached_property - def answer_id(self): - return self.rendered_block["question"]["answers"][0]["id"] + def answer_id(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["id"] + return value @cached_property - def confirm_option(self): - return self.rendered_block["question"]["answers"][0]["options"][0]["value"] + def confirm_option(self) -> str: + value: str = self.rendered_block["question"]["answers"][0]["options"][0][ + "value" + ] + return value @cached_property - def selected_option(self): + def selected_option(self) -> str: return self.form.get_data(self.answer_id) def handle_get(self) -> str: diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index 5083de11c4..f83e9b08e6 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -1,39 +1,39 @@ from flask import url_for +from werkzeug.datastructures import ImmutableDict from app.questionnaire.location import Location +from app.questionnaire.routing_path import RoutingPath from app.views.handlers.question import Question class ListAction(Question): @property - def parent_block(self): + def parent_block(self) -> ImmutableDict: parent_block_id = self._schema.parent_id_map[self.block["id"]] - return self._schema.get_block(parent_block_id) + # Type ignore: get_block is being called with a valid block_id + return self._schema.get_block(parent_block_id) # type: ignore @property - def parent_location(self): + def parent_location(self) -> Location: parent_block_id = self._schema.parent_id_map[self.block["id"]] return Location( section_id=self._current_location.section_id, block_id=parent_block_id ) - def _get_routing_path(self): + def _get_routing_path(self) -> RoutingPath: return self.router.routing_path(section_id=self.parent_location.section_id) - def is_location_valid(self): + def is_location_valid(self) -> bool: can_access_parent_location = self.router.can_access_location( self.parent_location, self._routing_path ) - if ( - not can_access_parent_location - or self._current_location.list_name != self.parent_block["for_list"] - ): - return False - - return True + return bool( + can_access_parent_location + and self._current_location.list_name == self.parent_block["for_list"] + ) - def get_previous_location_url(self): + def get_previous_location_url(self) -> str: if ( self._return_to == "section-summary" and self.router.can_display_section_summary( @@ -50,12 +50,12 @@ def get_previous_location_url(self): return_to_block_id=self._return_to_block_id, ) - def get_section_summary_url(self): + def get_section_summary_url(self) -> str: return url_for( "questionnaire.get_section", section_id=self.parent_location.section_id ) - def get_next_location_url(self): + def get_next_location_url(self) -> str: if self._return_to == "section-summary": if self.router.can_display_section_summary( self.parent_location.section_id, self.parent_location.list_item_id @@ -63,7 +63,8 @@ def get_next_location_url(self): return self.get_section_summary_url() if self.router.is_block_complete( - block_id=self.parent_location.block_id, + # Type ignore: the parent_location property above is initialised with a block_id so it won't be None + block_id=self.parent_location.block_id, # type: ignore section_id=self.parent_location.section_id, list_item_id=self.parent_location.list_item_id, ): @@ -81,7 +82,7 @@ def get_next_location_url(self): return_to_block_id=self._return_to_block_id, ) - def handle_post(self): + def handle_post(self) -> None: self.questionnaire_store_updater.update_same_name_items( self.parent_block["for_list"], self.parent_block.get("same_name_answer_ids"), @@ -98,13 +99,14 @@ def handle_post(self): def _get_location_url( self, *, - block_id=None, - return_to=None, - return_to_answer_id=None, - return_to_block_id=None, - ): + block_id: str | None = None, + return_to: str | None = None, + return_to_answer_id: str | None = None, + return_to_block_id: str | None = None, + ) -> str: if block_id and self._schema.is_block_valid(block_id): - section_id = self._schema.get_section_id_for_block_id(block_id) + # Type ignore: the above line check that block_id exists and is valid and therefore section exists + section_id: str = self._schema.get_section_id_for_block_id(block_id) # type: ignore return Location(section_id=section_id, block_id=block_id).url( return_to=return_to, return_to_answer_id=return_to_answer_id, diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index 0ccae5827e..af14b9f7f6 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -1,22 +1,20 @@ -from typing import MutableMapping - from app.views.handlers.list_action import ListAction class ListAddQuestion(ListAction): - def is_location_valid(self): + def is_location_valid(self) -> bool: if not super().is_location_valid() or self._current_location.list_item_id: return False return True - def get_next_location_url(self): + def get_next_location_url(self) -> str: return self.parent_location.url( return_to=self._return_to, return_to_answer_id=self._return_to_answer_id, return_to_block_id=self._return_to_block_id, ) - def handle_post(self): + def handle_post(self) -> None: # Ensure the section is in progress when user adds an item list_item_id = self.questionnaire_store_updater.add_list_item( self.parent_block["for_list"] @@ -24,7 +22,8 @@ def handle_post(self): # Clear the answer from the confirmation question on the list collector question answer_ids_to_remove = self._schema.get_answer_ids_for_block( - self.parent_location.block_id + # Type ignore: for parent_location block_id is not none + self.parent_location.block_id # type: ignore ) self.questionnaire_store_updater.remove_answers(answer_ids_to_remove) self.questionnaire_store_updater.remove_completed_location(self.parent_location) @@ -37,7 +36,7 @@ def handle_post(self): return super().handle_post() - def _resolve_custom_page_title_vars(self) -> MutableMapping: + def _resolve_custom_page_title_vars(self) -> dict[str, int]: # For list add blocks, no list item id is yet available. Instead, we resolve # `list_item_position` to the position in the list it would be if added. list_length = len( diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 8a8ade19f5..7c17d44adc 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -1,3 +1,5 @@ +from typing import Any + from flask import url_for from app.views.contexts import ListContext @@ -5,11 +7,11 @@ class ListCollector(Question): - def __init__(self, *args): + def __init__(self, *args: Any) -> None: self._is_adding = False super().__init__(*args) - def get_next_location_url(self): + def get_next_location_url(self) -> str: if self._is_adding: add_url = url_for( "questionnaire.block", @@ -23,7 +25,7 @@ def get_next_location_url(self): return super().get_next_location_url() - def get_context(self): + def get_context(self) -> dict[str, dict]: question_context = super().get_context() list_context = ListContext( self._language, @@ -46,7 +48,7 @@ def get_context(self): ), } - def handle_post(self): + def handle_post(self) -> None: answer_action = self._get_answer_action() if answer_action and answer_action["type"] == "RedirectToListAddBlock": @@ -56,4 +58,4 @@ def handle_post(self): self.questionnaire_store_updater.update_answers(self.form.data) self.questionnaire_store_updater.save() else: - return super().handle_post() + super().handle_post() diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index 1d640135b4..22d5326884 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -2,18 +2,19 @@ class ListEditQuestion(ListAction): - def is_location_valid(self): + def is_location_valid(self) -> bool: list_item_doesnt_exist = ( self._current_location.list_item_id not in self._questionnaire_store.list_store[ - self._current_location.list_name + # Type ignore: list_name/list_item_id already exist + self._current_location.list_name # type: ignore ].items ) if not super().is_location_valid() or list_item_doesnt_exist: return False return True - def handle_post(self): + def handle_post(self) -> None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index c9742638ca..5081018fa0 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -3,16 +3,18 @@ class ListRemoveQuestion(ListAction): - def is_location_valid(self): + def is_location_valid(self) -> bool: list_item_doesnt_exist = ( self._current_location.list_item_id not in self._questionnaire_store.list_store[ - self._current_location.list_name + # Type ignore: list_name will exist within the remove block + self._current_location.list_name # type: ignore ].items ) is_primary = ( self._questionnaire_store.list_store[ - self._current_location.list_name + # Type ignore: list_name will exist within the remove block + self._current_location.list_name # type: ignore ].primary_person == self._current_location.list_item_id ) @@ -20,29 +22,31 @@ def is_location_valid(self): return False return True - def handle_post(self): + def handle_post(self) -> None: answer_action = self._get_answer_action() if answer_action and answer_action["type"] == "RemoveListItemAndAnswers": list_name = self.parent_block["for_list"] self.questionnaire_store_updater.remove_list_item_and_answers( - list_name, self._current_location.list_item_id + # Type ignore: list_item_id will exist within the remove block + list_name, + self._current_location.list_item_id, # type: ignore ) self.capture_dependent_sections_for_list(self.parent_block["for_list"]) return super().handle_post() def individual_response_enabled(self) -> bool: - return ( - self.parent_block["for_list"] == self._schema.get_individual_response_list() - ) + # Type ignore: we know "for_list" will be a string + return self.parent_block["for_list"] == self._schema.get_individual_response_list() # type: ignore - def get_context(self): - context = super().get_context() + def get_context(self) -> dict: + context: dict = super().get_context() context["individual_response_enabled"] = self.individual_response_enabled() context["individual_response_url"] = individual_response_url( self._schema.get_individual_response_list(), - self._current_location.list_item_id, + # Type ignore: in a list so we know this cannot be None + self._current_location.list_item_id, # type: ignore self._questionnaire_store, journey="remove-person", ) diff --git a/app/views/handlers/primary_person_list_collector.py b/app/views/handlers/primary_person_list_collector.py index 84123fa2fe..fa8c31c209 100644 --- a/app/views/handlers/primary_person_list_collector.py +++ b/app/views/handlers/primary_person_list_collector.py @@ -1,15 +1,17 @@ +from typing import Any + from flask import url_for from app.views.handlers.question import Question class PrimaryPersonListCollector(Question): - def __init__(self, *args): + def __init__(self, *args: Any): self._is_adding = False - self._primary_person_id = None + self._primary_person_id: str | None = None super().__init__(*args) - def get_next_location_url(self): + def get_next_location_url(self) -> str: if self._is_adding: add_or_edit_url = url_for( "questionnaire.block", @@ -21,7 +23,7 @@ def get_next_location_url(self): return super().get_next_location_url() - def handle_post(self): + def handle_post(self) -> None: list_name = self.rendered_block["for_list"] answer_action = self._get_answer_action() diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index 13079791fd..dcc3dfd267 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -1,26 +1,31 @@ +from werkzeug.datastructures import ImmutableDict + from app.questionnaire.location import Location +from app.questionnaire.routing_path import RoutingPath from app.views.handlers.question import Question class PrimaryPersonQuestion(Question): @property - def parent_block(self): + def parent_block(self) -> ImmutableDict: parent_block_id = self._schema.parent_id_map[self.rendered_block["id"]] - return self._schema.get_block(parent_block_id) + # Type ignore: being called with a valid block_id + return self._schema.get_block(parent_block_id) # type: ignore @property - def parent_location(self): + def parent_location(self) -> Location: parent_id = self._schema.parent_id_map[self.rendered_block["id"]] return Location( section_id=self._current_location.section_id, block_id=parent_id ) - def _get_routing_path(self): + def _get_routing_path(self) -> RoutingPath: return self.router.routing_path(section_id=self._current_location.section_id) - def is_location_valid(self): + def is_location_valid(self) -> bool: primary_person_list_item_id = self._questionnaire_store.list_store[ - self.current_location.list_name + # Type ignore: list_name will exist by this point + self.current_location.list_name # type: ignore ].primary_person return ( @@ -30,15 +35,15 @@ def is_location_valid(self): ) ) - def get_previous_location_url(self): + def get_previous_location_url(self) -> str: return self.parent_location.url() - def get_next_location_url(self): + def get_next_location_url(self) -> str: return self.router.get_next_location_url( self.parent_location, self._routing_path, self._return_to ) - def handle_post(self): + def handle_post(self) -> None: same_name_answer_ids = self.parent_block.get("same_name_answer_ids") # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 483da976f7..7d48e02bf2 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -1,10 +1,10 @@ from functools import cached_property -from typing import Any +from typing import Mapping from flask import url_for from flask_babel import gettext -from app.forms.questionnaire_form import generate_form +from app.forms.questionnaire_form import QuestionnaireForm, generate_form from app.helpers import get_address_lookup_api_auth_token from app.questionnaire.location import Location from app.questionnaire.questionnaire_store_updater import ( @@ -19,12 +19,12 @@ class Question(BlockHandler): @staticmethod - def _has_redirect_to_list_add_action(answer_action): - return answer_action and answer_action["type"] == "RedirectToListAddBlock" + def _has_redirect_to_list_add_action(answer_action: Mapping | None) -> bool: + return bool(answer_action and answer_action["type"] == "RedirectToListAddBlock") @cached_property - def form(self): - question_json = self.rendered_block.get("question") + def form(self) -> QuestionnaireForm: + question_json = self.rendered_block["question"] if self._form_data: return generate_form( @@ -53,7 +53,7 @@ def form(self): ) @cached_property - def questionnaire_store_updater(self): + def questionnaire_store_updater(self) -> QuestionnaireStoreUpdater: return QuestionnaireStoreUpdater( self._current_location, self._schema, @@ -63,7 +63,7 @@ def questionnaire_store_updater(self): ) @cached_property - def rendered_block(self): + def rendered_block(self) -> dict: transformed_block = transform_variants( self.block, self._schema, @@ -89,7 +89,7 @@ def rendered_block(self): } @cached_property - def list_context(self): + def list_context(self) -> ListContext: return ListContext( language=self._language, schema=self._schema, @@ -100,10 +100,11 @@ def list_context(self): response_metadata=self._questionnaire_store.response_metadata, ) - def get_next_location_url(self): + def get_next_location_url(self) -> str: answer_action = self._get_answer_action() if self._has_redirect_to_list_add_action(answer_action): - location_url = self._get_list_add_question_url(answer_action["params"]) + # Type ignore: this is only called if answer_action is not none. + location_url = self._get_list_add_question_url(answer_action["params"]) # type: ignore if location_url: return location_url @@ -116,7 +117,7 @@ def get_next_location_url(self): self._return_to_block_id, ) - def _get_answers_for_question(self, question_json) -> dict[str, Any]: + def _get_answers_for_question(self, question_json: Mapping) -> dict: answers_by_answer_id = self._schema.get_answers_for_question_by_id( question_json ) @@ -135,7 +136,7 @@ def _get_answers_for_question(self, question_json) -> dict[str, Any]: return answer_value_by_answer_id - def _get_list_add_question_url(self, params): + def _get_list_add_question_url(self, params: dict) -> str | None: block_id = params["block_id"] list_name = params["list_name"] list_items = self._questionnaire_store.list_store[list_name].items @@ -143,17 +144,20 @@ def _get_list_add_question_url(self, params): if self._is_list_just_primary(list_items, list_name) or not list_items: return Location( - section_id=section_id, block_id=block_id, list_name=list_name + # Type ignore: section_id is always valid when this is called + section_id=section_id, # type: ignore + block_id=block_id, + list_name=list_name, ).url(previous=self.current_location.block_id) - def _is_list_just_primary(self, list_items, list_name): + def _is_list_just_primary(self, list_items: list[str], list_name: str) -> bool: return ( len(list_items) == 1 and list_items[0] == self._questionnaire_store.list_store[list_name].primary_person ) - def _get_answer_action(self): + def _get_answer_action(self) -> dict | None: answers = self.rendered_block["question"]["answers"] for answer in answers: @@ -162,7 +166,7 @@ def _get_answer_action(self): submitted_answer = self.form.data[answer["id"]] for option in answer.get("options", {}): - action = option.get("action") + action: dict | None = option.get("action") if action and ( option["value"] == submitted_answer @@ -170,7 +174,7 @@ def _get_answer_action(self): ): return action - def get_context(self): + def get_context(self) -> dict[str, dict]: context = build_question_context(self.rendered_block, self.form) context["return_to_hub_url"] = self.get_return_to_hub_url() context[ @@ -193,20 +197,20 @@ def get_context(self): return context - def get_last_viewed_question_guidance_context(self): + def get_last_viewed_question_guidance_context(self) -> dict | bool | None: if self.resume: first_location_in_section_url = self.router.get_first_location_in_section( self._routing_path ).url() return {"first_location_in_section_url": first_location_in_section_url} - def get_list_summary_context(self): + def get_list_summary_context(self) -> dict: return self.list_context( self.rendered_block["list_summary"]["summary"], self.rendered_block["list_summary"]["for_list"], ) - def handle_post(self): + def handle_post(self) -> None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) @@ -220,14 +224,14 @@ def handle_post(self): ) super().handle_post() - def get_return_to_hub_url(self): + def get_return_to_hub_url(self) -> str | None: if ( self.rendered_block["type"] in ["Question", "ConfirmationQuestion"] and self.router.can_access_hub() ): return url_for(".get_questionnaire") - def capture_dependent_sections_for_list(self, list_name): + def capture_dependent_sections_for_list(self, list_name: str) -> None: section_ids = self._schema.get_section_ids_dependent_on_list(list_name) section_ids.append(self.current_location.section_id) @@ -243,7 +247,7 @@ def capture_dependent_sections_for_list(self, list_name): ) ) - def clear_radio_answers(self): + def clear_radio_answers(self) -> None: answer_ids_to_remove = [] for answer in self.rendered_block["question"]["answers"]: if answer["type"] == "Radio": diff --git a/app/views/handlers/relationships/relationship_collector.py b/app/views/handlers/relationships/relationship_collector.py index 4f9ea82335..83b2271ae3 100644 --- a/app/views/handlers/relationships/relationship_collector.py +++ b/app/views/handlers/relationships/relationship_collector.py @@ -1,4 +1,4 @@ -from typing import MutableMapping +from typing import Mapping, MutableMapping from app.data_models.relationship_store import Relationship from app.questionnaire.location import Location @@ -6,7 +6,7 @@ class RelationshipCollector(RelationshipQuestion): - def is_location_valid(self): + def is_location_valid(self) -> bool: if isinstance(self._current_location, Location): return self.router.can_access_location( self._current_location, self._routing_path @@ -14,13 +14,14 @@ def is_location_valid(self): return super().is_location_valid() - def handle_post(self): + def handle_post(self) -> None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation relationship_answer = self.form.data.get(self.relationships_answer_id) relationship = Relationship( - self._current_location.list_item_id, - self._current_location.to_list_item_id, + # Type ignore: handle_post is only called from relationships endpoint and location class is assigned to RelationshipLocation + self._current_location.list_item_id, # type: ignore + self._current_location.to_list_item_id, # type: ignore relationship_answer, ) self.relationship_store.add_or_update(relationship) @@ -39,28 +40,31 @@ def handle_post(self): self.questionnaire_store_updater.save() - def _get_answers_for_question(self, question_json): + def _get_answers_for_question(self, question_json: Mapping) -> dict: relationship = self.relationship_store.get_relationship( - self._current_location.list_item_id, - self._current_location.to_list_item_id, + # relationship only used if these exist. RelationshipLocation wil already be determined. + self._current_location.list_item_id, # type: ignore + self._current_location.to_list_item_id, # type: ignore ) if relationship: return {self.relationships_answer_id: relationship.relationship} return {} - def _is_last_relationship(self): - if self.relationship_router.get_next_location(self._current_location): + def _is_last_relationship(self) -> bool: + if self.relationship_router.get_next_location(self._current_location): # type: ignore return False return True def _resolve_custom_page_title_vars(self) -> MutableMapping: page_title_vars = super()._resolve_custom_page_title_vars() - if to_list_item_position := self.current_location.to_list_item_id: + if to_list_item_position := self.current_location.to_list_item_id: # type: ignore page_title_vars[ "to_list_item_position" ] = self._questionnaire_store.list_store.list_item_position( - self.current_location.list_name, to_list_item_position + # Type ignore: list_name populated at this stage + self.current_location.list_name, # type: ignore + to_list_item_position, ) return page_title_vars diff --git a/app/views/handlers/relationships/relationship_question.py b/app/views/handlers/relationships/relationship_question.py index 48db03399d..1a7e88bbc8 100644 --- a/app/views/handlers/relationships/relationship_question.py +++ b/app/views/handlers/relationships/relationship_question.py @@ -1,58 +1,68 @@ from functools import cached_property -from app.data_models.relationship_store import RelationshipStore +from werkzeug.datastructures import ImmutableDict + +from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire.location import Location from app.questionnaire.relationship_router import RelationshipRouter +from app.questionnaire.router import RoutingPath from app.views.handlers.question import Question class RelationshipQuestion(Question): @cached_property - def relationships_block(self): + def relationships_block(self) -> ImmutableDict | None: return self._schema.get_block(self.block["id"]) @cached_property - def relationships_answer_id(self): + def relationships_answer_id(self) -> str: return self._schema.get_first_answer_id_for_block( - self.relationships_block["id"] + # Type ignore: block must exist when this is called + self.relationships_block["id"] # type: ignore ) @property - def parent_location(self): + def parent_location(self) -> Location: return Location( section_id=self._current_location.section_id, - block_id=self.relationships_block["id"], + # Type ignore: block must exist when this is called + block_id=self.relationships_block["id"], # type: ignore ) @cached_property - def unrelated_block_id(self): - return self.relationships_block.get("unrelated_block", {}).get("id") + def unrelated_block_id(self) -> str | None: + # Type ignore: called when relationships_block already exists + value: str = self.relationships_block.get("unrelated_block", {}).get("id") # type: ignore + return value @cached_property - def unrelated_answer_id(self): + def unrelated_answer_id(self) -> str | None: if self.unrelated_block_id: return self._schema.get_first_answer_id_for_block(self.unrelated_block_id) return None @cached_property - def unrelated_no_answer_values(self): + def unrelated_no_answer_values(self) -> list[str] | None: if self.unrelated_answer_id: return self._schema.get_unrelated_block_no_answer_values( self.unrelated_answer_id ) @cached_property - def relationship_store(self): + def relationship_store(self) -> RelationshipStore: answer = self._questionnaire_store.answer_store.get_answer( self.relationships_answer_id ) if answer: - return RelationshipStore(answer.value) + # Type ignore: for a relationship question the answer will always be a list of RelationshipDict + answer_value: list[RelationshipDict] = answer.value # type: ignore + return RelationshipStore(answer_value) return RelationshipStore() @property - def relationship_router(self): - list_name = self.relationships_block["for_list"] + def relationship_router(self) -> RelationshipRouter: + # Type ignore: list will be populated at this point as it is required to build relationship + list_name = self.relationships_block["for_list"] # type: ignore list_items = self._questionnaire_store.list_store[list_name].items return RelationshipRouter( @@ -61,46 +71,49 @@ def relationship_router(self): section_id=self._current_location.section_id, list_name=list_name, list_item_ids=list_items, - relationships_block_id=self.relationships_block["id"], + # Type ignore: block must exist before getting to this point + relationships_block_id=self.relationships_block["id"], # type: ignore unrelated_block_id=self.unrelated_block_id, unrelated_answer_id=self.unrelated_answer_id, unrelated_no_answer_values=self.unrelated_no_answer_values, ) - def _get_routing_path(self): + def _get_routing_path(self) -> RoutingPath: return self.router.routing_path(section_id=self.parent_location.section_id) - def is_location_valid(self): + def is_location_valid(self) -> bool: can_access_parent_location = self.router.can_access_location( self.parent_location, self._routing_path ) can_access_relationship_location = self.relationship_router.can_access_location( - self._current_location + self._current_location # type: ignore ) if not can_access_parent_location or not can_access_relationship_location: return False return True - def get_first_location_url(self): + def get_first_location_url(self) -> str: return self.relationship_router.get_first_location().url() - def get_last_location_url(self): + def get_last_location_url(self) -> str: return self.relationship_router.get_last_location().url() - def get_previous_location_url(self): + def get_previous_location_url(self) -> str: previous_location = self.relationship_router.get_previous_location( - self._current_location + # Type ignore: block will determine type of location to be relationship location + self._current_location # type: ignore ) if previous_location: return previous_location.url() - return self.router.get_previous_location_url( + return self.router.get_previous_location_url( # type: ignore self.parent_location, self._routing_path ) - def get_next_location_url(self): + def get_next_location_url(self) -> str: next_location = self.relationship_router.get_next_location( - self._current_location + # Type ignore: block will determine type of location to be relationship location + self._current_location # type: ignore ) if next_location: return next_location.url() diff --git a/app/views/handlers/relationships/unrelated_question.py b/app/views/handlers/relationships/unrelated_question.py index 500b0cc936..21f57a0980 100644 --- a/app/views/handlers/relationships/unrelated_question.py +++ b/app/views/handlers/relationships/unrelated_question.py @@ -1,31 +1,35 @@ from functools import cached_property +from werkzeug.datastructures import ImmutableDict + from app.data_models.relationship_store import Relationship from app.views.handlers.relationships.relationship_question import RelationshipQuestion class UnrelatedQuestion(RelationshipQuestion): @cached_property - def list_name(self): - return self.block["list_summary"]["for_list"] + def list_name(self) -> str: + value: str = self.block["list_summary"]["for_list"] + return value @cached_property - def relationships_block(self): + def relationships_block(self) -> ImmutableDict | None: parent_block_id = self._schema.parent_id_map[self.block["id"]] return self._schema.get_block(parent_block_id) @cached_property - def unrelated_block_id(self): - return self.block["id"] + def unrelated_block_id(self) -> str | None: + value: str | None = self.block["id"] + return value - def get_list_summary_context(self): + def get_list_summary_context(self) -> dict[str, dict]: return self.list_context( self.rendered_block["list_summary"]["summary"], self.list_name, for_list_item_ids=self.get_remaining_relationships_for_individual(), ) - def get_remaining_relationships_for_individual(self): + def get_remaining_relationships_for_individual(self) -> list[str]: """ Returns a list of 'to' item ids for the remaining relationships. These relationships won't be on the path if the user has selected @@ -33,12 +37,13 @@ def get_remaining_relationships_for_individual(self): """ list_model = self._questionnaire_store.list_store[self.list_name] previous_location = self.relationship_router.get_previous_location( - self.current_location + # Type ignore: block will determine type of location to be relationship location + self.current_location # type: ignore ) - previous_item_index = list_model.index(previous_location.to_list_item_id) + previous_item_index = list_model.index(previous_location.to_list_item_id) # type: ignore return list_model[previous_item_index + 1 :] - def handle_post(self): + def handle_post(self) -> None: if answer_action := self._get_answer_action(): self.handle_answer_action(answer_action["type"]) @@ -49,26 +54,29 @@ def handle_post(self): ) self.questionnaire_store_updater.save() - def handle_answer_action(self, answer_action_type): + def handle_answer_action(self, answer_action_type: str) -> None: from_list_item_id = self.current_location.list_item_id if answer_action_type == "RemoveUnrelatedRelationships": for to_list_item_id in self.get_remaining_relationships_for_individual(): relationship = self.relationship_store.get_relationship( - from_list_item_id, to_list_item_id + # Type ignore: from_list_item_id populated at this stage + from_list_item_id, # type: ignore + to_list_item_id, ) if relationship and ( relationship.relationship == self.relationship_router.UNRELATED_RELATIONSHIP_VALUE ): self.relationship_store.remove_relationship( - from_list_item_id, to_list_item_id + from_list_item_id, to_list_item_id # type: ignore ) elif answer_action_type == "AddUnrelatedRelationships": for to_list_item_id in self.get_remaining_relationships_for_individual(): relationship = Relationship( - list_item_id=from_list_item_id, + # Type ignore: from_list_item_id populated at this stage + list_item_id=from_list_item_id, # type: ignore to_list_item_id=to_list_item_id, relationship=self.relationship_router.UNRELATED_RELATIONSHIP_VALUE, ) diff --git a/app/views/handlers/submission.py b/app/views/handlers/submission.py index 97857f6578..7f1820e705 100644 --- a/app/views/handlers/submission.py +++ b/app/views/handlers/submission.py @@ -6,10 +6,15 @@ from sdc.crypto.encrypter import encrypt from app.authentication.auth_payload_version import AuthPayloadVersion +from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store from app.keys import KEY_PURPOSE_SUBMISSION -from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.questionnaire.questionnaire_schema import ( + DEFAULT_LANGUAGE_CODE, + QuestionnaireSchema, +) +from app.questionnaire.routing_path import RoutingPath from app.submitter.converter import convert_answers from app.submitter.converter_v2 import convert_answers_v2 from app.submitter.submission_failed import SubmissionFailedException @@ -29,29 +34,39 @@ def get_receipting_metadata(metadata: MetadataProxy) -> dict: class SubmissionHandler: - def __init__(self, schema, questionnaire_store, full_routing_path): + def __init__( + self, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + full_routing_path: list[RoutingPath], + ): self._schema = schema self._questionnaire_store = questionnaire_store self._full_routing_path = full_routing_path self._session_store = get_session_store() - self._metadata = questionnaire_store.metadata + # Type ignore: It isn't possible to not have metadata at this point + self._metadata: MetadataProxy = questionnaire_store.metadata # type: ignore @cached_property - def submitted_at(self): + def submitted_at(self) -> datetime: return datetime.now(timezone.utc).replace(microsecond=0) - def submit_questionnaire(self): + def submit_questionnaire(self) -> None: payload = self.get_payload() message = json_dumps(payload) encrypted_message = encrypt( - message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION + message, + # Type ignore: current_app can return empty Local Proxy. Similar to other files, this is ignored. + current_app.eq["key_store"], # type: ignore + KEY_PURPOSE_SUBMISSION, ) additional_metadata = get_receipting_metadata(self._metadata) - submitted = current_app.eq["submitter"].send_message( + # Type ignore: current_app can return empty Local Proxy. Similar to other files, this is ignored. + submitted = current_app.eq["submitter"].send_message( # type: ignore encrypted_message, case_id=self._metadata.case_id, tx_id=self._metadata.tx_id, @@ -66,7 +81,7 @@ def submit_questionnaire(self): self._questionnaire_store.submitted_at = self.submitted_at self._questionnaire_store.save() - def get_payload(self): + def get_payload(self) -> dict: answer_converter = ( convert_answers_v2 if self._metadata.version is AuthPayloadVersion.V2 @@ -81,6 +96,8 @@ def get_payload(self): ) payload["submission_language_code"] = ( - self._session_store.session_data.language_code or DEFAULT_LANGUAGE_CODE + # Type ignore: session_data will exist by this stage + self._session_store.session_data.language_code # type: ignore + or DEFAULT_LANGUAGE_CODE ) return payload diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index 2a28087133..21e6faeb38 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -1,7 +1,6 @@ from __future__ import annotations from functools import cached_property -from typing import Optional, Union from app.data_models import QuestionnaireStore from app.questionnaire import QuestionnaireSchema @@ -36,7 +35,7 @@ def router(self) -> Router: response_metadata=self._questionnaire_store.response_metadata, ) - def get_context(self) -> dict[str, Union[str, dict]]: + def get_context(self) -> dict[str, str | dict]: submit_questionnaire_context = SubmitQuestionnaireContext( language=self._language, schema=self._schema, @@ -48,7 +47,7 @@ def get_context(self) -> dict[str, Union[str, dict]]: ) return submit_questionnaire_context() - def get_previous_location_url(self) -> Optional[str]: + def get_previous_location_url(self) -> str | None: return self.router.get_last_location_in_questionnaire_url() @property diff --git a/app/views/handlers/thank_you.py b/app/views/handlers/thank_you.py index 183e99bff1..ad466efbab 100644 --- a/app/views/handlers/thank_you.py +++ b/app/views/handlers/thank_you.py @@ -2,9 +2,10 @@ from functools import cached_property from flask import session as cookie_session -from flask_babel import gettext +from flask_babel import LazyString, gettext from flask_login import current_user +from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_store import SessionStore from app.globals import get_metadata from app.helpers.template_helpers import get_survey_type @@ -43,14 +44,14 @@ def __init__( ) @cached_property - def confirmation_email(self): + def confirmation_email(self) -> ConfirmationEmail | None: try: return ConfirmationEmail(self._session_store, self._schema, self.PAGE_TITLE) except (ConfirmationEmailNotEnabled, ConfirmationEmailLimitReached): return None - def get_context(self): - metadata = get_metadata(current_user) or {} + def get_context(self) -> dict: + metadata: MetadataProxy = get_metadata(current_user) # type: ignore if not self._is_census_theme: guidance_content = self._schema.get_post_submission().get("guidance") @@ -69,10 +70,11 @@ def get_context(self): return build_census_thank_you_context( metadata, confirmation_email_form, - self._schema.form_type, + # Type ignore: form_type will already be populated at this stage + self._schema.form_type, # type: ignore ) - def get_page_title(self): + def get_page_title(self) -> str | LazyString: if self.confirmation_email: return self.confirmation_email.get_page_title() return self.PAGE_TITLE diff --git a/mypy.ini b/mypy.ini index ebbd7085f8..b318531b70 100644 --- a/mypy.ini +++ b/mypy.ini @@ -6,27 +6,22 @@ warn_no_return = False # Per-module options: -[mypy-app.data_models.*] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.helpers.*] +[mypy-app.views.handlers.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.questionnaire.*] +[mypy-app.data_models.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.views.handlers.submit] +[mypy-app.helpers.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.views.handlers.view_submitted_response_context] +[mypy-app.questionnaire.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True @@ -81,21 +76,6 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.views.handlers.view_submitted_response] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.views.handlers.pdf] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - -[mypy-app.views.handlers.feedback] -disallow_untyped_defs = True -warn_return_any = True -no_implicit_optional = True - [mypy-app.submitter.*] disallow_untyped_defs = True warn_return_any = True From 6e8b7b25979fa76aee3704fa171fd6fe70e8641b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:21:49 +0100 Subject: [PATCH 250/567] Schemas v3.64.0 (#1161) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 7b705cd7be..1db697439a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.63.0 +v3.64.0 From 30985652695070a00c70e262a085ffd007f1e0b6 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:59:12 +0100 Subject: [PATCH 251/567] Schemas v3.65.0 (#1166) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 1db697439a..5d3a7e6a60 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.64.0 +v3.65.0 From 3a50b0b936ecb183f119a7fd411e93622899a01a Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 24 Jul 2023 14:35:52 +0100 Subject: [PATCH 252/567] Merge feature-prepop into main (#1154) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.c Co-authored-by: petechd <53475968+petechd@users.noreply.github.com> Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Co-authored-by: Guilhem <122792081+ONS-Guilhem-Forey@users.noreply.github.com> Co-authored-by: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: katie-gardner Co-authored-by: Kyle Lawson_ONS <108286538+kylelawsonAND@users.noreply.github.com> Co-authored-by: Michael Donaghey_ONS <111740109+mgdon650@users.noreply.github.com> --- .development.env | 1 + .functional-tests.env | 1 + ...ad_version.py => auth_payload_versions.py} | 4 + app/authentication/authenticator.py | 12 +- app/data_models/answer_store.py | 6 +- app/data_models/list_store.py | 4 + app/data_models/metadata_proxy.py | 2 +- app/data_models/progress_store.py | 208 +++-- app/data_models/questionnaire_store.py | 76 +- app/data_models/supplementary_data_store.py | 122 +++ app/forms/questionnaire_form.py | 17 +- app/jinja_filters.py | 28 +- app/keys.py | 1 + app/questionnaire/dependencies.py | 2 +- app/questionnaire/path_finder.py | 5 +- app/questionnaire/placeholder_renderer.py | 12 +- app/questionnaire/placeholder_transforms.py | 2 +- app/questionnaire/questionnaire_schema.py | 233 ++++-- .../questionnaire_store_updater.py | 66 +- app/questionnaire/router.py | 42 +- app/questionnaire/rules/operations.py | 35 +- app/questionnaire/rules/rule_evaluator.py | 21 +- app/questionnaire/schema_utils.py | 4 +- app/questionnaire/value_source_resolver.py | 90 ++- app/routes/errors.py | 17 + app/routes/flush.py | 2 +- app/routes/session.py | 46 +- app/services/__init__.py | 0 app/services/supplementary_data.py | 129 ++++ app/settings.py | 1 + app/setup.py | 3 +- app/submitter/converter.py | 2 + app/submitter/converter_v2.py | 23 +- app/utilities/metadata_parser_v2.py | 2 +- app/utilities/request_session.py | 21 + app/utilities/schema.py | 22 +- app/utilities/supplementary_data_parser.py | 89 +++ .../contexts/calculated_summary_context.py | 18 +- .../grand_calculated_summary_context.py | 14 +- app/views/contexts/hub_context.py | 6 +- app/views/contexts/list_context.py | 77 +- app/views/contexts/section_summary_context.py | 3 +- app/views/contexts/summary/answer.py | 60 +- app/views/contexts/summary/block.py | 76 +- app/views/contexts/summary/group.py | 29 + .../contexts/summary/list_collector_block.py | 115 ++- app/views/contexts/summary/question.py | 5 + app/views/handlers/block.py | 2 +- app/views/handlers/block_factory.py | 2 + app/views/handlers/feedback.py | 2 +- app/views/handlers/individual_response.py | 2 +- app/views/handlers/list_action.py | 44 +- app/views/handlers/list_add_question.py | 27 +- app/views/handlers/list_collector.py | 40 +- app/views/handlers/list_edit_question.py | 28 + app/views/handlers/list_remove_question.py | 2 +- app/views/handlers/list_repeating_question.py | 82 ++ app/views/handlers/question.py | 62 +- .../relationships/unrelated_question.py | 6 +- app/views/handlers/submission.py | 2 +- dev-keys.yml | 75 ++ doc/run-mock-sds-endpoint.md | 27 + ...ctor_repeating_blocks_section_summary.json | 432 +++++++++++ ...t_collector_repeating_blocks_with_hub.json | 430 +++++++++++ ..._summary_repeating_and_static_answers.json | 726 ++++++++++++++++++ ...ulated_summary_repeating_answers_only.json | 322 ++++++++ ...w_calculated_summary_repeating_blocks.json | 518 +++++++++++++ ...est_optional_guidance_and_description.json | 219 ++++++ schemas/test/en/test_supplementary_data.json | 65 ++ ...ion_sum_against_total_dynamic_answers.json | 470 ++++++++++++ .../supplementary_data_no_repeat.json | 8 + .../supplementary_data_with_repeat.json | 85 ++ .../supplementary_data_with_repeat_v2.json | 117 +++ ...pplementary_dataset_metadata_response.json | 39 + scripts/mock_sds_endpoint.py | 67 ++ scripts/validate_test_schemas.sh | 2 +- templates/listrepeatingquestion.html | 1 + templates/macros/helpers.html | 4 +- templates/partials/contents.html | 2 + templates/partials/question.html | 20 +- .../partials/summary/collapsible-summary.html | 3 +- templates/partials/summary/list-summary.html | 16 +- templates/partials/summary/summary.html | 3 +- tests/app/conftest.py | 45 ++ tests/app/data_model/conftest.py | 75 ++ tests/app/data_model/test_answer_store.py | 4 +- tests/app/data_model/test_list_store.py | 8 + tests/app/data_model/test_metadata_proxy.py | 2 +- tests/app/data_model/test_progress_store.py | 105 ++- .../data_model/test_questionnaire_store.py | 139 ++++ .../test_supplementary_data_store.py | 86 +++ tests/app/forms/test_questionnaire_form.py | 110 +++ tests/app/parser/conftest.py | 2 +- tests/app/parser/test_metadata_parser.py | 2 +- .../parser/test_supplementary_data_parser.py | 208 +++++ tests/app/questionnaire/conftest.py | 43 +- .../rules/test_rule_evaluator.py | 8 +- .../test_dynamic_answer_options.py | 4 + tests/app/questionnaire/test_path_finder.py | 7 +- .../test_placeholder_renderer.py | 5 +- .../test_questionnaire_schema.py | 31 + .../test_questionnaire_store_updater.py | 83 +- .../test_value_source_resolver.py | 94 ++- tests/app/services/__init__.py | 0 tests/app/services/conftest.py | 67 ++ .../test_request_supplementary_data.py | 222 ++++++ tests/app/submitter/conftest.py | 75 +- .../submitter/test_convert_payload_0_0_1.py | 19 +- .../submitter/test_convert_payload_0_0_3.py | 206 ++++- tests/app/submitter/test_converter.py | 2 +- tests/app/test_jinja_filters.py | 20 +- tests/app/utilities/test_schema.py | 31 - tests/app/views/contexts/conftest.py | 75 ++ .../app/views/contexts/summary/test_answer.py | 25 +- .../test_calculated_summary_context.py | 196 +++++ .../test_grand_calculated_summary_context.py | 2 +- tests/app/views/contexts/test_list_context.py | 257 ++++++- .../contexts/test_section_summary_context.py | 468 ++--------- tests/app/views/handlers/conftest.py | 2 +- .../views/handlers/test_feedback_upload.py | 2 +- .../views/handlers/test_submission_handler.py | 2 +- .../base_pages/calculated-summary.page.js | 4 + tests/functional/generate_pages.py | 10 +- tests/functional/helpers.js | 31 +- ...edirect_to_list_add_block_checkbox.spec.js | 4 +- ...n_redirect_to_list_add_block_radio.spec.js | 4 +- .../calculated_summary.spec.js | 0 .../calculated_summary_test_case.js | 58 +- ...mmary_repeating_and_static_answers.spec.js | 214 ++++++ ...alculated_summary_repeating_blocks.spec.js | 236 ++++++ ...lculated_summary_repeating_section.spec.js | 70 +- .../list_collector_repeating_blocks.spec.js | 328 ++++++++ .../features/validation/sum/dynamic.spec.js | 154 ++++ tests/functional/spec/list_collector.spec.js | 8 +- .../list_collector_driving_question.spec.js | 4 +- ...ollector_driving_question_checkbox.spec.js | 8 +- .../list_collector_section_summary.spec.js | 12 +- .../spec/list_collector_variants.spec.js | 6 +- .../spec/question_description.spec.js | 27 + tests/integration/create_token.py | 43 +- tests/integration/integration_test_case.py | 93 ++- tests/integration/questionnaire/__init__.py | 6 + .../test_questionnaire_calculated_summary.py | 109 ++- ...onnaire_list_collector_repeating_blocks.py | 476 ++++++++++++ tests/integration/routes/test_session.py | 130 +++- ...pplementary_data-encryption-private-v1.pem | 51 ++ 146 files changed, 9410 insertions(+), 1114 deletions(-) rename app/authentication/{auth_payload_version.py => auth_payload_versions.py} (53%) create mode 100644 app/data_models/supplementary_data_store.py create mode 100644 app/services/__init__.py create mode 100644 app/services/supplementary_data.py create mode 100644 app/utilities/request_session.py create mode 100644 app/utilities/supplementary_data_parser.py create mode 100644 app/views/handlers/list_repeating_question.py create mode 100644 doc/run-mock-sds-endpoint.md create mode 100644 schemas/test/en/test_list_collector_repeating_blocks_section_summary.json create mode 100644 schemas/test/en/test_list_collector_repeating_blocks_with_hub.json create mode 100644 schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json create mode 100644 schemas/test/en/test_new_calculated_summary_repeating_answers_only.json create mode 100644 schemas/test/en/test_new_calculated_summary_repeating_blocks.json create mode 100644 schemas/test/en/test_optional_guidance_and_description.json create mode 100644 schemas/test/en/test_supplementary_data.json create mode 100644 schemas/test/en/test_validation_sum_against_total_dynamic_answers.json create mode 100644 scripts/mock_data/supplementary_data_no_repeat.json create mode 100644 scripts/mock_data/supplementary_data_with_repeat.json create mode 100644 scripts/mock_data/supplementary_data_with_repeat_v2.json create mode 100644 scripts/mock_data/supplementary_dataset_metadata_response.json create mode 100644 scripts/mock_sds_endpoint.py create mode 100644 templates/listrepeatingquestion.html create mode 100644 tests/app/data_model/test_supplementary_data_store.py create mode 100644 tests/app/parser/test_supplementary_data_parser.py create mode 100644 tests/app/services/__init__.py create mode 100644 tests/app/services/conftest.py create mode 100644 tests/app/services/test_request_supplementary_data.py rename tests/functional/spec/features/{ => calculated_summary}/calculated_summary.spec.js (100%) rename tests/functional/spec/features/{ => calculated_summary}/calculated_summary_test_case.js (88%) create mode 100644 tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js create mode 100644 tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js rename tests/functional/spec/features/{ => calculated_summary}/new_calculated_summary_repeating_section.spec.js (87%) create mode 100644 tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js create mode 100644 tests/functional/spec/features/validation/sum/dynamic.spec.js create mode 100644 tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py create mode 100644 tests/jwt-test-keys/sdc-sds-supplementary_data-encryption-private-v1.pem diff --git a/.development.env b/.development.env index f8011aa2d3..44da8d065a 100644 --- a/.development.env +++ b/.development.env @@ -27,3 +27,4 @@ CDN_ASSETS_PATH=/design-system ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log +SDS_API_BASE_URL=http://localhost:5003 diff --git a/.functional-tests.env b/.functional-tests.env index cd5b1c31a7..9da5874de6 100644 --- a/.functional-tests.env +++ b/.functional-tests.env @@ -28,3 +28,4 @@ ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS=35 +SDS_API_BASE_URL=http://localhost:5003 diff --git a/app/authentication/auth_payload_version.py b/app/authentication/auth_payload_versions.py similarity index 53% rename from app/authentication/auth_payload_version.py rename to app/authentication/auth_payload_versions.py index fe4d338d37..f467b7e118 100644 --- a/app/authentication/auth_payload_version.py +++ b/app/authentication/auth_payload_versions.py @@ -3,3 +3,7 @@ class AuthPayloadVersion(Enum): V2 = "v2" + + +class SupplementaryDataSchemaVersion(Enum): + V1 = "v1" diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 4a30e20b1c..5f2ac068e2 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -1,5 +1,6 @@ +from contextlib import contextmanager from datetime import datetime, timedelta, timezone -from typing import Any, Mapping, MutableMapping, Optional +from typing import Any, Generator, Mapping, MutableMapping, Optional from uuid import uuid4 from blinker import ANY @@ -11,6 +12,7 @@ from app.authentication.no_token_exception import NoTokenException from app.authentication.user import User +from app.data_models import QuestionnaireStore from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore from app.globals import ( @@ -132,9 +134,12 @@ def _create_session_data_from_metadata(metadata: Mapping[str, Any]) -> SessionDa ) -def store_session(metadata: MutableMapping) -> None: +@contextmanager +def create_session_questionnaire_store( + metadata: MutableMapping, +) -> Generator[QuestionnaireStore, None, None]: """ - Store new session and metadata + Context to manage creating and saving new session and questionnaire store :param metadata: metadata parsed from jwt token """ # also clear the secure cookie data @@ -155,6 +160,7 @@ def store_session(metadata: MutableMapping) -> None: create_session_store(eq_session_id, user_id, user_ik, session_data) questionnaire_store = get_questionnaire_store(user_id, user_ik) + yield questionnaire_store questionnaire_store.set_metadata(metadata) questionnaire_store.save() diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index 06aa813c9d..d42e6b79fc 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -137,15 +137,15 @@ def remove_answer( return False - def remove_all_answers_for_list_item_id(self, list_item_id: str) -> None: - """Remove all answers associated with a particular list_item_id. + def remove_all_answers_for_list_item_ids(self, *list_item_ids: str) -> None: + """Remove all answers associated with any of the list_item_ids passed in. This method iterates through the entire list of answers. *Not efficient.* """ keys_to_delete = [] for answer in self: - if answer.list_item_id == list_item_id: + if answer.list_item_id and answer.list_item_id in list_item_ids: keys_to_delete.append((answer.answer_id, answer.list_item_id)) for key in keys_to_delete: diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index ecfdcd22ec..6f13c50599 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -223,6 +223,10 @@ def add_list_item(self, list_name: str, primary_person: bool = False) -> str: return list_item_id + def delete_list(self, list_name: str) -> None: + """Deletes the entire list""" + del self._lists[list_name] + def serialize(self) -> list[ListModelDictType]: return [list_model.serialize() for list_model in self] diff --git a/app/data_models/metadata_proxy.py b/app/data_models/metadata_proxy.py index 28e12dee3d..a3b2414674 100644 --- a/app/data_models/metadata_proxy.py +++ b/app/data_models/metadata_proxy.py @@ -7,7 +7,7 @@ from werkzeug.datastructures import ImmutableDict -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.utilities.make_immutable import make_immutable diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index 8c317703c0..777c2f624b 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -1,11 +1,11 @@ from dataclasses import astuple, dataclass -from typing import Iterable, Iterator, MutableMapping, Optional +from typing import Iterable, Iterator, MutableMapping from app.data_models.progress import Progress, ProgressDictType from app.questionnaire.location import Location from app.utilities.types import LocationType -SectionKeyType = tuple[str, Optional[str]] +ProgressKeyType = tuple[str, str | None] @dataclass @@ -21,34 +21,52 @@ def __iter__(self) -> Iterator[tuple[str]]: class ProgressStore: """ - An object that stores and updates references to sections and blocks + An object that stores and updates references to sections and list items that have been started. """ def __init__( - self, in_progress_sections: Optional[Iterable[ProgressDictType]] = None + self, + in_progress_sections_and_repeating_blocks: Iterable[ProgressDictType] + | None = None, ) -> None: """ - Instantiate a ProgressStore object that tracks the status of sections and its completed blocks + Instantiate a ProgressStore object that tracks the progress status of Sections & Repeating Sections, + and their completed blocks, as well as Repeating Blocks for List Items. + - Standard Sections are keyed by Section ID, and a None List Item ID + - Repeating Sections (dynamic Sections created for List Items that have been added using a List Collector) + are keyed by their Section ID, and the List Item ID of the item it is the section for. + - Repeating Blocks for List Items are keyed by the Section ID for the Section in which their List Collector + appears, and the List Item ID. Repeating Blocks progress is only tracked if the List Collector + that created the List Item has Repeating Blocks, and progress of the Repeating Blocks for a List Item + indicates if all required Repeating Blocks from the List Collector have been completed for the List Item. Args: - in_progress_sections: A list of hierarchical dict containing the section status and completed blocks + in_progress_sections_and_repeating_blocks: A list of hierarchical dict containing the completion status + and completed blocks of Sections, Repeating Sections and List Items """ self._is_dirty: bool = False self._is_routing_backwards: bool = False - self._progress: MutableMapping[SectionKeyType, Progress] = self._build_map( - in_progress_sections or [] - ) + self._section_and_repeating_blocks_progress: MutableMapping[ + ProgressKeyType, Progress + ] = self._build_map(in_progress_sections_and_repeating_blocks or []) - def __contains__(self, section_key: SectionKeyType) -> bool: - return section_key in self._progress + def __contains__( + self, section_and_repeating_blocks_progress_key: ProgressKeyType + ) -> bool: + return ( + section_and_repeating_blocks_progress_key + in self._section_and_repeating_blocks_progress + ) @staticmethod - def _build_map(section_progress_list: Iterable[ProgressDictType]) -> MutableMapping: + def _build_map( + section_and_repeating_blocks_progress_list: Iterable[ProgressDictType], + ) -> MutableMapping: """ - Builds the progress_store's data structure from a list of progress dictionaries. + Builds the ProgressStore's data structure from a list of progress dictionaries. - The `section_key` is tuple consisting of `section_id` and the `list_item_id`. - The `section_progress` is a mutableMapping created from the Progress object. + The `progress_key` is tuple consisting of `section_id` and the `list_item_id`. + The `progress` is a mutableMapping created from the Progress object. Example structure: { @@ -63,10 +81,10 @@ def _build_map(section_progress_list: Iterable[ProgressDictType]) -> MutableMapp return { ( - section_progress["section_id"], - section_progress.get("list_item_id"), - ): Progress.from_dict(section_progress) - for section_progress in section_progress_list + progress["section_id"], + progress.get("list_item_id"), + ): Progress.from_dict(progress) + for progress in section_and_repeating_blocks_progress_list } @property @@ -77,77 +95,108 @@ def is_dirty(self) -> bool: def is_routing_backwards(self) -> bool: return self._is_routing_backwards - def is_section_complete( - self, section_id: str, list_item_id: Optional[str] = None + def is_section_or_repeating_blocks_progress_complete( + self, section_id: str, list_item_id: str | None = None ) -> bool: - return (section_id, list_item_id) in self.section_keys( + """ + Return True if the CompletionStatus of the Section or List Item specified by the given section_id and + list_item_id is COMPLETED or INDIVIDUAL_RESPONSE_REQUESTED, else False. + """ + return ( + section_id, + list_item_id, + ) in self.section_and_repeating_blocks_progress_keys( statuses={ CompletionStatus.COMPLETED, CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, } ) - def section_keys( + def section_and_repeating_blocks_progress_keys( self, - statuses: Optional[Iterable[str]] = None, - section_ids: Optional[Iterable[str]] = None, - ) -> list[SectionKeyType]: + statuses: Iterable[str] | None = None, + section_ids: Iterable[str] | None = None, + ) -> list[ProgressKeyType]: + """ + Return the Keys of the Section and Repeating Blocks progresses stored in this ProgressStore. + """ if not statuses: statuses = {*CompletionStatus()} - section_keys = [ + progress_keys = [ section_key - for section_key, section_progress in self._progress.items() + for section_key, section_progress in self._section_and_repeating_blocks_progress.items() if section_progress.status in statuses ] if section_ids is None: - return section_keys + return progress_keys return [ - section_key - for section_key in section_keys - if any(section_id in section_key for section_id in section_ids) + progress_key + for progress_key in progress_keys + if any(section_id in progress_key for section_id in section_ids) ] - def update_section_status( - self, section_status: str, section_id: str, list_item_id: Optional[str] = None + def update_section_or_repeating_blocks_progress_completion_status( + self, + completion_status: str, + section_id: str, + list_item_id: str | None = None, ) -> bool: + """ + Updates the completion status of the section or repeating blocks for a list item specified by the key based on the given section id and list item id. + """ updated = False section_key = (section_id, list_item_id) - if section_key in self._progress: - if self._progress[section_key].status != section_status: + if section_key in self._section_and_repeating_blocks_progress: + if ( + self._section_and_repeating_blocks_progress[section_key].status + != completion_status + ): updated = True - self._progress[section_key].status = section_status + self._section_and_repeating_blocks_progress[ + section_key + ].status = completion_status self._is_dirty = True - elif section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: - self._progress[section_key] = Progress( + elif completion_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: + self._section_and_repeating_blocks_progress[section_key] = Progress( section_id=section_id, list_item_id=list_item_id, block_ids=[], - status=section_status, + status=completion_status, ) self._is_dirty = True return updated - def get_section_status( - self, section_id: str, list_item_id: Optional[str] = None + def get_section_or_repeating_blocks_progress_status( + self, section_id: str, list_item_id: str | None = None ) -> str: - section_key = (section_id, list_item_id) - if section_key in self._progress: - return self._progress[section_key].status + """ + Return the CompletionStatus of the Section or Repeating Blocks for a list item, + specified by the given section_id and list_item_id. + Returns NOT_STARTED if the progress does not exist + """ + progress_key = (section_id, list_item_id) + if progress_key in self._section_and_repeating_blocks_progress: + return self._section_and_repeating_blocks_progress[progress_key].status return CompletionStatus.NOT_STARTED def get_block_status( self, *, block_id: str, section_id: str, list_item_id: str | None = None ) -> str: - section_blocks = self.get_completed_block_ids( + """ + Return the completion status of the block specified by the given block_id, + if it is part of the progress of the given Section or Repeating Blocks for list item + specified by the given section_id or list_item_id + """ + blocks = self.get_completed_block_ids( section_id=section_id, list_item_id=list_item_id ) - if block_id in section_blocks: + if block_id in blocks: return CompletionStatus.COMPLETED return CompletionStatus.NOT_STARTED @@ -155,13 +204,21 @@ def get_block_status( def get_completed_block_ids( self, *, section_id: str, list_item_id: str | None = None ) -> list[str]: - section_key = (section_id, list_item_id) - if section_key in self._progress: - return self._progress[section_key].block_ids + """ + Return the block ids recorded as part of the progress for the Section or Repeating Blocks + for list item specified by the given section_id and list_item_id + """ + progress_key = (section_id, list_item_id) + if progress_key in self._section_and_repeating_blocks_progress: + return self._section_and_repeating_blocks_progress[progress_key].block_ids return [] def add_completed_location(self, location: LocationType) -> None: + """ + Adds the block from the given Location, to the progress specified by the + section id and list item id within the Location. + """ section_id = location.section_id list_item_id = location.list_item_id @@ -172,12 +229,14 @@ def add_completed_location(self, location: LocationType) -> None: if location.block_id not in completed_block_ids: completed_block_ids.append(location.block_id) # type: ignore - section_key = (section_id, list_item_id) + progress_key = (section_id, list_item_id) - if section_key in self._progress: - self._progress[section_key].block_ids = completed_block_ids + if progress_key in self._section_and_repeating_blocks_progress: + self._section_and_repeating_blocks_progress[ + progress_key + ].block_ids = completed_block_ids else: - self._progress[section_key] = Progress( + self._section_and_repeating_blocks_progress[progress_key] = Progress( section_id=section_id, list_item_id=list_item_id, block_ids=completed_block_ids, @@ -187,15 +246,24 @@ def add_completed_location(self, location: LocationType) -> None: self._is_dirty = True def remove_completed_location(self, location: LocationType) -> bool: - section_key = (location.section_id, location.list_item_id) + """ + Removes the block in the given Location, from the progress specified by the + section id and list item id within the Location if it exists in the store. + """ + progress_key = (location.section_id, location.list_item_id) if ( - section_key in self._progress - and location.block_id in self._progress[section_key].block_ids + progress_key in self._section_and_repeating_blocks_progress + and location.block_id + in self._section_and_repeating_blocks_progress[progress_key].block_ids ): - self._progress[section_key].block_ids.remove(location.block_id) + self._section_and_repeating_blocks_progress[progress_key].block_ids.remove( + location.block_id + ) - if not self._progress[section_key].block_ids: - self._progress[section_key].status = CompletionStatus.IN_PROGRESS + if not self._section_and_repeating_blocks_progress[progress_key].block_ids: + self._section_and_repeating_blocks_progress[ + progress_key + ].status = CompletionStatus.IN_PROGRESS self._is_dirty = True return True @@ -209,32 +277,32 @@ def remove_progress_for_list_item_id(self, list_item_id: str) -> None: *Not efficient.* """ - section_keys_to_delete = [ + progress_keys_to_delete = [ (section_id, progress_list_item_id) - for section_id, progress_list_item_id in self._progress + for section_id, progress_list_item_id in self._section_and_repeating_blocks_progress if progress_list_item_id == list_item_id ] - for section_key in section_keys_to_delete: - del self._progress[section_key] + for progress_key in progress_keys_to_delete: + del self._section_and_repeating_blocks_progress[progress_key] self._is_dirty = True def serialize(self) -> list[Progress]: - return list(self._progress.values()) + return list(self._section_and_repeating_blocks_progress.values()) def remove_location_for_backwards_routing(self, location: Location) -> None: self.remove_completed_location(location=location) self._is_routing_backwards = True def clear(self) -> None: - self._progress.clear() + self._section_and_repeating_blocks_progress.clear() self._is_dirty = True - def started_section_keys( - self, section_ids: Optional[Iterable[str]] = None - ) -> list[SectionKeyType]: - return self.section_keys( + def started_section_and_repeating_blocks_progress_keys( + self, section_ids: Iterable[str] | None = None + ) -> list[ProgressKeyType]: + return self.section_and_repeating_blocks_progress_keys( statuses={CompletionStatus.COMPLETED, CompletionStatus.IN_PROGRESS}, section_ids=section_ids, ) diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 1848a3d4b2..96383bb687 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -7,6 +7,7 @@ from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.rules.utils import parse_iso_8601_datetime from app.utilities.json import json_dumps, json_loads @@ -28,13 +29,12 @@ def __init__( self.version = version self._metadata: MutableMapping = {} # self.metadata is a read-only view over self._metadata - self.metadata: Optional[MetadataProxy] = ( - MetadataProxy.from_dict(self._metadata) if self._metadata else None - ) + self.metadata: MetadataProxy | None = None self.response_metadata: MutableMapping = {} self.list_store = ListStore() self.answer_store = AnswerStore() self.progress_store = ProgressStore() + self.supplementary_data_store = SupplementaryDataStore() self.submitted_at: Optional[datetime] self.collection_exercise_sid: Optional[str] @@ -63,10 +63,79 @@ def set_metadata(self, to_set: MutableMapping) -> QuestionnaireStore: return self + def set_supplementary_data(self, to_set: MutableMapping) -> None: + """ + Used to set or update the supplementary data whenever the sds endpoint is called + (Which should be once per session, but only if the sds_dataset_id has changed) + + this updates ListStore to add/update any lists for supplementary data and stores the + identifier -> list_item_id mappings in the supplementary data store to use in the payload at the end + """ + if self.supplementary_data_store.list_mappings: + self._remove_old_supplementary_lists_and_answers(new_data=to_set) + + list_mappings = { + list_name: self._create_supplementary_list( + list_name=list_name, list_data=list_data + ) + for list_name, list_data in to_set.get("items", {}).items() + } + + self.supplementary_data_store = SupplementaryDataStore( + supplementary_data=to_set, list_mappings=list_mappings + ) + + def _create_supplementary_list( + self, *, list_name: str, list_data: list[dict] + ) -> dict[str, str]: + """ + Creates or updates a list in ListStore based off supplementary data + returns the identifier -> list_item_id mappings used + """ + list_mapping: dict[str, str] = {} + for list_item in list_data: + identifier = list_item["identifier"] + # if any pre-existing supplementary data already has a mapping for this list item + # then its already in the list store and doesn't require adding + if not ( + list_item_id := self.supplementary_data_store.list_mappings.get( + list_name, {} + ).get(identifier) + ): + list_item_id = self.list_store.add_list_item(list_name) + list_mapping[identifier] = list_item_id + return list_mapping + + def _remove_old_supplementary_lists_and_answers( + self, new_data: MutableMapping + ) -> None: + """ + In the case that existing supplementary data is being replaced with new data: any list items in the old data + but not the new data are removed from the list store and related answers are deleted + :param new_data - the new supplementary data for comparison + """ + deleted_list_item_ids: set[str] = set() + for list_name, mappings in self.supplementary_data_store.list_mappings.items(): + if list_name in new_data.get("items", {}): + new_identifiers = [ + item["identifier"] for item in new_data["items"][list_name] + ] + for identifier, list_item_id in mappings.items(): + if identifier not in new_identifiers: + self.list_store.delete_list_item(list_name, list_item_id) + deleted_list_item_ids.add(list_item_id) + else: + self.list_store.delete_list(list_name) + deleted_list_item_ids.update(mappings.values()) + self.answer_store.remove_all_answers_for_list_item_ids(*deleted_list_item_ids) + def _deserialize(self, data: str) -> None: json_data = json_loads(data) self.progress_store = ProgressStore(json_data.get("PROGRESS")) self.set_metadata(json_data.get("METADATA", {})) + self.supplementary_data_store = SupplementaryDataStore.deserialize( + json_data.get("SUPPLEMENTARY_DATA", {}) + ) self.answer_store = AnswerStore(json_data.get("ANSWERS")) self.list_store = ListStore.deserialize(json_data.get("LISTS")) self.response_metadata = json_data.get("RESPONSE_METADATA", {}) @@ -75,6 +144,7 @@ def serialize(self) -> str: data = { "METADATA": self._metadata, "ANSWERS": list(self.answer_store), + "SUPPLEMENTARY_DATA": self.supplementary_data_store.serialize(), "LISTS": self.list_store.serialize(), "PROGRESS": self.progress_store.serialize(), "RESPONSE_METADATA": self.response_metadata, diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py new file mode 100644 index 0000000000..e3dac5bd34 --- /dev/null +++ b/app/data_models/supplementary_data_store.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +from functools import cached_property +from typing import Iterable, Mapping, MutableMapping, TypeAlias + +from werkzeug.datastructures import ImmutableDict + +from app.utilities.make_immutable import make_immutable + +SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] +SupplementaryDataValueType: TypeAlias = dict | str | list | None + + +class InvalidSupplementaryDataSelector(Exception): + pass + + +class SupplementaryDataStore: + """ + An object that stores supplementary data + """ + + def __init__( + self, + supplementary_data: MutableMapping | None = None, + list_mappings: Mapping[str, Mapping] | None = None, + ): + """ + Initialised with the "data" value from the sds api response + and list mappings of the form { list_name: { identifier : list_item_id }} + """ + self._raw_data = supplementary_data or {} + self._list_mappings = list_mappings or {} + # use shallow copy of the data, as items will be popped off + self._data_map = self._build_map({**self._raw_data}) + + @cached_property + def raw_data(self) -> ImmutableDict: + data: ImmutableDict = make_immutable(self._raw_data) + return data + + @cached_property + def list_mappings(self) -> ImmutableDict: + mappings: ImmutableDict = make_immutable(self._list_mappings) + return mappings + + def _build_map( + self, data: MutableMapping + ) -> dict[SupplementaryDataKeyType, SupplementaryDataValueType]: + """ + The raw data will be of the form + { + "some_key": "some_value" + "items": { + "some_list": [ + {"identifier": ... }, + {"identifier": ... } + ] + } + } + each list item has an identifier which will link to a list-item-id in self.list_mappings + for example: {"some_list": {identifier-1: list_item_id-1, identifier-2: list_item_id-2 }} + + resulting map based off list mappings has the form + { + ("some_key", None): "some_value" + ("some_list", list_item_id-1): {"identifier": identifier-1, ...} + ("some_list", list_item_id-2): {"identifier": identifier-2, ...} + } + """ + list_items = data.pop("items", {}) + resulting_map: dict[SupplementaryDataKeyType, SupplementaryDataValueType] = { + (key, None): value for key, value in data.items() + } + for list_name, list_data in list_items.items(): + for item in list_data: + identifier = item["identifier"] + list_item_id = self._list_mappings[list_name][identifier] + resulting_map[(list_name, list_item_id)] = item + return resulting_map + + def get_data( + self, + *, + identifier: str, + selectors: Iterable[str] | None = None, + list_item_id: str | None = None, + ) -> SupplementaryDataValueType: + """ + Used to retrieve supplementary data in a similar style to AnswerStore + the identifier is the top level key for static data, and the name of the list for list items + selectors are used to reference nested data + + For example if you wanted the identifier for the first item in "some_list" + it would be get_data(identifier="some_list", selectors=["identifier"], list_item_id=list_item_id-1) + """ + value = self._data_map.get((identifier, list_item_id)) + # for nested data, index with each selector + for selector in selectors or []: + if not isinstance(value, dict): + raise InvalidSupplementaryDataSelector( + f"Cannot use the selector `{selector}` on non-nested data" + ) + value = value[selector] + + return value + + def serialize(self) -> dict: + return { + "data": self._raw_data, + "list_mappings": self._list_mappings, + } + + @classmethod + def deserialize(cls, serialized_data: Mapping) -> SupplementaryDataStore: + if not serialized_data: + return cls() + + return cls( + supplementary_data=serialized_data["data"], + list_mappings=serialized_data["list_mappings"], + ) diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index ef7922ddf0..d2d971d307 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -387,8 +387,23 @@ def _get_period_limits( return minimum, maximum def _get_formatted_calculation_values( - self, answers_list: Sequence[str] + self, answers_sequence: Sequence[str] ) -> list[str]: + answers_list: list[str] = [] + block_id = self.location.block_id if self.location else None + if block_id and block_id in self.schema.dynamic_answers_parent_block_ids: + list_name = self.schema.get_list_name_for_dynamic_answer(block_id) + list_item_ids = self.list_store[list_name] + for answer_id in answers_sequence: + if self.schema.is_answer_dynamic(answer_id): + answers_list.extend( + f"{answer_id}-{list_item_id}" for list_item_id in list_item_ids + ) + else: + answers_list.append(answer_id) + else: + answers_list = list(answers_sequence) + return [ self.get_data(answer_id).replace(" ", "").replace(",", "") for answer_id in answers_list diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 5f711d60f0..bdead05e8a 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -583,7 +583,6 @@ def map_summary_item_config( calculated_question: Optional[dict[str, list]], remove_link_text: str | None = None, remove_link_aria_label: str | None = None, - icon: Optional[str] = None, ) -> list[Union[dict[str, list], SummaryRow]]: rows: list[Union[dict[str, list], SummaryRow]] = [] @@ -613,7 +612,6 @@ def map_summary_item_config( else: list_collector_rows = map_list_collector_config( list_items=block["list"]["list_items"], - icon=icon, edit_link_text=edit_link_text, edit_link_aria_label=edit_link_aria_label, remove_link_text=remove_link_text, @@ -639,17 +637,17 @@ def map_summary_item_config_processor() -> dict[str, Callable]: # pylint: disable=too-many-locals @blueprint.app_template_filter() # type: ignore def map_list_collector_config( - list_items: list[dict[str, Union[str, int]]], - icon: Optional[str], + list_items: list[dict[str, str | int]], + render_icon: bool = False, edit_link_text: str = "", edit_link_aria_label: str = "", - remove_link_text: Optional[str] = None, - remove_link_aria_label: Optional[str] = None, - related_answers: Optional[dict] = None, - item_label: Optional[str] = None, - item_anchor: Optional[str] = None, -) -> list[Union[dict[str, list], SummaryRow]]: - rows: list[Union[dict[str, list], SummaryRow]] = [] + remove_link_text: str | None = None, + remove_link_aria_label: str | None = None, + related_answers: dict | None = None, + item_label: str | None = None, + item_anchor: str | None = None, +) -> list[dict[str, list] | SummaryRow]: + rows: list[dict[str, list] | SummaryRow] = [] for index, list_item in enumerate(list_items, start=1): item_name = list_item.get("item_title") @@ -695,6 +693,14 @@ def map_list_collector_config( } ) + icon = ( + "check" + if render_icon + and list_item.get("repeating_blocks") + and list_item.get("is_complete") + else None + ) + row_item = { "iconType": icon, "actions": actions, diff --git a/app/keys.py b/app/keys.py index 74eafb5224..a6ef1c6346 100644 --- a/app/keys.py +++ b/app/keys.py @@ -1,2 +1,3 @@ KEY_PURPOSE_AUTHENTICATION = "authentication" KEY_PURPOSE_SUBMISSION = "submission" +KEY_PURPOSE_SDS = "supplementary_data" diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index b3d9ad5d1a..6268aecd66 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -50,7 +50,7 @@ def get_block_ids_for_calculated_summary_dependencies( if key in sections_to_ignore: continue - if key in progress_store.started_section_keys(): + if key in progress_store.started_section_and_repeating_blocks_progress_keys(): routing_path = path_finder.routing_path(*key) block_ids_by_section[key] = routing_path.block_ids diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 89c50b18df..10348f179d 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -73,7 +73,8 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: block_id for dependent_section in dependencies_for_section for block_id in self.routing_path(dependent_section) - if (dependent_section, None) in self.progress_store.started_section_keys() + if (dependent_section, None) + in self.progress_store.started_section_and_repeating_blocks_progress_keys() ] def _get_not_skipped_blocks_in_section( @@ -277,7 +278,7 @@ def _remove_current_blocks_answers_for_backwards_routing( ) self.progress_store.remove_location_for_backwards_routing(this_location) - self.progress_store.update_section_status( + self.progress_store.update_section_or_repeating_blocks_progress_completion_status( CompletionStatus.IN_PROGRESS, this_location.section_id ) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index f9fbd21ec9..8b0c1fa94a 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -1,5 +1,5 @@ from copy import deepcopy -from typing import Any, Mapping, MutableMapping +from typing import Mapping, MutableMapping from jsonpointer import resolve_pointer, set_pointer @@ -124,9 +124,9 @@ def render( """ Transform the current schema json to a fully rendered dictionary """ - data_to_render_mutable: dict[ - str, Any - ] = QuestionnaireSchema.get_mutable_deepcopy(data_to_render) + data_to_render_mutable: dict = QuestionnaireSchema.get_mutable_deepcopy( + data_to_render + ) self._handle_and_resolve_dynamic_answers(data_to_render_mutable) @@ -156,9 +156,7 @@ def render( set_pointer(data_to_render_mutable, pointer, rendered_text) return data_to_render_mutable - def _handle_and_resolve_dynamic_answers( - self, data_to_render_mutable: dict[str, Any] - ) -> None: + def _handle_and_resolve_dynamic_answers(self, data_to_render_mutable: dict) -> None: pointers = find_pointers_containing(data_to_render_mutable, "dynamic_answers") for pointer in pointers: diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 7b2c660a71..89eca5bdaa 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -67,7 +67,7 @@ def remove_empty_from_list(list_to_filter: Sequence[str]) -> list[str]: :param list_to_filter: anything that is iterable :return: a list with no empty values - In this filter the following values are considered non empty: + In this filter the following values are considered empty: - None - any empty sequence, for example, '', (), []. - any empty mapping, for example, {}. diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 2eb751424d..cfff180db3 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -23,6 +23,7 @@ "ListEditQuestion", "ListRemoveQuestion", "PrimaryPersonListAddOrEditQuestion", + "ListRepeatingQuestion", ] RELATIONSHIP_CHILDREN = ["UnrelatedQuestion"] @@ -81,6 +82,9 @@ def __init__( ] = defaultdict(set) self._language_code = language_code self._questionnaire_json = questionnaire_json + self._list_names_by_list_repeating_block_id: dict[str, str] = {} + self._repeating_block_answer_ids: set[str] = set() + self.dynamic_answers_parent_block_ids: set[str] = set() # The ordering here is required as they depend on each other. self._sections_by_id = self._get_sections_by_id() @@ -88,7 +92,8 @@ def __init__( self._blocks_by_id = self._get_blocks_by_id() self._questions_by_id = self._get_questions_by_id() self._answers_by_id = self._get_answers_by_id() - self._dynamic_answer_ids: set[None] = set() + self._dynamic_answer_ids: set[str] = set() + self._list_dependent_block_additional_dependencies: dict[str, set[str]] = {} # Post schema parsing. self._populate_answer_dependencies() @@ -232,6 +237,14 @@ def is_view_submitted_response_enabled(self) -> bool: is_enabled: bool = schema.get("view_response", False) return is_enabled + @cached_property + def list_names_by_list_repeating_block_id(self) -> ImmutableDict[str, str]: + return ImmutableDict(self._list_names_by_list_repeating_block_id) + + @cached_property + def list_collector_repeating_block_ids(self) -> list[str]: + return list(self._list_names_by_list_repeating_block_id.keys()) + def get_all_when_rules_section_dependencies_for_section( self, section_id: str ) -> set[str]: @@ -289,6 +302,14 @@ def _get_blocks_by_id(self) -> dict[str, ImmutableDict]: nested_block_id = nested_block["id"] blocks[nested_block_id] = nested_block self._parent_id_map[nested_block_id] = block_id + if repeating_blocks := block.get("repeating_blocks"): + for repeating_block in repeating_blocks: + repeating_block_id = repeating_block["id"] + blocks[repeating_block_id] = repeating_block + self._parent_id_map[repeating_block_id] = block_id + self._list_names_by_list_repeating_block_id[ + repeating_block_id + ] = block["for_list"] return blocks @@ -309,10 +330,16 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: for question in self._get_flattened_questions(): question_id = question["id"] + is_for_repeating_block = ( + self._parent_id_map[question_id] + in self.list_collector_repeating_block_ids + ) for answer in get_answers_from_question(question): answer_id = answer["id"] self._parent_id_map[answer_id] = question_id + if is_for_repeating_block: + self._repeating_block_answer_ids.add(answer_id) answers_by_id[answer_id].append(answer) for option in answer.get("options", []): @@ -330,26 +357,20 @@ def _populate_answer_dependencies(self) -> None: self._update_answer_dependencies_for_summary(block) continue + if block["type"] == "ListCollector" and block.get("repeating_blocks"): + self._update_dependencies_for_list_repeating_blocks(block) + for question in self.get_all_questions_for_block(block): + self.update_dependencies_for_dynamic_answers( + question=question, block_id=block["id"] + ) + if question["type"] == "Calculated": self._update_answer_dependencies_for_calculations( question["calculations"], block_id=block["id"] ) continue - if dynamic_answers := question.get("dynamic_answers"): - for answer in dynamic_answers["answers"]: - value_source = dynamic_answers["values"] - self._update_answer_dependencies_for_value_source( - value_source, block_id=block["id"], answer_id=answer["id"] - ) - - self._dynamic_answer_ids.add(answer["id"]) - - self._update_answer_dependencies_for_answer( - answer, block_id=block["id"] - ) - for answer in question.get("answers", []): self._update_answer_dependencies_for_answer( answer, block_id=block["id"] @@ -360,6 +381,23 @@ def _populate_answer_dependencies(self) -> None: option["detail_answer"], block_id=block["id"] ) + def _update_dependencies_for_list_repeating_blocks( + self, list_collector_block: ImmutableDict + ) -> None: + """Blocks depending on repeating questions may need to depend on adding/removing items from the parent list collector, so update the map""" + list_block_dependencies: set[str] = set() + for child in ("add_block", "remove_block"): + if child_block := list_collector_block.get(child): + list_block_dependencies.update( + self.get_answer_ids_for_block(child_block["id"]) + ) + + if list_block_dependencies: + for repeating_block in list_collector_block["repeating_blocks"]: + self._list_dependent_block_additional_dependencies[ + repeating_block["id"] + ] = list_block_dependencies + def _update_answer_dependencies_for_summary(self, block: ImmutableDict) -> None: if block["type"] == "CalculatedSummary": self._update_answer_dependencies_for_calculated_summary_dependency( @@ -373,14 +411,32 @@ def _update_answer_dependencies_for_calculated_summary_dependency( ) -> None: """ update all calculated summary answers to be dependencies of the dependent block + + in the case that one of the calculated summary answers is dynamic/repeating, so has multiple answers for a particular list + the calculated summary block needs to depend on the `remove_block` and `add_block` for the list + so that adding/removing items requires re-confirming the calculated summary """ calculated_summary_answer_ids = get_calculated_summary_answer_ids( calculated_summary_block ) + answer_dependent = self._get_answer_dependent_for_block_id( + block_id=dependent_block["id"] + ) for answer_id in calculated_summary_answer_ids: - self._answer_dependencies_map[answer_id] |= { - self._get_answer_dependent_for_block_id(block_id=dependent_block["id"]) - } + if answer_id in [ + *self._dynamic_answer_ids, + *self._repeating_block_answer_ids, + ]: + # Type ignore: answer_id is valid so block must exist + block_id: str = self.get_block_for_answer_id(answer_id)["id"] # type: ignore + if block_id in self._list_dependent_block_additional_dependencies: + for ( + list_block_id + ) in self._list_dependent_block_additional_dependencies[block_id]: + self._answer_dependencies_map[list_block_id].add( + answer_dependent + ) + self._answer_dependencies_map[answer_id].add(answer_dependent) def _update_answer_dependencies_for_grand_calculated_summary( self, grand_calculated_summary_block: ImmutableDict @@ -473,22 +529,49 @@ def _update_answer_dependencies_for_value_source( ) } if value_source["source"] == "list": - section = self.get_section_for_block_id(block_id) - list_collector = self.get_list_collector_for_list( - # Returns of methods are optional, but they always exist at this point, same with optional returns below - section=section, # type: ignore - for_list=value_source["identifier"], # type: ignore + self._update_answer_dependencies_for_list_source( + block_id=block_id, list_name=value_source["identifier"] ) - add_block_question = self.get_add_block_for_list_collector( # type: ignore - list_collector["id"] # type: ignore - )["question"] - answer_ids_for_block = list( - self.get_answers_for_question_by_id(add_block_question) + + def _update_answer_dependencies_for_list_source( + self, *, block_id: str, list_name: str + ) -> None: + """Updates dependencies for a block depending on a list collector + + This method also stores a map of { block_depending_on_list_source -> {add_block, remove_block} }, because: + blocks like dynamic_answers, don't directly need to depend on the add_block/remove_block, + but a block depending on the dynamic answers might (such as a calculated summary) + """ + # Type ignore: section will always exist at this point, same with optional returns below + section: ImmutableDict = self.get_section_for_block_id(block_id) # type: ignore + list_collector: ImmutableDict = self.get_list_collector_for_list( # type: ignore + section=section, + for_list=list_name, + ) + + add_block_question = self.get_add_block_for_list_collector( # type: ignore + list_collector["id"] + )["question"] + answer_ids_for_block = list( + self.get_answers_for_question_by_id(add_block_question) + ) + for block_answer_id in answer_ids_for_block: + self._answer_dependencies_map[block_answer_id] |= { + self._get_answer_dependent_for_block_id( + block_id=block_id, for_list=list_name + ) + if self.is_block_in_repeating_section(block_id) + # non-repeating blocks such as dynamic-answers could depend on the list + else self._get_answer_dependent_for_block_id(block_id=block_id) + } + self._list_dependent_block_additional_dependencies[block_id] = set( + answer_ids_for_block + ) + # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies + if remove_block_id := self.get_remove_block_id_for_list(list_name): + self._list_dependent_block_additional_dependencies[block_id].update( + self.get_answer_ids_for_block(remove_block_id) ) - for block_answer_id in answer_ids_for_block: - self._answer_dependencies_map[block_answer_id] |= { - self._get_answer_dependent_for_block_id(block_id=block_id, for_list=value_source["identifier"]) # type: ignore - } def _get_answer_dependent_for_block_id( self, @@ -696,14 +779,12 @@ def get_group_for_block_id(self, block_id: str) -> ImmutableDict | None: return self._group_for_block(block_id) def get_first_block_id_for_group(self, group_id: str) -> str | None: - group = self.get_group(group_id) - if group: + if group := self.get_group(group_id): block_id: str = group["blocks"][0]["id"] return block_id def get_first_block_id_for_section(self, section_id: str) -> str | None: - section = self.get_section(section_id) - if section: + if section := self.get_section(section_id): group_id: str = section["groups"][0]["id"] return self.get_first_block_id_for_group(group_id) @@ -734,6 +815,13 @@ def is_answer_in_repeating_section(self, answer_id: str) -> bool | None: def is_answer_dynamic(self, answer_id: str) -> bool: return answer_id in self._dynamic_answer_ids + def is_answer_in_list_collector_repeating_block(self, answer_id: str) -> bool: + return answer_id in self._repeating_block_answer_ids + + def get_list_name_for_dynamic_answer(self, block_id: str) -> str: + # type ignore block always exists at this point + return self.get_block(block_id)["question"]["dynamic_answers"]["values"]["identifier"] # type: ignore + def is_repeating_answer( self, answer_id: str, @@ -763,22 +851,41 @@ def get_default_answer(self, answer_id: str) -> Answer | None: def get_add_block_for_list_collector( self, list_collector_id: str ) -> ImmutableDict | None: - add_block_map = { - "ListCollector": "add_block", - "PrimaryPersonListCollector": "add_or_edit_block", - } if list_collector := self.get_block(list_collector_id): + add_block_map = { + "ListCollector": "add_block", + "PrimaryPersonListCollector": "add_or_edit_block", + } add_block: ImmutableDict = list_collector[ add_block_map[list_collector["type"]] ] return add_block - def get_answer_ids_for_list_items(self, list_collector_id: str) -> list[str] | None: + def get_edit_block_for_list_collector( + self, list_collector_id: str + ) -> ImmutableDict | None: + # Type ignore: for any valid list collector id, list collector block will always exist + return self.get_block(list_collector_id).get("edit_block") # type: ignore + + def get_repeating_blocks_for_list_collector( + self, list_collector_id: str + ) -> list[ImmutableDict] | None: + if list_collector := self.get_block(list_collector_id): + return list_collector.get("repeating_blocks", []) + + def get_answer_ids_for_list_items(self, list_collector_id: str) -> list[str]: """ - Get answer ids used to add items to a list. + Get answer ids used to add items to a list, including any repeating block answers if any exist. """ + answer_ids = [] if add_block := self.get_add_block_for_list_collector(list_collector_id): - return self.get_answer_ids_for_block(add_block["id"]) + answer_ids.extend(self.get_answer_ids_for_block(add_block["id"])) + if repeating_blocks := self.get_repeating_blocks_for_list_collector( + list_collector_id + ): + for repeating_block in repeating_blocks: + answer_ids.extend(self.get_answer_ids_for_block(repeating_block["id"])) + return answer_ids def get_questions(self, question_id: str) -> list[ImmutableDict] | None: """Return a list of questions matching some question id @@ -851,9 +958,7 @@ def get_answer_format_for_calculated_summary( } def get_answer_ids_for_block(self, block_id: str) -> list[str]: - block = self.get_block(block_id) - - if block: + if block := self.get_block(block_id): if block.get("question"): return self.get_answer_ids_for_question(block["question"]) if block.get("question_variants"): @@ -938,11 +1043,12 @@ def is_list_block_type(block_type: str) -> bool: @staticmethod def is_question_block_type(block_type: str) -> bool: - return block_type in [ + return block_type in { "Question", "ListCollectorDrivingQuestion", "ConfirmationQuestion", - ] + "ListRepeatingQuestion", + } @staticmethod def has_address_lookup_answer(question: Mapping) -> bool: @@ -987,7 +1093,11 @@ def _block_for_answer(self, answer_id: str) -> ImmutableDict | None: parent_block_id = self._parent_id_map[block_id] parent_block = self.get_block(parent_block_id) - if parent_block and parent_block["type"] == "ListCollector": + if ( + parent_block + and parent_block["type"] == "ListCollector" + and block_id not in self.list_collector_repeating_block_ids + ): return parent_block return self.get_block(block_id) @@ -1099,7 +1209,11 @@ def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_f elif source == "progress" and identifier: if selector == "section" and identifier != current_section_id: # Type ignore: Added as this will be a set rather than a dict at this point - dependencies_ids_for_progress_value_source["sections"][identifier] = OrderedSet([current_section_id]) # type: ignore + dependencies_ids_for_progress_value_source["sections"][ + identifier + ] = OrderedSet( + [current_section_id] + ) # type: ignore elif selector == "block" and ( section_id := self.get_section_id_for_block_id(identifier) ): @@ -1108,7 +1222,9 @@ def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_f dependencies_ids_for_progress_value_source["blocks"][section_id] = { identifier: OrderedSet() } - dependencies_ids_for_progress_value_source["blocks"][section_id][identifier].add(current_section_id) # type: ignore + dependencies_ids_for_progress_value_source["blocks"][section_id][ + identifier # type: ignore + ].append(current_section_id) return answer_id_list, dependencies_ids_for_progress_value_source @@ -1248,6 +1364,23 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None: if item["for_list"] == list_name and item.get("item_anchor_answer_id"): return f"#{str(item['item_anchor_answer_id'])}" + def update_dependencies_for_dynamic_answers( + self, *, question: Mapping, block_id: str + ) -> None: + if dynamic_answers := question.get("dynamic_answers"): + self.dynamic_answers_parent_block_ids.add(block_id) + for answer in dynamic_answers["answers"]: + value_source = dynamic_answers["values"] + self._update_answer_dependencies_for_value_source( + value_source, + block_id=block_id, + answer_id=answer["id"], + ) + + self._dynamic_answer_ids.add(answer["id"]) + + self._update_answer_dependencies_for_answer(answer, block_id=block_id) + def is_summary_with_calculation(summary_type: str) -> bool: return summary_type in {"GrandCalculatedSummary", "CalculatedSummary"} diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 52143136c2..32fd4b25ff 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,13 +1,13 @@ from collections import defaultdict, namedtuple from itertools import combinations -from typing import Any, Iterable, Mapping +from typing import Iterable, Mapping from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict from app.data_models import AnswerValueTypes, QuestionnaireStore from app.data_models.answer_store import Answer -from app.data_models.progress_store import CompletionStatus, SectionKeyType +from app.data_models.progress_store import CompletionStatus, ProgressKeyType from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location @@ -41,7 +41,7 @@ def __init__( self._router = router self.dependent_block_id_by_section_key: Mapping[ - SectionKeyType, set[str] + ProgressKeyType, set[str] ] = defaultdict(set) self.dependent_sections: set[DependentSection] = set() @@ -147,36 +147,46 @@ def remove_primary_person(self, list_name: str) -> None: Any context for the primary person will be removed """ if list_item_id := self._list_store[list_name].primary_person: - self.remove_list_item_and_answers(list_name, list_item_id) + self.remove_list_item_data(list_name, list_item_id) - def remove_list_item_and_answers(self, list_name: str, list_item_id: str) -> None: - """Remove answers from the answer store and update the list store to remove it. + def remove_list_item_data(self, list_name: str, list_item_id: str) -> None: + """Remove answers from the answer store, remove list item progress from the progress store and update the list store to remove it. Any related relationship answers are re-evaluated for completeness. """ self._list_store.delete_list_item(list_name, list_item_id) - self._answer_store.remove_all_answers_for_list_item_id( - list_item_id=list_item_id - ) + self._answer_store.remove_all_answers_for_list_item_ids(list_item_id) - if answers := self.get_relationship_answers_for_list_name(list_name): + if answers := self._get_relationship_answers_for_list_name(list_name): self._remove_relationship_answers_for_list_item_id(list_item_id, answers) self._update_relationship_question_completeness(list_name) self._progress_store.remove_progress_for_list_item_id(list_item_id=list_item_id) + self._capture_dependencies_for_list_item_removal(list_name) + def _capture_dependencies_for_list_item_removal(self, list_name: str) -> None: + """ + If an item is removed from a list, dependencies of any child blocks need to be captured and their progress updated. + (E.g. dynamic-answers depending on a child block could have validation, so need revisiting if an item is removed) + """ for list_collector in self._schema.get_list_collectors_for_list( for_list=list_name, + # Type ignore: section must exist at this point section=self._schema.get_section(self._current_location.section_id), # type: ignore - # type ignore Section and answer_id below must exist at this point ): - block = self._schema.get_add_block_for_list_collector(list_collector["id"]) - answer_ids = self._schema.get_answer_ids_for_block(block["id"]) # type: ignore - # type ignore non-optional return, always exists - for answer_id in answer_ids: - self._capture_block_dependencies_for_answer(answer_id) - - def get_relationship_answers_for_list_name( + child_blocks = ( + list_collector.get("add_block"), + list_collector.get("remove_block"), + *list_collector.get("repeating_blocks", []), + ) + for child_block in child_blocks: + if child_block: + for answer_id in self._schema.get_answer_ids_for_block( + child_block["id"] + ): + self._capture_block_dependencies_for_answer(answer_id) + + def _get_relationship_answers_for_list_name( self, list_name: str ) -> list[Answer] | None: associated_relationship_collectors = ( @@ -238,13 +248,13 @@ def remove_completed_location(self, location: LocationType | None = None) -> boo location = location or self._current_location return self._progress_store.remove_completed_location(location) - def update_section_status( - self, is_complete: bool, section_id: str, list_item_id: str | None = None + def update_section_or_repeating_blocks_progress_completion_status( + self, *, is_complete: bool, section_id: str, list_item_id: str | None = None ) -> bool: status = ( CompletionStatus.COMPLETED if is_complete else CompletionStatus.IN_PROGRESS ) - return self._progress_store.update_section_status( + return self._progress_store.update_section_or_repeating_blocks_progress_completion_status( status, section_id, list_item_id ) @@ -380,7 +390,7 @@ def _update_section_dependencies(self, dependent_sections: Iterable) -> None: self.dependent_sections.add(DependentSection(section_id, None, None)) def update_answers( - self, form_data: Mapping[str, Any], list_item_id: str | None = None + self, form_data: Mapping, list_item_id: str | None = None ) -> None: list_item_id = list_item_id or self._current_location.list_item_id answers_by_answer_id = self._schema.get_answers_for_question_by_id( @@ -410,7 +420,7 @@ def update_progress_for_dependent_sections(self) -> None: """ evaluated_dependents: list[tuple] = [] - chronological_dependents = self.get_chronological_section_dependents() + chronological_dependents = self._get_chronological_section_dependents() for section in chronological_dependents: if ( @@ -443,7 +453,7 @@ def _evaluate_dependents( ) ) - if self.update_section_status( + if self.update_section_or_repeating_blocks_progress_completion_status( is_complete=is_path_complete, section_id=dependent_section.section_id, list_item_id=dependent_section.list_item_id, @@ -527,10 +537,12 @@ def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: def started_section_keys( self, section_ids: Iterable[str] | None = None - ) -> list[SectionKeyType]: - return self._progress_store.started_section_keys(section_ids) + ) -> list[ProgressKeyType]: + return self._progress_store.started_section_and_repeating_blocks_progress_keys( + section_ids + ) - def get_chronological_section_dependents(self) -> list: + def _get_chronological_section_dependents(self) -> list: sections = list(self._schema.get_section_ids()) return sorted( self.dependent_sections, key=lambda x: sections.index(x.section_id) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 685c5fbd2e..ea4be61b7f 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -4,7 +4,7 @@ from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import SectionKeyType +from app.data_models.progress_store import ProgressKeyType from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.path_finder import PathFinder @@ -97,7 +97,9 @@ def can_access_location( def can_access_hub(self) -> bool: return self._schema.is_flow_hub and all( - self._progress_store.is_section_complete(section_id) + self._progress_store.is_section_or_repeating_blocks_progress_complete( + section_id + ) for section_id in self._schema.get_section_ids_required_for_hub() if section_id in self.enabled_section_ids ) @@ -107,7 +109,9 @@ def can_display_section_summary( ) -> bool: return bool( self._schema.get_summary_for_section(section_id) - ) and self._progress_store.is_section_complete(section_id, list_item_id) + ) and self._progress_store.is_section_or_repeating_blocks_progress_complete( + section_id, list_item_id + ) def routing_path( self, section_id: str, list_item_id: str | None = None @@ -126,8 +130,10 @@ def get_next_location_url( Get the next location in the section. If the section is complete, determine where to go next, whether it be a summary, the hub or the next incomplete location. """ - is_section_complete = self._progress_store.is_section_complete( - location.section_id, location.list_item_id + is_section_complete = ( + self._progress_store.is_section_or_repeating_blocks_progress_complete( + location.section_id, location.list_item_id + ) ) if return_to_url := self._get_return_to_location_url( @@ -255,8 +261,10 @@ def _get_return_to_location_url( return url if is_section_complete is None: - is_section_complete = self._progress_store.is_section_complete( - location.section_id, location.list_item_id + is_section_complete = ( + self._progress_store.is_section_or_repeating_blocks_progress_complete( + location.section_id, location.list_item_id + ) ) if not is_section_complete: @@ -289,11 +297,15 @@ def _get_return_to_for_grand_calculated_summary( return None # Type ignore: if the block is valid, then we'll be able to find a section for it - grand_calculated_summary_section: str = self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore + grand_calculated_summary_section: str = ( + self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore + ) if grand_calculated_summary_section != location.section_id: # the grand calculated summary is in a different section which will have a different routing path # but don't go to it unless the current section is complete - if not self._progress_store.is_section_complete(location.section_id): + if not self._progress_store.is_section_or_repeating_blocks_progress_complete( + location.section_id + ): return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, return_to_block_id=return_to_block_id, @@ -507,13 +519,13 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: def get_enabled_section_keys( self, - ) -> Generator[SectionKeyType, None, None]: + ) -> Generator[ProgressKeyType, None, None]: for section_id in self.enabled_section_ids: repeating_list = self._schema.get_repeating_list_for_section(section_id) if repeating_list: for list_item_id in self._list_store[repeating_list]: - section_key: SectionKeyType = (section_id, list_item_id) + section_key: ProgressKeyType = (section_id, list_item_id) yield section_key else: section_key = (section_id, None) @@ -521,12 +533,16 @@ def get_enabled_section_keys( def _get_first_incomplete_section_key(self) -> tuple[str, str | None] | None: for section_id, list_item_id in self.get_enabled_section_keys(): - if not self._progress_store.is_section_complete(section_id, list_item_id): + if not self._progress_store.is_section_or_repeating_blocks_progress_complete( + section_id, list_item_id + ): return section_id, list_item_id def _get_last_complete_section_key(self) -> tuple[str, str | None] | None: for section_id, list_item_id in list(self.get_enabled_section_keys())[::-1]: - if self._progress_store.is_section_complete(section_id, list_item_id): + if self._progress_store.is_section_or_repeating_blocks_progress_complete( + section_id, list_item_id + ): return section_id, list_item_id def _is_section_enabled(self, section: Mapping) -> bool: diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 26dc082c9f..9ac1f48e38 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -5,12 +5,11 @@ TYPE_CHECKING, Iterable, Mapping, - Optional, Sequence, Sized, + TypeAlias, TypedDict, TypeVar, - Union, ) from babel.dates import format_datetime @@ -29,7 +28,7 @@ ) ComparableValue = TypeVar("ComparableValue", str, int, float, Decimal, date) -NonArrayPrimitiveTypes = Union[str, int, float, Decimal, None] +NonArrayPrimitiveTypes: TypeAlias = str | int | float | Decimal | None DAYS_OF_WEEK = { "MONDAY": 0, @@ -108,7 +107,7 @@ def evaluate_or(values: Iterable[bool]) -> bool: return any(iter(values)) @staticmethod - def evaluate_count(values: Optional[Sized]) -> int: + def evaluate_count(values: Sized | None) -> int: return len(values or []) @staticmethod @@ -132,10 +131,10 @@ def evaluate_any_in(lhs: Sequence, rhs: Sequence) -> bool: @staticmethod def resolve_date_from_string( - date_string: Optional[str], - offset: Optional[DateOffset] = None, + date_string: str | None, + offset: DateOffset | None = None, offset_by_full_weeks: bool = False, - ) -> Optional[date]: + ) -> date | None: datetime_value = parse_datetime(date_string) if not datetime_value: return None @@ -186,9 +185,9 @@ def format_date(self, date_to_format: date, date_format: str) -> str: def _resolve_self_reference( self, - self_reference_value: Union[ValueSourceTypes, date], - operands: Sequence[Union[ValueSourceTypes, date]], - ) -> list[Union[ValueSourceTypes, date]]: + self_reference_value: ValueSourceTypes | date, + operands: Sequence[ValueSourceTypes | date], + ) -> list[ValueSourceTypes | date]: resolved_operands = [] for operand in operands: if isinstance(operand, dict) and QuestionnaireSchema.has_operator(operand): @@ -211,7 +210,7 @@ def _resolve_self_reference( def evaluate_map( self, function: Mapping[str, list], - iterables: Sequence[Union[ValueSourceTypes, date]], + iterables: Sequence[ValueSourceTypes | date], ) -> list[str]: function_operator = next(iter(function)) function_operands = deepcopy(function[function_operator]) @@ -228,7 +227,7 @@ def evaluate_map( def evaluate_option_label_from_value(self, value: str, answer_id: str) -> str: answers = self.schema.get_answers_by_answer_id(answer_id) - label_options: Union[str, dict] = [ + label_options: str | dict = [ options["label"] for answer in answers for options in answer["options"] @@ -242,7 +241,11 @@ def evaluate_option_label_from_value(self, value: str, answer_id: str) -> str: label = self.renderer.render_placeholder(label_options, list_item_id=None) return label - @staticmethod - def evaluate_sum(*args: tuple) -> Union[int, float, Decimal]: - # type ignore added as will only return int, float or decimal - return sum(value for value in args if isinstance(value, (int, float, Decimal))) # type: ignore + def evaluate_sum(self, *args: tuple) -> int | float | Decimal: + """recursively evaluate the sum of any list-like arguments""" + return sum( + # Cannot use Iterable or Sequence as the type check for value as this would include primitive types like str + self.evaluate_sum(*value) if isinstance(value, (list, tuple)) else value + for value in args + if isinstance(value, (int, float, Decimal, list, tuple)) + ) diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 940a82ff2e..0677d86bbc 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from datetime import date from decimal import Decimal -from typing import Generator, Iterable, MutableMapping, Optional, Sequence, Union +from typing import Generator, Iterable, MutableMapping, Sequence, TypeAlias from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy @@ -16,9 +16,10 @@ ) from app.utilities.types import LocationType -RuleEvaluatorTypes = Union[ - bool, Optional[date], list[str], list[date], int, float, Decimal -] +RuleEvaluatorTypes: TypeAlias = ( + bool | date | list[str] | list[date] | int | float | Decimal | None +) +ResolvedOperand: TypeAlias = bool | date | ValueSourceTypes | None @dataclass @@ -30,7 +31,7 @@ class RuleEvaluator: response_metadata: MutableMapping location: LocationType | None progress_store: ProgressStore - routing_path_block_ids: Iterable | None = None + routing_path_block_ids: Iterable[str] | None = None language: str = DEFAULT_LANGUAGE_CODE # pylint: disable=attribute-defined-outside-init @@ -63,7 +64,7 @@ def __post_init__(self) -> None: language=self.language, schema=self.schema, renderer=renderer ) - def _evaluate(self, rule: dict[str, Sequence]) -> Union[bool, Optional[date]]: + def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operator_name = next(iter(rule)) operator = Operator(operator_name, self.operations) operands = rule[operator_name] @@ -73,7 +74,7 @@ def _evaluate(self, rule: dict[str, Sequence]) -> Union[bool, Optional[date]]: f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" ) - resolved_operands: Iterable[Union[bool, Optional[date], ValueSourceTypes]] + resolved_operands: Iterable[ResolvedOperand] if operator_name == Operator.MAP: resolved_iterables = self._resolve_operand(operands[1]) @@ -83,9 +84,7 @@ def _evaluate(self, rule: dict[str, Sequence]) -> Union[bool, Optional[date]]: return operator.evaluate(resolved_operands) - def _resolve_operand( - self, operand: ValueSourceTypes - ) -> Union[bool, Optional[date], ValueSourceTypes]: + def _resolve_operand(self, operand: ValueSourceTypes) -> ResolvedOperand: if isinstance(operand, dict) and "source" in operand: return self.value_source_resolver.resolve(operand) @@ -96,7 +95,7 @@ def _resolve_operand( def get_resolved_operands( self, operands: Sequence[ValueSourceTypes] - ) -> Generator[Union[bool, Optional[date], ValueSourceTypes], None, None]: + ) -> Generator[ResolvedOperand, None, None]: for operand in operands: yield self._resolve_operand(operand) diff --git a/app/questionnaire/schema_utils.py b/app/questionnaire/schema_utils.py index 28d7830ae2..f5da936495 100644 --- a/app/questionnaire/schema_utils.py +++ b/app/questionnaire/schema_utils.py @@ -18,9 +18,7 @@ def find_pointers_containing( if search_key in input_data: yield pointer or "" for k, v in input_data.items(): - if (isinstance(v, dict)) and search_key in v: - yield pointer + "/" + k if pointer else "/" + k - elif isinstance(v, (list, tuple, dict)): + if isinstance(v, (list, tuple, dict)): yield from find_pointers_containing( v, search_key, pointer + "/" + k if pointer else "/" + k ) diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index faa83774e1..bc89cbaccf 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -1,12 +1,16 @@ from dataclasses import dataclass from decimal import Decimal -from typing import Callable, Iterable, Mapping, MutableMapping +from typing import Callable, Iterable, Mapping, MutableMapping, TypeAlias from markupsafe import Markup from werkzeug.datastructures import ImmutableDict from app.data_models import ProgressStore -from app.data_models.answer import AnswerValueTypes, escape_answer_value +from app.data_models.answer import ( + AnswerValueEscapedTypes, + AnswerValueTypes, + escape_answer_value, +) from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListModel, ListStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException @@ -15,9 +19,10 @@ from app.questionnaire.rules import rule_evaluator from app.utilities.types import LocationType -ValueSourceTypes = None | str | int | Decimal | list -ValueSourceEscapedTypes = Markup | list[Markup] -IntOrDecimal = int | Decimal +ValueSourceTypes: TypeAlias = None | str | int | Decimal | list +ValueSourceEscapedTypes: TypeAlias = Markup | list[Markup] +IntOrDecimal: TypeAlias = int | Decimal +ResolvedAnswerList: TypeAlias = list[AnswerValueTypes | AnswerValueEscapedTypes | None] @dataclass @@ -42,10 +47,19 @@ def _is_answer_on_path(self, answer_id: str) -> bool: return True def _is_block_on_path(self, block_id: str) -> bool: - return ( - self.routing_path_block_ids is not None - and block_id in self.routing_path_block_ids - ) + # other usages of this function than _is_answer_on_path don't have this check so require it here + if not self.routing_path_block_ids: + return True + + if block_id in self.schema.list_collector_repeating_block_ids: + # repeating blocks aren't on the path, so check the parent list collector + list_name = self.schema.list_names_by_list_repeating_block_id[block_id] + # Type ignore: section and list collector will both exist if the block is repeating + section: ImmutableDict = self.schema.get_section_for_block_id(block_id) # type: ignore + list_collector_block: ImmutableDict = self.schema.get_list_collector_for_list(section, list_name) # type: ignore + return list_collector_block["id"] in self.routing_path_block_ids + + return block_id in self.routing_path_block_ids def _get_answer_value( self, @@ -100,12 +114,64 @@ def _resolve_list_item_id_for_value_source( else None ) + def _resolve_repeating_answers_for_list( + self, *, answer_id: str, list_name: str + ) -> ResolvedAnswerList: + """Return the list of answers in answer store that correspond to the given list name and dynamic/repeating answer_id""" + answer_values: ResolvedAnswerList = [] + for list_item_id in self.list_store[list_name]: + answer_value = self._get_answer_value( + answer_id=answer_id, list_item_id=list_item_id + ) + if answer_value is not None: + answer_values.append( + escape_answer_value(answer_value) + if self.escape_answer_values + else answer_value + ) + return answer_values + + def _resolve_dynamic_answers( + self, + answer_id: str, + ) -> ResolvedAnswerList | None: + # Type ignore: block must exist for this function to be called + question = self.schema.get_block_for_answer_id(answer_id).get("question", {}) # type: ignore + dynamic_answers = question["dynamic_answers"] + values = dynamic_answers["values"] + if values["source"] == "list": + return self._resolve_repeating_answers_for_list( + answer_id=answer_id, list_name=values["identifier"] + ) + + def _resolve_list_repeating_block_answers( + self, answer_id: str + ) -> ResolvedAnswerList: + # Type ignore: block must exist for this function to be called + repeating_block: ImmutableDict = self.schema.get_block_for_answer_id(answer_id) # type: ignore + list_name = self.schema.list_names_by_list_repeating_block_id[ + repeating_block["id"] + ] + return self._resolve_repeating_answers_for_list( + answer_id=answer_id, list_name=list_name + ) + def _resolve_answer_value_source( self, value_source: Mapping ) -> ValueSourceEscapedTypes | ValueSourceTypes: + """resolves answer value by first checking if the answer is dynamic whilst not in a repeating section, + which indicates that it is a repeating answer resolving to a list. Otherwise, retrieve answer value as normal. + """ list_item_id = self._resolve_list_item_id_for_value_source(value_source) answer_id = value_source["identifier"] + # if not in a repeating section and the id is for a list of dynamic/repeating block answers, then return the list of values + if not list_item_id: + if self.schema.is_answer_dynamic(answer_id): + return self._resolve_dynamic_answers(answer_id) + if self.schema.is_answer_in_list_collector_repeating_block(answer_id): + return self._resolve_list_repeating_block_answers(answer_id) + answer_value = self._get_answer_value( answer_id=answer_id, list_item_id=list_item_id ) @@ -130,7 +196,7 @@ def _resolve_progress_value_source( if selector == "section": # List item id is set to None here as we do not support checking progress value sources for # repeating sections - return self.progress_store.get_section_status( + return self.progress_store.get_section_or_repeating_blocks_progress_status( section_id=identifier, list_item_id=None ) @@ -172,9 +238,7 @@ def _resolve_calculated_summary_value_source( """ calculated_summary_block: ImmutableDict = self.schema.get_block(value_source["identifier"]) # type: ignore - if self.routing_path_block_ids and not self._is_block_on_path( - calculated_summary_block["id"] - ): + if not self._is_block_on_path(calculated_summary_block["id"]): return None calculation = calculated_summary_block["calculation"] diff --git a/app/routes/errors.py b/app/routes/errors.py index 2d8f8d2b6c..da309490d9 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -22,6 +22,11 @@ from app.globals import get_metadata from app.helpers.language_helper import handle_language from app.helpers.template_helpers import get_survey_config, render_template +from app.services.supplementary_data import ( + InvalidSupplementaryData, + MissingSupplementaryDataKey, + SupplementaryDataRequestFailed, +) from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter.previously_submitted_exception import PreviouslySubmittedException from app.submitter.submission_failed import SubmissionFailedException @@ -189,6 +194,18 @@ def too_many_feedback_requests( ) +@errors_blueprint.app_errorhandler(SupplementaryDataRequestFailed) +@errors_blueprint.app_errorhandler(MissingSupplementaryDataKey) +@errors_blueprint.app_errorhandler(InvalidSupplementaryData) +def supplementary_data_request_failed( + exception: SupplementaryDataRequestFailed + | MissingSupplementaryDataKey + | InvalidSupplementaryData, +) -> tuple[str, int]: + log_exception(exception, 500) + return _render_error_page(500, template=500) + + @errors_blueprint.app_errorhandler(SubmissionFailedException) def submission_failed( exception: SubmissionFailedException, diff --git a/app/routes/flush.py b/app/routes/flush.py index 28cb2093b2..e99cbb6b89 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -7,7 +7,7 @@ from sdc.crypto.key_store import KeyStore from structlog import contextvars, get_logger -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.authentication.user import User from app.authentication.user_id_generator import UserIDGenerator from app.data_models import QuestionnaireStore diff --git a/app/routes/session.py b/app/routes/session.py index 3a51b380b3..f377b89ffc 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -11,9 +11,13 @@ from werkzeug.exceptions import Unauthorized from werkzeug.wrappers.response import Response -from app.authentication.auth_payload_version import AuthPayloadVersion -from app.authentication.authenticator import decrypt_token, store_session +from app.authentication.auth_payload_versions import AuthPayloadVersion +from app.authentication.authenticator import ( + create_session_questionnaire_store, + decrypt_token, +) from app.authentication.jti_claim_storage import JtiTokenUsed, use_jti_claim +from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store, get_session_timeout_in_seconds from app.helpers.template_helpers import ( @@ -24,6 +28,7 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.routes.errors import _render_error_page +from app.services.supplementary_data import get_supplementary_data_v1 from app.utilities.metadata_parser import validate_runner_claims from app.utilities.metadata_parser_v2 import ( validate_questionnaire_claims, @@ -113,7 +118,8 @@ def login() -> Response: logger.info("decrypted token and parsed metadata") - store_session(claims) + with create_session_questionnaire_store(claims) as questionnaire_store: + _set_questionnaire_supplementary_data(questionnaire_store, metadata) cookie_session["expires_in"] = get_session_timeout_in_seconds(g.schema) @@ -132,6 +138,40 @@ def login() -> Response: return redirect(url_for("questionnaire.get_questionnaire")) +def _set_questionnaire_supplementary_data( + questionnaire_store: QuestionnaireStore, metadata: MetadataProxy +) -> None: + """ + If the survey metadata has an sds dataset id, and it either doesn't match what it stored, or there is no stored supplementary data + then fetch it and add it to the store + """ + if not (new_sds_dataset_id := metadata["sds_dataset_id"]): + return + + existing_sds_dataset_id = ( + questionnaire_store.metadata.survey_metadata["sds_dataset_id"] + if questionnaire_store.metadata and questionnaire_store.metadata.survey_metadata + else None + ) + + if existing_sds_dataset_id == new_sds_dataset_id: + # no need to fetch again + return + + supplementary_data = get_supplementary_data_v1( + # Type ignore: survey_id and either ru_ref or qid are required for schemas that use supplementary data + dataset_id=new_sds_dataset_id, + identifier=metadata["ru_ref"] or metadata["qid"], # type: ignore + survey_id=metadata["survey_id"], # type: ignore + ) + logger.info( + "fetched supplementary data", + survey_id=metadata["survey_id"], + sds_dataset_id=new_sds_dataset_id, + ) + questionnaire_store.set_supplementary_data(supplementary_data["data"]) + + def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: # Type ignore: decrypted_token["exp"] will return a valid timestamp with compatible typing expires_at = datetime.fromtimestamp(decrypted_token["exp"], tz=timezone.utc) # type: ignore diff --git a/app/services/__init__.py b/app/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py new file mode 100644 index 0000000000..cd27b22393 --- /dev/null +++ b/app/services/supplementary_data.py @@ -0,0 +1,129 @@ +import json +from typing import Mapping, MutableMapping +from urllib.parse import urlencode + +from flask import current_app +from marshmallow import ValidationError +from requests import RequestException +from sdc.crypto.jwe_helper import InvalidTokenException, JWEHelper +from sdc.crypto.key_store import KeyStore +from structlog import get_logger + +from app.keys import KEY_PURPOSE_SDS +from app.utilities.request_session import get_retryable_session +from app.utilities.supplementary_data_parser import validate_supplementary_data_v1 + +SUPPLEMENTARY_DATA_REQUEST_BACKOFF_FACTOR = 0.2 +SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES = 2 # Totals no. of request should be 3. The initial request + SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES +SUPPLEMENTARY_DATA_REQUEST_TIMEOUT = 3 +SUPPLEMENTARY_DATA_REQUEST_RETRY_STATUS_CODES = [ + 408, + 429, + 500, + 502, + 503, + 504, +] + +logger = get_logger() + + +class SupplementaryDataRequestFailed(Exception): + def __str__(self) -> str: + return "Supplementary Data request failed" + + +class MissingSupplementaryDataKey(Exception): + pass + + +class InvalidSupplementaryData(Exception): + pass + + +def get_supplementary_data_v1( + *, dataset_id: str, identifier: str, survey_id: str +) -> dict: + # Type ignore: current_app is a singleton in this application and has the key_store key in its eq attribute. + key_store = current_app.eq["key_store"] # type: ignore + if not key_store.get_key(purpose=KEY_PURPOSE_SDS, key_type="private"): + raise MissingSupplementaryDataKey + + supplementary_data_url = f"{current_app.config['SDS_API_BASE_URL']}/v1/unit_data" + + parameters = {"dataset_id": dataset_id, "identifier": identifier} + + encoded_parameters = urlencode(parameters) + constructed_supplementary_data_url = ( + f"{supplementary_data_url}?{encoded_parameters}" + ) + + session = get_retryable_session( + max_retries=SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES, + retry_status_codes=SUPPLEMENTARY_DATA_REQUEST_RETRY_STATUS_CODES, + backoff_factor=SUPPLEMENTARY_DATA_REQUEST_BACKOFF_FACTOR, + ) + + try: + response = session.get( + constructed_supplementary_data_url, + timeout=SUPPLEMENTARY_DATA_REQUEST_TIMEOUT, + ) + except RequestException as exc: + logger.exception( + "Error requesting supplementary data", + supplementary_data_url=constructed_supplementary_data_url, + ) + raise SupplementaryDataRequestFailed from exc + + if response.status_code == 200: + supplementary_data_response_content = response.content.decode() + supplementary_data = decrypt_supplementary_data( + key_store=key_store, + supplementary_data=json.loads(supplementary_data_response_content), + ) + + return validate_supplementary_data( + supplementary_data=supplementary_data, + dataset_id=dataset_id, + identifier=identifier, + survey_id=survey_id, + ) + + logger.error( + "got a non-200 response for supplementary data request", + status_code=response.status_code, + schema_url=constructed_supplementary_data_url, + ) + + raise SupplementaryDataRequestFailed + + +def decrypt_supplementary_data( + *, key_store: KeyStore, supplementary_data: MutableMapping +) -> Mapping: + if encrypted_data := supplementary_data.get("data"): + try: + decrypted_data = JWEHelper.decrypt( + encrypted_data, key_store=key_store, purpose=KEY_PURPOSE_SDS + ) + supplementary_data["data"] = json.loads(decrypted_data) + return supplementary_data + except InvalidTokenException as e: + raise InvalidSupplementaryData from e + + raise ValidationError("Supplementary data has no data to decrypt") + + +def validate_supplementary_data( + supplementary_data: Mapping, dataset_id: str, identifier: str, survey_id: str +) -> dict: + try: + return validate_supplementary_data_v1( + supplementary_data=supplementary_data, + dataset_id=dataset_id, + identifier=identifier, + survey_id=survey_id, + ) + except ValidationError as e: + raise ValidationError("Invalid supplementary data") from e diff --git a/app/settings.py b/app/settings.py index 675c8f1bd2..813e5637b4 100644 --- a/app/settings.py +++ b/app/settings.py @@ -144,6 +144,7 @@ def utcoffset_or_fail(date_value, key): SURVEY_TYPE = os.getenv("SURVEY_TYPE", "business") +SDS_API_BASE_URL = os.getenv("SDS_API_BASE_URL") ACCOUNT_SERVICE_BASE_URL = os.getenv( "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" diff --git a/app/setup.py b/app/setup.py index 81f9df690c..824b0416b6 100644 --- a/app/setup.py +++ b/app/setup.py @@ -26,7 +26,7 @@ from app.cloud_tasks import CloudTaskPublisher, LogCloudTaskPublisher from app.helpers import get_span_and_trace from app.jinja_filters import blueprint as filter_blueprint -from app.keys import KEY_PURPOSE_SUBMISSION +from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SUBMISSION from app.publisher import LogPublisher, PubSubPublisher from app.routes.dump import dump_blueprint from app.routes.errors import errors_blueprint @@ -122,6 +122,7 @@ def create_app( # noqa: C901 pylint: disable=too-complex, too-many-statements with open(application.config["EQ_KEYS_FILE"], encoding="UTF-8") as keys_file: keys = yaml.safe_load(keys_file) validate_required_keys(keys, KEY_PURPOSE_SUBMISSION) + validate_required_keys(keys, KEY_PURPOSE_AUTHENTICATION) application.eq["key_store"] = KeyStore(keys) if application.config["EQ_APPLICATION_VERSION"]: diff --git a/app/submitter/converter.py b/app/submitter/converter.py index e03c35a656..cb7e4e07f8 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -70,6 +70,7 @@ def convert_answers( answer_store = questionnaire_store.answer_store list_store = questionnaire_store.list_store progress_store = questionnaire_store.progress_store + supplementary_data_store = questionnaire_store.supplementary_data_store survey_id = schema.json["survey_id"] @@ -97,6 +98,7 @@ def convert_answers( metadata=metadata, response_metadata=response_metadata, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) return payload | optional_properties diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index c2e88745b2..e7405552f1 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -3,9 +3,10 @@ from structlog import get_logger -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import AnswerStore, ListStore, ProgressStore, QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, @@ -56,6 +57,7 @@ def convert_answers_v2( answer_store = questionnaire_store.answer_store list_store = questionnaire_store.list_store progress_store = questionnaire_store.progress_store + supplementary_data_store = questionnaire_store.supplementary_data_store survey_id = schema.json["survey_id"] @@ -87,6 +89,7 @@ def convert_answers_v2( metadata=metadata, response_metadata=response_metadata, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) logger.info("converted answer ready for submission") @@ -108,6 +111,7 @@ def get_optional_payload_properties( return payload +# pylint: disable=too-many-locals def get_payload_data( answer_store: AnswerStore, list_store: ListStore, @@ -116,7 +120,8 @@ def get_payload_data( metadata: MetadataProxy, response_metadata: MutableMapping, progress_store: ProgressStore, -) -> OrderedDict[str, Any] | dict[str, list[Any]]: + supplementary_data_store: SupplementaryDataStore, +) -> OrderedDict | dict[str, list | dict]: if schema.json["data_version"] == "0.0.1": return convert_answers_to_payload_0_0_1( metadata=metadata, @@ -136,10 +141,16 @@ def get_payload_data( full_routing_path=full_routing_path, ) - data: dict[str, list[Any]] = { - "answers": answers, - "lists": list_store.serialize(), - } + lists: list = list_store.serialize() + for list_ in lists: + # for any lists that were populated by supplementary data, provide the identifier -> list_item_id mappings + if mapping := supplementary_data_store.list_mappings.get(list_["name"]): + list_["supplementary_data_mapping"] = mapping + + data: dict[str, list | dict] = {"answers": answers, "lists": lists} + + if supplementary_data_store.raw_data: + data["supplementary_data"] = supplementary_data_store.raw_data if answer_codes := schema.json.get("answer_codes"): answer_ids_to_filter = {answer.answer_id for answer in answers} diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 51fa8eaa8d..41ff03a10c 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -14,7 +14,7 @@ ) from structlog import get_logger -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.questionnaire.rules.utils import parse_iso_8601_datetime from app.utilities.metadata_validators import DateString, RegionCode, UUIDString diff --git a/app/utilities/request_session.py b/app/utilities/request_session.py new file mode 100644 index 0000000000..4835d06a7a --- /dev/null +++ b/app/utilities/request_session.py @@ -0,0 +1,21 @@ +import requests +from requests.adapters import HTTPAdapter +from urllib3 import Retry + + +def get_retryable_session( + max_retries, retry_status_codes, backoff_factor +) -> requests.Session: + session = requests.Session() + + retries = Retry( + total=max_retries, + status_forcelist=retry_status_codes, + ) # Codes to retry according to Google Docs https://cloud.google.com/storage/docs/retry-strategy#client-libraries + + retries.backoff_factor = backoff_factor + + session.mount("http://", HTTPAdapter(max_retries=retries)) + session.mount("https://", HTTPAdapter(max_retries=retries)) + + return session diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 994f700423..4845a7df33 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -5,9 +5,7 @@ from pathlib import Path from typing import Any -import requests from requests import RequestException -from requests.adapters import HTTPAdapter, Retry from structlog import get_logger from app.data_models.metadata_proxy import MetadataProxy @@ -16,6 +14,7 @@ QuestionnaireSchema, ) from app.utilities.json import json_load, json_loads +from app.utilities.request_session import get_retryable_session logger = get_logger() @@ -28,7 +27,7 @@ "phm_0001": [["en", "cy"]], } -SCHEMA_REQUEST_MAX_BACKOFF = 0.2 +SCHEMA_REQUEST_BACKOFF_FACTOR = 0.2 SCHEMA_REQUEST_MAX_RETRIES = 2 # Totals no. of request should be 3. The initial request + SCHEMA_REQUEST_MAX_RETRIES SCHEMA_REQUEST_TIMEOUT = 3 SCHEMA_REQUEST_RETRY_STATUS_CODES = [ @@ -210,18 +209,11 @@ def load_schema_from_url( constructed_schema_url = f"{schema_url}?language={language_code}" - session = requests.Session() - - retries = Retry( - total=SCHEMA_REQUEST_MAX_RETRIES, - status_forcelist=SCHEMA_REQUEST_RETRY_STATUS_CODES, - ) # Codes to retry according to Google Docs https://cloud.google.com/storage/docs/retry-strategy#client-libraries - - # Type ignore: MyPy does not recognise BACKOFF_MAX however it is a property, albeit deprecated - retries.BACKOFF_MAX = SCHEMA_REQUEST_MAX_BACKOFF # type: ignore - - session.mount("http://", HTTPAdapter(max_retries=retries)) - session.mount("https://", HTTPAdapter(max_retries=retries)) + session = get_retryable_session( + max_retries=SCHEMA_REQUEST_MAX_RETRIES, + retry_status_codes=SCHEMA_REQUEST_RETRY_STATUS_CODES, + backoff_factor=SCHEMA_REQUEST_BACKOFF_FACTOR, + ) try: req = session.get(constructed_schema_url, timeout=SCHEMA_REQUEST_TIMEOUT) diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py new file mode 100644 index 0000000000..59fac29a71 --- /dev/null +++ b/app/utilities/supplementary_data_parser.py @@ -0,0 +1,89 @@ +from typing import Mapping + +from marshmallow import ( + INCLUDE, + Schema, + ValidationError, + fields, + validate, + validates_schema, +) + +from app.authentication.auth_payload_versions import SupplementaryDataSchemaVersion +from app.utilities.metadata_parser_v2 import VALIDATORS, StripWhitespaceMixin + + +class ItemsSchema(Schema): + identifier = VALIDATORS["string"](validate=validate.Length(min=1)) + + +class ItemsData(Schema, StripWhitespaceMixin): + pass + + +class SupplementaryData(Schema, StripWhitespaceMixin): + identifier = VALIDATORS["string"](validate=validate.Length(min=1)) + schema_version = VALIDATORS["string"]( + validate=validate.OneOf([SupplementaryDataSchemaVersion.V1.value]) + ) + items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) + + @validates_schema() + def validate_identifier(self, data, **kwargs): + # pylint: disable=no-self-use, unused-argument + if data and data["identifier"] != self.context["identifier"]: + raise ValidationError( + "Supplementary data did not return the specified Identifier" + ) + + +class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): + dataset_id = VALIDATORS["uuid"]() + survey_id = VALIDATORS["string"](validate=validate.Length(min=1)) + data = fields.Nested( + SupplementaryData, + required=True, + unknown=INCLUDE, + validate=validate.Length(min=1), + ) + + @validates_schema() + def validate_dataset_and_survey_id(self, data, **kwargs): + # pylint: disable=no-self-use, unused-argument + if data: + if data["dataset_id"] != self.context["dataset_id"]: + raise ValidationError( + "Supplementary data did not return the specified Dataset ID" + ) + + if data["survey_id"] != self.context["survey_id"]: + raise ValidationError( + "Supplementary data did not return the specified Survey ID" + ) + + +def validate_supplementary_data_v1( + supplementary_data: Mapping, + dataset_id: str, + identifier: str, + survey_id: str, +) -> dict: + """Validate claims required for supplementary data""" + supplementary_data_metadata_schema = SupplementaryDataMetadataSchema( + unknown=INCLUDE + ) + supplementary_data_metadata_schema.context = { + "dataset_id": dataset_id, + "identifier": identifier, + "survey_id": survey_id, + } + validated_supplementary_data = supplementary_data_metadata_schema.load( + supplementary_data + ) + + if supplementary_data_items := supplementary_data.get("data", {}).get("items"): + for key, values in supplementary_data_items.items(): + items = [ItemsSchema(unknown=INCLUDE).load(value) for value in values] + validated_supplementary_data["data"]["items"][key] = items + + return validated_supplementary_data diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index f74f45d1c6..2c63807cf8 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -212,12 +212,18 @@ def _remove_unwanted_questions_answers( ] if block_question["id"] in questions_to_keep: - answers_to_keep = [ - answer - for answer in block_question["answers"] - if answer["id"] in answer_ids_to_keep - ] - block_question["answers"] = answers_to_keep + if answers := block_question.get("answers"): + answers_to_keep = [ + answer for answer in answers if answer["id"] in answer_ids_to_keep + ] + block_question["answers"] = answers_to_keep + if dynamic_answers := block_question.get("dynamic_answers"): + dynamic_answers_to_keep = [ + answer + for answer in dynamic_answers["answers"] + if answer["id"] in answer_ids_to_keep + ] + block_question["dynamic_answers"]["answers"] = dynamic_answers_to_keep return transformed_block diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 467ba9f040..0c45b0ddae 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -17,7 +17,9 @@ def _build_grand_calculated_summary_section( Build list of calculated summary blocks that the grand calculated summary will be adding up """ # Type ignore: the block, group and section will all exist at this point - calculated_summary_group: ImmutableDict = self._schema.get_group_for_block_id(self.current_location.block_id) # type: ignore + calculated_summary_group: ImmutableDict = self._schema.get_group_for_block_id( + self.current_location.block_id # type: ignore + ) calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( rendered_block @@ -40,10 +42,16 @@ def _blocks_on_routing_path( Find all blocks on the routing path for each of the calculated summaries """ # Type ignore: each block must have a section id - section_ids: set[str] = {self._schema.get_section_id_for_block_id(block_id) for block_id in calculated_summary_ids} # type: ignore + section_ids: set[str] = { + self._schema.get_section_id_for_block_id(block_id) # type: ignore + for block_id in calculated_summary_ids + } # find any sections involved in the grand calculated summary (but only if they have started, to avoid evaluating the path if not necessary) started_sections = [ - key for key, _ in self._progress_store.started_section_keys(section_ids) + key + for key, _ in self._progress_store.started_section_and_repeating_blocks_progress_keys( + section_ids + ) ] routing_path_block_ids: list[str] = [] diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index d1f2a5c29f..66a44a49ab 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -157,8 +157,10 @@ def _get_row_for_section( ) -> dict[str, Union[str, list]]: row_id = f"{section_id}-{list_item_index}" if list_item_index else section_id - section_status = self._progress_store.get_section_status( - section_id, list_item_id + section_status = ( + self._progress_store.get_section_or_repeating_blocks_progress_status( + section_id, list_item_id + ) ) return self.get_row_context_for_section( diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index 7130b16786..937d577f39 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -1,5 +1,5 @@ from functools import partial -from typing import Any, Generator, Mapping, Optional, Sequence +from typing import Any, Generator, Mapping, Sequence from flask import url_for from flask_babel import lazy_gettext @@ -10,24 +10,28 @@ class ListContext(Context): def __call__( self, - summary_definition: Mapping[str, Any], + summary_definition: Mapping, for_list: str, - return_to: Optional[str] = None, - edit_block_id: Optional[str] = None, - remove_block_id: Optional[str] = None, - primary_person_edit_block_id: Optional[str] = None, - for_list_item_ids: Optional[Sequence[str]] = None, - ) -> dict[str, dict]: + section_id: str, + has_repeating_blocks: bool, + return_to: str | None = None, + edit_block_id: str | None = None, + remove_block_id: str | None = None, + primary_person_edit_block_id: str | None = None, + for_list_item_ids: Sequence[str] | None = None, + ) -> dict[str, Any]: list_items = ( list( self._build_list_items_context( - for_list, - return_to, - summary_definition, - edit_block_id, - remove_block_id, - primary_person_edit_block_id, - for_list_item_ids, + for_list=for_list, + section_id=section_id, + has_repeating_blocks=has_repeating_blocks, + return_to=return_to, + summary_definition=summary_definition, + edit_block_id=edit_block_id, + remove_block_id=remove_block_id, + primary_person_edit_block_id=primary_person_edit_block_id, + for_list_item_ids=for_list_item_ids, ) ) if summary_definition @@ -41,16 +45,20 @@ def __call__( } } + # pylint: disable=too-many-locals def _build_list_items_context( self, + *, for_list: str, - return_to: Optional[str], - summary_definition: Mapping[str, Any], - edit_block_id: Optional[str], - remove_block_id: Optional[str], - primary_person_edit_block_id: Optional[str], - for_list_item_ids: Optional[Sequence[str]], - ) -> Generator[dict[str, Any], Any, None]: + section_id: str, + has_repeating_blocks: bool, + return_to: str | None, + summary_definition: Mapping, + edit_block_id: str | None, + remove_block_id: str | None, + primary_person_edit_block_id: str | None, + for_list_item_ids: Sequence[str] | None, + ) -> Generator[dict, None, None]: list_item_ids = self._list_store[for_list] if for_list_item_ids: list_item_ids = [ @@ -77,17 +85,24 @@ def _build_list_items_context( ), "primary_person": is_primary, "list_item_id": list_item_id, + "is_complete": self._progress_store.is_section_or_repeating_blocks_progress_complete( + section_id=section_id, list_item_id=list_item_id + ), + "repeating_blocks": has_repeating_blocks, } if edit_block_id: - if is_primary and primary_person_edit_block_id: - list_item_context["edit_link"] = partial_url_for( - block_id=primary_person_edit_block_id - ) - else: - list_item_context["edit_link"] = partial_url_for( - block_id=edit_block_id - ) + block_id = ( + primary_person_edit_block_id + if is_primary and primary_person_edit_block_id + else edit_block_id + ) + # return to answer id is used to snap back to the appropriate list item when editing from a summary page + # unlike other repeating answers that use answer_id-list_item_id, the edit-block is linked to item-label and anchored by list item id + return_to_answer_id = list_item_id if return_to else None + list_item_context["edit_link"] = partial_url_for( + block_id=block_id, return_to_answer_id=return_to_answer_id + ) if remove_block_id: list_item_context["remove_link"] = partial_url_for( @@ -99,7 +114,7 @@ def _build_list_items_context( def _get_item_title( self, summary_definition: Mapping[str, Any], - list_item_id: Optional[str], + list_item_id: str | None, is_primary: bool, ) -> str: rendered_summary: dict[str, Any] = self._placeholder_renderer.render( diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 135a514c75..5f69365622 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -42,7 +42,7 @@ def __init__( def __call__( self, - return_to: Optional[str] = "section-summary", + return_to: str | None = "section-summary", view_submitted_response: bool = False, ) -> Mapping[str, Any]: summary = self.build_summary(return_to, view_submitted_response) @@ -181,6 +181,7 @@ def _custom_summary_elements( schema=self._schema, location=self.current_location, language=self._language, + return_to="section-summary", ) yield list_collector_block.list_summary_element(summary_element) diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index 2c275f3434..54f3bd7769 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -1,15 +1,15 @@ -from typing import Any, Mapping, Optional, Union +from typing import Mapping from flask import url_for from app.data_models.answer import AnswerValueEscapedTypes -RadioCheckboxTypes = dict[str, Union[str, AnswerValueEscapedTypes, None]] -DateRangeTypes = dict[str, Optional[AnswerValueEscapedTypes]] +RadioCheckboxTypes = dict[str, str | AnswerValueEscapedTypes | None] +DateRangeTypes = dict[str, AnswerValueEscapedTypes | None] -InferredAnswerValueTypes = Union[ - None, DateRangeTypes, str, AnswerValueEscapedTypes, RadioCheckboxTypes -] +InferredAnswerValueTypes = ( + None | DateRangeTypes | str | AnswerValueEscapedTypes | RadioCheckboxTypes +) class Answer: @@ -19,10 +19,11 @@ def __init__( answer_schema: Mapping[str, str], answer_value: InferredAnswerValueTypes, block_id: str, - list_name: Optional[str], - list_item_id: Optional[str], - return_to: Optional[str], - return_to_block_id: Optional[str], + list_name: str | None, + list_item_id: str | None, + return_to: str | None, + return_to_block_id: str | None, + is_in_repeating_section: bool, ) -> None: self.id = answer_schema["id"] self.label = answer_schema.get("label") @@ -31,15 +32,17 @@ def __init__( self.unit = answer_schema.get("unit") self.unit_length = answer_schema.get("unit_length") self.currency = answer_schema.get("currency") + self._original_answer_id = answer_schema.get("original_answer_id") self.link = self._build_link( block_id=block_id, list_name=list_name, list_item_id=list_item_id, return_to=return_to, return_to_block_id=return_to_block_id, + is_in_repeating_section=is_in_repeating_section, ) - def serialize(self) -> dict[str, Any]: + def serialize(self) -> dict: return { "id": self.id, "label": self.label, @@ -55,10 +58,11 @@ def _build_link( self, *, block_id: str, - list_name: Optional[str], - list_item_id: Optional[str], - return_to: Optional[str], - return_to_block_id: Optional[str], + list_name: str | None, + list_item_id: str | None, + return_to: str | None, + return_to_block_id: str | None, + is_in_repeating_section: bool, ) -> str: return url_for( endpoint="questionnaire.block", @@ -66,7 +70,31 @@ def _build_link( block_id=block_id, list_item_id=list_item_id, return_to=return_to, - return_to_answer_id=self.id if return_to else None, + return_to_answer_id=self._return_to_answer_id( + return_to=return_to, + list_item_id=list_item_id, + is_in_repeating_section=is_in_repeating_section, + ), return_to_block_id=return_to_block_id, _anchor=self.id, ) + + def _return_to_answer_id( + self, + *, + return_to: str | None, + list_item_id: str | None, + is_in_repeating_section: bool, + ) -> str | None: + """ + If the summary page using this answer has repeating answers, but it is not in a repeating section, + then the answer ids will be suffixed with list item id, so the return to answer id link also needs this to work correctly + """ + if return_to: + if ( + list_item_id + and not is_in_repeating_section + and not self._original_answer_id # original answer would mean id has already been suffixed + ): + return f"{self.id}-{list_item_id}" + return self.id diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 2a373cd292..384454aa07 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -1,9 +1,12 @@ -from typing import Any, Mapping, MutableMapping, Optional +from typing import Mapping, MutableMapping + +from jsonpointer import resolve_pointer from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator +from app.questionnaire.schema_utils import find_pointers_containing from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_variant from app.utilities.types import LocationType @@ -13,30 +16,32 @@ class Block: def __init__( self, - block_schema: Mapping[str, Any], + block_schema: Mapping, *, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, location: LocationType, - return_to: Optional[str], - return_to_block_id: Optional[str] = None, + return_to: str | None, + return_to_block_id: str | None = None, progress_store: ProgressStore, language: str, ) -> None: self.id = block_schema["id"] self.title = block_schema.get("title") self.number = block_schema.get("number") + self.location = location + self.schema = schema self._rule_evaluator = RuleEvaluator( - schema=schema, + schema=self.schema, answer_store=answer_store, list_store=list_store, metadata=metadata, response_metadata=response_metadata, - location=location, + location=self.location, progress_store=progress_store, ) @@ -45,9 +50,9 @@ def __init__( list_store=list_store, metadata=metadata, response_metadata=response_metadata, - schema=schema, - location=location, - list_item_id=location.list_item_id if location else None, + schema=self.schema, + location=self.location, + list_item_id=self.location.list_item_id if self.location else None, use_default_answer=True, progress_store=progress_store, ) @@ -58,8 +63,6 @@ def __init__( list_store=list_store, metadata=metadata, response_metadata=response_metadata, - schema=schema, - location=location, return_to=return_to, return_to_block_id=return_to_block_id, progress_store=progress_store, @@ -69,15 +72,13 @@ def __init__( def get_question( self, *, - block_schema: Mapping[str, Any], + block_schema: Mapping, answer_store: AnswerStore, list_store: ListStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, - schema: QuestionnaireSchema, - location: LocationType, - return_to: Optional[str], - return_to_block_id: Optional[str], + return_to: str | None, + return_to_block_id: str | None, progress_store: ProgressStore, language: str, ) -> dict[str, Question]: @@ -85,14 +86,14 @@ def get_question( variant = choose_variant( block_schema, - schema, + self.schema, metadata, response_metadata, answer_store, list_store, variants_key="question_variants", single_key="question", - current_location=location, + current_location=self.location, progress_store=progress_store, ) return Question( @@ -100,10 +101,10 @@ def get_question( answer_store=answer_store, list_store=list_store, progress_store=progress_store, - schema=schema, + schema=self.schema, rule_evaluator=self._rule_evaluator, value_source_resolver=self._value_source_resolver, - location=location, + location=self.location, block_id=self.id, return_to=return_to, return_to_block_id=return_to_block_id, @@ -112,10 +113,27 @@ def get_question( language=language, ).serialize() - def serialize(self) -> dict[str, Any]: - return { - "id": self.id, - "title": self.title, - "number": self.number, - "question": self.question, - } + def _handle_id_suffixing(self, block: dict) -> dict: + """ + If the block is repeating but not within a repeating section, summary pages will render it multiple times, once per list item + so the block id, as well as any other ids (e.g. question, answer) need suffixing with list_item_id to ensure the HTML rendered is valid and doesn't + have duplicate div ids + """ + if ( + self.location.list_item_id + and not self.schema.is_block_in_repeating_section(self.id) + ): + for pointer in find_pointers_containing(block, "id"): + data = resolve_pointer(block, pointer) + data["id"] = f"{data['id']}-{self.location.list_item_id}" + return block + + def serialize(self) -> dict: + return self._handle_id_suffixing( + { + "id": self.id, + "title": self.title, + "number": self.number, + "question": self.question, + } + ) diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 19a5f9aeff..54c325c404 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -89,6 +89,34 @@ def _build_blocks_and_links( blocks = [] for block in group_schema["blocks"]: + # the block type will only be ListRepeatingQuestion when in the context of a calculated summary or grand calculated summary + # any other summary like section-summary will use the parent list collector instead and render items as part of the ListCollector check further down + if block["type"] == "ListRepeatingQuestion": + # list repeating questions aren't themselves on the path, it's determined by the parent list collector + parent_list_collector_block_id = schema.parent_id_map[block["id"]] + if parent_list_collector_block_id not in routing_path_block_ids: + continue + + list_collector_block = ListCollectorBlock( + routing_path_block_ids=routing_path_block_ids, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=metadata, + response_metadata=response_metadata, + schema=schema, + location=location, + language=language, + return_to=return_to, + return_to_block_id=return_to_block_id, + ) + repeating_answer_blocks = ( + list_collector_block.get_repeating_block_related_answer_blocks( + block + ) + ) + blocks.extend(repeating_answer_blocks) + if block["id"] not in routing_path_block_ids: continue if block["type"] in [ @@ -152,6 +180,7 @@ def _build_blocks_and_links( schema=schema, location=location, language=language, + return_to=return_to, ) list_summary_element = list_collector_block.list_summary_element( diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 7f5905f6af..6ecab5159a 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Any, Iterable, Mapping, MutableMapping, Optional +from typing import Iterable, Mapping, MutableMapping, Sequence from flask import url_for from werkzeug.datastructures import ImmutableDict @@ -21,11 +21,13 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, - metadata: Optional[MetadataProxy], + metadata: MetadataProxy | None, response_metadata: MutableMapping, schema: QuestionnaireSchema, location: LocationType, language: str, + return_to: str | None, + return_to_block_id: str | None = None, ) -> None: self._location = location self._placeholder_renderer = PlaceholderRenderer( @@ -49,9 +51,11 @@ def __init__( self._response_metadata = response_metadata self._routing_path_block_ids = routing_path_block_ids self._progress_store = progress_store + self._return_to = return_to + self._return_to_block_id = return_to_block_id # pylint: disable=too-many-locals - def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: + def list_summary_element(self, summary: Mapping) -> dict: list_collector_block = None ( edit_block_id, @@ -61,7 +65,7 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: item_label, item_anchor, ) = (None, None, None, None, None, None) - current_list = self._list_store[summary["for_list"]] + list_model = self._list_store[summary["for_list"]] list_collector_blocks = list( self._schema.get_list_collectors_for_list( @@ -92,11 +96,14 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: edit_block_id = list_collector_block["edit_block"]["id"] remove_block_id = list_collector_block["remove_block"]["id"] add_link = self._add_link(summary, list_collector_block) - related_answers = self._get_related_answers(current_list) - item_anchor = self._schema.get_item_anchor(section_id, current_list.name) - item_label = self._schema.get_item_label(section_id, current_list.name) + repeating_blocks = list_collector_block.get("repeating_blocks", []) + related_answers = self._get_related_answer_blocks_by_list_item_id( + list_model=list_model, repeating_blocks=repeating_blocks + ) + item_anchor = self._schema.get_item_anchor(section_id, list_model.name) + item_label = self._schema.get_item_label(section_id, list_model.name) - if len(current_list) == 1 and current_list.primary_person: + if len(list_model) == 1 and list_model.primary_person: if primary_person_block := self._schema.get_list_collector_for_list( self._section, for_list=summary["for_list"], primary=True ): @@ -107,7 +114,9 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: list_summary_context = self.list_context( list_collector_block["summary"], for_list=list_collector_block["for_list"], - return_to="section-summary", + section_id=self._location.section_id, + has_repeating_blocks=bool(list_collector_block.get("repeating_blocks")), + return_to=self._return_to, edit_block_id=edit_block_id, remove_block_id=remove_block_id, primary_person_edit_block_id=primary_person_edit_block_id, @@ -126,6 +135,23 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: **list_summary_context, } + def get_repeating_block_related_answer_blocks( + self, block: ImmutableDict + ) -> list[dict]: + """ + Given a repeating block question to render, + return the list of rendered question blocks for each list item id + """ + list_name = self._schema.list_names_by_list_repeating_block_id[block["id"]] + list_model = self._list_store[list_name] + blocks: list[dict] = [] + if answer_blocks_by_list_item_id := self._get_related_answer_blocks_by_list_item_id( + list_model=list_model, repeating_blocks=[block] + ): + for answer_blocks in answer_blocks_by_list_item_id.values(): + blocks.extend(answer_blocks) + return blocks + @property def list_context(self) -> ListContext: return ListContext( @@ -140,15 +166,15 @@ def list_context(self) -> ListContext: def _add_link( self, - summary: Mapping[str, Any], - list_collector_block: Optional[Mapping[str, Any]], - ) -> Optional[str]: + summary: Mapping, + list_collector_block: Mapping | None, + ) -> str | None: if list_collector_block: return url_for( "questionnaire.block", list_name=summary["for_list"], block_id=list_collector_block["add_block"]["id"], - return_to="section-summary", + return_to=self._return_to, ) if driving_question_block := self._schema.get_driving_question_for_list( @@ -157,43 +183,54 @@ def _add_link( return url_for( "questionnaire.block", block_id=driving_question_block["id"], - return_to="section-summary", + return_to=self._return_to, ) - def _get_related_answers( - self, list_model: ListModel - ) -> Optional[dict[str, list[dict[str, Any]]]]: + def _get_related_answer_blocks_by_list_item_id( + self, *, list_model: ListModel, repeating_blocks: Sequence[ImmutableDict] + ) -> dict[str, list[dict]] | None: section_id = self._section["id"] related_answers = self._schema.get_related_answers_for_list_for_section( section_id=section_id, list_name=list_model.name ) - if not related_answers: + + blocks: list[dict | ImmutableDict] = [] + + if related_answers: + blocks += self._get_blocks_for_related_answers(related_answers) + + if len(list_model): + blocks += repeating_blocks + + if not blocks: return None related_answers_blocks = {} - blocks = self.get_blocks_for_related_answers(related_answers) - for list_id in list_model: serialized_blocks = [ - Block( - block, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - schema=self._schema, - location=Location( - list_name=list_model.name, - list_item_id=list_id, - section_id=section_id, - ), - return_to="section-summary", - return_to_block_id=None, - progress_store=self._progress_store, - language=self._language, - ).serialize() + # related answers for repeating blocks may use placeholders, so each block needs rendering here + self._placeholder_renderer.render( + data_to_render=Block( + block, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=Location( + list_name=list_model.name, + list_item_id=list_id, + section_id=section_id, + ), + return_to=self._return_to, + return_to_block_id=self._return_to_block_id, + progress_store=self._progress_store, + language=self._language, + ).serialize(), + list_item_id=list_id, + ) for block in blocks ] @@ -201,9 +238,7 @@ def _get_related_answers( return related_answers_blocks - def get_blocks_for_related_answers( - self, related_answers: tuple - ) -> list[dict[str, Any]]: + def _get_blocks_for_related_answers(self, related_answers: tuple) -> list[dict]: blocks = [] answers_by_block = defaultdict(list) diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index ac9724b8b7..63fb8ecfb1 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -56,6 +56,10 @@ def __init__( self.rule_evaluator = rule_evaluator self.value_source_resolver = value_source_resolver + # no need to call the method if no list item id + self._is_in_repeating_section = bool( + self.list_item_id and self.schema.is_block_in_repeating_section(block_id) + ) self.answers = self._build_answers( answer_store=answer_store, @@ -138,6 +142,7 @@ def _build_answers( list_item_id=list_item_id or self.list_item_id, return_to=return_to, return_to_block_id=return_to_block_id, + is_in_repeating_section=self._is_in_repeating_section, ).serialize() summary_answers.append(summary_answer) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index ecad1ba18d..144c3e76e5 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -139,7 +139,7 @@ def _update_section_completeness( ) -> None: location = location or self._current_location - self.questionnaire_store_updater.update_section_status( + self.questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status( is_complete=self.router.is_path_complete(self._routing_path), section_id=location.section_id, list_item_id=location.list_item_id, diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 6c3a4c03ff..440933048c 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -15,6 +15,7 @@ from app.views.handlers.list_collector import ListCollector from app.views.handlers.list_edit_question import ListEditQuestion from app.views.handlers.list_remove_question import ListRemoveQuestion +from app.views.handlers.list_repeating_question import ListRepeatingQuestion from app.views.handlers.primary_person_list_collector import PrimaryPersonListCollector from app.views.handlers.primary_person_question import PrimaryPersonQuestion from app.views.handlers.question import Question @@ -28,6 +29,7 @@ "ListAddQuestion": ListAddQuestion, "ListEditQuestion": ListEditQuestion, "ListRemoveQuestion": ListRemoveQuestion, + "ListRepeatingQuestion": ListRepeatingQuestion, "PrimaryPersonListCollector": PrimaryPersonListCollector, "PrimaryPersonListAddOrEditQuestion": PrimaryPersonQuestion, "RelationshipCollector": RelationshipCollector, diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index b13b163754..8f34d0bbf1 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -7,7 +7,7 @@ from sdc.crypto.encrypter import encrypt from werkzeug.datastructures import MultiDict -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.data_models.session_data import SessionData diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 86bc863e0e..8c0ca619cc 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -319,7 +319,7 @@ def _render_block(self) -> dict[str, Any]: ) def _update_section_status(self, status: str) -> None: - self._questionnaire_store.progress_store.update_section_status( + self._questionnaire_store.progress_store.update_section_or_repeating_blocks_progress_completion_status( # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None status, self.individual_section_id, # type: ignore diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index f83e9b08e6..f7db203cad 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -21,6 +21,7 @@ def parent_location(self) -> Location: ) def _get_routing_path(self) -> RoutingPath: + """Only the section id is required, as list collectors won't be in a repeating section""" return self.router.routing_path(section_id=self.parent_location.section_id) def is_location_valid(self) -> bool: @@ -34,13 +35,8 @@ def is_location_valid(self) -> bool: ) def get_previous_location_url(self) -> str: - if ( - self._return_to == "section-summary" - and self.router.can_display_section_summary( - self.parent_location.section_id, self.parent_location.list_item_id - ) - ): - return self.get_section_summary_url() + if url := self.get_section_or_final_summary_url(): + return url block_id = self._request_args.get("previous") return self._get_location_url( @@ -50,17 +46,26 @@ def get_previous_location_url(self) -> str: return_to_block_id=self._return_to_block_id, ) - def get_section_summary_url(self) -> str: - return url_for( - "questionnaire.get_section", section_id=self.parent_location.section_id - ) + def get_section_or_final_summary_url(self) -> str | None: + if ( + self._return_to == "section-summary" + and self.router.can_display_section_summary( + self.parent_location.section_id, self.parent_location.list_item_id + ) + ): + return url_for( + "questionnaire.get_section", + section_id=self.parent_location.section_id, + _anchor=self._return_to_answer_id, + ) + if self._return_to == "final-summary" and self.router.is_questionnaire_complete: + return url_for( + "questionnaire.submit_questionnaire", _anchor=self._return_to_answer_id + ) def get_next_location_url(self) -> str: - if self._return_to == "section-summary": - if self.router.can_display_section_summary( - self.parent_location.section_id, self.parent_location.list_item_id - ): - return self.get_section_summary_url() + if url := self.get_section_or_final_summary_url(): + return url if self.router.is_block_complete( # Type ignore: the parent_location property above is initialised with a block_id so it won't be None @@ -89,9 +94,7 @@ def handle_post(self) -> None: ) if self.questionnaire_store_updater.is_dirty(): - self._routing_path = self.router.routing_path( - self.current_location.section_id, self.current_location.list_item_id - ) + self._routing_path = self._get_routing_path() self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() @@ -103,6 +106,7 @@ def _get_location_url( return_to: str | None = None, return_to_answer_id: str | None = None, return_to_block_id: str | None = None, + anchor: str | None = None, ) -> str: if block_id and self._schema.is_block_valid(block_id): # Type ignore: the above line check that block_id exists and is valid and therefore section exists @@ -111,10 +115,12 @@ def _get_location_url( return_to=return_to, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, + _anchor=anchor, ) return self.parent_location.url( return_to=return_to, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, + _anchor=anchor, ) diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index af14b9f7f6..1a87db7dd3 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -1,13 +1,34 @@ +from typing import Any + +from flask import url_for + from app.views.handlers.list_action import ListAction class ListAddQuestion(ListAction): + def __init__(self, *args: Any) -> None: + self._list_item_id: str | None = None + super().__init__(*args) + def is_location_valid(self) -> bool: if not super().is_location_valid() or self._current_location.list_item_id: return False return True def get_next_location_url(self) -> str: + if self._list_item_id and ( + repeating_blocks := self.parent_block.get("repeating_blocks") + ): + return url_for( + "questionnaire.block", + list_name=self.parent_block["for_list"], + list_item_id=self._list_item_id, + block_id=repeating_blocks[0]["id"], + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + return self.parent_location.url( return_to=self._return_to, return_to_answer_id=self._return_to_answer_id, @@ -16,7 +37,7 @@ def get_next_location_url(self) -> str: def handle_post(self) -> None: # Ensure the section is in progress when user adds an item - list_item_id = self.questionnaire_store_updater.add_list_item( + self._list_item_id = self.questionnaire_store_updater.add_list_item( self.parent_block["for_list"] ) @@ -30,7 +51,9 @@ def handle_post(self) -> None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation - self.questionnaire_store_updater.update_answers(self.form.data, list_item_id) + self.questionnaire_store_updater.update_answers( + self.form.data, self._list_item_id + ) self.capture_dependent_sections_for_list(self.parent_block["for_list"]) diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 7c17d44adc..dd6833386e 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -1,3 +1,4 @@ +from functools import cached_property from typing import Any from flask import url_for @@ -11,6 +12,16 @@ def __init__(self, *args: Any) -> None: self._is_adding = False super().__init__(*args) + @cached_property + def repeating_block_ids(self) -> list[str]: + return [ + block["id"] for block in self.rendered_block.get("repeating_blocks", []) + ] + + @cached_property + def list_name(self) -> str: + return self.rendered_block["for_list"] # type: ignore + def get_next_location_url(self) -> str: if self._is_adding: add_url = url_for( @@ -23,6 +34,22 @@ def get_next_location_url(self) -> str: ) return add_url + if incomplete_block := self.get_first_incomplete_list_repeating_block_location( + repeating_block_ids=self.repeating_block_ids, + section_id=self.current_location.section_id, + list_name=self.list_name, + ): + repeating_block_url = url_for( + "questionnaire.block", + list_name=self.list_name, + list_item_id=incomplete_block.list_item_id, + block_id=incomplete_block.block_id, + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + return repeating_block_url + return super().get_next_location_url() def get_context(self) -> dict[str, dict]: @@ -41,10 +68,12 @@ def get_context(self) -> dict[str, dict]: **question_context, **list_context( self.rendered_block["summary"], - for_list=self.rendered_block["for_list"], + for_list=self.list_name, edit_block_id=self.rendered_block["edit_block"]["id"], remove_block_id=self.rendered_block["remove_block"]["id"], return_to=self._return_to, + section_id=self.current_location.section_id, + has_repeating_blocks=bool(self.repeating_block_ids), ), } @@ -57,5 +86,12 @@ def handle_post(self) -> None: # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) self.questionnaire_store_updater.save() - else: + elif self._is_list_collector_complete(): super().handle_post() + + def _is_list_collector_complete(self) -> bool: + return not self.get_first_incomplete_list_repeating_block_location( + repeating_block_ids=self.repeating_block_ids, + section_id=self.current_location.section_id, + list_name=self.list_name, + ) diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index 22d5326884..fa5b4b30cc 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -1,3 +1,5 @@ +from flask import url_for + from app.views.handlers.list_action import ListAction @@ -14,6 +16,32 @@ def is_location_valid(self) -> bool: return False return True + def get_next_location_url(self) -> str: + """ + Unless editing from the summary page, If there are repeating blocks and not all are complete, go to the next one + """ + if url := self.get_section_or_final_summary_url(): + return url + + if first_incomplete_block := self.get_first_incomplete_list_repeating_block_location_for_list_item( + repeating_block_ids=self._schema.list_collector_repeating_block_ids, + section_id=self.current_location.section_id, + # Type ignore: list_name and list_item_id will exist at this point + list_item_id=self.current_location.list_item_id, # type: ignore + list_name=self.current_location.list_name, # type: ignore + ): + return url_for( + "questionnaire.block", + list_name=first_incomplete_block.list_name, + list_item_id=first_incomplete_block.list_item_id, + block_id=first_incomplete_block.block_id, + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + + return super().get_next_location_url() + def handle_post(self) -> None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index 5081018fa0..1e42fd2519 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -27,7 +27,7 @@ def handle_post(self) -> None: if answer_action and answer_action["type"] == "RemoveListItemAndAnswers": list_name = self.parent_block["for_list"] - self.questionnaire_store_updater.remove_list_item_and_answers( + self.questionnaire_store_updater.remove_list_item_data( # Type ignore: list_item_id will exist within the remove block list_name, self._current_location.list_item_id, # type: ignore diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py new file mode 100644 index 0000000000..473221551e --- /dev/null +++ b/app/views/handlers/list_repeating_question.py @@ -0,0 +1,82 @@ +from functools import cached_property + +from flask import url_for +from werkzeug.datastructures import ImmutableDict + +from app.questionnaire import Location +from app.views.handlers.list_edit_question import ListEditQuestion + + +class ListRepeatingQuestion(ListEditQuestion): + @cached_property + def repeating_block_ids(self) -> list[str]: + return self._schema.list_collector_repeating_block_ids + + def get_previous_location_url(self) -> str: + """ + return to previous location, or when return to is None, navigate to the previous repeating block + unless this is the first repeating block, in which case, route back to the edit block + """ + if url := self.get_section_or_final_summary_url(): + return url + + if self.return_to and self.router.can_access_location( + Location( + section_id=self.current_location.section_id, + block_id=self.return_to_block_id, + ), + routing_path=self._routing_path, + ): + return self._get_location_url( + block_id=self._return_to_block_id, + anchor=self._return_to_answer_id, + ) + + repeating_block_index = self.repeating_block_ids.index( + # Type ignore: block_id will exist at this point + self.current_location.block_id # type: ignore + ) + if repeating_block_index != 0: + previous_repeating_block_id = self.repeating_block_ids[ + repeating_block_index - 1 + ] + return url_for( + "questionnaire.block", + list_name=self.current_location.list_name, + list_item_id=self.current_location.list_item_id, + block_id=previous_repeating_block_id, + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + + # Type ignore: edit_block will exist at this point + edit_block: ImmutableDict = self._schema.get_edit_block_for_list_collector( # type: ignore + self.parent_block["id"] + ) + return url_for( + "questionnaire.block", + list_name=self.current_location.list_name, + list_item_id=self.current_location.list_item_id, + block_id=edit_block["id"], + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + + def handle_post(self) -> None: + self.questionnaire_store_updater.add_completed_location(self.current_location) + if not self.get_first_incomplete_list_repeating_block_location_for_list_item( + repeating_block_ids=self.repeating_block_ids, + section_id=self.current_location.section_id, + # Type ignore: list_name and list_item_id will always exist at this point + list_item_id=self.current_location.list_item_id, # type: ignore + list_name=self.current_location.list_name, # type: ignore + ): + self.questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status( + is_complete=True, + section_id=self.current_location.section_id, + list_item_id=self.current_location.list_item_id, + ) + + super().handle_post() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 7d48e02bf2..62914d9a52 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Mapping +from typing import Mapping, Sequence from flask import url_for from flask_babel import gettext @@ -206,8 +206,10 @@ def get_last_viewed_question_guidance_context(self) -> dict | bool | None: def get_list_summary_context(self) -> dict: return self.list_context( - self.rendered_block["list_summary"]["summary"], - self.rendered_block["list_summary"]["for_list"], + summary_definition=self.rendered_block["list_summary"]["summary"], + for_list=self.rendered_block["list_summary"]["for_list"], + section_id=self.current_location.section_id, + has_repeating_blocks=bool(self.rendered_block.get("repeating_blocks")), ) def handle_post(self) -> None: @@ -235,10 +237,16 @@ def capture_dependent_sections_for_list(self, list_name: str) -> None: section_ids = self._schema.get_section_ids_dependent_on_list(list_name) section_ids.append(self.current_location.section_id) - section_keys_to_add = self.questionnaire_store_updater.started_section_keys( + for ( + section_id, + list_item_id, + ) in self.questionnaire_store_updater.started_section_keys( section_ids=section_ids - ) - for section_id, list_item_id in section_keys_to_add: + ): + # Only add sections which are repeated sections for this list, or the section in which this list is collected + # Prevents list item progresses being added as dependants as these are captured by started_section_keys(section_ids=section_ids) + if section_id == self.current_location.section_id and list_item_id: + continue self.questionnaire_store_updater.dependent_sections.add( DependentSection( section_id=section_id, @@ -258,3 +266,45 @@ def clear_radio_answers(self) -> None: answer_ids_to_remove, self.current_location.list_item_id ) self.questionnaire_store_updater.save() + + def get_first_incomplete_list_repeating_block_location( + self, *, repeating_block_ids: Sequence[str], section_id: str, list_name: str + ) -> Location | None: + if not repeating_block_ids: + return None + + list_model = self._questionnaire_store.list_store.get(list_name) + for list_item_id in list_model.items: + if incomplete_location := self.get_first_incomplete_list_repeating_block_location_for_list_item( + repeating_block_ids=repeating_block_ids, + section_id=section_id, + list_item_id=list_item_id, + list_name=list_name, + ): + return incomplete_location + + def get_first_incomplete_list_repeating_block_location_for_list_item( + self, + *, + repeating_block_ids: Sequence[str], + section_id: str, + list_item_id: str, + list_name: str, + ) -> Location | None: + if self._questionnaire_store.progress_store.is_section_or_repeating_blocks_progress_complete( + section_id=section_id, list_item_id=list_item_id + ): + return None + + for repeating_block_id in repeating_block_ids: + if not self.router.is_block_complete( + block_id=repeating_block_id, + section_id=section_id, + list_item_id=list_item_id, + ): + return Location( + section_id=section_id, + block_id=repeating_block_id, + list_name=list_name, + list_item_id=list_item_id, + ) diff --git a/app/views/handlers/relationships/unrelated_question.py b/app/views/handlers/relationships/unrelated_question.py index 21f57a0980..5f2f90f6fb 100644 --- a/app/views/handlers/relationships/unrelated_question.py +++ b/app/views/handlers/relationships/unrelated_question.py @@ -24,8 +24,10 @@ def unrelated_block_id(self) -> str | None: def get_list_summary_context(self) -> dict[str, dict]: return self.list_context( - self.rendered_block["list_summary"]["summary"], - self.list_name, + summary_definition=self.rendered_block["list_summary"]["summary"], + for_list=self.list_name, + section_id=self.current_location.section_id, + has_repeating_blocks=bool(self.rendered_block.get("repeating_blocks")), for_list_item_ids=self.get_remaining_relationships_for_individual(), ) diff --git a/app/views/handlers/submission.py b/app/views/handlers/submission.py index 7f1820e705..72203704f2 100644 --- a/app/views/handlers/submission.py +++ b/app/views/handlers/submission.py @@ -5,7 +5,7 @@ from flask import session as cookie_session from sdc.crypto.encrypter import encrypt -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store diff --git a/dev-keys.yml b/dev-keys.yml index 02629bd206..9dbdf4bab9 100644 --- a/dev-keys.yml +++ b/dev-keys.yml @@ -1,7 +1,10 @@ keys: 2225f01580a949801274a5f3e6861947018aff5b: + platform: sdc purpose: submission + service: sdx type: public + use: encryption value: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4UO1frTr8LPyyKXkQNi @@ -12,9 +15,13 @@ keys: ddIEdYRuxd9A7yWjP1I8jWC/DSq8UEAooVfe6XNsoWWF2zn9XDjhWw0hwEXSX/QT 9wIDAQAB -----END PUBLIC KEY----- + version: v1 709eb42cfee5570058ce0711f730bfbb7d4c8ade: + platform: sdc purpose: authentication + service: launcher type: public + use: signing value: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvZzMraB96Wd1zfHS3vW3 @@ -25,9 +32,13 @@ keys: JEfyLEM0zKrkQQ796gfYpkzDYwJvkiW7fb2Yh1teNHpFR5tozzMwUxkREl/TQ4U1 kwIDAQAB -----END PUBLIC KEY----- + version: v1 e19091072f920cbf3ca9f436ceba309e7d814a62: + platform: sdc purpose: authentication + service: eq type: private + use: encryption value: | -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAt8LZnIhuOdL/BC029GOaJkVUAqgp2PcmbFr2Qwhf/514DUUQ @@ -56,9 +67,13 @@ keys: OZrki4MBTK/6GFkHLFkF6w2Le+Y5Nos9O2UUZs45lwLEYbQ4yKcx2KlWGLZOypB8 i7/6TB95Ej2i5KgaSlcJjOyOx7g20TwDD1THtLXgY54d0Yr9T/U= -----END RSA PRIVATE KEY----- + version: v1 fe425f951a0917d7acdd49230b23a5c405c28510: + platform: sdc purpose: submission + service: eq type: private + use: signing value: | -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAq0rFub/fC2S4kcP4l9ivxgljh8l3hYOKG5z8mJ1cggoRqTaI @@ -87,3 +102,63 @@ keys: V7ZuRp//4vIzEONqqyLUFt1JZKuCwWQB2sRjKWpqRB07C0o243XefD5f7844nmXq H0xKMFRYjDqhFdTXFWVPnk/tu/MxoSTdK0FfsgUnjs+TGvN5TnBW -----END RSA PRIVATE KEY----- + version: v1 + df88fdad2612ae1e80571120e6c6371f55896696: + platform: sdc + purpose: supplementary_data + service: sds + type: private + use: encryption + value: | + -----BEGIN RSA PRIVATE KEY----- + MIIJKAIBAAKCAgEAvQCr8fkBjjThO2+bQ/Y2dIsO9fRcodxiC0Gyz36NKjVc0Gqh + Kthrcyvhv+birZRGRJDi6f7i0sQ1LO+ZvJGlHD5cWZMdHvzNZTa+jVIw5DswZxzc + SZuI1/EJmCVvNOEy+Tth79IPNV3KtsKIEN044Ih/BhIYYu/mjrKfM2vddFwT1BXJ + FH5grEusaeODYhPvCSwvy5QRFiQIbm/wCP+Y8Dm3EhSPulHBBFEcy4xf+gM9fYn5 + Fww+vMnUibAGU9rZhNBDI1dV2czW7/7et7hmTtrtZsPyaqHY05Td0B2a/N7ZhyQt + zTUqbxOVKw5OgnFn6UE1vmUWYSULSPKrLig76MSITyOH1h1mTK6T5UbYyzcFBUvx + nOi7M9ddqIR57DaDJqSA6vP6ngzN8UYqr36ZOBJqpfmj+mIGQN2HhhPBrI41AVvV + YU/5bC0IHZYWjd12+bHQhf41tLUsrK53p6xEG+n0Txgp8HMhFmWoKwWXNEkNvdAE + tgH7QnObjjTV10nS09EQFgA6kYA0YwTeIiVlx9Y0J7oUCmwosMpcbaAlImlgafTh + AutTqT83Iw8gs2bCRd6h8ysHi3QiqNn6dV/QlHRHqfNBwHapHRUbBTVenwOVrzyV + VnAd5a3P574zsm/gRz/9+rBIxebQdFrre+M4DLAnzLpdqv5+y/LqH3WiNMkCAwEA + AQKCAgBRVPqpNAhRU7wgwZRFGKyyVizn9nHuTVH7mhgCZmkE4tW/8kLMlzkV5KpO + 1GJzY70hQGAFZePh4wEnByxXEy3EC6nd+gqsDQmuJnK1icr0S+w2UxsQqdenZVhF + msZSMR6oVb99Xh2hT20uXGQFLc2OAe73g83utWG3wnHzxNUVf5Ig0AcpxICBZEcb + ggZFrGJOxi8DIgKATp06OP1IQgVkStHW+/YlrYyr+OO1TAD5K2/ImBkSq/hLcWb+ + oTr31tOH7b8WdDzDbvyHZlwdH0MXZ+qFMIkfDeqqkgMpzbOmYZemKhFznw9VoU2t + q4hpZbfbjm48MnAA+dnzWEoFoNa2RJMHhZsTNSuDCXXunPvWGTymonco1eqiH+zx + X5yVtMRXtpCn1vbsJ/f39DB56X0Yj+S2edAvhOHORt9RfXZ/nBM9LngOpqjKKz1v + w9Mh7g+vGv/l4BPIznbKsQZEWj5ndtoUYFPtnMA1dKLr03jhMfAzsvamF2U8IjVZ + idzgJwIkAl00ywVQswXIAa7bW/3/moXGlHFz93qYf64mROY4ASffKMkRiaeFpTW3 + nTrIy0h+F3vZxSSc2Qw55auNDRnefNr6wsa1hrYRnzIknloK/wcaIulWGIyzEW4i + mH/RWTl+pdu7zmqb0j1BMr48wbxIuusceiC4n42AJQbFIg5BSQKCAQEA4D8kHgmA + Ct9vkq+hoyl99JnfZlfDr+N0p3+7Nc4mbVsgtl604grWjg7RIEXYFzXwfVd7pG5M + P70zqUo8gIDhZGBUksr6PZ8GcAyG8RiBE+gzkhBycw5JxRvJC/3Qsu6am1hr+M7v + peQTUolgiNekIPncJbP/j/5CQiuMlCyFk9GHWvFhaqq2pRrkkUYZ6zplzPePlnB+ + CkN0+q6HWOAXh4wCfkaUwb+xFFWiRxqYxoSL6OnPcSrXVG3BEIztMbuC9AzdPhPM + M1zaiYQf6mxeT4XOQcBMdcumxxltCUH5tiuGdYOHtB6Biq+if/p7VzjxswrejAnb + 7jqlSLJZENajDwKCAQEA18Pym+HHz0HIdUuCcky/f1tyAnNijYoZP2En4ztKwcj6 + jp5CRgQC+ufdkrpJKWqhv3kHPCZvsKk066Rr03wTxvPMhb93xH4VBZ+bf58s7bUR + 4KdJhJEf8DJbJiTBDno3ddhwtSXBfW6Eoyy6x6X6Hkw4DZm0DGaFN8vfJAZ9OprJ + q4nO0NYP1A0aYzE7BDgq5Tkr03ecNHLzIqP8zIVkkn6ewbUUG7wGbjRV7XSOpvum + GUjy04eKMIIZkWztIfJS7IuzXa2wK3I8lDu0wrmrm17lVjpZ9/ja8KaTVkgabL2P + Lg25NMk1qj0AQj88RrVkmGRWpnbuc1qJ4DOnA8zKpwKCAQA3bYP45LJAfb/vSvgy + A0R93DbK7jCRXjBsYnccsorvBtJMIZamNLWZwXHRf1INUqjR4njOSPER5CtL0eyo + erK7g9ADxKYb6x3FPmNwXnUxPXjZxrTzWXnEfbyw+RjH0ZBni3CMvGGh6IEaKpiw + 2lRYTkorC5XEur0X6/nAeky+H9FMGlPQ8Mdagg4zFle7u+CDzEEylzWgRdI5UEBm + KGXIfEP1gG6ugTo843nMB3fxwbtvY7OBrmwxEzvgYmUSoN2agz+AY5Zar73Ytc7J + u+WH1HQJ7oU3rJHZrqAz5JnbfGCs1UkKrWupowYQihJImeusLKibhqhU9yv5jxPS + xKrjAoIBAFJSvAVH3wGv+rjuJ4ZOzB3emSBgP/D7COkKu7pSTBKmCRtTPLwUGcL7 + pqmuE+4Odkpk9iK4E5NW7A8ge9eEFtOo/5bkV+ELrh+oJx9Jb03+8SRDD6TZ7lKq + E+b4zQQmE3UOMOqczjd6bHcJwPYd2NGoiRZ/V5gHobqJOck4BJ3QozOk79j0Y7On + kDLafMb+Wzd8WcFkeJ/2X9gOs4yhNJ9EWnRUD6kJU3bG1yYze54wk84/7A5TP6GE + chbvdYanO4ZvQu9yLq5U9tIj+bL2PoiYa24780nOlFKPa9XWyuZEaRXMPKbsQmKC + xc+A6xGbchdG6Vy4MgCnQcXeT1H+2C8CggEBAMvwlPifgjCk2v7ejVPvA0K63aii + l8Acv6wtvELeQGHLg/ZmzbHNZG9mExhuHASeIEn/1p64dAUQ6NTvHq78bgZ4sa2M + U6surGKebDwcbgMZe5lN6jZhem9DHWyXCLco3FHWFj6bNMuBfOdUemVi4bEukcrM + ItfML9fhBIKva5aAWej+lEQydTImhLOfgXbEse7ic+ZWVddpuqNCq137Mcbc1q0t + QChlDkY6+uOdYa3xmSCHtDc64ymzBR6XAQHQnX9fqfLWdz3Ytk9s+H34Hrn6mJZ/ + /MRZOZimld2tdshUdEQ+Kaf4wVLcEwJuW5/NeMbba+iIA/sPqHRkYmVs1v8= + -----END RSA PRIVATE KEY----- + version: v1 diff --git a/doc/run-mock-sds-endpoint.md b/doc/run-mock-sds-endpoint.md new file mode 100644 index 0000000000..7aba12abb6 --- /dev/null +++ b/doc/run-mock-sds-endpoint.md @@ -0,0 +1,27 @@ +# Running the Mock SDS endpoint + +In order to test loading supplementary data, we have a development script that creates a Mock SDS endpoint in Flask that +returns mocked supplementary data and mock dataset metadata for that supplementary data. + +Ensure the following env var is set before running the script: + +```bash +SDS_API_BASE_URL=http://localhost:5003 +``` + +From the home directory, run using + +```bash +python -m scripts.mock_sds_endpoint +``` + +The following datasets are available using the mocked endpoint. On selecting a survey that supports supplementary data, +an `sds_dataset_id` dropdown will be shown in the metadata, and if you set the `survey_id` to `123`, it will be +populated with the following options. + +| Dataset ID | Description | +|----------------------------------------|--------------------------------------------------------------| +| `c067f6de-6d64-42b1-8b02-431a3486c178` | Basic supplementary data structure with no repeating items | +| `34a80231-c49a-44d0-91a6-8fe1fb190e64` | Supplementary data structure with repeating items | +| `6b378962-f0c7-4e8c-947e-7d24ee1b6b88` | Supplementary data structure with additional repeating items | + diff --git a/schemas/test/en/test_list_collector_repeating_blocks_section_summary.json b/schemas/test/en/test_list_collector_repeating_blocks_section_summary.json new file mode 100644 index 0000000000..c43fe8b93e --- /dev/null +++ b/schemas/test/en/test_list_collector_repeating_blocks_section_summary.json @@ -0,0 +1,432 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test a List Collector with Repeating Blocks and Section Summary Items", + "theme": "default", + "description": "A questionnaire to test a list collector with repeating blocks and section summary items", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "answer_codes": [ + { + "answer_id": "responsible-party-answer", + "code": "1" + }, + { + "answer_id": "any-companies-or-branches-answer", + "code": "2" + }, + { + "answer_id": "company-or-branch-name", + "code": "2a" + }, + { + "answer_id": "registration-number", + "code": "2b" + }, + { + "answer_id": "registration-date", + "code": "2c" + }, + { + "answer_id": "authorised-trader-uk-radio", + "code": "2d" + }, + { + "answer_id": "authorised-trader-eu-radio", + "code": "2e" + }, + { + "answer_id": "any-other-companies-or-branches-answer", + "code": "3" + }, + { + "answer_id": "any-other-trading-details-answer", + "code": "4" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section-companies", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-companies", + "blocks": [ + { + "type": "Question", + "id": "responsible-party", + "question": { + "type": "General", + "id": "responsible-party-question", + "title": "Are you the responsible party for reporting trading details for a company of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "any-companies-or-branches", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", + "question": { + "type": "General", + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-companies-or-branches-answer" + }, + "Yes" + ] + }, + "block": "any-other-companies-or-branches" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-companies-or-branches", + "type": "ListCollector", + "for_list": "companies", + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-company", + "type": "ListAddQuestion", + "question": { + "id": "add-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "repeating_blocks": [ + { + "id": "companies-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-number", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-date", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "companies-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {company_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-date" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this company been trading in the UK? (Mandatory)", + "id": "authorised-trader-uk-radio", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this company been trading in the EU? (Not mandatory)", + "id": "authorised-trader-eu-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ], + "edit_block": { + "id": "edit-company", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-company", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + } + }, + { + "id": "any-other-trading-details", + "type": "Question", + "question": { + "id": "any-other-trading-details-question", + "type": "General", + "title": "Do you have any other details about the trading you have reported for?", + "answers": [ + { + "id": "any-other-trading-details-answer", + "label": "Additional details", + "mandatory": false, + "type": "TextField" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json b/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json new file mode 100644 index 0000000000..d6fe0cfe47 --- /dev/null +++ b/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json @@ -0,0 +1,430 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test a List Collector with Repeating Blocks and Section Summary Items", + "theme": "default", + "description": "A questionnaire to test a list collector with repeating blocks and section summary items", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "answer_codes": [ + { + "answer_id": "responsible-party-answer", + "code": "1" + }, + { + "answer_id": "any-companies-or-branches-answer", + "code": "2" + }, + { + "answer_id": "company-or-branch-name", + "code": "2a" + }, + { + "answer_id": "registration-number", + "code": "2b" + }, + { + "answer_id": "registration-date", + "code": "2c" + }, + { + "answer_id": "authorised-trader-uk-radio", + "code": "2d" + }, + { + "answer_id": "authorised-trader-eu-radio", + "code": "2e" + }, + { + "answer_id": "any-other-companies-or-branches-answer", + "code": "3" + }, + { + "answer_id": "any-other-trading-details-answer", + "code": "4" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["section-companies"] + } + }, + "sections": [ + { + "id": "section-companies", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-companies", + "blocks": [ + { + "type": "Question", + "id": "responsible-party", + "question": { + "type": "General", + "id": "responsible-party-question", + "title": "Are you the responsible party for reporting trading details for a company of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "any-companies-or-branches", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", + "question": { + "type": "General", + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-companies-or-branches-answer" + }, + "Yes" + ] + }, + "block": "any-other-companies-or-branches" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-companies-or-branches", + "type": "ListCollector", + "for_list": "companies", + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-company", + "type": "ListAddQuestion", + "question": { + "id": "add-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "repeating_blocks": [ + { + "id": "companies-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-number", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-date", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "companies-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {company_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-date" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this company been trading in the UK? (Mandatory)", + "id": "authorised-trader-uk-radio", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this company been trading in the EU? (Not mandatory)", + "id": "authorised-trader-eu-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ], + "edit_block": { + "id": "edit-company", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-company", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + } + }, + { + "id": "any-other-trading-details", + "type": "Question", + "question": { + "id": "any-other-trading-details-question", + "type": "General", + "title": "Do you have any other details about the trading you have reported for?", + "answers": [ + { + "id": "any-other-trading-details-answer", + "label": "Additional details", + "mandatory": false, + "type": "TextField" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json new file mode 100644 index 0000000000..39a023d924 --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json @@ -0,0 +1,726 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary with Dynamic Answers", + "theme": "default", + "description": "A questionnaire to demo calculated summaries which use a list of repeating answers.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { "required_completed_sections": ["section-1"] } + }, + "sections": [ + { + "id": "section-1", + "title": "Weekly Shopping", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "supermarkets", + "title": "Supermarkets", + "add_link_text": "Add another supermarket", + "empty_list_text": "There are no supermarkets" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-supermarket", + "for_list": "supermarkets", + "question": { + "type": "General", + "id": "any-supermarket-question", + "title": "Do you visit any supermarkets for your weekly shopping?", + "answers": [ + { + "type": "Radio", + "id": "any-supermarket-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-supermarket", + "list_name": "supermarkets" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-supermarket-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "supermarkets", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add any more supermarkets?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-supermarket", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other supermarkets?", + "question": { + "id": "add-question", + "type": "General", + "title": "Which supermarkets do you use for your weekly shopping?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-supermarket", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the supermarket?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-supermarket", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this supermarket?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this supermarket?", + "warning": "All of the information about this supermarket will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Supermarkets", + "item_title": { + "text": "{supermarket_name}", + "placeholders": [ + { + "placeholder": "supermarket_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "supermarket-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-answer", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "How much do you spend on groceries at {transformed_value}?", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "cost-of-shopping", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + }, + { + "label": { + "text": "How much do you spend on other items at {transformed_value}?", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "cost-of-other", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "days-a-week", + "label": { + "text": "How many days a week do you shop at {transformed_value}?", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "mandatory": false, + "type": "Number", + "decimal_places": 0, + "minimum": { + "value": 1 + }, + "maximum": { + "value": 7 + } + } + ] + }, + "answers": [ + { + "id": "based-checkbox-answer", + "label": "Are supermarkets UK or non UK based?", + "instruction": "Select any answers that apply", + "mandatory": false, + "options": [ + { + "label": "UK based supermarkets", + "value": "UK based supermarkets" + }, + { + "label": "Non UK based supermarkets", + "value": "Non UK based supermarkets" + } + ], + "type": "Checkbox" + }, + { + "id": "extra-static-answer", + "label": "How much do you spend on food at other types of shop?", + "type": "Currency", + "mandatory": false, + "currency": "GBP", + "decimal_places": 2 + } + ], + "id": "dynamic-answer-question", + "title": "How much do you spend each week at each of the following supermarket?", + "type": "General" + } + }, + { + "type": "Question", + "id": "extra-spending-block", + "question": { + "id": "extra-spending-question", + "title": "How much extra money do you spend each week on online food shopping?", + "type": "General", + "guidance": { + "contents": [ + { + "title": "How to test", + "list": [ + "If you enter a value other than £0 an additional question opens up.", + "Test that if you answer £0 to this question and then edit the answer from the calculated summary change link", + "First you are taken to the new question which opens up (provided it isn’t already complete), and only then back to the calculated summary" + ] + } + ] + }, + "answers": [ + { + "id": "extra-spending-answer", + "label": "Online food shopping expenditure", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "extra-spending-method-block", + "skip_conditions": { + "when": { + "==": [ + { + "source": "answers", + "identifier": "extra-spending-answer" + }, + 0 + ] + } + }, + "question": { + "id": "extra-spending-method-question", + "title": "Do you use a mobile phone to do online food shopping?", + "type": "General", + "answers": [ + { + "id": "extra-spending-method-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-spending", + "title": "We calculate the total cost of your weekly shopping to be %(total)s. Is this correct?", + "calculation": { + "title": "Weekly shopping cost", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "cost-of-shopping" + }, + { + "source": "answers", + "identifier": "cost-of-other" + }, + { + "source": "answers", + "identifier": "extra-spending-answer" + }, + { + "source": "answers", + "identifier": "extra-static-answer" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-visits", + "title": "We calculate the total visits to the shop to be %(total)s. Is this correct?", + "calculation": { + "title": "Weekly shopping trips", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "days-a-week" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Shopping Details", + "enabled": { + "when": { + ">": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-spending" + }, + 0 + ] + } + }, + "groups": [ + { + "id": "group-2", + "blocks": [ + { + "type": "Question", + "id": "supermarket-transport", + "question": { + "id": "weekly-car-trips-question", + "title": { + "placeholders": [ + { + "placeholder": "total_visits", + "value": { + "identifier": "calculated-summary-visits", + "source": "calculated_summary" + } + } + ], + "text": "On how many of your {total_visits} weekly shopping trips do you travel by car?" + }, + "type": "General", + "answers": [ + { + "id": "weekly-car-trips-answer", + "label": "Number of visits by car", + "mandatory": true, + "description": "Cannot exceed the total weekly trips from section 1", + "type": "Number", + "decimal_places": 0, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "calculated-summary-visits" + } + } + } + ] + } + }, + { + "type": "Question", + "id": "supermarket-transport-cost", + "skip_conditions": { + "when": { + "==": [ + { + "source": "answers", + "identifier": "weekly-car-trips-answer" + }, + 0 + ] + } + }, + "question": { + "id": "weekly-trips-cost", + "title": "How much do you spend on parking when travelling to the shop by car?", + "type": "General", + "answers": [ + { + "id": "weekly-trips-cost-answer", + "label": "Weekly spending on parking", + "mandatory": true, + "type": "Currency", + "currency": "GBP" + } + ] + } + }, + { + "type": "Interstitial", + "id": "calculated-summary-piping", + "content_variants": [ + { + "content": { + "title": "You have provided the following information about your weekly shop.", + "contents": [ + { + "list": [ + { + "text": "Total weekly supermarket spending: {currency_total}", + "placeholders": [ + { + "placeholder": "currency_total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-spending" + } + } + } + ] + } + ] + }, + { + "text": "Total weekly supermarket visits: {number_total}", + "placeholders": [ + { + "placeholder": "number_total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-visits" + } + } + } + ] + } + ] + }, + { + "text": "Total of supermarket visits by car: {number_total}", + "placeholders": [ + { + "placeholder": "number_total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "answers", + "identifier": "weekly-car-trips-answer" + } + } + } + ] + } + ] + } + ] + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "weekly-car-trips-answer" + }, + 0 + ] + } + }, + { + "content": { + "title": "You have provided the following information about your weekly shop.", + "contents": [ + { + "list": [ + { + "text": "Total weekly supermarket spending: {currency_total}", + "placeholders": [ + { + "placeholder": "currency_total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-spending" + } + } + } + ] + } + ] + }, + { + "text": "Total weekly supermarket visits: {number_total}", + "placeholders": [ + { + "placeholder": "number_total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-visits" + } + } + } + ] + } + ] + }, + { + "text": "Total of supermarket visits by car: {number_total}", + "placeholders": [ + { + "placeholder": "number_total", + "transforms": [ + { + "transform": "format_number", + "arguments": { + "number": { + "source": "answers", + "identifier": "weekly-car-trips-answer" + } + } + } + ] + } + ] + }, + { + "text": "Total spending on parking: {currency_total}", + "placeholders": [ + { + "placeholder": "currency_total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "answers", + "identifier": "weekly-trips-cost-answer" + } + } + } + ] + } + ] + } + ] + } + ] + }, + "when": { + ">": [ + { + "source": "answers", + "identifier": "weekly-car-trips-answer" + }, + 0 + ] + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_repeating_answers_only.json b/schemas/test/en/test_new_calculated_summary_repeating_answers_only.json new file mode 100644 index 0000000000..40a6de8f9f --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_repeating_answers_only.json @@ -0,0 +1,322 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary with Dynamic Answers", + "theme": "default", + "description": "A simple demo of a calculated summary which uses a list of repeating answers.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": {} + }, + "sections": [ + { + "id": "section", + "title": "List Collector Section", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "transport", + "title": "Transport", + "add_link_text": "Add another method of transport", + "empty_list_text": "There are no uses of public transport" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-transport", + "for_list": "transport", + "question": { + "type": "General", + "id": "any-transport-question", + "title": "Do you use public transport?", + "answers": [ + { + "type": "Radio", + "id": "any-transport-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-transport", + "list_name": "transport" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-transport-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "transport", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add any more types of transport?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-transport", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other type of transport?", + "question": { + "id": "add-question", + "type": "General", + "title": "Which types of public transport do you use?", + "answers": [ + { + "id": "transport-name", + "label": "Transport type", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Train", + "value": "Train" + }, + { + "label": "Bus", + "value": "Bus" + }, + { + "label": "Tube", + "value": "Tube" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-transport", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the type of public transport?", + "answers": [ + { + "id": "transport-name", + "label": "Transport type", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Train", + "value": "Train" + }, + { + "label": "Bus", + "value": "Bus" + }, + { + "label": "Tube", + "value": "Tube" + } + ] + } + ] + } + }, + "remove_block": { + "id": "remove-transport", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this method of transport?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this method of transport?", + "warning": "All of the information about this method of transport will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "transport", + "item_title": { + "text": "{transport_name}", + "placeholders": [ + { + "placeholder": "transport_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "transport-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-answer", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "transport" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "transport" + }, + "answers": [ + { + "label": { + "text": "How much do you spend per month travelling by {transformed_value}?", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ] + }, + "id": "cost-of-transport", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + } + ] + }, + "id": "dynamic-answer-question", + "title": "How much do you spend per month on the following modes of public transport?", + "type": "General" + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-spending", + "title": "We calculate the total monthly spending on public transport to be %(total)s. Is this correct?", + "calculation": { + "title": "Monthly public transport spending", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "cost-of-transport" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_new_calculated_summary_repeating_blocks.json b/schemas/test/en/test_new_calculated_summary_repeating_blocks.json new file mode 100644 index 0000000000..83f0fef8e5 --- /dev/null +++ b/schemas/test/en/test_new_calculated_summary_repeating_blocks.json @@ -0,0 +1,518 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Calculated Summary of answers from Repeating blocks", + "theme": "default", + "description": "A demo of a calculated summary which uses a answers from the repeating blocks in a list collector.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["section-1"] + } + }, + "sections": [ + { + "id": "section-1", + "title": "Transport", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "transport", + "title": "transport", + "item_anchor_answer_id": "transport-name", + "item_label": "Name of transport", + "add_link_text": "Add another method of transport", + "empty_list_text": "No method of transport added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-1", + "blocks": [ + { + "id": "block-car", + "type": "Question", + "question": { + "id": "question-car", + "type": "General", + "title": "How much do you spend per month travelling by Car?", + "answers": [ + { + "id": "answer-car", + "label": "Monthly expenditure travelling by car", + "mandatory": false, + "type": "Currency", + "currency": "GBP" + } + ] + } + }, + { + "id": "block-skip", + "type": "Question", + "question": { + "id": "question-skip", + "type": "General", + "title": "Would you like to skip the list collector that asks about other methods of transport?", + "guidance": { + "contents": [ + { + "description": "Use this to check the calculated summary shows the correct values when the list collector is not on the path." + } + ] + }, + "answers": [ + { + "id": "answer-skip", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "list-collector", + "when": { + "==": [ + { + "identifier": "answer-skip", + "source": "answers" + }, + "No" + ] + } + }, + { + "block": "calculated-summary-spending" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "transport", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you use any other methods of transport?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-transport", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other method of transport?", + "question": { + "id": "add-question", + "type": "General", + "title": "What other method of transport do you use?", + "answers": [ + { + "id": "transport-name", + "label": "Transport", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Tube", + "value": "Tube" + }, + { + "label": "Bus", + "value": "Bus" + }, + { + "label": "Train", + "value": "Train" + }, + { + "label": "Plane", + "value": "Plane" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-transport", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to add any other method of transport?", + "question": { + "id": "add-question", + "type": "General", + "title": "What other method of transport do you use?", + "answers": [ + { + "id": "transport-name", + "label": "Transport", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Tube", + "value": "Tube" + }, + { + "label": "Bus", + "value": "Bus" + }, + { + "label": "Train", + "value": "Train" + }, + { + "label": "Plane", + "value": "Plane" + } + ] + } + ] + } + }, + "remove_block": { + "id": "remove-transport", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this method of transport?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this method of transport?", + "warning": "All of the information about this method of transport will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "repeating_blocks": [ + { + "id": "transport-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "transport-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details of your expenditure travelling by {transport_name}", + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ] + }, + "answers": [ + { + "id": "transport-company", + "label": { + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ], + "text": "Which company do primarily use for travelling by {transport_name}?" + }, + "mandatory": false, + "type": "TextField" + }, + { + "id": "transport-cost", + "label": { + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ], + "text": "Monthly season ticket expenditure for travel by {transport_name}" + }, + "mandatory": false, + "type": "Currency", + "currency": "GBP" + }, + { + "id": "transport-additional-cost", + "label": { + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ], + "text": "Additional monthly expenditure for travel by {transport_name}" + }, + "mandatory": false, + "type": "Currency", + "currency": "GBP" + } + ] + } + }, + { + "id": "transport-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "transport-repeating-block-2-question-1", + "type": "General", + "title": { + "text": "How often do you travel by {transport_name}?", + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ] + }, + "answers": [ + { + "id": "transport-count", + "label": { + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ], + "text": "Monthly journeys by {transport_name}" + }, + "mandatory": false, + "type": "Number" + } + ] + } + } + ], + "summary": { + "title": "transport", + "item_title": { + "text": "{transport_name}", + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name" + } + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-spending", + "title": "We calculate the total monthly expenditure on transport to be %(total)s. Is this correct?", + "calculation": { + "title": "Monthly transport expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "answer-car" + }, + { + "source": "answers", + "identifier": "transport-cost" + }, + { + "source": "answers", + "identifier": "transport-additional-cost" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-count", + "title": "We calculate the total journeys made per month to be %(total)s. Is this correct?", + "calculation": { + "title": "Monthly journeys", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "transport-count" + } + ] + } + }, + "skip_conditions": { + "when": { + "or": [ + { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "transport" + } + ] + }, + 0 + ] + }, + { + "==": [ + { + "source": "answers", + "identifier": "answer-skip" + }, + "Yes" + ] + } + ] + } + } + } + ] + } + ] + }, + { + "enabled": { + "when": { + ">": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-count" + }, + 0 + ] + } + }, + "id": "section-2", + "summary": { + "show_on_completion": true + }, + "title": "Travel Details", + "groups": [ + { + "id": "group-2", + "blocks": [ + { + "type": "Question", + "id": "family-journeys", + "question": { + "id": "family-journeys-question", + "title": { + "placeholders": [ + { + "placeholder": "total_journeys", + "value": { + "identifier": "calculated-summary-count", + "source": "calculated_summary" + } + } + ], + "text": "How many of your {total_journeys} journeys are to visit family?" + }, + "type": "General", + "answers": [ + { + "id": "family-journeys-answer", + "label": "Number of trips to visit family", + "mandatory": true, + "description": "Cannot exceed the total journeys from section 1", + "type": "Number", + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "calculated-summary-count" + } + } + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_optional_guidance_and_description.json b/schemas/test/en/test_optional_guidance_and_description.json new file mode 100644 index 0000000000..38cdb32f44 --- /dev/null +++ b/schemas/test/en/test_optional_guidance_and_description.json @@ -0,0 +1,219 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Other input fields", + "theme": "default", + "description": "A questionnaire to demo optional question guidance and descriptions", + "metadata": [ + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "user_id", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "description-block", + "question": { + "answers": [ + { + "id": "answer-1", + "label": "Enter any description text that you want to be displayed", + "max_length": 20, + "mandatory": false, + "type": "TextField" + }, + { + "id": "answer-2", + "label": "Enter any alternative text that you want to be displayed", + "max_length": 20, + "mandatory": false, + "type": "TextField" + } + ], + "description": [ + "If entered, only one of the text fields will be used for the description as the next question uses the first_non_empty_item placeholder" + ], + "id": "description-question", + "title": "Do not enter anything here so you get an empty question description and question guidance on the following pages!", + "type": "General" + } + }, + { + "type": "Question", + "id": "mandatory-radio", + "question": { + "guidance": { + "contents": [ + { + "description": { + "text": "{description_text}", + "placeholders": [ + { + "placeholder": "description_text", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "answer-1" + }, + { + "source": "answers", + "identifier": "answer-2" + } + ] + } + } + ] + } + ] + } + } + ] + }, + "description": [ + { + "text": "{description_text}", + "placeholders": [ + { + "placeholder": "description_text", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "answer-1" + }, + { + "source": "answers", + "identifier": "answer-2" + } + ] + } + } + ] + } + ] + } + ], + "answers": [ + { + "id": "mandatory-radio-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "mandatory-radio-question", + "title": "Were the question description and guidance displayed?", + "type": "General" + } + }, + { + "type": "Question", + "id": "mandatory-radio-two", + "question": { + "guidance": { + "contents": [ + { + "description": "Description with an empty content list" + }, + { + "list": [ + "List item one", + { + "text": "{description_text}", + "placeholders": [ + { + "placeholder": "description_text", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "answer-1" + }, + { + "source": "answers", + "identifier": "answer-2" + } + ] + } + } + ] + } + ] + } + ] + } + ] + }, + "answers": [ + { + "id": "mandatory-radio-answer-two", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "mandatory-radio-question-two", + "title": "Was the contents list in the question guidance displayed?", + "type": "General" + } + } + ], + "id": "radios" + } + ] + } + ] +} diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json new file mode 100644 index 0000000000..1f332a51be --- /dev/null +++ b/schemas/test/en/test_supplementary_data.json @@ -0,0 +1,65 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "123", + "title": "Test Supplementary Data", + "theme": "default", + "description": "A questionnaire to demo loading Supplementary data.", + "metadata": [ + { + "name": "survey_id", + "type": "string" + }, + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "sds_dataset_id", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "interstitial-section", + "groups": [ + { + "blocks": [ + { + "id": "interstitial-definition", + "content": { + "title": "Supplementary Data", + "contents": [ + { + "description": "You have successfully loaded Supplementary data" + } + ] + }, + "type": "Interstitial" + } + ], + "id": "interstitial", + "title": "Interstitial Definition" + } + ] + } + ] +} diff --git a/schemas/test/en/test_validation_sum_against_total_dynamic_answers.json b/schemas/test/en/test_validation_sum_against_total_dynamic_answers.json new file mode 100644 index 0000000000..b2ec85409f --- /dev/null +++ b/schemas/test/en/test_validation_sum_against_total_dynamic_answers.json @@ -0,0 +1,470 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to validate a sum of dynamic answers are equal to a given total", + "theme": "default", + "description": "A survey that tests calculated answers against a total", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["total-section"] + } + }, + "sections": [ + { + "id": "total-section", + "title": "Total", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "total-block", + "question": { + "guidance": { + "contents": [ + { + "description": "Answer will be used for validation in the next section." + } + ] + }, + "id": "total-question", + "title": "What percentage of your shopping do you do at supermarkets?", + "type": "General", + "answers": [ + { + "id": "total-answer", + "label": "Total", + "mandatory": true, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 0 + } + ] + } + } + ], + "id": "total-group" + } + ] + }, + { + "id": "dynamic-answers-section", + "title": "Supermarkets", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "supermarkets", + "title": "Household members", + "add_link_text": "Add another supermarket", + "empty_list_text": "There are no supermarkets" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-supermarket", + "for_list": "supermarkets", + "question": { + "type": "General", + "id": "any-supermarket-question", + "title": "Do you need to add any supermarkets?", + "answers": [ + { + "type": "Radio", + "id": "any-supermarket-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-supermarket", + "list_name": "supermarkets" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-supermarket-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "supermarkets", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add any more supermarkets?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-supermarket", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other supermarkets?", + "question": { + "guidance": { + "contents": [ + { + "description": "Maximum spending value will be used for each supermarket’s max spending validation and placeholders." + } + ] + }, + "id": "add-question", + "type": "General", + "title": "Which supermarkets do you use for your weekly shopping?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-supermarket", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the supermarket?", + "answers": [ + { + "id": "supermarket-name", + "label": "Supermarket", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-supermarket", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this supermarket?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this supermarket?", + "warning": "All of the information about this supermarket will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Supermarkets", + "item_title": { + "text": "{supermarket_name}", + "placeholders": [ + { + "placeholder": "supermarket_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "supermarket-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-answer", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "guidance": { + "contents": [ + { + "description": "Answers are validated against total percentage from previous section." + } + ] + }, + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "Percentage of shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "percentage-of-shopping", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 0 + } + ] + }, + "answers": [ + { + "label": { + "text": "Percentage of shopping elsewhere", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "percentage-of-shopping-elsewhere", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 0 + } + ], + "warning": "These answers must add up to the total provided in the previous section", + "calculations": [ + { + "calculation_type": "sum", + "answer_id": "total-answer", + "answers_to_calculate": ["percentage-of-shopping-elsewhere", "percentage-of-shopping"], + "conditions": ["equals"] + } + ], + "id": "dynamic-answer-question", + "title": "What percent of your shopping do you do at each of the following supermarket?", + "type": "Calculated" + } + }, + { + "type": "Question", + "id": "total-block-other", + "question": { + "guidance": { + "contents": [ + { + "description": "Answer will be used for validation in the next question." + } + ] + }, + "id": "total-question-other", + "title": "Total amount you spend", + "type": "General", + "answers": [ + { + "id": "total-answer-other", + "label": "Total", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "dynamic-answer-only", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "guidance": { + "contents": [ + { + "description": "Answers are validated against total amount from previous question." + } + ] + }, + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "How much do you spend at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "spending-amount", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "answers", + "identifier": "total-answer-other" + } + }, + "minimum": { + "value": 0 + } + } + ] + }, + "warning": "These answers must add up to the total provided in the previous question", + "calculations": [ + { + "calculation_type": "sum", + "answer_id": "total-answer-other", + "answers_to_calculate": ["spending-amount"], + "conditions": ["equals"] + } + ], + "id": "dynamic-answer-only-question", + "title": "How much do you spend at each of the following supermarket?", + "type": "Calculated" + } + } + ] + } + ] + } + ] +} diff --git a/scripts/mock_data/supplementary_data_no_repeat.json b/scripts/mock_data/supplementary_data_no_repeat.json new file mode 100644 index 0000000000..95a1fc57e9 --- /dev/null +++ b/scripts/mock_data/supplementary_data_no_repeat.json @@ -0,0 +1,8 @@ +{ + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A" + } +} \ No newline at end of file diff --git a/scripts/mock_data/supplementary_data_with_repeat.json b/scripts/mock_data/supplementary_data_with_repeat.json new file mode 100644 index 0000000000..d589cbfab7 --- /dev/null +++ b/scripts/mock_data/supplementary_data_with_repeat.json @@ -0,0 +1,85 @@ +{ + "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "note": { + "title": "Volume of total production", + "description": "Figures should cover the total quantity of the goods produced during the period of the return" + }, + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports or outdoor games", + "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "guidance_include": { + "title": "Include", + "list": [ + "for children's playgrounds", + "swimming pools and paddling pools" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", + "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" + ] + }, + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "89929002", + "label": "Volume of sales", + "unit_label": "Tonnes" + }, + "total_volume": { + "answer_code": "89929005", + "label": "Total volume produced", + "unit_label": "Tonnes" + } + }, + { + "identifier": "201630601", + "name": "Other Minerals", + "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", + "guidance_include": { + "title": "Include", + "list": [ + "natural graphite", + "quartz for industrial use", + "diatomite; magnesia; feldspar", + "magnesite; natural magnesium carbonate", + "talc including steatite and chlorite", + "unexpanded vermiculite and perlite" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "natural quartz sands" + ] + }, + "value_sales": { + "answer_code": "201630601", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "201630602", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "201630605", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + } + ] + } + } +} diff --git a/scripts/mock_data/supplementary_data_with_repeat_v2.json b/scripts/mock_data/supplementary_data_with_repeat_v2.json new file mode 100644 index 0000000000..a015e11c34 --- /dev/null +++ b/scripts/mock_data/supplementary_data_with_repeat_v2.json @@ -0,0 +1,117 @@ +{ + "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "note": { + "title": "Volume of total production", + "description": "New figures should cover the total quantity of the goods produced during the period of the return" + }, + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports", + "cn_codes": "2504 + 2512 + 2519 + 2524", + "guidance_include": { + "title": "Include", + "list": [ + "for children's playgrounds" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", + "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics", + "outdoor sports" + ] + }, + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "89929002", + "label": "Volume of sales", + "unit_label": "Tonnes" + }, + "total_volume": { + "answer_code": "89929005", + "label": "Total volume produced", + "unit_label": "Tonnes" + } + }, + { + "identifier": "201630601", + "name": "Other Minerals", + "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", + "guidance_include": { + "title": "Include", + "list": [ + "natural graphite", + "quartz for industrial use", + "diatomite; magnesia; feldspar", + "magnesite; natural magnesium carbonate", + "talc including steatite and chlorite", + "unexpanded vermiculite and perlite" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "natural quartz sands" + ] + }, + "value_sales": { + "answer_code": "201630601", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "201630602", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "201630605", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + }, + { + "identifier": "202346331", + "name": "Kitchen Equipment", + "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", + "guidance_include": { + "title": "Include", + "list": [ + "pots and pans", + "chopping board" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "cutlery" + ] + }, + "value_sales": { + "answer_code": "20234633101", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "20234633102", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "20234633103", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + } + ] + } + } +} diff --git a/scripts/mock_data/supplementary_dataset_metadata_response.json b/scripts/mock_data/supplementary_dataset_metadata_response.json new file mode 100644 index 0000000000..91af0583c5 --- /dev/null +++ b/scripts/mock_data/supplementary_dataset_metadata_response.json @@ -0,0 +1,39 @@ +[ + { + "survey_id": "123", + "period_id": "202301", + "title": "Supplementary data without repeating items", + "sds_schema_version": 1, + "sds_published_at": "2022-03-01T12:30:02Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" + }, + { + "survey_id": "123", + "period_id": "202302", + "title": "Supplementary data with repeating items", + "sds_schema_version": 1, + "sds_published_at": "2022-03-01T12:30:02Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64" + }, + { + "survey_id": "123", + "period_id": "202303", + "title": "Supplementary data with repeats v2 additional items", + "sds_schema_version": 1, + "sds_published_at": "2023-05-04T14:35:05Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88" + } +] + diff --git a/scripts/mock_sds_endpoint.py b/scripts/mock_sds_endpoint.py new file mode 100644 index 0000000000..8e4bf1a56a --- /dev/null +++ b/scripts/mock_sds_endpoint.py @@ -0,0 +1,67 @@ +import json + +import yaml +from flask import Flask, Response, request +from sdc.crypto.jwe_helper import JWEHelper +from sdc.crypto.key_store import KeyStore + +from app.keys import KEY_PURPOSE_SDS + +app = Flask(__name__) +with open("dev-keys.yml", encoding="UTF-8") as keys_file: + keys = KeyStore(yaml.safe_load(keys_file)) + + +@app.route("/v1/unit_data") +def get_sds_data(): + dataset_id = request.args.get("dataset_id") + + if dataset_id == "c067f6de-6d64-42b1-8b02-431a3486c178": + return encrypt_mock_data( + load_mock_data("scripts/mock_data/supplementary_data_no_repeat.json") + ) + if dataset_id == "34a80231-c49a-44d0-91a6-8fe1fb190e64": + return encrypt_mock_data( + load_mock_data("scripts/mock_data/supplementary_data_with_repeat.json") + ) + if dataset_id == "6b378962-f0c7-4e8c-947e-7d24ee1b6b88": + return encrypt_mock_data( + load_mock_data("scripts/mock_data/supplementary_data_with_repeat_v2.json") + ) + + return Response(status=404) + + +@app.route("/v1/dataset_metadata") +def get_sds_dataset_ids(): + survey_id = request.args.get("survey_id") + period_id = request.args.get("period_id") + + return load_mock_sds_dataset_metadata(survey_id, period_id) + + +def load_mock_data(filename): + with open(filename, encoding="utf-8") as mock_data_file: + return json.load(mock_data_file) + + +def load_mock_sds_dataset_metadata(survey_id: str, period_id: str): + del period_id # not required for mock + if survey_id == "123": + return load_mock_data( + "scripts/mock_data/supplementary_dataset_metadata_response.json" + ) + + return Response(status=404) + + +def encrypt_mock_data(mock_data): + key = keys.get_key(purpose=KEY_PURPOSE_SDS, key_type="private") + mock_data["data"] = JWEHelper.encrypt_with_key( + json.dumps(mock_data["data"]), key.kid, key.as_jwk() + ) + return mock_data + + +if __name__ == "__main__": + app.run(host="localhost", port=5003) diff --git a/scripts/validate_test_schemas.sh b/scripts/validate_test_schemas.sh index 9462d7d3b0..fc3a0b4f2f 100755 --- a/scripts/validate_test_schemas.sh +++ b/scripts/validate_test_schemas.sh @@ -62,4 +62,4 @@ done echo -e "\\n${green}$passed Passed${default} - ${red}$failed Failed${default}" -exit "$exit" +exit "$exit" \ No newline at end of file diff --git a/templates/listrepeatingquestion.html b/templates/listrepeatingquestion.html new file mode 100644 index 0000000000..d8516f06da --- /dev/null +++ b/templates/listrepeatingquestion.html @@ -0,0 +1 @@ +{% extends 'list-action.html' %} diff --git a/templates/macros/helpers.html b/templates/macros/helpers.html index 4d3744d670..48a2ddfb67 100644 --- a/templates/macros/helpers.html +++ b/templates/macros/helpers.html @@ -4,7 +4,9 @@ {%- macro format_paragraphs(paragraphs) -%} {%- for paragraph in paragraphs -%} -

      {{paragraph}}

      + {%- if paragraph -%} +

      {{paragraph}}

      + {%- endif -%} {%- else -%} {%- endfor -%} {%- endmacro -%} diff --git a/templates/partials/contents.html b/templates/partials/contents.html index 3aede16413..6fcb6730e9 100644 --- a/templates/partials/contents.html +++ b/templates/partials/contents.html @@ -28,7 +28,9 @@

      {{ item.title }}

      {%- if item.list -%}
        {%- for list_item in item.list -%} + {%- if list_item|length > 0 -%}
      • {{ list_item }}
      • + {%- endif -%} {% endfor %}
      {% endif %} diff --git a/templates/partials/question.html b/templates/partials/question.html index 9b5aab2d3e..d0207cfa28 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -6,6 +6,7 @@ {% from 'macros/helpers.html' import interviewer_note %} {% set form = content.form %} +{% set display = namespace(guidance=False) %} {% set title= interviewer_note(question.title) if block.interviewer_only else question.title %} {% set question_title= question.title %} @@ -58,12 +59,19 @@ {% set question_guidance %} {%- if question.guidance -%} {% set contents = question.guidance.contents %} - {% call onsPanel({ - "id": "question-guidance-" ~ question.id, - "classes": "ons-u-mb-m" - }) %} - {% include 'partials/contents.html' %} - {% endcall %} + {% for item in contents %} + {%- if (item['title'], item['description'], item['list'], item['definition'], item['guidance'])|select|first -%} + {% set display.guidance=True %} + {%- endif -%} + {% endfor %} + {%- if display.guidance -%} + {% call onsPanel({ + "id": "question-guidance-" ~ question.id, + "classes": "ons-u-mb-m" + }) %} + {% include 'partials/contents.html' %} + {% endcall %} + {%- endif -%} {% endif %} {% endset %} diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index efff1abdf9..2fc1005761 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -27,8 +27,7 @@ no_answer_provided=_("No answer provided"), edit_link_text=_("Change"), edit_link_aria_label=_("Change your answer for:"), - calculated_question=content.summary.calculated_question, - icon=None + calculated_question=content.summary.calculated_question ) } ] diff --git a/templates/partials/summary/list-summary.html b/templates/partials/summary/list-summary.html index 4c5e8f759c..50dde7e6d2 100644 --- a/templates/partials/summary/list-summary.html +++ b/templates/partials/summary/list-summary.html @@ -2,17 +2,17 @@ {% if list.editable %} {% set rows = map_list_collector_config( - list.list_items, - "person", - _("Change"), - _("Change details for {item_name}"), - _("Remove"), - _("Remove {item_name}") + list_items=list.list_items, + render_icon=True, + edit_link_text=_("Change"), + edit_link_aria_label=_("Change details for {item_name}"), + remove_link_text=_("Remove"), + remove_link_aria_label=_("Remove {item_name}") ) %} {% else %} {% set rows = map_list_collector_config( - list.list_items, - "person" + list_items=list.list_items, + render_icon=True ) %} {% endif %} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 03702f8a17..55543a9dbb 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -27,8 +27,7 @@ remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", edit_link_text=_("Change") if not view_submitted_response else "", edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", - calculated_question=content.summary.calculated_question, - icon=None + calculated_question=content.summary.calculated_question ), "classes": "ons-u-mt-m" if loop.index > 1 else "", "placeholderText": group.placeholder_text, diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 6c6a2cff45..f8d35d275c 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,10 +1,14 @@ # pylint: disable=redefined-outer-name from datetime import datetime, timedelta, timezone +from http.client import HTTPMessage import fakeredis import pytest from mock import MagicMock +from mock.mock import Mock +from requests.adapters import ConnectTimeoutError, ReadTimeoutError +from urllib3.connectionpool import HTTPConnectionPool, HTTPResponse from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore @@ -13,6 +17,7 @@ from app.data_models.progress_store import ProgressStore from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.publisher import PubSubPublisher from app.questionnaire.location import Location from app.setup import create_app @@ -159,6 +164,11 @@ def progress_store(): return ProgressStore() +@pytest.fixture +def supplementary_data_store(): + return SupplementaryDataStore() + + @pytest.fixture def publisher(mocker): mocker.patch( @@ -186,6 +196,41 @@ def current_location(): return Location(section_id="some-section", block_id="some-block") +@pytest.fixture +def location(): + return Location("test-section", "test-block", "test-list", "list_item_id") + + @pytest.fixture def mock_autoescape_context(mocker): return mocker.Mock(autoescape=True) + + +@pytest.fixture +def mocked_response_content(mocker): + decodable_content = Mock() + decodable_content.decode.return_value = b"{}" + mocker.patch("requests.models.Response.content", decodable_content) + + +@pytest.fixture +def mocked_make_request_with_timeout( + mocker, mocked_response_content # pylint: disable=unused-argument +): + connect_timeout_error = ConnectTimeoutError("connect timed out") + read_timeout_error = ReadTimeoutError( + pool=None, message="read timed out", url="test-url" + ) + + response_not_timed_out = HTTPResponse(status=200, headers={}, msg=HTTPMessage()) + response_not_timed_out.drain_conn = Mock(return_value=None) + + return mocker.patch.object( + HTTPConnectionPool, + "_make_request", + side_effect=[ + connect_timeout_error, + read_timeout_error, + response_not_timed_out, + ], + ) diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index 4d71f6623d..b50f356880 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -1,10 +1,14 @@ +# pylint: disable=redefined-outer-name from datetime import datetime, timedelta, timezone import pytest +from mock.mock import Mock +from app.data_models import ListStore, QuestionnaireStore from app.data_models.answer_store import Answer from app.data_models.progress_store import CompletionStatus from app.data_models.session_store import SessionStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.storage import storage_encryption from tests.app.parser.conftest import get_response_expires_at @@ -96,6 +100,7 @@ def basic_input(): "block_ids": ["a-test-block"], } ], + "SUPPLEMENTARY_DATA": {"data": {}, "list_mappings": {}}, "RESPONSE_METADATA": {"test-meta": "test"}, } @@ -154,3 +159,73 @@ def app_session_store_encoded(mocker, session_data): store.user_id, store.user_ik, store.pepper ) return store + + +@pytest.fixture +def supplementary_data(): + return { + "schema_version": "v1", + "identifier": "12346789012A", + "note": { + "title": "Volume of total production", + }, + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports or outdoor games", + "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales", + }, + }, + { + "identifier": "201630601", + "name": "Other Minerals", + "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", + "value_sales": { + "answer_code": "201630601", + "label": "Value of sales", + }, + }, + ] + }, + } + + +@pytest.fixture +def supplementary_data_list_mappings(): + return { + "products": { + "89929001": "item-1", + "201630601": "item-2", + }, + } + + +@pytest.fixture +def supplementary_data_store_with_data( + supplementary_data, supplementary_data_list_mappings +): + return SupplementaryDataStore( + supplementary_data=supplementary_data, + list_mappings=supplementary_data_list_mappings, + ) + + +@pytest.fixture +def questionnaire_store_with_supplementary_data( + questionnaire_store, supplementary_data_store_with_data +): + questionnaire_store = QuestionnaireStore(questionnaire_store.storage) + questionnaire_store.supplementary_data_store = supplementary_data_store_with_data + questionnaire_store.list_store = ListStore( + [{"items": ["item-1", "item-2"], "name": "products"}] + ) + # Mock the identifier generation in list store so the ids are item-1, item-2, ... + # pylint: disable=protected-access + questionnaire_store.list_store._generate_identifier = Mock( + side_effect=(f"item-{i}" for i in range(3, 100)) + ) + return questionnaire_store diff --git a/tests/app/data_model/test_answer_store.py b/tests/app/data_model/test_answer_store.py index 4688736bf8..44a68381f9 100644 --- a/tests/app/data_model/test_answer_store.py +++ b/tests/app/data_model/test_answer_store.py @@ -103,13 +103,13 @@ def test_remove_answer_without_list_item_id(basic_answer_store): def test_remove_all_answers_for_list_item_id(relationship_answer_store): - relationship_answer_store.remove_all_answers_for_list_item_id("abc123") + relationship_answer_store.remove_all_answers_for_list_item_ids("abc123") assert relationship_answer_store.get_answer("answer1") is None def test_remove_all_answers_for_list_item_id_doesnt_exist(relationship_answer_store): len_before = len(relationship_answer_store) - relationship_answer_store.remove_all_answers_for_list_item_id("not-an-id") + relationship_answer_store.remove_all_answers_for_list_item_ids("not-an-id") assert len(relationship_answer_store) == len_before diff --git a/tests/app/data_model/test_list_store.py b/tests/app/data_model/test_list_store.py index 23cc6b23c5..7240b6f39d 100644 --- a/tests/app/data_model/test_list_store.py +++ b/tests/app/data_model/test_list_store.py @@ -91,6 +91,14 @@ def test_delete_list_item_id(): assert not store._lists # pylint: disable=protected-access +def test_delete_list(): + store = ListStore() + store.add_list_item("people") + store.add_list_item("people") + store.delete_list("people") + assert not store._lists # pylint: disable=protected-access + + def test_delete_list_item_id_does_not_raise(): store = ListStore() store.add_list_item("people") diff --git a/tests/app/data_model/test_metadata_proxy.py b/tests/app/data_model/test_metadata_proxy.py index 79dce605c7..93c5cd603c 100644 --- a/tests/app/data_model/test_metadata_proxy.py +++ b/tests/app/data_model/test_metadata_proxy.py @@ -1,7 +1,7 @@ import pytest from werkzeug.datastructures import ImmutableDict -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models.metadata_proxy import MetadataProxy, SurveyMetadata METADATA_V1 = { diff --git a/tests/app/data_model/test_progress_store.py b/tests/app/data_model/test_progress_store.py index 72b218d61f..30553af5ef 100644 --- a/tests/app/data_model/test_progress_store.py +++ b/tests/app/data_model/test_progress_store.py @@ -10,8 +10,8 @@ def test_serialisation(): store.add_completed_location(Location(section_id="s1", block_id="one")) store.add_completed_location(Location(section_id="s1", block_id="two")) - store.update_section_status( - section_status=CompletionStatus.COMPLETED, section_id="s1" + store.update_section_or_repeating_blocks_progress_completion_status( + completion_status=CompletionStatus.COMPLETED, section_id="s1" ) store.add_completed_location( @@ -22,8 +22,8 @@ def test_serialisation(): list_item_id="abc123", ) ) - store.update_section_status( - section_status=CompletionStatus.IN_PROGRESS, + store.update_section_or_repeating_blocks_progress_completion_status( + completion_status=CompletionStatus.IN_PROGRESS, section_id="s2", list_item_id="abc123", ) @@ -67,11 +67,16 @@ def test_deserialisation(): ] store = ProgressStore(in_progress_sections) - assert store.get_section_status(section_id="s1") == CompletionStatus.IN_PROGRESS + assert ( + store.get_section_or_repeating_blocks_progress_status(section_id="s1") + == CompletionStatus.IN_PROGRESS + ) assert store.get_completed_block_ids(section_id="s1") == ["one", "two"] assert ( - store.get_section_status(section_id="s2", list_item_id="abc123") + store.get_section_or_repeating_blocks_progress_status( + section_id="s2", list_item_id="abc123" + ) == CompletionStatus.COMPLETED ) assert store.get_completed_block_ids(section_id="s2", list_item_id="abc123") == [ @@ -152,9 +157,14 @@ def test_add_completed_location_existing(): store.add_completed_location(non_repeating_location) store.add_completed_location(repeating_location) - assert store.get_section_status(section_id="s1") == CompletionStatus.COMPLETED assert ( - store.get_section_status(section_id="s2", list_item_id="abc123") + store.get_section_or_repeating_blocks_progress_status(section_id="s1") + == CompletionStatus.COMPLETED + ) + assert ( + store.get_section_or_repeating_blocks_progress_status( + section_id="s2", list_item_id="abc123" + ) == CompletionStatus.COMPLETED ) @@ -191,9 +201,14 @@ def test_add_completed_location_new(): store.add_completed_location(non_repeating_location) store.add_completed_location(repeating_location) - assert store.get_section_status(section_id="s1") == CompletionStatus.COMPLETED assert ( - store.get_section_status(section_id="s2", list_item_id="abc123") + store.get_section_or_repeating_blocks_progress_status(section_id="s1") + == CompletionStatus.COMPLETED + ) + assert ( + store.get_section_or_repeating_blocks_progress_status( + section_id="s2", list_item_id="abc123" + ) == CompletionStatus.COMPLETED ) @@ -244,9 +259,18 @@ def test_remove_completed_location(): ] assert store.get_completed_block_ids(section_id="s3") == [] - assert store.get_section_status("s1") == CompletionStatus.COMPLETED - assert store.get_section_status("s2", "abc123") == CompletionStatus.COMPLETED - assert store.get_section_status("s3") == CompletionStatus.IN_PROGRESS + assert ( + store.get_section_or_repeating_blocks_progress_status("s1") + == CompletionStatus.COMPLETED + ) + assert ( + store.get_section_or_repeating_blocks_progress_status("s2", "abc123") + == CompletionStatus.COMPLETED + ) + assert ( + store.get_section_or_repeating_blocks_progress_status("s3") + == CompletionStatus.IN_PROGRESS + ) assert store.is_dirty @@ -291,18 +315,23 @@ def test_update_section_status(): ] store = ProgressStore(completed) - store.update_section_status( - section_status=CompletionStatus.IN_PROGRESS, section_id="s1" + store.update_section_or_repeating_blocks_progress_completion_status( + completion_status=CompletionStatus.IN_PROGRESS, section_id="s1" ) - store.update_section_status( - section_status=CompletionStatus.IN_PROGRESS, + store.update_section_or_repeating_blocks_progress_completion_status( + completion_status=CompletionStatus.IN_PROGRESS, section_id="s2", list_item_id="abc123", ) - assert store.get_section_status(section_id="s1") == CompletionStatus.IN_PROGRESS assert ( - store.get_section_status(section_id="s2", list_item_id="abc123") + store.get_section_or_repeating_blocks_progress_status(section_id="s1") + == CompletionStatus.IN_PROGRESS + ) + assert ( + store.get_section_or_repeating_blocks_progress_status( + section_id="s2", list_item_id="abc123" + ) == CompletionStatus.IN_PROGRESS ) assert store.is_dirty @@ -319,9 +348,14 @@ def test_update_non_existing_section_status(): ] store = ProgressStore(completed) - store.update_section_status("s2", CompletionStatus.IN_PROGRESS) + store.update_section_or_repeating_blocks_progress_completion_status( + "s2", CompletionStatus.IN_PROGRESS + ) - assert store.get_section_status("s1") == CompletionStatus.COMPLETED + assert ( + store.get_section_or_repeating_blocks_progress_status("s1") + == CompletionStatus.COMPLETED + ) assert "s2" not in store assert store.get_completed_block_ids(section_id="s2") == [] @@ -346,9 +380,14 @@ def test_get_section_status(): ] store = ProgressStore(existing_progress) - assert store.get_section_status(section_id="s1") == CompletionStatus.COMPLETED assert ( - store.get_section_status(section_id="s2", list_item_id="abc123") + store.get_section_or_repeating_blocks_progress_status(section_id="s1") + == CompletionStatus.COMPLETED + ) + assert ( + store.get_section_or_repeating_blocks_progress_status( + section_id="s2", list_item_id="abc123" + ) == CompletionStatus.IN_PROGRESS ) @@ -413,9 +452,15 @@ def test_is_section_complete(): store = ProgressStore(completed) - assert store.is_section_complete(section_id="s1", list_item_id=None) is True - assert store.is_section_complete(section_id="s4", list_item_id="123abc") is True - assert store.is_section_complete(section_id="s5", list_item_id="456def") is True + assert store.is_section_or_repeating_blocks_progress_complete( + section_id="s1", list_item_id=None + ) + assert store.is_section_or_repeating_blocks_progress_complete( + section_id="s4", list_item_id="123abc" + ) + assert store.is_section_or_repeating_blocks_progress_complete( + section_id="s5", list_item_id="456def" + ) def test_remove_progress_for_list_item_id(): @@ -511,7 +556,9 @@ def test_in_progress_and_completed_section_ids(section_ids, expected_section_key store = ProgressStore(completed) statuses = {CompletionStatus.COMPLETED, CompletionStatus.IN_PROGRESS} - section_keys = store.section_keys(section_ids=section_ids, statuses=statuses) + section_keys = store.section_and_repeating_blocks_progress_keys( + section_ids=section_ids, statuses=statuses + ) assert sorted(section_keys) == expected_section_keys @@ -551,7 +598,9 @@ def test_section_keys(): ] store = ProgressStore(completed) - section_keys = store.section_keys(section_ids={"s1", "s2", "s3"}) + section_keys = store.section_and_repeating_blocks_progress_keys( + section_ids={"s1", "s2", "s3"} + ) assert sorted(section_keys) == sorted( [("s1", None), ("s2", None), ("s3", "abc123")] ) diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index f9d686770b..4bf8f5a218 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -4,6 +4,7 @@ from app.data_models.answer_store import AnswerStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.utilities.json import json_dumps, json_loads @@ -64,6 +65,9 @@ def test_questionnaire_store_updates_storage(questionnaire_store, basic_input): store.answer_store = AnswerStore(basic_input["ANSWERS"]) store.response_metadata = basic_input["RESPONSE_METADATA"] store.progress_store = ProgressStore(basic_input["PROGRESS"]) + store.supplementary_data_store = SupplementaryDataStore.deserialize( + basic_input["SUPPLEMENTARY_DATA"] + ) # When store.save() @@ -112,3 +116,138 @@ def test_questionnaire_store_raises_when_writing_to_metadata(questionnaire_store with pytest.raises(TypeError): store.metadata["no"] = "writing" + + +class TestQuestionnaireStoreWithSupplementaryData: + store: QuestionnaireStore + + def assert_list_store_data(self, list_name: str, list_item_ids: list[str]): + """Helper function to check that ListStore contains the given list with matching list_item_ids""" + lists = [list_model.name for list_model in self.store.list_store] + assert list_name in lists + assert self.store.list_store[list_name].items == list_item_ids + + def test_adding_new_supplementary_data( + self, questionnaire_store, supplementary_data + ): + """Tests that adding supplementary data adds supplementary list items to the list store + this test doesn't mock list item ids, and checks that they match those in list_mappings + """ + self.store = QuestionnaireStore(questionnaire_store.storage) + self.store.set_supplementary_data(supplementary_data) + assert "products" in self.store.supplementary_data_store.list_mappings + supplementary_list_item_ids = list( + self.store.supplementary_data_store.list_mappings["products"].values() + ) + # check list mapping ids match list store ids + self.assert_list_store_data("products", supplementary_list_item_ids) + + def test_updating_supplementary_data( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Test that overwriting supplementary data with additional lists/items adds them to the list store + without duplicating any existing data""" + self.store = questionnaire_store_with_supplementary_data + + supplementary_data["items"]["supermarkets"] = [{"identifier": "54321"}] + supplementary_data["items"]["products"].append({"identifier": "12345"}) + self.store.set_supplementary_data(supplementary_data) + + assert self.store.supplementary_data_store.list_mappings == { + "products": { + "89929001": "item-1", + "201630601": "item-2", + "12345": "item-3", + }, + "supermarkets": {"54321": "item-4"}, + } + + self.assert_list_store_data("products", ["item-1", "item-2", "item-3"]) + self.assert_list_store_data("supermarkets", ["item-4"]) + + def test_removing_some_supplementary_data( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Tests that if you overwrite existing supplementary data with data that is missing list item ids + or lists, that the list store is updated to remove that data""" + self.store = questionnaire_store_with_supplementary_data + + del supplementary_data["items"]["products"][0] + self.store.set_supplementary_data(supplementary_data) + + # products item-1 should be gone + self.assert_list_store_data("products", ["item-2"]) + + def test_removing_all_supplementary_data( + self, questionnaire_store_with_supplementary_data + ): + """Checks that removing all supplementary data clears out the list store""" + self.store = questionnaire_store_with_supplementary_data + self.store.set_supplementary_data({}) + assert len(list(self.store.list_store)) == 0 + + def test_removing_supplementary_lists_with_answers( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Tests that if you overwrite supplementary data, + related answers for old list/list_item_ids are removed from the answer store""" + self.store = questionnaire_store_with_supplementary_data + + # add some answers for the supplementary list items + self.store.answer_store = AnswerStore( + [ + { + "answer_id": "product-sales-answer", + "value": "100", + "list_item_id": "item-1", + }, + { + "answer_id": "product-sales-answer", + "value": "200", + "list_item_id": "item-2", + }, + ] + ) + + # delete the first product and update supplementary data + del supplementary_data["items"]["products"][0] + self.store.set_supplementary_data(supplementary_data) + + # item-1 should be gone + self.assert_list_store_data("products", ["item-2"]) + # the answer for it should be too + answers = list(self.store.answer_store.answer_map.keys()) + assert len(answers) == 1 + assert answers[0] == ("product-sales-answer", "item-2") + + # remove all answers + self.store.set_supplementary_data({}) + assert not self.store.answer_store.answer_map + + def test_removing_supplementary_data_ignores_non_supplementary_data( + self, questionnaire_store_with_supplementary_data + ): + """Tests that removing supplementary data does not affect other lists and answers""" + self.store = questionnaire_store_with_supplementary_data + # unrelated + self.store.answer_store = AnswerStore( + [ + { + "answer_id": "unrelated-answer", + "value": "100", + "list_item_id": "JxSW21", + }, + { + "answer_id": "sales", + "value": "200", + }, + ] + ) + self.store.list_store.add_list_item("supermarkets") + self.assert_list_store_data("products", ["item-1", "item-2"]) + self.assert_list_store_data("supermarkets", ["item-3"]) + + self.store.set_supplementary_data({}) + self.assert_list_store_data("supermarkets", ["item-3"]) + answers = list(self.store.answer_store.answer_map.keys()) + assert answers == [("unrelated-answer", "JxSW21"), ("sales", None)] diff --git a/tests/app/data_model/test_supplementary_data_store.py b/tests/app/data_model/test_supplementary_data_store.py new file mode 100644 index 0000000000..b1f267d4f9 --- /dev/null +++ b/tests/app/data_model/test_supplementary_data_store.py @@ -0,0 +1,86 @@ +import pytest + +from app.data_models.supplementary_data_store import ( + InvalidSupplementaryDataSelector, + SupplementaryDataStore, +) +from app.utilities.make_immutable import make_immutable + + +def test_supplementary_data_serialisation( + supplementary_data_store_with_data, + supplementary_data, + supplementary_data_list_mappings, +): + serialized = supplementary_data_store_with_data.serialize() + + assert serialized == { + "data": supplementary_data, + "list_mappings": supplementary_data_list_mappings, + } + + +def test_supplementary_data_deserialisation(): + raw_data = { + "identifier": "12346789012A", + "items": { + "products": [ + {"identifier": "89929001"}, + {"identifier": "201630601"}, + ] + }, + } + list_mappings = {"products": {"89929001": "item-1", "201630601": "item-2"}} + + serialized = { + "data": raw_data, + "list_mappings": list_mappings, + } + + deserialized = SupplementaryDataStore.deserialize(serialized) + + assert deserialized.raw_data == make_immutable(raw_data) + assert deserialized.list_mappings == make_immutable(list_mappings) + assert deserialized._data_map == { # pylint: disable=protected-access + ("identifier", None): "12346789012A", + ("products", "item-1"): {"identifier": "89929001"}, + ("products", "item-2"): {"identifier": "201630601"}, + } + + +def test_empty_supplementary_data_deserialisation(): + empty_store = SupplementaryDataStore.deserialize({}) + assert not empty_store.raw_data + assert not empty_store.list_mappings + assert not empty_store._data_map # pylint: disable=protected-access + + +@pytest.mark.parametrize( + "identifier,list_item_id,selectors,expected", + [ + ("identifier", None, None, "12346789012A"), + ("note", None, ["title"], "Volume of total production"), + ("products", "item-2", ["name"], "Other Minerals"), + ("products", "item-1", ["value_sales", "answer_code"], "89929001"), + ("INVALID", None, None, None), + ], +) +def test_get_supplementary_data( + supplementary_data_store_with_data, identifier, list_item_id, selectors, expected +): + assert ( + supplementary_data_store_with_data.get_data( + identifier=identifier, + list_item_id=list_item_id, + selectors=selectors, + ) + == expected + ) + + +def test_get_supplementary_data_invalid_selectors(supplementary_data_store_with_data): + with pytest.raises(InvalidSupplementaryDataSelector) as exception: + supplementary_data_store_with_data.get_data( + identifier="identifier", selectors=["INVALID"], list_item_id=None + ) + assert "Cannot use the selector `INVALID` on non-nested data" == str(exception) diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index d61f2e2a69..c82611746e 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1906,3 +1906,113 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): other_text_field = getattr(form, "other-answer-mandatory") assert other_text_field.data == "Other text field value" + + +def test_dynamic_answers_question_validates(app): + with app.test_request_context(): + schema = load_schema_from_name( + "test_validation_sum_against_total_dynamic_answers" + ) + answer_store = AnswerStore([{"answer_id": "total-answer", "value": 100}]) + question_schema = schema.get_block("dynamic-answer").get("question") + question_schema = QuestionnaireSchema.get_mutable_deepcopy(question_schema) + question_schema["answers"].append( + { + "label": "Percentage of shopping at Tesco", + "id": "percentage-of-shopping-lCIZsS", + "mandatory": False, + "type": "Percentage", + "maximum": {"value": 100}, + "decimal_places": 0, + "original_answer_id": "percentage-of-shopping", + "list_item_id": "lCIZsS", + } + ) + + list_store = ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]) + + form_data = MultiDict( + { + "percentage-of-shopping-lCIZsS": "25", + "percentage-of-shopping-elsewhere": "75", + } + ) + + expected_form_data = { + "csrf_token": None, + "percentage-of-shopping-lCIZsS": "25", + "percentage-of-shopping-elsewhere": "75", + } + + form = generate_form( + schema, + question_schema, + answer_store, + list_store, + metadata=get_metadata(), + response_metadata={}, + form_data=form_data, + progress_store=ProgressStore(), + location=Location( + section_id="section", + block_id="dynamic-answer", + list_name=None, + list_item_id=None, + ), + ) + + form.validate() + assert form.data, expected_form_data + + +def test_dynamic_answers_question_raises_validation_error(app): + with app.test_request_context(): + schema = load_schema_from_name( + "test_validation_sum_against_total_dynamic_answers" + ) + answer_store = AnswerStore([{"answer_id": "total-answer", "value": 100}]) + question_schema = schema.get_block("dynamic-answer").get("question") + question_schema = QuestionnaireSchema.get_mutable_deepcopy(question_schema) + question_schema["answers"].append( + { + "label": "Percentage of shopping at Tesco", + "id": "percentage-of-shopping-lCIZsS", + "mandatory": False, + "type": "Percentage", + "maximum": {"value": 100}, + "decimal_places": 0, + "original_answer_id": "percentage-of-shopping", + "list_item_id": "lCIZsS", + } + ) + + list_store = ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]) + + form_data = MultiDict( + { + "percentage-of-shopping-lCIZsS": "25", + "percentage-of-shopping-elsewhere": "70", + } + ) + + form = generate_form( + schema, + question_schema, + answer_store, + list_store, + metadata=get_metadata(), + response_metadata={}, + form_data=form_data, + progress_store=ProgressStore(), + location=Location( + section_id="section", + block_id="dynamic-answer", + list_name=None, + list_item_id=None, + ), + ) + + form.validate() + assert form.question_errors["dynamic-answer-question"] == schema.error_messages[ + "TOTAL_SUM_NOT_EQUALS" + ] % {"total": "100"} diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index ab7bbc6e26..e1c13e6e85 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -4,7 +4,7 @@ import pytest -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion def get_metadata(version): diff --git a/tests/app/parser/test_metadata_parser.py b/tests/app/parser/test_metadata_parser.py index 74121bfc2c..10b44e1350 100644 --- a/tests/app/parser/test_metadata_parser.py +++ b/tests/app/parser/test_metadata_parser.py @@ -4,7 +4,7 @@ from freezegun import freeze_time from marshmallow import ValidationError -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.utilities.metadata_parser import validate_runner_claims from app.utilities.metadata_parser_v2 import ( validate_questionnaire_claims, diff --git a/tests/app/parser/test_supplementary_data_parser.py b/tests/app/parser/test_supplementary_data_parser.py new file mode 100644 index 0000000000..e466e5bcaa --- /dev/null +++ b/tests/app/parser/test_supplementary_data_parser.py @@ -0,0 +1,208 @@ +from copy import deepcopy + +import pytest +from marshmallow import ValidationError + +from app.services.supplementary_data import validate_supplementary_data +from app.utilities.supplementary_data_parser import validate_supplementary_data_v1 + +SUPPLEMENTARY_DATA_PAYLOAD = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "items": { + "local_units": [ + { + "identifier": "0001", + "lu_name": "TEST NAME. 1", + "lu_address": [ + "FIRST ADDRESS 1", + "FIRST ADDRESS 2", + "TOWN", + "COUNTY", + "POST CODE", + ], + }, + { + "identifier": "0002", + "lu_name": "TEST NAME 2", + "lu_address": [ + "SECOND ADDRESS 1", + "SECOND ADDRESS 1", + "TOWN", + "COUNTY", + "POSTCODE", + ], + }, + ] + }, + }, +} + + +def test_invalid_supplementary_data_payload_raises_error(): + with pytest.raises(ValidationError) as error: + validate_supplementary_data( + supplementary_data={}, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(error.value) == "Invalid supplementary data" + + +def test_validate_supplementary_data_payload(): + validated_payload = validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert validated_payload == SUPPLEMENTARY_DATA_PAYLOAD + + +def test_validate_supplementary_data_payload_incorrect_dataset_id(): + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="331507ca-1039-4624-a342-7cbc3630e217", + identifier="12346789012A", + survey_id="123", + ) + + assert ( + str(error.value) + == "{'_schema': ['Supplementary data did not return the specified Dataset ID']}" + ) + + +def test_validate_supplementary_data_payload_incorrect_survey_id(): + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="234", + ) + + assert ( + str(error.value) + == "{'_schema': ['Supplementary data did not return the specified Survey ID']}" + ) + + +def test_validate_supplementary_data_payload_incorrect_identifier(): + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="000000000001", + survey_id="123", + ) + + assert ( + str(error.value) + == "{'data': {'_schema': ['Supplementary data did not return the specified Identifier']}}" + ) + + +def test_supplementary_data_payload_with_no_items_is_validated(): + payload = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + }, + } + + validated_payload = validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert validated_payload == payload + + +def test_validate_supplementary_data_payload_missing_survey_id(): + payload = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + }, + } + + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(error.value) == "{'survey_id': ['Missing data for required field.']}" + + +def test_validate_supplementary_data_payload_with_unknown_field(): + payload = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "some_field": "value", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + }, + } + + validated_payload = validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert validated_payload == payload + + +def test_validate_supplementary_data_invalid_schema_version(): + payload = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "some_field": "value", + "data": { + "schema_version": "v2", + "identifier": "12346789012A", + }, + } + + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="001", + identifier="12346789012A", + survey_id="123", + ) + + assert str(error.value) == "{'data': {'schema_version': ['Must be one of: v1.']}}" + + +def test_validate_supplementary_data_payload_missing_identifier_in_items(): + payload = deepcopy(SUPPLEMENTARY_DATA_PAYLOAD) + payload["data"]["items"]["local_units"][0].pop("identifier") + + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(error.value) == "{'identifier': ['Missing data for required field.']}" diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 5e92c71cd0..14d6a7bc28 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -48,11 +48,6 @@ def answer_store(): return AnswerStore([{"answer_id": "first-name", "value": "Joe"}]) -@pytest.fixture -def location(): - return Location("test-section", "test-block", "test-list", "list_item_id") - - @pytest.fixture def response_metadata(): return {"started_at": "2021-01-01T09:00:00.220038+00:00"} @@ -988,6 +983,10 @@ def mock_schema(mocker): } ) ) + schema.is_answer_dynamic = mocker.MagicMock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = mocker.MagicMock( + return_value=False + ) return schema @@ -1369,6 +1368,40 @@ def placeholder_transform_question_dynamic_answers_json(): } +@pytest.fixture +@pytest.mark.usefixtures("app", "gb_locale") +def placeholder_transform_question_repeating_block(): + return { + "id": "repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "transport-repeating-block-1-question", + "type": "General", + "title": "title", + }, + "answers": [ + { + "id": "transport-cost", + "label": { + "placeholders": [ + { + "placeholder": "transport_name", + "value": { + "source": "answers", + "identifier": "transport-name", + }, + } + ], + "text": "What is your monthly expenditure travelling by {transport_name}?", + }, + "mandatory": True, + "type": "Currency", + "currency": "GBP", + } + ], + } + + @pytest.fixture @pytest.mark.usefixtures("app", "gb_locale") def placeholder_transform_question_dynamic_answers_pointer_json(): diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 981d788c30..ba9ee6ceb7 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -3,7 +3,7 @@ import pytest from freezegun import freeze_time -from mock import Mock +from mock import MagicMock, Mock from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import Answer @@ -21,7 +21,7 @@ def get_mock_schema(): - schema = Mock( + schema = MagicMock( QuestionnaireSchema( { "questionnaire_flow": { @@ -31,6 +31,8 @@ def get_mock_schema(): } ) ) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) return schema @@ -52,6 +54,8 @@ def get_rule_evaluator( schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=True) schema.get_default_answer = Mock(return_value=None) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) return RuleEvaluator( language=language, diff --git a/tests/app/questionnaire/test_dynamic_answer_options.py b/tests/app/questionnaire/test_dynamic_answer_options.py index dd3ff1d7cf..a1b0212559 100644 --- a/tests/app/questionnaire/test_dynamic_answer_options.py +++ b/tests/app/questionnaire/test_dynamic_answer_options.py @@ -117,6 +117,10 @@ def test_dynamic_answer_options_answer_source( mock_schema.get_answers_by_answer_id = mocker.Mock(return_value=answer_schema) mock_schema.get_default_answer = mocker.Mock(return_value=None) + mock_schema.is_answer_dynamic = mocker.Mock(return_value=False) + mock_schema.is_answer_in_list_collector_repeating_block = mocker.Mock( + return_value=False + ) if checkbox_answer: value_source_resolver.answer_store.add_or_update( diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index f155904304..d145cd702d 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -426,7 +426,9 @@ def test_remove_answer_and_block_if_routing_backwards(list_store): assert len(path_finder.answer_store) == 2 assert not path_finder.answer_store.get_answer("confirm-zero-employees-answer") assert ( - path_finder.progress_store.get_section_status(section_id="default-section") + path_finder.progress_store.get_section_or_repeating_blocks_progress_status( + section_id="default-section" + ) == CompletionStatus.IN_PROGRESS ) @@ -482,7 +484,8 @@ def test_remove_answer_and_block_if_routing_backwards(list_store): "primary-person", ["name-block", "age"], ), - ( # Answering 'Yes' to the skip age question and the skip-confirmation question, but then changing you answer for the skip age question to 'No' + ( + # Answering 'Yes' to the skip age question and the skip-confirmation question, but then changing you answer for the skip age question to 'No' # means because confirmation is not longer on the path in primary-person you will be asked your age, name and why you didn't confirm skipping "No", "Yes", diff --git a/tests/app/questionnaire/test_placeholder_renderer.py b/tests/app/questionnaire/test_placeholder_renderer.py index b5257ae6b9..df87dcc43f 100644 --- a/tests/app/questionnaire/test_placeholder_renderer.py +++ b/tests/app/questionnaire/test_placeholder_renderer.py @@ -356,13 +356,16 @@ def get_placeholder_render( metadata=None, response_metadata=None, ): + schema = MagicMock() + schema.is_answer_dynamic = MagicMock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = MagicMock(return_value=False) renderer = PlaceholderRenderer( language=language, answer_store=answer_store, list_store=list_store, metadata=metadata or {}, response_metadata=response_metadata or {}, - schema=MagicMock(), + schema=schema, progress_store=ProgressStore(), location=Location(section_id="default-section"), ) diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 43b78f273f..450456c69f 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -5,6 +5,7 @@ from werkzeug.datastructures import ImmutableDict from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema +from app.utilities.schema import load_schema_from_name def assert_all_dict_values_are_hashable(data): @@ -890,3 +891,33 @@ def test_progress_dependencies_for_when_rules( "section-8": {"section-7", "section-2"}, "section-9": {"section-2"}, } == schema.when_rules_section_dependencies_for_progress + + +def test_get_blocks_with_repeating_blocks(): + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + assert len(schema.get_blocks()) == 9 + + +def test_get_block_with_repeating_blocks(): + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + block1 = schema.get_block("companies-repeating-block-1") + block2 = schema.get_block("companies-repeating-block-2") + + assert block1["id"] == "companies-repeating-block-1" + assert block2["id"] == "companies-repeating-block-2" + + +def test_get_block_for_answer_id_returns_repeating_block_for_repeating_block_answer_id(): + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + + block1 = schema.get_block_for_answer_id("registration-number") + block2 = schema.get_block_for_answer_id("authorised-trader-eu-radio") + + assert block1["id"] == "companies-repeating-block-1" + assert block2["id"] == "companies-repeating-block-2" diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index 4b900f9e4e..e123d4b6f0 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -16,6 +16,7 @@ DependentSection, QuestionnaireStoreUpdater, ) +from app.utilities.schema import load_schema_from_name # pylint: disable=too-many-locals, too-many-lines @@ -298,7 +299,7 @@ def test_remove_all_answers_with_list_item_id( questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None ) - questionnaire_store_updater.remove_list_item_and_answers("abc", "abcdef") + questionnaire_store_updater.remove_list_item_data("abc", "abcdef") assert len(mock_empty_answer_store) == 1 assert mock_empty_answer_store.get_answer("test3", "uvwxyz") @@ -646,7 +647,8 @@ def test_update_answers_captures_answer_dependencies( @pytest.mark.parametrize( "answer_dependent_answer_id, updated_answer_value, expected_output", [ - ( # when the answer dependent has an answer_id, then the dependent answer should be removed from the answer store + ( + # when the answer dependent has an answer_id, then the dependent answer should be removed from the answer store "second-answer", "answer updated", AnswerStore( @@ -665,7 +667,8 @@ def test_update_answers_captures_answer_dependencies( ] ), ), - ( # When the answer dependent has an answer_id, but the answer dependency value is not changed, then the answer store should not change + ( + # When the answer dependent has an answer_id, but the answer dependency value is not changed, then the answer store should not change "second-answer", "original answer", AnswerStore( @@ -826,13 +829,15 @@ def test_update_repeating_answers_with_answer_dependents( @pytest.mark.parametrize( "section_status, updated_answer_value, is_path_complete, expected_status", [ - ( # When an answer is changed which causes the path of a dependent section to be incomplete, Then that sections is update to IN_PROGRESS + ( + # When an answer is changed which causes the path of a dependent section to be incomplete, Then that sections is update to IN_PROGRESS "COMPLETED", "answer updated", False, "IN_PROGRESS", ), - ( # When an answer is changed which causes the path of a dependent section to be complete, Then that sections is update to COMPLETED + ( + # When an answer is changed which causes the path of a dependent section to be complete, Then that sections is update to COMPLETED "IN_PROGRESS", "answer updated", True, @@ -904,14 +909,20 @@ def test_answer_id_section_dependents( questionnaire_store_updater.update_answers(form_data) questionnaire_store_updater.update_progress_for_dependent_sections() - assert progress_store.get_section_status(section_id="section-2") is expected_status + assert ( + progress_store.get_section_or_repeating_blocks_progress_status( + section_id="section-2" + ) + is expected_status + ) @pytest.mark.parametrize( "list_item_1_section_status, list_item_2_section_status, updated_answer_value, " "is_list_item_1_path_complete, is_list_item_2_path_complete, expected_list_item_1_status, expected_list_item_2_status", [ - ( # When an answer is changed which causes repeating dependent section to be incomplete, Then those repeating sections are updated to IN_PROGRESS + ( + # When an answer is changed which causes repeating dependent section to be incomplete, Then those repeating sections are updated to IN_PROGRESS "COMPLETED", "COMPLETED", "answer updated", @@ -920,7 +931,8 @@ def test_answer_id_section_dependents( "IN_PROGRESS", "IN_PROGRESS", ), - ( # When an answer is changed which causes repeating dependent section to be complete, Then those repeating sections are updated to COMPLETED + ( + # When an answer is changed which causes repeating dependent section to be complete, Then those repeating sections are updated to COMPLETED "IN_PROGRESS", "IN_PROGRESS", "answer updated", @@ -929,7 +941,8 @@ def test_answer_id_section_dependents( "COMPLETED", "COMPLETED", ), - ( # When an answer is changed which causes repeating section paths to change, Then those repeating sections statuses are updated correctly + ( + # When an answer is changed which causes repeating section paths to change, Then those repeating sections statuses are updated correctly "COMPLETED", "IN_PROGRESS", "answer updated", @@ -1035,13 +1048,13 @@ def test_answer_id_section_dependents_repeating( questionnaire_store_updater.update_progress_for_dependent_sections() assert ( - progress_store.get_section_status( + progress_store.get_section_or_repeating_blocks_progress_status( section_id="section-2", list_item_id="list-item-id-1" ) is expected_list_item_1_status ) assert ( - progress_store.get_section_status( + progress_store.get_section_or_repeating_blocks_progress_status( section_id="section-2", list_item_id="list-item-id-2" ) is expected_list_item_2_status @@ -1134,7 +1147,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update ) mocker.patch( - "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater._get_chronological_section_dependents", return_value=[ DependentSection( section_id="breakdown-section", list_item_id=None, is_complete=False @@ -1150,7 +1163,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) assert ( - progress_store.get_section_status( + progress_store.get_section_or_repeating_blocks_progress_status( section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) == CompletionStatus.IN_PROGRESS @@ -1184,7 +1197,9 @@ def test_dependent_sections_current_section_status_not_updated(mocker): dependent_section_key: {dependent_block_id} } - questionnaire_store_updater.update_section_status = mocker.Mock() + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( + mocker.Mock() + ) assert dependent_block_id in progress_store.get_completed_block_ids( section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) @@ -1198,11 +1213,17 @@ def test_dependent_sections_current_section_status_not_updated(mocker): section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] ) # Status for current section is handled separately by handle post. - assert questionnaire_store_updater.update_section_status.call_count == 0 + assert ( + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count + == 0 + ) def test_dependent_sections_not_started_skipped(mock_router, mocker): # Given + schema = load_schema_from_name( + "test_validation_sum_against_total_hub_with_dependent_section" + ) current_location = Location( section_id="company-summary-section", block_id="total-turnover-block" ) @@ -1219,6 +1240,7 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): current_location=current_location, progress_store=progress_store, router=mock_router, + schema=schema, ) dependent_section_key = ("breakdown-section", None) @@ -1229,7 +1251,9 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): } questionnaire_store_updater.remove_completed_location = mocker.Mock() - questionnaire_store_updater.update_section_status = mocker.Mock() + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( + mocker.Mock() + ) # When questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() @@ -1237,7 +1261,10 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): # Then assert questionnaire_store_updater.remove_completed_location.call_count == 0 - assert questionnaire_store_updater.update_section_status.call_count == 0 + assert ( + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count + == 0 + ) def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): @@ -1273,7 +1300,9 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): questionnaire_store_updater.dependent_block_id_by_section_key = { dependent_section_key: {dependent_block_id} } - questionnaire_store_updater.update_section_status = mocker.Mock() + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( + mocker.Mock() + ) assert dependent_block_id not in progress_store.get_completed_block_ids( section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] @@ -1284,7 +1313,10 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): questionnaire_store_updater.update_progress_for_dependent_sections() # Then - assert questionnaire_store_updater.update_section_status.call_count == 0 + assert ( + questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count + == 0 + ) @pytest.mark.parametrize( @@ -1294,7 +1326,9 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_status_updated( mocker, dependent_section_status, mock_router ): - # Given + schema = load_schema_from_name( + "test_validation_sum_against_total_hub_with_dependent_section" + ) current_location = Location( section_id="company-summary-section", block_id="total-turnover-block" ) @@ -1336,6 +1370,7 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta progress_store=progress_store, list_store=list_store, router=mock_router, + schema=schema, ) questionnaire_store_updater.dependent_block_id_by_section_key = { @@ -1349,7 +1384,7 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta ) mocker.patch( - "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater._get_chronological_section_dependents", return_value=[ DependentSection( section_id="breakdown-section", list_item_id=None, is_complete=None @@ -1367,7 +1402,9 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta section_id=section_id, list_item_id=list_item_id ) assert ( - progress_store.get_section_status(section_id, list_item_id) + progress_store.get_section_or_repeating_blocks_progress_status( + section_id, list_item_id + ) == CompletionStatus.IN_PROGRESS ) assert questionnaire_store_updater.dependent_sections == { @@ -1524,7 +1561,7 @@ def test_questionnaire_store_updater_dependency_capture( schema=mock_schema, ) mocker.patch( - "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater.get_chronological_section_dependents", + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdater._get_chronological_section_dependents", return_value=[ DependentSection( section_id="section-1", list_item_id=None, is_complete=None diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 57957aa7dd..148277310a 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1,9 +1,9 @@ from typing import Mapping, Optional, Union import pytest -from mock import Mock +from mock import MagicMock, Mock -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import Answer, AnswerDict from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException @@ -20,7 +20,7 @@ def get_list_items(num: int): def get_mock_schema(): - schema = Mock( + schema = MagicMock( QuestionnaireSchema( { "questionnaire_flow": { @@ -30,6 +30,8 @@ def get_mock_schema(): } ) ) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) return schema @@ -51,6 +53,8 @@ def get_value_source_resolver( if not schema: schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=bool(list_item_id)) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) if not use_default_answer: schema.get_default_answer = Mock(return_value=None) @@ -342,6 +346,86 @@ def test_answer_source_default_answer(use_default_answer): ) +@pytest.mark.parametrize( + "answer_values,escape_answer_values", + (([], False), ([10, 5], False), ([100, 200, 300], False), ([HTML_CONTENT], True)), +) +def test_answer_source_dynamic_answer( + mocker, + placeholder_transform_question_dynamic_answers_json, + answer_values, + escape_answer_values, +): + """ + Tests that a dynamic answer id as a value source resolves to the list of answers for that list and question + """ + schema = mocker.MagicMock() + schema.is_answer_dynamic = Mock(return_value=True) + schema.get_block_for_answer_id = Mock( + return_value={"question": placeholder_transform_question_dynamic_answers_json} + ) + list_item_ids = get_list_items(len(answer_values)) + value_source_resolver = get_value_source_resolver( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="percentage-of-shopping", + value=value, + list_item_id=list_item_id, + ) + for list_item_id, value in zip(list_item_ids, answer_values) + ] + ), + list_store=ListStore([{"name": "supermarkets", "items": list_item_ids}]), + schema=schema, + escape_answer_values=escape_answer_values, + ) + expected_result = [ESCAPED_CONTENT] if escape_answer_values else answer_values + assert ( + value_source_resolver.resolve( + {"source": "answers", "identifier": "percentage-of-shopping"} + ) + == expected_result + ) + + +@pytest.mark.parametrize("answer_values", ([], [10, 5], [100, 200, 300])) +def test_answer_source_repeating_block_answers( + mocker, placeholder_transform_question_repeating_block, answer_values +): + """ + Tests that an answer id from a repeating block resolves to the list of answers for that list and repeating block question + """ + schema = mocker.MagicMock() + schema.list_names_by_list_repeating_block_id = {"repeating-block-1": "transport"} + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=True) + schema.get_block_for_answer_id = Mock( + return_value=placeholder_transform_question_repeating_block + ) + list_item_ids = get_list_items(len(answer_values)) + value_source_resolver = get_value_source_resolver( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="transport-cost", + value=value, + list_item_id=list_item_id, + ) + for list_item_id, value in zip(list_item_ids, answer_values) + ] + ), + list_store=ListStore([{"name": "transport", "items": list_item_ids}]), + schema=schema, + ) + assert ( + value_source_resolver.resolve( + {"source": "answers", "identifier": "transport-cost"} + ) + == answer_values + ) + + @pytest.mark.parametrize( "metadata_identifier, expected_result", [("region_code", "GB-ENG"), ("language_code", None)], @@ -553,6 +637,8 @@ def test_new_calculated_summary_value_source(mocker, list_item_id): }, }, ) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) location = Location( section_id="test-section", block_id="test-block", list_item_id=list_item_id @@ -608,6 +694,8 @@ def test_new_calculated_summary_nested_value_source(mocker, list_item_id): }, }, ) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) location = Location( section_id="test-section", block_id="test-block", list_item_id=list_item_id diff --git a/tests/app/services/__init__.py b/tests/app/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/app/services/conftest.py b/tests/app/services/conftest.py new file mode 100644 index 0000000000..e6357d380f --- /dev/null +++ b/tests/app/services/conftest.py @@ -0,0 +1,67 @@ +import pytest + + +@pytest.fixture +def decrypted_mock_supplementary_data_payload(): + return { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "items": { + "local_units": [ + { + "identifier": "0001", + "lu_name": "TEST NAME. 1", + "lu_address": [ + "FIRST ADDRESS 1", + "FIRST ADDRESS 2", + "TOWN", + "COUNTY", + "POST CODE", + ], + }, + { + "identifier": "0002", + "lu_name": "TEST NAME 2", + "lu_address": [ + "SECOND ADDRESS 1", + "SECOND ADDRESS 1", + "TOWN", + "COUNTY", + "POSTCODE", + ], + }, + ] + }, + }, + } + + +@pytest.fixture +def encrypted_mock_supplementary_data_payload(): + return { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + # pylint: disable-next=line-too-long + "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJkZjg4ZmRhZDI2MTJhZTFlODA1NzExMjBlNmM2MzcxZjU1ODk2Njk2In0.HXHbiTFa2XCuaKfSX4HfONLAVfZxEZo6_072A-ilMwhQKVhmQzqhDOX9dRID1yeoJnYxcdVGRDPwD5QvVHY2qsIOGkO3Brh59GHvSVASSCR21od7DBvlv0LoCDGAVcPt5bwwMbmziGQNkyfNnZG1EwAFk09lWfXwapJDsKbu1uhbW-F8HOOFZT7vR3paUmeTS0ekITwod-eZTD-B7KwUnDhJSm12cl9ob8MHirCaTE3jB64iQe_GvgdSUs1n5HZnX1f7rDQEpWm0OeuxPbEDrFmU-9wBV6LAjszypxPhQ0pv76TMu-VhjNBgf5Xm0cMO8ycmubdBdyWZiVUcmG0or9Mw0QvDYA9th27RChZPs5Le0w9oTnp5p5qzQexpPdzcS9Niqwabwx-NyUvTYkkFWc9poGNtrane4Ei4AFlV1-nQ4A8wMSIuOOSteYVombw-FEo9FhIhnuU8qyHoy0EaW5D1PMcdsphSb2ybTsiEJfdwwpxWDiuMDUpW6c2It_uSEpLCFHj51pQ8_Ez44gDyRE41NpSVvOB6h-nOggICQKggBDimtAuOyunZ5jhQWlKAeX4vAwM4iUEJ82c9lLTb8EDmQDOj6os9PCmyrZku5FHRXXDDvyuQAEqB-ARRnWT47LZOmaeTn4RI92qcejOqTNSG80mCUMY1PPu1fqxOiG8.5ukQQc5IT8fHgMRJ.jDXFWkIV9PIIrAC6EnVu5joGNdlmM52xgEqXCocUvZflvT8xMNocGGDUD1S0_FbQlpSP8FJcby4-B88yzxLgwil7mBqGtE7kWjALPGgnV9DsjEs-OblFFUIY9dVaKMTwaXhSxpjEt4j9NUOo-uyKOQIg-ZT745_zbkFZhlScuHz0u1YYaZGN04Md-eDI38erkTrS3pe2aovjbrwc9s_FMbrHEKlnuAUwxGPUxLTJXpTavHdmhhWriaI6a9ymng6mcefKqpNfWXc-3MIY2k10JCoo2KlVyYRN0up6T4OW56mAoXf7dUNSC8PtcZ5J3rpJ4XUnLZNVSL6Kz9BAmXY5SpZlU9zIBxHsirG9fS8gV6bI_MeMwbMawMCylcArlINVGVwDnUIvNXIJplVgm1OTHL0TiZzEFvnqkro3mzvVL-ElFeYO0uegqN8cDGkcv_lpAHH33j-IPdG-8CYerTCSVrT9.vWWMrdJFFLTVBSiv3KhbiQ", + } + + +@pytest.fixture +def mock_supplementary_data_payload_invalid_kid_in_data(): + return { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + # pylint: disable-next=line-too-long + "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJkZjg4ZmRhZDI2MTJhZTFlODA1NzExMjBlNmM2MzcxZjU1ODk2Njk3In0=.lssJXsMUE3dhWtQRUt7DTaZJvx4DpNdLW98cu8g4NijYX9TFpJiOFyzPxUlpFZb-fMa4zW9q6qZofQeQTbl_Ae3QAwGhuWF7v9NMdWM1aH377byyJJyJpdqlU4t-P03evRWZqAG2HtsNE2Zn1ORXn80Dc9IRkzutgrziLI8OBIZeO6-XEgbVCapsQApWkyux7QRdFH95wfda75nVvGqTbBOYvQiMTKd8KzpH2Vl200IOqEpmrcjUCE-yqdTupzcr88hwNI2ZYdv-pTNowJw1FPODZ7V_sE4Ac-JYv3yBTDcXdz3I5-rX8i2HXqz-g3VhveZiAl9q0AgklPkaO_oNWJzjrCb7DZGL4DjiGYuOcw8OSdOpKLXwkExMlado-wigxy1IWoCzFu2E5tWpmLc0WWcjKuBgD7-4tcn059F7GcwhX2uMRESCmc39pblvseM2UnmmQnwr8GvD7gqWdFwtBsECyXQ5UXAxWLJor_MtU8lAFZxiorRcrXZJwAivroPO9iEB-1Mvt2zZFWI_vMgpJCAIpETscotDKMVCG0UMfkKckJqLnmQpvF4oYTr77w1COBX5bi-AV8UrLJ7sVVktSXOBc_KCGRpoImA5cE67hW7mFUdJi1EHA39qt0tTqZD7izpu8sSLxsiuCkfsqrd4uAedcDdQm4QGxXOPD4pxois.wfWsetB3M0x9qfw5.43Wns86lGlbHj63b0ZxE2bxBQVus6FIqelb9LfSbvopLn5oR8FM4vDEnDp_rIyvjmV9YAZJ6HAHaYaWoNyIO0EorgamrB4R3-LqInANoe9c8xLZ9wl_QpE9aWnxsmFGZUWLO3q2fVTPnwBtA_LxK8FD0vjdLL9eHGYEmPVCGVX0BJX04TVW9aoemsx9Yn3ZtfvmQHuROiB-GcA5wOSb-GvhzfplY09GQr7g7221MiYCHYimmEJyxLV5clWPXu6izzVLDyG9l2ewCifiuBLD0O1U_fPlahHTmidwHKJEAEn39biNw5E_dr8WyZ3xBvJa9dP50m0xeyN4COR-xlYcEbuDcKoqN6BnY0bMNDxQYlBO--QcPLQ6h48uTJszwzsmNIwHoi0xy5dQah7c9Nt2lpMuNt1Wix-O8JWYCqaiCKxjwt9G8kabMbzhp1n3LetWweoyV7qJTbiB13Byv6SZwMO9M.8j8wtvwBAHzqRhv5Ii9jjQ", + } + + +@pytest.fixture +def mock_supplementary_data_payload_missing_data(): + return { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + } diff --git a/tests/app/services/test_request_supplementary_data.py b/tests/app/services/test_request_supplementary_data.py new file mode 100644 index 0000000000..fe8f9d9d03 --- /dev/null +++ b/tests/app/services/test_request_supplementary_data.py @@ -0,0 +1,222 @@ +import pytest +import responses +from flask import Flask, current_app +from marshmallow import ValidationError +from requests import RequestException +from sdc.crypto.key_store import KeyStore + +from app.services.supplementary_data import ( + SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES, + InvalidSupplementaryData, + MissingSupplementaryDataKey, + SupplementaryDataRequestFailed, + decrypt_supplementary_data, + get_supplementary_data_v1, +) +from tests.app.utilities.test_schema import get_mocked_make_request + +TEST_SDS_URL = "http://test.domain" +EXPECTED_SDS_DECRYPTION_VALIDATION_ERROR = "Supplementary data has no data to decrypt" + + +@responses.activate +def test_get_supplementary_data_v1_200( + app: Flask, + encrypted_mock_supplementary_data_payload, + decrypted_mock_supplementary_data_payload, +): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + + responses.add( + responses.GET, + f"{TEST_SDS_URL}/v1/unit_data", + json=encrypted_mock_supplementary_data_payload, + status=200, + ) + loaded_supplementary_data = get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert loaded_supplementary_data == decrypted_mock_supplementary_data_payload + + +@pytest.mark.parametrize( + "status_code", + [401, 403, 404, 501, 511], +) +@responses.activate +def test_get_supplementary_data_v1_non_200( + app: Flask, status_code, encrypted_mock_supplementary_data_payload +): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + + responses.add( + responses.GET, + f"{TEST_SDS_URL}/v1/unit_data", + json=encrypted_mock_supplementary_data_payload, + status=status_code, + ) + + with pytest.raises(SupplementaryDataRequestFailed) as exc: + get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(exc.value) == "Supplementary Data request failed" + + +@responses.activate +def test_get_supplementary_data_v1_request_failed(app: Flask): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + + responses.add(responses.GET, TEST_SDS_URL, body=RequestException()) + with pytest.raises(SupplementaryDataRequestFailed) as exc: + get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(exc.value) == "Supplementary Data request failed" + + +def test_get_supplementary_data_v1_retries_timeout_error( + app: Flask, + mocker, + mocked_make_request_with_timeout, + decrypted_mock_supplementary_data_payload, +): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + mocker.patch( + "app.services.supplementary_data.decrypt_supplementary_data", + return_value=decrypted_mock_supplementary_data_payload, + ) + + try: + supplementary_data = get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + except SupplementaryDataRequestFailed: + return pytest.fail("Supplementary data request unexpectedly failed") + + assert supplementary_data == decrypted_mock_supplementary_data_payload + + expected_call = ( + SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES + 1 + ) # Max retries + the initial request + assert mocked_make_request_with_timeout.call_count == expected_call + + +@pytest.mark.usefixtures("mocked_response_content") +def test_get_supplementary_data_v1_retries_transient_error( + app: Flask, mocker, decrypted_mock_supplementary_data_payload +): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + mocked_make_request = get_mocked_make_request( + mocker, status_codes=[500, 500, 200] + ) + + mocker.patch( + "app.services.supplementary_data.decrypt_supplementary_data", + return_value=decrypted_mock_supplementary_data_payload, + ) + + try: + supplementary_data = get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + except SupplementaryDataRequestFailed: + return pytest.fail("Supplementary data request unexpectedly failed") + + assert supplementary_data == decrypted_mock_supplementary_data_payload + + expected_call = ( + SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES + 1 + ) # Max retries + the initial request + + assert mocked_make_request.call_count == expected_call + + +def test_get_supplementary_data_v1_max_retries(app: Flask, mocker): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + + mocked_make_request = get_mocked_make_request( + mocker, status_codes=[500, 500, 500, 500] + ) + + with pytest.raises(SupplementaryDataRequestFailed) as exc: + get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert str(exc.value) == "Supplementary Data request failed" + assert mocked_make_request.call_count == 3 + + +def test_decrypt_supplementary_data_v1_decrypts_when_encrypted_payload_is_valid( + app: Flask, + encrypted_mock_supplementary_data_payload, + decrypted_mock_supplementary_data_payload, +): + with app.app_context(): + result = decrypt_supplementary_data( + key_store=app.eq["key_store"], + supplementary_data=encrypted_mock_supplementary_data_payload, + ) + assert result == decrypted_mock_supplementary_data_payload + + +def test_decrypt_supplementary_data_v1_raises_validation_error_when_encrypted_payload_missing_data( + app: Flask, mock_supplementary_data_payload_missing_data +): + with app.app_context(): + with pytest.raises(ValidationError) as e: + decrypt_supplementary_data( + key_store=app.eq["key_store"], + supplementary_data=mock_supplementary_data_payload_missing_data, + ) + assert EXPECTED_SDS_DECRYPTION_VALIDATION_ERROR in e.value.messages + + +def test_decrypt_supplementary_data_v1_raises_invalid_token_error_when_encrypted_data_kid_invalid( + app: Flask, mock_supplementary_data_payload_invalid_kid_in_data +): + with app.app_context(): + with pytest.raises(InvalidSupplementaryData): + decrypt_supplementary_data( + key_store=app.eq["key_store"], + supplementary_data=mock_supplementary_data_payload_invalid_kid_in_data, + ) + + +def test_get_supplementary_data_v1_raises_missing_supplementary_data_key_error_when_key_is_missing( + app: Flask, mocker +): + with app.app_context(): + mocker.patch.dict( + "app.services.supplementary_data.current_app.eq", + {"key_store": KeyStore({"keys": {}})}, + ) + + with pytest.raises(MissingSupplementaryDataKey): + get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index c92d9da656..7b9302885a 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -7,11 +7,12 @@ from mock import MagicMock from requests import Response -from app.authentication.auth_payload_version import AuthPayloadVersion -from app.data_models import QuestionnaireStore +from app.authentication.auth_payload_versions import AuthPayloadVersion +from app.data_models import ListStore, QuestionnaireStore from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.submitter import RabbitMQSubmitter @@ -86,6 +87,7 @@ def get_questionnaire_store(version): store = QuestionnaireStore(storage) store.answer_store = AnswerStore() + store.supplementary_data_store = SupplementaryDataStore() store.answer_store.add_or_update(user_answer) store.metadata = METADATA_V2 if version is AuthPayloadVersion.V2 else METADATA_V1 store.response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} @@ -161,3 +163,72 @@ def gcs_blob_with_retry(mocker): ) return blob + + +@pytest.fixture +def repeating_blocks_answer_store(): + return AnswerStore( + [ + {"answer_id": "responsible-party-answer", "value": "Yes"}, + {"answer_id": "any-companies-or-branches-answer", "value": "Yes"}, + { + "answer_id": "company-or-branch-name", + "value": "CompanyA", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": "123", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "CompanyB", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": "456", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "any-other-trading-details", + "value": "N/A", + }, + ] + ) + + +@pytest.fixture +def repeating_blocks_list_store(): + return ListStore([{"items": ["PlwgoG", "UHPLbX"], "name": "companies"}]) diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 6c14a3a4a2..6787ed23f0 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -2,7 +2,7 @@ import pytest -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema @@ -99,6 +99,7 @@ def test_answer_with_zero(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert data_payload["003"] == "0" @@ -138,6 +139,7 @@ def test_answer_with_float(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Check the converter correctly @@ -180,6 +182,7 @@ def test_answer_with_string(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Check the converter correctly @@ -222,6 +225,7 @@ def test_answer_without_qcode(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert not data_payload @@ -289,6 +293,7 @@ def test_converter_checkboxes_with_q_codes(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -368,6 +373,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value( questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -445,6 +451,7 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(v questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -517,6 +524,7 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -571,6 +579,7 @@ def test_converter_q_codes_for_empty_strings(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -639,6 +648,7 @@ def test_radio_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -685,6 +695,7 @@ def test_number_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -730,6 +741,7 @@ def test_percentage_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -775,6 +787,7 @@ def test_textarea_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -820,6 +833,7 @@ def test_currency_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -876,6 +890,7 @@ def test_dropdown_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -928,6 +943,7 @@ def test_date_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -974,6 +990,7 @@ def test_unit_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index aadf9d8862..3f1c9bda81 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -3,14 +3,16 @@ import pytest -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models.answer import Answer from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.submitter.converter_v2 import get_payload_data from app.utilities.json import json_dumps, json_loads +from app.utilities.make_immutable import make_immutable from app.utilities.schema import load_schema_from_name from tests.app.submitter.conftest import get_questionnaire_store from tests.app.submitter.schema import make_schema @@ -90,6 +92,7 @@ def test_convert_answers_v2_to_payload_0_0_3(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -147,6 +150,7 @@ def test_convert_payload_0_0_3_multiple_answers(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -199,6 +203,7 @@ def test_radio_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -241,6 +246,7 @@ def test_number_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -283,6 +289,7 @@ def test_percentage_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -327,6 +334,7 @@ def test_textarea_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -369,6 +377,7 @@ def test_currency_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -421,6 +430,7 @@ def test_dropdown_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -469,6 +479,7 @@ def test_date_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -517,6 +528,7 @@ def test_month_year_date_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -560,6 +572,7 @@ def test_unit_answer(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -616,6 +629,7 @@ def test_primary_person_list_item_conversion(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["answers"])) @@ -671,6 +685,7 @@ def test_list_item_conversion(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) del answer_objects[-1] @@ -721,6 +736,7 @@ def test_list_item_conversion_empty_list(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Answers not on the routing path @@ -767,6 +783,7 @@ def test_default_answers_not_present_when_not_answered(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -825,6 +842,7 @@ def test_list_structure_in_payload_is_as_expected(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -880,6 +898,7 @@ def test_primary_person_not_in_payload_when_not_answered(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -957,6 +976,7 @@ def test_relationships_in_payload(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1034,6 +1054,7 @@ def test_no_relationships_in_payload(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1128,6 +1149,7 @@ def test_unrelated_block_answers_in_payload(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1239,6 +1261,7 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1340,6 +1363,7 @@ def test_relationship_answers_not_on_path_in_payload(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1405,6 +1429,7 @@ def test_answers_codes_only_present_for_answered_questions(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -1443,6 +1468,7 @@ def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(ve questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -1479,6 +1505,7 @@ def test_no_answers_codes_in_payload_when_no_questions_answered(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -1524,6 +1551,7 @@ def test_payload_dynamic_answers(version): questionnaire_store.metadata, questionnaire_store.response_metadata, questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, ) # Then @@ -1535,3 +1563,179 @@ def test_payload_dynamic_answers(version): Answer(answer_id="percentage-of-shopping", value=21, list_item_id="vhECeh") in data_payload["answers"] ) + + +@pytest.mark.parametrize( + "version", + ( + None, + AuthPayloadVersion.V2, + ), +) +def test_repeating_block_answers_present( + version, repeating_blocks_answer_store, repeating_blocks_list_store +): + questionnaire_store = get_questionnaire_store(version) + + full_routing_path = [ + RoutingPath( + [ + "responsible-party", + "any-companies-or-branches", + "any-other-companies-or-branches", + "any-other-trading-details", + ], + section_id="section-companies", + ) + ] + + questionnaire_store.answer_store = repeating_blocks_answer_store + questionnaire_store.list_store = repeating_blocks_list_store + + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, + ) + + expected_answer_codes = [ + {"answer_id": "responsible-party-answer", "code": "1"}, + {"answer_id": "any-companies-or-branches-answer", "code": "2"}, + {"answer_id": "company-or-branch-name", "code": "2a"}, + {"answer_id": "registration-number", "code": "2b"}, + {"answer_id": "registration-date", "code": "2c"}, + {"answer_id": "authorised-trader-uk-radio", "code": "2d"}, + {"answer_id": "authorised-trader-eu-radio", "code": "2e"}, + ] + + expected_answers = [ + {"answer_id": "responsible-party-answer", "value": "Yes"}, + {"answer_id": "any-companies-or-branches-answer", "value": "Yes"}, + { + "answer_id": "company-or-branch-name", + "value": "CompanyA", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": "123", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "CompanyB", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": "456", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + ] + + answers_dict = json_loads(json_dumps(data_payload["answers"])) + answer_codes_dict = json_loads(json_dumps(data_payload["answer_codes"])) + + assert answers_dict == expected_answers + assert answer_codes_dict == expected_answer_codes + + +def test_payload_supplementary_data(): + questionnaire_store = get_questionnaire_store(AuthPayloadVersion.V2) + + full_routing_path = [ + RoutingPath( + ["dynamic-answer"], + section_id="section", + ) + ] + + supplementary_data = { + "schema_version": "v1", + "identifier": "12346789012A", + "note": {"title": "supermarket test survey", "description": "test data"}, + "items": { + "supermarkets": [ + {"identifier": "123", "name": "Tesco"}, + {"identifier": "456", "name": "Aldi"}, + ] + }, + } + supermarkets_list_map = {"123": "tUJzGV", "456": "vhECeh"} + + list_item_ids = list(supermarkets_list_map.values()) + questionnaire_store.supplementary_data_store = SupplementaryDataStore( + supplementary_data=supplementary_data, + list_mappings={"supermarkets": supermarkets_list_map}, + ) + questionnaire_store.list_store = ListStore( + [{"items": list_item_ids, "name": "supermarkets"}] + ) + questionnaire_store.answer_store = AnswerStore( + [ + Answer("percentage-of-shopping", 12, list_item_ids[0]).to_dict(), + Answer("percentage-of-shopping", 21, list_item_ids[1]).to_dict(), + ] + ) + + schema = load_schema_from_name("test_supplementary_data") + + data_payload = get_payload_data( + questionnaire_store.answer_store, + questionnaire_store.list_store, + schema, + full_routing_path, + questionnaire_store.metadata, + questionnaire_store.response_metadata, + questionnaire_store.progress_store, + questionnaire_store.supplementary_data_store, + ) + + assert "supplementary_data" in data_payload + assert "lists" in data_payload + assert data_payload["supplementary_data"] == make_immutable(supplementary_data) + assert len(data_payload["lists"]) == 1 + assert data_payload["lists"][0] == { + "items": list_item_ids, + "name": "supermarkets", + "supplementary_data_mapping": make_immutable(supermarkets_list_map), + } diff --git a/tests/app/submitter/test_converter.py b/tests/app/submitter/test_converter.py index 6dde299be1..d9634a4593 100644 --- a/tests/app/submitter/test_converter.py +++ b/tests/app/submitter/test_converter.py @@ -2,7 +2,7 @@ import pytest -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.submitter.converter import convert_answers from app.submitter.converter_v2 import ( diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 18473b779a..8e785ef481 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -323,14 +323,14 @@ def test_map_list_collector_config_no_actions(): {"item_title": "Joe Bloggs", "list_item_id": "two"}, ] - output = map_list_collector_config(list_items, "icon") + output = map_list_collector_config(list_items, False) expected = [ { "rowItems": [ { "actions": [], - "iconType": "icon", + "iconType": None, "id": "one", "rowTitleAttributes": { "data-list-item-id": "one", @@ -344,7 +344,7 @@ def test_map_list_collector_config_no_actions(): "rowItems": [ { "actions": [], - "iconType": "icon", + "iconType": None, "id": "two", "rowTitleAttributes": { "data-list-item-id": "two", @@ -381,7 +381,7 @@ def test_map_list_collector_config(): output = map_list_collector_config( list_items, - "icon", + False, "edit_link_text", "edit_link_aria_label", "remove_link_text", @@ -400,7 +400,7 @@ def test_map_list_collector_config(): "url": "/primary/change", } ], - "iconType": "icon", + "iconType": None, "id": "primary", "rowTitleAttributes": { "data-list-item-id": "primary", @@ -427,7 +427,7 @@ def test_map_list_collector_config(): "url": "/nonprimary/remove", }, ], - "iconType": "icon", + "iconType": None, "id": "nonprimary", "rowTitleAttributes": { "data-list-item-id": "nonprimary", @@ -457,7 +457,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): output = map_list_collector_config( list_items, - "icon", + False, "edit_link_text", "edit_link_aria_label", "remove_link_text", @@ -521,7 +521,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "url": "/nonprimary/remove", }, ], - "iconType": "icon", + "iconType": None, "id": "VHoiow", "rowTitle": "Name of UK company or branch", "rowTitleAttributes": { @@ -783,7 +783,6 @@ def test_calculated_summary_config(): "id": "calculated-summary-question", "answers": [{"id": "calculated-summary-answer", "value": "£2.00"}], }, - icon="", ) assert to_dict(expected) == to_dict(result) @@ -809,7 +808,7 @@ def test_summary_item_config_with_list_collector(): "url": "remove_link_url", }, ], - "iconType": "", + "iconType": None, "id": "vmmPmD", "rowTitle": "Company A", "rowTitleAttributes": { @@ -939,7 +938,6 @@ def test_summary_item_config_with_list_collector(): edit_link_text="Change", edit_link_aria_label="Change your answer for:", calculated_question={}, - icon="", ) assert to_dict(expected) == to_dict(result) diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index c93689431a..2df35289c2 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -5,7 +5,6 @@ import responses from mock import Mock, patch from requests import RequestException -from requests.adapters import ConnectTimeoutError, ReadTimeoutError from urllib3.connectionpool import HTTPConnectionPool, HTTPResponse from app.questionnaire import QuestionnaireSchema @@ -250,13 +249,6 @@ def test_load_schema_from_metadata_with_schema_url_and_override_language_code(): assert loaded_schema.language_code == language_code -@pytest.fixture(name="mocked_response_content") -def mocked_response_content_fixture(mocker): - decodable_content = Mock() - decodable_content.decode.return_value = b"{}" - mocker.patch("requests.models.Response.content", decodable_content) - - def get_mocked_make_request(mocker, status_codes): mocked_responses = [] for status_code in status_codes: @@ -279,29 +271,6 @@ def get_mocked_make_request(mocker, status_codes): return patched_make_request -@pytest.fixture(name="mocked_make_request_with_timeout") -def mocked_make_request_with_timeout_fixture( - mocker, mocked_response_content # pylint: disable=unused-argument -): - connect_timeout_error = ConnectTimeoutError("connect timed out") - read_timeout_error = ReadTimeoutError( - pool=None, message="read timed out", url="test-url" - ) - - response_not_timed_out = HTTPResponse(status=200, headers={}, msg=HTTPMessage()) - response_not_timed_out.drain_conn = Mock(return_value=None) - - return mocker.patch.object( - HTTPConnectionPool, - "_make_request", - side_effect=[ - connect_timeout_error, - read_timeout_error, - response_not_timed_out, - ], - ) - - def test_load_schema_from_url_retries_timeout_error(mocked_make_request_with_timeout): load_schema_from_url.cache_clear() diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index d10dd27c78..6df95e9334 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -185,6 +185,64 @@ def people_answer_store(): ) +@pytest.fixture +def repeating_blocks_answer_store(): + return AnswerStore( + [ + { + "answer_id": "company-or-branch-name", + "value": "CompanyA", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-number", + "value": "123", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "Yes", + "list_item_id": "PlwgoG", + }, + { + "answer_id": "company-or-branch-name", + "value": "CompanyB", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-number", + "value": "456", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "registration-date", + "value": "2023-01-01", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-uk-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + { + "answer_id": "authorised-trader-eu-radio", + "value": "No", + "list_item_id": "UHPLbX", + }, + ] + ) + + @pytest.fixture def companies_answer_store(): return AnswerStore( @@ -312,6 +370,11 @@ def people_list_store(): return ListStore([{"items": ["PlwgoG", "UHPLbX"], "name": "people"}]) +@pytest.fixture +def repeating_blocks_list_store(): + return ListStore([{"items": ["PlwgoG", "UHPLbX"], "name": "companies"}]) + + @pytest.fixture def response_metadata(): return {"started_at": "2021-01-01T09:00:00.220038+00:00"} @@ -334,6 +397,18 @@ def test_grand_calculated_summary_schema(): return load_schema_from_name("test_grand_calculated_summary") +@pytest.fixture +def test_calculated_summary_repeating_and_static_answers_schema(): + return load_schema_from_name( + "test_new_calculated_summary_repeating_and_static_answers" + ) + + +@pytest.fixture +def test_calculated_summary_repeating_blocks(): + return load_schema_from_name("test_new_calculated_summary_repeating_blocks") + + @pytest.fixture def test_calculated_summary_answers(): answers = [ diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index b7896a8f95..ab36c64bc9 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -5,14 +5,17 @@ @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "return_to, return_to_block_id", + "return_to, return_to_block_id, is_in_repeating_section", [ - ("section-summary", None), - (None, None), - ("calculated-summary", "total"), + ("section-summary", None, False), + (None, None, False), + ("calculated-summary", "total", False), + ("section-summary", None, True), + (None, None, True), + ("calculated-summary", "total", True), ], ) -def test_create_answer(return_to, return_to_block_id): +def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): answer = Answer( answer_schema={"id": "answer-id", "label": "Answer Label", "type": "date"}, answer_value="An answer", @@ -21,6 +24,7 @@ def test_create_answer(return_to, return_to_block_id): list_item_id="answer-item-id", return_to=return_to, return_to_block_id=return_to_block_id, + is_in_repeating_section=is_in_repeating_section, ) assert answer.id == "answer-id" @@ -28,10 +32,16 @@ def test_create_answer(return_to, return_to_block_id): assert answer.value == "An answer" assert answer.type == "date" + return_to_answer_id = answer.id + if not is_in_repeating_section: + return_to_answer_id += "-answer-item-id" + if return_to and return_to_block_id: - query_string = f"?return_to={return_to}&return_to_answer_id={answer.id}&return_to_block_id={return_to_block_id}" + query_string = f"?return_to={return_to}&return_to_answer_id={return_to_answer_id}&return_to_block_id={return_to_block_id}" elif return_to: - query_string = f"?return_to={return_to}&return_to_answer_id={answer.id}" + query_string = ( + f"?return_to={return_to}&return_to_answer_id={return_to_answer_id}" + ) else: query_string = "" @@ -52,6 +62,7 @@ def test_date_answer_type(): list_item_id="answer-item-id", return_to="section-summary", return_to_block_id=None, + is_in_repeating_section=False, ) # Then diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 8e9e4f4dab..1cab04f550 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -1,5 +1,6 @@ import pytest +from app.data_models import AnswerStore, ListStore from app.questionnaire import Location from app.questionnaire.routing_path import RoutingPath from app.views.contexts.calculated_summary_context import CalculatedSummaryContext @@ -232,3 +233,198 @@ def test_build_view_context_for_return_to_calculated_summary( assert f"return_to=calculated-summary,{return_to}" in answer_change_link assert f"return_to_answer_id={return_to_answer_id}" in answer_change_link assert f"return_to_block_id={block_id},{return_to_block_id}" in answer_change_link + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize( + "block_id,expected_answer_ids,expected_block_ids", + ( + ( + "calculated-summary-spending", + [ + "cost-of-shopping-CHKtQS", + "cost-of-shopping-laFWcs", + "cost-of-other-CHKtQS", + "cost-of-other-laFWcs", + "extra-static-answer", + ], + ["dynamic-answer", "extra-spending-block"], + ), + ( + "calculated-summary-visits", + [ + "days-a-week-CHKtQS", + "days-a-week-laFWcs", + ], + ["dynamic-answer"], + ), + ), +) +def test_build_view_context_for_calculated_summary_with_dynamic_answers( + test_calculated_summary_repeating_and_static_answers_schema, + answer_store, + progress_store, + mocker, + block_id, + expected_answer_ids, + expected_block_ids, +): + """ + Tests that when different dynamic answers for the same list are used in different calculated summaries + that the calculated summary context filters the answers to keep correctly. + """ + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.MagicMock(return_value="en_GB"), + ) + + block_ids = [ + "any-supermarket", + "list-collector", + "dynamic-answer", + "extra-spending-block", + ] + + calculated_summary_context = CalculatedSummaryContext( + language="en", + schema=test_calculated_summary_repeating_and_static_answers_schema, + answer_store=answer_store, + list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "supermarkets"}]), + progress_store=progress_store, + metadata=None, + response_metadata={}, + routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), + current_location=Location(section_id="section-1", block_id=block_id), + ) + + context = calculated_summary_context.build_view_context() + assert "summary" in context + assert_summary_context(context) + context_summary = context["summary"] + calculation_blocks = context_summary["sections"][0]["groups"][0]["blocks"] + + block_ids = [block["id"] for block in calculation_blocks] + assert block_ids == expected_block_ids + + answers_to_keep = calculation_blocks[0]["question"]["answers"] + answer_ids = [answer["id"] for answer in answers_to_keep] + assert answer_ids == expected_answer_ids + + # blocks with dynamic answers show each answer suffixed with the list item id, so the anchor needs to also include it + assert all( + answer["link"].endswith( + f"return_to=calculated-summary&return_to_answer_id={answer['id']}&return_to_block_id={block_id}#{answer['id']}" + ) + for answer in answers_to_keep + ) + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize( + "block_id,expected_answer_ids,expected_answer_labels,expected_block_ids,anchors", + ( + ( + "calculated-summary-spending", + [ + "answer-car", + "transport-cost-CHKtQS", + "transport-additional-cost-CHKtQS", + "transport-cost-laFWcs", + "transport-additional-cost-laFWcs", + ], + [ + "Monthly expenditure travelling by car", + "Monthly season ticket expenditure for travel by Train", + "Additional monthly expenditure for travel by Train", + "Monthly season ticket expenditure for travel by Bus", + "Additional monthly expenditure for travel by Bus", + ], + [ + "block-car", + "transport-repeating-block-1-CHKtQS", + "transport-repeating-block-1-laFWcs", + ], + [ + "answer-car", + "transport-cost", + "transport-additional-cost", + "transport-cost", + "transport-additional-cost", + ], + ), + ( + "calculated-summary-count", + ["transport-count-CHKtQS", "transport-count-laFWcs"], + ["Monthly journeys by Train", "Monthly journeys by Bus"], + [ + "transport-repeating-block-2-CHKtQS", + "transport-repeating-block-2-laFWcs", + ], + ["transport-count", "transport-count"], + ), + ), +) +def test_build_view_context_for_calculated_summary_with_answers_from_repeating_blocks( + test_calculated_summary_repeating_blocks, + progress_store, + mocker, + block_id, + expected_answer_ids, + expected_answer_labels, + expected_block_ids, + anchors, +): + """ + Tests that when different dynamic answers for the same list are used in different calculated summaries + that the calculated summary context filters the answers to keep correctly. + """ + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.MagicMock(return_value="en_GB"), + ) + + block_ids = ["block-car", "list-collector"] + answer_store = AnswerStore( + [ + {"answer_id": "transport-name", "value": "Train", "list_item_id": "CHKtQS"}, + {"answer_id": "transport-name", "value": "Bus", "list_item_id": "laFWcs"}, + ] + ) + + calculated_summary_context = CalculatedSummaryContext( + language="en", + schema=test_calculated_summary_repeating_blocks, + answer_store=answer_store, + list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "transport"}]), + progress_store=progress_store, + metadata=None, + response_metadata={}, + routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), + current_location=Location(section_id="section-1", block_id=block_id), + ) + + context = calculated_summary_context.build_view_context() + assert "summary" in context + assert_summary_context(context) + context_summary = context["summary"] + calculation_blocks = context_summary["sections"][0]["groups"][0]["blocks"] + + block_ids = [block["id"] for block in calculation_blocks] + assert block_ids == expected_block_ids + + questions = [block["question"] for block in calculation_blocks] + answers = [answer for question in questions for answer in question["answers"]] + answer_ids = [answer["id"] for answer in answers] + assert answer_ids == expected_answer_ids + + answer_labels = [answer["label"] for answer in answers] + assert answer_labels == expected_answer_labels + + # on summary pages, repeating block answer ids are suffixed with list item ids, + # but the anchor on the change links needs to not have them, because the repeating block itself doesn't do that + assert all( + answer["link"].endswith( + f"return_to=calculated-summary&return_to_answer_id={answer['id']}&return_to_block_id={block_id}#{anchor}" + ) + for anchor, answer in zip(anchors, answers) + ) diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index 3c53d30390..c29225c904 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -59,7 +59,7 @@ def test_build_view_context_for_grand_calculated_summary( answer_store=test_grand_calculated_summary_answers, list_store=list_store, progress_store=ProgressStore( - in_progress_sections=[ + in_progress_sections_and_repeating_blocks=[ { "section_id": "section-1", "status": CompletionStatus.COMPLETED, diff --git a/tests/app/views/contexts/test_list_context.py b/tests/app/views/contexts/test_list_context.py index 6e2119d7bc..24b3c883b9 100644 --- a/tests/app/views/contexts/test_list_context.py +++ b/tests/app/views/contexts/test_list_context.py @@ -1,6 +1,7 @@ import pytest -from app.data_models.progress_store import ProgressStore +from app.data_models import CompletionStatus, ProgressStore +from app.data_models.progress import ProgressDictType from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name from app.views.contexts import ListContext @@ -8,44 +9,61 @@ @pytest.mark.usefixtures("app") def test_build_list_collector_context( - list_collector_block, schema, people_answer_store, people_list_store + list_collector_block, + schema, + people_answer_store, + people_list_store, + progress_store, ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, people_answer_store, people_list_store, - None, + progress_store, metadata={}, response_metadata={}, ) - list_context = list_context(list_collector_block["summary"], for_list="people") + list_context = list_context( + list_collector_block["summary"], + for_list="people", + section_id="section-id", + has_repeating_blocks=False, + ) assert all(keys in list_context["list"] for keys in ["list_items", "editable"]) @pytest.mark.usefixtures("app") def test_build_list_summary_context_no_summary_block( - schema, people_answer_store, people_list_store + schema, people_answer_store, people_list_store, progress_store ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, people_answer_store, people_list_store, - None, + progress_store, metadata={}, response_metadata={}, ) - list_context = list_context(None, for_list="people") + list_context = list_context( + summary_definition=None, + for_list="people", + section_id="section-id", + has_repeating_blocks=False, + ) assert list_context == {"list": {"editable": False, "list_items": []}} @pytest.mark.usefixtures("app") def test_build_list_summary_context( - list_collector_block, people_answer_store, people_list_store + list_collector_block, + people_answer_store, + people_list_store, + progress_store, ): schema = load_schema_from_name("test_list_collector_primary_person") expected = [ @@ -55,6 +73,8 @@ def test_build_list_summary_context( "remove_link": "/questionnaire/people/PlwgoG/remove-person/", "primary_person": False, "list_item_id": "PlwgoG", + "is_complete": False, + "repeating_blocks": False, }, { "item_title": "Barry Pheloung", @@ -62,6 +82,8 @@ def test_build_list_summary_context( "remove_link": "/questionnaire/people/UHPLbX/remove-person/", "primary_person": False, "list_item_id": "UHPLbX", + "is_complete": False, + "repeating_blocks": False, }, ] @@ -70,14 +92,16 @@ def test_build_list_summary_context( schema, people_answer_store, people_list_store, - None, + progress_store, metadata={}, response_metadata={}, ) list_context = list_context( - list_collector_block["summary"], - "people", + summary_definition=list_collector_block["summary"], + for_list="people", + section_id="section-id", + has_repeating_blocks=False, edit_block_id=list_collector_block["edit_block"]["id"], remove_block_id=list_collector_block["remove_block"]["id"], ) @@ -87,14 +111,17 @@ def test_build_list_summary_context( @pytest.mark.usefixtures("app") def test_assert_primary_person_string_appended( - list_collector_block, people_answer_store, people_list_store + list_collector_block, + people_answer_store, + people_list_store, + progress_store, ): schema = load_schema_from_name("test_list_collector_primary_person") people_list_store["people"].primary_person = "PlwgoG" list_context = ListContext( language=DEFAULT_LANGUAGE_CODE, - progress_store=ProgressStore(), + progress_store=progress_store, list_store=people_list_store, schema=schema, answer_store=people_answer_store, @@ -102,7 +129,10 @@ def test_assert_primary_person_string_appended( response_metadata={}, ) list_context = list_context( - list_collector_block["summary"], list_collector_block["for_list"] + summary_definition=list_collector_block["summary"], + for_list=list_collector_block["for_list"], + section_id="section-id", + has_repeating_blocks=False, ) assert list_context["list"]["list_items"][0]["primary_person"] is True @@ -112,13 +142,16 @@ def test_assert_primary_person_string_appended( @pytest.mark.usefixtures("app") def test_for_list_item_ids( - list_collector_block, people_answer_store, people_list_store + list_collector_block, + people_answer_store, + people_list_store, + progress_store, ): schema = load_schema_from_name("test_list_collector_primary_person") list_context = ListContext( language=DEFAULT_LANGUAGE_CODE, - progress_store=ProgressStore(), + progress_store=progress_store, list_store=people_list_store, schema=schema, answer_store=people_answer_store, @@ -126,17 +159,205 @@ def test_for_list_item_ids( response_metadata={}, ) list_context = list_context( - list_collector_block["summary"], - list_collector_block["for_list"], + summary_definition=list_collector_block["summary"], + for_list=list_collector_block["for_list"], for_list_item_ids=["UHPLbX"], + section_id="section-id", + has_repeating_blocks=False, ) expected = [ { "item_title": "Barry Pheloung", "primary_person": False, + "is_complete": False, + "repeating_blocks": False, "list_item_id": "UHPLbX", } ] assert expected == list_context["list"]["list_items"] + + +@pytest.mark.usefixtures("app") +def test_list_context_items_complete_without_repeating_blocks( + people_answer_store, people_list_store, list_collector_block +): + schema = load_schema_from_name("test_list_collector_primary_person") + expected = [ + { + "item_title": "Toni Morrison", + "edit_link": "/questionnaire/people/PlwgoG/edit-person/", + "remove_link": "/questionnaire/people/PlwgoG/remove-person/", + "primary_person": False, + "list_item_id": "PlwgoG", + "is_complete": True, + "repeating_blocks": False, + }, + { + "item_title": "Barry Pheloung", + "edit_link": "/questionnaire/people/UHPLbX/edit-person/", + "remove_link": "/questionnaire/people/UHPLbX/remove-person/", + "primary_person": False, + "list_item_id": "UHPLbX", + "is_complete": True, + "repeating_blocks": False, + }, + ] + + progress_store = ProgressStore( + [ + ProgressDictType( + section_id="section-id", + list_item_id="PlwgoG", + status=CompletionStatus.COMPLETED, + block_ids=[], + ), + ProgressDictType( + section_id="section-id", + list_item_id="UHPLbX", + status=CompletionStatus.COMPLETED, + block_ids=[], + ), + ] + ) + + list_context = ListContext( + DEFAULT_LANGUAGE_CODE, + schema, + people_answer_store, + people_list_store, + progress_store, + metadata={}, + response_metadata={}, + ) + + list_context = list_context( + summary_definition=list_collector_block["summary"], + for_list="people", + section_id="section-id", + has_repeating_blocks=False, + edit_block_id=list_collector_block["edit_block"]["id"], + remove_block_id=list_collector_block["remove_block"]["id"], + ) + + assert expected == list_context["list"]["list_items"] + + +@pytest.mark.usefixtures("app") +def test_list_context_items_incomplete_with_repeating_blocks( + repeating_blocks_answer_store, repeating_blocks_list_store, progress_store +): + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + list_collector_block = schema.get_block("any-other-companies-or-branches") + expected = [ + { + "item_title": "CompanyA", + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/", + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/", + "primary_person": False, + "list_item_id": "PlwgoG", + "is_complete": False, + "repeating_blocks": True, + }, + { + "item_title": "CompanyB", + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/", + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/", + "primary_person": False, + "list_item_id": "UHPLbX", + "is_complete": False, + "repeating_blocks": True, + }, + ] + + list_context = ListContext( + DEFAULT_LANGUAGE_CODE, + schema, + repeating_blocks_answer_store, + repeating_blocks_list_store, + progress_store, + metadata={}, + response_metadata={}, + ) + + list_context = list_context( + summary_definition=list_collector_block["summary"], + for_list=list_collector_block["for_list"], + section_id="section-companies", + has_repeating_blocks=True, + edit_block_id=list_collector_block["edit_block"]["id"], + remove_block_id=list_collector_block["remove_block"]["id"], + ) + + assert expected == list_context["list"]["list_items"] + + +@pytest.mark.usefixtures("app") +def test_list_context_items_complete_with_repeating_blocks( + repeating_blocks_answer_store, repeating_blocks_list_store +): + schema = load_schema_from_name( + "test_list_collector_repeating_blocks_section_summary" + ) + list_collector_block = schema.get_block("any-other-companies-or-branches") + expected = [ + { + "item_title": "CompanyA", + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/", + "remove_link": "/questionnaire/companies/PlwgoG/remove-company/", + "primary_person": False, + "list_item_id": "PlwgoG", + "is_complete": True, + "repeating_blocks": True, + }, + { + "item_title": "CompanyB", + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/", + "remove_link": "/questionnaire/companies/UHPLbX/remove-company/", + "primary_person": False, + "list_item_id": "UHPLbX", + "is_complete": True, + "repeating_blocks": True, + }, + ] + + progress_store = ProgressStore( + [ + ProgressDictType( + section_id="section-companies", + list_item_id="PlwgoG", + status=CompletionStatus.COMPLETED, + block_ids=[], + ), + ProgressDictType( + section_id="section-companies", + list_item_id="UHPLbX", + status=CompletionStatus.COMPLETED, + block_ids=[], + ), + ] + ) + + list_context = ListContext( + DEFAULT_LANGUAGE_CODE, + schema, + repeating_blocks_answer_store, + repeating_blocks_list_store, + progress_store, + metadata={}, + response_metadata={}, + ) + + list_context = list_context( + summary_definition=list_collector_block["summary"], + for_list=list_collector_block["for_list"], + section_id="section-companies", + has_repeating_blocks=True, + edit_block_id=list_collector_block["edit_block"]["id"], + remove_block_id=list_collector_block["remove_block"]["id"], + ) + + assert expected == list_context["list"]["list_items"] diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 863cee7942..8c64cfbc7d 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -124,7 +124,7 @@ def test_custom_section( @pytest.mark.usefixtures("app") -def test_context_for_section_list_summary(people_answer_store): +def test_context_for_section_list_summary(people_answer_store, progress_store): schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( @@ -137,7 +137,7 @@ def test_context_for_section_list_summary(people_answer_store): {"items": ["gTrlio"], "name": "visitors"}, ] ), - progress_store=ProgressStore(), + progress_store=progress_store, metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), response_metadata={}, current_location=Location(section_id="section"), @@ -167,18 +167,22 @@ def test_context_for_section_list_summary(people_answer_store): "editable": True, "list_items": [ { - "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", + "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary&return_to_answer_id=PlwgoG", "item_title": "Toni Morrison", "list_item_id": "PlwgoG", "primary_person": False, "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, }, { - "edit_link": "/questionnaire/people/UHPLbX/edit-person/?return_to=section-summary", + "edit_link": "/questionnaire/people/UHPLbX/edit-person/?return_to=section-summary&return_to_answer_id=UHPLbX", "item_title": "Barry Pheloung", "list_item_id": "UHPLbX", "primary_person": False, "remove_link": "/questionnaire/people/UHPLbX/remove-person/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, }, ], }, @@ -197,11 +201,13 @@ def test_context_for_section_list_summary(people_answer_store): "editable": True, "list_items": [ { - "edit_link": "/questionnaire/visitors/gTrlio/edit-visitor-person/?return_to=section-summary", + "edit_link": "/questionnaire/visitors/gTrlio/edit-visitor-person/?return_to=section-summary&return_to_answer_id=gTrlio", "item_title": "", "list_item_id": "gTrlio", "primary_person": False, "remove_link": "/questionnaire/visitors/gTrlio/remove-visitor/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, } ], }, @@ -221,386 +227,54 @@ def test_context_for_section_list_summary(people_answer_store): # pylint: disable=line-too-long -@pytest.mark.usefixtures("app") -def test_context_for_section_summary_with_list_summary(companies_answer_store): - schema = load_schema_from_name("test_list_collector_section_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, - answer_store=companies_answer_store, - list_store=ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, - ] - ), - progress_store=ProgressStore(), - metadata=None, - response_metadata={}, - current_location=Location(section_id="section-companies"), - routing_path=RoutingPath( - [ - "any-other-companies-or-branches", - ], - section_id="section-companies", +@pytest.mark.parametrize( + "test_schema, answer_store_fixture, item_label, answer_1_label, answer_2_label", + [ + ( + "test_list_collector_section_summary", + "companies_answer_store", + "Name of UK company or branch", + "Registration number", + "Is this UK company or branch an authorised insurer?", ), - ) - context = summary_context() - expected = { - "summary": { - "answers_are_editable": True, - "collapsible": False, - "sections": [ - { - "groups": [ - { - "blocks": [], - "id": "group-companies-0", - "links": {}, - "placeholder_text": None, - "title": None, - }, - { - "blocks": [ - { - "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", - "add_link_text": "Add another UK company or branch", - "empty_list_text": "No UK company or branch added", - "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK company or branch", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", - "item_title": "company a", - "list_item_id": "PlwgoG", - "primary_person": False, - "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", - }, - { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", - "item_title": "company b", - "list_item_id": "UHPLbX", - "primary_person": False, - "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", - }, - ], - }, - "list_name": "companies", - "related_answers": { - "PlwgoG": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 123, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - "UHPLbX": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 456, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "No", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - }, - "title": "Companies or UK branches", - "type": "List", - } - ], - "id": "group-companies-1", - "links": { - "add_link": Link( - text="Add another UK company or branch", - url="/questionnaire/companies/add-company/?return_to=section-summary", - target="_self", - attributes={"data-qa": "add-item-link"}, - ) - }, - "placeholder_text": "No UK company or branch added", - "title": None, - }, - { - "blocks": [], - "id": "group-companies-2", - "links": {}, - "placeholder_text": None, - "title": None, - }, - ], - "title": "General insurance business", - } - ], - "page_title": "General insurance business", - "summary_type": "SectionSummary", - "title": "General insurance business", - } - } - - assert context == expected - - -@pytest.mark.usefixtures("app") -def test_context_for_section_summary_with_list_summary_and_first_variant( - companies_variants_answer_store_first_variant, -): - schema = load_schema_from_name("test_list_collector_variants_section_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, - answer_store=companies_variants_answer_store_first_variant, - list_store=ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, - ] + ( + "test_list_collector_variants_section_summary", + "companies_variants_answer_store_first_variant", + "Name of UK or non-UK company or branch", + "UK Registration number", + "Is this UK company or branch an authorised insurer?", ), - progress_store=ProgressStore(), - metadata=None, - response_metadata={}, - current_location=Location(section_id="section-companies"), - routing_path=RoutingPath( - [ - "any-other-companies-or-branches", - ], - section_id="section-companies", + ( + "test_list_collector_variants_section_summary", + "companies_variants_answer_store_second_variant", + "Name of UK or non-UK company or branch", + "Non-UK Registration number", + "Is this non-UK company or branch an authorised insurer?", ), - ) - context = summary_context() - expected = { - "summary": { - "answers_are_editable": True, - "collapsible": False, - "sections": [ - { - "groups": [ - { - "blocks": [], - "id": "group-companies-0", - "links": {}, - "placeholder_text": None, - "title": None, - }, - { - "blocks": [ - { - "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", - "add_link_text": "Add another UK company or branch", - "empty_list_text": "No UK company or branch added", - "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK or non-UK company or branch", - "list": { - "editable": True, - "list_items": [ - { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", - "item_title": "company a", - "list_item_id": "PlwgoG", - "primary_person": False, - "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", - }, - { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", - "item_title": "company b", - "list_item_id": "UHPLbX", - "primary_person": False, - "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", - }, - ], - }, - "list_name": "companies", - "related_answers": { - "PlwgoG": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "UK Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 123, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "Yes", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - "UHPLbX": [ - { - "id": "edit-company", - "number": None, - "question": { - "answers": [ - { - "currency": None, - "id": "registration-number", - "label": "UK Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", - "type": "number", - "unit": None, - "unit_length": None, - "value": 456, - }, - { - "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", - "type": "radio", - "unit": None, - "unit_length": None, - "value": { - "detail_answer_value": None, - "label": "No", - }, - }, - ], - "id": "edit-question-companies", - "number": None, - "title": "What is the name of the company?", - "type": "General", - }, - "title": None, - } - ], - }, - "title": "Companies or UK branches", - "type": "List", - } - ], - "id": "group-companies-1", - "links": { - "add_link": Link( - text="Add another UK company or branch", - url="/questionnaire/companies/add-company/?return_to=section-summary", - target="_self", - attributes={"data-qa": "add-item-link"}, - ) - }, - "placeholder_text": "No UK company or branch added", - "title": None, - }, - { - "blocks": [], - "id": "group-companies-2", - "links": {}, - "placeholder_text": None, - "title": None, - }, - ], - "title": "General insurance business", - } - ], - "page_title": "General insurance business", - "summary_type": "SectionSummary", - "title": "General insurance business", - } - } - - assert context == expected - - + ], +) @pytest.mark.usefixtures("app") -def test_context_for_section_summary_with_list_summary_and_second_variant( - companies_variants_answer_store_second_variant, +def test_context_for_section_summary_with_list_summary_and_first_variant( + test_schema, + answer_store_fixture, + item_label, + answer_1_label, + answer_2_label, + progress_store, + request, ): - schema = load_schema_from_name("test_list_collector_variants_section_summary") - + schema = load_schema_from_name(test_schema) + answer_store = request.getfixturevalue(answer_store_fixture) summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, schema=schema, - answer_store=companies_variants_answer_store_second_variant, + answer_store=answer_store, list_store=ListStore( [ {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, ] ), - progress_store=ProgressStore(), + progress_store=progress_store, metadata=None, response_metadata={}, current_location=Location(section_id="section-companies"), @@ -633,23 +307,27 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( "add_link_text": "Add another UK company or branch", "empty_list_text": "No UK company or branch added", "item_anchor": "#company-or-branch-name", - "item_label": "Name of UK or non-UK company or branch", + "item_label": item_label, "list": { "editable": True, "list_items": [ { - "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary", + "edit_link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=PlwgoG", "item_title": "company a", "list_item_id": "PlwgoG", "primary_person": False, "remove_link": "/questionnaire/companies/PlwgoG/remove-company/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, }, { - "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary", + "edit_link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=UHPLbX", "item_title": "company b", "list_item_id": "UHPLbX", "primary_person": False, "remove_link": "/questionnaire/companies/UHPLbX/remove-company/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, }, ], }, @@ -657,15 +335,15 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( "related_answers": { "PlwgoG": [ { - "id": "edit-company", + "id": "edit-company-PlwgoG", "number": None, "question": { "answers": [ { "currency": None, - "id": "registration-number", - "label": "Non-UK Registration number", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "id": "registration-number-PlwgoG", + "label": answer_1_label, + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=registration-number-PlwgoG#registration-number", "type": "number", "unit": None, "unit_length": None, @@ -673,9 +351,9 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( }, { "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this non-UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "id": "authorised-insurer-radio-PlwgoG", + "label": answer_2_label, + "link": "/questionnaire/companies/PlwgoG/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio-PlwgoG#authorised-insurer-radio", "type": "radio", "unit": None, "unit_length": None, @@ -685,7 +363,7 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( }, }, ], - "id": "edit-question-companies", + "id": "edit-question-companies-PlwgoG", "number": None, "title": "What is the name of the company?", "type": "General", @@ -695,15 +373,15 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( ], "UHPLbX": [ { - "id": "edit-company", + "id": "edit-company-UHPLbX", "number": None, "question": { "answers": [ { "currency": None, - "id": "registration-number", - "label": "Non-UK Registration number", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number#registration-number", + "id": "registration-number-UHPLbX", + "label": answer_1_label, + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=registration-number-UHPLbX#registration-number", "type": "number", "unit": None, "unit_length": None, @@ -711,9 +389,9 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( }, { "currency": None, - "id": "authorised-insurer-radio", - "label": "Is this non-UK company or branch an authorised insurer?", - "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio", + "id": "authorised-insurer-radio-UHPLbX", + "label": answer_2_label, + "link": "/questionnaire/companies/UHPLbX/edit-company/?return_to=section-summary&return_to_answer_id=authorised-insurer-radio-UHPLbX#authorised-insurer-radio", "type": "radio", "unit": None, "unit_length": None, @@ -723,7 +401,7 @@ def test_context_for_section_summary_with_list_summary_and_second_variant( }, }, ], - "id": "edit-question-companies", + "id": "edit-question-companies-UHPLbX", "number": None, "title": "What is the name of the company?", "type": "General", @@ -812,7 +490,7 @@ def test_context_for_driving_question_summary_empty_list(): @pytest.mark.usefixtures("app") -def test_context_for_driving_question_summary(): +def test_context_for_driving_question_summary(progress_store): schema = load_schema_from_name("test_list_collector_driving_question") summary_context = SectionSummaryContext( @@ -830,7 +508,7 @@ def test_context_for_driving_question_summary(): ] ), ListStore([{"items": ["PlwgoG"], "name": "people"}]), - ProgressStore(), + progress_store, metadata=None, response_metadata={}, current_location=Location(section_id="section"), @@ -856,11 +534,13 @@ def test_context_for_driving_question_summary(): "editable": True, "list_items": [ { - "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary", + "edit_link": "/questionnaire/people/PlwgoG/edit-person/?return_to=section-summary&return_to_answer_id=PlwgoG", "item_title": "Toni Morrison", "list_item_id": "PlwgoG", "primary_person": False, "remove_link": "/questionnaire/people/PlwgoG/remove-person/?return_to=section-summary", + "is_complete": False, + "repeating_blocks": False, } ], }, diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index 6fde601054..8ac724a725 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -5,7 +5,7 @@ from freezegun import freeze_time from mock import Mock -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_data import SessionData diff --git a/tests/app/views/handlers/test_feedback_upload.py b/tests/app/views/handlers/test_feedback_upload.py index 3f4415d265..89904b4119 100644 --- a/tests/app/views/handlers/test_feedback_upload.py +++ b/tests/app/views/handlers/test_feedback_upload.py @@ -2,7 +2,7 @@ from freezegun import freeze_time -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.views.handlers.feedback import ( FeedbackMetadata, diff --git a/tests/app/views/handlers/test_submission_handler.py b/tests/app/views/handlers/test_submission_handler.py index c4466ce2a3..6ce4431ffa 100644 --- a/tests/app/views/handlers/test_submission_handler.py +++ b/tests/app/views/handlers/test_submission_handler.py @@ -3,7 +3,7 @@ import pytest from freezegun import freeze_time -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models.session_store import SessionStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.utilities.schema import load_schema_from_name diff --git a/tests/functional/base_pages/calculated-summary.page.js b/tests/functional/base_pages/calculated-summary.page.js index e147911349..7ee638b426 100644 --- a/tests/functional/base_pages/calculated-summary.page.js +++ b/tests/functional/base_pages/calculated-summary.page.js @@ -12,6 +12,10 @@ class CalculatedSummaryPage extends BasePage { calculatedSummaryAnswer() { return "[data-qa=calculated-summary-answer]"; } + + summaryItems() { + return "div.ons-summary__items"; + } } export default CalculatedSummaryPage; diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index d55a82741e..988ad19007 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -293,7 +293,6 @@ """ ) - NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_LABEL_GETTER = Template( r""" ${list_name}ListLabel(listItemInstance) { return `dt[data-qa="list-item-` + listItemInstance + `-label"]`; } @@ -831,6 +830,15 @@ def process_block( relative_require, page_filename=f'{block["id"]}-{list_operation}.page.js', ) + for repeating_block in block.get("repeating_blocks", []): + process_block( + repeating_block, + dir_out, + schema_data, + spec_file, + relative_require, + page_filename=f'{repeating_block["id"]}-repeating-block.page.js', + ) if block["type"] == "PrimaryPersonListCollector": process_block( diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index bde74f09d4..9261798965 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -1,9 +1,32 @@ -const checkPeopleInList = async (peopleExpected, listLabel) => { +export const checkItemsInList = async (itemsExpected, listLabel) => { await $(listLabel(1)).waitForDisplayed(); - for (let i = 1; i <= peopleExpected.length; i++) { - await expect(await $(listLabel(i)).getText()).to.equal(peopleExpected[i - 1]); + for (let i = 1; i <= itemsExpected.length; i++) { + await expect(await $(listLabel(i)).getText()).to.equal(itemsExpected[i - 1]); } }; -export default checkPeopleInList; +export const checkListItemComplete = async (listItemLabel) => { + await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).to.be.true; +}; +export const checkListItemIncomplete = async (listItemLabel) => { + await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).to.be.false; +}; + +export const assertSummaryValues = async (values) => { + // check each summary value provided is present and that the number of them matches what is on the page + // needs to include both dynamic and static answers on any summary with both + const summaryValues = 'dd[class="ons-summary__values"]'; + await values.map(async (value, index) => { + await expect(await $$(summaryValues)[index].getText()).to.equal(value); + }); + await expect(await $$(summaryValues).length).to.equal(values.length); +}; + +export const repeatingAnswerChangeLink = (answerIndex) => { + return $$('dd[class="ons-summary__actions"]')[answerIndex].$("a"); +}; + +export const listItemIds = () => { + return $$("[data-list-item-id]").map((listItem) => listItem.getAttribute("data-list-item-id")); +}; diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js index 06cac9a8ac..e60bdc1929 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-remove.page"; @@ -37,7 +37,7 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; - checkPeopleInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); + checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js index 892f882d9b..0cef38a688 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-remove.page"; @@ -37,7 +37,7 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; - checkPeopleInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); + checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { diff --git a/tests/functional/spec/features/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js similarity index 100% rename from tests/functional/spec/features/calculated_summary.spec.js rename to tests/functional/spec/features/calculated_summary/calculated_summary.spec.js diff --git a/tests/functional/spec/features/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js similarity index 88% rename from tests/functional/spec/features/calculated_summary_test_case.js rename to tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index d4ee91e1af..4953a4aa71 100644 --- a/tests/functional/spec/features/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -1,32 +1,32 @@ -import CurrencyTotalPlaybackPage from "../../generated_pages/calculated_summary/currency-total-playback.page"; -import UnitTotalPlaybackPage from "../../generated_pages/calculated_summary/unit-total-playback.page"; -import NumberTotalPlaybackPage from "../../generated_pages/calculated_summary/number-total-playback.page"; -import ThirdNumberBlockPage from "../../generated_pages/calculated_summary/third-number-block.page"; -import FourthNumberBlockPage from "../../generated_pages/calculated_summary/fourth-number-block.page"; -import FourthAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/fourth-and-a-half-number-block.page"; -import SixthNumberBlockPage from "../../generated_pages/calculated_summary/sixth-number-block.page"; -import FifthNumberBlockPage from "../../generated_pages/calculated_summary/fifth-number-block.page"; -import SkipFourthBlockPage from "../../generated_pages/calculated_summary/skip-fourth-block.page"; -import PercentageTotalPlaybackPage from "../../generated_pages/calculated_summary/percentage-total-playback.page"; -import CalculatedSummaryTotalConfirmation from "../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page"; -import SetMinMaxBlockPage from "../../generated_pages/calculated_summary/set-min-max-block.page"; -import SubmitPage from "../../generated_pages/calculated_summary/submit.page"; -import ThirdAndAHalfNumberBlockPage from "../../generated_pages/calculated_summary/third-and-a-half-number-block.page"; -import ThankYouPage from "../../base_pages/thank-you.page"; -import FirstNumberBlockPage from "../../generated_pages/calculated_summary/first-number-block.page"; -import SecondNumberBlockPage from "../../generated_pages/calculated_summary/second-number-block.page"; -import HubPage from "../../base_pages/hub.page"; -import SkipFirstNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/skip-first-block.page"; -import FirstNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/first-number-block.page"; -import FirstAndAHalfNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/first-and-a-half-number-block.page"; -import SecondNumberBlockPageSectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/second-number-block.page"; -import CalculatedSummarySectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-1.page"; -import CalculatedSummarySectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-2.page"; -import ThirdNumberBlockPageSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/third-number-block.page"; -import SectionSummarySectionOne from "../../generated_pages/calculated_summary_cross_section_dependencies/questions-section-summary.page"; -import SectionSummarySectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; -import DependencyQuestionSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; -import MinMaxSectionTwo from "../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; +import CurrencyTotalPlaybackPage from "../../../generated_pages/calculated_summary/currency-total-playback.page"; +import UnitTotalPlaybackPage from "../../../generated_pages/calculated_summary/unit-total-playback.page"; +import NumberTotalPlaybackPage from "../../../generated_pages/calculated_summary/number-total-playback.page"; +import ThirdNumberBlockPage from "../../../generated_pages/calculated_summary/third-number-block.page"; +import FourthNumberBlockPage from "../../../generated_pages/calculated_summary/fourth-number-block.page"; +import FourthAndAHalfNumberBlockPage from "../../../generated_pages/calculated_summary/fourth-and-a-half-number-block.page"; +import SixthNumberBlockPage from "../../../generated_pages/calculated_summary/sixth-number-block.page"; +import FifthNumberBlockPage from "../../../generated_pages/calculated_summary/fifth-number-block.page"; +import SkipFourthBlockPage from "../../../generated_pages/calculated_summary/skip-fourth-block.page"; +import PercentageTotalPlaybackPage from "../../../generated_pages/calculated_summary/percentage-total-playback.page"; +import CalculatedSummaryTotalConfirmation from "../../../generated_pages/calculated_summary/calculated-summary-total-confirmation.page"; +import SetMinMaxBlockPage from "../../../generated_pages/calculated_summary/set-min-max-block.page"; +import SubmitPage from "../../../generated_pages/calculated_summary/submit.page"; +import ThirdAndAHalfNumberBlockPage from "../../../generated_pages/calculated_summary/third-and-a-half-number-block.page"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import FirstNumberBlockPage from "../../../generated_pages/calculated_summary/first-number-block.page"; +import SecondNumberBlockPage from "../../../generated_pages/calculated_summary/second-number-block.page"; +import HubPage from "../../../base_pages/hub.page"; +import SkipFirstNumberBlockPageSectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/skip-first-block.page"; +import FirstNumberBlockPageSectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/first-number-block.page"; +import FirstAndAHalfNumberBlockPageSectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/first-and-a-half-number-block.page"; +import SecondNumberBlockPageSectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/second-number-block.page"; +import CalculatedSummarySectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-1.page"; +import CalculatedSummarySectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/currency-total-playback-2.page"; +import ThirdNumberBlockPageSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/third-number-block.page"; +import SectionSummarySectionOne from "../../../generated_pages/calculated_summary_cross_section_dependencies/questions-section-summary.page"; +import SectionSummarySectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; +import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; +import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; class TestCase { testCase(schema) { diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js new file mode 100644 index 0000000000..cc133e9d7c --- /dev/null +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -0,0 +1,214 @@ +import HubPage from "../../../base_pages/hub.page"; +import AnySupermarketPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/any-supermarket.page.js"; +import ListCollectorPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/list-collector.page.js"; +import ExtraSpendingBlockPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/extra-spending-block.page.js"; +import CalculatedSummarySpendingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-spending.page.js"; +import CalculatedSummaryVisitsPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-visits.page.js"; +import ListCollectorAddPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/list-collector-add.page"; +import DynamicAnswerPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/dynamic-answer.page"; +import SummaryPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/section-1-summary.page"; +import ExtraSpendingMethodBlockPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/extra-spending-method-block.page"; +import ListCollectorRemovePage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/list-collector-remove.page"; +import SupermarketTransportPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport.page"; +import SupermarketTransportCostPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport-cost.page"; +import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; +import { assertSummaryValues } from "../../../helpers"; + +describe("Calculated summary with repeating answers", function () { + // These tests are flaky therefore we add a retry. The cause is unknown. + // :TODO: Revert this in future when we have a fix for this. + this.retries(5); + + const summaryActions = 'dd[class="ons-summary__actions"]'; + const dynamicAnswerChangeLink = (answerIndex) => $$(summaryActions)[answerIndex].$("a"); + + before("Completing the list collector and dynamic answer", async () => { + await browser.openQuestionnaire("test_new_calculated_summary_repeating_and_static_answers.json"); + await $(HubPage.submit()).click(); + await $(AnySupermarketPage.yes()).click(); + await $(AnySupermarketPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Lidl"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $$(DynamicAnswerPage.inputs())[0].setValue(300); + await $$(DynamicAnswerPage.inputs())[1].setValue(200); + await $$(DynamicAnswerPage.inputs())[2].setValue(30); + await $$(DynamicAnswerPage.inputs())[3].setValue(15); + await $$(DynamicAnswerPage.inputs())[4].setValue(4); + await $$(DynamicAnswerPage.inputs())[5].setValue(2); + await $(DynamicAnswerPage.extraStatic()).setValue(5); + await $(DynamicAnswerPage.submit()).click(); + await $(ExtraSpendingBlockPage.extraSpending()).setValue(0); + }); + + it("Given I complete all list collector dynamic answers for two calculated summaries one of which also has static answers, I'm taken to each one in turn, showing the correct answers", async () => { + await $(ExtraSpendingBlockPage.submit()).click(); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?" + ); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).to.contain("£550.00"); + await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total visits to the shop to be 6. Is this correct?" + ); + await assertSummaryValues(["4", "2"]); + }); + + it("Given I click on a change link, when I use the previous button, I return to the calculated summary", async () => { + await dynamicAnswerChangeLink(1).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await $(DynamicAnswerPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); + }); + + it("Given I click on a change link, edit an answer and continue, I return to the calculated summary to reconfirm it", async () => { + await dynamicAnswerChangeLink(0).click(); + await $$(DynamicAnswerPage.inputs())[5].setValue(3); + await $(DynamicAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total visits to the shop to be 7. Is this correct?" + ); + await assertSummaryValues(["4", "3"]); + await $(CalculatedSummaryVisitsPage.submit()).click(); + }); + + it("Given I go back and change an answer that opens up a new question before the calculated summary, I am taken to the new question, and then the calculated summary", async () => { + await $(SummaryPage.extraSpendingAnswerEdit()).click(); + await $(ExtraSpendingBlockPage.extraSpending()).setValue(50); + await $(ExtraSpendingBlockPage.submit()).click(); + + // new question + await expect(await browser.getUrl()).to.contain(ExtraSpendingMethodBlockPage.pageName); + await $(ExtraSpendingMethodBlockPage.yes()).click(); + await $(ExtraSpendingMethodBlockPage.submit()).click(); + + // then calculated summary + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?" + ); + + // then jump straight back to section summary (as other calculated summary is unchanged + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); + }); + + it("Given I add a new item to the list, I return to the list collector block, then the dynamic answers, then both calculated summaries to confirm newly added answers", async () => { + await $(SummaryPage.supermarketsListAddLink()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Sainsburys"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + + // return to dynamic answer + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await $$(DynamicAnswerPage.inputs())[2].setValue(100); + await $$(DynamicAnswerPage.inputs())[5].setValue(10); + await $$(DynamicAnswerPage.inputs())[8].setValue(7); + await $(DynamicAnswerPage.submit()).click(); + + // Currently when a section is incomplete, you are taken to each block in the section in turn, if the return_to is inaccessible + // this has been changed for calculated summaries to go to the first incomplete block, but not yet in the general case + // so the expected behaviour is to revisit these two blocks before the calculated summary + await $(ExtraSpendingBlockPage.submit()).click(); + await $(ExtraSpendingMethodBlockPage.submit()).click(); + + // first calc summary + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?" + ); + await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); + + // second calculated summary + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total visits to the shop to be 14. Is this correct?" + ); + await assertSummaryValues(["4", "3", "2"]); + await $(CalculatedSummaryVisitsPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); + }); + + it("Given I remove an item from the list which changes the calculated summaries, I return to each calculated summary to confirm new total with answers removed", async () => { + await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Tesco"); + await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Lidl"); + await expect(await $(SummaryPage.supermarketsListLabel(3)).getText()).to.equal("Sainsburys"); + await expect(await $(SummaryPage.supermarketsListLabel(4)).isExisting()).to.be.false; + await $(SummaryPage.supermarketsListRemoveLink(1)).click(); + + await expect(await browser.getUrl()).to.contain(ListCollectorRemovePage.pageName); + await $(ListCollectorRemovePage.yes()).click(); + await $(ListCollectorRemovePage.submit()).click(); + + // section is now incomplete so step through each block until calculated summary is re-confirmed + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await $(DynamicAnswerPage.submit()).click(); + await $(ExtraSpendingBlockPage.submit()).click(); + await $(ExtraSpendingMethodBlockPage.submit()).click(); + + // Tesco is now gone + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?" + ); + await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total visits to the shop to be 10. Is this correct?" + ); + await assertSummaryValues(["3", "7"]); + await $(CalculatedSummaryVisitsPage.submit()).click(); + + await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Lidl"); + await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Sainsburys"); + await expect(await $(SummaryPage.supermarketsListLabel(3)).isExisting()).to.be.false; + }); + + it("Given I proceed to the second section and enter a value greater than the calculated summary from the previous section, the correct error message is displayed", async () => { + await $(SummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(SupermarketTransportPage.weeklyCarTrips()).setValue(11); + await $(SupermarketTransportPage.submit()).click(); + await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 10"); + }); + + it("Given I change my answer to a value less than the calculated summary from the previous section, I am able to proceed", async () => { + await $(SupermarketTransportPage.weeklyCarTrips()).setValue(9); + await $(SupermarketTransportPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SupermarketTransportCostPage.pageName); + }); + + it("Given I reach the final block, the calculated summary of dynamic answers is piped in correctly", async () => { + await $(SupermarketTransportCostPage.weeklyTripsCost()).setValue(30); + await $(SupermarketTransportCostPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryPipingPage.pageName); + await expect(await $("body").getText()).to.have.string("Total weekly supermarket spending: £380.00"); + await expect(await $("body").getText()).to.have.string("Total weekly supermarket visits: 10"); + await expect(await $("body").getText()).to.have.string("Total of supermarket visits by car: 9"); + await expect(await $("body").getText()).to.have.string("Total spending on parking: £30.00"); + await $(CalculatedSummaryPipingPage.submit()).click(); + }); + + it("Given I return to section 1 and update the calculated summary used in section 2 validation, the progress of section 2 is updated", async () => { + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await $(HubPage.summaryRowLink("section-1")).click(); + await dynamicAnswerChangeLink(8).click(); + await $$(DynamicAnswerPage.inputs())[5].setValue(1); + await $(DynamicAnswerPage.submit()).click(); + await $(ExtraSpendingBlockPage.submit()).click(); + await $(ExtraSpendingMethodBlockPage.submit()).click(); + await $(CalculatedSummarySpendingPage.submit()).click(); + await $(CalculatedSummaryVisitsPage.submit()).click(); + await $(SummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + }); +}); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js new file mode 100644 index 0000000000..13abc8836b --- /dev/null +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -0,0 +1,236 @@ +import SectionOnePage from "../../../generated_pages/new_calculated_summary_repeating_blocks/section-1-summary.page.js"; +import SectionTwoPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/section-2-summary.page.js"; +import BlockCarPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/block-car.page.js"; +import AddTransportPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/list-collector-add.page.js"; +import RemoveTransportPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/list-collector-remove.page.js"; +import TransportRepeatingBlock1Page from "../../../generated_pages/new_calculated_summary_repeating_blocks/transport-repeating-block-1-repeating-block.page.js"; +import TransportRepeatingBlock2Page from "../../../generated_pages/new_calculated_summary_repeating_blocks/transport-repeating-block-2-repeating-block.page.js"; +import ListCollectorPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/list-collector.page.js"; +import CalculatedSummarySpendingPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/calculated-summary-spending.page.js"; +import CalculatedSummaryCountPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/calculated-summary-count.page.js"; +import HubPage from "../../../base_pages/hub.page"; +import FamilyJourneysPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/family-journeys.page"; +import BlockSkipPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/block-skip.page"; +import { assertSummaryValues, repeatingAnswerChangeLink } from "../../../helpers"; + +describe("Feature: Calculated Summary using Repeating Blocks", () => { + before("Reaching the first calculated summary", async () => { + await browser.openQuestionnaire("test_new_calculated_summary_repeating_blocks.json"); + await $(BlockCarPage.car()).setValue(100); + await $(BlockCarPage.submit()).click(); + await $(BlockSkipPage.no()).click(); + await $(BlockSkipPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(AddTransportPage.transportName()).selectByAttribute("value", "Bus"); + await $(AddTransportPage.submit()).click(); + await $(TransportRepeatingBlock1Page.transportCompany()).setValue("First"); + await $(TransportRepeatingBlock1Page.transportCost()).setValue(30); + await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(5); + await $(TransportRepeatingBlock1Page.submit()).click(); + await $(TransportRepeatingBlock2Page.transportCount()).setValue(10); + await $(TransportRepeatingBlock2Page.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(AddTransportPage.transportName()).selectByAttribute("value", "Plane"); + await $(AddTransportPage.submit()).click(); + await $(TransportRepeatingBlock1Page.transportCompany()).setValue("EasyJet"); + await $(TransportRepeatingBlock1Page.transportCost()).setValue(0); + await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(265); + await $(TransportRepeatingBlock1Page.submit()).click(); + await $(TransportRepeatingBlock2Page.transportCount()).setValue(2); + await $(TransportRepeatingBlock2Page.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + }); + + it("Given I have a calculated summary using both list repeating block and static answers, When I reach the calculated summary page, Then I see the correct items and total.", async () => { + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?" + ); + await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00"]); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly expenditure travelling by car"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly season ticket expenditure for travel by Bus"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Bus"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly season ticket expenditure for travel by Plane"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Plane"); + await $(CalculatedSummarySpendingPage.submit()).click(); + }); + + it("Given I have a calculated summary using a single answer from a repeating block, When I reach the calculated summary page, Then I see the correct items and total", async () => { + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total journeys made per month to be 12. Is this correct?" + ); + await assertSummaryValues(["10", "2"]); + await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Bus"); + await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Plane"); + await $(CalculatedSummaryCountPage.submit()).click(); + }); + + it("Given I add a new item to the list, When I complete the repeating blocks and press continue, Then I see the first calculated summary page which the updated total", async () => { + await $(SectionOnePage.transportListAddLink()).click(); + await $(AddTransportPage.transportName()).selectByAttribute("value", "Train"); + await $(AddTransportPage.submit()).click(); + await $(TransportRepeatingBlock1Page.transportCompany()).setValue("Great Western Railway"); + await $(TransportRepeatingBlock1Page.transportCost()).setValue(100); + await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(50); + await $(TransportRepeatingBlock1Page.submit()).click(); + await $(TransportRepeatingBlock2Page.transportCount()).setValue(6); + await $(TransportRepeatingBlock2Page.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?" + ); + await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + }); + + it("Given I am on the first calculated summary, When I confirm the total, Then I see the second calculated summary with an updated total", async () => { + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total journeys made per month to be 18. Is this correct?" + ); + await assertSummaryValues(["10", "2", "6"]); + await $(CalculatedSummaryCountPage.previous()).click(); + }); + + it("Given I am on the first calculated summary, When I use one of the change links, Then I see the correct repeating block", async () => { + await repeatingAnswerChangeLink(1).click(); + await expect(await browser.getUrl()).to.contain(TransportRepeatingBlock1Page.pageName); + }); + + it("Given I have used a change link on a calculated summary to go back to the first repeating block, When I press continue, Then I see the calculated summary I came from", async () => { + await $(TransportRepeatingBlock1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + }); + + it("Given I am on a calculated summary with change links for repeating blocks, When I use a change link and click previous, Then I see the calculated summary I came from", async () => { + await repeatingAnswerChangeLink(1).click(); + await $(TransportRepeatingBlock1Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + }); + + it("Given I use a repeating block change link on the first calculated summary, When I edit my answer and press continue, Then I see the first calculated summary with a new correct total", async () => { + await repeatingAnswerChangeLink(1).click(); + await $(TransportRepeatingBlock1Page.transportCost()).setValue(60); + await $(TransportRepeatingBlock1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?" + ); + await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + await $(CalculatedSummarySpendingPage.submit()).click(); + }); + + it("Given I use a repeating block change link on the second calculated summary, When I edit my answer and press continue, Then I see the second calculated summary with a new correct total", async () => { + await repeatingAnswerChangeLink(2).click(); + await $(TransportRepeatingBlock2Page.transportCount()).setValue(12); + await $(TransportRepeatingBlock2Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total journeys made per month to be 24. Is this correct?" + ); + await assertSummaryValues(["10", "2", "12"]); + await $(CalculatedSummaryCountPage.submit()).click(); + }); + + it("Given I use a remove link for on the summary page, When I press yes to confirm deleting the item, Then I see see the first calculated summary where I'm asked to reconfirm the total", async () => { + await $(SectionOnePage.transportListRemoveLink(1)).click(); + await $(RemoveTransportPage.yes()).click(); + await $(RemoveTransportPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?" + ); + await assertSummaryValues(["£100.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + }); + + it("Given I have confirmed the first updated total, When I press continue, Then I see the next calculated summary to confirm that total too", async () => { + await $(CalculatedSummarySpendingPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total journeys made per month to be 14. Is this correct?" + ); + await assertSummaryValues(["2", "12"]); + }); + + it("Given I have a second section, When I begin and answer the first question with a total higher than the calculated summary, Then I see an error message preventing me from continuing", async () => { + await $(CalculatedSummaryCountPage.submit()).click(); + await $(SectionOnePage.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(FamilyJourneysPage.questionTitle()).getText()).to.contain("How many of your 14 journeys are to visit family?"); + await $(FamilyJourneysPage.answer()).setValue(15); + await $(FamilyJourneysPage.submit()).click(); + await expect(await $(FamilyJourneysPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 14"); + }); + + it("Given I enter a value below the calculated summary from section 1, When I press Continue, Then I see my answer displayed on the next page", async () => { + await $(FamilyJourneysPage.answer()).setValue(10); + await $(FamilyJourneysPage.submit()).click(); + await expect(await $(SectionTwoPage.familyJourneysQuestion()).getText()).to.contain("How many of your 14 journeys are to visit family?"); + await expect(await $(SectionTwoPage.familyJourneysAnswer()).getText()).to.contain("10"); + await $(SectionTwoPage.submit()).click(); + }); + + it("Given I use the add list item link, When I add a new item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await $(HubPage.summaryRowLink("section-1")).click(); + await $(SectionOnePage.transportListAddLink()).click(); + await $(AddTransportPage.transportName()).selectByAttribute("value", "Tube"); + await $(AddTransportPage.submit()).click(); + await $(TransportRepeatingBlock1Page.submit()).click(); + await $(TransportRepeatingBlock2Page.transportCount()).setValue(2); + await $(TransportRepeatingBlock2Page.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(CalculatedSummarySpendingPage.submit()).click(); + await $(CalculatedSummaryCountPage.submit()).click(); + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + }); + + it("Given I complete section-2 again, When I remove a list item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { + await $(HubPage.submit()).click(); + await $(FamilyJourneysPage.answer()).setValue(16); + await $(FamilyJourneysPage.submit()).click(); + await $(SectionTwoPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await $(HubPage.summaryRowLink("section-1")).click(); + await $(SectionOnePage.transportListRemoveLink(3)).click(); + await $(RemoveTransportPage.yes()).click(); + await $(RemoveTransportPage.submit()).click(); + await $(CalculatedSummarySpendingPage.submit()).click(); + await $(CalculatedSummaryCountPage.submit()).click(); + await $(SectionOnePage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + }); + + it("Given I have a question which removes the list collector from the path, When I change my answer to the question removing the list collector and route backwards from the summary, Then I see the first calculated summary with an updated total", async () => { + await $(HubPage.summaryRowLink("section-1")).click(); + await $(SectionOnePage.answerSkipEdit()).click(); + await $(BlockSkipPage.yes()).click(); + await $(BlockSkipPage.submit()).click(); + // calculated summary progress is not altered by removing the list collector from the path so next location is summary page + await expect(await browser.getUrl()).to.contain(SectionOnePage.pageName); + await $(SectionOnePage.previous()).click(); + // other calculated summary should not be on the path, so go straight back to the spending one which now has none of the list items + await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?" + ); + await assertSummaryValues(["£100.00"]); + }); + + it("Given I confirm the calculated summary and finish the section, When I return to the Hub, Then I see that section 2 is no longer available", async () => { + await $(CalculatedSummarySpendingPage.submit()).click(); + await $(SectionOnePage.submit()).click(); + // section 2 is now gone + await expect(await $$(HubPage.summaryItems()).length).to.equal(1); + }); +}); diff --git a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js similarity index 87% rename from tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js rename to tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 8391e5602d..4229eb140a 100644 --- a/tests/functional/spec/features/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -1,38 +1,38 @@ -import FirstNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/first-number-block.page.js"; -import SecondNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/second-number-block.page.js"; -import ThirdNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/third-number-block.page.js"; -import ThirdAndAHalfNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/third-and-a-half-number-block.page.js"; -import SkipFourthBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/skip-fourth-block.page.js"; -import FourthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fourth-number-block.page.js"; -import FourthAndAHalfNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fourth-and-a-half-number-block.page.js"; -import FifthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/fifth-number-block.page.js"; -import SixthNumberBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/sixth-number-block.page.js"; -import CurrencyTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/currency-total-playback.page.js"; -import SetMinMaxBlockPage from "../../generated_pages/new_calculated_summary_repeating_section/set-min-max-block.page.js"; -import UnitTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/unit-total-playback.page.js"; -import PercentageTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/percentage-total-playback.page.js"; -import NumberTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/number-total-playback.page.js"; -import BreakdownPage from "../../generated_pages/new_calculated_summary_repeating_section/breakdown.page.js"; -import SecondCurrencyTotalPlaybackPage from "../../generated_pages/new_calculated_summary_repeating_section/second-currency-total-playback.page.js"; -import CalculatedSummaryTotalConfirmation from "../../generated_pages/new_calculated_summary_repeating_section/calculated-summary-total-confirmation.page.js"; -import SubmitPage from "../../generated_pages/new_calculated_summary_repeating_section/personal-details-section-summary.page.js"; -import ThankYouPage from "../../base_pages/thank-you.page.js"; -import HubPage from "../../base_pages/hub.page.js"; -import PrimaryPersonListCollectorPage from "../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector.page"; -import PrimaryPersonListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector-add.page.js"; -import ListCollectorPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector.page"; -import ListCollectorAddPage from "../../generated_pages/new_calculated_summary_repeating_section/list-collector-add.page"; -import SkipFirstNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/skip-first-block.page"; -import FirstNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-number-block.page"; -import FirstAndAHalfNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-and-a-half-number-block.page"; -import SecondNumberBlockPageSectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/second-number-block.page"; -import CalculatedSummarySectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-1.page"; -import CalculatedSummarySectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-2.page"; -import ThirdNumberBlockPageSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/third-number-block.page"; -import SectionSummarySectionOne from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/questions-section-summary.page"; -import SectionSummarySectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; -import DependencyQuestionSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; -import MinMaxSectionTwo from "../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; +import FirstNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/first-number-block.page.js"; +import SecondNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/second-number-block.page.js"; +import ThirdNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/third-number-block.page.js"; +import ThirdAndAHalfNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/third-and-a-half-number-block.page.js"; +import SkipFourthBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/skip-fourth-block.page.js"; +import FourthNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/fourth-number-block.page.js"; +import FourthAndAHalfNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/fourth-and-a-half-number-block.page.js"; +import FifthNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/fifth-number-block.page.js"; +import SixthNumberBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/sixth-number-block.page.js"; +import CurrencyTotalPlaybackPage from "../../../generated_pages/new_calculated_summary_repeating_section/currency-total-playback.page.js"; +import SetMinMaxBlockPage from "../../../generated_pages/new_calculated_summary_repeating_section/set-min-max-block.page.js"; +import UnitTotalPlaybackPage from "../../../generated_pages/new_calculated_summary_repeating_section/unit-total-playback.page.js"; +import PercentageTotalPlaybackPage from "../../../generated_pages/new_calculated_summary_repeating_section/percentage-total-playback.page.js"; +import NumberTotalPlaybackPage from "../../../generated_pages/new_calculated_summary_repeating_section/number-total-playback.page.js"; +import BreakdownPage from "../../../generated_pages/new_calculated_summary_repeating_section/breakdown.page.js"; +import SecondCurrencyTotalPlaybackPage from "../../../generated_pages/new_calculated_summary_repeating_section/second-currency-total-playback.page.js"; +import CalculatedSummaryTotalConfirmation from "../../../generated_pages/new_calculated_summary_repeating_section/calculated-summary-total-confirmation.page.js"; +import SubmitPage from "../../../generated_pages/new_calculated_summary_repeating_section/personal-details-section-summary.page.js"; +import ThankYouPage from "../../../base_pages/thank-you.page.js"; +import HubPage from "../../../base_pages/hub.page.js"; +import PrimaryPersonListCollectorPage from "../../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector.page"; +import PrimaryPersonListCollectorAddPage from "../../../generated_pages/new_calculated_summary_repeating_section/primary-person-list-collector-add.page.js"; +import ListCollectorPage from "../../../generated_pages/new_calculated_summary_repeating_section/list-collector.page"; +import ListCollectorAddPage from "../../../generated_pages/new_calculated_summary_repeating_section/list-collector-add.page"; +import SkipFirstNumberBlockPageSectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/skip-first-block.page"; +import FirstNumberBlockPageSectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-number-block.page"; +import FirstAndAHalfNumberBlockPageSectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/first-and-a-half-number-block.page"; +import SecondNumberBlockPageSectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/second-number-block.page"; +import CalculatedSummarySectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-1.page"; +import CalculatedSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/currency-total-playback-2.page"; +import ThirdNumberBlockPageSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/third-number-block.page"; +import SectionSummarySectionOne from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/questions-section-summary.page"; +import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; +import DependencyQuestionSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; +import MinMaxSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js new file mode 100644 index 0000000000..086fc894e3 --- /dev/null +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -0,0 +1,328 @@ +import ResponsiblePartyPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/responsible-party.page"; +import AnyCompaniesOrBranchesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-companies-or-branches.page"; +import AddCompanyPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-companies-or-branches-add.page"; +import EditCompanyPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-companies-or-branches-edit.page"; +import RemoveCompanyPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-companies-or-branches-remove.page"; +import CompaniesRepeatingBlock1Page from "../../../generated_pages/list_collector_repeating_blocks_section_summary/companies-repeating-block-1-repeating-block.page"; +import CompaniesRepeatingBlock2Page from "../../../generated_pages/list_collector_repeating_blocks_section_summary/companies-repeating-block-2-repeating-block.page"; +import AnyOtherCompaniesOrBranchesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-companies-or-branches.page"; +import SectionCompaniesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/section-companies-summary.page"; +import AnyOtherTradingDetailsPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-trading-details.page"; +import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/submit.page"; +import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete } from "../../../helpers"; +import HubPage from "../../../base_pages/hub.page"; + +const summaryValues = 'dd[class="ons-summary__values"]'; +async function proceedToListCollector() { + await $(ResponsiblePartyPage.yes()).click(); + await $(AnyCompaniesOrBranchesPage.submit()).click(); + await $(AnyCompaniesOrBranchesPage.yes()).click(); + await $(AnyCompaniesOrBranchesPage.submit()).click(); +} + +async function addCompany( + companyOrBranchName, + registrationNumber, + registrationDateDay, + registrationDateMonth, + registrationDateYear, + authorisedTraderUk, + authorisedTraderEu +) { + await $(AddCompanyPage.companyOrBranchName()).setValue(companyOrBranchName); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(registrationNumber); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(registrationDateDay); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(registrationDateMonth); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(registrationDateYear); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + if (authorisedTraderUk) { + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); + } else { + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + } + if (authorisedTraderEu) { + await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); + } else if (authorisedTraderEu !== undefined) { + await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); + } + await $(CompaniesRepeatingBlock2Page.submit()).click(); +} + +describe("List Collector Repeating Blocks", function () { + // These tests are flaky therefore we add a retry. The cause is unknown. + // :TODO: Revert this in future when we have a fix for this. + this.retries(5); + + describe("Given a normal journey through the list collector with repeating blocks, the answers can be submitted.", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); + }); + it("The user is able to add companies, complete repeating blocks, and submit.", async () => { + await proceedToListCollector(); + await addCompany("ONS", 123, 1, 1, 2023, true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("GOV", 456, 2, 2, 2023, false, false); + + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + + await $(AnyOtherTradingDetailsPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + await $(SubmitPage.submit()).click(); + }); + }); + + describe("Given a journey through the list collector with repeating blocks, the companies can be added, removed and edited.", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); + }); + it("The user is able to add companies and complete repeating blocks.", async () => { + await proceedToListCollector(); + await addCompany("ONS", 123, 1, 1, 2023, true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("GOV", 456, 2, 2, 2023, false, false); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("MOD", 789, 3, 3, 2023, true); + }); + + it("The list collector shows all of the companies.", async () => { + const companiesExpected = ["ONS", "GOV", "MOD"]; + checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); + }); + + it("The list collector allows the name of 'GOV' to be changed", async () => { + await $(AnyOtherCompaniesOrBranchesPage.listEditLink(2)).click(); + await $(EditCompanyPage.companyOrBranchName()).setValue("Government"); + await $(EditCompanyPage.submit()).click(); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.equal("Government"); + }); + + it("The list collector allows removal of 'Government'", async () => { + await $(AnyOtherCompaniesOrBranchesPage.listRemoveLink(2)).click(); + await $(RemoveCompanyPage.yes()).click(); + await $(RemoveCompanyPage.submit()).click(); + }); + + it("The list collector does not show 'GOV' anymore.", async () => { + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.not.have.string("Government"); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.equal("MOD"); + }); + + it("The list collector can add more companies.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("Council", 101, 4, 4, 2023, false, true); + }); + + it("The list collector shows all of the companies.", async () => { + const companiesExpected = ["ONS", "MOD", "Council", "another one"]; + checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); + }); + + it("The list collector can then be submitted", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + + await $(AnyOtherTradingDetailsPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + await $(SubmitPage.submit()).click(); + }); + }); + + describe("Given a journey through the list collector with repeating blocks, any incomplete repeating_block will be revisited before navigating to the next block after the list collector.", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); + }); + it("The user is able to add companies complete some repeating blocks and leave others incomplete.", async () => { + await proceedToListCollector(); + + await addCompany("ONS", 123, 1, 1, 2023, true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); + await $(EditCompanyPage.cancelAndReturn()).click(); + + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await $(CompaniesRepeatingBlock2Page.cancelAndReturn()).click(); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); + await $(EditCompanyPage.cancelAndReturn()).click(); + + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("NAV", 101, 4, 4, 2023, true, true); + }); + + it("The list collector shows all of the companies as well as checkmarks on the complete items 1 and 4, but not on the incomplete items 3 and 4.", async () => { + const companiesExpected = ["ONS", "GOV", "MOD", "NAV"]; + checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); + checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + checkListItemIncomplete(`dt[data-qa="list-item-2-label"]`); + checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + }); + + it("Attempting to complete the list collector will navigate the user to the first incomplete block of the second list item.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + }); + + it("Attempting to complete the list collector will navigate the user to the first incomplete block of the third list item.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + }); + + it("All items are now marked as completed with the checkmark icon.", async () => { + checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + checkListItemComplete(`dt[data-qa="list-item-3-label"]`); + checkListItemComplete(`dt[data-qa="list-item-4-label"]`); + }); + + it("The list collector can now be submitted.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AnyOtherTradingDetailsPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + await $(SubmitPage.submit()).click(); + }); + }); + + describe("Given a journey through the list collector with repeating blocks, the answers from repeating blocks can be edited.", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); + }); + it("The user is able to add companies, complete repeating blocks and navigate to the section summary.", async () => { + await proceedToListCollector(); + await addCompany("ONS", 123, 1, 1, 2023, true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await addCompany("GOV", 456, 2, 2, 2023, false); + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AnyOtherTradingDetailsPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + }); + + it("Edit each type of answer on different items from the section summary.", async () => { + await expect(await $$(summaryValues)[8].getText()).to.have.string(456); + await repeatingAnswerChangeLink(8).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await expect(await $$(summaryValues)[8].getText()).to.have.string(789); + + await expect(await $$(summaryValues)[4].getText()).to.have.string("1 January 2023"); + await repeatingAnswerChangeLink(4).click(); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(4); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(4); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await expect(await $$(summaryValues)[4].getText()).to.have.string("4 April 2023"); + + await expect(await $$(summaryValues)[5].getText()).to.have.string("Yes"); + await repeatingAnswerChangeLink(5).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + await expect(await $$(summaryValues)[5].getText()).to.have.string("No"); + + await expect(await $$(summaryValues)[11].getText()).to.have.string("No answer provided"); + await repeatingAnswerChangeLink(11).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + await expect(await $$(summaryValues)[11].getText()).to.have.string("Yes"); + }); + + it("The list collector can then be submitted", async () => { + await $(SubmitPage.submit()).click(); + }); + }); +}); + +describe("Given a journey through the list collector with repeating blocks, in a mandatory section of hub questionnaire, the incomplete repeating blocks mark the list collector incomplete and thus navigate back there.", function () { + // These tests are flaky therefore we add a retry. The cause is unknown. + // :TODO: Revert this in future when we have a fix for this. + this.retries(5); + + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_with_hub.json"); + }); + it("The user is able to add a compete company and an incomplete company.", async () => { + await proceedToListCollector(); + + await addCompany("ONS", 123, 1, 1, 2023, true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); + }); + + it("Navigating to the root of the questionnaire will redirect to the incomplete list collector, which we can then complete.", async () => { + await browser.url("questionnaire/"); + await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); + + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + }); + + it("Another incomplete item can be added via the section summary.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(AnyOtherTradingDetailsPage.submit()).click(); + await $(SectionCompaniesPage.companiesListAddLink()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); + }); + + it("Navigating to the submit page of the section will redirect to the incomplete list collector, which we can then complete.", async () => { + await browser.url("questionnaire/sections/section-companies/"); + await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); + + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); + }); + + it("The section can now be submitted and and hub completed.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + await $(HubPage.submit()).click(); + }); +}); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js new file mode 100644 index 0000000000..895d30c094 --- /dev/null +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -0,0 +1,154 @@ +import ListCollectorPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector.page"; +import ListCollectorAddPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector-add.page"; +import DynamicAnswerPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/dynamic-answer.page"; +import DynamicAnswerOnlyPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/dynamic-answer-only.page"; +import TotalBlockPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block.page"; +import DriverPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/any-supermarket.page"; +import SectionSummaryPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/dynamic-answers-section-summary.page"; +import ListCollectorRemovePage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector-remove.page"; +import ListCollectorEditPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector-edit.page"; +import HubPage from "../../../../base_pages/hub.page"; +import TotalBlockOtherPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block-other.page"; + +describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", function () { + // These tests are flaky therefore we add a retry. The cause is unknown. + // :TODO: Revert this in future when we have a fix for this. + this.retries(5); + + const summaryTitles = 'dt[class="ons-summary__item-title"]'; + beforeEach(async () => { + await browser.openQuestionnaire("test_validation_sum_against_total_dynamic_answers.json"); + }); + + describe("Given I add list items with hardcoded total used for validation of dynamic answers", () => { + it("When I continue and enter numbers on dynamic and static answers page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { + await addTwoSupermarkets(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await $$(DynamicAnswerPage.inputs())[0].setValue(33); + await $$(DynamicAnswerPage.inputs())[1].setValue(33); + await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); + await $(DynamicAnswerPage.submit()).click(); + await expect(await $(DynamicAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 100"); + }); + }); + describe("Given I add list items with hardcoded total used for validation of dynamic answers", () => { + it("When I continue and enter numbers on dynamic and static answers page that add up to that total, Then I should be able to get to the subsequent question", async () => { + await addTwoSupermarkets(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await $$(DynamicAnswerPage.inputs())[0].setValue(34); + await $$(DynamicAnswerPage.inputs())[1].setValue(33); + await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); + await $(DynamicAnswerPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(TotalBlockOtherPage.pageName); + }); + }); + describe("Given I add list items with custom total used for validation of dynamic answers", () => { + it("When I continue and enter numbers on dynamic answers only page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { + await addTwoSupermarkets(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await $$(DynamicAnswerPage.inputs())[0].setValue(34); + await $$(DynamicAnswerPage.inputs())[1].setValue(33); + await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); + await $(DynamicAnswerPage.submit()).click(); + await $(TotalBlockOtherPage.totalOther()).setValue(100); + await $(TotalBlockOtherPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerOnlyPage.pageName); + await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); + await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(0); + await $(DynamicAnswerOnlyPage.submit()).click(); + await expect(await $(DynamicAnswerOnlyPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £100.00"); + }); + }); + describe("Given I add list items with custom total used for validation of dynamic answers", () => { + it("When I continue and enter numbers on dynamic answers only page that add up to that total, Then I should be able to get to the summary", async () => { + await addTwoSupermarkets(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await fillDynamicAnswers(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + }); + }); + describe("Given I add list items and fill all the dynamic answers", () => { + it("When I continue and add another list item, Then I should be revisiting dynamic answers which should be updated to reflect the changes", async () => { + await addTwoSupermarkets(); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await fillDynamicAnswers(); + await $(SectionSummaryPage.supermarketsListAddLink()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Morrisons"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); + }); + }); + describe("Given I add list items and fill all the dynamic answers", () => { + it("When I continue and remove existing list item, Then I should be revisiting dynamic answers which should be updated to reflect the changes", async () => { + await addTwoSupermarkets(); + await fillDynamicAnswers(); + await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); + await $(ListCollectorRemovePage.yes()).click(); + await $(ListCollectorRemovePage.submit()).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); + }); + }); + describe("Given I add list items and fill all the dynamic answers", () => { + it("When I continue and edit existing list item, Then I should be revisiting dynamic answers which should be updated to reflect the changes", async () => { + await addTwoSupermarkets(); + await fillDynamicAnswers(); + await $(SectionSummaryPage.supermarketsListEditLink(1)).click(); + await $(ListCollectorEditPage.supermarketName()).setValue("Aldi"); + await $(ListCollectorEditPage.submit()).click(); + await $(DynamicAnswerPage.submit()).click(); + await $(TotalBlockOtherPage.submit()).click(); + await $(DynamicAnswerOnlyPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: 2000 }); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); + }); + }); + describe("Given I add list items and fill all the dynamic answers", () => { + it("When I journey backwards, Then I should be revisiting all the previous blocks", async () => { + await addTwoSupermarkets(); + await fillDynamicAnswers(); + await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await $(SectionSummaryPage.previous()).click(); + await $(DynamicAnswerOnlyPage.previous()).click(); + await $(TotalBlockOtherPage.previous()).click(); + await $(DynamicAnswerPage.previous()).click(); + await $(ListCollectorPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(DriverPage.pageName); + }); + }); +}); + +async function addTwoSupermarkets() { + await $(TotalBlockPage.total()).setValue(100); + await $(TotalBlockPage.submit()).click(); + await $(HubPage.summaryRowLink("dynamic-answers-section")).click(); + await $(DriverPage.yes()).click(); + await $(DriverPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Asda"); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); +} + +async function fillDynamicAnswers() { + await $$(DynamicAnswerPage.inputs())[0].setValue(34); + await $$(DynamicAnswerPage.inputs())[1].setValue(33); + await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); + await $(DynamicAnswerPage.submit()).click(); + await $(TotalBlockOtherPage.totalOther()).setValue(100); + await $(TotalBlockOtherPage.submit()).click(); + await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); + await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(50); + await $(DynamicAnswerOnlyPage.submit()).click(); +} diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index a3ea2ab759..380abddd67 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import AnotherListCollectorPage from "../generated_pages/list_collector/another-list-collector-block.page.js"; import AnotherListCollectorAddPage from "../generated_pages/list_collector/another-list-collector-block-add.page.js"; import AnotherListCollectorEditPage from "../generated_pages/list_collector/another-list-collector-block-edit.page.js"; @@ -52,7 +52,7 @@ describe("List Collector", () => { it("The collector shows all of the household members in the summary", async () => { const peopleExpected = ["Marcus Twin", "Samuel Clemens", "Olivia Clemens", "Suzy Clemens"]; - checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); + checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire allows the name of a person to be changed", async () => { @@ -110,7 +110,7 @@ describe("List Collector", () => { it("The collector shows everyone on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; - checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); + checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("When No is answered on the list collector the user sees an interstitial", async () => { @@ -126,7 +126,7 @@ describe("List Collector", () => { it("The collector still shows the same list of people on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; - checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); + checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The collector allows the user to add another person to the same list", async () => { diff --git a/tests/functional/spec/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector_driving_question.spec.js index 1280e2a020..b5286fcb9e 100644 --- a/tests/functional/spec/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector_driving_question.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import HubPage from "../base_pages/hub.page.js"; import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_driving_question/anyone-usually-live-at.page.js"; import AnyoneElseLiveAtListCollectorPage from "../generated_pages/list_collector_driving_question/anyone-else-live-at.page.js"; @@ -29,7 +29,7 @@ describe("List Collector Driving Question", () => { const peopleExpected = ["Marcus Twin", "Suzy Clemens"]; - await checkPeopleInList(peopleExpected, SectionSummaryPage.peopleListLabel); + await checkItemsInList(peopleExpected, SectionSummaryPage.peopleListLabel); }); }); diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js index c5b5a73087..8ea27498d8 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import HubPage from "../base_pages/hub.page.js"; import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector.page.js"; import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector-add.page.js"; @@ -35,7 +35,7 @@ describe("List Collector Driving Checkbox Question", () => { await $(ListCollectorTemporaryAwayPage.submit()).click(); const householdMembersExpected = ["Marcus Twin (You)", "Suzy Clemens"]; - await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); @@ -49,7 +49,7 @@ describe("List Collector Driving Checkbox Question", () => { await $(PrimaryPersonListCollectorPage.submit()).click(); const householdMembersExpected = ["Suzy Clemens"]; - await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); @@ -65,7 +65,7 @@ describe("List Collector Driving Checkbox Question", () => { await $(ListCollectorTemporaryAwayPage.submit()).click(); const householdMembersExpected = ["Suzy Clemens", "Christopher Pike"]; - await checkPeopleInList(householdMembersExpected, SummaryPage.peopleListLabel); + await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); }); }); }); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index 0c9ff489ff..c3baa42f50 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -10,6 +10,7 @@ import HouseholderCheckboxPage from "../generated_pages/list_collector_section_s import SubmitPage from "../generated_pages/list_collector_section_summary/submit.page"; import ThankYouPage from "../base_pages/thank-you.page"; import ViewSubmittedResponsePage from "../generated_pages/list_collector_section_summary/view-submitted-response.page"; +import { listItemIds } from "../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -32,9 +33,14 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); - await expect(await $(companiesListRowItemAnchor(1)).getHTML()).to.contain("return_to=section-summary#company-or-branch-name"); - await expect(await $(companiesListRowItemAnchor(2)).getHTML()).to.contain("return_to_answer_id=registration-number#registration-number"); - await expect(await $(companiesListRowItemAnchor(3)).getHTML()).to.contain("return_to_answer_id=authorised-insurer-radio#authorised-insurer-radio"); + const listItemId = (await listItemIds())[0]; + await expect(await $(companiesListRowItemAnchor(1)).getHTML()).to.contain( + `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name` + ); + await expect(await $(companiesListRowItemAnchor(2)).getHTML()).to.contain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); + await expect(await $(companiesListRowItemAnchor(3)).getHTML()).to.contain( + `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio` + ); }); it("When I add multiple items, Then all the items should be visible on the section summary and have correct values", async () => { await drivingQuestionYes(); diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector_variants.spec.js index 7bc017d46c..1a3e75d76a 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector_variants.spec.js @@ -1,4 +1,4 @@ -import checkPeopleInList from "../helpers"; +import { checkItemsInList } from "../helpers"; import YouLiveHerePage from "../generated_pages/list_collector_variants/you-live-here-block.page.js"; import ListCollectorPage from "../generated_pages/list_collector_variants/list-collector.page.js"; import ListCollectorAddPage from "../generated_pages/list_collector_variants/list-collector-add.page.js"; @@ -30,7 +30,7 @@ describe("List Collector With Variants", () => { it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; - checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); + checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire has the correct question text on the change and remove pages", async () => { @@ -76,7 +76,7 @@ describe("List Collector With Variants", () => { it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; - checkPeopleInList(peopleExpected, ListCollectorPage.listLabel); + checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire has the correct question text on the change and remove pages", async () => { diff --git a/tests/functional/spec/question_description.spec.js b/tests/functional/spec/question_description.spec.js index 1d103c8a75..cf55e85acd 100644 --- a/tests/functional/spec/question_description.spec.js +++ b/tests/functional/spec/question_description.spec.js @@ -1,4 +1,9 @@ import NameBlockPage from "../generated_pages/question_description/name-block.page.js"; +import DescriptionBlockPage from "../generated_pages/optional_guidance_and_description/description-block.page"; +import RadioPage from "../generated_pages/optional_guidance_and_description/mandatory-radio.page"; +import RadioPageTwo from "../generated_pages/optional_guidance_and_description/mandatory-radio-two.page"; +import IntroductionPage from "../generated_pages/question_guidance/introduction.page"; +import GuidancePage from "../generated_pages/question_guidance/block-test-guidance-title.page"; describe("Question description", () => { it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", async () => { @@ -6,3 +11,25 @@ describe("Question description", () => { await expect(await $(NameBlockPage.questionTitle()).getHTML()).to.contain("

      Answer the question

      Go on

      "); }); }); + +describe("Optional question description and guidance", () => { + it("Given a question description has been set in the schema, When the value to be displayed is None, Then it is not rendered on the page", async () => { + await browser.openQuestionnaire("test_optional_guidance_and_description.json"); + await $(DescriptionBlockPage.submit()).click(); + await expect(await $(RadioPage.questionTitle()).getHTML()).to.not.contain("

      ''

      "); + await expect(await $(RadioPage.guidance()).isExisting()).to.be.false; + await $(RadioPage.no()).click(); + await $(RadioPage.submit()).click(); + await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.contain("
    • List item one
    • "); + await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.not.contain("
    • "); + }); +}); + +describe("Question guidance", () => { + it("Given a question guidance with multiple content items, When it is rendered, Then there should only be one guidance box", async () => { + await browser.openQuestionnaire("test_question_guidance.json"); + await $(IntroductionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(GuidancePage.pageName); + await expect(await $$("#question-guidance-question-test-guidance-title").length).to.equal(1); + }); +}); diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 8d5ae365ef..6af59e1cce 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -1,9 +1,10 @@ import time +from copy import deepcopy from uuid import uuid4 from sdc.crypto.encrypter import encrypt -from app.authentication.auth_payload_version import AuthPayloadVersion +from app.authentication.auth_payload_versions import AuthPayloadVersion from app.keys import KEY_PURPOSE_AUTHENTICATION from tests.app.parser.conftest import get_response_expires_at @@ -51,6 +52,31 @@ "account_service_url": ACCOUNT_SERVICE_URL, } +PAYLOAD_V2_SUPPLEMENTARY_DATA = { + "version": AuthPayloadVersion.V2.value, + "survey_metadata": { + "data": { + "user_id": "integration-test", + "period_str": "April 2016", + "period_id": "201604", + "ru_ref": "123456789012A", + "ru_name": "Integration Testing", + "ref_p_start_date": "2016-04-01", + "ref_p_end_date": "2016-04-30", + "trad_as": "Integration Tests", + "employment_date": "1983-06-02", + "display_address": "68 Abingdon Road, Goathill", + "sds_dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + } + }, + "collection_exercise_sid": "789", + "response_id": "1234567890123456", + "language_code": "en", + "roles": [], + "account_service_url": ACCOUNT_SERVICE_URL, +} + PAYLOAD_V2_SOCIAL = { "version": AuthPayloadVersion.V2.value, "survey_metadata": { @@ -113,6 +139,21 @@ def create_token_v2(self, schema_name, theme="default", **extra_payload): return self.generate_token(payload) + def create_supplementary_data_token( + self, schema_name, sds_dataset_id=None, **extra_payload + ): + payload = PAYLOAD_V2_SUPPLEMENTARY_DATA + + if sds_dataset_id: + payload = deepcopy(payload) + payload["survey_metadata"]["data"]["sds_dataset_id"] = sds_dataset_id + + payload = self._get_payload_with_params( + schema_name=schema_name, payload=payload, **extra_payload + ) + + return self.generate_token(payload) + def create_token_invalid_version(self, schema_name, **extra_payload): payload = self._get_payload_with_params( schema_name=schema_name, payload=PAYLOAD_V2_BUSINESS, **extra_payload diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 3f1b6a7aa6..2ab0066a91 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -10,7 +10,7 @@ from mock import patch from sdc.crypto.key_store import KeyStore -from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SUBMISSION +from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SDS, KEY_PURPOSE_SUBMISSION from app.setup import create_app from app.utilities.json import json_loads from application import configure_logging @@ -23,6 +23,8 @@ EQ_SUBMISSION_SDX_PRIVATE_KEY = "2225f01580a949801274a5f3e6861947018aff5b" EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY = "fe425f951a0917d7acdd49230b23a5c405c28510" +EQ_SUPPLEMENTARY_DATA_PRIVATE_KEY = "df88fdad2612ae1e80571120e6c6371f55896696" + KEYS_FOLDER = "./tests/jwt-test-keys" @@ -34,6 +36,41 @@ def get_file_contents(filename, trim=False): return data +KEYS_DICT = { + "keys": { + EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID: { + "purpose": KEY_PURPOSE_AUTHENTICATION, + "type": "private", + "value": get_file_contents("sdc-rrm-authentication-signing-private-v1.pem"), + }, + SR_USER_AUTHENTICATION_PUBLIC_KEY_KID: { + "purpose": KEY_PURPOSE_AUTHENTICATION, + "type": "public", + "value": get_file_contents( + "sdc-sr-authentication-encryption-public-v1.pem" + ), + }, + EQ_SUBMISSION_SDX_PRIVATE_KEY: { + "purpose": KEY_PURPOSE_SUBMISSION, + "type": "private", + "value": get_file_contents("sdc-sdx-submission-encryption-private-v1.pem"), + }, + EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY: { + "purpose": KEY_PURPOSE_SUBMISSION, + "type": "public", + "value": get_file_contents("sdc-sr-submission-signing-private-v1.pem"), + }, + EQ_SUPPLEMENTARY_DATA_PRIVATE_KEY: { + "purpose": KEY_PURPOSE_SDS, + "type": "private", + "value": get_file_contents( + "sdc-sds-supplementary_data-encryption-private-v1.pem" + ), + }, + } +} + + class IntegrationTestCase(unittest.TestCase): # pylint: disable=too-many-public-methods def setUp(self): # Cache for requests @@ -43,6 +80,7 @@ def setUp(self): self.redirect_url = None self.last_response_headers = None self.last_cookie = None + self.key_store = None # Perform setup steps self._set_up_app() @@ -68,43 +106,10 @@ def _set_up_app(self, setting_overrides=None): return_value=(Mock(), "test-project-id"), ): self._application = create_app(overrides) - self._key_store = KeyStore( - { - "keys": { - EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID: { - "purpose": KEY_PURPOSE_AUTHENTICATION, - "type": "private", - "value": get_file_contents( - "sdc-rrm-authentication-signing-private-v1.pem" - ), - }, - SR_USER_AUTHENTICATION_PUBLIC_KEY_KID: { - "purpose": KEY_PURPOSE_AUTHENTICATION, - "type": "public", - "value": get_file_contents( - "sdc-sr-authentication-encryption-public-v1.pem" - ), - }, - EQ_SUBMISSION_SDX_PRIVATE_KEY: { - "purpose": KEY_PURPOSE_SUBMISSION, - "type": "private", - "value": get_file_contents( - "sdc-sdx-submission-encryption-private-v1.pem" - ), - }, - EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY: { - "purpose": KEY_PURPOSE_SUBMISSION, - "type": "public", - "value": get_file_contents( - "sdc-sr-submission-signing-private-v1.pem" - ), - }, - } - } - ) + self.key_store = KeyStore(KEYS_DICT) self.token_generator = TokenGenerator( - self._key_store, + self.key_store, EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID, SR_USER_AUTHENTICATION_PUBLIC_KEY_KID, ) @@ -127,6 +132,19 @@ def launchSurvey(self, schema_name="test_dates", **payload_kwargs): self.get(f"/session?token={token}") + def launchSupplementaryDataSurvey( + self, schema_name="test_supplementary_data", **payload_kwargs + ): + """ + Launch a survey as an authenticated user and follow re-directs + :param schema_name: The name of the schema to load + """ + token = self.token_generator.create_supplementary_data_token( + schema_name=schema_name, **payload_kwargs + ) + + self.get(f"/session?token={token}") + def launchSurveyV2( self, theme="default", schema_name="test_dates", **payload_kwargs ): @@ -249,6 +267,9 @@ def options(self, url, **kwargs): def getLinkById(self, identifier: str): return self.getHtmlSoup().find("a", id=identifier) + def getLinksByAttribute(self, attributes: dict[str, str]): + return [tag["href"] for tag in self.getHtmlSoup().findAll("a", attributes)] + def getSignOutButton(self): return self.getHtmlSoup().find("a", {"data-qa": "btn-save-sign-out"}) diff --git a/tests/integration/questionnaire/__init__.py b/tests/integration/questionnaire/__init__.py index f655dc2a3f..b5da4c7e6e 100644 --- a/tests/integration/questionnaire/__init__.py +++ b/tests/integration/questionnaire/__init__.py @@ -27,3 +27,9 @@ def get_link(self, action, position): selector = f"[data-qa='list-item-{action}-{position}-link']" selected = self.getHtmlSoup().select(selector) return selected[0].get("href") + + def get_list_item_change_link(self, answer_id, list_item_id): + """change link on calculated summary for list item""" + selector = f"[data-qa='{answer_id}-{list_item_id}-edit']" + selected = self.getHtmlSoup().select(selector) + return selected[0].get("href") diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index c0d4ac7155..6bad6f9cc3 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireCalculatedSummary(QuestionnaireTestCase): @@ -186,3 +186,110 @@ def test_calculated_summary_value_sources_across_sections_repeating(self): } ) self.assertInBody("Enter an answer more than or equal to £60.00") + + def test_calculated_summary_repeating_answers_only(self): + """ + Tests a calculated summary with a dynamic answer source resolving to a list of repeating answers + """ + self.launchSurvey("test_new_calculated_summary_repeating_answers_only") + + self.post({"any-transport-answer": "Yes"}) + self.post({"transport-name": "Bus"}) + self.post({"list-collector-answer": "Yes"}) + self.post({"transport-name": "Tube"}) + + # get the ids before finishing the collector + list_item_ids = self.get_list_item_ids() + assert len(list_item_ids) == 2 + self.post({"list-collector-answer": "No"}) + + self.post( + { + f"cost-of-transport-{list_item_ids[0]}": "100", + f"cost-of-transport-{list_item_ids[1]}": "200", + } + ) + self.assertInBody( + "We calculate the total monthly spending on public transport to be £300.00. Is this correct?" + ) + + def test_new_calculated_summary_repeating_blocks(self): + """ + Tests a calculated summary with a repeating block answer id source resolving to a list of answers + """ + self.launchSurvey("test_new_calculated_summary_repeating_blocks") + self.post({"answer-car": "100"}) + self.post({"answer-skip": "No"}) + self.post({"list-collector-answer": "Yes"}) + self.post({"transport-name": "Bus"}) + self.post( + { + "transport-company": "First", + "transport-cost": "30", + "transport-additional-cost": "5", + } + ) + self.post({"transport-count": "10"}) + self.post({"list-collector-answer": "Yes"}) + self.post({"transport-name": "Plane"}) + self.post( + { + "transport-company": "EasyJet", + "transport-cost": "0", + "transport-additional-cost": "265", + } + ) + self.post({"transport-count": "2"}) + list_item_ids = self.get_list_item_ids() + self.post({"list-collector-answer": "No"}) + self.assertInBody( + "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?" + ) + self.post() + self.assertInBody( + "We calculate the total journeys made per month to be 12. Is this correct?" + ) + + # check that using a change link and editing an answer takes you straight back to the relevant calculated summary + change_link = self.get_list_item_change_link( + "transport-count", list_item_ids[1] + ) + self.get(change_link) + self.post({"transport-count": "4"}) + self.assertInUrl("/calculated-summary-count/") + self.assertInBody( + "We calculate the total journeys made per month to be 14. Is this correct?" + ) + self.previous() + + # likewise for the other calculated summary + change_link = self.get_list_item_change_link("transport-cost", list_item_ids[0]) + self.get(change_link) + self.post( + { + "transport-company": "First", + "transport-cost": "300", + "transport-additional-cost": "50", + } + ) + self.assertInUrl("/calculated-summary-spending/") + self.assertInBody( + "We calculate the total monthly expenditure on transport to be £715.00. Is this correct?" + ) + + # check that removing the list collector from the path updates the calculated summary correctly + self.previous() + self.previous() + self.post({"answer-skip": "Yes"}) + + # calculated summary count should now not be on the path + self.assertInUrl("/calculated-summary-spending/") + self.assertInBody( + "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?" + ) + # no list items should be there + self.assertNotInBody("Give details of your expenditure travelling by") + self.post() + # should be absent from section summary too + self.assertInUrl("/sections/section-1") + self.assertNotInBody("Name of transport") diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py new file mode 100644 index 0000000000..4c8cebeccf --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py @@ -0,0 +1,476 @@ +from datetime import date + +from ..integration_test_case import IntegrationTestCase + +# pylint: disable=too-many-public-methods + + +class TestQuestionnaireListCollectorRepeatingBlocks(IntegrationTestCase): + def launch_repeating_blocks_test_survey(self): + self.launchSurvey("test_list_collector_repeating_blocks_section_summary") + self.post({"responsible-party-answer": "Yes"}) + + def add_company(self, company_name: str): + self.assertInUrl("/companies/add-company/") + self.post({"company-or-branch-name": company_name}) + + def add_company_from_list_collector( + self, company_name: str, is_driving: bool = False + ): + if is_driving: + self.assertInUrl("/questionnaire/any-companies-or-branches/") + self.post({"any-companies-or-branches-answer": "Yes"}) + else: + self.assertInUrl("/questionnaire/any-other-companies-or-branches/") + self.post({"any-other-companies-or-branches-answer": "Yes"}) + self.add_company(company_name) + + def post_repeating_block_1(self, registration_number: int, registration_date: date): + self.assertInUrl("/companies/") + self.assertInUrl("/companies-repeating-block-1/") + self.post( + { + "registration-number": registration_number, + "registration-date-day": registration_date.day, + "registration-date-month": registration_date.month, + "registration-date-year": registration_date.year, + } + ) + + def post_repeating_block_2(self, trader_uk: str, trader_eu: str | None = None): + self.assertInUrl("/companies/") + self.assertInUrl("/companies-repeating-block-2/") + self.post( + { + "authorised-trader-uk-radio": trader_uk, + "authorised-trader-eu-radio": trader_eu, + } + ) + + def add_company_and_repeating_blocks( + self, + company_name: str, + registration_number: int, + registration_date: date, + trader_uk: str, + trader_eu: str | None = None, + is_driving: bool = False, + ): + self.add_company_from_list_collector( + company_name=company_name, is_driving=is_driving + ) + self.post_repeating_block_1( + registration_number=registration_number, registration_date=registration_date + ) + self.post_repeating_block_2(trader_uk=trader_uk, trader_eu=trader_eu) + + def assert_company_completed(self, company_number: int, label_number: int): + selector = f"[data-qa='list-item-{label_number}-label']" + self.assertInSelector(f"Company{company_number}", selector) + self.assertInSelector("ons-summary__item-title-icon--check", selector) + + def assert_company_incomplete(self, company_number: int, label_number: int): + selector = f"[data-qa='list-item-{label_number}-label']" + self.assertInSelector(f"Company{company_number}", selector) + self.assertNotInSelector("ons-summary__item-title-icon--check", selector) + + def assert_list_item_answers_in_summary(self): + self.assertInSelector("Company1", "[class='ons-summary__item']") + + def get_list_item_link(self, action, position): + selector = f"[data-qa='list-item-{action}-{position}-link']" + selected = self.getHtmlSoup().select(selector) + return selected[0].get("href") + + def get_link(self, selector: str): + return self.getHtmlSoup().select(selector)[0]["href"] + + def get_list_item_ids(self): + result = self.getHtmlSoup().select("[data-list-item-id]") + return [list_item["data-list-item-id"] for list_item in result] + + def click_edit_link(self, answer_id: str, position: int): + list_item_ids = self.get_list_item_ids() + selector = f"[data-qa='{answer_id}-{list_item_ids[position]}-edit']" + selected = self.getHtmlSoup().select(selector) + edit_link = selected[0]["href"] + self.get(edit_link) + + def click_add_link(self): + add_link = self.get_link("[data-qa='add-item-link']") + self.get(add_link) + + def click_cancel_link(self): + cancel_link = self.get_link("[id='cancel-and-return']") + self.get(cancel_link) + + def add_three_companies(self): + # Add first company + self.add_company_and_repeating_blocks( + company_name="Company1", + registration_number=123, + registration_date=date(2023, 1, 1), + trader_uk="Yes", + trader_eu="Yes", + is_driving=True, + ) + + # Add second company + self.add_company_and_repeating_blocks( + company_name="Company2", + registration_number=456, + registration_date=date(2023, 2, 2), + trader_uk="Yes", + ) + + # Add third company + self.add_company_and_repeating_blocks( + company_name="Company3", + registration_number=789, + registration_date=date(2023, 3, 3), + trader_uk="No", + trader_eu="Yes", + ) + + self.assert_company_completed(1, 1) + self.assert_company_completed(2, 2) + self.assert_company_completed(3, 3) + + def test_invalid_invalid_list_item_id(self): + self.launch_repeating_blocks_test_survey() + self.get( + "/questionnaire/companies/non-existing-list-item-id/companies-repeating-block-1/" + ) + self.assertStatusNotFound() + + def test_happy_path(self): + self.launch_repeating_blocks_test_survey() + + # Add some items to the list + self.add_three_companies() + + # Remove item 2 + remove_link = self.get_list_item_link("remove", 2) + self.get(remove_link) + self.assertInBody("Are you sure you want to remove this company or UK branch?") + + # Cancel + self.post({"remove-confirmation": "No"}) + self.assertEqualUrl("/questionnaire/any-other-companies-or-branches/") + + # Remove again + self.get(remove_link) + self.post({"remove-confirmation": "Yes"}) + + # Check list item 3 has moved to second position + self.assert_company_completed(3, 2) + + # Test the previous link + edit_link_1 = self.get_list_item_link("change", 1) + remove_link_1 = self.get_list_item_link("remove", 1) + + self.get(edit_link_1) + self.assertInUrl("/edit-company/") + self.previous() + self.assertEqualUrl("/questionnaire/any-other-companies-or-branches/") + + self.get(remove_link_1) + self.assertInUrl("/remove-company/") + self.previous() + self.assertEqualUrl("/questionnaire/any-other-companies-or-branches/") + + # Submit survey + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_incomplete_repeating_blocks(self): + self.launch_repeating_blocks_test_survey() + + # Add first company - only add block and first repeating block + self.add_company_from_list_collector(company_name="Company1", is_driving=True) + self.post_repeating_block_1( + registration_number=123, registration_date=date(2023, 1, 1) + ) + self.previous() # return to previous repeating block + self.previous() # return to edit block + self.previous() # return to list collector + + # Add second company - complete + self.add_company_and_repeating_blocks( + company_name="Company2", + registration_number=456, + registration_date=date(2023, 2, 2), + trader_uk="Yes", + ) + + # Add third company - only the add block + self.add_company_from_list_collector(company_name="Company3") + self.click_cancel_link() # Return to edit block + self.click_cancel_link() # Return to the list collector + + # Add fourth company - complete + self.add_company_and_repeating_blocks( + company_name="Company4", + registration_number=101, + registration_date=date(2023, 4, 4), + trader_uk="No", + ) + + # Assert completeness + self.assert_company_incomplete(1, 1) + self.assert_company_completed(2, 2) + self.assert_company_incomplete(3, 3) + self.assert_company_completed(4, 4) + + # Attempt to move along path after list collector - will route to first incomplete block of first incomplete item + self.post({"any-other-companies-or-branches-answer": "No"}) + + # Should be routed to incomplete block 2 of item 1 + self.post_repeating_block_2(trader_uk="Yes", trader_eu="Yes") + self.assert_company_completed(1, 1) + self.post({"any-other-companies-or-branches-answer": "No"}) + + # Should be routed to incomplete block 1 of item 3 + self.post_repeating_block_1( + registration_number=789, registration_date=date(2023, 3, 3) + ) + self.post_repeating_block_2(trader_uk="Yes", trader_eu="No") + self.assert_company_completed(3, 3) + + # Can now submit the survey as all items are complete + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_adding_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launch_repeating_blocks_test_survey() + + # Add first company + self.add_company_and_repeating_blocks( + company_name="Company1", + registration_number=123, + registration_date=date(2023, 1, 1), + trader_uk="Yes", + trader_eu="Yes", + is_driving=True, + ) + + # Assert list items complete + self.assert_company_completed(1, 1) + + # Navigate to section summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.assertInUrl("/sections/section-companies/") + + # Add second company - from section summary + self.click_add_link() + self.assertInUrl("?return_to=section-summary") + self.add_company(company_name="Company2") + self.post_repeating_block_1( + registration_number=456, registration_date=date(2023, 2, 2) + ) + self.post_repeating_block_2(trader_uk="Yes", trader_eu="No") + self.assert_company_completed(2, 2) + + # Navigate to the submit page summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + self.assertInUrl("/submit/") + self.click_add_link() + self.assertInUrl("?return_to=final-summary") + + # Add third company - from submit summary + self.add_company(company_name="Company3") + self.post_repeating_block_1( + registration_number=789, registration_date=date(2023, 3, 3) + ) + self.post_repeating_block_2(trader_uk="No") + self.assert_company_completed(3, 3) + + # Submit + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_removing_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launch_repeating_blocks_test_survey() + + # Add some items to the list + self.add_three_companies() + + # Navigate to section summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + + # Remove item 3 + remove_link = self.get_list_item_link("remove", 3) + self.get(remove_link) + self.assertInUrl("?return_to=section-summary") + self.assertInBody("Are you sure you want to remove this company or UK branch?") + self.post({"remove-confirmation": "Yes"}) + + # Navigate to submit summary + self.assertInUrl("/sections/section-companies/") + self.post() + + # Remove item 2 + remove_link = self.get_list_item_link("remove", 2) + self.get(remove_link) + self.assertInUrl("?return_to=final-summary") + self.assertInBody("Are you sure you want to remove this company or UK branch?") + self.post({"remove-confirmation": "Yes"}) + + # Submit + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_edit_repeating_block_from_the_summary_page_adds_the_return_to_param_to_the_url( + self, + ): + self.launch_repeating_blocks_test_survey() + + # Add some items to the list + self.add_three_companies() + + # Navigate to section summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.assertInUrl("/sections/section-companies/") + + # Edit item 3 + self.click_edit_link("registration-number", 2) + self.assertInUrl("?return_to=section-summary") + self.post_repeating_block_1( + registration_number=000, registration_date=date(2023, 5, 5) + ) + self.assertInUrl("/sections/section-companies/") + + # Remove item 2 + self.click_edit_link("authorised-trader-uk-radio", 2) + self.assertInUrl("?return_to=section-summary") + self.post_repeating_block_2(trader_uk="No", trader_eu="No") + self.assertInUrl("/sections/section-companies/") + + # Submit + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_edit_incomplete_repeating_block_from_summary_page_routes_to_next_repeating_block( + self, + ): + self.launch_repeating_blocks_test_survey() + + # add incomplete item with answers for first repeating block + self.add_company_from_list_collector(company_name="Company4", is_driving=True) + self.post_repeating_block_1( + registration_number=123, registration_date=date(2023, 1, 1) + ) + + # go back to edit block and change the name + self.click_cancel_link() + self.click_cancel_link() + self.assertInUrl("/edit-company/") + self.post({"company-or-branch-name": "Company5"}) + + # should jump straight to repeating block 2 as this is the first incomplete one + self.assertInUrl("/companies-repeating-block-2/") + + def test_adding_incomplete_list_item_from_summary_returns_to_list_collector_not_summary( + self, + ): + self.launch_repeating_blocks_test_survey() + + # Add some items to the list + self.add_three_companies() + + # Navigate to section summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.assertInUrl("/sections/section-companies/") + + # Add incomplete item from section summary add link + self.click_add_link() + self.add_company(company_name="Company4") + self.click_cancel_link() # cancel and go back to edit block + self.click_cancel_link() # cancel and go back to list collector + self.assertInUrl("/any-other-companies-or-branches/") + self.assert_company_incomplete(4, 4) + + # Complete the incomplete item + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post_repeating_block_1( + registration_number=101, registration_date=date(2023, 4, 4) + ) + self.post_repeating_block_2(trader_uk="Yes", trader_eu="Yes") + self.assert_company_completed(4, 4) + + # Navigate to submit summary + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + + # Add incomplete item from submit summary add link + self.click_add_link() + self.add_company(company_name="Company5") + self.click_cancel_link() + self.click_cancel_link() + self.assertInUrl("/any-other-companies-or-branches/") + self.assert_company_incomplete(5, 5) + + # Complete the incomplete item + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post_repeating_block_1( + registration_number=121, registration_date=date(2023, 5, 5) + ) + self.post_repeating_block_2(trader_uk="No", trader_eu="No") + self.assert_company_completed(5, 5) + + # Submit + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + self.post() + self.assertInBody( + "Thank you for completing the Test a List Collector with Repeating Blocks and Section Summary Items" + ) + + def test_edit_repeating_block_from_submit_page_returns_to_submit_page( + self, + ): + self.launch_repeating_blocks_test_survey() + + # Add some items and progress to submit page + self.add_three_companies() + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + self.post() + self.assertInUrl("/submit/") + + # click an edit link + self.click_edit_link("registration-number", 2) + + # previous link should return to submit page + self.previous() + self.assertInUrl("/submit/") diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 64379e468c..026d581d1d 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -1,17 +1,42 @@ import time from datetime import datetime, timedelta, timezone +import responses from freezegun import freeze_time +from mock.mock import patch +from sdc.crypto.key_store import KeyStore from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.services.supplementary_data import SupplementaryDataRequestFailed from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.utilities.json import json_loads -from tests.integration.integration_test_case import IntegrationTestCase +from tests.app.services.test_request_supplementary_data import TEST_SDS_URL +from tests.integration.integration_test_case import ( + EQ_SUBMISSION_SDX_PRIVATE_KEY, + EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY, + EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID, + KEYS_DICT, + SR_USER_AUTHENTICATION_PUBLIC_KEY_KID, + IntegrationTestCase, +) TIME_TO_FREEZE = datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc) EQ_SESSION_TIMEOUT_SECONDS = 45 * 60 BUSINESS_URL = ACCOUNT_SERVICE_BASE_URL SOCIAL_URL = ACCOUNT_SERVICE_BASE_URL_SOCIAL +TEST_SDS_URL = "http://localhost:5003/v1/unit_data" + +mock_supplementary_data_payload_missing_data = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", +} + +mock_supplementary_data_payload_invalid_kid_in_data = { + "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + # pylint: disable-next=line-too-long + "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJkZjg4ZmRhZDI2MTJhZTFlODA1NzExMjBlNmM2MzcxZjU1ODk2Njk3In0.lssJXsMUE3dhWtQRUt7DTaZJvx4DpNdLW98cu8g4NijYX9TFpJiOFyzPxUlpFZb-fMa4zW9q6qZofQeQTbl_Ae3QAwGhuWF7v9NMdWM1aH377byyJJyJpdqlU4t-P03evRWZqAG2HtsNE2Zn1ORXn80Dc9IRkzutgrziLI8OBIZeO6-XEgbVCapsQApWkyux7QRdFH95wfda75nVvGqTbBOYvQiMTKd8KzpH2Vl200IOqEpmrcjUCE-yqdTupzcr88hwNI2ZYdv-pTNowJw1FPODZ7V_sE4Ac-JYv3yBTDcXdz3I5-rX8i2HXqz-g3VhveZiAl9q0AgklPkaO_oNWJzjrCb7DZGL4DjiGYuOcw8OSdOpKLXwkExMlado-wigxy1IWoCzFu2E5tWpmLc0WWcjKuBgD7-4tcn059F7GcwhX2uMRESCmc39pblvseM2UnmmQnwr8GvD7gqWdFwtBsECyXQ5UXAxWLJor_MtU8lAFZxiorRcrXZJwAivroPO9iEB-1Mvt2zZFWI_vMgpJCAIpETscotDKMVCG0UMfkKckJqLnmQpvF4oYTr77w1COBX5bi-AV8UrLJ7sVVktSXOBc_KCGRpoImA5cE67hW7mFUdJi1EHA39qt0tTqZD7izpu8sSLxsiuCkfsqrd4uAedcDdQm4QGxXOPD4pxois.wfWsetB3M0x9qfw5.43Wns86lGlbHj63b0ZxE2bxBQVus6FIqelb9LfSbvopLn5oR8FM4vDEnDp_rIyvjmV9YAZJ6HAHaYaWoNyIO0EorgamrB4R3-LqInANoe9c8xLZ9wl_QpE9aWnxsmFGZUWLO3q2fVTPnwBtA_LxK8FD0vjdLL9eHGYEmPVCGVX0BJX04TVW9aoemsx9Yn3ZtfvmQHuROiB-GcA5wOSb-GvhzfplY09GQr7g7221MiYCHYimmEJyxLV5clWPXu6izzVLDyG9l2ewCifiuBLD0O1U_fPlahHTmidwHKJEAEn39biNw5E_dr8WyZ3xBvJa9dP50m0xeyN4COR-xlYcEbuDcKoqN6BnY0bMNDxQYlBO--QcPLQ6h48uTJszwzsmNIwHoi0xy5dQah7c9Nt2lpMuNt1Wix-O8JWYCqaiCKxjwt9G8kabMbzhp1n3LetWweoyV7qJTbiB13Byv6SZwMO9M.8j8wtvwBAHzqRhv5Ii9jjQ", +} class TestSession(IntegrationTestCase): @@ -30,6 +55,11 @@ def setUp(self): } ) + def assert_supplementary_data_500_page(self): + self.launchSupplementaryDataSurvey() + self.assertStatusCode(500) + self.assertInBody("Sorry, there is a problem with this service") + def test_session_expired(self): self.get("/session-expired") self.assertInBody("Sorry, you need to sign in again") @@ -88,6 +118,104 @@ def test_patch_session_expiry_extends_session(self): self.assertIn("expires_at", parsed_json) self.assertEqual(parsed_json["expires_at"], expected_expires_at) + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + ) + def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( + self, + mock_set, + mock_get, + ): + self.launchSupplementaryDataSurvey() + self.assertStatusOK() + mock_get.assert_called_once() + mock_set.assert_called_once() + + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + ) + def test_supplementary_data_is_reloaded_when_changed_sds_dataset_id_in_metadata( + self, + mock_set, + mock_get, + ): + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="first") + self.assertStatusOK() + mock_set.assert_called_once() + mock_get.assert_called_once() + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="second") + self.assertStatusOK() + self.assertEqual(mock_get.call_count, 2) + self.assertEqual(mock_set.call_count, 2) + + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + ) + def test_supplementary_data_is_not_reloaded_when_same_sds_dataset_id_in_metadata( + self, + mock_set, + mock_get, + ): + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") + self.assertStatusOK() + mock_set.assert_called_once() + mock_get.assert_called_once() + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") + self.assertStatusOK() + mock_get.assert_called_once() + mock_set.assert_called_once() + + def test_supplementary_data_raises_500_error_when_sds_api_request_fails(self): + with patch( + "app.routes.session.get_supplementary_data_v1", + side_effect=SupplementaryDataRequestFailed, + ): + self.assert_supplementary_data_500_page() + + @responses.activate + def test_supplementary_data_raises_500_error_when_supplementary_data_invalid(self): + responses.add( + responses.GET, + TEST_SDS_URL, + json=mock_supplementary_data_payload_invalid_kid_in_data, + status=200, + ) + self.assert_supplementary_data_500_page() + + @responses.activate + def test_supplementary_data_raises_500_error_when_supplementary_data_missing_data( + self, + ): + responses.add( + responses.GET, + TEST_SDS_URL, + json=mock_supplementary_data_payload_missing_data, + status=200, + ) + self.assert_supplementary_data_500_page() + + def test_supplementary_data_raises_500_error_when_missing_supplementary_data_key( + self, + ): + self.key_store = KeyStore( + { + "keys": { + k: KEYS_DICT["keys"][k] + for k in ( + EQ_USER_AUTHENTICATION_RRM_PRIVATE_KEY_KID, + SR_USER_AUTHENTICATION_PUBLIC_KEY_KID, + EQ_SUBMISSION_SDX_PRIVATE_KEY, + EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY, + ) + } + } + ) + + self.assert_supplementary_data_500_page() + class TestCensusSession(IntegrationTestCase): def setUp(self): diff --git a/tests/jwt-test-keys/sdc-sds-supplementary_data-encryption-private-v1.pem b/tests/jwt-test-keys/sdc-sds-supplementary_data-encryption-private-v1.pem new file mode 100644 index 0000000000..7105e02018 --- /dev/null +++ b/tests/jwt-test-keys/sdc-sds-supplementary_data-encryption-private-v1.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKAIBAAKCAgEAvQCr8fkBjjThO2+bQ/Y2dIsO9fRcodxiC0Gyz36NKjVc0Gqh +Kthrcyvhv+birZRGRJDi6f7i0sQ1LO+ZvJGlHD5cWZMdHvzNZTa+jVIw5DswZxzc +SZuI1/EJmCVvNOEy+Tth79IPNV3KtsKIEN044Ih/BhIYYu/mjrKfM2vddFwT1BXJ +FH5grEusaeODYhPvCSwvy5QRFiQIbm/wCP+Y8Dm3EhSPulHBBFEcy4xf+gM9fYn5 +Fww+vMnUibAGU9rZhNBDI1dV2czW7/7et7hmTtrtZsPyaqHY05Td0B2a/N7ZhyQt +zTUqbxOVKw5OgnFn6UE1vmUWYSULSPKrLig76MSITyOH1h1mTK6T5UbYyzcFBUvx +nOi7M9ddqIR57DaDJqSA6vP6ngzN8UYqr36ZOBJqpfmj+mIGQN2HhhPBrI41AVvV +YU/5bC0IHZYWjd12+bHQhf41tLUsrK53p6xEG+n0Txgp8HMhFmWoKwWXNEkNvdAE +tgH7QnObjjTV10nS09EQFgA6kYA0YwTeIiVlx9Y0J7oUCmwosMpcbaAlImlgafTh +AutTqT83Iw8gs2bCRd6h8ysHi3QiqNn6dV/QlHRHqfNBwHapHRUbBTVenwOVrzyV +VnAd5a3P574zsm/gRz/9+rBIxebQdFrre+M4DLAnzLpdqv5+y/LqH3WiNMkCAwEA +AQKCAgBRVPqpNAhRU7wgwZRFGKyyVizn9nHuTVH7mhgCZmkE4tW/8kLMlzkV5KpO +1GJzY70hQGAFZePh4wEnByxXEy3EC6nd+gqsDQmuJnK1icr0S+w2UxsQqdenZVhF +msZSMR6oVb99Xh2hT20uXGQFLc2OAe73g83utWG3wnHzxNUVf5Ig0AcpxICBZEcb +ggZFrGJOxi8DIgKATp06OP1IQgVkStHW+/YlrYyr+OO1TAD5K2/ImBkSq/hLcWb+ +oTr31tOH7b8WdDzDbvyHZlwdH0MXZ+qFMIkfDeqqkgMpzbOmYZemKhFznw9VoU2t +q4hpZbfbjm48MnAA+dnzWEoFoNa2RJMHhZsTNSuDCXXunPvWGTymonco1eqiH+zx +X5yVtMRXtpCn1vbsJ/f39DB56X0Yj+S2edAvhOHORt9RfXZ/nBM9LngOpqjKKz1v +w9Mh7g+vGv/l4BPIznbKsQZEWj5ndtoUYFPtnMA1dKLr03jhMfAzsvamF2U8IjVZ +idzgJwIkAl00ywVQswXIAa7bW/3/moXGlHFz93qYf64mROY4ASffKMkRiaeFpTW3 +nTrIy0h+F3vZxSSc2Qw55auNDRnefNr6wsa1hrYRnzIknloK/wcaIulWGIyzEW4i +mH/RWTl+pdu7zmqb0j1BMr48wbxIuusceiC4n42AJQbFIg5BSQKCAQEA4D8kHgmA +Ct9vkq+hoyl99JnfZlfDr+N0p3+7Nc4mbVsgtl604grWjg7RIEXYFzXwfVd7pG5M +P70zqUo8gIDhZGBUksr6PZ8GcAyG8RiBE+gzkhBycw5JxRvJC/3Qsu6am1hr+M7v +peQTUolgiNekIPncJbP/j/5CQiuMlCyFk9GHWvFhaqq2pRrkkUYZ6zplzPePlnB+ +CkN0+q6HWOAXh4wCfkaUwb+xFFWiRxqYxoSL6OnPcSrXVG3BEIztMbuC9AzdPhPM +M1zaiYQf6mxeT4XOQcBMdcumxxltCUH5tiuGdYOHtB6Biq+if/p7VzjxswrejAnb +7jqlSLJZENajDwKCAQEA18Pym+HHz0HIdUuCcky/f1tyAnNijYoZP2En4ztKwcj6 +jp5CRgQC+ufdkrpJKWqhv3kHPCZvsKk066Rr03wTxvPMhb93xH4VBZ+bf58s7bUR +4KdJhJEf8DJbJiTBDno3ddhwtSXBfW6Eoyy6x6X6Hkw4DZm0DGaFN8vfJAZ9OprJ +q4nO0NYP1A0aYzE7BDgq5Tkr03ecNHLzIqP8zIVkkn6ewbUUG7wGbjRV7XSOpvum +GUjy04eKMIIZkWztIfJS7IuzXa2wK3I8lDu0wrmrm17lVjpZ9/ja8KaTVkgabL2P +Lg25NMk1qj0AQj88RrVkmGRWpnbuc1qJ4DOnA8zKpwKCAQA3bYP45LJAfb/vSvgy +A0R93DbK7jCRXjBsYnccsorvBtJMIZamNLWZwXHRf1INUqjR4njOSPER5CtL0eyo +erK7g9ADxKYb6x3FPmNwXnUxPXjZxrTzWXnEfbyw+RjH0ZBni3CMvGGh6IEaKpiw +2lRYTkorC5XEur0X6/nAeky+H9FMGlPQ8Mdagg4zFle7u+CDzEEylzWgRdI5UEBm +KGXIfEP1gG6ugTo843nMB3fxwbtvY7OBrmwxEzvgYmUSoN2agz+AY5Zar73Ytc7J +u+WH1HQJ7oU3rJHZrqAz5JnbfGCs1UkKrWupowYQihJImeusLKibhqhU9yv5jxPS +xKrjAoIBAFJSvAVH3wGv+rjuJ4ZOzB3emSBgP/D7COkKu7pSTBKmCRtTPLwUGcL7 +pqmuE+4Odkpk9iK4E5NW7A8ge9eEFtOo/5bkV+ELrh+oJx9Jb03+8SRDD6TZ7lKq +E+b4zQQmE3UOMOqczjd6bHcJwPYd2NGoiRZ/V5gHobqJOck4BJ3QozOk79j0Y7On +kDLafMb+Wzd8WcFkeJ/2X9gOs4yhNJ9EWnRUD6kJU3bG1yYze54wk84/7A5TP6GE +chbvdYanO4ZvQu9yLq5U9tIj+bL2PoiYa24780nOlFKPa9XWyuZEaRXMPKbsQmKC +xc+A6xGbchdG6Vy4MgCnQcXeT1H+2C8CggEBAMvwlPifgjCk2v7ejVPvA0K63aii +l8Acv6wtvELeQGHLg/ZmzbHNZG9mExhuHASeIEn/1p64dAUQ6NTvHq78bgZ4sa2M +U6surGKebDwcbgMZe5lN6jZhem9DHWyXCLco3FHWFj6bNMuBfOdUemVi4bEukcrM +ItfML9fhBIKva5aAWej+lEQydTImhLOfgXbEse7ic+ZWVddpuqNCq137Mcbc1q0t +QChlDkY6+uOdYa3xmSCHtDc64ymzBR6XAQHQnX9fqfLWdz3Ytk9s+H34Hrn6mJZ/ +/MRZOZimld2tdshUdEQ+Kaf4wVLcEwJuW5/NeMbba+iIA/sPqHRkYmVs1v8= +-----END RSA PRIVATE KEY----- From 141cc020cec9acc0d4a2747b4f310bbd5f6113cf Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Tue, 25 Jul 2023 15:22:58 +0100 Subject: [PATCH 253/567] Using supplementary data non list items (#1169) * Add SupplementaryDataStore dependency * Using Supplementary data non list items * Testing Supplementary data non list items * Additional supplementary data tests * Add test for submitted response * Test for section 1 summary * Temporarily disable tests --- app/data_models/__init__.py | 2 + app/forms/questionnaire_form.py | 21 +- app/questionnaire/path_finder.py | 5 + app/questionnaire/placeholder_parser.py | 6 +- app/questionnaire/placeholder_renderer.py | 7 +- app/questionnaire/router.py | 11 +- app/questionnaire/rules/rule_evaluator.py | 10 +- app/questionnaire/value_source_resolver.py | 16 +- app/questionnaire/variants.py | 11 + app/routes/dump.py | 2 + app/routes/flush.py | 1 + app/routes/questionnaire.py | 3 + app/submitter/convert_payload_0_0_1.py | 10 +- app/submitter/converter_v2.py | 1 + .../contexts/calculated_summary_context.py | 13 +- app/views/contexts/context.py | 5 + .../grand_calculated_summary_context.py | 1 + app/views/contexts/preview_context.py | 10 +- app/views/contexts/section_preview_context.py | 9 +- app/views/contexts/section_summary_context.py | 11 +- .../contexts/submit_questionnaire_context.py | 1 + app/views/contexts/summary/block.py | 14 +- .../summary/calculated_summary_block.py | 9 +- app/views/contexts/summary/group.py | 16 +- .../contexts/summary/list_collector_block.py | 7 +- app/views/contexts/summary/question.py | 10 +- app/views/contexts/summary_context.py | 5 +- .../view_submitted_response_context.py | 1 + app/views/handlers/block.py | 2 + app/views/handlers/calculation_summary.py | 1 + app/views/handlers/confirm_email.py | 1 + app/views/handlers/content.py | 1 + app/views/handlers/feedback.py | 1 + app/views/handlers/individual_response.py | 3 + app/views/handlers/list_collector.py | 1 + app/views/handlers/question.py | 4 + app/views/handlers/section.py | 2 + app/views/handlers/submit_questionnaire.py | 2 + app/views/handlers/view_preview_questions.py | 1 + schemas/test/en/test_supplementary_data.json | 900 +++++++++++++++++- .../supplementary_data_no_repeat.json | 30 +- .../supplementary_data_no_repeat_v2.json | 23 + ...metadata_response_non_repeating_items.json | 26 + ...set_metadata_response_repeating_items.json | 27 + scripts/mock_sds_endpoint.py | 33 +- tests/app/conftest.py | 58 ++ tests/app/data_model/conftest.py | 54 -- tests/app/forms/field_handlers/conftest.py | 4 +- tests/app/forms/test_field_factory.py | 4 +- tests/app/forms/test_questionnaire_form.py | 438 +++++---- tests/app/questionnaire/conftest.py | 18 +- .../rules/test_rule_evaluator.py | 51 +- .../test_dynamic_answer_options.py | 9 +- tests/app/questionnaire/test_path_finder.py | 220 ++++- .../questionnaire/test_placeholder_parser.py | 27 +- .../test_placeholder_renderer.py | 8 +- .../test_questionnaire_store_updater.py | 16 +- tests/app/questionnaire/test_router.py | 3 + tests/app/questionnaire/test_schema_utils.py | 12 +- .../test_value_source_resolver.py | 69 +- .../submitter/test_convert_payload_0_0_1.py | 1 + .../app/views/contexts/summary/test_block.py | 1 + .../views/contexts/summary/test_question.py | 114 ++- .../test_calculated_summary_context.py | 8 + .../test_grand_calculated_summary_context.py | 2 + tests/app/views/contexts/test_hub_context.py | 53 +- tests/app/views/contexts/test_list_context.py | 30 +- .../views/contexts/test_preview_context.py | 3 + .../contexts/test_section_summary_context.py | 32 +- .../app/views/contexts/test_submit_context.py | 16 +- .../views/contexts/test_summary_context.py | 8 +- tests/functional/generate_pages.py | 17 + tests/functional/jwt_helper.js | 35 +- .../supplementary_data_non_list_items.spec.js | 175 ++++ tests/functional/wdio.conf.js | 6 + tests/integration/create_token.py | 16 +- ...naire_supplementary_data_non_list_items.py | 60 ++ 77 files changed, 2439 insertions(+), 405 deletions(-) create mode 100644 scripts/mock_data/supplementary_data_no_repeat_v2.json create mode 100644 scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json create mode 100644 scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json create mode 100644 tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js create mode 100644 tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py diff --git a/app/data_models/__init__.py b/app/data_models/__init__.py index 331a6fdd4d..d081cdbe90 100644 --- a/app/data_models/__init__.py +++ b/app/data_models/__init__.py @@ -6,6 +6,7 @@ ListStore, ProgressStore, QuestionnaireStore, + SupplementaryDataStore, ) from .session_data import SessionData from .session_store import SessionStore @@ -21,4 +22,5 @@ "QuestionnaireStore", "SessionData", "SessionStore", + "SupplementaryDataStore", ] diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index d2d971d307..35d60251d3 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -12,7 +12,13 @@ from werkzeug.datastructures import ImmutableMultiDict, MultiDict from wtforms import validators -from app.data_models import AnswerStore, AnswerValueTypes, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + AnswerValueTypes, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.forms import error_messages from app.forms.field_handlers import DateHandler, FieldHandler, get_field_handler @@ -51,6 +57,7 @@ def __init__( response_metadata: MutableMapping, location: Union[None, Location, RelationshipLocation], progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, **kwargs: Union[MultiDict, Mapping, None], ): self.schema = schema @@ -64,6 +71,7 @@ def __init__( self.options_with_detail_answer: dict = {} self.question_title = self.question.get("title", "") self.progress_store = progress_store + self.supplementary_data_store = supplementary_data_store self.value_source_resolver = ValueSourceResolver( answer_store=self.answer_store, schema=self.schema, @@ -73,6 +81,7 @@ def __init__( location=self.location, list_item_id=self.location.list_item_id if self.location else None, progress_store=self.progress_store, + supplementary_data_store=self.supplementary_data_store, ) super().__init__(**kwargs) @@ -318,6 +327,7 @@ def _get_period_range_for_single_date( list_item_id=list_item_id, escape_answer_values=False, progress_store=self.progress_store, + supplementary_data_store=self.supplementary_data_store, ) rule_evaluator = RuleEvaluator( @@ -328,6 +338,7 @@ def _get_period_range_for_single_date( response_metadata=self.response_metadata, location=self.location, progress_store=self.progress_store, + supplementary_data_store=self.supplementary_data_store, ) handler = DateHandler( @@ -474,6 +485,7 @@ def get_answer_fields( response_metadata: MutableMapping, location: LocationType | None, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None @@ -491,6 +503,7 @@ def get_answer_fields( progress_store=progress_store, metadata=metadata, response_metadata=response_metadata, + supplementary_data_store=supplementary_data_store, ), data=question, ) @@ -512,6 +525,7 @@ def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResol routing_path_block_ids=block_ids, assess_routing_path=False, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) rule_evaluator = RuleEvaluator( @@ -522,6 +536,7 @@ def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResol response_metadata=response_metadata, location=location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) answer_fields = {} @@ -620,6 +635,7 @@ def _clear_detail_answer_field( def generate_form( + *, schema: QuestionnaireSchema, question_schema: QuestionSchemaType, answer_store: AnswerStore, @@ -627,6 +643,7 @@ def generate_form( metadata: MetadataProxy | None, response_metadata: MutableMapping, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, location: LocationType | None = None, data: dict[str, Any] | None = None, form_data: MultiDict | None = None, @@ -648,6 +665,7 @@ class DynamicForm(QuestionnaireForm): response_metadata, location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) for answer_id, field in answer_fields.items(): @@ -664,4 +682,5 @@ class DynamicForm(QuestionnaireForm): data=data, formdata=form_data, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 10348f179d..ba4d778de6 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -6,6 +6,7 @@ from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath @@ -22,6 +23,7 @@ def __init__( progress_store: ProgressStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, + supplementary_data_store: SupplementaryDataStore, ): self.answer_store = answer_store self.metadata = metadata @@ -29,6 +31,7 @@ def __init__( self.schema = schema self.progress_store = progress_store self.list_store = list_store + self.supplementary_data_store = supplementary_data_store def routing_path( self, section_id: str, list_item_id: str | None = None @@ -201,6 +204,7 @@ def _evaluate_routing_rules( progress_store=self.progress_store, location=this_location, routing_path_block_ids=routing_path_block_ids, + supplementary_data_store=self.supplementary_data_store, ) for rule in routing_rules: rule_valid = ( @@ -254,6 +258,7 @@ def evaluate_skip_conditions( progress_store=self.progress_store, location=current_location, routing_path_block_ids=routing_path_block_ids, + supplementary_data_store=self.supplementary_data_store, ) return when_rule_evaluator.evaluate(skip_conditions["when"]) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 72e54a49b3..e95edba07b 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -5,7 +5,7 @@ from ordered_set import OrderedSet -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy @@ -47,6 +47,7 @@ def __init__( schema: QuestionnaireSchema, renderer: PlaceholderRenderer, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, list_item_id: str | None = None, location: LocationType | None = None, placeholder_preview_mode: bool | None = False, @@ -63,6 +64,7 @@ def __init__( self._schema = schema self._location = location self._progress_store = progress_store + self._supplementary_data_store = supplementary_data_store self._placeholder_preview_mode = placeholder_preview_mode self._path_finder = pf.PathFinder( @@ -72,6 +74,7 @@ def __init__( progress_store=self._progress_store, metadata=self._metadata, response_metadata=self._response_metadata, + supplementary_data_store=self._supplementary_data_store, ) self._value_source_resolver = self._get_value_source_resolver() @@ -122,6 +125,7 @@ def _get_value_source_resolver( assess_routing_path=False, routing_path_block_ids=routing_path_block_ids, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, ) def _parse_placeholder(self, placeholder: Mapping) -> Any: diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 8b0c1fa94a..838e91a084 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -3,7 +3,7 @@ from jsonpointer import resolve_pointer, set_pointer -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer import AnswerValueTypes from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore @@ -30,6 +30,7 @@ def __init__( response_metadata: MutableMapping, schema: QuestionnaireSchema, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, location: LocationType | None = None, placeholder_preview_mode: bool | None = False, ): @@ -42,6 +43,7 @@ def __init__( self._schema = schema self._location = location self._progress_store = progress_store + self._supplementary_data_store = supplementary_data_store def render_pointer( self, @@ -88,6 +90,7 @@ def render_placeholder( renderer=self, progress_store=self._progress_store, placeholder_preview_mode=self._placeholder_preview_mode, + supplementary_data_store=self._supplementary_data_store, ) placeholder_data = QuestionnaireSchema.get_mutable_deepcopy(placeholder_data) @@ -144,6 +147,7 @@ def render( renderer=self, placeholder_preview_mode=self._placeholder_preview_mode, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, ) for pointer in pointers: @@ -216,6 +220,7 @@ def resolve_dynamic_answers( renderer=self, placeholder_preview_mode=self._placeholder_preview_mode, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, ) pointers = find_pointers_containing(answer, "placeholders") diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index ea4be61b7f..7ed5ff2948 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -2,7 +2,12 @@ from flask import url_for -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressKeyType from app.questionnaire import QuestionnaireSchema @@ -22,6 +27,7 @@ def __init__( progress_store: ProgressStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, + supplementary_data_store: SupplementaryDataStore, ): self._schema = schema self._answer_store = answer_store @@ -29,6 +35,7 @@ def __init__( self._progress_store = progress_store self._metadata = metadata self._response_metadata = response_metadata + self._supplementary_data_store = supplementary_data_store self._path_finder = PathFinder( self._schema, @@ -37,6 +44,7 @@ def __init__( self._progress_store, self._metadata, self._response_metadata, + self._supplementary_data_store, ) @property @@ -565,6 +573,7 @@ def _is_section_enabled(self, section: Mapping) -> bool: progress_store=self._progress_store, location=Location(section_id=section_id), routing_path_block_ids=routing_path_block_ids, + supplementary_data_store=self._supplementary_data_store, ) return bool(when_rule_evaluator.evaluate(enabled["when"])) diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 0677d86bbc..438898b075 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -3,7 +3,12 @@ from decimal import Decimal from typing import Generator, Iterable, MutableMapping, Sequence, TypeAlias -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer @@ -31,6 +36,7 @@ class RuleEvaluator: response_metadata: MutableMapping location: LocationType | None progress_store: ProgressStore + supplementary_data_store: SupplementaryDataStore routing_path_block_ids: Iterable[str] | None = None language: str = DEFAULT_LANGUAGE_CODE @@ -48,6 +54,7 @@ def __post_init__(self) -> None: routing_path_block_ids=self.routing_path_block_ids, progress_store=self.progress_store, use_default_answer=True, + supplementary_data_store=self.supplementary_data_store, ) renderer: PlaceholderRenderer = PlaceholderRenderer( @@ -59,6 +66,7 @@ def __post_init__(self) -> None: schema=self.schema, location=self.location, progress_store=self.progress_store, + supplementary_data_store=self.supplementary_data_store, ) self.operations = Operations( language=self.language, schema=self.schema, renderer=renderer diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index bc89cbaccf..c9dd501529 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -5,7 +5,7 @@ from markupsafe import Markup from werkzeug.datastructures import ImmutableDict -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer import ( AnswerValueEscapedTypes, AnswerValueTypes, @@ -19,7 +19,7 @@ from app.questionnaire.rules import rule_evaluator from app.utilities.types import LocationType -ValueSourceTypes: TypeAlias = None | str | int | Decimal | list +ValueSourceTypes: TypeAlias = None | str | int | Decimal | list | dict ValueSourceEscapedTypes: TypeAlias = Markup | list[Markup] IntOrDecimal: TypeAlias = int | Decimal ResolvedAnswerList: TypeAlias = list[AnswerValueTypes | AnswerValueEscapedTypes | None] @@ -35,6 +35,7 @@ class ValueSourceResolver: location: LocationType | None list_item_id: str | None progress_store: ProgressStore + supplementary_data_store: SupplementaryDataStore routing_path_block_ids: Iterable[str] | None = None use_default_answer: bool = False escape_answer_values: bool = False @@ -262,6 +263,7 @@ def _resolve_calculated_summary_value_source( self.metadata, self.response_metadata, progress_store=self.progress_store, + supplementary_data_store=self.supplementary_data_store, location=self.location, routing_path_block_ids=self.routing_path_block_ids, ) @@ -281,6 +283,15 @@ def _resolve_location_source(self, value_source: Mapping) -> str | None: def _resolve_response_metadata_source(self, value_source: Mapping) -> str | None: return self.response_metadata.get(value_source.get("identifier")) + def _resolve_supplementary_data_source( + self, value_source: Mapping + ) -> ValueSourceTypes: + return self.supplementary_data_store.get_data( + identifier=value_source["identifier"], + selectors=value_source.get("selectors"), + list_item_id=value_source.get("list_item_id"), + ) + @staticmethod def get_calculation_operator( calculation_type: str, @@ -307,6 +318,7 @@ def resolve( "location": self._resolve_location_source, "response_metadata": self._resolve_response_metadata_source, "progress": self._resolve_progress_value_source, + "supplementary_data": self._resolve_supplementary_data_source, } return resolve_method_mapping[source](value_source) diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index 1cdfaac3ed..bcfa245d8e 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -6,6 +6,7 @@ from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.utilities.types import LocationType @@ -23,6 +24,7 @@ def choose_variant( # type: ignore single_key: str, current_location: LocationType, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> dict: if block.get(single_key): # Type ignore: the key passed in will be for a dictionary @@ -38,6 +40,7 @@ def choose_variant( # type: ignore response_metadata, location=current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) if when_rule_evaluator.evaluate(when_rules): @@ -54,6 +57,7 @@ def choose_question_to_display( list_store: ListStore, current_location: LocationType, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> dict: return choose_variant( block, @@ -66,6 +70,7 @@ def choose_question_to_display( single_key="question", current_location=current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) @@ -78,6 +83,7 @@ def choose_content_to_display( list_store: ListStore, current_location: LocationType, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> dict: return choose_variant( block, @@ -90,6 +96,7 @@ def choose_content_to_display( single_key="content", current_location=current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) @@ -102,6 +109,7 @@ def transform_variants( list_store: ListStore, current_location: LocationType, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> ImmutableDict: output_block = dict(block) if "question_variants" in block: @@ -114,6 +122,7 @@ def transform_variants( list_store, current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) output_block.pop("question_variants", None) output_block.pop("question", None) @@ -130,6 +139,7 @@ def transform_variants( list_store, current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) output_block.pop("content_variants", None) output_block.pop("content", None) @@ -149,6 +159,7 @@ def transform_variants( list_store, current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) return ImmutableDict(output_block) diff --git a/app/routes/dump.py b/app/routes/dump.py index c22765dd50..cfafa6cd12 100644 --- a/app/routes/dump.py +++ b/app/routes/dump.py @@ -39,6 +39,7 @@ def dump_routing( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) response = [ @@ -68,6 +69,7 @@ def dump_submission( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) routing_path = router.full_routing_path() diff --git a/app/routes/flush.py b/app/routes/flush.py index e99cbb6b89..efc5b4c25a 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -87,6 +87,7 @@ def _submit_data(user: User) -> bool: progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) full_routing_path = router.full_routing_path() diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 7f54dfce8d..af9f21508c 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -163,6 +163,7 @@ def get_questionnaire( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) if not router.can_access_hub(): @@ -188,6 +189,7 @@ def get_questionnaire( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) context = hub_context( survey_complete=router.is_questionnaire_complete, @@ -246,6 +248,7 @@ def get_preview( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) except PreviewNotEnabledException as exc: raise NotFound from exc diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index 4e1aeada96..9932ed2533 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -4,7 +4,12 @@ from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.answer import AnswerValueTypes, ListAnswer from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema @@ -25,6 +30,7 @@ def convert_answers_to_payload_0_0_1( schema: QuestionnaireSchema, full_routing_path: Iterable[RoutingPath], progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, ) -> OrderedDict[str, Any]: """ Convert answers into the data format below @@ -40,6 +46,7 @@ def convert_answers_to_payload_0_0_1( :param schema: QuestionnaireSchema class with populated schema json :param full_routing_path: a list of section routing paths followed in the questionnaire :param progress_store: progress store + :param supplementary_data_store: supplementary data store :return: data in a formatted form """ data = OrderedDict() @@ -68,6 +75,7 @@ def convert_answers_to_payload_0_0_1( list_store, current_location=current_location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) for answer_id, answer in schema.get_answers_for_question_by_id( question diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index e7405552f1..70a79f3808 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -131,6 +131,7 @@ def get_payload_data( schema=schema, full_routing_path=full_routing_path, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) if schema.json["data_version"] == "0.0.3": diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 2c63807cf8..9cbbb5bd43 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -2,7 +2,12 @@ from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.jinja_filters import ( format_number, @@ -37,6 +42,7 @@ def __init__( response_metadata: MutableMapping, routing_path: RoutingPath, current_location: LocationType, + supplementary_data_store: SupplementaryDataStore, return_to: str | None = None, return_to_block_id: str | None = None, ) -> None: @@ -48,6 +54,7 @@ def __init__( progress_store, metadata, response_metadata, + supplementary_data_store, ) self.routing_path_block_ids = routing_path.block_ids self.current_location = current_location @@ -81,6 +88,7 @@ def build_groups_for_section( location=self.current_location, language=self._language, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, return_to=return_to, return_to_block_id=return_to_block_id, summary_type="CalculatedSummary", @@ -197,6 +205,7 @@ def _remove_unwanted_questions_answers( self._list_store, self.current_location, self._progress_store, + self._supplementary_data_store, ) transformed_block: dict = QuestionnaireSchema.get_mutable_deepcopy( block_to_transform @@ -245,6 +254,7 @@ def _get_evaluated_total( routing_path_block_ids=routing_path_block_ids, location=self.current_location, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, ) # Type ignore: in the case of a calculated summation it will always be a numeric type calculated_total: NumericType = evaluate_calculated_summary.evaluate(calculation) # type: ignore @@ -279,6 +289,7 @@ def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: self._list_store, current_location=self.current_location, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, ) for answer in question["answers"]: if not answer_format["type"]: diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index a0033abd94..0ad55db4a5 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -5,6 +5,7 @@ from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.router import Router @@ -20,6 +21,7 @@ def __init__( progress_store: ProgressStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, + supplementary_data_store: SupplementaryDataStore, placeholder_preview_mode: bool = False, ) -> None: self._language = language @@ -27,6 +29,7 @@ def __init__( self._answer_store = answer_store self._list_store = list_store self._progress_store = progress_store + self._supplementary_data_store = supplementary_data_store self._metadata = metadata self._response_metadata = response_metadata self._placeholder_preview_mode = placeholder_preview_mode @@ -38,6 +41,7 @@ def __init__( progress_store=self._progress_store, metadata=self._metadata, response_metadata=self._response_metadata, + supplementary_data_store=supplementary_data_store, ) self._placeholder_renderer = PlaceholderRenderer( @@ -49,4 +53,5 @@ def __init__( schema=self._schema, progress_store=self._progress_store, placeholder_preview_mode=self._placeholder_preview_mode, + supplementary_data_store=supplementary_data_store, ) diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 0c45b0ddae..03a019192b 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -85,6 +85,7 @@ def build_groups_for_section( location=self.current_location, language=self._language, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, return_to="grand-calculated-summary", return_to_block_id=return_to_block_id, summary_type="GrandCalculatedSummary", diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py index 057cbe73d6..cf7990511c 100644 --- a/app/views/contexts/preview_context.py +++ b/app/views/contexts/preview_context.py @@ -2,7 +2,12 @@ from flask_babel import lazy_gettext -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.views.contexts import Context @@ -23,6 +28,7 @@ def __init__( progress_store: ProgressStore, metadata: Optional[MetadataProxy], response_metadata: MutableMapping[str, Union[str, int, list]], + supplementary_data_store: SupplementaryDataStore, ): if not schema.preview_enabled: raise PreviewNotEnabledException @@ -35,6 +41,7 @@ def __init__( progress_store, metadata, response_metadata, + supplementary_data_store, placeholder_preview_mode=True, ) @@ -55,6 +62,7 @@ def build_all_sections(self) -> Generator[dict, None, None]: answer_store=self._answer_store, list_store=self._list_store, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, metadata=self._metadata, response_metadata=self._response_metadata, section_id=section_id, diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py index 5a139ff376..c4048c2577 100644 --- a/app/views/contexts/section_preview_context.py +++ b/app/views/contexts/section_preview_context.py @@ -1,6 +1,11 @@ from typing import MutableMapping, Optional -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.views.contexts.context import Context @@ -16,6 +21,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, metadata: Optional[MetadataProxy], response_metadata: MutableMapping, section_id: str, @@ -28,6 +34,7 @@ def __init__( progress_store, metadata, response_metadata, + supplementary_data_store, placeholder_preview_mode=True, ) self._section_id = section_id diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 5f69365622..86c3e05a6c 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -3,7 +3,12 @@ from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.questionnaire import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content @@ -27,6 +32,7 @@ def __init__( response_metadata: MutableMapping, routing_path: RoutingPath, current_location: LocationType, + supplementary_data_store: SupplementaryDataStore, ) -> None: super().__init__( language, @@ -36,6 +42,7 @@ def __init__( progress_store, metadata, response_metadata, + supplementary_data_store, ) self.routing_path = routing_path self.current_location = current_location @@ -147,6 +154,7 @@ def build_summary( location=self.current_location, language=self._language, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, return_to=return_to, return_to_block_id=None, view_submitted_response=view_submitted_response, @@ -181,6 +189,7 @@ def _custom_summary_elements( schema=self._schema, location=self.current_location, language=self._language, + supplementary_data_store=self._supplementary_data_store, return_to="section-summary", ) yield list_collector_block.list_summary_element(summary_element) diff --git a/app/views/contexts/submit_questionnaire_context.py b/app/views/contexts/submit_questionnaire_context.py index 736bc8ccb6..6729aa6284 100644 --- a/app/views/contexts/submit_questionnaire_context.py +++ b/app/views/contexts/submit_questionnaire_context.py @@ -40,6 +40,7 @@ def __call__(self) -> dict[str, Union[str, dict]]: metadata=self._metadata, response_metadata=self._response_metadata, view_submitted_response=False, + supplementary_data_store=self._supplementary_data_store, ) context["summary"] = summary_context( answers_are_editable=True, return_to="final-summary" diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 384454aa07..78e8109f87 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -2,7 +2,12 @@ from jsonpointer import resolve_pointer -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -27,6 +32,7 @@ def __init__( return_to: str | None, return_to_block_id: str | None = None, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, language: str, ) -> None: self.id = block_schema["id"] @@ -43,6 +49,7 @@ def __init__( response_metadata=response_metadata, location=self.location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) self._value_source_resolver = ValueSourceResolver( @@ -55,6 +62,7 @@ def __init__( list_item_id=self.location.list_item_id if self.location else None, use_default_answer=True, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) self.question = self.get_question( @@ -66,6 +74,7 @@ def __init__( return_to=return_to, return_to_block_id=return_to_block_id, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, language=language, ) @@ -80,6 +89,7 @@ def get_question( return_to: str | None, return_to_block_id: str | None, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, language: str, ) -> dict[str, Question]: """Taking question variants into account, return the question which was displayed to the user""" @@ -95,12 +105,14 @@ def get_question( single_key="question", current_location=self.location, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) return Question( variant, answer_store=answer_store, list_store=list_store, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, schema=self.schema, rule_evaluator=self._rule_evaluator, value_source_resolver=self._value_source_resolver, diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py index 07808dae50..d5ba4dbede 100644 --- a/app/views/contexts/summary/calculated_summary_block.py +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -3,7 +3,12 @@ from flask import url_for -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -27,6 +32,7 @@ def __init__( return_to_block_id: str | None = None, progress_store: ProgressStore, routing_path_block_ids: Iterable[str], + supplementary_data_store: SupplementaryDataStore, ) -> None: """ A Calculated summary block that is rendered as part of a grand calculated summary @@ -48,6 +54,7 @@ def __init__( location=location, progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, + supplementary_data_store=supplementary_data_store, ) # Type ignore: for a calculated summary the resolved answer would only ever be one of these 3 diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 54c325c404..910faf920a 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -2,7 +2,12 @@ from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer @@ -14,6 +19,7 @@ class Group: + # pylint: disable=too-many-locals def __init__( self, *, @@ -27,6 +33,7 @@ def __init__( location: LocationType, language: str, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, return_to: str | None, return_to_block_id: str | None = None, summary_type: str | None = None, @@ -54,6 +61,7 @@ def __init__( return_to_block_id=return_to_block_id, view_submitted_response=view_submitted_response, summary_type=summary_type, + supplementary_data_store=supplementary_data_store, ) self.placeholder_renderer = PlaceholderRenderer( @@ -65,6 +73,7 @@ def __init__( response_metadata=response_metadata, schema=schema, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) # pylint: disable=too-many-locals @@ -81,6 +90,7 @@ def _build_blocks_and_links( location: LocationType, return_to: str | None, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, language: str, return_to_block_id: str | None, view_submitted_response: bool | None = False, @@ -107,6 +117,7 @@ def _build_blocks_and_links( schema=schema, location=location, language=language, + supplementary_data_store=supplementary_data_store, return_to=return_to, return_to_block_id=return_to_block_id, ) @@ -137,6 +148,7 @@ def _build_blocks_and_links( return_to_block_id=return_to_block_id, progress_store=progress_store, language=language, + supplementary_data_store=supplementary_data_store, ).serialize() ] ) @@ -157,6 +169,7 @@ def _build_blocks_and_links( return_to_block_id=return_to_block_id, progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, + supplementary_data_store=supplementary_data_store, ).serialize() ] ) @@ -181,6 +194,7 @@ def _build_blocks_and_links( location=location, language=language, return_to=return_to, + supplementary_data_store=supplementary_data_store, ) list_summary_element = list_collector_block.list_summary_element( diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 6ecab5159a..d0dfbf1021 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -4,7 +4,7 @@ from flask import url_for from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ProgressStore +from app.data_models import AnswerStore, ProgressStore, SupplementaryDataStore from app.data_models.list_store import ListModel, ListStore from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema @@ -26,6 +26,7 @@ def __init__( schema: QuestionnaireSchema, location: LocationType, language: str, + supplementary_data_store: SupplementaryDataStore, return_to: str | None, return_to_block_id: str | None = None, ) -> None: @@ -39,6 +40,7 @@ def __init__( schema=schema, progress_store=progress_store, location=location, + supplementary_data_store=supplementary_data_store, ) self._list_store = list_store self._schema = schema @@ -51,6 +53,7 @@ def __init__( self._response_metadata = response_metadata self._routing_path_block_ids = routing_path_block_ids self._progress_store = progress_store + self._supplementary_data_store = supplementary_data_store self._return_to = return_to self._return_to_block_id = return_to_block_id @@ -162,6 +165,7 @@ def list_context(self) -> ListContext: self._progress_store, self._metadata, self._response_metadata, + self._supplementary_data_store, ) def _add_link( @@ -227,6 +231,7 @@ def _get_related_answer_blocks_by_list_item_id( return_to=self._return_to, return_to_block_id=self._return_to_block_id, progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, language=self._language, ).serialize(), list_item_id=list_id, diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 63fb8ecfb1..6e2b9b8ed7 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -4,7 +4,12 @@ from markupsafe import Markup, escape from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.answer import AnswerValueEscapedTypes, escape_answer_value from app.data_models.metadata_proxy import MetadataProxy from app.forms.field_handlers.select_handlers import DynamicAnswerOptions @@ -29,6 +34,7 @@ def __init__( answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, + supplementary_data_store: SupplementaryDataStore, schema: QuestionnaireSchema, rule_evaluator: RuleEvaluator, value_source_resolver: ValueSourceResolver, @@ -48,6 +54,7 @@ def __init__( self.answer_store = answer_store self.list_store = list_store self.progress_store = progress_store + self.supplementary_data_store = supplementary_data_store self.summary = question_schema.get("summary") self.title = ( question_schema.get("title") or question_schema["answers"][0]["label"] @@ -303,6 +310,7 @@ def _get_resolved_answers( language=language, metadata=metadata, response_metadata=response_metadata, + supplementary_data_store=self.supplementary_data_store, ) resolved_question = ImmutableDict( diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index 117f6a1f06..6e989a2d99 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -2,7 +2,7 @@ from app.questionnaire.location import Location -from ...data_models import AnswerStore, ListStore, ProgressStore +from ...data_models import AnswerStore, ListStore, ProgressStore, SupplementaryDataStore from ...data_models.metadata_proxy import MetadataProxy from ...questionnaire import QuestionnaireSchema from .context import Context @@ -20,6 +20,7 @@ def __init__( metadata: MetadataProxy | None, response_metadata: MutableMapping, view_submitted_response: bool, + supplementary_data_store: SupplementaryDataStore, ) -> None: super().__init__( language, @@ -29,6 +30,7 @@ def __init__( progress_store, metadata, response_metadata, + supplementary_data_store, ) self.view_submitted_response = view_submitted_response self.summaries: list[dict] = [] @@ -87,6 +89,7 @@ def build_summary_item( routing_path=self._router.routing_path( section_id, list_item_id=list_item_id ), + supplementary_data_store=self._supplementary_data_store, ) summary = section_summary_context( diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index 740983f18e..2c7433ef39 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -64,6 +64,7 @@ def build_view_submitted_response_context( metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, view_submitted_response=True, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) context["summary"] = summary_context() context["pdf_url"] = url_for("post_submission.get_view_submitted_response_pdf") diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 144c3e76e5..586fc26c97 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -84,6 +84,7 @@ def placeholder_renderer(self) -> PlaceholderRenderer: schema=self._schema, location=self._current_location, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -95,6 +96,7 @@ def router(self) -> Router: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def is_location_valid(self) -> bool: diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 54df21bb41..109225f377 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -21,6 +21,7 @@ def get_context(self) -> dict[str, dict]: routing_path=self._routing_path, return_to=self.return_to, return_to_block_id=self.return_to_block_id, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) context = summary_context.build_view_context() diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index aa6f2ea45d..b0b4af444f 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -84,6 +84,7 @@ def form(self) -> QuestionnaireForm: response_metadata=self._questionnaire_store.response_metadata, form_data=self._form_data, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index ab536629c5..c189cef9db 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -19,6 +19,7 @@ def rendered_block(self) -> dict: self._questionnaire_store.list_store, self._current_location, self._questionnaire_store.progress_store, + self._questionnaire_store.supplementary_data_store, ) content_page_title = transformed_block.get( diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 8f34d0bbf1..9044ba50eb 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -72,6 +72,7 @@ def form(self) -> QuestionnaireForm: data=None, form_data=self._form_data, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_context(self) -> Mapping[str, Union[str, bool, dict]]: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 8c0ca619cc..a3f537dff9 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -180,6 +180,7 @@ def placeholder_renderer(self) -> PlaceholderRenderer: schema=self._schema, location=None, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -191,6 +192,7 @@ def router(self) -> Router: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -210,6 +212,7 @@ def form(self) -> QuestionnaireForm: data=self._answers, form_data=self._form_data, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_context(self) -> dict: diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index dd6833386e..384693e7b2 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -62,6 +62,7 @@ def get_context(self) -> dict[str, dict]: self._questionnaire_store.progress_store, self._questionnaire_store.metadata, self._questionnaire_store.response_metadata, + self._questionnaire_store.supplementary_data_store, ) return { diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 62914d9a52..2e9ef2b780 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -37,6 +37,7 @@ def form(self) -> QuestionnaireForm: location=self._current_location, form_data=self._form_data, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) answers = self._get_answers_for_question(question_json) @@ -50,6 +51,7 @@ def form(self) -> QuestionnaireForm: location=self._current_location, data=answers, progress_store=self._questionnaire_store.progress_store, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -73,6 +75,7 @@ def rendered_block(self) -> dict: self._questionnaire_store.list_store, self._current_location, self._questionnaire_store.progress_store, + self._questionnaire_store.supplementary_data_store, ) page_title = transformed_block.get("page_title") or self._get_safe_page_title( transformed_block["question"]["title"] @@ -98,6 +101,7 @@ def list_context(self) -> ListContext: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_next_location_url(self) -> str: diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index 0715ec9d9b..ca2bc24c2a 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -28,6 +28,7 @@ def __init__( progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) if not self._is_valid_location(): raise InvalidLocationException(f"location {self._section_id} is not valid") @@ -53,6 +54,7 @@ def get_context(self) -> Mapping: self._questionnaire_store.response_metadata, self._routing_path, self.current_location, + self._questionnaire_store.supplementary_data_store, ) return section_summary_context() diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index 21e6faeb38..2425ee9a8c 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -33,6 +33,7 @@ def router(self) -> Router: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_context(self) -> dict[str, str | dict]: @@ -44,6 +45,7 @@ def get_context(self) -> dict[str, str | dict]: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) return submit_questionnaire_context() diff --git a/app/views/handlers/view_preview_questions.py b/app/views/handlers/view_preview_questions.py index cc800cb790..cd2691afe2 100644 --- a/app/views/handlers/view_preview_questions.py +++ b/app/views/handlers/view_preview_questions.py @@ -26,6 +26,7 @@ def get_context(self) -> dict[str, object]: progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, response_metadata=self._questionnaire_store.response_metadata, + supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) context = { "hide_sign_out_button": True, diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json index 1f332a51be..d5fd4a1273 100644 --- a/schemas/test/en/test_supplementary_data.json +++ b/schemas/test/en/test_supplementary_data.json @@ -6,7 +6,7 @@ "survey_id": "123", "title": "Test Supplementary Data", "theme": "default", - "description": "A questionnaire to demo loading Supplementary data.", + "description": "A questionnaire to demo using Supplementary data for placeholders, validation and routing in both repeating and non repeating sections.", "metadata": [ { "name": "survey_id", @@ -30,34 +30,904 @@ } ], "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } + "type": "Hub", + "options": {} + }, + "post_submission": { + "view_response": true }, "sections": [ { - "id": "interstitial-section", + "id": "introduction-section", + "title": "Introduction", + "groups": [ + { + "id": "introduction-group", + "title": "Introduction Group", + "blocks": [ + { + "id": "introduction-block", + "type": "Introduction", + "primary_content": [ + { + "id": "business-details", + "title": { + "text": "You are completing this survey for {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "contents": [ + { + "description": { + "text": "If the company details or structure have changed contact us on {telephone_number_link}", + "placeholders": [ + { + "placeholder": "telephone_number_link", + "value": { + "source": "supplementary_data", + "identifier": "company_details", + "selectors": ["telephone_number"] + } + } + ] + } + }, + { + "guidance": { + "contents": [ + { + "title": "Guidance for completing this survey", + "list": [ + "The company name, telephone number all come from supplementary data", + "if you picked the supplementary dataset with guidance, there will be a 3rd bullet point below this one, with the supplementary guidance.", + { + "text": "{survey_guidance}", + "placeholders": [ + { + "placeholder": "survey_guidance", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "supplementary_data", + "identifier": "guidance" + } + ] + } + } + ] + } + ] + } + ] + } + ] + } + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "section-1", + "title": "Company Details", + "summary": { + "page_title": "Summary title", + "show_on_completion": true + }, "groups": [ { + "id": "group-1", + "title": "Group 1", "blocks": [ { - "id": "interstitial-definition", + "id": "email-block", + "type": "Question", + "question": { + "id": "email-question", + "type": "General", + "guidance": { + "contents": [ + { + "description": "If you answer no, an additional block will open up allowing entering of a different email" + } + ] + }, + "title": { + "text": "Is {email} still the correct contact email for {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "email", + "value": { + "source": "supplementary_data", + "identifier": "company_details", + "selectors": ["email"] + } + } + ] + }, + "answers": [ + { + "id": "same-email-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "source": "answers", + "identifier": "same-email-answer" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "new-email", + "question": { + "id": "new-email-question", + "type": "General", + "title": { + "text": "What is the new contact email for {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "answers": [ + { + "id": "new-email-answer", + "label": "Contact email", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + { + "type": "Question", + "id": "trading", + "question": { + "id": "trading-question", + "type": "General", + "title": { + "text": "When did {company_name} begin trading?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "answers": [ + { + "id": "trading-answer", + "label": "Date commenced", + "mandatory": true, + "type": "Radio", + "dynamic_options": { + "values": { + "map": [ + { + "format-date": ["self", "yyyy-MM-dd"] + }, + { + "date-range": [ + { + "date": [ + { + "source": "supplementary_data", + "identifier": "incorporation_date" + } + ] + }, + 7 + ] + } + ] + }, + "transform": { + "format-date": [ + { + "date": ["self"] + }, + "EEEE d MMMM yyyy" + ] + } + } + } + ] + } + }, + { + "type": "Question", + "id": "sales-breakdown-block", + "question": { + "id": "sales-breakdown-question", + "title": { + "text": "How much of the {sales} total UK sales was from Bristol and London?", + "placeholders": [ + { + "placeholder": "sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "supplementary_data", + "identifier": "total_uk_sales" + } + } + } + ] + } + ] + }, + "type": "Calculated", + "warning": "These answers must not exceed the reported total from the supplementary data", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "source": "supplementary_data", + "identifier": "total_uk_sales" + }, + "answers_to_calculate": ["sales-bristol-answer", "sales-london-answer"], + "conditions": ["less than", "equals"] + } + ], + "answers": [ + { + "id": "sales-bristol-answer", + "label": "Bristol Sales", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "sales-london-answer", + "label": "London Sales", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-sales", + "title": "Total value of sales from Bristol and London is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total sales", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "sales-london-answer" + }, + { + "source": "answers", + "identifier": "sales-bristol-answer" + } + ] + } + } + }, + { + "id": "section-1-interstitial", + "type": "Interstitial", "content": { - "title": "Supplementary Data", + "title": { + "text": "Summary of information provided for {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, "contents": [ { - "description": "You have successfully loaded Supplementary data" + "list": [ + { + "text": "Telephone Number: {phone}", + "placeholders": [ + { + "placeholder": "phone", + "value": { + "source": "supplementary_data", + "identifier": "company_details", + "selectors": ["telephone_number"] + } + } + ] + }, + { + "text": "Email: {company_email}", + "placeholders": [ + { + "placeholder": "company_email", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "new-email-answer" + }, + { + "source": "supplementary_data", + "identifier": "company_details", + "selectors": ["email"] + } + ] + } + } + ] + } + ] + }, + { + "text": "Note Title: {note_title}", + "placeholders": [ + { + "placeholder": "note_title", + "value": { + "source": "supplementary_data", + "identifier": "note", + "selectors": ["title"] + } + } + ] + }, + { + "text": "Note Description: {note_description}", + "placeholders": [ + { + "placeholder": "note_description", + "value": { + "source": "supplementary_data", + "identifier": "note", + "selectors": ["description"] + } + } + ] + }, + { + "text": "Note Example Title: {note_title}", + "placeholders": [ + { + "placeholder": "note_title", + "value": { + "source": "supplementary_data", + "identifier": "note", + "selectors": ["example", "title"] + } + } + ] + }, + { + "text": "Note Example Description: {note_description}", + "placeholders": [ + { + "placeholder": "note_description", + "value": { + "source": "supplementary_data", + "identifier": "note", + "selectors": ["example", "description"] + } + } + ] + }, + { + "text": "Incorporation Date: {incorporation_date}", + "placeholders": [ + { + "placeholder": "incorporation_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "source": "supplementary_data", + "identifier": "incorporation_date" + } + }, + "transform": "format_date" + } + ] + } + ] + }, + { + "text": "Trading start date: {trading_date}", + "placeholders": [ + { + "placeholder": "trading_date", + "transforms": [ + { + "arguments": { + "date_format": "d MMMM yyyy", + "date_to_format": { + "source": "answers", + "identifier": "trading-answer" + } + }, + "transform": "format_date" + } + ] + } + ] + }, + { + "text": "Guidance: {guidance}", + "placeholders": [ + { + "placeholder": "guidance", + "value": { + "source": "supplementary_data", + "identifier": "guidance" + } + } + ] + }, + { + "text": "Total Uk Sales: {sales}", + "placeholders": [ + { + "placeholder": "sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "supplementary_data", + "identifier": "total_uk_sales" + } + } + } + ] + } + ] + }, + { + "text": "Bristol sales: {bristol_sales}", + "placeholders": [ + { + "placeholder": "bristol_sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "answers", + "identifier": "sales-bristol-answer" + } + } + } + ] + } + ] + }, + { + "text": "London sales: {london_sales}", + "placeholders": [ + { + "placeholder": "london_sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "answers", + "identifier": "sales-london-answer" + } + } + } + ] + } + ] + }, + { + "text": "Sum of Bristol and London sales: {total_sales}", + "placeholders": [ + { + "placeholder": "total_sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-sales" + } + } + } + ] + } + ] + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Employees", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "employees", + "title": "employees", + "add_link_text": "Add another employee", + "empty_list_text": "There are no employees" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-2", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-employees", + "for_list": "employees", + "question": { + "type": "General", + "id": "any-employee-question", + "title": "Do you have any employees to report on?", + "answers": [ + { + "type": "Radio", + "id": "any-employee-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-employee", + "list_name": "employees" + } + } + }, + { + "label": "No", + "value": "No" + } + ] } ] }, - "type": "Interstitial" + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-employee-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "employees", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add any more employees?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-employee", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other employees?", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the employee?", + "answers": [ + { + "id": "employee-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "employee-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-employee", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the employee?", + "answers": [ + { + "id": "employee-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "employee-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-employee", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this employee?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this employee?", + "warning": "All of the information about this employee will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "employees", + "item_title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Employee Details", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "employees", + "title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + }, + "groups": [ + { + "id": "group-3", + "blocks": [ + { + "type": "Question", + "id": "length-of-employment", + "question": { + "id": "length-employment-question", + "type": "General", + "title": { + "text": "When did {employee_name} start working for {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "employment-start", + "label": { + "text": "Start date at {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + }, + "minimum": { + "value": { + "source": "supplementary_data", + "identifier": "incorporation_date" + } + } + } + ] + } } - ], - "id": "interstitial", - "title": "Interstitial Definition" + ] } ] } diff --git a/scripts/mock_data/supplementary_data_no_repeat.json b/scripts/mock_data/supplementary_data_no_repeat.json index 95a1fc57e9..d69ea41648 100644 --- a/scripts/mock_data/supplementary_data_no_repeat.json +++ b/scripts/mock_data/supplementary_data_no_repeat.json @@ -1,8 +1,24 @@ { - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A" - } -} \ No newline at end of file + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "company_name": "Tesco", + "company_details": { + "telephone_number": "01171231231", + "email": "contact@tesco.org" + }, + "note": { + "title": "Value of total sales", + "description": "Total value of goods sold during the period of the return", + "example": { + "title": "Including", + "description": "Sales across all UK stores" + } + }, + "guidance": "Some supplementary guidance about the survey", + "total_uk_sales": 555000.00, + "incorporation_date": "1947-11-27" + } +} diff --git a/scripts/mock_data/supplementary_data_no_repeat_v2.json b/scripts/mock_data/supplementary_data_no_repeat_v2.json new file mode 100644 index 0000000000..e96f12fdc0 --- /dev/null +++ b/scripts/mock_data/supplementary_data_no_repeat_v2.json @@ -0,0 +1,23 @@ +{ + "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "company_name": "Lidl", + "company_details": { + "telephone_number": "01174564561", + "email": "contact@lidl.org" + }, + "note": { + "title": "Value of total sales", + "description": "Total value of goods sold", + "example": { + "title": "Includes", + "description": "Sales across all EU stores" + } + }, + "total_uk_sales": 555000.00, + "incorporation_date": "1947-11-27" + } +} diff --git a/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json b/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json new file mode 100644 index 0000000000..37ea85a11b --- /dev/null +++ b/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json @@ -0,0 +1,26 @@ +[ + { + "survey_id": "123", + "period_id": "202301", + "title": "Supplementary data without repeating items", + "sds_schema_version": 1, + "sds_published_at": "2022-03-01T12:30:02Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" + }, + { + "survey_id": "123", + "period_id": "202301", + "title": "Supplementary data without repeating items v2", + "sds_schema_version": 1, + "sds_published_at": "2023-07-01T11:25:01Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd" + } +] diff --git a/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json b/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json new file mode 100644 index 0000000000..28044cc8b8 --- /dev/null +++ b/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json @@ -0,0 +1,27 @@ +[ + { + "survey_id": "456", + "period_id": "202302", + "title": "Supplementary data with repeating items", + "sds_schema_version": 1, + "sds_published_at": "2022-03-01T12:30:02Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64" + }, + { + "survey_id": "456", + "period_id": "202303", + "title": "Supplementary data with repeats v2 additional items", + "sds_schema_version": 1, + "sds_published_at": "2023-05-04T14:35:05Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88" + } +] + diff --git a/scripts/mock_sds_endpoint.py b/scripts/mock_sds_endpoint.py index 8e4bf1a56a..63e12b24e0 100644 --- a/scripts/mock_sds_endpoint.py +++ b/scripts/mock_sds_endpoint.py @@ -16,18 +16,15 @@ def get_sds_data(): dataset_id = request.args.get("dataset_id") - if dataset_id == "c067f6de-6d64-42b1-8b02-431a3486c178": - return encrypt_mock_data( - load_mock_data("scripts/mock_data/supplementary_data_no_repeat.json") - ) - if dataset_id == "34a80231-c49a-44d0-91a6-8fe1fb190e64": - return encrypt_mock_data( - load_mock_data("scripts/mock_data/supplementary_data_with_repeat.json") - ) - if dataset_id == "6b378962-f0c7-4e8c-947e-7d24ee1b6b88": - return encrypt_mock_data( - load_mock_data("scripts/mock_data/supplementary_data_with_repeat_v2.json") - ) + guid_filename_map = { + "c067f6de-6d64-42b1-8b02-431a3486c178": "supplementary_data_no_repeat", + "693dc252-2e90-4412-bd9c-c4d953e36fcd": "supplementary_data_no_repeat_v2", + "34a80231-c49a-44d0-91a6-8fe1fb190e64": "supplementary_data_with_repeat", + "6b378962-f0c7-4e8c-947e-7d24ee1b6b88": "supplementary_data_with_repeat_v2", + } + + if filename := guid_filename_map.get(dataset_id): + return encrypt_mock_data(load_mock_data(f"scripts/mock_data/{filename}.json")) return Response(status=404) @@ -47,10 +44,14 @@ def load_mock_data(filename): def load_mock_sds_dataset_metadata(survey_id: str, period_id: str): del period_id # not required for mock - if survey_id == "123": - return load_mock_data( - "scripts/mock_data/supplementary_dataset_metadata_response.json" - ) + + survey_id_filename_map = { + "123": "supplementary_dataset_metadata_response_non_repeating_items", + "456": "supplementary_dataset_metadata_response_repeating_items", + } + + if filename := survey_id_filename_map.get(survey_id): + return load_mock_data(f"scripts/mock_data/{filename}.json") return Response(status=404) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index f8d35d275c..b3562ffb07 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -234,3 +234,61 @@ def mocked_make_request_with_timeout( response_not_timed_out, ], ) + + +@pytest.fixture +def supplementary_data(): + return { + "schema_version": "v1", + "identifier": "12346789012A", + "note": { + "title": "Volume of total production", + "example": { + "title": "Including", + "description": "Sales across all UK stores", + }, + }, + "guidance": "Some supplementary guidance about the survey", + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports or outdoor games", + "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales", + }, + }, + { + "identifier": "201630601", + "name": "Other Minerals", + "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", + "value_sales": { + "answer_code": "201630601", + "label": "Value of sales", + }, + }, + ] + }, + } + + +@pytest.fixture +def supplementary_data_list_mappings(): + return { + "products": { + "89929001": "item-1", + "201630601": "item-2", + }, + } + + +@pytest.fixture +def supplementary_data_store_with_data( + supplementary_data, supplementary_data_list_mappings +): + return SupplementaryDataStore( + supplementary_data=supplementary_data, + list_mappings=supplementary_data_list_mappings, + ) diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index b50f356880..5e12d9ebcc 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -8,7 +8,6 @@ from app.data_models.answer_store import Answer from app.data_models.progress_store import CompletionStatus from app.data_models.session_store import SessionStore -from app.data_models.supplementary_data_store import SupplementaryDataStore from app.storage import storage_encryption from tests.app.parser.conftest import get_response_expires_at @@ -161,59 +160,6 @@ def app_session_store_encoded(mocker, session_data): return store -@pytest.fixture -def supplementary_data(): - return { - "schema_version": "v1", - "identifier": "12346789012A", - "note": { - "title": "Volume of total production", - }, - "items": { - "products": [ - { - "identifier": "89929001", - "name": "Articles and equipment for sports or outdoor games", - "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", - "value_sales": { - "answer_code": "89929001", - "label": "Value of sales", - }, - }, - { - "identifier": "201630601", - "name": "Other Minerals", - "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", - "value_sales": { - "answer_code": "201630601", - "label": "Value of sales", - }, - }, - ] - }, - } - - -@pytest.fixture -def supplementary_data_list_mappings(): - return { - "products": { - "89929001": "item-1", - "201630601": "item-2", - }, - } - - -@pytest.fixture -def supplementary_data_store_with_data( - supplementary_data, supplementary_data_list_mappings -): - return SupplementaryDataStore( - supplementary_data=supplementary_data, - list_mappings=supplementary_data_list_mappings, - ) - - @pytest.fixture def questionnaire_store_with_supplementary_data( questionnaire_store, supplementary_data_store_with_data diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 098c1385b1..45900567b5 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -1,6 +1,6 @@ import pytest -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.forms.field_handlers.select_handlers import Choice, ChoiceWithDetailAnswer @@ -34,6 +34,7 @@ def rule_evaluator(): schema=get_mock_schema(), location=None, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) return evaluator @@ -51,6 +52,7 @@ def value_source_resolver(): list_item_id=None, escape_answer_values=False, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) return resolver diff --git a/tests/app/forms/test_field_factory.py b/tests/app/forms/test_field_factory.py index 13fd7a0d3b..c8c9f214ab 100644 --- a/tests/app/forms/test_field_factory.py +++ b/tests/app/forms/test_field_factory.py @@ -1,6 +1,6 @@ import pytest -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.forms import error_messages from app.forms.field_handlers import get_field_handler from app.questionnaire import QuestionnaireSchema @@ -45,6 +45,7 @@ def test_invalid_field_type_raises_on_invalid(answer_store, list_store): list_item_id=None, escape_answer_values=False, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) rule_evaluator = RuleEvaluator( @@ -55,6 +56,7 @@ def test_invalid_field_type_raises_on_invalid(answer_store, list_store): schema=schema, location=None, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) # Given diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index c82611746e..e255747e6b 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -4,7 +4,7 @@ import pytest from werkzeug.datastructures import MultiDict -from app.data_models import ListStore, ProgressStore +from app.data_models import ListStore, ProgressStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore from app.forms import error_messages from app.forms.questionnaire_form import generate_form @@ -34,13 +34,14 @@ def test_form_ids_match_block_answer_ids(app, answer_store, list_store): question_schema = schema.get_block("name-block").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) for answer_id in schema.get_answer_ids_for_block("name-block"): @@ -71,14 +72,15 @@ def test_form_date_range_populates_data(app, answer_store, list_store): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert form.data == expected_form_data @@ -108,14 +110,15 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -152,14 +155,15 @@ def test_date_range_to_precedes_from_raises_question_error( } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -196,14 +200,15 @@ def test_date_range_too_large_period_raises_question_error( } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -239,14 +244,15 @@ def test_date_range_too_small_period_raises_question_error( } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -280,14 +286,15 @@ def test_date_range_valid_period(app, answer_store, list_store): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -327,14 +334,15 @@ def test_date_combined_single_validation(app, answer_store, list_store): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, - response_metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -379,14 +387,15 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -429,14 +438,15 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, - response_metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -477,14 +487,15 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, - response_metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -529,14 +540,15 @@ def test_date_mm_yyyy_combined_range_too_small_validation( } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -579,14 +591,15 @@ def test_date_mm_yyyy_combined_range_too_large_validation( } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, - response_metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -620,14 +633,15 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -665,14 +679,15 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -706,14 +721,15 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s } form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -747,14 +763,15 @@ def test_date_raises_ValueError_when_any_date_range_parts_are_falsy( response_metadata = {} form = generate_form( - schema, - question_schema, - answer_store, - list_store, - metadata, - response_metadata, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(ValueError): @@ -800,14 +817,15 @@ def test_bespoke_message_for_date_validation_range( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) mocker.patch( @@ -858,14 +876,15 @@ def test_invalid_minimum_period_limit_and_single_date_periods( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -919,14 +938,15 @@ def test_invalid_maximum_period_limit_and_single_date_periods( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -981,14 +1001,15 @@ def test_period_limits_minimum_not_set_and_single_date_periods( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with mocker.patch( @@ -1047,14 +1068,15 @@ def test_invalid_date_range_and_single_date_periods( metadata = {"schema_name": "test_date_validation_range"} form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(metadata), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -1093,14 +1115,15 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): form_data = MultiDict({"breakdown-1": "3", "breakdown-2": "5"}) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -1131,14 +1154,15 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke form_data = MultiDict({"breakdown-1": "3", "breakdown-2": "5"}) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) mocker.patch( @@ -1340,15 +1364,16 @@ def test_calculated_field( form_data = MultiDict(breakdowns) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, location=location, metadata=metadata, response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1396,14 +1421,15 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1433,14 +1459,15 @@ def test_multi_calculation(app, answer_store, list_store): # With no answers question validation should pass form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1450,14 +1477,15 @@ def test_multi_calculation(app, answer_store, list_store): form_data["breakdown-1"] = "10" form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1467,14 +1495,15 @@ def test_multi_calculation(app, answer_store, list_store): form_data["breakdown-1"] = "1" form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1501,14 +1530,15 @@ def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_st expected_form_data = {"csrf_token": None, "feeling-answer": "Good"} form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1522,13 +1552,14 @@ def test_form_errors_are_correctly_mapped(app, answer_store, list_store): question_schema = schema.get_block("set-min-max-block").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1546,13 +1577,14 @@ def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store question_schema = schema.get_block("date-block").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1582,14 +1614,15 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( # Option is selected therefore the detail answer should be mandatory (schema defined) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"mandatory-checkbox-answer": "Your choice"}), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") @@ -1597,14 +1630,15 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( # Option not selected therefore the detail answer should not be mandatory form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, data={"mandatory-checkbox-answer": "Ham"}, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") @@ -1622,14 +1656,15 @@ def test_answer_with_detail_answer_errors_are_correctly_mapped( question_schema = schema.get_block("radio-mandatory").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"radio-mandatory-answer": "Other"}), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1654,14 +1689,15 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): question_schema = schema.get_block("set-min-max-block").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=MultiDict({"set-minimum": "-10001"}), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1683,14 +1719,15 @@ def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1723,20 +1760,22 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( schema=schema, progress_store=ProgressStore(), location=Location(section_id="mutually-exclusive-checkbox-section"), + supplementary_data_store=SupplementaryDataStore(), ) rendered_schema = renderer.render( data_to_render=question_schema, list_item_id=None ) form = generate_form( - schema, - rendered_schema, - answer_store, - list_store, + schema=schema, + question_schema=rendered_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=MultiDict(), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) error = form.question_errors["mutually-exclusive-checkbox-question"] @@ -1765,14 +1804,15 @@ def test_mutually_exclusive_question_raises_error_when_both_answered( ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1788,13 +1828,14 @@ def test_date_range_form(app, answer_store, list_store): question_schema = schema.get_block("date-block").get("question") form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert hasattr(form, "date-range-from-answer") @@ -1824,14 +1865,15 @@ def test_date_range_form_with_data(app, answer_store, list_store): ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert hasattr(form, "date-range-from-answer") @@ -1863,14 +1905,15 @@ def test_form_for_radio_other_not_selected(app, answer_store, list_store): ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert hasattr(form, "radio-mandatory-answer") @@ -1894,14 +1937,15 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) other_text_field = getattr(form, "other-answer-mandatory") @@ -1945,10 +1989,10 @@ def test_dynamic_answers_question_validates(app): } form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, @@ -1959,6 +2003,7 @@ def test_dynamic_answers_question_validates(app): list_name=None, list_item_id=None, ), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1996,10 +2041,10 @@ def test_dynamic_answers_question_raises_validation_error(app): ) form = generate_form( - schema, - question_schema, - answer_store, - list_store, + schema=schema, + question_schema=question_schema, + answer_store=answer_store, + list_store=list_store, metadata=get_metadata(), response_metadata={}, form_data=form_data, @@ -2010,6 +2055,7 @@ def test_dynamic_answers_question_raises_validation_error(app): list_name=None, list_item_id=None, ), + supplementary_data_store=SupplementaryDataStore(), ) form.validate() diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 14d6a7bc28..b447026559 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -2,7 +2,7 @@ import pytest -from app.data_models import QuestionnaireStore +from app.data_models import QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy @@ -65,6 +65,7 @@ def parser(answer_store, location, mock_schema, mock_renderer): location=location, renderer=mock_renderer, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) @@ -1007,6 +1008,7 @@ def placeholder_renderer(option_label_from_value_schema): schema=option_label_from_value_schema, progress_store=ProgressStore(), location=Location(section_id="checkbox-section"), + supplementary_data_store=SupplementaryDataStore(), ) return renderer @@ -1021,6 +1023,7 @@ def mock_renderer(mock_schema): response_metadata={}, schema=mock_schema, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) @@ -1167,9 +1170,19 @@ def mock_empty_progress_store(mocker): return progress_store +@pytest.fixture +def mock_empty_supplementary_data_store(mocker): + supplementary_data_store = mocker.MagicMock(spec=SupplementaryDataStore) + return supplementary_data_store + + @pytest.fixture def mock_questionnaire_store( - populated_list_store, mock_empty_answer_store, mock_empty_progress_store, mocker + populated_list_store, + mock_empty_answer_store, + mock_empty_progress_store, + mock_empty_supplementary_data_store, + mocker, ): return mocker.MagicMock( spec=QuestionnaireStore, @@ -1177,6 +1190,7 @@ def mock_questionnaire_store( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index ba9ee6ceb7..3812ce06b1 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -5,7 +5,12 @@ from freezegun import freeze_time from mock import MagicMock, Mock -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.answer import Answer from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema @@ -49,6 +54,7 @@ def get_rule_evaluator( ), routing_path_block_ids: Optional[list] = None, progress: ProgressStore = ProgressStore(), + supplementary_data_store: SupplementaryDataStore = SupplementaryDataStore(), ): if not schema: schema = get_mock_schema() @@ -67,6 +73,7 @@ def get_rule_evaluator( location=location, routing_path_block_ids=routing_path_block_ids, progress_store=progress, + supplementary_data_store=supplementary_data_store, ) @@ -981,7 +988,8 @@ def test_format_date(rule, expected_result): @freeze_time("2021-01-01") -def test_map_without_nested_date_operator(): +@pytest.mark.parametrize("source", ("response_metadata", "supplementary_data")) +def test_map_without_nested_date_operator(source): rule = { Operator.MAP: [ {Operator.FORMAT_DATE: ["self", "yyyy-MM-dd"]}, @@ -989,7 +997,7 @@ def test_map_without_nested_date_operator(): Operator.DATE_RANGE: [ { Operator.DATE: [ - {"source": "response_metadata", "identifier": "started_at"}, + {"source": source, "identifier": "started_at"}, {"days": -7, "day_of_week": "MONDAY"}, ] }, @@ -999,8 +1007,10 @@ def test_map_without_nested_date_operator(): ] } + date_map = {"started_at": datetime.now(timezone.utc).isoformat()} rule_evaluator = get_rule_evaluator( - response_metadata={"started_at": datetime.now(timezone.utc).isoformat()} + response_metadata=date_map, + supplementary_data_store=SupplementaryDataStore(date_map), ) assert rule_evaluator.evaluate(rule=rule) == [ @@ -1048,3 +1058,36 @@ def test_map_with_nested_date_operator(offset, expected_result): ) assert rule_evaluator.evaluate(rule=rule) == expected_result + + +def test_supplementary_data_source(supplementary_data_store_with_data): + schema = get_mock_schema() + schema.is_repeating_answer = Mock(return_value=False) + answer_store = AnswerStore( + [{"answer_id": "same-answer", "value": "Volume of total production"}] + ) + + rule_evaluator = get_rule_evaluator( + schema=schema, + answer_store=answer_store, + supplementary_data_store=supplementary_data_store_with_data, + location=Location( + section_id="some-section", block_id="some-block", list_item_id="item-1" + ), + ) + + assert ( + rule_evaluator.evaluate( + rule={ + Operator.EQUAL: [ + { + "source": "supplementary_data", + "identifier": "note", + "selectors": ["title"], + }, + {"source": "answers", "identifier": "same-answer"}, + ] + } + ) + is True + ) diff --git a/tests/app/questionnaire/test_dynamic_answer_options.py b/tests/app/questionnaire/test_dynamic_answer_options.py index a1b0212559..90ba59898a 100644 --- a/tests/app/questionnaire/test_dynamic_answer_options.py +++ b/tests/app/questionnaire/test_dynamic_answer_options.py @@ -1,7 +1,12 @@ # pylint: disable=redefined-outer-name import pytest -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.answer_store import Answer from app.questionnaire.dynamic_answer_options import DynamicAnswerOptions from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -18,6 +23,7 @@ def rule_evaluator(mock_schema, response_metadata): schema=mock_schema, location=None, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) return evaluator @@ -36,6 +42,7 @@ def value_source_resolver(mock_schema, response_metadata): routing_path_block_ids=None, use_default_answer=True, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) return resolver diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index d145cd702d..52bd6b7525 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -9,7 +9,7 @@ from tests.app.questionnaire.conftest import get_metadata -def test_simple_path(answer_store, list_store): +def test_simple_path(answer_store, list_store, supplementary_data_store): schema = load_schema_from_name("test_textfield") progress_store = ProgressStore( [ @@ -21,7 +21,15 @@ def test_simple_path(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) section_id = schema.get_section_id_for_block_id("name-block") routing_path = path_finder.routing_path(section_id=section_id) @@ -31,29 +39,58 @@ def test_simple_path(answer_store, list_store): assert routing_path == assumed_routing_path -def test_introduction_in_path_when_in_schema(answer_store, list_store, progress_store): +def test_introduction_in_path_when_in_schema( + answer_store, + list_store, + progress_store, + supplementary_data_store, +): schema = load_schema_from_name("test_introduction") current_section = schema.get_section("introduction-section") - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=current_section["id"]) assert "introduction" in routing_path def test_introduction_not_in_path_when_not_in_schema( - answer_store, list_store, progress_store + answer_store, + list_store, + progress_store, + supplementary_data_store, ): schema = load_schema_from_name("test_checkbox") current_section = schema.get_section("default-section") - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=current_section["id"]) assert "introduction" not in routing_path -def test_routing_basic_and_conditional_path(answer_store, list_store, progress_store): +def test_routing_basic_and_conditional_path( + answer_store, + list_store, + progress_store, + supplementary_data_store, +): # Given schema = load_schema_from_name("test_routing_number_equals") section_id = schema.get_section_id_for_block_id("number-question") @@ -67,14 +104,24 @@ def test_routing_basic_and_conditional_path(answer_store, list_store, progress_s answer_store.add_or_update(answer_1) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) # Then assert routing_path == expected_path -def test_routing_path_with_complete_introduction(answer_store, list_store): +def test_routing_path_with_complete_introduction( + answer_store, list_store, supplementary_data_store +): schema = load_schema_from_name("test_introduction") section_id = schema.get_section_id_for_block_id("introduction") progress_store = ProgressStore( @@ -102,13 +149,21 @@ def test_routing_path_with_complete_introduction(answer_store, list_store): section_id="introduction-section", ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_routing_path -def test_routing_path(answer_store, list_store): +def test_routing_path(answer_store, list_store, supplementary_data_store): schema = load_schema_from_name("test_submit_with_summary") section_id = schema.get_section_id_for_block_id("dessert") expected_path = RoutingPath( @@ -131,13 +186,23 @@ def test_routing_path(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_path -def test_routing_path_with_repeating_sections(answer_store, list_store): +def test_routing_path_with_repeating_sections( + answer_store, list_store, supplementary_data_store +): schema = load_schema_from_name("test_repeating_sections_with_hub_and_spoke") progress_store = ProgressStore( @@ -154,7 +219,15 @@ def test_routing_path_with_repeating_sections(answer_store, list_store): } ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) repeating_section_id = "personal-details-section" routing_path = path_finder.routing_path( @@ -171,7 +244,9 @@ def test_routing_path_with_repeating_sections(answer_store, list_store): assert routing_path == expected_path -def test_routing_path_empty_routing_rules(answer_store, list_store): +def test_routing_path_empty_routing_rules( + answer_store, list_store, supplementary_data_store +): schema = load_schema_from_name("test_checkbox") section_id = schema.get_section_id_for_block_id("mandatory-checkbox") expected_path = RoutingPath( @@ -198,13 +273,23 @@ def test_routing_path_empty_routing_rules(answer_store, list_store): ] ) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) assert routing_path == expected_path -def test_routing_path_with_conditional_value_not_in_metadata(answer_store, list_store): +def test_routing_path_with_conditional_value_not_in_metadata( + answer_store, list_store, supplementary_data_store +): schema = load_schema_from_name("test_metadata_routing") section_id = schema.get_section_id_for_block_id("block1") expected_path = RoutingPath( @@ -223,7 +308,13 @@ def test_routing_path_with_conditional_value_not_in_metadata(answer_store, list_ ) path_finder = PathFinder( - schema, answer_store, list_store, progress_store, get_metadata(), {} + schema, + answer_store, + list_store, + progress_store, + get_metadata(), + {}, + supplementary_data_store=supplementary_data_store, ) routing_path = path_finder.routing_path(section_id=section_id) @@ -231,7 +322,9 @@ def test_routing_path_with_conditional_value_not_in_metadata(answer_store, list_ assert routing_path == expected_path -def test_routing_path_should_skip_block(answer_store, list_store): +def test_routing_path_should_skip_block( + answer_store, list_store, supplementary_data_store +): # Given schema = load_schema_from_name("test_skip_condition_block") section_id = schema.get_section_id_for_block_id("should-skip") @@ -251,7 +344,15 @@ def test_routing_path_should_skip_block(answer_store, list_store): ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -264,7 +365,9 @@ def test_routing_path_should_skip_block(answer_store, list_store): assert routing_path == expected_routing_path -def test_routing_path_should_skip_group(answer_store, list_store): +def test_routing_path_should_skip_group( + answer_store, list_store, supplementary_data_store +): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -284,7 +387,15 @@ def test_routing_path_should_skip_group(answer_store, list_store): ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -296,7 +407,9 @@ def test_routing_path_should_skip_group(answer_store, list_store): assert routing_path == expected_routing_path -def test_routing_path_should_not_skip_group(answer_store, list_store): +def test_routing_path_should_not_skip_group( + answer_store, list_store, supplementary_data_store +): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -316,7 +429,15 @@ def test_routing_path_should_not_skip_group(answer_store, list_store): ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) routing_path = path_finder.routing_path(section_id=section_id) # Then @@ -329,7 +450,10 @@ def test_routing_path_should_not_skip_group(answer_store, list_store): def test_get_routing_path_when_first_block_in_group_skipped( - answer_store, list_store, progress_store + answer_store, + list_store, + progress_store, + supplementary_data_store, ): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -338,7 +462,15 @@ def test_get_routing_path_when_first_block_in_group_skipped( ) # When - path_finder = PathFinder(schema, answer_store, list_store, progress_store, None, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + None, + {}, + supplementary_data_store=supplementary_data_store, + ) # Then expected_route = RoutingPath( @@ -349,7 +481,12 @@ def test_get_routing_path_when_first_block_in_group_skipped( assert expected_route == path_finder.routing_path(section_id="default-section") -def test_build_path_with_group_routing(answer_store, list_store, progress_store): +def test_build_path_with_group_routing( + answer_store, + list_store, + progress_store, + supplementary_data_store, +): # 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") @@ -357,7 +494,15 @@ def test_build_path_with_group_routing(answer_store, list_store, progress_store) answer_store.add_or_update(Answer(answer_id="which-group-answer", value="group2")) # When i build the path - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + {}, + {}, + supplementary_data_store=supplementary_data_store, + ) path = path_finder.routing_path(section_id=section_id) # Then it should route me straight to Group2 and not Group1 @@ -365,7 +510,9 @@ def test_build_path_with_group_routing(answer_store, list_store, progress_store) assert "group2-block" in path -def test_remove_answer_and_block_if_routing_backwards(list_store): +def test_remove_answer_and_block_if_routing_backwards( + list_store, supplementary_data_store +): schema = load_schema_from_name("test_confirmation_question_backwards_routing") section_id = schema.get_section_id_for_block_id("confirm-zero-employees-block") @@ -395,7 +542,15 @@ def test_remove_answer_and_block_if_routing_backwards(list_store): answer_store.add_or_update(number_of_employees_answer) answer_store.add_or_update(confirm_zero_answer) - path_finder = PathFinder(schema, answer_store, list_store, progress_store, {}, {}) + path_finder = PathFinder( + schema, + answer_store, + list_store, + progress_store, + {}, + {}, + supplementary_data_store=supplementary_data_store, + ) assert ( len( @@ -501,6 +656,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( section_id, expected_route, answer_store, + supplementary_data_store, ): # Given a schema which has when rules in a section which has dependencies on other sections answers schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") @@ -542,6 +698,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( progress_store, metadata=None, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) routing_path = path_finder.routing_path(section_id=section_id) @@ -576,7 +733,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( ), ) def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating( - skip_age_answer, expected_route, answer_store + skip_age_answer, expected_route, answer_store, supplementary_data_store ): # Given a schema with repeating sections which has when rules dependent on another section schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") @@ -619,6 +776,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating progress_store, metadata=None, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) routing_path = path_finder.routing_path( section_id="household-personal-details-section", list_item_id="lCIZsS" diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 6974b969c1..8cc719d1ee 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1,6 +1,6 @@ from mock import Mock -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.questionnaire import Location @@ -43,6 +43,7 @@ def test_metadata_placeholder(mock_renderer, mock_schema, mock_location): renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -85,6 +86,7 @@ def test_previous_answer_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -122,6 +124,7 @@ def test_metadata_transform_placeholder(mock_renderer, mock_schema, mock_locatio renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -163,6 +166,7 @@ def test_response_metadata_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -207,6 +211,7 @@ def test_multiple_answer_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -246,6 +251,7 @@ def test_first_non_empty_item_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -287,6 +293,7 @@ def test_format_list_answer_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -331,6 +338,7 @@ def test_placeholder_parser_escapes_answers(mock_renderer, mock_schema, mock_loc renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -381,6 +389,7 @@ def test_multiple_metadata_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -423,6 +432,7 @@ def test_multiple_metadata_list_transform_placeholder( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -463,6 +473,7 @@ def test_checkbox_transform_placeholder(mock_renderer, mock_schema, mock_locatio renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -507,6 +518,7 @@ def test_mixed_transform_placeholder(mock_renderer, mock_schema, mock_location): renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -546,6 +558,7 @@ def test_mixed_transform_placeholder_value(mock_renderer, mock_schema, mock_loca renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -574,6 +587,7 @@ def test_list_source_count(mock_renderer, mock_schema, mock_location): renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -613,6 +627,7 @@ def test_list_source_count_in_transform(mock_renderer, mock_schema, mock_locatio renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -659,6 +674,7 @@ def test_chain_transform_placeholder(mock_renderer, mock_schema, mock_location): renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -705,6 +721,7 @@ def test_placeholder_resolves_answer_value_based_on_first_item_in_list( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -737,6 +754,7 @@ def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -778,6 +796,7 @@ def test_placeholder_resolves_same_name_items( list_item_id="abc123", progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -884,6 +903,7 @@ def test_placeholder_resolves_name_is_duplicate_chain( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -901,6 +921,7 @@ def test_placeholder_resolves_name_is_duplicate_chain( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1003,6 +1024,7 @@ def test_placeholder_resolves_list_has_items_chain( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1020,6 +1042,7 @@ def test_placeholder_resolves_list_has_items_chain( renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1054,6 +1077,7 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render renderer=mock_renderer, progress_store=ProgressStore(), location=location, + supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -1128,6 +1152,7 @@ def test_placeholder_parser_calculated_summary_dependencies_cache( renderer=mock_renderer, progress_store=progress_store, location=location, + supplementary_data_store=SupplementaryDataStore(), ) placeholder_1 = placeholder_parser(placeholder_list=placeholder_list_1) diff --git a/tests/app/questionnaire/test_placeholder_renderer.py b/tests/app/questionnaire/test_placeholder_renderer.py index df87dcc43f..7c4c0ee42f 100644 --- a/tests/app/questionnaire/test_placeholder_renderer.py +++ b/tests/app/questionnaire/test_placeholder_renderer.py @@ -1,7 +1,7 @@ import pytest from mock import MagicMock -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.questionnaire import Location @@ -56,6 +56,7 @@ def test_renders_pointer( location=mock_location, renderer=mock_renderer, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ), ) @@ -142,6 +143,7 @@ def test_errors_on_invalid_pointer(placholder_transform_question_json, mock_sche location=None, renderer=renderer, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ), ) @@ -164,6 +166,7 @@ def test_errors_on_invalid_json(mock_schema): location=None, renderer=renderer, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ), ) @@ -333,6 +336,7 @@ def get_placeholder_render_dynamic_answers( answer_store=AnswerStore(), list_store=ListStore(), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), metadata=None, response_metadata=None, ): @@ -345,6 +349,7 @@ def get_placeholder_render_dynamic_answers( metadata=metadata or {}, response_metadata=response_metadata or {}, schema=schema, + supplementary_data_store=supplementary_data_store, ) @@ -368,5 +373,6 @@ def get_placeholder_render( schema=schema, progress_store=ProgressStore(), location=Location(section_id="default-section"), + supplementary_data_store=SupplementaryDataStore(), ) return renderer diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index e123d4b6f0..3944e98d15 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -6,7 +6,7 @@ from ordered_set import OrderedSet from werkzeug.datastructures import MultiDict -from app.data_models import QuestionnaireStore +from app.data_models import QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import AnswerDict, AnswerStore from app.data_models.list_store import ListStore from app.data_models.progress_store import CompletionStatus, ProgressStore @@ -294,6 +294,7 @@ def test_remove_all_answers_with_list_item_id( answer_store=mock_empty_answer_store, list_store=mocker.MagicMock(spec=ListStore), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -326,6 +327,7 @@ def test_remove_primary_person( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -349,6 +351,7 @@ def test_add_primary_person( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -362,6 +365,7 @@ def test_remove_completed_relationship_locations_for_list_name( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, + mock_empty_supplementary_data_store, mock_router, populated_list_store, mocker, @@ -376,6 +380,7 @@ def test_remove_completed_relationship_locations_for_list_name( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -400,6 +405,7 @@ def test_remove_completed_relationship_locations_for_list_name_no_locations( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, + mock_empty_supplementary_data_store, mock_router, populated_list_store, mocker, @@ -415,6 +421,7 @@ def test_remove_completed_relationship_locations_for_list_name_no_locations( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -435,6 +442,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( mock_empty_schema, mock_empty_answer_store, mock_empty_progress_store, + mock_empty_supplementary_data_store, mock_router, populated_list_store, mocker, @@ -445,6 +453,7 @@ def test_update_relationship_question_completeness_no_relationship_collectors( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -496,6 +505,7 @@ def test_update_same_name_items( answer_store=mock_empty_answer_store, list_store=populated_list_store, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -1070,10 +1080,13 @@ def get_questionnaire_store_updater( progress_store=None, router=None, current_question=None, + supplementary_data_store=None, ): answer_store = AnswerStore() if answer_store is None else answer_store list_store = ListStore() if list_store is None else list_store progress_store = ProgressStore() if progress_store is None else progress_store + supplementary_data_store = supplementary_data_store or SupplementaryDataStore() + mock_schema = ( MagicMock( QuestionnaireSchema({"questionnaire_flow": {"type": "Hub", "options": {}}}) @@ -1090,6 +1103,7 @@ def get_questionnaire_store_updater( answer_store=answer_store, list_store=list_store, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) current_question = current_question or {} diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 7c1ec52e6f..eb13b568b1 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -8,6 +8,7 @@ from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath @@ -21,6 +22,7 @@ class RouterTestCase: list_store = ListStore() progress_store = ProgressStore() metadata = get_metadata() + supplementary_data_store = SupplementaryDataStore() response_metadata = {} @cached_property @@ -32,6 +34,7 @@ def router(self): self.progress_store, self.metadata, self.response_metadata, + self.supplementary_data_store, ) diff --git a/tests/app/questionnaire/test_schema_utils.py b/tests/app/questionnaire/test_schema_utils.py index 2fd44b5dc5..cee9be9196 100644 --- a/tests/app/questionnaire/test_schema_utils.py +++ b/tests/app/questionnaire/test_schema_utils.py @@ -1,4 +1,4 @@ -from app.data_models import ProgressStore +from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.questionnaire.location import Location @@ -35,6 +35,7 @@ def test_transform_variants_with_question_variants(question_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) compare_transformed_block(block, transformed_block, "Question 1, No") @@ -50,6 +51,7 @@ def test_transform_variants_with_question_variants(question_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) compare_transformed_block(block, transformed_block, "Question 1, Yes") @@ -74,6 +76,7 @@ def test_transform_variants_with_content(content_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert transformed_block != block @@ -99,6 +102,7 @@ def test_transform_variants_with_no_variants(question_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert transformed_block == block @@ -123,6 +127,7 @@ def test_transform_variants_list_collector(list_collector_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) compare_transformed_block( @@ -146,6 +151,7 @@ def test_transform_variants_list_collector(list_collector_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) compare_transformed_block( @@ -178,6 +184,7 @@ def test_choose_content_to_display(content_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert content_to_display["title"] == "You are over 16" @@ -193,6 +200,7 @@ def test_choose_content_to_display(content_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert content_to_display["title"] == "You are ageless" @@ -217,6 +225,7 @@ def test_choose_question_to_display(question_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert question_to_display["title"] == "Question 1, Yes" @@ -232,6 +241,7 @@ def test_choose_question_to_display(question_variant_schema): ListStore({}), Location(section_id=section_id, block_id=block["id"]), progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) assert question_to_display["title"] == "Question 1, No" diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 148277310a..1c64625760 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -4,7 +4,12 @@ from mock import MagicMock, Mock from app.authentication.auth_payload_versions import AuthPayloadVersion -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.data_models.answer import Answer, AnswerDict from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire import Location, QuestionnaireSchema @@ -49,6 +54,7 @@ def get_value_source_resolver( use_default_answer=False, escape_answer_values=False, progress_store: ProgressStore | None = None, + supplementary_data_store: SupplementaryDataStore | None = None, ): if not schema: schema = get_mock_schema() @@ -71,6 +77,7 @@ def get_value_source_resolver( use_default_answer=use_default_answer, escape_answer_values=escape_answer_values, progress_store=progress_store, + supplementary_data_store=supplementary_data_store, ) @@ -784,3 +791,63 @@ def test_progress_values_source_throws_if_no_location_given(): value_source_resolver.resolve( {"source": "progress", "selector": "block", "identifier": "a-block"} ) + + +@pytest.mark.parametrize("in_repeating_section", [True, False]) +@pytest.mark.parametrize( + "value_source,expected_result", + [ + ( + {"identifier": "guidance"}, + "Some supplementary guidance about the survey", + ), + ( + {"identifier": "note", "selectors": ["title"]}, + "Volume of total production", + ), + ( + {"identifier": "note", "selectors": ["example", "title"]}, + "Including", + ), + ( + {"identifier": "note", "selectors": ["example", "description"]}, + "Sales across all UK stores", + ), + ( + {"identifier": "INVALID"}, + None, + ), + ], +) +def test_supplementary_data_value_source_non_list_items( + supplementary_data_store_with_data, + value_source, + expected_result, + in_repeating_section, +): + list_store = ListStore([{"name": "some-list", "items": get_list_items(3)}]) + location = ( + Location( + section_id="section", + block_id="block-id", + list_name="some-list", + list_item_id="item-1", + ) + if in_repeating_section + else Location(section_id="section", block_id="block-id") + ) + value_source_resolver = get_value_source_resolver( + supplementary_data_store=supplementary_data_store_with_data, + location=location, + list_item_id=location.list_item_id, + list_store=list_store, + ) + assert ( + value_source_resolver.resolve( + { + "source": "supplementary_data", + **value_source, + } + ) + == expected_result + ) diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 6787ed23f0..8616cd320a 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -60,6 +60,7 @@ def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): schema=QuestionnaireSchema(questionnaire), full_routing_path=full_routing_path, progress_store=questionnaire_store.progress_store, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) assert answer_object["002"] == "2016-03-30" assert len(answer_object) == 1 diff --git a/tests/app/views/contexts/summary/test_block.py b/tests/app/views/contexts/summary/test_block.py index 90beccc047..60ef08903f 100644 --- a/tests/app/views/contexts/summary/test_block.py +++ b/tests/app/views/contexts/summary/test_block.py @@ -32,6 +32,7 @@ def test_create_block(mocker): return_to="final-summary", progress_store=ProgressStore(), language="en", + supplementary_data_store=mocker.MagicMock(), ) # Then diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index a5389a70e2..04298c8103 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -2,7 +2,7 @@ import pytest from mock import MagicMock -from app.data_models import Answer, ListStore, ProgressStore +from app.data_models import Answer, ListStore, ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -20,6 +20,7 @@ def get_rule_evaluator(answer_store, list_store, schema, response_metadata=None) response_metadata=response_metadata or {}, location=None, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) @@ -35,6 +36,7 @@ def get_value_source_resolver(answer_store, list_store, schema, response_metadat routing_path_block_ids=None, use_default_answer=True, progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), ) @@ -129,7 +131,13 @@ def address_questionnaire_schema(concatenation_type): ) -def address_question(answer_store, list_store, progress_store, schema): +def address_question( + answer_store, + list_store, + progress_store, + schema, + supplementary_data_store, +): question_schema = schema.get_questions("what-is-your-address-question")[0] return Question( question_schema, @@ -147,6 +155,7 @@ def address_question(answer_store, list_store, progress_store, schema): language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) @@ -173,6 +182,7 @@ def test_create_question( list_store, progress_store, mock_schema, + supplementary_data_store, ): # Given question_schema = { @@ -199,6 +209,7 @@ def test_create_question( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -221,6 +232,7 @@ def test_concatenate_textfield_answers( list_store, progress_store, answer_store, + supplementary_data_store, ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -232,7 +244,13 @@ def test_concatenate_textfield_answers( ): answer_store.add_or_update(answer) - question = address_question(answer_store, list_store, progress_store, schema) + question = address_question( + answer_store, + list_store, + progress_store, + schema, + supplementary_data_store, + ) # Then assert ( question.answers[0]["value"] @@ -255,6 +273,7 @@ def test_concatenate_textfield_answers_default( list_store, answer_store, progress_store, + supplementary_data_store, ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -266,7 +285,13 @@ def test_concatenate_textfield_answers_default( answer_store.add_or_update(answer) # When - question = address_question(answer_store, list_store, progress_store, schema) + question = address_question( + answer_store, + list_store, + progress_store, + schema, + supplementary_data_store, + ) # Then assert ( @@ -291,6 +316,7 @@ def test_concatenate_number_and_checkbox_answers( answer_store, progress_store, mock_schema, + supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="age", value=7)) @@ -341,6 +367,7 @@ def test_concatenate_number_and_checkbox_answers( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -353,7 +380,11 @@ def test_concatenate_number_and_checkbox_answers( @pytest.mark.usefixtures("app") def test_merge_date_range_answers( - answer_store, list_store, progress_store, mock_schema + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value="13/02/2016")) @@ -386,6 +417,7 @@ def test_merge_date_range_answers( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -396,7 +428,11 @@ def test_merge_date_range_answers( @pytest.mark.usefixtures("app") def test_merge_multiple_date_range_answers( - answer_store, list_store, progress_store, mock_schema + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, ): # Given for answer in ( @@ -436,6 +472,7 @@ def test_merge_multiple_date_range_answers( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -448,7 +485,11 @@ def test_merge_multiple_date_range_answers( @pytest.mark.usefixtures("app") def test_create_question_with_multiple_answers( - answer_store, list_store, progress_store, mock_schema + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, ): # Given for answer in ( @@ -484,6 +525,7 @@ def test_create_question_with_multiple_answers( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -493,7 +535,13 @@ def test_create_question_with_multiple_answers( @pytest.mark.usefixtures("app") -def test_checkbox_button_options(answer_store, list_store, progress_store, mock_schema): +def test_checkbox_button_options( + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, +): # Given answer_store.add_or_update( Answer(answer_id="answer_1", value=["Light Side", "Dark Side"]) @@ -533,6 +581,7 @@ def test_checkbox_button_options(answer_store, list_store, progress_store, mock_ language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -543,7 +592,11 @@ def test_checkbox_button_options(answer_store, list_store, progress_store, mock_ @pytest.mark.usefixtures("app") def test_checkbox_button_detail_answer_empty( - answer_store, list_store, progress_store, mock_schema + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value=["other", ""])) @@ -586,6 +639,7 @@ def test_checkbox_button_detail_answer_empty( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -641,6 +695,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( list_store, progress_store, mock_schema, + supplementary_data_store, ): # Given for answer in answers: @@ -677,6 +732,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -686,7 +742,11 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( @pytest.mark.usefixtures("app") def test_checkbox_button_other_option_text( - answer_store, list_store, progress_store, mock_schema + answer_store, + list_store, + progress_store, + mock_schema, + supplementary_data_store, ): # Given answer_store.add_or_update( @@ -732,6 +792,7 @@ def test_checkbox_button_other_option_text( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -786,6 +847,7 @@ def test_radio_answer_with_detail_answers_returns_correct_value( list_store, progress_store, mock_schema, + supplementary_data_store, ): # Given for answer in answers: @@ -822,6 +884,7 @@ def test_radio_answer_with_detail_answers_returns_correct_value( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -870,6 +933,7 @@ def test_answer_types_selected_option_label( list_store, progress_store, mock_schema, + supplementary_data_store, ): for answer in answers: answer_store.add_or_update(answer) @@ -905,6 +969,7 @@ def test_answer_types_selected_option_label( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -913,7 +978,12 @@ def test_answer_types_selected_option_label( @pytest.mark.usefixtures("app") def test_dynamic_checkbox_answer_options( - answer_store, list_store, progress_store, mock_schema, dynamic_answer_options_schema + answer_store, + list_store, + progress_store, + mock_schema, + dynamic_answer_options_schema, + supplementary_data_store, ): # Given answer_schema = { @@ -954,6 +1024,7 @@ def test_dynamic_checkbox_answer_options( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -997,6 +1068,7 @@ def test_dynamic_answer_options( list_store, progress_store, answer_store_value, + supplementary_data_store, expected, dynamic_answer_options_schema, mock_schema, @@ -1032,6 +1104,7 @@ def test_dynamic_answer_options( language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -1074,7 +1147,14 @@ def test_dynamic_answer_options( ), ), ) -def test_get_answer(answer_schema, answer_store, expected, list_store, progress_store): +def test_get_answer( + answer_schema, + answer_store, + expected, + list_store, + progress_store, + supplementary_data_store, +): schema = address_questionnaire_schema("Newline") # Given @@ -1097,6 +1177,7 @@ def test_get_answer(answer_schema, answer_store, expected, list_store, progress_ language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then @@ -1179,7 +1260,13 @@ def test_get_answer(answer_schema, answer_store, expected, list_store, progress_ ), ), ) -def test_dynamic_answers(answer_store, list_store, progress_store, expected): +def test_dynamic_answers( + answer_store, + list_store, + progress_store, + expected, + supplementary_data_store, +): schema = load_schema_from_name("test_dynamic_answers_list_source", "en") # Given @@ -1249,6 +1336,7 @@ def test_dynamic_answers(answer_store, list_store, progress_store, expected): language="en", metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) # Then diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 1cab04f550..657ccade3a 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -76,6 +76,7 @@ def test_build_view_context_for_currency_calculated_summary( test_calculated_summary_answers_skipped_fourth, list_store, progress_store, + supplementary_data_store, mocker, return_to_answer_id, skip_fourth, @@ -121,6 +122,7 @@ def test_build_view_context_for_currency_calculated_summary( response_metadata={}, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), + supplementary_data_store=supplementary_data_store, ) context = calculated_summary_context.build_view_context() @@ -183,6 +185,7 @@ def test_build_view_context_for_return_to_calculated_summary( test_grand_calculated_summary_answers, list_store, progress_store, + supplementary_data_store, mocker, block_id, return_to_answer_id, @@ -220,6 +223,7 @@ def test_build_view_context_for_return_to_calculated_summary( current_location=Location(section_id="default-section", block_id=block_id), return_to=return_to, return_to_block_id=return_to_block_id, + supplementary_data_store=supplementary_data_store, ) context = calculated_summary_context.build_view_context() @@ -264,6 +268,7 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( test_calculated_summary_repeating_and_static_answers_schema, answer_store, progress_store, + supplementary_data_store, mocker, block_id, expected_answer_ids, @@ -295,6 +300,7 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( response_metadata={}, routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), current_location=Location(section_id="section-1", block_id=block_id), + supplementary_data_store=supplementary_data_store, ) context = calculated_summary_context.build_view_context() @@ -367,6 +373,7 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( def test_build_view_context_for_calculated_summary_with_answers_from_repeating_blocks( test_calculated_summary_repeating_blocks, progress_store, + supplementary_data_store, mocker, block_id, expected_answer_ids, @@ -397,6 +404,7 @@ def test_build_view_context_for_calculated_summary_with_answers_from_repeating_b answer_store=answer_store, list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "transport"}]), progress_store=progress_store, + supplementary_data_store=supplementary_data_store, metadata=None, response_metadata={}, routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index c29225c904..c90e0a4413 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -34,6 +34,7 @@ def test_build_view_context_for_grand_calculated_summary( test_grand_calculated_summary_schema, test_grand_calculated_summary_answers, list_store, + supplementary_data_store, mocker, return_to_answer_id, ): @@ -88,6 +89,7 @@ def test_build_view_context_for_grand_calculated_summary( current_location=Location(section_id="default-section", block_id=block_id), return_to=None, return_to_block_id=None, + supplementary_data_store=supplementary_data_store, ) context = grand_calculated_summary_context.build_view_context() diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 8ae1b64c26..76edb93862 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -9,7 +9,13 @@ @pytest.fixture -def router(schema, answer_store, list_store, progress_store): +def router( + schema, + answer_store, + list_store, + progress_store, + supplementary_data_store, +): return Router( schema, answer_store, @@ -17,11 +23,16 @@ def router(schema, answer_store, list_store, progress_store): progress_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) def test_get_not_started_row_for_section( - schema, progress_store, answer_store, list_store + schema, + progress_store, + answer_store, + list_store, + supplementary_data_store, ): expected = { "rowItems": [ @@ -50,6 +61,7 @@ def test_get_not_started_row_for_section( answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) actual = hub.get_row_context_for_section( @@ -63,7 +75,11 @@ def test_get_not_started_row_for_section( def test_get_completed_row_for_section( - schema, progress_store, answer_store, list_store + schema, + progress_store, + answer_store, + list_store, + supplementary_data_store, ): expected = { "rowItems": [ @@ -93,6 +109,7 @@ def test_get_completed_row_for_section( answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) actual = hub.get_row_context_for_section( @@ -105,7 +122,13 @@ def test_get_completed_row_for_section( assert expected == actual -def test_get_context(progress_store, answer_store, list_store, router): +def test_get_context( + progress_store, + answer_store, + list_store, + router, + supplementary_data_store, +): schema = load_schema_from_name("test_hub_and_spoke") hub = HubContext( language="en", @@ -115,6 +138,7 @@ def test_get_context(progress_store, answer_store, list_store, router): answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) expected_context = { @@ -133,7 +157,11 @@ def test_get_context(progress_store, answer_store, list_store, router): def test_get_context_custom_content_incomplete( - progress_store, answer_store, list_store, router + progress_store, + answer_store, + list_store, + router, + supplementary_data_store, ): schema = load_schema_from_name("test_hub_and_spoke_custom_content") hub_context = HubContext( @@ -144,6 +172,7 @@ def test_get_context_custom_content_incomplete( answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) expected_context = { @@ -162,7 +191,11 @@ def test_get_context_custom_content_incomplete( def test_get_context_custom_content_complete( - progress_store, answer_store, list_store, router + progress_store, + answer_store, + list_store, + router, + supplementary_data_store, ): schema = load_schema_from_name("test_hub_and_spoke_custom_content") hub_context = HubContext( @@ -173,6 +206,7 @@ def test_get_context_custom_content_complete( answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) expected_context = { @@ -191,7 +225,11 @@ def test_get_context_custom_content_complete( def test_get_context_no_list_items_survey_incomplete_individual_response_disabled( - progress_store, answer_store, list_store, router + progress_store, + answer_store, + list_store, + router, + supplementary_data_store, ): schema = load_schema_from_name("test_individual_response") hub_context = HubContext( @@ -202,6 +240,7 @@ def test_get_context_no_list_items_survey_incomplete_individual_response_disable answer_store=answer_store, metadata=get_metadata(), response_metadata={}, + supplementary_data_store=supplementary_data_store, ) assert not ( diff --git a/tests/app/views/contexts/test_list_context.py b/tests/app/views/contexts/test_list_context.py index 24b3c883b9..fd7a96a56c 100644 --- a/tests/app/views/contexts/test_list_context.py +++ b/tests/app/views/contexts/test_list_context.py @@ -14,6 +14,7 @@ def test_build_list_collector_context( people_answer_store, people_list_store, progress_store, + supplementary_data_store, ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, @@ -23,6 +24,7 @@ def test_build_list_collector_context( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( @@ -37,7 +39,11 @@ def test_build_list_collector_context( @pytest.mark.usefixtures("app") def test_build_list_summary_context_no_summary_block( - schema, people_answer_store, people_list_store, progress_store + schema, + people_answer_store, + people_list_store, + progress_store, + supplementary_data_store, ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, @@ -47,6 +53,7 @@ def test_build_list_summary_context_no_summary_block( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( @@ -64,6 +71,7 @@ def test_build_list_summary_context( people_answer_store, people_list_store, progress_store, + supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") expected = [ @@ -95,6 +103,7 @@ def test_build_list_summary_context( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( @@ -115,6 +124,7 @@ def test_assert_primary_person_string_appended( people_answer_store, people_list_store, progress_store, + supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") people_list_store["people"].primary_person = "PlwgoG" @@ -127,6 +137,7 @@ def test_assert_primary_person_string_appended( answer_store=people_answer_store, metadata=None, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( summary_definition=list_collector_block["summary"], @@ -146,6 +157,7 @@ def test_for_list_item_ids( people_answer_store, people_list_store, progress_store, + supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") @@ -157,6 +169,7 @@ def test_for_list_item_ids( answer_store=people_answer_store, metadata=None, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( summary_definition=list_collector_block["summary"], @@ -181,7 +194,10 @@ def test_for_list_item_ids( @pytest.mark.usefixtures("app") def test_list_context_items_complete_without_repeating_blocks( - people_answer_store, people_list_store, list_collector_block + people_answer_store, + people_list_store, + list_collector_block, + supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") expected = [ @@ -230,6 +246,7 @@ def test_list_context_items_complete_without_repeating_blocks( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( @@ -246,7 +263,10 @@ def test_list_context_items_complete_without_repeating_blocks( @pytest.mark.usefixtures("app") def test_list_context_items_incomplete_with_repeating_blocks( - repeating_blocks_answer_store, repeating_blocks_list_store, progress_store + repeating_blocks_answer_store, + repeating_blocks_list_store, + progress_store, + supplementary_data_store, ): schema = load_schema_from_name( "test_list_collector_repeating_blocks_section_summary" @@ -281,6 +301,7 @@ def test_list_context_items_incomplete_with_repeating_blocks( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( @@ -297,7 +318,7 @@ def test_list_context_items_incomplete_with_repeating_blocks( @pytest.mark.usefixtures("app") def test_list_context_items_complete_with_repeating_blocks( - repeating_blocks_answer_store, repeating_blocks_list_store + repeating_blocks_answer_store, repeating_blocks_list_store, supplementary_data_store ): schema = load_schema_from_name( "test_list_collector_repeating_blocks_section_summary" @@ -349,6 +370,7 @@ def test_list_context_items_complete_with_repeating_blocks( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) list_context = list_context( diff --git a/tests/app/views/contexts/test_preview_context.py b/tests/app/views/contexts/test_preview_context.py index bf0b3d3413..ae6dda20f5 100644 --- a/tests/app/views/contexts/test_preview_context.py +++ b/tests/app/views/contexts/test_preview_context.py @@ -24,6 +24,7 @@ def test_build_preview_rendering_context( progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) preview_context = preview_context() @@ -46,6 +47,7 @@ def test_build_preview_context( progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) context = preview_context() @@ -286,4 +288,5 @@ def test_preview_questions_disabled_raises_exception( progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, + supplementary_data_store=questionnaire_store.supplementary_data_store, ) diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 8c64cfbc7d..67e2a554d6 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -4,6 +4,7 @@ from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.questionnaire.routing_path import RoutingPath @@ -15,7 +16,12 @@ def test_build_summary_rendering_context( - test_section_summary_schema, answer_store, list_store, progress_store, mocker + test_section_summary_schema, + answer_store, + list_store, + progress_store, + mocker, + supplementary_data_store, ): section_summary_context = SectionSummaryContext( "en", @@ -27,6 +33,7 @@ def test_build_summary_rendering_context( response_metadata={}, current_location=Location(section_id="property-details-section"), routing_path=mocker.MagicMock(), + supplementary_data_store=supplementary_data_store, ) single_section_context = section_summary_context() @@ -35,7 +42,12 @@ def test_build_summary_rendering_context( def test_build_view_context_for_section_summary( - test_section_summary_schema, answer_store, list_store, progress_store, mocker + test_section_summary_schema, + answer_store, + list_store, + progress_store, + mocker, + supplementary_data_store, ): summary_context = SectionSummaryContext( "en", @@ -50,6 +62,7 @@ def test_build_view_context_for_section_summary( block_id="property-details-summary", ), routing_path=mocker.MagicMock(), + supplementary_data_store=supplementary_data_store, ) context = summary_context() @@ -104,6 +117,7 @@ def test_custom_section( list_store, progress_store, mocker, + supplementary_data_store, ): for answer in answers: answer_store.add_or_update(answer) @@ -118,13 +132,16 @@ def test_custom_section( response_metadata={}, current_location=location, routing_path=mocker.MagicMock(), + supplementary_data_store=supplementary_data_store, ) context = summary_context() assert context["summary"][title_key] == expected_title @pytest.mark.usefixtures("app") -def test_context_for_section_list_summary(people_answer_store, progress_store): +def test_context_for_section_list_summary( + people_answer_store, progress_store, supplementary_data_store +): schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( @@ -149,6 +166,7 @@ def test_context_for_section_list_summary(people_answer_store, progress_store): ], section_id="section", ), + supplementary_data_store=supplementary_data_store, ) context = summary_context() @@ -261,6 +279,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( answer_1_label, answer_2_label, progress_store, + supplementary_data_store, request, ): schema = load_schema_from_name(test_schema) @@ -284,6 +303,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( ], section_id="section-companies", ), + supplementary_data_store=supplementary_data_store, ) context = summary_context() expected = { @@ -460,6 +480,7 @@ def test_context_for_driving_question_summary_empty_list(): response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath(["anyone-usually-live-at"], section_id="section"), + supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -515,6 +536,7 @@ def test_context_for_driving_question_summary(progress_store): routing_path=RoutingPath( ["anyone-usually-live-at", "anyone-else-live-at"], section_id="section" ), + supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -581,6 +603,7 @@ def test_titles_for_repeating_section_summary(people_answer_store): list_item_id="PlwgoG", ), routing_path=MagicMock(), + supplementary_data_store=SupplementaryDataStore(), ) context = section_summary_context() @@ -607,6 +630,7 @@ def test_titles_for_repeating_section_summary(people_answer_store): list_item_id="UHPLbX", ), routing_path=MagicMock(), + supplementary_data_store=SupplementaryDataStore(), ) context = section_summary_context() @@ -636,6 +660,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): ], section_id="section", ), + supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -674,6 +699,7 @@ def test_primary_links_for_section_summary(people_answer_store): ], section_id="section", ), + supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() diff --git a/tests/app/views/contexts/test_submit_context.py b/tests/app/views/contexts/test_submit_context.py index 0bd749c53a..78cc34d190 100644 --- a/tests/app/views/contexts/test_submit_context.py +++ b/tests/app/views/contexts/test_submit_context.py @@ -29,7 +29,12 @@ ), ) def test_custom_submission_content( - schema_name, expected, answer_store, list_store, progress_store + schema_name, + expected, + answer_store, + list_store, + progress_store, + supplementary_data_store, ): schema = load_schema_from_name(schema_name) submit_questionnaire_context = SubmitQuestionnaireContext( @@ -40,6 +45,7 @@ def test_custom_submission_content( progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) context = submit_questionnaire_context() @@ -52,7 +58,12 @@ def test_custom_submission_content( @pytest.mark.usefixtures("app") -def test_questionnaire_context(answer_store, list_store, progress_store): +def test_questionnaire_context( + answer_store, + list_store, + progress_store, + supplementary_data_store, +): schema = load_schema_from_name("test_submit_with_summary") submit_questionnaire_context = SubmitQuestionnaireContext( "en", @@ -62,6 +73,7 @@ def test_questionnaire_context(answer_store, list_store, progress_store): progress_store, metadata={}, response_metadata={}, + supplementary_data_store=supplementary_data_store, ) context = submit_questionnaire_context() diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index 96f7ff96ac..c4af05f0e0 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -1,7 +1,12 @@ import pytest from markupsafe import Markup -from app.data_models import AnswerStore, ListStore, ProgressStore +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name from app.views.contexts.summary_context import SummaryContext @@ -105,6 +110,7 @@ def test_context_for_summary(): metadata=None, response_metadata={}, view_submitted_response=False, + supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() expected = { diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 988ad19007..c630d638ad 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -102,6 +102,12 @@ """ ) +CONTENT_ITEM_GETTER = Template( + r""" ${contentName}Content() { return `#${contentId}`; } + +""" +) + QUESTION_ERROR_PANEL = Template( r""" ${questionName}ErrorPanel() { return `#${questionOrAnswerId}-error`; } @@ -590,6 +596,10 @@ def process_guidance(context, page_spec): page_spec.write(GUIDANCE_PANEL_GETTER.safe_substitute(context)) +def process_content(context, page_spec): + page_spec.write(CONTENT_ITEM_GETTER.safe_substitute(context)) + + # pylint: disable=too-many-locals def write_summary_spec( page_spec, @@ -897,6 +907,13 @@ def process_block( has_guidance = False for content in block.get("primary_content", []): contents_block = content.get("contents") + content_context = { + "contentId": content["id"], + "contentName": camel_case( + generate_pascal_case_from_id(content["id"]) + ), + } + process_content(content_context, page_spec) if contents_block and _has_guidance_in_primary_contents(contents_block): has_guidance = True diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index 75f458fda9..3fe2cdb3e5 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -66,8 +66,11 @@ export function generateToken( userId, collectionId, responseId, + surveyId = "123", periodId = "201605", periodStr = "May 2016", + ruRef = "12346789012A", + sdsDatasetId = null, regionCode = "GB-ENG", languageCode = "en", includeLogoutUrl = true, @@ -107,7 +110,7 @@ export function generateToken( region_code: regionCode, language_code: languageCode, account_service_url: "http://localhost:8000", - survey_metadata: getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr), + survey_metadata: getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId), version: "v2", response_expires_at: isoDate, }; @@ -181,7 +184,7 @@ export function generateToken( }); } -function getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr) { +function getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId) { let surveyMetadata = {}; if (theme === "social") { @@ -194,20 +197,20 @@ function getSurveyMetadata(theme, userId, displayAddress, periodId, periodStr) { }; } else { surveyMetadata = { - survey_metadata: { - data: { - user_id: userId, - display_address: displayAddress, - ru_ref: "12346789012A", - period_id: periodId, - period_str: periodStr, - ref_p_start_date: "2017-01-01", - ref_p_end_date: "2017-02-01", - employment_date: "2016-06-10", - return_by: "2017-03-01", - ru_name: "Apple", - trad_as: "Apple", - }, + data: { + user_id: userId, + display_address: displayAddress, + ru_ref: ruRef, + survey_id: surveyId, + period_id: periodId, + period_str: periodStr, + sds_dataset_id: sdsDatasetId, + ref_p_start_date: "2017-01-01", + ref_p_end_date: "2017-02-01", + employment_date: "2016-06-10", + return_by: "2017-03-01", + ru_name: "Apple", + trad_as: "Apple", }, }; } diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js new file mode 100644 index 0000000000..47489e5d8d --- /dev/null +++ b/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js @@ -0,0 +1,175 @@ +import HubPage from "../../../base_pages/hub.page"; +import IntroductionBlockPage from "../../../generated_pages/supplementary_data/introduction-block.page.js"; +import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; +import EmailBlockPage from "../../../generated_pages/supplementary_data/email-block.page.js"; +import NewEmailPage from "../../../generated_pages/supplementary_data/new-email.page.js"; +import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; +import CalculatedSummarySalesPage from "../../../generated_pages/supplementary_data/calculated-summary-sales.page.js"; +import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; +import TradingPage from "../../../generated_pages/supplementary_data/trading.page"; +import ListCollectorPage from "../../../generated_pages/supplementary_data/list-collector.page"; +import AnyEmployeesPage from "../../../generated_pages/supplementary_data/any-employees.page"; +import Section2Page from "../../../generated_pages/supplementary_data/section-2-summary.page.js"; +import AddEmployeePage from "../../../generated_pages/supplementary_data/list-collector-add.page.js"; +import Section3Page from "../../../generated_pages/supplementary_data/section-3-summary.page.js"; +import LengthOfEmploymentPage from "../../../generated_pages/supplementary_data/length-of-employment.page.js"; +import SupermarketTransportPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport.page"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page"; + +// :TODO: this test currently only runs locally, remove the .skip once the mock endpoint is running in a container +describe.skip("Using supplementary data non list items", () => { + const summaryTitles = ".ons-summary__item-title"; + const summaryValues = ".ons-summary__values"; + const summaryRowTitles = ".ons-summary__row-title"; + + before("Starting the survey", async () => { + await browser.openQuestionnaire("test_supplementary_data.json", { + version: "v2", + sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + }); + }); + + it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { + await $(HubPage.submit()).click(); + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( + "If the company details or structure have changed contact us on 01171231231" + ); + await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); + await $(IntroductionBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(EmailBlockPage.yes()).click(); + await $(EmailBlockPage.submit()).click(); + }); + + it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { + await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).to.equal("Thursday 27 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).to.equal("Friday 28 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).to.equal("Saturday 29 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(3)).getText()).to.equal("Sunday 30 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(4)).getText()).to.equal("Monday 1 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).to.equal("Tuesday 2 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).to.equal("Wednesday 3 December 1947"); + await $(TradingPage.answerByIndex(3)).click(); + await $(TradingPage.submit()).click(); + }); + + it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { + await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); + await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); + await $(SalesBreakdownBlockPage.submit()).click(); + await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 555,000"); + }); + + it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { + await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); + await $(SalesBreakdownBlockPage.submit()).click(); + await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( + "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?" + ); + }); + + it("Given I have an interstitial block with all answers and supplementary data, When I reach this block, Then I see the placeholders rendered correctly", async () => { + await $(CalculatedSummarySalesPage.submit()).click(); + await expect(await $(Section1InterstitialPage.questionText()).getText()).to.contain("Summary of information provided for Tesco"); + await expect(await $("body").getText()).to.have.string("Telephone Number: 01171231231"); + await expect(await $("body").getText()).to.have.string("Email: contact@tesco.org"); + await expect(await $("body").getText()).to.have.string("Note Title: Value of total sales"); + await expect(await $("body").getText()).to.have.string("Note Description: Total value of goods sold during the period of the return"); + await expect(await $("body").getText()).to.have.string("Note Example Title: Including"); + await expect(await $("body").getText()).to.have.string("Note Example Description: Sales across all UK stores"); + await expect(await $("body").getText()).to.have.string("Incorporation Date: 27 November 1947"); + await expect(await $("body").getText()).to.have.string("Trading start date: 30 November 1947"); + await expect(await $("body").getText()).to.have.string("Guidance: Some supplementary guidance about the survey"); + await expect(await $("body").getText()).to.have.string("Total Uk Sales: £555,000.00"); + await expect(await $("body").getText()).to.have.string("Bristol sales: £333,000.00"); + await expect(await $("body").getText()).to.have.string("London sales: £111,000.00"); + await expect(await $("body").getText()).to.have.string("Sum of Bristol and London sales: £444,000.00"); + }); + + it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { + await $(Section1InterstitialPage.submit()).click(); + await expect(await $(Section1Page.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); + await expect(await $(Section1Page.sameEmailAnswer()).getText()).to.contain("Yes"); + await expect(await $(Section1Page.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); + await expect(await $(Section1Page.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(Section1Page.salesBristolAnswer()).getText()).to.contain("£333,000.00"); + await expect(await $(Section1Page.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + }); + + it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { + await $(Section1Page.sameEmailAnswerEdit()).click(); + await $(EmailBlockPage.no()).click(); + await $(EmailBlockPage.submit()).click(); + await $(NewEmailPage.answer()).setValue("new@tesco.com"); + await $(NewEmailPage.submit()).click(); + await $(Section1Page.previous()).click(); + await expect(await $("body").getText()).to.have.string("Email: new@tesco.com"); + await $(Section1InterstitialPage.submit()).click(); + await $(Section1Page.submit()).click(); + }); + + it("Given I add some employees via a list collector, When I begin a repeating section for that employee, Then I see static supplementary data piped in correctly", async () => { + await $(HubPage.submit()).click(); + await $(AnyEmployeesPage.yes()).click(); + await $(AnyEmployeesPage.submit()).click(); + await $(AddEmployeePage.employeeFirstName()).setValue("Jane"); + await $(AddEmployeePage.employeeLastName()).setValue("Doe"); + await $(AddEmployeePage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(AddEmployeePage.employeeFirstName()).setValue("John"); + await $(AddEmployeePage.employeeLastName()).setValue("Smith"); + await $(AddEmployeePage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(Section2Page.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Jane Doe start working for Tesco?"); + await expect(await $(LengthOfEmploymentPage.employmentStartLegend()).getText()).to.contain("Start date at Tesco"); + }); + + it("Given I have validation on the start date in the repeating section, When I enter a date before the incorporation date, Then I see an error message", async () => { + await $(LengthOfEmploymentPage.day()).setValue(1); + await $(LengthOfEmploymentPage.month()).setValue(1); + await $(LengthOfEmploymentPage.year()).setValue(1930); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); + }); + + it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { + await $(LengthOfEmploymentPage.year()).setValue(1990); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(Section3Page.lengthEmploymentQuestion()).getText()).to.contain("When did Jane Doe start working for Tesco?"); + await expect(await $(Section3Page.employmentStart()).getText()).to.contain("1 January 1990"); + }); + + it("Given I can view my response after submission, When I submit the survey, Then I see the values I've entered and correct rendering with supplementary data", async () => { + await $(Section3Page.submit()).click(); + await $(HubPage.submit()).click(); + await $(LengthOfEmploymentPage.day()).setValue(5); + await $(LengthOfEmploymentPage.month()).setValue(6); + await $(LengthOfEmploymentPage.year()).setValue(2011); + await $(LengthOfEmploymentPage.submit()).click(); + await $(Section3Page.submit()).click(); + await $(HubPage.submit()).click(); + await $(ThankYouPage.savePrintAnswersLink()).click(); + await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); + await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).to.contain("No"); + await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).to.contain("What is the new contact email for Tesco?"); + await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).to.contain("new@tesco.com"); + await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); + await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).to.contain("£333,000.00"); + await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + await expect(await $(ViewSubmittedResponsePage.group3Content(0)).$$(summaryTitles)[0].getText()).to.equal("When did Jane Doe start working for Tesco?"); + await expect(await $(ViewSubmittedResponsePage.group3Content(0)).$$(summaryValues)[0].getText()).to.equal("1 January 1990"); + await expect(await $(ViewSubmittedResponsePage.group3Content("0-1")).$$(summaryTitles)[0].getText()).to.equal( + "When did John Smith start working for Tesco?" + ); + await expect(await $(ViewSubmittedResponsePage.group3Content("0-1")).$$(summaryValues)[0].getText()).to.equal("5 June 2011"); + }); +}); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 6bfaa0d92a..bcb7d716c6 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -208,8 +208,11 @@ export const config = { userId = JwtHelper.getRandomString(10), collectionId = JwtHelper.getRandomString(10), responseId = JwtHelper.getRandomString(16), + surveyId = "123", periodId = "201605", periodStr = "May 2016", + ruRef = "12346789012A", + sdsDatasetId = null, region = "GB-ENG", language = "en", includeLogoutUrl = false, @@ -221,8 +224,11 @@ export const config = { userId, collectionId, responseId, + surveyId, periodId, periodStr, + ruRef, + sdsDatasetId, regionCode: region, languageCode: language, includeLogoutUrl, diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 6af59e1cce..87c43a0df1 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -139,14 +139,14 @@ def create_token_v2(self, schema_name, theme="default", **extra_payload): return self.generate_token(payload) - def create_supplementary_data_token( - self, schema_name, sds_dataset_id=None, **extra_payload - ): - payload = PAYLOAD_V2_SUPPLEMENTARY_DATA - - if sds_dataset_id: - payload = deepcopy(payload) - payload["survey_metadata"]["data"]["sds_dataset_id"] = sds_dataset_id + def create_supplementary_data_token(self, schema_name, **extra_payload): + payload = deepcopy(PAYLOAD_V2_SUPPLEMENTARY_DATA) + + # iterate over a copy so items can be deleted + for key, value in list(extra_payload.items()): + if key in {"sds_dataset_id", "ru_ref", "survey_id"}: + payload["survey_metadata"]["data"][key] = value + del extra_payload[key] payload = self._get_payload_with_params( schema_name=schema_name, payload=payload, **extra_payload diff --git a/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py b/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py new file mode 100644 index 0000000000..b63a102135 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py @@ -0,0 +1,60 @@ +import pytest + +from tests.integration.integration_test_case import IntegrationTestCase + + +# :TODO: this test currently only runs locally, remove the skip once the mock endpoint is running in a container +@pytest.mark.skip("This test can only run locally at the moment") +class TestQuestionnaireSupplementaryDataNonListItems(IntegrationTestCase): + def test_supplementary_data_non_list_items(self): + self.launchSupplementaryDataSurvey( + "test_supplementary_data", + sds_dataset_id="693dc252-2e90-4412-bd9c-c4d953e36fcd", + ru_ref="12346789012A", + survey_id="123", + ) + self.post() + self.assertInBody("You are completing this survey for Lidl") + self.assertInBody( + "If the company details or structure have changed contact us on 01174564561" + ) + + # as there is no supplementary guidance, the list should only contain the two static pieces. + guidance_list_items = ( + self.getHtmlSoup().find("div", {"id": "business-details"}).findAll("li") + ) + assert len(guidance_list_items) == 2 + self.post() + self.post() + self.post({"same-email-answer": "Yes"}) + self.post({"trading-answer": "1947-11-30"}) + + # supplementary data can be used for validation + self.post({"sales-bristol-answer": "444000", "sales-london-answer": "222000"}) + self.assertInBody("Enter answers that add up to or are less than 555,000") + + # a calculated sum equal to the supplementary data value should be allowed and show correctly + self.post({"sales-bristol-answer": "444000", "sales-london-answer": "111000"}) + self.assertInBody( + "Total value of sales from Bristol and London is calculated to be £555,000.00. Is this correct?" + ) + self.post() + + # all supplementary data values can be piped into the interstitial block + self.assertInBody("Telephone Number: 01174564561") + self.assertInBody("Email: contact@lidl.org") + self.assertInBody("Note Title: Value of total sales") + self.assertInBody("Note Description: Total value of goods") + self.assertInBody("Note Example Title: Includes") + self.assertInBody( + "Note Example Description: Sales across all EU stores" + ) + self.assertInBody("Incorporation Date: 27 November 1947") + self.assertInBody("Trading start date: 30 November 1947") + self.assertInBody("Guidance: None") + self.assertInBody("Total Uk Sales: £555,000.00") + self.assertInBody("Bristol sales: £444,000.00") + self.assertInBody("London sales: £111,000.00") + self.assertInBody( + "Sum of Bristol and London sales: £555,000.00" + ) From a3ec81599cb845d5e0ebd840a545103a7612b175 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:47:26 +0100 Subject: [PATCH 254/567] Make placeholders to take into account answers on a path (#1094) Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Co-authored-by: petechd --- app/data_models/progress_store.py | 27 +- app/forms/questionnaire_form.py | 40 +- app/questionnaire/dependencies.py | 57 +- app/questionnaire/placeholder_parser.py | 115 ++-- app/questionnaire/questionnaire_schema.py | 43 ++ .../questionnaire_store_updater.py | 10 +- app/questionnaire/router.py | 11 +- app/questionnaire/value_source_resolver.py | 16 +- app/utilities/mappings.py | 4 +- app/utilities/types.py | 7 +- ...test_placeholder_first_non_empty_item.json | 275 +++++++++ ...empty_item_cross_section_dependencies.json | 283 ++++++++++ ...rst_non_empty_item_repeating_sections.json | 528 ++++++++++++++++++ .../questionnaire/test_placeholder_parser.py | 117 ++++ .../views/contexts/test_summary_context.py | 1 - .../placeholder_answers_on_the_path.spec.js | 110 ++++ 16 files changed, 1541 insertions(+), 103 deletions(-) create mode 100644 schemas/test/en/test_placeholder_first_non_empty_item.json create mode 100644 schemas/test/en/test_placeholder_first_non_empty_item_cross_section_dependencies.json create mode 100644 schemas/test/en/test_placeholder_first_non_empty_item_repeating_sections.json create mode 100644 tests/functional/spec/placeholder_answers_on_the_path.spec.js diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index 777c2f624b..0b7384cc4d 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -3,9 +3,7 @@ from app.data_models.progress import Progress, ProgressDictType from app.questionnaire.location import Location -from app.utilities.types import LocationType - -ProgressKeyType = tuple[str, str | None] +from app.utilities.types import LocationType, SectionKey @dataclass @@ -47,11 +45,11 @@ def __init__( self._is_dirty: bool = False self._is_routing_backwards: bool = False self._section_and_repeating_blocks_progress: MutableMapping[ - ProgressKeyType, Progress + SectionKey, Progress ] = self._build_map(in_progress_sections_and_repeating_blocks or []) def __contains__( - self, section_and_repeating_blocks_progress_key: ProgressKeyType + self, section_and_repeating_blocks_progress_key: SectionKey ) -> bool: return ( section_and_repeating_blocks_progress_key @@ -116,7 +114,7 @@ def section_and_repeating_blocks_progress_keys( self, statuses: Iterable[str] | None = None, section_ids: Iterable[str] | None = None, - ) -> list[ProgressKeyType]: + ) -> list[SectionKey]: """ Return the Keys of the Section and Repeating Blocks progresses stored in this ProgressStore. """ @@ -148,7 +146,7 @@ def update_section_or_repeating_blocks_progress_completion_status( Updates the completion status of the section or repeating blocks for a list item specified by the key based on the given section id and list item id. """ updated = False - section_key = (section_id, list_item_id) + section_key = SectionKey(section_id, list_item_id) if section_key in self._section_and_repeating_blocks_progress: if ( self._section_and_repeating_blocks_progress[section_key].status @@ -179,7 +177,7 @@ def get_section_or_repeating_blocks_progress_status( specified by the given section_id and list_item_id. Returns NOT_STARTED if the progress does not exist """ - progress_key = (section_id, list_item_id) + progress_key = SectionKey(section_id, list_item_id) if progress_key in self._section_and_repeating_blocks_progress: return self._section_and_repeating_blocks_progress[progress_key].status @@ -208,7 +206,7 @@ def get_completed_block_ids( Return the block ids recorded as part of the progress for the Section or Repeating Blocks for list item specified by the given section_id and list_item_id """ - progress_key = (section_id, list_item_id) + progress_key = SectionKey(section_id, list_item_id) if progress_key in self._section_and_repeating_blocks_progress: return self._section_and_repeating_blocks_progress[progress_key].block_ids @@ -228,9 +226,7 @@ def add_completed_location(self, location: LocationType) -> None: if location.block_id not in completed_block_ids: completed_block_ids.append(location.block_id) # type: ignore - - progress_key = (section_id, list_item_id) - + progress_key = SectionKey(section_id, list_item_id) if progress_key in self._section_and_repeating_blocks_progress: self._section_and_repeating_blocks_progress[ progress_key @@ -250,7 +246,7 @@ def remove_completed_location(self, location: LocationType) -> bool: Removes the block in the given Location, from the progress specified by the section id and list item id within the Location if it exists in the store. """ - progress_key = (location.section_id, location.list_item_id) + progress_key = SectionKey(location.section_id, location.list_item_id) if ( progress_key in self._section_and_repeating_blocks_progress and location.block_id @@ -276,9 +272,8 @@ def remove_progress_for_list_item_id(self, list_item_id: str) -> None: *Not efficient.* """ - progress_keys_to_delete = [ - (section_id, progress_list_item_id) + SectionKey(section_id, progress_list_item_id) for section_id, progress_list_item_id in self._section_and_repeating_blocks_progress if progress_list_item_id == list_item_id ] @@ -301,7 +296,7 @@ def clear(self) -> None: def started_section_and_repeating_blocks_progress_keys( self, section_ids: Iterable[str] | None = None - ) -> list[ProgressKeyType]: + ) -> list[SectionKey]: return self.section_and_repeating_blocks_progress_keys( statuses={CompletionStatus.COMPLETED, CompletionStatus.IN_PROGRESS}, section_ids=section_ids, diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 35d60251d3..a60548c61f 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -25,14 +25,14 @@ from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType from app.questionnaire.dependencies import ( - get_block_ids_for_calculated_summary_dependencies, + get_routing_path_block_ids_by_section_for_calculated_summary_dependencies, ) from app.questionnaire.path_finder import PathFinder from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.utilities.mappings import get_flattened_mapping_values -from app.utilities.types import LocationType +from app.utilities.types import LocationType, SectionKey logger = logging.getLogger(__name__) @@ -489,28 +489,30 @@ def get_answer_fields( ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None - routing_path_block_ids: dict[tuple, tuple[str, ...]] = {} + block_ids_by_section: dict[SectionKey, tuple[str, ...]] = {} if location and progress_store: - routing_path_block_ids = get_block_ids_for_calculated_summary_dependencies( - schema=schema, - location=location, - progress_store=progress_store, - path_finder=PathFinder( - schema=schema, - answer_store=answer_store, - list_store=list_store, + block_ids_by_section = ( + get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + location=location, progress_store=progress_store, - metadata=metadata, - response_metadata=response_metadata, - supplementary_data_store=supplementary_data_store, - ), - data=question, + path_finder=PathFinder( + schema=schema, + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + metadata=metadata, + response_metadata=response_metadata, + supplementary_data_store=supplementary_data_store, + ), + data=question, + ignore_keys=["when"], + schema=schema, + ) ) - block_ids = None - if routing_path_block_ids: - block_ids = get_flattened_mapping_values(routing_path_block_ids) + if block_ids_by_section: + block_ids = get_flattened_mapping_values(block_ids_by_section) def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResolver: return ValueSourceResolver( diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index 6268aecd66..7868e99e9d 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -9,43 +9,41 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.questionnaire_schema import get_sources_for_type_from_data from app.utilities.mappings import get_flattened_mapping_values -from app.utilities.types import LocationType +from app.utilities.types import LocationType, SectionKey if TYPE_CHECKING: from app.questionnaire.path_finder import PathFinder # pragma: no cover -def get_block_ids_for_calculated_summary_dependencies( - schema: QuestionnaireSchema, +def get_routing_path_block_ids_by_section_for_dependent_sections( + *, location: LocationType, progress_store: ProgressStore, path_finder: PathFinder, + source_type: str, data: MultiDict | Mapping | Sequence, sections_to_ignore: list | None = None, -) -> dict[tuple, tuple[str, ...]]: - block_ids_by_section: dict[tuple, tuple[str, ...]] = {} - + ignore_keys: list[str] | None = None, + dependent_sections: dict[str, set[str]] | dict[str, OrderedSet[str]], +) -> dict[SectionKey, tuple[str, ...]]: + block_ids_by_section: dict[SectionKey, tuple[str, ...]] = {} sections_to_ignore = sections_to_ignore or [] - dependent_sections = schema.calculated_summary_section_dependencies_by_block[ - location.section_id - ] - if block_id := location.block_id: - dependents = OrderedSet(dependent_sections[block_id]) - else: - dependents = get_flattened_mapping_values(dependent_sections) + dependents = ( + OrderedSet(dependent_sections.get(location.block_id, [])) + if location.block_id + else get_flattened_mapping_values(dependent_sections) + ) if dependents and not get_sources_for_type_from_data( - source_type="calculated_summary", - data=data, - ignore_keys=["when"], + source_type=source_type, data=data, ignore_keys=ignore_keys ): return block_ids_by_section for section in dependents: # Dependent sections other than the current section cannot be a repeating section list_item_id = location.list_item_id if section == location.section_id else None - key = (section, list_item_id) + key = SectionKey(section, list_item_id) if key in sections_to_ignore: continue @@ -55,3 +53,28 @@ def get_block_ids_for_calculated_summary_dependencies( block_ids_by_section[key] = routing_path.block_ids return block_ids_by_section + + +def get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + *, + location: LocationType, + progress_store: ProgressStore, + path_finder: PathFinder, + data: MultiDict | Mapping | Sequence, + sections_to_ignore: list | None = None, + ignore_keys: list[str] | None = None, + schema: QuestionnaireSchema, +) -> dict[SectionKey, tuple[str, ...]]: + dependent_sections = schema.calculated_summary_section_dependencies_by_block[ + location.section_id + ] + return get_routing_path_block_ids_by_section_for_dependent_sections( + location=location, + progress_store=progress_store, + sections_to_ignore=sections_to_ignore, + data=data, + path_finder=path_finder, + source_type="calculated_summary", + ignore_keys=ignore_keys, + dependent_sections=dependent_sections, + ) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index e95edba07b..9720bc3d27 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -1,9 +1,15 @@ from __future__ import annotations from decimal import Decimal -from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, Sequence, TypeAlias - -from ordered_set import OrderedSet +from typing import ( + TYPE_CHECKING, + Any, + Iterable, + Mapping, + MutableMapping, + Sequence, + TypeAlias, +) from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore @@ -12,16 +18,18 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire import path_finder as pf from app.questionnaire.dependencies import ( - get_block_ids_for_calculated_summary_dependencies, + get_routing_path_block_ids_by_section_for_calculated_summary_dependencies, + get_routing_path_block_ids_by_section_for_dependent_sections, ) from app.questionnaire.placeholder_transforms import PlaceholderTransforms +from app.questionnaire.questionnaire_schema import TRANSFORMS_REQUIRING_ROUTING_PATH from app.questionnaire.value_source_resolver import ( ValueSourceEscapedTypes, ValueSourceResolver, ValueSourceTypes, ) from app.utilities.mappings import get_flattened_mapping_values -from app.utilities.types import LocationType +from app.utilities.types import LocationType, SectionKey if TYPE_CHECKING: from app.questionnaire.placeholder_renderer import ( @@ -83,12 +91,11 @@ def __init__( def __call__( self, placeholder_list: Sequence[Mapping] ) -> MutableMapping[str, ValueSourceEscapedTypes | ValueSourceTypes]: - placeholder_list = QuestionnaireSchema.get_mutable_deepcopy(placeholder_list) - sections_to_ignore = list(self._routing_path_block_ids_by_section_key) - if routing_path_block_ids_map := self._get_routing_path_block_ids( - sections_to_ignore=sections_to_ignore, data=placeholder_list + if routing_path_block_ids_map := self._get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + data=placeholder_list, + sections_to_ignore=sections_to_ignore, ): self._routing_path_block_ids_by_section_key.update( routing_path_block_ids_map @@ -98,7 +105,7 @@ def __call__( routing_path_block_ids_map ) self._value_source_resolver = self._get_value_source_resolver( - routing_path_block_ids + routing_path_block_ids=routing_path_block_ids, ) for placeholder in placeholder_list: @@ -110,7 +117,10 @@ def __call__( return self._placeholder_map def _get_value_source_resolver( - self, routing_path_block_ids: OrderedSet[str] | None = None + self, + *, + routing_path_block_ids: Iterable[str] | None = None, + assess_routing_path: bool | None = False, ) -> ValueSourceResolver: return ValueSourceResolver( answer_store=self._answer_store, @@ -122,7 +132,7 @@ def _get_value_source_resolver( escape_answer_values=True, response_metadata=self._response_metadata, use_default_answer=True, - assess_routing_path=False, + assess_routing_path=assess_routing_path, routing_path_block_ids=routing_path_block_ids, progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, @@ -146,19 +156,23 @@ def _parse_transforms( for transform in transform_list: transform_args: MutableMapping = {} - + value_source_resolver = self._get_value_source_resolver_for_transform( + transform + ) for arg_key, arg_value in transform["arguments"].items(): resolved_value: ValueSourceEscapedTypes | ValueSourceTypes | TransformedValueTypes if isinstance(arg_value, list): - resolved_value = self._resolve_value_source_list(arg_value) + resolved_value = value_source_resolver.resolve_list( + value_source_list=arg_value, + ) elif isinstance(arg_value, dict): if "value" in arg_value: resolved_value = arg_value["value"] elif arg_value["source"] == "previous_transform": resolved_value = transformed_value else: - resolved_value = self._value_source_resolver.resolve(arg_value) + resolved_value = value_source_resolver.resolve(arg_value) else: resolved_value = arg_value @@ -170,32 +184,65 @@ def _parse_transforms( return transformed_value - def _resolve_value_source_list( - self, value_source_list: list[dict] - ) -> list[ValueSourceTypes]: - values: list[ValueSourceTypes] = [] - for value_source in value_source_list: - value = self._value_source_resolver.resolve(value_source) - if isinstance(value, list): - values.extend(value) - else: - values.append(value) - return values - - def _get_routing_path_block_ids( - self, data: Sequence[Mapping], sections_to_ignore: list | None = None - ) -> dict[tuple, tuple[str, ...]] | None: - if self._location: - return get_block_ids_for_calculated_summary_dependencies( - schema=self._schema, + def _get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + self, + data: Sequence[Mapping], + sections_to_ignore: list[str] | None = None, + ) -> dict[SectionKey, tuple[str, ...]] | None: + if not self._location: + return {} + + return ( + get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( location=self._location, progress_store=self._progress_store, sections_to_ignore=sections_to_ignore, data=data, path_finder=self._path_finder, + ignore_keys=["when"], + schema=self._schema, ) - return {} + ) def _all_value_sources_metadata(self, placeholder: Mapping) -> bool: sources = self._schema.get_values_for_key(placeholder, key="source") return all(source == "metadata" for source in sources) + + def _get_value_source_resolver_for_transform( + self, transform: Mapping + ) -> ValueSourceResolver: + if ( + self._location + and transform["transform"] in TRANSFORMS_REQUIRING_ROUTING_PATH + ): + dependent_sections = ( + self._schema.placeholder_transform_section_dependencies_by_block[ + self._location.section_id + ] + ) + block_ids = get_routing_path_block_ids_by_section_for_dependent_sections( + location=self._location, + progress_store=self._progress_store, + sections_to_ignore=["when"], + path_finder=self._path_finder, + data=transform, + source_type="answers", + dependent_sections=dependent_sections, + ) + self._routing_path_block_ids_by_section_key.update(block_ids) + transform_routing_path_block_ids = get_flattened_mapping_values(block_ids) + + value_source_routing_block_ids = ( + self._value_source_resolver.routing_path_block_ids or set() + ) + + routing_path_block_ids = ( + set(value_source_routing_block_ids) | transform_routing_path_block_ids + ) + + return self._get_value_source_resolver( + routing_path_block_ids=routing_path_block_ids, + assess_routing_path=True, + ) + + return self._value_source_resolver diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index cfff180db3..e11a43dc4c 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -32,6 +32,8 @@ DependencyDictType: TypeAlias = dict[str, OrderedSet[str]] +TRANSFORMS_REQUIRING_ROUTING_PATH = {"first_non_empty_item"} + class InvalidSchemaConfigurationException(Exception): pass @@ -80,6 +82,9 @@ def __init__( self._when_rules_section_dependencies_by_answer: dict[ str, set[str] ] = defaultdict(set) + self._placeholder_transform_section_dependencies_by_block: dict[ + str, dict[str, set[str]] + ] = defaultdict(lambda: defaultdict(set)) self._language_code = language_code self._questionnaire_json = questionnaire_json self._list_names_by_list_repeating_block_id: dict[str, str] = {} @@ -99,6 +104,13 @@ def __init__( self._populate_answer_dependencies() self._populate_when_rules_section_dependencies() self._populate_calculated_summary_section_dependencies() + self._populate_placeholder_transform_section_dependencies() + + @property + def placeholder_transform_section_dependencies_by_block( + self, + ) -> dict[str, dict[str, set[str]]]: + return self._placeholder_transform_section_dependencies_by_block @cached_property def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: @@ -1336,6 +1348,15 @@ def _get_calculated_summary_section_dependencies( return section_dependencies + def _get_section_ids_for_answer_ids(self, answer_ids: set[str]) -> OrderedSet[str]: + section_dependencies: OrderedSet[str] = OrderedSet() + for answer_id in answer_ids: + block = self.get_block_for_answer_id(answer_id) + # Type ignore: block_id and section_id is never None + section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore + section_dependencies.add(section_id) # type: ignore + return section_dependencies + def get_summary_item_for_list_for_section( self, *, section_id: str, list_name: str ) -> ImmutableDict | None: @@ -1364,6 +1385,28 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None: if item["for_list"] == list_name and item.get("item_anchor_answer_id"): return f"#{str(item['item_anchor_answer_id'])}" + def _populate_placeholder_transform_section_dependencies(self) -> None: + for block in self.get_blocks(): + transforms = get_mappings_with_key("transform", block) + placeholder_answer_ids = { + item["identifier"] + for transform in transforms + if transform["transform"] in TRANSFORMS_REQUIRING_ROUTING_PATH + for item in transform["arguments"]["items"] + if item.get("source") == "answers" + } + placeholder_dependencies = self._get_section_ids_for_answer_ids( + answer_ids=placeholder_answer_ids + ) + if placeholder_dependencies: + # Type Ignore: At this point we section id and block id cannot be None + section_id = self.get_section_id_for_block_id(block["id"]) + self._placeholder_transform_section_dependencies_by_block[section_id][ # type: ignore + block["id"] + ].update( + placeholder_dependencies + ) + def update_dependencies_for_dynamic_answers( self, *, question: Mapping, block_id: str ) -> None: diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 32fd4b25ff..d0541c8642 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -7,13 +7,13 @@ from app.data_models import AnswerValueTypes, QuestionnaireStore from app.data_models.answer_store import Answer -from app.data_models.progress_store import CompletionStatus, ProgressKeyType +from app.data_models.progress_store import CompletionStatus from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent from app.questionnaire.router import Router -from app.utilities.types import LocationType +from app.utilities.types import LocationType, SectionKey DependentSection = namedtuple("DependentSection", "section_id list_item_id is_complete") @@ -41,7 +41,7 @@ def __init__( self._router = router self.dependent_block_id_by_section_key: Mapping[ - ProgressKeyType, set[str] + SectionKey, set[str] ] = defaultdict(set) self.dependent_sections: set[DependentSection] = set() @@ -313,7 +313,7 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: ) self.dependent_block_id_by_section_key[ - (dependency.section_id, list_item_id) + SectionKey(dependency.section_id, list_item_id) ].add(dependency.block_id) def _get_list_item_ids_for_dependency( @@ -537,7 +537,7 @@ def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: def started_section_keys( self, section_ids: Iterable[str] | None = None - ) -> list[ProgressKeyType]: + ) -> list[SectionKey]: return self._progress_store.started_section_and_repeating_blocks_progress_keys( section_ids ) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 7ed5ff2948..0317baf869 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -9,13 +9,12 @@ SupplementaryDataStore, ) from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import ProgressKeyType from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.utilities.types import LocationType +from app.utilities.types import LocationType, SectionKey class Router: @@ -435,7 +434,7 @@ def get_next_location_url_for_end_of_section(self) -> str: return self.get_first_incomplete_location_in_questionnaire_url() def get_section_resume_url(self, routing_path: RoutingPath) -> str: - section_key = (routing_path.section_id, routing_path.list_item_id) + section_key = SectionKey(routing_path.section_id, routing_path.list_item_id) if section_key in self._progress_store: location = self._get_first_incomplete_location_in_section(routing_path) @@ -527,16 +526,16 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: def get_enabled_section_keys( self, - ) -> Generator[ProgressKeyType, None, None]: + ) -> Generator[SectionKey, None, None]: for section_id in self.enabled_section_ids: repeating_list = self._schema.get_repeating_list_for_section(section_id) if repeating_list: for list_item_id in self._list_store[repeating_list]: - section_key: ProgressKeyType = (section_id, list_item_id) + section_key = SectionKey(section_id, list_item_id) yield section_key else: - section_key = (section_id, None) + section_key = SectionKey(section_id, None) yield section_key def _get_first_incomplete_section_key(self) -> tuple[str, str | None] | None: diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index c9dd501529..422f628554 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -39,7 +39,7 @@ class ValueSourceResolver: routing_path_block_ids: Iterable[str] | None = None use_default_answer: bool = False escape_answer_values: bool = False - assess_routing_path: bool = True + assess_routing_path: bool | None = True def _is_answer_on_path(self, answer_id: str) -> bool: if self.routing_path_block_ids: @@ -174,7 +174,8 @@ def _resolve_answer_value_source( return self._resolve_list_repeating_block_answers(answer_id) answer_value = self._get_answer_value( - answer_id=answer_id, list_item_id=list_item_id + answer_id=answer_id, + list_item_id=list_item_id, ) if isinstance(answer_value, Mapping): @@ -283,6 +284,16 @@ def _resolve_location_source(self, value_source: Mapping) -> str | None: def _resolve_response_metadata_source(self, value_source: Mapping) -> str | None: return self.response_metadata.get(value_source.get("identifier")) + def resolve_list(self, value_source_list: list[Mapping]) -> list[ValueSourceTypes]: + values: list[ValueSourceTypes] = [] + for value_source in value_source_list: + value = self.resolve(value_source) + if isinstance(value, list): + values.extend(value) + else: + values.append(value) + return values + def _resolve_supplementary_data_source( self, value_source: Mapping ) -> ValueSourceTypes: @@ -310,7 +321,6 @@ def resolve( return self._resolve_calculated_summary_value_source( value_source=value_source, assess_routing_path=True ) - resolve_method_mapping = { "answers": self._resolve_answer_value_source, "list": self._resolve_list_value_source, diff --git a/app/utilities/mappings.py b/app/utilities/mappings.py index 7069a25155..f0dd2e17fb 100644 --- a/app/utilities/mappings.py +++ b/app/utilities/mappings.py @@ -2,9 +2,11 @@ from ordered_set import OrderedSet +from app.utilities.types import SectionKey + def get_flattened_mapping_values( - map_to_flatten: Mapping[tuple, Iterable[str]] | Mapping[str, Iterable[str]] + map_to_flatten: Mapping[SectionKey, Iterable[str]] | Mapping[str, Iterable[str]] ) -> OrderedSet[str]: return OrderedSet([x for v in map_to_flatten.values() for x in v]) diff --git a/app/utilities/types.py b/app/utilities/types.py index 9217bb6207..9bcf57b6ff 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -1,6 +1,11 @@ -from typing import TypeAlias +from typing import NamedTuple, TypeAlias from app.questionnaire.location import Location from app.questionnaire.relationship_location import RelationshipLocation LocationType: TypeAlias = Location | RelationshipLocation + + +class SectionKey(NamedTuple): + section_id: str + list_item_id: str | None diff --git a/schemas/test/en/test_placeholder_first_non_empty_item.json b/schemas/test/en/test_placeholder_first_non_empty_item.json new file mode 100644 index 0000000000..a0c6d6e8df --- /dev/null +++ b/schemas/test/en/test_placeholder_first_non_empty_item.json @@ -0,0 +1,275 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Other input fields", + "theme": "default", + "description": "Questionnaire to check placeholder takes account answers on a path", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "ru_ref", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "id": "default-group", + "blocks": [ + { + "id": "date-question-block", + "type": "Question", + "question": { + "id": "date-question", + "title": { + "text": "Are you able to report for the period from {ref_p_start_date} to {ref_p_end_date}?", + "placeholders": [ + { + "placeholder": "ref_p_start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ref_p_end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "date-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes, I can report for this period", + "value": "Yes, I can report for this period" + }, + { + "label": "No, I need to report for a different period", + "value": "No, I need to report for a different period" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "total-turnover-block", + "when": { + "in": [ + { + "identifier": "date-answer", + "source": "answers" + }, + ["Yes, I can report for this period"] + ] + } + }, + { + "block": "date-entry-block" + } + ] + }, + { + "id": "date-entry-block", + "type": "Question", + "question": { + "id": "date-entry-question", + "title": "What are the dates of the period that you will be reporting for?", + "guidance": { + "contents": [ + { + "description": "Enter a date between 1st of May 2016 and the 31st of May 2016" + } + ] + }, + "type": "DateRange", + "answers": [ + { + "id": "date-entry-answer-from", + "type": "Date", + "mandatory": true, + "label": "From", + "minimum": { + "value": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "offset_by": { + "days": -19 + } + } + }, + { + "id": "date-entry-answer-to", + "type": "Date", + "mandatory": true, + "label": "To", + "maximum": { + "value": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "offset_by": { + "days": 20 + } + } + } + ] + } + }, + { + "id": "total-turnover-block", + "type": "Question", + "question": { + "id": "total-turnover-question", + "title": { + "text": "For the period {date_entry_answer_from} to {date_entry_answer_to}, what was {ru_name}'s total turnover, excluding VAT?", + "placeholders": [ + { + "placeholder": "date_entry_answer_from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-from" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "date_entry_answer_to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-to" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "General", + "answers": [ + { + "id": "total-turnover-answer", + "mandatory": true, + "type": "Currency", + "label": "Total turnover excluding VAT", + "description": "Enter the full value (e.g. 56,234.33) or a value to the nearest £thousand (e.g. 56,000). Do not enter '56' for £56,000.", + "decimal_places": 2, + "currency": "GBP" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_placeholder_first_non_empty_item_cross_section_dependencies.json b/schemas/test/en/test_placeholder_first_non_empty_item_cross_section_dependencies.json new file mode 100644 index 0000000000..ec3c2f1734 --- /dev/null +++ b/schemas/test/en/test_placeholder_first_non_empty_item_cross_section_dependencies.json @@ -0,0 +1,283 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Other input fields", + "theme": "default", + "description": "Questionnaire to check placeholder takes account answers on a path with cross section dependencies", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "ru_ref", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "default-section", + "title": "Date Selection", + "groups": [ + { + "id": "default-group", + "blocks": [ + { + "id": "date-question-block", + "type": "Question", + "question": { + "id": "date-question", + "title": { + "text": "Are you able to report for the period from {ref_p_start_date} to {ref_p_end_date}?", + "placeholders": [ + { + "placeholder": "ref_p_start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ref_p_end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "date-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes, I can report for this period", + "value": "Yes, I can report for this period" + }, + { + "label": "No, I need to report for a different period", + "value": "No, I need to report for a different period" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "in": [ + { + "identifier": "date-answer", + "source": "answers" + }, + ["Yes, I can report for this period"] + ] + } + }, + { + "block": "date-entry-block" + } + ] + }, + { + "id": "date-entry-block", + "type": "Question", + "question": { + "id": "date-entry-question", + "title": "What are the dates of the period that you will be reporting for?", + "guidance": { + "contents": [ + { + "description": "Enter a date between 1st of May 2016 and the 31st of May 2016" + } + ] + }, + "type": "DateRange", + "answers": [ + { + "id": "date-entry-answer-from", + "type": "Date", + "mandatory": true, + "label": "From", + "minimum": { + "value": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "offset_by": { + "days": -19 + } + } + }, + { + "id": "date-entry-answer-to", + "type": "Date", + "mandatory": true, + "label": "To", + "maximum": { + "value": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "offset_by": { + "days": 20 + } + } + } + ] + } + } + ] + } + ] + }, + { + "id": "second-section", + "title": "Food Expenses", + "groups": [ + { + "id": "second-section-group", + "blocks": [ + { + "id": "food-question-block", + "type": "Question", + "question": { + "id": "food-question", + "title": { + "text": "For the period {date_entry_answer_from} to {date_entry_answer_to}, how much do you spend on food?", + "placeholders": [ + { + "placeholder": "date_entry_answer_from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-from" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "date_entry_answer_to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-to" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "General", + "answers": [ + { + "id": "food-answer", + "mandatory": true, + "type": "Currency", + "label": "Total food expense", + "description": "Enter the full value (e.g. 32.33) or a value to the nearest £thousand (e.g. 56,000). Do not enter '56' for £56,000.", + "decimal_places": 2, + "currency": "GBP" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_placeholder_first_non_empty_item_repeating_sections.json b/schemas/test/en/test_placeholder_first_non_empty_item_repeating_sections.json new file mode 100644 index 0000000000..5feccb91fd --- /dev/null +++ b/schemas/test/en/test_placeholder_first_non_empty_item_repeating_sections.json @@ -0,0 +1,528 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test first non empty item with repeating sections", + "theme": "default", + "description": "Questionnaire to test first non empty item with repeating sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "date-section", + "title": "Date Range", + "groups": [ + { + "id": "date-group", + "blocks": [ + { + "id": "date-question-block", + "type": "Question", + "question": { + "id": "date-question", + "title": { + "text": "Are you able to report for the period from {ref_p_start_date} to {ref_p_end_date}?", + "placeholders": [ + { + "placeholder": "ref_p_start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ref_p_end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "date-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes, I can report for this period", + "value": "Yes, I can report for this period" + }, + { + "label": "No, I need to report for a different period", + "value": "No, I need to report for a different period" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "in": [ + { + "identifier": "date-answer", + "source": "answers" + }, + ["Yes, I can report for this period"] + ] + } + }, + { + "block": "date-entry-block" + } + ] + }, + { + "id": "date-entry-block", + "type": "Question", + "question": { + "id": "date-entry-question", + "title": "What are the dates of the period that you will be reporting for?", + "guidance": { + "contents": [ + { + "description": "Enter a date between 1st of May 2016 and the 31st of May 2016" + } + ] + }, + "type": "DateRange", + "answers": [ + { + "id": "date-entry-answer-from", + "type": "Date", + "mandatory": true, + "label": "From", + "minimum": { + "value": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "offset_by": { + "days": -19 + } + } + }, + { + "id": "date-entry-answer-to", + "type": "Date", + "mandatory": true, + "label": "To", + "maximum": { + "value": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "offset_by": { + "days": 20 + } + } + } + ] + } + } + ] + } + ] + }, + { + "id": "list-collector-section", + "title": "List Collector Section", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "people", + "title": "Household members", + "add_link_text": "Add someone to this household", + "empty_list_text": "There are no householders" + } + ] + }, + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "title": { + "text": "Between {date_entry_answer_from} to {date_entry_answer_to} did anyone else live here?", + "placeholders": [ + { + "placeholder": "date_entry_answer_from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-from" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "date_entry_answer_to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-to" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add anyone else?", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this person?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "warning": "All of the information about this person will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "repeating-section", + "title": "Personal Details", + "summary": { "show_on_completion": true }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "personal-details-group", + "title": "Personal Details", + "blocks": [ + { + "type": "Question", + "id": "personal-details-block", + "question": { + "type": "General", + "id": "personal-details-question", + "title": { + "text": "Between {date_entry_answer_from} to {date_entry_answer_to} what did you drink the most?", + "placeholders": [ + { + "placeholder": "date_entry_answer_from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-from" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "date_entry_answer_to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-to" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "answers": [ + { + "id": "personal-details-answer", + "mandatory": true, + "options": [ + { + "label": "Coffee", + "value": "Coffee" + }, + { + "label": "Tea", + "value": "Tea" + } + ], + "type": "Checkbox" + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 8cc719d1ee..cf0318b45e 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1087,6 +1087,15 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render def test_placeholder_parser_calculated_summary_dependencies_cache( mocker, mock_renderer ): + """ + Tests Calculated Summaries fetches the dependencies using the routing path cache + Mocker patch the routing_path function in the Path Finder class to check the number of calls + Both placeholders lists use the calculated summary placeholder that requires the Path. + The first and second placeholder list is from the same section so when we call the second list, it should use the cache from the first call. + Set Location to the BlockId where the transform is required and the values have already been set + Set Answer Store with values to check if the transform is working as expected in the Schema. + With calculated summaries we check the two values in the answer source sum to the expected number + """ schema = load_schema_from_name("test_calculated_summary") path_finder = mocker.patch("app.questionnaire.path_finder.PathFinder.routing_path") @@ -1162,3 +1171,111 @@ def test_placeholder_parser_calculated_summary_dependencies_cache( placeholder_2 = placeholder_parser(placeholder_list=placeholder_list_2) assert placeholder_2["unit-total-playback"] == 11 assert path_finder.called == 1 + + +def test_placeholder_dependencies_cache(mocker, mock_renderer): + """ + Tests Placeholder Parser fetches the placeholder dependencies using the routing path cache + Mocker patch the routing_path function in the Path Finder class to check the number of calls + Both placeholders lists use the first_non_empty_item transform that requires the Path. + The first and second placeholder list is from the same section so when we call the second list, it should use the cache from the first call. + Set Location to the BlockId where the transform is required and the values have already been set + Set Answer Store with values to check if the transform is working as expected in the Schema. + """ + schema = load_schema_from_name("test_placeholder_first_non_empty_item") + path_finder = mocker.patch("app.questionnaire.path_finder.PathFinder.routing_path") + placeholder_list_1 = [ + { + "placeholder": "date_entry_answer_from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "answers", + "identifier": "date-entry-answer-from", + }, + {"source": "metadata", "identifier": "ref_p_start_date"}, + ] + }, + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": {"source": "previous_transform"}, + "date_format": "d MMMM yyyy", + }, + }, + ], + } + ] + + placeholder_list_2 = [ + { + "placeholder": "date_entry_answer_to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + {"source": "answers", "identifier": "date-entry-answer-to"}, + {"source": "metadata", "identifier": "ref_p_end_date"}, + ] + }, + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": {"source": "previous_transform"}, + "date_format": "d MMMM yyyy", + }, + }, + ], + }, + { + "placeholder": "ru_name", + "value": {"source": "metadata", "identifier": "ru_name"}, + }, + ] + + location = Location( + section_id="default-section", + block_id="total-turnover-block", + ) + + progress_store = ProgressStore( + [ + { + "section_id": "default-section", + "block_ids": ["date-question-block", "date-entry-block"], + "status": "COMPLETED", + } + ] + ) + answer_store = AnswerStore( + [ + {"answer_id": "date-entry-answer-from", "value": "2016-04-16"}, + {"answer_id": "date-entry-answer-to", "value": "2016-04-28"}, + ] + ) + placeholder_parser = PlaceholderParser( + language="en", + answer_store=answer_store, + list_store=ListStore(), + metadata=get_metadata(), + response_metadata={}, + schema=schema, + renderer=mock_renderer, + progress_store=progress_store, + location=location, + supplementary_data_store=SupplementaryDataStore(), + ) + + placeholder_1 = placeholder_parser(placeholder_list=placeholder_list_1) + assert placeholder_1["date_entry_answer_from"] == "16 April 2016" + assert path_finder.called == 1 + + placeholder_2 = placeholder_parser(placeholder_list=placeholder_list_2) + assert placeholder_2["date_entry_answer_to"] == "28 April 2016" + assert path_finder.called == 1 diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index c4af05f0e0..e55f48576d 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -444,5 +444,4 @@ def test_context_for_summary(): "summary_type": "Summary", "view_submitted_response": False, } - assert context == expected diff --git a/tests/functional/spec/placeholder_answers_on_the_path.spec.js b/tests/functional/spec/placeholder_answers_on_the_path.spec.js new file mode 100644 index 0000000000..a608c84696 --- /dev/null +++ b/tests/functional/spec/placeholder_answers_on_the_path.spec.js @@ -0,0 +1,110 @@ +import DateEntryBlockPage from "../generated_pages/placeholder_first_non_empty_item/date-entry-block.page"; +import DateQuestionBlockPage from "../generated_pages/placeholder_first_non_empty_item/date-question-block.page"; +import TotalTurnoverBlockPage from "../generated_pages/placeholder_first_non_empty_item/total-turnover-block.page"; +import FoodQuestionBlockPage from "../generated_pages/placeholder_first_non_empty_item_cross_section_dependencies/food-question-block.page"; + +import AddPersonPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/list-collector-add.page"; +import ListCollectorPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/list-collector.page"; +import PersonalDetailsBlockPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/personal-details-block.page"; +import HubPage from "../base_pages/hub.page.js"; + +describe("First Non Empty Item Transform", () => { + before("Launch survey", async () => { + await browser.openQuestionnaire("test_placeholder_first_non_empty_item.json"); + }); + + it("When the custom date range is entered and the answer is changed back to metadata date range, Then metadata date should be displayed", async () => { + // Set the date + await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); + await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); + await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); + await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); + await $(DateEntryBlockPage.submit()).click(); + // Change to original dates + await $(TotalTurnoverBlockPage.previous()).click(); + await $(DateEntryBlockPage.previous()).click(); + await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + expect(await browser.getUrl()).to.contain(TotalTurnoverBlockPage.pageName); + expect(await $(TotalTurnoverBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + }); +}); + +describe("First Non Empty Item Transform Cross Section", () => { + before("Launch survey", async () => { + await browser.openQuestionnaire("test_placeholder_first_non_empty_item_cross_section_dependencies.json"); + await $(HubPage.submit()).click(); + }); + + it("Given a custom date range is entered, When the answer is changed back to metadata range, Then the metadata date should be displayed for both sections", async () => { + // Set the date + await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); + await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); + await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); + await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); + await $(DateEntryBlockPage.submit()).click(); + + // Check date changed and then change to original dates + await $(HubPage.submit()).click(); + expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); + await $(FoodQuestionBlockPage.previous()).click(); + await $(HubPage.summaryRowLink("default-section")).click(); + await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + // Check the next section if the metadata date is shown + await $(HubPage.submit()).click(); + expect(await browser.getUrl()).to.contain(FoodQuestionBlockPage.pageName); + expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + }); +}); + +describe("First Non Empty Item Transform Repeating Sections", () => { + before("Launch survey", async () => { + await browser.openQuestionnaire("test_placeholder_first_non_empty_item_repeating_sections.json"); + await $(HubPage.submit()).click(); + }); + it("Given a custom date range is entered, When the answer is changed back to metadata range, Then the metadata date should be displayed for the repeating section title", async () => { + // Set the date + await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); + await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); + await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); + await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); + await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); + await $(DateEntryBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + + // Add a person to the list collector + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(AddPersonPage.firstName()).setValue("Paul"); + await $(AddPersonPage.lastName()).setValue("Pogba"); + await $(AddPersonPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorPage.submit()).click(); + // Check Repeating Section has the set dates + await $(HubPage.submit()).click(); + expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); + expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); + await $(PersonalDetailsBlockPage.previous()).click(); + // Change to original dates + await $(HubPage.summaryRowLink("date-section")).click(); + await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); + await $(DateQuestionBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + // Check the list collector has metadata dates in the title + expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); + expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + }); +}); From 4fb55219d7c6dd7b31124b22cf804480dbb8e3ac Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:36:19 +0100 Subject: [PATCH 255/567] Schemas v3.66.0 (#1175) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5d3a7e6a60..527d3c58e2 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.65.0 +v3.66.0 From a06be03e653c1173f3fd6243077348b3ec913078 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:57:31 +0100 Subject: [PATCH 256/567] Remove non-JS GA (#1174) --- app/setup.py | 1 - templates/layouts/_base.html | 13 ------------- tests/integration/test_app_create.py | 3 --- tests/integration/test_application_variables.py | 2 +- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/app/setup.py b/app/setup.py index 824b0416b6..ae91d0bbbd 100644 --- a/app/setup.py +++ b/app/setup.py @@ -70,7 +70,6 @@ "'unsafe-inline'", ], "connect-src": ["'self'", "https://www.google-analytics.com"], - "frame-src": ["https://www.googletagmanager.com"], "img-src": [ "'self'", "data:", diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 3e21578aaa..17f9e92af1 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -110,19 +110,6 @@ {% endblock %} -{% block bodyStart %} - {% if google_tag_manager_id and google_tag_manager_auth %} - - - - {% endif %} -{% endblock %} - - {% block skipLink %} {{ onsSkipToContent({ diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index a547908dfa..8e4e23c3ae 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -153,9 +153,6 @@ def test_csp_policy_headers(self): f"font-src 'self' data: https://fonts.gstatic.com {cdn_url}", csp_policy_parts, ) - self.assertIn( - "frame-src https://www.googletagmanager.com", csp_policy_parts - ) self.assertIn( f"connect-src 'self' https://www.google-analytics.com {cdn_url} {address_lookup_api_url}", csp_policy_parts, diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 8c8a228ef7..49c5e03c20 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -29,7 +29,7 @@ def test_google_analytics_code_and_credentials_are_present(self): self.assertInHead( f'dataLayer = [{{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) - self.assertInBody("https://www.googletagmanager.com") + self.assertInHead("https://www.googletagmanager.com") self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_AUTH) self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_ID) From 1b938eac56826e2a165d647aae325bbee433efe3 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:24:03 +0100 Subject: [PATCH 257/567] Using supplementary data list items (#1173) * Using supplementary data list items * Expand rule evaluator test for supplementary data * Accept cookies for edge case functional tests * Allow formatted list with optional data * Cookie fix for other flaky tests --- app/data_models/supplementary_data_store.py | 20 +- app/questionnaire/placeholder_transforms.py | 11 +- app/questionnaire/value_source_resolver.py | 24 +- schemas/test/en/test_supplementary_data.json | 883 +++++++++++++++++- scripts/mock_data/supplementary_data.json | 131 +++ .../supplementary_data_no_repeat.json | 24 - .../supplementary_data_no_repeat_v2.json | 23 - scripts/mock_data/supplementary_data_v2.json | 162 ++++ .../supplementary_data_with_repeat.json | 85 -- .../supplementary_data_with_repeat_v2.json | 117 --- ...pplementary_dataset_metadata_response.json | 61 +- ...metadata_response_non_repeating_items.json | 26 - ...set_metadata_response_repeating_items.json | 27 - scripts/mock_sds_endpoint.py | 9 +- tests/app/conftest.py | 1 + .../test_supplementary_data_store.py | 2 + .../rules/test_rule_evaluator.py | 20 +- .../test_placeholder_transforms.py | 6 + .../test_value_source_resolver.py | 128 +++ tests/functional/generate_pages.py | 6 +- tests/functional/helpers.js | 20 +- ...mmary_repeating_and_static_answers.spec.js | 1 + .../list_collector_repeating_blocks.spec.js | 2 + .../supplementary_data.spec.js | 439 +++++++++ .../supplementary_data_non_list_items.spec.js | 175 ---- .../features/validation/sum/dynamic.spec.js | 1 + ...naire_supplementary_data_non_list_items.py | 60 -- 27 files changed, 1821 insertions(+), 643 deletions(-) create mode 100644 scripts/mock_data/supplementary_data.json delete mode 100644 scripts/mock_data/supplementary_data_no_repeat.json delete mode 100644 scripts/mock_data/supplementary_data_no_repeat_v2.json create mode 100644 scripts/mock_data/supplementary_data_v2.json delete mode 100644 scripts/mock_data/supplementary_data_with_repeat.json delete mode 100644 scripts/mock_data/supplementary_data_with_repeat_v2.json delete mode 100644 scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json delete mode 100644 scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json create mode 100644 tests/functional/spec/features/supplementary_data/supplementary_data.spec.js delete mode 100644 tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js delete mode 100644 tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index e3dac5bd34..dd73031841 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -94,17 +94,31 @@ def get_data( For example if you wanted the identifier for the first item in "some_list" it would be get_data(identifier="some_list", selectors=["identifier"], list_item_id=list_item_id-1) """ + if self.is_data_repeating(identifier) and not list_item_id: + raise InvalidSupplementaryDataSelector( + f"Cannot reference items from `{identifier}` outside a repeat" + ) + value = self._data_map.get((identifier, list_item_id)) - # for nested data, index with each selector + # for nested data, index with each selector, or return None if there is no data to index for selector in selectors or []: - if not isinstance(value, dict): + if value is None: + return None + if not isinstance(value, Mapping): + # if value is not None, and also not index able, raise an error raise InvalidSupplementaryDataSelector( f"Cannot use the selector `{selector}` on non-nested data" ) - value = value[selector] + value = value.get(selector) return value + def is_data_repeating(self, identifier: str) -> bool: + """ + Returns true if the identifier is for one of the lists + """ + return identifier in self._list_mappings + def serialize(self) -> dict: return { "data": self._raw_data, diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 89eca5bdaa..be38d9c89e 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -55,10 +55,13 @@ def format_date(self, date_to_format: str, date_format: str) -> str: ) return formatted_datetime - @staticmethod - def format_list(list_to_format: Sequence[str]) -> str: - formatted_list = "
        " + "".join(f"
      • {item}
      • " for item in list_to_format) - formatted_list += "
      " + def format_list(self, list_to_format: Sequence[str] | None) -> str: + filtered_list = self.remove_empty_from_list(list_to_format or []) + formatted_list = ( + f"
        {''.join(f'
      • {item}
      • ' for item in filtered_list)}
      " + if filtered_list + else "" + ) return formatted_list @staticmethod diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 422f628554..c36382ccbb 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -88,25 +88,29 @@ def _get_answer_value( def _resolve_list_item_id_for_value_source( self, value_source: Mapping ) -> str | None: - list_item_id: str | None = None - if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: raise InvalidLocationException( "list_item_selector source location used without location" ) + # Type ignore: the identifier is a string, same below + return getattr(self.location, list_item_selector["identifier"]) # type: ignore - list_item_id = getattr(self.location, list_item_selector["identifier"]) - - elif list_item_selector["source"] == "list": - list_item_id = getattr( + if list_item_selector["source"] == "list": + return getattr( # type: ignore self.list_store[list_item_selector["identifier"]], list_item_selector["selector"], ) - if list_item_id: - return list_item_id + if value_source["source"] == "supplementary_data": + return ( + self.list_item_id + if self.supplementary_data_store.is_data_repeating( + value_source["identifier"] + ) + else None + ) return ( self.list_item_id @@ -297,10 +301,12 @@ def resolve_list(self, value_source_list: list[Mapping]) -> list[ValueSourceType def _resolve_supplementary_data_source( self, value_source: Mapping ) -> ValueSourceTypes: + list_item_id = self._resolve_list_item_id_for_value_source(value_source) + return self.supplementary_data_store.get_data( identifier=value_source["identifier"], selectors=value_source.get("selectors"), - list_item_id=value_source.get("list_item_id"), + list_item_id=list_item_id, ) @staticmethod diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json index d5fd4a1273..ac1c0336ab 100644 --- a/schemas/test/en/test_supplementary_data.json +++ b/schemas/test/en/test_supplementary_data.json @@ -31,7 +31,9 @@ ], "questionnaire_flow": { "type": "Hub", - "options": {} + "options": { + "required_completed_sections": ["introduction-section"] + } }, "post_submission": { "view_response": true @@ -45,6 +47,26 @@ "id": "introduction-group", "title": "Introduction Group", "blocks": [ + { + "id": "loaded-successfully-block", + "type": "Interstitial", + "content": { + "title": "Supplementary Data", + "contents": [ + { + "title": "You have successfully loaded Supplementary data", + "description": "Press continue to proceed to the introduction", + "guidance": { + "contents": [ + { + "description": "The purpose of this block, is to test that supplementary data loads successfully, separate to using the supplementary data" + } + ] + } + } + ] + } + }, { "id": "introduction-block", "type": "Introduction", @@ -130,7 +152,7 @@ }, "groups": [ { - "id": "group-1", + "id": "introduction", "title": "Group 1", "blocks": [ { @@ -615,6 +637,178 @@ { "type": "List", "for_list": "employees", + "title": "Employees", + "empty_list_text": "There are no employees", + "add_link_text": "DO NOT use this link to add another employee, this will be replaced with list collector content" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "employee-reporting", + "blocks": [ + { + "id": "list-collector-employees", + "type": "ListCollector", + "for_list": "employees", + "question": { + "id": "confirmation-employee-question", + "type": "General", + "title": "Do you need to add any more employees?", + "guidance": { + "contents": [ + { + "title": "Important guidance", + "description": "This list collector is a placeholder until it can be replaced with a list collector content block - do not add any people to this list because it won’t work correctly" + } + ] + }, + "answers": [ + { + "id": "list-collector-employees-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-employee", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other employees?", + "question": { + "id": "add-employee-question", + "type": "General", + "title": "What is the name of the employee?", + "answers": [ + { + "id": "employee-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "employee-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-employee", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-employee-question", + "type": "General", + "title": "What is the name of the employee?", + "answers": [ + { + "id": "employee-first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "employee-last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-employee", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this employee?", + "question": { + "id": "remove-employee-question", + "type": "General", + "title": "Are you sure you want to remove this employee?", + "warning": "All of the information about this employee will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "employees", + "item_title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Additional Employees", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "additional-employees", "title": "employees", "add_link_text": "Add another employee", "empty_list_text": "There are no employees" @@ -624,20 +818,27 @@ }, "groups": [ { - "id": "group-2", + "id": "additional-employee-reporting", "blocks": [ { "type": "ListCollectorDrivingQuestion", - "id": "any-employees", - "for_list": "employees", + "id": "any-additional-employees", + "for_list": "additional-employees", "question": { "type": "General", - "id": "any-employee-question", - "title": "Do you have any employees to report on?", + "id": "any-additional-employee-question", + "title": "Do you have any additional employees to report on?", + "guidance": { + "contents": [ + { + "description": "This uses a different employees list, and the items from this list and the supplementary list will then be used in repeating sections" + } + ] + }, "answers": [ { "type": "Radio", - "id": "any-employee-answer", + "id": "any-additional-employee-answer", "mandatory": true, "options": [ { @@ -646,8 +847,8 @@ "action": { "type": "RedirectToListAddBlock", "params": { - "block_id": "add-employee", - "list_name": "employees" + "block_id": "add-additional-employee", + "list_name": "additional-employees" } } }, @@ -666,28 +867,28 @@ "==": [ { "source": "answers", - "identifier": "any-employee-answer" + "identifier": "any-additional-employee-answer" }, "No" ] } }, { - "block": "list-collector" + "block": "list-collector-additional" } ] }, { - "id": "list-collector", + "id": "list-collector-additional", "type": "ListCollector", - "for_list": "employees", + "for_list": "additional-employees", "question": { - "id": "confirmation-question", + "id": "confirmation-additional-question", "type": "General", "title": "Do you need to add any more employees?", "answers": [ { - "id": "list-collector-answer", + "id": "list-collector-additional-answer", "mandatory": true, "type": "Radio", "options": [ @@ -707,11 +908,11 @@ ] }, "add_block": { - "id": "add-employee", + "id": "add-additional-employee", "type": "ListAddQuestion", "cancel_text": "Don’t need to add any other employees?", "question": { - "id": "add-question", + "id": "add-additional-question", "type": "General", "title": "What is the name of the employee?", "answers": [ @@ -731,11 +932,11 @@ } }, "edit_block": { - "id": "edit-employee", + "id": "edit-additional-employee", "type": "ListEditQuestion", "cancel_text": "Don’t need to change anything?", "question": { - "id": "edit-question", + "id": "edit-additional-question", "type": "General", "title": "What is the name of the employee?", "answers": [ @@ -755,11 +956,11 @@ } }, "remove_block": { - "id": "remove-employee", + "id": "remove-additional-employee", "type": "ListRemoveQuestion", "cancel_text": "Don’t need to remove this employee?", "question": { - "id": "remove-question", + "id": "remove-additional-question", "type": "General", "title": "Are you sure you want to remove this employee?", "warning": "All of the information about this employee will be deleted", @@ -820,7 +1021,33 @@ ] }, { - "id": "section-3", + "enabled": { + "when": { + "and": [ + { + "==": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + }, + { + "==": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-3" + }, + "COMPLETED" + ] + } + ] + } + }, + "id": "section-4", "title": "Employee Details", "summary": { "show_on_completion": true @@ -838,12 +1065,14 @@ "delimiter": " ", "list_to_concatenate": [ { - "source": "answers", - "identifier": "employee-first-name" + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] }, { - "source": "answers", - "identifier": "employee-last-name" + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] } ] }, @@ -856,7 +1085,7 @@ }, "groups": [ { - "id": "group-3", + "id": "employee-detail-questions", "blocks": [ { "type": "Question", @@ -882,12 +1111,14 @@ "delimiter": " ", "list_to_concatenate": [ { - "source": "answers", - "identifier": "employee-first-name" + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] }, { - "source": "answers", - "identifier": "employee-last-name" + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] } ] }, @@ -930,6 +1161,594 @@ ] } ] + }, + { + "enabled": { + "when": { + "and": [ + { + "==": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + }, + "COMPLETED" + ] + }, + { + "==": [ + { + "source": "progress", + "selector": "section", + "identifier": "section-3" + }, + "COMPLETED" + ] + } + ] + } + }, + "id": "section-5", + "title": "Additional Employee Details", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "additional-employees", + "title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + }, + "groups": [ + { + "id": "additional-employee-detail-questions", + "blocks": [ + { + "type": "Question", + "id": "additional-length-of-employment", + "question": { + "id": "additional-length-employment-question", + "type": "General", + "title": { + "text": "When did {employee_name} start working for {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "additional-employment-start", + "label": { + "text": "Start date at {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + }, + "minimum": { + "value": { + "source": "supplementary_data", + "identifier": "incorporation_date" + } + } + } + ] + } + } + ] + } + ] + }, + { + "id": "section-6", + "title": "Product details", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "products", + "title": "Products", + "add_link_text": "DO NOT use this link to add another product, this will be replaced with list collector content", + "empty_list_text": "There are no products" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "product-reporting", + "blocks": [ + { + "id": "list-collector-products", + "type": "ListCollector", + "for_list": "products", + "question": { + "id": "confirmation-product-question", + "type": "General", + "title": "Do you need to add any more products?", + "guidance": { + "contents": [ + { + "title": "Important guidance", + "description": "This list collector is a placeholder until it can be replaced with a list collector content block - do not add any products to this list because it won’t work correctly" + }, + { + "description": "Until this can be replaced with a list collector content block, keep clicking no and pressing save and continue to complete the repeating blocks" + } + ] + }, + "answers": [ + { + "id": "list-collector-products-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-product", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other products?", + "question": { + "id": "add-product-question", + "type": "General", + "title": "What is the name of the product?", + "guidance": { + "contents": [ + { + "description": "This uses a different employees list, and the items from this list and the supplementary list will then be used in repeating sections" + } + ] + }, + "answers": [ + { + "id": "product-name", + "label": "Product name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-product", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-product-question", + "type": "General", + "title": "What is the name of the product?", + "answers": [ + { + "id": "product-name", + "label": "Product name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-product", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this product?", + "question": { + "id": "remove-product-question", + "type": "General", + "title": "Are you sure you want to remove this product?", + "warning": "All of the information about this product will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "repeating_blocks": [ + { + "id": "product-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "product-repeating-block-1-question", + "type": "General", + "guidance": { + "contents": [ + { + "title": { + "text": "{guidance_include}", + "placeholders": [ + { + "placeholder": "guidance_include", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["guidance_include", "title"] + } + ] + } + } + ] + } + ] + }, + "description": { + "text": "{guidance_include_list}", + "placeholders": [ + { + "placeholder": "guidance_include_list", + "transforms": [ + { + "transform": "format_list", + "arguments": { + "list_to_format": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["guidance_include", "list"] + } + } + } + ] + } + ] + } + }, + { + "title": { + "text": "{guidance_exclude}", + "placeholders": [ + { + "placeholder": "guidance_exclude", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["guidance_exclude", "title"] + } + ] + } + } + ] + } + ] + }, + "description": { + "text": "{guidance_exclude_list}", + "placeholders": [ + { + "placeholder": "guidance_exclude_list", + "transforms": [ + { + "transform": "format_list", + "arguments": { + "list_to_format": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["guidance_exclude", "list"] + } + } + } + ] + } + ] + } + } + ] + }, + "title": { + "text": "Volume of production and sales for {product_name}", + "placeholders": [ + { + "placeholder": "product_name", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + ] + }, + "answers": [ + { + "id": "product-volume-sales", + "label": { + "text": "{volume_sales} for {product_name}", + "placeholders": [ + { + "placeholder": "volume_sales", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["volume_sales", "label"] + } + }, + { + "placeholder": "product_name", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + ] + }, + "mandatory": false, + "type": "Unit", + "unit": "mass-kilogram", + "unit_length": "short" + }, + { + "id": "product-volume-total", + "label": { + "text": "{total_volume} for {product_name}", + "placeholders": [ + { + "placeholder": "total_volume", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["total_volume", "label"] + } + }, + { + "placeholder": "product_name", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + ] + }, + "mandatory": false, + "type": "Unit", + "unit": "mass-kilogram", + "unit_length": "short" + } + ] + } + } + ], + "summary": { + "title": "products", + "item_title": { + "text": "{product_name}", + "placeholders": [ + { + "placeholder": "product_name", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-volume-sales", + "title": "We calculate the total volume of sales over the previous quarter to be %(total)s. Is this correct?", + "calculation": { + "title": "Total sales volume", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "product-volume-sales" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-volume-total", + "title": "We calculate the total volume produced over the previous quarter to be %(total)s. Is this correct?", + "calculation": { + "title": "Total volume produced", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "product-volume-total" + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-products", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "products" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "products" + }, + "answers": [ + { + "label": { + "text": "{value_sales} for {product_name}", + "placeholders": [ + { + "placeholder": "value_sales", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["value_sales", "label"] + } + }, + { + "placeholder": "product_name", + "value": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + ] + }, + "id": "product-sales-answer", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + } + ] + }, + "answers": [ + { + "id": "extra-static-answer", + "label": "Value of sales from other categories", + "type": "Currency", + "mandatory": false, + "currency": "GBP", + "decimal_places": 2 + } + ], + "id": "dynamic-answer-question", + "title": "Sales during the previous quarter", + "type": "General" + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-value-sales", + "title": "We calculate the total value of sales over the previous quarter to be %(total)s. Is this correct?", + "calculation": { + "title": "Total sales value", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "product-sales-answer" + }, + { + "source": "answers", + "identifier": "extra-static-answer" + } + ] + } + } + } + ] + } + ] } ] } diff --git a/scripts/mock_data/supplementary_data.json b/scripts/mock_data/supplementary_data.json new file mode 100644 index 0000000000..56548159ce --- /dev/null +++ b/scripts/mock_data/supplementary_data.json @@ -0,0 +1,131 @@ +{ + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "company_name": "Tesco", + "company_details": { + "telephone_number": "01171231231", + "email": "contact@tesco.org" + }, + "note": { + "title": "Value of total sales", + "description": "Total value of goods sold during the period of the return", + "example": { + "title": "Including", + "description": "Sales across all UK stores" + } + }, + "guidance": "Some supplementary guidance about the survey", + "total_uk_sales": 555000.00, + "incorporation_date": "1947-11-27", + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports or outdoor games", + "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "guidance_include": { + "title": "Include", + "list": [ + "for children's playgrounds", + "swimming pools and paddling pools" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", + "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" + ] + }, + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "89929002", + "label": "Volume of sales", + "unit_label": "Tonnes" + }, + "total_volume": { + "answer_code": "89929005", + "label": "Total volume produced", + "unit_label": "Tonnes" + } + }, + { + "identifier": "202346331", + "name": "Kitchen Equipment", + "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", + "guidance_include": { + "title": "Include", + "list": [ + "pots and pans", + "chopping board" + ] + }, + "value_sales": { + "answer_code": "20234633101", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "20234633102", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "20234633103", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + } + ], + "employees": [ + { + "identifier": "429001", + "personal_details": { + "forename": "Harry", + "surname": "Potter", + "address": { + "postcode": "BS1 1AJ", + "house_number": "12", + "city": "Bristol" + } + }, + "employment_details": { + "job_title": "Customer assistant", + "start_date": "2020-01-01", + "salary": { + "payroll_number": "54345", + "value": "25000", + "currency": "GBP" + } + } + }, + { + "identifier": "429002", + "personal_details": { + "forename": "Clark", + "surname": "Kent", + "address": { + "postcode": "E1 7DD", + "house_number": "21", + "city": "London" + } + }, + "employment_details": { + "job_title": "Warehouse operative", + "start_date": "2015-05-10", + "salary": { + "payroll_number": "34123", + "value": "29000", + "currency": "GBP" + } + } + } + ] + } + } +} diff --git a/scripts/mock_data/supplementary_data_no_repeat.json b/scripts/mock_data/supplementary_data_no_repeat.json deleted file mode 100644 index d69ea41648..0000000000 --- a/scripts/mock_data/supplementary_data_no_repeat.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "company_name": "Tesco", - "company_details": { - "telephone_number": "01171231231", - "email": "contact@tesco.org" - }, - "note": { - "title": "Value of total sales", - "description": "Total value of goods sold during the period of the return", - "example": { - "title": "Including", - "description": "Sales across all UK stores" - } - }, - "guidance": "Some supplementary guidance about the survey", - "total_uk_sales": 555000.00, - "incorporation_date": "1947-11-27" - } -} diff --git a/scripts/mock_data/supplementary_data_no_repeat_v2.json b/scripts/mock_data/supplementary_data_no_repeat_v2.json deleted file mode 100644 index e96f12fdc0..0000000000 --- a/scripts/mock_data/supplementary_data_no_repeat_v2.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "company_name": "Lidl", - "company_details": { - "telephone_number": "01174564561", - "email": "contact@lidl.org" - }, - "note": { - "title": "Value of total sales", - "description": "Total value of goods sold", - "example": { - "title": "Includes", - "description": "Sales across all EU stores" - } - }, - "total_uk_sales": 555000.00, - "incorporation_date": "1947-11-27" - } -} diff --git a/scripts/mock_data/supplementary_data_v2.json b/scripts/mock_data/supplementary_data_v2.json new file mode 100644 index 0000000000..90060f6bf2 --- /dev/null +++ b/scripts/mock_data/supplementary_data_v2.json @@ -0,0 +1,162 @@ +{ + "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd", + "survey_id": "123", + "data": { + "schema_version": "v1", + "identifier": "12346789012A", + "company_name": "Lidl", + "company_details": { + "telephone_number": "01174564561", + "email": "contact@lidl.org" + }, + "note": { + "title": "Value of total sales", + "description": "Total value of goods sold", + "example": { + "title": "Includes", + "description": "Sales across all EU stores" + } + }, + "total_uk_sales": 555000.00, + "incorporation_date": "1947-11-27", + "items": { + "products": [ + { + "identifier": "89929001", + "name": "Articles and equipment for sports or outdoor games", + "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "guidance_include": { + "title": "Include", + "list": [ + "for children's playgrounds", + "swimming pools and paddling pools" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", + "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" + ] + }, + "value_sales": { + "answer_code": "89929001", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "89929002", + "label": "Volume of sales", + "unit_label": "Tonnes" + }, + "total_volume": { + "answer_code": "89929005", + "label": "Total volume produced", + "unit_label": "Tonnes" + } + }, + { + "identifier": "202346331", + "name": "Kitchen Equipment", + "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", + "guidance_include": { + "title": "Include", + "list": [ + "pots and pans", + "chopping board" + ] + }, + "guidance_exclude": { + "title": "Exclude", + "list": [ + "cutlery" + ] + }, + "value_sales": { + "answer_code": "20234633101", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "20234633102", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "20234633103", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + }, + { + "identifier": "246331", + "name": "Groceries", + "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", + "guidance_include": { + "title": "Include", + "list": [ + "fruit and vegetables", + "baked goods" + ] + }, + "value_sales": { + "answer_code": "202101", + "label": "Value of sales" + }, + "volume_sales": { + "answer_code": "202102", + "label": "Volume of sales", + "unit_label": "Kilogram" + }, + "total_volume": { + "answer_code": "202103", + "label": "Total volume produced", + "unit_label": "Kilogram" + } + } + ], + "employees": [ + { + "identifier": "429001", + "personal_details": { + "forename": "Harry", + "surname": "Potter", + "address": { + "postcode": "BS1 1AJ", + "house_number": "12", + "city": "Bristol" + } + }, + "employment_details": { + "job_title": "Customer assistant", + "start_date": "2020-01-01", + "salary": { + "payroll_number": "54345", + "value": "25000", + "currency": "GBP" + } + } + }, + { + "identifier": "529001", + "personal_details": { + "forename": "Bruce", + "surname": "Wayne", + "address": { + "postcode": "BS1 1HJ", + "house_number": "15", + "city": "Bristol" + } + }, + "employment_details": { + "job_title": "Customer assistant", + "start_date": "2019-03-01", + "salary": { + "payroll_number": "4345", + "value": "27000", + "currency": "GBP" + } + } + } + ] + } + } +} diff --git a/scripts/mock_data/supplementary_data_with_repeat.json b/scripts/mock_data/supplementary_data_with_repeat.json deleted file mode 100644 index d589cbfab7..0000000000 --- a/scripts/mock_data/supplementary_data_with_repeat.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "note": { - "title": "Volume of total production", - "description": "Figures should cover the total quantity of the goods produced during the period of the return" - }, - "items": { - "products": [ - { - "identifier": "89929001", - "name": "Articles and equipment for sports or outdoor games", - "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", - "guidance_include": { - "title": "Include", - "list": [ - "for children's playgrounds", - "swimming pools and paddling pools" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", - "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" - ] - }, - "value_sales": { - "answer_code": "89929001", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "89929002", - "label": "Volume of sales", - "unit_label": "Tonnes" - }, - "total_volume": { - "answer_code": "89929005", - "label": "Total volume produced", - "unit_label": "Tonnes" - } - }, - { - "identifier": "201630601", - "name": "Other Minerals", - "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", - "guidance_include": { - "title": "Include", - "list": [ - "natural graphite", - "quartz for industrial use", - "diatomite; magnesia; feldspar", - "magnesite; natural magnesium carbonate", - "talc including steatite and chlorite", - "unexpanded vermiculite and perlite" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "natural quartz sands" - ] - }, - "value_sales": { - "answer_code": "201630601", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "201630602", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "201630605", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - } - ] - } - } -} diff --git a/scripts/mock_data/supplementary_data_with_repeat_v2.json b/scripts/mock_data/supplementary_data_with_repeat_v2.json deleted file mode 100644 index a015e11c34..0000000000 --- a/scripts/mock_data/supplementary_data_with_repeat_v2.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "note": { - "title": "Volume of total production", - "description": "New figures should cover the total quantity of the goods produced during the period of the return" - }, - "items": { - "products": [ - { - "identifier": "89929001", - "name": "Articles and equipment for sports", - "cn_codes": "2504 + 2512 + 2519 + 2524", - "guidance_include": { - "title": "Include", - "list": [ - "for children's playgrounds" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", - "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics", - "outdoor sports" - ] - }, - "value_sales": { - "answer_code": "89929001", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "89929002", - "label": "Volume of sales", - "unit_label": "Tonnes" - }, - "total_volume": { - "answer_code": "89929005", - "label": "Total volume produced", - "unit_label": "Tonnes" - } - }, - { - "identifier": "201630601", - "name": "Other Minerals", - "cn_codes": "5908 + 5910 + 591110 + 591120 + 591140", - "guidance_include": { - "title": "Include", - "list": [ - "natural graphite", - "quartz for industrial use", - "diatomite; magnesia; feldspar", - "magnesite; natural magnesium carbonate", - "talc including steatite and chlorite", - "unexpanded vermiculite and perlite" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "natural quartz sands" - ] - }, - "value_sales": { - "answer_code": "201630601", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "201630602", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "201630605", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - }, - { - "identifier": "202346331", - "name": "Kitchen Equipment", - "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", - "guidance_include": { - "title": "Include", - "list": [ - "pots and pans", - "chopping board" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "cutlery" - ] - }, - "value_sales": { - "answer_code": "20234633101", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "20234633102", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "20234633103", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - } - ] - } - } -} diff --git a/scripts/mock_data/supplementary_dataset_metadata_response.json b/scripts/mock_data/supplementary_dataset_metadata_response.json index 91af0583c5..2182fd5c33 100644 --- a/scripts/mock_data/supplementary_dataset_metadata_response.json +++ b/scripts/mock_data/supplementary_dataset_metadata_response.json @@ -1,39 +1,26 @@ [ - { - "survey_id": "123", - "period_id": "202301", - "title": "Supplementary data without repeating items", - "sds_schema_version": 1, - "sds_published_at": "2022-03-01T12:30:02Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" - }, - { - "survey_id": "123", - "period_id": "202302", - "title": "Supplementary data with repeating items", - "sds_schema_version": 1, - "sds_published_at": "2022-03-01T12:30:02Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64" - }, - { - "survey_id": "123", - "period_id": "202303", - "title": "Supplementary data with repeats v2 additional items", - "sds_schema_version": 1, - "sds_published_at": "2023-05-04T14:35:05Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88" - } + { + "survey_id": "123", + "period_id": "202301", + "title": "Supplementary data", + "sds_schema_version": 1, + "sds_published_at": "2022-03-01T12:30:02Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" + }, + { + "survey_id": "123", + "period_id": "202301", + "title": "Supplementary data v2", + "sds_schema_version": 1, + "sds_published_at": "2023-07-01T11:25:01Z", + "total_reporting_units": 2, + "schema_version": "v1.0.0", + "sds_dataset_version": 1, + "filename": "", + "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd" + } ] - diff --git a/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json b/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json deleted file mode 100644 index 37ea85a11b..0000000000 --- a/scripts/mock_data/supplementary_dataset_metadata_response_non_repeating_items.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "survey_id": "123", - "period_id": "202301", - "title": "Supplementary data without repeating items", - "sds_schema_version": 1, - "sds_published_at": "2022-03-01T12:30:02Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" - }, - { - "survey_id": "123", - "period_id": "202301", - "title": "Supplementary data without repeating items v2", - "sds_schema_version": 1, - "sds_published_at": "2023-07-01T11:25:01Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd" - } -] diff --git a/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json b/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json deleted file mode 100644 index 28044cc8b8..0000000000 --- a/scripts/mock_data/supplementary_dataset_metadata_response_repeating_items.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "survey_id": "456", - "period_id": "202302", - "title": "Supplementary data with repeating items", - "sds_schema_version": 1, - "sds_published_at": "2022-03-01T12:30:02Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "34a80231-c49a-44d0-91a6-8fe1fb190e64" - }, - { - "survey_id": "456", - "period_id": "202303", - "title": "Supplementary data with repeats v2 additional items", - "sds_schema_version": 1, - "sds_published_at": "2023-05-04T14:35:05Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "6b378962-f0c7-4e8c-947e-7d24ee1b6b88" - } -] - diff --git a/scripts/mock_sds_endpoint.py b/scripts/mock_sds_endpoint.py index 63e12b24e0..faf45f0658 100644 --- a/scripts/mock_sds_endpoint.py +++ b/scripts/mock_sds_endpoint.py @@ -17,10 +17,8 @@ def get_sds_data(): dataset_id = request.args.get("dataset_id") guid_filename_map = { - "c067f6de-6d64-42b1-8b02-431a3486c178": "supplementary_data_no_repeat", - "693dc252-2e90-4412-bd9c-c4d953e36fcd": "supplementary_data_no_repeat_v2", - "34a80231-c49a-44d0-91a6-8fe1fb190e64": "supplementary_data_with_repeat", - "6b378962-f0c7-4e8c-947e-7d24ee1b6b88": "supplementary_data_with_repeat_v2", + "c067f6de-6d64-42b1-8b02-431a3486c178": "supplementary_data", + "693dc252-2e90-4412-bd9c-c4d953e36fcd": "supplementary_data_v2", } if filename := guid_filename_map.get(dataset_id): @@ -46,8 +44,7 @@ def load_mock_sds_dataset_metadata(survey_id: str, period_id: str): del period_id # not required for mock survey_id_filename_map = { - "123": "supplementary_dataset_metadata_response_non_repeating_items", - "456": "supplementary_dataset_metadata_response_repeating_items", + "123": "supplementary_dataset_metadata_response", } if filename := survey_id_filename_map.get(survey_id): diff --git a/tests/app/conftest.py b/tests/app/conftest.py index b3562ffb07..4db6de3204 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -255,6 +255,7 @@ def supplementary_data(): "identifier": "89929001", "name": "Articles and equipment for sports or outdoor games", "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", + "guidance": {"title": "Include", "description": "sportswear"}, "value_sales": { "answer_code": "89929001", "label": "Value of sales", diff --git a/tests/app/data_model/test_supplementary_data_store.py b/tests/app/data_model/test_supplementary_data_store.py index b1f267d4f9..1a43acd442 100644 --- a/tests/app/data_model/test_supplementary_data_store.py +++ b/tests/app/data_model/test_supplementary_data_store.py @@ -62,6 +62,8 @@ def test_empty_supplementary_data_deserialisation(): ("note", None, ["title"], "Volume of total production"), ("products", "item-2", ["name"], "Other Minerals"), ("products", "item-1", ["value_sales", "answer_code"], "89929001"), + ("products", "item-1", ["guidance", "title"], "Include"), + ("products", "item-2", ["guidance", "title"], None), ("INVALID", None, None, None), ], ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 3812ce06b1..6737f3aa74 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -1060,12 +1060,20 @@ def test_map_with_nested_date_operator(offset, expected_result): assert rule_evaluator.evaluate(rule=rule) == expected_result -def test_supplementary_data_source(supplementary_data_store_with_data): +@pytest.mark.parametrize( + "identifier,selectors,value", + [ + ("note", ["title"], "Volume of total production"), + ("products", ["name"], "Articles and equipment for sports or outdoor games"), + ], +) +def test_supplementary_data_source( + supplementary_data_store_with_data, identifier, selectors, value +): + """Tests rule evaluation of repeating and non-repeating supplementary data source inside a repeat""" schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=False) - answer_store = AnswerStore( - [{"answer_id": "same-answer", "value": "Volume of total production"}] - ) + answer_store = AnswerStore([{"answer_id": "same-answer", "value": value}]) rule_evaluator = get_rule_evaluator( schema=schema, @@ -1082,8 +1090,8 @@ def test_supplementary_data_source(supplementary_data_store_with_data): Operator.EQUAL: [ { "source": "supplementary_data", - "identifier": "note", - "selectors": ["title"], + "identifier": identifier, + "selectors": selectors, }, {"source": "answers", "identifier": "same-answer"}, ] diff --git a/tests/app/questionnaire/test_placeholder_transforms.py b/tests/app/questionnaire/test_placeholder_transforms.py index c529b8f3f7..81c9268085 100644 --- a/tests/app/questionnaire/test_placeholder_transforms.py +++ b/tests/app/questionnaire/test_placeholder_transforms.py @@ -87,6 +87,12 @@ def test_format_list(transformer): assert expected_result == format_value +@pytest.mark.parametrize("list_to_format", ([], None, [[], (), ""])) +def test_format_list_empty_or_none(transformer, list_to_format): + transform = transformer() + assert transform.format_list(list_to_format) == "" + + @pytest.mark.parametrize( "name, expected", ( diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 1c64625760..8f91f01ca0 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -12,6 +12,7 @@ ) from app.data_models.answer import Answer, AnswerDict from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException +from app.data_models.supplementary_data_store import InvalidSupplementaryDataSelector from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.location import InvalidLocationException from app.questionnaire.relationship_location import RelationshipLocation @@ -813,6 +814,10 @@ def test_progress_values_source_throws_if_no_location_given(): {"identifier": "note", "selectors": ["example", "description"]}, "Sales across all UK stores", ), + ( + {"identifier": "note", "selectors": ["invalid", "description"]}, + None, + ), ( {"identifier": "INVALID"}, None, @@ -851,3 +856,126 @@ def test_supplementary_data_value_source_non_list_items( ) == expected_result ) + + +@pytest.mark.parametrize( + "list_item_id, value_source ,expected_result", + [ + ( + "item-1", + {"identifier": "products", "selectors": ["name"]}, + "Articles and equipment for sports or outdoor games", + ), + ( + "item-1", + {"identifier": "products", "selectors": ["value_sales", "answer_code"]}, + "89929001", + ), + ( + "item-1", + {"identifier": "products", "selectors": ["value_sales", "label"]}, + "Value of sales", + ), + ( + "item-1", + {"identifier": "products", "selectors": ["guidance", "description"]}, + "sportswear", + ), + ( + "item-2", + {"identifier": "products", "selectors": ["guidance", "description"]}, + None, + ), + ( + "item-2", + {"identifier": "products", "selectors": ["non_existing_optional_key"]}, + None, + ), + ( + "item-2", + {"identifier": "products", "selectors": ["name"]}, + "Other Minerals", + ), + ( + "item-2", + {"identifier": "products", "selectors": ["value_sales", "answer_code"]}, + "201630601", + ), + ], +) +def test_supplementary_data_value_source_list_items( + supplementary_data_store_with_data, + list_item_id, + value_source, + expected_result, +): + list_store = ListStore([{"name": "products", "items": get_list_items(2)}]) + location = Location( + section_id="section", + block_id="block-id", + list_name="products", + list_item_id=list_item_id, + ) + value_source_resolver = get_value_source_resolver( + supplementary_data_store=supplementary_data_store_with_data, + location=location, + list_item_id=list_item_id, + list_store=list_store, + ) + assert ( + value_source_resolver.resolve( + { + "source": "supplementary_data", + **value_source, + } + ) + == expected_result + ) + + +def test_supplementary_data_invalid_selector_raises_exception( + supplementary_data_store_with_data, +): + location = Location( + section_id="section", + block_id="block-id", + ) + value_source_resolver = get_value_source_resolver( + supplementary_data_store=supplementary_data_store_with_data, + location=location, + ) + with pytest.raises(InvalidSupplementaryDataSelector) as e: + value_source_resolver.resolve( + { + "source": "supplementary_data", + "identifier": "guidance", + "selectors": ["invalid"], + } + ) + + assert e.value.args[0] == "Cannot use the selector `invalid` on non-nested data" + + +def test_supplementary_data_list_item_outside_repeating_section_raises_exception( + supplementary_data_store_with_data, +): + list_store = ListStore([{"name": "products", "items": get_list_items(2)}]) + location = Location( + section_id="section", + block_id="block-id", + ) + value_source_resolver = get_value_source_resolver( + supplementary_data_store=supplementary_data_store_with_data, + location=location, + list_store=list_store, + ) + with pytest.raises(InvalidSupplementaryDataSelector) as e: + value_source_resolver.resolve( + { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"], + } + ) + + assert e.value.args[0] == "Cannot reference items from `products` outside a repeat" diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index c630d638ad..0130fb0ab0 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -614,7 +614,11 @@ def write_summary_spec( if summary_element["type"] == "List" ] for list_block in list_summaries: - list_context = {"list_name": list_block["for_list"]} + list_context = { + "list_name": camel_case( + generate_pascal_case_from_id(list_block["for_list"]) + ) + } if answers_are_editable: if show_non_item_answers: page_spec.write( diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 9261798965..3097d9c6ee 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -13,16 +13,20 @@ export const checkListItemIncomplete = async (listItemLabel) => { await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).to.be.false; }; -export const assertSummaryValues = async (values) => { - // check each summary value provided is present and that the number of them matches what is on the page - // needs to include both dynamic and static answers on any summary with both - const summaryValues = 'dd[class="ons-summary__values"]'; - await values.map(async (value, index) => { - await expect(await $$(summaryValues)[index].getText()).to.equal(value); - }); - await expect(await $$(summaryValues).length).to.equal(values.length); +const assertSummaryFunction = (selector) => { + return async (entities) => { + // check each summary value/item/title is present and that the number of them matches what is on the page + await entities.map(async (entity, index) => { + await expect(await $$(selector)[index].getText()).to.equal(entity); + }); + await expect(await $$(selector).length).to.equal(entities.length); + }; }; +export const assertSummaryValues = assertSummaryFunction('dd[class="ons-summary__values"]'); +export const assertSummaryTitles = assertSummaryFunction('dt[class="ons-summary__title"]'); +export const assertSummaryItems = assertSummaryFunction('dd[class="ons-summary__item--text"]'); + export const repeatingAnswerChangeLink = (answerIndex) => { return $$('dd[class="ons-summary__actions"]')[answerIndex].$("a"); }; diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index cc133e9d7c..e94f6461ed 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -24,6 +24,7 @@ describe("Calculated summary with repeating answers", function () { before("Completing the list collector and dynamic answer", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_and_static_answers.json"); + await $(HubPage.acceptCookies()).click(); await $(HubPage.submit()).click(); await $(AnySupermarketPage.yes()).click(); await $(AnySupermarketPage.submit()).click(); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 086fc894e3..fe2b5d1561 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -57,6 +57,8 @@ describe("List Collector Repeating Blocks", function () { describe("Given a normal journey through the list collector with repeating blocks, the answers can be submitted.", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); + // These tests sometimes fail when a button is on the screen, but right on the very edge, accept cookies to increase screen space + await $(ResponsiblePartyPage.acceptCookies()).click(); }); it("The user is able to add companies, complete repeating blocks, and submit.", async () => { await proceedToListCollector(); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js new file mode 100644 index 0000000000..1da142dde5 --- /dev/null +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -0,0 +1,439 @@ +import AddAdditionalEmployeePage from "../../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; +import AdditionalLengthOfEmploymentPage from "../../../generated_pages/supplementary_data/additional-length-of-employment.page.js"; +import AnyAdditionalEmployeesPage from "../../../generated_pages/supplementary_data/any-additional-employees.page.js"; +import CalculatedSummarySalesPage from "../../../generated_pages/supplementary_data/calculated-summary-sales.page.js"; +import CalculatedSummaryValueSalesPage from "../../../generated_pages/supplementary_data/calculated-summary-value-sales.page.js"; +import CalculatedSummaryVolumeSalesPage from "../../../generated_pages/supplementary_data/calculated-summary-volume-sales.page.js"; +import CalculatedSummaryVolumeTotalPage from "../../../generated_pages/supplementary_data/calculated-summary-volume-total.page.js"; +import DynamicProductsPage from "../../../generated_pages/supplementary_data/dynamic-products.page.js"; +import EmailBlockPage from "../../../generated_pages/supplementary_data/email-block.page.js"; +import HubPage from "../../../base_pages/hub.page"; +import IntroductionBlockPage from "../../../generated_pages/supplementary_data/introduction-block.page.js"; +import LengthOfEmploymentPage from "../../../generated_pages/supplementary_data/length-of-employment.page.js"; +import ListCollectorAdditionalPage from "../../../generated_pages/supplementary_data/list-collector-additional.page.js"; +import ListCollectorEmployeesPage from "../../../generated_pages/supplementary_data/list-collector-employees.page.js"; +import ListCollectorProductsPage from "../../../generated_pages/supplementary_data/list-collector-products.page.js"; +import LoadedSuccessfullyBlockPage from "../../../generated_pages/supplementary_data/loaded-successfully-block.page.js"; +import NewEmailPage from "../../../generated_pages/supplementary_data/new-email.page.js"; +import ProductRepeatingBlock1Page from "../../../generated_pages/supplementary_data/product-repeating-block-1-repeating-block.page.js"; +import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; +import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; +import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; +import Section2Page from "../../../generated_pages/supplementary_data/section-2-summary.page.js"; +import Section3Page from "../../../generated_pages/supplementary_data/section-3-summary.page.js"; +import Section4Page from "../../../generated_pages/supplementary_data/section-4-summary.page.js"; +import Section5Page from "../../../generated_pages/supplementary_data/section-5-summary.page.js"; +import Section6Page from "../../../generated_pages/supplementary_data/section-6-summary.page.js"; +import ThankYouPage from "../../../base_pages/thank-you.page"; +import TradingPage from "../../../generated_pages/supplementary_data/trading.page.js"; +import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page.js"; +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues } from "../../../helpers"; +import { getRandomString } from "../../../jwt_helper"; + +// :TODO: this test currently only runs locally, remove the .skip once the mock endpoint is running in a container +describe.skip("Using supplementary data", () => { + const responseId = getRandomString(16); + const summaryItems = ".ons-summary__item--text"; + const summaryValues = ".ons-summary__values"; + const summaryRowTitles = ".ons-summary__row-title"; + + before("Starting the survey", async () => { + await browser.openQuestionnaire("test_supplementary_data.json", { + version: "v2", + sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + responseId: responseId, + }); + }); + + it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { + await $(LoadedSuccessfullyBlockPage.submit()).click(); + await $(IntroductionBlockPage.acceptCookies()).click(); + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( + "If the company details or structure have changed contact us on 01171231231" + ); + await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); + await $(IntroductionBlockPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(EmailBlockPage.yes()).click(); + await $(EmailBlockPage.submit()).click(); + }); + + it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { + await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).to.equal("Thursday 27 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).to.equal("Friday 28 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).to.equal("Saturday 29 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(3)).getText()).to.equal("Sunday 30 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(4)).getText()).to.equal("Monday 1 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).to.equal("Tuesday 2 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).to.equal("Wednesday 3 December 1947"); + await $(TradingPage.answerByIndex(3)).click(); + await $(TradingPage.submit()).click(); + }); + + it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { + await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); + await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); + await $(SalesBreakdownBlockPage.submit()).click(); + await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 555,000"); + }); + + it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { + await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); + await $(SalesBreakdownBlockPage.submit()).click(); + await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( + "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?" + ); + }); + + it("Given I have an interstitial block with all answers and supplementary data, When I reach this block, Then I see the placeholders rendered correctly", async () => { + await $(CalculatedSummarySalesPage.submit()).click(); + await expect(await $(Section1InterstitialPage.questionText()).getText()).to.contain("Summary of information provided for Tesco"); + await expect(await $("body").getText()).to.have.string("Telephone Number: 01171231231"); + await expect(await $("body").getText()).to.have.string("Email: contact@tesco.org"); + await expect(await $("body").getText()).to.have.string("Note Title: Value of total sales"); + await expect(await $("body").getText()).to.have.string("Note Description: Total value of goods sold during the period of the return"); + await expect(await $("body").getText()).to.have.string("Note Example Title: Including"); + await expect(await $("body").getText()).to.have.string("Note Example Description: Sales across all UK stores"); + await expect(await $("body").getText()).to.have.string("Incorporation Date: 27 November 1947"); + await expect(await $("body").getText()).to.have.string("Trading start date: 30 November 1947"); + await expect(await $("body").getText()).to.have.string("Guidance: Some supplementary guidance about the survey"); + await expect(await $("body").getText()).to.have.string("Total Uk Sales: £555,000.00"); + await expect(await $("body").getText()).to.have.string("Bristol sales: £333,000.00"); + await expect(await $("body").getText()).to.have.string("London sales: £111,000.00"); + await expect(await $("body").getText()).to.have.string("Sum of Bristol and London sales: £444,000.00"); + }); + + it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { + await $(Section1InterstitialPage.submit()).click(); + await expect(await $(Section1Page.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); + await expect(await $(Section1Page.sameEmailAnswer()).getText()).to.contain("Yes"); + await expect(await $(Section1Page.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); + await expect(await $(Section1Page.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(Section1Page.salesBristolAnswer()).getText()).to.contain("£333,000.00"); + await expect(await $(Section1Page.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + }); + + it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { + await $(Section1Page.sameEmailAnswerEdit()).click(); + await $(EmailBlockPage.no()).click(); + await $(EmailBlockPage.submit()).click(); + await $(NewEmailPage.answer()).setValue("new.contact@gmail.com"); + await $(NewEmailPage.submit()).click(); + await $(Section1Page.previous()).click(); + await expect(await $("body").getText()).to.have.string("Email: new.contact@gmail.com"); + await $(Section1InterstitialPage.submit()).click(); + await $(Section1Page.submit()).click(); + }); + + it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { + await $(HubPage.submit()).click(); + // TODO once list collector content block is merged in update this test accordingly + await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).to.contain("Harry Potter"); + await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).to.contain("Clark Kent"); + await $(ListCollectorEmployeesPage.no()).click(); + await $(ListCollectorEmployeesPage.submit()).click(); + }); + + it("Given I have a list collector block using a supplementary list, When I reach the section summary, I see the supplementary list items in the list", async () => { + // TODO once list collector content block is merged in update this test accordingly + await expect(await $(Section2Page.employeesListLabel(1)).getText()).to.contain("Harry Potter"); + await expect(await $(Section2Page.employeesListLabel(2)).getText()).to.contain("Clark Kent"); + await $(Section2Page.submit()).click(); + }); + + it("Given I add some additional employees via a list collector, When I return to the Hub, Then I see new enabled sections for the supplementary list items, and my added ones", async () => { + await $(HubPage.submit()).click(); + await $(AnyAdditionalEmployeesPage.yes()).click(); + await $(AnyAdditionalEmployeesPage.submit()).click(); + await $(AddAdditionalEmployeePage.employeeFirstName()).setValue("Jane"); + await $(AddAdditionalEmployeePage.employeeLastName()).setValue("Doe"); + await $(AddAdditionalEmployeePage.submit()).click(); + await $(ListCollectorAdditionalPage.yes()).click(); + await $(ListCollectorAdditionalPage.submit()).click(); + await $(AddAdditionalEmployeePage.employeeFirstName()).setValue("John"); + await $(AddAdditionalEmployeePage.employeeLastName()).setValue("Smith"); + await $(AddAdditionalEmployeePage.submit()).click(); + await $(ListCollectorAdditionalPage.no()).click(); + await $(ListCollectorAdditionalPage.submit()).click(); + await $(Section3Page.submit()).click(); + await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); + await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Clark Kent"); + await expect(await $(HubPage.summaryItems("section-5-1")).getText()).to.contain("Jane Doe"); + await expect(await $(HubPage.summaryItems("section-5-2")).getText()).to.contain("John Smith"); + await $(HubPage.submit()).click(); + }); + + it("Given I have repeating sections for both supplementary and dynamic list items, When I start a repeating section for a supplementary list item, Then I see static supplementary data correctly piped in", async () => { + await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Harry Potter start working for Tesco?"); + await expect(await $(LengthOfEmploymentPage.employmentStartLegend()).getText()).to.contain("Start date at Tesco"); + }); + + it("Given I have validation on the start date in the repeating section, When I enter a date before the incorporation date, Then I see an error message", async () => { + await $(LengthOfEmploymentPage.day()).setValue(1); + await $(LengthOfEmploymentPage.month()).setValue(1); + await $(LengthOfEmploymentPage.year()).setValue(1930); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); + }); + + it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { + await $(LengthOfEmploymentPage.year()).setValue(1990); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Harry Potter start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).to.contain("1 January 1990"); + }); + + it("Given I complete the repeating section for the other supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { + await $(Section4Page.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Clark Kent start working for Tesco?"); + await $(LengthOfEmploymentPage.day()).setValue(5); + await $(LengthOfEmploymentPage.month()).setValue(6); + await $(LengthOfEmploymentPage.year()).setValue(2011); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Clark Kent start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).to.contain("5 June 2011"); + }); + + it("Given I move onto the dynamic list items, When I start a repeating section for a dynamic list item, Then I see static supplementary data correctly piped in and the same validation and summary", async () => { + await $(Section4Page.submit()).click(); + await $(HubPage.submit()).click(); + await expect(await $(AdditionalLengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Jane Doe start working for Tesco?"); + await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).to.contain("Start date at Tesco"); + await $(AdditionalLengthOfEmploymentPage.day()).setValue(1); + await $(AdditionalLengthOfEmploymentPage.month()).setValue(1); + await $(AdditionalLengthOfEmploymentPage.year()).setValue(1930); + await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); + await $(AdditionalLengthOfEmploymentPage.year()).setValue(2000); + await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did Jane Doe start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("1 January 2000"); + await $(Section5Page.submit()).click(); + await $(HubPage.submit()).click(); + await $(AdditionalLengthOfEmploymentPage.day()).setValue(3); + await $(AdditionalLengthOfEmploymentPage.month()).setValue(3); + await $(AdditionalLengthOfEmploymentPage.year()).setValue(2010); + await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did John Smith start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("3 March 2010"); + await $(Section5Page.submit()).click(); + }); + + it("Given I have some repeating blocks with supplementary data, When I begin the section, Then I see the supplementary names rendered correctly", async () => { + await $(HubPage.submit()).click(); + await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).to.contain("Articles and equipment for sports or outdoor games"); + await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).to.contain("Kitchen Equipment"); + await $(ListCollectorProductsPage.no()).click(); + await $(ListCollectorProductsPage.submit()).click(); + }); + + it("Given I have repeating blocks with supplementary data, When I start the first repeating block, Then I see the supplementary data for the first list item", async () => { + await expect(await $("body").getHTML()).to.have.string("

      Include

      "); + await expect(await $("body").getHTML()).to.have.string("
    • for children's playgrounds
    • "); + await expect(await $("body").getHTML()).to.have.string("
    • swimming pools and paddling pools
    • "); + await expect(await $("body").getHTML()).to.have.string("

      Exclude

      "); + await expect(await $("body").getHTML()).to.have.string( + "
    • sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates
    • " + ); + await expect(await $("body").getHTML()).to.have.string( + "
    • for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics
    • " + ); + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).to.contain( + "Volume of sales for Articles and equipment for sports or outdoor games" + ); + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain( + "Total volume produced for Articles and equipment for sports or outdoor games" + ); + await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(100); + await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(200); + }); + + it("Given I have repeating blocks with supplementary data, When I start the second repeating block, Then I see the supplementary data for the second list item", async () => { + await $(ProductRepeatingBlock1Page.submit()).click(); + // TODO once using list collector content, shouldn't need these two lines + await $(ListCollectorProductsPage.no()).click(); + await $(ListCollectorProductsPage.submit()).click(); + await expect(await $("body").getText()).to.have.string("Include"); + await expect(await $("body").getText()).to.have.string("pots and pans"); + await expect(await $("body").getText()).not.to.have.string("Exclude"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).to.contain("Volume of sales for Kitchen Equipment"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain("Total volume produced for Kitchen Equipment"); + await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(50); + await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(300); + await $(ProductRepeatingBlock1Page.submit()).click(); + }); + + it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { + await $(ListCollectorProductsPage.no()).click(); + await $(ListCollectorProductsPage.submit()).click(); + await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?" + ); + assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); + assertSummaryValues(["100 kg", "50 kg"]); + await $(CalculatedSummaryVolumeSalesPage.submit()).click(); + }); + + it("Given I have another calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { + await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?" + ); + assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); + assertSummaryValues(["200 kg", "300 kg"]); + await $(CalculatedSummaryVolumeTotalPage.submit()).click(); + }); + + it("Given I have dynamic answers using a supplementary list, When I reach the dynamic answer page, Then I see the correct supplementary data in the answer labels", async () => { + await expect(await $$(DynamicProductsPage.labels())[0].getText()).to.contain("Value of sales for Articles and equipment for sports or outdoor games"); + await expect(await $$(DynamicProductsPage.labels())[1].getText()).to.contain("Value of sales for Kitchen Equipment"); + await expect(await $$(DynamicProductsPage.labels())[2].getText()).to.contain("Value of sales from other categories"); + await $$(DynamicProductsPage.inputs())[0].setValue(110); + await $$(DynamicProductsPage.inputs())[1].setValue(220); + await $$(DynamicProductsPage.inputs())[2].setValue(330); + await $(DynamicProductsPage.submit()).click(); + }); + + it("Given I have a calculated summary of dynamic answers for a supplementary list, When I reach the calculated summary, Then I see the correct supplementary data in the title and labels", async () => { + await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).to.contain( + "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?" + ); + assertSummaryItems([ + "Value of sales for Articles and equipment for sports or outdoor games", + "Value of sales for Kitchen Equipment", + "Value of sales from other categories", + ]); + assertSummaryValues(["£110.00", "£220.00", "£330.00"]); + await $(CalculatedSummaryValueSalesPage.submit()).click(); + }); + + it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { + await expect(await $$(summaryRowTitles)[0].getText()).to.contain("Sales during the previous quarter"); + assertSummaryItems([ + "Articles and equipment for sports or outdoor games", + "Volume of sales for Articles and equipment for sports or outdoor games", + "Total volume produced for Articles and equipment for sports or outdoor games", + "Kitchen Equipment", + "Volume of sales for Kitchen Equipment", + "Total volume produced for Kitchen Equipment", + "Value of sales for Articles and equipment for sports or outdoor games", + "Value of sales for Kitchen Equipment", + "Value of sales from other categories", + ]); + assertSummaryValues(["100 kg", "200 kg", "110 kg", "50 kg", "300 kg", "220 kg", "£110.00", "£220.00", "£330.00"]); + await $(Section6Page.submit()).click(); + }); + + it("Given I relaunch the survey for a newer version of the supplementary data, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not the old ones", async () => { + await browser.openQuestionnaire("test_supplementary_data.json", { + version: "v2", + sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", + responseId: responseId, + }); + await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); + await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Bruce Wayne"); + await expect(await $(HubPage.summaryItems("section-5-1")).getText()).to.contain("Jane Doe"); + await expect(await $(HubPage.summaryItems("section-5-2")).getText()).to.contain("John Smith"); + await expect(await $(HubPage.summaryRowState("section-4-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-4-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-5-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-5-2")).getText()).to.equal("Completed"); + await expect(await $("body").getText()).to.not.have.string("Clark Kent"); + }); + + it("Given I now have a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { + await $(HubPage.submit()).click(); + await $(LengthOfEmploymentPage.day()).setValue(10); + await $(LengthOfEmploymentPage.month()).setValue(10); + await $(LengthOfEmploymentPage.year()).setValue(1999); + await $(LengthOfEmploymentPage.submit()).click(); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Bruce Wayne start working for Lidl?"); + await expect(await $(Section4Page.employmentStart()).getText()).to.contain("10 October 1999"); + await $(Section4Page.submit()).click(); + }); + + it("Given I can view my response after submission, When I submit the survey, Then I see the values I've entered and correct rendering with supplementary data", async () => { + await $(HubPage.submit()).click(); + await $(ThankYouPage.savePrintAnswersLink()).click(); + + assertSummaryTitles(["Company Details", "Employees", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); + + // Company details + await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).to.contain("Is contact@lidl.org still the correct contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).to.contain("No"); + await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).to.contain("What is the new contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).to.contain("new.contact@gmail.com"); + await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).to.contain("When did Lidl begin trading?"); + await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).to.contain("£333,000.00"); + await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + + // Employees + await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[0].getText()).to.equal("Harry Potter"); + await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[1].getText()).to.equal("Bruce Wayne"); + + // Additional Employees + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).to.contain("Do you have any additional employees to report on?"); + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).to.contain("Yes"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[0].getText()).to.equal("Jane Doe"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[1].getText()).to.equal("John Smith"); + + // Harry Potter + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( + "When did Harry Potter start working for Lidl?" + ); + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 1990"); + + // Bruce Wayne + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryItems)[0].getText()).to.equal( + "When did Bruce Wayne start working for Lidl?" + ); + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryValues)[0].getText()).to.equal("10 October 1999"); + + // Jane Doe + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( + "When did Jane Doe start working for Lidl?" + ); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 2000"); + + // John Smith + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryItems)[0].getText()).to.equal( + "When did John Smith start working for Lidl?" + ); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryValues)[0].getText()).to.equal("3 March 2010"); + + // Product details + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).to.equal( + "Articles and equipment for sports or outdoor games" + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).to.equal( + "Volume of sales for Articles and equipment for sports or outdoor games" + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).to.equal( + "Total volume produced for Articles and equipment for sports or outdoor games" + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).to.equal("100 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).to.equal("200 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).to.equal("Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).to.equal("Volume of sales for Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).to.equal( + "Total volume produced for Kitchen Equipment" + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).to.equal("50 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).to.equal("300 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).to.equal("Sales during the previous quarter"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).to.equal( + "Value of sales for Articles and equipment for sports or outdoor games" + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[1].getText()).to.equal("Value of sales for Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[2].getText()).to.equal("Value of sales for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[3].getText()).to.equal("Value of sales from other categories"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[0].getText()).to.equal("£110.00"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[1].getText()).to.equal("£220.00"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[2].getText()).to.equal("No answer provided"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[3].getText()).to.equal("£330.00"); + }); +}); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js deleted file mode 100644 index 47489e5d8d..0000000000 --- a/tests/functional/spec/features/supplementary_data/supplementary_data_non_list_items.spec.js +++ /dev/null @@ -1,175 +0,0 @@ -import HubPage from "../../../base_pages/hub.page"; -import IntroductionBlockPage from "../../../generated_pages/supplementary_data/introduction-block.page.js"; -import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; -import EmailBlockPage from "../../../generated_pages/supplementary_data/email-block.page.js"; -import NewEmailPage from "../../../generated_pages/supplementary_data/new-email.page.js"; -import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; -import CalculatedSummarySalesPage from "../../../generated_pages/supplementary_data/calculated-summary-sales.page.js"; -import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; -import TradingPage from "../../../generated_pages/supplementary_data/trading.page"; -import ListCollectorPage from "../../../generated_pages/supplementary_data/list-collector.page"; -import AnyEmployeesPage from "../../../generated_pages/supplementary_data/any-employees.page"; -import Section2Page from "../../../generated_pages/supplementary_data/section-2-summary.page.js"; -import AddEmployeePage from "../../../generated_pages/supplementary_data/list-collector-add.page.js"; -import Section3Page from "../../../generated_pages/supplementary_data/section-3-summary.page.js"; -import LengthOfEmploymentPage from "../../../generated_pages/supplementary_data/length-of-employment.page.js"; -import SupermarketTransportPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport.page"; -import ThankYouPage from "../../../base_pages/thank-you.page"; -import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page"; - -// :TODO: this test currently only runs locally, remove the .skip once the mock endpoint is running in a container -describe.skip("Using supplementary data non list items", () => { - const summaryTitles = ".ons-summary__item-title"; - const summaryValues = ".ons-summary__values"; - const summaryRowTitles = ".ons-summary__row-title"; - - before("Starting the survey", async () => { - await browser.openQuestionnaire("test_supplementary_data.json", { - version: "v2", - sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", - }); - }); - - it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { - await $(HubPage.submit()).click(); - await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); - await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( - "If the company details or structure have changed contact us on 01171231231" - ); - await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); - await $(IntroductionBlockPage.submit()).click(); - await $(HubPage.submit()).click(); - await $(EmailBlockPage.yes()).click(); - await $(EmailBlockPage.submit()).click(); - }); - - it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { - await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).to.equal("Thursday 27 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).to.equal("Friday 28 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).to.equal("Saturday 29 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(3)).getText()).to.equal("Sunday 30 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(4)).getText()).to.equal("Monday 1 December 1947"); - await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).to.equal("Tuesday 2 December 1947"); - await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).to.equal("Wednesday 3 December 1947"); - await $(TradingPage.answerByIndex(3)).click(); - await $(TradingPage.submit()).click(); - }); - - it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { - await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); - await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); - await $(SalesBreakdownBlockPage.submit()).click(); - await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 555,000"); - }); - - it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { - await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); - await $(SalesBreakdownBlockPage.submit()).click(); - await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( - "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?" - ); - }); - - it("Given I have an interstitial block with all answers and supplementary data, When I reach this block, Then I see the placeholders rendered correctly", async () => { - await $(CalculatedSummarySalesPage.submit()).click(); - await expect(await $(Section1InterstitialPage.questionText()).getText()).to.contain("Summary of information provided for Tesco"); - await expect(await $("body").getText()).to.have.string("Telephone Number: 01171231231"); - await expect(await $("body").getText()).to.have.string("Email: contact@tesco.org"); - await expect(await $("body").getText()).to.have.string("Note Title: Value of total sales"); - await expect(await $("body").getText()).to.have.string("Note Description: Total value of goods sold during the period of the return"); - await expect(await $("body").getText()).to.have.string("Note Example Title: Including"); - await expect(await $("body").getText()).to.have.string("Note Example Description: Sales across all UK stores"); - await expect(await $("body").getText()).to.have.string("Incorporation Date: 27 November 1947"); - await expect(await $("body").getText()).to.have.string("Trading start date: 30 November 1947"); - await expect(await $("body").getText()).to.have.string("Guidance: Some supplementary guidance about the survey"); - await expect(await $("body").getText()).to.have.string("Total Uk Sales: £555,000.00"); - await expect(await $("body").getText()).to.have.string("Bristol sales: £333,000.00"); - await expect(await $("body").getText()).to.have.string("London sales: £111,000.00"); - await expect(await $("body").getText()).to.have.string("Sum of Bristol and London sales: £444,000.00"); - }); - - it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { - await $(Section1InterstitialPage.submit()).click(); - await expect(await $(Section1Page.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); - await expect(await $(Section1Page.sameEmailAnswer()).getText()).to.contain("Yes"); - await expect(await $(Section1Page.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); - await expect(await $(Section1Page.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(Section1Page.salesBristolAnswer()).getText()).to.contain("£333,000.00"); - await expect(await $(Section1Page.salesLondonAnswer()).getText()).to.contain("£111,000.00"); - }); - - it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { - await $(Section1Page.sameEmailAnswerEdit()).click(); - await $(EmailBlockPage.no()).click(); - await $(EmailBlockPage.submit()).click(); - await $(NewEmailPage.answer()).setValue("new@tesco.com"); - await $(NewEmailPage.submit()).click(); - await $(Section1Page.previous()).click(); - await expect(await $("body").getText()).to.have.string("Email: new@tesco.com"); - await $(Section1InterstitialPage.submit()).click(); - await $(Section1Page.submit()).click(); - }); - - it("Given I add some employees via a list collector, When I begin a repeating section for that employee, Then I see static supplementary data piped in correctly", async () => { - await $(HubPage.submit()).click(); - await $(AnyEmployeesPage.yes()).click(); - await $(AnyEmployeesPage.submit()).click(); - await $(AddEmployeePage.employeeFirstName()).setValue("Jane"); - await $(AddEmployeePage.employeeLastName()).setValue("Doe"); - await $(AddEmployeePage.submit()).click(); - await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); - await $(AddEmployeePage.employeeFirstName()).setValue("John"); - await $(AddEmployeePage.employeeLastName()).setValue("Smith"); - await $(AddEmployeePage.submit()).click(); - await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(Section2Page.submit()).click(); - await $(HubPage.submit()).click(); - await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Jane Doe start working for Tesco?"); - await expect(await $(LengthOfEmploymentPage.employmentStartLegend()).getText()).to.contain("Start date at Tesco"); - }); - - it("Given I have validation on the start date in the repeating section, When I enter a date before the incorporation date, Then I see an error message", async () => { - await $(LengthOfEmploymentPage.day()).setValue(1); - await $(LengthOfEmploymentPage.month()).setValue(1); - await $(LengthOfEmploymentPage.year()).setValue(1930); - await $(LengthOfEmploymentPage.submit()).click(); - await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); - }); - - it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { - await $(LengthOfEmploymentPage.year()).setValue(1990); - await $(LengthOfEmploymentPage.submit()).click(); - await expect(await $(Section3Page.lengthEmploymentQuestion()).getText()).to.contain("When did Jane Doe start working for Tesco?"); - await expect(await $(Section3Page.employmentStart()).getText()).to.contain("1 January 1990"); - }); - - it("Given I can view my response after submission, When I submit the survey, Then I see the values I've entered and correct rendering with supplementary data", async () => { - await $(Section3Page.submit()).click(); - await $(HubPage.submit()).click(); - await $(LengthOfEmploymentPage.day()).setValue(5); - await $(LengthOfEmploymentPage.month()).setValue(6); - await $(LengthOfEmploymentPage.year()).setValue(2011); - await $(LengthOfEmploymentPage.submit()).click(); - await $(Section3Page.submit()).click(); - await $(HubPage.submit()).click(); - await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); - await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).to.contain("No"); - await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).to.contain("What is the new contact email for Tesco?"); - await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).to.contain("new@tesco.com"); - await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); - await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).to.contain("£333,000.00"); - await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).to.contain("£111,000.00"); - await expect(await $(ViewSubmittedResponsePage.group3Content(0)).$$(summaryTitles)[0].getText()).to.equal("When did Jane Doe start working for Tesco?"); - await expect(await $(ViewSubmittedResponsePage.group3Content(0)).$$(summaryValues)[0].getText()).to.equal("1 January 1990"); - await expect(await $(ViewSubmittedResponsePage.group3Content("0-1")).$$(summaryTitles)[0].getText()).to.equal( - "When did John Smith start working for Tesco?" - ); - await expect(await $(ViewSubmittedResponsePage.group3Content("0-1")).$$(summaryValues)[0].getText()).to.equal("5 June 2011"); - }); -}); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 895d30c094..c1cb92edbe 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -22,6 +22,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ describe("Given I add list items with hardcoded total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic and static answers page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { + await $(TotalBlockPage.acceptCookies()).click(); await addTwoSupermarkets(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); diff --git a/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py b/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py deleted file mode 100644 index b63a102135..0000000000 --- a/tests/integration/questionnaire/test_questionnaire_supplementary_data_non_list_items.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest - -from tests.integration.integration_test_case import IntegrationTestCase - - -# :TODO: this test currently only runs locally, remove the skip once the mock endpoint is running in a container -@pytest.mark.skip("This test can only run locally at the moment") -class TestQuestionnaireSupplementaryDataNonListItems(IntegrationTestCase): - def test_supplementary_data_non_list_items(self): - self.launchSupplementaryDataSurvey( - "test_supplementary_data", - sds_dataset_id="693dc252-2e90-4412-bd9c-c4d953e36fcd", - ru_ref="12346789012A", - survey_id="123", - ) - self.post() - self.assertInBody("You are completing this survey for Lidl") - self.assertInBody( - "If the company details or structure have changed contact us on 01174564561" - ) - - # as there is no supplementary guidance, the list should only contain the two static pieces. - guidance_list_items = ( - self.getHtmlSoup().find("div", {"id": "business-details"}).findAll("li") - ) - assert len(guidance_list_items) == 2 - self.post() - self.post() - self.post({"same-email-answer": "Yes"}) - self.post({"trading-answer": "1947-11-30"}) - - # supplementary data can be used for validation - self.post({"sales-bristol-answer": "444000", "sales-london-answer": "222000"}) - self.assertInBody("Enter answers that add up to or are less than 555,000") - - # a calculated sum equal to the supplementary data value should be allowed and show correctly - self.post({"sales-bristol-answer": "444000", "sales-london-answer": "111000"}) - self.assertInBody( - "Total value of sales from Bristol and London is calculated to be £555,000.00. Is this correct?" - ) - self.post() - - # all supplementary data values can be piped into the interstitial block - self.assertInBody("Telephone Number: 01174564561") - self.assertInBody("Email: contact@lidl.org") - self.assertInBody("Note Title: Value of total sales") - self.assertInBody("Note Description: Total value of goods") - self.assertInBody("Note Example Title: Includes") - self.assertInBody( - "Note Example Description: Sales across all EU stores" - ) - self.assertInBody("Incorporation Date: 27 November 1947") - self.assertInBody("Trading start date: 30 November 1947") - self.assertInBody("Guidance: None") - self.assertInBody("Total Uk Sales: £555,000.00") - self.assertInBody("Bristol sales: £444,000.00") - self.assertInBody("London sales: £111,000.00") - self.assertInBody( - "Sum of Bristol and London sales: £555,000.00" - ) From 0b08e4b4a2be9ceeb5ccc2a9e46de23722a566bc Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:57:22 +0100 Subject: [PATCH 258/567] Remove mock SDS from Runner (#1163) --- doc/run-mock-sds-endpoint.md | 27 --- scripts/mock_data/supplementary_data.json | 131 -------------- scripts/mock_data/supplementary_data_v2.json | 162 ------------------ ...pplementary_dataset_metadata_response.json | 26 --- scripts/mock_sds_endpoint.py | 65 ------- 5 files changed, 411 deletions(-) delete mode 100644 doc/run-mock-sds-endpoint.md delete mode 100644 scripts/mock_data/supplementary_data.json delete mode 100644 scripts/mock_data/supplementary_data_v2.json delete mode 100644 scripts/mock_data/supplementary_dataset_metadata_response.json delete mode 100644 scripts/mock_sds_endpoint.py diff --git a/doc/run-mock-sds-endpoint.md b/doc/run-mock-sds-endpoint.md deleted file mode 100644 index 7aba12abb6..0000000000 --- a/doc/run-mock-sds-endpoint.md +++ /dev/null @@ -1,27 +0,0 @@ -# Running the Mock SDS endpoint - -In order to test loading supplementary data, we have a development script that creates a Mock SDS endpoint in Flask that -returns mocked supplementary data and mock dataset metadata for that supplementary data. - -Ensure the following env var is set before running the script: - -```bash -SDS_API_BASE_URL=http://localhost:5003 -``` - -From the home directory, run using - -```bash -python -m scripts.mock_sds_endpoint -``` - -The following datasets are available using the mocked endpoint. On selecting a survey that supports supplementary data, -an `sds_dataset_id` dropdown will be shown in the metadata, and if you set the `survey_id` to `123`, it will be -populated with the following options. - -| Dataset ID | Description | -|----------------------------------------|--------------------------------------------------------------| -| `c067f6de-6d64-42b1-8b02-431a3486c178` | Basic supplementary data structure with no repeating items | -| `34a80231-c49a-44d0-91a6-8fe1fb190e64` | Supplementary data structure with repeating items | -| `6b378962-f0c7-4e8c-947e-7d24ee1b6b88` | Supplementary data structure with additional repeating items | - diff --git a/scripts/mock_data/supplementary_data.json b/scripts/mock_data/supplementary_data.json deleted file mode 100644 index 56548159ce..0000000000 --- a/scripts/mock_data/supplementary_data.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "company_name": "Tesco", - "company_details": { - "telephone_number": "01171231231", - "email": "contact@tesco.org" - }, - "note": { - "title": "Value of total sales", - "description": "Total value of goods sold during the period of the return", - "example": { - "title": "Including", - "description": "Sales across all UK stores" - } - }, - "guidance": "Some supplementary guidance about the survey", - "total_uk_sales": 555000.00, - "incorporation_date": "1947-11-27", - "items": { - "products": [ - { - "identifier": "89929001", - "name": "Articles and equipment for sports or outdoor games", - "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", - "guidance_include": { - "title": "Include", - "list": [ - "for children's playgrounds", - "swimming pools and paddling pools" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", - "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" - ] - }, - "value_sales": { - "answer_code": "89929001", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "89929002", - "label": "Volume of sales", - "unit_label": "Tonnes" - }, - "total_volume": { - "answer_code": "89929005", - "label": "Total volume produced", - "unit_label": "Tonnes" - } - }, - { - "identifier": "202346331", - "name": "Kitchen Equipment", - "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", - "guidance_include": { - "title": "Include", - "list": [ - "pots and pans", - "chopping board" - ] - }, - "value_sales": { - "answer_code": "20234633101", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "20234633102", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "20234633103", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - } - ], - "employees": [ - { - "identifier": "429001", - "personal_details": { - "forename": "Harry", - "surname": "Potter", - "address": { - "postcode": "BS1 1AJ", - "house_number": "12", - "city": "Bristol" - } - }, - "employment_details": { - "job_title": "Customer assistant", - "start_date": "2020-01-01", - "salary": { - "payroll_number": "54345", - "value": "25000", - "currency": "GBP" - } - } - }, - { - "identifier": "429002", - "personal_details": { - "forename": "Clark", - "surname": "Kent", - "address": { - "postcode": "E1 7DD", - "house_number": "21", - "city": "London" - } - }, - "employment_details": { - "job_title": "Warehouse operative", - "start_date": "2015-05-10", - "salary": { - "payroll_number": "34123", - "value": "29000", - "currency": "GBP" - } - } - } - ] - } - } -} diff --git a/scripts/mock_data/supplementary_data_v2.json b/scripts/mock_data/supplementary_data_v2.json deleted file mode 100644 index 90060f6bf2..0000000000 --- a/scripts/mock_data/supplementary_data_v2.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd", - "survey_id": "123", - "data": { - "schema_version": "v1", - "identifier": "12346789012A", - "company_name": "Lidl", - "company_details": { - "telephone_number": "01174564561", - "email": "contact@lidl.org" - }, - "note": { - "title": "Value of total sales", - "description": "Total value of goods sold", - "example": { - "title": "Includes", - "description": "Sales across all EU stores" - } - }, - "total_uk_sales": 555000.00, - "incorporation_date": "1947-11-27", - "items": { - "products": [ - { - "identifier": "89929001", - "name": "Articles and equipment for sports or outdoor games", - "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", - "guidance_include": { - "title": "Include", - "list": [ - "for children's playgrounds", - "swimming pools and paddling pools" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates", - "for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics" - ] - }, - "value_sales": { - "answer_code": "89929001", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "89929002", - "label": "Volume of sales", - "unit_label": "Tonnes" - }, - "total_volume": { - "answer_code": "89929005", - "label": "Total volume produced", - "unit_label": "Tonnes" - } - }, - { - "identifier": "202346331", - "name": "Kitchen Equipment", - "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", - "guidance_include": { - "title": "Include", - "list": [ - "pots and pans", - "chopping board" - ] - }, - "guidance_exclude": { - "title": "Exclude", - "list": [ - "cutlery" - ] - }, - "value_sales": { - "answer_code": "20234633101", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "20234633102", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "20234633103", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - }, - { - "identifier": "246331", - "name": "Groceries", - "cn_codes": "1028 + 5908 + 5910 + 591110 + 591120", - "guidance_include": { - "title": "Include", - "list": [ - "fruit and vegetables", - "baked goods" - ] - }, - "value_sales": { - "answer_code": "202101", - "label": "Value of sales" - }, - "volume_sales": { - "answer_code": "202102", - "label": "Volume of sales", - "unit_label": "Kilogram" - }, - "total_volume": { - "answer_code": "202103", - "label": "Total volume produced", - "unit_label": "Kilogram" - } - } - ], - "employees": [ - { - "identifier": "429001", - "personal_details": { - "forename": "Harry", - "surname": "Potter", - "address": { - "postcode": "BS1 1AJ", - "house_number": "12", - "city": "Bristol" - } - }, - "employment_details": { - "job_title": "Customer assistant", - "start_date": "2020-01-01", - "salary": { - "payroll_number": "54345", - "value": "25000", - "currency": "GBP" - } - } - }, - { - "identifier": "529001", - "personal_details": { - "forename": "Bruce", - "surname": "Wayne", - "address": { - "postcode": "BS1 1HJ", - "house_number": "15", - "city": "Bristol" - } - }, - "employment_details": { - "job_title": "Customer assistant", - "start_date": "2019-03-01", - "salary": { - "payroll_number": "4345", - "value": "27000", - "currency": "GBP" - } - } - } - ] - } - } -} diff --git a/scripts/mock_data/supplementary_dataset_metadata_response.json b/scripts/mock_data/supplementary_dataset_metadata_response.json deleted file mode 100644 index 2182fd5c33..0000000000 --- a/scripts/mock_data/supplementary_dataset_metadata_response.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "survey_id": "123", - "period_id": "202301", - "title": "Supplementary data", - "sds_schema_version": 1, - "sds_published_at": "2022-03-01T12:30:02Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "c067f6de-6d64-42b1-8b02-431a3486c178" - }, - { - "survey_id": "123", - "period_id": "202301", - "title": "Supplementary data v2", - "sds_schema_version": 1, - "sds_published_at": "2023-07-01T11:25:01Z", - "total_reporting_units": 2, - "schema_version": "v1.0.0", - "sds_dataset_version": 1, - "filename": "", - "dataset_id": "693dc252-2e90-4412-bd9c-c4d953e36fcd" - } -] diff --git a/scripts/mock_sds_endpoint.py b/scripts/mock_sds_endpoint.py deleted file mode 100644 index faf45f0658..0000000000 --- a/scripts/mock_sds_endpoint.py +++ /dev/null @@ -1,65 +0,0 @@ -import json - -import yaml -from flask import Flask, Response, request -from sdc.crypto.jwe_helper import JWEHelper -from sdc.crypto.key_store import KeyStore - -from app.keys import KEY_PURPOSE_SDS - -app = Flask(__name__) -with open("dev-keys.yml", encoding="UTF-8") as keys_file: - keys = KeyStore(yaml.safe_load(keys_file)) - - -@app.route("/v1/unit_data") -def get_sds_data(): - dataset_id = request.args.get("dataset_id") - - guid_filename_map = { - "c067f6de-6d64-42b1-8b02-431a3486c178": "supplementary_data", - "693dc252-2e90-4412-bd9c-c4d953e36fcd": "supplementary_data_v2", - } - - if filename := guid_filename_map.get(dataset_id): - return encrypt_mock_data(load_mock_data(f"scripts/mock_data/{filename}.json")) - - return Response(status=404) - - -@app.route("/v1/dataset_metadata") -def get_sds_dataset_ids(): - survey_id = request.args.get("survey_id") - period_id = request.args.get("period_id") - - return load_mock_sds_dataset_metadata(survey_id, period_id) - - -def load_mock_data(filename): - with open(filename, encoding="utf-8") as mock_data_file: - return json.load(mock_data_file) - - -def load_mock_sds_dataset_metadata(survey_id: str, period_id: str): - del period_id # not required for mock - - survey_id_filename_map = { - "123": "supplementary_dataset_metadata_response", - } - - if filename := survey_id_filename_map.get(survey_id): - return load_mock_data(f"scripts/mock_data/{filename}.json") - - return Response(status=404) - - -def encrypt_mock_data(mock_data): - key = keys.get_key(purpose=KEY_PURPOSE_SDS, key_type="private") - mock_data["data"] = JWEHelper.encrypt_with_key( - json.dumps(mock_data["data"]), key.kid, key.as_jwk() - ) - return mock_data - - -if __name__ == "__main__": - app.run(host="localhost", port=5003) From ac6958aea2736121355d16577fa4f7465d475da6 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:45:55 +0100 Subject: [PATCH 259/567] Update JS packages to latest (#1177) --- package.json | 44 +- tests/functional/jwt_helper.js | 14 +- tests/functional/spec/checkbox.spec.js | 2 +- .../functional/spec/components/radio/radio.js | 2 +- .../calculated_summary_test_case.js | 36 +- ...mmary_repeating_and_static_answers.spec.js | 16 +- ...alculated_summary_repeating_blocks.spec.js | 18 +- ...lculated_summary_repeating_section.spec.js | 36 +- .../dynamic_answer_options/function_driven.js | 2 +- .../dynamic_answers_list_value_source.spec.js | 28 +- ...summary_cross_section_dependencies.spec.js | 12 +- ...lculated_summary_multiple_sections.spec.js | 16 +- ...ulated_summary_overlapping_answers.spec.js | 16 +- .../placeholder_date_difference.spec.js | 2 +- .../list_collector_repeating_blocks.spec.js | 2 +- ...bmit_with_summary_return_to_answer.spec.js | 6 +- .../supplementary_data.spec.js | 40 +- .../view_submitted_response.spec.js | 2 +- tests/functional/spec/feedback.spec.js | 2 +- tests/functional/spec/introduction.spec.js | 6 +- ...ollector_driving_question_checkbox.spec.js | 4 +- .../list_collector_section_summary.spec.js | 6 +- tests/functional/spec/preview.spec.js | 2 +- .../spec/question_definitions.spec.js | 2 +- .../question_definitions_array_type.spec.js | 2 +- .../spec/radio_checkbox_descriptions.spec.js | 4 +- ..._and_skipping_section_dependencies.spec.js | 4 +- tests/functional/spec/thank_you.spec.js | 2 +- .../functional/spec/timeout/timeout_modal.js | 4 +- tests/functional/wdio.conf.js | 4 +- yarn.lock | 5187 +++++++++-------- 31 files changed, 3051 insertions(+), 2472 deletions(-) diff --git a/package.json b/package.json index 589f4068c3..ef3ee1004f 100644 --- a/package.json +++ b/package.json @@ -23,37 +23,37 @@ "wdio": "wdio run ./wdio.conf.js" }, "devDependencies": { - "@babel/core": "^7.17.5", - "@babel/plugin-transform-runtime": "^7.17.0", - "@babel/preset-env": "^7.16.11", - "@babel/register": "^7.17.0", - "@babel/runtime": "^7.17.2", - "@wdio/cli": "^8.3.10", - "@wdio/local-runner": "^8.3.10", - "@wdio/mocha-framework": "^8.3.0", - "@wdio/spec-reporter": "^8.3.0", - "chai": "^4.3.6", - "chromedriver": "^114.0.0", - "eslint": "^8.10.0", + "@babel/core": "^7.22.9", + "@babel/plugin-transform-runtime": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/register": "^7.22.5", + "@babel/runtime": "^7.22.6", + "@wdio/cli": "^8.14.4", + "@wdio/local-runner": "^8.14.3", + "@wdio/mocha-framework": "^8.14.0", + "@wdio/spec-reporter": "^8.14.0", + "chai": "^4.3.7", + "chromedriver": "^115.0.0", + "eslint": "^8.46.0", "eslint-cli": "^1.1.1", - "eslint-config-standard": "^14.1.1", + "eslint-config-standard": "^17.1.0", "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-import": "^2.25.4", + "eslint-plugin-import": "^2.28.0", "eslint-plugin-json": "^3.1.0", + "eslint-plugin-n": "^16.0.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.0.0", - "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-promise": "^6.1.1", "json-web-key": "^0.4.0", - "jsrsasign": "^10.5.25", - "lint-staged": "^12.3.5", + "jsrsasign": "^10.8.6", + "lint-staged": "^13.2.3", "livereload": "^0.9.3", "node-forge": "^1.2.1", "node-jose": "^2.2.0", - "prettier": "^2.5.1", - "typescript": "^4.7.4", - "uuid": "^8.3.2", + "prettier": "^3.0.1", + "typescript": "^5.1.6", + "uuid": "^9.0.0", "wdio-chromedriver-service": "^8.1.1", - "webdriverio": "8.3.10" + "webdriverio": "8.14.3" }, "prettier": {} } diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index 3fe2cdb3e5..d05713f379 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -75,7 +75,7 @@ export function generateToken( languageCode = "en", includeLogoutUrl = true, displayAddress = "", - } + }, ) { const schemaParts = schemaRegEx.exec(schema); @@ -100,9 +100,9 @@ export function generateToken( if (version === "v2") { payload = { tx_id: txId, - jti: jti, - iat: iat, - exp: exp, + jti, + iat, + exp, case_id: caseId, response_id: responseId, schema_name: `${schemaParts[1]}_${schemaParts[2]}`, @@ -117,9 +117,9 @@ export function generateToken( } else { payload = { tx_id: txId, - jti: jti, - iat: iat, - exp: exp, + jti, + iat, + exp, user_id: userId, case_id: caseId, ru_ref: "12346789012A", diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index c40bd5a93f..9f13e9e24c 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -44,7 +44,7 @@ describe('Checkbox with "other" option', () => { await $(MandatoryCheckboxPage.submit()).click(); // Then await expect(await $(MandatoryCheckboxPage.error()).getHTML()).to.contain( - 'Select at least one answer to ‘Which pizza toppings would you like?’' + 'Select at least one answer to ‘Which pizza toppings would you like?’', ); }); diff --git a/tests/functional/spec/components/radio/radio.js b/tests/functional/spec/components/radio/radio.js index ccdf6c3e88..97585100a7 100644 --- a/tests/functional/spec/components/radio/radio.js +++ b/tests/functional/spec/components/radio/radio.js @@ -51,7 +51,7 @@ describe("Component: Radio", () => { it("When I have submitted the page without any option, Then the question text is hidden in the error message using a span element", async () => { await $(RadioMandatoryOverriddenPage.submit()).click(); await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).to.contain( - 'Select an answer to ‘What do you prefer for breakfast?’' + 'Select an answer to ‘What do you prefer for breakfast?’', ); }); }); diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index 4953a4aa71..b03d811c9b 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -74,7 +74,7 @@ class TestCase { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £20.71. Is this correct?" + "We calculate the total of currency values entered to be £20.71. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); @@ -85,7 +85,7 @@ class TestCase { await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Second answer label also in currency total (optional)" + "Second answer label also in currency total (optional)", ); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); @@ -93,7 +93,7 @@ class TestCase { await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Fourth answer label also in total (optional)" + "Fourth answer label also in total (optional)", ); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); @@ -106,7 +106,7 @@ class TestCase { it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( - "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer" + "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer", ); }); @@ -129,7 +129,7 @@ class TestCase { await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £30.71. Is this correct?" + "We calculate the total of currency values entered to be £30.71. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); @@ -141,7 +141,7 @@ class TestCase { await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £28.37. Is this correct?" + "We calculate the total of currency values entered to be £28.37. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); @@ -166,7 +166,7 @@ class TestCase { await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.36. Is this correct?" + "We calculate the total of currency values entered to be £9.36. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); @@ -175,7 +175,7 @@ class TestCase { // Totals and titles should be shown await $(CurrencyTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" + "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); @@ -195,7 +195,7 @@ class TestCase { // Totals and titles should be shown await $(UnitTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of percentage values entered to be 79%. Is this correct?" + "We calculate the total of percentage values entered to be 79%. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); @@ -211,7 +211,7 @@ class TestCase { // Totals and titles should be shown await $(UnitTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of number values entered to be 124.58. Is this correct?" + "We calculate the total of number values entered to be 124.58. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); @@ -269,7 +269,7 @@ class TestCase { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.41. Is this correct?" + "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -295,7 +295,7 @@ class TestCase { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £15.91. Is this correct?" + "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -322,7 +322,7 @@ class TestCase { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £6.91. Is this correct?" + "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -363,7 +363,7 @@ class TestCase { await $(SubmitPage.setMinimumAnswerEdit()).click(); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).to.contain( - "Set minimum and maximum values based on your calculated summary total of £25.92" + "Set minimum and maximum values based on your calculated summary total of £25.92", ); await $(SetMinMaxBlockPage.submit()).click(); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £25.92"); @@ -402,10 +402,10 @@ class TestCase { it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( - "60 - calculated summary answer (previous section)" + "60 - calculated summary answer (previous section)", ); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( - "40 - calculated summary answer (current section)" + "40 - calculated summary answer (current section)", ); }); @@ -433,10 +433,10 @@ class TestCase { await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( - "30 - calculated summary answer (previous section)" + "30 - calculated summary answer (previous section)", ); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( - "40 - calculated summary answer (current section)" + "40 - calculated summary answer (current section)", ); }); } diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index e94f6461ed..d9bea2ef81 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -50,13 +50,13 @@ describe("Calculated summary with repeating answers", function () { it("Given I complete all list collector dynamic answers for two calculated summaries one of which also has static answers, I'm taken to each one in turn, showing the correct answers", async () => { await $(ExtraSpendingBlockPage.submit()).click(); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?" + "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?", ); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).to.contain("£550.00"); await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); await $(CalculatedSummarySpendingPage.submit()).click(); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total visits to the shop to be 6. Is this correct?" + "We calculate the total visits to the shop to be 6. Is this correct?", ); await assertSummaryValues(["4", "2"]); }); @@ -74,7 +74,7 @@ describe("Calculated summary with repeating answers", function () { await $(DynamicAnswerPage.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total visits to the shop to be 7. Is this correct?" + "We calculate the total visits to the shop to be 7. Is this correct?", ); await assertSummaryValues(["4", "3"]); await $(CalculatedSummaryVisitsPage.submit()).click(); @@ -93,7 +93,7 @@ describe("Calculated summary with repeating answers", function () { // then calculated summary await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?" + "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?", ); // then jump straight back to section summary (as other calculated summary is unchanged @@ -124,14 +124,14 @@ describe("Calculated summary with repeating answers", function () { // first calc summary await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?" + "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?", ); await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); // second calculated summary await $(CalculatedSummarySpendingPage.submit()).click(); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total visits to the shop to be 14. Is this correct?" + "We calculate the total visits to the shop to be 14. Is this correct?", ); await assertSummaryValues(["4", "3", "2"]); await $(CalculatedSummaryVisitsPage.submit()).click(); @@ -157,12 +157,12 @@ describe("Calculated summary with repeating answers", function () { // Tesco is now gone await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?" + "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?", ); await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); await $(CalculatedSummarySpendingPage.submit()).click(); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total visits to the shop to be 10. Is this correct?" + "We calculate the total visits to the shop to be 10. Is this correct?", ); await assertSummaryValues(["3", "7"]); await $(CalculatedSummaryVisitsPage.submit()).click(); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index 13abc8836b..d7b34a00de 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -46,7 +46,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I have a calculated summary using both list repeating block and static answers, When I reach the calculated summary page, Then I see the correct items and total.", async () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?" + "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00"]); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly expenditure travelling by car"); @@ -59,7 +59,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I have a calculated summary using a single answer from a repeating block, When I reach the calculated summary page, Then I see the correct items and total", async () => { await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total journeys made per month to be 12. Is this correct?" + "We calculate the total journeys made per month to be 12. Is this correct?", ); await assertSummaryValues(["10", "2"]); await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Bus"); @@ -81,7 +81,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(ListCollectorPage.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?" + "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); }); @@ -90,7 +90,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(CalculatedSummarySpendingPage.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total journeys made per month to be 18. Is this correct?" + "We calculate the total journeys made per month to be 18. Is this correct?", ); await assertSummaryValues(["10", "2", "6"]); await $(CalculatedSummaryCountPage.previous()).click(); @@ -118,7 +118,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(TransportRepeatingBlock1Page.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?" + "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); await $(CalculatedSummarySpendingPage.submit()).click(); @@ -130,7 +130,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(TransportRepeatingBlock2Page.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total journeys made per month to be 24. Is this correct?" + "We calculate the total journeys made per month to be 24. Is this correct?", ); await assertSummaryValues(["10", "2", "12"]); await $(CalculatedSummaryCountPage.submit()).click(); @@ -142,7 +142,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(RemoveTransportPage.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?" + "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£0.00", "£265.00", "£100.00", "£50.00"]); }); @@ -151,7 +151,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(CalculatedSummarySpendingPage.submit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total journeys made per month to be 14. Is this correct?" + "We calculate the total journeys made per month to be 14. Is this correct?", ); await assertSummaryValues(["2", "12"]); }); @@ -222,7 +222,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { // other calculated summary should not be on the path, so go straight back to the spending one which now has none of the list items await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?" + "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?", ); await assertSummaryValues(["£100.00"]); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 4229eb140a..69c522408a 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -62,7 +62,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £20.71. Is this correct?" + "We calculate the total of currency values entered to be £20.71. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); @@ -73,7 +73,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Second answer label also in currency total (optional)" + "Second answer label also in currency total (optional)", ); await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); @@ -81,7 +81,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( - "Fourth answer label also in total (optional)" + "Fourth answer label also in total (optional)", ); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); @@ -94,7 +94,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( - "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer" + "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer", ); }); @@ -117,7 +117,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £30.71. Is this correct?" + "We calculate the total of currency values entered to be £30.71. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); }); @@ -129,7 +129,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £28.37. Is this correct?" + "We calculate the total of currency values entered to be £28.37. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); @@ -154,7 +154,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.36. Is this correct?" + "We calculate the total of currency values entered to be £9.36. Is this correct?", ); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); }); @@ -163,7 +163,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { // Totals and titles should be shown await $(CurrencyTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of unit values entered to be 1,467 cm. Is this correct?" + "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); @@ -183,7 +183,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { // Totals and titles should be shown await $(UnitTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of percentage values entered to be 79%. Is this correct?" + "We calculate the total of percentage values entered to be 79%. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); @@ -203,7 +203,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { // Totals and titles should be shown await $(UnitTotalPlaybackPage.submit()).click(); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of number values entered to be 124.58. Is this correct?" + "We calculate the total of number values entered to be 124.58. Is this correct?", ); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); @@ -223,7 +223,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(BreakdownPage.submit()).click(); await expect(await browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of number values entered to be £124.58. Is this correct?" + "We calculate the total of number values entered to be £124.58. Is this correct?", ); await expect(await $("body").getText()).to.have.string("Enter two values that add up to the previous calculated summary total of £124.58"); await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); @@ -269,7 +269,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £9.41. Is this correct?" + "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -297,7 +297,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £15.91. Is this correct?" + "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -326,7 +326,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SixthNumberBlockPage.submit()).click(); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total of currency values entered to be £6.91. Is this correct?" + "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await $(CurrencyTotalPlaybackPage.submit()).click(); @@ -445,10 +445,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( - "60 - calculated summary answer (previous section)" + "60 - calculated summary answer (previous section)", ); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( - "40 - calculated summary answer (current section)" + "40 - calculated summary answer (current section)", ); }); @@ -476,10 +476,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( - "30 - calculated summary answer (previous section)" + "30 - calculated summary answer (previous section)", ); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( - "40 - calculated summary answer (current section)" + "40 - calculated summary answer (current section)", ); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 409017a77b..9b08f5a242 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -17,7 +17,7 @@ const testCases = [ { schemaName: "test_dynamic_answer_options_function_driven.json", answerOptionCount: 7, - dropdownOptionValues: dropdownOptionValues, + dropdownOptionValues, }, ]; diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index c024ca0667..b66c0a8853 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -30,7 +30,7 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); - await $(DynamicAnswerPage.labels()).waitForExist({ timeout: timeout }); + await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); await setMinimumAndGetSectionSummary(timeout); @@ -40,7 +40,7 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); @@ -52,7 +52,7 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); @@ -71,8 +71,8 @@ describe("Dynamic answers list value source", () => { await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); - await $(DynamicAnswerPage.labels()).waitForExist({ timeout: timeout }); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); + await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).to.equal("12"); await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).to.equal("21"); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); @@ -86,11 +86,11 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); - await $$(DynamicAnswerPage.inputs())[0].waitForExist({ timeout: timeout }); + await $$(DynamicAnswerPage.inputs())[0].waitForExist({ timeout }); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $$(DynamicAnswerPage.inputs())[1].setValue(12); await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); }); @@ -103,7 +103,7 @@ describe("Dynamic answers list value source", () => { await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).to.be.true; await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[1].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).to.be.true; @@ -116,7 +116,7 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[0].$("a").click(); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); }); @@ -125,7 +125,7 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.supermarketsListRemoveLink(1)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.supermarketsListRemoveLink(1)).waitForExist({ timeout }); await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await $(ListCollectorRemovePage.submit()).click(); @@ -134,7 +134,7 @@ describe("Dynamic answers list value source", () => { await $(SetMinimumPage.submit()).click(); await $(DynamicAnswerOnlyPage.submit()).click(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(5); @@ -155,7 +155,7 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await $(DriverPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.true; await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.true; await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); @@ -182,7 +182,7 @@ async function addTwoSupermarkets(timeout) { await $(ListCollectorAddPage.submit()).click(); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).click(); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout: timeout }); + await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); } async function setMinimumAndGetSectionSummary(timeout) { @@ -190,5 +190,5 @@ async function setMinimumAndGetSectionSummary(timeout) { await $(SetMinimumPage.setMinimum()).setValue(2); await $(SetMinimumPage.submit()).click(); await $(DynamicAnswerOnlyPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: timeout }); + await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); } diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index 13dd580147..4b6391b705 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -40,7 +40,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£330.00"); await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).to.contain("£70.00"); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £400.00. Is this correct?" + "The grand calculated summary is calculated to be £400.00. Is this correct?", ); await $(CurrencyAllPage.submit()).click(); }); @@ -54,7 +54,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £330.00. Is this correct?" + "The grand calculated summary is calculated to be £330.00. Is this correct?", ); await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; }); @@ -69,7 +69,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £330.00. Is this correct?" + "The grand calculated summary is calculated to be £330.00. Is this correct?", ); await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; await $(CurrencyAllPage.submit()).click(); @@ -85,7 +85,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £460.00. Is this correct?" + "The grand calculated summary is calculated to be £460.00. Is this correct?", ); }); it("Given I provide an answer to question 3b from the grand calculated summary, this opens up an additional question, and when I press continue I am taken to this question first, then the calculated summary, and then the grand calculated summary", async () => { @@ -100,7 +100,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(CurrencyQuestion3Page.submit()).click(); await expect(await browser.getUrl()).to.contain(CurrencyAllPage.pageName); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £471.00. Is this correct?" + "The grand calculated summary is calculated to be £471.00. Is this correct?", ); await $(CurrencyAllPage.submit()).click(); }); @@ -113,7 +113,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£30.00"); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £171.00. Is this correct?" + "The grand calculated summary is calculated to be £171.00. Is this correct?", ); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js index d9a3ed2a9c..30c82ca769 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js @@ -44,7 +44,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £415.00. Is this correct?" + "Grand Calculated Summary for section 1 and 2 is calculated to be £415.00. Is this correct?", ); await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); @@ -56,7 +56,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 4 is calculated to be £15.00. Is this correct?" + "Calculated Summary for Question 4 is calculated to be £15.00. Is this correct?", ); await $(CalculatedSummary4Page.q4A1Edit()).click(); await expect(await browser.getUrl()).to.contain(Block4Page.pageName); @@ -67,21 +67,21 @@ describe("Feature: Grand Calculated Summary", () => { // first taken back to the calculated summary which has updated await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 4 is calculated to be £60.00. Is this correct?" + "Calculated Summary for Question 4 is calculated to be £60.00. Is this correct?", ); await $(CalculatedSummary4Page.submit()).click(); // then taken back to the grand calculated summary which has also been updated correctly await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £460.00. Is this correct?" + "Grand Calculated Summary for section 1 and 2 is calculated to be £460.00. Is this correct?", ); }); it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £100.00. Is this correct?" + "Calculated Summary for Question 1 is calculated to be £100.00. Is this correct?", ); // change first answer @@ -93,7 +93,7 @@ describe("Feature: Grand Calculated Summary", () => { // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £190.00. Is this correct?" + "Calculated Summary for Question 1 is calculated to be £190.00. Is this correct?", ); // change another answer @@ -104,7 +104,7 @@ describe("Feature: Grand Calculated Summary", () => { // back at updated calculated summary await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £550.00. Is this correct?" + "Calculated Summary for Question 1 is calculated to be £550.00. Is this correct?", ); // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary @@ -115,7 +115,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummary1Page.submit()).click(); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £910.00. Is this correct?" + "Grand Calculated Summary for section 1 and 2 is calculated to be £910.00. Is this correct?", ); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index c466bad957..990e685913 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -33,7 +33,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(Section1SummaryPage.submit()).click(); await $(HubPage.submit()).click(); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?" + "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?", ); await $(GrandCalculatedSummaryShoppingPage.submit()).click(); }); @@ -52,7 +52,7 @@ describe("Feature: Grand Calculated Summary", () => { // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?" + "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?", ); }); @@ -65,21 +65,21 @@ describe("Feature: Grand Calculated Summary", () => { // taken back to the FIRST calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( - "Total of eggs and cheese is calculated to be £700.00. Is this correct?" + "Total of eggs and cheese is calculated to be £700.00. Is this correct?", ); await $(CalculatedSummary2Page.submit()).click(); // taken back to the SECOND calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Total extra items cost is calculated to be £410.00. Is this correct?" + "Total extra items cost is calculated to be £410.00. Is this correct?", ); await $(CalculatedSummary4Page.submit()).click(); // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary of purchases this week comes to £1,220.00. Is this correct?" + "Grand Calculated Summary of purchases this week comes to £1,220.00. Is this correct?", ); }); @@ -92,21 +92,21 @@ describe("Feature: Grand Calculated Summary", () => { // taken back to the FIRST calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( - "Total of eggs and cheese is calculated to be £800.00. Is this correct?" + "Total of eggs and cheese is calculated to be £800.00. Is this correct?", ); await $(CalculatedSummary2Page.submit()).click(); // taken back to the SECOND calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Total extra items cost is calculated to be £510.00. Is this correct?" + "Total extra items cost is calculated to be £510.00. Is this correct?", ); await $(CalculatedSummary4Page.submit()).click(); // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?" + "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?", ); await $(GrandCalculatedSummaryShoppingPage.submit()).click(); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js index 81f5706091..50a0cb1d86 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js @@ -33,7 +33,7 @@ describe("Difference check (months and years)", () => { await $(AgeBlockMonthYearPage.submit()).click(); await expect(await $(AgeTestMonthYearPage.heading()).getText()).to.equal( - `It has been ${getYears("1990/01/01")} years since you last went on holiday. Is this correct?` + `It has been ${getYears("1990/01/01")} years since you last went on holiday. Is this correct?`, ); }); }); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index fe2b5d1561..f73dd16b05 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -27,7 +27,7 @@ async function addCompany( registrationDateMonth, registrationDateYear, authorisedTraderUk, - authorisedTraderEu + authorisedTraderEu, ) { await $(AddCompanyPage.companyOrBranchName()).setValue(companyOrBranchName); await $(AddCompanyPage.submit()).click(); diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index a9598dc402..35352ef9a3 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -26,7 +26,7 @@ describe("Summary Anchor Scrolling", () => { await $(InsuranceAddressPage.submit()).click(); await $(AddressDurationPage.submit()).click(); await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( - "insurance-address/?return_to=section-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2" + "insurance-address/?return_to=section-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", ); }); @@ -34,7 +34,7 @@ describe("Summary Anchor Scrolling", () => { await $(InsuranceAddressPage.submit()).click(); await $(AddressDurationPage.submit()).click(); await expect(await $(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( - "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name" + "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name", ); }); @@ -69,7 +69,7 @@ describe("Summary Anchor Scrolling", () => { await $(HouseholdDetailsSummaryPage.submit()).click(); await $(SubmitPage.summaryShowAllButton()).click(); await expect(await $(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( - "?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2" + "?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", ); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 1da142dde5..6e8bac8a5c 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -41,7 +41,7 @@ describe.skip("Using supplementary data", () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", - responseId: responseId, + responseId, }); }); @@ -50,7 +50,7 @@ describe.skip("Using supplementary data", () => { await $(IntroductionBlockPage.acceptCookies()).click(); await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( - "If the company details or structure have changed contact us on 01171231231" + "If the company details or structure have changed contact us on 01171231231", ); await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); await $(IntroductionBlockPage.submit()).click(); @@ -82,7 +82,7 @@ describe.skip("Using supplementary data", () => { await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); await $(SalesBreakdownBlockPage.submit()).click(); await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( - "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?" + "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?", ); }); @@ -236,16 +236,16 @@ describe.skip("Using supplementary data", () => { await expect(await $("body").getHTML()).to.have.string("
    • swimming pools and paddling pools
    • "); await expect(await $("body").getHTML()).to.have.string("

      Exclude

      "); await expect(await $("body").getHTML()).to.have.string( - "
    • sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates
    • " + "
    • sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates
    • ", ); await expect(await $("body").getHTML()).to.have.string( - "
    • for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics
    • " + "
    • for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics
    • ", ); await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).to.contain( - "Volume of sales for Articles and equipment for sports or outdoor games" + "Volume of sales for Articles and equipment for sports or outdoor games", ); await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain( - "Total volume produced for Articles and equipment for sports or outdoor games" + "Total volume produced for Articles and equipment for sports or outdoor games", ); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(100); await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(200); @@ -270,7 +270,7 @@ describe.skip("Using supplementary data", () => { await $(ListCollectorProductsPage.no()).click(); await $(ListCollectorProductsPage.submit()).click(); await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?" + "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); assertSummaryValues(["100 kg", "50 kg"]); @@ -279,7 +279,7 @@ describe.skip("Using supplementary data", () => { it("Given I have another calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?" + "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?", ); assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); assertSummaryValues(["200 kg", "300 kg"]); @@ -298,7 +298,7 @@ describe.skip("Using supplementary data", () => { it("Given I have a calculated summary of dynamic answers for a supplementary list, When I reach the calculated summary, Then I see the correct supplementary data in the title and labels", async () => { await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).to.contain( - "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?" + "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?", ); assertSummaryItems([ "Value of sales for Articles and equipment for sports or outdoor games", @@ -330,7 +330,7 @@ describe.skip("Using supplementary data", () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", - responseId: responseId, + responseId, }); await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Bruce Wayne"); @@ -383,50 +383,50 @@ describe.skip("Using supplementary data", () => { // Harry Potter await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( - "When did Harry Potter start working for Lidl?" + "When did Harry Potter start working for Lidl?", ); await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 1990"); // Bruce Wayne await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryItems)[0].getText()).to.equal( - "When did Bruce Wayne start working for Lidl?" + "When did Bruce Wayne start working for Lidl?", ); await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryValues)[0].getText()).to.equal("10 October 1999"); // Jane Doe await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( - "When did Jane Doe start working for Lidl?" + "When did Jane Doe start working for Lidl?", ); await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 2000"); // John Smith await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryItems)[0].getText()).to.equal( - "When did John Smith start working for Lidl?" + "When did John Smith start working for Lidl?", ); await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryValues)[0].getText()).to.equal("3 March 2010"); // Product details await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).to.equal( - "Articles and equipment for sports or outdoor games" + "Articles and equipment for sports or outdoor games", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).to.equal( - "Volume of sales for Articles and equipment for sports or outdoor games" + "Volume of sales for Articles and equipment for sports or outdoor games", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).to.equal( - "Total volume produced for Articles and equipment for sports or outdoor games" + "Total volume produced for Articles and equipment for sports or outdoor games", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).to.equal("100 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).to.equal("200 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).to.equal("Kitchen Equipment"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).to.equal("Volume of sales for Kitchen Equipment"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).to.equal( - "Total volume produced for Kitchen Equipment" + "Total volume produced for Kitchen Equipment", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).to.equal("50 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).to.equal("300 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).to.equal("Sales during the previous quarter"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).to.equal( - "Value of sales for Articles and equipment for sports or outdoor games" + "Value of sales for Articles and equipment for sports or outdoor games", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[1].getText()).to.equal("Value of sales for Kitchen Equipment"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[2].getText()).to.equal("Value of sales for Groceries"); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 6ae9cc47f5..62b0128499 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -55,7 +55,7 @@ describe("View Submitted Response", () => { await $(ViewSubmittedResponsePage.downloadButton()).click(); await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.true; await expect(await $(ViewSubmittedResponsePage.informationPanel()).getHTML()).to.contain( - "For security, you can no longer view or get a copy of your answers" + "For security, you can no longer view or get a copy of your answers", ); }); }); diff --git a/tests/functional/spec/feedback.spec.js b/tests/functional/spec/feedback.spec.js index 9f49629c4f..5919a683f3 100644 --- a/tests/functional/spec/feedback.spec.js +++ b/tests/functional/spec/feedback.spec.js @@ -28,7 +28,7 @@ describe("Feedback", () => { await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); await expect(await $(FeedbackPage.errorPanel()).isExisting()).to.be.true; await expect(await $(FeedbackPage.errorPanel()).getText()).to.contain( - "There are 2 problems with your feedback\nSelect what your feedback is about\nEnter your feedback" + "There are 2 problems with your feedback\nSelect what your feedback is about\nEnter your feedback", ); }); diff --git a/tests/functional/spec/introduction.spec.js b/tests/functional/spec/introduction.spec.js index 33077210c9..1c7dd64431 100644 --- a/tests/functional/spec/introduction.spec.js +++ b/tests/functional/spec/introduction.spec.js @@ -10,12 +10,12 @@ describe("Introduction page", () => { await browser.openQuestionnaire(introductionSchema); await expect(await $(IntroductionPage.useOfData()).getText()).to.contain("How we use your data"); await expect(await $(IntroductionPage.useOfInformation()).getText()).to.contain( - "Data should relate to all sites in England, Scotland and Wales unless otherwise stated." + "Data should relate to all sites in England, Scotland and Wales unless otherwise stated.", ); await expect(await $(IntroductionPage.legalResponse()).getText()).to.contain("Your response is legally required"); await expect(await $(IntroductionPage.legalBasis()).getText()).to.contain("Notice is given under section 999 of the Test Act 2000"); await expect(await $(IntroductionPage.introDescription()).getText()).to.contain( - "To take part, all you need to do is check that you have the information you need to answer the survey questions." + "To take part, all you need to do is check that you have the information you need to answer the survey questions.", ); }); it("Given I start a survey with introduction guidance set, When I view the introduction page, Then I should be able to see introduction guidance", async () => { @@ -23,7 +23,7 @@ describe("Introduction page", () => { await expect(await $(IntroductionPage.guidancePanel(1)).isDisplayed()).to.be.true; await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain("Coronavirus (COVID-19) guidance"); await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain( - "Explain your figures in the comment section to minimise us contacting you and to help us tell an industry story" + "Explain your figures in the comment section to minimise us contacting you and to help us tell an industry story", ); }); }); diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js index 8ea27498d8..924d06abe8 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js @@ -83,7 +83,7 @@ describe("Given the user says no one else lives in the house", () => { await $(AnyoneUsuallyLiveAtPage.submit()).click(); await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( - "You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?" + "You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?", ); }); }); @@ -98,7 +98,7 @@ describe("Given a person does not live in the house", () => { await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); await $(AnyoneUsuallyLiveAtPage.submit()).click(); await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( - "You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?" + "You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?", ); await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index c3baa42f50..501dc6e305 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -35,11 +35,11 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); const listItemId = (await listItemIds())[0]; await expect(await $(companiesListRowItemAnchor(1)).getHTML()).to.contain( - `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name` + `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name`, ); await expect(await $(companiesListRowItemAnchor(2)).getHTML()).to.contain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); await expect(await $(companiesListRowItemAnchor(3)).getHTML()).to.contain( - `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio` + `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio`, ); }); it("When I add multiple items, Then all the items should be visible on the section summary and have correct values", async () => { @@ -105,7 +105,7 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).to.be.true; await expect(await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).to.be.true; await expect(await $(AnyCompaniesOrBranchesAddPage.heading()).getText()).to.contain( - "Give details about the company or branch that undertakes general insurance business" + "Give details about the company or branch that undertakes general insurance business", ); }); it("When I add an item and relevant data, Then I should be able to edit that item from the section summary page.", async () => { diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index e62d6610e7..f264602ef1 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -68,7 +68,7 @@ describe("Introduction preview questions", () => { expect(await browser.getUrl()).to.contain("questionnaire/preview"); expect(await $(previewSectionTitle).getText()).to.equal("Main section"); expect(await $$(previewQuestion)[2].$("h3").getText()).to.equal( - "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?" + "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", ); }); diff --git a/tests/functional/spec/question_definitions.spec.js b/tests/functional/spec/question_definitions.spec.js index ee925326d7..abec9c374d 100644 --- a/tests/functional/spec/question_definitions.spec.js +++ b/tests/functional/spec/question_definitions.spec.js @@ -14,7 +14,7 @@ describe("Component: Definition", () => { // Then await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( - "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." + "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power.", ); }); diff --git a/tests/functional/spec/question_definitions_array_type.spec.js b/tests/functional/spec/question_definitions_array_type.spec.js index 51e32785f2..66a8b9b899 100644 --- a/tests/functional/spec/question_definitions_array_type.spec.js +++ b/tests/functional/spec/question_definitions_array_type.spec.js @@ -14,7 +14,7 @@ describe("Component: Definition", () => { // Then await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( - "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power." + "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power.", ); }); diff --git a/tests/functional/spec/radio_checkbox_descriptions.spec.js b/tests/functional/spec/radio_checkbox_descriptions.spec.js index e6dbb60d3d..38fdcbe3c9 100644 --- a/tests/functional/spec/radio_checkbox_descriptions.spec.js +++ b/tests/functional/spec/radio_checkbox_descriptions.spec.js @@ -9,7 +9,7 @@ describe("Checkbox and Radio item descriptions", () => { it("When the schema defines a description for a checkbox option, then that description is displayed", async () => { await expect( - await $(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText() + await $(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText(), ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); }); @@ -17,7 +17,7 @@ describe("Checkbox and Radio item descriptions", () => { await $(CheckboxBlockPage.newBusinessPracticesForOrganisingProcedures()).click(); await $(CheckboxBlockPage.submit()).click(); await expect( - await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText() + await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText(), ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); }); }); diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 127af94bf1..f1a20b0515 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -187,7 +187,7 @@ describe("Routing and skipping section dependencies", () => { await expectPersonalDetailsName(); await expectPersonalDetailsAge(); await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( - "I did, but it was removed from the path as I changed my answer to No on the skip question" + "I did, but it was removed from the path as I changed my answer to No on the skip question", ); }); }); @@ -478,7 +478,7 @@ const expectPersonalDetailsAge = async () => { const expectReasonNoConfirmationAnswer = async () => { await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( - "I did not visit section 2, so confirmation was not needed" + "I did not visit section 2, so confirmation was not needed", ); }; diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index de0b431a2d..ef00141f1f 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -58,7 +58,7 @@ describe("Thank You Default View Response Enabled", () => { await expect(await $(ThankYouPage.viewAnswersTitle()).getHTML()).to.contain("Get a copy of your answers"); await expect(await $(ThankYouPage.viewAnswersLink()).getText()).to.contain("save or print your answers"); await expect(await $(ThankYouPage.viewSubmittedCountdown()).getHTML()).to.contain( - "For security, your answers will only be available to view for another" + "For security, your answers will only be available to view for another", ); }); diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index ec634787e6..336612c2ab 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -12,7 +12,7 @@ class TestCase { "This is because you have either:", "been inactive for 45 minutes and your session has timed out to protect your information", "followed a link to a page you are not signed in to", - "followed a link to a survey that has already been submitted" + "followed a link to a survey that has already been submitted", ) .to.not.include("To protect your information, your progress will be saved and you will be signed out in"); }).timeout(140000); @@ -44,7 +44,7 @@ class TestCase { async checkTimeoutModal() { await $(TimeoutModalPage.timer()).waitForDisplayed({ timeout: 70000 }); await expect(await $(TimeoutModalPage.timer()).getText()).to.equal( - "To protect your information, your progress will be saved and you will be signed out in 59 seconds." + "To protect your information, your progress will be saved and you will be signed out in 59 seconds.", ); } } diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index bcb7d716c6..eeb2c27b7f 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -216,7 +216,7 @@ export const config = { region = "GB-ENG", language = "en", includeLogoutUrl = false, - } = {} + } = {}, ) { const token = await JwtHelper.generateToken(schema, { version, @@ -234,7 +234,7 @@ export const config = { includeLogoutUrl, }); this.url(`/session?token=${token}`); - } + }, ); }, // beforeCommand: function (commandName, args) { diff --git a/yarn.lock b/yarn.lock index ebd0b67798..d6a9cb3247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,414 +2,294 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== - -"@babel/core@^7.17.5": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz" - integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.9" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" +"@assemblyscript/loader@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" + integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== + +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" + json5 "^2.2.2" + semver "^6.3.1" -"@babel/generator@^7.17.9", "@babel/generator@^7.20.7": - version "7.20.14" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== +"@babel/generator@^7.22.7", "@babel/generator@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": - version "7.20.12" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz" - integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.2.1" - -"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2", "@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" + integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" + integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" + integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" - semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== dependencies: - "@babel/types" "^7.18.6" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/types" "^7.22.5" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== +"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" + integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.9" + +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helper-wrap-function@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" + integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: - "@babel/types" "^7.20.7" - -"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.17.7", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - -"@babel/helpers@^7.17.9": - version "7.20.13" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.17.9", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== +"@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - -"@babel/plugin-proposal-async-generator-functions@^7.16.8": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz" - integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-proposal-export-namespace-from@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-proposal-numeric-separator@^7.16.7": +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - -"@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.16.7", "@babel/plugin-proposal-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz" - integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.16.11": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-private-property-in-object@^7.16.7": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz" - integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== dependencies: "@babel/helper-create-regexp-features-plugin" "^7.18.6" @@ -417,396 +297,551 @@ "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-arrow-functions@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-to-generator@^7.16.8": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== +"@babel/plugin-transform-async-generator-functions@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b" + integrity sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== +"@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + +"@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.16.7": - version "7.20.15" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz" - integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - -"@babel/plugin-transform-classes@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/plugin-transform-block-scoping@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" + integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== +"@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== +"@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== +"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-duplicate-keys@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== +"@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== +"@babel/plugin-transform-dynamic-import@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" + integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-transform-for-of@^7.16.7": - version "7.18.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== +"@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-function-name@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== +"@babel/plugin-transform-export-namespace-from@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" + integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-literals@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== +"@babel/plugin-transform-for-of@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== +"@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" + integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-modules-amd@^7.16.7": - version "7.20.11" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== +"@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.20.11" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== +"@babel/plugin-transform-logical-assignment-operators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" + integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.20.11" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== +"@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-umd@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== +"@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== +"@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-new-target@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== +"@babel/plugin-transform-modules-systemjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" -"@babel/plugin-transform-object-super@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== +"@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-parameters@^7.16.7", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-property-literals@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== +"@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" + integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-regenerator@^7.16.7": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== +"@babel/plugin-transform-numeric-separator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" + integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" + integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.5" + +"@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + +"@babel/plugin-transform-optional-catch-binding@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" + integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" + integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" + integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" regenerator-transform "^0.15.1" -"@babel/plugin-transform-reserved-words@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== +"@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-runtime@^7.17.0": - version "7.17.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz" - integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== +"@babel/plugin-transform-runtime@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz#a87b11e170cbbfb018e6a2bf91f5c6e533b9e027" + integrity sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ== dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - semver "^6.3.0" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== +"@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-spread@^7.16.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== +"@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" -"@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== +"@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-template-literals@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== +"@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typeof-symbol@^7.16.7": - version "7.18.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== +"@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.18.10" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== +"@babel/plugin-transform-unicode-escapes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.16.11": - version "7.16.11" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz" - integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== - dependencies: - "@babel/compat-data" "^7.16.8" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" - "@babel/plugin-proposal-async-generator-functions" "^7.16.8" - "@babel/plugin-proposal-class-properties" "^7.16.7" - "@babel/plugin-proposal-class-static-block" "^7.16.7" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.16.7" - "@babel/plugin-proposal-json-strings" "^7.16.7" - "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.16.7" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.16.7" - "@babel/plugin-proposal-private-methods" "^7.16.11" - "@babel/plugin-proposal-private-property-in-object" "^7.16.7" - "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" +"@babel/preset-env@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.9.tgz#57f17108eb5dfd4c5c25a44c1977eba1df310ac7" + integrity sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -816,50 +851,67 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.16.7" - "@babel/plugin-transform-async-to-generator" "^7.16.8" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.16.7" - "@babel/plugin-transform-classes" "^7.16.7" - "@babel/plugin-transform-computed-properties" "^7.16.7" - "@babel/plugin-transform-destructuring" "^7.16.7" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.16.7" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.16.7" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.16.7" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.16.7" - "@babel/plugin-transform-modules-commonjs" "^7.16.8" - "@babel/plugin-transform-modules-systemjs" "^7.16.7" - "@babel/plugin-transform-modules-umd" "^7.16.7" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" - "@babel/plugin-transform-new-target" "^7.16.7" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.16.7" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.16.7" - "@babel/plugin-transform-reserved-words" "^7.16.7" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.16.7" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.16.7" - "@babel/plugin-transform-typeof-symbol" "^7.16.7" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.7" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-classes" "^7.22.6" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" + "@babel/plugin-transform-numeric-separator" "^7.22.5" + "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.6" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.16.8" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.20.2" - semver "^6.3.0" + "@babel/types" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + core-js-compat "^3.31.0" + semver "^6.3.1" "@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + version "0.1.6" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6.tgz#31bcdd8f19538437339d17af00d177d854d9d458" + integrity sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" @@ -867,10 +919,10 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/register@^7.17.0": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz" - integrity sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA== +"@babel/register@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939" + integrity sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -880,58 +932,70 @@ "@babel/regjsgen@^0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.17.2", "@babel/runtime@^7.8.4": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/traverse@^7.17.9", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": - version "7.20.13" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" +"@babel/runtime@^7.22.6", "@babel/runtime@^7.8.4": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== +"@babel/types@^7.22.5", "@babel/types@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" -"@eslint/eslintrc@^1.2.2": - version "1.4.1" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz" - integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" + integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== + +"@eslint/eslintrc@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.1.tgz#18d635e24ad35f7276e8a49d135c7d3ca6a46f93" + integrity sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.4.0" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -939,58 +1003,72 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@eslint/js@^8.46.0": + version "8.46.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" + integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== + +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" - resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@jest/expect-utils@^29.4.3": - version "29.4.3" - resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.3.tgz" - integrity sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jest/expect-utils@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" + integrity sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg== dependencies: jest-get-type "^29.4.3" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/types@^29.4.3": - version "29.4.3" - resolved "https://registry.npmjs.org/@jest/types/-/types-29.4.3.tgz" - integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA== +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -998,322 +1076,386 @@ "@jridgewell/resolve-uri@3.1.0": version "3.1.0" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@sinclair/typebox@^0.25.16": - version "0.25.23" - resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.23.tgz" - integrity sha512-VEB8ygeP42CFLWyAJhN5OklpxUliqdNEUcXb4xZ/CINqtYGTjL5ukluKdKzQ0iWdUxyQ7B0539PAUhHKrCNWSQ== +"@ljharb/through@^2.3.9": + version "2.3.9" + resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.9.tgz#85f221eb82f9d555e180e87d6e50fb154af85408" + integrity sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@puppeteer/browsers@1.4.6": + version "1.4.6" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.4.6.tgz#1f70fd23d5d2ccce9d29b038e5039d7a1049ca77" + integrity sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ== + dependencies: + debug "4.3.4" + extract-zip "2.0.1" + progress "2.0.3" + proxy-agent "6.3.0" + tar-fs "3.0.4" + unbzip2-stream "1.4.3" + yargs "17.7.1" + +"@puppeteer/browsers@^1.4.6": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.5.0.tgz#2c445f7e41133d4aa23f776748d70211ea4e98ed" + integrity sha512-za318PweGINh5LnHSph7C4xhs0tmRjCD8EPpzcKlw4nzSPhnULj+LTG3+TGefZvW1ti5gjw2JkdQvQsivBeZlg== + dependencies: + debug "4.3.4" + extract-zip "2.0.1" + progress "2.0.3" + proxy-agent "6.3.0" + tar-fs "3.0.4" + unbzip2-stream "1.4.3" + yargs "17.7.1" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sindresorhus/is@^5.2.0": - version "5.3.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz" - integrity sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw== + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== "@szmarczak/http-timer@^5.0.1": version "5.0.1" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== dependencies: defer-to-connect "^2.0.1" "@testim/chrome-version@^1.1.3": version "1.1.3" - resolved "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.3.tgz#fbb68696899d7b8c1b9b891eded9c04fe2cd5529" integrity sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A== +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@types/http-cache-semantics@^4.0.1": version "4.0.1" - resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.1" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== dependencies: "@types/istanbul-lib-report" "*" "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/mocha@^10.0.0": version "10.0.1" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== -"@types/node@*", "@types/node@^18.0.0": - version "18.13.0" - resolved "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz" - integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== +"@types/node@*", "@types/node@^20.1.0", "@types/node@^20.1.1": + version "20.4.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.6.tgz#b66b66c9bb5d49b199f03399e341c9d6036e9e88" + integrity sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA== "@types/normalize-package-data@^2.4.1": version "2.4.1" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== "@types/stack-utils@^2.0.0": version "2.0.1" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/which@^2.0.1": version "2.0.2" - resolved "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/@types/which/-/which-2.0.2.tgz#54541d02d6b1daee5ec01ac0d1b37cecf37db1ae" integrity sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw== "@types/ws@^8.5.3": - version "8.5.4" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" - integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "21.0.0" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": version "2.10.0" - resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== dependencies: "@types/node" "*" -"@wdio/cli@^8.3.10": - version "8.3.10" - resolved "https://registry.npmjs.org/@wdio/cli/-/cli-8.3.10.tgz" - integrity sha512-Zlr/qhzZLL7tWJB28mJ/p2KcOlb2FPfJLocUq3uqnGmTsbTdjSp0h0tVBYE6BjmwL7pKLlE+g9/fYFkjiRQMTA== - dependencies: - "@types/node" "^18.0.0" - "@wdio/config" "8.3.2" - "@wdio/globals" "8.3.10" - "@wdio/logger" "8.1.0" - "@wdio/protocols" "8.3.5" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" +"@wdio/cli@^8.14.4": + version "8.14.4" + resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-8.14.4.tgz#3eba90787fcf697251004e97a6f70ca82000c47a" + integrity sha512-67a/DhDr2jm/mnH+Asvn0vvY9eT9vK4lLecBv20X0hMkLAS2qKLEbJz1+JfewP2ScArlSluG4R7Zq/qU0F+i3g== + dependencies: + "@types/node" "^20.1.1" + "@wdio/config" "8.14.0" + "@wdio/globals" "8.14.3" + "@wdio/logger" "8.11.0" + "@wdio/protocols" "8.11.0" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" async-exit-hook "^2.0.1" - chalk "^5.0.1" + chalk "^5.2.0" chokidar "^3.5.3" - cli-spinners "^2.6.1" - ejs "^3.1.8" - execa "^7.0.0" - import-meta-resolve "^2.1.0" - inquirer "9.1.2" + cli-spinners "^2.9.0" + ejs "^3.1.9" + execa "^7.1.1" + import-meta-resolve "^3.0.0" + inquirer "9.2.9" lodash.flattendeep "^4.4.0" lodash.pickby "^4.6.0" lodash.union "^4.6.0" - mkdirp "^2.0.0" - read-pkg-up "9.1.0" - recursive-readdir "^2.2.2" - webdriverio "8.3.10" - yargs "^17.5.1" + read-pkg-up "10.0.0" + recursive-readdir "^2.2.3" + webdriverio "8.14.3" + yargs "^17.7.2" yarn-install "^1.0.0" -"@wdio/config@8.3.2": - version "8.3.2" - resolved "https://registry.npmjs.org/@wdio/config/-/config-8.3.2.tgz" - integrity sha512-lLZh53MorYRxAr08x2o6B1npRgc5ZmGEGdzH2neILnVcs/x3fQV9uFKpC9sKtMmU8B0G0oMUSoIaj8GfqygcNQ== +"@wdio/config@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.14.0.tgz#0bb0aec83b0972d1118dfcb4d5d5962ecc751025" + integrity sha512-p1bXsoljGuLrw8I2ZKbh6SkmBzyzDZykpHDpCsCsNASVQSVsL7e0PMykLCfKFWq3lQsg7aOX9DPYOtFlOykz/w== dependencies: - "@wdio/logger" "8.1.0" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" decamelize "^6.0.0" - deepmerge-ts "^4.2.2" - glob "^8.0.3" - import-meta-resolve "^2.1.0" - read-pkg-up "^9.1.0" - -"@wdio/globals@8.3.10": - version "8.3.10" - resolved "https://registry.npmjs.org/@wdio/globals/-/globals-8.3.10.tgz" - integrity sha512-ZlvCqOWZjkbDFxtskAaON/pH56RzSAfRTNPLMzIK7j3xnZT2d5Oi0te0Oo0Hs13sEoTEaqSzwJjBVIHueOk3Dw== + deepmerge-ts "^5.0.0" + glob "^10.2.2" + import-meta-resolve "^3.0.0" + read-pkg-up "^10.0.0" + +"@wdio/globals@8.14.3", "@wdio/globals@^8.13.1": + version "8.14.3" + resolved "https://registry.yarnpkg.com/@wdio/globals/-/globals-8.14.3.tgz#1d5be730f7d36297c2abaa37416b6681bb84bb1c" + integrity sha512-CKQeYnWa8umh7UrSSZWBqHUDIaB/j55xEY0m6lw57kjAAmh6XJxfnqfkddo64EPICZuEHRWrwLJVSRXRCBRxrA== optionalDependencies: - expect-webdriverio "^4.0.1" - webdriverio "8.3.10" - -"@wdio/globals@^8.2.4": - version "8.3.9" - resolved "https://registry.npmjs.org/@wdio/globals/-/globals-8.3.9.tgz" - integrity sha512-Y4cB2l92NO2WqgkRQE/oLwrvCAT/pPSvmFXKvk0avsHbTPHnukaf0gfB0ku0EKbsyaC9glwPE/PEVrXSC8Hsiw== - optionalDependencies: - expect-webdriverio "^4.0.1" - webdriverio "8.3.9" - -"@wdio/local-runner@^8.3.10": - version "8.3.10" - resolved "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.3.10.tgz" - integrity sha512-5FZCu+AgcWVGaLpWV7nHchzECFlVdVS3OF2GjhPz3srTPh0NAQkvH0+WXtpXxgNgeURIcaBUx/NxqquMyuc7DQ== - dependencies: - "@types/node" "^18.0.0" - "@wdio/logger" "8.1.0" - "@wdio/repl" "8.1.0" - "@wdio/runner" "8.3.10" - "@wdio/types" "8.3.0" + expect-webdriverio "^4.2.5" + webdriverio "8.14.3" + +"@wdio/local-runner@^8.14.3": + version "8.14.3" + resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-8.14.3.tgz#39fd347a0463fe6e173152cecd26bb42f6ec5767" + integrity sha512-y4pHl8xphE0fEuDI1JXBVH7FQo8YAyEk32lmpOD7rsI/+M+b+ukYcSQtfHfVPVYGNVvipFbiavi8yJPho0jF+w== + dependencies: + "@types/node" "^20.1.0" + "@wdio/logger" "8.11.0" + "@wdio/repl" "8.10.1" + "@wdio/runner" "8.14.3" + "@wdio/types" "8.14.0" async-exit-hook "^2.0.1" split2 "^4.1.0" stream-buffers "^3.0.2" -"@wdio/logger@8.1.0", "@wdio/logger@^8.1.0": - version "8.1.0" - resolved "https://registry.npmjs.org/@wdio/logger/-/logger-8.1.0.tgz" - integrity sha512-QRC5b7FF4JIYUCqggnVA0sZ80TwIUFN9JyBSbuGuMxaSLSLujSo7WfuSrnQXVvsRbnJ16wWwJWYigfLkxOW86Q== +"@wdio/logger@8.11.0", "@wdio/logger@^8.1.0", "@wdio/logger@^8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-8.11.0.tgz#df28cb65d7b9e308a0cd1445a99adc8b5730174c" + integrity sha512-IsuKSaYi7NKEdgA57h8muzlN/MVp1dQG+V4C//7g4m03YJUnNQLvDhJzLjdeNTfvZy61U7foQSyt+3ktNzZkXA== dependencies: chalk "^5.1.2" loglevel "^1.6.0" loglevel-plugin-prefix "^0.8.4" - strip-ansi "^6.0.0" + strip-ansi "^7.1.0" -"@wdio/mocha-framework@^8.3.0": - version "8.3.0" - resolved "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.3.0.tgz" - integrity sha512-19BVsTe0DBlArNy12HTYAEFAepUXvU67JceKUOpNcYIr8lpk6ZqPVZ8tm4GiBTDvt7FSt8u5Iw2BsnsrcbQGuA== +"@wdio/mocha-framework@^8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-8.14.0.tgz#78e1568cdb9b89e11dfcc007720869509962d485" + integrity sha512-j2ngUFGF0T+Zax3Tops6TxyWNy+OxvMRsloZaE+wIMrAMnp6ocGC+W4si2cW5ZuZlvaoM6j406XZOmxW//pUCA== dependencies: "@types/mocha" "^10.0.0" - "@types/node" "^18.0.0" - "@wdio/logger" "8.1.0" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" + "@types/node" "^20.1.0" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" mocha "^10.0.0" -"@wdio/protocols@8.3.5": - version "8.3.5" - resolved "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.3.5.tgz" - integrity sha512-/zWz+ok6gIB1/L/VEOCoD8nCB8inNF+rsVOYps3FgNbrliQ782YLfA1uAVJTw3U6CaO+7zZ8aJw82EswpoxWjg== +"@wdio/protocols@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-8.11.0.tgz#78f46d416e68c32effbd60cda1fb317db2c02067" + integrity sha512-eXTMYt/XoaX53H/Q2qmsn1uWthIC5aSTGtX9YyXD/AkagG2hXeX3lLmzNWBaSIvKR+vWXRYbg3Y/7IvL2s25Wg== -"@wdio/repl@8.1.0": - version "8.1.0" - resolved "https://registry.npmjs.org/@wdio/repl/-/repl-8.1.0.tgz" - integrity sha512-96G4TzbYnRf95+GURo15FYt6iTYq85nbWU6YQedLRAV15RfSp4foKTbAnq++bKKMALNL6gdzTc+HGhQr3Q0sQg== +"@wdio/repl@8.10.1": + version "8.10.1" + resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-8.10.1.tgz#4f1858335d510b3a73432be163ef303aa79db362" + integrity sha512-VZ1WFHTNKjR8Ga97TtV2SZM6fvRjWbYI2i/f4pJB4PtusorKvONAMJf2LQcUBIyzbVobqr7KSrcjmSwRolI+yw== dependencies: - "@types/node" "^18.0.0" + "@types/node" "^20.1.0" -"@wdio/reporter@8.3.0": - version "8.3.0" - resolved "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.3.0.tgz" - integrity sha512-SCI6YXMDOs+rIDXUqaQYncv3SAcMPU9nQKGCLWjtZXsYHYlLZMT/PaFy7lWyebsObiRAKJYayXOqG7ymS6hDzA== +"@wdio/reporter@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-8.14.0.tgz#1f597392370947f982e415d37cf305fcf9da4330" + integrity sha512-xzsHRlnb35vFB/zs04Zsuh2Y24DRDuLWTYA96PxDNqMIjhkqJbERTJWSlRwiBhyhz6N8NrwAQfghyh93d554eg== dependencies: - "@types/node" "^18.0.0" - "@wdio/logger" "8.1.0" - "@wdio/types" "8.3.0" + "@types/node" "^20.1.0" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.14.0" diff "^5.0.0" object-inspect "^1.12.0" - supports-color "9.3.1" - -"@wdio/runner@8.3.10": - version "8.3.10" - resolved "https://registry.npmjs.org/@wdio/runner/-/runner-8.3.10.tgz" - integrity sha512-jdbcbbC0oMB79WQO1wEmLb6q1msiF7RAUnLoj7TSLrBr/yVqSuURIHpde5fo4d0GdYoPuVIS0VF06bw6o7QhTA== - dependencies: - "@types/node" "^18.0.0" - "@wdio/config" "8.3.2" - "@wdio/globals" "8.3.10" - "@wdio/logger" "8.1.0" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" - deepmerge-ts "^4.2.2" - expect-webdriverio "^4.0.1" + supports-color "9.4.0" + +"@wdio/runner@8.14.3": + version "8.14.3" + resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-8.14.3.tgz#8171d370074c1e80ca5f7eead9897463cf0a6abf" + integrity sha512-jjDSyQQ+65219E9DAOKNNQ37YHYHpYCJztdfoT6twnA7+KhT/+Or4P9c23TxWNv7KH2CU7HThJKHmmZpHfd7OA== + dependencies: + "@types/node" "^20.1.0" + "@wdio/config" "8.14.0" + "@wdio/globals" "8.14.3" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" + deepmerge-ts "^5.0.0" + expect-webdriverio "^4.2.5" gaze "^1.1.2" - webdriver "8.3.8" - webdriverio "8.3.10" + webdriver "8.14.3" + webdriverio "8.14.3" -"@wdio/spec-reporter@^8.3.0": - version "8.3.0" - resolved "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.3.0.tgz" - integrity sha512-2K+qMNM5rYMCJdw5M3H6XfXUkto/jLnE2GgAI7VoTo6mZVOGGCRRPVtCa7NR/asWF+P0iBl9PJbPFv2/TzV59A== +"@wdio/spec-reporter@^8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-8.14.0.tgz#8f4f6303182fd67632abfebb473689a1697967a9" + integrity sha512-LmDXAcA1Xy5Yd+0lxhsULfZwr0hqxjjwLO4Uys6qUyGGSUJpL8psF26sO8kFYLHBpDN0DNxzvNm7iIvPAtE1dw== dependencies: - "@wdio/reporter" "8.3.0" - "@wdio/types" "8.3.0" + "@wdio/reporter" "8.14.0" + "@wdio/types" "8.14.0" chalk "^5.1.2" easy-table "^1.2.0" pretty-ms "^7.0.0" -"@wdio/types@8.3.0": - version "8.3.0" - resolved "https://registry.npmjs.org/@wdio/types/-/types-8.3.0.tgz" - integrity sha512-TTs3ETVOJtooTIY/u2+feeBnMBx2Hb4SEItN31r0pFncn37rnIZXE/buywLNf5wvZW9ukxoCup5hCnohOR27eQ== +"@wdio/types@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.14.0.tgz#9050fbcc94423429b13d21105bb82b18fc7e3fc7" + integrity sha512-Bhk3+3Sj47nnGwWjRMYSIp7pUFV/kOQ3o6ycwR4dKtUJpdMYLuQccWJNAp1AfM3g2mVZ07FindBgNkV5bED5zg== dependencies: - "@types/node" "^18.0.0" + "@types/node" "^20.1.0" -"@wdio/utils@8.3.0": - version "8.3.0" - resolved "https://registry.npmjs.org/@wdio/utils/-/utils-8.3.0.tgz" - integrity sha512-BbgzxAu4AN99l9hwaRUvkvBJLeIxON7F6ts+vq4LASRiGVRErrwYGeO9H3ffYgpCAaYSvXnw2GtOf2SQGaPoLA== +"@wdio/utils@8.14.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.14.0.tgz#70af46c28384e5575648edf8ddf61666add5160c" + integrity sha512-T7Vh4exPsjm8QtxpLbOmb7MeZVOQSFjfTncc+5eEuoYlpbJeJOcp1DSfIoTeoU9ibYToIyWEfW3MSvlDeG5JrA== dependencies: - "@wdio/logger" "8.1.0" - "@wdio/types" "8.3.0" - import-meta-resolve "^2.2.0" - p-iteration "^1.1.8" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.14.0" + import-meta-resolve "^3.0.0" acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" +agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + aggregate-error@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -1323,70 +1465,63 @@ ajv@^6.10.0, ajv@^6.12.4: ansi-colors@4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^4.3.0: +ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" -ansi-escapes@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" - integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== - dependencies: - type-fest "^1.0.2" - ansi-regex@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== ansi-styles@^2.2.1: version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== ansi-styles@^6.0.0, ansi-styles@^6.1.0: version "6.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== anymatch@~3.1.2: version "3.1.3" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -1394,7 +1529,7 @@ anymatch@~3.1.2: archiver-utils@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== dependencies: glob "^7.1.4" @@ -1410,7 +1545,7 @@ archiver-utils@^2.1.0: archiver@^5.0.0: version "5.3.1" - resolved "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== dependencies: archiver-utils "^2.1.0" @@ -1423,19 +1558,27 @@ archiver@^5.0.0: argparse@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.0.0: - version "5.1.3" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== dependencies: - deep-equal "^2.0.5" + dequal "^2.0.3" -array-includes@^3.1.4: +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-includes@^3.1.6: version "3.1.6" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" @@ -1444,9 +1587,20 @@ array-includes@^3.1.4: get-intrinsic "^1.1.3" is-string "^1.0.7" -array.prototype.flat@^1.2.5: +array.prototype.findlastindex@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz#bc229aef98f6bd0533a2bc61ff95209875526c9b" + integrity sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +array.prototype.flat@^1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" @@ -1454,9 +1608,31 @@ array.prototype.flat@^1.2.5: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" + integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + asn1.js@^5.0.1: version "5.4.1" - resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== dependencies: bn.js "^4.0.0" @@ -1466,113 +1642,139 @@ asn1.js@^5.0.1: assertion-error@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-exit-hook@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== async@^3.2.3: version "3.2.4" - resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== available-typed-arrays@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^1.2.1: - version "1.3.3" - resolved "https://registry.npmjs.org/axios/-/axios-1.3.3.tgz" - integrity sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA== +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.3" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== +b4a@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" + integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== + +babel-plugin-polyfill-corejs2@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" + integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.2" + semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz" - integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== +babel-plugin-polyfill-corejs3@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" + integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.2" - core-js-compat "^3.21.0" + "@babel/helper-define-polyfill-provider" "^0.4.2" + core-js-compat "^3.31.0" -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" + integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.4.2" balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.2.0, base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base64url@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== +basic-ftp@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.3.tgz#b14c0fe8111ce001ec913686434fe0c2fb461228" + integrity sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g== + +big-integer@^1.6.17: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.0.3: +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + +bl@^4.0.3, bl@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" readable-stream "^3.4.0" -bl@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz" - integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== - dependencies: - buffer "^6.0.3" - inherits "^2.0.4" - readable-stream "^3.4.0" +bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== bn.js@^4.0.0: version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -1580,46 +1782,51 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-stdout@1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== +browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" - resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer-indexof-polyfill@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" + integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== + buffer@^5.2.1, buffer@^5.5.0: version "5.7.1" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -1627,15 +1834,27 @@ buffer@^5.2.1, buffer@^5.5.0: buffer@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" ieee754 "^1.2.1" +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== + +builtins@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" + cac@^3.0.3: version "3.0.4" - resolved "https://registry.npmjs.org/cac/-/cac-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" integrity sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w== dependencies: camelcase-keys "^3.0.0" @@ -1648,25 +1867,25 @@ cac@^3.0.3: cacheable-lookup@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== -cacheable-request@^10.2.1: - version "10.2.7" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.7.tgz" - integrity sha512-I4SA6mKgDxcxVbSt/UmIkb9Ny8qSkg6ReBHtAAXnZHk7KOSx5g3DTiAOaYzcHCs6oOdHn+bip9T48E6tMvK9hw== +cacheable-request@^10.2.8: + version "10.2.13" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.13.tgz#b7012bb4a2acdb18cb54d2dff751d766b3500842" + integrity sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA== dependencies: "@types/http-cache-semantics" "^4.0.1" get-stream "^6.0.1" http-cache-semantics "^4.1.1" - keyv "^4.5.2" + keyv "^4.5.3" mimic-response "^4.0.0" normalize-url "^8.0.0" responselike "^3.0.0" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" @@ -1674,12 +1893,19 @@ call-bind@^1.0.0, call-bind@^1.0.2: callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camaro@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camaro/-/camaro-6.2.0.tgz#90a49bc146e41095d39e5c8b109a75882b19239b" + integrity sha512-81zTKgZb2LnkZKtLbIqLqBzQ6stWSlWC3I/lZd5u4NJVljDgMcsZqn9zZ+Yij/yNyiVpko0EhOKdYa6YAbOWrA== + dependencies: + piscina "^3.2.0" + camelcase-keys@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-3.0.0.tgz#fc0c6c360363f7377e3793b9a16bccf1070c1ca4" integrity sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ== dependencies: camelcase "^3.0.0" @@ -1687,35 +1913,47 @@ camelcase-keys@^3.0.0: camelcase@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg== camelcase@^6.0.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001452" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001452.tgz" - integrity sha512-Lkp0vFjMkBB3GTpLR8zk4NwW5EdRdnitwYJHDOOKIU85x4ckYCPQ+9WlVvSVClHxVReefkUMtWZH2l9KGlD51w== +caniuse-lite@^1.0.30001517: + version "1.0.30001519" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" + integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== -chai@^4.3.6: - version "4.3.6" - resolved "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" - deep-eql "^3.0.1" + deep-eql "^4.1.2" get-func-name "^2.0.0" loupe "^2.3.1" pathval "^1.1.1" type-detect "^4.0.5" +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== + dependencies: + traverse ">=0.3.0 <0.4" + +chalk@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" + integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== + chalk@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== dependencies: ansi-styles "^2.2.1" @@ -1726,39 +1964,39 @@ chalk@^1.1.3: chalk@^2.0.0, chalk@^2.0.1: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.0.0, chalk@^5.0.1, chalk@^5.1.2: - version "5.2.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz" - integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== +chalk@^5.1.2, chalk@^5.2.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== check-error@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== chokidar@3.5.3, chokidar@^3.5.0, chokidar@^3.5.3: version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" @@ -1771,66 +2009,61 @@ chokidar@3.5.3, chokidar@^3.5.0, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chrome-launcher@^0.15.0: - version "0.15.1" - resolved "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.1.tgz" - integrity sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg== +chrome-launcher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-1.0.0.tgz#c8bc62a89bebfdcb71976a9bd7e3d66e2106ee8b" + integrity sha512-74IMFVfgni/bQ4GotUNJpH2vDR+Sh9cXNPVhPXiedeiB0+5j7/8i8LAqS7WlyNSKqtxJ/CgbOpBDPLkzqDVhlw== dependencies: "@types/node" "*" escape-string-regexp "^4.0.0" is-wsl "^2.2.0" - lighthouse-logger "^1.0.0" + lighthouse-logger "^2.0.1" -chromedriver@^114.0.0: - version "114.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-114.0.1.tgz#6b781922e74e2f6520cbd3e363dfabc41c4347f5" - integrity sha512-Srkyt7xv+RL9aSNVkmARm0tAfw84fIBKge9c1MCTiHfW0tjuNFdhKVlgD0TmPmwSKOeFJrTdd1Flf2hGWWKsUw== +chromedriver@^115.0.0: + version "115.0.0" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-115.0.0.tgz#2d50dd1d12d553c1c6cad22d329dc5cf63487ada" + integrity sha512-mkPL+sXMLMUenoXCiKREw+cBl3ibfhiWxkiv9ByIPpqtrrInCt9zKdOolAsbmN/ndlH51WtT5ukUKbeRdrpikg== dependencies: "@testim/chrome-version" "^1.1.3" - axios "^1.2.1" - compare-versions "^5.0.1" + axios "^1.4.0" + compare-versions "^6.0.0" extract-zip "^2.0.1" https-proxy-agent "^5.0.1" proxy-from-env "^1.1.0" tcp-port-used "^1.0.1" +chromium-bidi@0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.16.tgz#8a67bfdf6bb8804efc22765a82859d20724b46ab" + integrity sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA== + dependencies: + mitt "3.0.0" + ci-info@^3.2.0: version "3.8.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== clean-stack@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" -cli-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz" - integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== - dependencies: - restore-cursor "^4.0.0" - -cli-spinners@^2.6.1: - version "2.7.0" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== +cli-spinners@^2.5.0, cli-spinners@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== cli-truncate@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: slice-ansi "^3.0.0" @@ -1838,7 +2071,7 @@ cli-truncate@^2.1.0: cli-truncate@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== dependencies: slice-ansi "^5.0.0" @@ -1846,12 +2079,12 @@ cli-truncate@^3.1.0: cli-width@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== cliui@^7.0.2: version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -1860,7 +2093,7 @@ cliui@^7.0.2: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -1869,7 +2102,7 @@ cliui@^8.0.1: clone-deep@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" @@ -1878,63 +2111,68 @@ clone-deep@^4.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.16: - version "2.0.19" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +colorette@^2.0.19: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^9.3.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== commondir@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compare-versions@^5.0.1: - version "5.0.3" - resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.3.tgz" - integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== +compare-versions@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.0.0.tgz#a3edb527e4487bfab9a8b62ffe70cebc9b87675b" + integrity sha512-s2MzYxfRsE9f/ow8hjn7ysa7pod1xhHdQMsgiJtKx6XSNf4x2N1KG4fjrkUmXcP/e9Y2ZX4zB6sHIso0Lm6evQ== compress-commons@^4.1.0: version "4.1.1" - resolved "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== dependencies: buffer-crc32 "^0.2.13" @@ -1944,57 +2182,57 @@ compress-commons@^4.1.0: concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== convert-source-map@^1.7.0: version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== -core-js-compat@^3.20.2, core-js-compat@^3.21.0: - version "3.28.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.28.0.tgz" - integrity sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg== +core-js-compat@^3.31.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" + integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== dependencies: - browserslist "^4.21.5" + browserslist "^4.21.9" core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== crc-32@^1.2.0: version "1.2.2" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== crc32-stream@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== dependencies: crc-32 "^1.2.0" readable-stream "^3.4.0" -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== +cross-fetch@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== dependencies: - node-fetch "2.6.7" + node-fetch "^2.6.12" cross-spawn@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" integrity sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA== dependencies: lru-cache "^4.0.1" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -2003,191 +2241,179 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: css-shorthand-properties@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== css-value@^0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" integrity sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q== -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +data-uri-to-buffer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" + integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== + +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" debug@4.3.1: version "4.3.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" debug@^2.6.8, debug@^2.6.9: version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.2.7: version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" decamelize@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== decamelize@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-6.0.0.tgz#8cad4d916fde5c41a264a43d0ecc56fe3d31749e" integrity sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA== decompress-response@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== dependencies: mimic-response "^3.1.0" -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" -deep-equal@^2.0.5: - version "2.2.0" - resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz" - integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== - dependencies: - call-bind "^1.0.2" - es-get-iterator "^1.1.2" - get-intrinsic "^1.1.3" - is-arguments "^1.1.1" - is-array-buffer "^3.0.1" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" - deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge-ts@^4.2.2: - version "4.3.0" - resolved "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-4.3.0.tgz" - integrity sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw== +deepmerge-ts@^5.0.0, deepmerge-ts@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz#c55206cc4c7be2ded89b9c816cf3608884525d7a" + integrity sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw== defaults@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" defer-to-connect@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -devtools-protocol@0.0.1082910: - version "0.0.1082910" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1082910.tgz" - integrity sha512-RqoZ2GmqaNxyx+99L/RemY5CkwG9D0WEfOKxekwCRXOGrDCep62ngezEJUVMq6rISYQ+085fJnWDQqGHlxVNww== - -devtools-protocol@^0.0.1103684: - version "0.0.1103684" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1103684.tgz" - integrity sha512-44Qr4zFQkzW8r4WdDOSuQoxIzPDczY/K1RDfyxzEsiG2nSzbTojDW3becX5+HrDw3gENG8jY6ffbHZ2/Ix5LSA== - -devtools@8.3.8: - version "8.3.8" - resolved "https://registry.npmjs.org/devtools/-/devtools-8.3.8.tgz" - integrity sha512-zMulliftMVMUiMRp/YYoCFA3DebzdFij7gkIHlj1yq+gHqwsPSd6pNO+CyZVa0CJhH5uHOs3FAsbIpXeR/woew== - dependencies: - "@types/node" "^18.0.0" - "@wdio/config" "8.3.2" - "@wdio/logger" "8.1.0" - "@wdio/protocols" "8.3.5" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" - chrome-launcher "^0.15.0" - edge-paths "^3.0.5" - import-meta-resolve "^2.1.0" - puppeteer-core "19.6.3" - query-selector-shadow-dom "^1.0.0" - ua-parser-js "^1.0.1" - uuid "^9.0.0" - which "^3.0.0" +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +devtools-protocol@0.0.1147663: + version "0.0.1147663" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" + integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== + +devtools-protocol@^0.0.1170846: + version "0.0.1170846" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1170846.tgz#e5822c62c56a8aff7c937bb04e69823f4235fc42" + integrity sha512-GFZiHgvL4JW7+8hIMQgwYNUaIRRCsqfXd11ZbOTdu2VzDeu0Le4l1c3u4FFRWCSvMg82OFip9k/sYyz4M/PJIA== diff-sequences@^29.4.3: version "29.4.3" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== diff@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== diff@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== + dependencies: + readable-stream "^2.0.2" + eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== easy-table@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb" integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww== dependencies: ansi-regex "^5.0.1" @@ -2196,60 +2422,74 @@ easy-table@^1.2.0: edge-paths@^3.0.5: version "3.0.5" - resolved "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz" + resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-3.0.5.tgz#9a35361d701d9b5dc07f641cebe8da01ede80937" integrity sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg== dependencies: "@types/which" "^2.0.1" which "^2.0.2" -ejs@^3.1.8: - version "3.1.8" - resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz" - integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== +edgedriver@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/edgedriver/-/edgedriver-5.3.2.tgz#2e1859f347f9373defede0f8dcf788762369d27a" + integrity sha512-jxVSDomvdf8P/vEr5BWfhM+1/hEYeeipwaSWGltBMoJY9OLEf7o/vo+PmM3C2o4+LzGuyL/zSlfXRDAWFNkn4g== + dependencies: + "@wdio/logger" "^8.11.0" + camaro "^6.2.0" + decamelize "^6.0.0" + edge-paths "^3.0.5" + node-fetch "^3.3.1" + unzipper "^0.10.14" + which "^3.0.1" + +ejs@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.284: - version "1.4.295" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz" - integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== +electron-to-chromium@^1.4.477: + version "1.4.482" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.482.tgz#77c5ed37b93d4dda860e27538e0e2a01d6a19e02" + integrity sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -error-ex@^1.2.0, error-ex@^1.3.1: +error-ex@^1.2.0, error-ex@^1.3.2: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== +es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: + version "1.22.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" + integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.1" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.1" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -2257,8 +2497,8 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" @@ -2266,35 +2506,25 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: is-string "^1.0.7" is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.0" + safe-array-concat "^1.0.0" safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-get-iterator@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" + which-typed-array "^1.1.10" es-set-tostringtag@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== dependencies: get-intrinsic "^1.1.3" @@ -2303,14 +2533,14 @@ es-set-tostringtag@^2.0.1: es-shim-unscopables@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== dependencies: has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -2319,107 +2549,145 @@ es-to-primitive@^1.2.1: es6-promise@^4.2.8: version "4.2.8" - resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-cli@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/eslint-cli/-/eslint-cli-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-cli/-/eslint-cli-1.1.1.tgz#ae6979edd8ee6e78c6d413b525f4052cb2a94cfd" integrity sha512-Gu+fYzt7M+jIb5szUHLl5Ex0vFY7zErbi78D7ZaaLunvVTxHRvbOlfzmJlIUWsV5WDM4qyu9TD7WnGgDaDgaMA== dependencies: chalk "^2.0.1" debug "^2.6.8" resolve "^1.3.3" -eslint-config-standard@^14.1.1: - version "14.1.1" - resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz" - integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== +eslint-config-standard@^17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== -eslint-import-resolver-node@^0.3.6: +eslint-import-resolver-node@^0.3.7: version "0.3.7" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" is-core-module "^2.11.0" resolve "^1.22.1" -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" eslint-plugin-chai-friendly@^0.7.2: version "0.7.2" - resolved "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz#0ebfbb2c1244f5de2997f3963d155758234f2b0f" integrity sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg== +eslint-plugin-es-x@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.2.0.tgz#5779d742ad31f8fd780b9481331481e142b72311" + integrity sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.6.0" + eslint-plugin-es@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== dependencies: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.25.4: - version "2.26.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== +eslint-plugin-import@^2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz#8d66d6925117b06c4018d491ae84469eb3cb1005" + integrity sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.findlastindex "^1.2.2" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.8.0" has "^1.0.3" - is-core-module "^2.8.1" + is-core-module "^2.12.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" + object.fromentries "^2.0.6" + object.groupby "^1.0.0" + object.values "^1.1.6" + resolve "^1.22.3" + semver "^6.3.1" + tsconfig-paths "^3.14.2" eslint-plugin-json@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz#251108ba1681c332e0a442ef9513bd293619de67" integrity sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g== dependencies: lodash "^4.17.21" vscode-json-languageservice "^4.1.6" +eslint-plugin-n@^16.0.1: + version "16.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.1.tgz#baa62bb3af52940a53ba15386348ad9b0b425ada" + integrity sha512-CDmHegJN0OF3L5cz5tATH84RPQm9kG+Yx39wIqIwPR2C0uhBGMWfbbOtetR83PQjjidA5aXMu+LEFw1jaSwvTA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + builtins "^5.0.1" + eslint-plugin-es-x "^7.1.0" + ignore "^5.2.4" + is-core-module "^2.12.1" + minimatch "^3.1.2" + resolve "^1.22.2" + semver "^7.5.3" + eslint-plugin-node@^11.1.0: version "11.1.0" - resolved "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== dependencies: eslint-plugin-es "^3.0.0" @@ -2429,146 +2697,126 @@ eslint-plugin-node@^11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-promise@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz" - integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw== - -eslint-plugin-standard@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz" - integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== +eslint-plugin-promise@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" eslint-utils@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - eslint-visitor-keys@^1.1.0: version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@^8.10.0: - version "8.14.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz" - integrity sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw== - dependencies: - "@eslint/eslintrc" "^1.2.2" - "@humanwhocodes/config-array" "^0.9.2" - ajv "^6.10.0" +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz#8c2095440eca8c933bedcadf16fefa44dbe9ba5f" + integrity sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw== + +eslint@^8.46.0: + version "8.46.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.46.0.tgz#a06a0ff6974e53e643acc42d1dcf2e7f797b3552" + integrity sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.1" + "@eslint/js" "^8.46.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.1" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.2" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.6.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.1, espree@^9.4.0: - version "9.4.1" - resolved "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz" - integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" +eventemitter-asyncresource@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b" + integrity sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ== -execa@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-7.0.0.tgz" - integrity sha512-tQbH0pH/8LHTnwTrsKWideqi6rFB/QNUawEwrn+WHyz7PX1Tuz2u7wfTvbaNBdP5JD5LVWxNo8/A8CHNZ3bV6g== +execa@^7.0.0, execa@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.1" @@ -2580,31 +2828,32 @@ execa@^7.0.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" -expect-webdriverio@^4.0.1: - version "4.1.2" - resolved "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.1.2.tgz" - integrity sha512-punU19F1Nq0m9H7f5+A/OtT/LljA+d04sVB9nHIFDYbumdDBOy3I23EFzCsSZMvMdda5r3stKmKalTX4SG2yhA== +expect-webdriverio@^4.2.5: + version "4.2.7" + resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-4.2.7.tgz#ae7525202e4e02f46b0c9c10e0cd9af4f5234b52" + integrity sha512-ulNspQoTct5/0OBfNSNzBKldSgoQxzmHBi/TLtrsLZY5QCZ7svD7Csdtm+YEW6IBg/v8MI7ibiMcpWY9cjcG6A== dependencies: - expect "^29.4.0" - jest-matcher-utils "^29.4.0" + expect "^29.6.1" + jest-matcher-utils "^29.6.1" optionalDependencies: - "@wdio/globals" "^8.2.4" - webdriverio "^8.2.4" + "@wdio/globals" "^8.13.1" + webdriverio "^8.13.1" -expect@^29.4.0: - version "29.4.3" - resolved "https://registry.npmjs.org/expect/-/expect-29.4.3.tgz" - integrity sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg== +expect@^29.6.1: + version "29.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" + integrity sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA== dependencies: - "@jest/expect-utils" "^29.4.3" + "@jest/expect-utils" "^29.6.2" + "@types/node" "*" jest-get-type "^29.4.3" - jest-matcher-utils "^29.4.3" - jest-message-util "^29.4.3" - jest-util "^29.4.3" + jest-matcher-utils "^29.6.2" + jest-message-util "^29.6.2" + jest-util "^29.6.2" external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2613,7 +2862,7 @@ external-editor@^3.0.3: extract-zip@2.0.1, extract-zip@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: debug "^4.1.1" @@ -2624,34 +2873,54 @@ extract-zip@2.0.1, extract-zip@^2.0.1: fast-deep-equal@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.0.tgz#03e381bcbfb29932d7c3afde6e15e83e05ab4d8b" + integrity sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw== + fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + fd-slicer@~1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== dependencies: pend "~1.2.0" +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + figures@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f" integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg== dependencies: escape-string-regexp "^5.0.0" @@ -2659,37 +2928,37 @@ figures@^5.0.0: file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" -filelist@^1.0.1: +filelist@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: minimatch "^5.0.1" fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-cache-dir@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" make-dir "^2.0.0" pkg-dir "^3.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -2697,7 +2966,7 @@ find-up@5.0.0: find-up@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== dependencies: path-exists "^2.0.0" @@ -2705,14 +2974,14 @@ find-up@^1.0.0: find-up@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" find-up@^6.3.0: version "6.3.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== dependencies: locate-path "^7.1.0" @@ -2720,7 +2989,7 @@ find-up@^6.3.0: flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -2728,72 +2997,106 @@ flat-cache@^3.0.4: flat@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.1.0: version "3.2.7" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.15.0: version "1.15.2" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== for-each@^0.3.3: version "0.3.3" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data-encoder@^2.1.2: version "2.1.4" - resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== form-data@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + fs-constants@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^11.1.0: - version "11.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + function-bind@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== function.prototype.name@^1.1.5: version "1.1.5" - resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== dependencies: call-bind "^1.0.2" @@ -2801,84 +3104,109 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gaze@^1.1.2: version "1.1.3" - resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== dependencies: globule "^1.0.0" +geckodriver@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.1.3.tgz#4a9e25a301e53a6fb5a9e1d72e3d29b7b1815727" + integrity sha512-w3vZvibJnDLui3ZcyWz/CjiWET31br7ALakbfEqssSzwBFzolOj6/FnXPgcmxSkdH1iMBOc83dewLEqR8xoilA== + dependencies: + "@wdio/logger" "^8.11.0" + decamelize "^6.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + node-fetch "^3.3.1" + tar-fs "^3.0.4" + unzipper "^0.10.14" + which "^3.0.1" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-func-name@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== dependencies: function-bind "^1.1.1" has "^1.0.3" + has-proto "^1.0.1" has-symbols "^1.0.3" +get-port@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.0.0.tgz#ffcd83da826146529e307a341d7801cae351daff" + integrity sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw== + get-stream@^5.1.0: version "5.2.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" -get-stream@^6.0.0, get-stream@^6.0.1: +get-stream@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" get-intrinsic "^1.1.1" -glob-parent@^6.0.1: +get-uri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.1.tgz#cff2ba8d456c3513a04b70c45de4dbcca5b1527c" + integrity sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^5.0.1" + debug "^4.3.4" + fs-extra "^8.1.0" + +glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@7.2.0: version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -2888,9 +3216,20 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.2.2: + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -2900,20 +3239,9 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3: - version "8.1.0" - resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - glob@~7.1.1: version "7.1.7" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -2925,26 +3253,26 @@ glob@~7.1.1: globals@^11.1.0: version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0, globals@^13.6.0: +globals@^13.19.0: version "13.20.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" globalthis@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== dependencies: define-properties "^1.1.3" globule@^1.0.0: version "1.3.4" - resolved "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb" integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== dependencies: glob "~7.1.1" @@ -2953,20 +3281,20 @@ globule@^1.0.0: gopd@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== dependencies: get-intrinsic "^1.1.3" -got@^12.1.0: - version "12.5.3" - resolved "https://registry.npmjs.org/got/-/got-12.5.3.tgz" - integrity sha512-8wKnb9MGU8IPGRIo+/ukTy9XLJBwDiCpIf5TVzQ9Cpol50eMTpBq2GAuDsuDIz7hTYmZgMgC1e9ydr6kSDWs3w== +"got@^ 12.6.1": + version "12.6.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== dependencies: "@sindresorhus/is" "^5.2.0" "@szmarczak/http-timer" "^5.0.1" cacheable-lookup "^7.0.0" - cacheable-request "^10.2.1" + cacheable-request "^10.2.8" decompress-response "^6.0.0" form-data-encoder "^2.1.2" get-stream "^6.0.1" @@ -2975,199 +3303,229 @@ got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== grapheme-splitter@^1.0.2: version "1.0.4" - resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + has-ansi@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== dependencies: ansi-regex "^2.0.0" has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== dependencies: get-intrinsic "^1.1.1" has-proto@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-tostringtag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" +hdr-histogram-js@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz#0b860534655722b6e3f3e7dca7b78867cf43dcb5" + integrity sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g== + dependencies: + "@assemblyscript/loader" "^0.10.1" + base64-js "^1.2.0" + pako "^1.0.3" + +hdr-histogram-percentiles-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz#9409f4de0c2dda78e61de2d9d78b1e9f3cba283c" + integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw== + he@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -hosted-git-info@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" - integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== +hosted-git-info@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" + integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== dependencies: - lru-cache "^6.0.0" + lru-cache "^7.5.1" http-cache-semantics@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== +http-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" + integrity sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http2-wrapper@^2.1.10: version "2.2.0" - resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.0.tgz#b80ad199d216b7d3680195077bd7b9060fa9d7f3" integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== dependencies: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.1: +https-proxy-agent@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" + integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== + dependencies: + agent-base "^7.0.2" + debug "4" human-signals@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz" - integrity sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ== + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.1.1, ignore@^5.2.0: +ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" -import-meta-resolve@^2.1.0, import-meta-resolve@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-2.2.1.tgz" - integrity sha512-C6lLL7EJPY44kBvA80gq4uMsVFw5x3oSKfuMl1cuZ2RkI5+UJqQXgn+6hlUew0y4ig7Ypt4CObAAIzU53Nfpuw== +import-meta-resolve@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz#94a6aabc623874fbc2f3525ec1300db71c6cbc11" + integrity sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg== imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== indent-string@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@9.1.2: - version "9.1.2" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-9.1.2.tgz" - integrity sha512-Hj2Ml1WpxKJU2npP2Rj0OURGkHV+GtNW2CwFdHDiXlqUBAUrWTcZHxCkFywX/XHzOS7wrG/kExgJFbUkVgyHzg== +inquirer@9.2.9: + version "9.2.9" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.2.9.tgz#49c16cae502b773209598f8668d8ddbfe5b07ecb" + integrity sha512-0VXHov2GGwWquYxwxlcIcm3yOHvFb2jh/+HkY8/AUXSTWShpo6QJMlSfHi5Xo74NO40UePBM3rQcI3OkzOF/7A== dependencies: - ansi-escapes "^5.0.0" - chalk "^5.0.1" - cli-cursor "^4.0.0" + "@ljharb/through" "^2.3.9" + ansi-escapes "^4.3.2" + chalk "^5.3.0" + cli-cursor "^3.1.0" cli-width "^4.0.0" external-editor "^3.0.3" figures "^5.0.0" lodash "^4.17.21" - mute-stream "0.0.8" - ora "^6.1.2" - run-async "^2.4.0" - rxjs "^7.5.6" - string-width "^5.1.2" - strip-ansi "^7.0.1" - through "^2.3.6" - wrap-ansi "^8.0.1" + mute-stream "1.0.0" + ora "^5.4.1" + run-async "^3.0.0" + rxjs "^7.8.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wrap-ansi "^6.0.1" -internal-slot@^1.0.4: +internal-slot@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: get-intrinsic "^1.2.0" @@ -3176,48 +3534,50 @@ internal-slot@^1.0.4: ip-regex@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" +ip@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" + integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" is-typed-array "^1.1.10" is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" @@ -3225,199 +3585,172 @@ is-boolean-object@^1.1.0: is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.12.0, is-core-module@^2.12.1, is-core-module@^2.8.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" -is-date-object@^1.0.1, is-date-object@^1.0.5: +is-date-object@^1.0.1: version "1.0.5" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-docker@^2.0.0: version "2.2.1" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-interactive@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" - integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== - -is-map@^2.0.1, is-map@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-negative-zero@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.7" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-obj@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== is-plain-object@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-set@^2.0.1, is-set@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - is-shared-array-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== dependencies: call-bind "^1.0.2" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-stream@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-unicode-supported@^1.1.0, is-unicode-supported@^1.2.0: +is-unicode-supported@^1.2.0: version "1.3.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== is-url@^1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== is-utf8@^0.2.0: version "0.2.1" - resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - is-weakref@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" is2@^2.0.6: version "2.0.9" - resolved "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz" + resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.9.tgz#ff63b441f90de343fa8fac2125ee170da8e8240d" integrity sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g== dependencies: deep-is "^0.1.3" @@ -3426,80 +3759,89 @@ is2@^2.0.6: isarray@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isarray@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +jackspeak@^2.0.3: + version "2.2.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.2.tgz#707c62733924b8dc2a0a629dc6248577788b5385" + integrity sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: - version "10.8.5" - resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== dependencies: async "^3.2.3" chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" + filelist "^1.0.4" + minimatch "^3.1.2" -jest-diff@^29.4.3: - version "29.4.3" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.3.tgz" - integrity sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA== +jest-diff@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" + integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== dependencies: chalk "^4.0.0" diff-sequences "^29.4.3" jest-get-type "^29.4.3" - pretty-format "^29.4.3" + pretty-format "^29.6.2" jest-get-type@^29.4.3: version "29.4.3" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-matcher-utils@^29.4.0, jest-matcher-utils@^29.4.3: - version "29.4.3" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz" - integrity sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg== +jest-matcher-utils@^29.6.1, jest-matcher-utils@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" + integrity sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ== dependencies: chalk "^4.0.0" - jest-diff "^29.4.3" + jest-diff "^29.6.2" jest-get-type "^29.4.3" - pretty-format "^29.4.3" + pretty-format "^29.6.2" -jest-message-util@^29.4.3: - version "29.4.3" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.3.tgz" - integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw== +jest-message-util@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" + integrity sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.3" + "@jest/types" "^29.6.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.4.3" + pretty-format "^29.6.2" slash "^3.0.0" stack-utils "^2.0.3" -jest-util@^29.4.3: - version "29.4.3" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.4.3.tgz" - integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q== +jest-util@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" + integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" @@ -3508,176 +3850,187 @@ jest-util@^29.4.3: js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" jsesc@^2.5.1: version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== jsesc@~0.5.0: version "0.5.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" + integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-web-key@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/json-web-key/-/json-web-key-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/json-web-key/-/json-web-key-0.4.0.tgz#a8e7268d1741c3a87c51c5070ea7ea988e9a78b7" integrity sha512-4MwQAsadU3y7ZTfhwup/2lYJUEB7mAOMOjfNvuHGJkgkvXaBaiWlFsuWNFpWkTG23ZlMm56TBB3BI9cVQTxjzA== dependencies: asn1.js "^5.0.1" -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -json5@^2.2.1: +json5@^2.2.2: version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" -jsrsasign@^10.5.25: - version "10.5.25" - resolved "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.5.25.tgz" - integrity sha512-N7zxHaCwYvFlXsybq4p4RxRwn4AbEq3cEiyjbCrWmwA7g8aS4LTKDJ9AJmsXxwtYesYx0imJ+ITtkyyxLCgeIg== +jsrsasign@^10.8.6: + version "10.8.6" + resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.8.6.tgz#ebf7f3c812c6517af84f0d8a10115e0dbfabe145" + integrity sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw== -keyv@^4.5.2: - version "4.5.2" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz" - integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== dependencies: json-buffer "3.0.1" kind-of@^6.0.2: version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== ky@^0.33.0: - version "0.33.2" - resolved "https://registry.npmjs.org/ky/-/ky-0.33.2.tgz" - integrity sha512-f6oS2rKUcPu5FzdqCDbFpmzis/JlqFZw8uIHm/jf8Kc3vtnW+VDhuashOAKyBZv8bFiZFZUMNxTC0JtahEvujA== + version "0.33.3" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.33.3.tgz#bf1ad322a3f2c3428c13cfa4b3af95e6c4a2f543" + integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== lazystream@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== dependencies: readable-stream "^2.0.5" levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" type-check "~0.4.0" -lighthouse-logger@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz" - integrity sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA== +lighthouse-logger@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz#48895f639b61cca89346bb6f47f7403a3895fa02" + integrity sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ== dependencies: debug "^2.6.9" marky "^1.2.2" -lilconfig@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz" - integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lines-and-columns@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== -lint-staged@^12.3.5: - version "12.4.1" - resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz" - integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== +lint-staged@^13.2.3: + version "13.2.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.3.tgz#f899aad6c093473467e9c9e316e3c2d8a28f87a7" + integrity sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg== dependencies: + chalk "5.2.0" cli-truncate "^3.1.0" - colorette "^2.0.16" - commander "^8.3.0" - debug "^4.3.3" - execa "^5.1.1" - lilconfig "2.0.4" - listr2 "^4.0.1" - micromatch "^4.0.4" + commander "^10.0.0" + debug "^4.3.4" + execa "^7.0.0" + lilconfig "2.1.0" + listr2 "^5.0.7" + micromatch "^4.0.5" normalize-path "^3.0.0" - object-inspect "^1.12.0" - pidtree "^0.5.0" + object-inspect "^1.12.3" + pidtree "^0.6.0" string-argv "^0.3.1" - supports-color "^9.2.1" - yaml "^1.10.2" + yaml "^2.2.2" -listr2@^4.0.1: - version "4.0.5" - resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz" - integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== + +listr2@^5.0.7: + version "5.0.8" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" + integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== dependencies: cli-truncate "^2.1.0" - colorette "^2.0.16" + colorette "^2.0.19" log-update "^4.0.0" p-map "^4.0.0" rfdc "^1.3.0" - rxjs "^7.5.5" + rxjs "^7.8.0" through "^2.3.8" wrap-ansi "^7.0.0" livereload-js@^3.3.1: version "3.4.1" - resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.4.1.tgz#ba90fbc708ed1b9a024bb89c4ee12c96ea03d66f" integrity sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g== livereload@^0.9.3: version "0.9.3" - resolved "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz" + resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.3.tgz#a714816375ed52471408bede8b49b2ee6a0c55b1" integrity sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw== dependencies: chokidar "^3.5.0" @@ -3687,7 +4040,7 @@ livereload@^0.9.3: load-json-file@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== dependencies: graceful-fs "^4.1.2" @@ -3698,7 +4051,7 @@ load-json-file@^1.0.0: locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" @@ -3706,97 +4059,89 @@ locate-path@^3.0.0: locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" locate-path@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== dependencies: p-locate "^6.0.0" lodash.clonedeep@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.defaults@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== lodash.difference@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== lodash.flatten@^4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== lodash.flattendeep@^4.4.0: version "4.4.0" - resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== lodash.isplainobject@^4.0.6: version "4.0.6" - resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.pickby@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" integrity sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q== lodash.union@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== lodash.zip@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== lodash@^4.17.21: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.1.0: +log-symbols@4.1.0, log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" -log-symbols@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz" - integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== - dependencies: - chalk "^5.0.0" - is-unicode-supported "^1.1.0" - log-update@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== dependencies: ansi-escapes "^4.3.0" @@ -3806,34 +4151,34 @@ log-update@^4.0.0: loglevel-plugin-prefix@^0.8.4: version "0.8.4" - resolved "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz" + resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== loglevel@^1.6.0: version "1.8.1" - resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== long@^5.2.0: - version "5.2.1" - resolved "https://registry.npmjs.org/long/-/long-5.2.1.tgz" - integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== loupe@^2.3.1: version "2.3.6" - resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" lowercase-keys@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== lru-cache@^4.0.1: version "4.1.5" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" @@ -3841,21 +4186,31 @@ lru-cache@^4.0.1: lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" +lru-cache@^7.14.1, lru-cache@^7.5.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" + integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" @@ -3863,22 +4218,22 @@ make-dir@^2.0.0, make-dir@^2.1.0: map-obj@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== marky@^1.2.2: version "1.2.5" - resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0" integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -micromatch@^4.0.4: +micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -3886,94 +4241,106 @@ micromatch@^4.0.4: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-fn@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== mimic-response@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== mimic-response@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== minimalistic-assert@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== minimatch@5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: brace-expansion "^2.0.1" minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" -minimatch@^6.1.4: - version "6.2.0" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz" - integrity sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg== +minimatch@^9.0.0, minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" minimatch@~3.0.2: version "3.0.8" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" + integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== + +mitt@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" + integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== + mkdirp-classic@^0.5.2: version "0.5.3" - resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^2.0.0: - version "2.1.3" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.3.tgz" - integrity sha512-sjAkg21peAG9HS+Dkx7hlG9Ztx7HLeKnvB3NQRcu/mltCVmvkF0pisbiTSfDVYTT86XEfZrTUosLdZLStquZUw== +"mkdirp@>=0.5 0": + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" mocha@^10.0.0: version "10.2.0" - resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: ansi-colors "4.1.1" @@ -4000,49 +4367,86 @@ mocha@^10.0.0: ms@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2, ms@^2.1.1: +ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.1: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== nanoid@3.3.3: version "3.3.3" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + +nice-napi@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b" + integrity sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA== + dependencies: + node-addon-api "^3.0.0" + node-gyp-build "^4.2.2" + +node-addon-api@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== dependencies: whatwg-url "^5.0.0" +node-fetch@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-forge@^1.2.1: version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-gyp-build@^4.2.2: + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + node-jose@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.2.0.tgz#b64f3225ad6bec328509a420800de597ba2bf3ed" integrity sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw== dependencies: base64url "^3.0.1" @@ -4055,14 +4459,14 @@ node-jose@^2.2.0: process "^0.11.10" uuid "^9.0.0" -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== normalize-package-data@^2.3.2: version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -4070,61 +4474,46 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" - integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== +normalize-package-data@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" + integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== dependencies: - hosted-git-info "^4.0.1" - is-core-module "^2.5.0" - semver "^7.3.4" - validate-npm-package-license "^3.0.1" + hosted-git-info "^6.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a" integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - npm-run-path@^5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== dependencies: path-key "^4.0.0" -object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.12.0, object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4: version "4.1.4" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: call-bind "^1.0.2" @@ -4132,9 +4521,28 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.values@^1.1.5: +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.groupby@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.0.tgz#cb29259cf90f37e7bac6437686c1ea8c916d12a9" + integrity sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.21.2" + get-intrinsic "^1.2.1" + +object.values@^1.1.6: version "1.1.6" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" @@ -4143,205 +4551,237 @@ object.values@^1.1.5: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.0: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" onetime@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== dependencies: mimic-fn "^4.0.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" "opts@>= 1.2.0": version "2.0.2" - resolved "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1" integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg== -ora@^6.1.2: - version "6.1.2" - resolved "https://registry.npmjs.org/ora/-/ora-6.1.2.tgz" - integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw== - dependencies: - bl "^5.0.0" - chalk "^5.0.0" - cli-cursor "^4.0.0" - cli-spinners "^2.6.1" - is-interactive "^2.0.0" - is-unicode-supported "^1.1.0" - log-symbols "^5.1.0" - strip-ansi "^7.0.1" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" wcwidth "^1.0.1" os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== p-cancelable@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -p-iteration@^1.1.8: - version "1.1.8" - resolved "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.8.tgz" - integrity sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ== - p-limit@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-limit@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== dependencies: yocto-queue "^1.0.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-locate@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== dependencies: p-limit "^4.0.0" p-map@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz#db42120c64292685dafaf2bd921e223c56bfb13b" + integrity sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + pac-resolver "^7.0.0" + socks-proxy-agent "^8.0.1" + +pac-resolver@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.0.tgz#79376f1ca26baf245b96b34c339d79bff25e900c" + integrity sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg== + dependencies: + degenerator "^5.0.0" + ip "^1.1.8" + netmask "^2.0.2" + +pako@^1.0.3: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + pako@^2.0.4: version "2.1.0" - resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== dependencies: error-ex "^1.2.0" -parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== +parse-json@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-7.0.0.tgz#51c0713f233b804eb5adee3ef1e75d3243e0ff06" + integrity sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw== dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" + "@babel/code-frame" "^7.21.4" + error-ex "^1.3.2" + json-parse-even-better-errors "^3.0.0" + lines-and-columns "^2.0.3" + type-fest "^3.8.0" parse-ms@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== path-exists@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-exists@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-key@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== dependencies: graceful-fs "^4.1.2" @@ -4350,112 +4790,142 @@ path-type@^1.0.0: pathval@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== pend@~1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== picocolors@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pidtree@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz" - integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pify@^2.0.0: version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pify@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pinkie-promise@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" - resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== pirates@^4.0.5: - version "4.0.5" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +piscina@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/piscina/-/piscina-3.2.0.tgz#f5a1dde0c05567775690cccefe59d9223924d154" + integrity sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA== + dependencies: + eventemitter-asyncresource "^1.0.0" + hdr-histogram-js "^2.0.1" + hdr-histogram-percentiles-obj "^3.0.0" + optionalDependencies: + nice-napi "^1.0.2" pkg-dir@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== dependencies: find-up "^3.0.0" prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.5.1: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== +prettier@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.1.tgz#65271fc9320ce4913c57747a70ce635b30beaa40" + integrity sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ== -pretty-format@^29.4.3: - version "29.4.3" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz" - integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== +pretty-format@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" + integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.0" ansi-styles "^5.0.0" react-is "^18.0.0" pretty-ms@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== dependencies: parse-ms "^2.1.0" process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process@^0.11.10: version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -proxy-from-env@1.1.0, proxy-from-env@^1.1.0: +progress@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +proxy-agent@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" + integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.0" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.1" + +proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== pseudomap@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== pump@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" @@ -4463,59 +4933,65 @@ pump@^3.0.0: punycode@^2.1.0: version "2.3.0" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -puppeteer-core@19.6.3: - version "19.6.3" - resolved "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.6.3.tgz" - integrity sha512-8MbhioSlkDaHkmolpQf9Z7ui7jplFfOFTnN8d5kPsCazRRTNIH6/bVxPskn0v5Gh9oqOBlknw0eHH0/OBQAxpQ== +puppeteer-core@^20.9.0: + version "20.9.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-20.9.0.tgz#6f4b420001b64419deab38d398a4d9cd071040e6" + integrity sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg== dependencies: - cross-fetch "3.1.5" + "@puppeteer/browsers" "1.4.6" + chromium-bidi "0.4.16" + cross-fetch "4.0.0" debug "4.3.4" - devtools-protocol "0.0.1082910" - extract-zip "2.0.1" - https-proxy-agent "5.0.1" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.11.0" + devtools-protocol "0.0.1147663" + ws "8.13.0" query-selector-shadow-dom@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz#1c7b0058eff4881ac44f45d8f84ede32e9a2f349" integrity sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + quick-lru@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" react-is@^18.0.0: version "18.2.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -read-pkg-up@9.1.0, read-pkg-up@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz" - integrity sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg== +read-pkg-up@10.0.0, read-pkg-up@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-10.0.0.tgz#0542d21ff1001d2bfff1f6eac8b4d1d1dc486617" + integrity sha512-jgmKiS//w2Zs+YbX039CorlkOp8FIVbSAN8r8GJHDsGlmNPXo+VeHkqAwCiQVTTx5/LwLZTcEw59z3DvcLbr0g== dependencies: find-up "^6.3.0" - read-pkg "^7.1.0" - type-fest "^2.5.0" + read-pkg "^8.0.0" + type-fest "^3.12.0" read-pkg-up@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== dependencies: find-up "^1.0.0" @@ -4523,27 +4999,27 @@ read-pkg-up@^1.0.1: read-pkg@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz" - integrity sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg== +read-pkg@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-8.0.0.tgz#78b93774c15a3f151b56d5790d5127a5cb9fc507" + integrity sha512-Ajb9oSjxXBw0YyOiwtQ2dKbAA/vMnUPnY63XcCk+mXo0BwIdQEMgZLZiMWGttQHcUhUgbK0mH85ethMPKXxziw== dependencies: "@types/normalize-package-data" "^2.4.1" - normalize-package-data "^3.0.2" - parse-json "^5.2.0" - type-fest "^2.0.0" + normalize-package-data "^5.0.0" + parse-json "^7.0.0" + type-fest "^3.8.0" -readable-stream@^2.0.0, readable-stream@^2.0.5: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -4554,77 +5030,77 @@ readable-stream@^2.0.0, readable-stream@^2.0.5: util-deprecate "~1.0.1" readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" readdir-glob@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz" - integrity sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA== + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== dependencies: minimatch "^5.1.0" readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" -recursive-readdir@^2.2.2: +recursive-readdir@^2.2.3: version "2.2.3" - resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== dependencies: minimatch "^3.0.5" regenerate-unicode-properties@^10.1.0: version "10.1.0" - resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== dependencies: regenerate "^1.4.2" regenerate@^1.4.2: version "1.4.2" - resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.11: version "0.13.11" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== regenerator-transform@^0.15.1: version "0.15.1" - resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== +regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" -regexpp@^3.0.0, regexpp@^3.2.0: +regexpp@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.2.1: - version "5.3.0" - resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.0.tgz" - integrity sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ== +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== dependencies: "@babel/regjsgen" "^0.8.0" regenerate "^1.4.2" @@ -4635,107 +5111,142 @@ regexpu-core@^5.2.1: regjsparser@^0.9.1: version "0.9.1" - resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== dependencies: jsesc "~0.5.0" require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== resolve-alpn@^1.2.0: version "1.2.1" - resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.3: - version "1.22.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.3.3: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.22.3: + version "1.22.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283" + integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.12.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" responselike@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== dependencies: lowercase-keys "^3.0.0" resq@^1.9.1: version "1.11.0" - resolved "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz" + resolved "https://registry.yarnpkg.com/resq/-/resq-1.11.0.tgz#edec8c58be9af800fd628118c0ca8815283de196" integrity sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw== dependencies: fast-deep-equal "^2.0.1" restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" -restore-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz" - integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== rgb2hex@0.2.5: version "0.2.5" - resolved "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz" + resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.5.tgz#f82230cd3ab1364fa73c99be3a691ed688f8dbdc" integrity sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw== -rimraf@3.0.2, rimraf@^3.0.2: +rimraf@2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-async@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" + integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== -rxjs@^7.5.5, rxjs@^7.5.6: - version "7.8.0" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.8.0, rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" -safe-buffer@^5.1.0, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safaridriver@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/safaridriver/-/safaridriver-0.1.0.tgz#8ff901e847b003c6a52b534028f57cddc82d6b14" + integrity sha512-azzzIP3gR1TB9bVPv7QO4Zjw0rR1BWEU/s2aFdUMN48gxDjxEB13grAEuXDmkKPgE74cObymDxmAmZnL3clj4w== + +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" -safe-buffer@~5.2.0: +safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-regex-test@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: call-bind "^1.0.2" @@ -4744,81 +5255,91 @@ safe-regex-test@^1.0.0: "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== "semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.1.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4: - version "7.3.8" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.0.0, semver@^7.3.5, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" serialize-error@^8.0.0: version "8.1.0" - resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== dependencies: type-fest "^0.20.2" serialize-javascript@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" +setimmediate@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + shallow-clone@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== dependencies: ansi-styles "^4.0.0" @@ -4827,7 +5348,7 @@ slice-ansi@^3.0.0: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -4836,83 +5357,106 @@ slice-ansi@^4.0.0: slice-ansi@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== dependencies: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" + integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== + dependencies: + agent-base "^7.0.1" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" + integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + dependencies: + ip "^2.0.0" + smart-buffer "^4.2.0" + source-map-support@^0.5.16: version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0: +source-map@^0.6.0, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== split2@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz" - integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== stack-utils@^2.0.3: version "2.0.6" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - stream-buffers@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== +streamx@^2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.0.tgz#f58c92e6f726b5390dcabd6dd9094d29a854d698" + integrity sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + string-argv@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" - integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -4921,16 +5465,25 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" @@ -4939,7 +5492,7 @@ string.prototype.trimend@^1.0.6: string.prototype.trimstart@^1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" @@ -4948,120 +5501,114 @@ string.prototype.trimstart@^1.0.6: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" strip-bom@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - strip-final-newline@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== suffix@^0.1.0: version "0.1.1" - resolved "https://registry.npmjs.org/suffix/-/suffix-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.1.tgz#cc58231646a0ef1102f79478ef3a9248fd9c842f" integrity sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA== supports-color@8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-color@9.3.1, supports-color@^9.2.1: - version "9.3.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.3.1.tgz" - integrity sha512-knBY82pjmnIzK3NifMo3RxEIRD9E0kIzV4BKcyTZ9+9kWgLMxd4PrsTSMoFQUabgRBbF8KOLRDCyKgNV+iK44Q== +supports-color@9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== supports-color@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tar-fs@2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== +tar-fs@3.0.4, tar-fs@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" + integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== dependencies: - chownr "^1.1.1" mkdirp-classic "^0.5.2" pump "^3.0.0" - tar-stream "^2.1.4" + tar-stream "^3.1.5" -tar-stream@^2.1.4, tar-stream@^2.2.0: +tar-stream@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== dependencies: bl "^4.0.3" @@ -5070,9 +5617,18 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.1.5: + version "3.1.6" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.6.tgz#6520607b55a06f4a2e2e04db360ba7d338cc5bab" + integrity sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tcp-port-used@^1.0.1, tcp-port-used@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== dependencies: debug "4.3.1" @@ -5080,107 +5636,132 @@ tcp-port-used@^1.0.1, tcp-port-used@^1.0.2: text-table@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through@^2.3.6, through@^2.3.8: +through@^2.3.8: version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tmp@^0.0.33: version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" to-fast-properties@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== + +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.0.1, tslib@^2.1.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" + integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.20.2: version "0.20.2" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^1.0.2: - version "1.4.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" - integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== +type-fest@^3.12.0, type-fest@^3.8.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" -type-fest@^2.0.0, type-fest@^2.5.0: - version "2.19.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" - integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" typed-array-length@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== dependencies: call-bind "^1.0.2" for-each "^0.3.3" is-typed-array "^1.1.9" -typescript@^4.7.4: - version "4.7.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== - -ua-parser-js@^1.0.1: - version "1.0.33" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz" - integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ== +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== unbox-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: call-bind "^1.0.2" @@ -5190,7 +5771,7 @@ unbox-primitive@^1.0.2: unbzip2-stream@1.4.3: version "1.4.3" - resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== dependencies: buffer "^5.2.1" @@ -5198,12 +5779,12 @@ unbzip2-stream@1.4.3: unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== unicode-match-property-ecmascript@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== dependencies: unicode-canonical-property-names-ecmascript "^2.0.0" @@ -5211,57 +5792,68 @@ unicode-match-property-ecmascript@^2.0.0: unicode-match-property-value-ecmascript@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +unzipper@^0.10.14: + version "0.10.14" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" + integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + graceful-fs "^4.2.2" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - uuid@^9.0.0: version "9.0.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -5269,7 +5861,7 @@ validate-npm-package-license@^3.0.1: vscode-json-languageservice@^4.1.6: version "4.2.1" - resolved "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== dependencies: jsonc-parser "^3.0.0" @@ -5280,34 +5872,43 @@ vscode-json-languageservice@^4.1.6: vscode-languageserver-textdocument@^1.0.3: version "1.0.8" - resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== vscode-languageserver-types@^3.16.0: version "3.17.3" - resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== vscode-nls@^5.0.0: version "5.2.0" - resolved "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-uri@^3.0.3: version "3.0.7" - resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== +wait-port@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-1.0.4.tgz#6f9474645ddbf7701ac100ab6762438edf6e5689" + integrity sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw== + dependencies: + chalk "^4.1.2" + commander "^9.3.0" + debug "^4.3.4" + wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" wdio-chromedriver-service@^8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/wdio-chromedriver-service/-/wdio-chromedriver-service-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/wdio-chromedriver-service/-/wdio-chromedriver-service-8.1.1.tgz#bdd3776b9a6ccfcec30ec425f906e0fb1c41f966" integrity sha512-pN3GiOkTIMnalfq4PJAHdX95pDp1orHnTY8W1fIbd6ok81ba97UjerTgS7lUDRUh1p0MAm35Ww0uc0/9wzB7SA== dependencies: "@wdio/logger" "^8.1.0" @@ -5315,93 +5916,74 @@ wdio-chromedriver-service@^8.1.1: split2 "^4.1.0" tcp-port-used "^1.0.2" -webdriver@8.3.8: - version "8.3.8" - resolved "https://registry.npmjs.org/webdriver/-/webdriver-8.3.8.tgz" - integrity sha512-85ga5IMX8btQbLTQhJrt6HoXNDK0msEV2Ff1E2UYnfgmK8IGvqtZWxz8LM3fKrnjS8NNgxpYB4y81ixfnzRRFA== +web-streams-polyfill@^3.0.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + +webdriver@8.14.3: + version "8.14.3" + resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.14.3.tgz#9b87f62655d422bce54d17b5fca2cde9aa701442" + integrity sha512-7hAhzXHuNKkowa2yN0KgNiHVxOOWpBjt0Ql7EBQadlF6wWzSFX8qTdtEBuDk9VcR/ijDE2X9UyptNCU1j83TvQ== dependencies: - "@types/node" "^18.0.0" + "@puppeteer/browsers" "^1.4.6" + "@types/node" "^20.1.0" "@types/ws" "^8.5.3" - "@wdio/config" "8.3.2" - "@wdio/logger" "8.1.0" - "@wdio/protocols" "8.3.5" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" - deepmerge-ts "^4.2.2" - got "^12.1.0" + "@wdio/config" "8.14.0" + "@wdio/logger" "8.11.0" + "@wdio/protocols" "8.11.0" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" + chrome-launcher "^1.0.0" + decamelize "^6.0.0" + deepmerge-ts "^5.1.0" + edgedriver "^5.3.2" + geckodriver "^4.1.3" + get-port "^7.0.0" + got "^ 12.6.1" ky "^0.33.0" + safaridriver "^0.1.0" + wait-port "^1.0.4" ws "^8.8.0" -webdriverio@8.3.10: - version "8.3.10" - resolved "https://registry.npmjs.org/webdriverio/-/webdriverio-8.3.10.tgz" - integrity sha512-kfbrPiqIZzJ6ZhSQmvId6LMzWxQ7eVnZxTVP1OvuuIQ26BoHbxs05hNJSdgm6utWj7f+34VeWiu0e+w5kZyEyA== - dependencies: - "@types/node" "^18.0.0" - "@wdio/config" "8.3.2" - "@wdio/logger" "8.1.0" - "@wdio/protocols" "8.3.5" - "@wdio/repl" "8.1.0" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" - archiver "^5.0.0" - aria-query "^5.0.0" - css-shorthand-properties "^1.1.1" - css-value "^0.0.1" - devtools "8.3.8" - devtools-protocol "^0.0.1103684" - grapheme-splitter "^1.0.2" - import-meta-resolve "^2.1.0" - is-plain-obj "^4.1.0" - lodash.clonedeep "^4.5.0" - lodash.zip "^4.2.0" - minimatch "^6.1.4" - puppeteer-core "19.6.3" - query-selector-shadow-dom "^1.0.0" - resq "^1.9.1" - rgb2hex "0.2.5" - serialize-error "^8.0.0" - webdriver "8.3.8" - -webdriverio@8.3.9, webdriverio@^8.2.4: - version "8.3.9" - resolved "https://registry.npmjs.org/webdriverio/-/webdriverio-8.3.9.tgz" - integrity sha512-r1fkBs06zqt9UGAlGVK/bGY+mfF/4SBDGTzKQSh+osqSDAtu/heWE2Xwn9CTbbzLlK1HhBVftDhQYEFOmfSvDg== - dependencies: - "@types/node" "^18.0.0" - "@wdio/config" "8.3.2" - "@wdio/logger" "8.1.0" - "@wdio/protocols" "8.3.5" - "@wdio/repl" "8.1.0" - "@wdio/types" "8.3.0" - "@wdio/utils" "8.3.0" +webdriverio@8.14.3, webdriverio@^8.13.1: + version "8.14.3" + resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.14.3.tgz#490cdd505b6cf57a50d6f67841b8295d6f25c94f" + integrity sha512-NMVap664xbbPKrVWpcxyTEVmkUadS8wu2eBtT8uMhesLCtaCZjxTF5andHpIEFitKhgHf22SeP++1ZrQpMoRCA== + dependencies: + "@types/node" "^20.1.0" + "@wdio/config" "8.14.0" + "@wdio/logger" "8.11.0" + "@wdio/protocols" "8.11.0" + "@wdio/repl" "8.10.1" + "@wdio/types" "8.14.0" + "@wdio/utils" "8.14.0" archiver "^5.0.0" aria-query "^5.0.0" css-shorthand-properties "^1.1.1" css-value "^0.0.1" - devtools "8.3.8" - devtools-protocol "^0.0.1103684" + devtools-protocol "^0.0.1170846" grapheme-splitter "^1.0.2" - import-meta-resolve "^2.1.0" + import-meta-resolve "^3.0.0" is-plain-obj "^4.1.0" lodash.clonedeep "^4.5.0" lodash.zip "^4.2.0" - minimatch "^6.1.4" - puppeteer-core "19.6.3" + minimatch "^9.0.0" + puppeteer-core "^20.9.0" query-selector-shadow-dom "^1.0.0" resq "^1.9.1" rgb2hex "0.2.5" serialize-error "^8.0.0" - webdriver "8.3.8" + webdriver "8.14.3" webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -5409,7 +5991,7 @@ whatwg-url@^5.0.0: which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -5418,80 +6000,64 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.10, which-typed-array@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^1.2.9: version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -which@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/which/-/which-3.0.0.tgz" - integrity sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ== +which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== dependencies: isexe "^2.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - workerpool@6.2.1: version "6.2.1" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.0.1: +wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" @@ -5500,62 +6066,62 @@ wrap-ansi@^8.0.1: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@8.11.0: - version "8.11.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== +ws@8.13.0, ws@^8.8.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== ws@^7.4.3: version "7.5.9" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.8.0: - version "8.12.1" - resolved "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz" - integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== - y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== -yargs-parser@20.2.4, yargs-parser@^20.2.2: +yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -5565,7 +6131,7 @@ yargs-unparser@2.0.0: yargs@16.2.0: version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" @@ -5576,10 +6142,23 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.5.1: - version "17.7.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz" - integrity sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ== +yargs@17.7.1: + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -5591,7 +6170,7 @@ yargs@^17.5.1: yarn-install@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/yarn-install/-/yarn-install-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/yarn-install/-/yarn-install-1.0.0.tgz#57f45050b82efd57182b3973c54aa05cb5d25230" integrity sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg== dependencies: cac "^3.0.3" @@ -5600,7 +6179,7 @@ yarn-install@^1.0.0: yauzl@^2.10.0: version "2.10.0" - resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== dependencies: buffer-crc32 "~0.2.3" @@ -5608,17 +6187,17 @@ yauzl@^2.10.0: yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yocto-queue@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== zip-stream@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== dependencies: archiver-utils "^2.1.0" From 4bf4dd10cf28352f97c5406f1d1df00eb1c8fb03 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 8 Aug 2023 12:00:24 +0100 Subject: [PATCH 260/567] Update CSP policy urls for GA4 (#1179) --- app/setup.py | 15 ++++++++++----- tests/integration/test_app_create.py | 10 ++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/setup.py b/app/setup.py index ae91d0bbbd..b3223e3813 100644 --- a/app/setup.py +++ b/app/setup.py @@ -58,9 +58,8 @@ "font-src": ["'self'", "data:", "https://fonts.gstatic.com"], "script-src": [ "'self'", - "https://www.googletagmanager.com", - "https://www.google-analytics.com", - "https://ssl.google-analytics.com", + "https://tagmanager.google.com", + "https://*.googletagmanager.com", "'unsafe-inline'", ], "style-src": [ @@ -69,13 +68,19 @@ "https://fonts.googleapis.com", "'unsafe-inline'", ], - "connect-src": ["'self'", "https://www.google-analytics.com"], + "connect-src": [ + "'self'", + "https://*.google-analytics.com", + "https://*.analytics.google.com", + "https://*.googletagmanager.com", + ], "img-src": [ "'self'", "data:", - "https://www.google-analytics.com", "https://ssl.gstatic.com", "https://www.gstatic.com", + "https://*.google-analytics.com", + "https://*.googletagmanager.com", ], "object-src": ["'none'"], "base-uri": ["'none'"], diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index 8e4e23c3ae..3f233a265d 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -137,8 +137,8 @@ def test_csp_policy_headers(self): csp_policy_parts = headers["Content-Security-Policy"].split("; ") self.assertIn(f"default-src 'self' {cdn_url}", csp_policy_parts) self.assertIn( - "script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com " - f"https://ssl.google-analytics.com 'unsafe-inline' {cdn_url} 'nonce-{request.csp_nonce}'", + "script-src 'self' https://tagmanager.google.com https://*.googletagmanager.com " + f"'unsafe-inline' {cdn_url} 'nonce-{request.csp_nonce}'", csp_policy_parts, ) self.assertIn( @@ -146,7 +146,8 @@ def test_csp_policy_headers(self): csp_policy_parts, ) self.assertIn( - f"img-src 'self' data: https://www.google-analytics.com https://ssl.gstatic.com https://www.gstatic.com {cdn_url}", + "img-src 'self' data: https://ssl.gstatic.com https://www.gstatic.com https://*.google-analytics.com" + f" https://*.googletagmanager.com {cdn_url}", csp_policy_parts, ) self.assertIn( @@ -154,7 +155,8 @@ def test_csp_policy_headers(self): csp_policy_parts, ) self.assertIn( - f"connect-src 'self' https://www.google-analytics.com {cdn_url} {address_lookup_api_url}", + "connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com" + f" https://*.googletagmanager.com {cdn_url} {address_lookup_api_url}", csp_policy_parts, ) self.assertIn( From 76827b0c16b4dc52699f0f5d46660bf06941954e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:26:22 +0100 Subject: [PATCH 261/567] Update valid units and remove metric ton support (#1178) --- app/jinja_filters.py | 6 +----- schemas/test/en/test_placeholder_transform.json | 4 ++-- schemas/test/en/test_unit_patterns.json | 4 ++-- tests/app/test_jinja_filters.py | 10 +++++----- tests/functional/spec/features/units.spec.js | 1 - .../questionnaire/test_questionnaire_locale.py | 8 ++++---- 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index bdead05e8a..a4ae83b030 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -75,13 +75,9 @@ def format_unit( value: int | float | Decimal, length: UnitLengthType = "short", ) -> str: - # mass-metric-ton no longer supported for en_GB and related locales, but still present in business schema and allowed in validator, - # until removed from schema we substitute mass-tonne for mass-metric-ton before format unit - measurement_unit = "mass-tonne" if unit == "mass-metric-ton" else unit - formatted_unit: str = units.format_unit( value=value, - measurement_unit=measurement_unit, + measurement_unit=unit, length=length, locale=flask_babel.get_locale(), ) diff --git a/schemas/test/en/test_placeholder_transform.json b/schemas/test/en/test_placeholder_transform.json index 6986a52fc2..4ad87b158d 100644 --- a/schemas/test/en/test_placeholder_transform.json +++ b/schemas/test/en/test_placeholder_transform.json @@ -284,7 +284,7 @@ { "id": "average-distance", "mandatory": false, - "unit": "mile", + "unit": "length-mile", "type": "Unit", "unit_length": "long", "label": "Average commuting distance", @@ -315,7 +315,7 @@ "source": "answers", "identifier": "average-distance" }, - "unit": "mile", + "unit": "length-mile", "unit_length": "long" } } diff --git a/schemas/test/en/test_unit_patterns.json b/schemas/test/en/test_unit_patterns.json index f3d6ed684f..398a0f09d5 100644 --- a/schemas/test/en/test_unit_patterns.json +++ b/schemas/test/en/test_unit_patterns.json @@ -250,11 +250,11 @@ "question": { "answers": [ { - "id": "mass-metric-ton", + "id": "mass-tonne", "label": "Mass tonnes", "mandatory": false, "type": "Unit", - "unit": "mass-metric-ton", + "unit": "mass-tonne", "unit_length": "short" } ], diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 8e785ef481..d6d6685268 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -153,9 +153,9 @@ def test_format_percentage(percentage, formatted_percentage): ("duration-year", 100, "short", "100 bl", "cy"), ("duration-hour", 100, "long", "100 awr", "cy"), ("duration-year", 100, "long", "100 mlynedd", "cy"), - ("mass-metric-ton", 100, "long", "100 tonnes", "en_GB"), - ("mass-metric-ton", 1, "long", "1 tonne", "en_GB"), - ("mass-metric-ton", 100, "short", "100 t", "en_GB"), + ("mass-tonne", 100, "long", "100 tonnes", "en_GB"), + ("mass-tonne", 1, "long", "1 tonne", "en_GB"), + ("mass-tonne", 100, "short", "100 t", "en_GB"), ), ) def test_format_unit(unit, value, length, formatted_unit, language, mocker): @@ -207,8 +207,8 @@ def test_format_unit(unit, value, length, formatted_unit, language, mocker): ("duration-hour", "long", "awr", "cy"), ("duration-year", "short", "bl", "cy"), ("duration-year", "long", "flynedd", "cy"), - ("mass-metric-ton", "long", "tonnes", "en_GB"), - ("mass-metric-ton", "short", "t", "en_GB"), + ("mass-tonne", "long", "tonnes", "en_GB"), + ("mass-tonne", "short", "t", "en_GB"), ), ) def test_format_unit_input_label(unit, length, formatted_unit, language, mocker): diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index 0873bbf8fb..3ce00a818c 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -53,6 +53,5 @@ describe("Units", () => { await $(SetAreaUnitsBlockPage.submit()).click(); await $(SetVolumeUnitsBlockPage.submit()).click(); await expect(await $("body").getText()).to.have.string("tonnes"); - await expect(await $("body").getText()).to.not.have.string("metric tons"); }); }); diff --git a/tests/integration/questionnaire/test_questionnaire_locale.py b/tests/integration/questionnaire/test_questionnaire_locale.py index aa8075bc40..c745c35cbc 100644 --- a/tests/integration/questionnaire/test_questionnaire_locale.py +++ b/tests/integration/questionnaire/test_questionnaire_locale.py @@ -3,13 +3,13 @@ class TestSession(IntegrationTestCase): def test_none_language_code_defaults_to_en(self): - # Given a questionnaire with an answer of type mass-metric-tonnes launches with no language code in the runner claims + # Given a questionnaire with an answer of type mass-tonne launches with no language code in the runner claims token = self.token_generator.create_token_with_none_language_code( "test_unit_patterns" ) self.get(url=f"/session?token={token}") - # Skip to the mass-metric-tonnes answer + # Skip to the mass-tonnes answer self.post() self.post() self.post() @@ -17,5 +17,5 @@ def test_none_language_code_defaults_to_en(self): # Then the cookie_session[language_code] will have defaulted to DEFAULT_LANGUAGE_CODE (en), and the tooltip will show "tonnes" not "metric tons" self.assertInBody("Weight Units") - self.assertInSelector("tonnes", "[id='mass-metric-ton-type']") - self.assertNotInSelector("metric tons", "[id='mass-metric-ton-type']") + self.assertInSelector("tonnes", "[id='mass-tonne-type']") + self.assertNotInSelector("metric tons", "[id='mass-tonne-type']") From 34b6d6f8384a2f92af2cc44b9358c21c5a57be0f Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:31:57 +0100 Subject: [PATCH 262/567] Schemas v3.66.1 (#1181) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 527d3c58e2..82c973e8c9 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.66.0 +v3.66.1 From d9e233695013a7a6b04ca9f805efd6d2621761d6 Mon Sep 17 00:00:00 2001 From: Kyle Lawson_ONS <108286538+kylelawsonAND@users.noreply.github.com> Date: Thu, 10 Aug 2023 09:21:47 +0100 Subject: [PATCH 263/567] Authenticate SDS Requests with GCP (#1172) * Adding OIDC_TOKEN_BACKEND env var and setting the generator (or log for local) as an app singleton * Token is acquired and appended to supp data requests. * Adding check_expiry decorator to refresh the credentials based on cred.expiry * Renaming OIDC service + fixing date time comparison. * Fixing datetime comparison with local credentials. * Adding pytz stubs package pytz * Fixing localize to utc from pytz * Splitting oidc service into separate files & fixing localization bug with local oidc * Allowing none SDS Client ID for local * Simplify and add tests for credentials with GCP * Test ttl for gcp credentials * Retry test * Fix the shared caching between the oidc tests --------- Co-authored-by: katie-gardner Co-authored-by: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- .development.env | 1 + .functional-tests.env | 1 + Pipfile | 5 +- Pipfile.lock | 1666 +++++++++-------- app/oidc/__init__.py | 0 app/oidc/gcp_oidc.py | 35 + app/oidc/local_oidc.py | 21 + app/oidc/oidc.py | 10 + app/services/supplementary_data.py | 10 + app/settings.py | 10 + app/setup.py | 23 + tests/app/oidc/__init__.py | 0 tests/app/oidc/test_gcp_oidc.py | 154 ++ .../test_request_supplementary_data.py | 32 + tests/integration/test_app_create.py | 63 +- 15 files changed, 1236 insertions(+), 795 deletions(-) create mode 100644 app/oidc/__init__.py create mode 100644 app/oidc/gcp_oidc.py create mode 100644 app/oidc/local_oidc.py create mode 100644 app/oidc/oidc.py create mode 100644 tests/app/oidc/__init__.py create mode 100644 tests/app/oidc/test_gcp_oidc.py diff --git a/.development.env b/.development.env index 44da8d065a..54a7cf6ba5 100644 --- a/.development.env +++ b/.development.env @@ -28,3 +28,4 @@ ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log SDS_API_BASE_URL=http://localhost:5003 +OIDC_TOKEN_BACKEND=local \ No newline at end of file diff --git a/.functional-tests.env b/.functional-tests.env index 9da5874de6..b40db06e04 100644 --- a/.functional-tests.env +++ b/.functional-tests.env @@ -29,3 +29,4 @@ COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS=35 SDS_API_BASE_URL=http://localhost:5003 +OIDC_TOKEN_BACKEND=local diff --git a/Pipfile b/Pipfile index 33b9d009cf..11d579eced 100644 --- a/Pipfile +++ b/Pipfile @@ -35,6 +35,8 @@ types-redis = "*" types-PyYAML = "*" types-python-dateutil = "*" pytest-mock = "*" +types-cachetools = "*" +types-pytz = "*" [packages] colorama = "*" @@ -42,7 +44,7 @@ flask = "==2.2.2" flask-babel = "*" flask-login = "*" flask-wtf = "*" -gevent = {version = "*",platform_python_implementation = "=='CPython'"} +gevent = { version = "*", platform_python_implementation = "=='CPython'" } google-cloud-datastore = "*" grpcio = "*" gunicorn = "*" @@ -73,6 +75,7 @@ simplejson = "*" markupsafe = "*" pdfkit = "*" ordered-set = "*" +cachetools = "*" [requires] python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index 3af7ed83cf..1f3bdb46fa 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "6048e514736937d357347a60fbe097ce97d92ec7b8b5dc1b1c8050d4d106d61b" + "sha256": "5a5a0591a85922a3644f5ff4dd47966d518f71baa6f7ba03c523dce5ec70c993" }, "pipfile-spec": 6, "requires": { @@ -42,19 +42,19 @@ }, "boto3": { "hashes": [ - "sha256:6648aff15d19927cd26db47eb56362ccd313a1ddbd7aaa3235ef05d05d398252", - "sha256:fe8248b80c4f0fdaed8b8779467c4431a5e52177e02ccd137d51ec51194ebb00" + "sha256:84b7952858e9319968b0348d9894a91a6bb5f31e81a45c68044d040a12362abe", + "sha256:a6e711e0b6960c3a5b789bd30c5a18eea7263f2a59fc07f85efa5e04804e49d2" ], "index": "pypi", - "version": "==1.26.125" + "version": "==1.28.15" }, "botocore": { "hashes": [ - "sha256:77f7793cb36074eb84d606a23ad6e1d57c20f7a2eeab7d9136d3e63c584e0504", - "sha256:ac57003292f18206ee942eafc381ecd9a3420a3844d6b7e1c1b0f4b88b28263b" + "sha256:b3a0f787f275711875476cbe12a0123b2e6570b2f505e2fa509dcec3c5410b57", + "sha256:b46d1ce4e0cf42d28fdf61ce0c999904645d38b51cb809817a361c0cec16d487" ], "markers": "python_version >= '3.7'", - "version": "==1.29.146" + "version": "==1.31.15" }, "brotli": { "hashes": [ @@ -148,16 +148,16 @@ "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590", "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b" ], - "markers": "python_version >= '3.7'", + "index": "pypi", "version": "==5.3.1" }, "certifi": { "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", + "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" ], "markers": "python_version >= '3.6'", - "version": "==2023.5.7" + "version": "==2023.7.22" }, "cffi": { "hashes": [ @@ -230,92 +230,92 @@ }, "charset-normalizer": { "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.0" + "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", + "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", + "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", + "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", + "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", + "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", + "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", + "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", + "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", + "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", + "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", + "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", + "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", + "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", + "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", + "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", + "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", + "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", + "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", + "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", + "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", + "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", + "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", + "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", + "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", + "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", + "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", + "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", + "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", + "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", + "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", + "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", + "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", + "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", + "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", + "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", + "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", + "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", + "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", + "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", + "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", + "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", + "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", + "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", + "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", + "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", + "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", + "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", + "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", + "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", + "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", + "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", + "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", + "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", + "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", + "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", + "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", + "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", + "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", + "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", + "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", + "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", + "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", + "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", + "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", + "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", + "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", + "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", + "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", + "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", + "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", + "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", + "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", + "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", + "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.2.0" }, "click": { "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd", + "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5" ], "markers": "python_version >= '3.7'", - "version": "==8.1.3" + "version": "==8.1.6" }, "colorama": { "hashes": [ @@ -335,28 +335,32 @@ }, "cryptography": { "hashes": [ - "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", - "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", - "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", - "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", - "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", - "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", - "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", - "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", - "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", - "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", - "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", - "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", - "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", - "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", - "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", - "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", - "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", - "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", - "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" - ], - "index": "pypi", - "version": "==41.0.0" + "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711", + "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7", + "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd", + "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e", + "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58", + "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0", + "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d", + "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83", + "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831", + "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766", + "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b", + "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c", + "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182", + "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f", + "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa", + "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4", + "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a", + "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2", + "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76", + "sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5", + "sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee", + "sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f", + "sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.2" }, "deprecated": { "hashes": [ @@ -368,11 +372,11 @@ }, "dnspython": { "hashes": [ - "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9", - "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46" + "sha256:5b7488477388b8c0b70a8ce93b227c5603bc7b77f1565afe8e729c36c51447d7", + "sha256:c33971c79af5be968bb897e95c2448e11a645ee84d93b265ce0b7aabe5dfdca8" ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.3.0" + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==2.4.1" }, "email-validator": { "hashes": [ @@ -432,121 +436,100 @@ }, "gevent": { "hashes": [ - "sha256:018f93de7d5318d2fb440f846839a4464738468c3476d5c9cf7da45bb71c18bd", - "sha256:0d581f22a5be6281b11ad6309b38b18f0638cf896931223cbaa5adb904826ef6", - "sha256:1472012493ca1fac103f700d309cb6ef7964dcdb9c788d1768266e77712f5e49", - "sha256:172caa66273315f283e90a315921902cb6549762bdcb0587fd60cb712a9d6263", - "sha256:17b68f4c9e20e47ad49fe797f37f91d5bbeace8765ce2707f979a8d4ec197e4d", - "sha256:1ca01da176ee37b3527a2702f7d40dbc9ffb8cfc7be5a03bfa4f9eec45e55c46", - "sha256:1d543c9407a1e4bca11a8932916988cfb16de00366de5bf7bc9e7a3f61e60b18", - "sha256:1e1286a76f15b5e15f1e898731d50529e249529095a032453f2c101af3fde71c", - "sha256:1e955238f59b2947631c9782a713280dd75884e40e455313b5b6bbc20b92ff73", - "sha256:1f001cac0ba8da76abfeb392a3057f81fab3d67cc916c7df8ea977a44a2cc989", - "sha256:1ff3796692dff50fec2f381b9152438b221335f557c4f9b811f7ded51b7a25a1", - "sha256:2929377c8ebfb6f4d868d161cd8de2ea6b9f6c7a5fcd4f78bcd537319c16190b", - "sha256:319d8b1699b7b8134de66d656cd739b308ab9c45ace14d60ae44de7775b456c9", - "sha256:323b207b281ba0405fea042067fa1a61662e5ac0d574ede4ebbda03efd20c350", - "sha256:3b7eae8a0653ba95a224faaddf629a913ace408edb67384d3117acf42d7dcf89", - "sha256:4114f0f439f0b547bb6f1d474fee99ddb46736944ad2207cef3771828f6aa358", - "sha256:4197d423e198265eef39a0dea286ef389da9148e070310f34455ecee8172c391", - "sha256:494c7f29e94df9a1c3157d67bb7edfa32a46eed786e04d9ee68d39f375e30001", - "sha256:4e2f008c82dc54ec94f4de12ca6feea60e419babb48ec145456907ae61625aa4", - "sha256:53ee7f170ed42c7561fe8aff5d381dc9a4124694e70580d0c02fba6aafc0ea37", - "sha256:54f4bfd74c178351a4a05c5c7df6f8a0a279ff6f392b57608ce0e83c768207f9", - "sha256:58898dbabb5b11e4d0192aae165ad286dc6742c543e1be9d30dc82753547c508", - "sha256:59b47e81b399d49a5622f0f503c59f1ce57b7705306ea0196818951dfc2f36c8", - "sha256:5aa99e4882a9e909b4756ee799c6fa0f79eb0542779fad4cc60efa23ec1b2aa8", - "sha256:6c04ee32c11e9fcee47c1b431834878dc987a7a2cc4fe126ddcae3bad723ce89", - "sha256:84c517e33ed604fa06b7d756dc0171169cc12f7fdd68eb7b17708a62eebf4516", - "sha256:8729129edef2637a8084258cb9ec4e4d5ca45d97ac77aa7a6ff19ccb530ab731", - "sha256:877abdb3a669576b1d51ce6a49b7260b2a96f6b2424eb93287e779a3219d20ba", - "sha256:8c192d2073e558e241f0b592c1e2b34127a4481a5be240cad4796533b88b1a98", - "sha256:8f2477e7b0a903a01485c55bacf2089110e5f767014967ba4b287ff390ae2638", - "sha256:96c56c280e3c43cfd075efd10b250350ed5ffd3c1514ec99a080b1b92d7c8374", - "sha256:97cd42382421779f5d82ec5007199e8a84aa288114975429e4fd0a98f2290f10", - "sha256:98bc510e80f45486ef5b806a1c305e0e89f0430688c14984b0dbdec03331f48b", - "sha256:990d7069f14dc40674e0d5cb43c68fd3bad8337048613b9bb94a0c4180ffc176", - "sha256:9d85574eb729f981fea9a78998725a06292d90a3ed50ddca74530c3148c0be41", - "sha256:a2237451c721a0f874ef89dbb4af4fdc172b76a964befaa69deb15b8fff10f49", - "sha256:a47a4e77e2bc668856aad92a0b8de7ee10768258d93cd03968e6c7ba2e832f76", - "sha256:a5488eba6a568b4d23c072113da4fc0feb1b5f5ede7381656dc913e0d82204e2", - "sha256:ae90226074a6089371a95f20288431cd4b3f6b0b096856afd862e4ac9510cddd", - "sha256:b43d500d7d3c0e03070dee813335bb5315215aa1cf6a04c61093dfdd718640b3", - "sha256:b6c144e08dfad4106effc043a026e5d0c0eff6ad031904c70bf5090c63f3a6a7", - "sha256:d21ad79cca234cdbfa249e727500b0ddcbc7adfff6614a96e6eaa49faca3e4f2", - "sha256:d82081656a5b9a94d37c718c8646c757e1617e389cdc533ea5e6a6f0b8b78545", - "sha256:da4183f0b9d9a1e25e1758099220d32c51cc2c6340ee0dea3fd236b2b37598e4", - "sha256:db562a8519838bddad0c439a2b12246bab539dd50e299ea7ff3644274a33b6a5", - "sha256:ddaa3e310a8f1a45b5c42cf50b54c31003a3028e7d4e085059090ea0e7a5fddd", - "sha256:ed7f16613eebf892a6a744d7a4a8f345bc6f066a0ff3b413e2479f9c0a180193", - "sha256:efc003b6c1481165af61f0aeac248e0a9ac8d880bb3acbe469b448674b2d5281", - "sha256:f01c9adbcb605364694b11dcd0542ec468a29ac7aba2fb5665dc6caf17ba4d7e", - "sha256:f23d0997149a816a2a9045af29c66f67f405a221745b34cefeac5769ed451db8", - "sha256:f3329bedbba4d3146ae58c667e0f9ac1e6f1e1e6340c7593976cdc60aa7d1a47", - "sha256:f7ed2346eb9dc4344f9cb0d7963ce5b74fe16fdd031a2809bb6c2b6eba7ebcd5" + "sha256:11b9bb0bce45170ff992760385a86e6955ccb88dba4a82a64d5ce9459290d8d6", + "sha256:1234849b0bc4df560924aa92f7c01ca3f310677735fb508a2b0d7a61bb946916", + "sha256:34086bcc1252ae41e1cb81cf13c4a7678031595c12f4e9a1c3d0ab433f20826a", + "sha256:369241d1a6a3fe3ef4eba454b71e0168026560c5344fc4bc37196867041982ac", + "sha256:3e0d76a7848726e0646324a1adc011355dcd91875e7913badd1ada2e5eeb8a6e", + "sha256:5b230007a665d2cf5cf8878c9f56a2b8bacbdc4fe0235afc5269b71cd00528e5", + "sha256:5da07d65dfa23fe419c37cea110bf951b42af6bf3a1fff244043a75c9185dbd5", + "sha256:6a51a8e3cdaa6901e47d56f84cb5f92b1bf3deea920bce69cf7a245df16159ac", + "sha256:6b3dd449c80814357f6568eb095a2be2421b805d59fa97c65094707e04a181f9", + "sha256:6bd9ea1b5fbdc7e5921a9e515f34a450eb3927a902253a33caedcce2d19d7d96", + "sha256:746a1e12f280dab07389e6709164b1e1a6caaf50493ea5b1dcaa73cff005174c", + "sha256:769e8811ded08fe7d8b09ad8ebb72d47aecc112411e0726e7296b7ed187ed629", + "sha256:76ca6f893953ab898ebbff5d772103318a85044e55d0bad401d6b49d71bb76e7", + "sha256:83b6d61a8e9da25edb304ca7fba19ee57bb1ffa801f9df3e668bfed7bb8386cb", + "sha256:8c284390f0f6d0b5be3bf805fa8e0ae1329065f2b0ac5af5423c67183197deb8", + "sha256:919423e803939726c99ab2d29ea46b8676af549cee72d263f2b24758ec607b2c", + "sha256:94b013587f7c4697d620c129627f7b12d7d9f6e40ab198635891ca2098cd8556", + "sha256:9c7c349aa23d67cf5cc3b2c87aaedcfead976d0577b1cfcd07ffeba63baba79c", + "sha256:a1d2f1e67d04fde47ca2deac89733df28ef3a7ec1d7359a79f57d4778cced16d", + "sha256:a226b42cb9a49580ca7729572a4f8289d1fa28cd2529c9f4eed3e14b995d1c9c", + "sha256:a8f62e8d37913512823923e05607a296389aeb50ccca8a271ae7cedb5b17faeb", + "sha256:add904a7ef960cd4e133e61eb7413982c5e4203928160be1c09752ac06a25e71", + "sha256:aeb1511cf0786152af741c47ee462dac81b57bbd1fbbe08ab562b6c8c9ad75ed", + "sha256:c1dba07207b15b371e50372369edf256a142cb5cdf8599849cbf8660327efa06", + "sha256:c92b837b60e850c50fc6d723d1e363e786d37fd9d51e564e07df52ad5e8a86d4", + "sha256:cea93f4f77badbddc711620cca164ad75c74056603908e621a5ba1b97adbc39c", + "sha256:d0d3630674c1b344b256a298ab1ff43220f840b12af768131b5d74e485924237", + "sha256:debc177e88a8c876cb1a4d974f985d03670177bdc61e1c084a8d525f1a50b12d", + "sha256:dec7b08daf08385fb281b81ec2e7e703243975d867f40ae0a8a3e30b380eb9ea", + "sha256:df4d7be3352126458cc818309ca6a3b678c209b1ae33e56b6975c6a8309f2068", + "sha256:f522b6b015f1bfa9d8d3716ddffb23e3d4a8933df3e4ebf0a29a65a9fa74382b" ], "index": "pypi", "markers": "platform_python_implementation == 'CPython'", - "version": "==22.10.2" + "version": "==23.7.0" }, "google-api-core": { "extras": [ - + "grpc" ], "hashes": [ - "sha256:4b9bb5d5a380a0befa0573b302651b8a9a89262c1730e37bf423cec511804c22", - "sha256:ce222e27b0de0d7bc63eb043b956996d6dccab14cc3b690aaea91c9cc99dc16e" + "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a", + "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a" ], "markers": "python_version >= '3.7'", - "version": "==2.11.0" + "version": "==2.11.1" }, "google-auth": { "hashes": [ - "sha256:a9cfa88b3e16196845e64a3658eb953992129d13ac7337b064c6546f77c17183", - "sha256:ea165e014c7cbd496558796b627c271aa8c18b4cba79dc1cc962b24c5efdfb85" + "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce", + "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873" ], "markers": "python_version >= '3.6'", - "version": "==2.19.1" + "version": "==2.22.0" }, "google-cloud-core": { "hashes": [ - "sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe", - "sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a" + "sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb", + "sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863" ], "markers": "python_version >= '3.7'", - "version": "==2.3.2" + "version": "==2.3.3" }, "google-cloud-datastore": { "hashes": [ - "sha256:4c2f0e8825482a8998c8c5b672c86206a6d988e449f251bb9bf17dae9a056c2e", - "sha256:788c512ea6d63012e711e0371a0e425ec852990051aca5c9c9a73804c448b1af" + "sha256:153d8dac05f0248d7f7d6f0986a31f8fb22d3cdfb764718eac0f28e3cadc8144", + "sha256:710ef27ebdfb50340f4671c532ad4c1525665985e9421984ffcd6b96b57e34bb" ], "index": "pypi", - "version": "==2.15.1" + "version": "==2.16.1" }, "google-cloud-pubsub": { "hashes": [ - "sha256:587da7d535ca858ceeed7036205355e5a6dd3e44ea4abc96f4e50d1abfd8843b", - "sha256:e41ec9635cc68dbd238f572f295b3d0bae6340c66ba31a70dfb890950ab33c2b" + "sha256:00691ca49bed09946f92f6001f9f7456f561b32a205e4f82dd8aa74dc3e97d9e", + "sha256:4a2bf3c04f8278504f934b2bdbafe927fa650d7c5397c7633a0b30e10a5127f3" ], "index": "pypi", - "version": "==2.16.0" + "version": "==2.18.1" }, "google-cloud-storage": { "hashes": [ - "sha256:248e210c13bc109909160248af546a91cb2dabaf3d7ebbf04def9dd49f02dbb6", - "sha256:4388da1ff5bda6d729f26dbcaf1bfa020a2a52a7b91f0a8123edbda51660802c" + "sha256:934b31ead5f3994e5360f9ff5750982c5b6b11604dc072bc452c25965e076dc7", + "sha256:9433cf28801671de1c80434238fb1e7e4a1ba3087470e90f70c928ea77c2b9d7" ], "index": "pypi", - "version": "==2.8.0" + "version": "==2.10.0" }, "google-cloud-tasks": { "hashes": [ - "sha256:616f2c32945cf68e812375a32f8a0f1d9cc39f4a39de03942593af6d13af7c20", - "sha256:99b845055c7a1b27b73349429f2d6f6a0eb9f409b4cdeec89df99e16bd197833" + "sha256:095d8a10049efb975186a809670791ff9ed1891255236b55a778b5bf8803fd08", + "sha256:e83d205cc4ed71ab0b100b770863403ce02e69f5fac69e1c969fdf6e2d8eafb1" ], "index": "pypi", - "version": "==2.13.1" + "version": "==2.14.0" }, "google-crc32c": { "hashes": [ @@ -632,11 +615,11 @@ }, "googleapis-common-protos": { "hashes": [ - "sha256:4168fcb568a826a52f23510412da405abd93f4d23ba544bb68d943b14ba3cb44", - "sha256:b287dc48449d1d41af0c69f4ea26242b5ae4c3d7249a38b0984c86a4caffff1f" + "sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e", + "sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a" ], "markers": "python_version >= '3.7'", - "version": "==1.59.0" + "version": "==1.59.1" }, "greenlet": { "hashes": [ @@ -701,7 +684,7 @@ "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1", "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526" ], - "markers": "platform_python_implementation == 'CPython'", + "markers": "python_version < '3.12' and platform_python_implementation == 'CPython'", "version": "==2.0.2" }, "grpc-google-iam-v1": { @@ -714,70 +697,70 @@ }, "grpcio": { "hashes": [ - "sha256:02000b005bc8b72ff50c477b6431e8886b29961159e8b8d03c00b3dd9139baed", - "sha256:031bbd26656e0739e4b2c81c172155fb26e274b8d0312d67aefc730bcba915b6", - "sha256:1209d6b002b26e939e4c8ea37a3d5b4028eb9555394ea69fb1adbd4b61a10bb8", - "sha256:125ed35aa3868efa82eabffece6264bf638cfdc9f0cd58ddb17936684aafd0f8", - "sha256:1382bc499af92901c2240c4d540c74eae8a671e4fe9839bfeefdfcc3a106b5e2", - "sha256:16bca8092dd994f2864fdab278ae052fad4913f36f35238b2dd11af2d55a87db", - "sha256:1c59d899ee7160638613a452f9a4931de22623e7ba17897d8e3e348c2e9d8d0b", - "sha256:1d109df30641d050e009105f9c9ca5a35d01e34d2ee2a4e9c0984d392fd6d704", - "sha256:1fa7d6ddd33abbd3c8b3d7d07c56c40ea3d1891ce3cd2aa9fa73105ed5331866", - "sha256:21c4a1aae861748d6393a3ff7867473996c139a77f90326d9f4104bebb22d8b8", - "sha256:224166f06ccdaf884bf35690bf4272997c1405de3035d61384ccb5b25a4c1ca8", - "sha256:2262bd3512ba9e9f0e91d287393df6f33c18999317de45629b7bd46c40f16ba9", - "sha256:2585b3c294631a39b33f9f967a59b0fad23b1a71a212eba6bc1e3ca6e6eec9ee", - "sha256:27fb030a4589d2536daec5ff5ba2a128f4f155149efab578fe2de2cb21596d3d", - "sha256:30fbbce11ffeb4f9f91c13fe04899aaf3e9a81708bedf267bf447596b95df26b", - "sha256:3930669c9e6f08a2eed824738c3d5699d11cd47a0ecc13b68ed11595710b1133", - "sha256:3b170e441e91e4f321e46d3cc95a01cb307a4596da54aca59eb78ab0fc03754d", - "sha256:3db71c6f1ab688d8dfc102271cedc9828beac335a3a4372ec54b8bf11b43fd29", - "sha256:48cb7af77238ba16c77879009003f6b22c23425e5ee59cb2c4c103ec040638a5", - "sha256:49eace8ea55fbc42c733defbda1e4feb6d3844ecd875b01bb8b923709e0f5ec8", - "sha256:533eaf5b2a79a3c6f35cbd6a095ae99cac7f4f9c0e08bdcf86c130efd3c32adf", - "sha256:5942a3e05630e1ef5b7b5752e5da6582460a2e4431dae603de89fc45f9ec5aa9", - "sha256:62117486460c83acd3b5d85c12edd5fe20a374630475388cfc89829831d3eb79", - "sha256:650f5f2c9ab1275b4006707411bb6d6bc927886874a287661c3c6f332d4c068b", - "sha256:6dc1e2c9ac292c9a484ef900c568ccb2d6b4dfe26dfa0163d5bc815bb836c78d", - "sha256:73c238ef6e4b64272df7eec976bb016c73d3ab5a6c7e9cd906ab700523d312f3", - "sha256:775a2f70501370e5ba54e1ee3464413bff9bd85bd9a0b25c989698c44a6fb52f", - "sha256:860fcd6db7dce80d0a673a1cc898ce6bc3d4783d195bbe0e911bf8a62c93ff3f", - "sha256:87f47bf9520bba4083d65ab911f8f4c0ac3efa8241993edd74c8dd08ae87552f", - "sha256:960b176e0bb2b4afeaa1cd2002db1e82ae54c9b6e27ea93570a42316524e77cf", - "sha256:a7caf553ccaf715ec05b28c9b2ab2ee3fdb4036626d779aa09cf7cbf54b71445", - "sha256:a947d5298a0bbdd4d15671024bf33e2b7da79a70de600ed29ba7e0fef0539ebb", - "sha256:a97b0d01ae595c997c1d9d8249e2d2da829c2d8a4bdc29bb8f76c11a94915c9a", - "sha256:b7655f809e3420f80ce3bf89737169a9dce73238af594049754a1128132c0da4", - "sha256:c33744d0d1a7322da445c0fe726ea6d4e3ef2dfb0539eadf23dce366f52f546c", - "sha256:c55a9cf5cba80fb88c850915c865b8ed78d5e46e1f2ec1b27692f3eaaf0dca7e", - "sha256:d2f62fb1c914a038921677cfa536d645cb80e3dd07dc4859a3c92d75407b90a5", - "sha256:d8ae6e0df3a608e99ee1acafaafd7db0830106394d54571c1ece57f650124ce9", - "sha256:e355ee9da9c1c03f174efea59292b17a95e0b7b4d7d2a389265f731a9887d5a9", - "sha256:e3e526062c690517b42bba66ffe38aaf8bc99a180a78212e7b22baa86902f690", - "sha256:eb0807323572642ab73fd86fe53d88d843ce617dd1ddf430351ad0759809a0ae", - "sha256:ebff0738be0499d7db74d20dca9f22a7b27deae31e1bf92ea44924fd69eb6251", - "sha256:ed36e854449ff6c2f8ee145f94851fe171298e1e793f44d4f672c4a0d78064e7", - "sha256:ed3d458ded32ff3a58f157b60cc140c88f7ac8c506a1c567b2a9ee8a2fd2ce54", - "sha256:f4a7dca8ccd8023d916b900aa3c626f1bd181bd5b70159479b142f957ff420e4" - ], - "index": "pypi", - "version": "==1.54.0" + "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57", + "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa", + "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d", + "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9", + "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9", + "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12", + "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475", + "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226", + "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b", + "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca", + "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5", + "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d", + "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35", + "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48", + "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116", + "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d", + "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774", + "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1", + "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95", + "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed", + "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a", + "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5", + "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a", + "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c", + "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950", + "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d", + "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c", + "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c", + "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756", + "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e", + "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f", + "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1", + "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9", + "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4", + "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316", + "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6", + "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd", + "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328", + "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f", + "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93", + "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64", + "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8", + "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87", + "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed", + "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c" + ], + "index": "pypi", + "version": "==1.56.2" }, "grpcio-status": { "hashes": [ - "sha256:96968314e0c8576b2b631be3917c665964c8018900cb980d58a736fbff828578", - "sha256:b50305d52c0df6169493cca5f2e39b9b4d773b3f30d4a7a6b6dd7c18cb89007c" + "sha256:63f3842867735f59f5d70e723abffd2e8501a6bcd915612a1119e52f10614782", + "sha256:a046b2c0118df4a5687f4585cca9d3c3bae5c498c4dff055dcb43fb06a1180c8" ], "markers": "python_version >= '3.6'", - "version": "==1.54.0" + "version": "==1.56.2" }, "gunicorn": { "hashes": [ - "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", - "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" ], "index": "pypi", - "version": "==20.1.0" + "version": "==21.2.0" }, "htmlmin": { "hashes": [ @@ -796,11 +779,11 @@ }, "humanize": { "hashes": [ - "sha256:401201aca462749773f02920139f302450cb548b70489b9b4b92be39fe3c3c50", - "sha256:5f1f22bc65911eb1a6ffe7659bd6598e33dcfeeb904eb16ee1e705a09bf75916" + "sha256:7ca0e43e870981fa684acb5b062deb307218193bca1a01f2b2676479df849b3a", + "sha256:df7c429c2d27372b249d3f26eb53b07b166b661326e0325793e0a988082e3889" ], "index": "pypi", - "version": "==4.6.0" + "version": "==4.7.0" }, "idna": { "hashes": [ @@ -836,11 +819,11 @@ }, "jsonpointer": { "hashes": [ - "sha256:51801e558539b4e9cd268638c078c6c5746c9ac96bc38152d443400e4f3793e9", - "sha256:97cba51526c829282218feb99dab1b1e6bdf8efd1c43dc9d57be093c0d69c99a" + "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", + "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" ], "index": "pypi", - "version": "==2.3" + "version": "==2.4" }, "jwcrypto": { "hashes": [ @@ -851,67 +834,67 @@ }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" - ], - "index": "pypi", - "version": "==2.1.2" + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" + ], + "index": "pypi", + "version": "==2.1.3" }, "marshmallow": { "hashes": [ - "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78", - "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b" + "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", + "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" ], "index": "pypi", - "version": "==3.19.0" + "version": "==3.20.1" }, "ordered-set": { "hashes": [ @@ -940,38 +923,38 @@ }, "pika": { "hashes": [ - "sha256:89f5e606646caebe3c00cbdbc4c2c609834adde45d7507311807b5775edac8e0", - "sha256:beb19ff6dd1547f99a29acc2c6987ebb2ba7c44bf44a3f8e305877c5ef7d2fdc" + "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", + "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" ], "index": "pypi", - "version": "==1.3.1" + "version": "==1.3.2" }, "proto-plus": { "hashes": [ - "sha256:0e8cda3d5a634d9895b75c573c9352c16486cb75deb0e078b5fda34db4243165", - "sha256:de34e52d6c9c6fcd704192f09767cb561bb4ee64e70eede20b0834d841f0be4d" + "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df", + "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b" ], "markers": "python_version >= '3.6'", - "version": "==1.22.2" + "version": "==1.22.3" }, "protobuf": { "hashes": [ - "sha256:09310bce43353b46d73ba7e3bca78273b9bc50349509b9698e64d288c6372c2a", - "sha256:20874e7ca4436f683b64ebdbee2129a5a2c301579a67d1a7dda2cdf62fb7f5f7", - "sha256:25e3370eda26469b58b602e29dff069cfaae8eaa0ef4550039cc5ef8dc004511", - "sha256:281342ea5eb631c86697e1e048cb7e73b8a4e85f3299a128c116f05f5c668f8f", - "sha256:384dd44cb4c43f2ccddd3645389a23ae61aeb8cfa15ca3a0f60e7c3ea09b28b3", - "sha256:54a533b971288af3b9926e53850c7eb186886c0c84e61daa8444385a4720297f", - "sha256:6c081863c379bb1741be8f8193e893511312b1d7329b4a75445d1ea9955be69e", - "sha256:86df87016d290143c7ce3be3ad52d055714ebaebb57cc659c387e76cfacd81aa", - "sha256:8da6070310d634c99c0db7df48f10da495cc283fd9e9234877f0cd182d43ab7f", - "sha256:b2cfab63a230b39ae603834718db74ac11e52bccaaf19bf20f5cce1a84cf76df", - "sha256:c52cfcbfba8eb791255edd675c1fe6056f723bf832fa67f0442218f8817c076e", - "sha256:ce744938406de1e64b91410f473736e815f28c3b71201302612a68bf01517fea", - "sha256:efabbbbac1ab519a514579ba9ec52f006c28ae19d97915951f69fa70da2c9e91" + "sha256:0a5759f5696895de8cc913f084e27fd4125e8fb0914bb729a17816a33819f474", + "sha256:351cc90f7d10839c480aeb9b870a211e322bf05f6ab3f55fcb2f51331f80a7d2", + "sha256:5fea3c64d41ea5ecf5697b83e41d09b9589e6f20b677ab3c48e5f242d9b7897b", + "sha256:6dd9b9940e3f17077e820b75851126615ee38643c2c5332aa7a359988820c720", + "sha256:7b19b6266d92ca6a2a87effa88ecc4af73ebc5cfde194dc737cf8ef23a9a3b12", + "sha256:8547bf44fe8cec3c69e3042f5c4fb3e36eb2a7a013bb0a44c018fc1e427aafbd", + "sha256:9053df6df8e5a76c84339ee4a9f5a2661ceee4a0dab019e8663c50ba324208b0", + "sha256:c3e0939433c40796ca4cfc0fac08af50b00eb66a40bbbc5dee711998fb0bbc1e", + "sha256:ccd9430c0719dce806b93f89c91de7977304729e55377f872a92465d548329a9", + "sha256:e1c915778d8ced71e26fcf43c0866d7499891bca14c4368448a82edc61fdbc70", + "sha256:e9d0be5bf34b275b9f87ba7407796556abeeba635455d036c7351f7c183ef8ff", + "sha256:effeac51ab79332d44fba74660d40ae79985901ac21bca408f8dc335a81aa597", + "sha256:fee88269a090ada09ca63551bf2f573eb2424035bcf2cb1b121895b01a46594a" ], "markers": "python_version >= '3.7'", - "version": "==4.23.2" + "version": "==4.23.4" }, "pyasn1": { "hashes": [ @@ -1067,65 +1050,65 @@ }, "pyyaml": { "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "index": "pypi", - "version": "==6.0" + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "version": "==6.0.1" }, "redis": { "hashes": [ - "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2", - "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893" + "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d", + "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c" ], "index": "pypi", - "version": "==4.5.4" + "version": "==4.6.0" }, "requests": { "hashes": [ - "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", - "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", - "version": "==2.29.0" + "version": "==2.31.0" }, "rsa": { "hashes": [ @@ -1145,19 +1128,19 @@ }, "sdc-cryptography": { "hashes": [ - "sha256:6642ce646c5c0cb0bfbfd4091fff5f9abcbd8c0af3129b977e071af06e66d29e", - "sha256:9ee921569c8b82543aba5f1e2591fc96589c0ec0759f979a4c24248d802159c1" + "sha256:0abf35a298e2be51379f9867b701524a255357921651ff35ebfe1c6b2eb81f8d", + "sha256:80e5e9dae84830bbb072f807ca880d186790ece443fddce34dbee7bf2c271a7d" ], "index": "pypi", - "version": "==1.1.1" + "version": "==1.1.2" }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", + "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" ], "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "version": "==68.0.0" }, "simplejson": { "hashes": [ @@ -1268,11 +1251,11 @@ }, "ua-parser": { "hashes": [ - "sha256:ed3efc695f475ffe56248c9789b3016247e9c20e3556cfa4d5aadc78ab4b26c6", - "sha256:f97126300df8ac0f8f2c9d8559669532d626a1af529265fd253cba56e73ab36e" + "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", + "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" ], "index": "pypi", - "version": "==0.16.1" + "version": "==0.18.0" }, "urllib3": { "hashes": [ @@ -1284,18 +1267,18 @@ }, "uwsgi": { "hashes": [ - "sha256:35a30d83791329429bc04fe44183ce4ab512fcf6968070a7bfba42fc5a0552a9" + "sha256:4cc4727258671ac5fa17ab422155e9aaef8a2008ebb86e4404b66deaae965db2" ], "index": "pypi", - "version": "==2.0.21" + "version": "==2.0.22" }, "werkzeug": { "hashes": [ - "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76", - "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f" + "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890", + "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330" ], "markers": "python_version >= '3.8'", - "version": "==2.3.4" + "version": "==2.3.6" }, "wrapt": { "hashes": [ @@ -1388,10 +1371,11 @@ }, "zope.event": { "hashes": [ - "sha256:73d9e3ef750cca14816a9c322c7250b0d7c9dbc337df5d1b807ff8d3d0b9e97c", - "sha256:81d98813046fc86cc4136e3698fee628a3282f9c320db18658c21749235fce80" + "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" ], - "version": "==4.6" + "markers": "python_version >= '3.7'", + "version": "==5.0" }, "zope.interface": { "hashes": [ @@ -1433,11 +1417,11 @@ "develop": { "astroid": { "hashes": [ - "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324", - "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f" + "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c", + "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.5" + "version": "==2.15.6" }, "async-timeout": { "hashes": [ @@ -1465,66 +1449,55 @@ }, "black": { "hashes": [ - "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", - "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", - "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", - "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", - "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", - "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", - "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", - "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", - "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", - "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", - "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", - "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", - "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", - "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", - "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", - "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", - "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", - "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", - "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", - "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", - "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", - "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", - "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", - "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", - "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" - ], - "index": "pypi", - "version": "==23.3.0" - }, - "blinker": { - "hashes": [ - "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", - "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" - ], - "index": "pypi", - "version": "==1.6.2" + "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3", + "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb", + "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087", + "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320", + "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6", + "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3", + "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc", + "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f", + "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587", + "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91", + "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a", + "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad", + "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926", + "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9", + "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be", + "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd", + "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96", + "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491", + "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2", + "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a", + "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f", + "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995" + ], + "index": "pypi", + "version": "==23.7.0" }, "boto3": { "hashes": [ - "sha256:6648aff15d19927cd26db47eb56362ccd313a1ddbd7aaa3235ef05d05d398252", - "sha256:fe8248b80c4f0fdaed8b8779467c4431a5e52177e02ccd137d51ec51194ebb00" + "sha256:84b7952858e9319968b0348d9894a91a6bb5f31e81a45c68044d040a12362abe", + "sha256:a6e711e0b6960c3a5b789bd30c5a18eea7263f2a59fc07f85efa5e04804e49d2" ], "index": "pypi", - "version": "==1.26.125" + "version": "==1.28.15" }, "botocore": { "hashes": [ - "sha256:77f7793cb36074eb84d606a23ad6e1d57c20f7a2eeab7d9136d3e63c584e0504", - "sha256:ac57003292f18206ee942eafc381ecd9a3420a3844d6b7e1c1b0f4b88b28263b" + "sha256:b3a0f787f275711875476cbe12a0123b2e6570b2f505e2fa509dcec3c5410b57", + "sha256:b46d1ce4e0cf42d28fdf61ce0c999904645d38b51cb809817a361c0cec16d487" ], "markers": "python_version >= '3.7'", - "version": "==1.29.146" + "version": "==1.31.15" }, "certifi": { "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", + "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" ], "markers": "python_version >= '3.6'", - "version": "==2023.5.7" + "version": "==2023.7.22" }, "cffi": { "hashes": [ @@ -1597,92 +1570,92 @@ }, "charset-normalizer": { "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.0" + "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", + "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", + "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", + "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", + "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", + "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", + "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", + "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", + "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", + "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", + "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", + "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", + "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", + "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", + "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", + "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", + "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", + "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", + "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", + "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", + "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", + "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", + "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", + "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", + "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", + "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", + "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", + "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", + "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", + "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", + "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", + "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", + "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", + "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", + "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", + "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", + "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", + "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", + "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", + "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", + "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", + "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", + "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", + "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", + "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", + "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", + "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", + "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", + "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", + "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", + "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", + "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", + "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", + "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", + "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", + "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", + "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", + "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", + "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", + "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", + "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", + "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", + "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", + "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", + "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", + "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", + "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", + "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", + "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", + "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", + "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", + "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", + "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", + "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", + "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.2.0" }, "click": { "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd", + "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5" ], "markers": "python_version >= '3.7'", - "version": "==8.1.3" + "version": "==8.1.6" }, "coverage": { "extras": [ @@ -1755,60 +1728,64 @@ }, "cryptography": { "hashes": [ - "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", - "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", - "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", - "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", - "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", - "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", - "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", - "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", - "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", - "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", - "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", - "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", - "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", - "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", - "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", - "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", - "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", - "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", - "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" - ], - "index": "pypi", - "version": "==41.0.0" + "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711", + "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7", + "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd", + "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e", + "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58", + "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0", + "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d", + "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83", + "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831", + "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766", + "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b", + "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c", + "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182", + "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f", + "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa", + "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4", + "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a", + "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2", + "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76", + "sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5", + "sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee", + "sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f", + "sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.2" }, "dill": { "hashes": [ - "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", - "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" + "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", + "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" ], "markers": "python_version < '3.11'", - "version": "==0.3.6" + "version": "==0.3.7" }, "exceptiongroup": { "hashes": [ - "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e", - "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785" + "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5", + "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f" ], "markers": "python_version < '3.11'", - "version": "==1.1.1" + "version": "==1.1.2" }, "execnet": { "hashes": [ - "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5", - "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142" + "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", + "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.9.0" + "markers": "python_version >= '3.7'", + "version": "==2.0.2" }, "fakeredis": { "hashes": [ - "sha256:69a504328a89e5e5f2d05a4236b570fb45244c96997c5002c8c6a0503b95f289", - "sha256:e0fef512b8ec49679d373456aa4698a4103005ecd7ca0b13170a2c1d3af949c5" + "sha256:a99ef6e5642c31e91d36be78809fec3743e2bf7aaa682685b0d65a849fecd148", + "sha256:e304bc7addb2f862c3550cb7db58548418a0fadd4cd78a4de66464c84fbc2195" ], "index": "pypi", - "version": "==2.11.2" + "version": "==2.17.0" }, "flake8": { "hashes": [ @@ -1836,10 +1813,11 @@ }, "flake8-mock": { "hashes": [ - "sha256:2fa775e7589f4e1ad74f35d60953eb20937f5d7355235e54bf852c6837f2bede" + "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867", + "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6" ], "index": "pypi", - "version": "==0.3" + "version": "==0.4" }, "flake8-print": { "hashes": [ @@ -1931,11 +1909,19 @@ }, "jsonschema": { "hashes": [ - "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d", - "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6" + "sha256:971be834317c22daaa9132340a51c01b50910724082c2c1a2ac87eeec153a3fe", + "sha256:fb3642735399fa958c0d2aad7057901554596c63349f4f6b283c493cf692a25d" ], "index": "pypi", - "version": "==4.17.3" + "version": "==4.18.4" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", + "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" + ], + "markers": "python_version >= '3.8'", + "version": "==2023.7.1" }, "lazy-object-proxy": { "hashes": [ @@ -1981,59 +1967,59 @@ }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" - ], - "index": "pypi", - "version": "==2.1.2" + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" + ], + "index": "pypi", + "version": "==2.1.3" }, "mccabe": { "hashes": [ @@ -2044,51 +2030,51 @@ }, "mock": { "hashes": [ - "sha256:06f18d7d65b44428202b145a9a36e99c2ee00d1eb992df0caf881d4664377891", - "sha256:0e0bc5ba78b8db3667ad636d964eb963dc97a59f04c6f6214c5f0e4a8f726c56" + "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", + "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" ], "index": "pypi", - "version": "==5.0.2" + "version": "==5.1.0" }, "moto": { "hashes": [ - "sha256:c3ecc2dda1a7b3a3c46655490bc6a4660b7bb47e31eaed7bbd54adeb01f8471f", - "sha256:df5b52eff70bf125ee03ea72c4e01f2daff243796f984a534c3dee92a0b93522" + "sha256:545afeb4df94dfa730e2d7e87366dc26b4a33c2891f462cbb049f040c80ed1ec", + "sha256:7d3bd748a34641715ba469c761f72fb8ec18f349987c98f5a0f9be85a07a9911" ], "index": "pypi", - "version": "==4.1.8" + "version": "==4.1.14" }, "mypy": { "hashes": [ - "sha256:023fe9e618182ca6317ae89833ba422c411469156b690fde6a315ad10695a521", - "sha256:031fc69c9a7e12bcc5660b74122ed84b3f1c505e762cc4296884096c6d8ee140", - "sha256:2de7babe398cb7a85ac7f1fd5c42f396c215ab3eff731b4d761d68d0f6a80f48", - "sha256:2e93a8a553e0394b26c4ca683923b85a69f7ccdc0139e6acd1354cc884fe0128", - "sha256:390bc685ec209ada4e9d35068ac6988c60160b2b703072d2850457b62499e336", - "sha256:3a2d219775a120581a0ae8ca392b31f238d452729adbcb6892fa89688cb8306a", - "sha256:3efde4af6f2d3ccf58ae825495dbb8d74abd6d176ee686ce2ab19bd025273f41", - "sha256:4a99fe1768925e4a139aace8f3fb66db3576ee1c30b9c0f70f744ead7e329c9f", - "sha256:4b41412df69ec06ab141808d12e0bf2823717b1c363bd77b4c0820feaa37249e", - "sha256:4c8d8c6b80aa4a1689f2a179d31d86ae1367ea4a12855cc13aa3ba24bb36b2d8", - "sha256:4d19f1a239d59f10fdc31263d48b7937c585810288376671eaf75380b074f238", - "sha256:4e4a682b3f2489d218751981639cffc4e281d548f9d517addfd5a2917ac78119", - "sha256:695c45cea7e8abb6f088a34a6034b1d273122e5530aeebb9c09626cea6dca4cb", - "sha256:701189408b460a2ff42b984e6bd45c3f41f0ac9f5f58b8873bbedc511900086d", - "sha256:70894c5345bea98321a2fe84df35f43ee7bb0feec117a71420c60459fc3e1eed", - "sha256:8293a216e902ac12779eb7a08f2bc39ec6c878d7c6025aa59464e0c4c16f7eb9", - "sha256:8d26b513225ffd3eacece727f4387bdce6469192ef029ca9dd469940158bc89e", - "sha256:a197ad3a774f8e74f21e428f0de7f60ad26a8d23437b69638aac2764d1e06a6a", - "sha256:bea55fc25b96c53affab852ad94bf111a3083bc1d8b0c76a61dd101d8a388cf5", - "sha256:c9a084bce1061e55cdc0493a2ad890375af359c766b8ac311ac8120d3a472950", - "sha256:d0e9464a0af6715852267bf29c9553e4555b61f5904a4fc538547a4d67617937", - "sha256:d8e9187bfcd5ffedbe87403195e1fc340189a68463903c39e2b63307c9fa0394", - "sha256:eaeaa0888b7f3ccb7bcd40b50497ca30923dba14f385bde4af78fac713d6d6f6", - "sha256:f46af8d162f3d470d8ffc997aaf7a269996d205f9d746124a179d3abe05ac602", - "sha256:f70a40410d774ae23fcb4afbbeca652905a04de7948eaf0b1789c8d1426b72d1", - "sha256:fe91be1c51c90e2afe6827601ca14353bbf3953f343c2129fa1e247d55fd95ba" - ], - "index": "pypi", - "version": "==1.2.0" + "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042", + "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd", + "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2", + "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01", + "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7", + "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3", + "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816", + "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3", + "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc", + "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4", + "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b", + "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8", + "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c", + "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462", + "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7", + "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc", + "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258", + "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b", + "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9", + "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6", + "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f", + "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1", + "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828", + "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878", + "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f", + "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b" + ], + "index": "pypi", + "version": "==1.4.1" }, "mypy-extensions": { "hashes": [ @@ -2108,11 +2094,11 @@ }, "pathspec": { "hashes": [ - "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687", - "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293" + "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", + "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" ], "markers": "python_version >= '3.7'", - "version": "==0.11.1" + "version": "==0.11.2" }, "pep8": { "hashes": [ @@ -2124,19 +2110,19 @@ }, "platformdirs": { "hashes": [ - "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", - "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" + "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d", + "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d" ], "markers": "python_version >= '3.7'", - "version": "==3.5.1" + "version": "==3.10.0" }, "pluggy": { "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" + "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", + "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" + "markers": "python_version >= '3.7'", + "version": "==1.2.0" }, "pycodestyle": { "hashes": [ @@ -2163,11 +2149,11 @@ }, "pylint": { "hashes": [ - "sha256:761907349e699f8afdcd56c4fe02f3021ab5b3a0fc26d19a9bfdc66c7d0d5cd5", - "sha256:a6cbb4c6e96eab4a3c7de7c6383c512478f58f88d95764507d84c899d656a89a" + "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413", + "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252" ], "index": "pypi", - "version": "==2.17.3" + "version": "==2.17.5" }, "pylint-mccabe": { "hashes": [ @@ -2184,54 +2170,21 @@ "index": "pypi", "version": "==0.2.3" }, - "pyrsistent": { - "hashes": [ - "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8", - "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440", - "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a", - "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c", - "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3", - "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393", - "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9", - "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da", - "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf", - "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64", - "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a", - "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3", - "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98", - "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2", - "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8", - "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf", - "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc", - "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7", - "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28", - "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2", - "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b", - "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a", - "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64", - "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19", - "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1", - "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9", - "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.3" - }, "pytest": { "hashes": [ - "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", - "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" + "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", + "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" ], "index": "pypi", - "version": "==7.3.1" + "version": "==7.4.0" }, "pytest-cov": { "hashes": [ - "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b", - "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470" + "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", + "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" ], "index": "pypi", - "version": "==4.0.0" + "version": "==4.1.0" }, "pytest-flask": { "hashes": [ @@ -2243,11 +2196,11 @@ }, "pytest-mock": { "hashes": [ - "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b", - "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f" + "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39", + "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f" ], "index": "pypi", - "version": "==3.10.0" + "version": "==3.11.1" }, "pytest-sugar": { "hashes": [ @@ -2259,11 +2212,11 @@ }, "pytest-xdist": { "hashes": [ - "sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727", - "sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9" + "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93", + "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2" ], "index": "pypi", - "version": "==3.2.1" + "version": "==3.3.1" }, "python-dateutil": { "hashes": [ @@ -2275,65 +2228,73 @@ }, "pyyaml": { "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "index": "pypi", - "version": "==6.0" + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "version": "==6.0.1" }, "redis": { "hashes": [ - "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2", - "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893" + "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d", + "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c" ], "index": "pypi", - "version": "==4.5.4" + "version": "==4.6.0" + }, + "referencing": { + "hashes": [ + "sha256:47237742e990457f7512c7d27486394a9aadaf876cbfaa4be65b27b4f4d47c6b", + "sha256:c257b08a399b6c2f5a3510a50d28ab5dbc7bbde049bcaf954d43c446f83ab548" + ], + "markers": "python_version >= '3.8'", + "version": "==0.30.0" }, "requests": { "hashes": [ - "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", - "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", - "version": "==2.29.0" + "version": "==2.31.0" }, "responses": { "hashes": [ @@ -2343,6 +2304,109 @@ "index": "pypi", "version": "==0.23.1" }, + "rpds-py": { + "hashes": [ + "sha256:0173c0444bec0a3d7d848eaeca2d8bd32a1b43f3d3fde6617aac3731fa4be05f", + "sha256:01899794b654e616c8625b194ddd1e5b51ef5b60ed61baa7a2d9c2ad7b2a4238", + "sha256:02938432352359805b6da099c9c95c8a0547fe4b274ce8f1a91677401bb9a45f", + "sha256:03421628f0dc10a4119d714a17f646e2837126a25ac7a256bdf7c3943400f67f", + "sha256:03975db5f103997904c37e804e5f340c8fdabbb5883f26ee50a255d664eed58c", + "sha256:0766babfcf941db8607bdaf82569ec38107dbb03c7f0b72604a0b346b6eb3298", + "sha256:07e2c54bef6838fa44c48dfbc8234e8e2466d851124b551fc4e07a1cfeb37260", + "sha256:0836d71ca19071090d524739420a61580f3f894618d10b666cf3d9a1688355b1", + "sha256:095b460e117685867d45548fbd8598a8d9999227e9061ee7f012d9d264e6048d", + "sha256:0e7521f5af0233e89939ad626b15278c71b69dc1dfccaa7b97bd4cdf96536bb7", + "sha256:0f2996fbac8e0b77fd67102becb9229986396e051f33dbceada3debaacc7033f", + "sha256:1054a08e818f8e18910f1bee731583fe8f899b0a0a5044c6e680ceea34f93876", + "sha256:13b602dc3e8dff3063734f02dcf05111e887f301fdda74151a93dbbc249930fe", + "sha256:141acb9d4ccc04e704e5992d35472f78c35af047fa0cfae2923835d153f091be", + "sha256:14c408e9d1a80dcb45c05a5149e5961aadb912fff42ca1dd9b68c0044904eb32", + "sha256:159fba751a1e6b1c69244e23ba6c28f879a8758a3e992ed056d86d74a194a0f3", + "sha256:190ca6f55042ea4649ed19c9093a9be9d63cd8a97880106747d7147f88a49d18", + "sha256:196cb208825a8b9c8fc360dc0f87993b8b260038615230242bf18ec84447c08d", + "sha256:1fcdee18fea97238ed17ab6478c66b2095e4ae7177e35fb71fbe561a27adf620", + "sha256:207f57c402d1f8712618f737356e4b6f35253b6d20a324d9a47cb9f38ee43a6b", + "sha256:24a81c177379300220e907e9b864107614b144f6c2a15ed5c3450e19cf536fae", + "sha256:29cd8bfb2d716366a035913ced99188a79b623a3512292963d84d3e06e63b496", + "sha256:2d8b3b3a2ce0eaa00c5bbbb60b6713e94e7e0becab7b3db6c5c77f979e8ed1f1", + "sha256:35da5cc5cb37c04c4ee03128ad59b8c3941a1e5cd398d78c37f716f32a9b7f67", + "sha256:44659b1f326214950a8204a248ca6199535e73a694be8d3e0e869f820767f12f", + "sha256:47c5f58a8e0c2c920cc7783113df2fc4ff12bf3a411d985012f145e9242a2764", + "sha256:4bd4dc3602370679c2dfb818d9c97b1137d4dd412230cfecd3c66a1bf388a196", + "sha256:4ea6b73c22d8182dff91155af018b11aac9ff7eca085750455c5990cb1cfae6e", + "sha256:50025635ba8b629a86d9d5474e650da304cb46bbb4d18690532dd79341467846", + "sha256:517cbf6e67ae3623c5127206489d69eb2bdb27239a3c3cc559350ef52a3bbf0b", + "sha256:5855c85eb8b8a968a74dc7fb014c9166a05e7e7a8377fb91d78512900aadd13d", + "sha256:5a46859d7f947061b4010e554ccd1791467d1b1759f2dc2ec9055fa239f1bc26", + "sha256:65a0583c43d9f22cb2130c7b110e695fff834fd5e832a776a107197e59a1898e", + "sha256:674c704605092e3ebbbd13687b09c9f78c362a4bc710343efe37a91457123044", + "sha256:682726178138ea45a0766907957b60f3a1bf3acdf212436be9733f28b6c5af3c", + "sha256:686ba516e02db6d6f8c279d1641f7067ebb5dc58b1d0536c4aaebb7bf01cdc5d", + "sha256:6a5d3fbd02efd9cf6a8ffc2f17b53a33542f6b154e88dd7b42ef4a4c0700fdad", + "sha256:6aa8326a4a608e1c28da191edd7c924dff445251b94653988efb059b16577a4d", + "sha256:700375326ed641f3d9d32060a91513ad668bcb7e2cffb18415c399acb25de2ab", + "sha256:71f2f7715935a61fa3e4ae91d91b67e571aeb5cb5d10331ab681256bda2ad920", + "sha256:745f5a43fdd7d6d25a53ab1a99979e7f8ea419dfefebcab0a5a1e9095490ee5e", + "sha256:79f594919d2c1a0cc17d1988a6adaf9a2f000d2e1048f71f298b056b1018e872", + "sha256:7d68dc8acded354c972116f59b5eb2e5864432948e098c19fe6994926d8e15c3", + "sha256:7f67da97f5b9eac838b6980fc6da268622e91f8960e083a34533ca710bec8611", + "sha256:83b32f0940adec65099f3b1c215ef7f1d025d13ff947975a055989cb7fd019a4", + "sha256:876bf9ed62323bc7dcfc261dbc5572c996ef26fe6406b0ff985cbcf460fc8a4c", + "sha256:890ba852c16ace6ed9f90e8670f2c1c178d96510a21b06d2fa12d8783a905193", + "sha256:8b08605d248b974eb02f40bdcd1a35d3924c83a2a5e8f5d0fa5af852c4d960af", + "sha256:8b2eb034c94b0b96d5eddb290b7b5198460e2d5d0c421751713953a9c4e47d10", + "sha256:8b9ec12ad5f0a4625db34db7e0005be2632c1013b253a4a60e8302ad4d462afd", + "sha256:8c8d7594e38cf98d8a7df25b440f684b510cf4627fe038c297a87496d10a174f", + "sha256:8d3335c03100a073883857e91db9f2e0ef8a1cf42dc0369cbb9151c149dbbc1b", + "sha256:8d70e8f14900f2657c249ea4def963bed86a29b81f81f5b76b5a9215680de945", + "sha256:9039a11bca3c41be5a58282ed81ae422fa680409022b996032a43badef2a3752", + "sha256:91378d9f4151adc223d584489591dbb79f78814c0734a7c3bfa9c9e09978121c", + "sha256:9251eb8aa82e6cf88510530b29eef4fac825a2b709baf5b94a6094894f252387", + "sha256:933a7d5cd4b84f959aedeb84f2030f0a01d63ae6cf256629af3081cf3e3426e8", + "sha256:978fa96dbb005d599ec4fd9ed301b1cc45f1a8f7982d4793faf20b404b56677d", + "sha256:987b06d1cdb28f88a42e4fb8a87f094e43f3c435ed8e486533aea0bf2e53d931", + "sha256:99b1c16f732b3a9971406fbfe18468592c5a3529585a45a35adbc1389a529a03", + "sha256:99e7c4bb27ff1aab90dcc3e9d37ee5af0231ed98d99cb6f5250de28889a3d502", + "sha256:9c439fd54b2b9053717cca3de9583be6584b384d88d045f97d409f0ca867d80f", + "sha256:9ea4d00850ef1e917815e59b078ecb338f6a8efda23369677c54a5825dbebb55", + "sha256:9f30d205755566a25f2ae0382944fcae2f350500ae4df4e795efa9e850821d82", + "sha256:a06418fe1155e72e16dddc68bb3780ae44cebb2912fbd8bb6ff9161de56e1798", + "sha256:a0805911caedfe2736935250be5008b261f10a729a303f676d3d5fea6900c96a", + "sha256:a1f044792e1adcea82468a72310c66a7f08728d72a244730d14880cd1dabe36b", + "sha256:a216b26e5af0a8e265d4efd65d3bcec5fba6b26909014effe20cd302fd1138fa", + "sha256:a987578ac5214f18b99d1f2a3851cba5b09f4a689818a106c23dbad0dfeb760f", + "sha256:aad51239bee6bff6823bbbdc8ad85136c6125542bbc609e035ab98ca1e32a192", + "sha256:ab2299e3f92aa5417d5e16bb45bb4586171c1327568f638e8453c9f8d9e0f020", + "sha256:ab6919a09c055c9b092798ce18c6c4adf49d24d4d9e43a92b257e3f2548231e7", + "sha256:b0c43f8ae8f6be1d605b0465671124aa8d6a0e40f1fb81dcea28b7e3d87ca1e1", + "sha256:b1440c291db3f98a914e1afd9d6541e8fc60b4c3aab1a9008d03da4651e67386", + "sha256:b52e7c5ae35b00566d244ffefba0f46bb6bec749a50412acf42b1c3f402e2c90", + "sha256:bf4151acb541b6e895354f6ff9ac06995ad9e4175cbc6d30aaed08856558201f", + "sha256:c27ee01a6c3223025f4badd533bea5e87c988cb0ba2811b690395dfe16088cfe", + "sha256:c545d9d14d47be716495076b659db179206e3fd997769bc01e2d550eeb685596", + "sha256:c5934e2833afeaf36bd1eadb57256239785f5af0220ed8d21c2896ec4d3a765f", + "sha256:c7671d45530fcb6d5e22fd40c97e1e1e01965fc298cbda523bb640f3d923b387", + "sha256:c861a7e4aef15ff91233751619ce3a3d2b9e5877e0fcd76f9ea4f6847183aa16", + "sha256:d25b1c1096ef0447355f7293fbe9ad740f7c47ae032c2884113f8e87660d8f6e", + "sha256:d55777a80f78dd09410bd84ff8c95ee05519f41113b2df90a69622f5540c4f8b", + "sha256:d576c3ef8c7b2d560e301eb33891d1944d965a4d7a2eacb6332eee8a71827db6", + "sha256:dd9da77c6ec1f258387957b754f0df60766ac23ed698b61941ba9acccd3284d1", + "sha256:de0b6eceb46141984671802d412568d22c6bacc9b230174f9e55fc72ef4f57de", + "sha256:e07e5dbf8a83c66783a9fe2d4566968ea8c161199680e8ad38d53e075df5f0d0", + "sha256:e564d2238512c5ef5e9d79338ab77f1cbbda6c2d541ad41b2af445fb200385e3", + "sha256:ed89861ee8c8c47d6beb742a602f912b1bb64f598b1e2f3d758948721d44d468", + "sha256:ef1f08f2a924837e112cba2953e15aacfccbbfcd773b4b9b4723f8f2ddded08e", + "sha256:f411330a6376fb50e5b7a3e66894e4a39e60ca2e17dce258d53768fea06a37bd", + "sha256:f68996a3b3dc9335037f82754f9cdbe3a95db42bde571d8c3be26cc6245f2324", + "sha256:f7fdf55283ad38c33e35e2855565361f4bf0abd02470b8ab28d499c663bc5d7c", + "sha256:f963c6b1218b96db85fc37a9f0851eaf8b9040aa46dec112611697a7023da535", + "sha256:fa2818759aba55df50592ecbc95ebcdc99917fa7b55cc6796235b04193eb3c55", + "sha256:fae5cb554b604b3f9e2c608241b5d8d303e410d7dfb6d397c335f983495ce7f6", + "sha256:fb39aca7a64ad0c9490adfa719dbeeb87d13be137ca189d2564e596f8ba32c07" + ], + "markers": "python_version >= '3.8'", + "version": "==0.9.2" + }, "s3transfer": { "hashes": [ "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", @@ -2392,73 +2456,89 @@ }, "tomlkit": { "hashes": [ - "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171", - "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3" + "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", + "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" ], "markers": "python_version >= '3.7'", - "version": "==0.11.8" + "version": "==0.12.1" + }, + "types-cachetools": { + "hashes": [ + "sha256:595f0342d246c8ba534f5a762cf4c2f60ecb61e8002b8b2277fd5cf791d4e851", + "sha256:f7f8a25bfe306f2e6bc2ad0a2f949d9e72f2d91036d509c36d3810bf728bc6e1" + ], + "index": "pypi", + "version": "==5.3.0.6" }, "types-pyopenssl": { "hashes": [ - "sha256:43e307e8dfb3a7a8208a19874ca060305f460c529d4eaca8a2669ea89499f244", - "sha256:ba803a99440b0c2e9ab4e197084aeefc55bdfe8a580d367b2aa4210810a21240" + "sha256:19536aa3debfbe25a918cf0d898e9f5fbbe6f3594a429da7914bf331deb1b342", + "sha256:6a010dac9ecd42b582d7dd2cc3e9e40486b79b3b64bb2fffba1474ff96af906d" ], - "version": "==23.2.0.0" + "version": "==23.2.0.2" }, "types-python-dateutil": { "hashes": [ - "sha256:355b2cb82b31e556fd18e7b074de7c350c680ab80608f0cc55ba6770d986d67d", - "sha256:fe5b545e678ec13e3ddc83a0eee1545c1b5e2fba4cfc39b276ab6f4e7604a923" + "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b", + "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9" + ], + "index": "pypi", + "version": "==2.8.19.14" + }, + "types-pytz": { + "hashes": [ + "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3", + "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac" ], "index": "pypi", - "version": "==2.8.19.12" + "version": "==2023.3.0.0" }, "types-pyyaml": { "hashes": [ - "sha256:5aed5aa66bd2d2e158f75dda22b059570ede988559f030cf294871d3b647e3e8", - "sha256:c51b1bd6d99ddf0aa2884a7a328810ebf70a4262c292195d3f4f9a0005f9eeb6" + "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b", + "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d" ], "index": "pypi", - "version": "==6.0.12.9" + "version": "==6.0.12.11" }, "types-redis": { "hashes": [ - "sha256:2db530f54facec3149147bfe61d5ac24f5fe4e871823d95a601cd2c1d775d8a0", - "sha256:bf04192f415b2b42ecefd70bb4b91eb0352e48f2716a213e038e35c096a639c2" + "sha256:67c44c14369c33c2a300da2a50b5607c0fc888f7b85eeb7c73e15c78a0f05edd", + "sha256:efdef37dc0c04bf5786195651fd694f8bfdd693eac09ec4af46d90f72652558f" ], "index": "pypi", - "version": "==4.5.4.1" + "version": "==4.6.0.3" }, "types-requests": { "hashes": [ - "sha256:4cf6e323e856c779fbe8815bb977a5bf5d6c5034713e4c17ff2a9a20610f5b27", - "sha256:c86f4a955d943d2457120dbe719df24ef0924e11177164d10a0373cf311d7b4d" + "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a", + "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40" ], "index": "pypi", - "version": "==2.29.0.0" + "version": "==2.31.0.2" }, "types-simplejson": { "hashes": [ - "sha256:38982991785db84c78f9012f5b9a03069ec3be020f468fb8fcd950b23e3a3341", - "sha256:64aff3d151104e0458bfc9d64ffb78efd0157ff740a9f00904a30a5465f7c013" + "sha256:8ba093dc7884f59b3e62aed217144085e675a269debc32678fd80e0b43b2b86f", + "sha256:ebc81f886f89d99d6b80c726518aa2228bc77c26438f18fd81455e4f79f8ee1b" ], "index": "pypi", - "version": "==3.19.0.0" + "version": "==3.19.0.2" }, "types-urllib3": { "hashes": [ - "sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5", - "sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c" + "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f", + "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e" ], - "version": "==1.26.25.13" + "version": "==1.26.25.14" }, "typing-extensions": { "hashes": [ - "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", - "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" + "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", + "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2" ], "markers": "python_version >= '3.7'", - "version": "==4.6.3" + "version": "==4.7.1" }, "urllib3": { "hashes": [ @@ -2470,11 +2550,11 @@ }, "werkzeug": { "hashes": [ - "sha256:1d5a58e0377d1fe39d061a5de4469e414e78ccb1e1e59c0f5ad6fa1c36c52b76", - "sha256:48e5e61472fee0ddee27ebad085614ebedb7af41e88f687aaf881afb723a162f" + "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890", + "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330" ], "markers": "python_version >= '3.8'", - "version": "==2.3.4" + "version": "==2.3.6" }, "wrapt": { "hashes": [ diff --git a/app/oidc/__init__.py b/app/oidc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/oidc/gcp_oidc.py b/app/oidc/gcp_oidc.py new file mode 100644 index 0000000000..18671ac081 --- /dev/null +++ b/app/oidc/gcp_oidc.py @@ -0,0 +1,35 @@ +from cachetools.func import ttl_cache +from google.auth.credentials import Credentials +from google.auth.transport.requests import Request +from google.oauth2.id_token import fetch_id_token_credentials +from structlog import get_logger + +from app.oidc.oidc import OIDCCredentialsService +from app.settings import OIDC_TOKEN_LEEWAY_IN_SECONDS, OIDC_TOKEN_VALIDITY_IN_SECONDS + +TTL = OIDC_TOKEN_VALIDITY_IN_SECONDS - OIDC_TOKEN_LEEWAY_IN_SECONDS + +logger = get_logger() + + +class OIDCCredentialsServiceGCP(OIDCCredentialsService): + @staticmethod + @ttl_cache(maxsize=None, ttl=TTL) + def get_credentials(*, iap_client_id: str) -> Credentials: + """ + The credentials are valid for OIDC_TOKEN_VALIDITY_IN_SECONDS, and we use a ttl cache of this minus a configurable leeway + this is to ensure that even in the edge case where the timers on the cache and the credentials don't quite align, + the OIDC_TOKEN_LEEWAY_IN_SECONDS should be more than enough to cover it and guarantee safety + """ + logger.info("fetching OIDC credentials from GCP", iap_client_id=iap_client_id) + + request = Request() + + credentials = fetch_id_token_credentials( + audience=iap_client_id, request=request + ) + + # Refresh the credential to obtain an ID token. + credentials.refresh(request) + + return credentials diff --git a/app/oidc/local_oidc.py b/app/oidc/local_oidc.py new file mode 100644 index 0000000000..6eb8240433 --- /dev/null +++ b/app/oidc/local_oidc.py @@ -0,0 +1,21 @@ +from functools import lru_cache + +from google.auth.credentials import AnonymousCredentials, Credentials +from structlog import get_logger + +from app.oidc.oidc import OIDCCredentialsService + +logger = get_logger() + + +class OIDCCredentialsServiceLocal(OIDCCredentialsService): + @staticmethod + @lru_cache + def get_credentials(*, iap_client_id: str) -> Credentials: + """ + Anonymous credentials don't expire so can be generated once and cached + """ + logger.info("generating local OIDC credentials", iap_client_id=iap_client_id) + + # Return Credentials which do not provide any authentication or make any requests for tokens + return AnonymousCredentials() diff --git a/app/oidc/oidc.py b/app/oidc/oidc.py new file mode 100644 index 0000000000..3dea808793 --- /dev/null +++ b/app/oidc/oidc.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod + +from google.auth.credentials import Credentials + + +class OIDCCredentialsService(ABC): + @staticmethod + @abstractmethod + def get_credentials(*, iap_client_id: str) -> Credentials: # pragma no cover + ... diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index cd27b22393..711f25cc19 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -10,6 +10,8 @@ from structlog import get_logger from app.keys import KEY_PURPOSE_SDS +from app.oidc.oidc import OIDCCredentialsService +from app.settings import SDS_OAUTH2_CLIENT_ID from app.utilities.request_session import get_retryable_session from app.utilities.supplementary_data_parser import validate_supplementary_data_v1 @@ -64,6 +66,14 @@ def get_supplementary_data_v1( backoff_factor=SUPPLEMENTARY_DATA_REQUEST_BACKOFF_FACTOR, ) + # Type ignore: oidc_credentials_service is a singleton of this application + oidc_credentials_service: OIDCCredentialsService = current_app.eq["oidc_credentials_service"] # type: ignore + # Type ignore: SDS_OAUTH2_CLIENT_ID is an env var which must exist as it is verified in setup.py + credentials = oidc_credentials_service.get_credentials( + iap_client_id=SDS_OAUTH2_CLIENT_ID # type: ignore + ) + credentials.apply(headers=session.headers) + try: response = session.get( constructed_supplementary_data_url, diff --git a/app/settings.py b/app/settings.py index 813e5637b4..55d1f1fa02 100644 --- a/app/settings.py +++ b/app/settings.py @@ -146,6 +146,16 @@ def utcoffset_or_fail(date_value, key): SDS_API_BASE_URL = os.getenv("SDS_API_BASE_URL") +OIDC_TOKEN_BACKEND = os.getenv("OIDC_TOKEN_BACKEND") + +OIDC_TOKEN_VALIDITY_IN_SECONDS = int( + os.getenv("OIDC_TOKEN_VALIDITY_IN_SECONDS", "3600") +) + +OIDC_TOKEN_LEEWAY_IN_SECONDS = int(os.getenv("OIDC_TOKEN_LEEWAY_IN_SECONDS", "300")) + +SDS_OAUTH2_CLIENT_ID = os.getenv("SDS_OAUTH2_CLIENT_ID") + ACCOUNT_SERVICE_BASE_URL = os.getenv( "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" ) diff --git a/app/setup.py b/app/setup.py index b3223e3813..8b86c3fdc6 100644 --- a/app/setup.py +++ b/app/setup.py @@ -27,6 +27,8 @@ from app.helpers import get_span_and_trace from app.jinja_filters import blueprint as filter_blueprint from app.keys import KEY_PURPOSE_AUTHENTICATION, KEY_PURPOSE_SUBMISSION +from app.oidc.gcp_oidc import OIDCCredentialsServiceGCP +from app.oidc.local_oidc import OIDCCredentialsServiceLocal from app.publisher import LogPublisher, PubSubPublisher from app.routes.dump import dump_blueprint from app.routes.errors import errors_blueprint @@ -169,6 +171,8 @@ def before_request(): # pylint: disable=unused-variable setup_task_client(application) + setup_oidc(application) + application.eq["id_generator"] = UserIDGenerator( application.config["EQ_SERVER_SIDE_STORAGE_USER_ID_ITERATIONS"], application.eq["secret_store"].get_secret_by_name( @@ -382,6 +386,25 @@ def setup_task_client(application): raise NotImplementedError("Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND") +def setup_oidc(application): + def sds_client_id_exists(): + if not application.config.get("SDS_OAUTH2_CLIENT_ID"): + raise MissingEnvironmentVariable("Setting SDS_OAUTH2_CLIENT_ID Missing") + + if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): + raise MissingEnvironmentVariable("Setting OIDC_TOKEN_BACKEND Missing") + + if oidc_token_backend == "gcp": + sds_client_id_exists() + application.eq["oidc_credentials_service"] = OIDCCredentialsServiceGCP() + + elif oidc_token_backend == "local": + application.eq["oidc_credentials_service"] = OIDCCredentialsServiceLocal() + + else: + raise NotImplementedError("Unknown OIDC_TOKEN_BACKEND") + + def setup_publisher(application): if application.config["EQ_PUBLISHER_BACKEND"] == "pubsub": application.eq["publisher"] = PubSubPublisher() diff --git a/tests/app/oidc/__init__.py b/tests/app/oidc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/app/oidc/test_gcp_oidc.py b/tests/app/oidc/test_gcp_oidc.py new file mode 100644 index 0000000000..cb269fd4ab --- /dev/null +++ b/tests/app/oidc/test_gcp_oidc.py @@ -0,0 +1,154 @@ +# pylint: disable=redefined-outer-name +import time + +import pytest +import responses +from cachetools.func import ttl_cache +from freezegun import freeze_time +from google.auth.exceptions import RefreshError, TransportError +from google.auth.transport.requests import Request +from mock import Mock, patch + +from app.oidc.gcp_oidc import TTL, OIDCCredentialsServiceGCP + +TEST_SDS_OAUTH2_CLIENT_ID = "TEST_SDS_OAUTH2_CLIENT_ID" +MOCK_TOKEN_URL = "http://mock-url" + + +@pytest.fixture +def oidc_credentials_service(): + oidc_credentials_service = OIDCCredentialsServiceGCP() + yield oidc_credentials_service + + # the get credentials method is static, and other tests are affected by the cache, so ensure it is cleared in the fixture teardown + oidc_credentials_service.get_credentials.cache.clear() + + +@pytest.fixture +def patch_authentication(): + # this fixture allows reaching the _metadata.get call with dummy credentials data, to observe a mocked request + with ( + patch( + "google.auth.compute_engine._metadata.get_service_account_info", + Mock(return_value={"email": "mock-email@gcp.com"}), + ), + patch( + "google.auth.compute_engine._metadata.ping", + Mock(return_value=True), + ), + patch( + "google.auth.compute_engine.credentials.jwt._unverified_decode", + Mock(return_value=(None, {"exp": 1672576200}, None, None)), + ), + patch( + "google.auth._helpers.update_query", + Mock(return_value=MOCK_TOKEN_URL), + ), + ): + yield + + +@patch("app.oidc.gcp_oidc.Request") +@patch("app.oidc.gcp_oidc.fetch_id_token_credentials") +def test_get_credentials(mock_token_fetch, mock_request, oidc_credentials_service): + """ + fetch credentials and check the gcp call is made + """ + oidc_credentials_service.get_credentials(iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID) + mock_token_fetch.assert_called_once_with( + audience=TEST_SDS_OAUTH2_CLIENT_ID, request=mock_request() + ) + mock_token_fetch.return_value.refresh.assert_called_once_with(mock_request()) + + +@patch("app.oidc.gcp_oidc.Request", Mock) +@patch("app.oidc.gcp_oidc.fetch_id_token_credentials", Mock(side_effect=RefreshError)) +def test_get_credentials_failure(oidc_credentials_service): + """ + Check that if the request for credentials fails, the cache does not get updated + """ + with pytest.raises(RefreshError): + oidc_credentials_service.get_credentials( + iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID + ) + + assert not oidc_credentials_service.get_credentials.cache + + +@freeze_time("2023-01-01T12:00:00") +@patch("app.oidc.gcp_oidc.Request", Mock) +@patch("app.oidc.gcp_oidc.fetch_id_token_credentials") +def test_get_credentials_ttl(mock_token_fetch, oidc_credentials_service): + """ + by default, TTLCache uses a cached version of time.monotonic for the timer + which means that mocking time with freezegun doesn't work, as it won't affect the timer + to work around this and test the caching, we can replace the timer + (as per https://github.com/spulec/freezegun/issues/477 ) + """ + # overwrite the timer + oidc_credentials_service.get_credentials = ttl_cache( + maxsize=None, ttl=TTL, timer=time.monotonic + )(oidc_credentials_service.get_credentials.__wrapped__) + + # initial fetch + oidc_credentials_service.get_credentials(iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID) + assert mock_token_fetch.call_count == 1 + assert mock_token_fetch.return_value.refresh.call_count == 1 + + for datetime_to_invoke_at, expected_call_count in [ + # still valid after 30 minutes (valid until 12:55) + ("2023-01-01T12:30:00", 1), + # becomes invalid after 55 minutes (this new call makes it valid until 13:50) + ("2023-01-01T12:55:00", 2), + # this fetch is valid for another 1 minute until 13:50 + ("2023-01-01T13:49:00", 2), + # then at 13:50 it becomes invalid so new call is made + ("2023-01-01T13:50:00", 3), + ]: + # Mock the current time and check the call counter for the credentials service + with freeze_time(datetime_to_invoke_at): + oidc_credentials_service.get_credentials( + iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID + ) + assert mock_token_fetch.call_count == expected_call_count + assert ( + mock_token_fetch.return_value.refresh.call_count == expected_call_count + ) + + +@pytest.mark.usefixtures("patch_authentication") +@freeze_time("2023-01-01T12:00:00") +@responses.activate +def test_get_credentials_with_retry(oidc_credentials_service): + """ + Test that fetching the credentials is implemented with a retry. + If the library changes, this test will fail, and we will need to implement our own. + """ + responses.add(responses.GET, url=MOCK_TOKEN_URL, body=TransportError()) + responses.add(responses.GET, url=MOCK_TOKEN_URL, body=TransportError()) + responses.add(responses.GET, url=MOCK_TOKEN_URL, json={}, status=200) + + # use a side effect of Request so that the code executes as normal but the call count can be tracked + tracked_request = Mock(side_effect=Request()) + + with patch("app.oidc.gcp_oidc.Request", Mock(return_value=tracked_request)): + credentials = oidc_credentials_service.get_credentials( + iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID + ) + assert credentials.valid + # the request should have 3 attempts, the two transport failures and the success + assert tracked_request.call_count == 3 + + +@pytest.mark.usefixtures("patch_authentication") +def test_get_credentials_transport_failure(oidc_credentials_service): + """ + If the request repeatedly fails with transport error, we get a refresh error + don't assert the error message as it could change, but check the error is raised + """ + failing_request = Mock(side_effect=TransportError) + with patch("app.oidc.gcp_oidc.Request", Mock(return_value=failing_request)): + with pytest.raises(RefreshError): + oidc_credentials_service.get_credentials( + iap_client_id=TEST_SDS_OAUTH2_CLIENT_ID + ) diff --git a/tests/app/services/test_request_supplementary_data.py b/tests/app/services/test_request_supplementary_data.py index fe8f9d9d03..98546735db 100644 --- a/tests/app/services/test_request_supplementary_data.py +++ b/tests/app/services/test_request_supplementary_data.py @@ -2,9 +2,11 @@ import responses from flask import Flask, current_app from marshmallow import ValidationError +from mock import Mock from requests import RequestException from sdc.crypto.key_store import KeyStore +from app.oidc.gcp_oidc import OIDCCredentialsServiceGCP from app.services.supplementary_data import ( SUPPLEMENTARY_DATA_REQUEST_MAX_RETRIES, InvalidSupplementaryData, @@ -220,3 +222,33 @@ def test_get_supplementary_data_v1_raises_missing_supplementary_data_key_error_w identifier="12346789012A", survey_id="123", ) + + +@responses.activate +def test_get_supplementary_data_v1_with_gcp_authentication( + app: Flask, mocker, encrypted_mock_supplementary_data_payload +): + with app.app_context(): + current_app.config["SDS_API_BASE_URL"] = TEST_SDS_URL + + mock_oidc_service = Mock(spec=OIDCCredentialsServiceGCP) + mocker.patch.dict( + "app.services.supplementary_data.current_app.eq", + {"oidc_credentials_service": mock_oidc_service}, + ) + + responses.add( + responses.GET, + f"{TEST_SDS_URL}/v1/unit_data", + json=encrypted_mock_supplementary_data_payload, + status=200, + ) + + get_supplementary_data_v1( + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + mock_oidc_service.get_credentials.assert_called_once_with( + iap_client_id=current_app.config["SDS_OAUTH2_CLIENT_ID"] + ) diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index 3f233a265d..ba046be6f2 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -9,8 +9,10 @@ from mock import patch from app.cloud_tasks import CloudTaskPublisher +from app.oidc.gcp_oidc import OIDCCredentialsServiceGCP +from app.oidc.local_oidc import OIDCCredentialsServiceLocal from app.publisher import LogPublisher, PubSubPublisher -from app.setup import create_app +from app.setup import MissingEnvironmentVariable, create_app from app.storage.datastore import Datastore from app.storage.dynamodb import Dynamodb from app.submitter.submitter import ( @@ -388,3 +390,62 @@ def test_conditional_expected_secret(self, mock_safe_load): assert "Missing Secret [ADDRESS_LOOKUP_API_AUTH_TOKEN_SECRET]" in str( ex.exception ) + + def test_setup_oidc_service_gcp(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "gcp" + self._setting_overrides["SDS_OAUTH2_CLIENT_ID"] = "1234567890" + + # When + application = create_app(self._setting_overrides) + + # Then + assert isinstance( + application.eq["oidc_credentials_service"], OIDCCredentialsServiceGCP + ) + + def test_setup_oidc_service_local(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "local" + + # When + application = create_app(self._setting_overrides) + + # Then + assert isinstance( + application.eq["oidc_credentials_service"], OIDCCredentialsServiceLocal + ) + + def test_oidc_backend_invalid_raises_exception(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "invalid" + + # When + with self.assertRaises(NotImplementedError) as ex: + create_app(self._setting_overrides) + + # Then + assert "Unknown OIDC_TOKEN_BACKEND" in str(ex.exception) + + def test_oidc_backend_missing_raises_exception(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "" + + # When + with self.assertRaises(MissingEnvironmentVariable) as ex: + create_app(self._setting_overrides) + + # Then + assert "Setting OIDC_TOKEN_BACKEND Missing" in str(ex.exception) + + def test_sds_oauth_2_client_id_missing_raises_exception(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "gcp" + self._setting_overrides["SDS_OAUTH2_CLIENT_ID"] = "" + + # When + with self.assertRaises(MissingEnvironmentVariable) as ex: + create_app(self._setting_overrides) + + # Then + assert "Setting SDS_OAUTH2_CLIENT_ID Missing" in str(ex.exception) From fe54b959d8946f45abd82b26a92e42d1597cf484 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:12:44 +0100 Subject: [PATCH 264/567] Schemas v3.67.0 (#1185) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 82c973e8c9..0217daedd6 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.66.1 +v3.67.0 From 1f87371c2b853935d4278eb034dd08ed5c151f0e Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:08:31 +0100 Subject: [PATCH 265/567] Update WebdriverIO to use Internal Chrome Driver (#1180) --- .github/workflows/pull_request.yml | 6 +- README.md | 2 +- package.json | 6 +- tests/functional/wdio.conf.js | 5 +- yarn.lock | 329 +++++++++++++++-------------- 5 files changed, 177 insertions(+), 171 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 4125b82418..84c2d72fb8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -136,12 +136,10 @@ jobs: runs-on: ubuntu-22.04 env: EQ_RUN_FUNCTIONAL_TESTS_HEADLESS: True + # :TODO: Revisit & update when 2 instances can be used without adverse effects + EQ_FUNCTIONAL_TEST_MAX_INSTANCES: 1 steps: - uses: actions/checkout@v3 - - name: Update to latest stable Chrome - run: | - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb - sudo apt install ./google-chrome-stable_current_amd64.deb - uses: actions/setup-node@v3 with: node-version: "18.14.0" diff --git a/README.md b/README.md index 6539948d3c..d7f111bc4f 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ Available commands: ### Development with functional tests -The tests are written using [WebdriverIO](https://webdriver.io/docs), [Chai](https://www.chaijs.com/), and [Mocha](https://mochajs.org/) +The tests are written using [WebdriverIO](https://webdriver.io/docs/gettingstarted), [Chai](https://www.chaijs.com/), and [Mocha](https://mochajs.org/) ### Functional test options diff --git a/package.json b/package.json index ef3ee1004f..a51657cbd3 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "format": "yarn format:tests && yarn format:test-schemas", "format:tests": "prettier \"tests/functional/**/*.js\" --write && eslint --fix \"tests/functional/**/*.js\"", "format:test-schemas": "prettier \"schemas/test/*/*.json\" --write && eslint --fix \"schemas/test/*/*.json\"", - "wdio": "wdio run ./wdio.conf.js" + "wdio": "wdio run ./tests/functional/wdio.conf.js" }, "devDependencies": { "@babel/core": "^7.22.9", @@ -33,7 +33,6 @@ "@wdio/mocha-framework": "^8.14.0", "@wdio/spec-reporter": "^8.14.0", "chai": "^4.3.7", - "chromedriver": "^115.0.0", "eslint": "^8.46.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^17.1.0", @@ -52,8 +51,7 @@ "prettier": "^3.0.1", "typescript": "^5.1.6", "uuid": "^9.0.0", - "wdio-chromedriver-service": "^8.1.1", - "webdriverio": "8.14.3" + "webdriverio": "^8.15.0" }, "prettier": {} } diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index eeb2c27b7f..bbaa35304c 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -45,7 +45,7 @@ export const config = { // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // - maxInstances: 2, + maxInstances: parseInt(process.env.EQ_FUNCTIONAL_TEST_MAX_INSTANCES || 2), // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: // https://docs.saucelabs.com/reference/platforms-configurator @@ -53,6 +53,7 @@ export const config = { capabilities: [ { browserName: "chrome", + browserVersion: "stable", // If outputDir is provided WebdriverIO can capture driver session logs // it is possible to configure which logTypes to include/exclude. // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs @@ -116,7 +117,7 @@ export const config = { // Services take over a specific job you don't want to take care of. They enhance // your test setup with almost no effort. Unlike plugins, they don't add new // commands. Instead, they hook themselves up into the test process. - services: ["chromedriver"], + services: [], // Framework you want to run your specs with. // The following are supported: Mocha, Jasmine, and Cucumber diff --git a/yarn.lock b/yarn.lock index d6a9cb3247..e4dd914e58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1159,6 +1159,19 @@ unbzip2-stream "1.4.3" yargs "17.7.1" +"@puppeteer/browsers@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.6.0.tgz#d52413a7039e40a5ef72fb13fb6505fd87ce842e" + integrity sha512-R2ib8j329427jtKB/qlz0MJbwfJE/6I8ocJLiajsRqJ2PPI8DbjiNzC3lQZeISXEcjOBVhbG2RafN8SlHdcT+A== + dependencies: + debug "4.3.4" + extract-zip "2.0.1" + progress "2.0.3" + proxy-agent "6.3.0" + tar-fs "3.0.4" + unbzip2-stream "1.4.3" + yargs "17.7.1" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1176,11 +1189,6 @@ dependencies: defer-to-connect "^2.0.1" -"@testim/chrome-version@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.1.3.tgz#fbb68696899d7b8c1b9b891eded9c04fe2cd5529" - integrity sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A== - "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" @@ -1309,6 +1317,20 @@ import-meta-resolve "^3.0.0" read-pkg-up "^10.0.0" +"@wdio/config@8.15.4": + version "8.15.4" + resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.15.4.tgz#850bb69c1d5e9032efb75b220fe8060cbc55012f" + integrity sha512-zCiztDJUVELPEJm6p5jD0ze3noyhABR6I7nSJiiT5RZgYbbQ5lhE7tC38VKfiSL9HgDFoItH0wA3uMq0AYDG+g== + dependencies: + "@wdio/logger" "8.11.0" + "@wdio/types" "8.15.0" + "@wdio/utils" "8.15.4" + decamelize "^6.0.0" + deepmerge-ts "^5.0.0" + glob "^10.2.2" + import-meta-resolve "^3.0.0" + read-pkg-up "^10.0.0" + "@wdio/globals@8.14.3", "@wdio/globals@^8.13.1": version "8.14.3" resolved "https://registry.yarnpkg.com/@wdio/globals/-/globals-8.14.3.tgz#1d5be730f7d36297c2abaa37416b6681bb84bb1c" @@ -1331,7 +1353,7 @@ split2 "^4.1.0" stream-buffers "^3.0.2" -"@wdio/logger@8.11.0", "@wdio/logger@^8.1.0", "@wdio/logger@^8.11.0": +"@wdio/logger@8.11.0", "@wdio/logger@^8.11.0": version "8.11.0" resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-8.11.0.tgz#df28cb65d7b9e308a0cd1445a99adc8b5730174c" integrity sha512-IsuKSaYi7NKEdgA57h8muzlN/MVp1dQG+V4C//7g4m03YJUnNQLvDhJzLjdeNTfvZy61U7foQSyt+3ktNzZkXA== @@ -1358,6 +1380,11 @@ resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-8.11.0.tgz#78f46d416e68c32effbd60cda1fb317db2c02067" integrity sha512-eXTMYt/XoaX53H/Q2qmsn1uWthIC5aSTGtX9YyXD/AkagG2hXeX3lLmzNWBaSIvKR+vWXRYbg3Y/7IvL2s25Wg== +"@wdio/protocols@8.14.6": + version "8.14.6" + resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-8.14.6.tgz#357db1b304244039e8befb036cdf891d826d69fd" + integrity sha512-KM2taEMUDEt1of0Na/6kIv/aNzX0pmr0etpKRci4QrWPQ1O11dXxWjkatFILQz6qOVKvQ0v+vkTPQRUllmH+uQ== + "@wdio/repl@8.10.1": version "8.10.1" resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-8.10.1.tgz#4f1858335d510b3a73432be163ef303aa79db362" @@ -1412,6 +1439,13 @@ dependencies: "@types/node" "^20.1.0" +"@wdio/types@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.15.0.tgz#f678c831957a2a05a3bc771df1702eae88a8e07c" + integrity sha512-W37Hogp6fTHg+wAdfPdpcMe2gm7LgL9eacSIuPI54CJ+fMrVlSB1I7x+RM6rMk2mccBXAmKL/cKe7y4YOX1PQA== + dependencies: + "@types/node" "^20.1.0" + "@wdio/utils@8.14.0": version "8.14.0" resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.14.0.tgz#70af46c28384e5575648edf8ddf61666add5160c" @@ -1421,6 +1455,25 @@ "@wdio/types" "8.14.0" import-meta-resolve "^3.0.0" +"@wdio/utils@8.15.4": + version "8.15.4" + resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.15.4.tgz#4c7789e628f599adba988c7fcd4c535cc80e27ef" + integrity sha512-dYiiSmIR0drNoTDy6zQZanBAG/69PiBRFQsZiF56E+IJ9ikt0Oc88btNBwQP8jOI96q2qNwDlc46Lihei8xPxQ== + dependencies: + "@puppeteer/browsers" "^1.6.0" + "@wdio/logger" "8.11.0" + "@wdio/types" "8.15.0" + chrome-launcher "^1.0.0" + decamelize "^6.0.0" + deepmerge-ts "^5.1.0" + edgedriver "^5.3.3" + geckodriver "^4.2.0" + get-port "^7.0.0" + got "^13.0.0" + import-meta-resolve "^3.0.0" + safaridriver "^0.1.0" + wait-port "^1.0.4" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1431,13 +1484,6 @@ acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" @@ -1667,25 +1713,11 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - b4a@^1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" @@ -2019,19 +2051,6 @@ chrome-launcher@^1.0.0: is-wsl "^2.2.0" lighthouse-logger "^2.0.1" -chromedriver@^115.0.0: - version "115.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-115.0.0.tgz#2d50dd1d12d553c1c6cad22d329dc5cf63487ada" - integrity sha512-mkPL+sXMLMUenoXCiKREw+cBl3ibfhiWxkiv9ByIPpqtrrInCt9zKdOolAsbmN/ndlH51WtT5ukUKbeRdrpikg== - dependencies: - "@testim/chrome-version" "^1.1.3" - axios "^1.4.0" - compare-versions "^6.0.0" - extract-zip "^2.0.1" - https-proxy-agent "^5.0.1" - proxy-from-env "^1.1.0" - tcp-port-used "^1.0.1" - chromium-bidi@0.4.16: version "0.4.16" resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.16.tgz#8a67bfdf6bb8804efc22765a82859d20724b46ab" @@ -2143,13 +2162,6 @@ colorette@^2.0.19: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -2165,11 +2177,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compare-versions@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.0.0.tgz#a3edb527e4487bfab9a8b62ffe70cebc9b87675b" - integrity sha512-s2MzYxfRsE9f/ow8hjn7ysa7pod1xhHdQMsgiJtKx6XSNf4x2N1KG4fjrkUmXcP/e9Y2ZX4zB6sHIso0Lm6evQ== - compress-commons@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" @@ -2266,13 +2273,6 @@ debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2350,11 +2350,6 @@ degenerator@^5.0.0: escodegen "^2.1.0" esprima "^4.0.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -2370,6 +2365,11 @@ devtools-protocol@^0.0.1170846: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1170846.tgz#e5822c62c56a8aff7c937bb04e69823f4235fc42" integrity sha512-GFZiHgvL4JW7+8hIMQgwYNUaIRRCsqfXd11ZbOTdu2VzDeu0Le4l1c3u4FFRWCSvMg82OFip9k/sYyz4M/PJIA== +devtools-protocol@^0.0.1182435: + version "0.0.1182435" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1182435.tgz#68155464f987d66390fae28b02782a1b9c153a0c" + integrity sha512-EmlkWb62wSbQNE1gRZZsi4KZYRaF5Skpp183LhRU7+sadKR06O1dHCjZmFSEG6Kv7P6S/UYLxcY3NlYwqKM99w== + diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -2441,6 +2441,19 @@ edgedriver@^5.3.2: unzipper "^0.10.14" which "^3.0.1" +edgedriver@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/edgedriver/-/edgedriver-5.3.3.tgz#d26a207305cd42ee9069b976c08132ab0780d8bb" + integrity sha512-Gfb0gzBhpiXaVqLsIkua3RItwfiEGHVt9lXYG4bySoSluDtqIvwFBnMAEHKBS+j16ebEJSQh475e20X2lfzgjg== + dependencies: + "@wdio/logger" "^8.11.0" + camaro "^6.2.0" + decamelize "^6.0.0" + edge-paths "^3.0.5" + node-fetch "^3.3.2" + unzipper "^0.10.14" + which "^3.0.1" + ejs@^3.1.9: version "3.1.9" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" @@ -2860,7 +2873,7 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -extract-zip@2.0.1, extract-zip@^2.0.1: +extract-zip@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -3005,11 +3018,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -3030,15 +3038,6 @@ form-data-encoder@^2.1.2: resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - formdata-polyfill@^4.0.10: version "4.0.10" resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" @@ -3051,15 +3050,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@^11.1.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -3130,6 +3120,20 @@ geckodriver@^4.1.3: unzipper "^0.10.14" which "^3.0.1" +geckodriver@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.2.0.tgz#a385370011ae29fa8c68351b01d054a92b92d945" + integrity sha512-I2BlybeMFMzpxHRrh8X4VwP4ys74JHszyUgfezOrbTR7PEybFneDcuEjKIQxKV6vFPmsdwhwF1x8AshdQo56xA== + dependencies: + "@wdio/logger" "^8.11.0" + decamelize "^6.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + node-fetch "^3.3.1" + tar-fs "^3.0.4" + unzipper "^0.10.14" + which "^3.0.1" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3303,6 +3307,23 @@ gopd@^1.0.1: p-cancelable "^3.0.0" responselike "^3.0.0" +got@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -3423,14 +3444,6 @@ http2-wrapper@^2.1.10: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" @@ -3532,11 +3545,6 @@ internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" -ip-regex@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - ip@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" @@ -3724,11 +3732,6 @@ is-unicode-supported@^1.2.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== -is-url@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -3748,15 +3751,6 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -is2@^2.0.6: - version "2.0.9" - resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.9.tgz#ff63b441f90de343fa8fac2125ee170da8e8240d" - integrity sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g== - dependencies: - deep-is "^0.1.3" - ip-regex "^4.1.0" - is-url "^1.2.4" - isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -3921,15 +3915,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - jsrsasign@^10.8.6: version "10.8.6" resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.8.6.tgz#ebf7f3c812c6517af84f0d8a10115e0dbfabe145" @@ -4239,18 +4224,6 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -4425,7 +4398,7 @@ node-fetch@^2.6.12: dependencies: whatwg-url "^5.0.0" -node-fetch@^3.3.1: +node-fetch@^3.3.1, node-fetch@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== @@ -5275,6 +5248,13 @@ semver@^7.0.0, semver@^7.3.5, semver@^7.5.3: dependencies: lru-cache "^6.0.0" +serialize-error@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-11.0.1.tgz#7cfa2b54f7aca3e4cbfc0137259d94d93793f813" + integrity sha512-B5yw3/Lg+Daspbs0f+iO3Qim0+lALnaLS8aZUAy8Y0tO92tkOoMEuwtKo4jpZ5XO16CTwMi4tYN8cZQI3QF2Qw== + dependencies: + type-fest "^2.12.2" + serialize-error@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" @@ -5626,14 +5606,6 @@ tar-stream@^3.1.5: fast-fifo "^1.2.0" streamx "^2.15.0" -tcp-port-used@^1.0.1, tcp-port-used@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" - integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== - dependencies: - debug "4.3.1" - is2 "^2.0.6" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -5710,6 +5682,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^2.12.2: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + type-fest@^3.12.0, type-fest@^3.8.0: version "3.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" @@ -5805,11 +5782,6 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - unzipper@^0.10.14: version "0.10.14" resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" @@ -5906,16 +5878,6 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -wdio-chromedriver-service@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/wdio-chromedriver-service/-/wdio-chromedriver-service-8.1.1.tgz#bdd3776b9a6ccfcec30ec425f906e0fb1c41f966" - integrity sha512-pN3GiOkTIMnalfq4PJAHdX95pDp1orHnTY8W1fIbd6ok81ba97UjerTgS7lUDRUh1p0MAm35Ww0uc0/9wzB7SA== - dependencies: - "@wdio/logger" "^8.1.0" - fs-extra "^11.1.0" - split2 "^4.1.0" - tcp-port-used "^1.0.2" - web-streams-polyfill@^3.0.3: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" @@ -5946,6 +5908,23 @@ webdriver@8.14.3: wait-port "^1.0.4" ws "^8.8.0" +webdriver@8.15.4: + version "8.15.4" + resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.15.4.tgz#2cd010229c0021955ffb31243192ab67d4f8b844" + integrity sha512-9fbha7DJeMX7amb/TYtSjpQ2AAsUa7+auI8Riwu0vuP1tm5zEqQvm+xl25TPNiPtmwPHxfn4aZlQvJ589NnWkw== + dependencies: + "@types/node" "^20.1.0" + "@types/ws" "^8.5.3" + "@wdio/config" "8.15.4" + "@wdio/logger" "8.11.0" + "@wdio/protocols" "8.14.6" + "@wdio/types" "8.15.0" + "@wdio/utils" "8.15.4" + deepmerge-ts "^5.1.0" + got "^ 12.6.1" + ky "^0.33.0" + ws "^8.8.0" + webdriverio@8.14.3, webdriverio@^8.13.1: version "8.14.3" resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.14.3.tgz#490cdd505b6cf57a50d6f67841b8295d6f25c94f" @@ -5976,6 +5955,36 @@ webdriverio@8.14.3, webdriverio@^8.13.1: serialize-error "^8.0.0" webdriver "8.14.3" +webdriverio@^8.15.0: + version "8.15.4" + resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.15.4.tgz#b299c4cc3aa4c1fbc1b56582c1c002e3b9566953" + integrity sha512-UkZI2ampTsBzTC+mDu8kZwUd7hKVML/zNhFk2EUyE9HaDXY6GHuVkzT2vU++3IXHbMUhRZTSy7IRaMlG8EAmNA== + dependencies: + "@types/node" "^20.1.0" + "@wdio/config" "8.15.4" + "@wdio/logger" "8.11.0" + "@wdio/protocols" "8.14.6" + "@wdio/repl" "8.10.1" + "@wdio/types" "8.15.0" + "@wdio/utils" "8.15.4" + archiver "^5.0.0" + aria-query "^5.0.0" + css-shorthand-properties "^1.1.1" + css-value "^0.0.1" + devtools-protocol "^0.0.1182435" + grapheme-splitter "^1.0.2" + import-meta-resolve "^3.0.0" + is-plain-obj "^4.1.0" + lodash.clonedeep "^4.5.0" + lodash.zip "^4.2.0" + minimatch "^9.0.0" + puppeteer-core "^20.9.0" + query-selector-shadow-dom "^1.0.0" + resq "^1.9.1" + rgb2hex "0.2.5" + serialize-error "^11.0.1" + webdriver "8.15.4" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From 5267ca34585b864eae3d86eae7cea2e40c627092 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Fri, 18 Aug 2023 13:27:38 +0100 Subject: [PATCH 266/567] Dockerise mock sds (#1187) * dockerise mock sds * use localhost for dev config * Add base url to runner env variables too --- .development.env | 2 +- Makefile | 1 + README.md | 10 +- docker-compose-dev-linux.yml | 9 ++ docker-compose-dev-mac.yml | 19 ++++ docker-compose.yml | 92 ++++++++++--------- .../supplementary_data.spec.js | 3 +- 7 files changed, 90 insertions(+), 46 deletions(-) diff --git a/.development.env b/.development.env index 54a7cf6ba5..80623dfb5e 100644 --- a/.development.env +++ b/.development.env @@ -28,4 +28,4 @@ ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log SDS_API_BASE_URL=http://localhost:5003 -OIDC_TOKEN_BACKEND=local \ No newline at end of file +OIDC_TOKEN_BACKEND=local diff --git a/Makefile b/Makefile index 3f522ef5e2..670c25ee31 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,7 @@ run-uwsgi-async: link-development-env dev-compose-up: docker-compose -f docker-compose-dev-mac.yml pull eq-questionnaire-launcher + docker-compose -f docker-compose-dev-mac.yml pull sds docker-compose -f docker-compose-dev-mac.yml up -d dev-compose-up-linux: diff --git a/README.md b/README.md index d7f111bc4f..8cd514bfe7 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ make run ### Supporting services -Runner requires three supporting services - a questionnaire launcher, a storage backend, and a cache. +Runner requires four supporting services - a questionnaire launcher, a storage backend, a cache and the supplementary data service. #### Run supporting services with Docker @@ -112,7 +112,13 @@ make dev-compose-up-linux ##### [Questionnaire launcher](https://github.com/ONSDigital/eq-questionnaire-launcher) ``` shell -docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest +docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 -e SDS_API_BASE_URL=http://docker.for.mac.host.internal:5003 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest +``` + +##### [Mock Supplementary data service](https://github.com/ONSDigital/eq-runner-mock-sds) + +``` shell +docker run -it -p 5003:5003 onsdigital/eq-runner-mock-sds:latest ``` ##### Storage backends diff --git a/docker-compose-dev-linux.yml b/docker-compose-dev-linux.yml index 7c5fbe10ca..00e6b81d18 100644 --- a/docker-compose-dev-linux.yml +++ b/docker-compose-dev-linux.yml @@ -20,12 +20,21 @@ services: environment: SURVEY_RUNNER_URL: http://localhost:5000 SURVEY_RUNNER_SCHEMA_URL: http://host.docker.internal:5000 + SDS_API_BASE_URL: http://host.docker.internal:5003 networks: - eq-env restart: always ports: - "8000:8000" + sds: + image: onsdigital/eq-runner-mock-sds:latest + networks: + - eq-env + restart: always + ports: + - "5003:5003" + networks: eq-env: driver: bridge diff --git a/docker-compose-dev-mac.yml b/docker-compose-dev-mac.yml index f36a807109..536df1aee3 100644 --- a/docker-compose-dev-mac.yml +++ b/docker-compose-dev-mac.yml @@ -3,11 +3,15 @@ version: "3" services: datastore: image: knarz/datastore-emulator + networks: + - eq-env ports: - "8432:8432" redis: image: redis:4 + networks: + - eq-env ports: - "6379:6379" @@ -16,6 +20,21 @@ services: environment: SURVEY_RUNNER_URL: http://localhost:5000 SURVEY_RUNNER_SCHEMA_URL: http://docker.for.mac.host.internal:5000 + SDS_API_BASE_URL: http://docker.for.mac.host.internal:5003 + networks: + - eq-env restart: always ports: - "8000:8000" + + sds: + image: "onsdigital/eq-runner-mock-sds:latest" + networks: + - eq-env + restart: always + ports: + - "5003:5003" + +networks: + eq-env: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index 0b2f70a8d4..a74ade875e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,47 +2,57 @@ version: '3' services: - datastore: - image: knarz/datastore-emulator - networks: - - eq-env - - redis: - image: redis:4 - networks: - - eq-env - - eq-questionnaire-runner: - image: onsdigital/eq-questionnaire-runner:latest - build: ./ - env_file: - - ${RUNNER_ENV_FILE} - environment: - DATASTORE_EMULATOR_HOST: datastore:8432 - EQ_REDIS_HOST: "redis" - restart: always - depends_on: - - datastore - - redis - networks: - - eq-env - ports: - - "5000:5000" - - eq-questionnaire-launcher: - image: onsdigital/eq-questionnaire-launcher:latest - environment: - SURVEY_RUNNER_URL: http://localhost:5000 - SURVEY_RUNNER_SCHEMA_URL: http://eq-questionnaire-runner:5000 - restart: always - depends_on: - - eq-questionnaire-runner - networks: - - eq-env - ports: - - "8000:8000" + datastore: + image: knarz/datastore-emulator + networks: + - eq-env + + redis: + image: redis:4 + networks: + - eq-env + + eq-questionnaire-runner: + image: onsdigital/eq-questionnaire-runner:latest + build: ./ + env_file: + - ${RUNNER_ENV_FILE} + environment: + DATASTORE_EMULATOR_HOST: datastore:8432 + EQ_REDIS_HOST: "redis" + SDS_API_BASE_URL: http://sds:5003 + restart: always + depends_on: + - datastore + - redis + networks: + - eq-env + ports: + - "5000:5000" + + eq-questionnaire-launcher: + image: onsdigital/eq-questionnaire-launcher:latest + environment: + SURVEY_RUNNER_URL: http://localhost:5000 + SURVEY_RUNNER_SCHEMA_URL: http://eq-questionnaire-runner:5000 + SDS_API_BASE_URL: http://sds:5003 + restart: always + depends_on: + - eq-questionnaire-runner + networks: + - eq-env + ports: + - "8000:8000" + + sds: + image: onsdigital/eq-runner-mock-sds:latest + networks: + - eq-env + restart: always + ports: + - "5003:5003" networks: - eq-env: - driver: bridge + eq-env: + driver: bridge diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 6e8bac8a5c..0295c0e321 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -30,8 +30,7 @@ import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_da import { assertSummaryItems, assertSummaryTitles, assertSummaryValues } from "../../../helpers"; import { getRandomString } from "../../../jwt_helper"; -// :TODO: this test currently only runs locally, remove the .skip once the mock endpoint is running in a container -describe.skip("Using supplementary data", () => { +describe("Using supplementary data", () => { const responseId = getRandomString(16); const summaryItems = ".ons-summary__item--text"; const summaryValues = ".ons-summary__values"; From d2dda0a51b49090c563de4d5783094551cd041f6 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Mon, 21 Aug 2023 17:44:52 +0100 Subject: [PATCH 267/567] Grand Calculated Summary with repeating answers (#1186) * GCS repeating answers * Move check for section for performance --- app/questionnaire/router.py | 42 +- app/views/handlers/list_repeating_question.py | 19 +- ..._calculated_summary_multiple_sections.json | 348 ----- ..._calculated_summary_repeating_answers.json | 1170 +++++++++++++++++ tests/app/questionnaire/conftest.py | 71 + tests/app/questionnaire/test_router.py | 41 +- ...lculated_summary_multiple_sections.spec.js | 154 --- ...lculated_summary_repeating_answers.spec.js | 457 +++++++ ..._questionnaire_grand_calculated_summary.py | 9 +- 9 files changed, 1776 insertions(+), 535 deletions(-) delete mode 100644 schemas/test/en/test_grand_calculated_summary_multiple_sections.json create mode 100644 schemas/test/en/test_grand_calculated_summary_repeating_answers.json delete mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js create mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 0317baf869..0170131fc7 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -78,7 +78,7 @@ def get_last_location_in_questionnaire_url(self) -> str | None: routing_path = self.routing_path(*section_key) return self.get_last_location_in_section(routing_path).url() - def is_list_item_in_list_store(self, list_item_id: str, list_name: str) -> bool: + def _is_list_item_in_list_store(self, list_item_id: str, list_name: str) -> bool: return list_item_id in self._list_store[list_name] def can_access_location( @@ -94,7 +94,7 @@ def can_access_location( if ( location.list_item_id and location.list_name - and not self.is_list_item_in_list_store( + and not self._is_list_item_in_list_store( location.list_item_id, location.list_name ) ): @@ -143,10 +143,10 @@ def get_next_location_url( ) ) - if return_to_url := self._get_return_to_location_url( - location, - return_to, - routing_path, + if return_to_url := self.get_return_to_location_url( + location=location, + return_to=return_to, + routing_path=routing_path, is_for_previous=False, is_section_complete=is_section_complete, return_to_answer_id=return_to_answer_id, @@ -164,7 +164,7 @@ def get_next_location_url( next_location: Location = self._get_first_incomplete_location_in_section(routing_path) # type: ignore return next_location.url() - return self.get_next_block_url( + return self._get_next_block_url( location, routing_path, return_to=return_to, @@ -192,10 +192,10 @@ def get_previous_location_url( Returns the previous 'location' to visit given a set of user answers or returns to the summary if the `return_to` var is set and the section is complete. """ - if return_to_url := self._get_return_to_location_url( - location, - return_to, - routing_path, + if return_to_url := self.get_return_to_location_url( + location=location, + return_to=return_to, + routing_path=routing_path, is_for_previous=True, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, @@ -230,8 +230,9 @@ def get_previous_location_url( return None - def _get_return_to_location_url( + def get_return_to_location_url( self, + *, location: LocationType, return_to: str | None, routing_path: RoutingPath, @@ -309,9 +310,12 @@ def _get_return_to_for_grand_calculated_summary( ) if grand_calculated_summary_section != location.section_id: # the grand calculated summary is in a different section which will have a different routing path - # but don't go to it unless the current section is complete - if not self._progress_store.is_section_or_repeating_blocks_progress_complete( - location.section_id + # but don't go to it unless the section is enabled and the current section is complete + if ( + not self._progress_store.is_section_or_repeating_blocks_progress_complete( + location.section_id + ) + or grand_calculated_summary_section not in self.enabled_section_ids ): return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, @@ -524,7 +528,7 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: return allowable_path - def get_enabled_section_keys( + def _get_enabled_section_keys( self, ) -> Generator[SectionKey, None, None]: for section_id in self.enabled_section_ids: @@ -539,14 +543,14 @@ def get_enabled_section_keys( yield section_key def _get_first_incomplete_section_key(self) -> tuple[str, str | None] | None: - for section_id, list_item_id in self.get_enabled_section_keys(): + for section_id, list_item_id in self._get_enabled_section_keys(): if not self._progress_store.is_section_or_repeating_blocks_progress_complete( section_id, list_item_id ): return section_id, list_item_id def _get_last_complete_section_key(self) -> tuple[str, str | None] | None: - for section_id, list_item_id in list(self.get_enabled_section_keys())[::-1]: + for section_id, list_item_id in list(self._get_enabled_section_keys())[::-1]: if self._progress_store.is_section_or_repeating_blocks_progress_complete( section_id, list_item_id ): @@ -578,7 +582,7 @@ def _is_section_enabled(self, section: Mapping) -> bool: return bool(when_rule_evaluator.evaluate(enabled["when"])) @staticmethod - def get_next_block_url( + def _get_next_block_url( location: LocationType, routing_path: RoutingPath, **kwargs: str | None, diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py index 473221551e..c256878b6a 100644 --- a/app/views/handlers/list_repeating_question.py +++ b/app/views/handlers/list_repeating_question.py @@ -3,7 +3,6 @@ from flask import url_for from werkzeug.datastructures import ImmutableDict -from app.questionnaire import Location from app.views.handlers.list_edit_question import ListEditQuestion @@ -20,17 +19,17 @@ def get_previous_location_url(self) -> str: if url := self.get_section_or_final_summary_url(): return url - if self.return_to and self.router.can_access_location( - Location( - section_id=self.current_location.section_id, - block_id=self.return_to_block_id, - ), + # the locations list_item_id is referring to where to return to within the context of a repeating section + # since the list collector won't be in a repeating section, use the parent location which doesn't have a list item id + if url := self.router.get_return_to_location_url( + location=self.parent_location, + return_to=self._return_to, routing_path=self._routing_path, + is_for_previous=True, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, ): - return self._get_location_url( - block_id=self._return_to_block_id, - anchor=self._return_to_answer_id, - ) + return url repeating_block_index = self.repeating_block_ids.index( # Type ignore: block_id will exist at this point diff --git a/schemas/test/en/test_grand_calculated_summary_multiple_sections.json b/schemas/test/en/test_grand_calculated_summary_multiple_sections.json deleted file mode 100644 index 8b7700df1d..0000000000 --- a/schemas/test/en/test_grand_calculated_summary_multiple_sections.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "title": "Grand Calculated Summary cross section demo", - "theme": "default", - "description": "A schema to showcase grand calculated summaries across multiple sections and groups", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "ru_name", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": {} - }, - "sections": [ - { - "id": "section-1", - "title": "Section 1", - "summary": { - "show_on_completion": true - }, - "groups": [ - { - "id": "group-1", - "title": "Group title for questions about food", - "blocks": [ - { - "type": "Question", - "id": "block-1", - "question": { - "type": "General", - "id": "question-1", - "title": "How much do you spend per week on fruit and veg? (Question 1, Group 1, Section 1)", - "answers": [ - { - "id": "q1-a1", - "label": "Money spent on fruit", - "type": "Currency", - "currency": "GBP", - "mandatory": false - }, - { - "id": "q1-a2", - "label": "Money spent on veg", - "type": "Currency", - "currency": "GBP", - "mandatory": false - } - ] - } - }, - { - "type": "Question", - "id": "block-2", - "question": { - "type": "General", - "id": "question-2", - "title": "How much do you spend per week on other food? (Question 2, Group 1, Section 1)", - "answers": [ - { - "id": "q2-a1", - "label": "Money spent on bread", - "type": "Currency", - "currency": "GBP", - "mandatory": false - }, - { - "id": "q2-a2", - "label": "Money spent on not bread", - "type": "Currency", - "currency": "GBP", - "mandatory": false - } - ] - } - }, - { - "type": "CalculatedSummary", - "id": "calculated-summary-1", - "title": "Calculated Summary for Question 1 is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Total food expenditure", - "operation": { - "+": [ - { - "source": "answers", - "identifier": "q1-a1" - }, - { - "source": "answers", - "identifier": "q1-a2" - }, - { - "source": "answers", - "identifier": "q2-a1" - }, - { - "source": "answers", - "identifier": "q2-a2" - } - ] - } - } - } - ] - }, - { - "id": "group-2", - "title": "Group title for clothing questions", - "blocks": [ - { - "type": "Question", - "id": "block-3", - "question": { - "type": "General", - "id": "question-3", - "title": "How much do you spend per week on clothes? (Question 3, Group 2, Section 1)", - "answers": [ - { - "id": "q3-a1", - "label": "Money spent on jumpers", - "type": "Currency", - "currency": "GBP", - "mandatory": false - }, - { - "id": "q3-a2", - "label": "Money spent on hats", - "type": "Currency", - "currency": "GBP", - "mandatory": false - } - ] - } - }, - { - "type": "CalculatedSummary", - "id": "calculated-summary-2", - "title": "Calculated summary for question 3 is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Total clothes expenditure", - "operation": { - "+": [ - { - "source": "answers", - "identifier": "q3-a1" - }, - { - "source": "answers", - "identifier": "q3-a2" - } - ] - } - } - }, - { - "type": "CalculatedSummary", - "id": "calculated-summary-3", - "title": "Calculated summary for section 1 is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Total food and clothes expenditure", - "operation": { - "+": [ - { - "source": "answers", - "identifier": "q1-a1" - }, - { - "source": "answers", - "identifier": "q1-a2" - }, - { - "source": "answers", - "identifier": "q2-a1" - }, - { - "source": "answers", - "identifier": "q2-a2" - }, - { - "source": "answers", - "identifier": "q3-a1" - }, - { - "source": "answers", - "identifier": "q3-a2" - } - ] - } - } - }, - { - "type": "GrandCalculatedSummary", - "id": "grand-calculated-summary-1", - "title": "Grand Calculated Summary which should match the previous calculated summary is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Grand calculated summary", - "operation": { - "+": [ - { - "source": "calculated_summary", - "identifier": "calculated-summary-1" - }, - { - "source": "calculated_summary", - "identifier": "calculated-summary-2" - } - ] - } - } - } - ] - } - ] - }, - { - "id": "section-2", - "title": "Section 2", - "groups": [ - { - "id": "group-3", - "title": "Group title for questions about games", - "blocks": [ - { - "type": "Question", - "id": "block-4", - "question": { - "type": "General", - "id": "question-4", - "title": "How much do you spend per week on games? (Question 4, section 2, group 3)", - "guidance": { - "contents": [ - { - "description": "Note:" - }, - { - "list": [ - "The grand calculated summary section after this will only show if the total spending on games is not zero", - "You should test that if you use the change links on the grand calculated summary to come back here and set both answers to 0, that you are not routed to the grand calculated summary when you press continue on the calculated summary, but instead, taken to the Hub.", - "If you use the change links on the grand calculated summary to edit these answer values to a non-zero sum, pressing continue twice should take you back to the grand calculated summary" - ] - } - ] - }, - "answers": [ - { - "id": "q4-a1", - "label": "Video games", - "type": "Currency", - "currency": "GBP", - "mandatory": false - }, - { - "id": "q4-a2", - "label": "Board games", - "type": "Currency", - "currency": "GBP", - "mandatory": false - } - ] - } - }, - { - "type": "CalculatedSummary", - "id": "calculated-summary-4", - "title": "Calculated Summary for Question 4 is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Total games expenditure", - "operation": { - "+": [ - { - "source": "answers", - "identifier": "q4-a1" - }, - { - "source": "answers", - "identifier": "q4-a2" - } - ] - } - } - } - ] - } - ] - }, - { - "id": "section-3", - "title": "Grand calculated summary section which only shows if the previous calculated summary is non zero", - "enabled": { - "when": { - "!=": [ - 0, - { - "source": "calculated_summary", - "identifier": "calculated-summary-4" - } - ] - } - }, - "groups": [ - { - "id": "group-4", - "title": "Group title for the grand calculated summary of both sections", - "blocks": [ - { - "type": "GrandCalculatedSummary", - "id": "grand-calculated-summary-2", - "title": "Grand Calculated Summary for section 1 and 2 is calculated to be %(total)s. Is this correct?", - "calculation": { - "title": "Total food clothes and games expenditure", - "operation": { - "+": [ - { - "source": "calculated_summary", - "identifier": "calculated-summary-1" - }, - { - "source": "calculated_summary", - "identifier": "calculated-summary-2" - }, - { - "source": "calculated_summary", - "identifier": "calculated-summary-4" - } - ] - } - } - } - ] - } - ] - } - ] -} diff --git a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json new file mode 100644 index 0000000000..bca1827361 --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json @@ -0,0 +1,1170 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Grand Calculated Summary cross section demo", + "theme": "default", + "description": "A schema to showcase grand calculated summary across multiple sections featuring static, dynamic and list repeating block answers", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "section-1", + "title": "Food and clothing", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "group-1", + "title": "Food", + "blocks": [ + { + "type": "Question", + "id": "block-1", + "question": { + "type": "General", + "id": "question-1", + "title": "How much do you spend per month on fruit and veg?", + "answers": [ + { + "id": "q1-a1", + "label": "Money spent on fruit", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q1-a2", + "label": "Money spent on veg", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "Question", + "id": "block-2", + "question": { + "type": "General", + "id": "question-2", + "title": "How much do you spend per month on other food?", + "answers": [ + { + "id": "q2-a1", + "label": "Money spent on bread", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q2-a2", + "label": "Money spent on not bread", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-1", + "title": "Calculated Summary for food expenditure is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly food expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + } + } + } + ] + }, + { + "id": "group-2", + "title": "Clothing", + "blocks": [ + { + "type": "Question", + "id": "block-3", + "question": { + "type": "General", + "id": "question-3", + "title": "How much do you spend per month on clothes?", + "answers": [ + { + "id": "q3-a1", + "label": "Money spent on jumpers", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q3-a2", + "label": "Money spent on hats", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-2", + "title": "Calculated summary for clothes expenditure is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly clothes expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q3-a2" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-3", + "title": "Calculated summary for food and clothing is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total food and clothes expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a1" + }, + { + "source": "answers", + "identifier": "q2-a2" + }, + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q3-a2" + } + ] + } + } + }, + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-1", + "title": "Grand Calculated Summary which should match the previous calculated summary is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Grand calculated summary", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Entertainment", + "groups": [ + { + "id": "group-3", + "title": "Group title for questions about games", + "blocks": [ + { + "type": "Question", + "id": "block-4", + "question": { + "type": "General", + "id": "question-4", + "title": "How much do you spend per week on games?", + "guidance": { + "contents": [ + { + "description": "Note:" + }, + { + "list": [ + "The grand calculated summary section after this will only show if the total spending on games is not zero", + "You should test that if you use the change links on the grand calculated summary to come back here and set both answers to 0, that you are not routed to the grand calculated summary when you press continue on the calculated summary, but instead, taken to the Hub.", + "If you use the change links on the grand calculated summary to edit these answer values to a non-zero sum, pressing continue twice should take you back to the grand calculated summary" + ] + } + ] + }, + "answers": [ + { + "id": "q4-a1", + "label": "Video games", + "type": "Currency", + "currency": "GBP", + "mandatory": false + }, + { + "id": "q4-a2", + "label": "Board games", + "type": "Currency", + "currency": "GBP", + "mandatory": false + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-4", + "title": "Calculated Summary for games expenditure is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total games expenditure", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q4-a1" + }, + { + "source": "answers", + "identifier": "q4-a2" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Grand calculated summary of shopping and entertainment", + "enabled": { + "when": { + "and": [ + { + "!=": [ + 0, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + } + ] + }, + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-1" + } + ] + }, + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + } + ] + } + ] + } + }, + "groups": [ + { + "id": "group-4", + "title": "Group title for the grand calculated summary of both sections", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-2", + "title": "Grand Calculated Summary for shopping and entertainment is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total food clothes and games expenditure", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-4", + "title": "Utility bills", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "utility-bills", + "title": "Utility bills", + "item_anchor_answer_id": "utility-bill-name", + "item_label": "Utility bill", + "add_link_text": "Add another utility bill", + "empty_list_text": "No utility bills added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-5", + "title": "Utility bills", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-utility-bills", + "for_list": "utility-bills", + "question": { + "type": "General", + "id": "any-utility-bills-question", + "title": "Do you have any monthly expenditure on Utility bills?", + "answers": [ + { + "type": "Radio", + "id": "any-utility-bills-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-utility-bill", + "list_name": "utility-bills" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-utility-bills-answer" + }, + "Yes" + ] + }, + "block": "any-other-utility-bills" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-utility-bills", + "type": "ListCollector", + "for_list": "utility-bills", + "question": { + "id": "any-other-utility-bills-question", + "type": "General", + "title": "Do you need to add any other Utility bills?", + "answers": [ + { + "id": "any-other-utility-bills-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-utility-bill", + "type": "ListAddQuestion", + "question": { + "id": "add-utility-bill-question", + "type": "General", + "title": "What bill do you need to add expenditure for?", + "answers": [ + { + "id": "utility-bill-name", + "label": "Utility bill", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Electricity", + "value": "Electricity" + }, + { + "label": "Water", + "value": "Water" + }, + { + "label": "Gas", + "value": "Gas" + }, + { + "label": "Internet", + "value": "Internet" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-utility-bill", + "type": "ListEditQuestion", + "question": { + "id": "edit-utility-bill-question", + "type": "General", + "title": "What is the name of the game?", + "answers": [ + { + "id": "utility-bill-name", + "label": "Name of Utility bill", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-utility-bill", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-utility-bill-question", + "type": "General", + "title": "Are you sure you want to remove this Utility bill?", + "answers": [ + { + "id": "remove-utility-bill-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Utility bills", + "item_title": { + "text": "{utility_bill}", + "placeholders": [ + { + "placeholder": "utility_bill", + "value": { + "source": "answers", + "identifier": "utility-bill-name" + } + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-answer", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "utility-bills" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "utility-bills" + }, + "answers": [ + { + "label": { + "text": "Monthly expenditure on {utility_bill} bills", + "placeholders": [ + { + "placeholder": "utility_bill", + "value": { + "source": "answers", + "identifier": "utility-bill-name" + } + } + ] + }, + "id": "utility-bill-monthly-cost", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + } + ] + }, + "id": "dynamic-answer-question", + "title": "Monthly expenditure on utility bills", + "type": "General" + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-5", + "title": "Calculated Summary for monthly spending on utility bills is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly expenditure on utility bills", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "utility-bill-monthly-cost" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-5", + "title": "Streaming services", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "streaming-services", + "title": "Streaming services", + "item_anchor_answer_id": "streaming-service-name", + "item_label": "Streaming service", + "add_link_text": "Add another streaming service", + "empty_list_text": "No streaming services added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-6", + "title": "Streaming services", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-streaming-services", + "for_list": "streaming-services", + "question": { + "type": "General", + "id": "any-streaming-services-question", + "title": "Do you have any monthly expenditure on streaming services?", + "answers": [ + { + "type": "Radio", + "id": "any-streaming-services-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-streaming-service", + "list_name": "streaming-services" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-streaming-services-answer" + }, + "Yes" + ] + }, + "block": "any-other-streaming-services" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-streaming-services", + "type": "ListCollector", + "for_list": "streaming-services", + "question": { + "id": "any-other-streaming-services-question", + "type": "General", + "title": "Do you need to add any other streaming services?", + "answers": [ + { + "id": "any-other-streaming-services-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-streaming-service", + "type": "ListAddQuestion", + "question": { + "id": "add-streaming-service-question", + "type": "General", + "title": "What is the name of the Streaming service?", + "answers": [ + { + "id": "streaming-service-name", + "label": "Name of Streaming service", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Netflix", + "value": "Netflix" + }, + { + "label": "Prime video", + "value": "Prime video" + }, + { + "label": "Now TV", + "value": "Now TV" + }, + { + "label": "Apple TV", + "value": "Apple TV" + }, + { + "label": "Disney+", + "value": "Disney+" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-streaming-service", + "type": "ListEditQuestion", + "question": { + "id": "edit-streaming-service-question", + "type": "General", + "title": "What is the name of the Streaming service?", + "answers": [ + { + "id": "streaming-service-name", + "label": "Name of Streaming service", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-streaming-service", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-streaming-service-question", + "type": "General", + "title": "Are you sure you want to remove this Streaming service?", + "answers": [ + { + "id": "remove-streaming-service-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "repeating_blocks": [ + { + "id": "streaming-service-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "streaming-service-repeating-block-1-question", + "type": "General", + "title": { + "text": "What is your monthly expenditure on {streaming_service} bills?", + "placeholders": [ + { + "placeholder": "streaming_service", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + }, + "answers": [ + { + "id": "streaming-service-monthly-cost", + "label": { + "text": "Monthly subscription cost for {streaming_service}", + "placeholders": [ + { + "placeholder": "streaming_service", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + }, + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "streaming-service-extra-cost", + "label": { + "text": "Additional monthly spending on {streaming_service}", + "placeholders": [ + { + "placeholder": "streaming_service", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + }, + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "id": "streaming-service-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "streaming-service-repeating-block-2-question", + "type": "General", + "title": { + "text": "What is your average monthly internet usage for {streaming_service}?", + "placeholders": [ + { + "placeholder": "streaming_service", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + }, + "answers": [ + { + "id": "streaming-service-usage", + "label": { + "text": "Monthly Internet usage for {streaming_service}", + "placeholders": [ + { + "placeholder": "streaming_service", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + }, + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "digital-gigabyte", + "decimal_places": 2 + } + ] + } + } + ], + "summary": { + "title": "Streaming services", + "item_title": { + "text": "{streaming_service_name}", + "placeholders": [ + { + "placeholder": "streaming_service_name", + "value": { + "source": "answers", + "identifier": "streaming-service-name" + } + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-6", + "title": "Calculated Summary for monthly expenditure on streaming services is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly expenditure on streaming services", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "streaming-service-monthly-cost" + }, + { + "source": "answers", + "identifier": "streaming-service-extra-cost" + } + ] + } + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-7", + "title": "Total monthly internet usage on streaming services is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total internet usage on streaming services", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "streaming-service-usage" + } + ] + } + } + }, + { + "type": "Question", + "id": "other-internet-usage", + "question": { + "type": "General", + "id": "other-internet-question", + "title": "Do you have any additional internet usage to report?", + "answers": [ + { + "id": "media-downloads", + "label": "Internet usage on media downloads", + "mandatory": false, + "type": "Unit", + "unit_length": "short", + "unit": "digital-gigabyte", + "decimal_places": 2 + }, + { + "id": "misc-internet", + "label": "Other miscellaneous internet usage", + "mandatory": false, + "type": "Unit", + "unit_length": "short", + "unit": "digital-gigabyte", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-8", + "title": "Total monthly internet usage on other services is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total internet usage on other services", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "media-downloads" + }, + { + "source": "answers", + "identifier": "misc-internet" + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-6", + "title": "Grand calculated summary of household expenditure", + "enabled": { + "when": { + "and": [ + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-1" + } + ] + }, + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-2" + } + ] + }, + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-4" + } + ] + }, + { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-5" + } + ] + } + ] + } + }, + "groups": [ + { + "id": "group-7", + "title": "Expenditure grand totals", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-3", + "title": "Grand Calculated Summary for monthly spending on bills and services is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly expenditure on bills and streaming services", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-5" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-6" + } + ] + } + } + }, + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-4", + "title": "Grand Calculated Summary for internet usage is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total internet usage", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-7" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-8" + } + ] + } + } + }, + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-5", + "title": "Grand Calculated Summary for total monthly household expenditure is calculated to be %(total)s. Is this correct?", + "calculation": { + "title": "Total monthly expenditure", + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-2" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-4" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-5" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-6" + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index b447026559..bdf08461c7 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1347,6 +1347,77 @@ def grand_calculated_summary_progress_store(): ) +@pytest.fixture +def grand_calculated_summary_repeating_answers_schema(): + return load_schema_from_name("test_grand_calculated_summary_repeating_answers") + + +@pytest.fixture +def grand_calculated_summary_repeating_answers_progress_store(): + return ProgressStore( + [ + { + "section_id": "section-5", + "block_ids": [ + "any-streaming-services", + "any-other-streaming-services", + "calculated-summary-6", + "calculated-summary-7", + "other-internet-usage", + "calculated-summary-8", + ], + "status": "COMPLETED", + }, + { + "section_id": "section-5", + "block_ids": [ + "streaming-service-repeating-block-1", + "streaming-service-repeating-block-2", + ], + "status": "COMPLETED", + "list_item_id": "item-1", + }, + { + "section_id": "section-5", + "block_ids": [ + "streaming-service-repeating-block-1", + "streaming-service-repeating-block-2", + ], + "status": "COMPLETED", + "list_item_id": "item-2", + }, + { + "section_id": "section-4", + "block_ids": [ + "any-utility-bills", + "any-other-utility-bills", + "dynamic-answer", + "calculated-summary-5", + ], + "status": "COMPLETED", + }, + { + "section_id": "section-1", + "block_ids": [ + "block-1", + "block-2", + "calculated-summary-1", + "block-3", + "calculated-summary-2", + "calculated-summary-3", + "grand-calculated-summary-1", + ], + "status": "COMPLETED", + }, + { + "section_id": "section-2", + "block_ids": ["block-4", "calculated-summary-4"], + "status": "COMPLETED", + }, + ] + ) + + @pytest.fixture @pytest.mark.usefixtures("app", "gb_locale") def placeholder_transform_question_dynamic_answers_json(): diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index eb13b568b1..78955c68ed 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -798,7 +798,7 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( you are routed to the next block in the incomplete section rather than the grand calculated summary """ self.schema = load_schema_from_name( - "test_grand_calculated_summary_multiple_sections" + "test_grand_calculated_summary_repeating_answers" ) # calculated summary 3 is not complete yet self.progress_store = ProgressStore( @@ -1235,6 +1235,45 @@ def test_return_to_grand_calculated_summary_invalid_url( assert expected_url == previous_location_url + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_repeating_answer( + self, + grand_calculated_summary_repeating_answers_progress_store, + grand_calculated_summary_repeating_answers_schema, + ): + """ + Test returning to a calculated summary from a list repeating question as part of a grand calculated summary change link + """ + self.schema = grand_calculated_summary_repeating_answers_schema + self.progress_store = grand_calculated_summary_repeating_answers_progress_store + + parent_location = Location( + section_id="section-5", + block_id="any-other-streaming-services", + ) + + routing_path = RoutingPath( + ["calculated-summary-6"], + section_id="section-5", + ) + next_location_url = self.router.get_previous_location_url( + parent_location, + routing_path, + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="calculated-summary-6", + return_to_block_id="calculated-summary-6,grand-calculated-summary-3", + ) + + expected_previous_url = url_for( + "questionnaire.block", + return_to="grand-calculated-summary", + block_id="calculated-summary-6", + return_to_block_id="grand-calculated-summary-3", + _anchor="calculated-summary-6", + ) + + assert expected_previous_url == next_location_url + @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js deleted file mode 100644 index 30c82ca769..0000000000 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_multiple_sections.spec.js +++ /dev/null @@ -1,154 +0,0 @@ -import HubPage from "../../../base_pages/hub.page"; -import Block1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-1.page"; -import Block2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-2.page"; -import CalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-1.page"; -import Block3Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-3.page"; -import Block4Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/block-4.page"; -import CalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-2.page"; -import CalculatedSummary3Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-3.page"; -import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/calculated-summary-4.page"; -import GrandCalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/grand-calculated-summary-1.page"; -import GrandCalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_multiple_sections/grand-calculated-summary-2.page"; -import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_multiple_sections/section-1-summary.page"; - -describe("Feature: Grand Calculated Summary", () => { - describe("Given I have a Grand Calculated Summary across multiple sections", () => { - before("Reaching the grand calculated summary section", async () => { - await browser.openQuestionnaire("test_grand_calculated_summary_multiple_sections.json"); - await $(HubPage.submit()).click(); - - // complete 2 questions in section 1 - await $(Block1Page.q1A1()).setValue(10); - await $(Block1Page.q1A2()).setValue(20); - await $(Block1Page.submit()).click(); - await $(Block2Page.q2A1()).setValue(30); - await $(Block2Page.q2A2()).setValue(40); - await $(Block2Page.submit()).click(); - await $(CalculatedSummary1Page.submit()).click(); - - // and the one for section 2 - await $(Block3Page.q3A1()).setValue(100); - await $(Block3Page.q3A2()).setValue(200); - await $(Block3Page.submit()).click(); - await $(CalculatedSummary2Page.submit()).click(); - await $(CalculatedSummary3Page.submit()).click(); - await $(GrandCalculatedSummary1Page.submit()).click(); - await $(Section1SummaryPage.submit()).click(); - await $(HubPage.submit()).click(); - await $(Block4Page.q4A1()).setValue(5); - await $(Block4Page.q4A2()).setValue(10); - await $(Block4Page.submit()).click(); - await $(CalculatedSummary4Page.submit()).click(); - await $(HubPage.submit()).click(); - }); - - it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £415.00. Is this correct?", - ); - await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); - - await $(CalculatedSummary1Page.submit()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); - }); - - it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { - await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 4 is calculated to be £15.00. Is this correct?", - ); - await $(CalculatedSummary4Page.q4A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block4Page.pageName); - - await $(Block4Page.q4A1()).setValue(50); - await $(Block4Page.submit()).click(); - - // first taken back to the calculated summary which has updated - await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 4 is calculated to be £60.00. Is this correct?", - ); - await $(CalculatedSummary4Page.submit()).click(); - - // then taken back to the grand calculated summary which has also been updated correctly - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £460.00. Is this correct?", - ); - }); - - it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { - await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £100.00. Is this correct?", - ); - - // change first answer - await $(CalculatedSummary1Page.q1A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block1Page.pageName); - await $(Block1Page.q1A1()).setValue(100); - await $(Block1Page.submit()).click(); - - // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing - await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £190.00. Is this correct?", - ); - - // change another answer - await $(CalculatedSummary1Page.q2A2Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block2Page.pageName); - await $(Block2Page.q2A2()).setValue(400); - await $(Block2Page.submit()).click(); - - // back at updated calculated summary - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( - "Calculated Summary for Question 1 is calculated to be £550.00. Is this correct?", - ); - - // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary - await $(CalculatedSummary1Page.submit()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummary3Page.pageName); - await $(CalculatedSummary3Page.submit()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary1Page.pageName); - await $(GrandCalculatedSummary1Page.submit()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( - "Grand Calculated Summary for section 1 and 2 is calculated to be £910.00. Is this correct?", - ); - }); - - it("Given I edit an answer included in a grand calculated summary, both the calculated and grand calculated summary sections should return to partially completed.", async () => { - await $(GrandCalculatedSummary2Page.submit()).click(); - await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Completed"); - - // Now edit an answer from section 2 and go back to the hub - await $(HubPage.summaryRowLink("section-3")).click(); - await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); - await $(CalculatedSummary4Page.q4A1Edit()).click(); - await $(Block4Page.q4A1()).setValue(1); - await $(Block4Page.submit()).click(); - await $(CalculatedSummary4Page.previous()).click(); - await $(Block4Page.previous()).click(); - - // calculated summary section should be in progress - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); - // TODO: grand calculated summary should not show, but this requires progress source, until this is implemented, it should at least show as in progress - await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Partially completed"); - }); - - it("Given I set both answers to block 4 to zero which removes the Grand Calculated Summary from the path, I am routed back to the Hub after the calculated summary", async () => { - await $(HubPage.summaryRowLink("section-3")).click(); - await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); - await $(CalculatedSummary4Page.q4A1Edit()).click(); - await $(Block4Page.q4A1()).setValue(0); - await $(Block4Page.q4A2()).setValue(0); - await $(Block4Page.submit()).click(); - await $(CalculatedSummary4Page.submit()).click(); - // should be back at Hub, and grand calculated summary section not present - await expect(await browser.getUrl()).to.contain(HubPage.pageName); - await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; - }); - }); -}); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js new file mode 100644 index 0000000000..33190f1853 --- /dev/null +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -0,0 +1,457 @@ +import HubPage from "../../../base_pages/hub.page"; +import Block1Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/block-1.page"; +import Block2Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/block-2.page"; +import CalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-1.page"; +import Block3Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/block-3.page"; +import Block4Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/block-4.page"; +import CalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-2.page"; +import CalculatedSummary3Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-3.page"; +import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-4.page"; +import GrandCalculatedSummary1Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-1.page"; +import GrandCalculatedSummary2Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-2.page"; +import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-1-summary.page"; +import AddUtilityBillPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-other-utility-bills-add.page.js"; +import AnyOtherUtilityBillsPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-other-utility-bills.page.js"; +import DynamicAnswerPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/dynamic-answer.page.js"; +import CalculatedSummary5Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-5.page.js"; +import AnyStreamingServicesPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-streaming-services.page.js"; +import AddStreamingServicePage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-other-streaming-services-add.page.js"; +import RemoveStreamingServicePage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-other-streaming-services-remove.page.js"; +import StreamingServiceRepeatingBlock1Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/streaming-service-repeating-block-1-repeating-block.page.js"; +import StreamingServiceRepeatingBlock2Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/streaming-service-repeating-block-2-repeating-block.page.js"; +import AnyOtherStreamingServicesPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-other-streaming-services.page.js"; +import CalculatedSummary6Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-6.page.js"; +import CalculatedSummary7Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-7.page.js"; +import OtherInternetUsagePage from "../../../generated_pages/grand_calculated_summary_repeating_answers/other-internet-usage.page.js"; +import CalculatedSummary8Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/calculated-summary-8.page.js"; +import GrandCalculatedSummary3Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-3.page.js"; +import GrandCalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-4.page.js"; +import GrandCalculatedSummary5Page from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-5.page.js"; +import AnyUtilityBillsPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-utility-bills.page"; +import Section4SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-4-summary.page"; +import Section5SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-5-summary.page"; +import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink } from "../../../helpers"; + +describe("Feature: Grand Calculated Summary", () => { + describe("Given I have a Grand Calculated Summary across multiple sections", () => { + before("Reaching the grand calculated summary section", async () => { + await browser.openQuestionnaire("test_grand_calculated_summary_repeating_answers.json"); + await $(HubPage.submit()).click(); + + // complete 2 questions in section 1 + await $(Block1Page.q1A1()).setValue(10); + await $(Block1Page.q1A2()).setValue(20); + await $(Block1Page.submit()).click(); + await $(Block2Page.q2A1()).setValue(30); + await $(Block2Page.q2A2()).setValue(40); + await $(Block2Page.submit()).click(); + await $(CalculatedSummary1Page.submit()).click(); + + // and the one for section 2 + await $(Block3Page.q3A1()).setValue(100); + await $(Block3Page.q3A2()).setValue(200); + await $(Block3Page.submit()).click(); + await $(CalculatedSummary2Page.submit()).click(); + await $(CalculatedSummary3Page.submit()).click(); + await $(GrandCalculatedSummary1Page.submit()).click(); + await $(Section1SummaryPage.submit()).click(); + await $(HubPage.submit()).click(); + await $(Block4Page.q4A1()).setValue(5); + await $(Block4Page.q4A2()).setValue(10); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.submit()).click(); + await $(HubPage.submit()).click(); + }); + + it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for shopping and entertainment is calculated to be £415.00. Is this correct?", + ); + await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + + await $(CalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + }); + + it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for games expenditure is calculated to be £15.00. Is this correct?", + ); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block4Page.pageName); + + await $(Block4Page.q4A1()).setValue(50); + await $(Block4Page.submit()).click(); + + // first taken back to the calculated summary which has updated + await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for games expenditure is calculated to be £60.00. Is this correct?", + ); + await $(CalculatedSummary4Page.submit()).click(); + + // then taken back to the grand calculated summary which has also been updated correctly + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for shopping and entertainment is calculated to be £460.00. Is this correct?", + ); + }); + + it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { + await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for food expenditure is calculated to be £100.00. Is this correct?", + ); + + // change first answer + await $(CalculatedSummary1Page.q1A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block1Page.pageName); + await $(Block1Page.q1A1()).setValue(100); + await $(Block1Page.submit()).click(); + + // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing + await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for food expenditure is calculated to be £190.00. Is this correct?", + ); + + // change another answer + await $(CalculatedSummary1Page.q2A2Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block2Page.pageName); + await $(Block2Page.q2A2()).setValue(400); + await $(Block2Page.submit()).click(); + + // back at updated calculated summary + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for food expenditure is calculated to be £550.00. Is this correct?", + ); + + // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary + await $(CalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummary3Page.pageName); + await $(CalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary1Page.pageName); + await $(GrandCalculatedSummary1Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for shopping and entertainment is calculated to be £910.00. Is this correct?", + ); + }); + + it("Given I edit an answer included in a grand calculated summary, the calculated summary sections should return to partially completed, and the grand calculated summary becomes unavailable.", async () => { + await $(GrandCalculatedSummary2Page.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Completed"); + + // Now edit an answer from section 2 and go back to the hub + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await $(Block4Page.q4A1()).setValue(1); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.previous()).click(); + await $(Block4Page.previous()).click(); + + // calculated summary section should be in progress + await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + }); + + it("Given I confirm the calculated summary, When I return to the Hub, Then I see the grand calculated summary come back marked as partially completed", async () => { + await $(HubPage.summaryRowLink("section-2")).click(); + await $(CalculatedSummary4Page.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Partially completed"); + }); + + it("Given I set both answers to block 4 to zero which removes the Grand Calculated Summary from the path, I am routed back to the Hub after the calculated summary", async () => { + await $(HubPage.summaryRowLink("section-3")).click(); + await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); + await $(CalculatedSummary4Page.q4A1Edit()).click(); + await $(Block4Page.q4A1()).setValue(0); + await $(Block4Page.q4A2()).setValue(0); + await $(Block4Page.submit()).click(); + await $(CalculatedSummary4Page.submit()).click(); + // should be back at Hub, and grand calculated summary section not present + await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + }); + + it("Given I have a grand calculated summary section requiring completion of all previous sections, When I complete each section in turn, Then I don't see the grand calculated summary until all sections are complete, at which point I see it on the Hub", async () => { + // no grand calculated summary section on the hub + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + + await $(HubPage.submit()).click(); + await $(AnyUtilityBillsPage.yes()).click(); + await $(AnyUtilityBillsPage.submit()).click(); + await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Electricity"); + await $(AddUtilityBillPage.submit()).click(); + await $(AnyOtherUtilityBillsPage.yes()).click(); + await $(AnyOtherUtilityBillsPage.submit()).click(); + await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Internet"); + await $(AddUtilityBillPage.submit()).click(); + await $(AnyOtherUtilityBillsPage.yes()).click(); + await $(AnyOtherUtilityBillsPage.submit()).click(); + await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Gas"); + await $(AddUtilityBillPage.submit()).click(); + await $(AnyOtherUtilityBillsPage.no()).click(); + await $(AnyOtherUtilityBillsPage.submit()).click(); + await $$(DynamicAnswerPage.inputs())[0].setValue(150); + await $$(DynamicAnswerPage.inputs())[1].setValue(35); + await $$(DynamicAnswerPage.inputs())[2].setValue(65); + await $(DynamicAnswerPage.submit()).click(); + await $(CalculatedSummary5Page.submit()).click(); + await $(Section4SummaryPage.submit()).click(); + // still no grand calculated summary yet + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + + await $(HubPage.submit()).click(); + await $(AnyStreamingServicesPage.yes()).click(); + await $(AnyStreamingServicesPage.submit()).click(); + await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Netflix"); + await $(AddStreamingServicePage.submit()).click(); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(10); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceExtraCost()).setValue(0); + await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(20); + await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await $(AnyOtherStreamingServicesPage.yes()).click(); + await $(AnyOtherStreamingServicesPage.submit()).click(); + await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Prime video"); + await $(AddStreamingServicePage.submit()).click(); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(8); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceExtraCost()).setValue(12); + await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(25); + await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await $(AnyOtherStreamingServicesPage.no()).click(); + await $(AnyOtherStreamingServicesPage.submit()).click(); + await $(CalculatedSummary6Page.submit()).click(); + await $(CalculatedSummary7Page.submit()).click(); + await $(OtherInternetUsagePage.mediaDownloads()).setValue(50); + await $(OtherInternetUsagePage.miscInternet()).setValue(5); + await $(OtherInternetUsagePage.submit()).click(); + await $(CalculatedSummary8Page.submit()).click(); + await $(Section5SummaryPage.submit()).click(); + // grand calculated summary now present + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.equal("Not started"); + }); + + it("Given I have a calculated summary of repeating answers and a calculated summary of dynamic answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { + await $(HubPage.submit()).click(); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £280.00. Is this correct?", + ); + assertSummaryValues(["£250.00", "£30.00", "£280.00"]); + assertSummaryItems([ + "Total monthly expenditure on utility bills", + "Total monthly expenditure on streaming services", + "Total monthly expenditure on bills and streaming services", + ]); + }); + + it("Given I have 2 calculated summaries of list repeating block answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", + ); + assertSummaryValues(["45 GB", "55 GB", "100 GB"]); + assertSummaryItems(["Total internet usage on streaming services", "Total internet usage on other services", "Total internet usage"]); + }); + + it("Given I have multiple calculated summaries of static, repeating and dynamic answers, When I reach the grand calculated summary of them all, Then I see the correct total and items", async () => { + await $(GrandCalculatedSummary4Page.submit()).click(); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,130.00. Is this correct?", + ); + assertSummaryValues(["£550.00", "£300.00", "£0.00", "£250.00", "£30.00", "£1,130.00"]); + assertSummaryValues([ + "Total monthly food expenditure", + "Total monthly clothes expenditure", + "Total games expenditure", + "Total monthly expenditure on utility bills", + "Total monthly expenditure on streaming services", + ]); + }); + + it("Given I a grand calculated summary featuring repeating answers, When I click edit links to return to a dynamic answer then previous twice, Then I return to the grand calculated summary where I started", async () => { + await $(GrandCalculatedSummary5Page.calculatedSummary5Edit()).click(); + await repeatingAnswerChangeLink(0).click(); + await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await $(DynamicAnswerPage.previous()).click(); + await $(CalculatedSummary5Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + }); + + it("Given I have a grand calculated summary featuring repeating answers, When I edit a dynamic answer, Then I return to the calculated summary to confirm, and then each affected grand calculated summary in turn", async () => { + await $(GrandCalculatedSummary5Page.calculatedSummary5Edit()).click(); + await repeatingAnswerChangeLink(1).click(); + await $$(DynamicAnswerPage.inputs())[0].setValue("175"); + await $(DynamicAnswerPage.submit()).click(); + await $(CalculatedSummary5Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £305.00. Is this correct?", + ); + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,155.00. Is this correct?", + ); + }); + + it("Given I have a grand calculated summary featuring repeating answers, When I click edit links to return to a list repeating block answer then previous twice, Then I return to the grand calculated summary anchored from where I started", async () => { + await $(GrandCalculatedSummary5Page.calculatedSummary6Edit()).click(); + await repeatingAnswerChangeLink(2).click(); + await $(StreamingServiceRepeatingBlock1Page.previous()).click(); + await $(CalculatedSummary5Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + }); + + it("Given I have a grand calculated summary featuring repeating answers, When I edit a list repeating block answer, Then I return to the calculated summary to confirm, and then the grand calculated summary to confirm", async () => { + await $(GrandCalculatedSummary5Page.calculatedSummary6Edit()).click(); + await repeatingAnswerChangeLink(2).click(); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(12); + await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await $(CalculatedSummary5Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £309.00. Is this correct?", + ); + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", + ); + await $(GrandCalculatedSummary5Page.submit()).click(); + }); + + it("Given I have a grand calculated summary featuring dynamic answers, When I add an item to the list collector and return to the hub, Then I see the section with dynamic answers is in progress, and the grand calculated summary section is not available", async () => { + await $(HubPage.summaryRowLink("section-4")).click(); + await $(Section4SummaryPage.utilityBillsListAddLink()).click(); + await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Water"); + await $(AddUtilityBillPage.submit()).click(); + await $(AnyOtherUtilityBillsPage.no()).click(); + await $(AnyOtherUtilityBillsPage.submit()).click(); + await $$(DynamicAnswerPage.inputs())[3].setValue("40"); + await $(DynamicAnswerPage.submit()).click(); + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-4")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + }); + + it("Given I complete the in progress section, When I return to the Hub, Then I see the grand calculated summary section re-enabled, and partially completed", async () => { + await $(HubPage.summaryRowLink("section-4")).click(); + await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for monthly spending on utility bills is calculated to be £315.00. Is this correct?", + ); + await $(CalculatedSummary5Page.submit()).click(); + await $(Section4SummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + }); + + it("Given I return to the grand calculated summary section, When I go to each grand calculated summary, Then I see the correct new values", async () => { + await $(HubPage.summaryRowLink("section-6")).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", + ); + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", + ); + await $(GrandCalculatedSummary4Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", + ); + await $(GrandCalculatedSummary5Page.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Completed"); + }); + + it("Given I add a list item for the section with list repeating blocks, When I return to the hub before and after completing the section, Then I see the grand calculated summary go from unavailable, to enabled and in progress", async () => { + await $(HubPage.summaryRowLink("section-5")).click(); + await $(Section5SummaryPage.streamingServicesListAddLink()).click(); + await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Disney+"); + await $(AddStreamingServicePage.submit()).click(); + await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(10); + await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(5); + await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await $(AnyOtherStreamingServicesPage.no()).click(); + await $(AnyOtherStreamingServicesPage.submit()).click(); + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for monthly expenditure on streaming services is calculated to be £44.00. Is this correct?", + ); + await browser.url(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-5")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + await $(HubPage.summaryRowLink("section-5")).click(); + await $(CalculatedSummary6Page.submit()).click(); + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( + "Total monthly internet usage on streaming services is calculated to be 50 GB. Is this correct?", + ); + await $(CalculatedSummary7Page.submit()).click(); + await $(Section5SummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + }); + + it("Given I the grand calculated summary section is now incomplete, When I return to the section, Then I am taken to each updated grand calculated summary to confirm the new total", async () => { + await $(HubPage.summaryRowLink("section-6")).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £359.00. Is this correct?", + ); + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for internet usage is calculated to be 105 GB. Is this correct?", + ); + await $(GrandCalculatedSummary4Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", + ); + await $(GrandCalculatedSummary5Page.submit()).click(); + }); + + it("Given I remove a list item involved in the grand calculated summary, When I confirm, Then I am taken to each affected calculated summary to reconfirm, and when I return to the Hub the grand calculated summary is in progress", async () => { + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Completed"); + await $(HubPage.summaryRowLink("section-5")).click(); + await $(Section5SummaryPage.streamingServicesListRemoveLink(1)).click(); + await $(RemoveStreamingServicePage.yes()).click(); + await $(RemoveStreamingServicePage.submit()).click(); + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( + "Calculated Summary for monthly expenditure on streaming services is calculated to be £34.00. Is this correct?", + ); + await $(CalculatedSummary6Page.submit()).click(); + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( + "Total monthly internet usage on streaming services is calculated to be 30 GB. Is this correct?", + ); + await $(CalculatedSummary7Page.submit()).click(); + await $(Section5SummaryPage.submit()).click(); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + }); + + it("Given the section has reverted to partially complete, When I go back to the section, Then I am taken to each grand calculated summary to reconfirm with correct values", async () => { + await $(HubPage.summaryRowLink("section-6")).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", + ); + await $(GrandCalculatedSummary3Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for internet usage is calculated to be 85 GB. Is this correct?", + ); + await $(GrandCalculatedSummary4Page.submit()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", + ); + await $(GrandCalculatedSummary5Page.submit()).click(); + }); + }); +}); diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 3750564cc3..2120a26c88 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -26,7 +26,10 @@ def test_grand_calculated_summary(self): ) def test_grand_calculated_summary_multiple_sections(self): - self.launchSurvey("test_grand_calculated_summary_multiple_sections") + """ + Use the repeating answers schema to test the grand calculated summary which uses calculated summaries in multiple different sections + """ + self.launchSurvey("test_grand_calculated_summary_repeating_answers") # section 1 self.post() self.post({"q1-a1": 10, "q1-a2": 20}) @@ -36,7 +39,7 @@ def test_grand_calculated_summary_multiple_sections(self): self.post() # confirm calculated and grand calculated summary self.assertInBody( - "Calculated summary for section 1 is calculated to be £210.00. Is this correct?" + "Calculated summary for food and clothing is calculated to be £210.00. Is this correct?" ) self.post() self.assertInBody( @@ -51,7 +54,7 @@ def test_grand_calculated_summary_multiple_sections(self): # grand calculated summary section with calculated summaries from multiple sections self.post() self.assertInBody( - "Grand Calculated Summary for section 1 and 2 is calculated to be £510.00. Is this correct?" + "Grand Calculated Summary for shopping and entertainment is calculated to be £510.00. Is this correct?" ) def _complete_upto_grand_calculated_summary_cross_section_dependencies(self): From 6fe5e24e168c9a2d95ff752c97a25c930391f53c Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:30:34 +0100 Subject: [PATCH 268/567] Bug Fix: Editing list items 404 when using multiple list collectors with repeating blocks (#1184) --- app/views/handlers/list_edit_question.py | 11 +- app/views/handlers/list_repeating_question.py | 6 - ...t_collector_repeating_blocks_with_hub.json | 398 +++++++++++++++++- .../list_collector_repeating_blocks.spec.js | 30 +- ...onnaire_list_collector_repeating_blocks.py | 28 ++ 5 files changed, 463 insertions(+), 10 deletions(-) diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index fa5b4b30cc..9148b01225 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -1,9 +1,18 @@ +from functools import cached_property + from flask import url_for from app.views.handlers.list_action import ListAction class ListEditQuestion(ListAction): + @cached_property + def repeating_block_ids(self) -> list[str]: + return [ + repeating_block["id"] + for repeating_block in self.parent_block.get("repeating_blocks", []) + ] + def is_location_valid(self) -> bool: list_item_doesnt_exist = ( self._current_location.list_item_id @@ -24,7 +33,7 @@ def get_next_location_url(self) -> str: return url if first_incomplete_block := self.get_first_incomplete_list_repeating_block_location_for_list_item( - repeating_block_ids=self._schema.list_collector_repeating_block_ids, + repeating_block_ids=self.repeating_block_ids, section_id=self.current_location.section_id, # Type ignore: list_name and list_item_id will exist at this point list_item_id=self.current_location.list_item_id, # type: ignore diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py index c256878b6a..29287ca6e1 100644 --- a/app/views/handlers/list_repeating_question.py +++ b/app/views/handlers/list_repeating_question.py @@ -1,5 +1,3 @@ -from functools import cached_property - from flask import url_for from werkzeug.datastructures import ImmutableDict @@ -7,10 +5,6 @@ class ListRepeatingQuestion(ListEditQuestion): - @cached_property - def repeating_block_ids(self) -> list[str]: - return self._schema.list_collector_repeating_block_ids - def get_previous_location_url(self) -> str: """ return to previous location, or when return to is None, navigate to the previous repeating block diff --git a/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json b/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json index d6fe0cfe47..9eff68c1a7 100644 --- a/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json +++ b/schemas/test/en/test_list_collector_repeating_blocks_with_hub.json @@ -57,6 +57,42 @@ { "answer_id": "any-other-trading-details-answer", "code": "4" + }, + { + "answer_id": "responsible-party-business-answer", + "code": "5" + }, + { + "answer_id": "any-businesses-or-branches-answer", + "code": "6" + }, + { + "answer_id": "business-or-branch-name", + "code": "6a" + }, + { + "answer_id": "registration-business-number", + "code": "6b" + }, + { + "answer_id": "registration-business-date", + "code": "6c" + }, + { + "answer_id": "authorised-business-trader-uk-radio", + "code": "6d" + }, + { + "answer_id": "authorised-business-trader-eu-radio", + "code": "6e" + }, + { + "answer_id": "any-other-business-businesses-or-branches-answer", + "code": "7" + }, + { + "answer_id": "any-other-business-trading-details-answer", + "code": "8" } ], "questionnaire_flow": { @@ -68,7 +104,7 @@ "sections": [ { "id": "section-companies", - "title": "General insurance business", + "title": "General insurance companies", "summary": { "show_on_completion": true, "items": [ @@ -425,6 +461,366 @@ ] } ] + }, + { + "id": "section-businesses", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "businesses", + "title": "Businesses or UK branches", + "item_anchor_answer_id": "business-or-branch-name", + "item_label": "Name of UK business or branch", + "add_link_text": "Add another UK business or branch", + "empty_list_text": "No UK business or branch added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-businesses", + "blocks": [ + { + "type": "Question", + "id": "responsible-party-business", + "question": { + "type": "General", + "id": "responsible-party-business-question", + "title": "Are you the responsible party for reporting trading details for a business of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-business-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "any-businesses-or-branches", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-business-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-businesses-or-branches", + "for_list": "businesses", + "question": { + "type": "General", + "id": "any-businesses-or-branches-question", + "title": "Do any businesses or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-businesses-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-business", + "list_name": "businesses" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-businesses-or-branches-answer" + }, + "Yes" + ] + }, + "block": "any-other-business-businesses-or-branches" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-business-businesses-or-branches", + "type": "ListCollector", + "for_list": "businesses", + "question": { + "id": "any-other-business-businesses-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK businesses or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-business-businesses-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-business", + "type": "ListAddQuestion", + "question": { + "id": "add-question-businesses", + "type": "General", + "title": "What is the name and registration number of the business?", + "answers": [ + { + "id": "business-or-branch-name", + "label": "Name of UK business or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "repeating_blocks": [ + { + "id": "businesses-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "businesses-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {business_name}", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-business-number", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-business-date", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "businesses-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "businesses-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {business_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-business-date" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this business been trading in the UK? (Mandatory)", + "id": "authorised-business-trader-uk-radio", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this business been trading in the EU? (Not mandatory)", + "id": "authorised-business-trader-eu-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ], + "edit_block": { + "id": "edit-business", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-businesses", + "type": "General", + "title": "What is the name and registration number of the business?", + "answers": [ + { + "id": "business-or-branch-name", + "label": "Name of UK business or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-business", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-businesses", + "type": "General", + "title": "Are you sure you want to remove this business or UK branch?", + "answers": [ + { + "id": "remove-confirmation-business", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Businesses or UK branches", + "item_title": { + "text": "{business_name}", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + } + ] + } + } + }, + { + "id": "any-other-business-trading-details", + "type": "Question", + "question": { + "id": "any-other-business-trading-details-question", + "type": "General", + "title": "Do you have any other details about the trading you have reported for?", + "answers": [ + { + "id": "any-other-business-trading-details-answer", + "label": "Additional details", + "mandatory": false, + "type": "TextField" + } + ] + } + } + ] + } + ] } ] } diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index f73dd16b05..135d3029e3 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -11,6 +11,7 @@ import AnyOtherTradingDetailsPage from "../../../generated_pages/list_collector_ import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/submit.page"; import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; +import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; const summaryValues = 'dd[class="ons-summary__values"]'; async function proceedToListCollector() { @@ -320,11 +321,36 @@ describe("Given a journey through the list collector with repeating blocks, in a await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); await $(CompaniesRepeatingBlock2Page.submit()).click(); }); - - it("The section can now be submitted and and hub completed.", async () => { + it("The user is able to add additional company via a list collector, edit this newly added list item and return to list collector page", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(SectionCompaniesPage.submit()).click(); + await $(HubPage.summaryRowLink("section-companies")).click(); + await $(SectionCompaniesPage.companiesListAddLink()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("MOJ"); + await $(AddCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.previous()).click(); + await $(EditCompanyPage.previous()).click(); + await $(AnyOtherCompaniesOrBranchesPage.listEditLink(4)).click(); + await expect(await browser.getUrl()).to.contain(EditCompanyPage.pageName); + await $(EditCompanyPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.previous()).click(); + await $(EditCompanyPage.previous()).click(); + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.submit()).click(); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); + await $(CompaniesRepeatingBlock2Page.submit()).click(); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); await $(SectionCompaniesPage.submit()).click(); + }); + it("The section can now be submitted and and hub will redirect user to the next section.", async () => { await $(HubPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ResponsiblePartyHubPage.pageName); }); }); diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py index 4c8cebeccf..5bfe207f5a 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py @@ -474,3 +474,31 @@ def test_edit_repeating_block_from_submit_page_returns_to_submit_page( # previous link should return to submit page self.previous() self.assertInUrl("/submit/") + + def test_previous_link_in_two_list_collectors_with_repeating_blocks_returns_to_previous_location( + self, + ): + self.launchSurvey("test_list_collector_repeating_blocks_with_hub") + self.post({"responsible-party-answer": "Yes"}) + + # Add some items to first list collector in first section + self.add_three_companies() + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post({"any-other-trading-details-answer": "No other details"}) + + # Progress through the first section summary and the hub to second section + self.post() + self.post() + + # Answer initial questions in the second section + self.post({"responsible-party-business-answer": "Yes"}) + self.post({"any-businesses-or-branches-answer": "Yes"}) + + # Add item name using second list collector + self.post({"business-or-branch-name": "Business1"}) + + # Navigate back using previous link + self.previous() + + # previous link should return to second list collector's edit first item page + self.assertInUrl("/edit-business/") From 5e9ebed1c92228c79c5f0a4462acd3b9224a3081 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:50:55 +0100 Subject: [PATCH 269/567] Schemas v3.68.0 (#1190) Co-authored-by: petechd <53475968+petechd@users.noreply.github.com> --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0217daedd6..fbcc8f8389 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.67.0 +v3.68.0 From f6fbf0e81d5924b0ea992f1c6b1989b086303935 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:05:25 +0100 Subject: [PATCH 270/567] Bug Fix: Schemas with dynamic answers blocks in separate section to the list collector not loading (#1183) --- app/questionnaire/questionnaire_schema.py | 110 +++++++++-------- .../questionnaire_store_updater.py | 4 +- app/questionnaire/value_source_resolver.py | 8 +- .../contexts/summary/list_collector_block.py | 13 +- .../en/test_dynamic_answers_list_source.json | 112 +++++++++++++++++- .../test_questionnaire_schema.py | 15 +-- .../dynamic_answers_list_value_source.spec.js | 109 ++++++++++------- 7 files changed, 249 insertions(+), 122 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index e11a43dc4c..85521f1963 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -61,7 +61,10 @@ def __init__( self, questionnaire_json: Mapping, language_code: str = DEFAULT_LANGUAGE_CODE ): self._parent_id_map: dict[str, str] = {} - self._list_name_to_section_map: dict[str, list[str]] = {} + self._section_dependencies_by_list_name: dict[str, list[str]] = {} + self._list_collector_section_ids_by_list_name: dict[ + str, list[str] + ] = defaultdict(list) self._answer_dependencies_map: dict[str, set[AnswerDependent]] = defaultdict( set ) @@ -302,6 +305,9 @@ def _get_blocks_by_id(self) -> dict[str, ImmutableDict]: "PrimaryPersonListCollector", "RelationshipCollector", ): + self._list_collector_section_ids_by_list_name[ + block["for_list"] + ].append(self._parent_id_map[group["id"]]) for nested_block_name in [ "add_block", "edit_block", @@ -554,36 +560,34 @@ def _update_answer_dependencies_for_list_source( blocks like dynamic_answers, don't directly need to depend on the add_block/remove_block, but a block depending on the dynamic answers might (such as a calculated summary) """ - # Type ignore: section will always exist at this point, same with optional returns below - section: ImmutableDict = self.get_section_for_block_id(block_id) # type: ignore - list_collector: ImmutableDict = self.get_list_collector_for_list( # type: ignore - section=section, + list_collectors = self.get_list_collectors_for_list( for_list=list_name, ) - add_block_question = self.get_add_block_for_list_collector( # type: ignore - list_collector["id"] - )["question"] - answer_ids_for_block = list( - self.get_answers_for_question_by_id(add_block_question) - ) - for block_answer_id in answer_ids_for_block: - self._answer_dependencies_map[block_answer_id] |= { - self._get_answer_dependent_for_block_id( - block_id=block_id, for_list=list_name - ) - if self.is_block_in_repeating_section(block_id) - # non-repeating blocks such as dynamic-answers could depend on the list - else self._get_answer_dependent_for_block_id(block_id=block_id) - } - self._list_dependent_block_additional_dependencies[block_id] = set( - answer_ids_for_block - ) - # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies - if remove_block_id := self.get_remove_block_id_for_list(list_name): - self._list_dependent_block_additional_dependencies[block_id].update( - self.get_answer_ids_for_block(remove_block_id) + for list_collector in list_collectors: + add_block_question = self.get_add_block_for_list_collector( + list_collector["id"] # type: ignore + )["question"] + answer_ids_for_block = list( + self.get_answers_for_question_by_id(add_block_question) + ) + for block_answer_id in answer_ids_for_block: + self._answer_dependencies_map[block_answer_id] |= { + self._get_answer_dependent_for_block_id( + block_id=block_id, for_list=list_name + ) + if self.is_block_in_repeating_section(block_id) + # non-repeating blocks such as dynamic-answers could depend on the list + else self._get_answer_dependent_for_block_id(block_id=block_id) + } + self._list_dependent_block_additional_dependencies[block_id] = set( + answer_ids_for_block ) + # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies + if remove_block_id := self.get_remove_block_id_for_list(list_name): + self._list_dependent_block_additional_dependencies[block_id].update( + self.get_answer_ids_for_block(remove_block_id) + ) def _get_answer_dependent_for_block_id( self, @@ -627,10 +631,10 @@ def get_section(self, section_id: str) -> ImmutableDict | None: def get_section_ids_dependent_on_list(self, list_name: str) -> list[str]: try: - return self._list_name_to_section_map[list_name] + return self._section_dependencies_by_list_name[list_name] except KeyError: section_ids = self._section_ids_associated_to_list_name(list_name) - self._list_name_to_section_map[list_name] = section_ids + self._section_dependencies_by_list_name[list_name] = section_ids return section_ids def get_submission(self) -> ImmutableDict: @@ -905,30 +909,36 @@ def get_questions(self, question_id: str) -> list[ImmutableDict] | None: """ return self._questions_by_id.get(question_id) - @staticmethod - def get_list_collectors_for_list( - section: Mapping, for_list: str, primary: bool = False - ) -> Generator[ImmutableDict, None, None]: - collector_type = "PrimaryPersonListCollector" if primary else "ListCollector" + def get_list_collectors_for_list_for_sections( + self, sections: list[str], for_list: str, primary: bool = False + ) -> list[ImmutableDict]: + blocks: list[ImmutableDict] = [] + for section_id in sections: + if section := self.get_section(section_id): + collector_type = ( + "PrimaryPersonListCollector" if primary else "ListCollector" + ) - return ( - block - for block in QuestionnaireSchema.get_blocks_for_section(section) - if block["type"] == collector_type and block["for_list"] == for_list + blocks.extend( + block + for block in self.get_blocks_for_section(section) + if block["type"] == collector_type and block["for_list"] == for_list + ) + + return blocks + + def get_list_collectors_for_list( + self, for_list: str, primary: bool = False, section_id: str | None = None + ) -> list[ImmutableDict]: + sections = ( + [section_id] + if section_id + else self._list_collector_section_ids_by_list_name[for_list] ) - @staticmethod - def get_list_collector_for_list( - section: Mapping, for_list: str, primary: bool = False - ) -> ImmutableDict | None: - try: - return next( - QuestionnaireSchema.get_list_collectors_for_list( - section, for_list, primary - ) - ) - except StopIteration: - return None + return self.get_list_collectors_for_list_for_sections( + sections, for_list, primary + ) @classmethod def get_answers_for_question_by_id( diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index d0541c8642..13c820258b 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -170,9 +170,7 @@ def _capture_dependencies_for_list_item_removal(self, list_name: str) -> None: (E.g. dynamic-answers depending on a child block could have validation, so need revisiting if an item is removed) """ for list_collector in self._schema.get_list_collectors_for_list( - for_list=list_name, - # Type ignore: section must exist at this point - section=self._schema.get_section(self._current_location.section_id), # type: ignore + for_list=list_name ): child_blocks = ( list_collector.get("add_block"), diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index c36382ccbb..edd64eb648 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -52,13 +52,9 @@ def _is_block_on_path(self, block_id: str) -> bool: if not self.routing_path_block_ids: return True + # repeating blocks aren't on the path, so check the parent list collector if block_id in self.schema.list_collector_repeating_block_ids: - # repeating blocks aren't on the path, so check the parent list collector - list_name = self.schema.list_names_by_list_repeating_block_id[block_id] - # Type ignore: section and list collector will both exist if the block is repeating - section: ImmutableDict = self.schema.get_section_for_block_id(block_id) # type: ignore - list_collector_block: ImmutableDict = self.schema.get_list_collector_for_list(section, list_name) # type: ignore - return list_collector_block["id"] in self.routing_path_block_ids + return self.schema.parent_id_map[block_id] in self.routing_path_block_ids return block_id in self.routing_path_block_ids diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index d0dfbf1021..b6b510bd5c 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -72,7 +72,7 @@ def list_summary_element(self, summary: Mapping) -> dict: list_collector_blocks = list( self._schema.get_list_collectors_for_list( - self._section, for_list=summary["for_list"] + for_list=summary["for_list"], section_id=self._section["id"] ) ) @@ -107,12 +107,13 @@ def list_summary_element(self, summary: Mapping) -> dict: item_label = self._schema.get_item_label(section_id, list_model.name) if len(list_model) == 1 and list_model.primary_person: - if primary_person_block := self._schema.get_list_collector_for_list( - self._section, for_list=summary["for_list"], primary=True + if primary_list_collectors := self._schema.get_list_collectors_for_list( + for_list=summary["for_list"], primary=True ): - primary_person_edit_block_id = edit_block_id = primary_person_block[ - "add_or_edit_block" - ]["id"] + for primary_person_block in primary_list_collectors: + primary_person_edit_block_id = edit_block_id = primary_person_block[ + "add_or_edit_block" + ]["id"] list_summary_context = self.list_context( list_collector_block["summary"], diff --git a/schemas/test/en/test_dynamic_answers_list_source.json b/schemas/test/en/test_dynamic_answers_list_source.json index 4c2ebf94ef..b77837d41d 100644 --- a/schemas/test/en/test_dynamic_answers_list_source.json +++ b/schemas/test/en/test_dynamic_answers_list_source.json @@ -22,13 +22,13 @@ } ], "questionnaire_flow": { - "type": "Linear", - "options": {} + "type": "Hub", + "options": { "required_completed_sections": ["list-collector-section"] } }, "sections": [ { - "id": "section", - "title": "List Collector Section", + "id": "list-collector-section", + "title": "Supermarket Shopping Section", "summary": { "show_on_completion": true, "items": [ @@ -44,7 +44,7 @@ }, "groups": [ { - "id": "group", + "id": "list-collector-group", "blocks": [ { "type": "ListCollectorDrivingQuestion", @@ -481,6 +481,108 @@ ] } ] + }, + { + "id": "dynamic-answers-section", + "title": "Online Shopping Section", + "enabled": { + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "any-supermarket-answer" + } + ] + } + }, + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "dynamic-answers-group", + "blocks": [ + { + "type": "Question", + "id": "dynamic-answer-separate-section", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "supermarkets" + } + ] + }, + 0 + ] + } + }, + "question": { + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "supermarkets" + }, + "answers": [ + { + "label": { + "text": "Percentage of online shopping at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "id": "percentage-of-online-shopping", + "mandatory": false, + "type": "Percentage", + "maximum": { + "value": 100 + }, + "decimal_places": 0 + }, + { + "id": "online-days-a-week", + "label": { + "text": "How many days a week do you shop online at {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "supermarket-name" + } + } + ] + }, + "mandatory": false, + "type": "Number", + "decimal_places": 0, + "minimum": { + "value": 1 + }, + "maximum": { + "value": 7 + } + } + ] + }, + "id": "dynamic-answer-online-question", + "title": "What percent of your online shopping do you do at each of the following supermarket?", + "type": "General" + } + } + ] + } + ] } ] } diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 450456c69f..ac08a92094 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -333,24 +333,15 @@ def test_is_repeating_answer_within_list_collector( def test_get_list_collector_for_list(list_collector_variant_schema): schema = QuestionnaireSchema(list_collector_variant_schema) - section = schema.get_section("section") - result = QuestionnaireSchema.get_list_collector_for_list(section, for_list="people") + result = schema.get_list_collectors_for_list(for_list="people") - assert result["id"] == "block1" + assert result[0]["id"] == "block1" - filtered_result = QuestionnaireSchema.get_list_collector_for_list( - section, for_list="people" - ) + filtered_result = schema.get_list_collectors_for_list(for_list="people") assert filtered_result == result - no_result = QuestionnaireSchema.get_list_collector_for_list( - section, for_list="not-valid" - ) - - assert no_result is None - def test_has_address_lookup_answer(): question = { diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index b66c0a8853..104afe9454 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -5,7 +5,9 @@ import ListCollectorPage from "../../generated_pages/dynamic_answers_list_source import ListCollectorAddPage from "../../generated_pages/dynamic_answers_list_source/list-collector-add.page"; import ListCollectorRemovePage from "../../generated_pages/dynamic_answers_list_source/list-collector-remove.page"; import SetMinimumPage from "../../generated_pages/dynamic_answers_list_source/minimum-spending.page"; -import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/section-summary.page"; +import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/list-collector-section-summary.page"; +import HubPage from "../../base_pages/hub.page"; +import OnlineShoppingPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer-separate-section.page"; describe("Dynamic answers list value source", () => { const summaryTitles = ".ons-summary__item-title"; @@ -52,15 +54,15 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(8); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(8); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(8); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(8); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are revisited, Then they should be visible and have correct values", async () => { await addTwoSupermarkets(timeout); @@ -90,21 +92,21 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $$(DynamicAnswerPage.inputs())[1].setValue(12); await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); }); it("Given list items have been added and the dynamic answers are submitted, When the summary edit answer link is used for dynamic answer, Then the focus is on correct answer option", async () => { await addTwoSupermarkets(timeout); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[0].$("a").click(); + await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).to.be.true; await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[1].$("a").click(); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[1].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).to.be.true; }); @@ -113,12 +115,12 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.groupContent(2)).$$(summaryActions)[0].$("a").click(); + await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $(DynamicAnswerPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); }); it("Given list items have been added and the dynamic answers are submitted, When the list items are removed and answers updated, Then they should be displayed correctly on summary", async () => { await addTwoSupermarkets(timeout); @@ -134,13 +136,13 @@ describe("Dynamic answers list value source", () => { await $(SetMinimumPage.submit()).click(); await $(DynamicAnswerOnlyPage.submit()).click(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(5); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(5); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(5); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(5); }); - it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No', Then after changing answer to 'Yes' all answers should re-appear on summary", async () => { + it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No' and subsequently changed back to 'Yes', Then all answers should re-appear on summary", async () => { await addTwoSupermarkets(timeout); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); @@ -150,22 +152,27 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); await $(DriverPage.submit()).click(); - await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.false; - await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.false; + await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Tesco"); + await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Aldi"); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await $(DriverPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.supermarketsListEditLink(1)).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.supermarketsListAddLink()).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles).length).to.equal(8); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryValues).length).to.equal(8); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(8); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(8); + }); + + it("Given list items have been added, When the dynamic answers are displayed in a separate section, Then the correct answers should be visible", async () => { + await addTwoSupermarketsAndGetToNextSection(timeout); + await expect(await $$(OnlineShoppingPage.labels())[0].getText()).to.equal("Percentage of online shopping at Tesco"); + await expect(await $$(OnlineShoppingPage.labels())[1].getText()).to.equal("Percentage of online shopping at Aldi"); + await expect(await $$(OnlineShoppingPage.labels()).length).to.equal(4); }); }); @@ -185,10 +192,32 @@ async function addTwoSupermarkets(timeout) { await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); } +async function addTwoSupermarketsAndGetToNextSection(timeout) { + await $(DriverPage.yes()).click(); + await $(DriverPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); + await $(ListCollectorAddPage.setMaximum()).setValue(10000); + await $(ListCollectorAddPage.submit()).click(); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await $$(DynamicAnswerPage.inputs())[0].setValue(12); + await $$(DynamicAnswerPage.inputs())[1].setValue(21); + await $$(DynamicAnswerPage.inputs())[2].setValue(3); + await $$(DynamicAnswerPage.inputs())[3].setValue(7); + await setMinimumAndGetSectionSummary(timeout); + await $(SectionSummaryPage.submit()).click(); + await $(HubPage.submit()).click(); +} + async function setMinimumAndGetSectionSummary(timeout) { await $(DynamicAnswerPage.submit()).click(); await $(SetMinimumPage.setMinimum()).setValue(2); await $(SetMinimumPage.submit()).click(); await $(DynamicAnswerOnlyPage.submit()).click(); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout }); + await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); } From 71effd023a8086bb79fba4bfa105ba250cf86f51 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 29 Aug 2023 12:09:32 +0100 Subject: [PATCH 271/567] Fix functional tests flakiness (#1193) --- tests/functional/helpers.js | 13 + ...edirect_to_list_add_block_checkbox.spec.js | 16 +- ...n_redirect_to_list_add_block_radio.spec.js | 16 +- .../functional/spec/census_thank_you.spec.js | 5 +- tests/functional/spec/checkbox.spec.js | 53 ++-- .../spec/components/address/address.spec.js | 27 +- .../checkbox/checkbox_detail_answer.spec.js | 13 +- .../checkbox_detail_answer_dropdown.spec.js | 22 +- .../checkbox_detail_answer_multiple.spec.js | 18 +- .../checkbox_detail_answer_numeric.spec.js | 16 +- .../checkbox/checkbox_label.spec.js | 14 +- .../mutually_exclusive_checkbox.spec.js | 13 +- .../mutually_exclusive_currency.spec.js | 11 +- ...ally_exclusive_day_month_year_date.spec.js | 11 +- .../mutually_exclusive_duration.spec.js | 11 +- ...mutually_exclusive_month_year_date.spec.js | 11 +- ...tually_exclusive_multiple_checkbox.spec.js | 23 +- ...usive_multiple_day_month_year_date.spec.js | 19 +- ...ually_exclusive_multiple_textfield.spec.js | 19 +- .../mutually_exclusive_number.spec.js | 11 +- .../mutually_exclusive_percentage.spec.js | 11 +- .../mutually_exclusive_textarea.spec.js | 7 +- .../mutually_exclusive_textfield.spec.js | 11 +- .../mutually_exclusive_unit.spec.js | 11 +- .../mutually_exclusive_year_date.spec.js | 11 +- .../spec/components/dropdown/dropdown.spec.js | 27 +- .../functional/spec/components/radio/radio.js | 22 +- .../radio_detail_answer_dropdown.spec.js | 20 +- .../radio_detail_answer_multiple.spec.js | 16 +- .../radio/radio_detail_answer_numeric.spec.js | 16 +- .../radio/radio_visible_answers.spec.js | 8 +- .../radio/radio_voluntary_answers.spec.js | 8 +- .../spec/conditional_combined_routing.spec.js | 9 +- .../spec/confirmation_email.spec.js | 37 +-- .../functional/spec/content_variants.spec.js | 5 +- .../spec/custom_page_titles.spec.js | 31 +-- tests/functional/spec/dates.spec.js | 41 +-- tests/functional/spec/dob_date.spec.js | 5 +- tests/functional/spec/durations.spec.js | 21 +- tests/functional/spec/error_messages.spec.js | 17 +- tests/functional/spec/exit.spec.js | 13 +- .../calculated_summary_test_case.js | 162 ++++++------ ...mmary_repeating_and_static_answers.spec.js | 81 +++--- ...alculated_summary_repeating_blocks.spec.js | 97 ++++---- ...lculated_summary_repeating_section.spec.js | 234 +++++++++--------- .../conditional_checkbox_title.spec.js | 14 +- .../features/confirmation_question.spec.js | 13 +- ..._question_within_repeating_section.spec.js | 18 +- .../default_value/default_value.spec.js | 12 +- .../dynamic_answer_options/function_driven.js | 46 ++-- .../function_driven_mandatory.js | 18 +- .../radio_options_from_checkbox_answers.js | 10 +- .../dynamic_answers_list_value_source.spec.js | 59 ++--- .../enabled_section_checkbox.spec.js | 12 +- .../enabled_section_hub.spec.js | 10 +- .../enabled_section_radio.spec.js | 10 +- ...summary_cross_section_dependencies.spec.js | 58 ++--- ...ulated_summary_overlapping_answers.spec.js | 46 ++-- ...lculated_summary_repeating_answers.spec.js | 177 +++++++------ .../choose_another_section.spec.js | 8 +- .../hub_and_spoke/hub_and_spoke.spec.js | 66 ++--- .../hub_and_spoke_custom_content.spec.js | 8 +- .../hub_and_spoke_required_enable.spec.js | 8 +- .../features/hub_and_spoke/previous.spec.js | 8 +- .../last_viewed_guidance.spec.js | 8 +- .../last_viewed_guidance_hub.spec.js | 28 +-- ...option_based_on_first_item_in_list.spec.js | 44 ++-- .../placeholder_date_difference.spec.js | 10 +- .../placeholder_date_ranges.spec.js | 12 +- .../placeholder_default_value.spec.js | 18 +- .../placeholder/placeholder_metadata.spec.js | 4 +- ...laceholder_option_label_from_value.spec.js | 6 +- .../placeholder/playback_confirmation.spec.js | 4 +- .../progress/progress_value_source_blocks.js | 74 +++--- .../progress_value_source_repeating.js | 98 ++++---- .../custom_question_summary.spec.js | 14 +- .../list_collector_repeating_blocks.spec.js | 131 +++++----- ...eating_sections_with_hub_and_spoke.spec.js | 104 ++++---- .../spec/features/routing/all_in.spec.js | 7 +- .../spec/features/routing/and.spec.js | 17 +- .../routing/answer_comparison_routing.spec.js | 13 +- .../routing/answer_not_on_path.spec.js | 10 +- .../routing/answered_unanswered.spec.js | 32 +-- .../spec/features/routing/any_in.spec.js | 8 +- .../features/routing/checkbox_count.spec.js | 10 +- .../spec/features/routing/date.spec.js | 48 ++-- .../spec/features/routing/in.spec.js | 6 +- .../spec/features/routing/not.spec.js | 6 +- .../spec/features/routing/number.spec.js | 34 +-- .../spec/features/routing/or.spec.js | 18 +- .../routing/removes_completed_block.spec.js | 6 +- .../section_summary/section_summary.spec.js | 73 +++--- ...section_summary_repeating_sections.spec.js | 15 +- ...show_section_summary_on_completion.spec.js | 5 +- .../answer_comparison_skip_conditions.spec.js | 13 +- .../features/submit_page/submit_page.spec.js | 7 +- .../submit_page_collapsible_summary.spec.js | 17 +- ...submit_page_custom_submission_text.spec.js | 4 +- .../submit_page/submit_page_summary.spec.js | 32 +-- ...bmit_with_summary_return_to_answer.spec.js | 48 ++-- .../supplementary_data.spec.js | 106 ++++---- tests/functional/spec/features/units.spec.js | 30 +-- .../date-combined-mm-yyyy.spec.js | 18 +- .../date-combined-yyyy.spec.js | 12 +- .../date_validation/date-combined.spec.js | 11 +- .../date_validation/date-range.spec.js | 14 +- .../date_validation/date-single.spec.js | 20 +- .../features/validation/sum/dynamic.spec.js | 44 ++-- .../features/validation/sum/equal.spec.js | 16 +- .../validation/sum/equal_multiple.spec.js | 16 +- .../validation/sum/equal_or_less_than.spec.js | 22 +- ...qual_total_in_separate_section_hub.spec.js | 30 +-- ...otal_in_separate_section_repeating.spec.js | 46 ++-- .../features/validation/sum/less_than.spec.js | 18 +- .../validation/sum/value_source.spec.js | 32 +-- .../view_submitted_response.spec.js | 63 ++--- tests/functional/spec/feedback.spec.js | 11 +- .../functional/spec/interviewer_note.spec.js | 7 +- tests/functional/spec/language_code.spec.js | 49 ++-- tests/functional/spec/list_collector.spec.js | 81 +++--- .../list_collector_driving_question.spec.js | 24 +- ...ollector_driving_question_checkbox.spec.js | 36 +-- .../list_collector_primary_person.spec.js | 37 +-- .../list_collector_section_summary.spec.js | 36 +-- .../spec/list_collector_variants.spec.js | 22 +- ..._collector_variants_primary_person.spec.js | 37 +-- tests/functional/spec/mobile_number.spec.js | 7 +- .../functional/spec/multiple_answers.spec.js | 5 +- tests/functional/spec/multiple_piping.spec.js | 9 +- tests/functional/spec/numbers.spec.js | 25 +- .../spec/percentage_decimal.spec.js | 9 +- .../placeholder_answers_on_the_path.spec.js | 41 +-- .../spec/question_description.spec.js | 7 +- .../functional/spec/question_variants.spec.js | 19 +- ...estion_variants_first_item_in_list.spec.js | 23 +- .../spec/radio_checkbox_descriptions.spec.js | 3 +- ...tional_with_detail_answer_optional.spec.js | 11 +- .../spec/relationships-unrelated.spec.js | 31 +-- tests/functional/spec/relationships.spec.js | 98 ++++---- .../spec/relationships_primary.spec.js | 35 +-- ..._and_skipping_section_dependencies.spec.js | 151 +++++------ ...on_dependencies_calculated_summary.spec.js | 95 +++---- .../spec/routing_checkbox_contains.spec.js | 16 +- tests/functional/spec/save_sign_out.spec.js | 16 +- .../spec/skip_condition_block.spec.js | 6 +- .../spec/skip_condition_group.spec.js | 6 +- .../spec/skip_condition_list.spec.js | 34 +-- .../spec/skip_conditions_not_set.spec.js | 6 +- .../spec/skip_conditions_set.spec.js | 6 +- ...submit_with_custom_submission_text.spec.js | 4 +- tests/functional/spec/textarea.spec.js | 8 +- tests/functional/spec/textfield.spec.js | 6 +- .../spec/textfield_suggestions.spec.js | 6 +- tests/functional/spec/thank_you.spec.js | 14 +- .../functional/spec/timeout/timeout_modal.js | 3 +- ...eout_modal_post_submission_expired.spec.js | 5 +- ...out_modal_post_submission_extended.spec.js | 5 +- ...ost_submission_extended_new_window.spec.js | 5 +- 158 files changed, 2239 insertions(+), 2162 deletions(-) diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 3097d9c6ee..2f3ac3ef64 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -34,3 +34,16 @@ export const repeatingAnswerChangeLink = (answerIndex) => { export const listItemIds = () => { return $$("[data-list-item-id]").map((listItem) => listItem.getAttribute("data-list-item-id")); }; + +export const click = async (selector) => { + // This was introduced due to a css property on ons-btn: + // .ons-btn:active { + // top: 0.1666666667em + // } + // When the button is right on the very edge of the screen, webdriverio sees that the button is accessible, so does not scroll + // but clicks down on the very top of the button which moves down and just below the mouse. When the mouse click is released + // it's no longer over the button and the click silently fails. This means that when the test comes to do assertions on the following page + // they fail, as we never navigated to that page. + await $(selector).scrollIntoView({ block: "center", inline: "center" }); + await $(selector).click(); +}; diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js index e60bdc1929..f93a3b2334 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-remove.page"; @@ -12,14 +12,14 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { it('When the user selects "No", Then, they should be taken to the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); it('When the user selects "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.iThinkSo()).click(); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); }); @@ -30,10 +30,10 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - await $(AnyoneLiveAtListCollectorAddPage.submit()).click(); + await click(AnyoneLiveAtListCollectorAddPage.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; @@ -46,14 +46,14 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); - await $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); + await click(AnyoneLiveAtListCollectorRemovePage.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; }); @@ -64,7 +64,7 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { await $(AnyoneLiveAtListCollector.previous()).click(); await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); }); }); diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js index 0cef38a688..db643cb71d 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-remove.page"; @@ -12,14 +12,14 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { it('When the user answers "No", Then, they should be taken to straight the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); it('When the user answers "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.yes()).click(); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); }); @@ -30,10 +30,10 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - await $(AnyoneLiveAtListCollectorAddPage.submit()).click(); + await click(AnyoneLiveAtListCollectorAddPage.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; @@ -46,14 +46,14 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); - await $(AnyoneLiveAtListCollectorRemovePage.submit()).click(); + await click(AnyoneLiveAtListCollectorRemovePage.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; }); @@ -64,7 +64,7 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { await $(AnyoneLiveAtListCollector.previous()).click(); await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); - await $(AnyoneUsuallyLiveAt.submit()).click(); + await click(AnyoneUsuallyLiveAt.submit()); await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); }); }); diff --git a/tests/functional/spec/census_thank_you.spec.js b/tests/functional/spec/census_thank_you.spec.js index e939bd6aa5..478fa0033f 100644 --- a/tests/functional/spec/census_thank_you.spec.js +++ b/tests/functional/spec/census_thank_you.spec.js @@ -2,6 +2,7 @@ import { SubmitPage } from "../base_pages/submit.page.js"; import HubPage from "../base_pages/hub.page"; import ThankYouPage from "../base_pages/thank-you.page"; +import { click } from "../helpers"; describe("Thank You Census Household", () => { describe("Given I launch a census schema without feedback enabled", () => { @@ -10,8 +11,8 @@ describe("Thank You Census Household", () => { }); it("When I navigate to the thank you page, Then I should not see the feedback call to action", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.feedback()).isExisting()).to.equal(false); }); diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index 9f13e9e24c..38a634bb6b 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -2,6 +2,7 @@ import MandatoryCheckboxPage from "../generated_pages/checkbox/mandatory-checkbo import NonMandatoryCheckboxPage from "../generated_pages/checkbox/non-mandatory-checkbox.page"; import singleCheckboxPage from "../generated_pages/checkbox/single-checkbox.page"; import SubmitPage from "../generated_pages/checkbox/submit.page"; +import { click } from "../helpers"; describe('Checkbox with "other" option', () => { beforeEach("Load the survey", async () => { @@ -14,14 +15,14 @@ describe('Checkbox with "other" option', () => { it("Given a label has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the label should be visible", async () => { await $(MandatoryCheckboxPage.none()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $("body").getText()).to.have.string("Select any answers that apply"); }); it("Given that there is only one checkbox, When the checkbox answer is displayed, Then no label should be present", async () => { await $(MandatoryCheckboxPage.none()).click(); - await $(MandatoryCheckboxPage.submit()).click(); - await $(NonMandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); + await click(NonMandatoryCheckboxPage.submit()); await expect(await $("body").getText()).to.not.have.string("Select all that apply"); }); @@ -34,14 +35,14 @@ describe('Checkbox with "other" option', () => { it("Given a mandatory checkbox answer, When I select the other option, leave the input field empty and submit, Then an error should be displayed.", async () => { // When await $(MandatoryCheckboxPage.other()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; }); it("Given a mandatory checkbox answer, When I leave the input field empty and submit, Then the question text should be hidden in the error message using a span element.", async () => { // When - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(MandatoryCheckboxPage.error()).getHTML()).to.contain( 'Select at least one answer to ‘Which pizza toppings would you like?’', @@ -50,12 +51,12 @@ describe('Checkbox with "other" option', () => { it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.s", async () => { await $(MandatoryCheckboxPage.other()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; // When await $(MandatoryCheckboxPage.otherDetail()).setValue("Other Text"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(NonMandatoryCheckboxPage.pageName); }); @@ -63,9 +64,9 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.other()).click(); await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - await $(MandatoryCheckboxPage.submit()).click(); - await $(NonMandatoryCheckboxPage.submit()).click(); - await $(singleCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); + await click(NonMandatoryCheckboxPage.submit()); + await click(singleCheckboxPage.submit()); // Then await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("No answer provided"); }); @@ -74,10 +75,10 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.other()).click(); await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(NonMandatoryCheckboxPage.other()).click(); - await $(NonMandatoryCheckboxPage.submit()).click(); - await $(singleCheckboxPage.submit()).click(); + await click(NonMandatoryCheckboxPage.submit()); + await click(singleCheckboxPage.submit()); // Then await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("Other"); }); @@ -86,11 +87,11 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.other()).click(); await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(NonMandatoryCheckboxPage.other()).click(); await $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); - await $(NonMandatoryCheckboxPage.submit()).click(); - await $(singleCheckboxPage.submit()).click(); + await click(NonMandatoryCheckboxPage.submit()); + await click(singleCheckboxPage.submit()); // Then await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("The other value"); }); @@ -98,11 +99,11 @@ describe('Checkbox with "other" option', () => { it("Given that there is an escaped character in an answer label, when the user selects the answer, then the label should be displayed on the summary screen", async () => { // When await $(MandatoryCheckboxPage.hamCheese()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(NonMandatoryCheckboxPage.other()).click(); await $(NonMandatoryCheckboxPage.otherDetail()).setValue("The other value"); - await $(NonMandatoryCheckboxPage.submit()).click(); - await $(singleCheckboxPage.submit()).click(); + await click(NonMandatoryCheckboxPage.submit()); + await click(singleCheckboxPage.submit()); // Then await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.contain("Ham & Cheese"); }); @@ -111,11 +112,11 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.other()).click(); await $(MandatoryCheckboxPage.otherDetail()).setValue("Other value"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(NonMandatoryCheckboxPage.previous()).click(); await $(MandatoryCheckboxPage.other()).click(); await $(MandatoryCheckboxPage.hamCheese()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(NonMandatoryCheckboxPage.previous()).click(); // Then await $(MandatoryCheckboxPage.other()).click(); @@ -125,8 +126,8 @@ describe('Checkbox with "other" option', () => { it("Given a mandatory checkbox answer, when the user selects only one option, then the answer should not be displayed as a list on the summary screen", async () => { // When await $(MandatoryCheckboxPage.ham()).click(); - await $(MandatoryCheckboxPage.submit()).click(); - await $(NonMandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); + await click(NonMandatoryCheckboxPage.submit()); // Then const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; @@ -139,9 +140,9 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.ham()).click(); await $(MandatoryCheckboxPage.hamCheese()).click(); - await $(MandatoryCheckboxPage.submit()).click(); - await $(NonMandatoryCheckboxPage.submit()).click(); - await $(singleCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); + await click(NonMandatoryCheckboxPage.submit()); + await click(singleCheckboxPage.submit()); const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; diff --git a/tests/functional/spec/components/address/address.spec.js b/tests/functional/spec/components/address/address.spec.js index e3abfdc536..436ab0904e 100644 --- a/tests/functional/spec/components/address/address.spec.js +++ b/tests/functional/spec/components/address/address.spec.js @@ -2,6 +2,7 @@ import AddressConfirmation from "../../../generated_pages/address/address-confir import AddressMandatory from "../../../generated_pages/address/address-block-mandatory.page"; import AddressOptional from "../../../generated_pages/address/address-block-optional.page"; import SubmitPage from "../../../generated_pages/address/submit.page"; +import { click } from "../../../helpers"; describe("Address Answer Type", () => { beforeEach("Launch survey", async () => { @@ -15,9 +16,9 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Town()).setValue("Barry"); await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - await $(AddressMandatory.submit()).click(); - await $(AddressOptional.submit()).click(); - await $(AddressConfirmation.submit()).click(); + await click(AddressMandatory.submit()); + await click(AddressOptional.submit()); + await click(AddressConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); await expect(await $(SubmitPage.addressMandatory()).getHTML()).to.contain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); @@ -28,9 +29,9 @@ describe("Address Answer Type", () => { it("When the user enters only address line 1, Then the summary only displays address line 1", async () => { await $(AddressMandatory.Line1()).setValue("Evelyn Street"); - await $(AddressMandatory.submit()).click(); - await $(AddressOptional.submit()).click(); - await $(AddressConfirmation.submit()).click(); + await click(AddressMandatory.submit()); + await click(AddressOptional.submit()); + await click(AddressConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street"); }); @@ -38,7 +39,7 @@ describe("Address Answer Type", () => { describe("Given the user is on an mandatory address input question", () => { it("When the user submits the page without entering address line 1, Then an error is displayed", async () => { - await $(AddressMandatory.submit()).click(); + await click(AddressMandatory.submit()); await expect(await $(AddressMandatory.error()).getText()).to.equal("Enter an address"); }); }); @@ -47,10 +48,10 @@ describe("Address Answer Type", () => { it("When the user submits the page without entering any fields, Then the summary should display `No answer provided`.", async () => { // Get to optional address question await $(AddressMandatory.Line1()).setValue("Evelyn Street"); - await $(AddressMandatory.submit()).click(); + await click(AddressMandatory.submit()); - await $(AddressOptional.submit()).click(); - await $(AddressConfirmation.submit()).click(); + await click(AddressOptional.submit()); + await click(AddressConfirmation.submit()); await expect(await $(SubmitPage.addressOptional()).getText()).to.equal("No answer provided"); }); }); @@ -62,7 +63,7 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Town()).setValue("Barry"); await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - await $(AddressMandatory.submit()).click(); + await click(AddressMandatory.submit()); await expect(await browser.getUrl()).to.contain(AddressOptional.pageName); await browser.url(AddressMandatory.url()); @@ -79,8 +80,8 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Line2()).setValue("Apt 7"); await $(AddressMandatory.Town()).setValue("Barry"); await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); - await $(AddressMandatory.submit()).click(); - await $(AddressOptional.submit()).click(); + await click(AddressMandatory.submit()); + await click(AddressOptional.submit()); await expect(await $(AddressConfirmation.questionText()).getText()).to.equal("Please confirm the first line of your address is Evelyn Street"); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js index 31c7d3a2ba..b644c913ce 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js @@ -2,6 +2,7 @@ import CheckboxVisibleTruePage from "../../../generated_pages/checkbox_detail_an import CheckboxVisibleFalsePage from "../../../generated_pages/checkbox_detail_answer_textfield/checkbox-visible-false.page.js"; import CheckboxVisibleNonePage from "../../../generated_pages/checkbox_detail_answer_textfield/checkbox-visible-none.page.js"; import MutuallyExclusivePage from "../../../generated_pages/checkbox_detail_answer_textfield/mutually-exclusive.page.js"; +import { click } from "../../../helpers"; describe("Given the checkbox detail_answer questionnaire,", () => { beforeEach(async () => { @@ -16,23 +17,23 @@ describe("Given the checkbox detail_answer questionnaire,", () => { }); it("When a checkbox has a detail_answer with visible set to false, Then the detail answer write-in field should not be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); - await $(CheckboxVisibleTruePage.submit()).click(); + await click(CheckboxVisibleTruePage.submit()); await expect(await $(CheckboxVisibleFalsePage.otherDetail()).isDisplayed()).to.be.false; }); it("When a checkbox has a detail_answer with visible not set, Then the detail answer write-in field should not be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); - await $(CheckboxVisibleTruePage.submit()).click(); + await click(CheckboxVisibleTruePage.submit()); await $(CheckboxVisibleFalsePage.iceCream()).click(); - await $(CheckboxVisibleFalsePage.submit()).click(); + await click(CheckboxVisibleFalsePage.submit()); await expect(await $(CheckboxVisibleNonePage.otherDetail()).isDisplayed()).to.be.false; }); it("When a mutually exclusive checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); - await $(CheckboxVisibleTruePage.submit()).click(); + await click(CheckboxVisibleTruePage.submit()); await $(CheckboxVisibleFalsePage.iceCream()).click(); - await $(CheckboxVisibleFalsePage.submit()).click(); + await click(CheckboxVisibleFalsePage.submit()); await $(CheckboxVisibleNonePage.blue()).click(); - await $(CheckboxVisibleNonePage.submit()).click(); + await click(CheckboxVisibleNonePage.submit()); await expect(await $(MutuallyExclusivePage.otherDetail()).isDisplayed()).to.be.true; }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js index 77bacb77b9..2efb1b2ae1 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js @@ -1,7 +1,7 @@ import CheckboxDropdownPage from "../../../generated_pages/checkbox_detail_answer_dropdown/optional-checkbox-with-dropdown-detail-answer-block.page"; import SubmitPage from "../../../generated_pages/checkbox_detail_answer_dropdown/submit.page"; import DropdownMandatoryPage from "../../../generated_pages/dropdown_mandatory/dropdown-mandatory.page"; - +import { click } from "../../../helpers"; describe("Optional Checkbox with a Dropdown detail answer", () => { beforeEach(async () => { await browser.openQuestionnaire("test_checkbox_detail_answer_dropdown.json"); @@ -21,14 +21,14 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { }); it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { await $(CheckboxDropdownPage.fruit()).click(); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); @@ -36,7 +36,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { await $(CheckboxDropdownPage.fruit()).click(); await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); }); @@ -44,17 +44,17 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { await $(CheckboxDropdownPage.fruit()).click(); await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await $(SubmitPage.previous()).click(); await $(CheckboxDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { await $(CheckboxDropdownPage.jam()).click(); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); }); @@ -62,7 +62,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its details", async () => { await $(CheckboxDropdownPage.jam()).click(); await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); }); @@ -70,10 +70,10 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { it("When the user removes a previously submitted detail answer, Then the summary should not display the removed detail answer", async () => { await $(CheckboxDropdownPage.fruit()).click(); await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await $(SubmitPage.previous()).click(); await $(CheckboxDropdownPage.fruit()).click(); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); @@ -83,7 +83,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); await $(CheckboxDropdownPage.jam()).click(); await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - await $(CheckboxDropdownPage.submit()).click(); + await click(CheckboxDropdownPage.submit()); await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango\nJam\nStrawberry"); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js index 0def38c8dd..5ca268c37b 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js @@ -1,6 +1,6 @@ import MandatoryCheckboxPage from "../../../generated_pages/checkbox_detail_answer_multiple/mandatory-checkbox.page"; import SubmitPage from "../../../generated_pages/checkbox_detail_answer_multiple/submit.page"; - +import { click } from "../../../helpers"; describe('Checkbox with multiple "detail_answer" options', () => { const checkboxSchema = "test_checkbox_detail_answer_multiple.json"; @@ -21,7 +21,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); // Mandatory detail answer left blank await $(MandatoryCheckboxPage.yourChoice()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Enter your topping choice to continue"); @@ -31,12 +31,12 @@ describe('Checkbox with multiple "detail_answer" options', () => { // Given await browser.openQuestionnaire(checkboxSchema); await $(MandatoryCheckboxPage.yourChoice()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; // When await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -46,7 +46,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { // When await $(MandatoryCheckboxPage.cheese()).click(); await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese"); }); @@ -59,7 +59,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); await $(MandatoryCheckboxPage.yourChoice()).click(); await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese\nMozzarella\nYour choice\nBacon"); }); @@ -70,7 +70,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { // When await $(MandatoryCheckboxPage.yourChoice()).click(); await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Your choice\nBacon"); }); @@ -81,11 +81,11 @@ describe('Checkbox with multiple "detail_answer" options', () => { // When await $(MandatoryCheckboxPage.cheese()).click(); await $(MandatoryCheckboxPage.cheeseDetail()).setValue("Mozzarella"); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(SubmitPage.previous()).click(); await $(MandatoryCheckboxPage.cheese()).click(); await $(MandatoryCheckboxPage.ham()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await $(SubmitPage.previous()).click(); // Then await $(MandatoryCheckboxPage.cheese()).click(); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js index 4f6862221a..59a77740c4 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js @@ -1,6 +1,6 @@ import CheckboxNumericDetailPage from "../../../generated_pages/checkbox_detail_answer_numeric/checkbox-numeric-detail.page"; import SubmitPage from "../../../generated_pages/checkbox_detail_answer_numeric/submit.page"; - +import { click } from "../../../helpers"; describe('Checkbox with a numeric "detail_answer" option', () => { beforeEach(async () => { await browser.openQuestionnaire("test_checkbox_detail_answer_numeric.json"); @@ -14,7 +14,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("Other"); }); @@ -22,7 +22,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("15"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("15"); }); @@ -30,7 +30,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); @@ -39,7 +39,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("250"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); @@ -48,7 +48,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("-1"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); @@ -57,7 +57,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("biscuits"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); @@ -68,7 +68,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { // When await $(CheckboxNumericDetailPage.otherDetail()).setValue("0"); - await $(CheckboxNumericDetailPage.submit()).click(); + await click(CheckboxNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("0"); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_label.spec.js b/tests/functional/spec/components/checkbox/checkbox_label.spec.js index d60cd9c073..7cc7e15df8 100644 --- a/tests/functional/spec/components/checkbox/checkbox_label.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_label.spec.js @@ -1,7 +1,7 @@ import DefaultInstructionPage from "../../../generated_pages/checkbox_instruction/default-instruction-checkbox.page"; import NoInstructionPage from "../../../generated_pages/checkbox_instruction/no-instruction-checkbox.page"; import CustomInstructionPage from "../../../generated_pages/checkbox_instruction/custom-instruction-checkbox.page"; - +import { click } from "../../../helpers"; describe("Given the checkbox label variants questionnaire,", () => { beforeEach(async () => { await browser.openQuestionnaire("test_checkbox_instruction.json"); @@ -11,23 +11,23 @@ describe("Given the checkbox label variants questionnaire,", () => { }); it("Given an instruction has been set to null in the schema for a checkbox answer, When the checkbox answer is displayed, Then the instruction should not be visible", async () => { await $(DefaultInstructionPage.red()).click(); - await $(DefaultInstructionPage.submit()).click(); + await click(DefaultInstructionPage.submit()); await expect(await $("body").getText()).to.not.have.string("Select all that apply"); }); it("Given a custom instruction has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the custom instruction should be visible", async () => { await $(DefaultInstructionPage.red()).click(); - await $(DefaultInstructionPage.submit()).click(); + await click(DefaultInstructionPage.submit()); await $(NoInstructionPage.rugby()).click(); - await $(NoInstructionPage.submit()).click(); + await click(NoInstructionPage.submit()); await expect(await $("body").getText()).to.have.string("Select your answer"); }); it("Given a label and custom instruction have been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then both the custom instruction and label should be visible", async () => { await $(DefaultInstructionPage.red()).click(); - await $(DefaultInstructionPage.submit()).click(); + await click(DefaultInstructionPage.submit()); await $(NoInstructionPage.rugby()).click(); - await $(NoInstructionPage.submit()).click(); + await click(NoInstructionPage.submit()); await $(CustomInstructionPage.monday()).click(); - await $(CustomInstructionPage.submit()).click(); + await click(CustomInstructionPage.submit()); await expect(await $("body").getText()).to.have.string("Days of the Week"); await expect(await $("body").getText()).to.have.string("Select your answer"); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js index 126456e25f..4c16f9a8a1 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js @@ -1,5 +1,6 @@ import MandatoryCheckboxPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-checkbox.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-checkbox-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", () => { beforeEach(async () => { @@ -29,7 +30,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); @@ -40,7 +41,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", it("When the user returns to the question, Then the mutually exclusive other option should remain checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // When await $(SummaryPage.previous()).click(); @@ -65,7 +66,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -85,7 +86,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -102,7 +103,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", // When await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); @@ -119,7 +120,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js index 3c8915a8ab..4c1638eb62 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js @@ -1,5 +1,6 @@ import CurrencyPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-currency.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-currency-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Currency With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); - await $(CurrencyPage.submit()).click(); + await click(CurrencyPage.submit()); await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); @@ -41,7 +42,7 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", await $(CurrencyPage.currency()).getValue(); await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(CurrencyPage.submit()).click(); + await click(CurrencyPage.submit()); await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -60,7 +61,7 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", await expect(await $(CurrencyPage.currency()).getValue()).to.contain("123"); await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(CurrencyPage.submit()).click(); + await click(CurrencyPage.submit()); await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -77,7 +78,7 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(CurrencyPage.submit()).click(); + await click(CurrencyPage.submit()); await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); @@ -91,7 +92,7 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(CurrencyPage.submit()).click(); + await click(CurrencyPage.submit()); // Then await expect(await $(SummaryPage.currencyAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js index c45e29245a..131463a979 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js @@ -1,5 +1,6 @@ import DatePage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-date.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-date-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox Override", () => { beforeEach(async () => { @@ -27,7 +28,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); @@ -52,7 +53,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -75,7 +76,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); }); @@ -93,7 +94,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); @@ -109,7 +110,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(DatePage.submit()).click(); + await click(DatePage.submit()); // Then await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js index 0567ec7cc5..b39f61756b 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js @@ -1,5 +1,6 @@ import DurationPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-duration.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-duration-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Duration With Single Checkbox Override", () => { beforeEach(async () => { @@ -25,7 +26,7 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); @@ -47,7 +48,7 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -68,7 +69,7 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -87,7 +88,7 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); @@ -103,7 +104,7 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); // Then await expect(await $(SummaryPage.durationAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js index a5fe153634..2f9f3d5ee9 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js @@ -1,5 +1,6 @@ import MonthYearDatePage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-month-year-date.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-month-year-date-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Override", () => { beforeEach(async () => { @@ -24,7 +25,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); - await $(MonthYearDatePage.submit()).click(); + await click(MonthYearDatePage.submit()); await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); @@ -47,7 +48,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(MonthYearDatePage.submit()).click(); + await click(MonthYearDatePage.submit()); await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -68,7 +69,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(MonthYearDatePage.submit()).click(); + await click(MonthYearDatePage.submit()); await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -86,7 +87,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(MonthYearDatePage.submit()).click(); + await click(MonthYearDatePage.submit()); await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); @@ -101,7 +102,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(MonthYearDatePage.submit()).click(); + await click(MonthYearDatePage.submit()); // Then await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js index 0a87e9dc60..910b180f43 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js @@ -1,5 +1,6 @@ import MandatoryCheckboxPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-checkbox.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-checkbox-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", () => { beforeEach(async () => { @@ -32,7 +33,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); @@ -50,7 +51,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); @@ -61,7 +62,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", it("When the user returns to the question, Then the mutually exclusive option should remain checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // When await $(SummaryPage.previous()).click(); @@ -75,7 +76,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", it("When the user returns to the question, Then the mutually exclusive option should remain checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // When await $(SummaryPage.previous()).click(); @@ -100,7 +101,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -122,7 +123,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); @@ -143,7 +144,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -163,7 +164,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); @@ -175,7 +176,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); @@ -195,7 +196,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); @@ -213,7 +214,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; // When - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); // Then await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js index 81f7f6a67d..2d9c91b75e 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js @@ -1,5 +1,6 @@ import DatePage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-date.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-date-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio Override", () => { beforeEach(async () => { @@ -28,7 +29,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); @@ -46,7 +47,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -73,7 +74,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -100,7 +101,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -126,7 +127,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); @@ -147,7 +148,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // Then - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); @@ -161,7 +162,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // Then - await $(DatePage.submit()).click(); + await click(DatePage.submit()); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -179,7 +180,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; // When - await $(DatePage.submit()).click(); + await click(DatePage.submit()); // Then await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); @@ -197,7 +198,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - await $(DatePage.submit()).click(); + await click(DatePage.submit()); // Then await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js index 51053059bc..f521c45453 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js @@ -1,5 +1,6 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-textfield.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive_multiple/mutually-exclusive-textfield-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", () => { beforeEach(async () => { @@ -23,7 +24,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); @@ -38,7 +39,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -61,7 +62,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -84,7 +85,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -106,7 +107,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -126,7 +127,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // Then - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); @@ -139,7 +140,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; // Then - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -155,7 +156,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; // When - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); // Then await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); @@ -173,7 +174,7 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); // Then await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js index 46ca5f3c80..4e371beff7 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js @@ -1,5 +1,6 @@ import NumberPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-number.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-number-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Number With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(NumberPage.number()).getValue()).to.contain(""); - await $(NumberPage.submit()).click(); + await click(NumberPage.submit()); await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); @@ -41,7 +42,7 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( await expect(await $(NumberPage.number()).getValue()).to.contain("123"); await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(NumberPage.submit()).click(); + await click(NumberPage.submit()); await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -60,7 +61,7 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( await expect(await $(NumberPage.number()).getValue()).to.contain("123"); await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(NumberPage.submit()).click(); + await click(NumberPage.submit()); await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -77,7 +78,7 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(NumberPage.submit()).click(); + await click(NumberPage.submit()); await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); @@ -91,7 +92,7 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(NumberPage.submit()).click(); + await click(NumberPage.submit()); // Then await expect(await $(SummaryPage.numberAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js index ee491db85f..03908d8fb2 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js @@ -1,5 +1,6 @@ import PercentagePage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-percentage.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-percentage-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Percentage With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("99"); @@ -42,7 +43,7 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -61,7 +62,7 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -78,7 +79,7 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); @@ -92,7 +93,7 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); // Then await expect(await $(SummaryPage.percentageAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js index 67ea0e6f9a..6002cdf797 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js @@ -1,5 +1,6 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textarea.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textarea-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", () => { beforeEach(async () => { @@ -20,7 +21,7 @@ describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", await expect(await $(TextFieldPage.textarea()).getValue()).to.contain("Blue"); await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textareaAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textareaAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -37,7 +38,7 @@ describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.not.have.string("Blue"); @@ -51,7 +52,7 @@ describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); // Then await expect(await $(SummaryPage.textareaAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js index fd2de49deb..932f64a742 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js @@ -1,5 +1,6 @@ import TextFieldPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textfield.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-textfield-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Textfield With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); @@ -41,7 +42,7 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -60,7 +61,7 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -77,7 +78,7 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); @@ -91,7 +92,7 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); // Then await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js index 3589bc65f9..c8e741b64e 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js @@ -1,5 +1,6 @@ import UnitPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-unit.page"; import SummaryPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-unit-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(UnitPage.unit()).getValue()).to.contain(""); - await $(UnitPage.submit()).click(); + await click(UnitPage.submit()); await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); @@ -41,7 +42,7 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(UnitPage.submit()).click(); + await click(UnitPage.submit()); await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -60,7 +61,7 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(UnitPage.submit()).click(); + await click(UnitPage.submit()); await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -77,7 +78,7 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(UnitPage.submit()).click(); + await click(UnitPage.submit()); await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); @@ -91,7 +92,7 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(UnitPage.submit()).click(); + await click(UnitPage.submit()); // Then await expect(await $(SummaryPage.unitAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js index 6f45c75d81..8a82f80707 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js @@ -1,5 +1,6 @@ import YearDatePage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-year-date.page"; import SubmitPage from "../../../../generated_pages/mutually_exclusive/mutually-exclusive-year-date-section-summary.page"; +import { click } from "../../../../helpers"; describe("Component: Mutually Exclusive Year Date With Single Checkbox Override", () => { beforeEach(async () => { @@ -21,7 +22,7 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); - await $(YearDatePage.submit()).click(); + await click(YearDatePage.submit()); await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); @@ -41,7 +42,7 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(YearDatePage.submit()).click(); + await click(YearDatePage.submit()); await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -60,7 +61,7 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await $(YearDatePage.submit()).click(); + await click(YearDatePage.submit()); await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); @@ -77,7 +78,7 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; // Then - await $(YearDatePage.submit()).click(); + await click(YearDatePage.submit()); await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); @@ -91,7 +92,7 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; // When - await $(YearDatePage.submit()).click(); + await click(YearDatePage.submit()); // Then await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.contain("No answer provided"); diff --git a/tests/functional/spec/components/dropdown/dropdown.spec.js b/tests/functional/spec/components/dropdown/dropdown.spec.js index 652a35ba30..7f750c7a7c 100644 --- a/tests/functional/spec/components/dropdown/dropdown.spec.js +++ b/tests/functional/spec/components/dropdown/dropdown.spec.js @@ -3,6 +3,7 @@ import DropdownMandatorySummary from "../../../generated_pages/dropdown_mandator import DropdownMandatoryOverriddenPage from "../../../generated_pages/dropdown_mandatory_with_overridden_error/dropdown-mandatory-with-overridden-error.page"; import DropdownOptionalPage from "../../../generated_pages/dropdown_optional/dropdown-optional.page"; import DropdownOptionalSummary from "../../../generated_pages/dropdown_optional/submit.page"; +import { click } from "../../../helpers"; describe("Component: Dropdown", () => { // Mandatory @@ -13,19 +14,19 @@ describe("Component: Dropdown", () => { it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); }); it("When I have not selected a dropdown option and click Continue, Then the default error message should be displayed", async () => { - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.contain("Select an answer"); }); it("When I have selected a dropdown option and I try to select a default (disabled) dropdown option, Then the already selected option should be displayed in summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); await $(DropdownMandatoryPage.answer()).selectByAttribute("value", ""); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); }); @@ -36,23 +37,23 @@ describe("Component: Dropdown", () => { it("When I'm on the summary page and I click Edit then Continue, Then the answer on the summary page should be unchanged", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); }); it("When I'm on the summary page and I click Edit and change the answer, Then the newly selected answer should be displayed in the summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); - await $(DropdownMandatoryPage.submit()).click(); + await click(DropdownMandatoryPage.submit()); await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); }); }); @@ -63,7 +64,7 @@ describe("Component: Dropdown", () => { }); it("When I have not selected a dropdown option and click Continue, Then the overridden error message should be displayed", async () => { - await $(DropdownMandatoryOverriddenPage.submit()).click(); + await click(DropdownMandatoryOverriddenPage.submit()); await expect(await $(DropdownMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Overridden test error message."); }); }); @@ -75,23 +76,23 @@ describe("Component: Dropdown", () => { }); it('When I have not selected a dropdown option, Then the summary should display "No answer provided"', async () => { - await $(DropdownOptionalPage.submit()).click(); + await click(DropdownOptionalPage.submit()); await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); }); it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Rugby is better!"); - await $(DropdownOptionalPage.submit()).click(); + await click(DropdownOptionalPage.submit()); await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Rugby is better!"); }); it('When I have selected a dropdown option and I reselect the default option (Select an answer), Then the summary should display "No answer provided"', async () => { await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Chelsea"); - await $(DropdownOptionalPage.submit()).click(); + await click(DropdownOptionalPage.submit()); await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Chelsea"); await $(DropdownOptionalSummary.dropdownOptionalAnswerEdit()).click(); await $(DropdownOptionalPage.answer()).selectByAttribute("value", ""); - await $(DropdownOptionalPage.submit()).click(); + await click(DropdownOptionalPage.submit()); await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); }); }); diff --git a/tests/functional/spec/components/radio/radio.js b/tests/functional/spec/components/radio/radio.js index 97585100a7..28af6e2e1f 100644 --- a/tests/functional/spec/components/radio/radio.js +++ b/tests/functional/spec/components/radio/radio.js @@ -15,7 +15,7 @@ import RadioNonMandatoryDetailAnswerOverriddenPage from "../../../generated_page import RadioNonMandatoryDetailAnswerPage from "../../../generated_pages/radio_optional_with_detail_answer_mandatory/radio-non-mandatory.page"; import RadioNonMandatoryDetailAnswerSummary from "../../../generated_pages/radio_optional_with_detail_answer_mandatory/submit.page"; - +import { click } from "../../../helpers"; describe("Component: Radio", () => { describe("Given I start a Mandatory Radio survey", () => { before(async () => { @@ -24,7 +24,7 @@ describe("Component: Radio", () => { it("When I have selected a radio option that contains an escaped character, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.teaCoffee()).click(); - await $(RadioMandatoryPage.submit()).click(); + await click(RadioMandatoryPage.submit()); await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Tea & Coffee"); }); @@ -37,7 +37,7 @@ describe("Component: Radio", () => { it("When I have selected a radio option, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.coffee()).click(); - await $(RadioMandatoryPage.submit()).click(); + await click(RadioMandatoryPage.submit()); await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Coffee"); }); @@ -49,7 +49,7 @@ describe("Component: Radio", () => { }); it("When I have submitted the page without any option, Then the question text is hidden in the error message using a span element", async () => { - await $(RadioMandatoryOverriddenPage.submit()).click(); + await click(RadioMandatoryOverriddenPage.submit()); await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).to.contain( 'Select an answer to ‘What do you prefer for breakfast?’', ); @@ -64,7 +64,7 @@ describe("Component: Radio", () => { it("When I have selected a other text field, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryOptionalDetailAnswerPage.other()).click(); await $(RadioMandatoryOptionalDetailAnswerPage.otherDetail()).setValue("Hello World"); - await $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); + await click(RadioMandatoryOptionalDetailAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("Hello World"); }); @@ -77,7 +77,7 @@ describe("Component: Radio", () => { it("When I submit without any data in the other text field it should Then throw an overridden error", async () => { await $(RadioMandatoryDetailAnswerOverriddenPage.other()).click(); - await $(RadioMandatoryDetailAnswerOverriddenPage.submit()).click(); + await click(RadioMandatoryDetailAnswerOverriddenPage.submit()); await expect(await $(RadioMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); @@ -88,7 +88,7 @@ describe("Component: Radio", () => { }); it("When I submit without any data in the other text field is selected, Then the selected option should be displayed in the summary", async () => { - await $(RadioMandatoryOptionalDetailAnswerPage.submit()).click(); + await click(RadioMandatoryOptionalDetailAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("No answer provided"); }); @@ -100,7 +100,7 @@ describe("Component: Radio", () => { }); it("When I have submitted the page without any option, Then an overridden error is displayed", async () => { - await $(RadioMandatoryOverriddenPage.submit()).click(); + await click(RadioMandatoryOverriddenPage.submit()); await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); @@ -111,7 +111,7 @@ describe("Component: Radio", () => { }); it("When I have selected no option, Then the selected option should be displayed in the summary", async () => { - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await browser.getUrl()).to.contain(RadioNonMandatorySummary.pageName); await expect(await $(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); }); @@ -124,7 +124,7 @@ describe("Component: Radio", () => { it("When I have submitted an other option with an empty text field, Then an overridden error is displayed", async () => { await $(RadioNonMandatoryDetailAnswerOverriddenPage.other()).click(); - await $(RadioNonMandatoryDetailAnswerOverriddenPage.submit()).click(); + await click(RadioNonMandatoryDetailAnswerOverriddenPage.submit()); await expect(await $(RadioNonMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); }); }); @@ -137,7 +137,7 @@ describe("Component: Radio", () => { it("When I submit data in the other text field it should be persisted and Then displayed on the summary", async () => { await $(RadioNonMandatoryDetailAnswerPage.other()).click(); await $(RadioNonMandatoryDetailAnswerPage.otherDetail()).setValue("Hello World"); - await $(RadioNonMandatoryDetailAnswerPage.submit()).click(); + await click(RadioNonMandatoryDetailAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(RadioNonMandatoryDetailAnswerSummary.pageName); await expect(await $(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).to.contain("Hello World"); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js index 3e88f65451..654d0006ca 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js @@ -1,7 +1,7 @@ import RadioDropdownPage from "../../../generated_pages/radio_detail_answer_dropdown/optional-radio-with-dropdown-detail-answer-block.page"; import SubmitPage from "../../../generated_pages/radio_detail_answer_dropdown/submit.page"; import DropdownMandatoryPage from "../../../generated_pages/dropdown_mandatory/dropdown-mandatory.page"; - +import { click } from "../../../helpers"; describe("Optional Radio with a Dropdown detail answer", () => { beforeEach(async () => { await browser.openQuestionnaire("test_radio_detail_answer_dropdown.json"); @@ -21,14 +21,14 @@ describe("Optional Radio with a Dropdown detail answer", () => { }); it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); }); it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { await $(RadioDropdownPage.fruit()).click(); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); @@ -36,7 +36,7 @@ describe("Optional Radio with a Dropdown detail answer", () => { it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { await $(RadioDropdownPage.fruit()).click(); await $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); }); @@ -44,17 +44,17 @@ describe("Optional Radio with a Dropdown detail answer", () => { it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { await $(RadioDropdownPage.fruit()).click(); await $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await $(SubmitPage.previous()).click(); await $(RadioDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { await $(RadioDropdownPage.jam()).click(); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); }); @@ -62,7 +62,7 @@ describe("Optional Radio with a Dropdown detail answer", () => { it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its detail answer", async () => { await $(RadioDropdownPage.jam()).click(); await $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); }); @@ -70,10 +70,10 @@ describe("Optional Radio with a Dropdown detail answer", () => { it("When the user removes a previously submitted detail answer by selecting another radio option, Then the summary should only display the new radio option", async () => { await $(RadioDropdownPage.jam()).click(); await $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Raspberry"); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await $(SubmitPage.previous()).click(); await $(RadioDropdownPage.fruit()).click(); - await $(RadioDropdownPage.submit()).click(); + await click(RadioDropdownPage.submit()); await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js index 59b7820211..beb5cd0514 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js @@ -1,6 +1,6 @@ import MandatoryRadioPage from "../../../generated_pages/radio_detail_answer_multiple/radio-mandatory.page"; import SubmitPage from "../../../generated_pages/radio_detail_answer_multiple/submit.page"; - +import { click } from "../../../helpers"; describe('Radio with multiple "detail_answer" options', () => { const radioSchema = "test_radio_detail_answer_multiple.json"; @@ -17,7 +17,7 @@ describe('Radio with multiple "detail_answer" options', () => { await browser.openQuestionnaire(radioSchema); // When await $(MandatoryRadioPage.favouriteNotListed()).click(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); // Then await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; await expect(await $(MandatoryRadioPage.errorNumber(1)).getText()).to.contain("Enter your favourite to continue"); @@ -27,12 +27,12 @@ describe('Radio with multiple "detail_answer" options', () => { // Given await browser.openQuestionnaire(radioSchema); await $(MandatoryRadioPage.favouriteNotListed()).click(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; // When await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -42,7 +42,7 @@ describe('Radio with multiple "detail_answer" options', () => { // When await $(MandatoryRadioPage.eggs()).click(); await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); // Then await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs"); }); @@ -53,7 +53,7 @@ describe('Radio with multiple "detail_answer" options', () => { // When await $(MandatoryRadioPage.eggs()).click(); await $(MandatoryRadioPage.eggsDetail()).setValue("Scrambled"); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); // Then await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs\nScrambled"); }); @@ -64,10 +64,10 @@ describe('Radio with multiple "detail_answer" options', () => { // When await $(MandatoryRadioPage.favouriteNotListed()).click(); await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await $(SubmitPage.previous()).click(); await $(MandatoryRadioPage.eggs()).click(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await $(SubmitPage.previous()).click(); // Then await $(MandatoryRadioPage.favouriteNotListed()).click(); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js index 904d6d5324..6c71912857 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js @@ -1,6 +1,6 @@ import RadioNumericDetailPage from "../../../generated_pages/radio_detail_answer_numeric/radio-numeric-detail.page"; import SubmitPage from "../../../generated_pages/radio_detail_answer_numeric/submit.page"; - +import { click } from "../../../helpers"; describe('Radio with a numeric "detail_answer" option', () => { beforeEach(async () => { await browser.openQuestionnaire("test_radio_detail_answer_numeric.json"); @@ -14,7 +14,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("Other"); }); @@ -22,7 +22,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("15"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("15"); }); @@ -30,7 +30,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); @@ -39,7 +39,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("250"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); @@ -48,7 +48,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("-1"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); @@ -57,7 +57,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("biscuits"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); @@ -67,7 +67,7 @@ describe('Radio with a numeric "detail_answer" option', () => { it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { // When await $(RadioNumericDetailPage.otherDetail()).setValue("0"); - await $(RadioNumericDetailPage.submit()).click(); + await click(RadioNumericDetailPage.submit()); // Then await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("0"); }); diff --git a/tests/functional/spec/components/radio/radio_visible_answers.spec.js b/tests/functional/spec/components/radio/radio_visible_answers.spec.js index 612fbd7864..d9bc8202de 100644 --- a/tests/functional/spec/components/radio/radio_visible_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_visible_answers.spec.js @@ -1,7 +1,7 @@ import RadioVisibleTruePage from "../../../generated_pages/radio_detail_answer_visible/radio-visible-true.page.js"; import RadioVisibleFalsePage from "../../../generated_pages/radio_detail_answer_visible/radio-visible-false.page.js"; import RadioVisibleNonePage from "../../../generated_pages/radio_detail_answer_visible/radio-visible-none.page.js"; - +import { click } from "../../../helpers"; describe("Given I start a Radio survey with a write-in option", () => { beforeEach(async () => { await browser.openQuestionnaire("test_radio_detail_answer_visible.json"); @@ -18,15 +18,15 @@ describe("Given I start a Radio survey with a write-in option", () => { it("When I view a write-in radio and the visible option is set to false, Then the detail answer label should not be displayed", async () => { await $(RadioVisibleTruePage.coffee()).click(); - await $(RadioVisibleTruePage.submit()).click(); + await click(RadioVisibleTruePage.submit()); await expect(await $(RadioVisibleFalsePage.otherDetail()).isDisplayed()).to.equal(false); }); it("When I view a write-in radio and the visible option is not set, Then the detail answer label should not be displayed", async () => { await $(RadioVisibleTruePage.coffee()).click(); - await $(RadioVisibleFalsePage.submit()).click(); + await click(RadioVisibleFalsePage.submit()); await $(RadioVisibleFalsePage.iceCream()).click(); - await $(RadioVisibleFalsePage.submit()).click(); + await click(RadioVisibleFalsePage.submit()); await expect(await $(RadioVisibleNonePage.otherDetail()).isDisplayed()).to.equal(false); }); }); diff --git a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js index c6af4619cb..ba666fcdf1 100644 --- a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js @@ -1,6 +1,6 @@ import RadioVoluntaryTruePage from "../../../generated_pages/radio_voluntary/radio-voluntary-true.page.js"; import RadioVoluntaryFalsePage from "../../../generated_pages/radio_voluntary/radio-voluntary-false.page.js"; - +import { click } from "../../../helpers"; describe("Component: Radio", () => { describe("Given I start a Voluntary Radio survey", () => { before(async () => { @@ -21,17 +21,17 @@ describe("Component: Radio", () => { it("When I clear a previously saved voluntary radio option and submit, Then when returning to the page the radio option is no longer selected", async () => { await $(RadioVoluntaryTruePage.coffee()).click(); - await $(RadioVoluntaryTruePage.submit()).click(); + await click(RadioVoluntaryTruePage.submit()); await $(RadioVoluntaryTruePage.previous()).click(); await $(RadioVoluntaryTruePage.clearSelectionButton()).click(); - await $(RadioVoluntaryTruePage.submit()).click(); + await click(RadioVoluntaryTruePage.submit()); await $(RadioVoluntaryTruePage.previous()).click(); await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); }); it("When I select a non-voluntary radio option, Then the clear button should not be displayed on the page", async () => { - await $(RadioVoluntaryTruePage.submit()).click(); + await click(RadioVoluntaryTruePage.submit()); await $(RadioVoluntaryFalsePage.iceCream()).click(); await expect(await $(RadioVoluntaryFalsePage.clearSelectionButton()).isDisplayed()).to.equal(false); }); diff --git a/tests/functional/spec/conditional_combined_routing.spec.js b/tests/functional/spec/conditional_combined_routing.spec.js index 31b1fff6d0..0907db5481 100644 --- a/tests/functional/spec/conditional_combined_routing.spec.js +++ b/tests/functional/spec/conditional_combined_routing.spec.js @@ -2,6 +2,7 @@ import ConditionalCombinedRoutingPage from "../generated_pages/conditional_combi import ResponseAny from "../generated_pages/conditional_combined_routing/response-any.page"; import ResponseNotAny from "../generated_pages/conditional_combined_routing/response-not-any.page"; import SubmitPage from "../generated_pages/conditional_combined_routing/submit.page"; +import { click } from "../helpers"; describe("Conditional combined routing.", () => { beforeEach(async () => { @@ -11,7 +12,7 @@ describe("Conditional combined routing.", () => { it('Given a list of radio options, when I choose the option "Yes" or the option "Sometimes" then I should be routed to the relevant page', async () => { // When await $(ConditionalCombinedRoutingPage.yes()).click(); - await $(ConditionalCombinedRoutingPage.submit()).click(); + await click(ConditionalCombinedRoutingPage.submit()); // Then await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); @@ -20,7 +21,7 @@ describe("Conditional combined routing.", () => { // When await $(ConditionalCombinedRoutingPage.sometimes()).click(); - await $(ConditionalCombinedRoutingPage.submit()).click(); + await click(ConditionalCombinedRoutingPage.submit()); // Then await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); @@ -29,7 +30,7 @@ describe("Conditional combined routing.", () => { it('Given a list of radio options, when I choose the option "No, I prefer tea" then I should be routed to the relevant page', async () => { // When await $(ConditionalCombinedRoutingPage.noIPreferTea()).click(); - await $(ConditionalCombinedRoutingPage.submit()).click(); + await click(ConditionalCombinedRoutingPage.submit()); // Then await expect(await browser.getUrl()).to.contain(ResponseNotAny.pageName); }); @@ -37,7 +38,7 @@ describe("Conditional combined routing.", () => { it('Given a list of radio options, when I choose the option "No, I don\'t drink any hot drinks" then I should be routed to the submit page', async () => { // When await $(ConditionalCombinedRoutingPage.noIDonTDrinkAnyHotDrinks()).click(); - await $(ConditionalCombinedRoutingPage.submit()).click(); + await click(ConditionalCombinedRoutingPage.submit()); // Then await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index bb1cb7c429..7257be8242 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -4,6 +4,7 @@ import ThankYouPage from "../base_pages/thank-you.page"; import ConfirmationEmailPage from "../base_pages/confirmation-email.page"; import ConfirmationEmailSentPage from "../base_pages/confirmation-email-sent.page"; import ConfirmEmailPage from "../base_pages/confirm-email.page"; +import { click } from "../helpers"; describe("Email confirmation", () => { describe("Given I launch the test email confirmation survey", () => { @@ -12,14 +13,14 @@ describe("Email confirmation", () => { }); it("When I complete the survey and am on the thank you page, Then there is option to enter an email address", async () => { - await $(SubmitPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.email()).isExisting()).to.be.true; }); it("When I submit the form without providing an email address, Then I get an error message", async () => { - await $(ThankYouPage.submit()).click(); + await click(ThankYouPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address"); @@ -27,7 +28,7 @@ describe("Email confirmation", () => { it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ThankYouPage.email()).setValue("incorrect-format"); - await $(ThankYouPage.submit()).click(); + await click(ThankYouPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address in a valid format, for example name@example.com"); @@ -35,13 +36,13 @@ describe("Email confirmation", () => { it("When I submit the form with a valid email address, Then I go to the confirm email page", async () => { await $(ThankYouPage.email()).setValue("name@example.com"); - await $(ThankYouPage.submit()).click(); + await click(ThankYouPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); await expect(await $(ConfirmEmailPage.questionTitle()).getText()).to.equal("Is this email address correct?"); }); it("When I submit the confirm email page without providing an answer, Then I get an error message", async () => { - await $(ConfirmEmailPage.submit()).click(); + await click(ConfirmEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); await expect(await $(ConfirmEmailPage.errorPanel()).isExisting()).to.be.true; await expect(await $(ConfirmEmailPage.errorPanel()).getText()).to.contain("Select an answer"); @@ -49,14 +50,14 @@ describe("Email confirmation", () => { it("When I answer 'Yes' and submit the confirm email page, Then I go to email sent page", async () => { await $(ConfirmEmailPage.yes()).click(); - await $(ConfirmEmailPage.submit()).click(); + await click(ConfirmEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); }); it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", async () => { await $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); - await $(ConfirmationEmailPage.submit()).click(); + await click(ConfirmationEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address"); @@ -64,7 +65,7 @@ describe("Email confirmation", () => { it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ConfirmationEmailPage.email()).setValue("incorrect-format"); - await $(ConfirmationEmailPage.submit()).click(); + await click(ConfirmationEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address in a valid format, for example name@example.com"); @@ -72,9 +73,9 @@ describe("Email confirmation", () => { it("When I submit the form with a valid email and confirm it is correct, Then I go to the email confirmation page", async () => { await $(ConfirmationEmailPage.email()).setValue("name@example.com"); - await $(ConfirmationEmailPage.submit()).click(); + await click(ConfirmationEmailPage.submit()); await $(ConfirmEmailPage.yes()).click(); - await $(ConfirmEmailPage.submit()).click(); + await click(ConfirmEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); }); @@ -84,12 +85,12 @@ describe("Email confirmation", () => { await browser.openQuestionnaire("test_confirmation_email.json"); }); it("When I enter an email and answer 'No' on the confirm email page, Then I go the confirmation send page with the email pre-filled", async () => { - await $(SubmitPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); + await click(SubmitPage.submit()); await $(ThankYouPage.email()).setValue("name@example.com"); - await $(ThankYouPage.submit()).click(); + await click(ThankYouPage.submit()); await $(ConfirmEmailPage.no()).click(); - await $(ConfirmEmailPage.submit()).click(); + await click(ConfirmEmailPage.submit()); await expect(await browser.getUrl()).to.contain("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.email()).getValue()).to.equal("name@example.com"); }); @@ -102,10 +103,10 @@ describe("Email confirmation", () => { await browser.openQuestionnaire("test_confirmation_email.json"); }); it("When I view the email confirmation page, Then I should not see the feedback call to action", async () => { - await $(SubmitPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); + await click(SubmitPage.submit()); await $(ThankYouPage.email()).setValue("name@example.com"); - await $(ThankYouPage.submit()).click(); + await click(ThankYouPage.submit()); await expect(await $(ConfirmationEmailSentPage.feedbackLink()).isExisting()).to.equal(false); }); }); diff --git a/tests/functional/spec/content_variants.spec.js b/tests/functional/spec/content_variants.spec.js index e4938c94b6..5c7b1e3008 100644 --- a/tests/functional/spec/content_variants.spec.js +++ b/tests/functional/spec/content_variants.spec.js @@ -1,4 +1,5 @@ import ageQuestionBlock from "../generated_pages/variants_content/age-question-block.page.js"; +import { click } from "../helpers"; describe("QuestionVariants", () => { beforeEach(async () => { @@ -7,13 +8,13 @@ describe("QuestionVariants", () => { it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { await $(ageQuestionBlock.age()).setValue(12); - await $(ageQuestionBlock.submit()).click(); + await click(ageQuestionBlock.submit()); await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or younger"); }); it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { await $(ageQuestionBlock.age()).setValue(22); - await $(ageQuestionBlock.submit()).click(); + await click(ageQuestionBlock.submit()); await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or older"); }); }); diff --git a/tests/functional/spec/custom_page_titles.spec.js b/tests/functional/spec/custom_page_titles.spec.js index 246a24ec9e..d3759caa3e 100644 --- a/tests/functional/spec/custom_page_titles.spec.js +++ b/tests/functional/spec/custom_page_titles.spec.js @@ -6,6 +6,7 @@ import ListCollectorEditPage from "../generated_pages/custom_page_titles/list-co import ListCollectorPage from "../generated_pages/custom_page_titles/list-collector.page.js"; import ProxyPage from "../generated_pages/custom_page_titles/proxy.page.js"; import RelationshipsPage from "../generated_pages/custom_page_titles/relationships.page.js"; +import { click } from "../helpers"; describe("Feature: Custom Page Titles", () => { const schema = "test_custom_page_titles.json"; @@ -16,22 +17,22 @@ describe("Feature: Custom Page Titles", () => { }); it("When I navigate to the list collector page, Then I should see the custom page title", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); const expectedPageTitle = await browser.getTitle(); await expect(expectedPageTitle).to.equal("Custom page title - Test Custom Page Titles"); }); it("When I navigate to the add person page, Then I should see the custom page title", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); let expectedPageTitle = await browser.getTitle(); await expect(expectedPageTitle).to.equal("Add person 1 - Test Custom Page Titles"); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); expectedPageTitle = await browser.getTitle(); await expect(expectedPageTitle).to.equal("Add person 2 - Test Custom Page Titles"); }); @@ -39,29 +40,29 @@ describe("Feature: Custom Page Titles", () => { it("When I navigate to relationship collector pages, Then I should see the custom page titles", async () => { await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Olivia"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); let expectedPageTitle = await browser.getTitle(); await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 2 - Test Custom Page Titles"); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); expectedPageTitle = browser.getTitle(); await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); await expect(await expectedPageTitle).to.equal("How Person 2 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); await expect(await expectedPageTitle).to.equal("Custom section summary page title - Test Custom Page Titles"); }); @@ -78,13 +79,13 @@ describe("Feature: Custom Page Titles", () => { it("When I navigate to a repeating section which has custom page title, Then all page titles in the section should have the correct prefix", async () => { await browser.url(HubPage.url()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getTitle()).to.equal("Individual interstitial: Person 1 - Test Custom Page Titles"); - await $(IndividualInterstitialPage.submit()).click(); + await click(IndividualInterstitialPage.submit()); await expect(await browser.getTitle()).to.equal("Proxy question: Person 1 - Test Custom Page Titles"); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await expect(await browser.getTitle()).to.equal("What is your date of birth?: Person 1 - Test Custom Page Titles"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await expect(await browser.getTitle()).to.equal("Summary: Person 1 - Test Custom Page Titles"); }); }); diff --git a/tests/functional/spec/dates.spec.js b/tests/functional/spec/dates.spec.js index c3bcee459d..894a00058c 100644 --- a/tests/functional/spec/dates.spec.js +++ b/tests/functional/spec/dates.spec.js @@ -4,6 +4,7 @@ import DateSinglePage from "../generated_pages/dates/date-single-block.page"; import DateNonMandatoryPage from "../generated_pages/dates/date-non-mandatory-block.page"; import DateYearDatePage from "../generated_pages/dates/date-year-date-block.page"; import SubmitPage from "../generated_pages/dates/submit.page"; +import { click } from "../helpers"; describe("Date checks", () => { beforeEach("Load the survey", async () => { @@ -23,7 +24,7 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeTomonth()).setValue(5); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateMonthYearPage.legend()).getText()).to.contain("Date with month and year"); }); @@ -38,24 +39,24 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeTomonth()).setValue(5); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await $(DateMonthYearPage.Month()).setValue(4); await $(DateMonthYearPage.Year()).setValue(2018); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); await $(DateSinglePage.day()).setValue(4); await $(DateSinglePage.month()).setValue(1); await $(DateSinglePage.year()).setValue(1999); - await $(DateSinglePage.submit()).click(); + await click(DateSinglePage.submit()); - await $(DateNonMandatoryPage.submit()).click(); + await click(DateNonMandatoryPage.submit()); await $(DateYearDatePage.Year()).setValue(2005); - await $(DateYearDatePage.submit()).click(); + await click(DateYearDatePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -77,7 +78,7 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2015); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); // Then an error message is shown and the question panel is highlighted await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); @@ -98,7 +99,7 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2016); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); // Then an error message is shown and the question panel is highlighted await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); @@ -115,7 +116,7 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(""); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); // Then an error message is shown await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); @@ -128,13 +129,13 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeToday()).setValue(1); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); // When the year (month year type) is left empty await $(DateMonthYearPage.Month()).setValue(4); await $(DateMonthYearPage.Year()).setValue(""); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); // Then an error message is shown await expect(await $(DateMonthYearPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); @@ -147,18 +148,18 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeToday()).setValue(1); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); // When an error message is shown await $(DateMonthYearPage.Month()).setValue(4); await $(DateMonthYearPage.Year()).setValue(""); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); await expect(await $(DateMonthYearPage.error()).getText()).to.contain("Enter a valid date"); // Then when it is corrected, it goes to the next question await $(DateMonthYearPage.Year()).setValue(2018); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); await expect(await browser.getUrl()).to.contain(DateSinglePage.url()); }); @@ -170,21 +171,21 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeToday()).setValue(1); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await $(DateMonthYearPage.Month()).setValue(1); await $(DateMonthYearPage.Year()).setValue(2016); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); await $(DateSinglePage.day()).setValue(1); await $(DateSinglePage.month()).setValue(1); await $(DateSinglePage.year()).setValue(2016); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); // When non-mandatory is partially completed await $(DateNonMandatoryPage.day()).setValue(4); await $(DateNonMandatoryPage.month()).setValue(1); - await $(DateNonMandatoryPage.submit()).click(); + await click(DateNonMandatoryPage.submit()); // Then an error message is shown await expect(await $(DateNonMandatoryPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); @@ -197,11 +198,11 @@ describe("Date checks", () => { await $(DateRangePage.dateRangeToday()).setValue(1); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await $(DateMonthYearPage.Month()).setValue(1); await $(DateMonthYearPage.Year()).setValue(2016); - await $(DateMonthYearPage.submit()).click(); + await click(DateMonthYearPage.submit()); // When a user clicks the day label await $(DateSinglePage.dayLabel()).click(); diff --git a/tests/functional/spec/dob_date.spec.js b/tests/functional/spec/dob_date.spec.js index caac178f02..be5d9889aa 100644 --- a/tests/functional/spec/dob_date.spec.js +++ b/tests/functional/spec/dob_date.spec.js @@ -1,5 +1,6 @@ import DateOfBirthPage from "../generated_pages/dob_date/date-of-birth.page"; import UnderSixteenPage from "../generated_pages/dob_date/under-sixteen.page"; +import { click } from "../helpers"; describe("Date of birth check", () => { beforeEach("Load the survey", async () => { @@ -9,14 +10,14 @@ describe("Date of birth check", () => { await $(DateOfBirthPage.day()).setValue(12); await $(DateOfBirthPage.month()).setValue(4); await $(DateOfBirthPage.year()).setValue(2021); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are under 16!"); }); it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to over 16 page", async () => { await $(DateOfBirthPage.day()).setValue(12); await $(DateOfBirthPage.month()).setValue(4); await $(DateOfBirthPage.year()).setValue(1980); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are over 16!"); }); }); diff --git a/tests/functional/spec/durations.spec.js b/tests/functional/spec/durations.spec.js index 025202c442..87d0ea3402 100644 --- a/tests/functional/spec/durations.spec.js +++ b/tests/functional/spec/durations.spec.js @@ -1,5 +1,6 @@ import DurationPage from "../generated_pages/durations/duration-block.page.js"; import SubmitPage from "../generated_pages/durations/submit.page.js"; +import { click } from "../helpers"; describe("Durations", () => { beforeEach("Load the survey", async () => { @@ -20,11 +21,11 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("1 year 2 months"); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); it("Given the test_durations survey is selected when one of the units is 0 it is excluded from the summary", async () => { @@ -34,11 +35,11 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("2 months"); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); it("Given the test_durations survey is selected when no duration is entered the summary shows no answer provided", async () => { @@ -46,11 +47,11 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("No answer provided"); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); it("Given the test_durations survey is selected when one of the units is missing an error is shown", async () => { @@ -58,7 +59,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); @@ -71,7 +72,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(2); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); @@ -84,7 +85,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryYearMonthMonths()).setValue(12); await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); @@ -93,7 +94,7 @@ describe("Durations", () => { it("Given the test_durations survey is selected when the mandatory duration is missing an error is shown", async () => { await $(DurationPage.mandatoryYearYears()).setValue(1); await $(DurationPage.mandatoryMonthMonths()).setValue(1); - await $(DurationPage.submit()).click(); + await click(DurationPage.submit()); await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a duration"); }); diff --git a/tests/functional/spec/error_messages.spec.js b/tests/functional/spec/error_messages.spec.js index ac1d4a9fde..e0421c0d6f 100644 --- a/tests/functional/spec/error_messages.spec.js +++ b/tests/functional/spec/error_messages.spec.js @@ -1,5 +1,6 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; import BlockPage from "../generated_pages/percentage/block.page"; +import { click } from "../helpers"; async function answerAllButOne() { await $(AboutYou.textfield()).setValue("John Doe"); @@ -44,7 +45,7 @@ describe("Error Messages", () => { 14: "Enter an answer", }; - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There are 14 problems with your answer"); for (const [index, errorMessage] of Object.entries(errorMessageMap)) { @@ -53,7 +54,7 @@ describe("Error Messages", () => { }); it("Given a question has errors, When errors are displayed, Then the error message for each answer is correct", async () => { - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await expect(await $(AboutYou.textfieldErrorItem()).getText()).to.equal("Enter an answer"); await expect(await $(AboutYou.dateErrorItem()).getText()).to.equal("Enter a date"); @@ -72,26 +73,26 @@ describe("Error Messages", () => { }); it("Given a question has multiple errors, When the errors are displayed, Then the error messages are in a numbered list", async () => { - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await expect(await $(AboutYou.errorList()).isDisplayed()).to.be.true; }); it("Given a question has 1 error, When the error is displayed, Then error message isn't in a numbered list", async () => { await answerAllButOne(); - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await expect(await $(AboutYou.singleErrorLink()).isDisplayed()).to.be.true; }); it("Given a question has 1 error, When the error is displayed, Then error header is correct", async () => { await answerAllButOne(); - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There is a problem with your answer"); }); it("Given a question has errors, When an error message is clicked, Then the correct answer is focused", async () => { - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await $(AboutYou.errorNumber(1)).click(); await expect(await $(AboutYou.textfield()).isFocused()).to.be.true; @@ -142,13 +143,13 @@ describe("Error Message NaN value", () => { }); it("Given a NaN value was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { await $(BlockPage.answer()).setValue("NaN"); - await $(BlockPage.submit()).click(); + await click(BlockPage.submit()); await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); }); it("Given a NaN value with separators was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { await $(BlockPage.answer()).setValue(",NaN_"); - await $(BlockPage.submit()).click(); + await click(BlockPage.submit()); await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); }); diff --git a/tests/functional/spec/exit.spec.js b/tests/functional/spec/exit.spec.js index d43bfbcd79..fbfd493d29 100644 --- a/tests/functional/spec/exit.spec.js +++ b/tests/functional/spec/exit.spec.js @@ -1,6 +1,7 @@ import CensusThankYouPage from "../base_pages/census-thank-you.page.js"; import HubPage from "../base_pages/hub.page"; import { SubmitPage } from "../base_pages/submit.page.js"; +import { click } from "../helpers"; describe("Post submission exit", () => { beforeEach("Load the survey", async () => { @@ -8,23 +9,23 @@ describe("Post submission exit", () => { }); it("Given I click the exit button from the thank you page which has no session cookie, When I am redirected, Then I should be redirected to the correct log out url", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await browser.deleteAllCookies(); await $(CensusThankYouPage.exit()).click(); await expect(await browser.getUrl()).to.equal("https://surveys.ons.gov.uk/sign-in/"); }); it("Given I click the exit button from the thank you page, When I am redirected, Then I should be redirected to the correct log out url", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await $(CensusThankYouPage.exit()).click(); await expect(await browser.getUrl()).to.equal("https://census.gov.uk/en/start"); }); it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await $(CensusThankYouPage.exit()).click(); await browser.back(); await expect(await browser.getUrl()).to.contain("submitted/thank-you"); diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index b03d811c9b..5d3adad368 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -27,40 +27,40 @@ import SectionSummarySectionOne from "../../../generated_pages/calculated_summar import SectionSummarySectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; - +import { click } from "../../../helpers"; class TestCase { testCase(schema) { before("Get to Calculated Summary", async () => { await browser.openQuestionnaire(schema); await $(FirstNumberBlockPage.firstNumber()).setValue(1.23); - await $(FirstNumberBlockPage.submit()).click(); + await click(FirstNumberBlockPage.submit()); await $(SecondNumberBlockPage.secondNumber()).setValue(4.56); await $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); await $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); - await $(SecondNumberBlockPage.submit()).click(); + await click(SecondNumberBlockPage.submit()); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); await $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await click(ThirdAndAHalfNumberBlockPage.submit()); await $(SkipFourthBlockPage.no()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); await $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); - await $(FourthNumberBlockPage.submit()).click(); + await click(FourthNumberBlockPage.submit()); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); - await $(FourthAndAHalfNumberBlockPage.submit()).click(); + await click(FourthAndAHalfNumberBlockPage.submit()); await $(FifthNumberBlockPage.fifthPercent()).setValue(56); await $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); - await $(FifthNumberBlockPage.submit()).click(); + await click(FifthNumberBlockPage.submit()); await $(SixthNumberBlockPage.sixthPercent()).setValue(23); await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); - await $(SixthNumberBlockPage.submit()).click(); + await click(SixthNumberBlockPage.submit()); const browserUrl = await browser.getUrl(); @@ -118,14 +118,14 @@ class TestCase { it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { await $(CurrencyTotalPlaybackPage.fourthNumberAnswerEdit()).click(); await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); - await $(FourthNumberBlockPage.submit()).click(); + await click(FourthNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( @@ -137,7 +137,7 @@ class TestCase { it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); - await $(FourthAndAHalfNumberBlockPage.submit()).click(); + await click(FourthAndAHalfNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( @@ -155,10 +155,10 @@ class TestCase { await $(FourthNumberBlockPage.previous()).click(); await $(SkipFourthBlockPage.yes()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); const expectedUrl = await browser.getUrl(); @@ -173,7 +173,7 @@ class TestCase { it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(CurrencyTotalPlaybackPage.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); @@ -193,7 +193,7 @@ class TestCase { it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(UnitTotalPlaybackPage.submit()).click(); + await click(UnitTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); @@ -209,7 +209,7 @@ class TestCase { it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(UnitTotalPlaybackPage.submit()).click(); + await click(UnitTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be 124.58. Is this correct?", ); @@ -224,7 +224,7 @@ class TestCase { }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { - await $(NumberTotalPlaybackPage.submit()).click(); + await click(NumberTotalPlaybackPage.submit()); const content = await $("h1 + ul").getText(); const textsToAssert = [ @@ -239,48 +239,48 @@ class TestCase { }); it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -288,26 +288,26 @@ class TestCase { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -315,33 +315,33 @@ class TestCase { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("Given I am on a page with a placeholder containing a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the placeholder should display the updated value", async () => { await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await $(SkipFourthBlockPage.no()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await browser.url(CalculatedSummaryTotalConfirmation.url()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryTotalConfirmation.pageName); @@ -365,15 +365,15 @@ class TestCase { await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).to.contain( "Set minimum and maximum values based on your calculated summary total of £25.92", ); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £25.92"); await $(SetMinMaxBlockPage.setMinimum()).setValue(30.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); }); it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); } @@ -381,22 +381,22 @@ class TestCase { testCrossSectionDependencies(schema) { before("Get to the question containing calculated summary values with cross section dependencies", async () => { await browser.openQuestionnaire(schema); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(SkipFirstNumberBlockPageSectionOne.no()).click(); - await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await click(SkipFirstNumberBlockPageSectionOne.submit()); await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); - await $(FirstNumberBlockPageSectionOne.submit()).click(); + await click(FirstNumberBlockPageSectionOne.submit()); await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); - await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await click(FirstAndAHalfNumberBlockPageSectionOne.submit()); await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); - await $(SecondNumberBlockPageSectionOne.submit()).click(); - await $(CalculatedSummarySectionOne.submit()).click(); - await $(SectionSummarySectionOne.submit()).click(); - await $(HubPage.submit()).click(); + await click(SecondNumberBlockPageSectionOne.submit()); + await click(CalculatedSummarySectionOne.submit()); + await click(SectionSummarySectionOne.submit()); + await click(HubPage.submit()); await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); - await $(ThirdNumberBlockPageSectionTwo.submit()).click(); - await $(CalculatedSummarySectionTwo.submit()).click(); + await click(ThirdNumberBlockPageSectionTwo.submit()); + await click(CalculatedSummarySectionTwo.submit()); }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { @@ -411,24 +411,24 @@ class TestCase { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); - await $(DependencyQuestionSectionTwo.submit()).click(); + await click(DependencyQuestionSectionTwo.submit()); await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); - await $(MinMaxSectionTwo.submit()).click(); + await click(MinMaxSectionTwo.submit()); await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); - await $(MinMaxSectionTwo.submit()).click(); + await click(MinMaxSectionTwo.submit()); }); it("Given I remove answers from the path for a calculated summary in a previous section by changing an answer, When I return to the question with the calculated summary value source, Then the value displayed should be correct", async () => { - await $(SectionSummarySectionTwo.submit()).click(); + await click(SectionSummarySectionTwo.submit()); await $(HubPage.summaryRowLink("questions-section")).click(); await $(SectionSummarySectionOne.skipFirstBlockAnswerEdit()).click(); await $(SkipFirstNumberBlockPageSectionOne.yes()).click(); - await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); - await $(SectionSummarySectionOne.submit()).click(); + await click(SkipFirstNumberBlockPageSectionOne.submit()); + await click(SectionSummarySectionOne.submit()); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index d9bea2ef81..e139972c62 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -12,8 +12,7 @@ import ListCollectorRemovePage from "../../../generated_pages/new_calculated_sum import SupermarketTransportPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport.page"; import SupermarketTransportCostPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport-cost.page"; import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; -import { assertSummaryValues } from "../../../helpers"; - +import { assertSummaryValues, click } from "../../../helpers"; describe("Calculated summary with repeating answers", function () { // These tests are flaky therefore we add a retry. The cause is unknown. // :TODO: Revert this in future when we have a fix for this. @@ -25,17 +24,17 @@ describe("Calculated summary with repeating answers", function () { before("Completing the list collector and dynamic answer", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_and_static_answers.json"); await $(HubPage.acceptCookies()).click(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(AnySupermarketPage.yes()).click(); - await $(AnySupermarketPage.submit()).click(); + await click(AnySupermarketPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Lidl"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $$(DynamicAnswerPage.inputs())[0].setValue(300); await $$(DynamicAnswerPage.inputs())[1].setValue(200); await $$(DynamicAnswerPage.inputs())[2].setValue(30); @@ -43,18 +42,18 @@ describe("Calculated summary with repeating answers", function () { await $$(DynamicAnswerPage.inputs())[4].setValue(4); await $$(DynamicAnswerPage.inputs())[5].setValue(2); await $(DynamicAnswerPage.extraStatic()).setValue(5); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(ExtraSpendingBlockPage.extraSpending()).setValue(0); }); it("Given I complete all list collector dynamic answers for two calculated summaries one of which also has static answers, I'm taken to each one in turn, showing the correct answers", async () => { - await $(ExtraSpendingBlockPage.submit()).click(); + await click(ExtraSpendingBlockPage.submit()); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?", ); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).to.contain("£550.00"); await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total visits to the shop to be 6. Is this correct?", ); @@ -71,24 +70,24 @@ describe("Calculated summary with repeating answers", function () { it("Given I click on a change link, edit an answer and continue, I return to the calculated summary to reconfirm it", async () => { await dynamicAnswerChangeLink(0).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(3); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total visits to the shop to be 7. Is this correct?", ); await assertSummaryValues(["4", "3"]); - await $(CalculatedSummaryVisitsPage.submit()).click(); + await click(CalculatedSummaryVisitsPage.submit()); }); it("Given I go back and change an answer that opens up a new question before the calculated summary, I am taken to the new question, and then the calculated summary", async () => { await $(SummaryPage.extraSpendingAnswerEdit()).click(); await $(ExtraSpendingBlockPage.extraSpending()).setValue(50); - await $(ExtraSpendingBlockPage.submit()).click(); + await click(ExtraSpendingBlockPage.submit()); // new question await expect(await browser.getUrl()).to.contain(ExtraSpendingMethodBlockPage.pageName); await $(ExtraSpendingMethodBlockPage.yes()).click(); - await $(ExtraSpendingMethodBlockPage.submit()).click(); + await click(ExtraSpendingMethodBlockPage.submit()); // then calculated summary await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); @@ -97,29 +96,29 @@ describe("Calculated summary with repeating answers", function () { ); // then jump straight back to section summary (as other calculated summary is unchanged - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); }); it("Given I add a new item to the list, I return to the list collector block, then the dynamic answers, then both calculated summaries to confirm newly added answers", async () => { await $(SummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Sainsburys"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); // return to dynamic answer await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await $$(DynamicAnswerPage.inputs())[2].setValue(100); await $$(DynamicAnswerPage.inputs())[5].setValue(10); await $$(DynamicAnswerPage.inputs())[8].setValue(7); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); // Currently when a section is incomplete, you are taken to each block in the section in turn, if the return_to is inaccessible // this has been changed for calculated summaries to go to the first incomplete block, but not yet in the general case // so the expected behaviour is to revisit these two blocks before the calculated summary - await $(ExtraSpendingBlockPage.submit()).click(); - await $(ExtraSpendingMethodBlockPage.submit()).click(); + await click(ExtraSpendingBlockPage.submit()); + await click(ExtraSpendingMethodBlockPage.submit()); // first calc summary await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); @@ -129,12 +128,12 @@ describe("Calculated summary with repeating answers", function () { await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); // second calculated summary - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total visits to the shop to be 14. Is this correct?", ); await assertSummaryValues(["4", "3", "2"]); - await $(CalculatedSummaryVisitsPage.submit()).click(); + await click(CalculatedSummaryVisitsPage.submit()); await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); }); @@ -147,25 +146,25 @@ describe("Calculated summary with repeating answers", function () { await expect(await browser.getUrl()).to.contain(ListCollectorRemovePage.pageName); await $(ListCollectorRemovePage.yes()).click(); - await $(ListCollectorRemovePage.submit()).click(); + await click(ListCollectorRemovePage.submit()); // section is now incomplete so step through each block until calculated summary is re-confirmed await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await $(DynamicAnswerPage.submit()).click(); - await $(ExtraSpendingBlockPage.submit()).click(); - await $(ExtraSpendingMethodBlockPage.submit()).click(); + await click(DynamicAnswerPage.submit()); + await click(ExtraSpendingBlockPage.submit()); + await click(ExtraSpendingMethodBlockPage.submit()); // Tesco is now gone await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?", ); await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total visits to the shop to be 10. Is this correct?", ); await assertSummaryValues(["3", "7"]); - await $(CalculatedSummaryVisitsPage.submit()).click(); + await click(CalculatedSummaryVisitsPage.submit()); await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Lidl"); await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Sainsburys"); @@ -173,28 +172,28 @@ describe("Calculated summary with repeating answers", function () { }); it("Given I proceed to the second section and enter a value greater than the calculated summary from the previous section, the correct error message is displayed", async () => { - await $(SummaryPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SummaryPage.submit()); + await click(HubPage.submit()); await $(SupermarketTransportPage.weeklyCarTrips()).setValue(11); - await $(SupermarketTransportPage.submit()).click(); + await click(SupermarketTransportPage.submit()); await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 10"); }); it("Given I change my answer to a value less than the calculated summary from the previous section, I am able to proceed", async () => { await $(SupermarketTransportPage.weeklyCarTrips()).setValue(9); - await $(SupermarketTransportPage.submit()).click(); + await click(SupermarketTransportPage.submit()); await expect(await browser.getUrl()).to.contain(SupermarketTransportCostPage.pageName); }); it("Given I reach the final block, the calculated summary of dynamic answers is piped in correctly", async () => { await $(SupermarketTransportCostPage.weeklyTripsCost()).setValue(30); - await $(SupermarketTransportCostPage.submit()).click(); + await click(SupermarketTransportCostPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryPipingPage.pageName); await expect(await $("body").getText()).to.have.string("Total weekly supermarket spending: £380.00"); await expect(await $("body").getText()).to.have.string("Total weekly supermarket visits: 10"); await expect(await $("body").getText()).to.have.string("Total of supermarket visits by car: 9"); await expect(await $("body").getText()).to.have.string("Total spending on parking: £30.00"); - await $(CalculatedSummaryPipingPage.submit()).click(); + await click(CalculatedSummaryPipingPage.submit()); }); it("Given I return to section 1 and update the calculated summary used in section 2 validation, the progress of section 2 is updated", async () => { @@ -203,12 +202,12 @@ describe("Calculated summary with repeating answers", function () { await $(HubPage.summaryRowLink("section-1")).click(); await dynamicAnswerChangeLink(8).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(1); - await $(DynamicAnswerPage.submit()).click(); - await $(ExtraSpendingBlockPage.submit()).click(); - await $(ExtraSpendingMethodBlockPage.submit()).click(); - await $(CalculatedSummarySpendingPage.submit()).click(); - await $(CalculatedSummaryVisitsPage.submit()).click(); - await $(SummaryPage.submit()).click(); + await click(DynamicAnswerPage.submit()); + await click(ExtraSpendingBlockPage.submit()); + await click(ExtraSpendingMethodBlockPage.submit()); + await click(CalculatedSummarySpendingPage.submit()); + await click(CalculatedSummaryVisitsPage.submit()); + await click(SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index d7b34a00de..91d1332712 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -11,37 +11,36 @@ import CalculatedSummaryCountPage from "../../../generated_pages/new_calculated_ import HubPage from "../../../base_pages/hub.page"; import FamilyJourneysPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/family-journeys.page"; import BlockSkipPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/block-skip.page"; -import { assertSummaryValues, repeatingAnswerChangeLink } from "../../../helpers"; - +import { assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; describe("Feature: Calculated Summary using Repeating Blocks", () => { before("Reaching the first calculated summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_blocks.json"); await $(BlockCarPage.car()).setValue(100); - await $(BlockCarPage.submit()).click(); + await click(BlockCarPage.submit()); await $(BlockSkipPage.no()).click(); - await $(BlockSkipPage.submit()).click(); + await click(BlockSkipPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(AddTransportPage.transportName()).selectByAttribute("value", "Bus"); - await $(AddTransportPage.submit()).click(); + await click(AddTransportPage.submit()); await $(TransportRepeatingBlock1Page.transportCompany()).setValue("First"); await $(TransportRepeatingBlock1Page.transportCost()).setValue(30); await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(5); - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(TransportRepeatingBlock1Page.submit()); await $(TransportRepeatingBlock2Page.transportCount()).setValue(10); - await $(TransportRepeatingBlock2Page.submit()).click(); + await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(AddTransportPage.transportName()).selectByAttribute("value", "Plane"); - await $(AddTransportPage.submit()).click(); + await click(AddTransportPage.submit()); await $(TransportRepeatingBlock1Page.transportCompany()).setValue("EasyJet"); await $(TransportRepeatingBlock1Page.transportCost()).setValue(0); await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(265); - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(TransportRepeatingBlock1Page.submit()); await $(TransportRepeatingBlock2Page.transportCount()).setValue(2); - await $(TransportRepeatingBlock2Page.submit()).click(); + await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); }); it("Given I have a calculated summary using both list repeating block and static answers, When I reach the calculated summary page, Then I see the correct items and total.", async () => { @@ -54,7 +53,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Bus"); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly season ticket expenditure for travel by Plane"); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Plane"); - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); }); it("Given I have a calculated summary using a single answer from a repeating block, When I reach the calculated summary page, Then I see the correct items and total", async () => { @@ -64,21 +63,21 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await assertSummaryValues(["10", "2"]); await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Bus"); await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Plane"); - await $(CalculatedSummaryCountPage.submit()).click(); + await click(CalculatedSummaryCountPage.submit()); }); it("Given I add a new item to the list, When I complete the repeating blocks and press continue, Then I see the first calculated summary page which the updated total", async () => { await $(SectionOnePage.transportListAddLink()).click(); await $(AddTransportPage.transportName()).selectByAttribute("value", "Train"); - await $(AddTransportPage.submit()).click(); + await click(AddTransportPage.submit()); await $(TransportRepeatingBlock1Page.transportCompany()).setValue("Great Western Railway"); await $(TransportRepeatingBlock1Page.transportCost()).setValue(100); await $(TransportRepeatingBlock1Page.transportAdditionalCost()).setValue(50); - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(TransportRepeatingBlock1Page.submit()); await $(TransportRepeatingBlock2Page.transportCount()).setValue(6); - await $(TransportRepeatingBlock2Page.submit()).click(); + await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?", @@ -87,7 +86,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I am on the first calculated summary, When I confirm the total, Then I see the second calculated summary with an updated total", async () => { - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total journeys made per month to be 18. Is this correct?", @@ -102,7 +101,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I have used a change link on a calculated summary to go back to the first repeating block, When I press continue, Then I see the calculated summary I came from", async () => { - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(TransportRepeatingBlock1Page.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); }); @@ -115,31 +114,31 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I use a repeating block change link on the first calculated summary, When I edit my answer and press continue, Then I see the first calculated summary with a new correct total", async () => { await repeatingAnswerChangeLink(1).click(); await $(TransportRepeatingBlock1Page.transportCost()).setValue(60); - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(TransportRepeatingBlock1Page.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); }); it("Given I use a repeating block change link on the second calculated summary, When I edit my answer and press continue, Then I see the second calculated summary with a new correct total", async () => { await repeatingAnswerChangeLink(2).click(); await $(TransportRepeatingBlock2Page.transportCount()).setValue(12); - await $(TransportRepeatingBlock2Page.submit()).click(); + await click(TransportRepeatingBlock2Page.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total journeys made per month to be 24. Is this correct?", ); await assertSummaryValues(["10", "2", "12"]); - await $(CalculatedSummaryCountPage.submit()).click(); + await click(CalculatedSummaryCountPage.submit()); }); it("Given I use a remove link for on the summary page, When I press yes to confirm deleting the item, Then I see see the first calculated summary where I'm asked to reconfirm the total", async () => { await $(SectionOnePage.transportListRemoveLink(1)).click(); await $(RemoveTransportPage.yes()).click(); - await $(RemoveTransportPage.submit()).click(); + await click(RemoveTransportPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?", @@ -148,7 +147,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I have confirmed the first updated total, When I press continue, Then I see the next calculated summary to confirm that total too", async () => { - await $(CalculatedSummarySpendingPage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total journeys made per month to be 14. Is this correct?", @@ -157,21 +156,21 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I have a second section, When I begin and answer the first question with a total higher than the calculated summary, Then I see an error message preventing me from continuing", async () => { - await $(CalculatedSummaryCountPage.submit()).click(); - await $(SectionOnePage.submit()).click(); - await $(HubPage.submit()).click(); + await click(CalculatedSummaryCountPage.submit()); + await click(SectionOnePage.submit()); + await click(HubPage.submit()); await expect(await $(FamilyJourneysPage.questionTitle()).getText()).to.contain("How many of your 14 journeys are to visit family?"); await $(FamilyJourneysPage.answer()).setValue(15); - await $(FamilyJourneysPage.submit()).click(); + await click(FamilyJourneysPage.submit()); await expect(await $(FamilyJourneysPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 14"); }); it("Given I enter a value below the calculated summary from section 1, When I press Continue, Then I see my answer displayed on the next page", async () => { await $(FamilyJourneysPage.answer()).setValue(10); - await $(FamilyJourneysPage.submit()).click(); + await click(FamilyJourneysPage.submit()); await expect(await $(SectionTwoPage.familyJourneysQuestion()).getText()).to.contain("How many of your 14 journeys are to visit family?"); await expect(await $(SectionTwoPage.familyJourneysAnswer()).getText()).to.contain("10"); - await $(SectionTwoPage.submit()).click(); + await click(SectionTwoPage.submit()); }); it("Given I use the add list item link, When I add a new item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { @@ -180,33 +179,33 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(HubPage.summaryRowLink("section-1")).click(); await $(SectionOnePage.transportListAddLink()).click(); await $(AddTransportPage.transportName()).selectByAttribute("value", "Tube"); - await $(AddTransportPage.submit()).click(); - await $(TransportRepeatingBlock1Page.submit()).click(); + await click(AddTransportPage.submit()); + await click(TransportRepeatingBlock1Page.submit()); await $(TransportRepeatingBlock2Page.transportCount()).setValue(2); - await $(TransportRepeatingBlock2Page.submit()).click(); + await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(CalculatedSummarySpendingPage.submit()).click(); - await $(CalculatedSummaryCountPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(CalculatedSummarySpendingPage.submit()); + await click(CalculatedSummaryCountPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); }); it("Given I complete section-2 again, When I remove a list item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FamilyJourneysPage.answer()).setValue(16); - await $(FamilyJourneysPage.submit()).click(); - await $(SectionTwoPage.submit()).click(); + await click(FamilyJourneysPage.submit()); + await click(SectionTwoPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("section-1")).click(); await $(SectionOnePage.transportListRemoveLink(3)).click(); await $(RemoveTransportPage.yes()).click(); - await $(RemoveTransportPage.submit()).click(); - await $(CalculatedSummarySpendingPage.submit()).click(); - await $(CalculatedSummaryCountPage.submit()).click(); - await $(SectionOnePage.submit()).click(); + await click(RemoveTransportPage.submit()); + await click(CalculatedSummarySpendingPage.submit()); + await click(CalculatedSummaryCountPage.submit()); + await click(SectionOnePage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); }); @@ -215,7 +214,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(HubPage.summaryRowLink("section-1")).click(); await $(SectionOnePage.answerSkipEdit()).click(); await $(BlockSkipPage.yes()).click(); - await $(BlockSkipPage.submit()).click(); + await click(BlockSkipPage.submit()); // calculated summary progress is not altered by removing the list collector from the path so next location is summary page await expect(await browser.getUrl()).to.contain(SectionOnePage.pageName); await $(SectionOnePage.previous()).click(); @@ -228,8 +227,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I confirm the calculated summary and finish the section, When I return to the Hub, Then I see that section 2 is no longer available", async () => { - await $(CalculatedSummarySpendingPage.submit()).click(); - await $(SectionOnePage.submit()).click(); + await click(CalculatedSummarySpendingPage.submit()); + await click(SectionOnePage.submit()); // section 2 is now gone await expect(await $$(HubPage.summaryItems()).length).to.equal(1); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 69c522408a..f032bffb69 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -33,20 +33,20 @@ import SectionSummarySectionOne from "../../../generated_pages/new_calculated_su import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; import DependencyQuestionSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; - +import { click } from "../../../helpers"; describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { before("Get to Calculated Summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); await getToFirstCalculatedSummary(); @@ -106,14 +106,14 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain("currency-total-playback/?return_to=calculated-summary#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { await $(CurrencyTotalPlaybackPage.fourthNumberAnswerEdit()).click(); await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); - await $(FourthNumberBlockPage.submit()).click(); + await click(FourthNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( @@ -125,7 +125,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalEdit()).click(); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); - await $(FourthAndAHalfNumberBlockPage.submit()).click(); + await click(FourthAndAHalfNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( @@ -143,10 +143,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FourthNumberBlockPage.previous()).click(); await $(SkipFourthBlockPage.yes()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); const expectedUrl = await browser.getUrl(); @@ -161,7 +161,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(CurrencyTotalPlaybackPage.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); @@ -181,7 +181,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(UnitTotalPlaybackPage.submit()).click(); + await click(UnitTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); @@ -201,7 +201,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await $(UnitTotalPlaybackPage.submit()).click(); + await click(UnitTotalPlaybackPage.submit()); await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be 124.58. Is this correct?", ); @@ -216,11 +216,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", async () => { - await $(NumberTotalPlaybackPage.submit()).click(); + await click(NumberTotalPlaybackPage.submit()); await expect(await browser.getUrl()).to.contain(BreakdownPage.pageName); await $(BreakdownPage.answer1()).setValue(100.0); await $(BreakdownPage.answer2()).setValue(24.58); - await $(BreakdownPage.submit()).click(); + await click(BreakdownPage.submit()); await expect(await browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of number values entered to be £124.58. Is this correct?", @@ -230,7 +230,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); + await click(SecondCurrencyTotalPlaybackPage.submit()); const content = $("h1 + ul").getText(); const textsToAssert = ["Total currency values: £9.36", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; @@ -239,50 +239,50 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(BreakdownPage.submit()).click(); - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(BreakdownPage.submit()); + await click(SecondCurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -290,28 +290,28 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(BreakdownPage.submit()).click(); - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(BreakdownPage.submit()); + await click(SecondCurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -319,34 +319,34 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); - await $(ThirdNumberBlockPage.submit()).click(); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); - await $(SkipFourthBlockPage.submit()).click(); - await $(FifthNumberBlockPage.submit()).click(); - await $(SixthNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(ThirdAndAHalfNumberBlockPage.submit()); + await click(SkipFourthBlockPage.submit()); + await click(FifthNumberBlockPage.submit()); + await click(SixthNumberBlockPage.submit()); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); - await $(BreakdownPage.submit()).click(); - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); + await click(BreakdownPage.submit()); + await click(SecondCurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - await $(SetMinMaxBlockPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); @@ -354,29 +354,29 @@ describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", async () => { before("Get to Final Summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(PrimaryPersonListCollectorPage.no()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Jean"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Jane"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); await getToFirstCalculatedSummary(); await getToSubmitPage(); - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await getToFirstCalculatedSummary(); await getToSubmitPage(); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", async () => { @@ -391,7 +391,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await $(SkipFourthBlockPage.yes()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); @@ -404,8 +404,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); - await $(SetMinMaxBlockPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SetMinMaxBlockPage.submit()); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.pageName); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); @@ -415,31 +415,31 @@ describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section with a Dependency based on a calculated summary in another section", () => { before("Get to the Dependent question page", async () => { await browser.openQuestionnaire("test_new_calculated_summary_cross_section_dependencies_repeating.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); await $(SkipFirstNumberBlockPageSectionOne.no()).click(); - await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await click(SkipFirstNumberBlockPageSectionOne.submit()); await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); - await $(FirstNumberBlockPageSectionOne.submit()).click(); + await click(FirstNumberBlockPageSectionOne.submit()); await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); - await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await click(FirstAndAHalfNumberBlockPageSectionOne.submit()); await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); - await $(SecondNumberBlockPageSectionOne.submit()).click(); - await $(CalculatedSummarySectionOne.submit()).click(); - await $(SectionSummarySectionOne.submit()).click(); - await $(HubPage.submit()).click(); + await click(SecondNumberBlockPageSectionOne.submit()); + await click(CalculatedSummarySectionOne.submit()); + await click(SectionSummarySectionOne.submit()); + await click(HubPage.submit()); await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); - await $(ThirdNumberBlockPageSectionTwo.submit()).click(); - await $(CalculatedSummarySectionTwo.submit()).click(); + await click(ThirdNumberBlockPageSectionTwo.submit()); + await click(CalculatedSummarySectionTwo.submit()); }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { @@ -454,24 +454,24 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); - await $(DependencyQuestionSectionTwo.submit()).click(); + await click(DependencyQuestionSectionTwo.submit()); await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); - await $(MinMaxSectionTwo.submit()).click(); + await click(MinMaxSectionTwo.submit()); await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); - await $(MinMaxSectionTwo.submit()).click(); + await click(MinMaxSectionTwo.submit()); }); it("Given I remove answers from the path for a calculated summary in a previous section by changing an answer, When I return to the question with the calculated summary value source, Then the value displayed should be correct", async () => { - await $(SectionSummarySectionTwo.submit()).click(); + await click(SectionSummarySectionTwo.submit()); await $(HubPage.summaryRowLink("questions-section")).click(); await $(SectionSummarySectionOne.skipFirstBlockAnswerEdit()).click(); await $(SkipFirstNumberBlockPageSectionOne.yes()).click(); - await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); - await $(SectionSummarySectionOne.submit()).click(); + await click(SkipFirstNumberBlockPageSectionOne.submit()); + await click(SectionSummarySectionOne.submit()); await $(HubPage.summaryRowLink("calculated-summary-section-1")).click(); await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); @@ -487,43 +487,43 @@ describe("Feature: Calculated Summary Repeating Section", () => { const getToFirstCalculatedSummary = async () => { await $(FirstNumberBlockPage.firstNumber()).setValue(1.23); - await $(FirstNumberBlockPage.submit()).click(); + await click(FirstNumberBlockPage.submit()); await $(SecondNumberBlockPage.secondNumber()).setValue(4.56); await $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); await $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0.12); - await $(SecondNumberBlockPage.submit()).click(); + await click(SecondNumberBlockPage.submit()); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.45); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); await $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); - await $(ThirdAndAHalfNumberBlockPage.submit()).click(); + await click(ThirdAndAHalfNumberBlockPage.submit()); await $(SkipFourthBlockPage.no()).click(); - await $(SkipFourthBlockPage.submit()).click(); + await click(SkipFourthBlockPage.submit()); await $(FourthNumberBlockPage.fourthNumber()).setValue(9.01); - await $(FourthNumberBlockPage.submit()).click(); + await click(FourthNumberBlockPage.submit()); await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(2.34); - await $(FourthAndAHalfNumberBlockPage.submit()).click(); + await click(FourthAndAHalfNumberBlockPage.submit()); await $(FifthNumberBlockPage.fifthPercent()).setValue(56); await $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); - await $(FifthNumberBlockPage.submit()).click(); + await click(FifthNumberBlockPage.submit()); await $(SixthNumberBlockPage.sixthPercent()).setValue(23); await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); - await $(SixthNumberBlockPage.submit()).click(); + await click(SixthNumberBlockPage.submit()); }; const getToSubmitPage = async () => { - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(UnitTotalPlaybackPage.submit()).click(); - await $(PercentageTotalPlaybackPage.submit()).click(); - await $(NumberTotalPlaybackPage.submit()).click(); + await click(CurrencyTotalPlaybackPage.submit()); + await click(UnitTotalPlaybackPage.submit()); + await click(PercentageTotalPlaybackPage.submit()); + await click(NumberTotalPlaybackPage.submit()); await $(BreakdownPage.answer1()).setValue(100.0); await $(BreakdownPage.answer2()).setValue(24.58); - await $(BreakdownPage.submit()).click(); - await $(SecondCurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummaryTotalConfirmation.submit()).click(); + await click(BreakdownPage.submit()); + await click(SecondCurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummaryTotalConfirmation.submit()); }; diff --git a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js index af4efcb31c..774ad71884 100644 --- a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js +++ b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js @@ -2,7 +2,7 @@ import CheckBoxPage from "../../../generated_pages/titles_radio_and_checkbox/che import NameEntryPage from "../../../generated_pages/titles_radio_and_checkbox/preamble-block.page"; import RadioButtonsPage from "../../../generated_pages/titles_radio_and_checkbox/radio-block.page"; import SubmitPage from "../../../generated_pages/titles_radio_and_checkbox/submit.page"; - +import { click } from "../../../helpers"; describe("Feature: Conditional checkbox and radio question titles", () => { beforeEach(async () => { await browser.openQuestionnaire("test_titles_radio_and_checkbox.json"); @@ -11,13 +11,13 @@ describe("Feature: Conditional checkbox and radio question titles", () => { describe("Given I start the test_titles_radio_and_checkbox survey", () => { it("When I enter an expected name and submit", async () => { await $(NameEntryPage.name()).setValue("Peter"); - await $(NameEntryPage.submit()).click(); + await click(NameEntryPage.submit()); await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Peter make changes to this business?"); }); it("When I enter an unknown name and go to the checkbox page", async () => { await $(NameEntryPage.name()).setValue("Fred"); - await $(NameEntryPage.submit()).click(); + await click(NameEntryPage.submit()); await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategies()).click(); await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); @@ -25,20 +25,20 @@ describe("Feature: Conditional checkbox and radio question titles", () => { it("When I enter another known name page title should include selected title", async () => { await $(NameEntryPage.name()).setValue("Mary"); - await $(NameEntryPage.submit()).click(); + await click(NameEntryPage.submit()); await expect(await browser.getTitle()).to.contain("Did Mary make changes to this business? - Test Survey - Checkbox and Radio titles"); }); it("When I enter another known name and go to the summary", async () => { await $(NameEntryPage.name()).setValue("Mary"); - await $(NameEntryPage.submit()).click(); + await click(NameEntryPage.submit()); await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Mary make changes to this business"); await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategiesLabel()).click(); - await $(CheckBoxPage.submit()).click(); + await click(CheckBoxPage.submit()); await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Is Mary the boss?"); await $(RadioButtonsPage.radioMaybe()).click(); - await $(RadioButtonsPage.submit()).click(); + await click(RadioButtonsPage.submit()); await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Mary"); await expect(await $(SubmitPage.checkboxQuestion()).getText()).to.contain("Did Mary make changes to this business?"); }); diff --git a/tests/functional/spec/features/confirmation_question.spec.js b/tests/functional/spec/features/confirmation_question.spec.js index 70958b56a7..7a735f9353 100644 --- a/tests/functional/spec/features/confirmation_question.spec.js +++ b/tests/functional/spec/features/confirmation_question.spec.js @@ -1,6 +1,7 @@ import NumberOfEmployeesTotalBlockPage from "../../generated_pages/confirmation_question/number-of-employees-total-block.page.js"; import ConfirmZeroEmployeesBlockPage from "../../generated_pages/confirmation_question/confirm-zero-employees-block.page.js"; import SubmitPage from "../../generated_pages/confirmation_question/submit.page.js"; +import { click } from "../../helpers"; describe("Feature: Confirmation Question", () => { describe("Given I have a completed the confirmation question", () => { @@ -10,9 +11,9 @@ describe("Feature: Confirmation Question", () => { it("When I view the summary, Then the confirmation question should not be displayed", async () => { await $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); - await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); - await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await click(ConfirmZeroEmployeesBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); await expect(await $$(SubmitPage.confirmZeroEmployeesAnswer())).to.be.empty; @@ -21,18 +22,18 @@ describe("Feature: Confirmation Question", () => { describe("Given a confirmation Question", () => { it("When I answer 'No' to the confirmation question, Then I should be routed back to the source question", async () => { await browser.openQuestionnaire("test_confirmation_question.json"); - await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); - await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await click(ConfirmZeroEmployeesBlockPage.submit()); await expect(await browser.getUrl()).to.contain(NumberOfEmployeesTotalBlockPage.pageName); }); }); describe("Given a number of employees Question", () => { it("When I don't answer the number of employees question and go to summary, Then default value should be displayed for the the number of employees question", async () => { await browser.openQuestionnaire("test_confirmation_question.json"); - await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); - await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await click(ConfirmZeroEmployeesBlockPage.submit()); await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); }); }); diff --git a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js index 7baa7f37f5..abcf271e63 100644 --- a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js +++ b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js @@ -4,18 +4,18 @@ import CarerPage from "../../generated_pages/confirmation_question_within_repeat import DateOfBirthPage from "../../generated_pages/confirmation_question_within_repeating_section/dob-block.page"; import ConfirmDateOfBirthPage from "../../generated_pages/confirmation_question_within_repeating_section/confirm-dob-block.page"; import DefaultSectionSummary from "../../generated_pages/confirmation_question_within_repeating_section/default-section-summary.page"; - +import { click } from "../../helpers"; describe("Feature: Confirmation Question Within A Repeating Section", () => { describe("Given I am in a repeating section", () => { beforeEach("Add a person", async () => { await browser.openQuestionnaire("test_confirmation_question_within_repeating_section.json"); await $(DoesAnyoneLiveHerePage.yes()).click(); - await $(DoesAnyoneLiveHerePage.submit()).click(); + await click(DoesAnyoneLiveHerePage.submit()); await $(AddPersonPage.firstName()).setValue("John"); await $(AddPersonPage.lastName()).setValue("Doe"); - await $(AddPersonPage.submit()).click(); + await click(AddPersonPage.submit()); await $(DoesAnyoneLiveHerePage.no()).click(); - await $(DoesAnyoneLiveHerePage.submit()).click(); + await click(DoesAnyoneLiveHerePage.submit()); await expect(await browser.getUrl()).to.contain(DateOfBirthPage.url().split("/").slice(-1)[0]); }); @@ -25,11 +25,11 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(DateOfBirthPage.day()).setValue("01"); await $(DateOfBirthPage.month()).setValue("01"); await $(DateOfBirthPage.year()).setValue("2015"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); // Answer 'No' to confirmation question await $(ConfirmDateOfBirthPage.noINeedToChangeTheirDateOfBirth()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await expect(await browser.getUrl()).to.contain(DateOfBirthPage.pageName); }); }); @@ -39,10 +39,10 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(DateOfBirthPage.day()).setValue("01"); await $(DateOfBirthPage.month()).setValue("01"); await $(DateOfBirthPage.year()).setValue("2015"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await expect(await browser.getUrl()).to.contain("sections/default-section/"); await expect(await $(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).to.be.false; @@ -54,7 +54,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(DateOfBirthPage.day()).setValue("01"); await $(DateOfBirthPage.month()).setValue("01"); await $(DateOfBirthPage.year()).setValue("2000"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await expect(await browser.getUrl()).to.contain(CarerPage.pageName); await expect(await $(CarerPage.questionText()).getText()).to.contain("Does John Doe look"); diff --git a/tests/functional/spec/features/default_value/default_value.spec.js b/tests/functional/spec/features/default_value/default_value.spec.js index aa508dc0bb..03bfb11a5c 100644 --- a/tests/functional/spec/features/default_value/default_value.spec.js +++ b/tests/functional/spec/features/default_value/default_value.spec.js @@ -3,24 +3,24 @@ import QuestionPageTwo from "../../../generated_pages/default/number-question-tw import SubmitPage from "../../../generated_pages/default/submit.page.js"; import QuestionPageOneSkip from "../../../generated_pages/default_with_skip/number-question-one.page.js"; import QuestionPageThreeSkip from "../../../generated_pages/default_with_skip/number-question-three.page.js"; - +import { click } from "../../../helpers"; describe("Feature: Default Value", () => { it('Given I start default schema, When I do not answer a question, Then "no answer provided" is displayed on the Summary page', async () => { await browser.openQuestionnaire("test_default.json"); - await $(QuestionPageOne.submit()).click(); + await click(QuestionPageOne.submit()); await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); - await $(QuestionPageTwo.submit()).click(); + await click(QuestionPageTwo.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.answerOne()).getText()).to.contain("0"); }); it("Given I have not answered a question containing a default value, When I return to the question, Then no value should be displayed", async () => { await browser.openQuestionnaire("test_default.json"); - await $(QuestionPageOne.submit()).click(); + await click(QuestionPageOne.submit()); await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); - await $(QuestionPageTwo.submit()).click(); + await click(QuestionPageTwo.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.previous()).click(); await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); @@ -31,7 +31,7 @@ describe("Feature: Default Value", () => { it("Given I have not answered a question containing a default value, When a skip condition checks for the default value, Then I should skip the next question", async () => { await browser.openQuestionnaire("test_default_with_skip.json"); - await $(QuestionPageOneSkip.submit()).click(); + await click(QuestionPageOneSkip.submit()); await expect(await browser.getUrl()).to.contain(QuestionPageThreeSkip.pageName); await expect(await $(QuestionPageThreeSkip.questionText()).getText()).to.contain("Question Three"); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 9b08f5a242..2a34b1fb54 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -4,7 +4,7 @@ import DynamicRadioPage from "../../../generated_pages/dynamic_answer_options_fu import DynamicDropdownPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/dynamic-dropdown.page"; import DynamicMutuallyExclusivePage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/dynamic-mutually-exclusive.page"; import SubmitPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/submit.page"; - +import { click } from "../../../helpers"; const dropdownOptionValues = ["2020-12-28", "2020-12-29", "2020-12-30", "2020-12-31", "2021-01-01", "2021-01-02", "2021-01-03"]; const dropdownOptionValuesWithStaticOption = [...dropdownOptionValues, "I did not work"]; @@ -27,7 +27,7 @@ const openQuestionnaireAndSetUp = async (schema) => { await $(ReferenceDatePage.day()).setValue("1"); await $(ReferenceDatePage.month()).setValue("1"); await $(ReferenceDatePage.year()).setValue("2021"); - await $(ReferenceDatePage.submit()).click(); + await click(ReferenceDatePage.submit()); }; testCases.forEach(async (testCase) => { @@ -53,7 +53,7 @@ testCases.forEach(async (testCase) => { }); it("When I submit the page, then I should be taken to the next page", async () => { - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(DynamicRadioPage.pageName); }); }); @@ -67,7 +67,7 @@ testCases.forEach(async (testCase) => { }); it("When I submit the page, then I should be taken to the next page", async () => { - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); await expect(await browser.getUrl()).to.contain(DynamicDropdownPage.pageName); }); }); @@ -81,7 +81,7 @@ testCases.forEach(async (testCase) => { }); it("When I submit the page, then I should be taken to the next page", async () => { - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); await expect(await browser.getUrl()).to.contain(DynamicMutuallyExclusivePage.pageName); }); }); @@ -122,10 +122,10 @@ testCases.forEach(async (testCase) => { describe("Given a dynamic answer options questionnaire", () => { it("When I submit my questions without answering, then the summary should display `No answer provided` for each question", async () => { - await $(DynamicCheckboxPage.submit()).click(); - await $(DynamicRadioPage.submit()).click(); - await $(DynamicRadioPage.submit()).click(); - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicCheckboxPage.submit()); + await click(DynamicRadioPage.submit()); + await click(DynamicRadioPage.submit()); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("No answer provided"); @@ -138,20 +138,20 @@ testCases.forEach(async (testCase) => { // Answer Checkbox await $(DynamicCheckboxPage.answerByIndex(2)).click(); // Wednesday 30 December 2020 await $(DynamicCheckboxPage.answerByIndex(3)).click(); // Thursday 30 December 2020 - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); // Answer Radio await $(DynamicRadioPage.answerByIndex(1)).click(); // Tuesday 29 December 2020 - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); // Answer Dropdown await $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); // Saturday 2 January 2021 - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); // Answer Mutually exclusive await $(DynamicMutuallyExclusivePage.answerByIndex(0)).click(); // Monday 28 December 2020 await $(DynamicMutuallyExclusivePage.answerByIndex(6)).click(); // Sunday 3 January 2021 - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("Wednesday 30 December 2020\nThursday 31 December 2020"); @@ -173,27 +173,27 @@ describe(`Feature: Dynamically generated answer options driven by a function wit it("When I select a static answer option for each question, then my selected answer(s) should be displayed on the summary", async () => { // Answer Checkbox await $(DynamicCheckboxPage.answerByIndex(7)).click(); - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); // Answer Radio await $(DynamicRadioPage.answerByIndex(7)).click(); - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); // Answer Dropdown await $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); // Answer Mutually exclusive // Test static option for mutually exclusive from non exclusive choices await $(DynamicMutuallyExclusivePage.answerByIndex(7)).click(); - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("None of the above"); // Test exclusive static choice await $(SubmitPage.previous()).click(); await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("I did not work"); @@ -205,26 +205,26 @@ describe(`Feature: Dynamically generated answer options driven by a function wit it("When I edit and change the reference date which the other questions are dependent on, then all dependent answers are removed", async () => { await $(SubmitPage.referenceDateAnswerEdit()).click(); await $(ReferenceDatePage.day()).setValue("2"); - await $(ReferenceDatePage.submit()).click(); + await click(ReferenceDatePage.submit()); await expect(await $(DynamicCheckboxPage.answerByIndex(7)).isSelected()).to.be.false; await $(DynamicCheckboxPage.answerByIndex(7)).click(); - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); await expect(await $(DynamicRadioPage.answerByIndex(7)).isSelected()).to.be.false; await $(DynamicRadioPage.answerByIndex(7)).click(); - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); await expect(await $(DynamicDropdownPage.answer()).getText()).to.contain("Select an answer"); await $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); // The Mutually exclusive answer is not removed as it is a different answer_id to the dependent, however the block must be re-submitted await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js index f599bb5438..2109103837 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js @@ -3,7 +3,7 @@ import DynamicCheckboxPage from "../../../generated_pages/dynamic_answer_options import DynamicRadioPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options_mandatory/dynamic-radio.page"; import DynamicDropdownPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options_mandatory/dynamic-dropdown.page"; import DynamicMutuallyExclusivePage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options_mandatory/dynamic-mutually-exclusive.page"; - +import { click } from "../../../helpers"; describe(`Feature: Dynamically generated mandatory answer options driven by a function with static options`, () => { describe("Given a mandatory dynamic answer options questionnaire with static options", () => { before("Open questionnaire", async () => { @@ -12,11 +12,11 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu await $(ReferenceDatePage.day()).setValue("1"); await $(ReferenceDatePage.month()).setValue("1"); await $(ReferenceDatePage.year()).setValue("2021"); - await $(ReferenceDatePage.submit()).click(); + await click(ReferenceDatePage.submit()); }); it("When I do not answer the Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); await expect(await $(DynamicCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); await expect(await $(DynamicCheckboxPage.answerErrorItem()).getText()).to.contain("Select at least one answer"); await expect(await $(DynamicCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; @@ -25,9 +25,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu it("When I do not answer the Radio question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Radio question await $(DynamicCheckboxPage.answerByIndex(0)).click(); - await $(DynamicCheckboxPage.submit()).click(); + await click(DynamicCheckboxPage.submit()); - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); await expect(await $(DynamicRadioPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); await expect(await $(DynamicRadioPage.answerErrorItem()).getText()).to.contain("Select an answer"); await expect(await $(DynamicRadioPage.questionErrorPanel()).isExisting()).to.be.true; @@ -36,9 +36,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu it("When I do not answer the Dropdown question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Dropdown question await $(DynamicRadioPage.answerByIndex(0)).click(); - await $(DynamicRadioPage.submit()).click(); + await click(DynamicRadioPage.submit()); - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); await expect(await $(DynamicDropdownPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); await expect(await $(DynamicDropdownPage.answerErrorItem()).getText()).to.contain("Select an answer"); await expect(await $(DynamicDropdownPage.questionErrorPanel()).isExisting()).to.be.true; @@ -47,9 +47,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu it("When I do not answer the Mutually Exclusive Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { // Get to Mutually Exclusive question await $(DynamicDropdownPage.answer()).selectByAttribute("value", "2021-01-02"); - await $(DynamicDropdownPage.submit()).click(); + await click(DynamicDropdownPage.submit()); - await $(DynamicMutuallyExclusivePage.submit()).click(); + await click(DynamicMutuallyExclusivePage.submit()); await expect(await $(DynamicMutuallyExclusivePage.errorHeader()).getText()).to.contain("There is a problem with your answer"); await expect(await $(DynamicMutuallyExclusivePage.errorNumber(1)).getText()).to.contain("Select at least one answer"); await expect(await $(DynamicMutuallyExclusivePage.questionErrorPanel()).isExisting()).to.be.true; diff --git a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js index 1411498919..69a6751465 100644 --- a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js +++ b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js @@ -2,14 +2,14 @@ import HealedTheQuickestPage from "../../../generated_pages/dynamic_radio_option import InjurySustainedPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/injury-sustained.page"; import MostSeriousInjuryPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/most-serious-injury.page"; import SubmitPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/submit.page"; - +import { click } from "../../../helpers"; describe("Dynamic radio options from checkbox answers", () => { describe("Given the dynamic radio options from checkbox questionnaire and I am on the checkbox answer page", () => { it("When the respondent answers the checkbox question and submits, Then the radio question should show the answers from that checkbox as options, as well as a static option", async () => { await browser.openQuestionnaire("test_dynamic_radio_options_from_checkbox.json"); await $(InjurySustainedPage.head()).click(); await $(InjurySustainedPage.body()).click(); - await $(InjurySustainedPage.submit()).click(); + await click(InjurySustainedPage.submit()); await expect(await browser.getUrl()).to.contain(MostSeriousInjuryPage.pageName); await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).to.contain("Head"); @@ -20,7 +20,7 @@ describe("Dynamic radio options from checkbox answers", () => { it("When the respondent answers the radio question and submits, Then the next radio question should show only the answers from the first checkbox as options", async () => { await $(MostSeriousInjuryPage.answerLabelByIndex(0)).click(); - await $(MostSeriousInjuryPage.submit()).click(); + await click(MostSeriousInjuryPage.submit()); await expect(await browser.getUrl()).to.contain(HealedTheQuickestPage.pageName); await expect(await $(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).to.contain("Head"); @@ -30,7 +30,7 @@ describe("Dynamic radio options from checkbox answers", () => { it("When the respondent answers the radio question and submits, then the summary should display all the answers correctly", async () => { await $(HealedTheQuickestPage.answerLabelByIndex(1)).click(); - await $(HealedTheQuickestPage.submit()).click(); + await click(HealedTheQuickestPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.injurySustainedAnswer()).getText()).to.contain("Head\nBody"); @@ -41,7 +41,7 @@ describe("Dynamic radio options from checkbox answers", () => { it("When I edit and change the answer which the dynamic options is dependent on, then my selected answers are removed", async () => { await $(SubmitPage.injurySustainedAnswerEdit()).click(); await $(InjurySustainedPage.arms()).click(); - await $(InjurySustainedPage.submit()).click(); + await click(InjurySustainedPage.submit()); await expect(await $(MostSeriousInjuryPage.answerByIndex(0)).isSelected()).to.be.false; await expect(await $(MostSeriousInjuryPage.answerByIndex(1)).isSelected()).to.be.false; diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index 104afe9454..b4f579bc23 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -8,6 +8,7 @@ import SetMinimumPage from "../../generated_pages/dynamic_answers_list_source/mi import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/list-collector-section-summary.page"; import HubPage from "../../base_pages/hub.page"; import OnlineShoppingPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer-separate-section.page"; +import { click } from "../../helpers"; describe("Dynamic answers list value source", () => { const summaryTitles = ".ons-summary__item-title"; @@ -26,12 +27,12 @@ describe("Dynamic answers list value source", () => { }); it("Given list items have been added, When additional items are added using add link, Then the correct dynamic answers are displayed", async () => { await $(DriverPage.yes()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); @@ -39,9 +40,9 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); @@ -91,7 +92,7 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[0].waitForExist({ timeout }); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $$(DynamicAnswerPage.inputs())[1].setValue(12); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); @@ -104,7 +105,7 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).to.be.true; - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[1].$("a").click(); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); @@ -117,7 +118,7 @@ describe("Dynamic answers list value source", () => { await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await $$(DynamicAnswerPage.inputs())[0].setValue(21); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); @@ -130,11 +131,11 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.supermarketsListRemoveLink(1)).waitForExist({ timeout }); await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); - await $(ListCollectorRemovePage.submit()).click(); - await $(DynamicAnswerPage.submit()).click(); + await click(ListCollectorRemovePage.submit()); + await click(DynamicAnswerPage.submit()); await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await click(SetMinimumPage.submit()); + await click(DynamicAnswerOnlyPage.submit()); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); @@ -151,12 +152,12 @@ describe("Dynamic answers list value source", () => { await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Tesco"); await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Aldi"); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); @@ -178,46 +179,46 @@ describe("Dynamic answers list value source", () => { async function addTwoSupermarkets(timeout) { await $(DriverPage.yes()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); } async function addTwoSupermarketsAndGetToNextSection(timeout) { await $(DriverPage.yes()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SectionSummaryPage.submit()); + await click(HubPage.submit()); } async function setMinimumAndGetSectionSummary(timeout) { - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(SetMinimumPage.setMinimum()).setValue(2); - await $(SetMinimumPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await click(SetMinimumPage.submit()); + await click(DynamicAnswerOnlyPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); } diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js index 4fd74fd116..18ccd6e39c 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js @@ -1,7 +1,7 @@ import sectionOne from "../../../generated_pages/section_enabled_checkbox/section-1-block.page"; import sectionTwo from "../../../generated_pages/section_enabled_checkbox/section-2-block.page"; import SubmitPage from "../../../generated_pages/section_enabled_checkbox/submit.page"; - +import { click } from "../../../helpers"; describe("Feature: Section Enabled Based On Checkbox Answers", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire("test_section_enabled_checkbox.json"); @@ -9,14 +9,14 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { it("When the user selects `Section 2` and submits, Then section 2 should be displayed", async () => { await $(sectionOne.section1Section2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain("section-2-block"); }); it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { await $(sectionOne.section1Section3()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain("section-3-block"); }); @@ -24,16 +24,16 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { await $(sectionOne.section1Section2()).click(); await $(sectionOne.section1Section3()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain("section-2-block"); - await $(sectionTwo.submit()).click(); + await click(sectionTwo.submit()); await expect(await browser.getUrl()).to.contain("section-3-block"); }); it("When the user selects `Neither` and submits, Then they should be taken straight to the summary", async () => { await $(sectionOne.section1ExclusiveNeither()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js index 6c8bd37918..470b173938 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js @@ -1,6 +1,6 @@ import sectionOne from "../../../generated_pages/section_enabled_hub/section-1-block.page"; import hubPage from "../../../base_pages/hub.page"; - +import { click } from "../../../helpers"; describe("Feature: Section Enabled With Hub", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire("test_section_enabled_hub.json"); @@ -8,7 +8,7 @@ describe("Feature: Section Enabled With Hub", () => { it("When the user selects `Section 2` and submits, Then only section 2 should be displayed on the hub", async () => { await $(sectionOne.section1Section2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); @@ -18,7 +18,7 @@ describe("Feature: Section Enabled With Hub", () => { it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { await $(sectionOne.section1Section3()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); @@ -29,7 +29,7 @@ describe("Feature: Section Enabled With Hub", () => { it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { await $(sectionOne.section1Section2()).click(); await $(sectionOne.section1Section3()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); @@ -40,7 +40,7 @@ describe("Feature: Section Enabled With Hub", () => { it("When the user selects `Neither` and submits, Then hub should not display any other section and should be in the `Completed` state.", async () => { await $(sectionOne.section1ExclusiveNeither()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js index 4fb655901f..10b9e674b7 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js @@ -1,6 +1,6 @@ import sectionOne from "../../../generated_pages/section_enabled_radio/section-1-block.page"; import SubmitPage from "../../../generated_pages/section_enabled_radio/submit.page"; - +import { click } from "../../../helpers"; describe("Feature: Section Enabled Based On Radio Answers", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire("test_section_enabled_radio.json"); @@ -8,14 +8,14 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { it("When the user answers `Yes, enable section 2` and submits, Then section 2 should be displayed", async () => { await $(sectionOne.yesEnableSection2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain("section-2-block"); }); it("When the user answers `No, disable section 2` and submits, Then they should be taking straight to the summary", async () => { await $(sectionOne.noDisableSection2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; @@ -24,7 +24,7 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { describe("Given that section 2 is enabled", () => { beforeEach("Enable section 2", async () => { await $(sectionOne.yesEnableSection2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain("section-2-block"); }); @@ -34,7 +34,7 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { await expect(await browser.getUrl()).to.contain("section-1-block"); await $(sectionOne.noDisableSection2()).click(); - await $(sectionOne.submit()).click(); + await click(sectionOne.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index 4b6391b705..c314a9c3bf 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -10,46 +10,46 @@ import CurrencyQuestion3Page from "../../../generated_pages/grand_calculated_sum import CurrencyAllPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/currency-all.page"; import FirstNumberBlockPartAPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/first-number-block-part-a.page"; import FourthNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/fourth-number-block.page"; - +import { click } from "../../../helpers"; describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary", () => { before("Getting to the second calculated summary", async () => { await browser.openQuestionnaire("test_grand_calculated_summary_cross_section_dependencies.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(SkipFirstBlockPage.no()).click(); - await $(SkipFirstBlockPage.submit()).click(); + await click(SkipFirstBlockPage.submit()); await $(FirstNumberBlockPartAPage.firstNumberA()).setValue(300); - await $(FirstNumberBlockPartAPage.submit()).click(); + await click(FirstNumberBlockPartAPage.submit()); await $(SecondNumberBlockPage.secondNumberA()).setValue(10); await $(SecondNumberBlockPage.secondNumberB()).setValue(5); await $(SecondNumberBlockPage.secondNumberC()).setValue(15); - await $(SecondNumberBlockPage.submit()).click(); - await $(CurrencySection1Page.submit()).click(); - await $(QuestionsSectionSummaryPage.submit()).click(); + await click(SecondNumberBlockPage.submit()); + await click(CurrencySection1Page.submit()); + await click(QuestionsSectionSummaryPage.submit()); // section 2 - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(70); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); }); it("Given I don't skip the second calculated summary, it is included in the grand calculated summary", async () => { await $(SkipCalculatedSummaryPage.no()).click(); - await $(SkipCalculatedSummaryPage.submit()).click(); - await $(CurrencyQuestion3Page.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SkipCalculatedSummaryPage.submit()); + await click(CurrencyQuestion3Page.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); + await click(HubPage.submit()); await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£330.00"); await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).to.contain("£70.00"); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( "The grand calculated summary is calculated to be £400.00. Is this correct?", ); - await $(CurrencyAllPage.submit()).click(); + await click(CurrencyAllPage.submit()); }); it("Given I go back and skip the second calculated summary, it is not included in the grand calculated summary", async () => { await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); await $(SkipCalculatedSummaryPage.yes()).click(); - await $(SkipCalculatedSummaryPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipCalculatedSummaryPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); // Currently the grand calculated summary remains 'Completed' because none of the answers have changed await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); @@ -59,12 +59,12 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; }); it("Given I confirm the grand calculated summary, then edit an answer for question 3, the grand calculated summary updates to be incomplete, because this is a dependency", async () => { - await $(CurrencyAllPage.submit()).click(); + await click(CurrencyAllPage.submit()); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(130); - await $(ThirdNumberBlockPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); // Although the calculated summary is not on the path, the answer is still a grand calculated summary dependency, so it updates progress await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); @@ -72,15 +72,15 @@ describe("Feature: Grand Calculated Summary", () => { "The grand calculated summary is calculated to be £330.00. Is this correct?", ); await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; - await $(CurrencyAllPage.submit()).click(); + await click(CurrencyAllPage.submit()); }); it("Given I change my response to include the calculated summary, the grand calculated summary updates to re-include it", async () => { await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); await $(SkipCalculatedSummaryPage.no()).click(); - await $(SkipCalculatedSummaryPage.submit()).click(); - await $(CurrencyQuestion3Page.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipCalculatedSummaryPage.submit()); + await click(CurrencyQuestion3Page.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); // Currently, the grand calculated summary does not return to in-progress, because none of the answers it depends on have changed await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); @@ -92,24 +92,24 @@ describe("Feature: Grand Calculated Summary", () => { await $(CurrencyAllPage.currencyQuestion3Edit()).click(); await $(CurrencyQuestion3Page.thirdNumberAnswerPartBEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartB()).setValue(10); - await $(ThirdNumberBlockPage.submit()).click(); + await click(ThirdNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(FourthNumberBlockPage.pageName); await $(FourthNumberBlockPage.fourthNumber()).setValue(1); - await $(FourthNumberBlockPage.submit()).click(); + await click(FourthNumberBlockPage.submit()); await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); - await $(CurrencyQuestion3Page.submit()).click(); + await click(CurrencyQuestion3Page.submit()); await expect(await browser.getUrl()).to.contain(CurrencyAllPage.pageName); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( "The grand calculated summary is calculated to be £471.00. Is this correct?", ); - await $(CurrencyAllPage.submit()).click(); + await click(CurrencyAllPage.submit()); }); it("Given I go back to section one and skip the first block, it is not included in the first calculated summary and consequently not included in the grand calculated summary", async () => { await $(HubPage.summaryRowLink("questions-section")).click(); await $(QuestionsSectionSummaryPage.skipAnswer1Edit()).click(); await $(SkipFirstBlockPage.yes()).click(); - await $(SkipFirstBlockPage.submit()).click(); - await $(QuestionsSectionSummaryPage.submit()).click(); + await click(SkipFirstBlockPage.submit()); + await click(QuestionsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£30.00"); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index 990e685913..3f072b714d 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -8,34 +8,34 @@ import Block3Page from "../../../generated_pages/grand_calculated_summary_overla import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/calculated-summary-4.page"; import GrandCalculatedSummaryShoppingPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/grand-calculated-summary-shopping.page"; import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/section-1-summary.page"; - +import { click } from "../../../helpers"; describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary with overlapping answers", () => { before("completing the survey", async () => { await browser.openQuestionnaire("test_grand_calculated_summary_overlapping_answers.json"); - await $(IntroductionBlockPage.submit()).click(); + await click(IntroductionBlockPage.submit()); // grand calculated summary should not be enabled until section-1 complete await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(Block1Page.q1A1()).setValue(100); await $(Block1Page.q1A2()).setValue(200); - await $(Block1Page.submit()).click(); + await click(Block1Page.submit()); await $(Block2Page.q2A1()).setValue(10); await $(Block2Page.q2A2()).setValue(20); - await $(Block2Page.submit()).click(); - await $(CalculatedSummary1Page.submit()).click(); - await $(CalculatedSummary2Page.submit()).click(); + await click(Block2Page.submit()); + await click(CalculatedSummary1Page.submit()); + await click(CalculatedSummary2Page.submit()); await $(Block3Page.yesExtraBreadAndCheese()).click(); - await $(Block3Page.submit()).click(); - await $(CalculatedSummary4Page.submit()).click(); - await $(Section1SummaryPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(Block3Page.submit()); + await click(CalculatedSummary4Page.submit()); + await click(Section1SummaryPage.submit()); + await click(HubPage.submit()); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?", ); - await $(GrandCalculatedSummaryShoppingPage.submit()).click(); + await click(GrandCalculatedSummaryShoppingPage.submit()); }); it("Given I edit an answer that is only used in a single calculated summary, I am routed back to the calculated summary and then the grand calculated summary", async () => { @@ -43,11 +43,11 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummaryShoppingPage.calculatedSummary2Edit()).click(); await $(CalculatedSummary2Page.q1A2Edit()).click(); await $(Block1Page.q1A2()).setValue(300); - await $(Block1Page.submit()).click(); + await click(Block1Page.submit()); // taken back to calculated summary await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); - await $(CalculatedSummary2Page.submit()).click(); + await click(CalculatedSummary2Page.submit()); // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); @@ -60,21 +60,21 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummaryShoppingPage.calculatedSummary2Edit()).click(); await $(CalculatedSummary2Page.q2A2Edit()).click(); await $(Block2Page.q2A2()).setValue(400); - await $(Block2Page.submit()).click(); + await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( "Total of eggs and cheese is calculated to be £700.00. Is this correct?", ); - await $(CalculatedSummary2Page.submit()).click(); + await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( "Total extra items cost is calculated to be £410.00. Is this correct?", ); - await $(CalculatedSummary4Page.submit()).click(); + await click(CalculatedSummary4Page.submit()); // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); @@ -87,28 +87,28 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummaryShoppingPage.calculatedSummary4Edit()).click(); await $(CalculatedSummary4Page.q2A2Edit()).click(); await $(Block2Page.q2A2()).setValue(500); - await $(Block2Page.submit()).click(); + await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( "Total of eggs and cheese is calculated to be £800.00. Is this correct?", ); - await $(CalculatedSummary2Page.submit()).click(); + await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( "Total extra items cost is calculated to be £510.00. Is this correct?", ); - await $(CalculatedSummary4Page.submit()).click(); + await click(CalculatedSummary4Page.submit()); // then grand calculated summary await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?", ); - await $(GrandCalculatedSummaryShoppingPage.submit()).click(); + await click(GrandCalculatedSummaryShoppingPage.submit()); }); it("Given I change an answer and return to the Hub before all calculated summaries are confirmed, the grand calculated summary section becomes inaccessible", async () => { @@ -116,10 +116,10 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummaryShoppingPage.calculatedSummary4Edit()).click(); await $(CalculatedSummary4Page.q2A2Edit()).click(); await $(Block2Page.q2A2()).setValue(100); - await $(Block2Page.submit()).click(); + await click(Block2Page.submit()); // confirm one of the calculated summaries but return to the hub instead of confirming the other - await $(CalculatedSummary2Page.submit()).click(); + await click(CalculatedSummary2Page.submit()); await browser.url(HubPage.url()); // calculated summary 4 is not confirmed so GCS doesn't show diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 33190f1853..4bb68cfbd9 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -30,37 +30,36 @@ import GrandCalculatedSummary5Page from "../../../generated_pages/grand_calculat import AnyUtilityBillsPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-utility-bills.page"; import Section4SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-4-summary.page"; import Section5SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-5-summary.page"; -import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink } from "../../../helpers"; - +import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary across multiple sections", () => { before("Reaching the grand calculated summary section", async () => { await browser.openQuestionnaire("test_grand_calculated_summary_repeating_answers.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); // complete 2 questions in section 1 await $(Block1Page.q1A1()).setValue(10); await $(Block1Page.q1A2()).setValue(20); - await $(Block1Page.submit()).click(); + await click(Block1Page.submit()); await $(Block2Page.q2A1()).setValue(30); await $(Block2Page.q2A2()).setValue(40); - await $(Block2Page.submit()).click(); - await $(CalculatedSummary1Page.submit()).click(); + await click(Block2Page.submit()); + await click(CalculatedSummary1Page.submit()); // and the one for section 2 await $(Block3Page.q3A1()).setValue(100); await $(Block3Page.q3A2()).setValue(200); - await $(Block3Page.submit()).click(); - await $(CalculatedSummary2Page.submit()).click(); - await $(CalculatedSummary3Page.submit()).click(); - await $(GrandCalculatedSummary1Page.submit()).click(); - await $(Section1SummaryPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(Block3Page.submit()); + await click(CalculatedSummary2Page.submit()); + await click(CalculatedSummary3Page.submit()); + await click(GrandCalculatedSummary1Page.submit()); + await click(Section1SummaryPage.submit()); + await click(HubPage.submit()); await $(Block4Page.q4A1()).setValue(5); await $(Block4Page.q4A2()).setValue(10); - await $(Block4Page.submit()).click(); - await $(CalculatedSummary4Page.submit()).click(); - await $(HubPage.submit()).click(); + await click(Block4Page.submit()); + await click(CalculatedSummary4Page.submit()); + await click(HubPage.submit()); }); it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { @@ -70,7 +69,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); - await $(CalculatedSummary1Page.submit()).click(); + await click(CalculatedSummary1Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); }); @@ -83,14 +82,14 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await browser.getUrl()).to.contain(Block4Page.pageName); await $(Block4Page.q4A1()).setValue(50); - await $(Block4Page.submit()).click(); + await click(Block4Page.submit()); // first taken back to the calculated summary which has updated await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( "Calculated Summary for games expenditure is calculated to be £60.00. Is this correct?", ); - await $(CalculatedSummary4Page.submit()).click(); + await click(CalculatedSummary4Page.submit()); // then taken back to the grand calculated summary which has also been updated correctly await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); @@ -109,7 +108,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(CalculatedSummary1Page.q1A1Edit()).click(); await expect(await browser.getUrl()).to.contain(Block1Page.pageName); await $(Block1Page.q1A1()).setValue(100); - await $(Block1Page.submit()).click(); + await click(Block1Page.submit()); // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); @@ -121,7 +120,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(CalculatedSummary1Page.q2A2Edit()).click(); await expect(await browser.getUrl()).to.contain(Block2Page.pageName); await $(Block2Page.q2A2()).setValue(400); - await $(Block2Page.submit()).click(); + await click(Block2Page.submit()); // back at updated calculated summary await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( @@ -129,11 +128,11 @@ describe("Feature: Grand Calculated Summary", () => { ); // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary - await $(CalculatedSummary1Page.submit()).click(); + await click(CalculatedSummary1Page.submit()); await expect(await browser.getUrl()).to.contain(CalculatedSummary3Page.pageName); - await $(CalculatedSummary3Page.submit()).click(); + await click(CalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary1Page.pageName); - await $(GrandCalculatedSummary1Page.submit()).click(); + await click(GrandCalculatedSummary1Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for shopping and entertainment is calculated to be £910.00. Is this correct?", @@ -141,7 +140,7 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I edit an answer included in a grand calculated summary, the calculated summary sections should return to partially completed, and the grand calculated summary becomes unavailable.", async () => { - await $(GrandCalculatedSummary2Page.submit()).click(); + await click(GrandCalculatedSummary2Page.submit()); await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Completed"); // Now edit an answer from section 2 and go back to the hub @@ -149,7 +148,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); await $(CalculatedSummary4Page.q4A1Edit()).click(); await $(Block4Page.q4A1()).setValue(1); - await $(Block4Page.submit()).click(); + await click(Block4Page.submit()); await $(CalculatedSummary4Page.previous()).click(); await $(Block4Page.previous()).click(); @@ -160,7 +159,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I confirm the calculated summary, When I return to the Hub, Then I see the grand calculated summary come back marked as partially completed", async () => { await $(HubPage.summaryRowLink("section-2")).click(); - await $(CalculatedSummary4Page.submit()).click(); + await click(CalculatedSummary4Page.submit()); await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Partially completed"); }); @@ -170,8 +169,8 @@ describe("Feature: Grand Calculated Summary", () => { await $(CalculatedSummary4Page.q4A1Edit()).click(); await $(Block4Page.q4A1()).setValue(0); await $(Block4Page.q4A2()).setValue(0); - await $(Block4Page.submit()).click(); - await $(CalculatedSummary4Page.submit()).click(); + await click(Block4Page.submit()); + await click(CalculatedSummary4Page.submit()); // should be back at Hub, and grand calculated summary section not present await expect(await browser.getUrl()).to.contain(HubPage.pageName); await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; @@ -181,65 +180,65 @@ describe("Feature: Grand Calculated Summary", () => { // no grand calculated summary section on the hub await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(AnyUtilityBillsPage.yes()).click(); - await $(AnyUtilityBillsPage.submit()).click(); + await click(AnyUtilityBillsPage.submit()); await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Electricity"); - await $(AddUtilityBillPage.submit()).click(); + await click(AddUtilityBillPage.submit()); await $(AnyOtherUtilityBillsPage.yes()).click(); - await $(AnyOtherUtilityBillsPage.submit()).click(); + await click(AnyOtherUtilityBillsPage.submit()); await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Internet"); - await $(AddUtilityBillPage.submit()).click(); + await click(AddUtilityBillPage.submit()); await $(AnyOtherUtilityBillsPage.yes()).click(); - await $(AnyOtherUtilityBillsPage.submit()).click(); + await click(AnyOtherUtilityBillsPage.submit()); await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Gas"); - await $(AddUtilityBillPage.submit()).click(); + await click(AddUtilityBillPage.submit()); await $(AnyOtherUtilityBillsPage.no()).click(); - await $(AnyOtherUtilityBillsPage.submit()).click(); + await click(AnyOtherUtilityBillsPage.submit()); await $$(DynamicAnswerPage.inputs())[0].setValue(150); await $$(DynamicAnswerPage.inputs())[1].setValue(35); await $$(DynamicAnswerPage.inputs())[2].setValue(65); - await $(DynamicAnswerPage.submit()).click(); - await $(CalculatedSummary5Page.submit()).click(); - await $(Section4SummaryPage.submit()).click(); + await click(DynamicAnswerPage.submit()); + await click(CalculatedSummary5Page.submit()); + await click(Section4SummaryPage.submit()); // still no grand calculated summary yet await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(AnyStreamingServicesPage.yes()).click(); - await $(AnyStreamingServicesPage.submit()).click(); + await click(AnyStreamingServicesPage.submit()); await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Netflix"); - await $(AddStreamingServicePage.submit()).click(); + await click(AddStreamingServicePage.submit()); await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(10); await $(StreamingServiceRepeatingBlock1Page.streamingServiceExtraCost()).setValue(0); - await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await click(StreamingServiceRepeatingBlock1Page.submit()); await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(20); - await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await click(StreamingServiceRepeatingBlock2Page.submit()); await $(AnyOtherStreamingServicesPage.yes()).click(); - await $(AnyOtherStreamingServicesPage.submit()).click(); + await click(AnyOtherStreamingServicesPage.submit()); await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Prime video"); - await $(AddStreamingServicePage.submit()).click(); + await click(AddStreamingServicePage.submit()); await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(8); await $(StreamingServiceRepeatingBlock1Page.streamingServiceExtraCost()).setValue(12); - await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await click(StreamingServiceRepeatingBlock1Page.submit()); await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(25); - await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await click(StreamingServiceRepeatingBlock2Page.submit()); await $(AnyOtherStreamingServicesPage.no()).click(); - await $(AnyOtherStreamingServicesPage.submit()).click(); - await $(CalculatedSummary6Page.submit()).click(); - await $(CalculatedSummary7Page.submit()).click(); + await click(AnyOtherStreamingServicesPage.submit()); + await click(CalculatedSummary6Page.submit()); + await click(CalculatedSummary7Page.submit()); await $(OtherInternetUsagePage.mediaDownloads()).setValue(50); await $(OtherInternetUsagePage.miscInternet()).setValue(5); - await $(OtherInternetUsagePage.submit()).click(); - await $(CalculatedSummary8Page.submit()).click(); - await $(Section5SummaryPage.submit()).click(); + await click(OtherInternetUsagePage.submit()); + await click(CalculatedSummary8Page.submit()); + await click(Section5SummaryPage.submit()); // grand calculated summary now present await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.equal("Not started"); }); it("Given I have a calculated summary of repeating answers and a calculated summary of dynamic answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £280.00. Is this correct?", ); @@ -252,7 +251,7 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I have 2 calculated summaries of list repeating block answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); @@ -261,7 +260,7 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I have multiple calculated summaries of static, repeating and dynamic answers, When I reach the grand calculated summary of them all, Then I see the correct total and items", async () => { - await $(GrandCalculatedSummary4Page.submit()).click(); + await click(GrandCalculatedSummary4Page.submit()); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,130.00. Is this correct?", ); @@ -288,13 +287,13 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummary5Page.calculatedSummary5Edit()).click(); await repeatingAnswerChangeLink(1).click(); await $$(DynamicAnswerPage.inputs())[0].setValue("175"); - await $(DynamicAnswerPage.submit()).click(); - await $(CalculatedSummary5Page.submit()).click(); + await click(DynamicAnswerPage.submit()); + await click(CalculatedSummary5Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £305.00. Is this correct?", ); - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,155.00. Is this correct?", @@ -313,29 +312,29 @@ describe("Feature: Grand Calculated Summary", () => { await $(GrandCalculatedSummary5Page.calculatedSummary6Edit()).click(); await repeatingAnswerChangeLink(2).click(); await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(12); - await $(StreamingServiceRepeatingBlock1Page.submit()).click(); - await $(CalculatedSummary5Page.submit()).click(); + await click(StreamingServiceRepeatingBlock1Page.submit()); + await click(CalculatedSummary5Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £309.00. Is this correct?", ); - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", ); - await $(GrandCalculatedSummary5Page.submit()).click(); + await click(GrandCalculatedSummary5Page.submit()); }); it("Given I have a grand calculated summary featuring dynamic answers, When I add an item to the list collector and return to the hub, Then I see the section with dynamic answers is in progress, and the grand calculated summary section is not available", async () => { await $(HubPage.summaryRowLink("section-4")).click(); await $(Section4SummaryPage.utilityBillsListAddLink()).click(); await $(AddUtilityBillPage.utilityBillName()).selectByAttribute("value", "Water"); - await $(AddUtilityBillPage.submit()).click(); + await click(AddUtilityBillPage.submit()); await $(AnyOtherUtilityBillsPage.no()).click(); - await $(AnyOtherUtilityBillsPage.submit()).click(); + await click(AnyOtherUtilityBillsPage.submit()); await $$(DynamicAnswerPage.inputs())[3].setValue("40"); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-4")).getText()).to.contain("Partially completed"); await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; @@ -346,8 +345,8 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).to.contain( "Calculated Summary for monthly spending on utility bills is calculated to be £315.00. Is this correct?", ); - await $(CalculatedSummary5Page.submit()).click(); - await $(Section4SummaryPage.submit()).click(); + await click(CalculatedSummary5Page.submit()); + await click(Section4SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); }); @@ -357,17 +356,17 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); - await $(GrandCalculatedSummary4Page.submit()).click(); + await click(GrandCalculatedSummary4Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); - await $(GrandCalculatedSummary5Page.submit()).click(); + await click(GrandCalculatedSummary5Page.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Completed"); }); @@ -375,13 +374,13 @@ describe("Feature: Grand Calculated Summary", () => { await $(HubPage.summaryRowLink("section-5")).click(); await $(Section5SummaryPage.streamingServicesListAddLink()).click(); await $(AddStreamingServicePage.streamingServiceName()).selectByAttribute("value", "Disney+"); - await $(AddStreamingServicePage.submit()).click(); + await click(AddStreamingServicePage.submit()); await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(10); - await $(StreamingServiceRepeatingBlock1Page.submit()).click(); + await click(StreamingServiceRepeatingBlock1Page.submit()); await $(StreamingServiceRepeatingBlock2Page.streamingServiceUsage()).setValue(5); - await $(StreamingServiceRepeatingBlock2Page.submit()).click(); + await click(StreamingServiceRepeatingBlock2Page.submit()); await $(AnyOtherStreamingServicesPage.no()).click(); - await $(AnyOtherStreamingServicesPage.submit()).click(); + await click(AnyOtherStreamingServicesPage.submit()); await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( "Calculated Summary for monthly expenditure on streaming services is calculated to be £44.00. Is this correct?", ); @@ -389,12 +388,12 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(HubPage.summaryRowState("section-5")).getText()).to.contain("Partially completed"); await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; await $(HubPage.summaryRowLink("section-5")).click(); - await $(CalculatedSummary6Page.submit()).click(); + await click(CalculatedSummary6Page.submit()); await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( "Total monthly internet usage on streaming services is calculated to be 50 GB. Is this correct?", ); - await $(CalculatedSummary7Page.submit()).click(); - await $(Section5SummaryPage.submit()).click(); + await click(CalculatedSummary7Page.submit()); + await click(Section5SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); }); @@ -404,17 +403,17 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £359.00. Is this correct?", ); - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for internet usage is calculated to be 105 GB. Is this correct?", ); - await $(GrandCalculatedSummary4Page.submit()).click(); + await click(GrandCalculatedSummary4Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); - await $(GrandCalculatedSummary5Page.submit()).click(); + await click(GrandCalculatedSummary5Page.submit()); }); it("Given I remove a list item involved in the grand calculated summary, When I confirm, Then I am taken to each affected calculated summary to reconfirm, and when I return to the Hub the grand calculated summary is in progress", async () => { @@ -422,16 +421,16 @@ describe("Feature: Grand Calculated Summary", () => { await $(HubPage.summaryRowLink("section-5")).click(); await $(Section5SummaryPage.streamingServicesListRemoveLink(1)).click(); await $(RemoveStreamingServicePage.yes()).click(); - await $(RemoveStreamingServicePage.submit()).click(); + await click(RemoveStreamingServicePage.submit()); await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( "Calculated Summary for monthly expenditure on streaming services is calculated to be £34.00. Is this correct?", ); - await $(CalculatedSummary6Page.submit()).click(); + await click(CalculatedSummary6Page.submit()); await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( "Total monthly internet usage on streaming services is calculated to be 30 GB. Is this correct?", ); - await $(CalculatedSummary7Page.submit()).click(); - await $(Section5SummaryPage.submit()).click(); + await click(CalculatedSummary7Page.submit()); + await click(Section5SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); }); @@ -441,17 +440,17 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); - await $(GrandCalculatedSummary3Page.submit()).click(); + await click(GrandCalculatedSummary3Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for internet usage is calculated to be 85 GB. Is this correct?", ); - await $(GrandCalculatedSummary4Page.submit()).click(); + await click(GrandCalculatedSummary4Page.submit()); await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); - await $(GrandCalculatedSummary5Page.submit()).click(); + await click(GrandCalculatedSummary5Page.submit()); }); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js index cd8221fa3d..22280aecad 100644 --- a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js @@ -1,7 +1,7 @@ import EmploymentStatusBlockPage from "../../../generated_pages/hub_and_spoke/employment-status.page.js"; import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; import HubPage from "../../../base_pages/hub.page.js"; - +import { click } from "../../../helpers"; describe("Choose another section link", () => { it("When a user first views the Hub, then the link should not be displayed", async () => { await browser.openQuestionnaire("test_hub_and_spoke.json"); @@ -16,7 +16,7 @@ describe("Choose another section link", () => { it("When a user starts a new section and the hub is available, then the link should be displayed", async () => { await browser.openQuestionnaire("test_hub_complete_sections.json"); await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(HubPage.summaryRowLink("accommodation-section")).click(); await expect(await $("body").getText()).to.contain("Choose another section and return to this later"); }); @@ -24,10 +24,10 @@ describe("Choose another section link", () => { it("When a user gets to a section summary and the hub is available, then the link should not be displayed", async () => { await browser.openQuestionnaire("test_hub_complete_sections.json"); await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(HubPage.summaryRowLink("accommodation-section")).click(); await $(ProxyPage.noIMAnsweringForMyself()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 4afeab4596..969596a980 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -8,7 +8,7 @@ import HowManyPeopleLiveHere from "../../../generated_pages/hub_and_spoke/how-ma import HubPage from "../../../base_pages/hub.page.js"; import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; import RelationshipsSummary from "../../../generated_pages/hub_and_spoke/relationships-section-summary.page.js"; - +import { click } from "../../../helpers"; describe("Feature: Hub and Spoke", () => { const hubAndSpokeSchema = "test_hub_and_spoke.json"; @@ -58,13 +58,13 @@ describe("Feature: Hub and Spoke", () => { }); it("When the user starts a section, Then the first question in the section should be displayed", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); }); it("When the user starts a section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(EmploymentStatusBlockPage.previous()).click(); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(HubPage.url()); @@ -76,7 +76,7 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("employment-section")).click(); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); }); it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { @@ -103,38 +103,38 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("employment-section")).click(); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(EmploymentTypeBlockPage.studying()).click(); }); it("When the user clicks the 'Continue' button, it should return them to the hub", async () => { - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(HubPage.url()); }); it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); }); it("When the user returns to the Hub, Then the section should be marked as 'Completed'", async () => { - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); }); it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", async () => { - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); await $(HubPage.summaryRowLink("employment-section")).click(); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); }); it("When the user returns to the Hub and continues, Then they should progress to the next section", async () => { - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.url()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(ProxyPage.url()); }); @@ -145,7 +145,7 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("employment-section")).click(); await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); }); @@ -154,7 +154,7 @@ describe("Feature: Hub and Spoke", () => { await $(HubPage.summaryRowLink("employment-section")).click(); await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); }); @@ -163,7 +163,7 @@ describe("Feature: Hub and Spoke", () => { await $(HubPage.summaryRowLink("employment-section")).click(); await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await browser.url(HubPage.url()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(HubPage.url()); @@ -176,21 +176,21 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("employment-section")).click(); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(EmploymentTypeBlockPage.studying()).click(); - await $(EmploymentTypeBlockPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); + await click(HubPage.submit()); await $(ProxyPage.yes()).click(); - await $(ProxyPage.submit()).click(); - await $(AccomodationDetailsSummaryBlockPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ProxyPage.submit()); + await click(AccomodationDetailsSummaryBlockPage.submit()); + await click(HubPage.submit()); await $(DoesAnyoneLiveHere.no()).click(); - await $(DoesAnyoneLiveHere.submit()).click(); - await $(HouseholdSummary.submit()).click(); - await $(HubPage.submit()).click(); + await click(DoesAnyoneLiveHere.submit()); + await click(HouseholdSummary.submit()); + await click(HubPage.submit()); await $(AnyoneRelated.yes()).click(); - await $(AnyoneRelated.submit()).click(); - await $(RelationshipsSummary.submit()).click(); + await click(AnyoneRelated.submit()); + await click(RelationshipsSummary.submit()); }); it("It should return them to the hub", async () => { @@ -204,7 +204,7 @@ describe("Feature: Hub and Spoke", () => { }); it("When the user submits, it should show the thankyou page", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain("thank-you"); }); @@ -221,9 +221,9 @@ describe("Feature: Hub and Spoke", () => { it("The hub should only display when required sections are complete", async () => { await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(EmploymentTypeBlockPage.studying()).click(); - await $(EmploymentTypeBlockPage.submit()).click(); + await click(EmploymentTypeBlockPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.url()); }); }); @@ -233,13 +233,13 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("household-section")).click(); await $(DoesAnyoneLiveHere.no()).click(); - await $(DoesAnyoneLiveHere.submit()).click(); - await $(HouseholdSummary.submit()).click(); + await click(DoesAnyoneLiveHere.submit()); + await click(HouseholdSummary.submit()); }); it("When there are no changes, continue returns directly to the hub", async () => { await $(HubPage.summaryRowLink("household-section")).click(); - await $(HouseholdSummary.submit()).click(); + await click(HouseholdSummary.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(HubPage.url()); }); @@ -248,8 +248,8 @@ describe("Feature: Hub and Spoke", () => { await $(HubPage.summaryRowLink("household-section")).click(); await $(HouseholdSummary.doesAnyoneLiveHereAnswerEdit()).click(); await $(DoesAnyoneLiveHere.yes()).click(); - await $(DoesAnyoneLiveHere.submit()).click(); - await $(HouseholdSummary.submit()).click(); + await click(DoesAnyoneLiveHere.submit()); + await click(HouseholdSummary.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).to.contain(HowManyPeopleLiveHere.url()); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js index cae42aba0a..d2449609e8 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js @@ -2,7 +2,7 @@ import HouseholdSummary from "../../../generated_pages/hub_and_spoke_custom_cont import HowManyPeopleLiveHere from "../../../generated_pages/hub_and_spoke_custom_content/how-many-people-live-here.page.js"; import DoesAnyoneLiveHere from "../../../generated_pages/hub_and_spoke_custom_content/does-anyone-live-here.page.js"; import HubPage from "../../../base_pages/hub.page.js"; - +import { click } from "../../../helpers"; describe("Feature: Hub and Spoke with custom content", () => { const hubAndSpokeSchema = "test_hub_and_spoke_custom_content.json"; @@ -18,10 +18,10 @@ describe("Feature: Hub and Spoke with custom content", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await $(HubPage.summaryRowLink("household-section")).click(); await $(DoesAnyoneLiveHere.yes()).click(); - await $(DoesAnyoneLiveHere.submit()).click(); + await click(DoesAnyoneLiveHere.submit()); await $(HowManyPeopleLiveHere.answer1()).click(); - await $(HowManyPeopleLiveHere.submit()).click(); - await $(HouseholdSummary.submit()).click(); + await click(HowManyPeopleLiveHere.submit()); + await click(HouseholdSummary.submit()); await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js index f6f330b560..97fba7567d 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js @@ -1,21 +1,21 @@ import HouseholdRelationshipsBlockPage from "../../../generated_pages/hub_section_required_and_enabled/household-relationships-block.page"; import RelationshipsCountPage from "../../../generated_pages/hub_section_required_and_enabled/relationships-count.page"; import { SubmitPage } from "../../../base_pages/submit.page"; - +import { click } from "../../../helpers"; describe("Hub and spoke section required and enabled", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_hub_section_required_and_enabled.json"); }); it("Given a relationship question in household, When I answer 'Yes', meaning the second section is enabled, Then I am routed to second section", async () => { await $(HouseholdRelationshipsBlockPage.yes()).click(); - await $(HouseholdRelationshipsBlockPage.submit()).click(); + await click(HouseholdRelationshipsBlockPage.submit()); await expect(await $(RelationshipsCountPage.legend()).getText()).to.contain("How many people are related"); }); it("Given a relationship question in household, When I answer 'No', Then I am redirected to the hub and can submit my answers without completing the other section", async () => { await $(HouseholdRelationshipsBlockPage.no()).click(); - await $(HouseholdRelationshipsBlockPage.submit()).click(); + await click(HouseholdRelationshipsBlockPage.submit()); await expect(await $("body").getText()).to.contain("Submit survey"); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/previous.spec.js b/tests/functional/spec/features/hub_and_spoke/previous.spec.js index 6248ea3d0f..2206ea7974 100644 --- a/tests/functional/spec/features/hub_and_spoke/previous.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/previous.spec.js @@ -2,8 +2,8 @@ import EmploymentStatusBlockPage from "../../../generated_pages/hub_and_spoke/em import EmploymentTypePage from "../../../generated_pages/hub_and_spoke/employment-type.page.js"; import HubPage from "../../../base_pages/hub.page.js"; import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; +import { click } from "../../../helpers"; const schema = "test_hub_complete_sections.json"; - describe("Choose another section link", () => { beforeEach(async () => { await browser.openQuestionnaire(schema); @@ -15,19 +15,19 @@ describe("Choose another section link", () => { it("When a user gets to the hub, then the previous location link should not be displayed", async () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await expect(await $(HubPage.previous()).isExisting()).to.be.false; }); it("When a user gets to subsequent question, then the previous location link should be displayed", async () => { await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await expect(await $(EmploymentTypePage.previous()).isExisting()).to.be.true; }); it("When a user gets to subsequent questions past the hub, then the previous location link should be displayed", async () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); - await $(EmploymentStatusBlockPage.submit()).click(); + await click(EmploymentStatusBlockPage.submit()); await $(HubPage.summaryRowLink("accommodation-section")).click(); await expect(await $(ProxyPage.previous()).isExisting()).to.be.true; }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js index 752930b7ae..7fb20c8e6a 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js @@ -2,7 +2,7 @@ import { getRandomString } from "../../../jwt_helper"; import AddressConfirmationPage from "../../../generated_pages/last_viewed_question_guidance/address-confirmation.page"; import HouseholdInterstitialPage from "../../../generated_pages/last_viewed_question_guidance/household-interstitial.page.js"; import PrimaryPersonListCollectorPage from "../../../generated_pages/last_viewed_question_guidance/primary-person-list-collector.page.js"; - +import { click } from "../../../helpers"; describe("Last viewed question guidance", () => { const resumableLaunchParams = { responseId: getRandomString(16), @@ -28,7 +28,7 @@ describe("Last viewed question guidance", () => { }); it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { - await $(HouseholdInterstitialPage.submit()).click(); + await click(HouseholdInterstitialPage.submit()); await $(AddressConfirmationPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); await browser.pause(100); @@ -39,13 +39,13 @@ describe("Last viewed question guidance", () => { it("When the respondent answers the question and saves and continues, then last question guidance is not shown on the next question", async () => { await $(AddressConfirmationPage.yes()).click(); - await $(AddressConfirmationPage.submit()).click(); + await click(AddressConfirmationPage.submit()); await expect(await browser.getUrl()).to.contain(PrimaryPersonListCollectorPage.url()); await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); it("When the respondent uses the previous link from the next question, then last question guidance is not shown", async () => { - await $(AddressConfirmationPage.submit()).click(); + await click(AddressConfirmationPage.submit()); await $(PrimaryPersonListCollectorPage.previous()).click(); await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js index 7e324266df..4a9426cd19 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js @@ -8,7 +8,7 @@ import SportsPage from "../../../generated_pages/last_viewed_question_guidance_h import UnPaidWorkPage from "../../../generated_pages/last_viewed_question_guidance_hub/unpaid-work.page.js"; import WorkInterstitialPage from "../../../generated_pages/last_viewed_question_guidance_hub/work-interstitial.page.js"; import HubPage from "../../../base_pages/hub.page.js"; - +import { click } from "../../../helpers"; describe("Last viewed question guidance", () => { const resumableLaunchParams = { responseId: getRandomString(16), @@ -34,7 +34,7 @@ describe("Last viewed question guidance", () => { }); it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { - await $(WorkInterstitialPage.submit()).click(); + await click(WorkInterstitialPage.submit()); await $(PaidWorkPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); await expect(await $(PaidWorkPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(WorkInterstitialPage.url()); @@ -45,11 +45,11 @@ describe("Last viewed question guidance", () => { describe("Given the respondent has completed the required section and is on the hub", () => { before("Open survey and complete first section", async () => { await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json"); - await $(WorkInterstitialPage.submit()).click(); + await click(WorkInterstitialPage.submit()); await $(PaidWorkPage.yes()).click(); - await $(PaidWorkPage.submit()).click(); + await click(PaidWorkPage.submit()); await $(UnPaidWorkPage.yes()).click(); - await $(UnPaidWorkPage.submit()).click(); + await click(UnPaidWorkPage.submit()); }); it("When the respondent selects a section which is not started, then last question guidance is not shown", async () => { @@ -59,9 +59,9 @@ describe("Last viewed question guidance", () => { }); it("When the respondent selects a section which is in progress, then last question guidance is shown", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(GcsesPage.yes()).click(); - await $(GcsesPage.submit()).click(); + await click(GcsesPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("education-section")).click(); await expect(await browser.getUrl()).to.contain(ALevelsPage.url()); @@ -71,10 +71,10 @@ describe("Last viewed question guidance", () => { it("When the respondent selects a section which is complete , then last question guidance is not shown on the summary or any link clicked from the summary", async () => { await $(ALevelsPage.yes()).click(); - await $(ALevelsPage.submit()).click(); + await click(ALevelsPage.submit()); await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; - await $(EducationSectionSummaryPage.submit()).click(); + await click(EducationSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("education-section")).click(); await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); await $(EducationSectionSummaryPage.alevelsAnswerEdit()).click(); @@ -83,17 +83,17 @@ describe("Last viewed question guidance", () => { it("When the user clicks continue on the hub and it takes you to a section which is not started, then last question guidance is not shown", async () => { await browser.url(HubPage.url()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(SportsPage.url()); await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; }); it("When the user clicks continue on the hub and it takes you to a section which is in progress, then last question guidance is shown", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(SportsPage.yes()).click(); - await $(SportsPage.submit()).click(); + await click(SportsPage.submit()); await browser.url(HubPage.url()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(HobbiesPage.url()); await expect(await $(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(SportsPage.url()); await expect(await $(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; @@ -101,7 +101,7 @@ describe("Last viewed question guidance", () => { it("When the user clicks continue on the hub and it takes you to a section which is complete but doesnt have a summary, then last question guidance is not shown", async () => { await $(HobbiesPage.yes()).click(); - await $(HobbiesPage.submit()).click(); + await click(HobbiesPage.submit()); await $(HubPage.summaryRowLink("interests-section")).click(); await expect(await browser.getUrl()).to.contain(SportsPage.url()); await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; diff --git a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js index b04d872665..f6c1cf8c95 100644 --- a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js +++ b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js @@ -5,7 +5,7 @@ import FavouriteDrinkQuestion from "../../../generated_pages/placeholder_based_o import ListStatusQuestion from "../../../generated_pages/placeholder_based_on_first_item_in_list/list-status-2.page.js"; import SummaryPage from "../../../generated_pages/placeholder_based_on_first_item_in_list/personal-details-section-summary.page.js"; import HubPage from "../../../base_pages/hub.page.js"; - +import { click } from "../../../helpers"; describe("Component: Definition", () => { describe("Load the Survey", () => { beforeEach(async () => { @@ -14,20 +14,20 @@ describe("Component: Definition", () => { it("Given I am the first person in the list, When I get to the question page, Then I should see the default answer option", async () => { // Given - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); // When - await $(ListStatusInterstitial.submit()).click(); + await click(ListStatusInterstitial.submit()); await $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); - await $(FavouriteDrinkQuestion.submit()).click(); + await click(FavouriteDrinkQuestion.submit()); // Then await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Tea"); @@ -35,32 +35,32 @@ describe("Component: Definition", () => { it("Given I am not the first person in the list, When I get to the question page, Then I should see the correct answer option", async () => { // Given - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); // When - await $(ListStatusInterstitial.submit()).click(); + await click(ListStatusInterstitial.submit()); await $(FavouriteDrinkQuestion.answer()).setValue("Orange Juice"); - await $(FavouriteDrinkQuestion.submit()).click(); + await click(FavouriteDrinkQuestion.submit()); await $(ListStatusQuestion.listStatus2Tea()).click(); - await $(ListStatusQuestion.submit()).click(); - await $(SummaryPage.submit()).click(); - await $(HubPage.submit()).click(); - await $(ListStatusInterstitial.submit()).click(); + await click(ListStatusQuestion.submit()); + await click(SummaryPage.submit()); + await click(HubPage.submit()); + await click(ListStatusInterstitial.submit()); await $(FavouriteDrinkQuestion.answer()).setValue("Lemonade"); - await $(FavouriteDrinkQuestion.submit()).click(); + await click(FavouriteDrinkQuestion.submit()); // Then await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Orange Juice"); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js index 50a0cb1d86..f9ecf026ab 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js @@ -6,7 +6,7 @@ import AgeBlockDayMonthYearRangePage from "../../../generated_pages/placeholder_ import AgeTestDayMonthYearRangePage from "../../../generated_pages/placeholder_difference_in_years_range/age-test.page"; import AgeBlockMonthYearRangePage from "../../../generated_pages/placeholder_difference_in_years_month_year_range/date-block.page"; import AgeTestMonthYearRangePage from "../../../generated_pages/placeholder_difference_in_years_month_year_range/age-test.page"; - +import { click } from "../../../helpers"; describe("Difference check (years)", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_placeholder_difference_in_years.json"); @@ -16,7 +16,7 @@ describe("Difference check (years)", () => { await $(AgeBlockYearPage.day()).setValue(1); await $(AgeBlockYearPage.month()).setValue(1); await $(AgeBlockYearPage.year()).setValue(1990); - await $(AgeBlockYearPage.submit()).click(); + await click(AgeBlockYearPage.submit()); await expect(await $(AgeTestYearPage.heading()).getText()).to.equal(`You are ${getYears("1990/01/01")} years old. Is this correct?`); }); }); @@ -30,7 +30,7 @@ describe("Difference check (months and years)", () => { await $(AgeBlockMonthYearPage.Month()).setValue(1); await $(AgeBlockMonthYearPage.Year()).setValue(1990); - await $(AgeBlockMonthYearPage.submit()).click(); + await click(AgeBlockMonthYearPage.submit()); await expect(await $(AgeTestMonthYearPage.heading()).getText()).to.equal( `It has been ${getYears("1990/01/01")} years since you last went on holiday. Is this correct?`, @@ -49,7 +49,7 @@ describe("Difference check (months and years range)", () => { await $(AgeBlockMonthYearRangePage.periodToMonth()).setValue(1); await $(AgeBlockMonthYearRangePage.periodToYear()).setValue(1991); - await $(AgeBlockMonthYearRangePage.submit()).click(); + await click(AgeBlockMonthYearRangePage.submit()); await expect(await $(AgeTestMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); }); @@ -69,7 +69,7 @@ describe("Difference check (years range)", () => { await $(AgeBlockDayMonthYearRangePage.periodTomonth()).setValue(1); await $(AgeBlockDayMonthYearRangePage.periodToyear()).setValue(1991); - await $(AgeBlockDayMonthYearRangePage.submit()).click(); + await click(AgeBlockDayMonthYearRangePage.submit()); await expect(await $(AgeTestDayMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js index 159471b013..9fb84a6528 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js @@ -2,7 +2,7 @@ import DateQuestionPage from "../../../generated_pages/placeholder_transform_dat import DaysQuestionBlockPage from "../../../generated_pages/placeholder_transform_date_range_bounds/days-question-block.page"; import Block0Page from "../../../generated_pages/placeholder_transform_date_range_bounds/block0.page"; import RangeQuestionBlockPage from "../../../generated_pages/placeholder_transform_date_range_bounds/range-question-block.page"; - +import { click } from "../../../helpers"; describe("Date checks", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_placeholder_transform_date_range_bounds.json"); @@ -13,10 +13,10 @@ describe("Date checks", () => { await $(DateQuestionPage.month()).setValue(9); await $(DateQuestionPage.year()).setValue(2021); - await $(DateQuestionPage.submit()).click(); + await click(DateQuestionPage.submit()); await expect(await $(DaysQuestionBlockPage.questionText()).getText()).to.contain("Monday 30 August to Monday 13 September 2021"); - await $(DaysQuestionBlockPage.submit()).click(); + await click(DaysQuestionBlockPage.submit()); }); it("Given a reference date is provided, when I get to the next page, then the placeholder contains a formatted date range", async () => { @@ -24,8 +24,8 @@ describe("Date checks", () => { await $(DateQuestionPage.month()).setValue(9); await $(DateQuestionPage.year()).setValue(2021); - await $(DateQuestionPage.submit()).click(); - await $(DaysQuestionBlockPage.submit()).click(); + await click(DateQuestionPage.submit()); + await click(DaysQuestionBlockPage.submit()); await $(Block0Page.ref0day()).setValue(1); await $(Block0Page.ref0month()).setValue(5); @@ -35,7 +35,7 @@ describe("Date checks", () => { await $(Block0Page.ref1month()).setValue(5); await $(Block0Page.ref1year()).setValue(2019); - await $(Block0Page.submit()).click(); + await click(Block0Page.submit()); await expect(await $(RangeQuestionBlockPage.questionText()).getText()).to.contain("Wednesday 1 to Sunday 19 May 2019"); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js index 0cbadf3e55..042ef0723a 100644 --- a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js @@ -1,33 +1,33 @@ import EmployeesNumberBlockPage from "../../../generated_pages/placeholder_default_value/employees-number-block.page"; import EmployeesTrainingBlockPage from "../../../generated_pages/placeholder_default_value/employees-training-block.page"; import EmployeesNumberInterstitialPage from "../../../generated_pages/placeholder_default_value/employees-number-interstitial.page"; - +import { click } from "../../../helpers"; describe("Placeholder default value check", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_placeholder_default_value.json"); }); it("Given a question with default answer, When I do not enter any number and click submit, Then the interstitial page shows default employees number as 0", async () => { - await $(EmployeesNumberBlockPage.submit()).click(); + await click(EmployeesNumberBlockPage.submit()); await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 0"); }); it("Given a question with default answer, When I enter a number of employee and click submit, Then the interstitial page shows me the employees number entered", async () => { await $(EmployeesNumberBlockPage.employeesNo()).setValue("54"); - await $(EmployeesNumberBlockPage.submit()).click(); + await click(EmployeesNumberBlockPage.submit()); await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 54"); }); it("Given a training budget question with default answer, When I do not enter any amount and click submit, Then the interstitial page shows default amount as 250.00", async () => { - await $(EmployeesNumberBlockPage.submit()).click(); - await $(EmployeesNumberInterstitialPage.submit()).click(); - await $(EmployeesTrainingBlockPage.submit()).click(); + await click(EmployeesNumberBlockPage.submit()); + await click(EmployeesNumberInterstitialPage.submit()); + await click(EmployeesTrainingBlockPage.submit()); await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £250.00"); }); it("Given a training budget question with default answer, When I enter an amount and click submit, Then the interstitial page shows amount entered", async () => { - await $(EmployeesNumberBlockPage.submit()).click(); - await $(EmployeesNumberInterstitialPage.submit()).click(); + await click(EmployeesNumberBlockPage.submit()); + await click(EmployeesNumberInterstitialPage.submit()); await $(EmployeesTrainingBlockPage.employeesAvgTraining()).setValue("100"); - await $(EmployeesTrainingBlockPage.submit()).click(); + await click(EmployeesTrainingBlockPage.submit()); await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £100.00"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js index e6756457fe..3bfe6df62c 100644 --- a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js @@ -1,6 +1,6 @@ import MandatoryRadioPage from "../../../generated_pages/placeholder_metadata/mandatory-radio.page"; import SubmitPage from "../../../generated_pages/placeholder_metadata/submit.page"; - +import { click } from "../../../helpers"; describe("Placeholder metadata check", () => { describe("Given I launch placeholder metadata question", () => { before("Load the survey", async () => { @@ -11,7 +11,7 @@ describe("Placeholder metadata check", () => { }); it("When I answer responding unit question, Then I see confirmation page with my selected placeholder metadata option (ru_name)", async () => { await $(MandatoryRadioPage.answerRuName()).click(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await expect(await $(SubmitPage.mandatoryRadioAnswer()).getText()).to.equal("Apple"); await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); diff --git a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js index c1d2c2c740..07f643c226 100644 --- a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js @@ -1,6 +1,6 @@ import MandatoryRadioPage from "../../../generated_pages/placeholder_option_label_from_value/mandatory-radio.page"; import ConfirmationQuestionRadioBlockPage from "../../../generated_pages/placeholder_option_label_from_value/confirmation-question-radio-block.page"; - +import { click } from "../../../helpers"; describe("Option label value check", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_placeholder_option_label_from_value.json"); @@ -10,14 +10,14 @@ describe("Option label value check", () => { await expect(await $(MandatoryRadioPage.answerBusinessNamePipedLabel()).getText()).to.contain("Apple (piped)"); await $(MandatoryRadioPage.answerBusinessNamePiped()).click(); await $(MandatoryRadioPage.submit()).scrollIntoView(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Apple (piped)"); }); it("Given radio options are provided, when I select an answer (static) and go to the next page, then the question title contains the label text of the answer I selected", async () => { await $(MandatoryRadioPage.googleLtd()).click(); await $(MandatoryRadioPage.submit()).scrollIntoView(); - await $(MandatoryRadioPage.submit()).click(); + await click(MandatoryRadioPage.submit()); await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Google LTD"); }); }); diff --git a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js index cd403ef426..fa4786feab 100644 --- a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js +++ b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js @@ -1,5 +1,5 @@ import MandatoryCheckboxPage from "../../../generated_pages/placeholder_playback_list/mandatory-checkbox.page"; - +import { click } from "../../../helpers"; describe("Feature: Playback Confirmation", () => { beforeEach("Open the schema", async () => { await browser.openQuestionnaire("test_placeholder_playback_list.json"); @@ -8,7 +8,7 @@ describe("Feature: Playback Confirmation", () => { it("When the user submits an answer, their answers should be shown on the confirmation screen", async () => { await $(MandatoryCheckboxPage.cheese()).click(); await $(MandatoryCheckboxPage.ham()).click(); - await $(MandatoryCheckboxPage.submit()).click(); + await click(MandatoryCheckboxPage.submit()); await expect(await $("#confirm-answers-question ul").getHTML()) .to.contain("Cheese") diff --git a/tests/functional/spec/features/progress/progress_value_source_blocks.js b/tests/functional/spec/features/progress/progress_value_source_blocks.js index c7219c932c..449bd81ee3 100644 --- a/tests/functional/spec/features/progress/progress_value_source_blocks.js +++ b/tests/functional/spec/features/progress/progress_value_source_blocks.js @@ -8,7 +8,7 @@ import SixthQuestionPage from "../../../generated_pages/progress_value_source_bl import SeventhQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b7.page"; import SubmitPage from "../../../generated_pages/progress_value_source_blocks/submit.page"; import HubPage from "../../../base_pages/hub.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing based on progress value sources using block identifiers", () => { beforeEach(async () => { await browser.openQuestionnaire("test_progress_value_source_blocks.json"); @@ -17,17 +17,17 @@ describe("Feature: Routing based on progress value sources using block identifi describe("Given I have routing based on the completeness of a block", () => { it("When the block being evaluated is incomplete (Q2), Then the dependent question (Q4) should not be on the path or displayed on the summary", async () => { await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); - await $(ThirdQuestionPage.submit()).click(); + await click(ThirdQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("2"); - await $(FifthQuestionPage.submit()).click(); + await click(FifthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("3"); - await $(SeventhQuestionPage.submit()).click(); + await click(SeventhQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $("body").getText()).to.not.have.string("Section 1 Question 2"); @@ -38,28 +38,28 @@ describe("Feature: Routing based on progress value sources using block identifi describe("Given I have routing based on the completeness of a block", () => { it("When the blocks being evaluated are complete (Q2 + Q5), Then the dependent questions (Q4 + Q6) should be on the path and displayed on the summary", async () => { await $(FirstQuestionPage.q1A1()).setValue("1"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); - await $(SecondQuestionPage.submit()).click(); + await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); - await $(ThirdQuestionPage.submit()).click(); + await click(ThirdQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); - await $(FourthQuestionPage.submit()).click(); + await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); - await $(FifthQuestionPage.submit()).click(); + await click(FifthQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); - await $(SixthQuestionPage.submit()).click(); + await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); - await $(SeventhQuestionPage.submit()).click(); + await click(SeventhQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); @@ -70,38 +70,38 @@ describe("Feature: Routing based on progress value sources using block identifi describe("Given I have routing based on the completeness of a block", () => { it("When an answer is changed so that the block being evaluated is completed, Then the dependent questions (Q4 + Q6) should be on the path and displayed on the summary", async () => { await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); - await $(ThirdQuestionPage.submit()).click(); + await click(ThirdQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("2"); - await $(FifthQuestionPage.submit()).click(); + await click(FifthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("3"); - await $(SeventhQuestionPage.submit()).click(); + await click(SeventhQuestionPage.submit()); await $(SubmitPage.s1B1Q1A1Edit()).click(); await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("1"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); - await $(SecondQuestionPage.submit()).click(); + await click(SecondQuestionPage.submit()); - await $(ThirdQuestionPage.submit()).click(); + await click(ThirdQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); - await $(FourthQuestionPage.submit()).click(); + await click(FourthQuestionPage.submit()); - await $(FifthQuestionPage.submit()).click(); + await click(FifthQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("3"); - await $(SixthQuestionPage.submit()).click(); + await click(SixthQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); @@ -112,34 +112,34 @@ describe("Feature: Routing based on progress value sources using block identifi describe("Given I have routing based on the completeness of a block", () => { it("When an answer is removed form the path block being evaluated is no longer completed, Then the dependent questions (Q4 + Q6) should not be on the path and not be displayed on the summary", async () => { await $(FirstQuestionPage.q1A1()).setValue("1"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); - await $(SecondQuestionPage.submit()).click(); + await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); - await $(ThirdQuestionPage.submit()).click(); + await click(ThirdQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); - await $(FourthQuestionPage.submit()).click(); + await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); - await $(FifthQuestionPage.submit()).click(); + await click(FifthQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); - await $(SixthQuestionPage.submit()).click(); + await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); - await $(SeventhQuestionPage.submit()).click(); + await click(SeventhQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.s1B1Q1A1Edit()).click(); await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await expect(await $("body").getText()).to.not.have.string("Section 1 Question 4"); await expect(await $("body").getText()).to.not.have.string("Section 1 Question 6"); @@ -155,9 +155,9 @@ describe("Feature: Section enabled based on progress value sources using block i describe("Given I have a section enabled based on the completeness of a block", () => { it("When the block being evaluated is complete, Then the dependent section should be enabled", async () => { await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await $(SecondQuestionPage.q1A1()).setValue("1"); - await $(SecondQuestionPage.submit()).click(); + await click(SecondQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(ThirdQuestionSectionTwoPage.pageName); }); }); @@ -170,20 +170,20 @@ describe("Feature: Section enabled based on progress value sources using section describe("Given I have a section enabled based on the completeness of another section", () => { it("When the section being evaluated is complete, Then the dependent section should be enabled", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await $(SecondQuestionPage.q1A1()).setValue("1"); - await $(SecondQuestionPage.submit()).click(); + await click(SecondQuestionPage.submit()); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Not started"); }); }); describe("Given I have a section enabled based on the completeness of another section", () => { it("When the section being evaluated is incomplete, Then the dependent section should not be enabled", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FirstQuestionPage.q1A1()).setValue("0"); - await $(FirstQuestionPage.submit()).click(); + await click(FirstQuestionPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); diff --git a/tests/functional/spec/features/progress/progress_value_source_repeating.js b/tests/functional/spec/features/progress/progress_value_source_repeating.js index 51b544c022..0fc3193e01 100644 --- a/tests/functional/spec/features/progress/progress_value_source_repeating.js +++ b/tests/functional/spec/features/progress/progress_value_source_repeating.js @@ -11,7 +11,7 @@ import FirstNumberBlockPage from "../../../generated_pages/progress_value_source import SecondNumberBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/second-number-block.page"; import SectionTwoQuestionBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/s2-b1.page"; import CalculatedSummaryBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/calculated-summary-block.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing rules based on progress value sources in repeating sections", () => { beforeEach(async () => { await browser.openQuestionnaire("test_progress_block_value_source_repeating_sections.json"); @@ -19,77 +19,77 @@ describe("Feature: Routing rules based on progress value sources in repeating se describe("Given I have routing in a repeating section based on the completeness of a block", () => { it("When the block is incomplete, then I should not see the dependent question in the repeating section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(QuestionBlockPage.pageName); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); await $(HubPage.summaryRowLink("section-2-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SectionTwoSummaryPage.pageName); }); }); describe("Given I have routing in a repeating section based on the completeness of a block", () => { it("When the block is complete, then I should see the dependent question in the repeating section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(QuestionBlockPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(QuestionBlockPage.submit()); await $(RandomQuestionEnablerBlockPage.randomQuestionEnabler()).setValue(1); - await $(RandomQuestionEnablerBlockPage.submit()).click(); + await click(RandomQuestionEnablerBlockPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("section-2-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); }); }); describe("Given I have routing in a repeating section based on the completeness of a block", () => { it("When the status of the block changes from incomplete to complete, then the dependent question should be on the path in the repeating sections", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Joe"); await $(ListCollectorAddPage.lastName()).setValue("Bloggs"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Not started"); await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); await $(HubPage.summaryRowLink("section-2-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); - await $(SectionTwoSummaryPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); + await click(SectionTwoSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); - await $(HubPage.submit()).click(); - await $(QuestionBlockPage.submit()).click(); + await click(HubPage.submit()); + await click(QuestionBlockPage.submit()); await $(RandomQuestionEnablerBlockPage.randomQuestionEnabler()).setValue(1); - await $(RandomQuestionEnablerBlockPage.submit()).click(); + await click(RandomQuestionEnablerBlockPage.submit()); await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Partially completed"); await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); @@ -104,25 +104,25 @@ describe("Feature: Routing rules based on progress value sources in repeating se describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { it("When the calculated summary block is incomplete, then I should not see the dependent question in the repeating section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FirstNumberBlockPage.firstNumber()).setValue(1); - await $(FirstNumberBlockPage.submit()).click(); + await click(FirstNumberBlockPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("section-2")).click(); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); - await $(SectionThreeSummaryPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); + await click(SectionThreeSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); @@ -132,25 +132,25 @@ describe("Feature: Routing rules based on progress value sources in repeating se describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { it("When the calculated summary block is incomplete but is updated so that it is completed, then I should see the dependency should be updated in the repeating section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FirstNumberBlockPage.firstNumber()).setValue(1); - await $(FirstNumberBlockPage.submit()).click(); + await click(FirstNumberBlockPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("section-2")).click(); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); - await $(SectionThreeSummaryPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); + await click(SectionThreeSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); @@ -158,8 +158,8 @@ describe("Feature: Routing rules based on progress value sources in repeating se await $(HubPage.summaryRowLink("section-1")).click(); await $(SecondNumberBlockPage.secondNumber()).setValue(2); - await $(SecondNumberBlockPage.submit()).click(); - await $(CalculatedSummaryBlockPage.submit()).click(); + await click(SecondNumberBlockPage.submit()); + await click(CalculatedSummaryBlockPage.submit()); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); @@ -170,29 +170,29 @@ describe("Feature: Routing rules based on progress value sources in repeating se describe("Given I have routing in a repeating section based on the completeness of a calculated summary", () => { it("When the calculated summary block is complete, then I should see the dependent question in the repeating section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(FirstNumberBlockPage.firstNumber()).setValue(1); - await $(FirstNumberBlockPage.submit()).click(); + await click(FirstNumberBlockPage.submit()); await $(SecondNumberBlockPage.secondNumber()).setValue(2); - await $(SecondNumberBlockPage.submit()).click(); - await $(CalculatedSummaryBlockPage.submit()).click(); + await click(SecondNumberBlockPage.submit()); + await click(CalculatedSummaryBlockPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("section-2")).click(); await $(SectionTwoQuestionBlockPage.q1A1()).setValue(1); - await $(SectionTwoQuestionBlockPage.submit()).click(); + await click(SectionTwoQuestionBlockPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); - await $(DOBQuestionBlockPage.submit()).click(); + await click(DOBQuestionBlockPage.submit()); await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); }); }); diff --git a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js index 0d5318ba7d..e736288912 100644 --- a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js +++ b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js @@ -2,7 +2,7 @@ import AddressBlockPage from "../../../generated_pages/custom_question_summary/a import AgeBlock from "../../../generated_pages/custom_question_summary/age.page.js"; import NameBlockPage from "../../../generated_pages/custom_question_summary/name.page.js"; import SubmitPage from "../../../generated_pages/custom_question_summary/submit.page.js"; - +import { click } from "../../../helpers"; describe("Summary Screen", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_custom_question_summary.json"); @@ -16,9 +16,9 @@ describe("Summary Screen", () => { }); it("Given no values are entered in a question with multiple answers and concatenation set, when on the summary screen then the correct response should be displayed", async () => { - await $(NameBlockPage.submit()).click(); - await $(AddressBlockPage.submit()).click(); - await $(AgeBlock.submit()).click(); + await click(NameBlockPage.submit()); + await click(AddressBlockPage.submit()); + await click(AgeBlock.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("No answer provided"); }); @@ -26,13 +26,13 @@ describe("Summary Screen", () => { async function completeAllQuestions() { await $(NameBlockPage.first()).setValue("John"); await $(NameBlockPage.last()).setValue("Smith"); - await $(NameBlockPage.submit()).click(); + await click(NameBlockPage.submit()); await $(AddressBlockPage.line1()).setValue("Cardiff Road"); await $(AddressBlockPage.townCity()).setValue("Newport"); await $(AddressBlockPage.postcode()).setValue("NP10 8XG"); - await $(AddressBlockPage.submit()).click(); + await click(AddressBlockPage.submit()); await $(AgeBlock.number()).setValue(7); await $(AgeBlock.singleCheckboxThisAgeIsAnEstimate()).click(); - await $(AgeBlock.submit()).click(); + await click(AgeBlock.submit()); } }); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 135d3029e3..300b2e74c8 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -9,16 +9,15 @@ import AnyOtherCompaniesOrBranchesPage from "../../../generated_pages/list_colle import SectionCompaniesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/section-companies-summary.page"; import AnyOtherTradingDetailsPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-trading-details.page"; import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/submit.page"; -import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete } from "../../../helpers"; +import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; - const summaryValues = 'dd[class="ons-summary__values"]'; async function proceedToListCollector() { await $(ResponsiblePartyPage.yes()).click(); - await $(AnyCompaniesOrBranchesPage.submit()).click(); + await click(AnyCompaniesOrBranchesPage.submit()); await $(AnyCompaniesOrBranchesPage.yes()).click(); - await $(AnyCompaniesOrBranchesPage.submit()).click(); + await click(AnyCompaniesOrBranchesPage.submit()); } async function addCompany( @@ -31,12 +30,12 @@ async function addCompany( authorisedTraderEu, ) { await $(AddCompanyPage.companyOrBranchName()).setValue(companyOrBranchName); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(registrationNumber); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(registrationDateDay); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(registrationDateMonth); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(registrationDateYear); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); if (authorisedTraderUk) { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); } else { @@ -47,7 +46,7 @@ async function addCompany( } else if (authorisedTraderEu !== undefined) { await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); } - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); } describe("List Collector Repeating Blocks", function () { @@ -65,15 +64,15 @@ describe("List Collector Repeating Blocks", function () { await proceedToListCollector(); await addCompany("ONS", 123, 1, 1, 2023, true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("GOV", 456, 2, 2, 2023, false, false); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(AnyOtherTradingDetailsPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(AnyOtherTradingDetailsPage.submit()); + await click(SectionCompaniesPage.submit()); + await click(SubmitPage.submit()); }); }); @@ -85,10 +84,10 @@ describe("List Collector Repeating Blocks", function () { await proceedToListCollector(); await addCompany("ONS", 123, 1, 1, 2023, true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("GOV", 456, 2, 2, 2023, false, false); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("MOD", 789, 3, 3, 2023, true); }); @@ -100,14 +99,14 @@ describe("List Collector Repeating Blocks", function () { it("The list collector allows the name of 'GOV' to be changed", async () => { await $(AnyOtherCompaniesOrBranchesPage.listEditLink(2)).click(); await $(EditCompanyPage.companyOrBranchName()).setValue("Government"); - await $(EditCompanyPage.submit()).click(); + await click(EditCompanyPage.submit()); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.equal("Government"); }); it("The list collector allows removal of 'Government'", async () => { await $(AnyOtherCompaniesOrBranchesPage.listRemoveLink(2)).click(); await $(RemoveCompanyPage.yes()).click(); - await $(RemoveCompanyPage.submit()).click(); + await click(RemoveCompanyPage.submit()); }); it("The list collector does not show 'GOV' anymore.", async () => { @@ -117,7 +116,7 @@ describe("List Collector Repeating Blocks", function () { it("The list collector can add more companies.", async () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("Council", 101, 4, 4, 2023, false, true); }); @@ -128,11 +127,11 @@ describe("List Collector Repeating Blocks", function () { it("The list collector can then be submitted", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(AnyOtherTradingDetailsPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(AnyOtherTradingDetailsPage.submit()); + await click(SectionCompaniesPage.submit()); + await click(SubmitPage.submit()); }); }); @@ -145,27 +144,27 @@ describe("List Collector Repeating Blocks", function () { await addCompany("ONS", 123, 1, 1, 2023, true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); await $(EditCompanyPage.cancelAndReturn()).click(); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.cancelAndReturn()).click(); await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); await $(EditCompanyPage.cancelAndReturn()).click(); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("NAV", 101, 4, 4, 2023, true, true); }); @@ -180,22 +179,22 @@ describe("List Collector Repeating Blocks", function () { it("Attempting to complete the list collector will navigate the user to the first incomplete block of the second list item.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); }); it("Attempting to complete the list collector will navigate the user to the first incomplete block of the third list item.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); }); it("All items are now marked as completed with the checkmark icon.", async () => { @@ -207,10 +206,10 @@ describe("List Collector Repeating Blocks", function () { it("The list collector can now be submitted.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); - await $(AnyOtherTradingDetailsPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(AnyOtherTradingDetailsPage.submit()); + await click(SectionCompaniesPage.submit()); + await click(SubmitPage.submit()); }); }); @@ -222,43 +221,43 @@ describe("List Collector Repeating Blocks", function () { await proceedToListCollector(); await addCompany("ONS", 123, 1, 1, 2023, true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("GOV", 456, 2, 2, 2023, false); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); - await $(AnyOtherTradingDetailsPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(AnyOtherTradingDetailsPage.submit()); + await click(SectionCompaniesPage.submit()); }); it("Edit each type of answer on different items from the section summary.", async () => { await expect(await $$(summaryValues)[8].getText()).to.have.string(456); await repeatingAnswerChangeLink(8).click(); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await expect(await $$(summaryValues)[8].getText()).to.have.string(789); await expect(await $$(summaryValues)[4].getText()).to.have.string("1 January 2023"); await repeatingAnswerChangeLink(4).click(); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(4); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(4); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await expect(await $$(summaryValues)[4].getText()).to.have.string("4 April 2023"); await expect(await $$(summaryValues)[5].getText()).to.have.string("Yes"); await repeatingAnswerChangeLink(5).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); await expect(await $$(summaryValues)[5].getText()).to.have.string("No"); await expect(await $$(summaryValues)[11].getText()).to.have.string("No answer provided"); await repeatingAnswerChangeLink(11).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); await expect(await $$(summaryValues)[11].getText()).to.have.string("Yes"); }); it("The list collector can then be submitted", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); }); }); @@ -276,9 +275,9 @@ describe("Given a journey through the list collector with repeating blocks, in a await addCompany("ONS", 123, 1, 1, 2023, true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); }); @@ -287,23 +286,23 @@ describe("Given a journey through the list collector with repeating blocks, in a await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); }); it("Another incomplete item can be added via the section summary.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); - await $(AnyOtherTradingDetailsPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(AnyOtherTradingDetailsPage.submit()); await $(SectionCompaniesPage.companiesListAddLink()).click(); await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); }); @@ -312,45 +311,45 @@ describe("Given a journey through the list collector with repeating blocks, in a await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); }); it("The user is able to add additional company via a list collector, edit this newly added list item and return to list collector page", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(SectionCompaniesPage.submit()); await $(HubPage.summaryRowLink("section-companies")).click(); await $(SectionCompaniesPage.companiesListAddLink()).click(); await $(AddCompanyPage.companyOrBranchName()).setValue("MOJ"); - await $(AddCompanyPage.submit()).click(); + await click(AddCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.previous()).click(); await $(EditCompanyPage.previous()).click(); await $(AnyOtherCompaniesOrBranchesPage.listEditLink(4)).click(); await expect(await browser.getUrl()).to.contain(EditCompanyPage.pageName); - await $(EditCompanyPage.submit()).click(); + await click(EditCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.previous()).click(); await $(EditCompanyPage.previous()).click(); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); - await $(CompaniesRepeatingBlock1Page.submit()).click(); + await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); - await $(CompaniesRepeatingBlock2Page.submit()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); - await $(SectionCompaniesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(SectionCompaniesPage.submit()); }); it("The section can now be submitted and and hub will redirect user to the next section.", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ResponsiblePartyHubPage.pageName); }); }); diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index 8467e71d59..b7cc5ea238 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -16,7 +16,7 @@ import VisitorsDateOfBirthPage from "../../../generated_pages/repeating_sections import VisitorsListCollectorAddPage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block-add.page"; import VisitorsListCollectorPage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block.page"; import VisitorsListCollectorRemovePage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block-remove.page"; - +import { click } from "../../../helpers"; describe("Feature: Repeating Sections with Hub and Spoke", () => { describe("Given the user has added some members to the household and is on the Hub", () => { before("Open survey and add household members", async () => { @@ -33,41 +33,41 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // Add a primary person await $(PrimaryPersonPage.yes()).click(); - await $(PrimaryPersonPage.submit()).click(); + await click(PrimaryPersonPage.submit()); await $(PrimaryPersonAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonPage.submit()).click(); + await click(PrimaryPersonPage.submit()); // Add other household members (First list collector) await $(FirstListCollectorPage.yes()).click(); - await $(FirstListCollectorPage.submit()).click(); + await click(FirstListCollectorPage.submit()); await $(FirstListCollectorAddPage.firstName()).setValue("Jean"); await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - await $(FirstListCollectorAddPage.submit()).click(); + await click(FirstListCollectorAddPage.submit()); await $(FirstListCollectorPage.yes()).click(); - await $(FirstListCollectorPage.submit()).click(); + await click(FirstListCollectorPage.submit()); await $(FirstListCollectorAddPage.firstName()).setValue("Samuel"); await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - await $(FirstListCollectorAddPage.submit()).click(); + await click(FirstListCollectorAddPage.submit()); // Go to second list collector await $(FirstListCollectorPage.no()).click(); - await $(FirstListCollectorPage.submit()).click(); - await $(SecondListCollectorInterstitialPage.submit()).click(); + await click(FirstListCollectorPage.submit()); + await click(SecondListCollectorInterstitialPage.submit()); // Add other household members (Second list collector) await $(SecondListCollectorPage.yes()).click(); - await $(SecondListCollectorPage.submit()).click(); + await click(SecondListCollectorPage.submit()); await $(SecondListCollectorAddPage.firstName()).setValue("John"); await $(SecondListCollectorAddPage.lastName()).setValue("Doe"); - await $(SecondListCollectorAddPage.submit()).click(); + await click(SecondListCollectorAddPage.submit()); // Go back to the Hub await $(SecondListCollectorPage.no()).click(); - await $(SecondListCollectorPage.submit()).click(); + await click(SecondListCollectorPage.submit()); await $(VisitorsListCollectorPage.no()).click(); - await $(VisitorsListCollectorPage.submit()).click(); + await click(VisitorsListCollectorPage.submit()); }); beforeEach("Navigate to the Hub", async () => await browser.url(HubPage.url())); @@ -98,15 +98,15 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { it("When the user partially completes a repeating section, Then that section should be marked as 'Partially completed' on the Hub", async () => { await $(HubPage.summaryRowLink("personal-details-section-1")).click(); await $(ProxyPage.yes()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await $(DateOfBirthPage.day()).setValue("01"); await $(DateOfBirthPage.month()).setValue("03"); await $(DateOfBirthPage.year()).setValue("2000"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await browser.url(HubPage.url()); @@ -123,20 +123,20 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { it("When the user completes a repeating section, Then that section should be marked as 'Completed' on the Hub", async () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); await $(ProxyPage.yes()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await $(DateOfBirthPage.day()).setValue("09"); await $(DateOfBirthPage.month()).setValue("09"); await $(DateOfBirthPage.year()).setValue("1995"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await $(SexPage.female()).click(); - await $(SexPage.submit()).click(); + await click(SexPage.submit()); - await $(PersonalDetailsSummaryPage.submit()).click(); + await click(PersonalDetailsSummaryPage.submit()); await expect(await browser.getUrl()).to.contain(HubPage.url()); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); @@ -164,21 +164,21 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(SectionSummaryPage.visitorListAddLink()).click(); await $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); await $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); - await $(VisitorsListCollectorAddPage.submit()).click(); + await click(VisitorsListCollectorAddPage.submit()); await expect(await browser.getUrl()).to.contain("/questionnaire/visitors-block"); // Add second visitor await $(VisitorsListCollectorPage.yes()).click(); - await $(VisitorsListCollectorPage.submit()).click(); + await click(VisitorsListCollectorPage.submit()); await $(VisitorsListCollectorAddPage.firstName()).setValue("Yvonne"); await $(VisitorsListCollectorAddPage.lastName()).setValue("Yoe"); - await $(VisitorsListCollectorAddPage.submit()).click(); + await click(VisitorsListCollectorAddPage.submit()); // Exit the visitors list collector await $(VisitorsListCollectorPage.no()).click(); - await $(VisitorsListCollectorPage.submit()).click(); + await click(VisitorsListCollectorPage.submit()); - await $(SectionSummaryPage.submit()).click(); + await click(SectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("visitors-section-1")).getText()).to.equal("Not started"); await expect(await $(HubPage.summaryRowTitle("visitors-section-1")).getText()).to.equal("Joe Public"); @@ -189,22 +189,22 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { }); it("When the user clicks 'Continue' from the Hub, Then they should progress to the first incomplete section", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(ConfirmDateOfBirthPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); }); it("When the user answers on their behalf, Then they are shown the non proxy question variant", async () => { await $(HubPage.summaryRowLink("personal-details-section-4")).click(); await $(ProxyPage.noIMAnsweringForMyself()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await $(DateOfBirthPage.day()).setValue("07"); await $(DateOfBirthPage.month()).setValue("07"); await $(DateOfBirthPage.year()).setValue("1970"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesIAmAgeOld()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await expect(await $(SexPage.questionText()).getText()).to.equal("What is your sex?"); }); @@ -212,45 +212,45 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { it("When the user answers on on behalf of someone else, Then they are shown the proxy question variant for the relevant repeating section", async () => { await $(HubPage.summaryRowLink("personal-details-section-3")).click(); await $(ProxyPage.yes()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await $(DateOfBirthPage.day()).setValue("11"); await $(DateOfBirthPage.month()).setValue("11"); await $(DateOfBirthPage.year()).setValue("1990"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); - await $(ConfirmDateOfBirthPage.submit()).click(); + await click(ConfirmDateOfBirthPage.submit()); await expect(await $(SexPage.questionText()).getText()).to.equal("What is Samuel Clemens’ sex?"); }); it("When the user completes all sections, Then the Hub should be in the completed state", async () => { // Complete remaining sections - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(SexPage.male()).click(); - await $(SexPage.submit()).click(); - await $(PersonalDetailsSummaryPage.submit()).click(); + await click(SexPage.submit()); + await click(PersonalDetailsSummaryPage.submit()); - await $(HubPage.submit()).click(); - await $(SexPage.submit()).click(); - await $(PersonalDetailsSummaryPage.submit()).click(); + await click(HubPage.submit()); + await click(SexPage.submit()); + await click(PersonalDetailsSummaryPage.submit()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(SexPage.female()).click(); - await $(SexPage.submit()).click(); - await $(PersonalDetailsSummaryPage.submit()).click(); + await click(SexPage.submit()); + await click(PersonalDetailsSummaryPage.submit()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(VisitorsDateOfBirthPage.day()).setValue("03"); await $(VisitorsDateOfBirthPage.month()).setValue("09"); await $(VisitorsDateOfBirthPage.year()).setValue("1975"); - await $(VisitorsDateOfBirthPage.submit()).click(); + await click(VisitorsDateOfBirthPage.submit()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(VisitorsDateOfBirthPage.day()).setValue("31"); await $(VisitorsDateOfBirthPage.month()).setValue("07"); await $(VisitorsDateOfBirthPage.year()).setValue("1999"); - await $(VisitorsDateOfBirthPage.submit()).click(); + await click(VisitorsDateOfBirthPage.submit()); await expect(await $(HubPage.submit()).getText()).to.equal("Submit survey"); await expect(await $(HubPage.heading()).getText()).to.equal("Submit survey"); @@ -263,12 +263,12 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(SectionSummaryPage.visitorListAddLink()).click(); await $(VisitorsListCollectorAddPage.firstName()).setValue("Anna"); await $(VisitorsListCollectorAddPage.lastName()).setValue("Doe"); - await $(VisitorsListCollectorAddPage.submit()).click(); + await click(VisitorsListCollectorAddPage.submit()); await $(VisitorsListCollectorPage.no()).click(); - await $(VisitorsListCollectorPage.submit()).click(); + await click(VisitorsListCollectorPage.submit()); - await $(SectionSummaryPage.submit()).click(); + await click(SectionSummaryPage.submit()); // New visitor added to hub await expect(await $(HubPage.summaryRowState("visitors-section-3")).getText()).to.equal("Not started"); @@ -291,15 +291,15 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(SectionSummaryPage.visitorListRemoveLink(3)).click(); await $(VisitorsListCollectorRemovePage.yes()).click(); - await $(VisitorsListCollectorPage.submit()).click(); - await $(SectionSummaryPage.submit()).click(); + await click(VisitorsListCollectorPage.submit()); + await click(SectionSummaryPage.submit()); // Ensure final householder no longer exists await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; }); it("When the user submits, it should show the thank you page", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); }); diff --git a/tests/functional/spec/features/routing/all_in.spec.js b/tests/functional/spec/features/routing/all_in.spec.js index 2453610bb8..de3c6a87ce 100644 --- a/tests/functional/spec/features/routing/all_in.spec.js +++ b/tests/functional/spec/features/routing/all_in.spec.js @@ -1,6 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_all/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-india-and-malta.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-not-india-and-malta.page"; +import { click } from "../../../helpers"; describe("Feature: Routing - ALL-IN Operator", () => { describe("Equals", () => { @@ -12,18 +13,18 @@ describe("Feature: Routing - ALL-IN Operator", () => { it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); it("When I do select India only, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/and.spec.js b/tests/functional/spec/features/routing/and.spec.js index aece04f558..d24edd0ee9 100644 --- a/tests/functional/spec/features/routing/and.spec.js +++ b/tests/functional/spec/features/routing/and.spec.js @@ -2,6 +2,7 @@ import FirstNumberQuestionPage from "../../../generated_pages/routing_and/number import SecondNumberQuestionPage from "../../../generated_pages/routing_and/number-question-2.page"; import CorrectAnswerPage from "../../../generated_pages/routing_and/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_and/incorrect-answer.page"; +import { click } from "../../../helpers"; describe("Feature: Routing - And Operator", () => { describe("Equals", () => { @@ -12,33 +13,33 @@ describe("Feature: Routing - And Operator", () => { it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(123); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the incorrect page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(555); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the incorrect page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(123); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(555); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js index bfb9d23e6c..095475e2b3 100644 --- a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js +++ b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js @@ -1,5 +1,6 @@ import RouteComparison1Page from "../../../generated_pages/routing_answer_comparison/route-comparison-1.page.js"; import RouteComparison2Page from "../../../generated_pages/routing_answer_comparison/route-comparison-2.page.js"; +import { click } from "../../../helpers"; describe("Test routing skip", () => { beforeEach(async () => { @@ -8,25 +9,25 @@ describe("Test routing skip", () => { it("Given we start the routing test survey, When we enter a low number then a high number, Then, we should be routed to the fourth page", async () => { await $(RouteComparison1Page.answer()).setValue(1); - await $(RouteComparison1Page.submit()).click(); + await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(2); - await $(RouteComparison2Page.submit()).click(); + await click(RouteComparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("This page should never be skipped"); }); it("Given we start the routing test survey, When we enter a high number then a low number, Then, we should be routed to the third page", async () => { await $(RouteComparison1Page.answer()).setValue(1); - await $(RouteComparison1Page.submit()).click(); + await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(0); - await $(RouteComparison2Page.submit()).click(); + await click(RouteComparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); }); it("Given we start the routing test survey, When we enter an equal number on both questions, Then, we should be routed to the third page", async () => { await $(RouteComparison1Page.answer()).setValue(1); - await $(RouteComparison1Page.submit()).click(); + await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(1); - await $(RouteComparison2Page.submit()).click(); + await click(RouteComparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); }); }); diff --git a/tests/functional/spec/features/routing/answer_not_on_path.spec.js b/tests/functional/spec/features/routing/answer_not_on_path.spec.js index 6d679e39f2..20da2e9bad 100644 --- a/tests/functional/spec/features/routing/answer_not_on_path.spec.js +++ b/tests/functional/spec/features/routing/answer_not_on_path.spec.js @@ -3,7 +3,7 @@ import InvalidPathPage from "../../../generated_pages/routing_not_affected_by_an import InvalidPathInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/invalid-path-interstitial.page.js"; import ValidPathPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-path.page.js"; import ValidFinalInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-final-interstitial.page.js"; - +import { click } from "../../../helpers"; describe("Answers not on path are not considered when routing", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_not_affected_by_answers_not_on_path.json"); @@ -11,11 +11,11 @@ describe("Answers not on path are not considered when routing", () => { it("Given the user enters an answer on the first path, when they return to the second path, they should be routed to the valid path interstitial", async () => { await $(InitialChoicePage.goHereFirst()).click(); - await $(InitialChoicePage.submit()).click(); + await click(InitialChoicePage.submit()); await expect(await browser.getUrl()).to.contain(InvalidPathPage.pageName); await $(InvalidPathPage.answer()).setValue(123); - await $(InvalidPathPage.submit()).click(); + await click(InvalidPathPage.submit()); // We now have an answer in the store on the 'invalid' path @@ -26,10 +26,10 @@ describe("Answers not on path are not considered when routing", () => { // Take the second route await $(InitialChoicePage.goHereSecond()).click(); - await $(InitialChoicePage.submit()).click(); + await click(InitialChoicePage.submit()); await $(ValidPathPage.answer()).setValue(321); - await $(ValidPathPage.submit()).click(); + await click(ValidPathPage.submit()); // We should be routed to the valid interstitial page since the invalid path answer should not be considered whilst routing. await expect(await browser.getUrl()).to.contain(ValidFinalInterstitialPage.pageName); diff --git a/tests/functional/spec/features/routing/answered_unanswered.spec.js b/tests/functional/spec/features/routing/answered_unanswered.spec.js index fba7b60e0a..9965197a86 100644 --- a/tests/functional/spec/features/routing/answered_unanswered.spec.js +++ b/tests/functional/spec/features/routing/answered_unanswered.spec.js @@ -9,7 +9,7 @@ import QuestionTwoUnanswered from "../../../generated_pages/routing_answered_una import QuestionThree from "../../../generated_pages/routing_answered_unanswered/block-3.page"; import QuestionThreeAnsweredOrNotZero from "../../../generated_pages/routing_answered_unanswered/answered-question-3.page"; import QuestionThreeUnansweredOrAnswerZero from "../../../generated_pages/routing_answered_unanswered/unanswered-or-zero-question-3.page"; - +import { click } from "../../../helpers"; describe("Test routing question answered/unanswered", () => { describe("Given I am on the first question", () => { beforeEach("Load the questionnaire", async () => { @@ -18,19 +18,19 @@ describe("Test routing question answered/unanswered", () => { it("When I select any answer and submit, Then I should see a page saying I have answered the first question", async () => { await $(QuestionOne.ham()).click(); - await $(QuestionOne.submit()).click(); + await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionOne.cheese()).click(); - await $(QuestionOne.submit()).click(); + await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the first question", async () => { - await $(QuestionOne.submit()).click(); + await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You did not answer the first question!"); await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); }); @@ -39,25 +39,25 @@ describe("Test routing question answered/unanswered", () => { describe("Given I am on the second question", () => { beforeEach("Load the questionnaire and get to the second question", async () => { await browser.openQuestionnaire("test_routing_answered_unanswered.json"); - await $(QuestionOne.submit()).click(); - await $(QuestionOneUnanswered.submit()).click(); + await click(QuestionOne.submit()); + await click(QuestionOneUnanswered.submit()); }); it("When I select any answer and submit, Then I should see a page saying I have answered the second question", async () => { await $(QuestionTwo.pizzaHut()).click(); - await $(QuestionTwo.submit()).click(); + await click(QuestionTwo.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionTwo.dominoS()).click(); - await $(QuestionTwo.submit()).click(); + await click(QuestionTwo.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the second question", async () => { - await $(QuestionTwo.submit()).click(); + await click(QuestionTwo.submit()); await expect(await $(QuestionTwoUnanswered.heading()).getText()).to.contain("You did not answer the second question!"); await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); }); @@ -66,27 +66,27 @@ describe("Test routing question answered/unanswered", () => { describe("Given I am on the third question", () => { beforeEach("Load the questionnaire and get to the third question", async () => { await browser.openQuestionnaire("test_routing_answered_unanswered.json"); - await $(QuestionOne.submit()).click(); - await $(QuestionOneUnanswered.submit()).click(); - await $(QuestionTwo.submit()).click(); - await $(QuestionTwoUnanswered.submit()).click(); + await click(QuestionOne.submit()); + await click(QuestionOneUnanswered.submit()); + await click(QuestionTwo.submit()); + await click(QuestionTwoUnanswered.submit()); }); it("When I do not answer the question or answer `0` and submit, Then I should see a page saying I did not answer the question or that I chose `0`", async () => { - await $(QuestionThree.submit()).click(); + await click(QuestionThree.submit()); await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); await $(QuestionThreeUnansweredOrAnswerZero.previous()).click(); await $(QuestionThree.answer3()).setValue("0"); - await $(QuestionThree.submit()).click(); + await click(QuestionThree.submit()); await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); }); it("When I enter an answer greater than 0 and submit, Then I should see a page saying I chose at least one", async () => { await $(QuestionThree.answer3()).setValue("2"); - await $(QuestionThree.submit()).click(); + await click(QuestionThree.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You chose at least 1 slice"); await expect(await browser.getUrl()).to.contain(QuestionThreeAnsweredOrNotZero.pageName); }); diff --git a/tests/functional/spec/features/routing/any_in.spec.js b/tests/functional/spec/features/routing/any_in.spec.js index 41e4167fda..6ab4cef38f 100644 --- a/tests/functional/spec/features/routing/any_in.spec.js +++ b/tests/functional/spec/features/routing/any_in.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_any/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-india-or-malta-or-both.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-not-india-or-malta-or-both.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing - ANY-IN Operator", () => { describe("Equals", () => { describe("Given I start the ANY-IN operator routing survey", () => { @@ -12,18 +12,18 @@ describe("Feature: Routing - ANY-IN Operator", () => { it("When I do select India and Malta, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); it("When I do select India or Malta, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/checkbox_count.spec.js b/tests/functional/spec/features/routing/checkbox_count.spec.js index 3ba7397fbc..f40f6c5dd5 100644 --- a/tests/functional/spec/features/routing/checkbox_count.spec.js +++ b/tests/functional/spec/features/routing/checkbox_count.spec.js @@ -1,7 +1,7 @@ import ToppingCheckboxPage from "../../../generated_pages/routing_checkbox_count/topping-checkbox.page.js"; import CorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/incorrect-answer.page"; - +import { click } from "../../../helpers"; describe("Test routing using count of checkboxes checked", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_checkbox_count.json"); @@ -10,14 +10,14 @@ describe("Test routing using count of checkboxes checked", () => { it("Given a user selects 2 checkboxes, When they submit, Then they should be routed to the correct page", async () => { await $(ToppingCheckboxPage.cheese()).click(); await $(ToppingCheckboxPage.ham()).click(); - await $(ToppingCheckboxPage.submit()).click(); + await click(ToppingCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); }); it("Given a user selects no checkboxes, When they submit, Then they should be routed to the incorrect page", async () => { - await $(ToppingCheckboxPage.submit()).click(); + await click(ToppingCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); @@ -25,7 +25,7 @@ describe("Test routing using count of checkboxes checked", () => { it("Given a user selects 1 checkbox, When they submit, Then they should be routed to the incorrect page", async () => { await $(ToppingCheckboxPage.cheese()).click(); - await $(ToppingCheckboxPage.submit()).click(); + await click(ToppingCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); @@ -35,7 +35,7 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.cheese()).click(); await $(ToppingCheckboxPage.ham()).click(); await $(ToppingCheckboxPage.pineapple()).click(); - await $(ToppingCheckboxPage.submit()).click(); + await click(ToppingCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/features/routing/date.spec.js index 0945cc2624..fddbcab619 100644 --- a/tests/functional/spec/features/routing/date.spec.js +++ b/tests/functional/spec/features/routing/date.spec.js @@ -8,7 +8,7 @@ import DateGreaterThanQuestionPage from "../../../generated_pages/routing_date_g import DateGreaterThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_greater_than_or_equals/date-question.page"; import DateLessThanQuestionPage from "../../../generated_pages/routing_date_less_than/date-question.page"; import DateLessThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_less_than_or_equals/date-question.page"; - +import { click } from "../../../helpers"; const today = new Date(); const dayToday = today.getDate(); const monthToday = today.getMonth() + 1; // January is 0! @@ -34,14 +34,14 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsComparisonQuestionPage.day()).setValue(31); await $(DateEqualsComparisonQuestionPage.month()).setValue(3); await $(DateEqualsComparisonQuestionPage.year()).setValue(2020); - await $(DateEqualsComparisonQuestionPage.submit()).click(); + await click(DateEqualsComparisonQuestionPage.submit()); }); it("When I enter the same date, Then I should be routed to the correct page", async () => { await $(DateEqualsQuestionPage.day()).setValue(31); await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -49,7 +49,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(30); await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -57,7 +57,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(1); await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -65,7 +65,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(29); await $(DateEqualsQuestionPage.month()).setValue(2); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -73,7 +73,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(30); await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -81,7 +81,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(31); await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2019); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -89,7 +89,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(31); await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2021); - await $(DateEqualsQuestionPage.submit()).click(); + await click(DateEqualsQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); @@ -97,7 +97,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.day()).setValue(1); await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); - await $(DateEqualsComparisonQuestionPage.submit()).click(); + await click(DateEqualsComparisonQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); }); @@ -112,7 +112,7 @@ describe("Feature: Routing on a Date", () => { it("When I enter a different date to February 2018, Then I should be routed to the correct page", async () => { await $(DateNotEqualsQuestionPage.Month()).setValue(3); await $(DateNotEqualsQuestionPage.Year()).setValue(2018); - await $(DateNotEqualsQuestionPage.submit()).click(); + await click(DateNotEqualsQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -122,7 +122,7 @@ describe("Feature: Routing on a Date", () => { it("When I enter February 2018, Then I should be routed to the incorrect page", async () => { await $(DateNotEqualsQuestionPage.Month()).setValue(2); await $(DateNotEqualsQuestionPage.Year()).setValue(2018); - await $(DateNotEqualsQuestionPage.submit()).click(); + await click(DateNotEqualsQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -141,7 +141,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.day()).setValue(2); await $(DateGreaterThanQuestionPage.month()).setValue(3); await $(DateGreaterThanQuestionPage.year()).setValue(2017); - await $(DateGreaterThanQuestionPage.submit()).click(); + await click(DateGreaterThanQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -152,7 +152,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.day()).setValue(1); await $(DateGreaterThanQuestionPage.month()).setValue(3); await $(DateGreaterThanQuestionPage.year()).setValue(2017); - await $(DateGreaterThanQuestionPage.submit()).click(); + await click(DateGreaterThanQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -163,7 +163,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.day()).setValue(28); await $(DateGreaterThanQuestionPage.month()).setValue(2); await $(DateGreaterThanQuestionPage.year()).setValue(2017); - await $(DateGreaterThanQuestionPage.submit()).click(); + await click(DateGreaterThanQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -180,7 +180,7 @@ describe("Feature: Routing on a Date", () => { it("When I enter a date greater than 2017, Then I should be routed to the correct page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2018); - await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + await click(DateGreaterThanOrEqualsQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -189,7 +189,7 @@ describe("Feature: Routing on a Date", () => { it("When I enter 2017, Then I should be routed to the correct page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2017); - await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + await click(DateGreaterThanOrEqualsQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -198,7 +198,7 @@ describe("Feature: Routing on a Date", () => { it("When I enter a date less than March 2017, Then I should be routed to the incorrect page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2016); - await $(DateGreaterThanOrEqualsQuestionPage.submit()).click(); + await click(DateGreaterThanOrEqualsQuestionPage.submit()); const expectedUrl = await browser.getUrl(); @@ -217,7 +217,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.day()).setValue(dayYesterday); await $(DateLessThanQuestionPage.month()).setValue(monthYesterday); await $(DateLessThanQuestionPage.year()).setValue(yearYesterday); - await $(DateLessThanQuestionPage.submit()).click(); + await click(DateLessThanQuestionPage.submit()); const browserUrl = await browser.getUrl(); @@ -228,7 +228,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.day()).setValue(dayToday); await $(DateLessThanQuestionPage.month()).setValue(monthToday); await $(DateLessThanQuestionPage.year()).setValue(yearToday); - await $(DateLessThanQuestionPage.submit()).click(); + await click(DateLessThanQuestionPage.submit()); const browserUrl = await browser.getUrl(); @@ -239,7 +239,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.day()).setValue(dayTomorrow); await $(DateLessThanQuestionPage.month()).setValue(monthTomorrow); await $(DateLessThanQuestionPage.year()).setValue(yearTomorrow); - await $(DateLessThanQuestionPage.submit()).click(); + await click(DateLessThanQuestionPage.submit()); const browserUrl = await browser.getUrl(); @@ -258,7 +258,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayYesterday); await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthYesterday); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearYesterday); - await $(DateLessThanOrEqualsQuestionPage.submit()).click(); + await click(DateLessThanOrEqualsQuestionPage.submit()); const browserUrl = await browser.getUrl(); @@ -269,7 +269,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayToday); await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthToday); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearToday); - await $(DateLessThanOrEqualsQuestionPage.submit()).click(); + await click(DateLessThanOrEqualsQuestionPage.submit()); const browserUrl = await browser.getUrl(); @@ -280,7 +280,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.day()).setValue(dayTomorrow); await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthTomorrow); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearTomorrow); - await $(DateLessThanOrEqualsQuestionPage.submit()).click(); + await click(DateLessThanOrEqualsQuestionPage.submit()); const browserUrl = await browser.getUrl(); diff --git a/tests/functional/spec/features/routing/in.spec.js b/tests/functional/spec/features/routing/in.spec.js index 10d14998c1..93619ab486 100644 --- a/tests/functional/spec/features/routing/in.spec.js +++ b/tests/functional/spec/features/routing/in.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_in/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-india.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-not-india.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing - IN Operator", () => { describe("Equals", () => { describe("Given I start the IN operator routing survey", () => { @@ -11,13 +11,13 @@ describe("Feature: Routing - IN Operator", () => { it("When I do select India, Then I should be routed to the the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); it("When I do not select India, Then I should be routed to the the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/not.spec.js b/tests/functional/spec/features/routing/not.spec.js index 771a06ebe0..dd8bb7fee5 100644 --- a/tests/functional/spec/features/routing/not.spec.js +++ b/tests/functional/spec/features/routing/not.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_not/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-not-india.page"; import IndiaInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-india.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing - Not Operator", () => { describe("Equals", () => { describe("Given I start the not operator routing survey", () => { @@ -11,13 +11,13 @@ describe("Feature: Routing - Not Operator", () => { it("When I do not select India, Then I should be routed to the not India interstitial page", async () => { await $(CountryCheckboxPage.azerbaijan()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); }); it("When I select India, Then I should be routed to the India interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); - await $(CountryCheckboxPage.submit()).click(); + await click(CountryCheckboxPage.submit()); await expect(await browser.getUrl()).to.contain(IndiaInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/number.spec.js b/tests/functional/spec/features/routing/number.spec.js index a932146467..ed343b4e4b 100644 --- a/tests/functional/spec/features/routing/number.spec.js +++ b/tests/functional/spec/features/routing/number.spec.js @@ -1,7 +1,7 @@ import NumberQuestionPage from "../../../generated_pages/routing_number_equals/number-question.page"; import CorrectAnswerPage from "../../../generated_pages/routing_number_equals/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_number_equals/incorrect-answer.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing on a Number", () => { describe("Equals", () => { describe("Given I start number routing equals survey", () => { @@ -11,14 +11,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter a number that isn't 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(555); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); @@ -32,14 +32,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number that isn't 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(987); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); @@ -53,21 +53,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); @@ -81,21 +81,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(77); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); it("When I enter a number greater than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(765); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); @@ -109,21 +109,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); @@ -137,21 +137,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(23); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I enter a number larger than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(546); - await $(NumberQuestionPage.submit()).click(); + await click(NumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/or.spec.js b/tests/functional/spec/features/routing/or.spec.js index 5d0d8d4598..dbab536988 100644 --- a/tests/functional/spec/features/routing/or.spec.js +++ b/tests/functional/spec/features/routing/or.spec.js @@ -2,7 +2,7 @@ import FirstNumberQuestionPage from "../../../generated_pages/routing_or/number- import SecondNumberQuestionPage from "../../../generated_pages/routing_or/number-question-2.page"; import CorrectAnswerPage from "../../../generated_pages/routing_or/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_or/incorrect-answer.page"; - +import { click } from "../../../helpers"; describe("Feature: Routing - OR Operator", () => { describe("Equals", () => { describe("Given I start the or operator routing survey", () => { @@ -12,33 +12,33 @@ describe("Feature: Routing - OR Operator", () => { it("When I enter both answers correctly with 123 and 321, Then I should be routed to the correct page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(123); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the correct page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(555); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the correct page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(123); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { await $(FirstNumberQuestionPage.answer1()).setValue(555); - await $(FirstNumberQuestionPage.submit()).click(); + await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); - await $(SecondNumberQuestionPage.submit()).click(); + await click(SecondNumberQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/removes_completed_block.spec.js b/tests/functional/spec/features/routing/removes_completed_block.spec.js index 4ec39fc0aa..ff0b8760da 100644 --- a/tests/functional/spec/features/routing/removes_completed_block.spec.js +++ b/tests/functional/spec/features/routing/removes_completed_block.spec.js @@ -1,15 +1,15 @@ import NumberOfEmployeesTotalBlockPage from "../../../generated_pages/confirmation_question/number-of-employees-total-block.page.js"; import ConfirmZeroEmployeesBlockPage from "../../../generated_pages/confirmation_question/confirm-zero-employees-block.page.js"; import SubmitPage from "../../../generated_pages/confirmation_question/submit.page.js"; - +import { click } from "../../../helpers"; describe("Feature: Routing incompletes block if routing backwards", () => { describe("Given I have a confirmation Question", () => { before("Get to summary", async () => { await browser.openQuestionnaire("test_confirmation_question.json"); await $(NumberOfEmployeesTotalBlockPage.numberOfEmployeesTotal()).setValue(0); - await $(NumberOfEmployeesTotalBlockPage.submit()).click(); + await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yes()).click(); - await $(ConfirmZeroEmployeesBlockPage.submit()).click(); + await click(ConfirmZeroEmployeesBlockPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary.spec.js b/tests/functional/spec/features/section_summary/section_summary.spec.js index 8bb7228b6b..242c7ed6ae 100644 --- a/tests/functional/spec/features/section_summary/section_summary.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary.spec.js @@ -8,15 +8,16 @@ import ListedPage from "../../../generated_pages/section_summary/listed.page.js" import NumberOfPeoplePage from "../../../generated_pages/section_summary/number-of-people.page.js"; import PropertyDetailsSummaryPage from "../../../generated_pages/section_summary/property-details-section-summary.page.js"; import SubmitPage from "../../../generated_pages/section_summary/submit.page.js"; +import { click } from "../../../helpers"; describe("Section Summary", () => { describe("Given I start a Test Section Summary survey and complete to Section Summary", () => { beforeEach(async () => { await browser.openQuestionnaire("test_section_summary.json"); await $(InsuranceTypePage.both()).click(); - await $(InsuranceTypePage.submit()).click(); - await $(InsuranceAddressPage.submit()).click(); - await $(ListedPage.submit()).click(); + await click(InsuranceTypePage.submit()); + await click(InsuranceAddressPage.submit()); + await click(ListedPage.submit()); await expect(await $(PropertyDetailsSummaryPage.insuranceTypeAnswer()).getText()).to.contain("Both"); }); @@ -27,7 +28,7 @@ describe("Section Summary", () => { it("When I have selected an answer to edit and edit it, Then I should return to the section summary with new value displayed", async () => { await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.answer()).setValue("Test Address"); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); }); @@ -38,32 +39,32 @@ describe("Section Summary", () => { }); it("When I continue on the section summary page, Then I should be taken to the next section", async () => { - await $(PropertyDetailsSummaryPage.submit()).click(); + await click(PropertyDetailsSummaryPage.submit()); await expect(await browser.getUrl()).to.contain(HouseType.pageName); }); it("When I select edit from Section Summary but change routing, Then I should step through the section and be returned to the Section Summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); - await $(AddressDurationPage.submit()).click(); + await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); }); it("When I select edit from Section Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); }); }); @@ -72,22 +73,22 @@ describe("Section Summary", () => { beforeEach(async () => { await browser.openQuestionnaire("test_section_summary.json"); await $(InsuranceTypePage.both()).click(); - await $(InsuranceTypePage.submit()).click(); - await $(InsuranceAddressPage.submit()).click(); - await $(ListedPage.submit()).click(); - await $(PropertyDetailsSummaryPage.submit()).click(); - await $(HouseType.submit()).click(); - await $(HouseholdDetailsSummaryPage.submit()).click(); + await click(InsuranceTypePage.submit()); + await click(InsuranceAddressPage.submit()); + await click(ListedPage.submit()); + await click(PropertyDetailsSummaryPage.submit()); + await click(HouseType.submit()); + await click(HouseholdDetailsSummaryPage.submit()); await $(NumberOfPeoplePage.answer()).setValue(3); - await $(NumberOfPeoplePage.submit()).click(); - await $(HouseholdCountSectionSummaryPage.submit()).click(); + await click(NumberOfPeoplePage.submit()); + await click(HouseholdCountSectionSummaryPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); it("When I select edit from Final Summary and don't change an answer, Then I should be taken to the Final Summary", async () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); @@ -95,7 +96,7 @@ describe("Section Summary", () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.answer()).setValue("Test Address"); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); @@ -103,11 +104,11 @@ describe("Section Summary", () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); - await $(AddressDurationPage.submit()).click(); + await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -115,14 +116,14 @@ describe("Section Summary", () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -130,7 +131,7 @@ describe("Section Summary", () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); await $(InsuranceAddressPage.previous()).click(); await expect(await browser.getUrl()).to.contain(InsuranceTypePage.pageName); }); @@ -141,7 +142,7 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); await $(InsuranceAddressPage.answer()).setValue("Test Address"); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); }); @@ -152,15 +153,15 @@ describe("Section Summary", () => { }); it("When there is no title set in the sections summary, the section title is used for the section summary title", async () => { await $(InsuranceTypePage.both()).click(); - await $(InsuranceTypePage.submit()).click(); - await $(InsuranceAddressPage.submit()).click(); - await $(ListedPage.submit()).click(); + await click(InsuranceTypePage.submit()); + await click(InsuranceAddressPage.submit()); + await click(ListedPage.submit()); await expect(await $(PropertyDetailsSummaryPage.heading()).getText()).to.contain("Property Details Section"); }); it("When there is a title set in the sections summary, it is used for the section summary title", async () => { - await $(PropertyDetailsSummaryPage.submit()).click(); + await click(PropertyDetailsSummaryPage.submit()); await $(HouseType.semiDetached()).click(); - await $(HouseType.submit()).click(); + await click(HouseType.submit()); await expect(await $(HouseholdDetailsSummaryPage.heading()).getText()).to.contain("Household Summary - Semi-detached"); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js index ec47750691..cd65e29248 100644 --- a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js @@ -6,6 +6,7 @@ import PersonalSummaryPage from "../../../generated_pages/repeating_section_summ import ProxyPage from "../../../generated_pages/repeating_section_summaries/proxy.page"; import DateOfBirthPage from "../../../generated_pages/repeating_section_summaries/date-of-birth.page"; import HubPage from "../../../base_pages/hub.page.js"; +import { click } from "../../../helpers"; describe("Feature: Repeating Section Summaries", () => { describe("Given the user has added some members to the household and is on the Hub", () => { @@ -20,33 +21,33 @@ describe("Feature: Repeating Section Summaries", () => { // Add a primary person await $(PrimaryPersonPage.yes()).click(); - await $(PrimaryPersonPage.submit()).click(); + await click(PrimaryPersonPage.submit()); await $(PrimaryPersonAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonAddPage.lastName()).setValue("Twain"); - await $(PrimaryPersonPage.submit()).click(); + await click(PrimaryPersonPage.submit()); // Add other household members await $(FirstListCollectorPage.yes()).click(); - await $(FirstListCollectorPage.submit()).click(); + await click(FirstListCollectorPage.submit()); await $(FirstListCollectorAddPage.firstName()).setValue("Jean"); await $(FirstListCollectorAddPage.lastName()).setValue("Clemens"); - await $(FirstListCollectorAddPage.submit()).click(); + await click(FirstListCollectorAddPage.submit()); await $(FirstListCollectorPage.no()).click(); - await $(FirstListCollectorPage.submit()).click(); + await click(FirstListCollectorPage.submit()); }); describe("When the user finishes a repeating section", () => { before("Enter information for a repeating section", async () => { await $(HubPage.summaryRowLink("personal-details-section-1")).click(); await $(ProxyPage.yes()).click(); - await $(ProxyPage.submit()).click(); + await click(ProxyPage.submit()); await $(DateOfBirthPage.day()).setValue("30"); await $(DateOfBirthPage.month()).setValue("11"); await $(DateOfBirthPage.year()).setValue("1835"); - await $(DateOfBirthPage.submit()).click(); + await click(DateOfBirthPage.submit()); }); beforeEach("Navigate to the Section Summary", async () => { diff --git a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js index 2d71b9377e..512e047c31 100644 --- a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js +++ b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js @@ -5,6 +5,7 @@ import proxyQuestionPage from "../../../generated_pages/show_section_summary_on_ import accommodationSectionSummary from "../../../generated_pages/show_section_summary_on_completion/accommodation-section-summary.page"; import hubPage from "../../../base_pages/hub.page.js"; +import { click } from "../../../helpers"; describe("Feature: Show section summary on completion", () => { before("Launch survey", async () => { @@ -14,7 +15,7 @@ describe("Feature: Show section summary on completion", () => { describe("Given I am completing a section with the summary turned off for the forward journey", () => { it("When I reach the end of that section, Then I go straight to the hub", async () => { await $(employmentStatusBlockPage.workingAsAnEmployee()).click(); - await $(employmentStatusBlockPage.submit()).click(); + await click(employmentStatusBlockPage.submit()); await expect(await browser.getUrl()).to.contain(hubPage.url()); }); @@ -36,7 +37,7 @@ describe("Feature: Show section summary on completion", () => { it("When I reach the end of that section, Then I will be taken to the section summary to enable me to amend an answer", async () => { await $(hubPage.summaryRowLink("accommodation-section")).click(); await $(proxyQuestionPage.noIMAnsweringForMyself()).click(); - await $(proxyQuestionPage.submit()).click(); + await click(proxyQuestionPage.submit()); await expect(await browser.getUrl()).to.contain(accommodationSectionSummary.url()); }); diff --git a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js index 324b6a596f..42f636517c 100644 --- a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js +++ b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js @@ -1,5 +1,6 @@ import Comparison1Page from "../../../generated_pages/skip_condition_answer_comparison/comparison-1.page.js"; import Comparison2Page from "../../../generated_pages/skip_condition_answer_comparison/comparison-2.page.js"; +import { click } from "../../../helpers"; describe("Test skip condition answer comparisons", () => { beforeEach(async () => { @@ -8,23 +9,23 @@ describe("Test skip condition answer comparisons", () => { it("Given we start the skip condition survey, when we enter the same answers, then the interstitial should show that the answers are the same", async () => { await $(Comparison1Page.answer()).setValue(1); - await $(Comparison1Page.submit()).click(); + await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(1); - await $(Comparison2Page.submit()).click(); + await click(Comparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("Your second number was equal to your first number"); }); it("Given we start the skip condition survey, when we enter a high number then a low number, then the interstitial should show that the answers are low then high", async () => { await $(Comparison1Page.answer()).setValue(3); - await $(Comparison1Page.submit()).click(); + await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(2); - await $(Comparison2Page.submit()).click(); + await click(Comparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("Your first answer was greater than your second number"); }); it("Given we start the skip condition survey, when we enter a low number then a high number, then the interstitial should show that the answers are high then low", async () => { await $(Comparison1Page.answer()).setValue(1); - await $(Comparison1Page.submit()).click(); + await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(2); - await $(Comparison2Page.submit()).click(); + await click(Comparison2Page.submit()); await expect(await $("#main-content > p").getText()).to.contain("Your first answer was less than your second number"); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page.spec.js b/tests/functional/spec/features/submit_page/submit_page.spec.js index 1ab2c0ffe0..a206b88549 100644 --- a/tests/functional/spec/features/submit_page/submit_page.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page.spec.js @@ -1,6 +1,7 @@ import BreakfastPage from "../../../generated_pages/submit_with_custom_submission_text/breakfast.page.js"; import { SubmitPage } from "../../../base_pages/submit.page"; import { IntroductionPage } from "../../../base_pages/introduction.page"; +import { click } from "../../../helpers"; describe("Given I launch a linear flow questionnaire without summary", () => { beforeEach("Load the questionnaire", async () => { @@ -10,14 +11,14 @@ describe("Given I launch a linear flow questionnaire without summary", () => { it("When I complete the questionnaire, then I should be taken to the submit page without a summary", async () => { await $(BreakfastPage.answer()).setValue("Bacon"); - await $(BreakfastPage.submit()).click(); + await click(BreakfastPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); await expect(await $(SubmitPage.summary()).isExisting()).to.be.false; }); it("When I complete the questionnaire and submit the questionnaire, then the submission is successful", async () => { - await $(BreakfastPage.submit()).click(); + await click(BreakfastPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js index 3e1cecedcc..e9246b645e 100644 --- a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js @@ -7,21 +7,22 @@ import ListedPage from "../../../generated_pages/section_summary/listed.page.js" import NumberOfPeoplePage from "../../../generated_pages/section_summary/number-of-people.page.js"; import PropertyDetailsSummaryPage from "../../../generated_pages/section_summary/property-details-section-summary.page.js"; import SubmitPage from "../../../generated_pages/section_summary/submit.page.js"; +import { click } from "../../../helpers"; describe("Collapsible Summary", () => { describe("Given I complete a questionnaire with collapsible summary enabled", () => { beforeEach(async () => { await browser.openQuestionnaire("test_section_summary.json"); await $(InsuranceTypePage.both()).click(); - await $(InsuranceTypePage.submit()).click(); - await $(InsuranceAddressPage.submit()).click(); - await $(ListedPage.submit()).click(); - await $(PropertyDetailsSummaryPage.submit()).click(); - await $(HouseType.submit()).click(); - await $(HouseholdDetailsSummaryPage.submit()).click(); + await click(InsuranceTypePage.submit()); + await click(InsuranceAddressPage.submit()); + await click(ListedPage.submit()); + await click(PropertyDetailsSummaryPage.submit()); + await click(HouseType.submit()); + await click(HouseholdDetailsSummaryPage.submit()); await $(NumberOfPeoplePage.answer()).setValue(3); - await $(NumberOfPeoplePage.submit()).click(); - await $(HouseholdCountSectionSummaryPage.submit()).click(); + await click(NumberOfPeoplePage.submit()); + await click(HouseholdCountSectionSummaryPage.submit()); }); it("When I am on the submit page, Then a collapsed summary should be displayed with the group title and questions should not be displayed", async () => { diff --git a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js index e91c30bdfd..d8e43fb64d 100644 --- a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js @@ -1,6 +1,6 @@ import DessertBlockPage from "../../../generated_pages/submit_with_summary_custom_submission_text/dessert-block.page.js"; import SubmitPage from "../../../generated_pages/submit_with_summary_custom_submission_text/submit.page.js"; - +import { click } from "../../../helpers"; describe("Summary Screen", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_submit_with_summary_custom_submission_text.json"); @@ -8,7 +8,7 @@ describe("Summary Screen", () => { it("Given a questionnaire with a summary and custom submission content has been completed, then the correct submission content should be displayed", async () => { await $(DessertBlockPage.dessert()).setValue("Crème Brûlée"); - await $(DessertBlockPage.submit()).click(); + await click(DessertBlockPage.submit()); await expect(await $(SubmitPage.heading()).getText()).to.contain("Submission title"); await expect(await $(SubmitPage.warning()).getText()).to.contain("Submission warning"); await expect(await $(SubmitPage.guidance()).getText()).to.contain("Submission guidance"); diff --git a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js index 2b0b5b087f..35e28ab92a 100644 --- a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js @@ -3,7 +3,7 @@ import DessertConfirmationPage from "../../../generated_pages/submit_with_summar import NumbersPage from "../../../generated_pages/submit_with_summary/numbers.page.js"; import RadioPage from "../../../generated_pages/submit_with_summary/radio.page.js"; import SubmitPage from "../../../generated_pages/submit_with_summary/submit.page.js"; - +import { click } from "../../../helpers"; describe("Submit Page with Summary", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_submit_with_summary.json"); @@ -24,7 +24,7 @@ describe("Submit Page with Summary", () => { it("Given a questionnaire with a summary has been completed when the submit page is displayed then I should be able to submit the answers", async () => { await completeAllQuestions(); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); @@ -50,7 +50,7 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.radioAnswerEdit()).click(); await $(RadioPage.sausage()).click(); - await $(RadioPage.submit()).click(); + await click(RadioPage.submit()); await expect(await $(SubmitPage.radioAnswer()).getText()).to.contain("Sausage"); }); @@ -60,28 +60,28 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.numbersUnitAnswerEdit()).click(); await expect(await $(NumbersPage.unit()).isFocused()).to.be.true; await $(NumbersPage.unit()).setValue("654321"); - await $(NumbersPage.submit()).click(); + await click(NumbersPage.submit()); await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).to.contain("654,321 km²"); }); it("Given a number value of zero is entered when on the submit page then formatted 0 should be displayed on the summary", async () => { - await $(RadioPage.submit()).click(); + await click(RadioPage.submit()); await $(DessertPage.answer()).setValue("Cake"); - await $(DessertPage.submit()).click(); + await click(DessertPage.submit()); await $(DessertConfirmationPage.yes()).click(); - await $(DessertConfirmationPage.submit()).click(); + await click(DessertConfirmationPage.submit()); await $(NumbersPage.currency()).setValue("0"); - await $(NumbersPage.submit()).click(); + await click(NumbersPage.submit()); await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£0.00"); }); it("Given no value is entered when on the submit page summary then the correct response should be displayed", async () => { - await $(RadioPage.submit()).click(); + await click(RadioPage.submit()); await $(DessertPage.answer()).setValue("Cake"); - await $(DessertPage.submit()).click(); + await click(DessertPage.submit()); await $(DessertConfirmationPage.yes()).click(); - await $(DessertConfirmationPage.submit()).click(); - await $(NumbersPage.submit()).click(); + await click(DessertConfirmationPage.submit()); + await click(NumbersPage.submit()); await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("No answer provided"); }); @@ -94,15 +94,15 @@ describe("Submit Page with Summary", () => { async function completeAllQuestions() { await $(RadioPage.bacon()).click(); - await $(RadioPage.submit()).click(); + await click(RadioPage.submit()); await $(DessertPage.answer()).setValue("Crème Brûlée"); - await $(DessertPage.submit()).click(); + await click(DessertPage.submit()); await $(DessertConfirmationPage.yes()).click(); - await $(DessertConfirmationPage.submit()).click(); + await click(DessertConfirmationPage.submit()); await $(NumbersPage.currency()).setValue("1234"); await $(NumbersPage.unit()).setValue("123456"); await $(NumbersPage.decimal()).setValue("123456.78"); - await $(NumbersPage.submit()).click(); + await click(NumbersPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); } diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index 35352ef9a3..e4ed903e12 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -6,14 +6,14 @@ import HouseholdDetailsSummaryPage from "../../generated_pages/submit_with_summa import SubmitPage from "../../generated_pages/submit_with_summary_return_to_answer/submit.page.js"; import AddressDurationPage from "../../generated_pages/submit_with_summary_return_to_answer/address-duration.page.js"; import NamePage from "../../generated_pages/submit_with_summary_return_to_answer/name.page.js"; - +import { click } from "../../helpers"; describe("Summary Anchor Scrolling", () => { describe("Given I start a Test Section Summary survey", () => { beforeEach(async () => { await browser.openQuestionnaire("test_submit_with_summary_return_to_answer.json"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); await $(InsuranceTypePage.both()).click(); - await $(InsuranceTypePage.submit()).click(); + await click(InsuranceTypePage.submit()); }); it("When I have provided an answer and click through to the next question, Then the Previous link url shouldn't contain any anchors or reference to return_to or return_to_answer_id", async () => { @@ -23,50 +23,50 @@ describe("Summary Anchor Scrolling", () => { }); it("When I reach the section summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( "insurance-address/?return_to=section-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", ); }); it("When I reach the section summary page, Then the Change link url for a concatenated answer should contain return_to, return_to_answer_id query params", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await expect(await $(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name", ); }); it("When I edit an answer from the section summary page, Then the Previous link url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).to.contain("property-details-section/#insurance-address-answer2"); }); it("When I edit an answer from the section summary page and click the Previous link, Then the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await $(InsuranceAddressPage.previous()).click(); await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); }); it("When I edit an answer from the section summary page and click the Submit button, Then I am taken to the summary page and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); - await $(InsuranceAddressPage.submit()).click(); + await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); }); it("When I am on the final summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); - await $(PropertyDetailsSummaryPage.submit()).click(); - await $(HouseType.submit()).click(); - await $(HouseholdDetailsSummaryPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); + await click(PropertyDetailsSummaryPage.submit()); + await click(HouseType.submit()); + await click(HouseholdDetailsSummaryPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); await expect(await $(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( "?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", @@ -74,11 +74,11 @@ describe("Summary Anchor Scrolling", () => { }); it("When I edit an answer from the final summary page, Then the browser url contains return_to, return_to_answer_id query params", async () => { - await $(InsuranceAddressPage.submit()).click(); - await $(AddressDurationPage.submit()).click(); - await $(PropertyDetailsSummaryPage.submit()).click(); - await $(HouseType.submit()).click(); - await $(HouseholdDetailsSummaryPage.submit()).click(); + await click(InsuranceAddressPage.submit()); + await click(AddressDurationPage.submit()); + await click(PropertyDetailsSummaryPage.submit()); + await click(HouseType.submit()); + await click(HouseholdDetailsSummaryPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswer2Edit()).click(); await expect(await browser.getUrl()).to.contain("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 0295c0e321..4321a25440 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -27,7 +27,7 @@ import Section6Page from "../../../generated_pages/supplementary_data/section-6- import ThankYouPage from "../../../base_pages/thank-you.page"; import TradingPage from "../../../generated_pages/supplementary_data/trading.page.js"; import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page.js"; -import { assertSummaryItems, assertSummaryTitles, assertSummaryValues } from "../../../helpers"; +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, click } from "../../../helpers"; import { getRandomString } from "../../../jwt_helper"; describe("Using supplementary data", () => { @@ -45,17 +45,17 @@ describe("Using supplementary data", () => { }); it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { - await $(LoadedSuccessfullyBlockPage.submit()).click(); + await click(LoadedSuccessfullyBlockPage.submit()); await $(IntroductionBlockPage.acceptCookies()).click(); await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( "If the company details or structure have changed contact us on 01171231231", ); await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); - await $(IntroductionBlockPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(IntroductionBlockPage.submit()); + await click(HubPage.submit()); await $(EmailBlockPage.yes()).click(); - await $(EmailBlockPage.submit()).click(); + await click(EmailBlockPage.submit()); }); it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { @@ -67,26 +67,26 @@ describe("Using supplementary data", () => { await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).to.equal("Tuesday 2 December 1947"); await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).to.equal("Wednesday 3 December 1947"); await $(TradingPage.answerByIndex(3)).click(); - await $(TradingPage.submit()).click(); + await click(TradingPage.submit()); }); it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); - await $(SalesBreakdownBlockPage.submit()).click(); + await click(SalesBreakdownBlockPage.submit()); await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 555,000"); }); it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); - await $(SalesBreakdownBlockPage.submit()).click(); + await click(SalesBreakdownBlockPage.submit()); await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?", ); }); it("Given I have an interstitial block with all answers and supplementary data, When I reach this block, Then I see the placeholders rendered correctly", async () => { - await $(CalculatedSummarySalesPage.submit()).click(); + await click(CalculatedSummarySalesPage.submit()); await expect(await $(Section1InterstitialPage.questionText()).getText()).to.contain("Summary of information provided for Tesco"); await expect(await $("body").getText()).to.have.string("Telephone Number: 01171231231"); await expect(await $("body").getText()).to.have.string("Email: contact@tesco.org"); @@ -104,7 +104,7 @@ describe("Using supplementary data", () => { }); it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { - await $(Section1InterstitialPage.submit()).click(); + await click(Section1InterstitialPage.submit()); await expect(await $(Section1Page.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); await expect(await $(Section1Page.sameEmailAnswer()).getText()).to.contain("Yes"); await expect(await $(Section1Page.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); @@ -117,51 +117,51 @@ describe("Using supplementary data", () => { it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { await $(Section1Page.sameEmailAnswerEdit()).click(); await $(EmailBlockPage.no()).click(); - await $(EmailBlockPage.submit()).click(); + await click(EmailBlockPage.submit()); await $(NewEmailPage.answer()).setValue("new.contact@gmail.com"); - await $(NewEmailPage.submit()).click(); + await click(NewEmailPage.submit()); await $(Section1Page.previous()).click(); await expect(await $("body").getText()).to.have.string("Email: new.contact@gmail.com"); - await $(Section1InterstitialPage.submit()).click(); - await $(Section1Page.submit()).click(); + await click(Section1InterstitialPage.submit()); + await click(Section1Page.submit()); }); it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); // TODO once list collector content block is merged in update this test accordingly await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).to.contain("Harry Potter"); await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).to.contain("Clark Kent"); await $(ListCollectorEmployeesPage.no()).click(); - await $(ListCollectorEmployeesPage.submit()).click(); + await click(ListCollectorEmployeesPage.submit()); }); it("Given I have a list collector block using a supplementary list, When I reach the section summary, I see the supplementary list items in the list", async () => { // TODO once list collector content block is merged in update this test accordingly await expect(await $(Section2Page.employeesListLabel(1)).getText()).to.contain("Harry Potter"); await expect(await $(Section2Page.employeesListLabel(2)).getText()).to.contain("Clark Kent"); - await $(Section2Page.submit()).click(); + await click(Section2Page.submit()); }); it("Given I add some additional employees via a list collector, When I return to the Hub, Then I see new enabled sections for the supplementary list items, and my added ones", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(AnyAdditionalEmployeesPage.yes()).click(); - await $(AnyAdditionalEmployeesPage.submit()).click(); + await click(AnyAdditionalEmployeesPage.submit()); await $(AddAdditionalEmployeePage.employeeFirstName()).setValue("Jane"); await $(AddAdditionalEmployeePage.employeeLastName()).setValue("Doe"); - await $(AddAdditionalEmployeePage.submit()).click(); + await click(AddAdditionalEmployeePage.submit()); await $(ListCollectorAdditionalPage.yes()).click(); - await $(ListCollectorAdditionalPage.submit()).click(); + await click(ListCollectorAdditionalPage.submit()); await $(AddAdditionalEmployeePage.employeeFirstName()).setValue("John"); await $(AddAdditionalEmployeePage.employeeLastName()).setValue("Smith"); - await $(AddAdditionalEmployeePage.submit()).click(); + await click(AddAdditionalEmployeePage.submit()); await $(ListCollectorAdditionalPage.no()).click(); - await $(ListCollectorAdditionalPage.submit()).click(); - await $(Section3Page.submit()).click(); + await click(ListCollectorAdditionalPage.submit()); + await click(Section3Page.submit()); await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Clark Kent"); await expect(await $(HubPage.summaryItems("section-5-1")).getText()).to.contain("Jane Doe"); await expect(await $(HubPage.summaryItems("section-5-2")).getText()).to.contain("John Smith"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); }); it("Given I have repeating sections for both supplementary and dynamic list items, When I start a repeating section for a supplementary list item, Then I see static supplementary data correctly piped in", async () => { @@ -173,60 +173,60 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.day()).setValue(1); await $(LengthOfEmploymentPage.month()).setValue(1); await $(LengthOfEmploymentPage.year()).setValue(1930); - await $(LengthOfEmploymentPage.submit()).click(); + await click(LengthOfEmploymentPage.submit()); await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); }); it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { await $(LengthOfEmploymentPage.year()).setValue(1990); - await $(LengthOfEmploymentPage.submit()).click(); + await click(LengthOfEmploymentPage.submit()); await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Harry Potter start working for Tesco?"); await expect(await $(Section4Page.employmentStart()).getText()).to.contain("1 January 1990"); }); it("Given I complete the repeating section for the other supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { - await $(Section4Page.submit()).click(); - await $(HubPage.submit()).click(); + await click(Section4Page.submit()); + await click(HubPage.submit()); await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Clark Kent start working for Tesco?"); await $(LengthOfEmploymentPage.day()).setValue(5); await $(LengthOfEmploymentPage.month()).setValue(6); await $(LengthOfEmploymentPage.year()).setValue(2011); - await $(LengthOfEmploymentPage.submit()).click(); + await click(LengthOfEmploymentPage.submit()); await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Clark Kent start working for Tesco?"); await expect(await $(Section4Page.employmentStart()).getText()).to.contain("5 June 2011"); }); it("Given I move onto the dynamic list items, When I start a repeating section for a dynamic list item, Then I see static supplementary data correctly piped in and the same validation and summary", async () => { - await $(Section4Page.submit()).click(); - await $(HubPage.submit()).click(); + await click(Section4Page.submit()); + await click(HubPage.submit()); await expect(await $(AdditionalLengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Jane Doe start working for Tesco?"); await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).to.contain("Start date at Tesco"); await $(AdditionalLengthOfEmploymentPage.day()).setValue(1); await $(AdditionalLengthOfEmploymentPage.month()).setValue(1); await $(AdditionalLengthOfEmploymentPage.year()).setValue(1930); - await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await click(AdditionalLengthOfEmploymentPage.submit()); await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2000); - await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await click(AdditionalLengthOfEmploymentPage.submit()); await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did Jane Doe start working for Tesco?"); await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("1 January 2000"); - await $(Section5Page.submit()).click(); - await $(HubPage.submit()).click(); + await click(Section5Page.submit()); + await click(HubPage.submit()); await $(AdditionalLengthOfEmploymentPage.day()).setValue(3); await $(AdditionalLengthOfEmploymentPage.month()).setValue(3); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2010); - await $(AdditionalLengthOfEmploymentPage.submit()).click(); + await click(AdditionalLengthOfEmploymentPage.submit()); await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did John Smith start working for Tesco?"); await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("3 March 2010"); - await $(Section5Page.submit()).click(); + await click(Section5Page.submit()); }); it("Given I have some repeating blocks with supplementary data, When I begin the section, Then I see the supplementary names rendered correctly", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).to.contain("Articles and equipment for sports or outdoor games"); await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).to.contain("Kitchen Equipment"); await $(ListCollectorProductsPage.no()).click(); - await $(ListCollectorProductsPage.submit()).click(); + await click(ListCollectorProductsPage.submit()); }); it("Given I have repeating blocks with supplementary data, When I start the first repeating block, Then I see the supplementary data for the first list item", async () => { @@ -251,10 +251,10 @@ describe("Using supplementary data", () => { }); it("Given I have repeating blocks with supplementary data, When I start the second repeating block, Then I see the supplementary data for the second list item", async () => { - await $(ProductRepeatingBlock1Page.submit()).click(); + await click(ProductRepeatingBlock1Page.submit()); // TODO once using list collector content, shouldn't need these two lines await $(ListCollectorProductsPage.no()).click(); - await $(ListCollectorProductsPage.submit()).click(); + await click(ListCollectorProductsPage.submit()); await expect(await $("body").getText()).to.have.string("Include"); await expect(await $("body").getText()).to.have.string("pots and pans"); await expect(await $("body").getText()).not.to.have.string("Exclude"); @@ -262,18 +262,18 @@ describe("Using supplementary data", () => { await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain("Total volume produced for Kitchen Equipment"); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(50); await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(300); - await $(ProductRepeatingBlock1Page.submit()).click(); + await click(ProductRepeatingBlock1Page.submit()); }); it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { await $(ListCollectorProductsPage.no()).click(); - await $(ListCollectorProductsPage.submit()).click(); + await click(ListCollectorProductsPage.submit()); await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); assertSummaryValues(["100 kg", "50 kg"]); - await $(CalculatedSummaryVolumeSalesPage.submit()).click(); + await click(CalculatedSummaryVolumeSalesPage.submit()); }); it("Given I have another calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { @@ -282,7 +282,7 @@ describe("Using supplementary data", () => { ); assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); assertSummaryValues(["200 kg", "300 kg"]); - await $(CalculatedSummaryVolumeTotalPage.submit()).click(); + await click(CalculatedSummaryVolumeTotalPage.submit()); }); it("Given I have dynamic answers using a supplementary list, When I reach the dynamic answer page, Then I see the correct supplementary data in the answer labels", async () => { @@ -292,7 +292,7 @@ describe("Using supplementary data", () => { await $$(DynamicProductsPage.inputs())[0].setValue(110); await $$(DynamicProductsPage.inputs())[1].setValue(220); await $$(DynamicProductsPage.inputs())[2].setValue(330); - await $(DynamicProductsPage.submit()).click(); + await click(DynamicProductsPage.submit()); }); it("Given I have a calculated summary of dynamic answers for a supplementary list, When I reach the calculated summary, Then I see the correct supplementary data in the title and labels", async () => { @@ -305,7 +305,7 @@ describe("Using supplementary data", () => { "Value of sales from other categories", ]); assertSummaryValues(["£110.00", "£220.00", "£330.00"]); - await $(CalculatedSummaryValueSalesPage.submit()).click(); + await click(CalculatedSummaryValueSalesPage.submit()); }); it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { @@ -322,7 +322,7 @@ describe("Using supplementary data", () => { "Value of sales from other categories", ]); assertSummaryValues(["100 kg", "200 kg", "110 kg", "50 kg", "300 kg", "220 kg", "£110.00", "£220.00", "£330.00"]); - await $(Section6Page.submit()).click(); + await click(Section6Page.submit()); }); it("Given I relaunch the survey for a newer version of the supplementary data, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not the old ones", async () => { @@ -343,18 +343,18 @@ describe("Using supplementary data", () => { }); it("Given I now have a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(LengthOfEmploymentPage.day()).setValue(10); await $(LengthOfEmploymentPage.month()).setValue(10); await $(LengthOfEmploymentPage.year()).setValue(1999); - await $(LengthOfEmploymentPage.submit()).click(); + await click(LengthOfEmploymentPage.submit()); await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Bruce Wayne start working for Lidl?"); await expect(await $(Section4Page.employmentStart()).getText()).to.contain("10 October 1999"); - await $(Section4Page.submit()).click(); + await click(Section4Page.submit()); }); it("Given I can view my response after submission, When I submit the survey, Then I see the values I've entered and correct rendering with supplementary data", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ThankYouPage.savePrintAnswersLink()).click(); assertSummaryTitles(["Company Details", "Employees", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index 3ce00a818c..47d23539ea 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -4,19 +4,19 @@ import SetAreaUnitsBlockPage from "../../generated_pages/unit_patterns/set-area- import SetVolumeUnitsBlockPage from "../../generated_pages/unit_patterns/set-volume-units-block.page.js"; import SetWeightUnitsBlockPage from "../../generated_pages/unit_patterns/set-weight-units-block.page.js"; import SubmitPage from "../../generated_pages/unit_patterns/submit.page.js"; - +import { click } from "../../helpers"; describe("Units", () => { it("Given we do not set a language code and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - await $(SetLengthUnitsBlockPage.submit()).click(); + await click(SetLengthUnitsBlockPage.submit()); await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("hours"); await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("years"); await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); - await $(SetDurationUnitsBlockPage.submit()).click(); - await $(SetAreaUnitsBlockPage.submit()).click(); - await $(SetVolumeUnitsBlockPage.submit()).click(); - await $(SetWeightUnitsBlockPage.submit()).click(); + await click(SetDurationUnitsBlockPage.submit()); + await click(SetAreaUnitsBlockPage.submit()); + await click(SetVolumeUnitsBlockPage.submit()); + await click(SetWeightUnitsBlockPage.submit()); await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 hours"); await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 years"); }); @@ -24,16 +24,16 @@ describe("Units", () => { it("Given we set a language code for welsh and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "cy" }); await $(SetLengthUnitsBlockPage.submit()).scrollIntoView(); - await $(SetLengthUnitsBlockPage.submit()).click(); + await click(SetLengthUnitsBlockPage.submit()); await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("awr"); await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("flynedd"); await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); await $(SetDurationUnitsBlockPage.submit()).scrollIntoView(); - await $(SetDurationUnitsBlockPage.submit()).click(); - await $(SetAreaUnitsBlockPage.submit()).click(); - await $(SetVolumeUnitsBlockPage.submit()).click(); - await $(SetWeightUnitsBlockPage.submit()).click(); + await click(SetDurationUnitsBlockPage.submit()); + await click(SetAreaUnitsBlockPage.submit()); + await click(SetVolumeUnitsBlockPage.submit()); + await click(SetWeightUnitsBlockPage.submit()); await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 awr"); await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 mlynedd"); }); @@ -48,10 +48,10 @@ describe("Units", () => { it("Given we open a questionnaire with unit labels, when the weight unit label is highlighted by the tooltip, then the correct unit label should be displayed.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - await $(SetLengthUnitsBlockPage.submit()).click(); - await $(SetDurationUnitsBlockPage.submit()).click(); - await $(SetAreaUnitsBlockPage.submit()).click(); - await $(SetVolumeUnitsBlockPage.submit()).click(); + await click(SetLengthUnitsBlockPage.submit()); + await click(SetDurationUnitsBlockPage.submit()); + await click(SetAreaUnitsBlockPage.submit()); + await click(SetVolumeUnitsBlockPage.submit()); await expect(await $("body").getText()).to.have.string("tonnes"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js index df9ce26270..d7348c8936 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js @@ -1,6 +1,6 @@ import DateRangePage from "../../../../generated_pages/date_validation_mm_yyyy_combined/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_mm_yyyy_combined/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Combined question level and single validation for MM-YYYY dates", () => { before(async () => { await browser.openQuestionnaire("test_date_validation_mm_yyyy_combined.json"); @@ -13,7 +13,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); @@ -24,7 +24,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); @@ -35,7 +35,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter the year in a valid format. For example, 2023."); await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; }); @@ -46,7 +46,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(6); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after November 2016"); await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before June 2017"); }); @@ -57,7 +57,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(5); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 months"); }); @@ -67,7 +67,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(1); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 months"); }); @@ -78,14 +78,14 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat // Min range await $(DateRangePage.dateRangeToMonth()).setValue(3); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to March 2017"); // Max range await $(SubmitPage.dateRangeFromEdit()).click(); await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to April 2017"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js index 42c97a6adf..b95caaea73 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js @@ -1,6 +1,6 @@ import DateRangePage from "../../../../generated_pages/date_validation_yyyy_combined/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_yyyy_combined/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Combined question level and single validation for MM-YYYY dates", () => { before(async () => { await browser.openQuestionnaire("test_date_validation_yyyy_combined.json"); @@ -11,7 +11,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat it("When I enter dates that are too early and too late, Then I should see two validation errors", async () => { await $(DateRangePage.dateRangeFromYear()).setValue(2015); await $(DateRangePage.dateRangeToYear()).setValue(2021); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 2015"); await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 2021"); }); @@ -19,14 +19,14 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat it("When I enter a range too large, Then I should see a range validation error", async () => { await $(DateRangePage.dateRangeFromYear()).setValue(2016); await $(DateRangePage.dateRangeToYear()).setValue(2020); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 years"); }); it("When I enter a range too small, Then I should see a range validation error", async () => { await $(DateRangePage.dateRangeFromYear()).setValue(2016); await $(DateRangePage.dateRangeToYear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 years"); }); @@ -34,13 +34,13 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeFromYear()).setValue(2016); // Min range await $(DateRangePage.dateRangeToYear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2018"); // Max range await $(SubmitPage.dateRangeFromEdit()).click(); await $(DateRangePage.dateRangeToYear()).setValue(2019); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2019"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js index 704d551316..abbfef8650 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js @@ -1,5 +1,6 @@ import DateRangePage from "../../../../generated_pages/date_validation_combined/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_combined/submit.page"; +import { click } from "../../../../helpers"; describe("Feature: Combined question level and single validation for dates", () => { before(async () => { @@ -16,7 +17,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeToday()).setValue(22); await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 22 February 2017"); }); @@ -29,7 +30,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeToday()).setValue(21); await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 50 days"); }); @@ -41,7 +42,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeToday()).setValue(10); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 10 days"); }); @@ -54,7 +55,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeToday()).setValue(11); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 11 January 2017"); // Max range @@ -62,7 +63,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeToday()).setValue(20); await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 20 February 2017"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-range.spec.js b/tests/functional/spec/features/validation/date_validation/date-range.spec.js index ed7b385728..516d191034 100644 --- a/tests/functional/spec/features/validation/date_validation/date-range.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-range.spec.js @@ -1,6 +1,6 @@ import DateRangePage from "../../../../generated_pages/date_validation_range/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_range/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Question level validation for date ranges", () => { beforeEach(async () => { await browser.openQuestionnaire("test_date_validation_range.json"); @@ -16,7 +16,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(3); await $(DateRangePage.dateRangeTomonth()).setValue(3); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 1 month, 20 days"); }); }); @@ -30,7 +30,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(3); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 23 days"); }); }); @@ -44,7 +44,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(3); await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -60,7 +60,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(3); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); }); }); @@ -74,7 +74,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(1); await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); }); }); @@ -88,7 +88,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeToday()).setValue(3); await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); - await $(DateRangePage.submit()).click(); + await click(DateRangePage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-single.spec.js b/tests/functional/spec/features/validation/date_validation/date-single.spec.js index dc95539046..d33b6776b1 100644 --- a/tests/functional/spec/features/validation/date_validation/date-single.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-single.spec.js @@ -1,7 +1,7 @@ import DatePage from "../../../../generated_pages/date_validation_single/date-block.page"; import DatePeriodPage from "../../../../generated_pages/date_validation_single/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_single/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Validation for single date periods", () => { beforeEach(async () => { await browser.openQuestionnaire("test_date_validation_single.json"); @@ -13,12 +13,12 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeFromday()).setValue(13); await $(DatePeriodPage.dateRangeFrommonth()).setValue(2); await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await $(DatePeriodPage.dateRangeToday()).setValue(3); await $(DatePeriodPage.dateRangeTomonth()).setValue(3); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); }); }); @@ -28,12 +28,12 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeFromday()).setValue(13); await $(DatePeriodPage.dateRangeFrommonth()).setValue(7); await $(DatePeriodPage.dateRangeFromyear()).setValue(2017); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await $(DatePeriodPage.dateRangeToday()).setValue(3); await $(DatePeriodPage.dateRangeTomonth()).setValue(3); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date before 2 July 2017"); }); }); @@ -43,12 +43,12 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeFromday()).setValue(13); await $(DatePeriodPage.dateRangeFrommonth()).setValue(11); await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await $(DatePeriodPage.dateRangeToday()).setValue(13); await $(DatePeriodPage.dateRangeTomonth()).setValue(1); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await expect(await $(DatePeriodPage.errorNumber(2)).getText()).to.contain("Enter a date after 10 February 2018"); }); }); @@ -58,12 +58,12 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeFromday()).setValue(13); await $(DatePeriodPage.dateRangeFrommonth()).setValue(12); await $(DatePeriodPage.dateRangeFromyear()).setValue(2016); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await $(DatePeriodPage.dateRangeToday()).setValue(11); await $(DatePeriodPage.dateRangeTomonth()).setValue(2); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); - await $(DatePeriodPage.submit()).click(); + await click(DatePeriodPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -72,6 +72,6 @@ describe("Feature: Validation for single date periods", () => { await $(DatePage.day()).setValue(1); await $(DatePage.month()).setValue(1); await $(DatePage.year()).setValue(2018); - await $(DatePage.submit()).click(); + await click(DatePage.submit()); } }); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index c1cb92edbe..644b377387 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -9,7 +9,7 @@ import ListCollectorRemovePage from "../../../../generated_pages/validation_sum_ import ListCollectorEditPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector-edit.page"; import HubPage from "../../../../base_pages/hub.page"; import TotalBlockOtherPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block-other.page"; - +import { click } from "../../../../helpers"; describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", function () { // These tests are flaky therefore we add a retry. The cause is unknown. // :TODO: Revert this in future when we have a fix for this. @@ -29,7 +29,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $$(DynamicAnswerPage.inputs())[0].setValue(33); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await expect(await $(DynamicAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 100"); }); }); @@ -41,7 +41,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(TotalBlockOtherPage.pageName); }); }); @@ -53,13 +53,13 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(TotalBlockOtherPage.totalOther()).setValue(100); - await $(TotalBlockOtherPage.submit()).click(); + await click(TotalBlockOtherPage.submit()); await expect(await browser.getUrl()).to.contain(DynamicAnswerOnlyPage.pageName); await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(0); - await $(DynamicAnswerOnlyPage.submit()).click(); + await click(DynamicAnswerOnlyPage.submit()); await expect(await $(DynamicAnswerOnlyPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £100.00"); }); }); @@ -78,9 +78,9 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await fillDynamicAnswers(); await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Morrisons"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); }); @@ -91,7 +91,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await fillDynamicAnswers(); await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); - await $(ListCollectorRemovePage.submit()).click(); + await click(ListCollectorRemovePage.submit()); await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); }); @@ -102,10 +102,10 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await fillDynamicAnswers(); await $(SectionSummaryPage.supermarketsListEditLink(1)).click(); await $(ListCollectorEditPage.supermarketName()).setValue("Aldi"); - await $(ListCollectorEditPage.submit()).click(); - await $(DynamicAnswerPage.submit()).click(); - await $(TotalBlockOtherPage.submit()).click(); - await $(DynamicAnswerOnlyPage.submit()).click(); + await click(ListCollectorEditPage.submit()); + await click(DynamicAnswerPage.submit()); + await click(TotalBlockOtherPage.submit()); + await click(DynamicAnswerOnlyPage.submit()); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: 2000 }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); @@ -128,28 +128,28 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ async function addTwoSupermarkets() { await $(TotalBlockPage.total()).setValue(100); - await $(TotalBlockPage.submit()).click(); + await click(TotalBlockPage.submit()); await $(HubPage.summaryRowLink("dynamic-answers-section")).click(); await $(DriverPage.yes()).click(); - await $(DriverPage.submit()).click(); + await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Asda"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); } async function fillDynamicAnswers() { await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); - await $(DynamicAnswerPage.submit()).click(); + await click(DynamicAnswerPage.submit()); await $(TotalBlockOtherPage.totalOther()).setValue(100); - await $(TotalBlockOtherPage.submit()).click(); + await click(TotalBlockOtherPage.submit()); await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(50); - await $(DynamicAnswerOnlyPage.submit()).click(); + await click(DynamicAnswerOnlyPage.submit()); } diff --git a/tests/functional/spec/features/validation/sum/equal.spec.js b/tests/functional/spec/features/validation/sum/equal.spec.js index 559871e0bf..1c6b6c1be4 100644 --- a/tests/functional/spec/features/validation/sum/equal.spec.js +++ b/tests/functional/spec/features/validation/sum/equal.spec.js @@ -1,13 +1,13 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; - +import { click } from "../../../../helpers"; const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); await $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); await $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); }; describe("Feature: Sum of grouped answers equal to validation against total ", () => { @@ -18,7 +18,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); @@ -29,17 +29,17 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( describe("Given I completed a grouped answer validation question and I am on the summary", () => { it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question with valid answers before I can get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); await $(SubmitPage.totalAnswerEdit()).click(); await $(TotalAnswerPage.total()).setValue("15"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await browser.url(SubmitPage.url()); await expect(await browser.getUrl()).to.contain(BreakdownAnswerPage.pageName); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); await answerAndSubmitBreakdownQuestion("6", "3", "3", "3"); @@ -51,7 +51,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 5 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("5", "", "", ""); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -61,7 +61,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); diff --git a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js index bfd4f73c26..a082d35993 100644 --- a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_multiple/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_multiple/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_multiple/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Sum validation (Multi Rule Equals)", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_multiple.json"); @@ -10,8 +10,8 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { describe("Given I start a grouped answer with multi rule validation survey and enter 10 into the total", () => { it("When I continue and enter nothing, all zeros or 10 at breakdown level, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("10"); - await $(TotalAnswerPage.submit()).click(); - await $(BreakdownAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.previous()).click(); @@ -19,7 +19,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown2()).setValue("0"); await $(BreakdownAnswerPage.breakdown3()).setValue("0"); await $(BreakdownAnswerPage.breakdown4()).setValue("0"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.previous()).click(); @@ -27,7 +27,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown2()).setValue("2"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("4"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -35,16 +35,16 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { describe("Given I start a grouped answer with multi rule validation survey and enter 10 into the total", () => { it("When I continue and enter less between 1 - 9 or greater than 10, Then it should error", async () => { await $(TotalAnswerPage.total()).setValue("10"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("1"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); await $(BreakdownAnswerPage.breakdown2()).setValue("2"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("5"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js index 18c6ac2ce2..2858f38dd4 100644 --- a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Sum of grouped answers validation (equal or less than) against total", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_equal_or_less_than.json"); @@ -10,12 +10,12 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("2"); await $(BreakdownAnswerPage.breakdown2()).setValue("2"); await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -23,12 +23,12 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("3"); await $(BreakdownAnswerPage.breakdown2()).setValue("3"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -36,12 +36,12 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("4"); await $(BreakdownAnswerPage.breakdown2()).setValue(""); await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -49,12 +49,12 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 4 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("4"); await $(BreakdownAnswerPage.breakdown2()).setValue("4"); await $(BreakdownAnswerPage.breakdown3()).setValue("4"); await $(BreakdownAnswerPage.breakdown4()).setValue("4"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 12"); }); }); @@ -62,12 +62,12 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("3"); await $(BreakdownAnswerPage.breakdown2()).setValue("3"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 5"); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js index 4ba5dc0826..db733ec3a1 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js @@ -8,7 +8,7 @@ import BreakdownSectionSummary from "../../../../generated_pages/validation_sum_ import HubPage from "../../../../base_pages/hub.page"; import ThankYouPage from "../../../../base_pages/thank-you.page"; - +import { click } from "../../../../helpers"; const companyOverviewSectionID = "company-overview-section"; const breakdownSectionId = "breakdown-section"; @@ -16,23 +16,23 @@ const answerAndSubmitTurnoverBreakdownQuestion = async (breakdown1, breakdown2, await $(TurnoverBreakdownPage.turnoverBreakdown1()).setValue(breakdown1); await $(TurnoverBreakdownPage.turnoverBreakdown2()).setValue(breakdown2); await $(TurnoverBreakdownPage.turnoverBreakdown3()).setValue(breakdown3); - await $(TurnoverBreakdownPage.submit()).click(); + await click(TurnoverBreakdownPage.submit()); }; const answerAndSubmitEmployeeBreakdownQuestion = async (breakdown1, breakdown2) => { await $(EmployeesBreakdownPage.employeesBreakdown1()).setValue(breakdown1); await $(EmployeesBreakdownPage.employeesBreakdown2()).setValue(breakdown2); - await $(EmployeesBreakdownPage.submit()).click(); + await click(EmployeesBreakdownPage.submit()); }; const answerAndSubmitTotalTurnoverQuestion = async (total) => { await $(TotalTurnoverPage.totalTurnover()).setValue(total); - await $(TotalTurnoverPage.submit()).click(); + await click(TotalTurnoverPage.submit()); }; const answerAndSubmitTotalEmployeesQuestion = async (total) => { await $(TotalEmployeesPage.totalEmployees()).setValue(total); - await $(TotalEmployeesPage.submit()).click(); + await click(TotalEmployeesPage.submit()); }; describe("Feature: Validation - Sum of grouped answers to equal total (Total in separate section)", () => { @@ -41,7 +41,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await browser.openQuestionnaire("test_validation_sum_against_total_hub_with_dependent_section.json"); await answerAndSubmitTotalTurnoverQuestion(1000); await answerAndSubmitTotalEmployeesQuestion(10); - await $(CompanySectionSummary.submit()).click(); + await click(CompanySectionSummary.submit()); await expect(await $(HubPage.summaryRowState(companyOverviewSectionID)).getText()).to.equal("Completed"); }); @@ -51,7 +51,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in }); it("When I start the breakdown section and enter an answer that is not equal to the total for the turnover question, Then I should see a validation error", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await answerAndSubmitTurnoverBreakdownQuestion(1000, 250, 250); await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); @@ -62,7 +62,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitEmployeeBreakdownQuestion(5, 5); await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); @@ -75,13 +75,13 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in // Complete overview section await answerAndSubmitTotalTurnoverQuestion(1000); await answerAndSubmitTotalEmployeesQuestion(10); - await $(CompanySectionSummary.submit()).click(); + await click(CompanySectionSummary.submit()); // Complete breakdown section - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); await answerAndSubmitEmployeeBreakdownQuestion(5, 5); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); @@ -91,7 +91,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await $(CompanySectionSummary.totalTurnoverAnswerEdit()).click(); await answerAndSubmitTotalTurnoverQuestion(1500); - await $(CompanySectionSummary.submit()).click(); + await click(CompanySectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Partially completed"); }); @@ -104,7 +104,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in }); it("When I submit the turnover breakdown question with no changes, Then I should see a validation error", async () => { - await $(TurnoverBreakdownPage.submit()).click(); + await click(TurnoverBreakdownPage.submit()); await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,500.00"); }); @@ -113,13 +113,13 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitTurnoverBreakdownQuestion(500, 500, 500); await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); }); it("When I submit the questionnaire, Then I should see the thank you page", async () => { await $(HubPage.submit()).scrollIntoView(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index 22934a4ced..3c1ee36200 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -13,7 +13,7 @@ import BreakdownSectionSummary from "../../../../generated_pages/validation_sum_ import HubPage from "../../../../base_pages/hub.page"; import ThankYouPage from "../../../../base_pages/thank-you.page"; - +import { click } from "../../../../helpers"; const householderSectionId = "householders-section"; const householdOverviewSectionId = "household-overview-section"; const repeatingSectionId = (repeatIndex) => { @@ -22,27 +22,27 @@ const repeatingSectionId = (repeatIndex) => { const addPersonToHousehold = async (firstName, lastName) => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue(firstName); await $(ListCollectorAddPage.lastName()).setValue(lastName); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }; const answerAndSubmitTotalSpendingQuestion = async (total) => { await $(TotalSpendingPage.totalSpending()).setValue(total); - await $(TotalSpendingPage.submit()).click(); + await click(TotalSpendingPage.submit()); }; const answerAndSubmitEntertainmentSpendingQuestion = async (total) => { await $(EntertainmentSpendingPage.entertainmentSpending()).setValue(total); - await $(EntertainmentSpendingPage.submit()).click(); + await click(EntertainmentSpendingPage.submit()); }; const answerAndSubmitSpendingBreakdownQuestion = async (breakdown1, breakdown2, breakdown3) => { await $(SpendingBreakdownPage.spendingBreakdown1()).setValue(breakdown1); await $(SpendingBreakdownPage.spendingBreakdown2()).setValue(breakdown2); await $(SpendingBreakdownPage.spendingBreakdown3()).setValue(breakdown3); - await $(SpendingBreakdownPage.submit()).click(); + await click(SpendingBreakdownPage.submit()); }; const assertSpendingBreakdownAnswer = async (breakdown1, breakdown2, breakdown3) => { @@ -55,7 +55,7 @@ const answerAndSubmitEntertainmentBreakdownQuestion = async (breakdown1, breakdo await $(EntertainmentBreakdownPage.secondSpendingBreakdown1()).setValue(breakdown1); await $(EntertainmentBreakdownPage.secondSpendingBreakdown2()).setValue(breakdown2); await $(EntertainmentBreakdownPage.secondSpendingBreakdown3()).setValue(breakdown3); - await $(EntertainmentBreakdownPage.submit()).click(); + await click(EntertainmentBreakdownPage.submit()); }; const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { @@ -66,7 +66,7 @@ const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, cu }); it("When I submit the spending breakdown question with no changes, Then I should see a validation error", async () => { - await $(SpendingBreakdownPage.submit()).click(); + await click(SpendingBreakdownPage.submit()); await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain(`Enter answers that add up to £${newTotal}`); }); @@ -75,7 +75,7 @@ const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, cu await answerAndSubmitSpendingBreakdownQuestion(newTotal, 0, 0); await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).to.equal("Completed"); }); }; @@ -90,13 +90,13 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await addPersonToHousehold("Jane", "Doe"); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); - await $(ListCollectorPage.submit()).click(); - await $(ListCollectorSummaryPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(ListCollectorSummaryPage.submit()); // Complete household overview section await answerAndSubmitTotalSpendingQuestion(1000); await answerAndSubmitEntertainmentSpendingQuestion(500); - await $(HouseholdOverviewSectionSummary.submit()).click(); + await click(HouseholdOverviewSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(householderSectionId)).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState(householdOverviewSectionId)).getText()).to.equal("Completed"); @@ -110,7 +110,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating it("When I start a repeating section and don't skip the calculated question, and enter an answer that is not equal to the total for the spending question, Then I should see a validation error", async () => { await $(HubPage.summaryRowLink(repeatingSectionId(1))).click(); await $(BreakdownDrivingPage.yes()).click(); - await $(BreakdownDrivingPage.submit()).click(); + await click(BreakdownDrivingPage.submit()); await answerAndSubmitSpendingBreakdownQuestion(500, 500, 500); @@ -122,7 +122,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); }); @@ -130,10 +130,10 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating it("When I start another repeating section and answer 'No' to the driving question, Then I should not have to answer the breakdown question and the section is marked as 'Completed'", async () => { await $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); await $(BreakdownDrivingPage.no()).click(); - await $(BreakdownDrivingPage.submit()).click(); + await click(BreakdownDrivingPage.submit()); await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); @@ -143,7 +143,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); await answerAndSubmitTotalSpendingQuestion(1500); - await $(HouseholdOverviewSectionSummary.submit()).click(); + await click(HouseholdOverviewSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); // The 2nd repeating section skipped the breakdown question, therefore progress should updated for sections that have @@ -157,11 +157,11 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(HubPage.summaryRowLink(repeatingSectionId(2))).click(); await $(BreakdownSectionSummary.breakdownDrivingAnswerEdit()).click(); await $(BreakdownDrivingPage.yes()).click(); - await $(BreakdownDrivingPage.submit()).click(); + await click(BreakdownDrivingPage.submit()); await answerAndSubmitSpendingBreakdownQuestion(1000, 500, 0); await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); - await $(BreakdownSectionSummary.submit()).click(); + await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); @@ -170,7 +170,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); await answerAndSubmitTotalSpendingQuestion(2500); - await $(HouseholdOverviewSectionSummary.submit()).click(); + await click(HouseholdOverviewSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); // The 2nd repeating section is now on the path, therefore, its status should have been updated. @@ -185,15 +185,15 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); await expect(await $(TotalSpendingPage.totalSpending()).getValue()).to.equal("2500.00"); - await $(TotalSpendingPage.submit()).click(); - await $(HouseholdOverviewSectionSummary.submit()).click(); + await click(TotalSpendingPage.submit()); + await click(HouseholdOverviewSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); }); it("When I submit the questionnaire, Then I should see the thank you page", async () => { - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); diff --git a/tests/functional/spec/features/validation/sum/less_than.spec.js b/tests/functional/spec/features/validation/sum/less_than.spec.js index 301cca6e9f..a55ffb08ad 100644 --- a/tests/functional/spec/features/validation/sum/less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/less_than.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_less_than/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_less_than/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_less_than/submit.page"; - +import { click } from "../../../../helpers"; describe("Feature: Sum of grouped answers validation (less than) against total", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_less_than.json"); @@ -10,12 +10,12 @@ describe("Feature: Sum of grouped answers validation (less than) against total", describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 2 in each breakdown field, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("2"); await $(BreakdownAnswerPage.breakdown2()).setValue("2"); await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -23,12 +23,12 @@ describe("Feature: Sum of grouped answers validation (less than) against total", describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 4 into breakdown 1 and leave the others empty, Then I should be able to get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("4"); await $(BreakdownAnswerPage.breakdown2()).setValue(""); await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); }); @@ -36,12 +36,12 @@ describe("Feature: Sum of grouped answers validation (less than) against total", describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("3"); await $(BreakdownAnswerPage.breakdown2()).setValue("3"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £12.00"); }); }); @@ -49,12 +49,12 @@ describe("Feature: Sum of grouped answers validation (less than) against total", describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await $(BreakdownAnswerPage.breakdown1()).setValue("3"); await $(BreakdownAnswerPage.breakdown2()).setValue("3"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £5.00"); }); }); diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js index 5fc2cedea0..95ce212095 100644 --- a/tests/functional/spec/features/validation/sum/value_source.spec.js +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -3,13 +3,13 @@ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_agai import TotalPlaybackPage from "../../../../generated_pages/validation_sum_against_value_source/number-total-playback.page"; import SecondBreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/second-breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; - +import { click } from "../../../../helpers"; const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); await $(BreakdownAnswerPage.breakdown3()).setValue(breakdown3); await $(BreakdownAnswerPage.breakdown4()).setValue(breakdown4); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); }; const answerAndSubmitSecondBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { @@ -17,13 +17,13 @@ const answerAndSubmitSecondBreakdownQuestion = async (breakdown1, breakdown2, br await $(SecondBreakdownAnswerPage.secondBreakdown2()).setValue(breakdown2); await $(SecondBreakdownAnswerPage.secondBreakdown3()).setValue(breakdown3); await $(SecondBreakdownAnswerPage.secondBreakdown4()).setValue(breakdown4); - await $(SecondBreakdownAnswerPage.submit()).click(); + await click(SecondBreakdownAnswerPage.submit()); }; const answerBothBreakdownQuestions = async (array1, array2) => { await answerAndSubmitBreakdownQuestion(array1[0], array1[1], array1[2], array1[3]); - await $(TotalPlaybackPage.submit()).click(); + await click(TotalPlaybackPage.submit()); await answerAndSubmitSecondBreakdownQuestion(array2[0], array2[1], array2[2], array2[3]); }; @@ -36,7 +36,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I start a grouped answer validation survey and enter 12 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should be able to get to the total playback page", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); @@ -47,7 +47,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I have a calculated summary value of 12", () => { it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the summary page", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); @@ -58,15 +58,15 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I completed both grouped answer validation questions and I am on the summary", () => { it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); await $(SubmitPage.totalAnswerEdit()).click(); await $(TotalAnswerPage.total()).setValue("15"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); - await $(BreakdownAnswerPage.submit()).click(); + await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; @@ -81,13 +81,13 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I completed both grouped answer validation questions and I am on the summary", () => { it("When I go back from the summary and change the total, Then I must reconfirm the breakdown question based on answer value source with valid answers before I can continue", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); await $(SubmitPage.totalAnswerEdit()).click(); await $(TotalAnswerPage.total()).setValue("15"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("0", "3", "3", "3"); @@ -104,7 +104,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I completed both grouped answer validation questions and I am on the summary", () => { it("When I go back from the summary and change the first breakdown question answers so its total changes, Then I must reconfirm the second breakdown question based on calculated summary value source with valid answers before I can continue", async () => { await $(TotalAnswerPage.total()).setValue("12"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); @@ -112,9 +112,9 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerAndSubmitBreakdownQuestion("6", "3", "2", "1"); - await $(TotalPlaybackPage.submit()).click(); + await click(TotalPlaybackPage.submit()); - await $(SecondBreakdownAnswerPage.submit()).click(); + await click(SecondBreakdownAnswerPage.submit()); await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; @@ -131,7 +131,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I continue and enter 3 in each breakdown field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); @@ -142,7 +142,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour describe("Given I start a grouped answer validation survey and enter 5 into the total", () => { it("When I enter 3 in each breakdown field and continue to second breakdown and enter 3 in each field, Then I should see a validation error", async () => { await $(TotalAnswerPage.total()).setValue("5"); - await $(TotalAnswerPage.submit()).click(); + await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["2", "1", "1", "1"], ["3", "3", "3", "3"]); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 62b0128499..8a2973f992 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -20,15 +20,16 @@ import DependencyQuestionSectionTwo from "../../../generated_pages/view_submitte import SkippableBlockSectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/skippable-block.page"; import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; import ListCollectorAddPage from "../../../generated_pages/view_submitted_response_repeating_sections//list-collector-add.page"; +import { click } from "../../../helpers"; describe("View Submitted Response", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_view_submitted_response.json"); await $(NameBlockPage.answer()).setValue("John Smith"); - await $(NameBlockPage.submit()).click(); + await click(NameBlockPage.submit()); await $(AddressBlockPage.answer()).setValue("NP10 8XG"); - await $(AddressBlockPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(AddressBlockPage.submit()); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); @@ -70,58 +71,58 @@ const skippableRepeatingSectionAnswer = '[data-qa="skippable-answer"]'; describe("View Submitted Response Summary Page With Repeating Sections", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_view_submitted_response_repeating_sections.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(NameBlockPage.answer()).setValue("John Smith"); - await $(NameBlockPage.submit()).click(); + await click(NameBlockPage.submit()); await $(AddressBlockPage.answer()).setValue("NP10 8XG"); - await $(AddressBlockPage.submit()).click(); + await click(AddressBlockPage.submit()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); await $(SkipFirstNumberBlockPageSectionOne.no()).click(); - await $(SkipFirstNumberBlockPageSectionOne.submit()).click(); + await click(SkipFirstNumberBlockPageSectionOne.submit()); await $(FirstNumberBlockPageSectionOne.firstNumber()).setValue(10); - await $(FirstNumberBlockPageSectionOne.submit()).click(); + await click(FirstNumberBlockPageSectionOne.submit()); await $(FirstAndAHalfNumberBlockPageSectionOne.firstAndAHalfNumberAlsoInTotal()).setValue(20); - await $(FirstAndAHalfNumberBlockPageSectionOne.submit()).click(); + await click(FirstAndAHalfNumberBlockPageSectionOne.submit()); await $(SecondNumberBlockPageSectionOne.secondNumberAlsoInTotal()).setValue(30); - await $(SecondNumberBlockPageSectionOne.submit()).click(); - await $(CalculatedSummarySectionOne.submit()).click(); - await $(SectionSummarySectionOne.submit()).click(); - await $(HubPage.submit()).click(); + await click(SecondNumberBlockPageSectionOne.submit()); + await click(CalculatedSummarySectionOne.submit()); + await click(SectionSummarySectionOne.submit()); + await click(HubPage.submit()); await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(20); await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(20); - await $(ThirdNumberBlockPageSectionTwo.submit()).click(); - await $(CalculatedSummarySectionTwo.submit()).click(); + await click(ThirdNumberBlockPageSectionTwo.submit()); + await click(CalculatedSummarySectionTwo.submit()); await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2()).click(); - await $(DependencyQuestionSectionTwo.submit()).click(); + await click(DependencyQuestionSectionTwo.submit()); await $(SkippableBlockSectionTwo.skippable()).setValue(100); - await $(SkippableBlockSectionTwo.submit()).click(); - await $(SectionSummarySectionTwo.submit()).click(); - await $(HubPage.submit()).click(); + await click(SkippableBlockSectionTwo.submit()); + await click(SectionSummarySectionTwo.submit()); + await click(HubPage.submit()); await $(ThirdNumberBlockPageSectionTwo.thirdNumber()).setValue(40); await $(ThirdNumberBlockPageSectionTwo.thirdNumberAlsoInTotal()).setValue(40); - await $(ThirdNumberBlockPageSectionTwo.submit()).click(); - await $(CalculatedSummarySectionTwo.submit()).click(); + await click(ThirdNumberBlockPageSectionTwo.submit()); + await click(CalculatedSummarySectionTwo.submit()); await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2()).click(); - await $(DependencyQuestionSectionTwo.submit()).click(); - await $(SectionSummarySectionTwo.submit()).click(); + await click(DependencyQuestionSectionTwo.submit()); + await click(SectionSummarySectionTwo.submit()); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); diff --git a/tests/functional/spec/feedback.spec.js b/tests/functional/spec/feedback.spec.js index 5919a683f3..1ddf12ed95 100644 --- a/tests/functional/spec/feedback.spec.js +++ b/tests/functional/spec/feedback.spec.js @@ -4,13 +4,14 @@ import SubmitPage from "../generated_pages/feedback/submit.page"; import FeedbackPage from "../base_pages/feedback.page"; import FeedbackSentPage from "../base_pages/feedback-sent.page"; import ThankYouPage from "../base_pages/thank-you.page"; +import { click } from "../helpers"; describe("Feedback", () => { describe("Given I launch and complete the test feedback survey", () => { before(async () => { await browser.openQuestionnaire("test_feedback.json"); - await $(SchemaFeedbackPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SchemaFeedbackPage.submit()); + await click(SubmitPage.submit()); }); it("When I view the thank you page, Then I can see the feedback call to action", async () => { @@ -24,7 +25,7 @@ describe("Feedback", () => { await browser.url(FeedbackPage.url()); await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); await expect(await $(FeedbackPage.feedbackTitle()).getText()).to.contain("Give feedback about this service"); - await $(FeedbackPage.submit()).click(); + await click(FeedbackPage.submit()); await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); await expect(await $(FeedbackPage.errorPanel()).isExisting()).to.be.true; await expect(await $(FeedbackPage.errorPanel()).getText()).to.contain( @@ -36,7 +37,7 @@ describe("Feedback", () => { await browser.url(FeedbackPage.url()); await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); await $(FeedbackPage.feedbackText()).setValue("Well done!"); - await $(FeedbackPage.submit()).click(); + await click(FeedbackPage.submit()); await expect(await browser.getUrl()).to.contain(FeedbackSentPage.pageName); await expect(await $(FeedbackSentPage.feedbackThankYouText()).getText()).to.contain("Thank you for your feedback"); }); @@ -45,7 +46,7 @@ describe("Feedback", () => { await browser.url(FeedbackPage.url()); await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); await $(FeedbackPage.feedbackText()).setValue("Well done!"); - await $(FeedbackPage.submit()).click(); + await click(FeedbackPage.submit()); await $(FeedbackSentPage.doneButton()).click(); await expect(await browser.getUrl()).to.contain("thank-you"); await expect(await $(ThankYouPage.title()).getText()).to.contain("Thank you for completing the Feedback test schema"); diff --git a/tests/functional/spec/interviewer_note.spec.js b/tests/functional/spec/interviewer_note.spec.js index 7d90687cd8..4bb8f4201b 100644 --- a/tests/functional/spec/interviewer_note.spec.js +++ b/tests/functional/spec/interviewer_note.spec.js @@ -2,6 +2,7 @@ import ConfirmPage from "../generated_pages/interviewer_note/confirm-block.page. import FavouriteTeamPage from "../generated_pages/interviewer_note/favourite-team-block.page.js"; import FinalInterstitialPage from "../generated_pages/interviewer_note/final-interstitial-block.page.js"; import InitialInterstitialPage from "../generated_pages/interviewer_note/initial-interstitial-block.page.js"; +import { click } from "../helpers"; describe("Given I start a survey", () => { before(async () => { @@ -12,17 +13,17 @@ describe("Given I start a survey", () => { await expect(await $(InitialInterstitialPage.questionText()).getText()).to.contain("Interviewer note"); }); it("When I view question page and the interviewer_note is set to true then I should be able to see interviewer note", async () => { - await $(InitialInterstitialPage.submit()).click(); + await click(InitialInterstitialPage.submit()); await expect(await $(FavouriteTeamPage.questionText()).getText()).to.contain("Interviewer note"); }); it("When I view question page and the interviewer_note is set to false then I should not be able to see interviewer note", async () => { await $(FavouriteTeamPage.favouriteTeam()).setValue("TNS"); - await $(FavouriteTeamPage.submit()).click(); + await click(FavouriteTeamPage.submit()); await expect(await $(ConfirmPage.questionText()).getText()).to.not.contain("Interviewer note"); }); it("When I view interstitial page and the interviewer_note is not set then I should not be able to see interviewer note", async () => { await $(ConfirmPage.yes()).click(); - await $(ConfirmPage.submit()).click(); + await click(ConfirmPage.submit()); await expect(await $(FinalInterstitialPage.questionText()).getText()).to.not.contain("Interviewer note"); }); }); diff --git a/tests/functional/spec/language_code.spec.js b/tests/functional/spec/language_code.spec.js index a2772e16fc..79d946759f 100644 --- a/tests/functional/spec/language_code.spec.js +++ b/tests/functional/spec/language_code.spec.js @@ -3,6 +3,7 @@ import DobPage from "../generated_pages/language/dob-block.page"; import NumberOfPeoplePage from "../generated_pages/language/number-of-people-block.page"; import ConfirmNumberOfPeoplePage from "../generated_pages/language/confirm-number-of-people.page"; import HubPage from "../base_pages/hub.page.js"; +import { click } from "../helpers"; const PLURAL_TEST_DATA_SETS = [ { @@ -100,28 +101,28 @@ describe("Language Code", () => { await browser.openQuestionnaire("test_language.json", { language: "cy", }); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); await $(DobPage.day()).setValue(25); await $(DobPage.month()).setValue(9); await $(DobPage.year()).setValue(1969); - await $(DobPage.submit()).click(); + await click(DobPage.submit()); await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - await $(NumberOfPeoplePage.submit()).click(); + await click(NumberOfPeoplePage.submit()); await $(ConfirmNumberOfPeoplePage.yes()).click(); - await $(ConfirmNumberOfPeoplePage.submit()).click(); + await click(ConfirmNumberOfPeoplePage.submit()); await expect(await $(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); @@ -131,27 +132,27 @@ describe("Language Code", () => { language: "en", }); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); await $(DobPage.day()).setValue(25); await $(DobPage.month()).setValue(9); await $(DobPage.year()).setValue(1969); - await $(DobPage.submit()).click(); + await click(DobPage.submit()); await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - await $(NumberOfPeoplePage.submit()).click(); + await click(NumberOfPeoplePage.submit()); await $(ConfirmNumberOfPeoplePage.yes()).click(); - await $(ConfirmNumberOfPeoplePage.submit()).click(); + await click(ConfirmNumberOfPeoplePage.submit()); await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); @@ -161,7 +162,7 @@ describe("Language Code", () => { language: "en", }); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); await expect(await $("header").getText()).to.contain("Test Language Survey"); await $(NamePage.switchLanguage("cy")).click(); @@ -171,17 +172,17 @@ describe("Language Code", () => { await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); await $(DobPage.day()).setValue(25); await $(DobPage.month()).setValue(9); await $(DobPage.year()).setValue(1969); - await $(DobPage.submit()).click(); + await click(DobPage.submit()); await $(NumberOfPeoplePage.numberOfPeople()).setValue(0); - await $(NumberOfPeoplePage.submit()).click(); + await click(NumberOfPeoplePage.submit()); await $(ConfirmNumberOfPeoplePage.yes()).click(); - await $(ConfirmNumberOfPeoplePage.submit()).click(); + await click(ConfirmNumberOfPeoplePage.submit()); await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); @@ -192,7 +193,7 @@ describe("Language Code", () => { await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); @@ -202,7 +203,7 @@ describe("Language Code", () => { language: "cy", }); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); await $(NamePage.switchLanguage("en")).click(); await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); @@ -217,19 +218,19 @@ describe("Language Code", () => { language: "en", }); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); await $(DobPage.day()).setValue(25); await $(DobPage.month()).setValue(9); await $(DobPage.year()).setValue(1969); - await $(DobPage.submit()).click(); + await click(DobPage.submit()); await $(NumberOfPeoplePage.numberOfPeople()).setValue(numberOfPeople); - await $(NumberOfPeoplePage.submit()).click(); + await click(NumberOfPeoplePage.submit()); await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.en); await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.en); @@ -240,7 +241,7 @@ describe("Language Code", () => { await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.cy); await $(ConfirmNumberOfPeoplePage.yes()).click(); - await $(ConfirmNumberOfPeoplePage.submit()).click(); + await click(ConfirmNumberOfPeoplePage.submit()); }); } }); diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index 380abddd67..a446b6d816 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import AnotherListCollectorPage from "../generated_pages/list_collector/another-list-collector-block.page.js"; import AnotherListCollectorAddPage from "../generated_pages/list_collector/another-list-collector-block-add.page.js"; import AnotherListCollectorEditPage from "../generated_pages/list_collector/another-list-collector-block-edit.page.js"; @@ -29,25 +29,26 @@ describe("List Collector", () => { it("The user is able to add members of the household", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + // eslint-disable-next-line no-undef + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Olivia"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Suzy"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }); it("The collector shows all of the household members in the summary", async () => { @@ -59,14 +60,14 @@ describe("List Collector", () => { await $(ListCollectorPage.listEditLink(1)).click(); await $(ListCollectorEditPage.firstName()).setValue("Mark"); await $(ListCollectorEditPage.lastName()).setValue("Twain"); - await $(ListCollectorEditPage.submit()).click(); + await click(ListCollectorEditPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain"); }); it("The questionnaire allows me to remove the first person (Mark Twain) from the summary", async () => { await $(ListCollectorPage.listRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); - await $(ListCollectorRemovePage.submit()).click(); + await click(ListCollectorRemovePage.submit()); }); it("The collector summary does not show Mark Twain anymore.", async () => { @@ -76,21 +77,21 @@ describe("List Collector", () => { it("The questionnaire allows more people to be added", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(ListCollectorAddPage.questionText()).getText()).to.contain("What is the name of the person"); await $(ListCollectorAddPage.firstName()).setValue("Clara"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Jean"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }); it("The user is returned to the list collector when the cancel link is clicked on the add page.", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Someone"); await $(ListCollectorAddPage.lastName()).setValue("Else"); await $(ListCollectorAddPage.cancelAndReturn()).click(); @@ -99,10 +100,10 @@ describe("List Collector", () => { it("The user is returned to the list collector when the cancel link is clicked on the edit page.", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Someone"); await $(ListCollectorAddPage.lastName()).setValue("Else"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.listEditLink(1)).click(); await $(ListCollectorEditPage.cancelAndReturn()).click(); await expect(await browser.getUrl()).to.contain(ListCollectorPage.pageName); @@ -115,9 +116,9 @@ describe("List Collector", () => { it("When No is answered on the list collector the user sees an interstitial", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(NextInterstitialPage.pageName); - await $(NextInterstitialPage.submit()).click(); + await click(NextInterstitialPage.submit()); }); it("After the interstitial, the user should be on the second list collector page", async () => { @@ -131,17 +132,17 @@ describe("List Collector", () => { it("The collector allows the user to add another person to the same list", async () => { await $(AnotherListCollectorPage.yes()).click(); - await $(AnotherListCollectorPage.submit()).click(); + await click(AnotherListCollectorPage.submit()); await $(AnotherListCollectorAddPage.firstName()).setValue("Someone"); await $(AnotherListCollectorAddPage.lastName()).setValue("Else"); - await $(AnotherListCollectorAddPage.submit()).click(); + await click(AnotherListCollectorAddPage.submit()); await expect(await $(AnotherListCollectorPage.listLabel(6)).getText()).to.equal("Someone Else"); }); it("The collector allows the user to remove a person again", async () => { await $(AnotherListCollectorPage.listRemoveLink(5)).click(); await $(AnotherListCollectorRemovePage.yes()).click(); - await $(AnotherListCollectorRemovePage.submit()).click(); + await click(AnotherListCollectorRemovePage.submit()); }); it("The user is returned to the list collector when the previous link is clicked.", async () => { @@ -152,20 +153,20 @@ describe("List Collector", () => { await $(AnotherListCollectorEditPage.previous()).click(); await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); await $(AnotherListCollectorPage.yes()).click(); - await $(AnotherListCollectorPage.submit()).click(); + await click(AnotherListCollectorPage.submit()); await $(AnotherListCollectorEditPage.previous()).click(); await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); }); it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(AnotherListCollectorPage.no()).click(); - await $(AnotherListCollectorPage.submit()).click(); + await click(AnotherListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); }); it("The questionnaire allows submission", async () => { - await $(SummaryPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(SummaryPage.submit()); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); }); @@ -173,26 +174,26 @@ describe("List Collector", () => { describe("Given I start a list collector survey and complete to Section Summary", () => { beforeEach(async () => { await browser.openQuestionnaire("test_list_collector_list_summary.json"); - await $(IntroductionPage.submit()).click(); + await click(IntroductionPage.submit()); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(SectionSummaryListCollectorPage.yes()).click(); - await $(SectionSummaryListCollectorPage.submit()).click(); + await click(SectionSummaryListCollectorPage.submit()); await $(SectionSummaryListCollectorAddPage.firstName()).setValue("Samuel"); await $(SectionSummaryListCollectorAddPage.lastName()).setValue("Clemens"); - await $(SectionSummaryListCollectorAddPage.submit()).click(); + await click(SectionSummaryListCollectorAddPage.submit()); await $(SectionSummaryListCollectorPage.no()).click(); - await $(SectionSummaryListCollectorPage.submit()).click(); + await click(SectionSummaryListCollectorPage.submit()); await $(VisitorListCollectorPage.yes()).click(); - await $(VisitorListCollectorPage.submit()).click(); + await click(VisitorListCollectorPage.submit()); await $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Olivia"); await $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Clemens"); - await $(VisitorListCollectorAddPage.submit()).click(); + await click(VisitorListCollectorAddPage.submit()); await $(VisitorListCollectorPage.no()).click(); - await $(VisitorListCollectorPage.submit()).click(); + await click(VisitorListCollectorPage.submit()); }); it("The section summary should display contents of the list collector", async () => { @@ -205,16 +206,16 @@ describe("List Collector", () => { await $(PeopleListSectionSummaryPage.visitorsListAddLink(1)).click(); await $(VisitorListCollectorAddPage.firstNameVisitor()).setValue("Joe"); await $(VisitorListCollectorAddPage.lastNameVisitor()).setValue("Bloggs"); - await $(VisitorListCollectorAddPage.submit()).click(); + await click(VisitorListCollectorAddPage.submit()); await $(VisitorListCollectorPage.no()).click(); - await $(VisitorListCollectorPage.submit()).click(); + await click(VisitorListCollectorPage.submit()); await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).to.contain("Joe Bloggs"); }); it("When the user removes an item from the list, They should return to the section summary and it should display the updated list", async () => { await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); await $(SectionSummaryListCollectorRemovePage.yes()).click(); - await $(SectionSummaryListCollectorRemovePage.submit()).click(); + await click(SectionSummaryListCollectorRemovePage.submit()); await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).isExisting()).to.equal(false); }); @@ -222,7 +223,7 @@ describe("List Collector", () => { await $(PeopleListSectionSummaryPage.peopleListEditLink(1)).click(); await $(SectionSummaryListCollectorEditPage.firstName()).setValue("Mark"); await $(SectionSummaryListCollectorEditPage.lastName()).setValue("Twain"); - await $(SectionSummaryListCollectorEditPage.submit()).click(); + await click(SectionSummaryListCollectorEditPage.submit()); await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Mark Twain (You)"); }); diff --git a/tests/functional/spec/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector_driving_question.spec.js index b5286fcb9e..2ed53402d6 100644 --- a/tests/functional/spec/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector_driving_question.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import HubPage from "../base_pages/hub.page.js"; import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_driving_question/anyone-usually-live-at.page.js"; import AnyoneElseLiveAtListCollectorPage from "../generated_pages/list_collector_driving_question/anyone-else-live-at.page.js"; @@ -9,23 +9,23 @@ import SectionSummaryPage from "../generated_pages/list_collector_driving_questi describe("List Collector Driving Question", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_driving_question.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); }); describe("Given a happy journey through the list collector", () => { it("The collector shows all of the household members in the summary", async () => { await $(AnyoneUsuallyLiveAtPage.yes()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorAddPage.submit()); await $(AnyoneElseLiveAtListCollectorPage.yes()).click(); - await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorPage.submit()); await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Suzy"); await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Clemens"); - await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorAddPage.submit()); await $(AnyoneElseLiveAtListCollectorPage.no()).click(); - await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorPage.submit()); const peopleExpected = ["Marcus Twin", "Suzy Clemens"]; @@ -36,7 +36,7 @@ describe("List Collector Driving Question", () => { describe("Given the user answers no to the driving question", () => { it("The summary add link returns to the driving question", async () => { await $(AnyoneUsuallyLiveAtPage.no()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAtPage.url()); }); @@ -45,15 +45,15 @@ describe("List Collector Driving Question", () => { describe("Given the user answers yes to the driving question, adds someone and later removes them", () => { it("The summary add link should return to the original list collector", async () => { await $(AnyoneUsuallyLiveAtPage.yes()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await $(AnyoneElseLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneElseLiveAtListCollectorAddPage.lastName()).setValue("Twin"); - await $(AnyoneElseLiveAtListCollectorAddPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorAddPage.submit()); await $(AnyoneElseLiveAtListCollectorPage.no()).click(); - await $(AnyoneElseLiveAtListCollectorPage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorPage.submit()); await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); await $(AnyoneElseLiveAtListCollectorRemovePage.yes()).click(); - await $(AnyoneElseLiveAtListCollectorRemovePage.submit()).click(); + await click(AnyoneElseLiveAtListCollectorRemovePage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); await expect(await browser.getUrl()).to.contain(AnyoneElseLiveAtListCollectorAddPage.pageName); }); diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js index 924d06abe8..3747422f2f 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import HubPage from "../base_pages/hub.page.js"; import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector.page.js"; import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector-add.page.js"; @@ -11,7 +11,7 @@ import SummaryPage from "../generated_pages/list_collector_driving_checkbox/sect const beforeSetup = async () => { await browser.openQuestionnaire("test_list_collector_driving_checkbox.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); }; describe("List Collector Driving Checkbox Question", () => { @@ -20,19 +20,19 @@ describe("List Collector Driving Checkbox Question", () => { describe("Given a happy journey through the list collectors", () => { it("All of the household members and visitors are shown in the summary", async () => { await $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(AnyoneUsuallyLiveAtPage.familyMembersAndPartners()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Suzy"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.noIDoNotNeedToAddAPerson()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - await $(ListCollectorTemporaryAwayPage.submit()).click(); + await click(ListCollectorTemporaryAwayPage.submit()); const householdMembersExpected = ["Marcus Twin (You)", "Suzy Clemens"]; await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); @@ -46,7 +46,7 @@ describe("List Collector Driving Checkbox Question", () => { await $(ListCollectorPage.previous()).click(); await $(AnyoneUsuallyLiveAtPage.previous()).click(); await $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); const householdMembersExpected = ["Suzy Clemens"]; await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); @@ -57,12 +57,12 @@ describe("List Collector Driving Checkbox Question", () => { it("Then they are taken to the correct list add screen", async () => { await $(SummaryPage.previous()).click(); await $(ListCollectorTemporaryAwayPage.yesINeedToAddSomeone()).click(); - await $(ListCollectorTemporaryAwayPage.submit()).click(); + await click(ListCollectorTemporaryAwayPage.submit()); await $(ListCollectorTemporaryAwayAddPage.firstName()).setValue("Christopher"); await $(ListCollectorTemporaryAwayAddPage.lastName()).setValue("Pike"); - await $(ListCollectorTemporaryAwayAddPage.submit()).click(); + await click(ListCollectorTemporaryAwayAddPage.submit()); await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - await $(ListCollectorTemporaryAwayPage.submit()).click(); + await click(ListCollectorTemporaryAwayPage.submit()); const householdMembersExpected = ["Suzy Clemens", "Christopher Pike"]; await checkItemsInList(householdMembersExpected, SummaryPage.peopleListLabel); @@ -75,12 +75,12 @@ describe("Given the user says no one else lives in the house", () => { it("The user is asked if they need to add anyone that is temporarily away", async () => { await $(PrimaryPersonListCollectorPage.yesIUsuallyLiveHere()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( "You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?", @@ -92,16 +92,16 @@ describe("Given a person does not live in the house", () => { before("Load the survey", beforeSetup); it("The user is asked whether they live there", async () => { await $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await expect(await $(AnyoneUsuallyLiveAtPage.questionText()).getText()).to.equal("Do any of the following usually live at 12 Lovely Villas on 21 March?"); await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( "You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?", ); await $(ListCollectorTemporaryAwayPage.noThereAreNumberOfPeoplePeopleLivingHere()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); }); }); diff --git a/tests/functional/spec/list_collector_primary_person.spec.js b/tests/functional/spec/list_collector_primary_person.spec.js index 4b7647e564..de7913cf12 100644 --- a/tests/functional/spec/list_collector_primary_person.spec.js +++ b/tests/functional/spec/list_collector_primary_person.spec.js @@ -7,6 +7,7 @@ import SectionSummaryPage from "../generated_pages/list_collector/section-summar import { SubmitPage } from "../base_pages/submit.page.js"; import ThankYouPage from "../base_pages/thank-you.page.js"; import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_primary_person/anyone-usually-live-at.page.js"; +import { click } from "../helpers"; describe("Primary Person List Collector Survey", () => { describe("Given the user starts on the 'do you live here' question", () => { @@ -16,10 +17,10 @@ describe("Primary Person List Collector Survey", () => { it.skip("When the user says they do not live there, and changes their answer to yes, then the user can't navigate to the list collector", async () => { await $(PrimaryPersonListCollectorPage.noLabel()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.previous()).click(); await $(PrimaryPersonListCollectorPage.yesLabel()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await browser.url("questionnaire/list-collector"); await expect(await $(PrimaryPersonListCollectorPage.questionText()).getText()).to.contain("Do you live here"); }); @@ -32,28 +33,28 @@ describe("Primary Person List Collector Survey", () => { it("When the user says that they do live there, then they are shown as the primary person", async () => { await $(PrimaryPersonListCollectorPage.yesLabel()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); it("When the user adds another person, they are shown in the summary", async () => { await $(ListCollectorPage.yesLabel()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); it("When the user goes back and answers No, the primary person is not shown", async () => { await $(ListCollectorPage.previous()).click(); await $(PrimaryPersonListCollectorPage.no()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(AnyoneUsuallyLiveAtPage.no()).click(); - await $(AnyoneUsuallyLiveAtPage.submit()).click(); + await click(AnyoneUsuallyLiveAtPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); }); @@ -61,10 +62,10 @@ describe("Primary Person List Collector Survey", () => { await $(ListCollectorPage.previous()).click(); await $(AnyoneUsuallyLiveAtPage.previous()).click(); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); @@ -77,24 +78,24 @@ describe("Primary Person List Collector Survey", () => { await $(ListCollectorPage.listEditLink(1)).click(); await $(ListCollectorEditPage.firstName()).setValue("Mark"); await $(ListCollectorEditPage.lastName()).setValue("Twain"); - await $(ListCollectorEditPage.submit()).click(); + await click(ListCollectorEditPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); it("When the user views the summary, then it does not show the does anyone usually live here question", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect($("body").getText()).to.not.equal("usually live here"); }); it("When the user attempts to submit, then they are shown the confirmation page", async () => { - await $(SectionSummaryPage.submit()).click(); + await click(SectionSummaryPage.submit()); await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, do you wish to submit"); }); it("When the user submits, then they are allowed to submit the survey", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); @@ -106,7 +107,7 @@ describe("Primary Person List Collector Survey", () => { it("When the user says they do not live there, then an empty list is displayed", async () => { await $(PrimaryPersonListCollectorPage.no()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(AnyoneUsuallyLiveAtPage.no()).click(); await expect(await $(ListCollectorPage.listLabel(1)).isExisting()).to.be.false; }); @@ -114,11 +115,11 @@ describe("Primary Person List Collector Survey", () => { it("When the user clicks on the add person button multiple times, then only one person is added", async () => { await $(ListCollectorPage.previous()).click(); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twain"); - await $(PrimaryPersonListCollectorPage.submit()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); + await click(PrimaryPersonListCollectorPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); await expect(await $(ListCollectorPage.listLabel(2)).isExisting()).to.be.false; }); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index 501dc6e305..31d523d04f 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -10,7 +10,7 @@ import HouseholderCheckboxPage from "../generated_pages/list_collector_section_s import SubmitPage from "../generated_pages/list_collector_section_summary/submit.page"; import ThankYouPage from "../base_pages/thank-you.page"; import ViewSubmittedResponsePage from "../generated_pages/list_collector_section_summary/view-submitted-response.page"; -import { listItemIds } from "../helpers"; +import { click, listItemIds } from "../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -124,7 +124,7 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); - await $(AnyCompaniesOrBranchesAddPage.submit()).click(); + await click(AnyCompaniesOrBranchesAddPage.submit()); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Changed Company"); }); @@ -251,12 +251,12 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); await answerUkBasedQuestion(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await $(SectionSummaryPage.submit()).click(); + await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(HouseholderCheckboxPage.no()).click(); - await $(HouseholderCheckboxPage.submit()).click(); - await $(SectionSummaryTwoPage.submit()).click(); + await click(HouseholderCheckboxPage.submit()); + await click(SectionSummaryTwoPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); @@ -284,13 +284,13 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); await answerUkBasedQuestion(); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await $(SectionSummaryPage.submit()).click(); + await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(HouseholderCheckboxPage.no()).click(); - await $(HouseholderCheckboxPage.submit()).click(); - await $(SectionSummaryTwoPage.submit()).click(); - await $(SubmitPage.submit()).click(); + await click(HouseholderCheckboxPage.submit()); + await click(SectionSummaryTwoPage.submit()); + await click(SubmitPage.submit()); await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); @@ -310,12 +310,12 @@ describe("List Collector Section Summary and Summary Items", () => { const drivingQuestionYes = async () => { await $(AnyCompaniesOrBranchesDrivingQuestionPage.yes()).click(); - await $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); + await click(AnyCompaniesOrBranchesDrivingQuestionPage.submit()); }; const drivingQuestionNo = async () => { await $(AnyCompaniesOrBranchesDrivingQuestionPage.no()).click(); - await $(AnyCompaniesOrBranchesDrivingQuestionPage.submit()).click(); + await click(AnyCompaniesOrBranchesDrivingQuestionPage.submit()); }; const addCompany = async (name, number, authorised) => { @@ -326,28 +326,28 @@ const addCompany = async (name, number, authorised) => { } else { await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); } - await $(AnyCompaniesOrBranchesAddPage.submit()).click(); + await click(AnyCompaniesOrBranchesAddPage.submit()); }; const anyMoreCompaniesYes = async () => { await $(AnyCompaniesOrBranchesPage.yes()).click(); - await $(AnyCompaniesOrBranchesPage.submit()).click(); + await click(AnyCompaniesOrBranchesPage.submit()); }; const anyMoreCompaniesNo = async () => { await $(AnyCompaniesOrBranchesPage.no()).click(); - await $(AnyCompaniesOrBranchesPage.submit()).click(); + await click(AnyCompaniesOrBranchesPage.submit()); }; const removeFirstCompany = async () => { await $(SectionSummaryPage.companiesListRemoveLink(1)).click(); await $(AnyCompaniesOrBranchesRemovePage.yes()).click(); - await $(AnyCompaniesOrBranchesRemovePage.submit()).click(); + await click(AnyCompaniesOrBranchesRemovePage.submit()); }; const answerUkBasedQuestion = async () => { await $(UkBasedPage.yes()).click(); - await $(UkBasedPage.submit()).click(); + await click(UkBasedPage.submit()); }; const companiesListRowItem = (row, index) => { diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector_variants.spec.js index 1a3e75d76a..92df8229fc 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector_variants.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList } from "../helpers"; +import { checkItemsInList, click } from "../helpers"; import YouLiveHerePage from "../generated_pages/list_collector_variants/you-live-here-block.page.js"; import ListCollectorPage from "../generated_pages/list_collector_variants/list-collector.page.js"; import ListCollectorAddPage from "../generated_pages/list_collector_variants/list-collector-add.page.js"; @@ -15,17 +15,17 @@ describe("List Collector With Variants", () => { it("The user is asked questions about whether they live there", async () => { await $(YouLiveHerePage.yes()).click(); - await $(YouLiveHerePage.submit()).click(); + await click(YouLiveHerePage.submit()); await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone else live at 1 Pleasant Lane?"); }); it("The user is able to add members of the household", async () => { await $(ListCollectorPage.anyoneElseYes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }); it("The user can see all household members in the summary", async () => { @@ -44,12 +44,12 @@ describe("List Collector With Variants", () => { it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); }); @@ -61,17 +61,17 @@ describe("List Collector With Variants", () => { it("The user is asked questions about whether they live there", async () => { await $(YouLiveHerePage.no()).click(); - await $(YouLiveHerePage.submit()).click(); + await click(YouLiveHerePage.submit()); await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone live at 1 Pleasant Lane?"); }); it("The user is able to add members of the household", async () => { await $(ListCollectorPage.anyoneElseYes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }); it("The user can see all household members in the summary", async () => { @@ -90,12 +90,12 @@ describe("List Collector With Variants", () => { it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.url()); }); }); diff --git a/tests/functional/spec/list_collector_variants_primary_person.spec.js b/tests/functional/spec/list_collector_variants_primary_person.spec.js index 9765ba63b2..1c111aff44 100644 --- a/tests/functional/spec/list_collector_variants_primary_person.spec.js +++ b/tests/functional/spec/list_collector_variants_primary_person.spec.js @@ -5,6 +5,7 @@ import ListCollectorPage from "../generated_pages/list_collector_variants_primar import EditPersonPage from "../generated_pages/list_collector_variants_primary_person/list-collector-edit.page"; import SubmitPage from "../generated_pages/list_collector_variants_primary_person/submit.page"; import ThankYouPage from "../base_pages/thank-you.page.js"; +import { click } from "../helpers"; describe("List collector with variants primary person", () => { describe("Given that person lives in house", () => { @@ -12,7 +13,7 @@ describe("List collector with variants primary person", () => { await browser.openQuestionnaire("test_list_collector_variants_primary_person.json"); it("When the user is asked questions about whether they like variant, Then they are routed to section asking if they live in the house", async () => { await $(VariantBlockPage.yes()).click(); - await $(VariantBlockPage.submit()).click(); + await click(VariantBlockPage.submit()); await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here? (variant)"); }); }); @@ -23,44 +24,44 @@ describe("List collector with variants primary person", () => { }); it("When the user says that they do live there, Then they are shown as the primary person", async () => { await $(VariantBlockPage.yes()).click(); - await $(VariantBlockPage.submit()).click(); + await click(VariantBlockPage.submit()); await $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); }); it("When the user adds another person, Then they are shown in the list collector summary", async () => { await $(ListCollectorPage.yesLabel()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); it("When the user goes back and answers 'No' for 'Do you live here' question, Then the primary person is not shown", async () => { await $(ListCollectorPage.previous()).click(); await $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); }); it("When the user adds another person, Then the user is able to add members of the household", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); }); it("When the user adds the primary person again, Then the primary person is first in the list", async () => { await $(ListCollectorPage.previous()).click(); await $(PrimaryPersonListCollectorPage.youLiveHereYes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Mark"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); }); it("When the user views the summary, Then it does not show the remove link for the primary person", async () => { @@ -71,14 +72,14 @@ describe("List collector with variants primary person", () => { await $(ListCollectorPage.listEditLink(1)).click(); await $(EditPersonPage.firstName()).setValue("John"); await $(EditPersonPage.lastName()).setValue("Doe"); - await $(EditPersonPage.submit()).click(); + await click(EditPersonPage.submit()); await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); }); it("When the user answers 'no' to add any person, Then the questionnaire shows the confirmation page", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); @@ -89,12 +90,12 @@ describe("List collector with variants primary person", () => { it("When user updates the variant answer, Then it should come back to summary screen with updated answer", async () => { await $(SubmitPage.variantAnswerEdit()).click(); await $(VariantBlockPage.no()).click(); - await $(VariantBlockPage.submit()).click(); + await click(VariantBlockPage.submit()); await expect(await $(SubmitPage.variantAnswer()).getText()).to.equal("No"); }); it("When the user submits, Then they are allowed to submit the survey", async () => { - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); }); @@ -106,15 +107,15 @@ describe("Given the user starts on the 'Do you like variant' question", () => { }); it("When the user answers 'No' for variant question, Then they are routed to section asking if they live in the house", async () => { await $(VariantBlockPage.no()).click(); - await $(VariantBlockPage.submit()).click(); + await click(VariantBlockPage.submit()); await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here?"); }); it("When the user says they do not live there and anyone else, Then confirmation screen is displayed", async () => { await $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); }); diff --git a/tests/functional/spec/mobile_number.spec.js b/tests/functional/spec/mobile_number.spec.js index 494e8d015a..e0ccea6981 100644 --- a/tests/functional/spec/mobile_number.spec.js +++ b/tests/functional/spec/mobile_number.spec.js @@ -1,5 +1,6 @@ import MobileNumberBlockPage from "../generated_pages/mobile_number/mobile-number-block.page"; import submitPage from "../generated_pages/mobile_number/submit.page"; +import { click } from "../helpers"; describe("Mobile number validation", () => { beforeEach("Load the survey", async () => { @@ -7,17 +8,17 @@ describe("Mobile number validation", () => { }); it("Given I am asked to enter Mobile no, When I enter a valid mobile number with no prefix and submit, Then confirmation section is displayed with entered mobile number", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue(7712345678); - await $(MobileNumberBlockPage.submit()).click(); + await click(MobileNumberBlockPage.submit()); await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("7712345678"); }); it("Given I am asked to enter Mobile no, When I enter a valid mobile number with prefix (+44) and submit, Then confirmation section is displayed with entered mobile number", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue("+447712345678"); - await $(MobileNumberBlockPage.submit()).click(); + await click(MobileNumberBlockPage.submit()); await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("+447712345678"); }); it("Given I am asked to enter Mobile no, When I enter an invalid mobile number and submit, Then an error screen with invalid number information is displayed", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue("12345678"); - await $(MobileNumberBlockPage.submit()).click(); + await click(MobileNumberBlockPage.submit()); await expect(await $("body").getText()).to.contain("Enter a UK mobile number in a valid format"); }); }); diff --git a/tests/functional/spec/multiple_answers.spec.js b/tests/functional/spec/multiple_answers.spec.js index 582bfec62d..d3e4666c61 100644 --- a/tests/functional/spec/multiple_answers.spec.js +++ b/tests/functional/spec/multiple_answers.spec.js @@ -1,6 +1,7 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; import AgeBlock from "../generated_pages/multiple_answers/age-block.page"; import SubmitPage from "../generated_pages/multiple_answers/submit.page.js"; +import { click } from "../helpers"; async function answerAllQuestions() { await $(AboutYou.textfield()).setValue("John Doe"); @@ -21,11 +22,11 @@ async function answerAllQuestions() { await $(AboutYou.percentage()).setValue("3"); await $(AboutYou.mobileNumber()).setValue("07700900111"); await $(AboutYou.textarea()).setValue("Fuel type petrol"); - await $(AboutYou.submit()).click(); + await click(AboutYou.submit()); await $(AgeBlock.age()).setValue("10"); await $(AgeBlock.ageEstimateThisAgeIsAnEstimate()).click(); - await $(AgeBlock.submit()).click(); + await click(AgeBlock.submit()); } describe("Multiple Answers", () => { diff --git a/tests/functional/spec/multiple_piping.spec.js b/tests/functional/spec/multiple_piping.spec.js index 790fcae566..3bf58bd178 100644 --- a/tests/functional/spec/multiple_piping.spec.js +++ b/tests/functional/spec/multiple_piping.spec.js @@ -1,6 +1,7 @@ import AddressPage from "../generated_pages/multiple_piping/what-is-your-address.page"; import TextfieldPage from "../generated_pages/multiple_piping/textfield.page"; import MultiplePipingPage from "../generated_pages/multiple_piping/piping-question.page"; +import { click } from "../helpers"; describe("Piping", () => { const pipingSchema = "test_multiple_piping.json"; @@ -15,19 +16,19 @@ describe("Piping", () => { await $(AddressPage.townCity()).setValue("Newport"); await $(AddressPage.postcode()).setValue("NP10 8XG"); await $(AddressPage.country()).setValue("Wales"); - await $(AddressPage.submit()).click(); + await click(AddressPage.submit()); await $(TextfieldPage.firstText()).setValue("Fireman"); await $(TextfieldPage.secondText()).setValue("Sam"); - await $(TextfieldPage.submit()).click(); + await click(TextfieldPage.submit()); await expect(await $(MultiplePipingPage.answerAddressLabel()).getText()).to.contain("1 The ONS, Newport, NP10 8XG, Wales"); }); it("Given I enter values in multiple questions, When I navigate to the multiple piping question, Then I should see both values piped into the question", async () => { await $(AddressPage.addressLine1()).setValue("1 The ONS"); - await $(AddressPage.submit()).click(); + await click(AddressPage.submit()); await $(TextfieldPage.firstText()).setValue("Fireman"); await $(TextfieldPage.secondText()).setValue("Sam"); - await $(TextfieldPage.submit()).click(); + await click(TextfieldPage.submit()); await expect(await $(MultiplePipingPage.questionText()).getText()).to.contain("Does Fireman Sam live at 1 The ONS"); }); }); diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index 46b634cad2..26475781bf 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -2,6 +2,7 @@ import SetMinMax from "../generated_pages/numbers/set-min-max-block.page.js"; import TestMinMax from "../generated_pages/numbers/test-min-max-block.page.js"; import DetailAnswer from "../generated_pages/numbers/detail-answer-block.page"; import SubmitPage from "../generated_pages/numbers/submit.page"; +import { click } from "../helpers"; describe("Number validation", () => { before(async () => { @@ -16,7 +17,7 @@ describe("Number validation", () => { it("When I enter values outside of the set range, Then the correct error messages are displayed", async () => { await $(SetMinMax.setMinimum()).setValue("10"); await $(SetMinMax.setMaximum()).setValue("1020"); - await $(SetMinMax.submit()).click(); + await click(SetMinMax.submit()); await $(TestMinMax.testRange()).setValue("9"); await $(TestMinMax.testRangeExclusive()).setValue("10"); @@ -26,7 +27,7 @@ describe("Number validation", () => { await $(TestMinMax.testMaxExclusive()).setValue("12345"); await $(TestMinMax.testPercent()).setValue("101"); await $(TestMinMax.testDecimal()).setValue("5.4"); - await $(TestMinMax.submit()).click(); + await click(TestMinMax.submit()); await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to 10"); await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter an answer more than 10"); @@ -48,7 +49,7 @@ describe("Number validation", () => { await $(TestMinMax.testPercent()).setValue("100"); await $(TestMinMax.testRange()).setValue("12.344"); await $(TestMinMax.testDecimal()).setValue("11.234"); - await $(TestMinMax.submit()).click(); + await click(TestMinMax.submit()); await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter a number rounded to 2 decimal places"); await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 2 decimal places"); @@ -58,10 +59,10 @@ describe("Number validation", () => { await $(TestMinMax.testRange()).setValue("12"); await $(TestMinMax.testDecimal()).setValue("11.23"); await $(TestMinMax.testPercent()).setValue("99"); - await $(TestMinMax.submit()).click(); + await click(TestMinMax.submit()); await $(DetailAnswer.other()).click(); await $(DetailAnswer.otherDetail()).setValue("1020"); - await $(DetailAnswer.submit()).click(); + await click(DetailAnswer.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -69,14 +70,14 @@ describe("Number validation", () => { it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", async () => { await $(SubmitPage.setMaximumEdit()).click(); await $(SetMinMax.setMaximum()).setValue("1019"); - await $(SetMinMax.submit()).click(); - await $(TestMinMax.submit()).click(); - await $(DetailAnswer.submit()).click(); + await click(SetMinMax.submit()); + await click(TestMinMax.submit()); + await click(DetailAnswer.submit()); await expect(await $(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,019"); await $(DetailAnswer.otherDetail()).setValue("1019"); - await $(DetailAnswer.submit()).click(); + await click(DetailAnswer.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -84,13 +85,13 @@ describe("Number validation", () => { it("When I edit and change the minimum value, Then I must re-validate and submit any dependent answers again before I can return to the summary", async () => { await $(SubmitPage.setMinimumEdit()).click(); await $(SetMinMax.setMinimum()).setValue("11"); - await $(SetMinMax.submit()).click(); - await $(TestMinMax.submit()).click(); + await click(SetMinMax.submit()); + await click(TestMinMax.submit()); await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than 11"); await $(TestMinMax.testRangeExclusive()).setValue("12"); - await $(TestMinMax.submit()).click(); + await click(TestMinMax.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); diff --git a/tests/functional/spec/percentage_decimal.spec.js b/tests/functional/spec/percentage_decimal.spec.js index 9eb757f4c7..4c531e437f 100644 --- a/tests/functional/spec/percentage_decimal.spec.js +++ b/tests/functional/spec/percentage_decimal.spec.js @@ -1,13 +1,14 @@ import PercentagePage from "../generated_pages/percentage/block.page.js"; import PercentageDecimalPage from "../generated_pages/percentage/block-decimal.page.js"; import SubmitPage from "../generated_pages/percentage/submit.page.js"; +import { click } from "../helpers"; describe("Decimal places", () => { it("Given an answer allows 3 decimal places, When I enter a value to 3 decimal places and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { await browser.openQuestionnaire("test_percentage.json"); - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await $(PercentageDecimalPage.decimal()).setValue("3.333"); - await $(PercentageDecimalPage.submit()).click(); + await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.333"); @@ -15,9 +16,9 @@ describe("Decimal places", () => { it("Given an answer allows 3 decimal places, When I enter a value to 1 decimal place and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { await browser.openQuestionnaire("test_percentage.json"); - await $(PercentagePage.submit()).click(); + await click(PercentagePage.submit()); await $(PercentageDecimalPage.decimal()).setValue("3.3"); - await $(PercentageDecimalPage.submit()).click(); + await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.300"); diff --git a/tests/functional/spec/placeholder_answers_on_the_path.spec.js b/tests/functional/spec/placeholder_answers_on_the_path.spec.js index a608c84696..66a60dc1fc 100644 --- a/tests/functional/spec/placeholder_answers_on_the_path.spec.js +++ b/tests/functional/spec/placeholder_answers_on_the_path.spec.js @@ -7,6 +7,7 @@ import AddPersonPage from "../generated_pages/placeholder_first_non_empty_item_r import ListCollectorPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/list-collector.page"; import PersonalDetailsBlockPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/personal-details-block.page"; import HubPage from "../base_pages/hub.page.js"; +import { click } from "../helpers"; describe("First Non Empty Item Transform", () => { before("Launch survey", async () => { @@ -16,19 +17,19 @@ describe("First Non Empty Item Transform", () => { it("When the custom date range is entered and the answer is changed back to metadata date range, Then metadata date should be displayed", async () => { // Set the date await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); - await $(DateEntryBlockPage.submit()).click(); + await click(DateEntryBlockPage.submit()); // Change to original dates await $(TotalTurnoverBlockPage.previous()).click(); await $(DateEntryBlockPage.previous()).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); expect(await browser.getUrl()).to.contain(TotalTurnoverBlockPage.pageName); expect(await $(TotalTurnoverBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); }); @@ -37,30 +38,30 @@ describe("First Non Empty Item Transform", () => { describe("First Non Empty Item Transform Cross Section", () => { before("Launch survey", async () => { await browser.openQuestionnaire("test_placeholder_first_non_empty_item_cross_section_dependencies.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); }); it("Given a custom date range is entered, When the answer is changed back to metadata range, Then the metadata date should be displayed for both sections", async () => { // Set the date await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); - await $(DateEntryBlockPage.submit()).click(); + await click(DateEntryBlockPage.submit()); // Check date changed and then change to original dates - await $(HubPage.submit()).click(); + await click(HubPage.submit()); expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); await $(FoodQuestionBlockPage.previous()).click(); await $(HubPage.summaryRowLink("default-section")).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); // Check the next section if the metadata date is shown - await $(HubPage.submit()).click(); + await click(HubPage.submit()); expect(await browser.getUrl()).to.contain(FoodQuestionBlockPage.pageName); expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); }); @@ -69,40 +70,40 @@ describe("First Non Empty Item Transform Cross Section", () => { describe("First Non Empty Item Transform Repeating Sections", () => { before("Launch survey", async () => { await browser.openQuestionnaire("test_placeholder_first_non_empty_item_repeating_sections.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); }); it("Given a custom date range is entered, When the answer is changed back to metadata range, Then the metadata date should be displayed for the repeating section title", async () => { // Set the date await $(DateQuestionBlockPage.noINeedToReportForADifferentPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); await $(DateEntryBlockPage.dateEntryFromday()).setValue("5"); await $(DateEntryBlockPage.dateEntryFrommonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryFromyear()).setValue("2017"); await $(DateEntryBlockPage.dateEntryToday()).setValue("25"); await $(DateEntryBlockPage.dateEntryTomonth()).setValue("01"); await $(DateEntryBlockPage.dateEntryToyear()).setValue("2017"); - await $(DateEntryBlockPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(DateEntryBlockPage.submit()); + await click(HubPage.submit()); // Add a person to the list collector await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(AddPersonPage.firstName()).setValue("Paul"); await $(AddPersonPage.lastName()).setValue("Pogba"); - await $(AddPersonPage.submit()).click(); + await click(AddPersonPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(ListCollectorPage.submit()); // Check Repeating Section has the set dates - await $(HubPage.submit()).click(); + await click(HubPage.submit()); expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); await $(PersonalDetailsBlockPage.previous()).click(); // Change to original dates await $(HubPage.summaryRowLink("date-section")).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); - await $(DateQuestionBlockPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(DateQuestionBlockPage.submit()); + await click(HubPage.submit()); // Check the list collector has metadata dates in the title expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); diff --git a/tests/functional/spec/question_description.spec.js b/tests/functional/spec/question_description.spec.js index cf55e85acd..720c674f45 100644 --- a/tests/functional/spec/question_description.spec.js +++ b/tests/functional/spec/question_description.spec.js @@ -4,6 +4,7 @@ import RadioPage from "../generated_pages/optional_guidance_and_description/mand import RadioPageTwo from "../generated_pages/optional_guidance_and_description/mandatory-radio-two.page"; import IntroductionPage from "../generated_pages/question_guidance/introduction.page"; import GuidancePage from "../generated_pages/question_guidance/block-test-guidance-title.page"; +import { click } from "../helpers"; describe("Question description", () => { it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", async () => { @@ -15,11 +16,11 @@ describe("Question description", () => { describe("Optional question description and guidance", () => { it("Given a question description has been set in the schema, When the value to be displayed is None, Then it is not rendered on the page", async () => { await browser.openQuestionnaire("test_optional_guidance_and_description.json"); - await $(DescriptionBlockPage.submit()).click(); + await click(DescriptionBlockPage.submit()); await expect(await $(RadioPage.questionTitle()).getHTML()).to.not.contain("

      ''

      "); await expect(await $(RadioPage.guidance()).isExisting()).to.be.false; await $(RadioPage.no()).click(); - await $(RadioPage.submit()).click(); + await click(RadioPage.submit()); await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.contain("
    • List item one
    • "); await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.not.contain("
    • "); }); @@ -28,7 +29,7 @@ describe("Optional question description and guidance", () => { describe("Question guidance", () => { it("Given a question guidance with multiple content items, When it is rendered, Then there should only be one guidance box", async () => { await browser.openQuestionnaire("test_question_guidance.json"); - await $(IntroductionPage.submit()).click(); + await click(IntroductionPage.submit()); await expect(await browser.getUrl()).to.contain(GuidancePage.pageName); await expect(await $$("#question-guidance-question-test-guidance-title").length).to.equal(1); }); diff --git a/tests/functional/spec/question_variants.spec.js b/tests/functional/spec/question_variants.spec.js index f0b2976afb..0b484b0681 100644 --- a/tests/functional/spec/question_variants.spec.js +++ b/tests/functional/spec/question_variants.spec.js @@ -7,6 +7,7 @@ import firstNumberBlock from "../generated_pages/variants_question/first-number- import nameBlock from "../generated_pages/variants_question/name-block.page.js"; import proxyBlock from "../generated_pages/variants_question/proxy-block.page.js"; import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; +import { click } from "../helpers"; describe("QuestionVariants", () => { beforeEach(async () => { @@ -16,45 +17,45 @@ describe("QuestionVariants", () => { it("Given I am completing the survey, then the correct questions are shown based on my previous answers", async () => { await $(nameBlock.firstName()).setValue("Guido"); await $(nameBlock.lastName()).setValue("van Rossum"); - await $(nameBlock.submit()).click(); + await click(nameBlock.submit()); await expect(await $(proxyBlock.questionText()).getText()).to.contain("Are you Guido van Rossum?"); await $(proxyBlock.noIAmAnsweringOnTheirBehalf()).click(); - await $(proxyBlock.submit()).click(); + await click(proxyBlock.submit()); await expect(await $(ageBlock.questionText()).getText()).to.contain("What age is Guido van Rossum"); await $(ageBlock.age()).setValue(63); - await $(ageBlock.submit()).click(); + await click(ageBlock.submit()); await expect(await $(ageConfirmationBlock.questionText()).getText()).to.contain("Guido van Rossum is over 16?"); await $(ageConfirmationBlock.ageConfirmYes()).click(); - await $(ageConfirmationBlock.submit()).click(); + await click(ageConfirmationBlock.submit()); await expect(await $(basicVariantsSummary.ageQuestion()).getText()).to.contain("What age is Guido van Rossum"); await expect(await $(basicVariantsSummary.ageAnswer()).getText()).to.contain("63"); - await $(basicVariantsSummary.submit()).click(); + await click(basicVariantsSummary.submit()); await $(currencyBlock.sterling()).click(); - await $(currencyBlock.submit()).click(); + await click(currencyBlock.submit()); await expect(await $(firstNumberBlock.firstNumberLabel()).getText()).to.contain("First answer in GBP"); await $(firstNumberBlock.firstNumber()).setValue(123); - await $(firstNumberBlock.submit()).click(); + await click(firstNumberBlock.submit()); await $(secondNumberBlock.secondNumber()).setValue(321); - await $(secondNumberBlock.submit()).click(); + await click(secondNumberBlock.submit()); await expect(await $(currencySectionSummary.currencyAnswer()).getText()).to.contain("Sterling"); await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("£"); await $(currencySectionSummary.currencyAnswerEdit()).click(); await $(currencyBlock.usDollars()).click(); - await $(currencyBlock.submit()).click(); + await click(currencyBlock.submit()); await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("$"); }); diff --git a/tests/functional/spec/question_variants_first_item_in_list.spec.js b/tests/functional/spec/question_variants_first_item_in_list.spec.js index c73782ccf1..8597d6ee38 100644 --- a/tests/functional/spec/question_variants_first_item_in_list.spec.js +++ b/tests/functional/spec/question_variants_first_item_in_list.spec.js @@ -2,37 +2,38 @@ import ListCollectorPage from "../generated_pages/variants_first_item_in_list/li import ListCollectorAddPage from "../generated_pages/variants_first_item_in_list/list-collector-add.page.js"; import ListStatusQuestion from "../generated_pages/variants_first_item_in_list/list-status.page.js"; import HubPage from "../base_pages/hub.page.js"; +import { click } from "../helpers"; describe("Question Variants First Item in List", () => { it("Given I am the first person on the list, When the when rule is set, Then I should the correct question variant", async () => { await browser.openQuestionnaire("test_variants_first_item_in_list.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HubPage.submit()); await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are the first person in the list"); }); it("Given I am the second person on the list, When the when rule is set, Then I should the correct question variant", async () => { await browser.openQuestionnaire("test_variants_first_item_in_list.json"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(HubPage.summaryRowLink("personal-details-section-2")).click(); await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are not the first person in the list"); }); diff --git a/tests/functional/spec/radio_checkbox_descriptions.spec.js b/tests/functional/spec/radio_checkbox_descriptions.spec.js index 38fdcbe3c9..5a820c1a9d 100644 --- a/tests/functional/spec/radio_checkbox_descriptions.spec.js +++ b/tests/functional/spec/radio_checkbox_descriptions.spec.js @@ -1,5 +1,6 @@ import CheckboxBlockPage from "../generated_pages/radio_checkbox_descriptions/checkbox-block.page"; import RadioBlockPage from "../generated_pages/radio_checkbox_descriptions/radio-block.page"; +import { click } from "../helpers"; describe("Checkbox and Radio item descriptions", () => { describe("Given the user is presented with radio or checkbox options", () => { @@ -15,7 +16,7 @@ describe("Checkbox and Radio item descriptions", () => { it("When the schema defines a description for a radio option, then that description is displayed", async () => { await $(CheckboxBlockPage.newBusinessPracticesForOrganisingProcedures()).click(); - await $(CheckboxBlockPage.submit()).click(); + await click(CheckboxBlockPage.submit()); await expect( await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText(), ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); diff --git a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js index 35739849c1..4f7992dfaa 100644 --- a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js +++ b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js @@ -1,5 +1,6 @@ import RadioNonMandatoryPage from "../generated_pages/radio_optional_with_detail_answer_optional/radio-non-mandatory.page"; import SubmitPage from "../generated_pages/radio_optional_with_detail_answer_optional/submit.page"; +import { click } from "../helpers"; describe("Checkbox and Radio item descriptions", () => { beforeEach("load the survey", async () => { @@ -8,26 +9,26 @@ describe("Checkbox and Radio item descriptions", () => { describe("Given the user is presented with an optional radio answer with optional detail answer", () => { it("When no answer is provided, Then the expected answer is displayed", async () => { - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); }); it("When Toast is selected and no detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.toast()).click(); - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); }); it("When Other is selected and no detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.other()).click(); - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Other"); }); it("When Other is selected and detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.other()).click(); await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Eggs"); }); @@ -35,7 +36,7 @@ describe("Checkbox and Radio item descriptions", () => { await $(RadioNonMandatoryPage.other()).click(); await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); await $(RadioNonMandatoryPage.toast()).click(); - await $(RadioNonMandatoryPage.submit()).click(); + await click(RadioNonMandatoryPage.submit()); await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); }); }); diff --git a/tests/functional/spec/relationships-unrelated.spec.js b/tests/functional/spec/relationships-unrelated.spec.js index 6926b03d2e..650409666f 100644 --- a/tests/functional/spec/relationships-unrelated.spec.js +++ b/tests/functional/spec/relationships-unrelated.spec.js @@ -3,6 +3,7 @@ import ListCollectorAddPage from "../generated_pages/relationships_unrelated/lis import RelationshipsPage from "../generated_pages/relationships_unrelated/relationships.page.js"; import RelatedToAnyoneElsePage from "../generated_pages/relationships_unrelated/related-to-anyone-else.page.js"; import RelationshipsInterstitialPage from "../generated_pages/relationships_unrelated/relationship-interstitial.page.js"; +import { click } from "../helpers"; describe("Unrelated Relationships", () => { const schema = "test_relationships_unrelated.json"; @@ -20,14 +21,14 @@ describe("Unrelated Relationships", () => { await addPerson("Daniel", "Davis"); await addPerson("Eve", "Elliot"); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); }); it("When I answer 'Unrelated' twice, Then I will be asked if anyone else is related with a list of the remaining people", async () => { await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); @@ -39,9 +40,9 @@ describe("Unrelated Relationships", () => { }); it("When I return to the 'related to anyone else' question and select 'Yes', Then I will be taken to the next relationship for the first person", async () => { - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelatedToAnyoneElsePage.yes()).click(); - await $(RelatedToAnyoneElsePage.submit()).click(); + await click(RelatedToAnyoneElsePage.submit()); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Andrew Austin, Daniel Davis is their"); }); @@ -53,7 +54,7 @@ describe("Unrelated Relationships", () => { it("When I select 'No' to the 'related to anyone else' question, Then I will be taken to the first relationship for the second person", async () => { await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).click(); - await $(RelatedToAnyoneElsePage.submit()).click(); + await click(RelatedToAnyoneElsePage.submit()); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Betty Burns, Carla Clark is their"); }); @@ -66,19 +67,19 @@ describe("Unrelated Relationships", () => { }); it("When I click complete the remaining relationships, Then I will go to the relationships section complete page", async () => { - await $(RelatedToAnyoneElsePage.submit()).click(); + await click(RelatedToAnyoneElsePage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await expect(await browser.getUrl()).to.contain(RelationshipsInterstitialPage.pageName); }); }); @@ -86,10 +87,10 @@ describe("Unrelated Relationships", () => { async function addPerson(firstName, lastName) { await $(ListCollectorPage.yes()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue(firstName); await $(ListCollectorAddPage.lastName()).setValue(lastName); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); } }); }); diff --git a/tests/functional/spec/relationships.spec.js b/tests/functional/spec/relationships.spec.js index c6669c0b6d..ba8ee11f60 100644 --- a/tests/functional/spec/relationships.spec.js +++ b/tests/functional/spec/relationships.spec.js @@ -4,6 +4,7 @@ import ListCollectorRemovePage from "../generated_pages/relationships/list-colle import RelationshipsPage from "../generated_pages/relationships/relationships.page.js"; import RelationshipsInterstitialPage from "../generated_pages/relationships/relationship-interstitial.page.js"; import SectionSummaryPage from "../generated_pages/relationships/section-summary.page.js"; +import { click } from "../helpers"; describe("Relationships", () => { const schema = "test_relationships.json"; @@ -15,33 +16,34 @@ describe("Relationships", () => { it("When I have one household member, Then I will be not be asked about relationships", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + // eslint-disable-next-line no-undef + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); }); it("When I add two household members, Then I will be asked about one relationship", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsPage.submit()); + await click(RelationshipsInterstitialPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); }); @@ -52,51 +54,51 @@ describe("Relationships", () => { it("Then I will be asked about all relationships", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsPage.submit()); + await click(RelationshipsInterstitialPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); }); it("And go to the first relationship, Then the previous link should return to the list collector", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.previous()).click(); await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); }); it("And go to the first relationship, Then the 'Brother or Sister' option should have the text 'Including half brother or half sister'", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(RelationshipsPage.brotherOrSisterLabelDescription()).getText()).to.contain("Including half brother or half sister"); }); it("And go to the second relationship, Then the previous link should return to the first relationship", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.previous()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsInterstitialPage.submit()); await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Marcus"); }); it("And go to the section summary, Then the previous link should return to the last relationship Interstitial", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsPage.submit()); + await click(RelationshipsInterstitialPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); @@ -106,14 +108,14 @@ describe("Relationships", () => { it("When I add all relationships and return to the relationships, Then the relationships should be populated", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsPage.submit()); + await click(RelationshipsInterstitialPage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); @@ -124,21 +126,21 @@ describe("Relationships", () => { it("And go to the first relationship, Then the person's name should be in the question title and playback text", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(ListCollectorPage.questionText()).getText()).to.contain("Marcus Twin"); await expect(await $(RelationshipsPage.playback()).getText()).to.contain("Marcus Twin"); }); it("And go to the first relationship and submit without selecting an option, Then an error should be displayed", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(RelationshipsPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(RelationshipsPage.submit()); await expect(await $(RelationshipsPage.error()).isDisplayed()).to.be.true; }); it("And go to the first relationship and click 'Save and sign out', Then I should be signed out", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); await $(RelationshipsPage.saveSignOut()).click(); await expect(await browser.getUrl()).to.not.contain("questionnaire"); @@ -146,7 +148,7 @@ describe("Relationships", () => { it("And go to the first relationship, select a relationship and click 'Save and sign out', Then I should be signed out", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.saveSignOut()).click(); await expect(await browser.getUrl()).to.not.contain("questionnaire"); }); @@ -160,7 +162,7 @@ describe("Relationships", () => { it("Then I delete one of the original household members I will not be asked for the original members relationships again", async () => { await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); - await $(ListCollectorRemovePage.submit()).click(); + await click(ListCollectorRemovePage.submit()); await expect(await browser.getUrl()).to.contain("/sections/section/"); }); @@ -168,7 +170,7 @@ describe("Relationships", () => { await $(SectionSummaryPage.peopleListAddLink()).click(); await $(ListCollectorAddPage.firstName()).setValue("Tom"); await $(ListCollectorAddPage.lastName()).setValue("Bowden"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); }); }); @@ -178,33 +180,33 @@ describe("Relationships", () => { await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.legallyRegisteredCivilPartner()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); - await $(RelationshipsPage.submit()).click(); - await $(RelationshipsInterstitialPage.submit()).click(); + await click(RelationshipsPage.submit()); + await click(RelationshipsInterstitialPage.submit()); } async function addThreePeople() { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await $(ListCollectorAddPage.submit()).scrollIntoView(); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Olivia"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); } }); }); diff --git a/tests/functional/spec/relationships_primary.spec.js b/tests/functional/spec/relationships_primary.spec.js index 3306693cbd..ee22c938a3 100644 --- a/tests/functional/spec/relationships_primary.spec.js +++ b/tests/functional/spec/relationships_primary.spec.js @@ -3,6 +3,7 @@ import PrimaryPersonListCollectorAddPage from "../generated_pages/relationships_ import ListCollectorPage from "../generated_pages/relationships_primary/list-collector.page.js"; import ListCollectorAddPage from "../generated_pages/relationships_primary/list-collector-add.page.js"; import RelationshipsPage from "../generated_pages/relationships_primary/relationships.page.js"; +import { click } from "../helpers"; describe("Relationships - Primary Person", () => { const schema = "test_relationships_primary.json"; @@ -16,7 +17,7 @@ describe("Relationships - Primary Person", () => { await addPrimaryAndTwoOthers(); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is your"); }); @@ -24,11 +25,11 @@ describe("Relationships - Primary Person", () => { await addPrimaryAndTwoOthers(); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.relationshipBrotherOrSister()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.relationshipSonOrDaughter()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is their"); }); @@ -38,17 +39,17 @@ describe("Relationships - Primary Person", () => { await browser.url("/questionnaire/primary-person-list-collector"); await $(PrimaryPersonListCollectorPage.no()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await browser.url("/questionnaire/primary-person-list-collector"); await $(PrimaryPersonListCollectorPage.yes()).click(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Samuel Clemens is your"); }); @@ -58,31 +59,31 @@ describe("Relationships - Primary Person", () => { await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(RelationshipsPage.relationshipBrotherOrSister()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.relationshipSonOrDaughter()).click(); - await $(RelationshipsPage.submit()).click(); + await click(RelationshipsPage.submit()); await $(RelationshipsPage.relationshipBrotherOrSister()).click(); } async function addPrimaryAndTwoOthers() { await $(PrimaryPersonListCollectorPage.yes()).click(); await $(PrimaryPersonListCollectorPage.submit()).scrollIntoView(); - await $(PrimaryPersonListCollectorPage.submit()).click(); + await click(PrimaryPersonListCollectorPage.submit()); await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); - await $(PrimaryPersonListCollectorAddPage.submit()).click(); + await click(PrimaryPersonListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Olivia"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); } }); }); diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index f1a20b0515..992153844f 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -19,6 +19,7 @@ import RepeatingIsDependentPage from "../generated_pages/routing_and_skipping_se import RepeatingIsSmokerPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-is-smoker.page"; import HubPage from "../base_pages/hub.page"; +import { click } from "../helpers"; describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire", () => { @@ -87,25 +88,25 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("45"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.no()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await $(RepeatingIsSmokerPage.no()).click(); - await $(RepeatingIsSmokerPage.submit()).click(); + await click(RepeatingIsSmokerPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("10"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); @@ -118,18 +119,18 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.no()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; @@ -182,7 +183,7 @@ describe("Routing and skipping section dependencies", () => { await answerAndSubmitAgeQuestion(); await $(ReasonNoConfirmationPage.iDidButItWasRemovedFromThePathAsIChangedMyAnswerToNoOnTheSkipQuestion()).click(); - await $(ReasonNoConfirmationPage.submit()).click(); + await click(ReasonNoConfirmationPage.submit()); await expectPersonalDetailsName(); await expectPersonalDetailsAge(); @@ -202,7 +203,7 @@ describe("Routing and skipping section dependencies", () => { await selectPrimaryPerson(); await answerAndSubmitNameQuestion(); await answerAndSubmitReasonForNoConfirmationQuestion(); - await $(PrimaryPersonSummaryPage.submit()).click(); + await click(PrimaryPersonSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); @@ -228,10 +229,10 @@ describe("Routing and skipping section dependencies", () => { await addHouseholdMembers(); await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.no()).click(); - await $(RepeatingIsDependentPage.submit()).click(); - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await editNoToSkipAgeQuestion(); @@ -259,28 +260,28 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("45"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.no()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await $(RepeatingIsSmokerPage.no()).click(); - await $(RepeatingIsSmokerPage.submit()).click(); + await click(RepeatingIsSmokerPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).to.contain("No"); - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("19"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await $(RepeatingIsSmokerPage.no()).click(); - await $(RepeatingIsSmokerPage.submit()).click(); + await click(RepeatingIsSmokerPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("19"); @@ -294,24 +295,24 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("15"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("15"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingAgePage.answer()).setValue("10"); - await $(RepeatingAgePage.submit()).click(); + await click(RepeatingAgePage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); @@ -325,19 +326,19 @@ describe("Routing and skipping section dependencies", () => { await $(HubPage.summaryRowLink("household-personal-details-section-1")).click(); await $(RepeatingSexPage.female()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.no()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; - await $(HouseHoldPersonalDetailsSectionSummaryPage.submit()).click(); + await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); await $(RepeatingSexPage.male()).click(); - await $(RepeatingSexPage.submit()).click(); + await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.yes()).click(); - await $(RepeatingIsDependentPage.submit()).click(); + await click(RepeatingIsDependentPage.submit()); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; @@ -349,18 +350,18 @@ describe("Routing and skipping section dependencies", () => { const addHouseholdMembers = async () => { await $(HubPage.summaryRowLink("household-section")).click(); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Sarah"); await $(ListCollectorAddPage.lastName()).setValue("Smith"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Smith"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); - await $(HouseholdSectionSummaryPage.submit()).click(); + await click(ListCollectorPage.submit()); + await click(HouseholdSectionSummaryPage.submit()); }; const selectPrimaryPerson = async () => { @@ -370,102 +371,102 @@ const selectPrimaryPerson = async () => { const selectConfirmationSectionAndAnswerSecurityQuestion = async () => { await $(HubPage.summaryRowLink("skip-confirmation-section")).click(); await $(SecurityPage.yes()).click(); - await $(SecurityPage.submit()).click(); + await click(SecurityPage.submit()); }; const answerYesToSkipAgeQuestion = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipAgePage.yes()).click(); - await $(SkipAgePage.submit()).click(); + await click(SkipAgePage.submit()); await $(SkipEnableSectionPage.no()).click(); - await $(SkipEnableSectionPage.submit()).click(); + await click(SkipEnableSectionPage.submit()); await $(EnableSectionPage.yes()).click(); - await $(EnableSectionPage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(EnableSectionPage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const editNoToSkipAgeQuestion = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); await $(SkipAgePage.no()).click(); - await $(SkipAgePage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(SkipAgePage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const editYesToSkipAgeQuestion = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipSectionSummaryPage.skipAgeAnswerEdit()).click(); await $(SkipAgePage.yes()).click(); - await $(SkipAgePage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(SkipAgePage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const answerNoToSkipAgeQuestion = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipAgePage.no()).click(); - await $(SkipAgePage.submit()).click(); + await click(SkipAgePage.submit()); await $(SkipEnableSectionPage.no()).click(); - await $(SkipEnableSectionPage.submit()).click(); + await click(SkipEnableSectionPage.submit()); await $(EnableSectionPage.yes()).click(); - await $(EnableSectionPage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(EnableSectionPage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const answerNoToSkipConfirmationQuestion = async () => { await $(SkipConfirmationPage.no()).click(); - await $(SkipConfirmationPage.submit()).click(); - await $(SkipConfirmationSectionSummaryPage.submit()).click(); + await click(SkipConfirmationPage.submit()); + await click(SkipConfirmationSectionSummaryPage.submit()); }; const answerYesToSkipConfirmationQuestion = async () => { await $(SkipConfirmationPage.yes()).click(); - await $(SkipConfirmationPage.submit()).click(); - await $(SkipConfirmationSectionSummaryPage.submit()).click(); + await click(SkipConfirmationPage.submit()); + await click(SkipConfirmationSectionSummaryPage.submit()); }; const answerNoToSkipEnableQuestionAndYesToEnableSection = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipAgePage.no()).click(); - await $(SkipAgePage.submit()).click(); + await click(SkipAgePage.submit()); await $(SkipEnableSectionPage.no()).click(); - await $(SkipEnableSectionPage.submit()).click(); + await click(SkipEnableSectionPage.submit()); await $(EnableSectionPage.yes()).click(); - await $(EnableSectionPage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(EnableSectionPage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const answerNoToSkipEnableQuestionAndNoToEnableSection = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipAgePage.no()).click(); - await $(SkipAgePage.submit()).click(); + await click(SkipAgePage.submit()); await $(SkipEnableSectionPage.no()).click(); - await $(SkipEnableSectionPage.submit()).click(); + await click(SkipEnableSectionPage.submit()); await $(EnableSectionPage.no()).click(); - await $(EnableSectionPage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(EnableSectionPage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const changeSkipEnableQuestionToYes = async () => { await $(HubPage.summaryRowLink("skip-section")).click(); await $(SkipSectionSummaryPage.skipHouseholdSectionAnswerEdit()).click(); await $(SkipEnableSectionPage.yes()).click(); - await $(SkipEnableSectionPage.submit()).click(); - await $(SkipSectionSummaryPage.submit()).click(); + await click(SkipEnableSectionPage.submit()); + await click(SkipSectionSummaryPage.submit()); }; const answerAndSubmitNameQuestion = async () => { await $(NamePage.name()).setValue("John Smith"); - await $(NamePage.submit()).click(); + await click(NamePage.submit()); }; const answerAndSubmitAgeQuestion = async () => { await $(AgePage.answer()).setValue("50"); - await $(AgePage.submit()).click(); + await click(AgePage.submit()); }; const answerAndSubmitReasonForNoConfirmationQuestion = async () => { await $(ReasonNoConfirmationPage.iDidNotVisitSection2SoConfirmationWasNotNeeded()).click(); - await $(ReasonNoConfirmationPage.submit()).click(); + await click(ReasonNoConfirmationPage.submit()); }; const expectPersonalDetailsName = async () => { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index bd4926efe6..470594cf0a 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -9,6 +9,7 @@ import SkipQuestionPage from "../generated_pages/routing_and_skipping_section_de import ButterPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/butter-block.page"; import HubPage from "../base_pages/hub.page"; +import { click } from "../helpers"; describe("Routing and skipping section dependencies based on calculated summaries", () => { describe("Given the section dependencies based on a calculated summary questionnaire", () => { @@ -28,11 +29,11 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(25); await $(FirstQuestionBlockPage.bread()).setValue(25); await $(FirstQuestionBlockPage.cheese()).setValue(25); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; @@ -45,11 +46,11 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(1); await $(FirstQuestionBlockPage.bread()).setValue(1); await $(FirstQuestionBlockPage.cheese()).setValue(1); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); await expect(await browser.getUrl()).to.contain(FruitPage.pageName); @@ -61,11 +62,11 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(5); await $(FirstQuestionBlockPage.bread()).setValue(5); await $(FirstQuestionBlockPage.cheese()).setValue(5); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); await expect(await browser.getUrl()).to.contain(VegetablesPage.pageName); @@ -77,15 +78,15 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(30); await $(FirstQuestionBlockPage.bread()).setValue(30); await $(FirstQuestionBlockPage.cheese()).setValue(30); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); - await $(VegetablesPage.submit()).click(); + await click(VegetablesPage.submit()); await expect(await browser.getUrl()).to.contain(SecondQuestionBlockPage.pageName); }); @@ -95,15 +96,15 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(20); await $(FirstQuestionBlockPage.bread()).setValue(20); await $(FirstQuestionBlockPage.cheese()).setValue(20); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); - await $(VegetablesPage.submit()).click(); + await click(VegetablesPage.submit()); await expect(await browser.getUrl()).to.contain(DependentQuestionSectionSummaryPage.pageName); }); @@ -113,26 +114,26 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(20); await $(FirstQuestionBlockPage.bread()).setValue(20); await $(FirstQuestionBlockPage.cheese()).setValue(20); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); - await $(VegetablesPage.submit()).click(); - await $(DependentQuestionSectionSummaryPage.submit()).click(); + await click(VegetablesPage.submit()); + await click(DependentQuestionSectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Completed"); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); await $(FirstQuestionBlockPage.milk()).setValue(100); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); }); @@ -142,11 +143,11 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(10); await $(FirstQuestionBlockPage.bread()).setValue(10); await $(FirstQuestionBlockPage.cheese()).setValue(10); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; @@ -155,11 +156,11 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); await $(SkipQuestionPage.no()).click(); - await $(SkipQuestionPage.submit()).click(); + await click(SkipQuestionPage.submit()); await $(ButterPage.butter()).setValue(60); - await $(ButterPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(ButterPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; @@ -172,13 +173,13 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(FirstQuestionBlockPage.eggs()).setValue(10); await $(FirstQuestionBlockPage.bread()).setValue(10); await $(FirstQuestionBlockPage.cheese()).setValue(10); - await $(FirstQuestionBlockPage.submit()).click(); + await click(FirstQuestionBlockPage.submit()); await $(SkipQuestionPage.no()).click(); - await $(SkipQuestionPage.submit()).click(); + await click(SkipQuestionPage.submit()); await $(ButterPage.butter()).setValue(60); - await $(ButterPage.submit()).click(); - await $(CurrencyTotalPlaybackPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(ButterPage.submit()); + await click(CurrencyTotalPlaybackPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; @@ -187,8 +188,8 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); await $(SkipQuestionPage.yes()).click(); - await $(SkipQuestionPage.submit()).click(); - await $(CalculatedSummarySectionSummaryPage.submit()).click(); + await click(SkipQuestionPage.submit()); + await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; diff --git a/tests/functional/spec/routing_checkbox_contains.spec.js b/tests/functional/spec/routing_checkbox_contains.spec.js index c48b5c29b3..ca42bbec2f 100644 --- a/tests/functional/spec/routing_checkbox_contains.spec.js +++ b/tests/functional/spec/routing_checkbox_contains.spec.js @@ -2,7 +2,7 @@ import RoutingCheckboxContains from "../generated_pages/routing_checkbox_contain import ContainsAllPage from "../generated_pages/routing_checkbox_contains/country-interstitial-all.page"; import ContainsAnyPage from "../generated_pages/routing_checkbox_contains/country-interstitial-any.page"; import SubmitPage from "../generated_pages/routing_checkbox_contains/submit.page"; - +import { click } from "../helpers"; describe("Routing Checkbox Contains Condition.", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_checkbox_contains.json"); @@ -13,7 +13,7 @@ describe("Routing Checkbox Contains Condition.", () => { await expect(await $(RoutingCheckboxContains.liechtenstein()).isSelected()).to.be.false; await $(RoutingCheckboxContains.india()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); @@ -23,7 +23,7 @@ describe("Routing Checkbox Contains Condition.", () => { // When await $(RoutingCheckboxContains.india()).click(); await $(RoutingCheckboxContains.azerbaijan()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); @@ -33,7 +33,7 @@ describe("Routing Checkbox Contains Condition.", () => { // When await $(RoutingCheckboxContains.india()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); @@ -42,7 +42,7 @@ describe("Routing Checkbox Contains Condition.", () => { it('Given a list of checkbox options, when I select the option "Malta" or the option "Liechtenstein" or both then I should be routed to the summary condition page', async () => { // When await $(RoutingCheckboxContains.liechtenstein()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -52,7 +52,7 @@ describe("Routing Checkbox Contains Condition.", () => { // When await $(RoutingCheckboxContains.liechtenstein()).click(); await $(RoutingCheckboxContains.malta()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -62,7 +62,7 @@ describe("Routing Checkbox Contains Condition.", () => { // When await $(RoutingCheckboxContains.liechtenstein()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -73,7 +73,7 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.india()).click(); await $(RoutingCheckboxContains.azerbaijan()).click(); await $(RoutingCheckboxContains.liechtenstein()).click(); - await $(RoutingCheckboxContains.submit()).click(); + await click(RoutingCheckboxContains.submit()); // Then await expect(await browser.getUrl()).to.contain(ContainsAllPage.pageName); }); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index fe122f3389..98719b301a 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -7,7 +7,7 @@ import IntroInterstitialPage from "../generated_pages/introduction/general-busin import IntroThankYouPagePage from "../base_pages/thank-you.page"; import HouseHolderConfirmationPage from "../generated_pages/thank_you_census_household/household-confirmation.page"; import { getRandomString } from "../jwt_helper"; - +import { click } from "../helpers"; describe("Save sign out / Exit", () => { const responseId = getRandomString(16); @@ -25,7 +25,7 @@ describe("Save sign out / Exit", () => { await browser.openQuestionnaire("test_numbers.json", { userId: "test_user", responseId }); await $(SetMinMax.setMinimum()).setValue("10"); await $(SetMinMax.setMaximum()).setValue("1020"); - await $(SetMinMax.submit()).click(); + await click(SetMinMax.submit()); await $(TestMinMax.saveSignOut()).click(); await expect(await browser.getUrl()).to.contain("/signed-out"); @@ -41,11 +41,11 @@ describe("Save sign out / Exit", () => { await $(TestMinMax.testMin()).setValue("123"); await $(TestMinMax.testMax()).setValue("1000"); await $(TestMinMax.testPercent()).setValue("100"); - await $(TestMinMax.submit()).click(); + await click(TestMinMax.submit()); await $(DetailAnswer.answer1()).click(); - await $(DetailAnswer.submit()).click(); + await click(DetailAnswer.submit()); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); }); @@ -75,10 +75,10 @@ describe("Save sign out / Exit", () => { await $(IntroductionPage.getStarted()).click(); await expect(await $(IntroInterstitialPage.saveSignOut()).getText()).to.contain("Save and exit survey"); - await $(IntroInterstitialPage.submit()).click(); + await click(IntroInterstitialPage.submit()); await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and exit survey"); - await $(SubmitPage.submit()).click(); + await click(SubmitPage.submit()); await expect(await $(IntroThankYouPagePage.exitButton()).isExisting()).to.be.false; }); @@ -87,7 +87,7 @@ describe("Save sign out / Exit", () => { await browser.openQuestionnaire("test_thank_you_census_household.json"); await expect(await $(HouseHolderConfirmationPage.saveSignOut()).getText()).to.contain("Save and complete later"); - await $(HouseHolderConfirmationPage.submit()).click(); + await click(HouseHolderConfirmationPage.submit()); await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and complete later"); }); diff --git a/tests/functional/spec/skip_condition_block.spec.js b/tests/functional/spec/skip_condition_block.spec.js index 8906e9a0e1..2fa483db9d 100644 --- a/tests/functional/spec/skip_condition_block.spec.js +++ b/tests/functional/spec/skip_condition_block.spec.js @@ -1,7 +1,7 @@ import QuestionPage from "../generated_pages/skip_condition_block/do-you-want-to-skip.page"; import SkipPage from "../generated_pages/skip_condition_block/should-skip.page"; import SubmitPage from "../generated_pages/skip_condition_block/submit.page"; - +import { click } from "../helpers"; describe("Skip Conditions - Block", () => { const schema = "test_skip_condition_block.json"; @@ -12,13 +12,13 @@ describe("Skip Conditions - Block", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); - await $(QuestionPage.submit()).click(); + await click(QuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); - await $(QuestionPage.submit()).click(); + await click(QuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SkipPage.pageName); }); }); diff --git a/tests/functional/spec/skip_condition_group.spec.js b/tests/functional/spec/skip_condition_group.spec.js index dd35ec39aa..cc4e18b56d 100644 --- a/tests/functional/spec/skip_condition_group.spec.js +++ b/tests/functional/spec/skip_condition_group.spec.js @@ -1,7 +1,7 @@ import QuestionPage from "../generated_pages/skip_condition_group/do-you-want-to-skip.page"; import SkipPage from "../generated_pages/skip_condition_group/should-skip.page"; import SubmitPage from "../generated_pages/skip_condition_group/submit.page"; - +import { click } from "../helpers"; describe("Skip Conditions - Group", () => { const schema = "test_skip_condition_group.json"; @@ -12,13 +12,13 @@ describe("Skip Conditions - Group", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); - await $(QuestionPage.submit()).click(); + await click(QuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); - await $(QuestionPage.submit()).click(); + await click(QuestionPage.submit()); await expect(await browser.getUrl()).to.contain(SkipPage.pageName); }); }); diff --git a/tests/functional/spec/skip_condition_list.spec.js b/tests/functional/spec/skip_condition_list.spec.js index 9dd71278a5..95bfee02d1 100644 --- a/tests/functional/spec/skip_condition_list.spec.js +++ b/tests/functional/spec/skip_condition_list.spec.js @@ -3,7 +3,7 @@ import ListCollectorAddPage from "../generated_pages/skip_condition_list/list-co import LessThanTwoInterstitialPage from "../generated_pages/skip_condition_list/less-than-two-interstitial.page.js"; import TwoInterstitialPage from "../generated_pages/skip_condition_list/two-interstitial.page.js"; import MoreThanTwoInterstitialPage from "../generated_pages/skip_condition_list/more-than-two-interstitial.page.js"; - +import { click } from "../helpers"; describe("Feature: Routing on lists", () => { describe("Given I start skip condition list survey", () => { beforeEach(async () => { @@ -12,55 +12,55 @@ describe("Feature: Routing on lists", () => { it("When I don't add a person to the list, Then the less than two people skippable page should be shown", async () => { await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); }); it("When I add one person to the list, Then the less than two people skippable page should be shown", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); }); it("When I add two people to the list, Then the two people skippable page should be shown", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(TwoInterstitialPage.pageName); }); it("When I add three people to the list, Then the more than two people skippable page should be shown", async () => { await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.yes()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await $(ListCollectorAddPage.firstName()).setValue("Olivia"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); - await $(ListCollectorAddPage.submit()).click(); + await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); - await $(ListCollectorPage.submit()).click(); + await click(ListCollectorPage.submit()); await expect(await browser.getUrl()).to.contain(MoreThanTwoInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/skip_conditions_not_set.spec.js b/tests/functional/spec/skip_conditions_not_set.spec.js index 34ff105ab3..5f7f56b67b 100644 --- a/tests/functional/spec/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/skip_conditions_not_set.spec.js @@ -1,20 +1,20 @@ import FoodPage from "../generated_pages/skip_condition_not_set/food-block.page"; import DrinkPage from "../generated_pages/skip_condition_not_set/drink-block.page"; import SubmitPage from "../generated_pages/skip_condition_not_set/submit.page"; - +import { click } from "../helpers"; describe("Skip Conditions - Not Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_not_set.json"); }); it("Given I do not complete the first page, Then I should see the summary page", async () => { - await $(FoodPage.submit()).click(); + await click(FoodPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("Given I complete the first page, Then I should see the drink page", async () => { await $(FoodPage.bacon()).click(); - await $(FoodPage.submit()).click(); + await click(FoodPage.submit()); await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/skip_conditions_set.spec.js b/tests/functional/spec/skip_conditions_set.spec.js index 65bfa0589c..99d4aca253 100644 --- a/tests/functional/spec/skip_conditions_set.spec.js +++ b/tests/functional/spec/skip_conditions_set.spec.js @@ -1,7 +1,7 @@ import FoodPage from "../generated_pages/skip_condition_set/food-block.page"; import DrinkPage from "../generated_pages/skip_condition_set/drink-block.page"; import SubmitPage from "../generated_pages/skip_condition_set/submit.page"; - +import { click } from "../helpers"; describe("Skip Conditions - Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_set.json"); @@ -9,12 +9,12 @@ describe("Skip Conditions - Set", () => { it("Given I complete the first page, Then I should see the summary page", async () => { await $(FoodPage.bacon()).click(); - await $(FoodPage.submit()).click(); + await click(FoodPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("Given I do not complete the first page, Then I should see the drink page", async () => { - await $(FoodPage.submit()).click(); + await click(FoodPage.submit()); await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/submit_with_custom_submission_text.spec.js b/tests/functional/spec/submit_with_custom_submission_text.spec.js index 5153c61cac..f933804061 100644 --- a/tests/functional/spec/submit_with_custom_submission_text.spec.js +++ b/tests/functional/spec/submit_with_custom_submission_text.spec.js @@ -1,7 +1,7 @@ import BreakfastPage from "../generated_pages/submit_with_custom_submission_text/breakfast.page.js"; import IntroductionPage from "../generated_pages/submit_with_custom_submission_text/introduction.page.js"; import { SubmitPage } from "../base_pages/submit.page.js"; - +import { click } from "../helpers"; describe("Submit with custom submission text", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_submit_with_custom_submission_text.json"); @@ -10,7 +10,7 @@ describe("Submit with custom submission text", () => { it("Given a questionnaire with custom submission content has been started, when it is completed to the submit page, then the correct submission content should be displayed", async () => { await $(IntroductionPage.getStarted()).click(); await $(BreakfastPage.answer()).setValue("Eggs"); - await $(BreakfastPage.submit()).click(); + await click(BreakfastPage.submit()); await expect(await $(SubmitPage.heading()).getText()).to.contain("Submit your questionnaire"); await expect(await $(SubmitPage.warning()).getText()).to.contain("You cannot view your answers after submission"); await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, submit this to complete it"); diff --git a/tests/functional/spec/textarea.spec.js b/tests/functional/spec/textarea.spec.js index 126d8fce37..0de7384c0c 100644 --- a/tests/functional/spec/textarea.spec.js +++ b/tests/functional/spec/textarea.spec.js @@ -1,6 +1,6 @@ import TextareaBlock from "../generated_pages/textarea/textarea-block.page.js"; import TextareaSummary from "../generated_pages/textarea/submit.page.js"; - +import { click } from "../helpers"; describe("Textarea", () => { const textareaSchema = "test_textarea.json"; const textareaLimit = `${TextareaBlock.answer()} + [data-charcount-singular]`; @@ -14,19 +14,19 @@ describe("Textarea", () => { }); it('Given a textarea option, When no text is entered, Then the summary should display "No answer provided"', async () => { - await $(TextareaBlock.submit()).click(); + await click(TextareaBlock.submit()); await expect(await $(TextareaSummary.answer()).getText()).to.contain("No answer provided"); }); it("Given a textarea option, When some text is entered, Then the summary should display the text", async () => { await $(TextareaBlock.answer()).setValue("Some text"); - await $(TextareaBlock.submit()).click(); + await click(TextareaBlock.submit()); await expect(await $(TextareaSummary.answer()).getText()).to.contain("Some text"); }); it("Given a text entered in textarea , When user submits and revisits the textarea, Then the textarea must contain the text entered previously", async () => { await $(TextareaBlock.answer()).setValue("'Twenty><&Five'"); - await $(TextareaBlock.submit()).click(); + await click(TextareaBlock.submit()); await expect(await $(TextareaSummary.answer()).getText()).to.contain("'Twenty><&Five'"); await $(TextareaSummary.answerEdit()).click(); await $(TextareaBlock.answer()).getValue(); diff --git a/tests/functional/spec/textfield.spec.js b/tests/functional/spec/textfield.spec.js index f77b52d6ce..b842e004c2 100644 --- a/tests/functional/spec/textfield.spec.js +++ b/tests/functional/spec/textfield.spec.js @@ -1,6 +1,6 @@ import TextFieldPage from "../generated_pages/textfield/name-block.page.js"; import SubmitPage from "../generated_pages/textfield/submit.page.js"; - +import { click } from "../helpers"; describe("Textfield", () => { it("Given a textfield option, a user should be able to click the label of the textfield to focus", async () => { await browser.openQuestionnaire("test_textfield.json"); @@ -11,7 +11,7 @@ describe("Textfield", () => { it("Given a text entered in textfield , When user submits and revisits the textfield, Then the textfield must contain the text entered previously", async () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.name()).setValue("'Twenty><&Five'"); - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Twenty><&Five'"); await $(SubmitPage.nameAnswerEdit()).click(); @@ -21,7 +21,7 @@ describe("Textfield", () => { it("Given the string entered to the textfield is too long, When the user submits, then the correct error message is displayed", async () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.name()).setValue("This string is too long"); - await $(TextFieldPage.submit()).click(); + await click(TextFieldPage.submit()); await expect(await $(TextFieldPage.errorNumber(1)).getText()).to.contain("You have entered too many characters. Enter up to 20 characters"); }); }); diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index 80cd8fd70a..8aea9ebfb7 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -1,7 +1,7 @@ import SuggestionsPage from "../generated_pages/textfield_suggestions/country-block.page.js"; import MultipleSuggestionsPage from "../generated_pages/textfield_suggestions/multiple-country-block.page.js"; import SubmitPage from "../generated_pages/textfield_suggestions/submit.page.js"; - +import { click } from "../helpers"; describe("Suggestions", () => { it("Given I open a textfield with a suggestions url, when I have entered text, then it will show suggestions", async () => { await browser.openQuestionnaire("test_textfield_suggestions.json"); @@ -18,7 +18,7 @@ describe("Suggestions", () => { const suggestionsOption = $("#multiple-country-answer-listbox__option--0"); await $(SuggestionsPage.country()).setValue("United States of America"); - await $(SuggestionsPage.submit()).click(); + await click(SuggestionsPage.submit()); await $(MultipleSuggestionsPage.multipleCountry()).click(); // Browser needs to pause before typing starts to allow for the autosuggest Javascript to initialise await browser.pause(500); @@ -33,7 +33,7 @@ describe("Suggestions", () => { await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); // TODO there is an issue with the load-time of the auto-suggest dropdown causing this test to fail. Uncomment when this has been resolved. // await suggestionsOption.click(); - await $(MultipleSuggestionsPage.submit()).click(); + await click(MultipleSuggestionsPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.url()); }); }); diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index ef00141f1f..4596278662 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -4,7 +4,7 @@ import CheckboxPage from "../generated_pages/title/single-title-block.page"; import ThankYouPage from "../base_pages/thank-you.page"; import DidYouKnowPage from "../generated_pages/thank_you/did-you-know.page"; import ThankYouSubmitPage from "../generated_pages/thank_you/submit.page"; - +import { click } from "../helpers"; describe("Thank You Social", () => { describe("Given I launch a social themed questionnaire", () => { beforeEach(async () => { @@ -12,8 +12,8 @@ describe("Thank You Social", () => { }); it("When I navigate to the thank you page, Then I should see social theme content", async () => { - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Social Survey"); await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted"); @@ -31,8 +31,8 @@ describe("Thank You Default", () => { it("When I navigate to the thank you page, Then I should see default theme content", async () => { await $(CheckboxPage.good()).click(); - await $(SubmitPage.submit()).click(); - await $(HubPage.submit()).click(); + await click(SubmitPage.submit()); + await click(HubPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Question Title Test"); await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted for"); @@ -47,8 +47,8 @@ describe("Thank You Default View Response Enabled", () => { beforeEach(async () => { await browser.openQuestionnaire("test_thank_you.json"); await $(DidYouKnowPage.yes()).click(); - await $(DidYouKnowPage.submit()).click(); - await $(ThankYouSubmitPage.submit()).click(); + await click(DidYouKnowPage.submit()); + await click(ThankYouSubmitPage.submit()); await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); }); diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index 336612c2ab..7325e97135 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -1,4 +1,5 @@ import { TimeoutModalPage } from "../../base_pages/timeout-modal.page.js"; +import { click } from "../../helpers"; class TestCase { testCaseExpired(page) { @@ -21,7 +22,7 @@ class TestCase { testCaseExtended(page) { it("When the timeout modal is displayed, and I click the “Continue survey” button, Then my session will be extended", async () => { await this.checkTimeoutModal(); - await $(TimeoutModalPage.submit()).click(); + await click(TimeoutModalPage.submit()); await expect(await $(TimeoutModalPage.timer()).getText()).to.equal(""); await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired await browser.refresh(); diff --git a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js index 4dff85da36..714bda47d9 100644 --- a/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_expired/timeout_modal_post_submission_expired.spec.js @@ -2,13 +2,14 @@ import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/time import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; import ThankYouPage from "../../../base_pages/thank-you.page"; import { TimeoutModalTestCase } from "../timeout_modal.js"; +import { click } from "../../../helpers"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { before(async () => { await browser.openQuestionnaire("test_timeout_modal.json"); - await $(TimeoutInterstitialPage.submit()).click(); - await $(TimeoutSubmitPage.submit()).click(); + await click(TimeoutInterstitialPage.submit()); + await click(TimeoutSubmitPage.submit()); }); TimeoutModalTestCase.testCaseExpired(ThankYouPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js index d97b013a12..9c701a7002 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended/timeout_modal_post_submission_extended.spec.js @@ -2,13 +2,14 @@ import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/time import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; import ThankYouPage from "../../../base_pages/thank-you.page"; import { TimeoutModalTestCase } from "../timeout_modal.js"; +import { click } from "../../../helpers"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { before(async () => { await browser.openQuestionnaire("test_timeout_modal.json"); - await $(TimeoutInterstitialPage.submit()).click(); - await $(TimeoutSubmitPage.submit()).click(); + await click(TimeoutInterstitialPage.submit()); + await click(TimeoutSubmitPage.submit()); }); TimeoutModalTestCase.testCaseExtended(ThankYouPage); }); diff --git a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js index 3af53a896d..17bb98c81b 100644 --- a/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js +++ b/tests/functional/spec/timeout/timeout_modal_extended_new_window/timeout_modal_post_submission_extended_new_window.spec.js @@ -2,13 +2,14 @@ import TimeoutInterstitialPage from "../../../generated_pages/timeout_modal/time import TimeoutSubmitPage from "../../../generated_pages/timeout_modal/submit.page"; import ThankYouPage from "../../../base_pages/thank-you.page"; import { TimeoutModalTestCase } from "../timeout_modal.js"; +import { click } from "../../../helpers"; describe("Timeout Modal Post Submission Expired", () => { describe("Given I am completing the survey and get to post submission page,", () => { before(async () => { await browser.openQuestionnaire("test_timeout_modal.json"); - await $(TimeoutInterstitialPage.submit()).click(); - await $(TimeoutSubmitPage.submit()).click(); + await click(TimeoutInterstitialPage.submit()); + await click(TimeoutSubmitPage.submit()); }); TimeoutModalTestCase.testCaseExtendedNewWindow(ThankYouPage); }); From 5c9c6a9b4679659d845fed8972eda2d02f80e8e9 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:59:25 +0100 Subject: [PATCH 272/567] Bug Fix: `get_width_for_number` function when using value sources (#1138) --- app/jinja_filters.py | 36 +++- app/questionnaire/questionnaire_schema.py | 56 ++++- schemas/test/en/test_numbers.json | 191 +++++++++++++++++- tests/app/questionnaire/conftest.py | 11 +- .../test_questionnaire_schema.py | 39 +++- tests/app/test_jinja_filters.py | 67 +++++- tests/functional/spec/numbers.spec.js | 30 ++- tests/functional/spec/save_sign_out.spec.js | 11 + 8 files changed, 415 insertions(+), 26 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index a4ae83b030..a932c71008 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -7,12 +7,15 @@ import flask import flask_babel from babel import numbers, units -from flask import current_app +from flask import current_app, g from jinja2 import nodes, pass_eval_context from markupsafe import Markup, escape from wtforms import SelectFieldBase -from app.questionnaire.questionnaire_schema import is_summary_with_calculation +from app.questionnaire.questionnaire_schema import ( + QuestionnaireSchema, + is_summary_with_calculation, +) from app.questionnaire.rules.utils import parse_datetime from app.settings import MAX_NUMBER @@ -249,17 +252,34 @@ def should_wrap_with_fieldset_processor() -> dict[str, Callable]: return {"should_wrap_with_fieldset": should_wrap_with_fieldset} +def get_min_max_value_width( + min_max: Literal["minimum", "maximum"], answer: AnswerType, default_value: int +) -> int: + """ + This function gets the minimum and maximum value accepted for a question. + Which then allows us to use that value to set the width of the textbox to suit that min and max. + """ + + if ( + answer.get(min_max, {}) + and isinstance(answer[min_max]["value"], Mapping) + and answer[min_max]["value"].get("source") == "answers" + ): + schema: QuestionnaireSchema = g.get("schema") + identifier = answer[min_max]["value"].get("identifier") + return schema.min_and_max_map[identifier][min_max] + + return len(str(answer.get(min_max, {}).get("value", default_value))) + + @blueprint.app_template_filter() def get_width_for_number(answer: AnswerType) -> Optional[int]: allowable_widths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30, 40, 50] - min_value = answer.get("minimum", {}).get("value", 0) - max_value = answer.get("maximum", {}).get("value", MAX_NUMBER) - - min_value_width = len(str(min_value)) - max_value_width = len(str(max_value)) + min_value_width = get_min_max_value_width("minimum", answer, 0) + max_value_width = get_min_max_value_width("maximum", answer, MAX_NUMBER) - width = min_value_width if min_value_width > max_value_width else max_value_width + width = max(min_value_width, max_value_width) width += answer.get("decimal_places", 0) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 85521f1963..d7b842df16 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -3,7 +3,7 @@ from copy import deepcopy from dataclasses import dataclass from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, Sequence, TypeAlias +from typing import Any, Generator, Iterable, Literal, Mapping, Sequence, TypeAlias from flask_babel import force_locale from ordered_set import OrderedSet @@ -13,6 +13,7 @@ from app.forms import error_messages from app.questionnaire.rules.operator import OPERATION_MAPPING from app.questionnaire.schema_utils import get_answers_from_question +from app.settings import MAX_NUMBER from app.utilities.make_immutable import make_immutable from app.utilities.mappings import get_flattened_mapping_values, get_mappings_with_key @@ -34,6 +35,14 @@ TRANSFORMS_REQUIRING_ROUTING_PATH = {"first_non_empty_item"} +NUMERIC_ANSWER_TYPES = { + "Currency", + "Duration", + "Number", + "Percentage", + "Unit", +} + class InvalidSchemaConfigurationException(Exception): pass @@ -90,6 +99,9 @@ def __init__( ] = defaultdict(lambda: defaultdict(set)) self._language_code = language_code self._questionnaire_json = questionnaire_json + self._min_and_max_map: dict[str, dict[str, int]] = defaultdict( + lambda: defaultdict(int) + ) self._list_names_by_list_repeating_block_id: dict[str, str] = {} self._repeating_block_answer_ids: set[str] = set() self.dynamic_answers_parent_block_ids: set[str] = set() @@ -107,6 +119,7 @@ def __init__( self._populate_answer_dependencies() self._populate_when_rules_section_dependencies() self._populate_calculated_summary_section_dependencies() + self._populate_min_max_for_numeric_answers() self._populate_placeholder_transform_section_dependencies() @property @@ -119,6 +132,47 @@ def placeholder_transform_section_dependencies_by_block( def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: return ImmutableDict(self._answer_dependencies_map) + @cached_property + # Type ignore: safe to assume _min_and_max_map return type + def min_and_max_map(self) -> ImmutableDict[str, ImmutableDict[str, int]]: + return make_immutable(self._min_and_max_map) # type: ignore + + def _create_min_max_map( + self, + min_max: Literal["minimum", "maximum"], + answer_id: str, + answers: Iterable[ImmutableDict], + default_min_max: int, + ) -> None: + longest_value_length = 0 + for answer in answers: + value = answer.get(min_max, {}).get("value") + + if isinstance(value, float | int): + value_length = len(str(value)) + + longest_value_length = max(longest_value_length, value_length) + + elif isinstance(value, Mapping) and value: + if value.get("source") == "answers": + longest_value_length = max( + longest_value_length, + self._min_and_max_map[value["identifier"]][min_max], + ) + + self._min_and_max_map[answer_id][min_max] = ( + longest_value_length or default_min_max + ) + + def _populate_min_max_for_numeric_answers(self) -> None: + for answer_id, answers in self._answers_by_id.items(): + # validator ensures all answers will be of the same type so its sufficient to only check the first + if answers[0]["type"] in NUMERIC_ANSWER_TYPES: + self._create_min_max_map("minimum", answer_id, answers, 1) + self._create_min_max_map( + "maximum", answer_id, answers, len(str(MAX_NUMBER)) + ) + @cached_property def when_rules_section_dependencies_by_section( self, diff --git a/schemas/test/en/test_numbers.json b/schemas/test/en/test_numbers.json index 723cad7a2b..3e32776a7a 100644 --- a/schemas/test/en/test_numbers.json +++ b/schemas/test/en/test_numbers.json @@ -319,7 +319,7 @@ "maximum": { "value": { "source": "answers", - "identifier": "set-maximum" + "identifier": "test-range" } } } @@ -340,7 +340,7 @@ "arguments": { "number": { "source": "answers", - "identifier": "set-maximum" + "identifier": "test-range" } } } @@ -355,6 +355,193 @@ "id": "test" } ] + }, + { + "id": "currency-section", + "summary": { "show_on_completion": true }, + "groups": [ + { + "id": "currency-group", + "title": "Section Summary With Variants", + "blocks": [ + { + "type": "Question", + "id": "currency-block", + "question": { + "id": "currency-question", + "type": "General", + "title": "What currency would you like", + "answers": [ + { + "id": "currency-answer", + "type": "Radio", + "mandatory": true, + "options": [ + { + "label": "US Dollars", + "value": "US Dollars" + }, + { + "label": "Sterling", + "value": "Sterling" + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "first-number-block", + "question_variants": [ + { + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer in GBP", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": 1000 + }, + "minimum": { + "value": 1 + } + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "Sterling" + ] + } + }, + { + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer in USD", + "mandatory": true, + "type": "Currency", + "currency": "USD", + "decimal_places": 2, + "maximum": { + "value": 100 + }, + "minimum": { + "value": 10 + } + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "US Dollars" + ] + } + } + ] + }, + { + "type": "Question", + "id": "second-number-block", + "question_variants": [ + { + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer in GBP", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "answers", + "identifier": "first-number-answer" + } + }, + "minimum": { + "value": { + "source": "answers", + "identifier": "first-number-answer" + } + } + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "Sterling" + ] + } + }, + { + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer in USD", + "mandatory": true, + "type": "Currency", + "currency": "USD", + "decimal_places": 2, + "maximum": { + "value": { + "source": "answers", + "identifier": "set-maximum" + } + }, + "minimum": { + "value": 100 + } + } + ] + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "currency-answer" + }, + "US Dollars" + ] + } + } + ] + } + ] + } + ] } ] } diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index bdf08461c7..efb1335f0c 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -123,6 +123,7 @@ def question_variant_schema(): { "id": "answer1", "label": "Answer 1 Variant 1", + "type": "General", } ], }, @@ -183,6 +184,7 @@ def single_question_schema(): { "id": "answer1", "label": "Answer 1", + "type": "General", "default": "test", } ], @@ -241,6 +243,7 @@ def list_collector_variant_schema(): { "id": "answer1", "label": "Collector Answer 1 Variant Yes", + "type": "General", "action": { "type": "RedirectToListAddBlock" }, @@ -816,7 +819,13 @@ def question_schema(): "id": "question1", "title": "A Question", "type": "General", - "answers": [{"id": "answer1", "label": "Answer 1"}], + "answers": [ + { + "id": "answer1", + "label": "Answer 1", + "type": "General", + } + ], }, } ], diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index ac08a92094..5df0784446 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -28,6 +28,25 @@ def test_schema_json_is_immutable_and_hashable(question_schema): assert_all_dict_values_are_hashable(json) +def test_schema_min_max_populate(): + schema = load_schema_from_name("test_numbers") + assert schema.min_and_max_map == { + "set-minimum": {"maximum": 4, "minimum": 5}, + "set-maximum": {"maximum": 5, "minimum": 4}, + "test-range": {"maximum": 5, "minimum": 5}, + "test-range-exclusive": {"maximum": 5, "minimum": 5}, + "test-min": {"maximum": 15, "minimum": 4}, + "test-max": {"maximum": 4, "minimum": 1}, + "test-min-exclusive": {"maximum": 15, "minimum": 3}, + "test-max-exclusive": {"maximum": 4, "minimum": 1}, + "test-percent": {"maximum": 3, "minimum": 1}, + "test-decimal": {"maximum": 5, "minimum": 5}, + "other-answer": {"maximum": 5, "minimum": 1}, + "first-number-answer": {"maximum": 4, "minimum": 2}, + "second-number-answer": {"maximum": 5, "minimum": 3}, + } + + def test_schema_attributes_returns_hashable_values(question_schema): schema = QuestionnaireSchema(question_schema) for section in schema.get_sections(): @@ -635,8 +654,8 @@ def test_answer_dependencies_for_min_max(numbers_schema): }, "set-maximum": { AnswerDependent( - section_id="default-section", - block_id="detail-answer-block", + section_id="currency-section", + block_id="second-number-block", for_list=None, answer_id=None, ), @@ -647,6 +666,22 @@ def test_answer_dependencies_for_min_max(numbers_schema): answer_id=None, ), }, + "test-range": { + AnswerDependent( + section_id="default-section", + block_id="detail-answer-block", + for_list=None, + answer_id=None, + ) + }, + "first-number-answer": { + AnswerDependent( + section_id="currency-section", + block_id="second-number-block", + for_list=None, + answer_id=None, + ) + }, } diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index d6d6685268..024333e93b 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -3,6 +3,7 @@ import pytest import simplejson as json +from flask import g from jinja2 import Undefined from mock import Mock @@ -24,6 +25,8 @@ should_wrap_with_fieldset, strip_tags, ) +from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.utilities.schema import load_schema_from_name @pytest.mark.parametrize( @@ -247,7 +250,7 @@ def test_format_duration(duration, formatted_duration, app): ({"maximum": {"value": 123456}}, 6), ({"maximum": {"value": 12345678901}}, 15), ({"minimum": {"value": -123456}, "maximum": {"value": 1234}}, 7), - ({"decimal_places": 2, "maximum": {"value": 123456}}, 8), + ({"decimal_places": 6, "maximum": {"value": 123456}}, 15), ({"maximum": {"value": 999_999_999_999_999}}, 15), ({"decimal_places": 5, "maximum": {"value": 999_999_999_999_999}}, 20), ({"decimal_places": 6, "maximum": {"value": 999_999_999_999_999}}, 30), @@ -260,8 +263,38 @@ def test_format_duration(duration, formatted_duration, app): ), ), ) -def test_get_width_for_number(answer, width): - assert get_width_for_number(answer) == width +def test_get_width_for_number(answer, width, app): + with app.app_context(): + schema = QuestionnaireSchema({}) + g.schema = schema + assert get_width_for_number(answer) == width + + +@pytest.mark.parametrize( + "answer, width", + ( + ("set-minimum", 7), + ("set-maximum", 7), + ("test-range", 7), + ("test-range-exclusive", 7), + ("test-min", 15), + ("test-max", 4), + ("test-min-exclusive", 15), + ("test-max-exclusive", 4), + ("test-percent", 3), + ("test-decimal", 7), + ("other-answer", 5), + ("first-number-answer", 6), + ("second-number-answer", 6), + ("detail-answer", 15), + ), +) +def test_get_width_for_number_recursive(answer, width, app): + with app.test_request_context(): + schema = load_schema_from_name("test_numbers") + g.schema = schema + answer_to_test = schema.get_answers_by_answer_id(answer)[0] + assert get_width_for_number(answer_to_test) == width @pytest.mark.parametrize( @@ -311,6 +344,34 @@ def test_get_width_for_number(answer, width): }, True, ), + ( + { + "type": "General", + "answers": [ + { + "type": "Currency", + "minimum": { + "value": {"identifier": "set-minimum", "source": "answers"} + }, + } + ], + }, + False, + ), + ( + { + "type": "General", + "answers": [ + { + "type": "Currency", + "maximum": { + "value": {"identifier": "set-maximum", "source": "answers"} + }, + } + ], + }, + False, + ), ), ) def test_should_wrap_with_fieldset(question, expected): diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index 26475781bf..301fa2e59b 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -2,6 +2,10 @@ import SetMinMax from "../generated_pages/numbers/set-min-max-block.page.js"; import TestMinMax from "../generated_pages/numbers/test-min-max-block.page.js"; import DetailAnswer from "../generated_pages/numbers/detail-answer-block.page"; import SubmitPage from "../generated_pages/numbers/submit.page"; +import currencyBlock from "../generated_pages/variants_question/currency-block.page.js"; +import firstNumberBlock from "../generated_pages/variants_question/first-number-block.page.js"; +import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; +import currencySectionSummary from "../generated_pages/variants_question/currency-section-summary.page.js"; import { click } from "../helpers"; describe("Number validation", () => { @@ -40,14 +44,13 @@ describe("Number validation", () => { }); it("When I enter values inside the set range but provide too many decimal places, Then the correct error messages are displayed", async () => { - await $(TestMinMax.testRange()).setValue("1020"); + await $(TestMinMax.testRange()).setValue("12.344"); await $(TestMinMax.testRangeExclusive()).setValue("11"); await $(TestMinMax.testMin()).setValue("123"); await $(TestMinMax.testMax()).setValue("1019"); await $(TestMinMax.testMinExclusive()).setValue("124"); await $(TestMinMax.testMaxExclusive()).setValue("1233"); await $(TestMinMax.testPercent()).setValue("100"); - await $(TestMinMax.testRange()).setValue("12.344"); await $(TestMinMax.testDecimal()).setValue("11.234"); await click(TestMinMax.submit()); @@ -56,28 +59,37 @@ describe("Number validation", () => { }); it("When I enter values inside the set range, Then I should be able to submit the survey", async () => { - await $(TestMinMax.testRange()).setValue("12"); + await $(TestMinMax.testRange()).setValue("1019"); await $(TestMinMax.testDecimal()).setValue("11.23"); - await $(TestMinMax.testPercent()).setValue("99"); await click(TestMinMax.submit()); await $(DetailAnswer.other()).click(); - await $(DetailAnswer.otherDetail()).setValue("1020"); - await click(DetailAnswer.submit()); + await $(DetailAnswer.otherDetail()).setValue("1019"); + await click(TestMinMax.submit()); + await $(currencyBlock.usDollars()).click(); + await click(currencyBlock.submit()); + await $(firstNumberBlock.firstNumber()).setValue("50"); + await click(firstNumberBlock.submit()); + await $(secondNumberBlock.secondNumber()).setValue("321"); + await click(secondNumberBlock.submit()); + await click(currencySectionSummary.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", async () => { await $(SubmitPage.setMaximumEdit()).click(); - await $(SetMinMax.setMaximum()).setValue("1019"); + await $(SetMinMax.setMaximum()).setValue("1018"); await click(SetMinMax.submit()); + await $(TestMinMax.testRange()).setValue("1018"); await click(TestMinMax.submit()); await click(DetailAnswer.submit()); - await expect(await $(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,019"); + await expect(await $(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,018"); - await $(DetailAnswer.otherDetail()).setValue("1019"); + await $(DetailAnswer.otherDetail()).setValue("1001"); await click(DetailAnswer.submit()); + await click(secondNumberBlock.submit()); + await click(currencySectionSummary.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index 98719b301a..a41ff7de45 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -6,6 +6,10 @@ import IntroductionPage from "../generated_pages/introduction/introduction.page" import IntroInterstitialPage from "../generated_pages/introduction/general-business-information-completed.page"; import IntroThankYouPagePage from "../base_pages/thank-you.page"; import HouseHolderConfirmationPage from "../generated_pages/thank_you_census_household/household-confirmation.page"; +import currencyBlock from "../generated_pages/variants_question/currency-block.page.js"; +import firstNumberBlock from "../generated_pages/variants_question/first-number-block.page.js"; +import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; +import currencySectionSummary from "../generated_pages/variants_question/currency-section-summary.page.js"; import { getRandomString } from "../jwt_helper"; import { click } from "../helpers"; describe("Save sign out / Exit", () => { @@ -44,6 +48,13 @@ describe("Save sign out / Exit", () => { await click(TestMinMax.submit()); await $(DetailAnswer.answer1()).click(); await click(DetailAnswer.submit()); + await $(currencyBlock.usDollars()).click(); + await click(currencyBlock.submit()); + await $(firstNumberBlock.firstNumber()).setValue(50); + await click(firstNumberBlock.submit()); + await $(secondNumberBlock.secondNumber()).setValue(321); + await click(secondNumberBlock.submit()); + await click(currencySectionSummary.submit()); await click(SubmitPage.submit()); await expect(await browser.getUrl()).to.contain("thank-you"); From cc5d0ebbc72cfca693b921858ef4fcee42fe08ec Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:45:15 +0100 Subject: [PATCH 273/567] Schemas v3.69.0 (#1195) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index fbcc8f8389..a67ef46698 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.68.0 +v3.69.0 From 5e2843214214944c35552ed1d79b053e48cf3a5a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 30 Aug 2023 16:17:29 +0100 Subject: [PATCH 274/567] Add list collector content pages (#1156) --- app/forms/questionnaire_form.py | 2 +- app/jinja_filters.py | 6 +- app/questionnaire/questionnaire_schema.py | 63 +- app/questionnaire/router.py | 6 +- app/views/contexts/list_context.py | 2 +- app/views/contexts/section_summary_context.py | 8 +- app/views/contexts/summary/group.py | 38 +- .../summary/list_collector_base_block.py | 204 +++++++ .../contexts/summary/list_collector_block.py | 199 +------ .../summary/list_collector_content_block.py | 52 ++ app/views/handlers/block.py | 2 +- app/views/handlers/block_factory.py | 2 + app/views/handlers/list_action.py | 2 +- app/views/handlers/list_collector.py | 31 +- app/views/handlers/list_collector_content.py | 15 + app/views/handlers/list_repeating_question.py | 22 +- app/views/handlers/question.py | 11 +- .../en/test_list_collector_content_page.json | 550 ++++++++++++++++++ templates/listcollectorcontent.html | 25 + templates/partials/block.html | 1 + tests/app/test_jinja_filters.py | 4 +- tests/functional/generate_pages.py | 18 +- .../spec/list_collector_content.spec.js | 142 +++++ ...st_questionnaire_list_collector_content.py | 178 ++++++ 24 files changed, 1319 insertions(+), 264 deletions(-) create mode 100644 app/views/contexts/summary/list_collector_base_block.py create mode 100644 app/views/contexts/summary/list_collector_content_block.py create mode 100644 app/views/handlers/list_collector_content.py create mode 100644 schemas/test/en/test_list_collector_content_page.json create mode 100644 templates/listcollectorcontent.html create mode 100644 tests/functional/spec/list_collector_content.spec.js create mode 100644 tests/integration/questionnaire/test_questionnaire_list_collector_content.py diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index a60548c61f..63f2780376 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -545,7 +545,7 @@ def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResol question_title = question.get("title") value_source_resolved_for_location = _get_value_source_resolver(list_item_id) - for answer in question["answers"]: + for answer in question.get("answers", []): if "list_item_id" in answer: value_source_resolver = _get_value_source_resolver(answer["list_item_id"]) else: diff --git a/app/jinja_filters.py b/app/jinja_filters.py index a932c71008..3f19b4d77f 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -628,6 +628,7 @@ def map_summary_item_config( else: list_collector_rows = map_list_collector_config( list_items=block["list"]["list_items"], + editable=block["list"]["editable"], edit_link_text=edit_link_text, edit_link_aria_label=edit_link_aria_label, remove_link_text=remove_link_text, @@ -654,6 +655,7 @@ def map_summary_item_config_processor() -> dict[str, Callable]: @blueprint.app_template_filter() # type: ignore def map_list_collector_config( list_items: list[dict[str, str | int]], + editable: bool = True, render_icon: bool = False, edit_link_text: str = "", edit_link_aria_label: str = "", @@ -672,7 +674,7 @@ def map_list_collector_config( edit_link_aria_label_text = None remove_link_aria_label_text = None - if edit_link_text: + if edit_link_text and editable: url = ( f'{list_item.get("edit_link")}{item_anchor}' if item_anchor @@ -694,7 +696,7 @@ def map_list_collector_config( actions.append(edit_link) - if not list_item.get("primary_person") and remove_link_text: + if not list_item.get("primary_person") and remove_link_text and editable: if remove_link_aria_label: remove_link_aria_label_text = remove_link_aria_label.format( item_name=item_name diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index d7b842df16..d60687af0c 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -19,6 +19,8 @@ DEFAULT_LANGUAGE_CODE = "en" +LIST_COLLECTORS_WITH_REPEATING_BLOCKS = {"ListCollector", "ListCollectorContent"} + LIST_COLLECTOR_CHILDREN = [ "ListAddQuestion", "ListEditQuestion", @@ -354,11 +356,12 @@ def _get_blocks_by_id(self) -> dict[str, ImmutableDict]: self._parent_id_map[block_id] = group["id"] blocks[block_id] = block - if block["type"] in ( + if block["type"] in { "ListCollector", + "ListCollectorContent", "PrimaryPersonListCollector", "RelationshipCollector", - ): + }: self._list_collector_section_ids_by_list_name[ block["for_list"] ].append(self._parent_id_map[group["id"]]) @@ -619,25 +622,26 @@ def _update_answer_dependencies_for_list_source( ) for list_collector in list_collectors: - add_block_question = self.get_add_block_for_list_collector( - list_collector["id"] # type: ignore - )["question"] - answer_ids_for_block = list( - self.get_answers_for_question_by_id(add_block_question) - ) - for block_answer_id in answer_ids_for_block: - self._answer_dependencies_map[block_answer_id] |= { - self._get_answer_dependent_for_block_id( - block_id=block_id, for_list=list_name - ) - if self.is_block_in_repeating_section(block_id) - # non-repeating blocks such as dynamic-answers could depend on the list - else self._get_answer_dependent_for_block_id(block_id=block_id) - } - self._list_dependent_block_additional_dependencies[block_id] = set( - answer_ids_for_block - ) - # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies + if add_block := self.get_add_block_for_list_collector( # type: ignore + list_collector["id"] + ): + add_block_question = add_block["question"] + answer_ids_for_block = list( + self.get_answers_for_question_by_id(add_block_question) + ) + for block_answer_id in answer_ids_for_block: + self._answer_dependencies_map[block_answer_id] |= { + self._get_answer_dependent_for_block_id( + block_id=block_id, for_list=list_name + ) + if self.is_block_in_repeating_section(block_id) + # non-repeating blocks such as dynamic-answers could depend on the list + else self._get_answer_dependent_for_block_id(block_id=block_id) + } + self._list_dependent_block_additional_dependencies[block_id] = set( + answer_ids_for_block + ) + # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies if remove_block_id := self.get_remove_block_id_for_list(list_name): self._list_dependent_block_additional_dependencies[block_id].update( self.get_answer_ids_for_block(remove_block_id) @@ -759,7 +763,10 @@ def get_driving_question_for_list( def get_remove_block_id_for_list(self, list_name: str) -> str | None: for block in self.get_blocks(): - if block["type"] == "ListCollector" and block["for_list"] == list_name: + if ( + is_list_collector_block_editable(block) + and block["for_list"] == list_name + ): remove_block_id: str = block["remove_block"]["id"] return remove_block_id @@ -970,13 +977,15 @@ def get_list_collectors_for_list_for_sections( for section_id in sections: if section := self.get_section(section_id): collector_type = ( - "PrimaryPersonListCollector" if primary else "ListCollector" + {"PrimaryPersonListCollector"} + if primary + else LIST_COLLECTORS_WITH_REPEATING_BLOCKS ) blocks.extend( block for block in self.get_blocks_for_section(section) - if block["type"] == collector_type and block["for_list"] == for_list + if block["type"] in collector_type and block["for_list"] == for_list ) return blocks @@ -1171,7 +1180,7 @@ def _block_for_answer(self, answer_id: str) -> ImmutableDict | None: if ( parent_block - and parent_block["type"] == "ListCollector" + and parent_block["type"] in LIST_COLLECTORS_WITH_REPEATING_BLOCKS and block_id not in self.list_collector_repeating_block_ids ): return parent_block @@ -1530,3 +1539,7 @@ def get_calculation_block_ids_for_grand_calculated_summary( calculation_block=grand_calculated_summary_block, source_type="calculated_summary", ) + + +def is_list_collector_block_editable(block: Mapping) -> bool: + return bool(block["type"] == "ListCollector") diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 0170131fc7..366bcdb69e 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -532,9 +532,9 @@ def _get_enabled_section_keys( self, ) -> Generator[SectionKey, None, None]: for section_id in self.enabled_section_ids: - repeating_list = self._schema.get_repeating_list_for_section(section_id) - - if repeating_list: + if repeating_list := self._schema.get_repeating_list_for_section( + section_id + ): for list_item_id in self._list_store[repeating_list]: section_key = SectionKey(section_id, list_item_id) yield section_key diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index 937d577f39..8894a0ac1f 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -42,7 +42,7 @@ def __call__( "list": { "list_items": list_items, "editable": any([edit_block_id, remove_block_id]), - } + }, } # pylint: disable=too-many-locals diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 86c3e05a6c..1c2f4254fd 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -9,12 +9,12 @@ ProgressStore, SupplementaryDataStore, ) -from app.questionnaire import QuestionnaireSchema +from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire.questionnaire_schema import LIST_COLLECTORS_WITH_REPEATING_BLOCKS from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content from ...data_models.metadata_proxy import MetadataProxy -from ...utilities.types import LocationType from .context import Context from .summary import Group from .summary.list_collector_block import ListCollectorBlock @@ -31,7 +31,7 @@ def __init__( metadata: Optional[MetadataProxy], response_metadata: MutableMapping, routing_path: RoutingPath, - current_location: LocationType, + current_location: Location, supplementary_data_store: SupplementaryDataStore, ) -> None: super().__init__( @@ -212,7 +212,7 @@ def _get_refactored_groups(original_groups: dict) -> list[dict[str, Any]]: non_list_collector_blocks: list[dict[str, str]] = [] list_collector_blocks: list[dict[str, str]] = [] for block in group["blocks"]: - if block["type"] == "ListCollector": + if block["type"] in LIST_COLLECTORS_WITH_REPEATING_BLOCKS: # if list collector block encountered, close the previously started non list collector blocks list if exists if non_list_collector_blocks: previously_started_group = { diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 910faf920a..131692647e 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,4 +1,4 @@ -from typing import Iterable, Mapping, MutableMapping +from typing import Iterable, Mapping, MutableMapping, Type from werkzeug.datastructures import ImmutableDict @@ -11,11 +11,18 @@ from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.questionnaire.questionnaire_schema import ( + LIST_COLLECTORS_WITH_REPEATING_BLOCKS, + is_list_collector_block_editable, +) from app.survey_config.link import Link from app.utilities.types import LocationType from app.views.contexts.summary.block import Block from app.views.contexts.summary.calculated_summary_block import CalculatedSummaryBlock from app.views.contexts.summary.list_collector_block import ListCollectorBlock +from app.views.contexts.summary.list_collector_content_block import ( + ListCollectorContentBlock, +) class Group: @@ -107,7 +114,18 @@ def _build_blocks_and_links( if parent_list_collector_block_id not in routing_path_block_ids: continue - list_collector_block = ListCollectorBlock( + list_collector_block_class: Type[ + ListCollectorBlock | ListCollectorContentBlock + ] = ( + ListCollectorBlock + if is_list_collector_block_editable( + # Type ignore: return types differ + schema.get_block(parent_list_collector_block_id) # type: ignore + ) + else ListCollectorContentBlock + ) + + list_collector_block = list_collector_block_class( routing_path_block_ids=routing_path_block_ids, answer_store=answer_store, list_store=list_store, @@ -174,7 +192,7 @@ def _build_blocks_and_links( ] ) - elif block["type"] == "ListCollector": + elif block["type"] in LIST_COLLECTORS_WITH_REPEATING_BLOCKS: section: ImmutableDict | None = schema.get_section(location.section_id) summary_item: ImmutableDict | None @@ -183,7 +201,12 @@ def _build_blocks_and_links( section_id=section["id"], # type: ignore list_name=block["for_list"], ): - list_collector_block = ListCollectorBlock( + list_collector_block_class = ( + ListCollectorBlock + if is_list_collector_block_editable(block) + else ListCollectorContentBlock + ) + list_collector_block = list_collector_block_class( routing_path_block_ids=routing_path_block_ids, answer_store=answer_store, list_store=list_store, @@ -195,14 +218,17 @@ def _build_blocks_and_links( language=language, return_to=return_to, supplementary_data_store=supplementary_data_store, + return_to_block_id=return_to_block_id, ) - list_summary_element = list_collector_block.list_summary_element( summary_item ) blocks.extend([list_summary_element]) - if not view_submitted_response: + if ( + not view_submitted_response + and is_list_collector_block_editable(block) + ): self.links["add_link"] = Link( target="_self", text=list_summary_element["add_link_text"], diff --git a/app/views/contexts/summary/list_collector_base_block.py b/app/views/contexts/summary/list_collector_base_block.py new file mode 100644 index 0000000000..6d4c39967c --- /dev/null +++ b/app/views/contexts/summary/list_collector_base_block.py @@ -0,0 +1,204 @@ +from collections import defaultdict +from typing import Iterable, Mapping, MutableMapping, Sequence + +from werkzeug.datastructures import ImmutableDict + +from app.data_models import AnswerStore, ProgressStore, SupplementaryDataStore +from app.data_models.list_store import ListModel, ListStore +from app.data_models.metadata_proxy import MetadataProxy +from app.questionnaire import Location, QuestionnaireSchema +from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.questionnaire.questionnaire_schema import is_list_collector_block_editable +from app.utilities.types import LocationType +from app.views.contexts import list_context +from app.views.contexts.summary.block import Block + + +class ListCollectorBaseBlock: + def __init__( + self, + *, + routing_path_block_ids: Iterable[str], + answer_store: AnswerStore, + list_store: ListStore, + progress_store: ProgressStore, + metadata: MetadataProxy | None, + response_metadata: MutableMapping, + schema: QuestionnaireSchema, + location: LocationType, + language: str, + supplementary_data_store: SupplementaryDataStore, + return_to: str | None, + return_to_block_id: str | None = None, + ) -> None: + self._location = location + self._placeholder_renderer = PlaceholderRenderer( + language=language, + answer_store=answer_store, + list_store=list_store, + metadata=metadata, + response_metadata=response_metadata, + schema=schema, + progress_store=progress_store, + location=location, + supplementary_data_store=supplementary_data_store, + ) + self._list_store = list_store + self._schema = schema + self._location = location + # type ignore added as section should exist + self._section: ImmutableDict = self._schema.get_section(self._location.section_id) # type: ignore + self._language = language + self._answer_store = answer_store + self._metadata = metadata + self._response_metadata = response_metadata + self._routing_path_block_ids = routing_path_block_ids + self._progress_store = progress_store + self._supplementary_data_store = supplementary_data_store + self._return_to = return_to + self._return_to_block_id = return_to_block_id + + @property + def list_context(self) -> list_context.ListContext: + return list_context.ListContext( + self._language, + self._schema, + self._answer_store, + self._list_store, + self._progress_store, + self._metadata, + self._response_metadata, + self._supplementary_data_store, + ) + + def _list_collector_block_on_path(self, for_list: str) -> list[ImmutableDict]: + list_collector_blocks = list( + self._schema.get_list_collectors_for_list_for_sections( + [self._section["id"]], for_list=for_list + ) + ) + + return [ + list_collector_block + for list_collector_block in list_collector_blocks + if list_collector_block["id"] in self._routing_path_block_ids + ] + + def _list_collector_block( + self, for_list: str, list_collector_blocks_on_path: list[ImmutableDict] + ) -> ImmutableDict: + list_collector_blocks = list( + self._schema.get_list_collectors_for_list_for_sections( + [self._section["id"]], for_list=for_list + ) + ) + return ( + list_collector_blocks_on_path[0] + if list_collector_blocks_on_path + else list_collector_blocks[0] + ) + + def _get_related_answer_blocks_by_list_item_id( + self, *, list_model: ListModel, repeating_blocks: Sequence[ImmutableDict] + ) -> dict[str, list[dict]] | None: + section_id = self._section["id"] + + related_answers = self._schema.get_related_answers_for_list_for_section( + section_id=section_id, list_name=list_model.name + ) + + blocks: list[dict | ImmutableDict] = [] + + if related_answers: + blocks += self._get_blocks_for_related_answers(related_answers) + + if len(list_model): + blocks += repeating_blocks + + if not blocks: + return None + + related_answers_blocks = {} + + for list_id in list_model: + serialized_blocks = [ + # related answers for repeating blocks may use placeholders, so each block needs rendering here + self._placeholder_renderer.render( + data_to_render=Block( + block, + answer_store=self._answer_store, + list_store=self._list_store, + metadata=self._metadata, + response_metadata=self._response_metadata, + schema=self._schema, + location=Location( + list_name=list_model.name, + list_item_id=list_id, + section_id=section_id, + ), + return_to=self._return_to, + return_to_block_id=self._return_to_block_id, + progress_store=self._progress_store, + supplementary_data_store=self._supplementary_data_store, + language=self._language, + ).serialize(), + list_item_id=list_id, + ) + for block in blocks + ] + + related_answers_blocks[list_id] = serialized_blocks + + return related_answers_blocks + + def _get_blocks_for_related_answers(self, related_answers: tuple) -> list[dict]: + blocks = [] + answers_by_block = defaultdict(list) + + for answer in related_answers: + answer_id = answer["identifier"] + # block is not optional at this point + block: Mapping = self._schema.get_block_for_answer_id(answer_id) # type: ignore + + block_to_keep = ( + block["edit_block"] + if is_list_collector_block_editable(block) + else block + ) + answers_by_block[block_to_keep].append(answer_id) + + for immutable_block, answer_ids in answers_by_block.items(): + mutable_block = self._schema.get_mutable_deepcopy(immutable_block) + + # We need to filter out answers for both variants and normal questions + for variant_or_block in mutable_block.get( + "question_variants", [mutable_block] + ): + answers = [ + answer + for answer in variant_or_block["question"].get("answers", {}) + if answer["id"] in answer_ids + ] + # Mutate the answers to only keep the related answers + variant_or_block["question"]["answers"] = answers + + blocks.append(mutable_block) + + return blocks + + def get_repeating_block_related_answer_blocks( + self, block: ImmutableDict + ) -> list[dict]: + """ + Given a repeating block question to render, + return the list of rendered question blocks for each list item id + """ + list_name = self._schema.list_names_by_list_repeating_block_id[block["id"]] + list_model = self._list_store[list_name] + blocks: list[dict] = [] + if answer_blocks_by_list_item_id := self._get_related_answer_blocks_by_list_item_id( + list_model=list_model, repeating_blocks=[block] + ): + for answer_blocks in answer_blocks_by_list_item_id.values(): + blocks.extend(answer_blocks) + return blocks diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index b6b510bd5c..04b4ccdd08 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -1,62 +1,11 @@ -from collections import defaultdict -from typing import Iterable, Mapping, MutableMapping, Sequence +from typing import Mapping from flask import url_for -from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ProgressStore, SupplementaryDataStore -from app.data_models.list_store import ListModel, ListStore -from app.data_models.metadata_proxy import MetadataProxy -from app.questionnaire import Location, QuestionnaireSchema -from app.questionnaire.placeholder_renderer import PlaceholderRenderer -from app.utilities.types import LocationType -from app.views.contexts.list_context import ListContext -from app.views.contexts.summary.block import Block +from app.views.contexts.summary.list_collector_base_block import ListCollectorBaseBlock -class ListCollectorBlock: - def __init__( - self, - routing_path_block_ids: Iterable[str], - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - schema: QuestionnaireSchema, - location: LocationType, - language: str, - supplementary_data_store: SupplementaryDataStore, - return_to: str | None, - return_to_block_id: str | None = None, - ) -> None: - self._location = location - self._placeholder_renderer = PlaceholderRenderer( - language=language, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, - schema=schema, - progress_store=progress_store, - location=location, - supplementary_data_store=supplementary_data_store, - ) - self._list_store = list_store - self._schema = schema - self._location = location - # type ignore added as section should exist - self._section: ImmutableDict = self._schema.get_section(self._location.section_id) # type: ignore - self._language = language - self._answer_store = answer_store - self._metadata = metadata - self._response_metadata = response_metadata - self._routing_path_block_ids = routing_path_block_ids - self._progress_store = progress_store - self._supplementary_data_store = supplementary_data_store - self._return_to = return_to - self._return_to_block_id = return_to_block_id - +class ListCollectorBlock(ListCollectorBaseBlock): # pylint: disable=too-many-locals def list_summary_element(self, summary: Mapping) -> dict: list_collector_block = None @@ -70,24 +19,14 @@ def list_summary_element(self, summary: Mapping) -> dict: ) = (None, None, None, None, None, None) list_model = self._list_store[summary["for_list"]] - list_collector_blocks = list( - self._schema.get_list_collectors_for_list( - for_list=summary["for_list"], section_id=self._section["id"] - ) - ) - add_link = self._add_link(summary, list_collector_block) - list_collector_blocks_on_path = [ - list_collector_block - for list_collector_block in list_collector_blocks - if list_collector_block["id"] in self._routing_path_block_ids - ] + list_collector_blocks_on_path = self._list_collector_block_on_path( + summary["for_list"] + ) - list_collector_block = ( - list_collector_blocks_on_path[0] - if list_collector_blocks_on_path - else list_collector_blocks[0] + list_collector_block = self._list_collector_block( + summary["for_list"], list_collector_blocks_on_path ) rendered_summary = self._placeholder_renderer.render( @@ -107,8 +46,10 @@ def list_summary_element(self, summary: Mapping) -> dict: item_label = self._schema.get_item_label(section_id, list_model.name) if len(list_model) == 1 and list_model.primary_person: - if primary_list_collectors := self._schema.get_list_collectors_for_list( - for_list=summary["for_list"], primary=True + if primary_list_collectors := self._schema.get_list_collectors_for_list_for_sections( + sections=[self._section["id"]], + for_list=summary["for_list"], + primary=True, ): for primary_person_block in primary_list_collectors: primary_person_edit_block_id = edit_block_id = primary_person_block[ @@ -139,36 +80,6 @@ def list_summary_element(self, summary: Mapping) -> dict: **list_summary_context, } - def get_repeating_block_related_answer_blocks( - self, block: ImmutableDict - ) -> list[dict]: - """ - Given a repeating block question to render, - return the list of rendered question blocks for each list item id - """ - list_name = self._schema.list_names_by_list_repeating_block_id[block["id"]] - list_model = self._list_store[list_name] - blocks: list[dict] = [] - if answer_blocks_by_list_item_id := self._get_related_answer_blocks_by_list_item_id( - list_model=list_model, repeating_blocks=[block] - ): - for answer_blocks in answer_blocks_by_list_item_id.values(): - blocks.extend(answer_blocks) - return blocks - - @property - def list_context(self) -> ListContext: - return ListContext( - self._language, - self._schema, - self._answer_store, - self._list_store, - self._progress_store, - self._metadata, - self._response_metadata, - self._supplementary_data_store, - ) - def _add_link( self, summary: Mapping, @@ -190,89 +101,3 @@ def _add_link( block_id=driving_question_block["id"], return_to=self._return_to, ) - - def _get_related_answer_blocks_by_list_item_id( - self, *, list_model: ListModel, repeating_blocks: Sequence[ImmutableDict] - ) -> dict[str, list[dict]] | None: - section_id = self._section["id"] - - related_answers = self._schema.get_related_answers_for_list_for_section( - section_id=section_id, list_name=list_model.name - ) - - blocks: list[dict | ImmutableDict] = [] - - if related_answers: - blocks += self._get_blocks_for_related_answers(related_answers) - - if len(list_model): - blocks += repeating_blocks - - if not blocks: - return None - - related_answers_blocks = {} - - for list_id in list_model: - serialized_blocks = [ - # related answers for repeating blocks may use placeholders, so each block needs rendering here - self._placeholder_renderer.render( - data_to_render=Block( - block, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - schema=self._schema, - location=Location( - list_name=list_model.name, - list_item_id=list_id, - section_id=section_id, - ), - return_to=self._return_to, - return_to_block_id=self._return_to_block_id, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, - language=self._language, - ).serialize(), - list_item_id=list_id, - ) - for block in blocks - ] - - related_answers_blocks[list_id] = serialized_blocks - - return related_answers_blocks - - def _get_blocks_for_related_answers(self, related_answers: tuple) -> list[dict]: - blocks = [] - answers_by_block = defaultdict(list) - - for answer in related_answers: - answer_id = answer["identifier"] - # block is not optional at this point - block: Mapping = self._schema.get_block_for_answer_id(answer_id) # type: ignore - - block_to_keep = ( - block["edit_block"] if block["type"] == "ListCollector" else block - ) - answers_by_block[block_to_keep].append(answer_id) - - for immutable_block, answer_ids in answers_by_block.items(): - mutable_block = self._schema.get_mutable_deepcopy(immutable_block) - - # We need to filter out answers for both variants and normal questions - for variant_or_block in mutable_block.get( - "question_variants", [mutable_block] - ): - answers = [ - answer - for answer in variant_or_block["question"].get("answers", {}) - if answer["id"] in answer_ids - ] - # Mutate the answers to only keep the related answers - variant_or_block["question"]["answers"] = answers - - blocks.append(mutable_block) - - return blocks diff --git a/app/views/contexts/summary/list_collector_content_block.py b/app/views/contexts/summary/list_collector_content_block.py new file mode 100644 index 0000000000..b0e0ff5a8e --- /dev/null +++ b/app/views/contexts/summary/list_collector_content_block.py @@ -0,0 +1,52 @@ +from typing import Any, Mapping + +from app.views.contexts.summary.list_collector_base_block import ListCollectorBaseBlock + + +class ListCollectorContentBlock(ListCollectorBaseBlock): + # pylint: disable=too-many-locals + def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: + related_answers = None + + item_label = None + + current_list = self._list_store[summary["for_list"]] + + list_collector_blocks_on_path = self._list_collector_block_on_path( + summary["for_list"] + ) + + list_collector_block = self._list_collector_block( + summary["for_list"], list_collector_blocks_on_path + ) + + rendered_summary = self._placeholder_renderer.render( + data_to_render=summary, list_item_id=self._location.list_item_id + ) + + if list_collector_blocks_on_path: + repeating_blocks = list_collector_block.get("repeating_blocks", []) + related_answers = self._get_related_answer_blocks_by_list_item_id( + list_model=current_list, repeating_blocks=repeating_blocks + ) + item_label = self._schema.get_item_label( + self._section["id"], current_list.name + ) + + list_summary_context = self.list_context( + list_collector_block["summary"], + for_list=list_collector_block["for_list"], + section_id=self._location.section_id, + has_repeating_blocks=bool(list_collector_block.get("repeating_blocks")), + return_to=self._return_to, + ) + + return { + "title": rendered_summary["title"], + "type": rendered_summary["type"], + "empty_list_text": rendered_summary.get("empty_list_text"), + "list_name": rendered_summary["for_list"], + "related_answers": related_answers, + "item_label": item_label, + **list_summary_context, + } diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 586fc26c97..bf7793beec 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -25,7 +25,7 @@ def __init__( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, language: str, - current_location: LocationType, + current_location: Location, request_args: MutableMapping, form_data: ImmutableMultiDict, ): diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 440933048c..2fb7816a7c 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -13,6 +13,7 @@ from app.views.handlers.content import Content from app.views.handlers.list_add_question import ListAddQuestion from app.views.handlers.list_collector import ListCollector +from app.views.handlers.list_collector_content import ListCollectorContent from app.views.handlers.list_edit_question import ListEditQuestion from app.views.handlers.list_remove_question import ListRemoveQuestion from app.views.handlers.list_repeating_question import ListRepeatingQuestion @@ -26,6 +27,7 @@ "ConfirmationQuestion": Question, "ListCollectorDrivingQuestion": Question, "ListCollector": ListCollector, + "ListCollectorContent": ListCollectorContent, "ListAddQuestion": ListAddQuestion, "ListEditQuestion": ListEditQuestion, "ListRemoveQuestion": ListRemoveQuestion, diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index f7db203cad..69066c1fa7 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -68,7 +68,7 @@ def get_next_location_url(self) -> str: return url if self.router.is_block_complete( - # Type ignore: the parent_location property above is initialised with a block_id so it won't be None + # Type ignore: block_id would exist at this point block_id=self.parent_location.block_id, # type: ignore section_id=self.parent_location.section_id, list_item_id=self.parent_location.list_item_id, diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 384693e7b2..5ec262621a 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -52,8 +52,7 @@ def get_next_location_url(self) -> str: return super().get_next_location_url() - def get_context(self) -> dict[str, dict]: - question_context = super().get_context() + def _get_list_context(self) -> dict[str, dict]: list_context = ListContext( self._language, self._schema, @@ -65,18 +64,22 @@ def get_context(self) -> dict[str, dict]: self._questionnaire_store.supplementary_data_store, ) - return { - **question_context, - **list_context( - self.rendered_block["summary"], - for_list=self.list_name, - edit_block_id=self.rendered_block["edit_block"]["id"], - remove_block_id=self.rendered_block["remove_block"]["id"], - return_to=self._return_to, - section_id=self.current_location.section_id, - has_repeating_blocks=bool(self.repeating_block_ids), - ), - } + return list_context( + self.rendered_block["summary"], + for_list=self.list_name, + edit_block_id=self.rendered_block.get("edit_block", {}).get("id"), + remove_block_id=self.rendered_block.get("remove_block", {}).get("id"), + return_to=self._return_to, + section_id=self.current_location.section_id, + has_repeating_blocks=bool(self.repeating_block_ids), + ) + + def _get_additional_view_context(self) -> dict: + """This is only needed so we can use it in List Collector Content class where we override the default behaviour of the Question class""" + return super().get_context() + + def get_context(self) -> dict: + return {**self._get_additional_view_context(), **self._get_list_context()} def handle_post(self) -> None: answer_action = self._get_answer_action() diff --git a/app/views/handlers/list_collector_content.py b/app/views/handlers/list_collector_content.py new file mode 100644 index 0000000000..676e25e3e8 --- /dev/null +++ b/app/views/handlers/list_collector_content.py @@ -0,0 +1,15 @@ +from app.views.handlers.list_collector import ListCollector +from app.views.handlers.question import Question + + +class ListCollectorContent(ListCollector): + def _get_additional_view_context(self) -> dict: + return self.rendered_block.get("content", {}) + + def handle_post(self) -> None: + if self._is_list_collector_complete(): + self._routing_path = self.router.routing_path( + section_id=self._current_location.section_id, + list_item_id=self._current_location.list_item_id, + ) + return super(Question, self).handle_post() diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py index 29287ca6e1..1dec1bb3ab 100644 --- a/app/views/handlers/list_repeating_question.py +++ b/app/views/handlers/list_repeating_question.py @@ -1,5 +1,4 @@ from flask import url_for -from werkzeug.datastructures import ImmutableDict from app.views.handlers.list_edit_question import ListEditQuestion @@ -43,15 +42,20 @@ def get_previous_location_url(self) -> str: return_to_block_id=self._return_to_block_id, ) - # Type ignore: edit_block will exist at this point - edit_block: ImmutableDict = self._schema.get_edit_block_for_list_collector( # type: ignore + if edit_block := self._schema.get_edit_block_for_list_collector( self.parent_block["id"] - ) - return url_for( - "questionnaire.block", - list_name=self.current_location.list_name, - list_item_id=self.current_location.list_item_id, - block_id=edit_block["id"], + ): + return url_for( + "questionnaire.block", + list_name=self.current_location.list_name, + list_item_id=self.current_location.list_item_id, + block_id=edit_block["id"], + return_to=self._return_to, + return_to_answer_id=self._return_to_answer_id, + return_to_block_id=self._return_to_block_id, + ) + + return self.parent_location.url( return_to=self._return_to, return_to_answer_id=self._return_to_answer_id, return_to_block_id=self._return_to_block_id, diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 2e9ef2b780..f087e90bf5 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -24,7 +24,7 @@ def _has_redirect_to_list_add_action(answer_action: Mapping | None) -> bool: @cached_property def form(self) -> QuestionnaireForm: - question_json = self.rendered_block["question"] + question_json = self.rendered_block.get("question", {}) if self._form_data: return generate_form( @@ -82,6 +82,11 @@ def rendered_block(self) -> dict: ) self._set_page_title(page_title) + + # We inherit from question in list collector content block which doesn't have "question" sub-block + if not transformed_block.get("question"): + return transformed_block + rendered_question = self.placeholder_renderer.render( data_to_render=transformed_block["question"], list_item_id=self._current_location.list_item_id, @@ -162,6 +167,10 @@ def _is_list_just_primary(self, list_items: list[str], list_name: str) -> bool: ) def _get_answer_action(self) -> dict | None: + # When used by list collector content class rendered block we won't have "question" sub-block + if not self.rendered_block.get("question"): + return None + answers = self.rendered_block["question"]["answers"] for answer in answers: diff --git a/schemas/test/en/test_list_collector_content_page.json b/schemas/test/en/test_list_collector_content_page.json new file mode 100644 index 0000000000..d31345c3ef --- /dev/null +++ b/schemas/test/en/test_list_collector_content_page.json @@ -0,0 +1,550 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test List Collector Section Summary Items", + "theme": "default", + "description": "A questionnaire to test list collector section summary items", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["section-companies"] + } + }, + "post_submission": { + "view_response": true + }, + "sections": [ + { + "id": "section-companies", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "related_answers": [ + { + "source": "answers", + "identifier": "registration-number" + }, + { + "source": "answers", + "identifier": "authorised-insurer-radio" + } + ] + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-companies", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", + "question": { + "type": "General", + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-companies-or-branches-answer" + }, + "No" + ] + }, + "block": "confirmation-checkbox" + }, + { + "block": "any-other-companies-or-branches" + } + ] + }, + { + "id": "any-other-companies-or-branches", + "type": "ListCollector", + "for_list": "companies", + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-company", + "type": "ListAddQuestion", + "question": { + "id": "add-question-companies", + "type": "General", + "title": "Give details about the company or branch that undertakes general insurance business", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "Registration number", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-company", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch", + "mandatory": true, + "type": "TextField" + }, + { + "id": "registration-number", + "label": "Registration number", + "mandatory": true, + "type": "Number" + }, + { + "type": "Radio", + "label": "Is this UK company or branch an authorised insurer?", + "id": "authorised-insurer-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "remove_block": { + "id": "remove-company", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + } + }, + { + "type": "Question", + "id": "confirmation-checkbox", + "question": { + "answers": [ + { + "id": "confirmation-checkbox-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ], + "type": "Radio" + } + ], + "id": "confirmation-checkbox-question", + "title": "Are all companies or branches based in UK?", + "type": "General" + }, + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "companies" + } + ] + }, + 3 + ] + } + } + } + ] + } + ] + }, + { + "id": "section-list-collector-contents", + "title": "List Collector Contents", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_label": "Name of UK company or branch" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-list-collector-contents", + "title": "Companies", + "blocks": [ + { + "type": "Question", + "id": "responsible-party", + "question": { + "type": "General", + "id": "responsible-party-question", + "title": "Are you the responsible party for reporting trading details for a company of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "list-collector-content", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "id": "list-collector-content", + "type": "ListCollectorContent", + "page_title": "Companies", + "for_list": "companies", + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + }, + "content": { + "title": "Companies", + "contents": [ + { + "guidance": { + "contents": [ + { + "description": "Include all companies" + } + ] + } + }, + { + "definition": { + "title": "Companies definition", + "contents": [ + { + "description": "Legal entities formed by a group of individuals to engage in and operate a business enterprise in a commercial or industrial capacity." + } + ] + } + }, + { + "description": "You have previously reported the following companies. Press continue to updated registration and trading information." + } + ] + }, + "repeating_blocks": [ + { + "id": "companies-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-number-repeating-block", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-date-repeating-block", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "companies-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {company_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-date-repeating-block" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this company been trading in the UK? (Mandatory)", + "id": "authorised-trader-uk-radio-repeating-block", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this company been trading in the EU? (Not mandatory)", + "id": "authorised-trader-eu-radio-repeating-block", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ] + } + ] + } + ] + } + ] +} diff --git a/templates/listcollectorcontent.html b/templates/listcollectorcontent.html new file mode 100644 index 0000000000..214f6a0bb1 --- /dev/null +++ b/templates/listcollectorcontent.html @@ -0,0 +1,25 @@ +{% extends 'layouts/_questionnaire.html' %} +{% import 'macros/helpers.html' as helpers %} + +{% set save_on_signout = true %} + +{% set continue_button_text = _("Continue") %} + +{% set title = content.title %} +{% set contents = content.contents %} + +{% block form_content %} + +

      {{ title }}

      + + {%- if content.list and content.list.list_items -%} + {% set list = content.list %} +
      + {% include 'partials/summary/list-summary.html' %} +
      + {%- endif -%} + + {% include 'partials/contents.html' %} + +{% endblock %} + diff --git a/templates/partials/block.html b/templates/partials/block.html index 71b854a4bb..c937050913 100644 --- a/templates/partials/block.html +++ b/templates/partials/block.html @@ -6,4 +6,5 @@ {% set question = block['question'] %} {% include 'partials/question.html' %} {% endif %} +

      diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 024333e93b..2d0ecbb0b9 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -384,7 +384,7 @@ def test_map_list_collector_config_no_actions(): {"item_title": "Joe Bloggs", "list_item_id": "two"}, ] - output = map_list_collector_config(list_items, False) + output = map_list_collector_config(list_items, True, False) expected = [ { @@ -442,6 +442,7 @@ def test_map_list_collector_config(): output = map_list_collector_config( list_items, + True, False, "edit_link_text", "edit_link_aria_label", @@ -518,6 +519,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): output = map_list_collector_config( list_items, + True, False, "edit_link_text", "edit_link_aria_label", diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 0130fb0ab0..239f8d9e36 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -833,25 +833,27 @@ def process_block( if not page_filename: page_filename = block["id"] + ".page.js" - if block["type"] == "ListCollector": - list_operations = ["add", "edit", "remove"] - for list_operation in list_operations: + if block["type"] in {"ListCollector", "ListCollectorContent"}: + for repeating_block in block.get("repeating_blocks", []): process_block( - block[f"{list_operation}_block"], + repeating_block, dir_out, schema_data, spec_file, relative_require, - page_filename=f'{block["id"]}-{list_operation}.page.js', + page_filename=f'{repeating_block["id"]}-repeating-block.page.js', ) - for repeating_block in block.get("repeating_blocks", []): + + if block["type"] == "ListCollector": + list_operations = ["add", "edit", "remove"] + for list_operation in list_operations: process_block( - repeating_block, + block[f"{list_operation}_block"], dir_out, schema_data, spec_file, relative_require, - page_filename=f'{repeating_block["id"]}-repeating-block.page.js', + page_filename=f'{block["id"]}-{list_operation}.page.js', ) if block["type"] == "PrimaryPersonListCollector": diff --git a/tests/functional/spec/list_collector_content.spec.js b/tests/functional/spec/list_collector_content.spec.js new file mode 100644 index 0000000000..5c7bf96c9d --- /dev/null +++ b/tests/functional/spec/list_collector_content.spec.js @@ -0,0 +1,142 @@ +import AnyOtherCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches-add.page.js"; +import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-companies-or-branches.page"; +import CompaniesSummaryPage from "../generated_pages/list_collector_content_page/section-companies-summary.page"; +import HubPage from "../base_pages/hub.page"; +import ResponsiblePartyQuestionPage from "../generated_pages/list_collector_content_page/responsible-party.page"; +import ListCollectorFirstRepeatingBlockPage from "../generated_pages/list_collector_content_page/companies-repeating-block-1-repeating-block.page"; +import ListCollectorSecondRepeatingBlockPage from "../generated_pages/list_collector_content_page/companies-repeating-block-2-repeating-block.page"; +import ListCollectorContentPage from "../generated_pages/list_collector_content_page/list-collector-content.page"; +import ListCollectorContentSectionSummaryPage from "../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; + +describe("List Collector Section Summary and Summary Items", () => { + describe("Given I launch the test list collector section summary items survey", () => { + beforeEach(async () => { + await browser.openQuestionnaire("test_list_collector_content_page.json"); + }); + it("When I get to the Hub, Then from there the next block in list collector content section should be list collector content page.", async () => { + await fillInListCollectorSection(); + await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Not started"); + await $(HubPage.submit()).click(); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await $(ResponsiblePartyQuestionPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ListCollectorContentPage.url()); + }); + it("When I get to the list collector content page, Then the relevant content and button is displayed.", async () => { + await fillInListCollectorSection(); + await $(HubPage.submit()).click(); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await $(ResponsiblePartyQuestionPage.submit()).click(); + await expect(await $(ListCollectorContentPage.heading()).getHTML()).to.contain("Companies"); + await expect(await $("#main-content > p").getText()).to.contain( + "You have previously reported the following companies. Press continue to updated registration and trading information.", + ); + await expect(await $("#main-content > #guidance-1").getText()).to.contain("Include all companies"); + await expect(await $("#main-content > #definition").getText()).to.contain("Companies definition"); + await expect(await $(ListCollectorContentPage.submit()).getText()).to.equal("Continue"); + }); + it("When I get to list collector content block section, Then I should be able to complete repeating blocks and get to the summary.", async () => { + await fillInListCollectorSection(); + await $(HubPage.submit()).click(); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await $(ResponsiblePartyQuestionPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); + await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(456); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); + await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await expect(await browser.getUrl()).to.contain(ListCollectorContentSectionSummaryPage.url()); + }); + it("When I fill in first item repeating blocks, Then after going back to the hub the section should be in progress.", async () => { + await fillInListCollectorSection(); + await $(HubPage.submit()).click(); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await $(ResponsiblePartyQuestionPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); + await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await $(ListCollectorContentPage.previous()).click(); + await $(ResponsiblePartyQuestionPage.previous()).click(); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Partially completed"); + }); + it("When I fill in both items repeating blocks, Then after going back to the hub the section should be completed.", async () => { + await fillInListCollectorSection(); + await $(HubPage.submit()).click(); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await $(ResponsiblePartyQuestionPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); + await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(456); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); + await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await $(ListCollectorContentPage.submit()).click(); + await $(ListCollectorContentSectionSummaryPage.previous()).click(); + await $(ListCollectorContentPage.previous()).click(); + await $(ResponsiblePartyQuestionPage.previous()).click(); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Completed"); + }); + }); +}); +const fillInListCollectorSection = async () => { + await $(AnyCompaniesOrBranchesPage.yes()).click(); + await $(AnyCompaniesOrBranchesPage.submit()).click(); + await addCompany("Company A", "123", true); + await anyMoreCompaniesYes(); + await addCompany("Company B", "456", true); + await anyMoreCompaniesNo(); + await $(CompaniesSummaryPage.submit()).click(); +}; +const addCompany = async (name, number, authorised) => { + await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue(name); + await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).setValue(number); + if (authorised) { + await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).click(); + } else { + await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); + } + await $(AnyCompaniesOrBranchesAddPage.submit()).click(); +}; + +const anyMoreCompaniesYes = async () => { + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); +}; + +const anyMoreCompaniesNo = async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); +}; diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py new file mode 100644 index 0000000000..120ba70755 --- /dev/null +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py @@ -0,0 +1,178 @@ +from . import QuestionnaireTestCase + +# pylint: disable=too-many-public-methods + + +class TestQuestionnaireListCollectorContent(QuestionnaireTestCase): + def get_add_someone_link(self): + selector = "[data-qa='add-item-link']" + selected = self.getHtmlSoup().select(selector) + return selected[0].get("href") + + def add_company(self, name: str, number: str, authorised_insurer: str): + self.post( + { + "company-or-branch-name": name, + "registration-number": number, + "authorised-insurer-radio": authorised_insurer, + } + ) + + def test_happy_path(self): + self.launchSurvey("test_list_collector_content_page") + + self.post({"any-companies-or-branches-answer": "Yes"}) + self.add_company("Company A", "123", "No") + self.post({"any-other-companies-or-branches-answer": "Yes"}) + self.add_company("Company B", "456", "No") + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + self.post() + + self.post({"responsible-party-answer": "Yes"}) + self.assertInUrl("/questionnaire/list-collector-content/") + self.assertInBody( + "You have previously reported the following companies. Press continue to updated registration and trading information." + ) + + self.post() + + self.assertInUrl("/companies-repeating-block-1/") + self.assertInBody("Give details about Company A") + + self.post( + { + "registration-number-repeating-block": "123", + "registration-date-repeating-block-day": "1", + "registration-date-repeating-block-month": "1", + "registration-date-repeating-block-year": "1990", + } + ) + + self.assertInUrl("/companies-repeating-block-2/") + self.assertInBody("Give details about how Company A") + + self.post( + { + "authorised-trader-uk-radio-repeating-block": "Yes", + "authorised-trader-eu-radio-repeating-block": "No", + } + ) + self.post() + + self.assertInBody("Give details about Company B") + self.assertInUrl("/companies-repeating-block-1/") + + self.post( + { + "registration-number-repeating-block": "456", + "registration-date-repeating-block-day": "1", + "registration-date-repeating-block-month": "1", + "registration-date-repeating-block-year": "1990", + } + ) + + self.assertInBody("Give details about how Company B") + + self.post( + { + "authorised-trader-uk-radio-repeating-block": "Yes", + "authorised-trader-eu-radio-repeating-block": "No", + } + ) + + self.assertInBody( + "You have previously reported the following companies. Press continue to updated registration and trading information." + ) + + self.post() + + self.assertInUrl("questionnaire/sections/section-list-collector-contents/") + self.assertInBody("List Collector Contents") + + self.post() + + self.assertInUrl("/questionnaire/") + + def test_hub_section_in_progress(self): + self.launchSurvey("test_list_collector_content_page") + + self.post({"any-companies-or-branches-answer": "Yes"}) + self.add_company("Company A", "123", "No") + self.post({"any-other-companies-or-branches-answer": "Yes"}) + self.add_company("Company B", "456", "No") + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + self.post() + + self.post({"responsible-party-answer": "Yes"}) + self.assertInUrl("/questionnaire/list-collector-content/") + self.assertInBody( + "You have previously reported the following companies. Press continue to updated registration and trading information." + ) + + self.post() + + self.assertInUrl("/companies-repeating-block-1/") + self.assertInBody("Give details about Company A") + + self.post( + { + "registration-number-repeating-block": "123", + "registration-date-repeating-block-day": "1", + "registration-date-repeating-block-month": "1", + "registration-date-repeating-block-year": "1990", + } + ) + self.previous() + self.previous() + self.previous() + self.previous() + + self.assertInBody("Partially completed") + + def test_hub_section_in_progress_when_one_complete(self): + self.launchSurvey("test_list_collector_content_page") + + self.post({"any-companies-or-branches-answer": "Yes"}) + self.add_company("Company A", "123", "No") + self.post({"any-other-companies-or-branches-answer": "Yes"}) + self.add_company("Company B", "456", "No") + self.post({"any-other-companies-or-branches-answer": "No"}) + self.post() + self.post() + + self.post({"responsible-party-answer": "Yes"}) + self.assertInUrl("/questionnaire/list-collector-content/") + self.assertInBody( + "You have previously reported the following companies. Press continue to updated registration and trading information." + ) + + self.post() + + self.assertInUrl("/companies-repeating-block-1/") + + self.assertInBody("Give details about Company A") + + self.post( + { + "registration-number-repeating-block": "123", + "registration-date-repeating-block-day": "1", + "registration-date-repeating-block-month": "1", + "registration-date-repeating-block-year": "1990", + } + ) + + self.assertInUrl("/companies-repeating-block-2/") + self.assertInBody("Give details about how Company A") + + self.post( + { + "authorised-trader-uk-radio-repeating-block": "Yes", + "authorised-trader-eu-radio-repeating-block": "No", + } + ) + self.previous() + self.previous() + + self.assertInBody("Partially completed") From 8072252756a3588a89995c25d45813537cefab90 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:25:52 +0100 Subject: [PATCH 275/567] Update supplementary data dev key's service (#1188) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> --- dev-keys.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-keys.yml b/dev-keys.yml index 9dbdf4bab9..e5cafc714e 100644 --- a/dev-keys.yml +++ b/dev-keys.yml @@ -106,7 +106,7 @@ keys: df88fdad2612ae1e80571120e6c6371f55896696: platform: sdc purpose: supplementary_data - service: sds + service: sdx type: private use: encryption value: | From 87e7364c4d54fee99e6a5e96649123f11c4b53f1 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 31 Aug 2023 16:07:55 +0100 Subject: [PATCH 276/567] Bind Contextvars before metadata is parsed (#1192) --- app/routes/session.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app/routes/session.py b/app/routes/session.py index f377b89ffc..900a29c27b 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -73,6 +73,27 @@ def login() -> Response: validate_jti(decrypted_token) + _data = ( + survey_metadata.get("data", {}) + if (survey_metadata := decrypted_token.get("survey_metadata")) + else decrypted_token + ) + ru_ref, qid = _data.get("ru_ref"), _data.get("qid") + + logger_args = { + key: value + for key, value in { + "tx_id": decrypted_token.get("tx_id"), + "case_id": decrypted_token.get("case_id"), + "schema_name": decrypted_token.get("schema_name"), + "schema_url": decrypted_token.get("schema_url"), + "ru_ref": ru_ref, + "qid": qid, + }.items() + if value + } + contextvars.bind_contextvars(**logger_args) + runner_claims = get_runner_claims(decrypted_token) metadata = MetadataProxy.from_dict(runner_claims) @@ -91,31 +112,10 @@ def login() -> Response: if questionnaire_claims: runner_claims["survey_metadata"]["data"] = questionnaire_claims - ru_ref = questionnaire_claims.get("ru_ref") - qid = questionnaire_claims.get("qid") claims = runner_claims else: - ru_ref = runner_claims["ru_ref"] - qid = None claims = {**runner_claims, **questionnaire_claims} - tx_id = claims["tx_id"] - case_id = claims["case_id"] - - logger_args = { - key: value - for key, value in { - "tx_id": tx_id, - "case_id": case_id, - "schema_name": metadata.schema_name, - "schema_url": metadata.schema_url, - "ru_ref": ru_ref, - "qid": qid, - }.items() - if value - } - contextvars.bind_contextvars(**logger_args) - logger.info("decrypted token and parsed metadata") with create_session_questionnaire_store(claims) as questionnaire_store: From 114cbd74724a06bff8fb0835a6e67c40862c9c75 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Mon, 4 Sep 2023 09:12:51 +0100 Subject: [PATCH 277/567] Route to first incomplete location when returning to summary (#1194) * Route to first incomplete location when returning to summary --- app/questionnaire/router.py | 34 ++--- ...ed_summary_cross_section_dependencies.json | 27 ++++ tests/app/questionnaire/test_router.py | 121 +++++++++++++++++- .../calculated_summary_test_case.js | 42 ++---- ...mmary_repeating_and_static_answers.spec.js | 17 +-- ...lculated_summary_repeating_section.spec.js | 53 ++------ .../dynamic_answers_list_value_source.spec.js | 2 - ...summary_cross_section_dependencies.spec.js | 31 ++++- ...lculated_summary_repeating_answers.spec.js | 14 +- .../list_collector_repeating_blocks.spec.js | 39 +++++- .../section_summary/section_summary.spec.js | 15 +-- .../features/validation/sum/dynamic.spec.js | 2 +- ...on_dependencies_calculated_summary.spec.js | 3 +- ..._questionnaire_grand_calculated_summary.py | 5 +- ...stionnaire_progress_value_source_blocks.py | 11 +- 15 files changed, 271 insertions(+), 145 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 366bcdb69e..50feee73b5 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -262,7 +262,6 @@ def get_return_to_location_url( return_to_block_id=return_to_block_id, location=location, routing_path=routing_path, - is_for_previous=is_for_previous, return_to_answer_id=return_to_answer_id, ) ): @@ -276,7 +275,13 @@ def get_return_to_location_url( ) if not is_section_complete: - return None + # go to the next incomplete item in the section whilst preserving return to parameters + return self._get_return_url_for_inaccessible_location( + is_for_previous=is_for_previous, + return_to_block_id=return_to_block_id, + return_to=return_to, + routing_path=routing_path, + ) if return_to == "section-summary": return self._get_section_url( @@ -317,13 +322,7 @@ def _get_return_to_for_grand_calculated_summary( ) or grand_calculated_summary_section not in self.enabled_section_ids ): - return self._get_return_url_for_inaccessible_location( - is_for_previous=is_for_previous, - return_to_block_id=return_to_block_id, - return_to=return_to, - routing_path=routing_path, - ) - + return None routing_path = self._path_finder.routing_path( section_id=grand_calculated_summary_section ) @@ -341,6 +340,7 @@ def _get_return_to_for_grand_calculated_summary( return_to=return_to, _anchor=return_to_answer_id, ) + # the above may alter routing_path, so retrieval of the next incomplete block needs to be here instead of returning None and allowing default behaviour return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, return_to_block_id=return_to_block_id, @@ -355,7 +355,6 @@ def _get_return_to_for_calculated_summary( return_to_block_id: str | None, location: LocationType, routing_path: RoutingPath, - is_for_previous: bool, return_to_answer_id: str | None = None, ) -> str | None: """ @@ -382,26 +381,19 @@ def _get_return_to_for_calculated_summary( ): # if the next location is valid, the new url is that location, and the new 'return to block id' is just what remains return_to_block_id = ",".join(remaining) if remaining else None - # if return_to is a list, return all but the first item, but if it's a single item then leave as is - return_to = return_to[return_to.find(",") + 1 :] + # remove first item and return the remaining ones + return_to_remaining = ",".join(return_to.split(",")[1:]) or None return url_for( "questionnaire.block", block_id=block_id, list_name=location.list_name, list_item_id=location.list_item_id, - return_to=return_to, + return_to=return_to_remaining, return_to_block_id=return_to_block_id, _anchor=return_to_answer_id, ) - return self._get_return_url_for_inaccessible_location( - is_for_previous=is_for_previous, - return_to_block_id=return_to_block_id, - return_to=return_to, - routing_path=routing_path, - ) - def _get_return_url_for_inaccessible_location( self, *, @@ -416,7 +408,7 @@ def _get_return_url_for_inaccessible_location( """ if ( not is_for_previous - and return_to_block_id + and return_to and ( next_incomplete_location := self._get_first_incomplete_location_in_section( routing_path diff --git a/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json b/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json index b0754d04b6..61512e3003 100644 --- a/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json +++ b/schemas/test/en/test_grand_calculated_summary_cross_section_dependencies.json @@ -301,6 +301,33 @@ }, "title": "Total monthly spending on internet and TV" } + }, + { + "id": "tv-choice-block", + "type": "Question", + "question": { + "id": "tv-choice-question", + "title": "Do you prefer to watch films on a television or computer?", + "type": "General", + "answers": [ + { + "id": "tv-choice-answer", + "label": "Preferred platform", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Television", + "value": "Television" + }, + { + "label": "Computer", + "value": "Computer" + } + ] + } + ] + } } ] } diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 78955c68ed..59c75651ef 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -514,8 +514,8 @@ def test_section_summary_on_completion_false(self): ) def test_return_to_calculated_summary(self, schema): """ - This tests that when you hit continue on an edited answer for a calculated summary - and all dependent answers for that calculated summary are complete, you are routed to the calculated summary + This tests that when you hit continue on an edited answer for a calculated summary and all other dependent answers are complete + you are routed to the calculated summary, anchored to the answer that you edited """ self.schema = load_schema_from_name(schema) # for the purposes of this test, assume the routing path consists only of the first two blocks and the calculated summary @@ -562,7 +562,6 @@ def test_return_to_calculated_summary(self, schema): "questionnaire.block", list_item_id=expected_location.list_item_id, block_id=expected_location.block_id, - return_to="calculated-summary", _anchor="first-number-answer", ) @@ -903,6 +902,121 @@ def test_return_to_calculated_summary_from_incomplete_section( assert expected_next_url == next_location_url + @pytest.mark.parametrize( + "schema,section,block_id,return_to,return_to_block_id,routing_path_block_ids,next_incomplete_block_id", + [ + ( + "test_list_collector_repeating_blocks_section_summary", + "section-companies", + "responsible-party", + "section-summary", + None, + [ + "responsible-party", + "any-other-companies-or-branches", + "any-companies-or-branches", + "any-other-trading-details", + ], + "any-other-companies-or-branches", + ), + ( + "test_list_collector_repeating_blocks_section_summary", + "section-companies", + "any-other-companies-or-branches", + "submit", + None, + [ + "responsible-party", + "any-other-companies-or-branches", + "any-companies-or-branches", + "any-other-trading-details", + ], + "any-other-trading-details", + ), + ( + "test_new_calculated_summary_repeating_and_static_answers", + "section-1", + "list-collector", + "section-1", + None, + [ + "any-supermarket", + "list-collector", + "dynamic-answer", + "extra-spending-block", + "extra-spending-method-block", + "calculated-summary-spending", + "calculated-summary-visits", + ], + "extra-spending-method-block", + ), + ( + "test_new_calculated_summary_repeating_and_static_answers", + "section-1", + "dynamic-answer", + "section-1", + "calculated-summary-visits", + [ + "any-supermarket", + "list-collector", + "dynamic-answer", + "extra-spending-block", + "extra-spending-method-block", + "calculated-summary-spending", + ], + "calculated-summary-spending", + ), + ], + ) + @pytest.mark.usefixtures("app") + def test_return_to_inaccessible_summary_routes_to_next_incomplete_block( + self, + schema, + section, + block_id, + return_to, + return_to_block_id, + routing_path_block_ids, + next_incomplete_block_id, + ): + """ + This tests that if you try to return to a section/final/calculated summary which is not yet accessible + then you route to the next incomplete block in the section with all return to parameters preserved + """ + self.schema = load_schema_from_name(schema) + current_location = Location(section_id=section, block_id=block_id) + routing_path = RoutingPath(routing_path_block_ids, section_id=section) + + # make a copy where next_incomplete_block_id is not yet completed + completed_block_ids = [*routing_path_block_ids] + completed_block_ids.remove(next_incomplete_block_id) + + self.progress_store = ProgressStore( + [ + { + "section_id": section, + "block_ids": completed_block_ids, + "status": "IN_PROGRESS", + } + ] + ) + + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to=return_to, + return_to_block_id=return_to_block_id, + ) + + expected_next_url = url_for( + "questionnaire.block", + return_to=return_to, + return_to_block_id=return_to_block_id, + block_id=next_incomplete_block_id, + ) + + assert expected_next_url == next_location_url + class TestRouterNextLocationLinearFlow(RouterTestCase): @pytest.mark.usefixtures("app") @@ -1061,7 +1175,6 @@ def test_return_to_calculated_summary(self): expected_location_url = url_for( "questionnaire.block", list_item_id=expected_location.list_item_id, - return_to="calculated-summary", block_id=expected_location.block_id, _anchor="first-number-answer", ) diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index 5d3adad368..b7de5d1757 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -28,6 +28,7 @@ import SectionSummarySectionTwo from "../../../generated_pages/calculated_summar import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; import { click } from "../../../helpers"; + class TestCase { testCase(schema) { before("Get to Calculated Summary", async () => { @@ -113,13 +114,13 @@ class TestCase { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain("/questionnaire/currency-total-playback/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -263,24 +264,21 @@ class TestCase { await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); + // first incomplete block + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); + + // second incomplete block await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); + + // back to summary await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -289,20 +287,11 @@ class TestCase { await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); - + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); @@ -316,20 +305,11 @@ class TestCase { await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); - + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index e139972c62..d504b0f72d 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -13,6 +13,7 @@ import SupermarketTransportPage from "../../../generated_pages/new_calculated_su import SupermarketTransportCostPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport-cost.page"; import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; import { assertSummaryValues, click } from "../../../helpers"; + describe("Calculated summary with repeating answers", function () { // These tests are flaky therefore we add a retry. The cause is unknown. // :TODO: Revert this in future when we have a fix for this. @@ -114,12 +115,6 @@ describe("Calculated summary with repeating answers", function () { await $$(DynamicAnswerPage.inputs())[8].setValue(7); await click(DynamicAnswerPage.submit()); - // Currently when a section is incomplete, you are taken to each block in the section in turn, if the return_to is inaccessible - // this has been changed for calculated summaries to go to the first incomplete block, but not yet in the general case - // so the expected behaviour is to revisit these two blocks before the calculated summary - await click(ExtraSpendingBlockPage.submit()); - await click(ExtraSpendingMethodBlockPage.submit()); - // first calc summary await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( @@ -137,7 +132,7 @@ describe("Calculated summary with repeating answers", function () { await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); }); - it("Given I remove an item from the list which changes the calculated summaries, I return to each calculated summary to confirm new total with answers removed", async () => { + it("Given I remove an item from the list which changes the calculated summaries, I return to each incomplete block only to confirm new dynamic answers and totals with answers removed", async () => { await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Tesco"); await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Lidl"); await expect(await $(SummaryPage.supermarketsListLabel(3)).getText()).to.equal("Sainsburys"); @@ -148,11 +143,9 @@ describe("Calculated summary with repeating answers", function () { await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); - // section is now incomplete so step through each block until calculated summary is re-confirmed + // section is now incomplete as dynamic answers and calculated summary depend on the removed item - step through each incomplete block only await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); await click(DynamicAnswerPage.submit()); - await click(ExtraSpendingBlockPage.submit()); - await click(ExtraSpendingMethodBlockPage.submit()); // Tesco is now gone await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( @@ -203,9 +196,7 @@ describe("Calculated summary with repeating answers", function () { await dynamicAnswerChangeLink(8).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(1); await click(DynamicAnswerPage.submit()); - await click(ExtraSpendingBlockPage.submit()); - await click(ExtraSpendingMethodBlockPage.submit()); - await click(CalculatedSummarySpendingPage.submit()); + await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); await click(CalculatedSummaryVisitsPage.submit()); await click(SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index f032bffb69..5c08dd1731 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -34,6 +34,7 @@ import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_su import DependencyQuestionSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; import { click } from "../../../helpers"; + describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { before("Get to Calculated Summary", async () => { @@ -101,13 +102,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("currency-total-playback/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain("currency-total-playback/?return_to=calculated-summary#third-number-answer"); + await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -245,12 +246,9 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); - await click(SetMinMaxBlockPage.submit()); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { - await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); @@ -258,31 +256,26 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(SetMinMaxBlockPage.submit()); }); - it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { + it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I go to each incomplete page in turn before I return to the summary", async () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); + // first incomplete block + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(BreakdownPage.submit()); - await click(SecondCurrencyTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); + + // second incomplete block await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); + + // back to summary await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); @@ -291,22 +284,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); - + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(BreakdownPage.submit()); - await click(SecondCurrencyTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); @@ -320,22 +302,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await click(ThirdAndAHalfNumberBlockPage.submit()); - await click(SkipFourthBlockPage.submit()); - await click(FifthNumberBlockPage.submit()); - await click(SixthNumberBlockPage.submit()); - + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); - await click(CurrencyTotalPlaybackPage.submit()); - await click(UnitTotalPlaybackPage.submit()); - await click(PercentageTotalPlaybackPage.submit()); - await click(NumberTotalPlaybackPage.submit()); - await click(BreakdownPage.submit()); - await click(SecondCurrencyTotalPlaybackPage.submit()); - await click(CalculatedSummaryTotalConfirmation.submit()); await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index b4f579bc23..e26e098ad4 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -133,8 +133,6 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); await click(DynamicAnswerPage.submit()); - await $(SetMinimumPage.setMinimum()).setValue(2); - await click(SetMinimumPage.submit()); await click(DynamicAnswerOnlyPage.submit()); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index c314a9c3bf..2c0be8f826 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -10,7 +10,9 @@ import CurrencyQuestion3Page from "../../../generated_pages/grand_calculated_sum import CurrencyAllPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/currency-all.page"; import FirstNumberBlockPartAPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/first-number-block-part-a.page"; import FourthNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/fourth-number-block.page"; +import tvChoiceBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/tv-choice-block.page"; import { click } from "../../../helpers"; + describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary", () => { before("Getting to the second calculated summary", async () => { @@ -35,6 +37,8 @@ describe("Feature: Grand Calculated Summary", () => { await $(SkipCalculatedSummaryPage.no()).click(); await click(SkipCalculatedSummaryPage.submit()); await click(CurrencyQuestion3Page.submit()); + await $(tvChoiceBlockPage.television()).click(); + await click(tvChoiceBlockPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); await click(HubPage.submit()); await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£330.00"); @@ -74,18 +78,33 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; await click(CurrencyAllPage.submit()); }); - it("Given I change my response to include the calculated summary, the grand calculated summary updates to re-include it", async () => { + it("Given I change my response to include the calculated summary, When I press continue, Then I am routed to the new block that opens up", async () => { await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); await $(SkipCalculatedSummaryPage.no()).click(); + await click(SkipCalculatedSummaryPage.submit()); + await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + }); + it("Given I confirm the calculated summary and the blocks following it are already complete, When I press submit, Then I am returned to the section summary anchored to the answer I edited initially", async () => { + await click(CurrencyQuestion3Page.submit()); + await expect(await browser.getUrl()).to.contain("calculated-summary-section/#skip-answer-2"); + }); + it("Given I change an answer, When I press previous from the now incomplete calculated summary, Then I am routed to the block before the calculated summary", async () => { + await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); + await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(120); + await click(ThirdNumberBlockPage.submit()); + await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + await $(CurrencyQuestion3Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(SkipCalculatedSummaryPage.pageName); + }); + it("Given I complete the section, When I go back to the grand calculated summary, Then I see the new calculated summary included", async () => { await click(SkipCalculatedSummaryPage.submit()); await click(CurrencyQuestion3Page.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - // Currently, the grand calculated summary does not return to in-progress, because none of the answers it depends on have changed - await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £460.00. Is this correct?", + "The grand calculated summary is calculated to be £450.00. Is this correct?", ); }); it("Given I provide an answer to question 3b from the grand calculated summary, this opens up an additional question, and when I press continue I am taken to this question first, then the calculated summary, and then the grand calculated summary", async () => { @@ -100,7 +119,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(CurrencyQuestion3Page.submit()); await expect(await browser.getUrl()).to.contain(CurrencyAllPage.pageName); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £471.00. Is this correct?", + "The grand calculated summary is calculated to be £461.00. Is this correct?", ); await click(CurrencyAllPage.submit()); }); @@ -113,7 +132,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£30.00"); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( - "The grand calculated summary is calculated to be £171.00. Is this correct?", + "The grand calculated summary is calculated to be £161.00. Is this correct?", ); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 4bb68cfbd9..cc1f22b854 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -62,7 +62,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(HubPage.submit()); }); - it("Given I click on the change link for a calculated summary then press continue, I am taken back to the grand calculated summary", async () => { + it("Given I click on the change link for a calculated summary, When I press continue, Then I am taken back to the grand calculated summary", async () => { await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( "Grand Calculated Summary for shopping and entertainment is calculated to be £415.00. Is this correct?", ); @@ -73,6 +73,18 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); }); + it("Given I click on the change link for a calculated summary then one for an answer, When I press previous twice, I am return to the calculated summary then grand calculated summary", async () => { + await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); + await $(CalculatedSummary1Page.q1A1Edit()).click(); + await expect(await browser.getUrl()).to.contain(Block1Page.pageName); + + await $(Block1Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + + await $(CalculatedSummary1Page.previous()).click(); + await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + }); + it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 300b2e74c8..9515fe6923 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -12,6 +12,7 @@ import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; + const summaryValues = 'dd[class="ons-summary__values"]'; async function proceedToListCollector() { await $(ResponsiblePartyPage.yes()).click(); @@ -204,11 +205,47 @@ describe("List Collector Repeating Blocks", function () { checkListItemComplete(`dt[data-qa="list-item-4-label"]`); }); - it("The list collector can now be submitted.", async () => { + it("Clicking a change link from the section summary and pressing previous or submit without changing an answer returns the user to the section summary anchored to the answer they clicked on", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(AnyOtherTradingDetailsPage.submit()); + + await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); + await click(AnyOtherTradingDetailsPage.submit()); + await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + + await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); + await $(AnyOtherTradingDetailsPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + }); + + it("Editing an answer from the section summary which does not affect progress and pressing continue returns the user to the section summary anchored to the answer they edited", async () => { + await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); + await $(AnyOtherTradingDetailsPage.answer()).setValue("No"); + await click(AnyOtherTradingDetailsPage.submit()); + await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + }); + + it("Clicking a change link from the final summary and pressing previous or submit without changing an answer returns the user to the final summary anchored to the answer they clicked on", async () => { await click(SectionCompaniesPage.submit()); + + await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); + await click(AnyOtherTradingDetailsPage.submit()); + await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + + await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); + await $(AnyOtherTradingDetailsPage.previous()).click(); + await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + }); + + it("Editing an answer from the final summary which does not affect progress and pressing continue returns the user to the final summary anchored to the answer they edited", async () => { + await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); + await $(AnyOtherTradingDetailsPage.answer()).setValue("Yes"); + await click(AnyOtherTradingDetailsPage.submit()); + await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + }); + + it("The list collector can now be submitted.", async () => { await click(SubmitPage.submit()); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary.spec.js b/tests/functional/spec/features/section_summary/section_summary.spec.js index 242c7ed6ae..1562ea3201 100644 --- a/tests/functional/spec/features/section_summary/section_summary.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary.spec.js @@ -47,8 +47,6 @@ describe("Section Summary", () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); @@ -58,8 +56,6 @@ describe("Section Summary", () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); @@ -105,8 +101,6 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); @@ -117,8 +111,6 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); - await click(InsuranceAddressPage.submit()); await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); @@ -126,14 +118,13 @@ describe("Section Summary", () => { await click(AddressDurationPage.submit()); await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); - - it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Then I should return to the question I originally edited", async () => { + it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Since I cannot return to the section summary yet I return to the previous block in the section", async () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await $(InsuranceAddressPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(InsuranceTypePage.pageName); + await $(AddressDurationPage.previous()).click(); + await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); }); it("When I change an answer, Then the final summary should display the updated value", async () => { diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 644b377387..2a9d470f71 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -10,6 +10,7 @@ import ListCollectorEditPage from "../../../../generated_pages/validation_sum_ag import HubPage from "../../../../base_pages/hub.page"; import TotalBlockOtherPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block-other.page"; import { click } from "../../../../helpers"; + describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", function () { // These tests are flaky therefore we add a retry. The cause is unknown. // :TODO: Revert this in future when we have a fix for this. @@ -104,7 +105,6 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $(ListCollectorEditPage.supermarketName()).setValue("Aldi"); await click(ListCollectorEditPage.submit()); await click(DynamicAnswerPage.submit()); - await click(TotalBlockOtherPage.submit()); await click(DynamicAnswerOnlyPage.submit()); await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: 2000 }); diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 470594cf0a..7ed73fa8e0 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -130,8 +130,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); await $(FirstQuestionBlockPage.milk()).setValue(100); await click(FirstQuestionBlockPage.submit()); - await $(SkipQuestionPage.yes()).click(); - await click(SkipQuestionPage.submit()); + await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 2120a26c88..1c784d32ca 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -79,8 +79,9 @@ def test_grand_calculated_summary_cross_section_dependencies_with_skip(self): self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") self._complete_upto_grand_calculated_summary_cross_section_dependencies() - # skip the calculated summary and go straight to section summary + # skip the calculated summary self.post({"skip-answer-2": "Yes"}) + self.post({"tv-choice-answer": "Television"}) self.post() # grand calculated summary which doesn't include skipped calculated summary @@ -96,6 +97,7 @@ def test_grand_calculated_summary_cross_section_dependencies_no_skip(self): # don't skip calculated summary, confirm it, and go to section summary self.post({"skip-answer-2": "No"}) self.post() + self.post({"tv-choice-answer": "Television"}) self.post() # grand calculated summary will now include the previous calculated summary @@ -116,6 +118,7 @@ def test_grand_calculated_summary_cross_section_dependencies_extra_question(self self.post({"fourth-number-answer": "40"}) self.post({"skip-answer-2": "No"}) self.post() + self.post({"tv-choice-answer": "Television"}) self.post() # grand calculated summary will now include the extra question answer diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py index b167e50887..b8279026ea 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py @@ -80,18 +80,11 @@ def test_block_value_source_dependencies_updated(self): self.assertInBody("Section 1 Question 2") self.post({"s1-b2-q1-a1": 1}) - self.assertInBody("Section 1 Question 3") - self.post({"s1-b3-q1-a1": 1}) - - # bBock 4 is not skipped because answer to block 1 is now 1 + # Routes to block 4 as it is the next incomplete block and no longer skipped because answer to block 1 is now 1. self.assertInBody("Section 1 Question 4") self.post({"s1-b4-q1-a1": 1}) - # Routes to block 5 because answer to block 1 is now 1 - self.assertInBody("Section 1 Question 5") - self.post({"s1-b5-q1-a1": 1}) - - # Routes to block 6 because answer to block 1 is now 1 + # Routes to block 6 as its the next incomplete block and no longer skipped because block 4 is answered self.assertInBody("Section 1 Question 6") self.post({"s1-b6-q1-a1": 1}) From 7bed39d432eb24ead6acbdfe93169f478d43b633 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:20:23 +0100 Subject: [PATCH 278/567] Update to DS v65 (#1189) --- .design-system-version | 2 +- app/jinja_filters.py | 18 +++++++--------- app/views/contexts/hub_context.py | 6 +++--- templates/partials/error-panel.html | 2 +- tests/app/test_jinja_filters.py | 22 ++++++++++---------- tests/app/views/contexts/test_hub_context.py | 4 ++-- tests/functional/base_pages/submit.page.js | 2 +- 7 files changed, 27 insertions(+), 29 deletions(-) diff --git a/.design-system-version b/.design-system-version index 2f9886f839..4eed309c20 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -62.2.1 +65.2.2 diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 3f19b4d77f..016c271870 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -446,7 +446,7 @@ def __init__( edit_link_aria_label: str, ) -> None: self.text = edit_link_text - self.ariaLabel = edit_link_aria_label + " " + item_title + self.visuallyHiddenText = edit_link_aria_label + " " + item_title self.url = answer["link"] self.attributes = { @@ -671,8 +671,8 @@ def map_list_collector_config( item_name = list_item.get("item_title") actions = [] - edit_link_aria_label_text = None - remove_link_aria_label_text = None + edit_link_hidden_text = None + remove_link_hidden_text = None if edit_link_text and editable: url = ( @@ -683,29 +683,27 @@ def map_list_collector_config( edit_link = { "text": edit_link_text, - "ariaLabel": edit_link_aria_label_text, + "visuallyHiddenText": edit_link_hidden_text, "url": url, "attributes": {"data-qa": f"list-item-change-{index}-link"}, } if edit_link_aria_label: - edit_link_aria_label_text = edit_link_aria_label.format( - item_name=item_name - ) - edit_link["ariaLabel"] = edit_link_aria_label_text + edit_link_hidden_text = edit_link_aria_label.format(item_name=item_name) + edit_link["visuallyHiddenText"] = edit_link_hidden_text actions.append(edit_link) if not list_item.get("primary_person") and remove_link_text and editable: if remove_link_aria_label: - remove_link_aria_label_text = remove_link_aria_label.format( + remove_link_hidden_text = remove_link_aria_label.format( item_name=item_name ) actions.append( { "text": remove_link_text, - "ariaLabel": remove_link_aria_label_text, + "visuallyHiddenText": remove_link_hidden_text, "url": list_item.get("remove_link"), "attributes": {"data-qa": f"list-item-remove-{index}-link"}, } diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 66a44a49ab..5a017f4d2e 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -95,9 +95,9 @@ def get_row_context_for_section( "actions": [ { "text": section_content["link"]["text"], - "ariaLabel": section_content["link"]["aria_label"].format( - section_name=section_name - ), + "visuallyHiddenText": section_content["link"][ + "aria_label" + ].format(section_name=section_name), "url": section_url, "attributes": {"data-qa": f"hub-row-{row_id}-link"}, } diff --git a/templates/partials/error-panel.html b/templates/partials/error-panel.html index b36dc8420c..03136914e7 100644 --- a/templates/partials/error-panel.html +++ b/templates/partials/error-panel.html @@ -1,5 +1,5 @@ {% from "components/panel/_macro.njk" import onsPanel %} -{% from "components/lists/_macro.njk" import onsList %} +{% from "components/list/_macro.njk" import onsList %} {% set error_list = [] %} {% for error_id, error in form.mapped_errors %} diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 2d0ecbb0b9..0ee42c03c8 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -456,7 +456,7 @@ def test_map_list_collector_config(): { "actions": [ { - "ariaLabel": "edit_link_aria_label", + "visuallyHiddenText": "edit_link_aria_label", "attributes": {"data-qa": "list-item-change-1-link"}, "text": "edit_link_text", "url": "/primary/change", @@ -477,13 +477,13 @@ def test_map_list_collector_config(): { "actions": [ { - "ariaLabel": "edit_link_aria_label", + "visuallyHiddenText": "edit_link_aria_label", "attributes": {"data-qa": "list-item-change-2-link"}, "text": "edit_link_text", "url": "/nonprimary/change", }, { - "ariaLabel": "remove_link_aria_label", + "visuallyHiddenText": "remove_link_aria_label", "attributes": {"data-qa": "list-item-remove-2-link"}, "text": "remove_link_text", "url": "/nonprimary/remove", @@ -572,13 +572,13 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): { "actions": [ { - "ariaLabel": "edit_link_aria_label", + "visuallyHiddenText": "edit_link_aria_label", "attributes": {"data-qa": "list-item-change-1-link"}, "text": "edit_link_text", "url": "/nonprimary/change", }, { - "ariaLabel": "remove_link_aria_label", + "visuallyHiddenText": "remove_link_aria_label", "attributes": {"data-qa": "list-item-remove-1-link"}, "text": "remove_link_text", "url": "/nonprimary/remove", @@ -596,7 +596,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): { "actions": [ { - "ariaLabel": "edit_link_aria_label Registration number", + "visuallyHiddenText": "edit_link_aria_label Registration number", "attributes": { "data-ga": "click", "data-ga-action": "Edit click", @@ -616,7 +616,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): { "actions": [ { - "ariaLabel": "edit_link_aria_label Is this UK " + "visuallyHiddenText": "edit_link_aria_label Is this UK " "company or branch an authorised " "insurer?", "attributes": { @@ -859,13 +859,13 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "ariaLabel": "Change your answer for:", + "visuallyHiddenText": "Change your answer for:", "attributes": {"data-qa": "list-item-change-1-link"}, "text": "Change", "url": "change_link_url", }, { - "ariaLabel": "Remove Company A", + "visuallyHiddenText": "Remove Company A", "attributes": {"data-qa": "list-item-remove-1-link"}, "text": "Remove", "url": "remove_link_url", @@ -882,7 +882,7 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "ariaLabel": "Change your answer for: " + "visuallyHiddenText": "Change your answer for: " "Registration number", "attributes": { "data-ga": "click", @@ -903,7 +903,7 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "ariaLabel": "Change your answer for: Is this UK " + "visuallyHiddenText": "Change your answer for: Is this UK " "company or branch an authorised " "insurer?", "attributes": { diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 76edb93862..b541fbfde7 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -44,7 +44,7 @@ def test_get_not_started_row_for_section( "actions": [ { "text": "Start section", - "ariaLabel": "Start Breakfast section", + "visuallyHiddenText": "Start Breakfast section", "url": "http://some/url", "attributes": {"data-qa": "hub-row-section-1-link"}, } @@ -92,7 +92,7 @@ def test_get_completed_row_for_section( "actions": [ { "text": "View answers", - "ariaLabel": "View answers for Breakfast", + "visuallyHiddenText": "View answers for Breakfast", "url": "http://some/url", "attributes": {"data-qa": "hub-row-section-1-link"}, } diff --git a/tests/functional/base_pages/submit.page.js b/tests/functional/base_pages/submit.page.js index 97ae06719c..d9d1e4ec81 100644 --- a/tests/functional/base_pages/submit.page.js +++ b/tests/functional/base_pages/submit.page.js @@ -14,7 +14,7 @@ class SubmitBasePage extends BasePage { } summaryShowAllButton() { - return ".ons-js-accordion-all"; + return ".ons-accordion__toggle-all"; } } From 1a9bf33e64235ffbcecbefb257c75ffc4b3b64e8 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:27:13 +0100 Subject: [PATCH 279/567] Update packages and version to Python 3.11 (#1191) --- .github/workflows/pull_request.yml | 12 +- .pylintrc | 2 +- .python-version | 2 +- Dockerfile | 4 +- Pipfile | 6 +- Pipfile.lock | 722 +++++++++--------- app/setup.py | 1 + scripts/extract_translation_templates.py | 4 +- .../views/contexts/summary/test_question.py | 10 +- 9 files changed, 397 insertions(+), 366 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 84c2d72fb8..4fd86c8c19 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -24,9 +24,11 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} cache: 'pipenv' - name: Install Pipenv - run: pip install pipenv==2022.11.30 + run: pip install pipenv==2023.8.22 - name: Install virtual environment - run: pipenv install --dev + run: | + sudo apt-get install libsnappy-dev + pipenv install --dev node-dependencies: runs-on: ubuntu-22.04 steps: @@ -63,7 +65,7 @@ jobs: - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv - run: pip install pipenv==2022.11.30 + run: pip install pipenv==2023.8.22 - name: Install virtual environment run: pipenv install --dev - name: Compile translations @@ -109,7 +111,7 @@ jobs: - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv - run: pip install pipenv==2022.11.30 + run: pip install pipenv==2023.8.22 - name: Install virtual environment run: pipenv install --dev - name: Load templates @@ -150,7 +152,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} cache: 'pipenv' - name: Install pipenv - run: pip install pipenv==2022.11.30 + run: pip install pipenv==2023.8.22 - name: Install virtual environment run: pipenv install --dev - name: Get yarn cache diff --git a/.pylintrc b/.pylintrc index 29c372eb45..09eead849c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -84,7 +84,7 @@ persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. -py-version=3.10 +py-version=3.11 # Discover python modules and packages in the file system subtree. recursive=no diff --git a/.python-version b/.python-version index 36435ac696..0c7d5f5f5d 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10.8 +3.11.4 diff --git a/Dockerfile b/Dockerfile index 6bffeb54e4..69e15103b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-slim-bullseye +FROM python:3.11-slim-bullseye EXPOSE 5000 @@ -18,7 +18,7 @@ COPY Pipfile Pipfile COPY Pipfile.lock Pipfile.lock RUN groupadd -r appuser && useradd -r -g appuser -u 9000 appuser && chown -R appuser:appuser . -RUN pip install pipenv==2022.11.30 && pipenv install --deploy --system && make build +RUN pip install pipenv==2023.8.22 && pipenv install --deploy --system && make build USER appuser diff --git a/Pipfile b/Pipfile index 11d579eced..1c8f3da8e1 100644 --- a/Pipfile +++ b/Pipfile @@ -13,7 +13,7 @@ pylint-mccabe = "*" pylint-quotes = "*" "beautifulsoup4" = "*" httmock = "*" -"flake8" = "==4.0.1" +"flake8" = "*" "flake8-debugger" = "*" "flake8-mock" = "*" "flake8-print" = "*" @@ -40,7 +40,7 @@ types-pytz = "*" [packages] colorama = "*" -flask = "==2.2.2" +flask = "*" flask-babel = "*" flask-login = "*" flask-wtf = "*" @@ -78,7 +78,7 @@ ordered-set = "*" cachetools = "*" [requires] -python_version = "3.10" +python_version = "3.11" [pipenv] allow_prereleases = false diff --git a/Pipfile.lock b/Pipfile.lock index 1f3bdb46fa..263b931aa1 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "5a5a0591a85922a3644f5ff4dd47966d518f71baa6f7ba03c523dce5ec70c993" + "sha256": "0944f900e71d5a21d0baa28eb3620e285f33a6504cba462e9134fbd7f2bc6bb9" }, "pipfile-spec": 6, "requires": { - "python_version": "3.10" + "python_version": "3.11" }, "sources": [ { @@ -16,14 +16,6 @@ ] }, "default": { - "async-timeout": { - "hashes": [ - "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", - "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" - ], - "markers": "python_full_version <= '3.11.2'", - "version": "==4.0.2" - }, "babel": { "hashes": [ "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610", @@ -38,23 +30,25 @@ "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==1.6.2" }, "boto3": { "hashes": [ - "sha256:84b7952858e9319968b0348d9894a91a6bb5f31e81a45c68044d040a12362abe", - "sha256:a6e711e0b6960c3a5b789bd30c5a18eea7263f2a59fc07f85efa5e04804e49d2" + "sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28", + "sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3" ], "index": "pypi", - "version": "==1.28.15" + "markers": "python_version >= '3.7'", + "version": "==1.28.32" }, "botocore": { "hashes": [ - "sha256:b3a0f787f275711875476cbe12a0123b2e6570b2f505e2fa509dcec3c5410b57", - "sha256:b46d1ce4e0cf42d28fdf61ce0c999904645d38b51cb809817a361c0cec16d487" + "sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664", + "sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c" ], "markers": "python_version >= '3.7'", - "version": "==1.31.15" + "version": "==1.31.32" }, "brotli": { "hashes": [ @@ -149,6 +143,7 @@ "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==5.3.1" }, "certifi": { @@ -311,11 +306,11 @@ }, "click": { "hashes": [ - "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd", - "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5" + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "markers": "python_version >= '3.7'", - "version": "==8.1.6" + "version": "==8.1.7" }, "colorama": { "hashes": [ @@ -323,6 +318,7 @@ "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" ], "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", "version": "==0.4.6" }, "coloredlogs": { @@ -331,36 +327,37 @@ "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" ], "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==15.0.1" }, "cryptography": { "hashes": [ - "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711", - "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7", - "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd", - "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e", - "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58", - "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0", - "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d", - "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83", - "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831", - "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766", - "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b", - "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c", - "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182", - "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f", - "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa", - "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4", - "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a", - "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2", - "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76", - "sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5", - "sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee", - "sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f", - "sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.2" + "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306", + "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84", + "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47", + "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d", + "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116", + "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207", + "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81", + "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087", + "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd", + "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507", + "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858", + "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae", + "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34", + "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906", + "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd", + "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922", + "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7", + "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4", + "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574", + "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1", + "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c", + "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e", + "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.3" }, "deprecated": { "hashes": [ @@ -372,11 +369,11 @@ }, "dnspython": { "hashes": [ - "sha256:5b7488477388b8c0b70a8ce93b227c5603bc7b77f1565afe8e729c36c51447d7", - "sha256:c33971c79af5be968bb897e95c2448e11a645ee84d93b265ce0b7aabe5dfdca8" + "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8", + "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984" ], "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==2.4.1" + "version": "==2.4.2" }, "email-validator": { "hashes": [ @@ -384,15 +381,17 @@ "sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.0.0.post2" }, "flask": { "hashes": [ - "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b", - "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526" + "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc", + "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" ], "index": "pypi", - "version": "==2.2.2" + "markers": "python_version >= '3.8'", + "version": "==2.3.3" }, "flask-babel": { "hashes": [ @@ -400,6 +399,7 @@ "sha256:deb3ee272d5adf97f5974ed09ab501243d63e7fb4a047501a00de4bd4aca4830" ], "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4.0'", "version": "==3.1.0" }, "flask-compress": { @@ -416,15 +416,16 @@ "sha256:c0a7baa9fdc448cdd3dd6f0939df72eec5177b2f7abe6cb82fc934d29caac9c3" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.6.2" }, "flask-talisman": { "hashes": [ - "sha256:205d3de7c5ecfad667c84123f5fbe580b1f68cd9a1b058d7353cc0348e15b1bf", - "sha256:be2767b6b2bc11b36bf9a0e09ffa10622fbe0d971fd7057a843f82cd795a854b" + "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208", + "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff" ], "index": "pypi", - "version": "==1.0.0" + "version": "==1.1.0" }, "flask-wtf": { "hashes": [ @@ -432,6 +433,7 @@ "sha256:7887d6f1ebb3e17bf648647422f0944c9a469d0fcf63e3b66fb9a83037e38b2c" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==1.1.1" }, "gevent": { @@ -468,8 +470,7 @@ "sha256:df4d7be3352126458cc818309ca6a3b678c209b1ae33e56b6975c6a8309f2068", "sha256:f522b6b015f1bfa9d8d3716ddffb23e3d4a8933df3e4ebf0a29a65a9fa74382b" ], - "index": "pypi", - "markers": "platform_python_implementation == 'CPython'", + "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", "version": "==23.7.0" }, "google-api-core": { @@ -501,19 +502,21 @@ }, "google-cloud-datastore": { "hashes": [ - "sha256:153d8dac05f0248d7f7d6f0986a31f8fb22d3cdfb764718eac0f28e3cadc8144", - "sha256:710ef27ebdfb50340f4671c532ad4c1525665985e9421984ffcd6b96b57e34bb" + "sha256:1beb0cfbfa577218d9a7f95949068ae0afb7dfdd311ec3640a92d06b45367abb", + "sha256:feafad874ed376a9d1f0899eafc9650d21fdb2228f90de9b8741a806c213b422" ], "index": "pypi", - "version": "==2.16.1" + "markers": "python_version >= '3.7'", + "version": "==2.17.0" }, "google-cloud-pubsub": { "hashes": [ - "sha256:00691ca49bed09946f92f6001f9f7456f561b32a205e4f82dd8aa74dc3e97d9e", - "sha256:4a2bf3c04f8278504f934b2bdbafe927fa650d7c5397c7633a0b30e10a5127f3" + "sha256:af14c79dac59f05592abc7d97f3e8430079e437a49f7a1c97f7324bb9d49cd9d", + "sha256:b40712339e5f81b7a035b0be8b2ae906684a4162e119a2b9ab33bc994ba1b72f" ], "index": "pypi", - "version": "==2.18.1" + "markers": "python_version >= '3.7'", + "version": "==2.18.3" }, "google-cloud-storage": { "hashes": [ @@ -521,15 +524,17 @@ "sha256:9433cf28801671de1c80434238fb1e7e4a1ba3087470e90f70c928ea77c2b9d7" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.10.0" }, "google-cloud-tasks": { "hashes": [ - "sha256:095d8a10049efb975186a809670791ff9ed1891255236b55a778b5bf8803fd08", - "sha256:e83d205cc4ed71ab0b100b770863403ce02e69f5fac69e1c969fdf6e2d8eafb1" + "sha256:b44ff8721f9a36f118892c5d45745393fba0ce0ef27fab3853e07cb5fba91a92", + "sha256:ca1a83df7391a78592dcca751583b22c9074d0a72dc8debdb4a4687ff9958a29" ], "index": "pypi", - "version": "==2.14.0" + "markers": "python_version >= '3.7'", + "version": "==2.14.1" }, "google-crc32c": { "hashes": [ @@ -615,11 +620,11 @@ }, "googleapis-common-protos": { "hashes": [ - "sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e", - "sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a" + "sha256:69f9bbcc6acde92cab2db95ce30a70bd2b81d20b12eff3f1aabaffcbe8a93918", + "sha256:e73ebb404098db405ba95d1e1ae0aa91c3e15a71da031a2eeb6b2e23e7bc3708" ], "markers": "python_version >= '3.7'", - "version": "==1.59.1" + "version": "==1.60.0" }, "greenlet": { "hashes": [ @@ -697,62 +702,63 @@ }, "grpcio": { "hashes": [ - "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57", - "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa", - "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d", - "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9", - "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9", - "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12", - "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475", - "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226", - "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b", - "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca", - "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5", - "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d", - "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35", - "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48", - "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116", - "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d", - "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774", - "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1", - "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95", - "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed", - "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a", - "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5", - "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a", - "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c", - "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950", - "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d", - "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c", - "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c", - "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756", - "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e", - "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f", - "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1", - "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9", - "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4", - "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316", - "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6", - "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd", - "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328", - "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f", - "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93", - "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64", - "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8", - "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87", - "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed", - "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c" - ], - "index": "pypi", - "version": "==1.56.2" + "sha256:00258cbe3f5188629828363ae8ff78477ce976a6f63fb2bb5e90088396faa82e", + "sha256:092fa155b945015754bdf988be47793c377b52b88d546e45c6a9f9579ac7f7b6", + "sha256:0f80bf37f09e1caba6a8063e56e2b87fa335add314cf2b78ebf7cb45aa7e3d06", + "sha256:20ec6fc4ad47d1b6e12deec5045ec3cd5402d9a1597f738263e98f490fe07056", + "sha256:2313b124e475aa9017a9844bdc5eafb2d5abdda9d456af16fc4535408c7d6da6", + "sha256:23e7d8849a0e58b806253fd206ac105b328171e01b8f18c7d5922274958cc87e", + "sha256:2f708a6a17868ad8bf586598bee69abded4996b18adf26fd2d91191383b79019", + "sha256:2f7349786da979a94690cc5c2b804cab4e8774a3cf59be40d037c4342c906649", + "sha256:34950353539e7d93f61c6796a007c705d663f3be41166358e3d88c45760c7d98", + "sha256:40b72effd4c789de94ce1be2b5f88d7b9b5f7379fe9645f198854112a6567d9a", + "sha256:4b089f7ad1eb00a104078bab8015b0ed0ebcb3b589e527ab009c53893fd4e613", + "sha256:4faea2cfdf762a664ab90589b66f416274887641ae17817de510b8178356bf73", + "sha256:5371bcd861e679d63b8274f73ac281751d34bd54eccdbfcd6aa00e692a82cd7b", + "sha256:5613a2fecc82f95d6c51d15b9a72705553aa0d7c932fad7aed7afb51dc982ee5", + "sha256:57b183e8b252825c4dd29114d6c13559be95387aafc10a7be645462a0fc98bbb", + "sha256:5b7a4ce8f862fe32b2a10b57752cf3169f5fe2915acfe7e6a1e155db3da99e79", + "sha256:5e5b58e32ae14658085c16986d11e99abd002ddbf51c8daae8a0671fffb3467f", + "sha256:60fe15288a0a65d5c1cb5b4a62b1850d07336e3ba728257a810317be14f0c527", + "sha256:6907b1cf8bb29b058081d2aad677b15757a44ef2d4d8d9130271d2ad5e33efca", + "sha256:76c44efa4ede1f42a9d5b2fed1fe9377e73a109bef8675fb0728eb80b0b8e8f2", + "sha256:7a635589201b18510ff988161b7b573f50c6a48fae9cb567657920ca82022b37", + "sha256:7b400807fa749a9eb286e2cd893e501b110b4d356a218426cb9c825a0474ca56", + "sha256:82640e57fb86ea1d71ea9ab54f7e942502cf98a429a200b2e743d8672171734f", + "sha256:871f9999e0211f9551f368612460442a5436d9444606184652117d6a688c9f51", + "sha256:9338bacf172e942e62e5889b6364e56657fbf8ac68062e8b25c48843e7b202bb", + "sha256:a8a8e560e8dbbdf29288872e91efd22af71e88b0e5736b0daf7773c1fecd99f0", + "sha256:aed90d93b731929e742967e236f842a4a2174dc5db077c8f9ad2c5996f89f63e", + "sha256:b363bbb5253e5f9c23d8a0a034dfdf1b7c9e7f12e602fc788c435171e96daccc", + "sha256:b4098b6b638d9e0ca839a81656a2fd4bc26c9486ea707e8b1437d6f9d61c3941", + "sha256:b53333627283e7241fcc217323f225c37783b5f0472316edcaa4479a213abfa6", + "sha256:b670c2faa92124b7397b42303e4d8eb64a4cd0b7a77e35a9e865a55d61c57ef9", + "sha256:bb396952cfa7ad2f01061fbc7dc1ad91dd9d69243bcb8110cf4e36924785a0fe", + "sha256:c60b83c43faeb6d0a9831f0351d7787a0753f5087cc6fa218d78fdf38e5acef0", + "sha256:c6ebecfb7a31385393203eb04ed8b6a08f5002f53df3d59e5e795edb80999652", + "sha256:d78d8b86fcdfa1e4c21f8896614b6cc7ee01a2a758ec0c4382d662f2a62cf766", + "sha256:d7f8df114d6b4cf5a916b98389aeaf1e3132035420a88beea4e3d977e5f267a5", + "sha256:e1cb52fa2d67d7f7fab310b600f22ce1ff04d562d46e9e0ac3e3403c2bb4cc16", + "sha256:e3fdf04e402f12e1de8074458549337febb3b45f21076cc02ef4ff786aff687e", + "sha256:e503cb45ed12b924b5b988ba9576dc9949b2f5283b8e33b21dcb6be74a7c58d0", + "sha256:f19ac6ac0a256cf77d3cc926ef0b4e64a9725cc612f97228cd5dc4bd9dbab03b", + "sha256:f1fb0fd4a1e9b11ac21c30c169d169ef434c6e9344ee0ab27cfa6f605f6387b2", + "sha256:fada6b07ec4f0befe05218181f4b85176f11d531911b64c715d1875c4736d73a", + "sha256:fd173b4cf02b20f60860dc2ffe30115c18972d7d6d2d69df97ac38dee03be5bf", + "sha256:fe752639919aad9ffb0dee0d87f29a6467d1ef764f13c4644d212a9a853a078d", + "sha256:fee387d2fab144e8a34e0e9c5ca0f45c9376b99de45628265cfa9886b1dbe62b" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.57.0" }, "grpcio-status": { "hashes": [ - "sha256:63f3842867735f59f5d70e723abffd2e8501a6bcd915612a1119e52f10614782", - "sha256:a046b2c0118df4a5687f4585cca9d3c3bae5c498c4dff055dcb43fb06a1180c8" + "sha256:15d6af055914ebbc4ed17e55ebfb8e6bb17a45a57fea32e6af19978fb7844690", + "sha256:b098da99df1eebe58337f8f78e50df990273ccacc1226fddeb47c590e3df9e02" ], "markers": "python_version >= '3.6'", - "version": "==1.56.2" + "version": "==1.57.0" }, "gunicorn": { "hashes": [ @@ -760,6 +766,7 @@ "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==21.2.0" }, "htmlmin": { @@ -779,11 +786,12 @@ }, "humanize": { "hashes": [ - "sha256:7ca0e43e870981fa684acb5b062deb307218193bca1a01f2b2676479df849b3a", - "sha256:df7c429c2d27372b249d3f26eb53b07b166b661326e0325793e0a988082e3889" + "sha256:8bc9e2bb9315e61ec06bf690151ae35aeb65651ab091266941edf97c90836404", + "sha256:9783373bf1eec713a770ecaa7c2d7a7902c98398009dfa3d8a2df91eec9311e8" ], "index": "pypi", - "version": "==4.7.0" + "markers": "python_version >= '3.8'", + "version": "==4.8.0" }, "idna": { "hashes": [ @@ -799,6 +807,7 @@ "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.1.2" }, "jinja2": { @@ -823,6 +832,7 @@ "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" ], "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", "version": "==2.4" }, "jwcrypto": { @@ -886,6 +896,7 @@ "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.1.3" }, "marshmallow": { @@ -894,6 +905,7 @@ "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==3.20.1" }, "ordered-set": { @@ -902,6 +914,7 @@ "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==4.1.0" }, "packaging": { @@ -927,6 +940,7 @@ "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==1.3.2" }, "proto-plus": { @@ -939,22 +953,22 @@ }, "protobuf": { "hashes": [ - "sha256:0a5759f5696895de8cc913f084e27fd4125e8fb0914bb729a17816a33819f474", - "sha256:351cc90f7d10839c480aeb9b870a211e322bf05f6ab3f55fcb2f51331f80a7d2", - "sha256:5fea3c64d41ea5ecf5697b83e41d09b9589e6f20b677ab3c48e5f242d9b7897b", - "sha256:6dd9b9940e3f17077e820b75851126615ee38643c2c5332aa7a359988820c720", - "sha256:7b19b6266d92ca6a2a87effa88ecc4af73ebc5cfde194dc737cf8ef23a9a3b12", - "sha256:8547bf44fe8cec3c69e3042f5c4fb3e36eb2a7a013bb0a44c018fc1e427aafbd", - "sha256:9053df6df8e5a76c84339ee4a9f5a2661ceee4a0dab019e8663c50ba324208b0", - "sha256:c3e0939433c40796ca4cfc0fac08af50b00eb66a40bbbc5dee711998fb0bbc1e", - "sha256:ccd9430c0719dce806b93f89c91de7977304729e55377f872a92465d548329a9", - "sha256:e1c915778d8ced71e26fcf43c0866d7499891bca14c4368448a82edc61fdbc70", - "sha256:e9d0be5bf34b275b9f87ba7407796556abeeba635455d036c7351f7c183ef8ff", - "sha256:effeac51ab79332d44fba74660d40ae79985901ac21bca408f8dc335a81aa597", - "sha256:fee88269a090ada09ca63551bf2f573eb2424035bcf2cb1b121895b01a46594a" + "sha256:06437f0d4bb0d5f29e3d392aba69600188d4be5ad1e0a3370e581a9bf75a3081", + "sha256:0b2b224e9541fe9f046dd7317d05f08769c332b7e4c54d93c7f0f372dedb0b1a", + "sha256:302e8752c760549ed4c7a508abc86b25d46553c81989343782809e1a062a2ef9", + "sha256:44837a5ed9c9418ad5d502f89f28ba102e9cd172b6668bc813f21716f9273348", + "sha256:55dd644adc27d2a624339332755fe077c7f26971045b469ebb9732a69ce1f2ca", + "sha256:5906c5e79ff50fe38b2d49d37db5874e3c8010826f2362f79996d83128a8ed9b", + "sha256:5d32363d14aca6e5c9e9d5918ad8fb65b091b6df66740ae9de50ac3916055e43", + "sha256:970c701ee16788d74f3de20938520d7a0aebc7e4fff37096a48804c80d2908cf", + "sha256:bd39b9094a4cc003a1f911b847ab379f89059f478c0b611ba1215053e295132e", + "sha256:d414199ca605eeb498adc4d2ba82aedc0379dca4a7c364ff9bc9a179aa28e71b", + "sha256:d4af4fd9e9418e819be30f8df2a16e72fbad546a7576ac7f3653be92a6966d30", + "sha256:df015c47d6855b8efa0b9be706c70bf7f050a4d5ac6d37fb043fbd95157a0e25", + "sha256:fc361148e902949dcb953bbcb148c99fe8f8854291ad01107e4120361849fd0e" ], "markers": "python_version >= '3.7'", - "version": "==4.23.4" + "version": "==4.24.1" }, "pyasn1": { "hashes": [ @@ -1092,15 +1106,17 @@ "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==6.0.1" }, "redis": { "hashes": [ - "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d", - "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c" + "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0", + "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120" ], "index": "pypi", - "version": "==4.6.0" + "markers": "python_version >= '3.7'", + "version": "==5.0.0" }, "requests": { "hashes": [ @@ -1108,6 +1124,7 @@ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.31.0" }, "rsa": { @@ -1120,11 +1137,11 @@ }, "s3transfer": { "hashes": [ - "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", - "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" + "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084", + "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861" ], "markers": "python_version >= '3.7'", - "version": "==0.6.1" + "version": "==0.6.2" }, "sdc-cryptography": { "hashes": [ @@ -1136,11 +1153,11 @@ }, "setuptools": { "hashes": [ - "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", - "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" + "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d", + "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b" ], - "markers": "python_version >= '3.7'", - "version": "==68.0.0" + "markers": "python_version >= '3.8'", + "version": "==68.1.2" }, "simplejson": { "hashes": [ @@ -1231,6 +1248,7 @@ "sha256:f96def94576f857abf58e031ce881b5a3fc25cbec64b2bc4824824a8a4367af9" ], "index": "pypi", + "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.19.1" }, "six": { @@ -1247,6 +1265,7 @@ "sha256:79b9e68e48b54e373441e130fa447944e6f87a05b35de23138e475c05d0f7e0e" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==23.1.0" }, "ua-parser": { @@ -1274,11 +1293,11 @@ }, "werkzeug": { "hashes": [ - "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890", - "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330" + "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8", + "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528" ], "markers": "python_version >= '3.8'", - "version": "==2.3.6" + "version": "==2.3.7" }, "wrapt": { "hashes": [ @@ -1423,14 +1442,6 @@ "markers": "python_full_version >= '3.7.2'", "version": "==2.15.6" }, - "async-timeout": { - "hashes": [ - "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15", - "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c" - ], - "markers": "python_full_version <= '3.11.2'", - "version": "==4.0.2" - }, "attrs": { "hashes": [ "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", @@ -1445,6 +1456,7 @@ "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" ], "index": "pypi", + "markers": "python_full_version >= '3.6.0'", "version": "==4.12.2" }, "black": { @@ -1473,23 +1485,34 @@ "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==23.7.0" }, + "blinker": { + "hashes": [ + "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", + "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.6.2" + }, "boto3": { "hashes": [ - "sha256:84b7952858e9319968b0348d9894a91a6bb5f31e81a45c68044d040a12362abe", - "sha256:a6e711e0b6960c3a5b789bd30c5a18eea7263f2a59fc07f85efa5e04804e49d2" + "sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28", + "sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3" ], "index": "pypi", - "version": "==1.28.15" + "markers": "python_version >= '3.7'", + "version": "==1.28.32" }, "botocore": { "hashes": [ - "sha256:b3a0f787f275711875476cbe12a0123b2e6570b2f505e2fa509dcec3c5410b57", - "sha256:b46d1ce4e0cf42d28fdf61ce0c999904645d38b51cb809817a361c0cec16d487" + "sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664", + "sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c" ], "markers": "python_version >= '3.7'", - "version": "==1.31.15" + "version": "==1.31.32" }, "certifi": { "hashes": [ @@ -1651,126 +1674,110 @@ }, "click": { "hashes": [ - "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd", - "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5" + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "markers": "python_version >= '3.7'", - "version": "==8.1.6" + "version": "==8.1.7" }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f", - "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2", - "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a", - "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a", - "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01", - "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6", - "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7", - "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f", - "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02", - "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c", - "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063", - "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a", - "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5", - "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959", - "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97", - "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6", - "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f", - "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9", - "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5", - "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f", - "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562", - "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe", - "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9", - "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f", - "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb", - "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb", - "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1", - "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb", - "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250", - "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e", - "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511", - "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5", - "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59", - "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2", - "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d", - "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3", - "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4", - "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de", - "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9", - "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833", - "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0", - "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9", - "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d", - "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050", - "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d", - "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6", - "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353", - "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb", - "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e", - "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8", - "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495", - "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2", - "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd", - "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27", - "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1", - "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818", - "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4", - "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e", - "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850", - "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3" - ], - "markers": "python_version >= '3.7'", - "version": "==7.2.7" + "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34", + "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e", + "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7", + "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b", + "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3", + "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985", + "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95", + "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2", + "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a", + "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74", + "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd", + "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af", + "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54", + "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865", + "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214", + "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54", + "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe", + "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0", + "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321", + "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446", + "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e", + "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527", + "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12", + "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f", + "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f", + "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84", + "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479", + "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e", + "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873", + "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70", + "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0", + "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977", + "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51", + "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28", + "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1", + "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254", + "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1", + "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd", + "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689", + "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d", + "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543", + "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9", + "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637", + "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071", + "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482", + "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1", + "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b", + "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5", + "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a", + "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393", + "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a", + "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba" + ], + "markers": "python_version >= '3.8'", + "version": "==7.3.0" }, "cryptography": { "hashes": [ - "sha256:01f1d9e537f9a15b037d5d9ee442b8c22e3ae11ce65ea1f3316a41c78756b711", - "sha256:079347de771f9282fbfe0e0236c716686950c19dee1b76240ab09ce1624d76d7", - "sha256:182be4171f9332b6741ee818ec27daff9fb00349f706629f5cbf417bd50e66fd", - "sha256:192255f539d7a89f2102d07d7375b1e0a81f7478925b3bc2e0549ebf739dae0e", - "sha256:2a034bf7d9ca894720f2ec1d8b7b5832d7e363571828037f9e0c4f18c1b58a58", - "sha256:342f3767e25876751e14f8459ad85e77e660537ca0a066e10e75df9c9e9099f0", - "sha256:439c3cc4c0d42fa999b83ded80a9a1fb54d53c58d6e59234cfe97f241e6c781d", - "sha256:49c3222bb8f8e800aead2e376cbef687bc9e3cb9b58b29a261210456a7783d83", - "sha256:674b669d5daa64206c38e507808aae49904c988fa0a71c935e7006a3e1e83831", - "sha256:7a9a3bced53b7f09da251685224d6a260c3cb291768f54954e28f03ef14e3766", - "sha256:7af244b012711a26196450d34f483357e42aeddb04128885d95a69bd8b14b69b", - "sha256:7d230bf856164de164ecb615ccc14c7fc6de6906ddd5b491f3af90d3514c925c", - "sha256:84609ade00a6ec59a89729e87a503c6e36af98ddcd566d5f3be52e29ba993182", - "sha256:9a6673c1828db6270b76b22cc696f40cde9043eb90373da5c2f8f2158957f42f", - "sha256:9b6d717393dbae53d4e52684ef4f022444fc1cce3c48c38cb74fca29e1f08eaa", - "sha256:9c3fe6534d59d071ee82081ca3d71eed3210f76ebd0361798c74abc2bcf347d4", - "sha256:a719399b99377b218dac6cf547b6ec54e6ef20207b6165126a280b0ce97e0d2a", - "sha256:b332cba64d99a70c1e0836902720887fb4529ea49ea7f5462cf6640e095e11d2", - "sha256:d124682c7a23c9764e54ca9ab5b308b14b18eba02722b8659fb238546de83a76", - "sha256:d73f419a56d74fef257955f51b18d046f3506270a5fd2ac5febbfa259d6c0fa5", - "sha256:f0dc40e6f7aa37af01aba07277d3d64d5a03dc66d682097541ec4da03cc140ee", - "sha256:f14ad275364c8b4e525d018f6716537ae7b6d369c094805cae45300847e0894f", - "sha256:f772610fe364372de33d76edcd313636a25684edb94cee53fd790195f5989d14" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.2" + "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306", + "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84", + "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47", + "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d", + "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116", + "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207", + "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81", + "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087", + "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd", + "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507", + "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858", + "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae", + "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34", + "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906", + "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd", + "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922", + "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7", + "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4", + "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574", + "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1", + "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c", + "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e", + "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.3" }, "dill": { "hashes": [ "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.11'", "version": "==0.3.7" }, - "exceptiongroup": { - "hashes": [ - "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5", - "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f" - ], - "markers": "python_version < '3.11'", - "version": "==1.1.2" - }, "execnet": { "hashes": [ "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", @@ -1781,19 +1788,21 @@ }, "fakeredis": { "hashes": [ - "sha256:a99ef6e5642c31e91d36be78809fec3743e2bf7aaa682685b0d65a849fecd148", - "sha256:e304bc7addb2f862c3550cb7db58548418a0fadd4cd78a4de66464c84fbc2195" + "sha256:bc7a82e9caec80096659fd9a810b72d21f833574be26ff7b97955db626d60bac", + "sha256:f9c18d3dba81a470953cc042868b411e334109e065cde53a7a82beef6702a1de" ], "index": "pypi", - "version": "==2.17.0" + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.18.0" }, "flake8": { "hashes": [ - "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d", - "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d" + "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", + "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" ], "index": "pypi", - "version": "==4.0.1" + "markers": "python_full_version >= '3.8.1'", + "version": "==6.1.0" }, "flake8-datetimez": { "hashes": [ @@ -1801,6 +1810,7 @@ "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==20.10.0" }, "flake8-debugger": { @@ -1809,6 +1819,7 @@ "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==4.1.2" }, "flake8-mock": { @@ -1825,6 +1836,7 @@ "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==5.0.0" }, "flake8-tuple": { @@ -1837,11 +1849,12 @@ }, "flask": { "hashes": [ - "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b", - "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526" + "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc", + "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" ], "index": "pypi", - "version": "==2.2.2" + "markers": "python_version >= '3.8'", + "version": "==2.3.3" }, "freezegun": { "hashes": [ @@ -1849,6 +1862,7 @@ "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==1.2.2" }, "httmock": { @@ -1889,6 +1903,7 @@ "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.1.2" }, "jinja2": { @@ -1909,11 +1924,12 @@ }, "jsonschema": { "hashes": [ - "sha256:971be834317c22daaa9132340a51c01b50910724082c2c1a2ac87eeec153a3fe", - "sha256:fb3642735399fa958c0d2aad7057901554596c63349f4f6b283c493cf692a25d" + "sha256:043dc26a3845ff09d20e4420d6012a9c91c9aa8999fa184e7efcfeccb41e32cb", + "sha256:6e1e7569ac13be8139b2dd2c21a55d350066ee3f80df06c608b398cdc6f30e8f" ], "index": "pypi", - "version": "==4.18.4" + "markers": "python_version >= '3.8'", + "version": "==4.19.0" }, "jsonschema-specifications": { "hashes": [ @@ -2019,14 +2035,16 @@ "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.1.3" }, "mccabe": { "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" ], - "version": "==0.6.1" + "markers": "python_version >= '3.6'", + "version": "==0.7.0" }, "mock": { "hashes": [ @@ -2034,47 +2052,51 @@ "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==5.1.0" }, "moto": { "hashes": [ - "sha256:545afeb4df94dfa730e2d7e87366dc26b4a33c2891f462cbb049f040c80ed1ec", - "sha256:7d3bd748a34641715ba469c761f72fb8ec18f349987c98f5a0f9be85a07a9911" + "sha256:00fbae396fc48c3596e47b4e3267c1a41ca01c968de023beb68e774c63910b58", + "sha256:e4835912f05627b6a53b938562b717122230fb038d023819133f8526f60ed0a7" ], "index": "pypi", - "version": "==4.1.14" + "markers": "python_version >= '3.7'", + "version": "==4.2.0" }, "mypy": { "hashes": [ - "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042", - "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd", - "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2", - "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01", - "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7", - "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3", - "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816", - "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3", - "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc", - "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4", - "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b", - "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8", - "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c", - "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462", - "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7", - "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc", - "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258", - "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b", - "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9", - "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6", - "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f", - "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1", - "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828", - "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878", - "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f", - "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b" - ], - "index": "pypi", - "version": "==1.4.1" + "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315", + "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0", + "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373", + "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a", + "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161", + "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275", + "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693", + "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb", + "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65", + "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4", + "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb", + "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243", + "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14", + "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4", + "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1", + "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a", + "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160", + "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25", + "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12", + "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d", + "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92", + "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770", + "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2", + "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70", + "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb", + "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5", + "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.5.1" }, "mypy-extensions": { "hashes": [ @@ -2126,11 +2148,11 @@ }, "pycodestyle": { "hashes": [ - "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20", - "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f" + "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0", + "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.8.0" + "markers": "python_version >= '3.8'", + "version": "==2.11.0" }, "pycparser": { "hashes": [ @@ -2141,11 +2163,11 @@ }, "pyflakes": { "hashes": [ - "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c", - "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e" + "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", + "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.4.0" + "markers": "python_version >= '3.8'", + "version": "==3.1.0" }, "pylint": { "hashes": [ @@ -2153,6 +2175,7 @@ "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252" ], "index": "pypi", + "markers": "python_full_version >= '3.7.2'", "version": "==2.17.5" }, "pylint-mccabe": { @@ -2168,6 +2191,7 @@ "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==0.2.3" }, "pytest": { @@ -2176,6 +2200,7 @@ "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==7.4.0" }, "pytest-cov": { @@ -2184,6 +2209,7 @@ "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==4.1.0" }, "pytest-flask": { @@ -2192,6 +2218,7 @@ "sha256:fe25b39ad0db09c3d1fe728edecf97ced85e774c775db259a6d25f0270a4e7c9" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "pytest-mock": { @@ -2200,6 +2227,7 @@ "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==3.11.1" }, "pytest-sugar": { @@ -2216,6 +2244,7 @@ "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==3.3.1" }, "python-dateutil": { @@ -2270,23 +2299,25 @@ "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==6.0.1" }, "redis": { "hashes": [ - "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d", - "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c" + "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0", + "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120" ], "index": "pypi", - "version": "==4.6.0" + "markers": "python_version >= '3.7'", + "version": "==5.0.0" }, "referencing": { "hashes": [ - "sha256:47237742e990457f7512c7d27486394a9aadaf876cbfaa4be65b27b4f4d47c6b", - "sha256:c257b08a399b6c2f5a3510a50d28ab5dbc7bbde049bcaf954d43c446f83ab548" + "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", + "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" ], "markers": "python_version >= '3.8'", - "version": "==0.30.0" + "version": "==0.30.2" }, "requests": { "hashes": [ @@ -2294,15 +2325,17 @@ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.31.0" }, "responses": { "hashes": [ - "sha256:8a3a5915713483bf353b6f4079ba8b2a29029d1d1090a503c70b0dc5d9d0c7bd", - "sha256:c4d9aa9fc888188f0c673eff79a8dadbe2e75b7fe879dc80a221a06e0a68138f" + "sha256:205029e1cb334c21cb4ec64fc7599be48b859a0fd381a42443cdd600bfe8b16a", + "sha256:e6fbcf5d82172fecc0aa1860fd91e58cbfd96cee5e96da5b63fa6eb3caa10dd3" ], "index": "pypi", - "version": "==0.23.1" + "markers": "python_version >= '3.7'", + "version": "==0.23.3" }, "rpds-py": { "hashes": [ @@ -2409,11 +2442,11 @@ }, "s3transfer": { "hashes": [ - "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", - "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" + "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084", + "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861" ], "markers": "python_version >= '3.7'", - "version": "==0.6.1" + "version": "==0.6.2" }, "six": { "hashes": [ @@ -2446,14 +2479,6 @@ "markers": "python_version >= '3.7'", "version": "==2.3.0" }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - }, "tomlkit": { "hashes": [ "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", @@ -2487,27 +2512,26 @@ }, "types-pytz": { "hashes": [ - "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3", - "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac" + "sha256:1a7b8d4aac70981cfa24478a41eadfcd96a087c986d6f150d77e3ceb3c2bdfab", + "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e" ], "index": "pypi", - "version": "==2023.3.0.0" + "version": "==2023.3.0.1" }, "types-pyyaml": { "hashes": [ "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b", "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d" ], - "index": "pypi", "version": "==6.0.12.11" }, "types-redis": { "hashes": [ - "sha256:67c44c14369c33c2a300da2a50b5607c0fc888f7b85eeb7c73e15c78a0f05edd", - "sha256:efdef37dc0c04bf5786195651fd694f8bfdd693eac09ec4af46d90f72652558f" + "sha256:4f662060247a2363c7a8f0b7e52915d68960870ff16a749a891eabcf87ed0be4", + "sha256:5f179d10bd3ca995a8134aafcddfc3e12d52b208437c4529ef27e68acb301f38" ], "index": "pypi", - "version": "==4.6.0.3" + "version": "==4.6.0.5" }, "types-requests": { "hashes": [ @@ -2550,11 +2574,11 @@ }, "werkzeug": { "hashes": [ - "sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890", - "sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330" + "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8", + "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528" ], "markers": "python_version >= '3.8'", - "version": "==2.3.6" + "version": "==2.3.7" }, "wrapt": { "hashes": [ diff --git a/app/setup.py b/app/setup.py index 8b86c3fdc6..7b2be50872 100644 --- a/app/setup.py +++ b/app/setup.py @@ -293,6 +293,7 @@ def setup_secure_headers(application): strict_transport_security=True, strict_transport_security_max_age=31536000, frame_options="DENY", + x_xss_protection=True, ) diff --git a/scripts/extract_translation_templates.py b/scripts/extract_translation_templates.py index 31da93265c..b9ffb698ad 100644 --- a/scripts/extract_translation_templates.py +++ b/scripts/extract_translation_templates.py @@ -22,8 +22,8 @@ def get_template_content(filename, ignore_context=False): with open(filename, encoding="UTF-8") as file: return list( filter( - lambda l: all( - not l.startswith(param) for param in line_beginnings_to_ignore + lambda line: all( + not line.startswith(param) for param in line_beginnings_to_ignore ), file.readlines(), ) diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index 04298c8103..a4a5f769b7 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -1,6 +1,5 @@ # pylint: disable=too-many-lines import pytest -from mock import MagicMock from app.data_models import Answer, ListStore, ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore @@ -163,7 +162,12 @@ def address_question( @pytest.mark.parametrize( "question_title, answers, expected_title, expected_len", ( - ("Question title", [MagicMock()], "Question title", 1), + ( + "Question title", + [{"type": "Number", "id": "age-answer", "mandatory": True, "label": "Age"}], + "Question title", + 1, + ), ("Question title", [], "Question title", 0), ( "", @@ -1074,7 +1078,7 @@ def test_dynamic_answer_options( mock_schema, ): # Given - answer_id = (f"dynamic-{answer_type.lower()}-answer",) + answer_id = f"dynamic-{answer_type.lower()}-answer" answer_schema = { "id": answer_id, "label": "Some label", From 8992ef9e4958e144737c9056e2d11bcf9848ad24 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:17:05 +0100 Subject: [PATCH 280/567] WDIO: Explicit wait (#1199) --- tests/functional/helpers.js | 3 +++ ...ated_summary_repeating_and_static_answers.spec.js | 6 +----- .../list_collector_repeating_blocks.spec.js | 12 ++---------- .../spec/features/validation/sum/dynamic.spec.js | 6 +----- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 2f3ac3ef64..08e113495b 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -46,4 +46,7 @@ export const click = async (selector) => { // they fail, as we never navigated to that page. await $(selector).scrollIntoView({ block: "center", inline: "center" }); await $(selector).click(); + + // Allow time in case the click loads a new page. + await browser.pause(100); }; diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index d504b0f72d..52e90f8f0e 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -14,11 +14,7 @@ import SupermarketTransportCostPage from "../../../generated_pages/new_calculate import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; import { assertSummaryValues, click } from "../../../helpers"; -describe("Calculated summary with repeating answers", function () { - // These tests are flaky therefore we add a retry. The cause is unknown. - // :TODO: Revert this in future when we have a fix for this. - this.retries(5); - +describe("Calculated summary with repeating answers", () => { const summaryActions = 'dd[class="ons-summary__actions"]'; const dynamicAnswerChangeLink = (answerIndex) => $$(summaryActions)[answerIndex].$("a"); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 9515fe6923..e5906212ba 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -50,11 +50,7 @@ async function addCompany( await click(CompaniesRepeatingBlock2Page.submit()); } -describe("List Collector Repeating Blocks", function () { - // These tests are flaky therefore we add a retry. The cause is unknown. - // :TODO: Revert this in future when we have a fix for this. - this.retries(5); - +describe("List Collector Repeating Blocks", () => { describe("Given a normal journey through the list collector with repeating blocks, the answers can be submitted.", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); @@ -299,11 +295,7 @@ describe("List Collector Repeating Blocks", function () { }); }); -describe("Given a journey through the list collector with repeating blocks, in a mandatory section of hub questionnaire, the incomplete repeating blocks mark the list collector incomplete and thus navigate back there.", function () { - // These tests are flaky therefore we add a retry. The cause is unknown. - // :TODO: Revert this in future when we have a fix for this. - this.retries(5); - +describe("Given a journey through the list collector with repeating blocks, in a mandatory section of hub questionnaire, the incomplete repeating blocks mark the list collector incomplete and thus navigate back there.", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_with_hub.json"); }); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 2a9d470f71..b64fa33f01 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -11,11 +11,7 @@ import HubPage from "../../../../base_pages/hub.page"; import TotalBlockOtherPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block-other.page"; import { click } from "../../../../helpers"; -describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", function () { - // These tests are flaky therefore we add a retry. The cause is unknown. - // :TODO: Revert this in future when we have a fix for this. - this.retries(5); - +describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", () => { const summaryTitles = 'dt[class="ons-summary__item-title"]'; beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_dynamic_answers.json"); From a632da19b370880e5c5fb0dad1db9c861eb99d77 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Mon, 11 Sep 2023 14:46:40 +0100 Subject: [PATCH 281/567] WDIO: Migrate to builtin assertion library - 1 (#1200) --- .../calculated_summary_test_case.js | 206 +++++++------- ...mmary_repeating_and_static_answers.spec.js | 79 +++--- ...alculated_summary_repeating_blocks.spec.js | 82 +++--- ...lculated_summary_repeating_section.spec.js | 219 +++++++-------- .../dynamic_answers_list_value_source.spec.js | 96 +++---- ...summary_cross_section_dependencies.spec.js | 43 +-- ...ulated_summary_overlapping_answers.spec.js | 40 +-- ...lculated_summary_repeating_answers.spec.js | 158 +++++------ .../list_collector_repeating_blocks.spec.js | 111 ++++---- .../supplementary_data.spec.js | 265 +++++++++--------- 10 files changed, 655 insertions(+), 644 deletions(-) diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index b7de5d1757..42c7a84859 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -28,6 +28,7 @@ import SectionSummarySectionTwo from "../../../generated_pages/calculated_summar import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; import { click } from "../../../helpers"; +import { expect } from "@wdio/globals"; class TestCase { testCase(schema) { @@ -63,50 +64,48 @@ class TestCase { await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); await click(SixthNumberBlockPage.submit()); - const browserUrl = await browser.getUrl(); - - await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", async () => { - await expect(await browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + await expect(await browser.getTitle()).toBe("Grand total of previous values - A test schema to demo Calculated Summary"); }); it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £20.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).to.contain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toContain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toContain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toContain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toContain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toContain( "Second answer label also in currency total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).to.contain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toContain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toContain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toContain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toContain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toContain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toContain( "Fourth answer label also in total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("£2.34"); // Answers not included in calculation should not be shown - await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; - await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; - await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; - await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).toHaveLength(0); + await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).toHaveLength(0); + await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).toHaveLength(0); + await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).toHaveLength(0); }); it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).toContain( "/questionnaire/first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer", ); }); @@ -114,13 +113,13 @@ class TestCase { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); + await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); + await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -128,11 +127,11 @@ class TestCase { await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await click(FourthNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { @@ -140,12 +139,12 @@ class TestCase { await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await click(FourthAndAHalfNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { @@ -161,67 +160,65 @@ class TestCase { await click(FifthNumberBlockPage.submit()); await click(SixthNumberBlockPage.submit()); - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; - await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); + await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £9.36. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(CurrencyTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("1,467 cm"); // Answers included in calculation should be shown - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toContain("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toContain("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toContain("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toContain("678 cm"); }); it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { - await expect(await browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + await expect(await browser.getTitle()).toBe("Total Unit Values - A test schema to demo Calculated Summary"); }); it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("79%"); // Answers included in calculation should be shown - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toContain("Fifth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toContain("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toContain("Sixth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toContain("23%"); }); it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of number values entered to be 124.58. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); // Answers included in calculation should be shown - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toContain("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toContain("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toContain("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toContain("45.67"); }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { @@ -229,93 +226,96 @@ class TestCase { const content = await $("h1 + ul").getText(); const textsToAssert = [ - "Total currency values (if Q4 not skipped): £28.37", - "Total currency values (if Q4 skipped)): £9.36", - "Total unit values: 1,467", - "Total percentage values: 79", + "Total currency values: £9.36", + "Total unformatted unit values: 1,467", + "Total formatted unit values: 1,467 cm", + "Total unformatted percentage values: 79", + "Total formatted percentage values: 79%", "Total number values: 124.58", ]; - textsToAssert.forEach(async (text) => await expect(content).to.containasync(text)); + for (const text of textsToAssert) { + await expect(content).toContain(text); + } }); it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(CalculatedSummaryTotalConfirmation.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); await click(SetMinMaxBlockPage.submit()); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); // first incomplete block - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); // second incomplete block - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); // back to summary - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I am on a page with a placeholder containing a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the placeholder should display the updated value", async () => { @@ -324,7 +324,7 @@ class TestCase { await click(SkipFourthBlockPage.submit()); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await browser.url(CalculatedSummaryTotalConfirmation.url()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryTotalConfirmation.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummaryTotalConfirmation.pageName); const content = await $("h1 + ul").getText(); const textsToAssert = [ "Total currency values: £25.92", @@ -335,18 +335,20 @@ class TestCase { "Total number values: 124.58", ]; - textsToAssert.forEach((text) => expect(content).to.contain(text)); + for (const text of textsToAssert) { + await expect(content).toContain(text); + } await browser.url(SubmitPage.url()); }); it("Given I am on a page with a dependent question based on a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the question should display the updated value", async () => { await $(SubmitPage.setMinimumAnswerEdit()).click(); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); - await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).toContain( "Set minimum and maximum values based on your calculated summary total of £25.92", ); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £25.92"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £25.92"); await $(SetMinMaxBlockPage.setMinimum()).setValue(30.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); @@ -354,7 +356,7 @@ class TestCase { it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); } @@ -380,11 +382,11 @@ class TestCase { }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { - await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( "60 - calculated summary answer (previous section)", ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( "40 - calculated summary answer (current section)", ); }); @@ -392,11 +394,11 @@ class TestCase { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); await click(DependencyQuestionSectionTwo.submit()); - await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); + await expect(browser).toHaveUrlContaining(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); - await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); await click(MinMaxSectionTwo.submit()); @@ -410,12 +412,12 @@ class TestCase { await click(SkipFirstNumberBlockPageSectionOne.submit()); await click(SectionSummarySectionOne.submit()); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); - await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); + await expect(await $("body").getText()).toContain("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( "30 - calculated summary answer (previous section)", ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( "40 - calculated summary answer (current section)", ); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index 52e90f8f0e..28472d1aea 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -13,6 +13,7 @@ import SupermarketTransportPage from "../../../generated_pages/new_calculated_su import SupermarketTransportCostPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport-cost.page"; import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; import { assertSummaryValues, click } from "../../../helpers"; +import { expect } from "@wdio/globals"; describe("Calculated summary with repeating answers", () => { const summaryActions = 'dd[class="ons-summary__actions"]'; @@ -45,13 +46,13 @@ describe("Calculated summary with repeating answers", () => { it("Given I complete all list collector dynamic answers for two calculated summaries one of which also has static answers, I'm taken to each one in turn, showing the correct answers", async () => { await click(ExtraSpendingBlockPage.submit()); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?", ); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).to.contain("£550.00"); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).toContain("£550.00"); await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total visits to the shop to be 6. Is this correct?", ); await assertSummaryValues(["4", "2"]); @@ -59,17 +60,17 @@ describe("Calculated summary with repeating answers", () => { it("Given I click on a change link, when I use the previous button, I return to the calculated summary", async () => { await dynamicAnswerChangeLink(1).click(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await $(DynamicAnswerPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); }); it("Given I click on a change link, edit an answer and continue, I return to the calculated summary to reconfirm it", async () => { await dynamicAnswerChangeLink(0).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(3); await click(DynamicAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total visits to the shop to be 7. Is this correct?", ); await assertSummaryValues(["4", "3"]); @@ -82,19 +83,19 @@ describe("Calculated summary with repeating answers", () => { await click(ExtraSpendingBlockPage.submit()); // new question - await expect(await browser.getUrl()).to.contain(ExtraSpendingMethodBlockPage.pageName); + await expect(browser).toHaveUrlContaining(ExtraSpendingMethodBlockPage.pageName); await $(ExtraSpendingMethodBlockPage.yes()).click(); await click(ExtraSpendingMethodBlockPage.submit()); // then calculated summary - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?", ); // then jump straight back to section summary (as other calculated summary is unchanged await click(CalculatedSummarySpendingPage.submit()); - await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SummaryPage.pageName); }); it("Given I add a new item to the list, I return to the list collector block, then the dynamic answers, then both calculated summaries to confirm newly added answers", async () => { @@ -105,59 +106,59 @@ describe("Calculated summary with repeating answers", () => { await click(ListCollectorPage.submit()); // return to dynamic answer - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await $$(DynamicAnswerPage.inputs())[2].setValue(100); await $$(DynamicAnswerPage.inputs())[5].setValue(10); await $$(DynamicAnswerPage.inputs())[8].setValue(7); await click(DynamicAnswerPage.submit()); // first calc summary - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?", ); await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); // second calculated summary await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total visits to the shop to be 14. Is this correct?", ); await assertSummaryValues(["4", "3", "2"]); await click(CalculatedSummaryVisitsPage.submit()); - await expect(await browser.getUrl()).to.contain(SummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SummaryPage.pageName); }); it("Given I remove an item from the list which changes the calculated summaries, I return to each incomplete block only to confirm new dynamic answers and totals with answers removed", async () => { - await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Tesco"); - await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Lidl"); - await expect(await $(SummaryPage.supermarketsListLabel(3)).getText()).to.equal("Sainsburys"); - await expect(await $(SummaryPage.supermarketsListLabel(4)).isExisting()).to.be.false; + await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).toBe("Tesco"); + await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).toBe("Lidl"); + await expect(await $(SummaryPage.supermarketsListLabel(3)).getText()).toBe("Sainsburys"); + await expect(await $(SummaryPage.supermarketsListLabel(4)).isExisting()).toBe(false); await $(SummaryPage.supermarketsListRemoveLink(1)).click(); - await expect(await browser.getUrl()).to.contain(ListCollectorRemovePage.pageName); + await expect(browser).toHaveUrlContaining(ListCollectorRemovePage.pageName); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); // section is now incomplete as dynamic answers and calculated summary depend on the removed item - step through each incomplete block only - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await click(DynamicAnswerPage.submit()); // Tesco is now gone - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?", ); await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total visits to the shop to be 10. Is this correct?", ); await assertSummaryValues(["3", "7"]); await click(CalculatedSummaryVisitsPage.submit()); - await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).to.equal("Lidl"); - await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).to.equal("Sainsburys"); - await expect(await $(SummaryPage.supermarketsListLabel(3)).isExisting()).to.be.false; + await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).toBe("Lidl"); + await expect(await $(SummaryPage.supermarketsListLabel(2)).getText()).toBe("Sainsburys"); + await expect(await $(SummaryPage.supermarketsListLabel(3)).isExisting()).toBe(false); }); it("Given I proceed to the second section and enter a value greater than the calculated summary from the previous section, the correct error message is displayed", async () => { @@ -165,37 +166,37 @@ describe("Calculated summary with repeating answers", () => { await click(HubPage.submit()); await $(SupermarketTransportPage.weeklyCarTrips()).setValue(11); await click(SupermarketTransportPage.submit()); - await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 10"); + await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).toContain("Enter an answer less than or equal to 10"); }); it("Given I change my answer to a value less than the calculated summary from the previous section, I am able to proceed", async () => { await $(SupermarketTransportPage.weeklyCarTrips()).setValue(9); await click(SupermarketTransportPage.submit()); - await expect(await browser.getUrl()).to.contain(SupermarketTransportCostPage.pageName); + await expect(browser).toHaveUrlContaining(SupermarketTransportCostPage.pageName); }); it("Given I reach the final block, the calculated summary of dynamic answers is piped in correctly", async () => { await $(SupermarketTransportCostPage.weeklyTripsCost()).setValue(30); await click(SupermarketTransportCostPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryPipingPage.pageName); - await expect(await $("body").getText()).to.have.string("Total weekly supermarket spending: £380.00"); - await expect(await $("body").getText()).to.have.string("Total weekly supermarket visits: 10"); - await expect(await $("body").getText()).to.have.string("Total of supermarket visits by car: 9"); - await expect(await $("body").getText()).to.have.string("Total spending on parking: £30.00"); + await expect(browser).toHaveUrlContaining(CalculatedSummaryPipingPage.pageName); + await expect(await $("body").getText()).toContain("Total weekly supermarket spending: £380.00"); + await expect(await $("body").getText()).toContain("Total weekly supermarket visits: 10"); + await expect(await $("body").getText()).toContain("Total of supermarket visits by car: 9"); + await expect(await $("body").getText()).toContain("Total spending on parking: £30.00"); await click(CalculatedSummaryPipingPage.submit()); }); it("Given I return to section 1 and update the calculated summary used in section 2 validation, the progress of section 2 is updated", async () => { - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-1")).click(); await dynamicAnswerChangeLink(8).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(1); await click(DynamicAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryVisitsPage.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); await click(CalculatedSummaryVisitsPage.submit()); await click(SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Partially completed"); }); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index 91d1332712..37b472bdaa 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -12,6 +12,8 @@ import HubPage from "../../../base_pages/hub.page"; import FamilyJourneysPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/family-journeys.page"; import BlockSkipPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/block-skip.page"; import { assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; +import { expect } from "@wdio/globals"; + describe("Feature: Calculated Summary using Repeating Blocks", () => { before("Reaching the first calculated summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_blocks.json"); @@ -44,25 +46,25 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { }); it("Given I have a calculated summary using both list repeating block and static answers, When I reach the calculated summary page, Then I see the correct items and total.", async () => { - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00"]); - await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly expenditure travelling by car"); - await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly season ticket expenditure for travel by Bus"); - await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Bus"); - await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Monthly season ticket expenditure for travel by Plane"); - await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).to.contain("Additional monthly expenditure for travel by Plane"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Monthly expenditure travelling by car"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Monthly season ticket expenditure for travel by Bus"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Additional monthly expenditure for travel by Bus"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Monthly season ticket expenditure for travel by Plane"); + await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Additional monthly expenditure for travel by Plane"); await click(CalculatedSummarySpendingPage.submit()); }); it("Given I have a calculated summary using a single answer from a repeating block, When I reach the calculated summary page, Then I see the correct items and total", async () => { - await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 12. Is this correct?", ); await assertSummaryValues(["10", "2"]); - await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Bus"); - await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).to.contain("Monthly journeys by Plane"); + await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).toContain("Monthly journeys by Bus"); + await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).toContain("Monthly journeys by Plane"); await click(CalculatedSummaryCountPage.submit()); }); @@ -78,8 +80,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); @@ -87,8 +89,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I am on the first calculated summary, When I confirm the total, Then I see the second calculated summary with an updated total", async () => { await click(CalculatedSummarySpendingPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); - await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 18. Is this correct?", ); await assertSummaryValues(["10", "2", "6"]); @@ -97,26 +99,26 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I am on the first calculated summary, When I use one of the change links, Then I see the correct repeating block", async () => { await repeatingAnswerChangeLink(1).click(); - await expect(await browser.getUrl()).to.contain(TransportRepeatingBlock1Page.pageName); + await expect(browser).toHaveUrlContaining(TransportRepeatingBlock1Page.pageName); }); it("Given I have used a change link on a calculated summary to go back to the first repeating block, When I press continue, Then I see the calculated summary I came from", async () => { await click(TransportRepeatingBlock1Page.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); }); it("Given I am on a calculated summary with change links for repeating blocks, When I use a change link and click previous, Then I see the calculated summary I came from", async () => { await repeatingAnswerChangeLink(1).click(); await $(TransportRepeatingBlock1Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); }); it("Given I use a repeating block change link on the first calculated summary, When I edit my answer and press continue, Then I see the first calculated summary with a new correct total", async () => { await repeatingAnswerChangeLink(1).click(); await $(TransportRepeatingBlock1Page.transportCost()).setValue(60); await click(TransportRepeatingBlock1Page.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); @@ -127,8 +129,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await repeatingAnswerChangeLink(2).click(); await $(TransportRepeatingBlock2Page.transportCount()).setValue(12); await click(TransportRepeatingBlock2Page.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); - await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 24. Is this correct?", ); await assertSummaryValues(["10", "2", "12"]); @@ -139,8 +141,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(SectionOnePage.transportListRemoveLink(1)).click(); await $(RemoveTransportPage.yes()).click(); await click(RemoveTransportPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?", ); await assertSummaryValues(["£100.00", "£0.00", "£265.00", "£100.00", "£50.00"]); @@ -148,8 +150,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I have confirmed the first updated total, When I press continue, Then I see the next calculated summary to confirm that total too", async () => { await click(CalculatedSummarySpendingPage.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummaryCountPage.pageName); - await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 14. Is this correct?", ); await assertSummaryValues(["2", "12"]); @@ -159,23 +161,23 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(CalculatedSummaryCountPage.submit()); await click(SectionOnePage.submit()); await click(HubPage.submit()); - await expect(await $(FamilyJourneysPage.questionTitle()).getText()).to.contain("How many of your 14 journeys are to visit family?"); + await expect(await $(FamilyJourneysPage.questionTitle()).getText()).toContain("How many of your 14 journeys are to visit family?"); await $(FamilyJourneysPage.answer()).setValue(15); await click(FamilyJourneysPage.submit()); - await expect(await $(FamilyJourneysPage.singleErrorLink()).getText()).to.contain("Enter an answer less than or equal to 14"); + await expect(await $(FamilyJourneysPage.singleErrorLink()).getText()).toContain("Enter an answer less than or equal to 14"); }); it("Given I enter a value below the calculated summary from section 1, When I press Continue, Then I see my answer displayed on the next page", async () => { await $(FamilyJourneysPage.answer()).setValue(10); await click(FamilyJourneysPage.submit()); - await expect(await $(SectionTwoPage.familyJourneysQuestion()).getText()).to.contain("How many of your 14 journeys are to visit family?"); - await expect(await $(SectionTwoPage.familyJourneysAnswer()).getText()).to.contain("10"); + await expect(await $(SectionTwoPage.familyJourneysQuestion()).getText()).toContain("How many of your 14 journeys are to visit family?"); + await expect(await $(SectionTwoPage.familyJourneysAnswer()).getText()).toContain("10"); await click(SectionTwoPage.submit()); }); it("Given I use the add list item link, When I add a new item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-1")).click(); await $(SectionOnePage.transportListAddLink()).click(); await $(AddTransportPage.transportName()).selectByAttribute("value", "Tube"); @@ -188,8 +190,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(CalculatedSummarySpendingPage.submit()); await click(CalculatedSummaryCountPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Partially completed"); }); it("Given I complete section-2 again, When I remove a list item and return to the Hub, Then I see the progress of section 2 has reverted to Partially Complete", async () => { @@ -197,8 +199,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(FamilyJourneysPage.answer()).setValue(16); await click(FamilyJourneysPage.submit()); await click(SectionTwoPage.submit()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-1")).click(); await $(SectionOnePage.transportListRemoveLink(3)).click(); await $(RemoveTransportPage.yes()).click(); @@ -206,8 +208,8 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(CalculatedSummarySpendingPage.submit()); await click(CalculatedSummaryCountPage.submit()); await click(SectionOnePage.submit()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Partially completed"); }); it("Given I have a question which removes the list collector from the path, When I change my answer to the question removing the list collector and route backwards from the summary, Then I see the first calculated summary with an updated total", async () => { @@ -216,11 +218,11 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(BlockSkipPage.yes()).click(); await click(BlockSkipPage.submit()); // calculated summary progress is not altered by removing the list collector from the path so next location is summary page - await expect(await browser.getUrl()).to.contain(SectionOnePage.pageName); + await expect(browser).toHaveUrlContaining(SectionOnePage.pageName); await $(SectionOnePage.previous()).click(); // other calculated summary should not be on the path, so go straight back to the spending one which now has none of the list items - await expect(await browser.getUrl()).to.contain(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?", ); await assertSummaryValues(["£100.00"]); @@ -230,6 +232,6 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(CalculatedSummarySpendingPage.submit()); await click(SectionOnePage.submit()); // section 2 is now gone - await expect(await $$(HubPage.summaryItems()).length).to.equal(1); + await expect(await $$(HubPage.summaryItems()).length).toBe(1); }); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 5c08dd1731..543099f0d0 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -34,6 +34,7 @@ import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_su import DependencyQuestionSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; import { click } from "../../../helpers"; +import { expect } from "@wdio/globals"; describe("Feature: Calculated Summary Repeating Section", () => { describe("Given I have a Calculated Summary in a Repeating Section", () => { @@ -51,50 +52,48 @@ describe("Feature: Calculated Summary Repeating Section", () => { await getToFirstCalculatedSummary(); - const browserUrl = await browser.getUrl(); - - await expect(await browserUrl).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", async () => { - await expect(await browser.getTitle()).to.equal("Grand total of previous values - A test schema to demo Calculated Summary"); + await expect(await browser.getTitle()).toBe("Grand total of previous values - A test schema to demo Calculated Summary"); }); it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £20.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).to.contain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).to.contain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).to.contain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).to.contain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toContain("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toContain("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toContain("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toContain("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toContain( "Second answer label also in currency total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).to.contain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).to.contain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).to.contain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).to.contain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).to.contain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toContain("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toContain("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toContain("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toContain("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toContain("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toContain( "Fourth answer label also in total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("£2.34"); // Answers not included in calculation should not be shown - await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).to.be.empty; - await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).to.be.empty; - await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).to.be.empty; - await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).to.be.empty; + await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).toHaveLength(0); + await expect(await $$(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal())).toHaveLength(0); + await expect(await $$(NumberTotalPlaybackPage.fifthNumberAnswer())).toHaveLength(0); + await expect(await $$(NumberTotalPlaybackPage.sixthNumberAnswer())).toHaveLength(0); }); it("Given I reach the calculated summary page, Then the Change link url should contain return_to, return_to_answer_id and return_to_block_id query params", async () => { - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).to.contain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerEdit()).getAttribute("href")).toContain( "first-number-block/?return_to=calculated-summary&return_to_answer_id=first-number-answer&return_to_block_id=currency-total-playback#first-number-answer", ); }); @@ -102,13 +101,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); + await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain("currency-total-playback/#third-number-answer"); + await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -116,11 +115,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await click(FourthNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { @@ -128,12 +127,12 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await click(FourthAndAHalfNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).to.contain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { @@ -149,85 +148,83 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(FifthNumberBlockPage.submit()); await click(SixthNumberBlockPage.submit()); - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).to.be.empty; - await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).to.be.empty; - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); + await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £9.36. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(CurrencyTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("1,467 cm"); // Answers included in calculation should be shown - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).to.contain("Second answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).to.contain("789 cm"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).to.contain("Third answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).to.contain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toContain("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toContain("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toContain("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toContain("678 cm"); }); it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { - await expect(await browser.getTitle()).to.equal("Total Unit Values - A test schema to demo Calculated Summary"); + await expect(await browser.getTitle()).toBe("Total Unit Values - A test schema to demo Calculated Summary"); }); it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("79%"); // Answers included in calculation should be shown - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).to.contain("Fifth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).to.contain("56%"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).to.contain("Sixth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).to.contain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toContain("Fifth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toContain("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toContain("Sixth answer label percentage tota"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toContain("23%"); }); it("Given the calculated summary has a custom title with the list item position, When I am on the percentage calculated summary, Then the page title should use the custom title with the list item position", async () => { - await expect(await browser.getTitle()).to.equal("Percentage Calculated Summary: Person 1 - A test schema to demo Calculated Summary"); + await expect(await browser.getTitle()).toBe("Percentage Calculated Summary: Person 1 - A test schema to demo Calculated Summary"); }); it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of number values entered to be 124.58. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).to.contain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); // Answers included in calculation should be shown - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).to.contain("Fifth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).to.contain("78.91"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).to.contain("Sixth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).to.contain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toContain("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toContain("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toContain("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toContain("45.67"); }); it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", async () => { await click(NumberTotalPlaybackPage.submit()); - await expect(await browser.getUrl()).to.contain(BreakdownPage.pageName); + await expect(browser).toHaveUrlContaining(BreakdownPage.pageName); await $(BreakdownPage.answer1()).setValue(100.0); await $(BreakdownPage.answer2()).setValue(24.58); await click(BreakdownPage.submit()); - await expect(await browser.getUrl()).to.contain(SecondCurrencyTotalPlaybackPage.pageName); - await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(SecondCurrencyTotalPlaybackPage.pageName); + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of number values entered to be £124.58. Is this correct?", ); - await expect(await $("body").getText()).to.have.string("Enter two values that add up to the previous calculated summary total of £124.58"); - await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).to.contain("124.58"); + await expect(await $("body").getText()).toContain("Enter two values that add up to the previous calculated summary total of £124.58"); + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { @@ -236,89 +233,89 @@ describe("Feature: Calculated Summary Repeating Section", () => { const content = $("h1 + ul").getText(); const textsToAssert = ["Total currency values: £9.36", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; - textsToAssert.forEach(async (text) => await expect(content).to.contain(text)); + textsToAssert.forEach(async (text) => await expect(content).toContain(text)); }); it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(CalculatedSummaryTotalConfirmation.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); await click(SetMinMaxBlockPage.submit()); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I go to each incomplete page in turn before I return to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); // first incomplete block - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); // second incomplete block - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); // back to summary - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £15.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to £6.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); @@ -351,35 +348,35 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", async () => { - await expect(await browser.getUrl()).to.contain(HubPage.pageName); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + await expect(browser).toHaveUrlContaining(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); it("Given I change an answer with a dependent calculated summary question, When I return to the hub, Then only the section status for the repeating section I updated should be incomplete", async () => { - await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(browser).toHaveUrlContaining(HubPage.pageName); await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await $(SkipFourthBlockPage.yes()).click(); await click(SkipFourthBlockPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); it("Given I return to a partially completed section with a calculated summary, When I answer the dependent questions and return to the hub, Then the section status for the repeating section I updated should be complete", async () => { - await expect(await browser.getUrl()).to.contain(HubPage.pageName); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + await expect(browser).toHaveUrlContaining(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(await browser.getUrl()).to.contain(SetMinMaxBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.pageName); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + await expect(browser).toHaveUrlContaining(HubPage.pageName); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); }); @@ -414,11 +411,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { - await expect(await browser.getUrl()).to.contain(DependencyQuestionSectionTwo.pageName); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( "60 - calculated summary answer (previous section)", ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( "40 - calculated summary answer (current section)", ); }); @@ -426,11 +423,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); await click(DependencyQuestionSectionTwo.submit()); - await expect(await browser.getUrl()).to.contain(MinMaxSectionTwo.pageName); + await expect(browser).toHaveUrlContaining(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); - await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to £60.00"); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); await click(MinMaxSectionTwo.submit()); @@ -444,12 +441,12 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(SkipFirstNumberBlockPageSectionOne.submit()); await click(SectionSummarySectionOne.submit()); await $(HubPage.summaryRowLink("calculated-summary-section-1")).click(); - await expect(await $("body").getText()).to.have.string("30 - calculated summary answer (previous section)"); + await expect(await $("body").getText()).toContain("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( "30 - calculated summary answer (previous section)", ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).to.contain( + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( "40 - calculated summary answer (current section)", ); }); diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index e26e098ad4..120616bd54 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -9,6 +9,7 @@ import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_sourc import HubPage from "../../base_pages/hub.page"; import OnlineShoppingPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer-separate-section.page"; import { click } from "../../helpers"; +import { expect } from "@wdio/globals"; describe("Dynamic answers list value source", () => { const summaryTitles = ".ons-summary__item-title"; @@ -21,9 +22,9 @@ describe("Dynamic answers list value source", () => { it("Given list items have been added, When the dynamic answers are displayed, Then the correct answers should be visible", async () => { await addTwoSupermarkets(timeout); - await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); }); it("Given list items have been added, When additional items are added using add link, Then the correct dynamic answers are displayed", async () => { await $(DriverPage.yes()).click(); @@ -34,8 +35,8 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); - await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(2); await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); @@ -44,9 +45,9 @@ describe("Dynamic answers list value source", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); - await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); }); it("Given list items have been added and the dynamic answers are submitted, When the summary is displayed, Then the correct answers should be visible and have correct values", async () => { await addTwoSupermarkets(timeout); @@ -56,14 +57,14 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[3].setValue(7); await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(8); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(8); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("12%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).toBe("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).toBe("3"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).toBe("7"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).toBe(8); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).toBe(8); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are revisited, Then they should be visible and have correct values", async () => { await addTwoSupermarkets(timeout); @@ -73,13 +74,13 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); - await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).to.equal("12"); - await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).to.equal("21"); - await expect(await $$(DynamicAnswerPage.labels())[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $$(DynamicAnswerPage.labels())[1].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).toBe("12"); + await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).toBe("21"); + await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with different values, Then they should be displayed correctly on summary", async () => { await addTwoSupermarkets(timeout); @@ -94,8 +95,8 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[1].setValue(12); await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("12%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("12%"); }); it("Given list items have been added and the dynamic answers are submitted, When the summary edit answer link is used for dynamic answer, Then the focus is on correct answer option", async () => { await addTwoSupermarkets(timeout); @@ -103,13 +104,13 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[1].setValue(21); await setMinimumAndGetSectionSummary(timeout); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).to.be.true; + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).toBe(true); await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[1].$("a").click(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).to.be.true; + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).toBe(true); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with answers updated, Then they should be displayed correctly on summary", async () => { await addTwoSupermarkets(timeout); @@ -120,8 +121,8 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[0].setValue(21); await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("21%"); }); it("Given list items have been added and the dynamic answers are submitted, When the list items are removed and answers updated, Then they should be displayed correctly on summary", async () => { await addTwoSupermarkets(timeout); @@ -134,12 +135,12 @@ describe("Dynamic answers list value source", () => { await click(ListCollectorRemovePage.submit()); await click(DynamicAnswerPage.submit()); await click(DynamicAnswerOnlyPage.submit()); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(5); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(5); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).toBe(5); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).toBe(5); }); it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No' and subsequently changed back to 'Yes', Then all answers should re-appear on summary", async () => { await addTwoSupermarkets(timeout); @@ -151,27 +152,28 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); await click(DriverPage.submit()); - await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Tesco"); - await expect(await $("body").getText()).to.not.have.string("Percentage of shopping at Aldi"); + await expect(await $("body").getText()).not.toContain("Percentage of shopping at Tesco"); + await expect(await $("body").getText()).not.toContain("Percentage of shopping at Aldi"); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await click(DriverPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Tesco"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).to.equal("12%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).to.equal("Percentage of shopping at Aldi"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).to.equal("21%"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).to.equal("3"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).to.equal("7"); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).to.equal(8); - await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).to.equal(8); + + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Tesco"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("12%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).toBe("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("21%"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[2].getText()).toBe("3"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[3].getText()).toBe("7"); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).toBe(8); + await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).toBe(8); }); it("Given list items have been added, When the dynamic answers are displayed in a separate section, Then the correct answers should be visible", async () => { await addTwoSupermarketsAndGetToNextSection(timeout); - await expect(await $$(OnlineShoppingPage.labels())[0].getText()).to.equal("Percentage of online shopping at Tesco"); - await expect(await $$(OnlineShoppingPage.labels())[1].getText()).to.equal("Percentage of online shopping at Aldi"); - await expect(await $$(OnlineShoppingPage.labels()).length).to.equal(4); + await expect(await $$(OnlineShoppingPage.labels())[0].getText()).toBe("Percentage of online shopping at Tesco"); + await expect(await $$(OnlineShoppingPage.labels())[1].getText()).toBe("Percentage of online shopping at Aldi"); + await expect(await $$(OnlineShoppingPage.labels()).length).toBe(4); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index 2c0be8f826..c8adcd7deb 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -12,6 +12,7 @@ import FirstNumberBlockPartAPage from "../../../generated_pages/grand_calculated import FourthNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/fourth-number-block.page"; import tvChoiceBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/tv-choice-block.page"; import { click } from "../../../helpers"; +import { expect } from "@wdio/globals"; describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary", () => { @@ -41,9 +42,9 @@ describe("Feature: Grand Calculated Summary", () => { await click(tvChoiceBlockPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); await click(HubPage.submit()); - await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£330.00"); - await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).to.contain("£70.00"); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyAllPage.currencySection1()).getText()).toContain("£330.00"); + await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).toContain("£70.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £400.00. Is this correct?", ); await click(CurrencyAllPage.submit()); @@ -55,12 +56,12 @@ describe("Feature: Grand Calculated Summary", () => { await click(SkipCalculatedSummaryPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); // Currently the grand calculated summary remains 'Completed' because none of the answers have changed - await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £330.00. Is this correct?", ); - await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; + await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).toBe(false); }); it("Given I confirm the grand calculated summary, then edit an answer for question 3, the grand calculated summary updates to be incomplete, because this is a dependency", async () => { await click(CurrencyAllPage.submit()); @@ -70,12 +71,12 @@ describe("Feature: Grand Calculated Summary", () => { await click(ThirdNumberBlockPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); // Although the calculated summary is not on the path, the answer is still a grand calculated summary dependency, so it updates progress - await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £330.00. Is this correct?", ); - await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).to.be.false; + await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).toBe(false); await click(CurrencyAllPage.submit()); }); it("Given I change my response to include the calculated summary, When I press continue, Then I am routed to the new block that opens up", async () => { @@ -83,27 +84,27 @@ describe("Feature: Grand Calculated Summary", () => { await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); await $(SkipCalculatedSummaryPage.no()).click(); await click(SkipCalculatedSummaryPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); }); it("Given I confirm the calculated summary and the blocks following it are already complete, When I press submit, Then I am returned to the section summary anchored to the answer I edited initially", async () => { await click(CurrencyQuestion3Page.submit()); - await expect(await browser.getUrl()).to.contain("calculated-summary-section/#skip-answer-2"); + await expect(await browser.getUrl()).toContain("calculated-summary-section/#skip-answer-2"); }); it("Given I change an answer, When I press previous from the now incomplete calculated summary, Then I am routed to the block before the calculated summary", async () => { await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(120); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); await $(CurrencyQuestion3Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(SkipCalculatedSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SkipCalculatedSummaryPage.pageName); }); it("Given I complete the section, When I go back to the grand calculated summary, Then I see the new calculated summary included", async () => { await click(SkipCalculatedSummaryPage.submit()); await click(CurrencyQuestion3Page.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £450.00. Is this correct?", ); }); @@ -112,13 +113,13 @@ describe("Feature: Grand Calculated Summary", () => { await $(CurrencyQuestion3Page.thirdNumberAnswerPartBEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartB()).setValue(10); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(FourthNumberBlockPage.pageName); + await expect(browser).toHaveUrlContaining(FourthNumberBlockPage.pageName); await $(FourthNumberBlockPage.fourthNumber()).setValue(1); await click(FourthNumberBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyQuestion3Page.pageName); + await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); await click(CurrencyQuestion3Page.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyAllPage.pageName); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CurrencyAllPage.pageName); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £461.00. Is this correct?", ); await click(CurrencyAllPage.submit()); @@ -130,8 +131,8 @@ describe("Feature: Grand Calculated Summary", () => { await click(SkipFirstBlockPage.submit()); await click(QuestionsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.currencySection1()).getText()).to.contain("£30.00"); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CurrencyAllPage.currencySection1()).getText()).toContain("£30.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( "The grand calculated summary is calculated to be £161.00. Is this correct?", ); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index 3f072b714d..bea7b81751 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -9,6 +9,8 @@ import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_su import GrandCalculatedSummaryShoppingPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/grand-calculated-summary-shopping.page"; import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/section-1-summary.page"; import { click } from "../../../helpers"; +import { expect } from "@wdio/globals"; + describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary with overlapping answers", () => { before("completing the survey", async () => { @@ -16,7 +18,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(IntroductionBlockPage.submit()); // grand calculated summary should not be enabled until section-1 complete - await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); await click(HubPage.submit()); await $(Block1Page.q1A1()).setValue(100); @@ -32,7 +34,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(CalculatedSummary4Page.submit()); await click(Section1SummaryPage.submit()); await click(HubPage.submit()); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?", ); await click(GrandCalculatedSummaryShoppingPage.submit()); @@ -46,12 +48,12 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block1Page.submit()); // taken back to calculated summary - await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); await click(CalculatedSummary2Page.submit()); // then grand calculated summary - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?", ); }); @@ -63,22 +65,22 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it - await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); - await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toContain( "Total of eggs and cheese is calculated to be £700.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it - await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Total extra items cost is calculated to be £410.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then grand calculated summary - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £1,220.00. Is this correct?", ); }); @@ -90,22 +92,22 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it - await expect(await browser.getUrl()).to.contain(CalculatedSummary2Page.pageName); - await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toContain( "Total of eggs and cheese is calculated to be £800.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it - await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Total extra items cost is calculated to be £510.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then grand calculated summary - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummaryShoppingPage.pageName); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?", ); await click(GrandCalculatedSummaryShoppingPage.submit()); @@ -123,8 +125,8 @@ describe("Feature: Grand Calculated Summary", () => { await browser.url(HubPage.url()); // calculated summary 4 is not confirmed so GCS doesn't show - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index cc1f22b854..7248e5f112 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -31,6 +31,8 @@ import AnyUtilityBillsPage from "../../../generated_pages/grand_calculated_summa import Section4SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-4-summary.page"; import Section5SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-5-summary.page"; import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; +import { expect } from "@wdio/globals"; + describe("Feature: Grand Calculated Summary", () => { describe("Given I have a Grand Calculated Summary across multiple sections", () => { before("Reaching the grand calculated summary section", async () => { @@ -63,97 +65,97 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I click on the change link for a calculated summary, When I press continue, Then I am taken back to the grand calculated summary", async () => { - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for shopping and entertainment is calculated to be £415.00. Is this correct?", ); await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); await click(CalculatedSummary1Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); }); it("Given I click on the change link for a calculated summary then one for an answer, When I press previous twice, I am return to the calculated summary then grand calculated summary", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); await $(CalculatedSummary1Page.q1A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block1Page.pageName); + await expect(browser).toHaveUrlContaining(Block1Page.pageName); await $(Block1Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); await $(CalculatedSummary1Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); }); it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for games expenditure is calculated to be £15.00. Is this correct?", ); await $(CalculatedSummary4Page.q4A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block4Page.pageName); + await expect(browser).toHaveUrlContaining(Block4Page.pageName); await $(Block4Page.q4A1()).setValue(50); await click(Block4Page.submit()); // first taken back to the calculated summary which has updated - await expect(await browser.getUrl()).to.contain(CalculatedSummary4Page.pageName); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for games expenditure is calculated to be £60.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then taken back to the grand calculated summary which has also been updated correctly - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for shopping and entertainment is calculated to be £460.00. Is this correct?", ); }); it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for food expenditure is calculated to be £100.00. Is this correct?", ); // change first answer await $(CalculatedSummary1Page.q1A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block1Page.pageName); + await expect(browser).toHaveUrlContaining(Block1Page.pageName); await $(Block1Page.q1A1()).setValue(100); await click(Block1Page.submit()); // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing - await expect(await browser.getUrl()).to.contain(CalculatedSummary1Page.pageName); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for food expenditure is calculated to be £190.00. Is this correct?", ); // change another answer await $(CalculatedSummary1Page.q2A2Edit()).click(); - await expect(await browser.getUrl()).to.contain(Block2Page.pageName); + await expect(browser).toHaveUrlContaining(Block2Page.pageName); await $(Block2Page.q2A2()).setValue(400); await click(Block2Page.submit()); // back at updated calculated summary - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for food expenditure is calculated to be £550.00. Is this correct?", ); // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary await click(CalculatedSummary1Page.submit()); - await expect(await browser.getUrl()).to.contain(CalculatedSummary3Page.pageName); + await expect(browser).toHaveUrlContaining(CalculatedSummary3Page.pageName); await click(CalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary1Page.pageName); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary1Page.pageName); await click(GrandCalculatedSummary1Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for shopping and entertainment is calculated to be £910.00. Is this correct?", ); }); it("Given I edit an answer included in a grand calculated summary, the calculated summary sections should return to partially completed, and the grand calculated summary becomes unavailable.", async () => { await click(GrandCalculatedSummary2Page.submit()); - await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).toBe("Completed"); // Now edit an answer from section 2 and go back to the hub await $(HubPage.summaryRowLink("section-3")).click(); @@ -165,14 +167,14 @@ describe("Feature: Grand Calculated Summary", () => { await $(Block4Page.previous()).click(); // calculated summary section should be in progress - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); }); it("Given I confirm the calculated summary, When I return to the Hub, Then I see the grand calculated summary come back marked as partially completed", async () => { await $(HubPage.summaryRowLink("section-2")).click(); await click(CalculatedSummary4Page.submit()); - await expect(await $(HubPage.summaryRowState("section-3")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).toBe("Partially completed"); }); it("Given I set both answers to block 4 to zero which removes the Grand Calculated Summary from the path, I am routed back to the Hub after the calculated summary", async () => { @@ -184,13 +186,13 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block4Page.submit()); await click(CalculatedSummary4Page.submit()); // should be back at Hub, and grand calculated summary section not present - await expect(await browser.getUrl()).to.contain(HubPage.pageName); - await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(HubPage.pageName); + await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); }); it("Given I have a grand calculated summary section requiring completion of all previous sections, When I complete each section in turn, Then I don't see the grand calculated summary until all sections are complete, at which point I see it on the Hub", async () => { // no grand calculated summary section on the hub - await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); await click(HubPage.submit()); await $(AnyUtilityBillsPage.yes()).click(); @@ -214,7 +216,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(CalculatedSummary5Page.submit()); await click(Section4SummaryPage.submit()); // still no grand calculated summary yet - await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); await click(HubPage.submit()); await $(AnyStreamingServicesPage.yes()).click(); @@ -245,13 +247,13 @@ describe("Feature: Grand Calculated Summary", () => { await click(CalculatedSummary8Page.submit()); await click(Section5SummaryPage.submit()); // grand calculated summary now present - await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Not started"); }); it("Given I have a calculated summary of repeating answers and a calculated summary of dynamic answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { await click(HubPage.submit()); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £280.00. Is this correct?", ); assertSummaryValues(["£250.00", "£30.00", "£280.00"]); @@ -264,7 +266,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have 2 calculated summaries of list repeating block answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { await click(GrandCalculatedSummary3Page.submit()); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); assertSummaryValues(["45 GB", "55 GB", "100 GB"]); @@ -273,7 +275,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have multiple calculated summaries of static, repeating and dynamic answers, When I reach the grand calculated summary of them all, Then I see the correct total and items", async () => { await click(GrandCalculatedSummary4Page.submit()); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,130.00. Is this correct?", ); assertSummaryValues(["£550.00", "£300.00", "£0.00", "£250.00", "£30.00", "£1,130.00"]); @@ -289,10 +291,10 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I a grand calculated summary featuring repeating answers, When I click edit links to return to a dynamic answer then previous twice, Then I return to the grand calculated summary where I started", async () => { await $(GrandCalculatedSummary5Page.calculatedSummary5Edit()).click(); await repeatingAnswerChangeLink(0).click(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await $(DynamicAnswerPage.previous()).click(); await $(CalculatedSummary5Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); }); it("Given I have a grand calculated summary featuring repeating answers, When I edit a dynamic answer, Then I return to the calculated summary to confirm, and then each affected grand calculated summary in turn", async () => { @@ -301,13 +303,13 @@ describe("Feature: Grand Calculated Summary", () => { await $$(DynamicAnswerPage.inputs())[0].setValue("175"); await click(DynamicAnswerPage.submit()); await click(CalculatedSummary5Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £305.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,155.00. Is this correct?", ); }); @@ -317,7 +319,7 @@ describe("Feature: Grand Calculated Summary", () => { await repeatingAnswerChangeLink(2).click(); await $(StreamingServiceRepeatingBlock1Page.previous()).click(); await $(CalculatedSummary5Page.previous()).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); }); it("Given I have a grand calculated summary featuring repeating answers, When I edit a list repeating block answer, Then I return to the calculated summary to confirm, and then the grand calculated summary to confirm", async () => { @@ -326,13 +328,13 @@ describe("Feature: Grand Calculated Summary", () => { await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(12); await click(StreamingServiceRepeatingBlock1Page.submit()); await click(CalculatedSummary5Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £309.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); @@ -348,38 +350,38 @@ describe("Feature: Grand Calculated Summary", () => { await $$(DynamicAnswerPage.inputs())[3].setValue("40"); await click(DynamicAnswerPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-4")).getText()).to.contain("Partially completed"); - await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("section-4")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); }); it("Given I complete the in progress section, When I return to the Hub, Then I see the grand calculated summary section re-enabled, and partially completed", async () => { await $(HubPage.summaryRowLink("section-4")).click(); - await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for monthly spending on utility bills is calculated to be £315.00. Is this correct?", ); await click(CalculatedSummary5Page.submit()); await click(Section4SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); }); it("Given I return to the grand calculated summary section, When I go to each grand calculated summary, Then I see the correct new values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Completed"); }); it("Given I add a list item for the section with list repeating blocks, When I return to the hub before and after completing the section, Then I see the grand calculated summary go from unavailable, to enabled and in progress", async () => { @@ -393,73 +395,73 @@ describe("Feature: Grand Calculated Summary", () => { await click(StreamingServiceRepeatingBlock2Page.submit()); await $(AnyOtherStreamingServicesPage.no()).click(); await click(AnyOtherStreamingServicesPage.submit()); - await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for monthly expenditure on streaming services is calculated to be £44.00. Is this correct?", ); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-5")).getText()).to.contain("Partially completed"); - await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("section-5")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); await $(HubPage.summaryRowLink("section-5")).click(); await click(CalculatedSummary6Page.submit()); - await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toContain( "Total monthly internet usage on streaming services is calculated to be 50 GB. Is this correct?", ); await click(CalculatedSummary7Page.submit()); await click(Section5SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); }); it("Given I the grand calculated summary section is now incomplete, When I return to the section, Then I am taken to each updated grand calculated summary to confirm the new total", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £359.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for internet usage is calculated to be 105 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); }); it("Given I remove a list item involved in the grand calculated summary, When I confirm, Then I am taken to each affected calculated summary to reconfirm, and when I return to the Hub the grand calculated summary is in progress", async () => { - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Completed"); await $(HubPage.summaryRowLink("section-5")).click(); await $(Section5SummaryPage.streamingServicesListRemoveLink(1)).click(); await $(RemoveStreamingServicePage.yes()).click(); await click(RemoveStreamingServicePage.submit()); - await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toContain( "Calculated Summary for monthly expenditure on streaming services is calculated to be £34.00. Is this correct?", ); await click(CalculatedSummary6Page.submit()); - await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toContain( "Total monthly internet usage on streaming services is calculated to be 30 GB. Is this correct?", ); await click(CalculatedSummary7Page.submit()); await click(Section5SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); }); it("Given the section has reverted to partially complete, When I go back to the section, Then I am taken to each grand calculated summary to reconfirm with correct values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for internet usage is calculated to be 85 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(await browser.getUrl()).to.contain(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index e5906212ba..4b2d32c42a 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -12,6 +12,7 @@ import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; +import { expect } from "@wdio/globals"; const summaryValues = 'dd[class="ons-summary__values"]'; async function proceedToListCollector() { @@ -59,10 +60,10 @@ describe("List Collector Repeating Blocks", () => { }); it("The user is able to add companies, complete repeating blocks, and submit.", async () => { await proceedToListCollector(); - await addCompany("ONS", 123, 1, 1, 2023, true, true); + await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("GOV", 456, 2, 2, 2023, false, false); + await addCompany("GOV", "456", "2", "2", "2023", false, false); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); @@ -79,13 +80,13 @@ describe("List Collector Repeating Blocks", () => { }); it("The user is able to add companies and complete repeating blocks.", async () => { await proceedToListCollector(); - await addCompany("ONS", 123, 1, 1, 2023, true, true); + await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("GOV", 456, 2, 2, 2023, false, false); + await addCompany("GOV", "456", "2", "2", "2023", false, false); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("MOD", 789, 3, 3, 2023, true); + await addCompany("MOD", "789", "3", "3", "2023", true); }); it("The list collector shows all of the companies.", async () => { @@ -97,7 +98,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.listEditLink(2)).click(); await $(EditCompanyPage.companyOrBranchName()).setValue("Government"); await click(EditCompanyPage.submit()); - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.equal("Government"); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("Government"); }); it("The list collector allows removal of 'Government'", async () => { @@ -107,14 +108,14 @@ describe("List Collector Repeating Blocks", () => { }); it("The list collector does not show 'GOV' anymore.", async () => { - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.not.have.string("Government"); - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).to.equal("MOD"); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toContain("Government"); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("MOD"); }); it("The list collector can add more companies.", async () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("Council", 101, 4, 4, 2023, false, true); + await addCompany("Council", "101", "4", "4", "2023", false, true); }); it("The list collector shows all of the companies.", async () => { @@ -139,7 +140,7 @@ describe("List Collector Repeating Blocks", () => { it("The user is able to add companies complete some repeating blocks and leave others incomplete.", async () => { await proceedToListCollector(); - await addCompany("ONS", 123, 1, 1, 2023, true, true); + await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); @@ -151,10 +152,10 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); await click(AddCompanyPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.cancelAndReturn()).click(); await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); @@ -162,7 +163,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("NAV", 101, 4, 4, 2023, true, true); + await addCompany("NAV", "101", "4", "4", "2023", true, true); }); it("The list collector shows all of the companies as well as checkmarks on the complete items 1 and 4, but not on the incomplete items 3 and 4.", async () => { @@ -177,10 +178,10 @@ describe("List Collector Repeating Blocks", () => { it("Attempting to complete the list collector will navigate the user to the first incomplete block of the second list item.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("456"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); @@ -208,18 +209,18 @@ describe("List Collector Repeating Blocks", () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); }); it("Editing an answer from the section summary which does not affect progress and pressing continue returns the user to the section summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("No"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).to.contain("section-companies/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); }); it("Clicking a change link from the final summary and pressing previous or submit without changing an answer returns the user to the final summary anchored to the answer they clicked on", async () => { @@ -227,18 +228,18 @@ describe("List Collector Repeating Blocks", () => { await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); }); it("Editing an answer from the final summary which does not affect progress and pressing continue returns the user to the final summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("Yes"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).to.contain("submit/#any-other-trading-details-answer"); + await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); }); it("The list collector can now be submitted.", async () => { @@ -252,10 +253,10 @@ describe("List Collector Repeating Blocks", () => { }); it("The user is able to add companies, complete repeating blocks and navigate to the section summary.", async () => { await proceedToListCollector(); - await addCompany("ONS", 123, 1, 1, 2023, true, true); + await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await addCompany("GOV", 456, 2, 2, 2023, false); + await addCompany("GOV", "456", "2", "2", "2023", false); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(AnyOtherTradingDetailsPage.submit()); @@ -263,30 +264,30 @@ describe("List Collector Repeating Blocks", () => { }); it("Edit each type of answer on different items from the section summary.", async () => { - await expect(await $$(summaryValues)[8].getText()).to.have.string(456); + await expect(await $$(summaryValues)[8].getText()).toContain("456"); await repeatingAnswerChangeLink(8).click(); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[8].getText()).to.have.string(789); + await expect(await $$(summaryValues)[8].getText()).toContain("789"); - await expect(await $$(summaryValues)[4].getText()).to.have.string("1 January 2023"); + await expect(await $$(summaryValues)[4].getText()).toContain("1 January 2023"); await repeatingAnswerChangeLink(4).click(); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(4); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(4); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("4"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("4"); await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[4].getText()).to.have.string("4 April 2023"); + await expect(await $$(summaryValues)[4].getText()).toContain("4 April 2023"); - await expect(await $$(summaryValues)[5].getText()).to.have.string("Yes"); + await expect(await $$(summaryValues)[5].getText()).toContain("Yes"); await repeatingAnswerChangeLink(5).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[5].getText()).to.have.string("No"); + await expect(await $$(summaryValues)[5].getText()).toContain("No"); - await expect(await $$(summaryValues)[11].getText()).to.have.string("No answer provided"); + await expect(await $$(summaryValues)[11].getText()).toContain("No answer provided"); await repeatingAnswerChangeLink(11).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[11].getText()).to.have.string("Yes"); + await expect(await $$(summaryValues)[11].getText()).toContain("Yes"); }); it("The list collector can then be submitted", async () => { @@ -302,7 +303,7 @@ describe("Given a journey through the list collector with repeating blocks, in a it("The user is able to add a compete company and an incomplete company.", async () => { await proceedToListCollector(); - await addCompany("ONS", 123, 1, 1, 2023, true, true); + await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); @@ -312,14 +313,14 @@ describe("Given a journey through the list collector with repeating blocks, in a it("Navigating to the root of the questionnaire will redirect to the incomplete list collector, which we can then complete.", async () => { await browser.url("questionnaire/"); - await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); + await expect(await browser.getUrl()).toContain(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(456); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(2); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(2); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("456"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); @@ -337,14 +338,14 @@ describe("Given a journey through the list collector with repeating blocks, in a it("Navigating to the submit page of the section will redirect to the incomplete list collector, which we can then complete.", async () => { await browser.url("questionnaire/sections/section-companies/"); - await expect(await browser.getUrl()).to.contain(AnyOtherCompaniesOrBranchesPage.url()); + await expect(await browser.getUrl()).toContain(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); await click(CompaniesRepeatingBlock2Page.submit()); @@ -360,16 +361,16 @@ describe("Given a journey through the list collector with repeating blocks, in a await $(CompaniesRepeatingBlock1Page.previous()).click(); await $(EditCompanyPage.previous()).click(); await $(AnyOtherCompaniesOrBranchesPage.listEditLink(4)).click(); - await expect(await browser.getUrl()).to.contain(EditCompanyPage.pageName); + await expect(browser).toHaveUrlContaining(EditCompanyPage.pageName); await click(EditCompanyPage.submit()); await $(CompaniesRepeatingBlock1Page.previous()).click(); await $(EditCompanyPage.previous()).click(); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue(789); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue(3); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue(2023); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); await click(CompaniesRepeatingBlock2Page.submit()); @@ -379,6 +380,6 @@ describe("Given a journey through the list collector with repeating blocks, in a }); it("The section can now be submitted and and hub will redirect user to the next section.", async () => { await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ResponsiblePartyHubPage.pageName); + await expect(browser).toHaveUrlContaining(ResponsiblePartyHubPage.pageName); }); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 4321a25440..65ba6ce6c9 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -29,6 +29,7 @@ import TradingPage from "../../../generated_pages/supplementary_data/trading.pag import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page.js"; import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, click } from "../../../helpers"; import { getRandomString } from "../../../jwt_helper"; +import { expect } from "@wdio/globals"; describe("Using supplementary data", () => { const responseId = getRandomString(16); @@ -47,11 +48,11 @@ describe("Using supplementary data", () => { it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { await click(LoadedSuccessfullyBlockPage.submit()); await $(IntroductionBlockPage.acceptCookies()).click(); - await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain("You are completing this survey for Tesco"); - await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).to.contain( + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).toContain("You are completing this survey for Tesco"); + await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).toContain( "If the company details or structure have changed contact us on 01171231231", ); - await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).to.contain("Some supplementary guidance about the survey"); + await expect(await $(IntroductionBlockPage.guidancePanel(1)).getText()).toContain("Some supplementary guidance about the survey"); await click(IntroductionBlockPage.submit()); await click(HubPage.submit()); await $(EmailBlockPage.yes()).click(); @@ -59,13 +60,13 @@ describe("Using supplementary data", () => { }); it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { - await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).to.equal("Thursday 27 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).to.equal("Friday 28 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).to.equal("Saturday 29 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(3)).getText()).to.equal("Sunday 30 November 1947"); - await expect(await $(TradingPage.answerLabelByIndex(4)).getText()).to.equal("Monday 1 December 1947"); - await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).to.equal("Tuesday 2 December 1947"); - await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).to.equal("Wednesday 3 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).toBe("Thursday 27 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).toBe("Friday 28 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).toBe("Saturday 29 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(3)).getText()).toBe("Sunday 30 November 1947"); + await expect(await $(TradingPage.answerLabelByIndex(4)).getText()).toBe("Monday 1 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(5)).getText()).toBe("Tuesday 2 December 1947"); + await expect(await $(TradingPage.answerLabelByIndex(6)).getText()).toBe("Wednesday 3 December 1947"); await $(TradingPage.answerByIndex(3)).click(); await click(TradingPage.submit()); }); @@ -74,44 +75,44 @@ describe("Using supplementary data", () => { await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); await click(SalesBreakdownBlockPage.submit()); - await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 555,000"); + await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).toContain("Enter answers that add up to or are less than 555,000"); }); it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); await click(SalesBreakdownBlockPage.submit()); - await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).toContain( "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?", ); }); it("Given I have an interstitial block with all answers and supplementary data, When I reach this block, Then I see the placeholders rendered correctly", async () => { await click(CalculatedSummarySalesPage.submit()); - await expect(await $(Section1InterstitialPage.questionText()).getText()).to.contain("Summary of information provided for Tesco"); - await expect(await $("body").getText()).to.have.string("Telephone Number: 01171231231"); - await expect(await $("body").getText()).to.have.string("Email: contact@tesco.org"); - await expect(await $("body").getText()).to.have.string("Note Title: Value of total sales"); - await expect(await $("body").getText()).to.have.string("Note Description: Total value of goods sold during the period of the return"); - await expect(await $("body").getText()).to.have.string("Note Example Title: Including"); - await expect(await $("body").getText()).to.have.string("Note Example Description: Sales across all UK stores"); - await expect(await $("body").getText()).to.have.string("Incorporation Date: 27 November 1947"); - await expect(await $("body").getText()).to.have.string("Trading start date: 30 November 1947"); - await expect(await $("body").getText()).to.have.string("Guidance: Some supplementary guidance about the survey"); - await expect(await $("body").getText()).to.have.string("Total Uk Sales: £555,000.00"); - await expect(await $("body").getText()).to.have.string("Bristol sales: £333,000.00"); - await expect(await $("body").getText()).to.have.string("London sales: £111,000.00"); - await expect(await $("body").getText()).to.have.string("Sum of Bristol and London sales: £444,000.00"); + await expect(await $(Section1InterstitialPage.questionText()).getText()).toContain("Summary of information provided for Tesco"); + await expect(await $("body").getText()).toContain("Telephone Number: 01171231231"); + await expect(await $("body").getText()).toContain("Email: contact@tesco.org"); + await expect(await $("body").getText()).toContain("Note Title: Value of total sales"); + await expect(await $("body").getText()).toContain("Note Description: Total value of goods sold during the period of the return"); + await expect(await $("body").getText()).toContain("Note Example Title: Including"); + await expect(await $("body").getText()).toContain("Note Example Description: Sales across all UK stores"); + await expect(await $("body").getText()).toContain("Incorporation Date: 27 November 1947"); + await expect(await $("body").getText()).toContain("Trading start date: 30 November 1947"); + await expect(await $("body").getText()).toContain("Guidance: Some supplementary guidance about the survey"); + await expect(await $("body").getText()).toContain("Total Uk Sales: £555,000.00"); + await expect(await $("body").getText()).toContain("Bristol sales: £333,000.00"); + await expect(await $("body").getText()).toContain("London sales: £111,000.00"); + await expect(await $("body").getText()).toContain("Sum of Bristol and London sales: £444,000.00"); }); it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { await click(Section1InterstitialPage.submit()); - await expect(await $(Section1Page.emailQuestion()).getText()).to.contain("Is contact@tesco.org still the correct contact email for Tesco?"); - await expect(await $(Section1Page.sameEmailAnswer()).getText()).to.contain("Yes"); - await expect(await $(Section1Page.tradingQuestion()).getText()).to.contain("When did Tesco begin trading?"); - await expect(await $(Section1Page.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(Section1Page.salesBristolAnswer()).getText()).to.contain("£333,000.00"); - await expect(await $(Section1Page.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + await expect(await $(Section1Page.emailQuestion()).getText()).toContain("Is contact@tesco.org still the correct contact email for Tesco?"); + await expect(await $(Section1Page.sameEmailAnswer()).getText()).toContain("Yes"); + await expect(await $(Section1Page.tradingQuestion()).getText()).toContain("When did Tesco begin trading?"); + await expect(await $(Section1Page.tradingAnswer()).getText()).toContain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).toContain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(Section1Page.salesBristolAnswer()).getText()).toContain("£333,000.00"); + await expect(await $(Section1Page.salesLondonAnswer()).getText()).toContain("£111,000.00"); }); it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { @@ -121,7 +122,7 @@ describe("Using supplementary data", () => { await $(NewEmailPage.answer()).setValue("new.contact@gmail.com"); await click(NewEmailPage.submit()); await $(Section1Page.previous()).click(); - await expect(await $("body").getText()).to.have.string("Email: new.contact@gmail.com"); + await expect(await $("body").getText()).toContain("Email: new.contact@gmail.com"); await click(Section1InterstitialPage.submit()); await click(Section1Page.submit()); }); @@ -129,16 +130,16 @@ describe("Using supplementary data", () => { it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { await click(HubPage.submit()); // TODO once list collector content block is merged in update this test accordingly - await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).to.contain("Harry Potter"); - await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).to.contain("Clark Kent"); + await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).toContain("Harry Potter"); + await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).toContain("Clark Kent"); await $(ListCollectorEmployeesPage.no()).click(); await click(ListCollectorEmployeesPage.submit()); }); it("Given I have a list collector block using a supplementary list, When I reach the section summary, I see the supplementary list items in the list", async () => { // TODO once list collector content block is merged in update this test accordingly - await expect(await $(Section2Page.employeesListLabel(1)).getText()).to.contain("Harry Potter"); - await expect(await $(Section2Page.employeesListLabel(2)).getText()).to.contain("Clark Kent"); + await expect(await $(Section2Page.employeesListLabel(1)).getText()).toContain("Harry Potter"); + await expect(await $(Section2Page.employeesListLabel(2)).getText()).toContain("Clark Kent"); await click(Section2Page.submit()); }); @@ -157,16 +158,16 @@ describe("Using supplementary data", () => { await $(ListCollectorAdditionalPage.no()).click(); await click(ListCollectorAdditionalPage.submit()); await click(Section3Page.submit()); - await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); - await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Clark Kent"); - await expect(await $(HubPage.summaryItems("section-5-1")).getText()).to.contain("Jane Doe"); - await expect(await $(HubPage.summaryItems("section-5-2")).getText()).to.contain("John Smith"); + await expect(await $(HubPage.summaryItems("section-4-1")).getText()).toContain("Harry Potter"); + await expect(await $(HubPage.summaryItems("section-4-2")).getText()).toContain("Clark Kent"); + await expect(await $(HubPage.summaryItems("section-5-1")).getText()).toContain("Jane Doe"); + await expect(await $(HubPage.summaryItems("section-5-2")).getText()).toContain("John Smith"); await click(HubPage.submit()); }); it("Given I have repeating sections for both supplementary and dynamic list items, When I start a repeating section for a supplementary list item, Then I see static supplementary data correctly piped in", async () => { - await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Harry Potter start working for Tesco?"); - await expect(await $(LengthOfEmploymentPage.employmentStartLegend()).getText()).to.contain("Start date at Tesco"); + await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).toContain("When did Harry Potter start working for Tesco?"); + await expect(await $(LengthOfEmploymentPage.employmentStartLegend()).getText()).toContain("Start date at Tesco"); }); it("Given I have validation on the start date in the repeating section, When I enter a date before the incorporation date, Then I see an error message", async () => { @@ -174,76 +175,76 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.month()).setValue(1); await $(LengthOfEmploymentPage.year()).setValue(1930); await click(LengthOfEmploymentPage.submit()); - await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); + await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).toContain("Enter a date after 26 November 1947"); }); it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { await $(LengthOfEmploymentPage.year()).setValue(1990); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Harry Potter start working for Tesco?"); - await expect(await $(Section4Page.employmentStart()).getText()).to.contain("1 January 1990"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Harry Potter start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).toContain("1 January 1990"); }); it("Given I complete the repeating section for the other supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { await click(Section4Page.submit()); await click(HubPage.submit()); - await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Clark Kent start working for Tesco?"); + await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).toContain("When did Clark Kent start working for Tesco?"); await $(LengthOfEmploymentPage.day()).setValue(5); await $(LengthOfEmploymentPage.month()).setValue(6); await $(LengthOfEmploymentPage.year()).setValue(2011); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Clark Kent start working for Tesco?"); - await expect(await $(Section4Page.employmentStart()).getText()).to.contain("5 June 2011"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Clark Kent start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).toContain("5 June 2011"); }); it("Given I move onto the dynamic list items, When I start a repeating section for a dynamic list item, Then I see static supplementary data correctly piped in and the same validation and summary", async () => { await click(Section4Page.submit()); await click(HubPage.submit()); - await expect(await $(AdditionalLengthOfEmploymentPage.questionTitle()).getText()).to.contain("When did Jane Doe start working for Tesco?"); - await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).to.contain("Start date at Tesco"); + await expect(await $(AdditionalLengthOfEmploymentPage.questionTitle()).getText()).toContain("When did Jane Doe start working for Tesco?"); + await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).toContain("Start date at Tesco"); await $(AdditionalLengthOfEmploymentPage.day()).setValue(1); await $(AdditionalLengthOfEmploymentPage.month()).setValue(1); await $(AdditionalLengthOfEmploymentPage.year()).setValue(1930); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).to.contain("Enter a date after 26 November 1947"); + await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).toContain("Enter a date after 26 November 1947"); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2000); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did Jane Doe start working for Tesco?"); - await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("1 January 2000"); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toContain("When did Jane Doe start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toContain("1 January 2000"); await click(Section5Page.submit()); await click(HubPage.submit()); await $(AdditionalLengthOfEmploymentPage.day()).setValue(3); await $(AdditionalLengthOfEmploymentPage.month()).setValue(3); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2010); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).to.contain("When did John Smith start working for Tesco?"); - await expect(await $(Section5Page.additionalEmploymentStart()).getText()).to.contain("3 March 2010"); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toContain("When did John Smith start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toContain("3 March 2010"); await click(Section5Page.submit()); }); it("Given I have some repeating blocks with supplementary data, When I begin the section, Then I see the supplementary names rendered correctly", async () => { await click(HubPage.submit()); - await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).to.contain("Articles and equipment for sports or outdoor games"); - await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).to.contain("Kitchen Equipment"); + await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).toContain("Articles and equipment for sports or outdoor games"); + await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).toContain("Kitchen Equipment"); await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); }); it("Given I have repeating blocks with supplementary data, When I start the first repeating block, Then I see the supplementary data for the first list item", async () => { - await expect(await $("body").getHTML()).to.have.string("

      Include

      "); - await expect(await $("body").getHTML()).to.have.string("
    • for children's playgrounds
    • "); - await expect(await $("body").getHTML()).to.have.string("
    • swimming pools and paddling pools
    • "); - await expect(await $("body").getHTML()).to.have.string("

      Exclude

      "); - await expect(await $("body").getHTML()).to.have.string( + await expect(await $("body").getHTML()).toContain("

      Include

      "); + await expect(await $("body").getHTML()).toContain("
    • for children's playgrounds
    • "); + await expect(await $("body").getHTML()).toContain("
    • swimming pools and paddling pools
    • "); + await expect(await $("body").getHTML()).toContain("

      Exclude

      "); + await expect(await $("body").getHTML()).toContain( "
    • sports holdalls, gloves, clothing of textile materials, footwear, protective eyewear, rackets, balls, skates
    • ", ); - await expect(await $("body").getHTML()).to.have.string( + await expect(await $("body").getHTML()).toContain( "
    • for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics
    • ", ); - await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).to.contain( + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toContain( "Volume of sales for Articles and equipment for sports or outdoor games", ); - await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain( + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toContain( "Total volume produced for Articles and equipment for sports or outdoor games", ); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(100); @@ -255,11 +256,11 @@ describe("Using supplementary data", () => { // TODO once using list collector content, shouldn't need these two lines await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); - await expect(await $("body").getText()).to.have.string("Include"); - await expect(await $("body").getText()).to.have.string("pots and pans"); - await expect(await $("body").getText()).not.to.have.string("Exclude"); - await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).to.contain("Volume of sales for Kitchen Equipment"); - await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).to.contain("Total volume produced for Kitchen Equipment"); + await expect(await $("body").getText()).toContain("Include"); + await expect(await $("body").getText()).toContain("pots and pans"); + await expect(await $("body").getText()).not.toContain("Exclude"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toContain("Volume of sales for Kitchen Equipment"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toContain("Total volume produced for Kitchen Equipment"); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(50); await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(300); await click(ProductRepeatingBlock1Page.submit()); @@ -268,7 +269,7 @@ describe("Using supplementary data", () => { it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); - await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); @@ -277,7 +278,7 @@ describe("Using supplementary data", () => { }); it("Given I have another calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { - await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?", ); assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); @@ -286,9 +287,9 @@ describe("Using supplementary data", () => { }); it("Given I have dynamic answers using a supplementary list, When I reach the dynamic answer page, Then I see the correct supplementary data in the answer labels", async () => { - await expect(await $$(DynamicProductsPage.labels())[0].getText()).to.contain("Value of sales for Articles and equipment for sports or outdoor games"); - await expect(await $$(DynamicProductsPage.labels())[1].getText()).to.contain("Value of sales for Kitchen Equipment"); - await expect(await $$(DynamicProductsPage.labels())[2].getText()).to.contain("Value of sales from other categories"); + await expect(await $$(DynamicProductsPage.labels())[0].getText()).toContain("Value of sales for Articles and equipment for sports or outdoor games"); + await expect(await $$(DynamicProductsPage.labels())[1].getText()).toContain("Value of sales for Kitchen Equipment"); + await expect(await $$(DynamicProductsPage.labels())[2].getText()).toContain("Value of sales from other categories"); await $$(DynamicProductsPage.inputs())[0].setValue(110); await $$(DynamicProductsPage.inputs())[1].setValue(220); await $$(DynamicProductsPage.inputs())[2].setValue(330); @@ -296,7 +297,7 @@ describe("Using supplementary data", () => { }); it("Given I have a calculated summary of dynamic answers for a supplementary list, When I reach the calculated summary, Then I see the correct supplementary data in the title and labels", async () => { - await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).to.contain( + await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?", ); assertSummaryItems([ @@ -309,7 +310,7 @@ describe("Using supplementary data", () => { }); it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { - await expect(await $$(summaryRowTitles)[0].getText()).to.contain("Sales during the previous quarter"); + await expect(await $$(summaryRowTitles)[0].getText()).toContain("Sales during the previous quarter"); assertSummaryItems([ "Articles and equipment for sports or outdoor games", "Volume of sales for Articles and equipment for sports or outdoor games", @@ -331,15 +332,15 @@ describe("Using supplementary data", () => { sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", responseId, }); - await expect(await $(HubPage.summaryItems("section-4-1")).getText()).to.contain("Harry Potter"); - await expect(await $(HubPage.summaryItems("section-4-2")).getText()).to.contain("Bruce Wayne"); - await expect(await $(HubPage.summaryItems("section-5-1")).getText()).to.contain("Jane Doe"); - await expect(await $(HubPage.summaryItems("section-5-2")).getText()).to.contain("John Smith"); - await expect(await $(HubPage.summaryRowState("section-4-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-4-2")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowState("section-5-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-5-2")).getText()).to.equal("Completed"); - await expect(await $("body").getText()).to.not.have.string("Clark Kent"); + await expect(await $(HubPage.summaryItems("section-4-1")).getText()).toContain("Harry Potter"); + await expect(await $(HubPage.summaryItems("section-4-2")).getText()).toContain("Bruce Wayne"); + await expect(await $(HubPage.summaryItems("section-5-1")).getText()).toContain("Jane Doe"); + await expect(await $(HubPage.summaryItems("section-5-2")).getText()).toContain("John Smith"); + await expect(await $(HubPage.summaryRowState("section-4-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-4-2")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("section-5-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-5-2")).getText()).toBe("Completed"); + await expect(await $("body").getText()).not.toContain("Clark Kent"); }); it("Given I now have a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { @@ -348,8 +349,8 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.month()).setValue(10); await $(LengthOfEmploymentPage.year()).setValue(1999); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).to.contain("When did Bruce Wayne start working for Lidl?"); - await expect(await $(Section4Page.employmentStart()).getText()).to.contain("10 October 1999"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Bruce Wayne start working for Lidl?"); + await expect(await $(Section4Page.employmentStart()).getText()).toContain("10 October 1999"); await click(Section4Page.submit()); }); @@ -360,79 +361,79 @@ describe("Using supplementary data", () => { assertSummaryTitles(["Company Details", "Employees", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); // Company details - await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).to.contain("Is contact@lidl.org still the correct contact email for Lidl?"); - await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).to.contain("No"); - await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).to.contain("What is the new contact email for Lidl?"); - await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).to.contain("new.contact@gmail.com"); - await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).to.contain("When did Lidl begin trading?"); - await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).to.contain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).to.contain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).to.contain("£333,000.00"); - await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).to.contain("£111,000.00"); + await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).toContain("Is contact@lidl.org still the correct contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).toContain("No"); + await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).toContain("What is the new contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).toContain("new.contact@gmail.com"); + await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).toContain("When did Lidl begin trading?"); + await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).toContain("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).toContain("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).toContain("£333,000.00"); + await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).toContain("£111,000.00"); // Employees - await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[0].getText()).to.equal("Harry Potter"); - await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[1].getText()).to.equal("Bruce Wayne"); + await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[0].getText()).toBe("Harry Potter"); + await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[1].getText()).toBe("Bruce Wayne"); // Additional Employees - await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).to.contain("Do you have any additional employees to report on?"); - await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).to.contain("Yes"); - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[0].getText()).to.equal("Jane Doe"); - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[1].getText()).to.equal("John Smith"); + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).toContain("Do you have any additional employees to report on?"); + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).toContain("Yes"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[0].getText()).toBe("Jane Doe"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[1].getText()).toBe("John Smith"); // Harry Potter - await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).toBe( "When did Harry Potter start working for Lidl?", ); - await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 1990"); + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).toBe("1 January 1990"); // Bruce Wayne - await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryItems)[0].getText()).toBe( "When did Bruce Wayne start working for Lidl?", ); - await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryValues)[0].getText()).to.equal("10 October 1999"); + await expect(await $(ViewSubmittedResponsePage.employeeDetailQuestionsContent("0-1")).$$(summaryValues)[0].getText()).toBe("10 October 1999"); // Jane Doe - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryItems)[0].getText()).toBe( "When did Jane Doe start working for Lidl?", ); - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).to.equal("1 January 2000"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent(0)).$$(summaryValues)[0].getText()).toBe("1 January 2000"); // John Smith - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryItems)[0].getText()).toBe( "When did John Smith start working for Lidl?", ); - await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryValues)[0].getText()).to.equal("3 March 2010"); + await expect(await $(ViewSubmittedResponsePage.additionalEmployeeDetailQuestionsContent("0-2")).$$(summaryValues)[0].getText()).toBe("3 March 2010"); // Product details - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).toBe( "Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).toBe( "Volume of sales for Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).toBe( "Total volume produced for Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).to.equal("100 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).to.equal("200 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).to.equal("Kitchen Equipment"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).to.equal("Volume of sales for Kitchen Equipment"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).toBe("100 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).toBe("200 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).toBe("Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).toBe("Volume of sales for Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).toBe( "Total volume produced for Kitchen Equipment", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).to.equal("50 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).to.equal("300 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).to.equal("Sales during the previous quarter"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).to.equal( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).toBe("50 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).toBe("300 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).toBe( "Value of sales for Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[1].getText()).to.equal("Value of sales for Kitchen Equipment"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[2].getText()).to.equal("Value of sales for Groceries"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[3].getText()).to.equal("Value of sales from other categories"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[0].getText()).to.equal("£110.00"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[1].getText()).to.equal("£220.00"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[2].getText()).to.equal("No answer provided"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[3].getText()).to.equal("£330.00"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[1].getText()).toBe("Value of sales for Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[2].getText()).toBe("Value of sales for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[3].getText()).toBe("Value of sales from other categories"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[0].getText()).toBe("£110.00"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[1].getText()).toBe("£220.00"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[2].getText()).toBe("No answer provided"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[3].getText()).toBe("£330.00"); }); }); From 645503af7758c95f7ae8f7398e6d3f256f71fc73 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:38:49 +0100 Subject: [PATCH 282/567] Add validation for supplementary data lists property (#1198) * Verify lists are populated or in supplementary data * Simplify list validation * Validate lists every time survey launches --- app/questionnaire/questionnaire_schema.py | 26 +- app/questionnaire/variants.py | 2 +- app/routes/session.py | 45 +++- schemas/test/en/test_supplementary_data.json | 251 ++---------------- tests/functional/generate_pages.py | 4 +- .../supplementary_data.spec.js | 19 +- tests/integration/routes/test_session.py | 71 +++++ 7 files changed, 151 insertions(+), 267 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index d60687af0c..21ab288610 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -20,6 +20,12 @@ DEFAULT_LANGUAGE_CODE = "en" LIST_COLLECTORS_WITH_REPEATING_BLOCKS = {"ListCollector", "ListCollectorContent"} +LIST_COLLECTOR_BLOCKS = { + "ListCollector", + "ListCollectorContent", + "PrimaryPersonListCollector", + "RelationshipCollector", +} LIST_COLLECTOR_CHILDREN = [ "ListAddQuestion", @@ -135,7 +141,7 @@ def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: return ImmutableDict(self._answer_dependencies_map) @cached_property - # Type ignore: safe to assume _min_and_max_map return type + # Type ignore: make_immutable uses generic types so return type is manually specified def min_and_max_map(self) -> ImmutableDict[str, ImmutableDict[str, int]]: return make_immutable(self._min_and_max_map) # type: ignore @@ -271,6 +277,10 @@ def preview_enabled(self) -> bool: def parent_id_map(self) -> Any: return self.serialize(self._parent_id_map) + @cached_property + def supplementary_lists(self) -> frozenset[str]: + return frozenset(self.json.get("supplementary_data", {}).get("lists", [])) + @classmethod def serialize(cls, data: Any) -> Any: return make_immutable(data) @@ -354,14 +364,8 @@ def _get_blocks_by_id(self) -> dict[str, ImmutableDict]: for block in group["blocks"]: block_id = block["id"] self._parent_id_map[block_id] = group["id"] - blocks[block_id] = block - if block["type"] in { - "ListCollector", - "ListCollectorContent", - "PrimaryPersonListCollector", - "RelationshipCollector", - }: + if block["type"] in LIST_COLLECTOR_BLOCKS: self._list_collector_section_ids_by_list_name[ block["for_list"] ].append(self._parent_id_map[group["id"]]) @@ -933,9 +937,9 @@ def get_add_block_for_list_collector( "ListCollector": "add_block", "PrimaryPersonListCollector": "add_or_edit_block", } - add_block: ImmutableDict = list_collector[ - add_block_map[list_collector["type"]] - ] + add_block: ImmutableDict | None = list_collector.get( + add_block_map.get(list_collector["type"]) + ) return add_block def get_edit_block_for_list_collector( diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index bcfa245d8e..b9b79bcc0c 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -146,7 +146,7 @@ def transform_variants( output_block["content"] = content - if block["type"] in ("ListCollector", "PrimaryPersonListCollector"): + if block["type"] in {"ListCollector", "PrimaryPersonListCollector"}: list_operations = ["add_block", "edit_block", "remove_block"] for list_operation in list_operations: if block.get(list_operation): diff --git a/app/routes/session.py b/app/routes/session.py index 900a29c27b..5f595310c7 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -119,7 +119,9 @@ def login() -> Response: logger.info("decrypted token and parsed metadata") with create_session_questionnaire_store(claims) as questionnaire_store: - _set_questionnaire_supplementary_data(questionnaire_store, metadata) + _set_questionnaire_supplementary_data( + questionnaire_store=questionnaire_store, metadata=metadata, schema=g.schema + ) cookie_session["expires_in"] = get_session_timeout_in_seconds(g.schema) @@ -139,23 +141,33 @@ def login() -> Response: def _set_questionnaire_supplementary_data( - questionnaire_store: QuestionnaireStore, metadata: MetadataProxy + *, + questionnaire_store: QuestionnaireStore, + metadata: MetadataProxy, + schema: QuestionnaireSchema, ) -> None: """ If the survey metadata has an sds dataset id, and it either doesn't match what it stored, or there is no stored supplementary data - then fetch it and add it to the store - """ - if not (new_sds_dataset_id := metadata["sds_dataset_id"]): - return + then fetch it, verify any schema supplementary lists are included in the fetched data, and add it to the questionnaire store + Validation of the supplementary lists must be performed every time a survey launches, not just when the supplementary data is fetched + as it is possible that the survey has changed but the dataset hasn't so the validity could have changed. + """ existing_sds_dataset_id = ( questionnaire_store.metadata.survey_metadata["sds_dataset_id"] if questionnaire_store.metadata and questionnaire_store.metadata.survey_metadata else None ) - if existing_sds_dataset_id == new_sds_dataset_id: - # no need to fetch again + if ( + not (new_sds_dataset_id := metadata["sds_dataset_id"]) + or existing_sds_dataset_id == new_sds_dataset_id + ): + # no need to fetch: either no supplementary data or it hasn't changed, just validate lists + _validate_supplementary_data_lists( + supplementary_data=questionnaire_store.supplementary_data_store.raw_data, + schema=schema, + ) return supplementary_data = get_supplementary_data_v1( @@ -169,9 +181,26 @@ def _set_questionnaire_supplementary_data( survey_id=metadata["survey_id"], sds_dataset_id=new_sds_dataset_id, ) + _validate_supplementary_data_lists( + supplementary_data=supplementary_data["data"], schema=schema + ) questionnaire_store.set_supplementary_data(supplementary_data["data"]) +def _validate_supplementary_data_lists( + *, supplementary_data: dict, schema: QuestionnaireSchema +) -> None: + """ + Validates that any lists the schema requires (which are those in the supplementary_data.lists property) + are included in the supplementary data + """ + supplementary_lists = supplementary_data.get("items", {}).keys() + if missing := schema.supplementary_lists - supplementary_lists: + raise ValidationError( + f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" + ) + + def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: # Type ignore: decrypted_token["exp"] will return a valid timestamp with compatible typing expires_at = datetime.fromtimestamp(decrypted_token["exp"], tz=timezone.utc) # type: ignore diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json index ac1c0336ab..09f6ba27da 100644 --- a/schemas/test/en/test_supplementary_data.json +++ b/schemas/test/en/test_supplementary_data.json @@ -29,6 +29,9 @@ "type": "string" } ], + "supplementary_data": { + "lists": ["employees", "products"] + }, "questionnaire_flow": { "type": "Hub", "options": { @@ -631,138 +634,32 @@ { "id": "section-2", "title": "Employees", - "summary": { - "show_on_completion": true, - "items": [ - { - "type": "List", - "for_list": "employees", - "title": "Employees", - "empty_list_text": "There are no employees", - "add_link_text": "DO NOT use this link to add another employee, this will be replaced with list collector content" - } - ], - "show_non_item_answers": true - }, "groups": [ { "id": "employee-reporting", "blocks": [ { "id": "list-collector-employees", - "type": "ListCollector", + "type": "ListCollectorContent", + "page_title": "Employees", "for_list": "employees", - "question": { - "id": "confirmation-employee-question", - "type": "General", - "title": "Do you need to add any more employees?", - "guidance": { - "contents": [ - { - "title": "Important guidance", - "description": "This list collector is a placeholder until it can be replaced with a list collector content block - do not add any people to this list because it won’t work correctly" - } - ] - }, - "answers": [ + "content": { + "title": "Employees", + "contents": [ { - "id": "list-collector-employees-answer", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RedirectToListAddBlock" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - }, - "add_block": { - "id": "add-employee", - "type": "ListAddQuestion", - "cancel_text": "Don’t need to add any other employees?", - "question": { - "id": "add-employee-question", - "type": "General", - "title": "What is the name of the employee?", - "answers": [ - { - "id": "employee-first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "employee-last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "edit_block": { - "id": "edit-employee", - "type": "ListEditQuestion", - "cancel_text": "Don’t need to change anything?", - "question": { - "id": "edit-employee-question", - "type": "General", - "title": "What is the name of the employee?", - "answers": [ - { - "id": "employee-first-name", - "label": "First name", - "mandatory": true, - "type": "TextField" - }, - { - "id": "employee-last-name", - "label": "Last name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "remove_block": { - "id": "remove-employee", - "type": "ListRemoveQuestion", - "cancel_text": "Don’t need to remove this employee?", - "question": { - "id": "remove-employee-question", - "type": "General", - "title": "Are you sure you want to remove this employee?", - "warning": "All of the information about this employee will be deleted", - "answers": [ - { - "id": "remove-confirmation", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RemoveListItemAndAnswers" - } - }, + "definition": { + "title": "Company employees", + "contents": [ { - "label": "No", - "value": "No" + "description": "List of previously reported employees." } ] } - ] - } + }, + { + "description": "You have previously reported on the above employees. Press continue to proceed to the next section where you can add any additional employees." + } + ] }, "summary": { "title": "employees", @@ -1310,7 +1207,6 @@ "type": "List", "for_list": "products", "title": "Products", - "add_link_text": "DO NOT use this link to add another product, this will be replaced with list collector content", "empty_list_text": "There are no products" } ], @@ -1322,118 +1218,17 @@ "blocks": [ { "id": "list-collector-products", - "type": "ListCollector", + "type": "ListCollectorContent", "for_list": "products", - "question": { - "id": "confirmation-product-question", - "type": "General", - "title": "Do you need to add any more products?", - "guidance": { - "contents": [ - { - "title": "Important guidance", - "description": "This list collector is a placeholder until it can be replaced with a list collector content block - do not add any products to this list because it won’t work correctly" - }, - { - "description": "Until this can be replaced with a list collector content block, keep clicking no and pressing save and continue to complete the repeating blocks" - } - ] - }, - "answers": [ + "page_title": "Products", + "content": { + "title": "Products", + "contents": [ { - "id": "list-collector-products-answer", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RedirectToListAddBlock" - } - }, - { - "label": "No", - "value": "No" - } - ] + "description": "You have previously provided information for the above products. Please press continue to proceed to questions on value and volume of sales." } ] }, - "add_block": { - "id": "add-product", - "type": "ListAddQuestion", - "cancel_text": "Don’t need to add any other products?", - "question": { - "id": "add-product-question", - "type": "General", - "title": "What is the name of the product?", - "guidance": { - "contents": [ - { - "description": "This uses a different employees list, and the items from this list and the supplementary list will then be used in repeating sections" - } - ] - }, - "answers": [ - { - "id": "product-name", - "label": "Product name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "edit_block": { - "id": "edit-product", - "type": "ListEditQuestion", - "cancel_text": "Don’t need to change anything?", - "question": { - "id": "edit-product-question", - "type": "General", - "title": "What is the name of the product?", - "answers": [ - { - "id": "product-name", - "label": "Product name", - "mandatory": true, - "type": "TextField" - } - ] - } - }, - "remove_block": { - "id": "remove-product", - "type": "ListRemoveQuestion", - "cancel_text": "Don’t need to remove this product?", - "question": { - "id": "remove-product-question", - "type": "General", - "title": "Are you sure you want to remove this product?", - "warning": "All of the information about this product will be deleted", - "answers": [ - { - "id": "remove-confirmation", - "mandatory": true, - "type": "Radio", - "options": [ - { - "label": "Yes", - "value": "Yes", - "action": { - "type": "RemoveListItemAndAnswers" - } - }, - { - "label": "No", - "value": "No" - } - ] - } - ] - } - }, "repeating_blocks": [ { "id": "product-repeating-block-1", diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 239f8d9e36..db798ebdd9 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -988,9 +988,11 @@ def process_block( process_question(question, page_spec, num_questions, page_name) if block["type"] == "ListCollector": - page_spec.write(LIST_SUMMARY_LABEL_GETTER) page_spec.write(LIST_SUMMARY_EDIT_LINK_GETTER) page_spec.write(LIST_SUMMARY_REMOVE_LINK_GETTER) + + if block["type"] in {"ListCollector", "ListCollectorContent"}: + page_spec.write(LIST_SUMMARY_LABEL_GETTER) page_spec.write(LIST_SUMMARY_LIST_GETTER) if block["type"] == "UnrelatedQuestion": diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 65ba6ce6c9..cbb25a4c92 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -19,7 +19,6 @@ import ProductRepeatingBlock1Page from "../../../generated_pages/supplementary_d import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; -import Section2Page from "../../../generated_pages/supplementary_data/section-2-summary.page.js"; import Section3Page from "../../../generated_pages/supplementary_data/section-3-summary.page.js"; import Section4Page from "../../../generated_pages/supplementary_data/section-4-summary.page.js"; import Section5Page from "../../../generated_pages/supplementary_data/section-5-summary.page.js"; @@ -129,20 +128,11 @@ describe("Using supplementary data", () => { it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { await click(HubPage.submit()); - // TODO once list collector content block is merged in update this test accordingly await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).toContain("Harry Potter"); await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).toContain("Clark Kent"); - await $(ListCollectorEmployeesPage.no()).click(); await click(ListCollectorEmployeesPage.submit()); }); - it("Given I have a list collector block using a supplementary list, When I reach the section summary, I see the supplementary list items in the list", async () => { - // TODO once list collector content block is merged in update this test accordingly - await expect(await $(Section2Page.employeesListLabel(1)).getText()).toContain("Harry Potter"); - await expect(await $(Section2Page.employeesListLabel(2)).getText()).toContain("Clark Kent"); - await click(Section2Page.submit()); - }); - it("Given I add some additional employees via a list collector, When I return to the Hub, Then I see new enabled sections for the supplementary list items, and my added ones", async () => { await click(HubPage.submit()); await $(AnyAdditionalEmployeesPage.yes()).click(); @@ -226,7 +216,6 @@ describe("Using supplementary data", () => { await click(HubPage.submit()); await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).toContain("Articles and equipment for sports or outdoor games"); await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).toContain("Kitchen Equipment"); - await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); }); @@ -253,8 +242,6 @@ describe("Using supplementary data", () => { it("Given I have repeating blocks with supplementary data, When I start the second repeating block, Then I see the supplementary data for the second list item", async () => { await click(ProductRepeatingBlock1Page.submit()); - // TODO once using list collector content, shouldn't need these two lines - await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); await expect(await $("body").getText()).toContain("Include"); await expect(await $("body").getText()).toContain("pots and pans"); @@ -267,8 +254,8 @@ describe("Using supplementary data", () => { }); it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { - await $(ListCollectorProductsPage.no()).click(); await click(ListCollectorProductsPage.submit()); + await expect(await browser.getUrl()).toContain(CalculatedSummaryVolumeSalesPage.pageName); await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); @@ -371,10 +358,6 @@ describe("Using supplementary data", () => { await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).toContain("£333,000.00"); await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).toContain("£111,000.00"); - // Employees - await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[0].getText()).toBe("Harry Potter"); - await expect(await $(ViewSubmittedResponsePage.employeeReportingContent(0)).$$(summaryItems)[1].getText()).toBe("Bruce Wayne"); - // Additional Employees await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).toContain("Do you have any additional employees to report on?"); await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).toContain("Yes"); diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 026d581d1d..626f324a31 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -3,6 +3,7 @@ import responses from freezegun import freeze_time +from marshmallow import ValidationError from mock.mock import patch from sdc.crypto.key_store import KeyStore @@ -119,54 +120,66 @@ def test_patch_session_expiry_extends_session(self): self.assertEqual(parsed_json["expires_at"], expected_expires_at) @patch("app.routes.session.get_supplementary_data_v1") + @patch("app.routes.session._validate_supplementary_data_lists") @patch( "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" ) def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( self, mock_set, + mock_validate, mock_get, ): self.launchSupplementaryDataSurvey() self.assertStatusOK() mock_get.assert_called_once() mock_set.assert_called_once() + mock_validate.assert_called_once() @patch("app.routes.session.get_supplementary_data_v1") + @patch("app.routes.session._validate_supplementary_data_lists") @patch( "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" ) def test_supplementary_data_is_reloaded_when_changed_sds_dataset_id_in_metadata( self, mock_set, + mock_validate, mock_get, ): self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="first") self.assertStatusOK() mock_set.assert_called_once() mock_get.assert_called_once() + mock_validate.assert_called_once() self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="second") self.assertStatusOK() self.assertEqual(mock_get.call_count, 2) self.assertEqual(mock_set.call_count, 2) + self.assertEqual(mock_validate.call_count, 2) @patch("app.routes.session.get_supplementary_data_v1") + @patch("app.routes.session._validate_supplementary_data_lists") @patch( "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" ) def test_supplementary_data_is_not_reloaded_when_same_sds_dataset_id_in_metadata( self, mock_set, + mock_validate, mock_get, ): self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") self.assertStatusOK() mock_set.assert_called_once() mock_get.assert_called_once() + mock_validate.assert_called_once() self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") self.assertStatusOK() mock_get.assert_called_once() mock_set.assert_called_once() + # validation should happen twice regardless + self.assertEqual(mock_validate.call_count, 2) def test_supplementary_data_raises_500_error_when_sds_api_request_fails(self): with patch( @@ -216,6 +229,64 @@ def test_supplementary_data_raises_500_error_when_missing_supplementary_data_key self.assert_supplementary_data_500_page() + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data", + ) + def test_supplementary_data_raises_500_error_when_missing_required_lists( + self, mock_set, mock_get + ): + """Tests that if the supplementary data being loaded does not cover all the dependent lists for the schema + that a validation error is raised""" + mock_get.return_value = {"data": {"items": {"products": []}}} + self.launchSupplementaryDataSurvey(schema_name="test_supplementary_data") + self.assertStatusCode(500) + mock_set.assert_not_called() + + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data", + ) + def test_supplementary_data_is_loaded_when_all_required_lists_present( + self, mock_set, mock_get + ): + mock_get.return_value = {"data": {"items": {"employees": [], "products": []}}} + self.launchSupplementaryDataSurvey(schema_name="test_supplementary_data") + self.assertStatusOK() + mock_set.assert_called_once() + + @patch("app.routes.session.get_supplementary_data_v1") + @patch( + "app.routes.session._validate_supplementary_data_lists", + side_effect=[ + None, + ValidationError( + "Supplementary data does not include the following lists required for the schema: missing" + ), + ], + ) + @patch( + "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + ) + def test_supplementary_data_raises_500_error_when_survey_becomes_invalid_for_same_dataset( + self, + mock_set, + mock_validate, + mock_get, + ): + """ + This checks the edge case in which a survey changes to have different lists, but the supplementary dataset id + remains the same, so the supplementary data is not fetched again, but is no longer valid for the survey + """ + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") + self.assertStatusOK() + mock_set.assert_called_once() + mock_get.assert_called_once() + mock_validate.assert_called_once() + self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") + self.assertStatusCode(500) + self.assertEqual(mock_validate.call_count, 2) + class TestCensusSession(IntegrationTestCase): def setUp(self): From 7a74d3c4c7cb3fed504c1511c0f423b7c9aff35e Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:00:10 +0100 Subject: [PATCH 283/567] Bug: Fix answer labels on summary pages when there is only one related answer (#1197) * Initial changes * Remove test schema * Restore summary row title --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Co-authored-by: katie-gardner Co-authored-by: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> --- app/jinja_filters.py | 27 ++-- .../test_list_collector_section_summary.json | 22 ++-- tests/app/test_jinja_filters.py | 119 ++++++++++++++++++ 3 files changed, 140 insertions(+), 28 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 016c271870..1eee54d05f 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -470,27 +470,19 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche self, question: SelectFieldBase._Option, answer: SelectFieldBase._Option, - multiple_answers: bool, answers_are_editable: bool, no_answer_provided: str, edit_link_text: str, edit_link_aria_label: str, summary_type: str, + use_answer_label: bool = False, ) -> None: - if "type" in answer: - answer_type = answer["type"] - else: - answer_type = "calculated" - + answer_type = answer.get("type", "calculated") if ( - ( - multiple_answers - or answer_type == "relationship" - or is_summary_with_calculation(summary_type) - ) - and "label" in answer - and answer["label"] - ): + answer_type == "relationship" + or is_summary_with_calculation(summary_type) + or use_answer_label + ) and answer.get("label"): self.rowTitle = answer["label"] self.rowTitleAttributes = {"data-qa": answer["id"] + "-label"} else: @@ -563,12 +555,12 @@ def __init__( no_answer_provided: str, edit_link_text: str, edit_link_aria_label: str, + use_answer_label: bool = False, ) -> None: self.rowTitle = strip_tags(question["title"]) self.id = question["id"] self.rowItems = [] - - multiple_answers = len(question["answers"]) > 1 + use_answer_label = use_answer_label or len(question["answers"]) > 1 if is_summary_with_calculation(summary_type) and not answers_are_editable: self.total = True @@ -578,12 +570,12 @@ def __init__( SummaryRowItem( question, answer, - multiple_answers, answers_are_editable, no_answer_provided, edit_link_text, edit_link_aria_label, summary_type, + use_answer_label, ) ) @@ -742,6 +734,7 @@ def map_list_collector_config( no_answer_provided=flask_babel.lazy_gettext("No answer provided"), edit_link_text=edit_link_text, edit_link_aria_label=edit_link_aria_label, + use_answer_label=True, ) row_items.extend(summary_row.rowItems) diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index 2791afd7dc..901fc6a680 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -21,6 +21,17 @@ "type": "string" } ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "post_submission": { + "view_response": true + }, "answer_codes": [ { "answer_id": "any-companies-or-branches-answer", @@ -63,17 +74,6 @@ "code": "7" } ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "post_submission": { - "view_response": true - }, "sections": [ { "id": "section-companies", diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 0ee42c03c8..18758db3b2 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -1006,5 +1006,124 @@ def test_summary_item_config_with_list_collector(): assert to_dict(expected) == to_dict(result) +@pytest.mark.usefixtures("gb_locale") +def test_summary_item_config_with_list_collector_and_one_related_answer(): + expected = [ + { + "rowItems": [ + { + "actions": [ + { + "visuallyHiddenText": "Change your answer for:", + "attributes": {"data-qa": "list-item-change-1-link"}, + "text": "Change", + "url": "change_link_url", + }, + { + "visuallyHiddenText": "Remove Company A", + "attributes": {"data-qa": "list-item-remove-1-link"}, + "text": "Remove", + "url": "remove_link_url", + }, + ], + "iconType": None, + "id": "vmmPmD", + "rowTitle": "Company A", + "rowTitleAttributes": { + "data-list-item-id": "vmmPmD", + "data-qa": "list-item-1-label", + }, + }, + { + "actions": [ + { + "visuallyHiddenText": "Change your answer for: " + "Registration number", + "attributes": { + "data-ga": "click", + "data-ga-action": "Edit click", + "data-ga-category": "Summary", + "data-qa": "registration-number-edit", + }, + "text": "Change", + "url": "edit_link_url", + } + ], + "attributes": {"data-qa": "registration-number"}, + "id": "registration-number", + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "valueList": [{"text": "123"}], + }, + ] + } + ] + + result = map_summary_item_config( + group={ + "blocks": [ + { + "title": "Companies or UK branches", + "type": "List", + "add_link": "/questionnaire/companies/add-company/?return_to=section-summary", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added", + "list_name": "companies", + "related_answers": { + "vmmPmD": [ + { + "id": "edit-company", + "title": None, + "number": None, + "question": { + "id": "add-question", + "type": "General", + "title": "Give details about the company or branch that undertakes general insurance business", + "number": None, + "answers": [ + { + "id": "registration-number", + "label": "Registration number", + "value": 123, + "type": "number", + "unit": None, + "unit_length": None, + "currency": None, + "link": "edit_link_url", + } + ], + }, + } + ] + }, + "answer_title": "Name of UK company or branch", + "list": { + "list_items": [ + { + "item_title": "Company A", + "primary_person": False, + "list_item_id": "vmmPmD", + "edit_link": "change_link_url", + "remove_link": "remove_link_url", + } + ], + "editable": True, + }, + } + ], + }, + summary_type="SectionSummary", + answers_are_editable=True, + no_answer_provided="No answer Provided", + remove_link_aria_label="Remove Company A", + remove_link_text="Remove", + edit_link_text="Change", + edit_link_aria_label="Change your answer for:", + calculated_question={}, + ) + + assert to_dict(expected) == to_dict(result) + + def to_dict(obj): return json.loads(json.dumps(obj, default=lambda o: o.__dict__)) From 804d8534eb55d5f0b150828099dea75d1a66bc3f Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Tue, 12 Sep 2023 12:31:03 +0100 Subject: [PATCH 284/567] Tweak README with python-snappy dependency install workaround (#1202) --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8cd514bfe7..75f4663a7e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,11 @@ pip install --upgrade pip setuptools pipenv pipenv install --dev ``` +###### NOTE: You may encounter an issue with the `python-snappy` installation with `pipenv` above on Mac OSX. If so, install with the recommended [workaround command](https://github.com/andrix/python-snappy#frequently-asked-questions) instead: +``` shell +CPPFLAGS="-I/usr/local/include -L/usr/local/lib" pipenv install --dev +``` + To update the design system templates run: ``` shell From b5f8adca4510baacca53b30591e8c222ccd93097 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:42:23 +0100 Subject: [PATCH 285/567] Update to schemas v3.70.0 (#1204) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a67ef46698..75a91e0267 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.69.0 +v3.70.0 From deae6929dc98d5ff2d309c95f448920f083d2367 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:36:58 +0100 Subject: [PATCH 286/567] Refactor progress store (#1196) --- app/data_models/answer_store.py | 6 +- app/data_models/list_store.py | 8 +- app/data_models/progress_store.py | 173 +++++++----------- app/questionnaire/dependencies.py | 4 +- app/questionnaire/location.py | 6 + app/questionnaire/path_finder.py | 28 +-- .../questionnaire_store_updater.py | 44 ++--- app/questionnaire/relationship_location.py | 6 + app/questionnaire/router.py | 128 +++++-------- app/questionnaire/routing_path.py | 7 + app/questionnaire/value_source_resolver.py | 16 +- app/utilities/types.py | 38 +++- .../grand_calculated_summary_context.py | 8 +- app/views/contexts/hub_context.py | 7 +- app/views/contexts/list_context.py | 5 +- app/views/contexts/summary_context.py | 4 +- app/views/handlers/block.py | 10 +- app/views/handlers/individual_response.py | 15 +- app/views/handlers/list_action.py | 10 +- app/views/handlers/list_collector_content.py | 3 +- app/views/handlers/list_edit_question.py | 5 +- app/views/handlers/list_repeating_question.py | 11 +- app/views/handlers/primary_person_question.py | 4 +- app/views/handlers/question.py | 34 ++-- .../relationships/relationship_question.py | 2 +- app/views/handlers/section.py | 4 +- tests/app/data_model/test_progress_store.py | 129 ++++++------- .../data_model/test_questionnaire_store.py | 5 +- tests/app/questionnaire/conftest.py | 2 +- tests/app/questionnaire/test_path_finder.py | 77 ++++---- .../test_questionnaire_store_updater.py | 116 +++++------- .../test_relationship_location.py | 4 + tests/app/questionnaire/test_router.py | 118 +++++++----- tests/app/questionnaire/test_routing_path.py | 4 +- .../submitter/test_convert_payload_0_0_1.py | 52 +++--- .../submitter/test_convert_payload_0_0_3.py | 107 ++++++----- .../test_grand_calculated_summary_context.py | 2 +- .../contexts/test_section_summary_context.py | 15 +- 38 files changed, 584 insertions(+), 633 deletions(-) diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index d42e6b79fc..afa291b543 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -21,13 +21,13 @@ class AnswerStore: } """ - def __init__(self, existing_answers: Optional[Iterable[AnswerDict]] = None): + def __init__(self, answers: Optional[Iterable[AnswerDict]] = None): """Instantiate an answer_store. Args: - existing_answers: If a list of answer dictionaries is provided, this will be used to initialise the store. + answers: If a list of answer dictionaries is provided, this will be used to initialise the store. """ - self.answer_map = self._build_map(existing_answers or []) + self.answer_map = self._build_map(answers or []) self._is_dirty = False def __iter__(self) -> Iterator[Answer]: diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 6f13c50599..adb713519c 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -129,10 +129,10 @@ class ListStore: ``` """ - def __init__(self, existing_items: Optional[Iterable[ListModelDictType]] = None): - existing_items = existing_items or [] + def __init__(self, items: Optional[Iterable[ListModelDictType]] = None): + items = items or [] - self._lists = self._build_map(existing_items) + self._lists = self._build_map(items) self._is_dirty = False @@ -235,4 +235,4 @@ def deserialize(cls, serialized: Iterable[ListModelDictType]) -> ListStore: if not serialized: return cls() - return cls(existing_items=serialized) + return cls(items=serialized) diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index 0b7384cc4d..9f454a31ef 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -2,8 +2,8 @@ from typing import Iterable, Iterator, MutableMapping from app.data_models.progress import Progress, ProgressDictType -from app.questionnaire.location import Location -from app.utilities.types import LocationType, SectionKey +from app.questionnaire.location import Location, SectionKey +from app.utilities.types import LocationType @dataclass @@ -25,8 +25,7 @@ class ProgressStore: def __init__( self, - in_progress_sections_and_repeating_blocks: Iterable[ProgressDictType] - | None = None, + progress: Iterable[ProgressDictType] | None = None, ) -> None: """ Instantiate a ProgressStore object that tracks the progress status of Sections & Repeating Sections, @@ -39,32 +38,27 @@ def __init__( that created the List Item has Repeating Blocks, and progress of the Repeating Blocks for a List Item indicates if all required Repeating Blocks from the List Collector have been completed for the List Item. Args: - in_progress_sections_and_repeating_blocks: A list of hierarchical dict containing the completion status + progress: A list of hierarchical dict containing the completion status and completed blocks of Sections, Repeating Sections and List Items """ self._is_dirty: bool = False self._is_routing_backwards: bool = False - self._section_and_repeating_blocks_progress: MutableMapping[ - SectionKey, Progress - ] = self._build_map(in_progress_sections_and_repeating_blocks or []) - - def __contains__( - self, section_and_repeating_blocks_progress_key: SectionKey - ) -> bool: - return ( - section_and_repeating_blocks_progress_key - in self._section_and_repeating_blocks_progress + self._progress: MutableMapping[SectionKey, Progress] = self._build_map( + progress or [] ) + def __contains__(self, section_key: SectionKey) -> bool: + return section_key in self._progress + @staticmethod def _build_map( - section_and_repeating_blocks_progress_list: Iterable[ProgressDictType], + section_list: Iterable[ProgressDictType], ) -> MutableMapping: """ - Builds the ProgressStore's data structure from a list of progress dictionaries. + Builds the ProgressStore's data structure from a list of section dictionaries. - The `progress_key` is tuple consisting of `section_id` and the `list_item_id`. - The `progress` is a mutableMapping created from the Progress object. + The `section_key` is tuple consisting of `section_id` and the `list_item_id`. + The `section` is a mutableMapping created from the Progress object. Example structure: { @@ -78,11 +72,10 @@ def _build_map( """ return { - ( - progress["section_id"], - progress.get("list_item_id"), - ): Progress.from_dict(progress) - for progress in section_and_repeating_blocks_progress_list + SectionKey( + section["section_id"], section.get("list_item_id") + ): Progress.from_dict(section) + for section in section_list } @property @@ -93,24 +86,19 @@ def is_dirty(self) -> bool: def is_routing_backwards(self) -> bool: return self._is_routing_backwards - def is_section_or_repeating_blocks_progress_complete( - self, section_id: str, list_item_id: str | None = None - ) -> bool: + def is_section_complete(self, section_key: SectionKey) -> bool: """ Return True if the CompletionStatus of the Section or List Item specified by the given section_id and list_item_id is COMPLETED or INDIVIDUAL_RESPONSE_REQUESTED, else False. """ - return ( - section_id, - list_item_id, - ) in self.section_and_repeating_blocks_progress_keys( + return section_key in self.section_keys( statuses={ CompletionStatus.COMPLETED, CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, } ) - def section_and_repeating_blocks_progress_keys( + def section_keys( self, statuses: Iterable[str] | None = None, section_ids: Iterable[str] | None = None, @@ -121,94 +109,70 @@ def section_and_repeating_blocks_progress_keys( if not statuses: statuses = {*CompletionStatus()} - progress_keys = [ + section_keys = [ section_key - for section_key, section_progress in self._section_and_repeating_blocks_progress.items() + for section_key, section_progress in self._progress.items() if section_progress.status in statuses ] if section_ids is None: - return progress_keys + return section_keys return [ progress_key - for progress_key in progress_keys + for progress_key in section_keys if any(section_id in progress_key for section_id in section_ids) ] - def update_section_or_repeating_blocks_progress_completion_status( - self, - completion_status: str, - section_id: str, - list_item_id: str | None = None, - ) -> bool: + def update_section_status(self, status: str, section_key: SectionKey) -> bool: """ - Updates the completion status of the section or repeating blocks for a list item specified by the key based on the given section id and list item id. + Updates the status of the Section or Repeating Blocks for a list item specified by the key based on the given section id and list item id. """ updated = False - section_key = SectionKey(section_id, list_item_id) - if section_key in self._section_and_repeating_blocks_progress: - if ( - self._section_and_repeating_blocks_progress[section_key].status - != completion_status - ): + if section_key in self._progress: + if self._progress[section_key].status != status: updated = True - self._section_and_repeating_blocks_progress[ - section_key - ].status = completion_status + self._progress[section_key].status = status self._is_dirty = True - elif completion_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: - self._section_and_repeating_blocks_progress[section_key] = Progress( - section_id=section_id, - list_item_id=list_item_id, - block_ids=[], - status=completion_status, + elif status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: + self._progress[section_key] = Progress( + block_ids=[], status=status, **section_key.to_dict() ) self._is_dirty = True return updated - def get_section_or_repeating_blocks_progress_status( - self, section_id: str, list_item_id: str | None = None - ) -> str: + def get_section_status(self, section_key: SectionKey) -> str: """ Return the CompletionStatus of the Section or Repeating Blocks for a list item, - specified by the given section_id and list_item_id. + specified by the given section_id and list_item_id in SectionKey. Returns NOT_STARTED if the progress does not exist """ - progress_key = SectionKey(section_id, list_item_id) - if progress_key in self._section_and_repeating_blocks_progress: - return self._section_and_repeating_blocks_progress[progress_key].status + if section_key in self._progress: + return self._progress[section_key].status return CompletionStatus.NOT_STARTED - def get_block_status( - self, *, block_id: str, section_id: str, list_item_id: str | None = None - ) -> str: + def get_block_status(self, *, block_id: str, section_key: SectionKey) -> str: """ Return the completion status of the block specified by the given block_id, if it is part of the progress of the given Section or Repeating Blocks for list item specified by the given section_id or list_item_id """ - blocks = self.get_completed_block_ids( - section_id=section_id, list_item_id=list_item_id - ) + blocks = self.get_completed_block_ids(section_key) if block_id in blocks: return CompletionStatus.COMPLETED return CompletionStatus.NOT_STARTED - def get_completed_block_ids( - self, *, section_id: str, list_item_id: str | None = None - ) -> list[str]: + def get_completed_block_ids(self, section_key: SectionKey) -> list[str]: """ Return the block ids recorded as part of the progress for the Section or Repeating Blocks - for list item specified by the given section_id and list_item_id + for list item specified by the given section_id and list_item_id in SectionKey """ - progress_key = SectionKey(section_id, list_item_id) - if progress_key in self._section_and_repeating_blocks_progress: - return self._section_and_repeating_blocks_progress[progress_key].block_ids + if section_key in self._progress: + return self._progress[section_key].block_ids return [] @@ -217,24 +181,17 @@ def add_completed_location(self, location: LocationType) -> None: Adds the block from the given Location, to the progress specified by the section id and list item id within the Location. """ - section_id = location.section_id - list_item_id = location.list_item_id - - completed_block_ids = self.get_completed_block_ids( - section_id=section_id, list_item_id=list_item_id - ) + completed_block_ids = self.get_completed_block_ids(location.section_key) if location.block_id not in completed_block_ids: completed_block_ids.append(location.block_id) # type: ignore - progress_key = SectionKey(section_id, list_item_id) - if progress_key in self._section_and_repeating_blocks_progress: - self._section_and_repeating_blocks_progress[ - progress_key - ].block_ids = completed_block_ids + progress_key = location.section_key + if progress_key in self._progress: + self._progress[progress_key].block_ids = completed_block_ids else: - self._section_and_repeating_blocks_progress[progress_key] = Progress( - section_id=section_id, - list_item_id=list_item_id, + self._progress[progress_key] = Progress( + section_id=location.section_id, + list_item_id=location.list_item_id, block_ids=completed_block_ids, status=CompletionStatus.IN_PROGRESS, ) @@ -246,20 +203,15 @@ def remove_completed_location(self, location: LocationType) -> bool: Removes the block in the given Location, from the progress specified by the section id and list item id within the Location if it exists in the store. """ - progress_key = SectionKey(location.section_id, location.list_item_id) + progress_key = location.section_key if ( - progress_key in self._section_and_repeating_blocks_progress - and location.block_id - in self._section_and_repeating_blocks_progress[progress_key].block_ids + progress_key in self._progress + and location.block_id in self._progress[progress_key].block_ids ): - self._section_and_repeating_blocks_progress[progress_key].block_ids.remove( - location.block_id - ) + self._progress[progress_key].block_ids.remove(location.block_id) - if not self._section_and_repeating_blocks_progress[progress_key].block_ids: - self._section_and_repeating_blocks_progress[ - progress_key - ].status = CompletionStatus.IN_PROGRESS + if not self._progress[progress_key].block_ids: + self._progress[progress_key].status = CompletionStatus.IN_PROGRESS self._is_dirty = True return True @@ -274,30 +226,33 @@ def remove_progress_for_list_item_id(self, list_item_id: str) -> None: """ progress_keys_to_delete = [ SectionKey(section_id, progress_list_item_id) - for section_id, progress_list_item_id in self._section_and_repeating_blocks_progress + for section_id, progress_list_item_id in self._progress if progress_list_item_id == list_item_id ] for progress_key in progress_keys_to_delete: - del self._section_and_repeating_blocks_progress[progress_key] + del self._progress[progress_key] self._is_dirty = True def serialize(self) -> list[Progress]: - return list(self._section_and_repeating_blocks_progress.values()) + return list(self._progress.values()) def remove_location_for_backwards_routing(self, location: Location) -> None: self.remove_completed_location(location=location) self._is_routing_backwards = True def clear(self) -> None: - self._section_and_repeating_blocks_progress.clear() + self._progress.clear() self._is_dirty = True - def started_section_and_repeating_blocks_progress_keys( + def started_section_keys( self, section_ids: Iterable[str] | None = None ) -> list[SectionKey]: - return self.section_and_repeating_blocks_progress_keys( + return self.section_keys( statuses={CompletionStatus.COMPLETED, CompletionStatus.IN_PROGRESS}, section_ids=section_ids, ) + + def is_block_complete(self, *, block_id: str, section_key: SectionKey) -> bool: + return block_id in self.get_completed_block_ids(section_key) diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index 7868e99e9d..e2a28e40c7 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -48,8 +48,8 @@ def get_routing_path_block_ids_by_section_for_dependent_sections( if key in sections_to_ignore: continue - if key in progress_store.started_section_and_repeating_blocks_progress_keys(): - routing_path = path_finder.routing_path(*key) + if key in progress_store.started_section_keys(): + routing_path = path_finder.routing_path(key) block_ids_by_section[key] = routing_path.block_ids return block_ids_by_section diff --git a/app/questionnaire/location.py b/app/questionnaire/location.py index 19d498660e..ae568090c0 100644 --- a/app/questionnaire/location.py +++ b/app/questionnaire/location.py @@ -5,6 +5,8 @@ from flask import url_for +from app.utilities.types import SectionKey + class InvalidLocationException(Exception): def __init__(self, value: str): @@ -57,3 +59,7 @@ def url(self, **kwargs: Any) -> str: list_item_id=self.list_item_id, **kwargs, ) + + @property + def section_key(self) -> SectionKey: + return SectionKey(self.section_id, self.list_item_id) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index ba4d778de6..6b364106ca 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -7,7 +7,7 @@ from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import CompletionStatus, ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore -from app.questionnaire.location import Location +from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator, RuleEvaluatorTypes @@ -33,22 +33,20 @@ def __init__( self.list_store = list_store self.supplementary_data_store = supplementary_data_store - def routing_path( - self, section_id: str, list_item_id: str | None = None - ) -> RoutingPath: + def routing_path(self, section_key: SectionKey) -> RoutingPath: """ Visits all the blocks in a section and returns a path given a list of answers. """ routing_path_block_ids: list[str] = [] - current_location = Location(section_id=section_id, list_item_id=list_item_id) - section = self.schema.get_section(section_id) + current_location = Location(**section_key.to_dict()) + section = self.schema.get_section(section_key.section_id) list_name = self.schema.get_repeating_list_for_section( current_location.section_id ) if section: when_rules_block_dependencies = self.get_when_rules_block_dependencies( - section_id + section_key.section_id ) blocks = self._get_not_skipped_blocks_in_section( current_location, @@ -62,7 +60,11 @@ def routing_path( blocks, current_location, when_rules_block_dependencies ) - return RoutingPath(routing_path_block_ids, section_id, list_item_id, list_name) + return RoutingPath( + block_ids=routing_path_block_ids, + list_name=list_name, + **section_key.to_dict(), + ) def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: """NB: At present when rules block dependencies does not fully support repeating sections. @@ -75,9 +77,9 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: return [ block_id for dependent_section in dependencies_for_section - for block_id in self.routing_path(dependent_section) - if (dependent_section, None) - in self.progress_store.started_section_and_repeating_blocks_progress_keys() + for block_id in self.routing_path(SectionKey(dependent_section)) + if SectionKey(dependent_section) + in self.progress_store.started_section_keys() ] def _get_not_skipped_blocks_in_section( @@ -283,8 +285,8 @@ def _remove_current_blocks_answers_for_backwards_routing( ) self.progress_store.remove_location_for_backwards_routing(this_location) - self.progress_store.update_section_or_repeating_blocks_progress_completion_status( - CompletionStatus.IN_PROGRESS, this_location.section_id + self.progress_store.update_section_status( + CompletionStatus.IN_PROGRESS, this_location.section_key ) def _remove_block_answers_for_backward_routing_according_to_when_rule( diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 13c820258b..e116132b41 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,4 +1,4 @@ -from collections import defaultdict, namedtuple +from collections import defaultdict from itertools import combinations from typing import Iterable, Mapping @@ -10,12 +10,10 @@ from app.data_models.progress_store import CompletionStatus from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema -from app.questionnaire.location import Location +from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_schema import AnswerDependent from app.questionnaire.router import Router -from app.utilities.types import LocationType, SectionKey - -DependentSection = namedtuple("DependentSection", "section_id list_item_id is_complete") +from app.utilities.types import DependentSection, LocationType class QuestionnaireStoreUpdater: @@ -246,15 +244,13 @@ def remove_completed_location(self, location: LocationType | None = None) -> boo location = location or self._current_location return self._progress_store.remove_completed_location(location) - def update_section_or_repeating_blocks_progress_completion_status( - self, *, is_complete: bool, section_id: str, list_item_id: str | None = None + def update_section_status( + self, *, is_complete: bool, section_key: SectionKey ) -> bool: status = ( CompletionStatus.COMPLETED if is_complete else CompletionStatus.IN_PROGRESS ) - return self._progress_store.update_section_or_repeating_blocks_progress_completion_status( - status, section_id, list_item_id - ) + return self._progress_store.update_section_status(status, section_key) def _update_answer( self, @@ -344,10 +340,10 @@ def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: ): for list_item_id in self._list_store[repeating_list].items: self.dependent_sections.add( - DependentSection(section_id, list_item_id, None) + DependentSection(section_id, list_item_id) ) else: - self.dependent_sections.add(DependentSection(section_id, None, None)) + self.dependent_sections.add(DependentSection(section_id)) def _capture_section_dependencies_progress_value_source_for_current_section( self, @@ -382,10 +378,10 @@ def _update_section_dependencies(self, dependent_sections: Iterable) -> None: ): for list_item_id in self._list_store[repeating_list].items: self.dependent_sections.add( - DependentSection(section_id, list_item_id, None) + DependentSection(section_id, list_item_id) ) else: - self.dependent_sections.add(DependentSection(section_id, None, None)) + self.dependent_sections.add(DependentSection(section_id)) def update_answers( self, form_data: Mapping, list_item_id: str | None = None @@ -445,16 +441,11 @@ def _evaluate_dependents( is_path_complete = dependent_section.is_complete if is_path_complete is None: is_path_complete = self._router.is_path_complete( - self._router.routing_path( - dependent_section.section_id, - list_item_id=dependent_section.list_item_id, - ) + self._router.routing_path(dependent_section.section_key) ) - if self.update_section_or_repeating_blocks_progress_completion_status( - is_complete=is_path_complete, - section_id=dependent_section.section_id, - list_item_id=dependent_section.list_item_id, + if self.update_section_status( + is_complete=is_path_complete, section_key=dependent_section.section_key ): dependents_of_dependent: OrderedSet = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( dependent_section.section_id, OrderedSet() @@ -492,7 +483,6 @@ def _evaluate_dependent_of_dependents( dependent_section=DependentSection( section_id=dependent_section_id, list_item_id=list_item_id, - is_complete=None, ), evaluated_dependents=evaluated_dependents, ) @@ -525,20 +515,18 @@ def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: ): # Since this section key will be marked as incomplete, any `DependentSection` with is_complete as `None` # can be removed as we do not need to re-evaluate progress as we already know the section would be incomplete. - dependent = DependentSection(section_id, list_item_id, None) + dependent = DependentSection(section_id, list_item_id) if dependent in self.dependent_sections: self.dependent_sections.remove(dependent) self.dependent_sections.add( - DependentSection(section_id, list_item_id, False) + DependentSection(section_id, list_item_id, is_complete=False) ) def started_section_keys( self, section_ids: Iterable[str] | None = None ) -> list[SectionKey]: - return self._progress_store.started_section_and_repeating_blocks_progress_keys( - section_ids - ) + return self._progress_store.started_section_keys(section_ids) def _get_chronological_section_dependents(self) -> list: sections = list(self._schema.get_section_ids()) diff --git a/app/questionnaire/relationship_location.py b/app/questionnaire/relationship_location.py index 4151641ea0..29c3bb1aba 100644 --- a/app/questionnaire/relationship_location.py +++ b/app/questionnaire/relationship_location.py @@ -3,6 +3,8 @@ from flask import url_for +from app.questionnaire.location import SectionKey + @dataclass class RelationshipLocation: @@ -32,3 +34,7 @@ def url(self, **kwargs: Any) -> str: block_id=self.block_id, **kwargs, ) + + @property + def section_key(self) -> SectionKey: + return SectionKey(self.section_id, self.list_item_id) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 50feee73b5..6f78e4ae16 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -10,11 +10,11 @@ ) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema -from app.questionnaire.location import Location +from app.questionnaire.location import Location, SectionKey from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.utilities.types import LocationType, SectionKey +from app.utilities.types import LocationType class Router: @@ -60,13 +60,9 @@ def is_questionnaire_complete(self) -> bool: return not first_incomplete_section_key def get_first_incomplete_location_in_questionnaire_url(self) -> str: - first_incomplete_section_key = self._get_first_incomplete_section_key() - - if first_incomplete_section_key: - section_id, list_item_id = first_incomplete_section_key - + if first_incomplete_section_key := self._get_first_incomplete_section_key(): section_routing_path = self._path_finder.routing_path( - section_id=section_id, list_item_id=list_item_id + first_incomplete_section_key ) return self.get_section_resume_url(section_routing_path) @@ -75,7 +71,7 @@ def get_first_incomplete_location_in_questionnaire_url(self) -> str: def get_last_location_in_questionnaire_url(self) -> str | None: section_key = self._get_last_complete_section_key() if section_key: - routing_path = self.routing_path(*section_key) + routing_path = self.routing_path(section_key) return self.get_last_location_in_section(routing_path).url() def _is_list_item_in_list_store(self, list_item_id: str, list_name: str) -> bool: @@ -104,26 +100,18 @@ def can_access_location( def can_access_hub(self) -> bool: return self._schema.is_flow_hub and all( - self._progress_store.is_section_or_repeating_blocks_progress_complete( - section_id - ) + self._progress_store.is_section_complete(SectionKey(section_id)) for section_id in self._schema.get_section_ids_required_for_hub() if section_id in self.enabled_section_ids ) - def can_display_section_summary( - self, section_id: str, list_item_id: str | None = None - ) -> bool: + def can_display_section_summary(self, section_key: SectionKey) -> bool: return bool( - self._schema.get_summary_for_section(section_id) - ) and self._progress_store.is_section_or_repeating_blocks_progress_complete( - section_id, list_item_id - ) + self._schema.get_summary_for_section(section_key.section_id) + ) and self._progress_store.is_section_complete(section_key) - def routing_path( - self, section_id: str, list_item_id: str | None = None - ) -> RoutingPath: - return self._path_finder.routing_path(section_id, list_item_id) + def routing_path(self, section_key: SectionKey) -> RoutingPath: + return self._path_finder.routing_path(section_key) def get_next_location_url( self, @@ -137,10 +125,8 @@ def get_next_location_url( Get the next location in the section. If the section is complete, determine where to go next, whether it be a summary, the hub or the next incomplete location. """ - is_section_complete = ( - self._progress_store.is_section_or_repeating_blocks_progress_complete( - location.section_id, location.list_item_id - ) + is_section_complete = self._progress_store.is_section_complete( + location.section_key ) if return_to_url := self.get_return_to_location_url( @@ -155,7 +141,9 @@ def get_next_location_url( return return_to_url if is_section_complete: - return self._get_next_location_url_for_complete_section(location) + return self._get_next_location_url_for_complete_section( + location.section_key + ) # Due to backwards routing you can be on the last block of the path but with an in_progress section is_last_block_on_path = routing_path[-1] == location.block_id @@ -173,10 +161,10 @@ def get_next_location_url( ) def _get_next_location_url_for_complete_section( - self, location: LocationType + self, section_key: SectionKey ) -> str: - if self._schema.show_summary_on_completion_for_section(location.section_id): - return self._get_section_url(location) + if self._schema.show_summary_on_completion_for_section(section_key.section_id): + return self._get_section_url(section_key) return self.get_next_location_url_for_end_of_section() @@ -248,7 +236,7 @@ def get_return_to_location_url( url := self._get_return_to_for_grand_calculated_summary( return_to=return_to, return_to_block_id=return_to_block_id, - location=location, + section_key=location.section_key, routing_path=routing_path, is_for_previous=is_for_previous, return_to_answer_id=return_to_answer_id, @@ -268,10 +256,8 @@ def get_return_to_location_url( return url if is_section_complete is None: - is_section_complete = ( - self._progress_store.is_section_or_repeating_blocks_progress_complete( - location.section_id, location.list_item_id - ) + is_section_complete = self._progress_store.is_section_complete( + location.section_key ) if not is_section_complete: @@ -285,7 +271,7 @@ def get_return_to_location_url( if return_to == "section-summary": return self._get_section_url( - location, return_to_answer_id=return_to_answer_id + location.section_key, return_to_answer_id=return_to_answer_id ) if return_to == "final-summary" and self.is_questionnaire_complete: return url_for( @@ -297,7 +283,7 @@ def _get_return_to_for_grand_calculated_summary( *, return_to: str | None, return_to_block_id: str | None, - location: LocationType, + section_key: SectionKey, routing_path: RoutingPath, is_for_previous: bool, return_to_answer_id: str | None = None, @@ -313,18 +299,18 @@ def _get_return_to_for_grand_calculated_summary( grand_calculated_summary_section: str = ( self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore ) - if grand_calculated_summary_section != location.section_id: + if grand_calculated_summary_section != section_key.section_id: # the grand calculated summary is in a different section which will have a different routing path - # but don't go to it unless the section is enabled and the current section is complete + # but does not go to it unless the section is enabled and the current section is complete if ( - not self._progress_store.is_section_or_repeating_blocks_progress_complete( - location.section_id + not self._progress_store.is_section_complete( + SectionKey(section_key.section_id) ) or grand_calculated_summary_section not in self.enabled_section_ids ): return None routing_path = self._path_finder.routing_path( - section_id=grand_calculated_summary_section + SectionKey(section_id=grand_calculated_summary_section) ) if self.can_access_location( # grand calculated summaries do not yet support repeating sections, when they do, this will need to make use of list item id as well @@ -430,9 +416,7 @@ def get_next_location_url_for_end_of_section(self) -> str: return self.get_first_incomplete_location_in_questionnaire_url() def get_section_resume_url(self, routing_path: RoutingPath) -> str: - section_key = SectionKey(routing_path.section_id, routing_path.list_item_id) - - if section_key in self._progress_store: + if routing_path.section_key in self._progress_store: location = self._get_first_incomplete_location_in_section(routing_path) if location: return location.url(resume=True) @@ -469,30 +453,22 @@ def full_routing_path(self) -> list[RoutingPath]: for list_item_id in self._list_store[repeating_list]: full_routing_path.append( self._path_finder.routing_path( - section_id=section_id, list_item_id=list_item_id + SectionKey(section_id, list_item_id) ) ) else: full_routing_path.append( - self._path_finder.routing_path(section_id=section_id) + self._path_finder.routing_path(SectionKey(section_id)) ) return full_routing_path - def is_block_complete( - self, *, block_id: str, section_id: str, list_item_id: str | None - ) -> bool: - return block_id in self._progress_store.get_completed_block_ids( - section_id=section_id, list_item_id=list_item_id - ) - def _get_first_incomplete_location_in_section( self, routing_path: RoutingPath ) -> Location | None: for block_id in routing_path: - if not self.is_block_complete( + if not self._progress_store.is_block_complete( block_id=block_id, - section_id=routing_path.section_id, - list_item_id=routing_path.list_item_id, + section_key=routing_path.section_key, ): return Location( block_id=block_id, @@ -511,10 +487,9 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: for block_id in routing_path: allowable_path.append(block_id) - if not self.is_block_complete( + if not self._progress_store.is_block_complete( block_id=block_id, - section_id=routing_path.section_id, - list_item_id=routing_path.list_item_id, + section_key=routing_path.section_key, ): return allowable_path @@ -528,25 +503,19 @@ def _get_enabled_section_keys( section_id ): for list_item_id in self._list_store[repeating_list]: - section_key = SectionKey(section_id, list_item_id) - yield section_key + yield SectionKey(section_id, list_item_id) else: - section_key = SectionKey(section_id, None) - yield section_key + yield SectionKey(section_id) - def _get_first_incomplete_section_key(self) -> tuple[str, str | None] | None: - for section_id, list_item_id in self._get_enabled_section_keys(): - if not self._progress_store.is_section_or_repeating_blocks_progress_complete( - section_id, list_item_id - ): - return section_id, list_item_id + def _get_first_incomplete_section_key(self) -> SectionKey | None: + for section_key in self._get_enabled_section_keys(): + if not self._progress_store.is_section_complete(section_key): + return section_key - def _get_last_complete_section_key(self) -> tuple[str, str | None] | None: - for section_id, list_item_id in list(self._get_enabled_section_keys())[::-1]: - if self._progress_store.is_section_or_repeating_blocks_progress_complete( - section_id, list_item_id - ): - return section_id, list_item_id + def _get_last_complete_section_key(self) -> SectionKey | None: + for section_key in list(self._get_enabled_section_keys())[::-1]: + if self._progress_store.is_section_complete(section_key): + return section_key def _is_section_enabled(self, section: Mapping) -> bool: if "enabled" not in section: @@ -592,12 +561,11 @@ def _get_next_block_url( @staticmethod def _get_section_url( - location: LocationType, + section_key: SectionKey, return_to_answer_id: str | None = None, ) -> str: return url_for( "questionnaire.get_section", - section_id=location.section_id, - list_item_id=location.list_item_id, _anchor=return_to_answer_id, + **section_key.to_dict(), ) diff --git a/app/questionnaire/routing_path.py b/app/questionnaire/routing_path.py index f6ab522010..92d0ca9449 100644 --- a/app/questionnaire/routing_path.py +++ b/app/questionnaire/routing_path.py @@ -1,11 +1,14 @@ from typing import Iterator, SupportsIndex +from app.utilities.types import SectionKey + class RoutingPath: """Holds a list of block_ids and has section_id, list_item_id and list_name attributes""" def __init__( self, + *, block_ids: list[str], section_id: str, list_item_id: str | None = None, @@ -44,3 +47,7 @@ def __eq__(self, other: object) -> bool: def index(self, value: str, *args: SupportsIndex) -> int: return self.block_ids.index(value, *args) + + @property + def section_key(self) -> SectionKey: + return SectionKey(self.section_id, self.list_item_id) diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index edd64eb648..5de135c697 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -15,7 +15,7 @@ from app.data_models.list_store import ListModel, ListStore from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException from app.questionnaire import QuestionnaireSchema -from app.questionnaire.location import InvalidLocationException +from app.questionnaire.location import InvalidLocationException, SectionKey from app.questionnaire.rules import rule_evaluator from app.utilities.types import LocationType @@ -198,9 +198,7 @@ def _resolve_progress_value_source( if selector == "section": # List item id is set to None here as we do not support checking progress value sources for # repeating sections - return self.progress_store.get_section_or_repeating_blocks_progress_status( - section_id=identifier, list_item_id=None - ) + return self.progress_store.get_section_status(SectionKey(identifier)) if selector == "block": if not self.location: @@ -214,10 +212,12 @@ def _resolve_progress_value_source( return self.progress_store.get_block_status( block_id=identifier, - section_id=section_id_for_block, - list_item_id=self.location.list_item_id - if self.location.section_id == section_id_for_block - else None, + section_key=SectionKey( + section_id=section_id_for_block, + list_item_id=self.location.list_item_id + if self.location.section_id == section_id_for_block + else None, + ), ) def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: diff --git a/app/utilities/types.py b/app/utilities/types.py index 9bcf57b6ff..72ae1be46b 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -1,11 +1,39 @@ -from typing import NamedTuple, TypeAlias +from typing import TYPE_CHECKING, NamedTuple, TypeAlias, TypedDict, Union -from app.questionnaire.location import Location -from app.questionnaire.relationship_location import RelationshipLocation +if TYPE_CHECKING: + from app.questionnaire.location import Location # pragma: no cover + from app.questionnaire.relationship_location import ( + RelationshipLocation, # pragma: no cover + ) -LocationType: TypeAlias = Location | RelationshipLocation +LocationType: TypeAlias = Union["Location", "RelationshipLocation"] -class SectionKey(NamedTuple): +class SectionKeyDict(TypedDict): section_id: str list_item_id: str | None + + +class SectionKey(NamedTuple): + section_id: str + list_item_id: str | None = None + + def to_dict(self) -> SectionKeyDict: + return SectionKeyDict( + section_id=self.section_id, list_item_id=self.list_item_id + ) + + +class DependentSection(NamedTuple): + """ + The 'is_complete' property is used when updating the progress of the section. If the value is 'None' + then the routing path for this section will be re-evaluated to determine if it is complete. + """ + + section_id: str + list_item_id: str | None = None + is_complete: bool | None = None + + @property + def section_key(self) -> SectionKey: + return SectionKey(self.section_id, self.list_item_id) diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 03a019192b..ae27219c36 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -2,6 +2,7 @@ from werkzeug.datastructures import ImmutableDict +from app.questionnaire.location import SectionKey from app.questionnaire.questionnaire_schema import ( get_calculation_block_ids_for_grand_calculated_summary, ) @@ -48,10 +49,7 @@ def _blocks_on_routing_path( } # find any sections involved in the grand calculated summary (but only if they have started, to avoid evaluating the path if not necessary) started_sections = [ - key - for key, _ in self._progress_store.started_section_and_repeating_blocks_progress_keys( - section_ids - ) + key for key, _ in self._progress_store.started_section_keys(section_ids) ] routing_path_block_ids: list[str] = [] @@ -61,7 +59,7 @@ def _blocks_on_routing_path( else: routing_path_block_ids.extend( # repeating calculated summaries are not supported at the moment, so no list item is needed - self._router.routing_path(section_id).block_ids + self._router.routing_path(SectionKey(section_id)).block_ids ) return routing_path_block_ids diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 5a017f4d2e..68a76f9954 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -6,6 +6,7 @@ from werkzeug.datastructures import ImmutableDict from app.data_models.progress_store import CompletionStatus +from app.questionnaire.location import SectionKey from app.views.contexts import Context @@ -157,10 +158,8 @@ def _get_row_for_section( ) -> dict[str, Union[str, list]]: row_id = f"{section_id}-{list_item_index}" if list_item_index else section_id - section_status = ( - self._progress_store.get_section_or_repeating_blocks_progress_status( - section_id, list_item_id - ) + section_status = self._progress_store.get_section_status( + SectionKey(section_id, list_item_id) ) return self.get_row_context_for_section( diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index 8894a0ac1f..6daf7babd5 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -4,6 +4,7 @@ from flask import url_for from flask_babel import lazy_gettext +from app.questionnaire.location import SectionKey from app.views.contexts.context import Context @@ -85,8 +86,8 @@ def _build_list_items_context( ), "primary_person": is_primary, "list_item_id": list_item_id, - "is_complete": self._progress_store.is_section_or_repeating_blocks_progress_complete( - section_id=section_id, list_item_id=list_item_id + "is_complete": self._progress_store.is_section_complete( + SectionKey(section_id, list_item_id) ), "repeating_blocks": has_repeating_blocks, } diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index 6e989a2d99..f103e7ffeb 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -86,9 +86,7 @@ def build_summary_item( metadata=self._metadata, response_metadata=self._response_metadata, current_location=location, - routing_path=self._router.routing_path( - section_id, list_item_id=list_item_id - ), + routing_path=self._router.routing_path(location.section_key), supplementary_data_store=self._supplementary_data_store, ) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index bf7793beec..3322b8906e 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -131,20 +131,16 @@ def handle_post(self) -> None: self.questionnaire_store_updater.save() def _get_routing_path(self) -> RoutingPath: - return self.router.routing_path( - section_id=self._current_location.section_id, - list_item_id=self._current_location.list_item_id, - ) + return self.router.routing_path(self._current_location.section_key) def _update_section_completeness( self, location: Optional[Union[Location, RelationshipLocation]] = None ) -> None: location = location or self._current_location - self.questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status( + self.questionnaire_store_updater.update_section_status( is_complete=self.router.is_path_complete(self._routing_path), - section_id=location.section_id, - list_item_id=location.list_item_id, + section_key=location.section_key, ) def _set_started_at_metadata(self) -> str | None: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index a3f537dff9..cec584acea 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -20,6 +20,7 @@ from app.helpers.template_helpers import render_template from app.publisher.exceptions import PublicationFailed from app.questionnaire import QuestionnaireSchema +from app.questionnaire.location import SectionKey from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.router import Router from app.views.contexts.question import build_question_context @@ -322,11 +323,9 @@ def _render_block(self) -> dict[str, Any]: ) def _update_section_status(self, status: str) -> None: - self._questionnaire_store.progress_store.update_section_or_repeating_blocks_progress_completion_status( - # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None + self._questionnaire_store.progress_store.update_section_status( status, - self.individual_section_id, # type: ignore - self._list_item_id, + SectionKey(self.individual_section_id, self._list_item_id), ) @property @@ -589,13 +588,13 @@ def handle_post(self) -> Response | None: def _update_section_completeness(self) -> None: if not self._questionnaire_store.progress_store.get_completed_block_ids( - section_id=self.individual_section_id, list_item_id=self._list_item_id + section_key := SectionKey( + section_id=self.individual_section_id, list_item_id=self._list_item_id + ) ): status = CompletionStatus.NOT_STARTED else: - routing_path = self.router.routing_path( - self.individual_section_id, self._list_item_id - ) + routing_path = self.router.routing_path(section_key) status = ( CompletionStatus.COMPLETED if self.router.is_path_complete(routing_path) diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index 69066c1fa7..97ec85c38d 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -21,8 +21,7 @@ def parent_location(self) -> Location: ) def _get_routing_path(self) -> RoutingPath: - """Only the section id is required, as list collectors won't be in a repeating section""" - return self.router.routing_path(section_id=self.parent_location.section_id) + return self.router.routing_path(self.parent_location.section_key) def is_location_valid(self) -> bool: can_access_parent_location = self.router.can_access_location( @@ -50,7 +49,7 @@ def get_section_or_final_summary_url(self) -> str | None: if ( self._return_to == "section-summary" and self.router.can_display_section_summary( - self.parent_location.section_id, self.parent_location.list_item_id + self.parent_location.section_key ) ): return url_for( @@ -67,11 +66,10 @@ def get_next_location_url(self) -> str: if url := self.get_section_or_final_summary_url(): return url - if self.router.is_block_complete( + if self._questionnaire_store.progress_store.is_block_complete( # Type ignore: block_id would exist at this point block_id=self.parent_location.block_id, # type: ignore - section_id=self.parent_location.section_id, - list_item_id=self.parent_location.list_item_id, + section_key=self.parent_location.section_key, ): return self.router.get_next_location_url( self.parent_location, diff --git a/app/views/handlers/list_collector_content.py b/app/views/handlers/list_collector_content.py index 676e25e3e8..a69c8fe322 100644 --- a/app/views/handlers/list_collector_content.py +++ b/app/views/handlers/list_collector_content.py @@ -9,7 +9,6 @@ def _get_additional_view_context(self) -> dict: def handle_post(self) -> None: if self._is_list_collector_complete(): self._routing_path = self.router.routing_path( - section_id=self._current_location.section_id, - list_item_id=self._current_location.list_item_id, + self._current_location.section_key ) return super(Question, self).handle_post() diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index 9148b01225..a9cc1ae08a 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -34,9 +34,8 @@ def get_next_location_url(self) -> str: if first_incomplete_block := self.get_first_incomplete_list_repeating_block_location_for_list_item( repeating_block_ids=self.repeating_block_ids, - section_id=self.current_location.section_id, - # Type ignore: list_name and list_item_id will exist at this point - list_item_id=self.current_location.list_item_id, # type: ignore + section_key=self.current_location.section_key, + # Type ignore: list_name will exist at this point list_name=self.current_location.list_name, # type: ignore ): return url_for( diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py index 1dec1bb3ab..bf2901ef02 100644 --- a/app/views/handlers/list_repeating_question.py +++ b/app/views/handlers/list_repeating_question.py @@ -65,15 +65,12 @@ def handle_post(self) -> None: self.questionnaire_store_updater.add_completed_location(self.current_location) if not self.get_first_incomplete_list_repeating_block_location_for_list_item( repeating_block_ids=self.repeating_block_ids, - section_id=self.current_location.section_id, - # Type ignore: list_name and list_item_id will always exist at this point - list_item_id=self.current_location.list_item_id, # type: ignore + section_key=self.current_location.section_key, + # Type ignore: list_name will always exist at this point list_name=self.current_location.list_name, # type: ignore ): - self.questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status( - is_complete=True, - section_id=self.current_location.section_id, - list_item_id=self.current_location.list_item_id, + self.questionnaire_store_updater.update_section_status( + is_complete=True, section_key=self.current_location.section_key ) super().handle_post() diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index dcc3dfd267..45965f6a74 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -14,13 +14,13 @@ def parent_block(self) -> ImmutableDict: @property def parent_location(self) -> Location: - parent_id = self._schema.parent_id_map[self.rendered_block["id"]] + parent_id = self._schema.parent_id_map[self.block["id"]] return Location( section_id=self._current_location.section_id, block_id=parent_id ) def _get_routing_path(self) -> RoutingPath: - return self.router.routing_path(section_id=self._current_location.section_id) + return self.router.routing_path(self.parent_location.section_key) def is_location_valid(self) -> bool: primary_person_list_item_id = self._questionnaire_store.list_store[ diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index f087e90bf5..b25c6ce7d2 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -6,12 +6,10 @@ from app.forms.questionnaire_form import QuestionnaireForm, generate_form from app.helpers import get_address_lookup_api_auth_token -from app.questionnaire.location import Location -from app.questionnaire.questionnaire_store_updater import ( - DependentSection, - QuestionnaireStoreUpdater, -) +from app.questionnaire.location import Location, SectionKey +from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.variants import transform_variants +from app.utilities.types import DependentSection from app.views.contexts import ListContext from app.views.contexts.question import build_question_context from app.views.handlers.block import BlockHandler @@ -234,8 +232,7 @@ def handle_post(self) -> None: # In order to support progress value source references of the previous block self.questionnaire_store_updater.add_completed_location() self._routing_path = self.router.routing_path( - section_id=self._current_location.section_id, - list_item_id=self._current_location.list_item_id, + self._current_location.section_key ) super().handle_post() @@ -262,9 +259,8 @@ def capture_dependent_sections_for_list(self, list_name: str) -> None: continue self.questionnaire_store_updater.dependent_sections.add( DependentSection( - section_id=section_id, - list_item_id=list_item_id, - is_complete=None, + section_id, + list_item_id, ) ) @@ -290,8 +286,7 @@ def get_first_incomplete_list_repeating_block_location( for list_item_id in list_model.items: if incomplete_location := self.get_first_incomplete_list_repeating_block_location_for_list_item( repeating_block_ids=repeating_block_ids, - section_id=section_id, - list_item_id=list_item_id, + section_key=SectionKey(section_id, list_item_id), list_name=list_name, ): return incomplete_location @@ -300,24 +295,19 @@ def get_first_incomplete_list_repeating_block_location_for_list_item( self, *, repeating_block_ids: Sequence[str], - section_id: str, - list_item_id: str, + section_key: SectionKey, list_name: str, ) -> Location | None: - if self._questionnaire_store.progress_store.is_section_or_repeating_blocks_progress_complete( - section_id=section_id, list_item_id=list_item_id - ): + if self._questionnaire_store.progress_store.is_section_complete(section_key): return None for repeating_block_id in repeating_block_ids: - if not self.router.is_block_complete( + if not self._questionnaire_store.progress_store.is_block_complete( block_id=repeating_block_id, - section_id=section_id, - list_item_id=list_item_id, + section_key=section_key, ): return Location( - section_id=section_id, block_id=repeating_block_id, list_name=list_name, - list_item_id=list_item_id, + **section_key.to_dict(), ) diff --git a/app/views/handlers/relationships/relationship_question.py b/app/views/handlers/relationships/relationship_question.py index 1a7e88bbc8..1022afed35 100644 --- a/app/views/handlers/relationships/relationship_question.py +++ b/app/views/handlers/relationships/relationship_question.py @@ -79,7 +79,7 @@ def relationship_router(self) -> RelationshipRouter: ) def _get_routing_path(self) -> RoutingPath: - return self.router.routing_path(section_id=self.parent_location.section_id) + return self.router.routing_path(self.parent_location.section_key) def is_location_valid(self) -> bool: can_access_parent_location = self.router.can_access_location( diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index ca2bc24c2a..f4b0665698 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -40,7 +40,7 @@ def __init__( ) self._routing_path = self._router.routing_path( - section_id=self._section_id, list_item_id=self._list_item_id + self.current_location.section_key ) def get_context(self) -> Mapping: @@ -69,7 +69,7 @@ def get_resume_url(self) -> str: def can_display_summary(self) -> bool: return self._router.can_display_section_summary( - self._section_id, self._list_item_id + self.current_location.section_key ) def _is_valid_location(self) -> bool: diff --git a/tests/app/data_model/test_progress_store.py b/tests/app/data_model/test_progress_store.py index 30553af5ef..23c5670c5d 100644 --- a/tests/app/data_model/test_progress_store.py +++ b/tests/app/data_model/test_progress_store.py @@ -2,7 +2,7 @@ from app.data_models.progress import Progress from app.data_models.progress_store import CompletionStatus, ProgressStore -from app.questionnaire.location import Location +from app.questionnaire.location import Location, SectionKey def test_serialisation(): @@ -10,8 +10,9 @@ def test_serialisation(): store.add_completed_location(Location(section_id="s1", block_id="one")) store.add_completed_location(Location(section_id="s1", block_id="two")) - store.update_section_or_repeating_blocks_progress_completion_status( - completion_status=CompletionStatus.COMPLETED, section_id="s1" + store.update_section_status( + status=CompletionStatus.COMPLETED, + section_key=SectionKey("s1"), ) store.add_completed_location( @@ -22,10 +23,9 @@ def test_serialisation(): list_item_id="abc123", ) ) - store.update_section_or_repeating_blocks_progress_completion_status( - completion_status=CompletionStatus.IN_PROGRESS, - section_id="s2", - list_item_id="abc123", + store.update_section_status( + status=CompletionStatus.IN_PROGRESS, + section_key=SectionKey("s2", "abc123"), ) serialized = store.serialize() @@ -68,18 +68,16 @@ def test_deserialisation(): store = ProgressStore(in_progress_sections) assert ( - store.get_section_or_repeating_blocks_progress_status(section_id="s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.IN_PROGRESS ) - assert store.get_completed_block_ids(section_id="s1") == ["one", "two"] + assert store.get_completed_block_ids(section_key=SectionKey("s1")) == ["one", "two"] assert ( - store.get_section_or_repeating_blocks_progress_status( - section_id="s2", list_item_id="abc123" - ) + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.COMPLETED ) - assert store.get_completed_block_ids(section_id="s2", list_item_id="abc123") == [ + assert store.get_completed_block_ids(section_key=SectionKey("s2", "abc123")) == [ "three", "four", ] @@ -122,10 +120,10 @@ def test_add_completed_location(): store.add_completed_location(non_repeating_location) store.add_completed_location(repeating_location) - assert store.get_completed_block_ids(section_id="s1") == [ + assert store.get_completed_block_ids(section_key=SectionKey("s1")) == [ non_repeating_location.block_id ] - assert store.get_completed_block_ids(section_id="s2", list_item_id="abc123") == [ + assert store.get_completed_block_ids(section_key=SectionKey("s2", "abc123")) == [ repeating_location.block_id ] @@ -158,19 +156,17 @@ def test_add_completed_location_existing(): store.add_completed_location(repeating_location) assert ( - store.get_section_or_repeating_blocks_progress_status(section_id="s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.COMPLETED ) assert ( - store.get_section_or_repeating_blocks_progress_status( - section_id="s2", list_item_id="abc123" - ) + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.COMPLETED ) - assert len(store.get_completed_block_ids(section_id="s1")) == 1 + assert len(store.get_completed_block_ids(section_key=SectionKey("s1"))) == 1 assert ( - len(store.get_completed_block_ids(section_id="s2", list_item_id="abc123")) == 2 + len(store.get_completed_block_ids(section_key=SectionKey("s2", "abc123"))) == 2 ) assert not store.is_dirty @@ -202,19 +198,17 @@ def test_add_completed_location_new(): store.add_completed_location(repeating_location) assert ( - store.get_section_or_repeating_blocks_progress_status(section_id="s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.COMPLETED ) assert ( - store.get_section_or_repeating_blocks_progress_status( - section_id="s2", list_item_id="abc123" - ) + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.COMPLETED ) - assert len(store.get_completed_block_ids(section_id="s1")) == 2 + assert len(store.get_completed_block_ids(section_key=SectionKey("s1"))) == 2 assert ( - len(store.get_completed_block_ids(section_id="s2", list_item_id="abc123")) == 3 + len(store.get_completed_block_ids(section_key=SectionKey("s2", "abc123"))) == 3 ) assert store.is_dirty @@ -253,22 +247,22 @@ def test_remove_completed_location(): store.remove_completed_location(repeating_location) store.remove_completed_location(last_and_final_location) - assert store.get_completed_block_ids(section_id="s1") == ["two"] - assert store.get_completed_block_ids(section_id="s2", list_item_id="abc123") == [ + assert store.get_completed_block_ids(section_key=SectionKey("s1")) == ["two"] + assert store.get_completed_block_ids(section_key=SectionKey("s2", "abc123")) == [ "four" ] - assert store.get_completed_block_ids(section_id="s3") == [] + assert store.get_completed_block_ids(section_key=SectionKey("s3")) == [] assert ( - store.get_section_or_repeating_blocks_progress_status("s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.COMPLETED ) assert ( - store.get_section_or_repeating_blocks_progress_status("s2", "abc123") + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.COMPLETED ) assert ( - store.get_section_or_repeating_blocks_progress_status("s3") + store.get_section_status(section_key=SectionKey("s3")) == CompletionStatus.IN_PROGRESS ) @@ -294,7 +288,7 @@ def test_remove_non_existent_completed_location(): store.remove_completed_location(non_repeating_location) store.remove_completed_location(repeating_location) - assert len(store.get_completed_block_ids(section_id="s1")) == 1 + assert len(store.get_completed_block_ids(section_key=SectionKey("s1"))) == 1 assert not store.is_dirty @@ -315,23 +309,21 @@ def test_update_section_status(): ] store = ProgressStore(completed) - store.update_section_or_repeating_blocks_progress_completion_status( - completion_status=CompletionStatus.IN_PROGRESS, section_id="s1" + store.update_section_status( + status=CompletionStatus.IN_PROGRESS, + section_key=SectionKey("s1"), ) - store.update_section_or_repeating_blocks_progress_completion_status( - completion_status=CompletionStatus.IN_PROGRESS, - section_id="s2", - list_item_id="abc123", + store.update_section_status( + status=CompletionStatus.IN_PROGRESS, + section_key=SectionKey("s2", "abc123"), ) assert ( - store.get_section_or_repeating_blocks_progress_status(section_id="s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.IN_PROGRESS ) assert ( - store.get_section_or_repeating_blocks_progress_status( - section_id="s2", list_item_id="abc123" - ) + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.IN_PROGRESS ) assert store.is_dirty @@ -348,17 +340,18 @@ def test_update_non_existing_section_status(): ] store = ProgressStore(completed) - store.update_section_or_repeating_blocks_progress_completion_status( - "s2", CompletionStatus.IN_PROGRESS + store.update_section_status( + status=CompletionStatus.IN_PROGRESS, + section_key=SectionKey("s2"), ) assert ( - store.get_section_or_repeating_blocks_progress_status("s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.COMPLETED ) assert "s2" not in store - assert store.get_completed_block_ids(section_id="s2") == [] + assert store.get_completed_block_ids(section_key=SectionKey("s2")) == [] assert not store.is_dirty @@ -381,13 +374,11 @@ def test_get_section_status(): store = ProgressStore(existing_progress) assert ( - store.get_section_or_repeating_blocks_progress_status(section_id="s1") + store.get_section_status(section_key=SectionKey("s1")) == CompletionStatus.COMPLETED ) assert ( - store.get_section_or_repeating_blocks_progress_status( - section_id="s2", list_item_id="abc123" - ) + store.get_section_status(section_key=SectionKey("s2", "abc123")) == CompletionStatus.IN_PROGRESS ) @@ -409,9 +400,9 @@ def test_get_section_locations(): ] store = ProgressStore(completed) - assert store.get_completed_block_ids(section_id="s1") == ["one"] + assert store.get_completed_block_ids(section_key=SectionKey("s1")) == ["one"] - assert store.get_completed_block_ids(section_id="s2", list_item_id="abc123") == [ + assert store.get_completed_block_ids(section_key=SectionKey("s2", "abc123")) == [ "three" ] @@ -452,15 +443,9 @@ def test_is_section_complete(): store = ProgressStore(completed) - assert store.is_section_or_repeating_blocks_progress_complete( - section_id="s1", list_item_id=None - ) - assert store.is_section_or_repeating_blocks_progress_complete( - section_id="s4", list_item_id="123abc" - ) - assert store.is_section_or_repeating_blocks_progress_complete( - section_id="s5", list_item_id="456def" - ) + assert store.is_section_complete(section_key=SectionKey("s1")) + assert store.is_section_complete(SectionKey("s4", "123abc")) + assert store.is_section_complete(SectionKey("s5", "456def")) def test_remove_progress_for_list_item_id(): @@ -495,15 +480,15 @@ def test_remove_progress_for_list_item_id(): store.remove_progress_for_list_item_id(list_item_id="abc123") - assert ("s3", "abc123") not in store - assert store.get_completed_block_ids(section_id="s3", list_item_id="abc123") == [] + assert (SectionKey("s3", "abc123")) not in store + assert store.get_completed_block_ids(SectionKey("s3", "abc123")) == [] - assert ("s4", "123abc") in store + assert SectionKey("s4", "123abc") in store store.remove_progress_for_list_item_id(list_item_id="123abc") - assert ("s4", "123abc") not in store - assert store.get_completed_block_ids(section_id="s4", list_item_id="123abc") == [] + assert (SectionKey("s4", "123abc")) not in store + assert store.get_completed_block_ids(SectionKey("s4", "123abc")) == [] @pytest.mark.parametrize( @@ -556,9 +541,7 @@ def test_in_progress_and_completed_section_ids(section_ids, expected_section_key store = ProgressStore(completed) statuses = {CompletionStatus.COMPLETED, CompletionStatus.IN_PROGRESS} - section_keys = store.section_and_repeating_blocks_progress_keys( - section_ids=section_ids, statuses=statuses - ) + section_keys = store.section_keys(section_ids=section_ids, statuses=statuses) assert sorted(section_keys) == expected_section_keys @@ -598,9 +581,7 @@ def test_section_keys(): ] store = ProgressStore(completed) - section_keys = store.section_and_repeating_blocks_progress_keys( - section_ids={"s1", "s2", "s3"} - ) + section_keys = store.section_keys(section_ids={"s1", "s2", "s3"}) assert sorted(section_keys) == sorted( [("s1", None), ("s2", None), ("s3", "abc123")] ) diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index 4bf8f5a218..9cd8cbc8eb 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -5,6 +5,7 @@ from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore +from app.questionnaire.location import SectionKey from app.utilities.json import json_dumps, json_loads @@ -32,14 +33,14 @@ def test_questionnaire_store_json_loads( assert ( len( store.progress_store.get_completed_block_ids( - section_id="a-test-section", list_item_id="abc123" + SectionKey("a-test-section", "abc123") ) ) == 1 ) assert ( store.progress_store.get_completed_block_ids( - section_id="a-test-section", list_item_id="abc123" + SectionKey("a-test-section", "abc123") )[0] == expected_completed_block_ids ) diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index efb1335f0c..7f6baa888f 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1211,7 +1211,7 @@ def block_ids(): @pytest.fixture def routing_path(block_ids): return RoutingPath( - block_ids, + block_ids=block_ids, section_id="section-1", list_item_id="list_item_id", list_name="list_name", diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index 52bd6b7525..c2ef8fc46d 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -6,6 +6,7 @@ from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name +from app.utilities.types import SectionKey from tests.app.questionnaire.conftest import get_metadata @@ -32,9 +33,11 @@ def test_simple_path(answer_store, list_store, supplementary_data_store): ) section_id = schema.get_section_id_for_block_id("name-block") - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) - assumed_routing_path = RoutingPath(["name-block"], section_id="default-section") + assumed_routing_path = RoutingPath( + block_ids=["name-block"], section_id="default-section" + ) assert routing_path == assumed_routing_path @@ -58,7 +61,7 @@ def test_introduction_in_path_when_in_schema( supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=current_section["id"]) + routing_path = path_finder.routing_path(SectionKey(current_section["id"])) assert "introduction" in routing_path @@ -80,7 +83,7 @@ def test_introduction_not_in_path_when_not_in_schema( supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=current_section["id"]) + routing_path = path_finder.routing_path(SectionKey(current_section["id"])) assert "introduction" not in routing_path @@ -95,7 +98,7 @@ def test_routing_basic_and_conditional_path( schema = load_schema_from_name("test_routing_number_equals") section_id = schema.get_section_id_for_block_id("number-question") expected_path = RoutingPath( - ["number-question", "correct-answer"], + block_ids=["number-question", "correct-answer"], section_id="default-section", ) @@ -113,7 +116,7 @@ def test_routing_basic_and_conditional_path( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) # Then assert routing_path == expected_path @@ -135,7 +138,7 @@ def test_routing_path_with_complete_introduction( ] ) expected_routing_path = RoutingPath( - [ + block_ids=[ "introduction", "report-radio", "reporting-date", @@ -158,7 +161,7 @@ def test_routing_path_with_complete_introduction( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_routing_path @@ -167,7 +170,7 @@ def test_routing_path(answer_store, list_store, supplementary_data_store): schema = load_schema_from_name("test_submit_with_summary") section_id = schema.get_section_id_for_block_id("dessert") expected_path = RoutingPath( - ["radio", "dessert", "dessert-confirmation", "numbers"], + block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) @@ -195,7 +198,7 @@ def test_routing_path(answer_store, list_store, supplementary_data_store): {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_path @@ -230,12 +233,10 @@ def test_routing_path_with_repeating_sections( ) repeating_section_id = "personal-details-section" - routing_path = path_finder.routing_path( - section_id=repeating_section_id, list_item_id="abc123" - ) + routing_path = path_finder.routing_path(SectionKey(repeating_section_id, "abc123")) expected_path = RoutingPath( - ["proxy", "date-of-birth", "confirm-dob", "sex"], + block_ids=["proxy", "date-of-birth", "confirm-dob", "sex"], section_id="personal-details-section", list_name="people", list_item_id="abc123", @@ -250,7 +251,7 @@ def test_routing_path_empty_routing_rules( 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"], + block_ids=["mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox"], section_id="default-section", ) @@ -282,7 +283,7 @@ def test_routing_path_empty_routing_rules( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_path @@ -293,7 +294,7 @@ def test_routing_path_with_conditional_value_not_in_metadata( schema = load_schema_from_name("test_metadata_routing") section_id = schema.get_section_id_for_block_id("block1") expected_path = RoutingPath( - ["block1", "block2", "block3"], section_id="default-section" + block_ids=["block1", "block2", "block3"], section_id="default-section" ) progress_store = ProgressStore( @@ -317,7 +318,7 @@ def test_routing_path_with_conditional_value_not_in_metadata( supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_path @@ -353,12 +354,12 @@ def test_routing_path_should_skip_block( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) # Then expected_routing_path_ids = ["do-you-want-to-skip"] expected_routing_path = RoutingPath( - expected_routing_path_ids, + block_ids=expected_routing_path_ids, section_id="default-section", ) @@ -396,11 +397,11 @@ def test_routing_path_should_skip_group( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) # Then expected_routing_path = RoutingPath( - ["do-you-want-to-skip"], + block_ids=["do-you-want-to-skip"], section_id="default-section", ) @@ -438,11 +439,11 @@ def test_routing_path_should_not_skip_group( {}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) # Then expected_routing_path = RoutingPath( - ["do-you-want-to-skip", "should-skip"], + block_ids=["do-you-want-to-skip", "should-skip"], section_id="default-section", ) @@ -478,7 +479,7 @@ def test_get_routing_path_when_first_block_in_group_skipped( block_ids=["do-you-want-to-skip"], ) - assert expected_route == path_finder.routing_path(section_id="default-section") + assert expected_route == path_finder.routing_path(SectionKey("default-section")) def test_build_path_with_group_routing( @@ -503,7 +504,7 @@ def test_build_path_with_group_routing( {}, supplementary_data_store=supplementary_data_store, ) - path = path_finder.routing_path(section_id=section_id) + path = path_finder.routing_path(SectionKey(section_id)) # Then it should route me straight to Group2 and not Group1 assert "group1-block" not in path @@ -555,17 +556,17 @@ def test_remove_answer_and_block_if_routing_backwards( assert ( len( path_finder.progress_store.get_completed_block_ids( - section_id="default-section" + SectionKey("default-section") ) ) == 3 ) assert len(path_finder.answer_store) == 3 - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) expected_path = RoutingPath( - [ + block_ids=[ "route-backwards-block", "number-of-employees-total-block", "confirm-zero-employees-block", @@ -575,15 +576,13 @@ def test_remove_answer_and_block_if_routing_backwards( ) assert routing_path == expected_path assert path_finder.progress_store.get_completed_block_ids( - section_id="default-section" - ) == progress_store.get_completed_block_ids(section_id="default-section") + SectionKey("default-section") + ) == progress_store.get_completed_block_ids(SectionKey("default-section")) assert len(path_finder.answer_store) == 2 assert not path_finder.answer_store.get_answer("confirm-zero-employees-answer") assert ( - path_finder.progress_store.get_section_or_repeating_blocks_progress_status( - section_id="default-section" - ) + path_finder.progress_store.get_section_status(SectionKey("default-section")) == CompletionStatus.IN_PROGRESS ) @@ -700,11 +699,11 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( response_metadata={}, supplementary_data_store=supplementary_data_store, ) - routing_path = path_finder.routing_path(section_id=section_id) + routing_path = path_finder.routing_path(SectionKey(section_id)) # Then the path is built correctly expected_routing_path = RoutingPath( - expected_route, + block_ids=expected_route, section_id=section_id, ) assert routing_path == expected_routing_path @@ -779,12 +778,14 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating supplementary_data_store=supplementary_data_store, ) routing_path = path_finder.routing_path( - section_id="household-personal-details-section", list_item_id="lCIZsS" + SectionKey( + section_id="household-personal-details-section", list_item_id="lCIZsS" + ) ) # Then the path is built correctly expected_routing_path = RoutingPath( - expected_route, + block_ids=expected_route, section_id="household-personal-details-section", list_item_id="lCIZsS", list_name="people", diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index 3944e98d15..d221f529a5 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -12,11 +12,9 @@ from app.data_models.progress_store import CompletionStatus, ProgressStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema -from app.questionnaire.questionnaire_store_updater import ( - DependentSection, - QuestionnaireStoreUpdater, -) +from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.utilities.schema import load_schema_from_name +from app.utilities.types import DependentSection, SectionKey # pylint: disable=too-many-locals, too-many-lines @@ -281,7 +279,7 @@ def test_remove_all_answers_with_list_item_id( mocker, ): mock_empty_answer_store = AnswerStore( - existing_answers=[ + answers=[ {"answer_id": "test1", "value": 1, "list_item_id": "abcdef"}, {"answer_id": "test2", "value": 2, "list_item_id": "abcdef"}, {"answer_id": "test3", "value": 3, "list_item_id": "uvwxyz"}, @@ -314,7 +312,7 @@ def test_remove_primary_person( mocker, ): mock_empty_answer_store = AnswerStore( - existing_answers=[ + answers=[ {"answer_id": "test1", "value": 1, "list_item_id": "abcdef"}, {"answer_id": "test2", "value": 2, "list_item_id": "abcdef"}, {"answer_id": "test3", "value": 3, "list_item_id": "xyzabc"}, @@ -475,7 +473,7 @@ def test_update_same_name_items( mocker, ): mock_empty_answer_store = AnswerStore( - existing_answers=[ + answers=[ {"answer_id": "first-name", "value": "Joe", "list_item_id": "abcdef"}, { "answer_id": "middle-name", @@ -919,12 +917,7 @@ def test_answer_id_section_dependents( questionnaire_store_updater.update_answers(form_data) questionnaire_store_updater.update_progress_for_dependent_sections() - assert ( - progress_store.get_section_or_repeating_blocks_progress_status( - section_id="section-2" - ) - is expected_status - ) + assert progress_store.get_section_status(SectionKey("section-2")) is expected_status @pytest.mark.parametrize( @@ -1058,15 +1051,11 @@ def test_answer_id_section_dependents_repeating( questionnaire_store_updater.update_progress_for_dependent_sections() assert ( - progress_store.get_section_or_repeating_blocks_progress_status( - section_id="section-2", list_item_id="list-item-id-1" - ) + progress_store.get_section_status(SectionKey("section-2", "list-item-id-1")) is expected_list_item_1_status ) assert ( - progress_store.get_section_or_repeating_blocks_progress_status( - section_id="section-2", list_item_id="list-item-id-2" - ) + progress_store.get_section_status(SectionKey("section-2", "list-item-id-2")) is expected_list_item_2_status ) @@ -1157,7 +1146,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update } assert dependent_block_id in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) mocker.patch( @@ -1174,11 +1163,11 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) assert ( - progress_store.get_section_or_repeating_blocks_progress_status( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + progress_store.get_section_status( + section_key=SectionKey(*dependent_section_key) ) == CompletionStatus.IN_PROGRESS ) @@ -1211,11 +1200,9 @@ def test_dependent_sections_current_section_status_not_updated(mocker): dependent_section_key: {dependent_block_id} } - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( - mocker.Mock() - ) + questionnaire_store_updater.update_section_status = mocker.Mock() assert dependent_block_id in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) # When @@ -1224,13 +1211,10 @@ def test_dependent_sections_current_section_status_not_updated(mocker): # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) # Status for current section is handled separately by handle post. - assert ( - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count - == 0 - ) + assert questionnaire_store_updater.update_section_status.call_count == 0 def test_dependent_sections_not_started_skipped(mock_router, mocker): @@ -1265,9 +1249,7 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): } questionnaire_store_updater.remove_completed_location = mocker.Mock() - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( - mocker.Mock() - ) + questionnaire_store_updater.update_section_status = mocker.Mock() # When questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() @@ -1275,10 +1257,7 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): # Then assert questionnaire_store_updater.remove_completed_location.call_count == 0 - assert ( - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count - == 0 - ) + assert questionnaire_store_updater.update_section_status.call_count == 0 def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): @@ -1314,12 +1293,10 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): questionnaire_store_updater.dependent_block_id_by_section_key = { dependent_section_key: {dependent_block_id} } - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status = ( - mocker.Mock() - ) + questionnaire_store_updater.update_section_status = mocker.Mock() assert dependent_block_id not in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) # When @@ -1327,10 +1304,7 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): questionnaire_store_updater.update_progress_for_dependent_sections() # Then - assert ( - questionnaire_store_updater.update_section_or_repeating_blocks_progress_completion_status.call_count - == 0 - ) + assert questionnaire_store_updater.update_section_status.call_count == 0 @pytest.mark.parametrize( @@ -1413,12 +1387,10 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta for list_item in list_store["some-list"]: section_id, list_item_id = "breakdown-section", list_item assert "turnover-breakdown-block" not in progress_store.get_completed_block_ids( - section_id=section_id, list_item_id=list_item_id + SectionKey(section_id, list_item_id) ) assert ( - progress_store.get_section_or_repeating_blocks_progress_status( - section_id, list_item_id - ) + progress_store.get_section_status(SectionKey(section_id, list_item_id)) == CompletionStatus.IN_PROGRESS ) assert questionnaire_store_updater.dependent_sections == { @@ -1471,7 +1443,7 @@ def test_dependent_sections_added_dependant_block_removed( } assert dependent_block_id in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) assert questionnaire_store_updater.dependent_sections == set() @@ -1480,7 +1452,7 @@ def test_dependent_sections_added_dependant_block_removed( # Then assert dependent_block_id not in progress_store.get_completed_block_ids( - section_id=dependent_section_key[0], list_item_id=dependent_section_key[1] + section_key=SectionKey(*dependent_section_key) ) assert questionnaire_store_updater.dependent_sections == { DependentSection( @@ -1496,37 +1468,37 @@ def test_dependent_sections_added_dependant_block_removed( # s1.s1 -> s1.s2 -> s2.s3 -> s3.s4 -> s3.s5 -> s3.s7 -> s2.s6 [], [ - call("section-1", list_item_id=None), - call("section-2", list_item_id=None), - call("section-3", list_item_id=None), - call("section-4", list_item_id=None), - call("section-5", list_item_id=None), - call("section-7", list_item_id=None), - call("section-6", list_item_id=None), + call(SectionKey("section-1")), + call(SectionKey("section-2")), + call(SectionKey("section-3")), + call(SectionKey("section-4")), + call(SectionKey("section-5")), + call(SectionKey("section-7")), + call(SectionKey("section-6")), ], ), ( # s1 -> s1.s2 -> s1.s3 -> s3.s4 -> s3.s5 -> s3.s7 ["section-2"], [ - call("section-1", list_item_id=None), - call("section-2", list_item_id=None), - call("section-3", list_item_id=None), - call("section-4", list_item_id=None), - call("section-5", list_item_id=None), - call("section-7", list_item_id=None), + call(SectionKey("section-1")), + call(SectionKey("section-2")), + call(SectionKey("section-3")), + call(SectionKey("section-4")), + call(SectionKey("section-5")), + call(SectionKey("section-7")), ], ), ( # s1 -> s1.s2 -> s2.s3 -> s2.s4 -> s2.s5 -> s2.s6 ["section-3"], [ - call("section-1", list_item_id=None), - call("section-2", list_item_id=None), - call("section-3", list_item_id=None), - call("section-4", list_item_id=None), - call("section-5", list_item_id=None), - call("section-6", list_item_id=None), + call(SectionKey("section-1")), + call(SectionKey("section-2")), + call(SectionKey("section-3")), + call(SectionKey("section-4")), + call(SectionKey("section-5")), + call(SectionKey("section-6")), ], ), ], diff --git a/tests/app/questionnaire/test_relationship_location.py b/tests/app/questionnaire/test_relationship_location.py index ed1d981f1b..9ed402e590 100644 --- a/tests/app/questionnaire/test_relationship_location.py +++ b/tests/app/questionnaire/test_relationship_location.py @@ -1,5 +1,6 @@ import pytest +from app.questionnaire.location import SectionKey from app.questionnaire.relationship_location import RelationshipLocation @@ -40,3 +41,6 @@ def test_create_location_from_dict(): assert location.block_id == "relationships" assert location.list_item_id == "id1" assert location.to_list_item_id == "id2" + assert location.section_key == SectionKey( + section_id=location.section_id, list_item_id=location.list_item_id + ) diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 59c75651ef..562ce279eb 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -9,7 +9,7 @@ from app.data_models.list_store import ListStore from app.data_models.progress_store import CompletionStatus, ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore -from app.questionnaire.location import Location +from app.questionnaire.location import Location, SectionKey from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name @@ -64,7 +64,7 @@ def test_full_routing_path_without_repeating_sections(self): expected_path = [ RoutingPath( - [ + block_ids=[ "mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox", @@ -93,7 +93,7 @@ def test_full_routing_path_with_repeating_sections(self): expected_path = [ RoutingPath( - [ + block_ids=[ "primary-person-list-collector", "list-collector", "next-interstitial", @@ -101,17 +101,15 @@ def test_full_routing_path_with_repeating_sections(self): "visitors-block", ], section_id="section", - list_name=None, - list_item_id=None, ), RoutingPath( - ["proxy", "date-of-birth", "confirm-dob", "sex"], + block_ids=["proxy", "date-of-birth", "confirm-dob", "sex"], section_id="personal-details-section", list_name="people", list_item_id="abc123", ), RoutingPath( - ["proxy", "date-of-birth", "confirm-dob", "sex"], + block_ids=["proxy", "date-of-birth", "confirm-dob", "sex"], section_id="personal-details-section", list_name="people", list_item_id="123abc", @@ -135,7 +133,7 @@ def test_is_complete(self): ] ) - routing_path = self.router.routing_path(section_id="default-section") + routing_path = self.router.routing_path(SectionKey("default-section")) is_path_complete = self.router.is_path_complete(routing_path) assert is_path_complete @@ -143,7 +141,7 @@ def test_is_complete(self): def test_is_not_complete(self): self.schema = load_schema_from_name("test_textfield") - routing_path = self.router.routing_path(section_id="default-section") + routing_path = self.router.routing_path(SectionKey("default-section")) is_path_complete = self.router.is_path_complete(routing_path) assert not is_path_complete @@ -239,7 +237,9 @@ def test_can_access(self): self.schema = load_schema_from_name("test_textfield") current_location = Location(section_id="default-section", block_id="name-block") - routing_path = RoutingPath(["name-block"], section_id="default-section") + routing_path = RoutingPath( + block_ids=["name-block"], section_id="default-section" + ) can_access_location = self.router.can_access_location( current_location, routing_path ) @@ -255,7 +255,9 @@ def test_can_access_with_list_name_and_list_name_id(self): list_name="default-list", list_item_id="default-list-id", ) - routing_path = RoutingPath(["name-block"], section_id="default-section") + routing_path = RoutingPath( + block_ids=["name-block"], section_id="default-section" + ) can_access_location = self.router.can_access_location( current_location, routing_path ) @@ -317,7 +319,7 @@ def test_cant_access_not_on_allowable_path(self): section_id="default-section", block_id="set-duration-units-block" ) routing_path = RoutingPath( - [ + block_ids=[ "set-length-units-block", "set-duration-units-block", "set-area-units-block", @@ -351,7 +353,11 @@ def test_within_section(self): section_id="default-section", block_id="mandatory-checkbox" ) routing_path = RoutingPath( - ["mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox"], + block_ids=[ + "mandatory-checkbox", + "non-mandatory-checkbox", + "single-checkbox", + ], section_id="default-section", ) next_location = self.router.get_next_location_url( @@ -383,7 +389,7 @@ def test_last_block_in_section_but_section_is_not_complete_when_routing_backward current_location = Location(section_id="section-1", block_id="block-1") # Simulates routing backwards. Last block in section does not mean section is complete. routing_path = RoutingPath( - ["block-1", "block-2", "block-1"], section_id="section-1" + block_ids=["block-1", "block-2", "block-1"], section_id="section-1" ) next_location = self.router.get_next_location_url( current_location, routing_path @@ -413,7 +419,7 @@ def test_return_to_section_summary_section_is_complete(self): section_id="property-details-section", block_id="insurance-type" ) routing_path = RoutingPath( - ["insurance-type", "insurance-address", "listed"], + block_ids=["insurance-type", "insurance-address", "listed"], section_id="property-details-section", ) next_location = self.router.get_next_location_url( @@ -446,7 +452,12 @@ def test_return_to_section_summary_section_is_in_progress(self): section_id="property-details-section", block_id="insurance-address" ) routing_path = RoutingPath( - ["insurance-type", "insurance-address", "address-duration", "listed"], + block_ids=[ + "insurance-type", + "insurance-address", + "address-duration", + "listed", + ], section_id="property-details-section", ) next_location = self.router.get_next_location_url( @@ -474,7 +485,7 @@ def test_section_summary_on_completion_true(self): current_location = Location( section_id="accommodation-section", block_id="proxy" ) - routing_path = RoutingPath(["proxy"], section_id="default-section") + routing_path = RoutingPath(block_ids=["proxy"], section_id="default-section") next_location = self.router.get_next_location_url( current_location, routing_path ) @@ -498,7 +509,8 @@ def test_section_summary_on_completion_false(self): section_id="employment-section", block_id="employment-type" ) routing_path = RoutingPath( - ["employment-status", "employment-type"], section_id="employment-section" + block_ids=["employment-status", "employment-type"], + section_id="employment-section", ) next_location = self.router.get_next_location_url( current_location, routing_path @@ -538,7 +550,7 @@ def test_return_to_calculated_summary(self, schema): ) routing_path = RoutingPath( - [ + block_ids=[ "first-number-block", "second-number-block", "currency-total-playback", @@ -595,7 +607,7 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): # block 3 is complete, and block 4 is not, so block 4 should be routed to before the calculated summary routing_path = RoutingPath( - [ + block_ids=[ "block-3", "block-4", "calculated-summary-block", @@ -661,7 +673,7 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( ) routing_path = RoutingPath( - ["fifth-number-block", "sixth-number-block"], + block_ids=["fifth-number-block", "sixth-number-block"], section_id="default-section", ) next_location_url = self.router.get_next_location_url( @@ -695,7 +707,7 @@ def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schem ) routing_path = RoutingPath( - ["fifth-number-block", "sixth-number-block"], + block_ids=["fifth-number-block", "sixth-number-block"], section_id="default-section", ) next_location_url = self.router.get_next_location_url( @@ -727,7 +739,7 @@ def test_return_to_grand_calculated_summary_from_answer( ) routing_path = RoutingPath( - ["distance-calculated-summary-1"], + block_ids=["distance-calculated-summary-1"], section_id="section-1", ) next_location_url = self.router.get_next_location_url( @@ -763,7 +775,7 @@ def test_return_to_grand_calculated_summary_from_calculated_summary( ) routing_path = RoutingPath( - ["distance-calculated-summary-1"], + block_ids=["distance-calculated-summary-1"], section_id="section-1", ) next_location_url = self.router.get_next_location_url( @@ -818,7 +830,7 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( current_location = Location(section_id="section-1", block_id="block-2") routing_path = RoutingPath( - [ + block_ids=[ "block-1", "block-2", "calculated-summary-1", @@ -875,7 +887,7 @@ def test_return_to_calculated_summary_from_incomplete_section( section_id="section-1", block_id="first-number-block" ) routing_path = RoutingPath( - [ + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", @@ -985,7 +997,7 @@ def test_return_to_inaccessible_summary_routes_to_next_incomplete_block( """ self.schema = load_schema_from_name(schema) current_location = Location(section_id=section, block_id=block_id) - routing_path = RoutingPath(routing_path_block_ids, section_id=section) + routing_path = RoutingPath(block_ids=routing_path_block_ids, section_id=section) # make a copy where next_incomplete_block_id is not yet completed completed_block_ids = [*routing_path_block_ids] @@ -1036,7 +1048,9 @@ def test_redirects_to_submit_page_when_questionnaire_complete( ) current_location = Location(section_id="default-section", block_id="name-block") - routing_path = RoutingPath(["name-block"], section_id="default-section") + routing_path = RoutingPath( + block_ids=["name-block"], section_id="default-section" + ) next_location = self.router.get_next_location_url( current_location, routing_path ) @@ -1059,7 +1073,7 @@ def test_return_to_final_summary_questionnaire_and_section_is_complete(self): ] ) current_location = Location(section_id="test-section", block_id="test-forced") - routing_path = RoutingPath(["test-forced"], section_id="test-section") + routing_path = RoutingPath(block_ids=["test-forced"], section_id="test-section") next_location = self.router.get_next_location_url( current_location, routing_path, return_to="final-summary" ) @@ -1083,7 +1097,7 @@ def test_return_to_final_summary_section_is_in_progress(self): section_id="default-section", block_id="dessert-confirmation" ) routing_path = RoutingPath( - ["radio", "dessert", "dessert-confirmation", "numbers"], + block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) next_location = self.router.get_next_location_url( @@ -1110,7 +1124,7 @@ def test_return_to_final_summary_questionnaire_is_not_complete(self): ) current_location = Location(section_id="test-section", block_id="test-forced") - routing_path = RoutingPath(["test-forced"], section_id="test-section") + routing_path = RoutingPath(block_ids=["test-forced"], section_id="test-section") next_location = self.router.get_next_location_url( current_location, routing_path, return_to="final-summary" ) @@ -1133,7 +1147,7 @@ def test_within_section(self): ) routing_path = RoutingPath( - ["mandatory-checkbox", "non-mandatory-checkbox"], + block_ids=["mandatory-checkbox", "non-mandatory-checkbox"], section_id="default-section", ) previous_location_url = self.router.get_previous_location_url( @@ -1154,7 +1168,7 @@ def test_return_to_calculated_summary(self): ) routing_path = RoutingPath( - [ + block_ids=[ "currency-total-playback-skipped-fourth", ], section_id="default-section", @@ -1211,7 +1225,7 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( ) routing_path = RoutingPath( - [ + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", @@ -1267,7 +1281,7 @@ def test_return_to_grand_calculated_summary_from_calculated_summary_incomplete_s ) routing_path = RoutingPath( - [ + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", @@ -1330,7 +1344,7 @@ def test_return_to_grand_calculated_summary_invalid_url( current_location = Location(section_id="section-1", block_id=current_block) routing_path = RoutingPath( - [ + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", @@ -1366,7 +1380,7 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( ) routing_path = RoutingPath( - ["calculated-summary-6"], + block_ids=["calculated-summary-6"], section_id="section-5", ) next_location_url = self.router.get_previous_location_url( @@ -1405,7 +1419,7 @@ def test_return_to_section_summary_section_is_complete(self): section_id="property-details-section", block_id="insurance-type" ) routing_path = RoutingPath( - ["insurance-type", "insurance-address", "listed"], + block_ids=["insurance-type", "insurance-address", "listed"], section_id="default-section", ) previous_location_url = self.router.get_previous_location_url( @@ -1438,7 +1452,7 @@ def test_return_to_section_summary_section_is_in_progress(self): section_id="property-details-section", block_id="insurance-address" ) routing_path = RoutingPath( - ["insurance-type", "insurance-address", "listed"], + block_ids=["insurance-type", "insurance-address", "listed"], section_id="default-section", ) previous_location_url = self.router.get_previous_location_url( @@ -1474,7 +1488,7 @@ def test_return_to_final_summary_section_is_complete(self): current_location = Location(section_id="default-section", block_id="radio") routing_path = RoutingPath( - ["radio", "dessert", "dessert-confirmation", "numbers"], + block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) previous_location = self.router.get_previous_location_url( @@ -1507,7 +1521,7 @@ def test_return_to_final_summary_section_is_in_progress(self): current_location = Location(section_id="default-section", block_id="dessert") routing_path = RoutingPath( - ["radio", "dessert", "dessert-confirmation", "numbers"], + block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) previous_location = self.router.get_previous_location_url( @@ -1538,7 +1552,11 @@ def test_is_none_on_first_block_single_section(self): ] ) routing_path = RoutingPath( - ["mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox"], + block_ids=[ + "mandatory-checkbox", + "non-mandatory-checkbox", + "single-checkbox", + ], section_id="default-section", ) @@ -1572,7 +1590,9 @@ def test_is_none_on_first_block_second_section(self): current_location = Location( section_id="house-details-section", block_id="house-type" ) - routing_path = RoutingPath(["house-type"], section_id="house-details-section") + routing_path = RoutingPath( + block_ids=["house-type"], section_id="house-details-section" + ) previous_location_url = self.router.get_previous_location_url( current_location, routing_path ) @@ -1590,7 +1610,8 @@ def test_is_not_none_on_first_block_in_section(self): ) routing_path = RoutingPath( - ["employment-status", "employment-type"], section_id="employment-section" + block_ids=["employment-status", "employment-type"], + section_id="employment-section", ) previous_location_url = self.router.get_previous_location_url( current_location, routing_path @@ -1657,9 +1678,7 @@ def test_last_block_not_on_path(self): ).url() last_completed_block_in_progress_store = ( - self.progress_store.get_completed_block_ids( - section_id=section_id, list_item_id=None - )[-1] + self.progress_store.get_completed_block_ids(SectionKey(section_id))[-1] ) last_location_url = self.router.get_last_location_in_questionnaire_url() @@ -1685,7 +1704,7 @@ def test_section_in_progress_returns_url_for_first_incomplete_location(self): ) section_routing_path = RoutingPath( - ["insurance-type", "insurance-address"], + block_ids=["insurance-type", "insurance-address"], section_id="property-details-section", ) @@ -1711,7 +1730,8 @@ def test_section_complete_returns_url_for_first_location( ) routing_path = RoutingPath( - ["employment-status", "employment-type"], section_id="employment-section" + block_ids=["employment-status", "employment-type"], + section_id="employment-section", ) section_resume_url = self.router.get_section_resume_url( diff --git a/tests/app/questionnaire/test_routing_path.py b/tests/app/questionnaire/test_routing_path.py index a6cb6cde77..226e2fac69 100644 --- a/tests/app/questionnaire/test_routing_path.py +++ b/tests/app/questionnaire/test_routing_path.py @@ -1,9 +1,10 @@ from app.questionnaire.routing_path import RoutingPath +from app.utilities.types import SectionKey def test_eq_to_routing_path(block_ids, routing_path): assert routing_path == RoutingPath( - block_ids, + block_ids=block_ids, section_id="section-1", list_item_id="list_item_id", list_name="list_name", @@ -40,3 +41,4 @@ def test_properties(block_ids, routing_path): assert "section-1" == routing_path.section_id assert "list_item_id" == routing_path.list_item_id assert "list_name" == routing_path.list_name + assert SectionKey("section-1", "list_item_id") == routing_path.section_key diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 8616cd320a..71b3e8b9f3 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -50,7 +50,7 @@ def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): questionnaire = make_schema("0.0.1", "section-1", "group-1", "block-1", question) full_routing_path = [ - RoutingPath(["block-1"], section_id="section-1", list_item_id=None) + RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] answer_object = convert_answers_to_payload_0_0_1( metadata=questionnaire_store.metadata, @@ -87,7 +87,7 @@ def test_answer_with_zero(version): questionnaire = make_schema("0.0.1", "section-1", "group-1", "block-1", question) full_routing_path = [ - RoutingPath(["block-1"], section_id="section-1", list_item_id=None) + RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] schema = QuestionnaireSchema(questionnaire) @@ -127,7 +127,7 @@ def test_answer_with_float(version): questionnaire = make_schema("0.0.1", "section-1", "group-1", "block-1", question) full_routing_path = [ - RoutingPath(["block-1"], section_id="section-1", list_item_id=None) + RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] schema = QuestionnaireSchema(questionnaire) @@ -170,7 +170,7 @@ def test_answer_with_string(version): questionnaire = make_schema("0.0.1", "section-1", "group-1", "block-1", question) full_routing_path = [ - RoutingPath(["block-1"], section_id="section-1", list_item_id=None) + RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] schema = QuestionnaireSchema(questionnaire) @@ -213,7 +213,7 @@ def test_answer_without_qcode(version): questionnaire = make_schema("0.0.1", "section-1", "group-1", "block-1", question) full_routing_path = [ - RoutingPath(["block-1"], section_id="section-1", list_item_id=None) + RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] schema = QuestionnaireSchema(questionnaire) @@ -242,7 +242,7 @@ def test_answer_without_qcode(version): def test_converter_checkboxes_with_q_codes(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] questionnaire_store.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -317,7 +317,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value( ): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] questionnaire_store.answer_store = AnswerStore( [ @@ -397,7 +397,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value( def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] questionnaire_store.answer_store = AnswerStore( [ @@ -471,7 +471,7 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(v def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] questionnaire_store.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] @@ -543,7 +543,7 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): def test_converter_q_codes_for_empty_strings(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="food", list_item_id=None)] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] questionnaire_store.answer_store = AnswerStore( [ Answer("crisps-answer", "").to_dict(), @@ -599,7 +599,9 @@ def test_radio_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["radio-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["radio-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [ @@ -669,7 +671,9 @@ def test_number_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["number-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["number-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [Answer("number-answer", 0.9999).to_dict()] @@ -715,7 +719,9 @@ def test_percentage_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["percentage-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["percentage-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [Answer("percentage-answer", 100).to_dict()] @@ -761,7 +767,9 @@ def test_textarea_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["textarea-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["textarea-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [Answer("textarea-answer", "example text.").to_dict()] @@ -807,7 +815,9 @@ def test_currency_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["currency-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["currency-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [Answer("currency-answer", 99.99).to_dict()] @@ -853,7 +863,9 @@ def test_dropdown_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["dropdown-block"], section_id="section-1", list_item_id=None) + RoutingPath( + block_ids=["dropdown-block"], section_id="section-1", list_item_id=None + ) ] questionnaire_store.answer_store = AnswerStore( [Answer("dropdown-answer", "Liverpool").to_dict()] @@ -909,9 +921,7 @@ def test_dropdown_answer(version): def test_date_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [ - RoutingPath(["date-block"], section_id="section-1", list_item_id=None) - ] + full_routing_path = [RoutingPath(block_ids=["date-block"], section_id="section-1")] questionnaire_store.answer_store = AnswerStore( [ @@ -963,9 +973,7 @@ def test_date_answer(version): def test_unit_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [ - RoutingPath(["unit-block"], section_id="section-1", list_item_id=None) - ] + full_routing_path = [RoutingPath(block_ids=["unit-block"], section_id="section-1")] questionnaire_store.answer_store = AnswerStore( [Answer("unit-answer", 10).to_dict()] ) diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 3f1c9bda81..fc2e8e5254 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -31,7 +31,9 @@ def test_convert_answers_v2_to_payload_0_0_3(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["about you", "where you live"], section_id="household-section") + RoutingPath( + block_ids=["about you", "where you live"], section_id="household-section" + ) ] questionnaire_store.answer_store = AnswerStore( @@ -111,7 +113,7 @@ def test_convert_answers_v2_to_payload_0_0_3(version): def test_convert_payload_0_0_3_multiple_answers(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["crisps"], section_id="section-1")] + full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="section-1")] answers = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -168,7 +170,7 @@ def test_convert_payload_0_0_3_multiple_answers(version): def test_radio_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["radio-block"], section_id="section-1")] + full_routing_path = [RoutingPath(block_ids=["radio-block"], section_id="section-1")] answers = AnswerStore([Answer("radio-answer", "Coffee").to_dict()]) questionnaire_store.answer_store = answers @@ -220,7 +222,9 @@ def test_radio_answer(version): def test_number_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["number-block"], section_id="section-1")] + full_routing_path = [ + RoutingPath(block_ids=["number-block"], section_id="section-1") + ] answers = AnswerStore([Answer("number-answer", 1.755).to_dict()]) questionnaire_store.answer_store = answers @@ -263,7 +267,9 @@ def test_number_answer(version): def test_percentage_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["percentage-block"], section_id="section-1")] + full_routing_path = [ + RoutingPath(block_ids=["percentage-block"], section_id="section-1") + ] answers = AnswerStore([Answer("percentage-answer", 99).to_dict()]) questionnaire_store.answer_store = answers @@ -306,7 +312,9 @@ def test_percentage_answer(version): def test_textarea_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["textarea-block"], section_id="section-1")] + full_routing_path = [ + RoutingPath(block_ids=["textarea-block"], section_id="section-1") + ] answers = AnswerStore( [Answer("textarea-answer", "This is an example text!").to_dict()] ) @@ -351,7 +359,9 @@ def test_textarea_answer(version): def test_currency_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["currency-block"], section_id="section-1")] + full_routing_path = [ + RoutingPath(block_ids=["currency-block"], section_id="section-1") + ] answers = AnswerStore([Answer("currency-answer", 100).to_dict()]) questionnaire_store.answer_store = answers @@ -394,7 +404,9 @@ def test_currency_answer(version): def test_dropdown_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["dropdown-block"], section_id="section-1")] + full_routing_path = [ + RoutingPath(block_ids=["dropdown-block"], section_id="section-1") + ] answers = AnswerStore([Answer("dropdown-answer", "Rugby is better!").to_dict()]) questionnaire_store.answer_store = answers @@ -448,7 +460,7 @@ def test_dropdown_answer(version): def test_date_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["date-block"], section_id="section-1")] + full_routing_path = [RoutingPath(block_ids=["date-block"], section_id="section-1")] answers = AnswerStore( [ Answer("single-date-answer", "01-01-1990").to_dict(), @@ -497,7 +509,7 @@ def test_date_answer(version): def test_month_year_date_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["date-block"], section_id="section-1")] + full_routing_path = [RoutingPath(block_ids=["date-block"], section_id="section-1")] answers = AnswerStore( [ Answer("single-date-answer", "01-01-1990").to_dict(), @@ -546,7 +558,7 @@ def test_month_year_date_answer(version): def test_unit_answer(version): questionnaire_store = get_questionnaire_store(version) - full_routing_path = [RoutingPath(["unit-block"], section_id="section-1")] + full_routing_path = [RoutingPath(block_ids=["unit-block"], section_id="section-1")] answers = AnswerStore([Answer("unit-answer", 10).to_dict()]) questionnaire_store.answer_store = answers @@ -591,7 +603,8 @@ def test_primary_person_list_item_conversion(version): routing_path = [ RoutingPath( - ["primary-person-list-collector", "list-collector"], section_id="section-1" + block_ids=["primary-person-list-collector", "list-collector"], + section_id="section-1", ) ] @@ -607,7 +620,7 @@ def test_primary_person_list_item_conversion(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": ["xJlKBy", "RfAGDc"], @@ -651,7 +664,11 @@ def test_list_item_conversion(version): routing_path = [ RoutingPath( - ["list-collector", "next-interstitial", "another-list-collector-block"], + block_ids=[ + "list-collector", + "next-interstitial", + "another-list-collector-block", + ], section_id="section-1", ) ] @@ -668,9 +685,7 @@ def test_list_item_conversion(version): answers = AnswerStore(answer_objects) - list_store = ListStore( - existing_items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}] - ) + list_store = ListStore(items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}]) questionnaire_store.answer_store = answers questionnaire_store.list_store = list_store @@ -711,7 +726,11 @@ def test_list_item_conversion_empty_list(version): routing_path = [ RoutingPath( - ["list-collector", "next-interstitial", "another-list-collector-block"], + block_ids=[ + "list-collector", + "next-interstitial", + "another-list-collector-block", + ], section_id="section-1", ) ] @@ -771,7 +790,8 @@ def test_default_answers_not_present_when_not_answered(version): routing_path = [ RoutingPath( - ["number-question-one", "number-question-two"], section_id="default-section" + block_ids=["number-question-one", "number-question-two"], + section_id="default-section", ) ] @@ -804,7 +824,8 @@ def test_list_structure_in_payload_is_as_expected(version): routing_path = [ RoutingPath( - ["primary-person-list-collector", "list-collector"], section_id="section-1" + block_ids=["primary-person-list-collector", "list-collector"], + section_id="section-1", ) ] @@ -820,7 +841,7 @@ def test_list_structure_in_payload_is_as_expected(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": ["xJlKBy", "RfAGDc"], @@ -864,7 +885,11 @@ def test_primary_person_not_in_payload_when_not_answered(version): routing_path = [ RoutingPath( - ["list-collector", "next-interstitial", "another-list-collector-block"], + block_ids=[ + "list-collector", + "next-interstitial", + "another-list-collector-block", + ], section_id="section-1", ) ] @@ -881,9 +906,7 @@ def test_primary_person_not_in_payload_when_not_answered(version): answers = AnswerStore(answer_objects) - list_store = ListStore( - existing_items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}] - ) + list_store = ListStore(items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}]) questionnaire_store.answer_store = answers questionnaire_store.list_store = list_store @@ -918,7 +941,7 @@ def test_relationships_in_payload(version): routing_path = [ RoutingPath( - ["list-collector", "relationships"], + block_ids=["list-collector", "relationships"], section_id="section", ) ] @@ -951,7 +974,7 @@ def test_relationships_in_payload(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": [ @@ -1011,7 +1034,7 @@ def test_no_relationships_in_payload(version): routing_path = [ RoutingPath( - ["list-collector", "relationships"], + block_ids=["list-collector", "relationships"], section_id="section", ) ] @@ -1029,7 +1052,7 @@ def test_no_relationships_in_payload(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": [ @@ -1075,7 +1098,7 @@ def test_unrelated_block_answers_in_payload(version): routing_path = [ RoutingPath( - ["list-collector", "relationships"], + block_ids=["list-collector", "relationships"], section_id="section", ) ] @@ -1122,7 +1145,7 @@ def test_unrelated_block_answers_in_payload(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": [ @@ -1192,7 +1215,7 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(version): routing_path = [ RoutingPath( - ["list-collector", "relationships"], + block_ids=["list-collector", "relationships"], section_id="section", ) ] @@ -1234,7 +1257,7 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": [ @@ -1284,7 +1307,7 @@ def test_relationship_answers_not_on_path_in_payload(version): routing_path = [ RoutingPath( - ["list-collector", "relationships"], + block_ids=["list-collector", "relationships"], section_id="section", ) ] @@ -1336,7 +1359,7 @@ def test_relationship_answers_not_on_path_in_payload(version): answers = AnswerStore(answer_objects) list_store = ListStore( - existing_items=[ + items=[ { "name": "people", "items": [ @@ -1410,7 +1433,9 @@ def test_answers_codes_only_present_for_answered_questions(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["mandatory-checkbox", "name-block"], section_id="default-section") + RoutingPath( + block_ids=["mandatory-checkbox", "name-block"], section_id="default-section" + ) ] questionnaire_store.answer_store = AnswerStore( @@ -1449,7 +1474,7 @@ def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(ve questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["mandatory-checkbox"], section_id="default-section") + RoutingPath(block_ids=["mandatory-checkbox"], section_id="default-section") ] questionnaire_store.answer_store = AnswerStore( @@ -1490,7 +1515,7 @@ def test_no_answers_codes_in_payload_when_no_questions_answered(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [ - RoutingPath(["mandatory-checkbox"], section_id="default-section") + RoutingPath(block_ids=["mandatory-checkbox"], section_id="default-section") ] questionnaire_store.answer_store = AnswerStore() @@ -1521,7 +1546,7 @@ def test_payload_dynamic_answers(version): full_routing_path = [ RoutingPath( - ["any-supermarket", "list-collector", "dynamic-answer"], + block_ids=["any-supermarket", "list-collector", "dynamic-answer"], section_id="section", ) ] @@ -1579,7 +1604,7 @@ def test_repeating_block_answers_present( full_routing_path = [ RoutingPath( - [ + block_ids=[ "responsible-party", "any-companies-or-branches", "any-other-companies-or-branches", @@ -1684,7 +1709,7 @@ def test_payload_supplementary_data(): full_routing_path = [ RoutingPath( - ["dynamic-answer"], + block_ids=["dynamic-answer"], section_id="section", ) ] diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index c90e0a4413..428facad4e 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -60,7 +60,7 @@ def test_build_view_context_for_grand_calculated_summary( answer_store=test_grand_calculated_summary_answers, list_store=list_store, progress_store=ProgressStore( - in_progress_sections_and_repeating_blocks=[ + progress=[ { "section_id": "section-1", "status": CompletionStatus.COMPLETED, diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 67e2a554d6..50cea1c3f4 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -159,7 +159,7 @@ def test_context_for_section_list_summary( response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( - [ + block_ids=[ "primary-person-list-collector", "list-collector", "visitor-list-collector", @@ -298,7 +298,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( response_metadata={}, current_location=Location(section_id="section-companies"), routing_path=RoutingPath( - [ + block_ids=[ "any-other-companies-or-branches", ], section_id="section-companies", @@ -479,7 +479,9 @@ def test_context_for_driving_question_summary_empty_list(): metadata=None, response_metadata={}, current_location=Location(section_id="section"), - routing_path=RoutingPath(["anyone-usually-live-at"], section_id="section"), + routing_path=RoutingPath( + block_ids=["anyone-usually-live-at"], section_id="section" + ), supplementary_data_store=SupplementaryDataStore(), ) @@ -534,7 +536,8 @@ def test_context_for_driving_question_summary(progress_store): response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( - ["anyone-usually-live-at", "anyone-else-live-at"], section_id="section" + block_ids=["anyone-usually-live-at", "anyone-else-live-at"], + section_id="section", ), supplementary_data_store=SupplementaryDataStore(), ) @@ -653,7 +656,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( - [ + block_ids=[ "primary-person-list-collector", "list-collector", "visitor-list-collector", @@ -692,7 +695,7 @@ def test_primary_links_for_section_summary(people_answer_store): response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( - [ + block_ids=[ "primary-person-list-collector", "list-collector", "visitor-list-collector", From 735c453b477268805a94333ebb99b7598add7d65 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:44:17 +0100 Subject: [PATCH 287/567] Schemas v3.70.1 (#1206) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 75a91e0267..3ba1a5c744 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.70.0 +v3.70.1 From 25c7518c024574de47008a1fb176633a32319f60 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:37:29 +0100 Subject: [PATCH 288/567] Use StrEnum for CompletionStatus/Use typed dict for progress (#1205) --- app/data_models/__init__.py | 2 +- app/data_models/progress.py | 20 +- app/data_models/progress_store.py | 35 +- app/questionnaire/path_finder.py | 3 +- .../questionnaire_store_updater.py | 3 +- app/views/contexts/hub_context.py | 6 +- app/views/handlers/individual_response.py | 2 +- tests/app/data_model/conftest.py | 16 +- tests/app/data_model/test_progress_store.py | 485 +++++++++--------- tests/app/questionnaire/conftest.py | 79 +-- .../rules/test_rule_evaluator.py | 13 +- tests/app/questionnaire/test_path_finder.py | 137 ++--- .../questionnaire/test_placeholder_parser.py | 21 +- .../test_questionnaire_store_updater.py | 177 +++---- tests/app/questionnaire/test_router.py | 314 ++++++------ .../test_grand_calculated_summary_context.py | 3 +- tests/app/views/contexts/test_hub_context.py | 2 +- tests/app/views/contexts/test_list_context.py | 10 +- .../views/contexts/test_summary_context.py | 55 +- 19 files changed, 697 insertions(+), 686 deletions(-) diff --git a/app/data_models/__init__.py b/app/data_models/__init__.py index d081cdbe90..ae4c7beed9 100644 --- a/app/data_models/__init__.py +++ b/app/data_models/__init__.py @@ -1,6 +1,6 @@ from .answer import Answer, AnswerValueTypes from .fulfilment_request import FulfilmentRequest -from .progress_store import CompletionStatus +from .progress import CompletionStatus from .questionnaire_store import ( AnswerStore, ListStore, diff --git a/app/data_models/progress.py b/app/data_models/progress.py index e42229eff8..7983ea57cb 100644 --- a/app/data_models/progress.py +++ b/app/data_models/progress.py @@ -1,29 +1,37 @@ from __future__ import annotations from dataclasses import dataclass +from enum import StrEnum from typing import Mapping, Optional, TypedDict -class ProgressDictType(TypedDict, total=False): +class CompletionStatus(StrEnum): + COMPLETED: str = "COMPLETED" + IN_PROGRESS: str = "IN_PROGRESS" + NOT_STARTED: str = "NOT_STARTED" + INDIVIDUAL_RESPONSE_REQUESTED: str = "INDIVIDUAL_RESPONSE_REQUESTED" + + +class ProgressDict(TypedDict, total=False): section_id: str block_ids: list[str] - status: str - list_item_id: str + status: CompletionStatus + list_item_id: str | None @dataclass class Progress: section_id: str block_ids: list[str] - status: str + status: CompletionStatus list_item_id: Optional[str] = None @classmethod - def from_dict(cls, progress_dict: ProgressDictType) -> Progress: + def from_dict(cls, progress_dict: ProgressDict) -> Progress: return cls( section_id=progress_dict["section_id"], block_ids=progress_dict["block_ids"], - status=progress_dict["status"], + status=CompletionStatus(progress_dict["status"]), list_item_id=progress_dict.get("list_item_id"), ) diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index 9f454a31ef..c381d5a98c 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -1,22 +1,11 @@ -from dataclasses import astuple, dataclass -from typing import Iterable, Iterator, MutableMapping +from typing import Iterable, MutableMapping -from app.data_models.progress import Progress, ProgressDictType +from app.data_models import CompletionStatus +from app.data_models.progress import Progress, ProgressDict from app.questionnaire.location import Location, SectionKey from app.utilities.types import LocationType -@dataclass -class CompletionStatus: - COMPLETED: str = "COMPLETED" - IN_PROGRESS: str = "IN_PROGRESS" - NOT_STARTED: str = "NOT_STARTED" - INDIVIDUAL_RESPONSE_REQUESTED: str = "INDIVIDUAL_RESPONSE_REQUESTED" - - def __iter__(self) -> Iterator[tuple[str]]: - return iter(astuple(self)) - - class ProgressStore: """ An object that stores and updates references to sections and list items @@ -25,7 +14,7 @@ class ProgressStore: def __init__( self, - progress: Iterable[ProgressDictType] | None = None, + progress: Iterable[ProgressDict] | None = None, ) -> None: """ Instantiate a ProgressStore object that tracks the progress status of Sections & Repeating Sections, @@ -52,7 +41,7 @@ def __contains__(self, section_key: SectionKey) -> bool: @staticmethod def _build_map( - section_list: Iterable[ProgressDictType], + section_list: Iterable[ProgressDict], ) -> MutableMapping: """ Builds the ProgressStore's data structure from a list of section dictionaries. @@ -100,14 +89,14 @@ def is_section_complete(self, section_key: SectionKey) -> bool: def section_keys( self, - statuses: Iterable[str] | None = None, + statuses: Iterable[CompletionStatus] | None = None, section_ids: Iterable[str] | None = None, ) -> list[SectionKey]: """ Return the Keys of the Section and Repeating Blocks progresses stored in this ProgressStore. """ if not statuses: - statuses = {*CompletionStatus()} + statuses = CompletionStatus section_keys = [ section_key @@ -124,7 +113,9 @@ def section_keys( if any(section_id in progress_key for section_id in section_ids) ] - def update_section_status(self, status: str, section_key: SectionKey) -> bool: + def update_section_status( + self, status: CompletionStatus, section_key: SectionKey + ) -> bool: """ Updates the status of the Section or Repeating Blocks for a list item specified by the key based on the given section id and list item id. """ @@ -143,7 +134,7 @@ def update_section_status(self, status: str, section_key: SectionKey) -> bool: return updated - def get_section_status(self, section_key: SectionKey) -> str: + def get_section_status(self, section_key: SectionKey) -> CompletionStatus: """ Return the CompletionStatus of the Section or Repeating Blocks for a list item, specified by the given section_id and list_item_id in SectionKey. @@ -154,7 +145,9 @@ def get_section_status(self, section_key: SectionKey) -> str: return CompletionStatus.NOT_STARTED - def get_block_status(self, *, block_id: str, section_key: SectionKey) -> str: + def get_block_status( + self, *, block_id: str, section_key: SectionKey + ) -> CompletionStatus: """ Return the completion status of the block specified by the given block_id, if it is part of the progress of the given Section or Repeating Blocks for list item diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 6b364106ca..5af6280c3f 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -2,10 +2,11 @@ from werkzeug.datastructures import ImmutableDict +from app.data_models import CompletionStatus from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.progress_store import ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_schema import QuestionnaireSchema diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index e116132b41..6f5143e1ff 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -5,9 +5,8 @@ from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerValueTypes, QuestionnaireStore +from app.data_models import AnswerValueTypes, CompletionStatus, QuestionnaireStore from app.data_models.answer_store import Answer -from app.data_models.progress_store import CompletionStatus from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 68a76f9954..dd72784b30 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -5,7 +5,7 @@ from flask_babel import lazy_gettext from werkzeug.datastructures import ImmutableDict -from app.data_models.progress_store import CompletionStatus +from app.data_models import CompletionStatus from app.questionnaire.location import SectionKey from app.views.contexts import Context @@ -81,7 +81,7 @@ def __call__( def get_row_context_for_section( self, section_name: Optional[str], - section_status: str, + section_status: CompletionStatus, section_url: str, row_id: str, ) -> dict[str, Union[str, list]]: @@ -117,7 +117,7 @@ def get_row_context_for_section( @staticmethod def get_section_url( - section_id: str, list_item_id: Optional[str], section_status: str + section_id: str, list_item_id: Optional[str], section_status: CompletionStatus ) -> str: if section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: return url_for( diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index cec584acea..c7fc62c0fb 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -322,7 +322,7 @@ def _render_block(self) -> dict[str, Any]: data_to_render=self.block_definition, list_item_id=self._list_item_id ) - def _update_section_status(self, status: str) -> None: + def _update_section_status(self, status: CompletionStatus) -> None: self._questionnaire_store.progress_store.update_section_status( status, SectionKey(self.individual_section_id, self._list_item_id), diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index 5e12d9ebcc..9fa4459e33 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -4,9 +4,9 @@ import pytest from mock.mock import Mock -from app.data_models import ListStore, QuestionnaireStore +from app.data_models import CompletionStatus, ListStore, QuestionnaireStore from app.data_models.answer_store import Answer -from app.data_models.progress_store import CompletionStatus +from app.data_models.progress import ProgressDict from app.data_models.session_store import SessionStore from app.storage import storage_encryption from tests.app.parser.conftest import get_response_expires_at @@ -92,12 +92,12 @@ def basic_input(): "ANSWERS": [{"answer_id": "test", "value": "test"}], "LISTS": [], "PROGRESS": [ - { - "section_id": "a-test-section", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["a-test-block"], - } + ProgressDict( + section_id="a-test-section", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["a-test-block"], + ) ], "SUPPLEMENTARY_DATA": {"data": {}, "list_mappings": {}}, "RESPONSE_METADATA": {"test-meta": "test"}, diff --git a/tests/app/data_model/test_progress_store.py b/tests/app/data_model/test_progress_store.py index 23c5670c5d..1f2712e286 100644 --- a/tests/app/data_model/test_progress_store.py +++ b/tests/app/data_model/test_progress_store.py @@ -1,7 +1,8 @@ import pytest -from app.data_models.progress import Progress -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models import CompletionStatus +from app.data_models.progress import Progress, ProgressDict +from app.data_models.progress_store import ProgressStore from app.questionnaire.location import Location, SectionKey @@ -32,38 +33,38 @@ def test_serialisation(): assert serialized == [ Progress.from_dict( - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - } + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ) ), Progress.from_dict( - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["another-one"], - } + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.IN_PROGRESS, + block_ids=["another-one"], + ) ), ] def test_deserialisation(): in_progress_sections = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three", "four"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three", "four"], + ), ] store = ProgressStore(in_progress_sections) @@ -85,18 +86,18 @@ def test_deserialisation(): def test_clear(): in_progress_sections = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three", "four"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three", "four"], + ), ] store = ProgressStore(in_progress_sections) @@ -132,18 +133,18 @@ def test_add_completed_location(): def test_add_completed_location_existing(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three", "four"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three", "four"], + ), ] store = ProgressStore(completed) @@ -174,18 +175,18 @@ def test_add_completed_location_existing(): def test_add_completed_location_new(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three", "four"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three", "four"], + ), ] store = ProgressStore(completed) @@ -216,24 +217,24 @@ def test_add_completed_location_new(): def test_remove_completed_location(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three", "four"], - }, - { - "section_id": "s3", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three", "four"], + ), + ProgressDict( + section_id="s3", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), ] store = ProgressStore(completed) @@ -271,12 +272,12 @@ def test_remove_completed_location(): def test_remove_non_existent_completed_location(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - } + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ) ] store = ProgressStore(completed) @@ -294,18 +295,18 @@ def test_remove_non_existent_completed_location(): def test_update_section_status(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.COMPLETED, - "block_ids": ["three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.COMPLETED, + block_ids=["three"], + ), ] store = ProgressStore(completed) @@ -331,12 +332,12 @@ def test_update_section_status(): def test_update_non_existing_section_status(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - } + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ) ] store = ProgressStore(completed) @@ -358,18 +359,18 @@ def test_update_non_existing_section_status(): def test_get_section_status(): existing_progress = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), ] store = ProgressStore(existing_progress) @@ -385,18 +386,18 @@ def test_get_section_status(): def test_get_section_locations(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one"], - }, - { - "section_id": "s2", - "list_item_id": "abc123", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one"], + ), + ProgressDict( + section_id="s2", + list_item_id="abc123", + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), ] store = ProgressStore(completed) @@ -409,36 +410,36 @@ def test_get_section_locations(): def test_is_section_complete(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s3", - "list_item_id": "abc123", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s4", - "list_item_id": "123abc", - "status": CompletionStatus.COMPLETED, - "block_ids": ["not-three"], - }, - { - "section_id": "s5", - "list_item_id": "456def", - "status": CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, - "block_ids": ["not-three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s3", + list_item_id="abc123", + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s4", + list_item_id="123abc", + status=CompletionStatus.COMPLETED, + block_ids=["not-three"], + ), + ProgressDict( + section_id="s5", + list_item_id="456def", + status=CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, + block_ids=["not-three"], + ), ] store = ProgressStore(completed) @@ -450,30 +451,30 @@ def test_is_section_complete(): def test_remove_progress_for_list_item_id(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s3", - "list_item_id": "abc123", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s4", - "list_item_id": "123abc", - "status": CompletionStatus.COMPLETED, - "block_ids": ["not-three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s3", + list_item_id="abc123", + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s4", + list_item_id="123abc", + status=CompletionStatus.COMPLETED, + block_ids=["not-three"], + ), ] store = ProgressStore(completed) @@ -506,36 +507,36 @@ def test_remove_progress_for_list_item_id(): ) def test_in_progress_and_completed_section_ids(section_ids, expected_section_keys): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s3", - "list_item_id": "abc123", - "status": CompletionStatus.NOT_STARTED, - "block_ids": ["three"], - }, - { - "section_id": "s4", - "list_item_id": "123abc", - "status": CompletionStatus.COMPLETED, - "block_ids": ["not-three"], - }, - { - "section_id": "s5", - "list_item_id": "456def", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["not-three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s3", + list_item_id="abc123", + status=CompletionStatus.NOT_STARTED, + block_ids=["three"], + ), + ProgressDict( + section_id="s4", + list_item_id="123abc", + status=CompletionStatus.COMPLETED, + block_ids=["not-three"], + ), + ProgressDict( + section_id="s5", + list_item_id="456def", + status=CompletionStatus.IN_PROGRESS, + block_ids=["not-three"], + ), ] store = ProgressStore(completed) @@ -548,36 +549,36 @@ def test_in_progress_and_completed_section_ids(section_ids, expected_section_key def test_section_keys(): completed = [ - { - "section_id": "s1", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["one", "two"], - }, - { - "section_id": "s2", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["three"], - }, - { - "section_id": "s3", - "list_item_id": "abc123", - "status": CompletionStatus.NOT_STARTED, - "block_ids": ["three"], - }, - { - "section_id": "s4", - "list_item_id": "123abc", - "status": CompletionStatus.COMPLETED, - "block_ids": ["not-three"], - }, - { - "section_id": "s5", - "list_item_id": "456def", - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["not-three"], - }, + ProgressDict( + section_id="s1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["one", "two"], + ), + ProgressDict( + section_id="s2", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["three"], + ), + ProgressDict( + section_id="s3", + list_item_id="abc123", + status=CompletionStatus.NOT_STARTED, + block_ids=["three"], + ), + ProgressDict( + section_id="s4", + list_item_id="123abc", + status=CompletionStatus.COMPLETED, + block_ids=["not-three"], + ), + ProgressDict( + section_id="s5", + list_item_id="456def", + status=CompletionStatus.IN_PROGRESS, + block_ids=["not-three"], + ), ] store = ProgressStore(completed) diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 7f6baa888f..8ad0e34862 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -2,11 +2,12 @@ import pytest -from app.data_models import QuestionnaireStore, SupplementaryDataStore +from app.data_models import CompletionStatus, QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.progress import ProgressDict +from app.data_models.progress_store import ProgressStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.placeholder_parser import PlaceholderParser @@ -1342,16 +1343,16 @@ def grand_calculated_summary_schema(): def grand_calculated_summary_progress_store(): return ProgressStore( [ - { - "section_id": "section-1", - "block_ids": [ + ProgressDict( + section_id="section-1", + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", "number-calculated-summary-1", ], - "status": CompletionStatus.COMPLETED, - } + status=CompletionStatus.COMPLETED, + ) ] ) @@ -1365,9 +1366,9 @@ def grand_calculated_summary_repeating_answers_schema(): def grand_calculated_summary_repeating_answers_progress_store(): return ProgressStore( [ - { - "section_id": "section-5", - "block_ids": [ + ProgressDict( + section_id="section-5", + block_ids=[ "any-streaming-services", "any-other-streaming-services", "calculated-summary-6", @@ -1375,39 +1376,39 @@ def grand_calculated_summary_repeating_answers_progress_store(): "other-internet-usage", "calculated-summary-8", ], - "status": "COMPLETED", - }, - { - "section_id": "section-5", - "block_ids": [ + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-5", + block_ids=[ "streaming-service-repeating-block-1", "streaming-service-repeating-block-2", ], - "status": "COMPLETED", - "list_item_id": "item-1", - }, - { - "section_id": "section-5", - "block_ids": [ + status=CompletionStatus.COMPLETED, + list_item_id="item-1", + ), + ProgressDict( + section_id="section-5", + block_ids=[ "streaming-service-repeating-block-1", "streaming-service-repeating-block-2", ], - "status": "COMPLETED", - "list_item_id": "item-2", - }, - { - "section_id": "section-4", - "block_ids": [ + status=CompletionStatus.COMPLETED, + list_item_id="item-2", + ), + ProgressDict( + section_id="section-4", + block_ids=[ "any-utility-bills", "any-other-utility-bills", "dynamic-answer", "calculated-summary-5", ], - "status": "COMPLETED", - }, - { - "section_id": "section-1", - "block_ids": [ + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-1", + block_ids=[ "block-1", "block-2", "calculated-summary-1", @@ -1416,13 +1417,13 @@ def grand_calculated_summary_repeating_answers_progress_store(): "calculated-summary-3", "grand-calculated-summary-1", ], - "status": "COMPLETED", - }, - { - "section_id": "section-2", - "block_ids": ["block-4", "calculated-summary-4"], - "status": "COMPLETED", - }, + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-2", + block_ids=["block-4", "calculated-summary-4"], + status=CompletionStatus.COMPLETED, + ), ] ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 6737f3aa74..e52157d628 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -13,6 +13,7 @@ ) from app.data_models.answer import Answer from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.relationship_location import RelationshipLocation from app.questionnaire.rules.operator import Operator @@ -275,12 +276,12 @@ def test_metadata_source(metadata_value, expected_result): def test_progress_source(identifier, selector, expected_result): schema = load_schema_from_name("test_progress_value_source_blocks") in_progress_sections = [ - { - "section_id": "section-1", - "list_item_id": None, - "status": "COMPLETED", - "block_ids": ["s1-b1", "s1-b2"], - }, + ProgressDict( + section_id="section-1", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["s1-b1", "s1-b2"], + ), ] rule_evaluator = get_rule_evaluator( diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index c2ef8fc46d..a7105ee659 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -1,8 +1,9 @@ import pytest -from app.data_models import ListStore +from app.data_models import CompletionStatus, ListStore from app.data_models.answer_store import Answer, AnswerStore -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.progress import ProgressDict +from app.data_models.progress_store import ProgressStore from app.questionnaire.path_finder import PathFinder from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name @@ -14,12 +15,12 @@ def test_simple_path(answer_store, list_store, supplementary_data_store): schema = load_schema_from_name("test_textfield") progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["name-block"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["name-block"], + ) ] ) path_finder = PathFinder( @@ -129,12 +130,12 @@ def test_routing_path_with_complete_introduction( section_id = schema.get_section_id_for_block_id("introduction") progress_store = ProgressStore( [ - { - "section_id": "introduction-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["introduction"], - } + ProgressDict( + section_id="introduction-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["introduction"], + ) ] ) expected_routing_path = RoutingPath( @@ -265,12 +266,12 @@ def test_routing_path_empty_routing_rules( progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["mandatory-checkbox"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["mandatory-checkbox"], + ) ] ) @@ -299,12 +300,12 @@ def test_routing_path_with_conditional_value_not_in_metadata( progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["block1"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["block1"], + ) ] ) @@ -335,12 +336,12 @@ def test_routing_path_should_skip_block( progress_store = ProgressStore( [ - { - "section_id": "introduction-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["do-you-want-to-skip"], - } + ProgressDict( + section_id="introduction-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["do-you-want-to-skip"], + ) ] ) @@ -378,12 +379,12 @@ def test_routing_path_should_skip_group( ) progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["do-you-want-to-skip"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["do-you-want-to-skip"], + ) ] ) @@ -420,12 +421,12 @@ def test_routing_path_should_not_skip_group( ) progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["do-you-want-to-skip"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["do-you-want-to-skip"], + ) ] ) @@ -664,12 +665,12 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( ) progress = [ - { - "section_id": "skip-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["skip-age"], - } + ProgressDict( + section_id="skip-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["skip-age"], + ) ] if skip_confirmation_answer: @@ -679,12 +680,12 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( answer_store.add_or_update(Answer(answer_id="security-answer", value="Yes")) progress.append( - { - "section_id": "skip-confirmation-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["security", "skip-confirmation"], - } + ProgressDict( + section_id="skip-confirmation-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["security", "skip-confirmation"], + ) ) progress_store = ProgressStore(progress) @@ -752,18 +753,18 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating progress_store = ProgressStore( [ - { - "section_id": "skip-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["skip-age"], - }, - { - "section_id": "household-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["list-collector"], - }, + ProgressDict( + section_id="skip-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["skip-age"], + ), + ProgressDict( + section_id="household-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["list-collector"], + ), ] ) diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index cf0318b45e..91419c2279 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -3,6 +3,7 @@ from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore +from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire import Location from app.questionnaire.placeholder_parser import PlaceholderParser from app.utilities.schema import load_schema_from_name @@ -1122,9 +1123,9 @@ def test_placeholder_parser_calculated_summary_dependencies_cache( progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": [ + ProgressDict( + section_id="default-section", + block_ids=[ "second-number-answer-unit-total", "third-and-a-half-number-answer-unit-total", "unit-total-playback", @@ -1132,8 +1133,8 @@ def test_placeholder_parser_calculated_summary_dependencies_cache( "sixth-percent-answer", "percentage-total-playback", ], - "status": "COMPLETED", - }, + status="COMPLETED", + ), ] ) @@ -1246,11 +1247,11 @@ def test_placeholder_dependencies_cache(mocker, mock_renderer): progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": ["date-question-block", "date-entry-block"], - "status": "COMPLETED", - } + ProgressDict( + section_id="default-section", + block_ids=["date-question-block", "date-entry-block"], + status=CompletionStatus.COMPLETED, + ) ] ) answer_store = AnswerStore( diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index d221f529a5..fa1808cf45 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -6,10 +6,11 @@ from ordered_set import OrderedSet from werkzeug.datastructures import MultiDict -from app.data_models import QuestionnaireStore, SupplementaryDataStore +from app.data_models import CompletionStatus, QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import AnswerDict, AnswerStore from app.data_models.list_store import ListStore -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.progress import ProgressDict +from app.data_models.progress_store import ProgressStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater @@ -839,29 +840,29 @@ def test_update_repeating_answers_with_answer_dependents( [ ( # When an answer is changed which causes the path of a dependent section to be incomplete, Then that sections is update to IN_PROGRESS - "COMPLETED", + CompletionStatus.COMPLETED, "answer updated", False, - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, ), ( # When an answer is changed which causes the path of a dependent section to be complete, Then that sections is update to COMPLETED - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, "answer updated", True, - "COMPLETED", + CompletionStatus.COMPLETED, ), ( # When an answer is not changed, Then a dependent section status should not change - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, "original answer", False, - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, ), ( # When an answer is not changed, Then a dependent section status should not change - "COMPLETED", + CompletionStatus.COMPLETED, "original answer", True, - "COMPLETED", + CompletionStatus.COMPLETED, ), ], ) @@ -898,11 +899,11 @@ def test_answer_id_section_dependents( ) progress_store = ProgressStore( [ - { - "section_id": "section-2", - "block_ids": ["second-block"], - "status": section_status, - } + ProgressDict( + section_id="section-2", + block_ids=["second-block"], + status=section_status, + ) ], ) current_question = mock_schema.get_block(location.block_id)["question"] @@ -926,42 +927,42 @@ def test_answer_id_section_dependents( [ ( # When an answer is changed which causes repeating dependent section to be incomplete, Then those repeating sections are updated to IN_PROGRESS - "COMPLETED", - "COMPLETED", + CompletionStatus.COMPLETED, + CompletionStatus.COMPLETED, "answer updated", False, False, - "IN_PROGRESS", - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, + CompletionStatus.IN_PROGRESS, ), ( # When an answer is changed which causes repeating dependent section to be complete, Then those repeating sections are updated to COMPLETED - "IN_PROGRESS", - "IN_PROGRESS", + CompletionStatus.IN_PROGRESS, + CompletionStatus.IN_PROGRESS, "answer updated", True, True, - "COMPLETED", - "COMPLETED", + CompletionStatus.COMPLETED, + CompletionStatus.COMPLETED, ), ( # When an answer is changed which causes repeating section paths to change, Then those repeating sections statuses are updated correctly - "COMPLETED", - "IN_PROGRESS", + CompletionStatus.COMPLETED, + CompletionStatus.IN_PROGRESS, "answer updated", False, True, - "IN_PROGRESS", - "COMPLETED", + CompletionStatus.IN_PROGRESS, + CompletionStatus.COMPLETED, ), ( # When an answer is not changed, Then a repeating dependent section status should not change - "COMPLETED", - "IN_PROGRESS", + CompletionStatus.COMPLETED, + CompletionStatus.IN_PROGRESS, "original answer", True, False, - "COMPLETED", - "IN_PROGRESS", + CompletionStatus.COMPLETED, + CompletionStatus.IN_PROGRESS, ), ], ) @@ -1013,18 +1014,18 @@ def test_answer_id_section_dependents_repeating( progress_store = ProgressStore( [ - { - "section_id": "section-2", - "block_ids": ["second-block"], - "status": list_item_1_section_status, - "list_item_id": "list-item-id-1", - }, - { - "section_id": "section-2", - "block_ids": ["second-block"], - "status": list_item_2_section_status, - "list_item_id": "list-item-id-2", - }, + ProgressDict( + section_id="section-2", + block_ids=["second-block"], + status=list_item_1_section_status, + list_item_id="list-item-id-1", + ), + ProgressDict( + section_id="section-2", + block_ids=["second-block"], + status=list_item_2_section_status, + list_item_id="list-item-id-2", + ), ], ) current_question = mock_schema.get_block(location.block_id)["question"] @@ -1118,18 +1119,18 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update ) progress_store = ProgressStore( [ - { - "section_id": "company-summary-section", - "block_ids": ["total-turnover-block", "total-employees-block"], - "status": "COMPLETED", - }, - { - "section_id": "breakdown-section", - "block_ids": [ + ProgressDict( + section_id="company-summary-section", + block_ids=["total-turnover-block", "total-employees-block"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="breakdown-section", + block_ids=[ "turnover-breakdown-block", ], - "status": dependent_section_status, - }, + status=dependent_section_status, + ), ], ) @@ -1180,14 +1181,14 @@ def test_dependent_sections_current_section_status_not_updated(mocker): ) progress_store = ProgressStore( [ - { - "section_id": "breakdown-section", - "block_ids": [ + ProgressDict( + section_id="breakdown-section", + block_ids=[ "total-turnover-block", "turnover-breakdown-block", ], - "status": CompletionStatus.COMPLETED, - }, + status=CompletionStatus.COMPLETED, + ), ], ) questionnaire_store_updater = get_questionnaire_store_updater( @@ -1227,11 +1228,11 @@ def test_dependent_sections_not_started_skipped(mock_router, mocker): ) progress_store = ProgressStore( [ - { - "section_id": "company-summary-section", - "block_ids": ["total-turnover-block", "total-employees-block"], - "status": "COMPLETED", - } + ProgressDict( + section_id="company-summary-section", + block_ids=["total-turnover-block", "total-employees-block"], + status=CompletionStatus.COMPLETED, + ) ], ) questionnaire_store_updater = get_questionnaire_store_updater( @@ -1267,18 +1268,18 @@ def test_dependent_sections_started_but_blocks_incomplete(mock_router, mocker): ) progress_store = ProgressStore( [ - { - "section_id": "company-summary-section", - "block_ids": ["total-turnover-block", "total-employees-block"], - "status": "COMPLETED", - }, - { - "section_id": "breakdown-section", - "block_ids": [ + ProgressDict( + section_id="company-summary-section", + block_ids=["total-turnover-block", "total-employees-block"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="breakdown-section", + block_ids=[ "turnover-breakdown-block", ], - "status": "IN_PROGRESS", - }, + status=CompletionStatus.IN_PROGRESS, + ), ], ) questionnaire_store_updater = get_questionnaire_store_updater( @@ -1330,11 +1331,11 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta ) progress_store = ProgressStore( [ - { - "section_id": "company-summary-section", - "block_ids": ["total-turnover-block", "total-employees-block"], - "status": "COMPLETED", - }, + ProgressDict( + section_id="company-summary-section", + block_ids=["total-turnover-block", "total-employees-block"], + status=CompletionStatus.COMPLETED, + ), { "section_id": "breakdown-section", "list_item_id": "item-1", @@ -1416,18 +1417,18 @@ def test_dependent_sections_added_dependant_block_removed( ) progress_store = ProgressStore( [ - { - "section_id": "company-summary-section", - "block_ids": ["total-turnover-block", "total-employees-block"], - "status": "COMPLETED", - }, - { - "section_id": "breakdown-section", - "block_ids": [ + ProgressDict( + section_id="company-summary-section", + block_ids=["total-turnover-block", "total-employees-block"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="breakdown-section", + block_ids=[ "turnover-breakdown-block", ], - "status": dependent_section_status, - }, + status=dependent_section_status, + ), ], ) questionnaire_store_updater = get_questionnaire_store_updater( diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 562ce279eb..c97d13b171 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -5,9 +5,11 @@ from flask import url_for from mock import Mock +from app.data_models import CompletionStatus from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models.progress import ProgressDict +from app.data_models.progress_store import ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location, SectionKey from app.questionnaire.router import Router @@ -43,11 +45,11 @@ def test_enabled_section_ids(self): self.schema = load_schema_from_name("test_section_enabled_checkbox") self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "block_ids": ["section-1-block"], - "status": CompletionStatus.COMPLETED, - } + ProgressDict( + section_id="section-1", + block_ids=["section-1-block"], + status=CompletionStatus.COMPLETED, + ) ] ) self.answer_store = AnswerStore( @@ -124,12 +126,12 @@ def test_is_complete(self): self.schema = load_schema_from_name("test_textfield") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["name-block"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["name-block"], + ) ] ) @@ -152,12 +154,12 @@ def test_is_complete(self): self.schema = load_schema_from_name("test_textfield") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["name-block"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["name-block"], + ) ] ) @@ -340,12 +342,12 @@ def test_within_section(self): self.schema = load_schema_from_name("test_checkbox") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["mandatory-checkbox"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["mandatory-checkbox"], + ) ] ) @@ -378,12 +380,12 @@ def test_last_block_in_section_but_section_is_not_complete_when_routing_backward self.schema.get_block.return_value = {"type": "Question"} self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["block-1"], - } + ProgressDict( + section_id="section-1", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["block-1"], + ) ] ) current_location = Location(section_id="section-1", block_id="block-1") @@ -440,12 +442,12 @@ def test_return_to_section_summary_section_is_in_progress(self): ) self.progress_store = ProgressStore( [ - { - "section_id": "property-details-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["insurance-type", "insurance-address", "listed"], - } + ProgressDict( + section_id="property-details-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["insurance-type", "insurance-address", "listed"], + ) ] ) current_location = Location( @@ -474,12 +476,12 @@ def test_section_summary_on_completion_true(self): self.schema = load_schema_from_name("test_show_section_summary_on_completion") self.progress_store = ProgressStore( [ - { - "section_id": "accommodation-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["proxy"], - } + ProgressDict( + section_id="accommodation-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["proxy"], + ) ] ) current_location = Location( @@ -497,12 +499,12 @@ def test_section_summary_on_completion_false(self): self.schema = load_schema_from_name("test_show_section_summary_on_completion") self.progress_store = ProgressStore( [ - { - "section_id": "employment-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["employment-status"], - } + ProgressDict( + section_id="employment-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["employment-status"], + ) ] ) current_location = Location( @@ -534,14 +536,14 @@ def test_return_to_calculated_summary(self, schema): # and that those two blocks are complete - this will be a sufficient condition to return to the calculated summary self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": [ + ProgressDict( + section_id="default-section", + block_ids=[ "first-number-block", "second-number-block", ], - "status": CompletionStatus.IN_PROGRESS, - } + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -595,11 +597,11 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): self.schema = load_schema_from_name(schema) self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": ["block-3"], - "status": CompletionStatus.IN_PROGRESS, - } + ProgressDict( + section_id="default-section", + block_ids=["block-3"], + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -660,11 +662,11 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( self.schema = load_schema_from_name(schema) self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": ["fifth-number-block"], - "status": CompletionStatus.IN_PROGRESS, - } + ProgressDict( + section_id="default-section", + block_ids=["fifth-number-block"], + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -694,11 +696,11 @@ def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schem self.schema = load_schema_from_name(schema) self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": ["fifth-number-block"], - "status": CompletionStatus.IN_PROGRESS, - } + ProgressDict( + section_id="default-section", + block_ids=["fifth-number-block"], + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -814,17 +816,17 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( # calculated summary 3 is not complete yet self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "block_ids": [ + ProgressDict( + section_id="section-1", + block_ids=[ "block-1", "block-2", "calculated-summary-1", "block-3", "calculated-summary-2", ], - "status": "IN_PROGRESS", - } + status="IN_PROGRESS", + ) ] ) @@ -872,14 +874,14 @@ def test_return_to_calculated_summary_from_incomplete_section( # second-number block not complete yet self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "block_ids": [ + ProgressDict( + section_id="section-1", + block_ids=[ "first-number-block", "distance-calculated-summary-1", ], - "status": CompletionStatus.IN_PROGRESS, - } + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -1005,11 +1007,11 @@ def test_return_to_inaccessible_summary_routes_to_next_incomplete_block( self.progress_store = ProgressStore( [ - { - "section_id": section, - "block_ids": completed_block_ids, - "status": "IN_PROGRESS", - } + ProgressDict( + section_id=section, + block_ids=completed_block_ids, + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -1038,12 +1040,12 @@ def test_redirects_to_submit_page_when_questionnaire_complete( self.schema = load_schema_from_name("test_textfield") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["name-block"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["name-block"], + ) ] ) @@ -1064,12 +1066,12 @@ def test_return_to_final_summary_questionnaire_and_section_is_complete(self): ) self.progress_store = ProgressStore( [ - { - "section_id": "test-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["test-forced"], - } + ProgressDict( + section_id="test-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["test-forced"], + ) ] ) current_location = Location(section_id="test-section", block_id="test-forced") @@ -1085,12 +1087,12 @@ def test_return_to_final_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_submit_with_summary") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["radio", "dessert", "dessert-confirmation"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["radio", "dessert", "dessert-confirmation"], + ) ] ) current_location = Location( @@ -1114,12 +1116,12 @@ def test_return_to_final_summary_questionnaire_is_not_complete(self): self.answer_store = AnswerStore([{"answer_id": "test-answer", "value": "Yes"}]) self.progress_store = ProgressStore( [ - { - "section_id": "test-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["test-forced"], - } + ProgressDict( + section_id="test-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["test-forced"], + ) ] ) @@ -1208,15 +1210,15 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( # trying to go to number-calculated-summary-1 but distance-calculated-summary-1 which comes before is not complete yet self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "block_ids": [ + ProgressDict( + section_id="section-1", + block_ids=[ "first-number-block", "second-number-block", "number-calculated-summary-1", ], - "status": CompletionStatus.IN_PROGRESS, - } + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -1264,15 +1266,15 @@ def test_return_to_grand_calculated_summary_from_calculated_summary_incomplete_s # number calculated summary is not complete, so the section is not complete self.progress_store = ProgressStore( [ - { - "section_id": "section-1", - "block_ids": [ + ProgressDict( + section_id="section-1", + block_ids=[ "first-number-block", "second-number-block", "distance-calculated-summary-1", ], - "status": CompletionStatus.IN_PROGRESS, - } + status=CompletionStatus.IN_PROGRESS, + ) ] ) @@ -1406,12 +1408,12 @@ def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") self.progress_store = ProgressStore( [ - { - "section_id": "property-details-section", - "list_item_id": None, - "status": CompletionStatus.COMPLETED, - "block_ids": ["insurance-type", "insurance-address", "listed"], - } + ProgressDict( + section_id="property-details-section", + list_item_id=None, + status=CompletionStatus.COMPLETED, + block_ids=["insurance-type", "insurance-address", "listed"], + ) ] ) @@ -1439,12 +1441,12 @@ def test_return_to_section_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_section_summary") self.progress_store = ProgressStore( [ - { - "section_id": "property-details-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["insurance-type", "insurance-address", "listed"], - } + ProgressDict( + section_id="property-details-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["insurance-type", "insurance-address", "listed"], + ) ] ) @@ -1543,12 +1545,12 @@ def test_is_none_on_first_block_single_section(self): self.schema = load_schema_from_name("test_checkbox") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["mandatory-checkbox"], - } + ProgressDict( + section_id="default-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["mandatory-checkbox"], + ) ] ) routing_path = RoutingPath( @@ -1626,15 +1628,15 @@ def test_block_on_path(self): self.schema = load_schema_from_name("test_checkbox") self.progress_store = ProgressStore( [ - { - "section_id": "default-section", - "block_ids": [ + ProgressDict( + section_id="default-section", + block_ids=[ "mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox", ], - "status": CompletionStatus.COMPLETED, - } + status=CompletionStatus.COMPLETED, + ) ] ) last_location_url = self.router.get_last_location_in_questionnaire_url() @@ -1663,11 +1665,11 @@ def test_last_block_not_on_path(self): completed_block_not_on_path = "test-optional" self.progress_store = ProgressStore( [ - { - "section_id": section_id, - "block_ids": [last_block_on_path, completed_block_not_on_path], - "status": CompletionStatus.COMPLETED, - } + ProgressDict( + section_id=section_id, + block_ids=[last_block_on_path, completed_block_not_on_path], + status=CompletionStatus.COMPLETED, + ) ] ) @@ -1694,12 +1696,12 @@ def test_section_in_progress_returns_url_for_first_incomplete_location(self): self.progress_store = ProgressStore( [ - { - "section_id": "property-details-section", - "list_item_id": None, - "status": CompletionStatus.IN_PROGRESS, - "block_ids": ["insurance-type"], - } + ProgressDict( + section_id="property-details-section", + list_item_id=None, + status=CompletionStatus.IN_PROGRESS, + block_ids=["insurance-type"], + ) ] ) @@ -1721,11 +1723,11 @@ def test_section_complete_returns_url_for_first_location( self.schema = load_schema_from_name("test_hub_complete_sections") self.progress_store = ProgressStore( [ - { - "section_id": "employment-section", - "block_ids": ["employment-status", "employment-type"], - "status": CompletionStatus.COMPLETED, - } + ProgressDict( + section_id="employment-section", + block_ids=["employment-status", "employment-type"], + status=CompletionStatus.COMPLETED, + ) ], ) diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index 428facad4e..f6fd32e49c 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -1,6 +1,7 @@ import pytest -from app.data_models.progress_store import CompletionStatus, ProgressStore +from app.data_models import CompletionStatus +from app.data_models.progress_store import ProgressStore from app.questionnaire import Location from app.questionnaire.routing_path import RoutingPath from app.views.contexts.grand_calculated_summary_context import ( diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index b541fbfde7..5b1172ddb6 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -1,7 +1,7 @@ # pylint: disable=redefined-outer-name import pytest -from app.data_models.progress_store import CompletionStatus +from app.data_models import CompletionStatus from app.questionnaire.router import Router from app.utilities.schema import load_schema_from_name from app.views.contexts import HubContext diff --git a/tests/app/views/contexts/test_list_context.py b/tests/app/views/contexts/test_list_context.py index fd7a96a56c..2040d82408 100644 --- a/tests/app/views/contexts/test_list_context.py +++ b/tests/app/views/contexts/test_list_context.py @@ -1,7 +1,7 @@ import pytest from app.data_models import CompletionStatus, ProgressStore -from app.data_models.progress import ProgressDictType +from app.data_models.progress import ProgressDict from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name from app.views.contexts import ListContext @@ -223,13 +223,13 @@ def test_list_context_items_complete_without_repeating_blocks( progress_store = ProgressStore( [ - ProgressDictType( + ProgressDict( section_id="section-id", list_item_id="PlwgoG", status=CompletionStatus.COMPLETED, block_ids=[], ), - ProgressDictType( + ProgressDict( section_id="section-id", list_item_id="UHPLbX", status=CompletionStatus.COMPLETED, @@ -347,13 +347,13 @@ def test_list_context_items_complete_with_repeating_blocks( progress_store = ProgressStore( [ - ProgressDictType( + ProgressDict( section_id="section-companies", list_item_id="PlwgoG", status=CompletionStatus.COMPLETED, block_ids=[], ), - ProgressDictType( + ProgressDict( section_id="section-companies", list_item_id="UHPLbX", status=CompletionStatus.COMPLETED, diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index e55f48576d..e9d288c125 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -7,6 +7,7 @@ ProgressStore, SupplementaryDataStore, ) +from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name from app.views.contexts.summary_context import SummaryContext @@ -59,45 +60,45 @@ def test_context_for_summary(): progress_store = ProgressStore( [ - { - "section_id": "name-section", - "block_ids": ["name", "address"], - "status": "COMPLETED", - }, - { - "section_id": "section", - "block_ids": ["primary-person-list-collector", "list-collector"], - "status": "COMPLETED", - }, - { - "section_id": "questions-section", - "block_ids": [ + ProgressDict( + section_id="name-section", + block_ids=["name", "address"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section", + block_ids=["primary-person-list-collector", "list-collector"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="questions-section", + block_ids=[ "skip-first-block", "second-number-block", "currency-total-playback-1", ], - "status": "COMPLETED", - }, - { - "section_id": "calculated-summary-section", - "block_ids": [ + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="calculated-summary-section", + block_ids=[ "third-number-block", "currency-total-playback-2", "mutually-exclusive-checkbox", ], - "status": "COMPLETED", - "list_item_id": "jufPpX", - }, - { - "section_id": "calculated-summary-section", - "block_ids": [ + status=CompletionStatus.COMPLETED, + list_item_id="jufPpX", + ), + ProgressDict( + section_id="calculated-summary-section", + block_ids=[ "third-number-block", "currency-total-playback-2", "mutually-exclusive-checkbox", ], - "status": "COMPLETED", - "list_item_id": "fjWZET", - }, + status=CompletionStatus.COMPLETED, + list_item_id="fjWZET", + ), ] ) From b9859c7d80714863c0c5f0902589100e8b7f8c2a Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Fri, 15 Sep 2023 12:40:42 +0100 Subject: [PATCH 289/567] Use fixed keys for supplementary data list mappings (#1201) * Amend supplementary data list mappings --- app/data_models/questionnaire_store.py | 17 +++++--- app/data_models/supplementary_data_store.py | 40 ++++++++++++++----- app/submitter/converter_v2.py | 2 +- app/utilities/supplementary_data_parser.py | 13 +++++- app/utilities/types.py | 7 ++++ tests/app/conftest.py | 15 ++++--- .../data_model/test_questionnaire_store.py | 25 +++++++----- .../test_supplementary_data_store.py | 11 +++-- .../parser/test_supplementary_data_parser.py | 21 +++++++++- .../submitter/test_convert_payload_0_0_3.py | 11 +++-- 10 files changed, 120 insertions(+), 42 deletions(-) diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 96383bb687..ae9fef59d6 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -10,6 +10,7 @@ from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.rules.utils import parse_iso_8601_datetime from app.utilities.json import json_dumps, json_loads +from app.utilities.types import SupplementaryDataListMapping if TYPE_CHECKING: from app.storage.encrypted_questionnaire_storage import ( # pragma: no cover @@ -87,24 +88,28 @@ def set_supplementary_data(self, to_set: MutableMapping) -> None: def _create_supplementary_list( self, *, list_name: str, list_data: list[dict] - ) -> dict[str, str]: + ) -> list[SupplementaryDataListMapping]: """ Creates or updates a list in ListStore based off supplementary data returns the identifier -> list_item_id mappings used """ - list_mapping: dict[str, str] = {} + list_mappings: list[SupplementaryDataListMapping] = [] for list_item in list_data: identifier = list_item["identifier"] # if any pre-existing supplementary data already has a mapping for this list item # then its already in the list store and doesn't require adding if not ( - list_item_id := self.supplementary_data_store.list_mappings.get( + list_item_id := self.supplementary_data_store.list_lookup.get( list_name, {} ).get(identifier) ): list_item_id = self.list_store.add_list_item(list_name) - list_mapping[identifier] = list_item_id - return list_mapping + list_mappings.append( + SupplementaryDataListMapping( + identifier=identifier, list_item_id=list_item_id + ) + ) + return list_mappings def _remove_old_supplementary_lists_and_answers( self, new_data: MutableMapping @@ -115,7 +120,7 @@ def _remove_old_supplementary_lists_and_answers( :param new_data - the new supplementary data for comparison """ deleted_list_item_ids: set[str] = set() - for list_name, mappings in self.supplementary_data_store.list_mappings.items(): + for list_name, mappings in self.supplementary_data_store.list_lookup.items(): if list_name in new_data.get("items", {}): new_identifiers = [ item["identifier"] for item in new_data["items"][list_name] diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index dd73031841..f8b740ffca 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -1,14 +1,16 @@ from __future__ import annotations from functools import cached_property -from typing import Iterable, Mapping, MutableMapping, TypeAlias +from typing import Iterable, Mapping, MutableMapping from werkzeug.datastructures import ImmutableDict from app.utilities.make_immutable import make_immutable - -SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] -SupplementaryDataValueType: TypeAlias = dict | str | list | None +from app.utilities.types import ( + SupplementaryDataKeyType, + SupplementaryDataListMapping, + SupplementaryDataValueType, +) class InvalidSupplementaryDataSelector(Exception): @@ -23,11 +25,17 @@ class SupplementaryDataStore: def __init__( self, supplementary_data: MutableMapping | None = None, - list_mappings: Mapping[str, Mapping] | None = None, + list_mappings: Mapping[str, list[SupplementaryDataListMapping]] | None = None, ): """ Initialised with the "data" value from the sds api response - and list mappings of the form { list_name: { identifier : list_item_id }} + and list mappings of the form + { + list_name: [ + {"identifier": identifier-1, "list_item_id": list_item_id-1 }, + {"identifier": identifier-2, "list_item_id": list_item_id-2 } + ] + } """ self._raw_data = supplementary_data or {} self._list_mappings = list_mappings or {} @@ -40,10 +48,22 @@ def raw_data(self) -> ImmutableDict: return data @cached_property - def list_mappings(self) -> ImmutableDict: - mappings: ImmutableDict = make_immutable(self._list_mappings) + def list_mappings(self) -> ImmutableDict[str, list[ImmutableDict]]: + mappings: ImmutableDict[str, list[ImmutableDict]] = make_immutable( + self._list_mappings + ) return mappings + @cached_property + def list_lookup(self) -> dict[str, dict[str | int, str]]: + """Create a lookup for easily finding the list_item_id for a given identifier""" + return { + list_name: { + mapping["identifier"]: mapping["list_item_id"] for mapping in list_data + } + for list_name, list_data in self._list_mappings.items() + } + def _build_map( self, data: MutableMapping ) -> dict[SupplementaryDataKeyType, SupplementaryDataValueType]: @@ -58,7 +78,7 @@ def _build_map( ] } } - each list item has an identifier which will link to a list-item-id in self.list_mappings + each list item has an identifier which will link to a list-item-id in self.list_lookup for example: {"some_list": {identifier-1: list_item_id-1, identifier-2: list_item_id-2 }} resulting map based off list mappings has the form @@ -75,7 +95,7 @@ def _build_map( for list_name, list_data in list_items.items(): for item in list_data: identifier = item["identifier"] - list_item_id = self._list_mappings[list_name][identifier] + list_item_id = self.list_lookup[list_name][identifier] resulting_map[(list_name, list_item_id)] = item return resulting_map diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index 70a79f3808..dacb484b83 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -146,7 +146,7 @@ def get_payload_data( for list_ in lists: # for any lists that were populated by supplementary data, provide the identifier -> list_item_id mappings if mapping := supplementary_data_store.list_mappings.get(list_["name"]): - list_["supplementary_data_mapping"] = mapping + list_["supplementary_data_mappings"] = mapping data: dict[str, list | dict] = {"answers": answers, "lists": lists} diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 59fac29a71..f95306ec44 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -6,6 +6,7 @@ ValidationError, fields, validate, + validates, validates_schema, ) @@ -14,7 +15,17 @@ class ItemsSchema(Schema): - identifier = VALIDATORS["string"](validate=validate.Length(min=1)) + identifier = fields.Field(required=True) + + @validates("identifier") + def validate_identifier(self, identifier): + # pylint: disable=no-self-use + if not (isinstance(identifier, str) and identifier.strip()) and not ( + isinstance(identifier, int) and identifier >= 0 + ): + raise ValidationError( + "Item identifier must be a non-empty string or non-negative integer" + ) class ItemsData(Schema, StripWhitespaceMixin): diff --git a/app/utilities/types.py b/app/utilities/types.py index 72ae1be46b..d0691c00b5 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -7,6 +7,8 @@ ) LocationType: TypeAlias = Union["Location", "RelationshipLocation"] +SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] +SupplementaryDataValueType: TypeAlias = dict | str | list | None class SectionKeyDict(TypedDict): @@ -37,3 +39,8 @@ class DependentSection(NamedTuple): @property def section_key(self) -> SectionKey: return SectionKey(self.section_id, self.list_item_id) + + +class SupplementaryDataListMapping(TypedDict): + identifier: str | int + list_item_id: str diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 4db6de3204..e8123efb4f 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -17,7 +17,10 @@ from app.data_models.progress_store import ProgressStore from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore -from app.data_models.supplementary_data_store import SupplementaryDataStore +from app.data_models.supplementary_data_store import ( + SupplementaryDataListMapping, + SupplementaryDataStore, +) from app.publisher import PubSubPublisher from app.questionnaire.location import Location from app.setup import create_app @@ -252,7 +255,7 @@ def supplementary_data(): "items": { "products": [ { - "identifier": "89929001", + "identifier": 89929001, "name": "Articles and equipment for sports or outdoor games", "cn_codes": "2504 + 250610 + 2512 + 2519 + 2524", "guidance": {"title": "Include", "description": "sportswear"}, @@ -278,10 +281,10 @@ def supplementary_data(): @pytest.fixture def supplementary_data_list_mappings(): return { - "products": { - "89929001": "item-1", - "201630601": "item-2", - }, + "products": [ + SupplementaryDataListMapping(identifier=89929001, list_item_id="item-1"), + SupplementaryDataListMapping(identifier="201630601", list_item_id="item-2"), + ], } diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index 9cd8cbc8eb..c1b9b022e1 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -7,6 +7,7 @@ from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import SectionKey from app.utilities.json import json_dumps, json_loads +from app.utilities.make_immutable import make_immutable @pytest.mark.parametrize( @@ -136,9 +137,9 @@ def test_adding_new_supplementary_data( """ self.store = QuestionnaireStore(questionnaire_store.storage) self.store.set_supplementary_data(supplementary_data) - assert "products" in self.store.supplementary_data_store.list_mappings + assert "products" in self.store.supplementary_data_store.list_lookup supplementary_list_item_ids = list( - self.store.supplementary_data_store.list_mappings["products"].values() + self.store.supplementary_data_store.list_lookup["products"].values() ) # check list mapping ids match list store ids self.assert_list_store_data("products", supplementary_list_item_ids) @@ -154,14 +155,18 @@ def test_updating_supplementary_data( supplementary_data["items"]["products"].append({"identifier": "12345"}) self.store.set_supplementary_data(supplementary_data) - assert self.store.supplementary_data_store.list_mappings == { - "products": { - "89929001": "item-1", - "201630601": "item-2", - "12345": "item-3", - }, - "supermarkets": {"54321": "item-4"}, - } + assert self.store.supplementary_data_store.list_mappings == make_immutable( + { + "products": [ + {"identifier": 89929001, "list_item_id": "item-1"}, + {"identifier": "201630601", "list_item_id": "item-2"}, + {"identifier": "12345", "list_item_id": "item-3"}, + ], + "supermarkets": [ + {"identifier": "54321", "list_item_id": "item-4"}, + ], + } + ) self.assert_list_store_data("products", ["item-1", "item-2", "item-3"]) self.assert_list_store_data("supermarkets", ["item-4"]) diff --git a/tests/app/data_model/test_supplementary_data_store.py b/tests/app/data_model/test_supplementary_data_store.py index 1a43acd442..8cb7421e29 100644 --- a/tests/app/data_model/test_supplementary_data_store.py +++ b/tests/app/data_model/test_supplementary_data_store.py @@ -25,12 +25,17 @@ def test_supplementary_data_deserialisation(): "identifier": "12346789012A", "items": { "products": [ - {"identifier": "89929001"}, + {"identifier": 89929001}, {"identifier": "201630601"}, ] }, } - list_mappings = {"products": {"89929001": "item-1", "201630601": "item-2"}} + list_mappings = { + "products": [ + {"identifier": 89929001, "list_item_id": "item-1"}, + {"identifier": "201630601", "list_item_id": "item-2"}, + ] + } serialized = { "data": raw_data, @@ -43,7 +48,7 @@ def test_supplementary_data_deserialisation(): assert deserialized.list_mappings == make_immutable(list_mappings) assert deserialized._data_map == { # pylint: disable=protected-access ("identifier", None): "12346789012A", - ("products", "item-1"): {"identifier": "89929001"}, + ("products", "item-1"): {"identifier": 89929001}, ("products", "item-2"): {"identifier": "201630601"}, } diff --git a/tests/app/parser/test_supplementary_data_parser.py b/tests/app/parser/test_supplementary_data_parser.py index e466e5bcaa..693fb8a89c 100644 --- a/tests/app/parser/test_supplementary_data_parser.py +++ b/tests/app/parser/test_supplementary_data_parser.py @@ -15,7 +15,7 @@ "items": { "local_units": [ { - "identifier": "0001", + "identifier": 1, "lu_name": "TEST NAME. 1", "lu_address": [ "FIRST ADDRESS 1", @@ -206,3 +206,22 @@ def test_validate_supplementary_data_payload_missing_identifier_in_items(): ) assert str(error.value) == "{'identifier': ['Missing data for required field.']}" + + +@pytest.mark.parametrize("invalid_identifier", ["", ["invalid"], -1, {}]) +def test_validate_supplementary_data_payload_invalid_identifier(invalid_identifier): + payload = deepcopy(SUPPLEMENTARY_DATA_PAYLOAD) + payload["data"]["items"]["local_units"][0]["identifier"] = invalid_identifier + + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=payload, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12346789012A", + survey_id="123", + ) + + assert ( + str(error.value) + == "{'identifier': ['Item identifier must be a non-empty string or non-negative integer']}" + ) diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index fc2e8e5254..75e101c384 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -1725,12 +1725,15 @@ def test_payload_supplementary_data(): ] }, } - supermarkets_list_map = {"123": "tUJzGV", "456": "vhECeh"} + supermarkets_list_mappings = [ + {"identifier": "123", "list_item_id": "tUJzGV"}, + {"identifier": "456", "list_item_id": "vhECeh"}, + ] - list_item_ids = list(supermarkets_list_map.values()) + list_item_ids = ["tUJzGV", "vhECeh"] questionnaire_store.supplementary_data_store = SupplementaryDataStore( supplementary_data=supplementary_data, - list_mappings={"supermarkets": supermarkets_list_map}, + list_mappings={"supermarkets": supermarkets_list_mappings}, ) questionnaire_store.list_store = ListStore( [{"items": list_item_ids, "name": "supermarkets"}] @@ -1762,5 +1765,5 @@ def test_payload_supplementary_data(): assert data_payload["lists"][0] == { "items": list_item_ids, "name": "supermarkets", - "supplementary_data_mapping": make_immutable(supermarkets_list_map), + "supplementary_data_mappings": make_immutable(supermarkets_list_mappings), } From 65ecca447d07ff025b83a21c292ea78ec8b820bd Mon Sep 17 00:00:00 2001 From: Michael Donaghey_ONS <111740109+mgdon650@users.noreply.github.com> Date: Mon, 18 Sep 2023 12:43:27 +0100 Subject: [PATCH 290/567] Bug Fix: Decimals are only showing first 3 decimal places (#1148) --------- Signed-off-by: Yuyutsu Rai Co-authored-by: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Co-authored-by: Yuyutsu Rai Co-authored-by: Rhys Berrow Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: katie-gardner Co-authored-by: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> --- app/forms/questionnaire_form.py | 55 ++-- app/forms/validators.py | 63 ++-- app/helpers/form_helpers.py | 17 +- app/jinja_filters.py | 41 ++- app/questionnaire/placeholder_parser.py | 10 +- app/questionnaire/placeholder_transforms.py | 53 +++- app/questionnaire/questionnaire_schema.py | 29 +- app/utilities/decimal_places.py | 124 ++++++++ .../contexts/calculated_summary_context.py | 25 +- .../grand_calculated_summary_context.py | 5 + app/views/contexts/summary/answer.py | 2 + ...and_grand_calculated_summary_decimals.json | 271 ++++++++++++++++++ ...alculated_summary_overlapping_answers.json | 12 +- ..._calculated_summary_repeating_answers.json | 24 +- schemas/test/en/test_multiple_answers.json | 3 +- ..._summary_repeating_and_static_answers.json | 3 +- ...w_calculated_summary_repeating_blocks.json | 9 +- schemas/test/en/test_numbers.json | 2 +- schemas/test/en/test_submit_with_summary.json | 3 +- schemas/test/en/test_unit_patterns.json | 15 +- tests/app/forms/test_questionnaire_form.py | 8 +- .../validation/test_sum_check_validator.py | 4 +- tests/app/questionnaire/conftest.py | 4 +- .../questionnaire/test_placeholder_parser.py | 68 ++++- .../test_placeholder_transforms.py | 92 +++--- tests/app/test_jinja_filters.py | 257 +++++++++++++---- tests/app/utilities/test_decimal_places.py | 44 +++ .../views/contexts/summary/test_question.py | 3 + .../contexts/test_section_summary_context.py | 4 + .../views/contexts/test_summary_context.py | 12 + tests/functional/spec/features/units.spec.js | 19 ++ .../view_submitted_response.spec.js | 2 +- tests/functional/spec/numbers.spec.js | 12 +- .../test_currency_single_checkbox_override.py | 2 +- .../test_questionnaire_calculated_summary.py | 53 ++++ ..._questionnaire_grand_calculated_summary.py | 48 ++++ 36 files changed, 1170 insertions(+), 228 deletions(-) create mode 100644 app/utilities/decimal_places.py create mode 100644 schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json create mode 100644 tests/app/utilities/test_decimal_places.py diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 63f2780376..833e1c5aae 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -14,7 +14,6 @@ from app.data_models import ( AnswerStore, - AnswerValueTypes, ListStore, ProgressStore, SupplementaryDataStore, @@ -160,13 +159,15 @@ def validate_date_range_question(self, question: QuestionSchemaType) -> bool: def validate_calculated_question(self, question: QuestionSchemaType) -> bool: for calculation in question["calculations"]: - result = self._get_target_total_and_currency(calculation, question) - if result: - target_total, currency = result + if result := self._get_target_total_and_currency(calculation, question): if self.answers_all_valid( calculation["answers_to_calculate"] ) and self._validate_calculated_question( - calculation, question, target_total, currency + calculation=calculation, + question=question, + target_total=result["target_total"], + currency=result["currency"], + decimal_places=result["decimal_places"], ): # Remove any previous question errors if it passes this OR before returning True if question["id"] in self.question_errors: @@ -203,27 +204,32 @@ def _get_target_total_and_currency( self, calculation: Calculation, question: QuestionSchemaType, - ) -> Optional[tuple[Union[Calculation, AnswerValueTypes], Optional[str]]]: - calculation_value: Union[Calculation, AnswerValueTypes] - currency: Optional[str] + ) -> dict: + decimal_places = self.schema.get_decimal_limit( + calculation["answers_to_calculate"] + ) + currency = question.get("currency") if "value" in calculation: if isinstance(calculation["value"], dict): - calculation_value = self.value_source_resolver.resolve(calculation["value"]) # type: ignore + target_total = self.value_source_resolver.resolve(calculation["value"]) # type: ignore else: - calculation_value = calculation["value"] - currency = question.get("currency") - return calculation_value, currency + target_total = calculation["value"] + else: + target_answer = self.schema.get_answers_by_answer_id( + calculation["answer_id"] + )[0] + target_total = self.answer_store.get_answer( + calculation["answer_id"] + ).value # type: ignore # expect not None - target_answer = self.schema.get_answers_by_answer_id(calculation["answer_id"])[ - 0 - ] - calculation_value = self.answer_store.get_answer( - calculation["answer_id"] - ).value # type: ignore # expect not None - currency = target_answer.get("currency") + currency = target_answer.get("currency") - return calculation_value, currency + return { + "currency": currency, + "target_total": target_total, + "decimal_places": decimal_places, + } def validate_date_range_with_period_limits_and_single_date_limits( self, @@ -283,6 +289,7 @@ def _validate_calculated_question( question: QuestionSchemaType, target_total: Any, currency: Optional[str], + decimal_places: int | None, ) -> bool: messages = None if "validation" in question: @@ -304,7 +311,13 @@ def _validate_calculated_question( # Validate grouped answers meet calculation_type criteria try: - validator(self, calculation["conditions"], calculation_total, target_total) + validator( + self, + conditions=calculation["conditions"], + total=calculation_total, + target_total=target_total, + decimal_limit=decimal_places, + ) except validators.ValidationError as e: self.question_errors[question["id"]] = str(e) return False diff --git a/app/forms/validators.py b/app/forms/validators.py index 1739808770..9d125f6f6c 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -130,38 +130,51 @@ def __call__( value: Union[int, Decimal] = field.data if value is not None: - error_message = self.validate_minimum(value) or self.validate_maximum(value) - if error_message: + decimal_limit = ( + field.places if isinstance(field, DecimalFieldWithSeparator) else None + ) + + if error_message := self.validate_minimum( + value=value, decimal_limit=decimal_limit + ) or self.validate_maximum(value=value, decimal_limit=decimal_limit): raise validators.ValidationError(error_message) - def validate_minimum(self, value: NumType) -> Optional[str]: + def validate_minimum( + self, *, value: NumType, decimal_limit: int | None + ) -> Optional[str]: if self.minimum is None: return None + minimum_value = format_playback_value( + value=self.minimum, + currency=self.currency, + decimal_limit=decimal_limit, + ) + if self.minimum_exclusive and value <= self.minimum: - return self.messages["NUMBER_TOO_SMALL_EXCLUSIVE"] % { - "min": format_playback_value(self.minimum, self.currency) - } + return self.messages["NUMBER_TOO_SMALL_EXCLUSIVE"] % {"min": minimum_value} if value < self.minimum: - return self.messages["NUMBER_TOO_SMALL"] % { - "min": format_playback_value(self.minimum, self.currency) - } + return self.messages["NUMBER_TOO_SMALL"] % {"min": minimum_value} return None - def validate_maximum(self, value: NumType) -> Optional[str]: + def validate_maximum( + self, *, value: NumType, decimal_limit: int | None + ) -> Optional[str]: if self.maximum is None: return None + maximum_value = format_playback_value( + value=self.maximum, + currency=self.currency, + decimal_limit=decimal_limit, + ) + if self.maximum_exclusive and value >= self.maximum: - return self.messages["NUMBER_TOO_LARGE_EXCLUSIVE"] % { - "max": format_playback_value(self.maximum, self.currency) - } + return self.messages["NUMBER_TOO_LARGE_EXCLUSIVE"] % {"max": maximum_value} if value > self.maximum: - return self.messages["NUMBER_TOO_LARGE"] % { - "max": format_playback_value(self.maximum, self.currency) - } + return self.messages["NUMBER_TOO_LARGE"] % {"max": maximum_value} return None @@ -405,8 +418,9 @@ def __call__( self, form: QuestionnaireForm, conditions: List[str], - total: Union[Decimal, int], - target_total: Union[Decimal, float], + total: Decimal | int, + target_total: Decimal | float | int, + decimal_limit: int | None = None, ) -> None: if len(conditions) > 1: try: @@ -424,9 +438,20 @@ def __call__( is_valid, message = self._is_valid(condition, total, target_total) if not is_valid: + decimal_limit = decimal_limit or ( + None + if isinstance(target_total, int) + else str(target_total)[::-1].find(".") + ) raise validators.ValidationError( self.messages[message] - % {"total": format_playback_value(target_total, self.currency)} + % { + "total": format_playback_value( + value=target_total, + currency=self.currency, + decimal_limit=decimal_limit, + ) + } ) @staticmethod diff --git a/app/helpers/form_helpers.py b/app/helpers/form_helpers.py index 52d773c879..ffde8e3359 100644 --- a/app/helpers/form_helpers.py +++ b/app/helpers/form_helpers.py @@ -4,8 +4,9 @@ import flask_babel from babel import numbers -from app.jinja_filters import format_number, get_formatted_currency +from app.jinja_filters import format_number from app.utilities import safe_content +from app.utilities.decimal_places import get_formatted_currency def sanitise_number(number: str) -> str: @@ -21,9 +22,19 @@ def sanitise_mobile_number(data: str) -> str: return re.sub(r"^(0{1,2}44|\+44|0)", "", data) -def format_playback_value(value: float | Decimal, currency: str | None = None) -> str: +def format_playback_value( + value: float | Decimal, + currency: str | None = None, + decimal_limit: int | None = None, +) -> str: + """ + For playback of values we set the decimal limit based on the number of decimal places + in the given value rather than any limit that has been set in the schema as we do for other methods + """ if currency: - return get_formatted_currency(value, currency) + return get_formatted_currency( + value=value, currency=currency, decimal_limit=decimal_limit + ) formatted_number: str = format_number(value) return formatted_number diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 1eee54d05f..e50eea9b43 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -1,4 +1,5 @@ # coding: utf-8 + import re from datetime import datetime from decimal import Decimal @@ -6,7 +7,7 @@ import flask import flask_babel -from babel import numbers, units +from babel import numbers from flask import current_app, g from jinja2 import nodes, pass_eval_context from markupsafe import Markup, escape @@ -18,6 +19,11 @@ ) from app.questionnaire.rules.utils import parse_datetime from app.settings import MAX_NUMBER +from app.utilities.decimal_places import ( + custom_format_decimal, + custom_format_unit, + get_formatted_currency, +) blueprint = flask.Blueprint("filters", __name__) FormType = Mapping[str, Mapping[str, Any]] @@ -34,15 +40,11 @@ def strip_tags(value: str) -> Markup: @blueprint.app_template_filter() -def format_number(value: Union[int, Decimal, float]) -> str: - formatted_number: str - if value or value == 0: - formatted_number = numbers.format_decimal( - value, locale=flask_babel.get_locale() - ) - return formatted_number +def format_number(value: int | Decimal | float) -> str: + locale = flask_babel.get_locale() - return "" + formatted_number: str = custom_format_decimal(value, locale) + return formatted_number def get_formatted_address(address_fields: dict[str, str]) -> str: @@ -50,16 +52,6 @@ def get_formatted_address(address_fields: dict[str, str]) -> str: return "
      ".join(address_field for address_field in address_fields.values()) -def get_formatted_currency(value: Union[float, Decimal], currency: str = "GBP") -> str: - if value or value == 0: - formatted_currency: str = numbers.format_currency( - number=value, currency=currency, locale=flask_babel.get_locale() - ) - return formatted_currency - - return "" - - @blueprint.app_template_filter() def get_currency_symbol(currency: str = "GBP") -> str: currency_symbol: str = numbers.get_currency_symbol( @@ -78,7 +70,7 @@ def format_unit( value: int | float | Decimal, length: UnitLengthType = "short", ) -> str: - formatted_unit: str = units.format_unit( + formatted_unit: str = custom_format_unit( value=value, measurement_unit=unit, length=length, @@ -506,8 +498,15 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche for option in value ] elif answer_type == "currency": + decimal_places = answer.get("decimal_places") self.valueList = [ - SummaryRowItemValue(get_formatted_currency(value, answer["currency"])) + SummaryRowItemValue( + get_formatted_currency( + value=value, + currency=answer["currency"], + decimal_limit=decimal_places, + ) + ) ] elif answer_type in ["date", "monthyeardate", "yeardate"]: if question["type"] == "DateRange": diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 9720bc3d27..f37e341ea5 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -22,7 +22,10 @@ get_routing_path_block_ids_by_section_for_dependent_sections, ) from app.questionnaire.placeholder_transforms import PlaceholderTransforms -from app.questionnaire.questionnaire_schema import TRANSFORMS_REQUIRING_ROUTING_PATH +from app.questionnaire.questionnaire_schema import ( + TRANSFORMS_REQUIRING_ROUTING_PATH, + TRANSFORMS_REQUIRING_UNRESOLVED_ARGUMENTS, +) from app.questionnaire.value_source_resolver import ( ValueSourceEscapedTypes, ValueSourceResolver, @@ -153,12 +156,15 @@ def _parse_transforms( self, transform_list: Sequence[Mapping] ) -> TransformedValueTypes: transformed_value: TransformedValueTypes = None - for transform in transform_list: transform_args: MutableMapping = {} value_source_resolver = self._get_value_source_resolver_for_transform( transform ) + + if transform["transform"] in TRANSFORMS_REQUIRING_UNRESOLVED_ARGUMENTS: + transform_args["unresolved_arguments"] = transform["arguments"] + for arg_key, arg_value in transform["arguments"].items(): resolved_value: ValueSourceEscapedTypes | ValueSourceTypes | TransformedValueTypes diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index be38d9c89e..af9a2a6898 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -1,11 +1,9 @@ from datetime import date, datetime, timezone from decimal import Decimal -from typing import TYPE_CHECKING, Literal, Sequence, Sized +from typing import TYPE_CHECKING, Literal, Mapping, Sequence, Sized from urllib.parse import quote -from babel import units from babel.dates import format_datetime -from babel.numbers import format_currency, format_decimal from dateutil.relativedelta import relativedelta from flask_babel import ngettext @@ -14,6 +12,11 @@ from app.questionnaire.rules.operations_helper import OperationHelper from app.questionnaire.rules.utils import parse_datetime from app.settings import DEFAULT_LOCALE +from app.utilities.decimal_places import ( + custom_format_decimal, + custom_format_unit, + get_formatted_currency, +) if TYPE_CHECKING: from app.questionnaire.placeholder_renderer import ( @@ -41,10 +44,38 @@ def __init__( input_date_format = "%Y-%m-%d" - def format_currency(self, number: Decimal | float, currency: str = "GBP") -> str: - formatted_currency: str = format_currency(number, currency, locale=self.locale) + def format_currency( + self, + number: int | Decimal | float, + unresolved_arguments: Mapping, + currency: str = "GBP", + ) -> str: + """ + The raw arguments for the transform are required here, in addition to the formatted number, as custom logic is required + to calculate the correct number of decimals based on the source of the transform. The decimal only takes into account if the source is + an answer or a calculated summary without any previous transform. + """ + formatted_currency: str = get_formatted_currency( + value=number, + currency=currency, + locale=self.locale, + decimal_limit=self._get_decimal_limit(unresolved_arguments), + ) return formatted_currency + def _get_decimal_limit(self, unresolved_arguments: Mapping) -> int | None: + decimal_limit = None + source = unresolved_arguments["number"].get("source") + identifier = unresolved_arguments["number"].get("identifier") + if source == "answers": + decimal_limit = self.schema.get_decimal_limit([identifier]) + elif source == "calculated_summary": + decimal_limit = self.schema.get_decimal_limit_from_calculated_summaries( + [identifier] + ) + + return decimal_limit + def format_date(self, date_to_format: str, date_format: str) -> str: date_as_datetime = datetime.strptime( date_to_format, self.input_date_format @@ -124,12 +155,8 @@ def format_possessive(self, string_to_format: str) -> str: return string_to_format - def format_number(self, number: int | Decimal | str) -> str: - if number or number == 0: - formatted_decimal: str = format_decimal(number, locale=self.locale) - return formatted_decimal - - return "" + def format_number(self, number: int | Decimal | float) -> str: + return custom_format_decimal(number, self.locale) @staticmethod def format_percentage(value: int | Decimal | str) -> str: @@ -142,12 +169,14 @@ def format_unit( unit_length: Literal["short", "long", "narrow"] | None = None, ) -> str: length = unit_length or "short" - formatted_unit: str = units.format_unit( + + formatted_unit: str = custom_format_unit( value=value, measurement_unit=unit, length=length, locale=self.locale, ) + return formatted_unit @staticmethod diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 21ab288610..65c5a9d02c 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -42,6 +42,7 @@ DependencyDictType: TypeAlias = dict[str, OrderedSet[str]] TRANSFORMS_REQUIRING_ROUTING_PATH = {"first_non_empty_item"} +TRANSFORMS_REQUIRING_UNRESOLVED_ARGUMENTS = ["format_currency"] NUMERIC_ANSWER_TYPES = { "Currency", @@ -1037,15 +1038,41 @@ def get_answer_format_for_calculated_summary( """ # Type ignore: the block will exist for any valid calculated summary id calculated_summary_block: ImmutableDict = self.get_block(calculated_summary_block_id) # type: ignore - first_answer_id = get_calculated_summary_answer_ids(calculated_summary_block)[0] + answer_ids = get_calculated_summary_answer_ids(calculated_summary_block) + decimal_limit = self.get_decimal_limit(answer_ids) + first_answer_id = answer_ids[0] first_answer = self.get_answers_by_answer_id(first_answer_id)[0] return { "type": first_answer["type"].lower(), "unit": first_answer.get("unit"), "unit_length": first_answer.get("unit_length"), "currency": first_answer.get("currency"), + "decimal_places": decimal_limit, } + def get_decimal_limit_from_calculated_summaries( + self, calculated_summary_block_ids: list[str] + ) -> int | None: + """ + Get the max number of decimal places from the calculated summary block(s) passed in + """ + decimal_limits: list[int] = [] + for calculated_summary_id in calculated_summary_block_ids: + # Type ignore: the block will exist for any valid calculated summary id + answer_ids = get_calculated_summary_answer_ids(self.get_block(calculated_summary_id)) # type: ignore + if (decimal_limit := self.get_decimal_limit(answer_ids)) is not None: + decimal_limits.append(decimal_limit) + return max(decimal_limits, default=None) + + def get_decimal_limit(self, answer_ids: list[str]) -> int | None: + decimal_limits: list[int] = [ + decimal_places + for answer_id in answer_ids + for answer in self.get_answers_by_answer_id(answer_id) + if (decimal_places := answer.get("decimal_places")) is not None + ] + return max(decimal_limits, default=None) + def get_answer_ids_for_block(self, block_id: str) -> list[str]: if block := self.get_block(block_id): if block.get("question"): diff --git a/app/utilities/decimal_places.py b/app/utilities/decimal_places.py new file mode 100644 index 0000000000..a9f9eeec0b --- /dev/null +++ b/app/utilities/decimal_places.py @@ -0,0 +1,124 @@ +from decimal import Decimal +from typing import Literal, TypeAlias + +import flask_babel +from babel import Locale, numbers, units +from babel.numbers import get_currency_precision + +UnitLengthType: TypeAlias = Literal["short", "long", "narrow"] + + +def custom_format_decimal(value: int | Decimal | float, locale: Locale | str) -> str: + """ + This function provides a wrapper for the numbers `format_decimal` method, generating the + number format (including the desired number of decimals), based on the value entered by the user and + the locale. + """ + number_format = get_number_format(value, locale) + + return numbers.format_decimal( + value, + format=number_format, + locale=locale, + ) + + +def get_formatted_currency( + *, + value: float | Decimal, + currency: str = "GBP", + locale: str | None = None, + decimal_limit: int | None = None, +) -> str: + """ + This function provides a wrapper for the numbers `format_currency` method, generating the + number format (including the desired number of decimals). + + The number of decimals displayed is based on the value entered by the user, the decimal limit set in the schema + and the locale. + """ + locale = locale or flask_babel.get_locale() + decimal_places = _get_decimal_places(value) + + # get locale pattern + parsed_locale = Locale.parse(locale) + + # Use the default babel currency format "standard" + number_format = parsed_locale.currency_formats["standard"] + + currency_precision = get_currency_precision(currency) + + # If there is no decimal limit then use the currency precision value if the number of decimals entered + # is less than the value returned by babel's currency precision method. + if ( + decimal_limit is not None + and currency_precision > decimal_limit >= decimal_places + ) or (decimal_limit is None and not decimal_places): + currency_digits = False + else: + currency_digits = decimal_places < currency_precision + + # The decimal limit is set to either the number of decimal places entered by the user, or the currency precision + # value for the given currency, whichever is larger. + decimal_limit = max(decimal_places, currency_precision) + + # Formats the number based on the number of decimal places and the decimal limit that have been calcualted + # above. + number_format.frac_prec = (min(decimal_places, decimal_limit), decimal_limit) + + return numbers.format_currency( + number=value, + currency=currency, + format=number_format, + locale=parsed_locale, + currency_digits=currency_digits, + ) + + +def custom_format_unit( + value: int | float | Decimal, + measurement_unit: str, + locale: Locale | str, + length: UnitLengthType = "short", +): + """ + This function provides a wrapper for the numbers `format_unit` method, generating the + number format (including the desired number of decimals), based on the value entered by the user and + the locale. + """ + number_format = get_number_format(value, locale) + + formatted_unit: str = units.format_unit( + value=value, + measurement_unit=measurement_unit, + length=length, + format=number_format, + locale=locale, + ) + + return formatted_unit + + +def get_number_format(value: int | float | Decimal, locale: Locale | str) -> str: + """ + Generates the number format based on the value entered by the user and the locale + + Format follows the number formats as specified in the babel docs e.g: '#,##0.###' + + Uses the decimal places set by the user with frac_prec to ensure that trailing zeroes + are not dropped and that the correct number of decimal places as entered by the user are displayed + after formatting. + """ + decimal_places = _get_decimal_places(value) + locale = Locale.parse(locale) + locale_decimal_format = locale.decimal_formats[None] + locale_decimal_format.frac_prec = (decimal_places, decimal_places) + return locale_decimal_format + + +def _get_decimal_places(value: int | float | Decimal | None) -> int: + """ + We use '.' rather than the decimal separator based on the locale as the separator will always be + formatted so that it is '.' by the time it reaches this method. + """ + return len(str(value).split(".")[1]) if "." in str(value) else 0 diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 9cbbb5bd43..2f3223c8a5 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,4 +1,4 @@ -from typing import Callable, Iterable, Literal, Mapping, MutableMapping, Tuple +from typing import Callable, Iterable, Mapping, MutableMapping, Tuple from werkzeug.datastructures import ImmutableDict @@ -9,12 +9,7 @@ SupplementaryDataStore, ) from app.data_models.metadata_proxy import MetadataProxy -from app.jinja_filters import ( - format_number, - format_percentage, - format_unit, - get_formatted_currency, -) +from app.jinja_filters import format_number, format_percentage, format_unit from app.questionnaire.questionnaire_schema import ( QuestionnaireSchema, get_calculated_summary_answer_ids, @@ -24,6 +19,7 @@ from app.questionnaire.schema_utils import get_answer_ids_in_block from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_question_to_display, transform_variants +from app.utilities.decimal_places import get_formatted_currency from app.utilities.types import LocationType from app.views.contexts.context import Context from app.views.contexts.summary.calculated_summary_block import NumericType @@ -278,6 +274,7 @@ def _get_formatted_total( def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: values_to_calculate: list = [] answer_format: dict = {"type": None} + decimal_limits: list[int] = [] for group in groups: for block in group["blocks"]: question = choose_question_to_display( @@ -299,19 +296,27 @@ def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: "unit_length": answer.get("unit_length"), "currency": answer.get("currency"), } + + if (decimal_places := answer.get("decimal_places")) is not None: + decimal_limits.append(decimal_places) + answer_value = answer.get("value") or 0 values_to_calculate.append(answer_value) - + answer_format["decimal_places"] = max(decimal_limits, default=None) return answer_format, values_to_calculate @staticmethod def _format_total( *, - answer_format: Mapping[str, Literal["short", "long", "narrow"]], + answer_format: Mapping, total: NumericType, ) -> str: if answer_format["type"] == "currency": - return get_formatted_currency(total, answer_format["currency"]) + return get_formatted_currency( + value=total, + currency=answer_format["currency"], + decimal_limit=answer_format.get("decimal_places"), + ) if answer_format["type"] == "unit": return format_unit( diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index ae27219c36..b88f30fa8d 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -120,6 +120,11 @@ def build_view_context(self) -> dict[str, dict]: answer_format = self._schema.get_answer_format_for_calculated_summary( calculated_summary_ids[0] ) + answer_format[ + "decimal_places" + ] = self._schema.get_decimal_limit_from_calculated_summaries( + calculated_summary_ids + ) formatted_total = self._format_total(answer_format=answer_format, total=total) return self._build_formatted_summary( diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index 54f3bd7769..9d272baf75 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -32,6 +32,7 @@ def __init__( self.unit = answer_schema.get("unit") self.unit_length = answer_schema.get("unit_length") self.currency = answer_schema.get("currency") + self.decimal_places = answer_schema.get("decimal_places") self._original_answer_id = answer_schema.get("original_answer_id") self.link = self._build_link( block_id=block_id, @@ -52,6 +53,7 @@ def serialize(self) -> dict: "unit_length": self.unit_length, "currency": self.currency, "link": self.link, + "decimal_places": self.decimal_places, } def _build_link( diff --git a/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json b/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json new file mode 100644 index 0000000000..304957746e --- /dev/null +++ b/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json @@ -0,0 +1,271 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "A test schema to demo Calculated Summary", + "theme": "default", + "description": "A schema to showcase Calculated Summary pages and usage in value source.", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "default-section", + "groups": [ + { + "id": "group", + "title": "Total a range of values", + "blocks": [ + { + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (Decimal limit: 1)", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 1 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question", + "title": "Second Number Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer", + "label": "Second answer in currency label (Decimal limit: 2)", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in currency total (Decimal limit: 3)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 3 + } + ] + } + }, + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer label (Decimal limit: 4)", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 4 + } + ] + } + }, + { + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "Fourth Number Question Title", + "type": "General", + "answers": [ + { + "id": "fourth-number-answer", + "label": "Fourth answer label (Decimal limit: 5)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 5 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "fourth-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Interstitial", + "id": "calculated-summary-total-confirmation", + "content": { + "title": "You have provided the following grand totals.", + "contents": [ + { + "list": [ + { + "text": "Total currency values: {currency_total}", + "placeholders": [ + { + "placeholder": "currency_total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "currency-total-playback" + } + } + } + ] + } + ] + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "fifth-number-block", + "question": { + "id": "fifth-number-question", + "title": "Fifth Number Question Title", + "type": "General", + "answers": [ + { + "id": "fifth-number-answer", + "label": "Fifth answer label (Decimal limit: 2)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "sixth-number-block", + "question": { + "id": "sixth-number-question", + "title": "Sixth Number Question Title", + "type": "General", + "answers": [ + { + "id": "sixth-number-answer", + "label": "Fifth answer label (Decimal limit: 2)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "fifth-number-answer" + }, + { + "source": "answers", + "identifier": "sixth-number-answer" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "GrandCalculatedSummary", + "id": "currency-grand-summary", + "title": "We calculate the grand total to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "currency-total-playback" + }, + { + "source": "calculated_summary", + "identifier": "currency-total-playback-2" + } + ] + }, + "title": "Grand calculated summary of journeys" + } + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json index 78bd81e4b0..7634df978e 100644 --- a/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json +++ b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json @@ -92,14 +92,16 @@ "label": "Money on milk", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q1-a2", "label": "Money on eggs", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } @@ -117,14 +119,16 @@ "label": "Money on bread", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q2-a2", "label": "Money on cheese", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } diff --git a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json index bca1827361..58e0f183b7 100644 --- a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json +++ b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json @@ -50,14 +50,16 @@ "label": "Money spent on fruit", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q1-a2", "label": "Money spent on veg", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } @@ -75,14 +77,16 @@ "label": "Money spent on bread", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q2-a2", "label": "Money spent on not bread", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } @@ -134,14 +138,16 @@ "label": "Money spent on jumpers", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q3-a2", "label": "Money spent on hats", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } @@ -261,14 +267,16 @@ "label": "Video games", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 }, { "id": "q4-a2", "label": "Board games", "type": "Currency", "currency": "GBP", - "mandatory": false + "mandatory": false, + "decimal_places": 2 } ] } diff --git a/schemas/test/en/test_multiple_answers.json b/schemas/test/en/test_multiple_answers.json index b8d4d4e260..6f1832cdac 100644 --- a/schemas/test/en/test_multiple_answers.json +++ b/schemas/test/en/test_multiple_answers.json @@ -102,7 +102,8 @@ "type": "Currency", "currency": "GBP", "mandatory": true, - "label": "What is your budget?" + "label": "What is your budget?", + "decimal_places": 2 }, { "id": "month-year-date-answer", diff --git a/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json index 39a023d924..b6b9f73f22 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json @@ -534,7 +534,8 @@ "label": "Weekly spending on parking", "mandatory": true, "type": "Currency", - "currency": "GBP" + "currency": "GBP", + "decimal_places": 2 } ] } diff --git a/schemas/test/en/test_new_calculated_summary_repeating_blocks.json b/schemas/test/en/test_new_calculated_summary_repeating_blocks.json index 83f0fef8e5..249413b174 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_blocks.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_blocks.json @@ -63,7 +63,8 @@ "label": "Monthly expenditure travelling by car", "mandatory": false, "type": "Currency", - "currency": "GBP" + "currency": "GBP", + "decimal_places": 2 } ] } @@ -303,7 +304,8 @@ }, "mandatory": false, "type": "Currency", - "currency": "GBP" + "currency": "GBP", + "decimal_places": 2 }, { "id": "transport-additional-cost", @@ -321,7 +323,8 @@ }, "mandatory": false, "type": "Currency", - "currency": "GBP" + "currency": "GBP", + "decimal_places": 2 } ] } diff --git a/schemas/test/en/test_numbers.json b/schemas/test/en/test_numbers.json index 3e32776a7a..d111304925 100644 --- a/schemas/test/en/test_numbers.json +++ b/schemas/test/en/test_numbers.json @@ -271,7 +271,7 @@ "mandatory": false, "type": "Currency", "currency": "GBP", - "decimal_places": 2, + "decimal_places": 5, "maximum": { "value": { "source": "answers", diff --git a/schemas/test/en/test_submit_with_summary.json b/schemas/test/en/test_submit_with_summary.json index 5ab6f32ed0..f43a9105e8 100644 --- a/schemas/test/en/test_submit_with_summary.json +++ b/schemas/test/en/test_submit_with_summary.json @@ -157,7 +157,8 @@ "label": "Currency", "mandatory": false, "type": "Currency", - "currency": "GBP" + "currency": "GBP", + "decimal_places": 2 }, { "id": "numbers-unit-answer", diff --git a/schemas/test/en/test_unit_patterns.json b/schemas/test/en/test_unit_patterns.json index 398a0f09d5..d795d95653 100644 --- a/schemas/test/en/test_unit_patterns.json +++ b/schemas/test/en/test_unit_patterns.json @@ -204,7 +204,8 @@ "mandatory": false, "type": "Unit", "unit": "volume-cubic-centimeter", - "unit_length": "short" + "unit_length": "short", + "decimal_places": 6 }, { "id": "cubic-metres", @@ -212,7 +213,8 @@ "mandatory": false, "type": "Unit", "unit": "volume-cubic-meter", - "unit_length": "short" + "unit_length": "short", + "decimal_places": 6 }, { "id": "litres", @@ -220,7 +222,8 @@ "mandatory": false, "type": "Unit", "unit": "volume-liter", - "unit_length": "short" + "unit_length": "short", + "decimal_places": 6 }, { "id": "hectolitres", @@ -228,7 +231,8 @@ "mandatory": false, "type": "Unit", "unit": "volume-hectoliter", - "unit_length": "short" + "unit_length": "short", + "decimal_places": 6 }, { "id": "megalitres", @@ -236,7 +240,8 @@ "mandatory": false, "type": "Unit", "unit": "volume-megaliter", - "unit_length": "short" + "unit_length": "short", + "decimal_places": 6 } ], "id": "set-volume-unit-questions", diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index e255747e6b..02a2b3dd24 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1180,7 +1180,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke [ "test_validation_sum_against_total_equal", "breakdown-block", - [Answer(answer_id="total-answer", value=10)], + [Answer(answer_id="total-answer", value=Decimal("10.00"))], { "breakdown-1": "", "breakdown-2": "5", @@ -1196,7 +1196,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke }, "breakdown-question", ["TOTAL_SUM_NOT_EQUALS"], - {"total": "10"}, + {"total": "10.00"}, ], [ "test_validation_sum_against_total_equal", @@ -1439,7 +1439,7 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v def test_multi_calculation(app, answer_store, list_store): - answer_total = Answer(answer_id="total-answer", value=10) + answer_total = Answer(answer_id="total-answer", value=Decimal("10.00")) answer_store.add_or_update(answer_total) @@ -1509,7 +1509,7 @@ def test_multi_calculation(app, answer_store, list_store): assert form.question_errors["breakdown-question"] == schema.error_messages[ "TOTAL_SUM_NOT_EQUALS" - ] % {"total": "10"} + ] % {"total": "10.00"} def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_store): diff --git a/tests/app/forms/validation/test_sum_check_validator.py b/tests/app/forms/validation/test_sum_check_validator.py index 9ddd5cf584..d1bbc185ba 100644 --- a/tests/app/forms/validation/test_sum_check_validator.py +++ b/tests/app/forms/validation/test_sum_check_validator.py @@ -46,7 +46,9 @@ def test_currency_playback(mock_form): validator(mock_form, conditions, calculation_total, target_total) assert error_messages["TOTAL_SUM_NOT_EQUALS"] % { - "total": format_playback_value(target_total, currency="EUR"), + "total": format_playback_value( + value=target_total, currency="EUR", decimal_limit=1 + ), } == str(exc.value) diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 8ad0e34862..dd2875c106 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1048,8 +1048,8 @@ def default_placeholder_value_schema(): @pytest.fixture -def transformer(mock_renderer, mock_schema): - def _transform(language="en"): +def transformer(mock_renderer, mock_schema, locale_string="en_GB"): + def _transform(language=locale_string): return PlaceholderTransforms( language=language, schema=mock_schema, renderer=mock_renderer ) diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 91419c2279..07760e4ae7 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -1,3 +1,6 @@ +from decimal import Decimal + +import pytest from mock import Mock from app.data_models import ProgressStore, SupplementaryDataStore @@ -51,9 +54,9 @@ def test_metadata_placeholder(mock_renderer, mock_schema, mock_location): assert period_str == placeholders["period"] -def test_previous_answer_transform_placeholder( - mock_renderer, mock_schema, mock_location -): +def test_previous_answer_transform_placeholder(mock_renderer, mock_location): + schema = load_schema_from_name("test_placeholder_transform") + placeholder_list = [ { "placeholder": "total_turnover", @@ -64,7 +67,7 @@ def test_previous_answer_transform_placeholder( "number": { "source": "answers", "identifier": "total-retail-turnover-answer", - } + }, }, } ], @@ -83,7 +86,7 @@ def test_previous_answer_transform_placeholder( list_store=ListStore(), metadata=get_metadata(), response_metadata={}, - schema=mock_schema, + schema=schema, renderer=mock_renderer, progress_store=ProgressStore(), location=mock_location, @@ -1280,3 +1283,58 @@ def test_placeholder_dependencies_cache(mocker, mock_renderer): placeholder_2 = placeholder_parser(placeholder_list=placeholder_list_2) assert placeholder_2["date_entry_answer_to"] == "28 April 2016" assert path_finder.called == 1 + + +@pytest.mark.parametrize( + "first_number, second_number, expected_result", + ( + ("1.2", "1", "£2.20"), + ("1", "2", "£3"), + ("1.123", "1.2", "£2.323"), + ), +) +def test_format_currency_placeholder_total_with_previous_transform( + mock_renderer, + mock_schema, + mock_location, + first_number, + second_number, + expected_result, +): + placeholder_list = [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "add", + "arguments": { + "lhs": Decimal(first_number), + "rhs": Decimal(second_number), + }, + }, + { + "transform": "format_currency", + "arguments": {"number": {"source": "previous_transform"}}, + }, + ], + } + ] + + metadata = get_metadata() + + parser = PlaceholderParser( + language="en", + answer_store=AnswerStore(), + list_store=ListStore(), + metadata=metadata, + response_metadata={}, + schema=mock_schema, + renderer=mock_renderer, + progress_store=ProgressStore(), + location=mock_location, + supplementary_data_store=SupplementaryDataStore(), + ) + + placeholders = parser(placeholder_list) + + assert placeholders["total"] == expected_result diff --git a/tests/app/questionnaire/test_placeholder_transforms.py b/tests/app/questionnaire/test_placeholder_transforms.py index 81c9268085..1af30ecdab 100644 --- a/tests/app/questionnaire/test_placeholder_transforms.py +++ b/tests/app/questionnaire/test_placeholder_transforms.py @@ -1,42 +1,44 @@ +import unicodedata from decimal import Decimal import pytest from app.questionnaire.placeholder_transforms import PlaceholderTransforms from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from tests.app.test_jinja_filters import ( + TEST_FORMAT_CURRENCY_PARAMS, + TEST_FORMAT_NUMBER_PARAMS, + TEST_FORMAT_UNIT_PARAMS, +) -@pytest.mark.parametrize( - "number, currency, expected", - ( - ("11", "GBP", "£11.00"), - ("11.99", "GBP", "£11.99"), - ("11000", "USD", "US$11,000.00"), - (0, None, "£0.00"), - (0.00, None, "£0.00"), - ), -) -def test_format_currency(number, currency, expected, transformer): - assert transformer().format_currency(number, currency or "GBP") == expected +@pytest.mark.parametrize(*TEST_FORMAT_CURRENCY_PARAMS) +def test_format_currency( + mocker, + transformer, + value, + currency, + locale_string, + decimal_limit, + expected_result, + app, +): + with app.app_context(): + mocker.patch( + "app.questionnaire.placeholder_transforms.PlaceholderTransforms._get_decimal_limit", + return_value=decimal_limit, + ) + result = transformer(language=locale_string).format_currency( + number=value, currency=currency, unresolved_arguments={} + ) + assert unicodedata.normalize("NFKD", result) == expected_result -@pytest.mark.parametrize( - "number, expected", - ( - (123, "123"), - ("123.4", "123.4"), - ("123.40", "123.4"), - ("1000", "1,000"), - ("10000", "10,000"), - ("100000000", "100,000,000"), - (0, "0"), - (0.00, "0"), - ("", ""), - (None, ""), - ), -) -def test_format_number(number, expected, transformer): - assert transformer().format_number(number) == expected +@pytest.mark.parametrize(*TEST_FORMAT_NUMBER_PARAMS) +def test_format_number(value, locale_string, expected_result, transformer, app): + with app.app_context(): + result = transformer(language=locale_string).format_number(value) + assert unicodedata.normalize("NFKD", result) == expected_result @pytest.mark.parametrize( @@ -55,19 +57,23 @@ def test_format_percentage(value, expected, transformer): assert transformer().format_percentage(value) == expected -@pytest.mark.parametrize( - "unit, value, unit_length, expected", - ( - ("millimeter", Decimal(0.123), "short", "0.123 mm"), - ("centimeter", "123", "short", "123 cm"), - ("kilometer", "123", "long", "123 kilometre"), - ("mile", "123", "short", "123 mi"), - ("mile", "123", "narrow", "123mi"), - ("mile", "123", None, "123 mi"), - ), -) -def test_format_unit(unit, value, unit_length, expected, transformer): - assert transformer().format_unit(unit, value, unit_length) == expected +@pytest.mark.parametrize(*TEST_FORMAT_UNIT_PARAMS) +def test_format_unit( + value, + measurement_unit, + locale_string, + length, + expected_result, + transformer, + app, +): + with app.app_context(): + assert ( + transformer(language=locale_string).format_unit( + measurement_unit, value, length + ) + == expected_result + ) def test_format_list(transformer): @@ -103,7 +109,7 @@ def test_format_list_empty_or_none(transformer, list_to_format): ), ) def test_format_possessive(name, expected, transformer): - assert transformer().format_possessive(name) == expected + assert transformer(language="en").format_possessive(name) == expected @pytest.mark.parametrize( diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 18758db3b2..69841c695c 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -1,10 +1,12 @@ # coding: utf-8 +# pylint: disable=too-many-lines +import unicodedata from datetime import datetime, timezone +from decimal import Decimal import pytest import simplejson as json from flask import g -from jinja2 import Undefined from mock import Mock from app.jinja_filters import ( @@ -18,7 +20,6 @@ format_unit_input_label, get_currency_symbol, get_formatted_address, - get_formatted_currency, get_width_for_number, map_list_collector_config, map_summary_item_config, @@ -26,8 +27,161 @@ strip_tags, ) from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.utilities.decimal_places import get_formatted_currency from app.utilities.schema import load_schema_from_name +TEST_FORMAT_CURRENCY_PARAMS = ( + "value, currency, locale_string, decimal_limit, expected_result", + [ + # This test case assumes that the number of decimal places entered by the + # user has already been validated before reaching the get_formatted_currency method. + # When there is no decimal limit set, providing the user has entered a decimal, then the currency local + # currency precision will take precedence if the number of decimal places entered by the user is less than the + # currency precision value. If the number of decimal places entered by the user is greater than the currency + # precision value then we will display the number of decimal places as entered by the user + # The Jordanian Dinar is used as an example in this test case as the currency precision is set to .000 + (Decimal("2"), "GBP", "en_GB", None, "£2"), + (Decimal("2"), "JOD", "en_GB", None, "JOD2"), + (Decimal("2.1"), "GBP", "en_GB", None, "£2.10"), + (Decimal("2.1"), "JOD", "en_GB", None, "JOD2.100"), + (Decimal("2"), "GBP", "en_GB", 1, "£2"), + (Decimal("2"), "GBP", "en_GB", 2, "£2.00"), + (Decimal("2"), "GBP", "en_GB", 0, "£2"), + (Decimal("2"), "GBP", "en_GB", 2, "£2.00"), + (Decimal("2"), "GBP", "en_GB", 6, "£2.00"), + (Decimal("2.1"), "GBP", "en_GB", 0, "£2.10"), + (Decimal("2.12"), "GBP", "en_GB", None, "£2.12"), + (Decimal("2.12"), "JOD", "en_GB", None, "JOD2.120"), + (Decimal("2.12"), "GBP", "en_GB", 0, "£2.12"), + (Decimal("2.123"), "GBP", "en_GB", None, "£2.123"), + (Decimal("2.123"), "JOD", "en_GB", None, "JOD2.123"), + (Decimal("123.1234"), "GBP", "en_GB", 0, "£123.1234"), + (Decimal("3000.44545"), "GBP", "en_GB", None, "£3,000.44545"), + (Decimal("3000"), "GBP", "en_GB", 0, "£3,000"), + (Decimal("3000"), "JPY", "en_GB", 0, "JP¥3,000"), + (Decimal("3000"), "JPY", "ja_JP", 0, "¥3,000"), + (123, "GBP", "en_GB", 1, "£123"), + (Decimal("2.1"), "GBP", "en_GB", 1, "£2.1"), + (Decimal("123.4"), "HUF", "hu_HU", 1, "123,4 Ft"), + (11, "GBP", "en_GB", 2, "£11.00"), + (11000, "USD", "en_GB", 2, "US$11,000.00"), + (11000, "USD", "en_GB", 2, "US$11,000.00"), + (11000, "PLN", "pl_PL", 2, "11 000,00 zł"), + (Decimal("2.1"), "GBP", "en_GB", 2, "£2.10"), + (Decimal("11.99"), "GBP", "en_GB", 2, "£11.99"), + (Decimal("2.1"), "GBP", "en_GB", 4, "£2.10"), + (Decimal("2.1"), "GBP", "en_GB", 5, "£2.10"), + (2, "GBP", "en_GB", 6, "£2.00"), + (Decimal("1.1"), "GBP", "en_GB", 6, "£1.10"), + (Decimal("1.10"), "GBP", "en_GB", 6, "£1.10"), + (Decimal("1.100"), "GBP", "en_GB", 6, "£1.100"), + (Decimal("1.1000"), "GBP", "en_GB", 6, "£1.1000"), + (Decimal("2.14564"), "GBP", "en_GB", 6, "£2.14564"), + (Decimal("3000.445"), "GBP", "en_GB", 6, "£3,000.445"), + ], +) + +TEST_FORMAT_NUMBER_PARAMS = ( + "value, locale_string, expected_result", + [ + (123, "en_GB", "123"), + (123, "es_ES", "123"), + (Decimal("123.4"), "en_GB", "123.4"), + (Decimal("123.4"), "es_US", "123.4"), + (Decimal("123.40"), "en_GB", "123.40"), + (Decimal("123.400"), "en_GB", "123.400"), + (Decimal("123.4000"), "en_GB", "123.4000"), + (Decimal("123.4000"), "pl_PL", "123,4000"), + (Decimal("123.40000"), "en_GB", "123.40000"), + (Decimal("123.40000"), "ja_JP", "123.40000"), + (Decimal("123434.7678"), "en_GB", "123,434.7678"), + (Decimal("123434.7678"), "hu_HU", "123 434,7678"), + (Decimal("123.45678"), "en_GB", "123.45678"), + (Decimal("2344.6533"), "en_GB", "2,344.6533"), + (1000, "en_GB", "1,000"), + (1000, "en_US", "1,000"), + (10000, "en_GB", "10,000"), + (10000, "es_ES", "10.000"), + (100000000, "en_GB", "100,000,000"), + (0, "en_GB", "0"), + (0, "de_DE", "0"), + (Decimal("0.00"), "en_GB", "0.00"), + (Decimal("0.000"), "en_GB", "0.000"), + (Decimal("0.000"), "es_ES", "0,000"), + (Decimal("0.00000"), "en_GB", "0.00000"), + (Decimal("0.00000"), "es_ES", "0,00000"), + ], +) + +TEST_FORMAT_UNIT_PARAMS = ( + "value, measurement_unit, locale_string, length, expected_result", + [ + (123, "mile", "en_GB", "short", "123 mi"), + ( + Decimal("0.123"), + "millimeter", + "en_GB", + "short", + "0.123 mm", + ), + ( + Decimal("0.123"), + "millimeter", + "es_ES", + "short", + "0,123 mm", + ), + (123, "centimeter", "en_GB", "short", "123 cm"), + (123, "centimeter", "pl_PL", "short", "123 cm"), + (123, "kilometer", "en_GB", "long", "123 kilometres"), + (3, "kilometer", "en_GB", "long", "3 kilometres"), + (3, "kilometer", "hu_HU", "long", "3 kilométer"), + (Decimal("1.2"), "kilometer", "en_GB", "long", "1.2 kilometres"), + (Decimal("1.20"), "kilometer", "en_GB", "long", "1.20 kilometres"), + (Decimal("1.200"), "kilometer", "en_GB", "long", "1.200 kilometres"), + (Decimal("1.2000"), "kilometer", "en_GB", "long", "1.2000 kilometres"), + (Decimal("1.20000"), "kilometer", "en_GB", "long", "1.20000 kilometres"), + (Decimal("1.20000"), "kilometer", "de_DE", "long", "1,20000 Kilometer"), + (Decimal("1.2"), "kilometer", "pl_PL", "long", "1,2 kilometra"), + (Decimal("1.2345"), "kilometer", "en_GB", "long", "1.2345 kilometres"), + (Decimal("1.2345"), "kilometer", "es_ES", "long", "1,2345 kilómetros"), + (123, "mile", "en_GB", "short", "123 mi"), + (123, "mile", "en_GB", "narrow", "123mi"), + (123, "mile", "en_US", "narrow", "123mi"), + (Decimal("0.123"), "millimeter", "en_GB", "short", "0.123 mm"), + (123, "centimeter", "en_GB", "short", "123 cm"), + (100, "length-meter", "en_GB", "short", "100 m"), + (100, "length-centimeter", "en_GB", "short", "100 cm"), + (100, "area-square-meter", "en_GB", "short", "100 m²"), + (100, "area-square-centimeter", "en_GB", "short", "100 cm²"), + (100, "area-square-kilometer", "en_GB", "short", "100 km²"), + (100, "area-square-mile", "en_GB", "short", "100 sq mi"), + (100, "area-hectare", "en_GB", "short", "100 ha"), + (100, "area-acre", "en_GB", "short", "100 ac"), + (100, "volume-cubic-meter", "en_GB", "short", "100 m³"), + (100, "volume-cubic-centimeter", "en_GB", "short", "100 cm³"), + (100, "volume-liter", "en_GB", "short", "100 l"), + (100, "volume-hectoliter", "en_GB", "short", "100 hl"), + (100, "volume-megaliter", "en_GB", "short", "100 Ml"), + (100, "duration-hour", "en_GB", "short", "100 hrs"), + (100, "duration-hour", "en_GB", "long", "100 hours"), + (100, "duration-year", "en_GB", "long", "100 years"), + (100, "mass-tonne", "en_GB", "long", "100 tonnes"), + (1, "mass-tonne", "en_GB", "long", "1 tonne"), + (100, "mass-tonne", "en_GB", "short", "100 t"), + ], +) + +TEST_FORMAT_UNIT_LANGUAGE_PARAMS = ( + "unit, value, length, formatted_unit, language", + [ + ("duration-hour", 100, "short", "100 awr", "cy"), + ("duration-year", 100, "short", "100 bl", "cy"), + ("duration-hour", 100, "long", "100 awr", "cy"), + ("duration-year", 100, "long", "100 mlynedd", "cy"), + ], +) + @pytest.mark.parametrize( "tagged_string, expected", @@ -56,29 +210,33 @@ def test_get_currency_symbol(currency, symbol): assert get_currency_symbol(currency) == symbol -def test_get_formatted_currency_with_no_value(): - assert get_formatted_currency("") == "" +@pytest.mark.parametrize(*TEST_FORMAT_CURRENCY_PARAMS) +def test_get_custom_currency( + mocker, value, currency, locale_string, decimal_limit, expected_result, app +): + with app.app_context(): + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.Mock(return_value=locale_string), + ) + result = get_formatted_currency( + value=value, currency=currency, decimal_limit=decimal_limit + ) + + assert unicodedata.normalize("NFKD", result) == expected_result + +@pytest.mark.parametrize(*TEST_FORMAT_NUMBER_PARAMS) +def test_format_number(mocker, value, locale_string, expected_result, app): + with app.app_context(): + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.Mock(return_value=locale_string), + ) -@pytest.mark.usefixtures("gb_locale") -@pytest.mark.parametrize( - "number, formatted_number", - ( - (123, "123"), - ("123.4", "123.4"), - ("123.40", "123.4"), - ("1000", "1,000"), - ("10000", "10,000"), - ("100000000", "100,000,000"), - (0, "0"), - (0.00, "0"), - ("", ""), - (None, ""), - (Undefined(), ""), - ), -) -def test_format_number(number, formatted_number): - assert format_number(number) == formatted_number + result = format_number(value) + + assert unicodedata.normalize("NFKD", result) == expected_result def test_format_date_time_in_bst(mock_autoescape_context, app): @@ -131,37 +289,22 @@ def test_format_percentage(percentage, formatted_percentage): @pytest.mark.usefixtures("app") -@pytest.mark.parametrize( - "unit, value, length, formatted_unit, language", - ( - ("length-meter", 100, "short", "100 m", "en_GB"), - ("length-centimeter", 100, "short", "100 cm", "en_GB"), - ("length-mile", 100, "short", "100 mi", "en_GB"), - ("length-kilometer", 100, "short", "100 km", "en_GB"), - ("area-square-meter", 100, "short", "100 m²", "en_GB"), - ("area-square-centimeter", 100, "short", "100 cm²", "en_GB"), - ("area-square-kilometer", 100, "short", "100 km²", "en_GB"), - ("area-square-mile", 100, "short", "100 sq mi", "en_GB"), - ("area-hectare", 100, "short", "100 ha", "en_GB"), - ("area-acre", 100, "short", "100 ac", "en_GB"), - ("volume-cubic-meter", 100, "short", "100 m³", "en_GB"), - ("volume-cubic-centimeter", 100, "short", "100 cm³", "en_GB"), - ("volume-liter", 100, "short", "100 l", "en_GB"), - ("volume-hectoliter", 100, "short", "100 hl", "en_GB"), - ("volume-megaliter", 100, "short", "100 Ml", "en_GB"), - ("duration-hour", 100, "short", "100 hrs", "en_GB"), - ("duration-hour", 100, "long", "100 hours", "en_GB"), - ("duration-year", 100, "long", "100 years", "en_GB"), - ("duration-hour", 100, "short", "100 awr", "cy"), - ("duration-year", 100, "short", "100 bl", "cy"), - ("duration-hour", 100, "long", "100 awr", "cy"), - ("duration-year", 100, "long", "100 mlynedd", "cy"), - ("mass-tonne", 100, "long", "100 tonnes", "en_GB"), - ("mass-tonne", 1, "long", "1 tonne", "en_GB"), - ("mass-tonne", 100, "short", "100 t", "en_GB"), - ), -) -def test_format_unit(unit, value, length, formatted_unit, language, mocker): +@pytest.mark.parametrize(*TEST_FORMAT_UNIT_PARAMS) +def test_format_unit( + value, measurement_unit, locale_string, length, expected_result, mocker +): + mocker.patch( + "app.jinja_filters.flask_babel.get_locale", + mocker.Mock(return_value=locale_string), + ) + assert format_unit(measurement_unit, value, length=length) == expected_result + + +@pytest.mark.usefixtures("app") +@pytest.mark.parametrize(*TEST_FORMAT_UNIT_LANGUAGE_PARAMS) +def test_format_unit_non_gb_locale( + unit, value, length, formatted_unit, language, mocker +): mocker.patch( "app.jinja_filters.flask_babel.get_locale", mocker.Mock(return_value=language) ) @@ -282,7 +425,7 @@ def test_get_width_for_number(answer, width, app): ("test-min-exclusive", 15), ("test-max-exclusive", 4), ("test-percent", 3), - ("test-decimal", 7), + ("test-decimal", 10), ("other-answer", 5), ("first-number-answer", 6), ("second-number-answer", 6), @@ -743,6 +886,7 @@ def test_calculated_summary_config(): "type": "currency", "currency": "GBP", "link": "/questionnaire/first-number-block/?return_to=final-summary&return_to_answer_id=first-number-answer#first-number-answer", + "decimal_places": 2, } ], }, @@ -765,6 +909,7 @@ def test_calculated_summary_config(): "type": "currency", "currency": "GBP", "link": "/questionnaire/second-number-block/?return_to=final-summary&return_to_answer_id=second-number-answer#second-number-answer", + "decimal_places": 2, } ], }, @@ -808,6 +953,7 @@ def test_calculated_summary_config(): "/questionnaire/first-number-block/?return_to=final-summary" "&return_to_answer_id=first-number-answer#first-number-answer" ), + "decimal_places": 2, } ], }, @@ -830,6 +976,7 @@ def test_calculated_summary_config(): "/questionnaire/second-number-block/?return_to=final-summary" "&return_to_answer_id=second-number-answer#second-number-answer" ), + "decimal_places": 2, } ], }, diff --git a/tests/app/utilities/test_decimal_places.py b/tests/app/utilities/test_decimal_places.py new file mode 100644 index 0000000000..5ada02c153 --- /dev/null +++ b/tests/app/utilities/test_decimal_places.py @@ -0,0 +1,44 @@ +import unicodedata + +import pytest + +from app.utilities.decimal_places import ( + custom_format_decimal, + custom_format_unit, + get_formatted_currency, +) +from tests.app.test_jinja_filters import ( + TEST_FORMAT_CURRENCY_PARAMS, + TEST_FORMAT_NUMBER_PARAMS, + TEST_FORMAT_UNIT_PARAMS, +) + + +@pytest.mark.parametrize(*TEST_FORMAT_NUMBER_PARAMS) +def test_custom_format_decimal(value, locale_string, expected_result): + result = custom_format_decimal(value, locale_string) + + assert unicodedata.normalize("NFKD", result) == expected_result + + +@pytest.mark.parametrize(*TEST_FORMAT_UNIT_PARAMS) +def test_custom_format_unit( + value, measurement_unit, locale_string, length, expected_result +): + result = custom_format_unit(value, measurement_unit, locale_string, length) + + assert result == expected_result + + +@pytest.mark.parametrize(*TEST_FORMAT_CURRENCY_PARAMS) +def test_custom_format_currency( + value, currency, locale_string, decimal_limit, expected_result +): + result = get_formatted_currency( + value=value, + currency=currency, + locale=locale_string, + decimal_limit=decimal_limit, + ) + + assert unicodedata.normalize("NFKD", result) == expected_result diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index a4a5f769b7..7f38634742 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -1234,6 +1234,7 @@ def test_get_answer( "unit": None, "unit_length": None, "value": 12, + "decimal_places": 0, }, { "currency": None, @@ -1244,6 +1245,7 @@ def test_get_answer( "unit": None, "unit_length": None, "value": 21, + "decimal_places": 0, }, { "currency": None, @@ -1259,6 +1261,7 @@ def test_get_answer( "label": "Non UK based supermarkets", } ], + "decimal_places": None, }, ], ), diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 50cea1c3f4..2c4185f540 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -368,6 +368,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( "unit": None, "unit_length": None, "value": 123, + "decimal_places": None, }, { "currency": None, @@ -381,6 +382,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( "detail_answer_value": None, "label": "Yes", }, + "decimal_places": None, }, ], "id": "edit-question-companies-PlwgoG", @@ -406,6 +408,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( "unit": None, "unit_length": None, "value": 456, + "decimal_places": None, }, { "currency": None, @@ -419,6 +422,7 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( "detail_answer_value": None, "label": "No", }, + "decimal_places": None, }, ], "id": "edit-question-companies-UHPLbX", diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index e9d288c125..54a98fac2e 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -136,6 +136,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": Markup("John"), + "decimal_places": None, } ], "id": "name-question", @@ -167,6 +168,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": Markup("1 Street"), + "decimal_places": None, } ], "id": "address-question", @@ -206,6 +208,7 @@ def test_context_for_summary(): "detail_answer_value": None, "label": "Yes", }, + "decimal_places": None, } ], "id": "skip-first-block-question", @@ -229,6 +232,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": 1, + "decimal_places": 2, } ], "id": "second-number-question-also-in-total", @@ -265,6 +269,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": 2, + "decimal_places": 2, }, { "currency": "GBP", @@ -275,6 +280,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": 2, + "decimal_places": 2, }, ], "id": "third-number-question", @@ -303,6 +309,7 @@ def test_context_for_summary(): "label": "4 - calculated summary answer (current section)", } ], + "decimal_places": None, } ], "id": "mutually-exclusive-checkbox-question", @@ -326,6 +333,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": None, + "decimal_places": 0, } ], "id": "skippable-question", @@ -362,6 +370,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": 1, + "decimal_places": 2, }, { "currency": "GBP", @@ -372,6 +381,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": 1, + "decimal_places": 2, }, ], "id": "third-number-question", @@ -400,6 +410,7 @@ def test_context_for_summary(): "label": "1 - calculated summary answer (previous section)", } ], + "decimal_places": None, } ], "id": "mutually-exclusive-checkbox-question", @@ -423,6 +434,7 @@ def test_context_for_summary(): "unit": None, "unit_length": None, "value": None, + "decimal_places": 0, } ], "id": "skippable-question", diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index 47d23539ea..d1202b6b0a 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -54,4 +54,23 @@ describe("Units", () => { await click(SetVolumeUnitsBlockPage.submit()); await expect(await $("body").getText()).to.have.string("tonnes"); }); + + it("Given we open a questionnaire with unit inputs, when the unit allows a maximum of 6 decimal places, then the correct number of decimal places should be displayed on the summary.", async () => { + await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); + await $(SetLengthUnitsBlockPage.submit()).click(); + await $(SetDurationUnitsBlockPage.submit()).click(); + await $(SetAreaUnitsBlockPage.submit()).click(); + await $(SetVolumeUnitsBlockPage.cubicCentimetres()).setValue(1.1); + await $(SetVolumeUnitsBlockPage.cubicMetres()).setValue(1.12); + await $(SetVolumeUnitsBlockPage.litres()).setValue(1.123); + await $(SetVolumeUnitsBlockPage.hectolitres()).setValue(1.1234); + await $(SetVolumeUnitsBlockPage.megalitres()).setValue("1.10000"); + await $(SetVolumeUnitsBlockPage.submit()).click(); + await $(SetWeightUnitsBlockPage.submit()).click(); + await expect(await $(SubmitPage.cubicCentimetres()).getText()).to.equal("1.1 cm³"); + await expect(await $(SubmitPage.cubicMetres()).getText()).to.equal("1.12 m³"); + await expect(await $(SubmitPage.litres()).getText()).to.equal("1.123 l"); + await expect(await $(SubmitPage.hectolitres()).getText()).to.equal("1.1234 hl"); + await expect(await $(SubmitPage.megalitres()).getText()).to.equal("1.10000 Ml"); + }); }); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 8a2973f992..73aafa0250 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -145,7 +145,7 @@ describe("View Submitted Response Summary Page With Repeating Sections", () => { await expect(await $(firstGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); await expect(await $(firstGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("40 - calculated summary answer (current section)"); await expect(await $("body").getHTML()).to.contain("How much did Marcus Twin spend on fruit?"); - await expect(await $(firstGroup).$$(skippableRepeatingSectionAnswer)[0].getText()).to.equal("£100.00"); + await expect(await $(firstGroup).$$(skippableRepeatingSectionAnswer)[0].getText()).to.equal("£100"); await expect(await $("body").getHTML()).to.contain("John Doe"); await expect(await $(secondGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); await expect(await $(secondGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("80 - calculated summary answer (current section)"); diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index 301fa2e59b..8694bf1b8d 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -51,16 +51,18 @@ describe("Number validation", () => { await $(TestMinMax.testMinExclusive()).setValue("124"); await $(TestMinMax.testMaxExclusive()).setValue("1233"); await $(TestMinMax.testPercent()).setValue("100"); - await $(TestMinMax.testDecimal()).setValue("11.234"); + await $(TestMinMax.testRange()).setValue("12.123456"); + await $(TestMinMax.testDecimal()).setValue("11.123456"); await click(TestMinMax.submit()); await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter a number rounded to 2 decimal places"); - await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 2 decimal places"); + await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 5 decimal places"); }); it("When I enter values inside the set range, Then I should be able to submit the survey", async () => { await $(TestMinMax.testRange()).setValue("1019"); - await $(TestMinMax.testDecimal()).setValue("11.23"); + await $(TestMinMax.testDecimal()).setValue("11.10000"); + await $(TestMinMax.testPercent()).setValue("99"); await click(TestMinMax.submit()); await $(DetailAnswer.other()).click(); await $(DetailAnswer.otherDetail()).setValue("1019"); @@ -107,5 +109,9 @@ describe("Number validation", () => { await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); }); + + it("When a number with more than 3 decimal places has been entered, Then it should be displayed correctly on the summary", async () => { + await expect(await $(SubmitPage.testDecimal()).getText()).to.equal("£11.10000"); + }); }); }); diff --git a/tests/integration/components/mutually_exclusive/test_currency_single_checkbox_override.py b/tests/integration/components/mutually_exclusive/test_currency_single_checkbox_override.py index 1b02997a1d..7e1c1977b3 100644 --- a/tests/integration/components/mutually_exclusive/test_currency_single_checkbox_override.py +++ b/tests/integration/components/mutually_exclusive/test_currency_single_checkbox_override.py @@ -21,7 +21,7 @@ def test_non_exclusive_answer(self): # Then self.assertInUrl("/sections/mutually-exclusive-currency-section/") - self.assertInBody("£10.00") + self.assertInBody("£10") def test_exclusive_answer(self): # When diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 6bad6f9cc3..2cddb74a30 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -66,6 +66,15 @@ def test_new_calculated_summary(self): "We calculate the total of currency values entered to be £80.00" ) + def test_calculated_summary_total_playback(self): + self.launchSurvey("test_new_calculated_summary") + self._complete_calculated_summary_path_with_skip() + self.post() + self.post() + self.post() + self.post() + self.assertInBody("Total currency values: £80.00") + def test_new_calculated_summary_no_skip(self): self.launchSurvey("test_new_calculated_summary") self._complete_calculated_summary_path_no_skip() @@ -293,3 +302,47 @@ def test_new_calculated_summary_repeating_blocks(self): # should be absent from section summary too self.assertInUrl("/sections/section-1") self.assertNotInBody("Name of transport") + + def test_calculated_summary_default_decimal_places(self): + """ + When multiple decimal limits are set in the schema but no decimals + are entered then we should default to two decimal places on the calculated summary page + and the playback page + """ + self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.post({"first-number-answer": "10"}) + self.post( + { + "second-number-answer": "20", + "second-number-answer-also-in-total": "20", + } + ) + self.post({"third-number-answer": "30"}) + self.post({"fourth-number-answer": "40"}) + self.assertInBody( + "We calculate the total of currency values entered to be £120.00" + ) + self.post() + self.assertInBody("Total currency values: £120.00") + + def test_calculated_summary_with_varying_decimal_places(self): + """ + When multiple decimal limits are set in the schema and a mixture of decimal + places are entered then we should use the largest number of decimal places that are below the decimal limit + on the calculated summary page and the playback page + """ + self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.post({"first-number-answer": "10.1"}) + self.post( + { + "second-number-answer": "20.12", + "second-number-answer-also-in-total": "20.123", + } + ) + self.post({"third-number-answer": "30.1234"}) + self.post({"fourth-number-answer": "40.12345"}) + self.assertInBody( + "We calculate the total of currency values entered to be £120.58985" + ) + self.post() + self.assertInBody("Total currency values: £120.58985") diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 1c784d32ca..02f08ff857 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -167,3 +167,51 @@ def test_grand_calculated_summary_overlapping_answers_no_overlap(self): self.assertInBody( "Grand Calculated Summary of purchases this week comes to £330.00. Is this correct?" ) + + def test_grand_calculated_summary_default_decimal_places(self): + """ + When multiple decimal limits are set in the schema but no decimals + are entered then we should default to two decimal places on the grand calculated summary page + """ + self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.post({"first-number-answer": "10"}) + self.post( + { + "second-number-answer": "20", + "second-number-answer-also-in-total": "20", + } + ) + self.post({"third-number-answer": "30"}) + self.post({"fourth-number-answer": "40"}) + self.post() + self.post() + self.post({"fifth-number-answer": "50"}) + self.post({"sixth-number-answer": "60"}) + self.assertInBody( + "We calculate the total of currency values entered to be £110.00. Is this correct?" + ) + + def test_grand_calculated_summary_with_varying_decimal_places(self): + """ + When multiple decimal limits are set in the schema and a mixture of decimal + places are entered then we should use the largest number of decimal places that are below the decimal limit + on the grand calculated summary page + """ + self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.post({"first-number-answer": "10.1"}) + self.post( + { + "second-number-answer": "20.12", + "second-number-answer-also-in-total": "20.123", + } + ) + self.post({"third-number-answer": "30.1234"}) + self.post({"fourth-number-answer": "40.12345"}) + self.post() + self.post() + self.post({"fifth-number-answer": "50"}) + self.post({"sixth-number-answer": "60"}) + self.post() + self.assertInBody( + "We calculate the grand total to be £230.58985. Is this correct?" + ) From 02265ef37a20aa6a9e8d51f1ec30671daf98a286 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 19 Sep 2023 13:15:17 +0100 Subject: [PATCH 291/567] Schemas v3.71.0 (#1207) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3ba1a5c744..7deeecce8a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.70.1 +v3.71.0 From d104aab9fe35dc57901c4c2619262e743761b9e8 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 20 Sep 2023 14:32:07 +0100 Subject: [PATCH 292/567] Update type hint style guide markdowns (#1208) --- doc/python-type-hinting.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/doc/python-type-hinting.md b/doc/python-type-hinting.md index 0f423aee1f..42f782e4a3 100644 --- a/doc/python-type-hinting.md +++ b/doc/python-type-hinting.md @@ -39,7 +39,7 @@ def get_objects_matching(ids: Sequence[str]) -> dict[int, dict] If the typing used for a generic type would be Any or indeterministic, do not specify it: -``` +```python items: list[Any] = ["demo", 2, true] # Incorrect items: list[str | int | bool] = ["demo", 2, true] # Incorrect items: list = ["demo", 2, true] # Correct @@ -49,13 +49,13 @@ This same ruling applies for key-val types such as Mapping. If the key type and value types are known, they may be specified: -``` -known_types_dict: dict[str, str] = {"name" = "demo"} +```python +known_types_dict: dict[str, str] = {"name": "demo"} ``` If the key type is known, and the value types are deterministic, use TypedDict: -``` +```python from typing import TypedDict class Movie(TypedDict): @@ -65,7 +65,7 @@ class Movie(TypedDict): If the key type is known but the value types are indeterministic or the key type is not known, do not declare the types: -``` +```python json_data: dict = json.loads(stringified_json) ``` @@ -89,10 +89,8 @@ def test(self, var: None | int | str) -> None: - Make return types as specific as possible (to be predictable to callers) ```python - def increment_values(self, values: Sequence[int]) -> list[int]: - - -return [value + 1 for value in values] +def increment_values(self, values: Sequence[int]) -> list[int]: + return [value + 1 for value in values] ``` ## Self Type From 7c8e9d9de12cb53869b1963282cedde4eff2c85c Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:21:41 +0100 Subject: [PATCH 293/567] Use absolute imports in Runner (#1209) --- .pylintrc | 2 +- Pipfile | 3 +- Pipfile.lock | 951 +++++++++--------- app/cloud_tasks/__init__.py | 5 +- app/cloud_tasks/cloud_task_publishers.py | 2 +- app/data_models/__init__.py | 12 +- app/forms/__init__.py | 2 +- app/forms/fields/__init__.py | 8 +- app/helpers/__init__.py | 6 +- app/publisher/__init__.py | 2 +- app/questionnaire/__init__.py | 7 +- app/storage/__init__.py | 6 +- app/storage/dynamodb.py | 3 +- app/storage/redis.py | 3 +- app/submitter/__init__.py | 2 +- app/survey_config/__init__.py | 10 +- app/utilities/__init__.py | 2 +- app/views/contexts/__init__.py | 16 +- app/views/contexts/section_summary_context.py | 9 +- .../contexts/submit_questionnaire_context.py | 4 +- app/views/contexts/summary/__init__.py | 2 +- app/views/contexts/summary_context.py | 16 +- app/views/handlers/__init__.py | 3 +- app/views/handlers/relationships/__init__.py | 6 +- ...t_confirmation_email_fulfilment_request.py | 3 +- .../views/handlers/test_feedback_upload.py | 3 +- .../handlers/test_preview_questions_pdf.py | 3 +- .../test_question_with_dynamic_answers.py | 5 +- .../handlers/test_view_preview_questions.py | 3 +- .../test_view_submitted_response_pdf.py | 3 +- .../test_questionnaire_custom_page_titles.py | 2 +- ..._questionnaire_grand_calculated_summary.py | 2 +- ...ionnaire_list_change_evaluates_sections.py | 2 +- .../test_questionnaire_list_collector.py | 2 +- ...st_questionnaire_list_collector_content.py | 2 +- ...tionnaire_list_collector_primary_person.py | 2 +- ...onnaire_list_collector_repeating_blocks.py | 2 +- ...ct_to_list_add_question_action_checkbox.py | 2 +- ...irect_to_list_add_question_action_radio.py | 2 +- .../test_questionnaire_relationships.py | 2 +- ...t_questionnaire_relationships_unrelated.py | 2 +- .../test_questionnaire_same_name_items.py | 3 +- 42 files changed, 593 insertions(+), 534 deletions(-) diff --git a/.pylintrc b/.pylintrc index 09eead849c..a79574a5cb 100644 --- a/.pylintrc +++ b/.pylintrc @@ -77,7 +77,7 @@ limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. -load-plugins=pylint.extensions.mccabe,pylint_quotes,pylint.extensions.no_self_use +load-plugins=pylint.extensions.mccabe,pylint_quotes,pylint.extensions.no_self_use,pylint_absolute_imports # Pickle collected data for later comparisons. persistent=yes diff --git a/Pipfile b/Pipfile index 1c8f3da8e1..e8420b8a72 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,7 @@ jsonschema = "*" pylint = "*" pylint-mccabe = "*" pylint-quotes = "*" +pylint-absolute-imports = "*" "beautifulsoup4" = "*" httmock = "*" "flake8" = "*" @@ -44,7 +45,7 @@ flask = "*" flask-babel = "*" flask-login = "*" flask-wtf = "*" -gevent = { version = "*", platform_python_implementation = "=='CPython'" } +gevent = { version = "==23.7.0", platform_python_implementation = "=='CPython'" } google-cloud-datastore = "*" grpcio = "*" gunicorn = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 263b931aa1..6a0dcdec38 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0944f900e71d5a21d0baa28eb3620e285f33a6504cba462e9134fbd7f2bc6bb9" + "sha256": "93bae4a3d9d1e963132a771d87f74d22c50452836a32d4b5f620797e209c4b71" }, "pipfile-spec": 6, "requires": { @@ -35,107 +35,109 @@ }, "boto3": { "hashes": [ - "sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28", - "sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3" + "sha256:8149f17587c68e556743018f213f00ece81a0f021adb9b9b697a2ee8802583d7", + "sha256:8860ab54a26d1d596d64fc9de7e40c4d7c53c100311208cbd90d9272c3385513" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.32" + "version": "==1.28.51" }, "botocore": { "hashes": [ - "sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664", - "sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c" + "sha256:8e133add22f07b55d21e14176b97b82e993d5f9aca70f5b0cd0908cb105ba53a", + "sha256:91dfb38801d45214875a892bd1e908fc7a894c2ed170bacd67e6929af72f2bd2" ], "markers": "python_version >= '3.7'", - "version": "==1.31.32" + "version": "==1.31.51" }, "brotli": { "hashes": [ - "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019", - "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df", - "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d", - "sha256:160c78292e98d21e73a4cc7f76a234390e516afcd982fa17e1422f7c6a9ce9c8", - "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b", - "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c", - "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c", - "sha256:268fe94547ba25b58ebc724680609c8ee3e5a843202e9a381f6f9c5e8bdb5c70", - "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f", - "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181", - "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130", - "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19", - "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be", - "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be", - "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a", - "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa", - "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429", - "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126", - "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7", - "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad", - "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679", - "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4", - "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0", - "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b", - "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6", - "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438", - "sha256:503fa6af7da9f4b5780bb7e4cbe0c639b010f12be85d02c99452825dd0feef3f", - "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389", - "sha256:5913a1177fc36e30fcf6dc868ce23b0453952c78c04c266d3149b3d39e1410d6", - "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26", - "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337", - "sha256:5cb1e18167792d7d21e21365d7650b72d5081ed476123ff7b8cac7f45189c0c7", - "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14", - "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2", - "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430", - "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296", - "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12", - "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f", - "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7", - "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d", - "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a", - "sha256:7cb81373984cc0e4682f31bc3d6be9026006d96eecd07ea49aafb06897746452", - "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c", - "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761", - "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649", - "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b", - "sha256:88c63a1b55f352b02c6ffd24b15ead9fc0e8bf781dbe070213039324922a2eea", - "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c", - "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f", - "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a", - "sha256:9744a863b489c79a73aba014df554b0e7a0fc44ef3f8a0ef2a52919c7d155031", - "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267", - "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5", - "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7", - "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d", - "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c", - "sha256:a72661af47119a80d82fa583b554095308d6a4c356b2a554fdc2799bc19f2a43", - "sha256:afde17ae04d90fbe53afb628f7f2d4ca022797aa093e809de5c3cf276f61bbfa", - "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde", - "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17", - "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f", - "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8", - "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb", - "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb", - "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d", - "sha256:c2415d9d082152460f2bd4e382a1e85aed233abc92db5a3880da2257dc7daf7b", - "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4", - "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755", - "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a", - "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d", - "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a", - "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3", - "sha256:db844eb158a87ccab83e868a762ea8024ae27337fc7ddcbfcddd157f841fdfe7", - "sha256:defed7ea5f218a9f2336301e6fd379f55c655bea65ba2476346340a0ce6f74a1", - "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb", - "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a", - "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91", - "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b", - "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1", - "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806", - "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3", - "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1" - ], - "version": "==1.0.9" + "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", + "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", + "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", + "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", + "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", + "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", + "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", + "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", + "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", + "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", + "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", + "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", + "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", + "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", + "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", + "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", + "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", + "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", + "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", + "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", + "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", + "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", + "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", + "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", + "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", + "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", + "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", + "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", + "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", + "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", + "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", + "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", + "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", + "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", + "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", + "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", + "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", + "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", + "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", + "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", + "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", + "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", + "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", + "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", + "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", + "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", + "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", + "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", + "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", + "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", + "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", + "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", + "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", + "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", + "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", + "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", + "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", + "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", + "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", + "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", + "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", + "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", + "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", + "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", + "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", + "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", + "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", + "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", + "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", + "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", + "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", + "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", + "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", + "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", + "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", + "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", + "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", + "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", + "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", + "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", + "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", + "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", + "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.1.0" }, "cachetools": { "hashes": [ @@ -332,32 +334,32 @@ }, "cryptography": { "hashes": [ - "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306", - "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84", - "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47", - "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d", - "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116", - "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207", - "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81", - "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087", - "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd", - "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507", - "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858", - "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae", - "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34", - "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906", - "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd", - "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922", - "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7", - "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4", - "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574", - "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1", - "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c", - "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e", - "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.3" + "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", + "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", + "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", + "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", + "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", + "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", + "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", + "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", + "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", + "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", + "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", + "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", + "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", + "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", + "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", + "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", + "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", + "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", + "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", + "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", + "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", + "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", + "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.4" }, "deprecated": { "hashes": [ @@ -404,11 +406,11 @@ }, "flask-compress": { "hashes": [ - "sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b", - "sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d" + "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c", + "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee" ], "index": "pypi", - "version": "==1.13" + "version": "==1.14" }, "flask-login": { "hashes": [ @@ -486,11 +488,11 @@ }, "google-auth": { "hashes": [ - "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce", - "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873" + "sha256:2cec41407bd1e207f5b802638e32bb837df968bb5c05f413d0fa526fac4cf7a7", + "sha256:753a26312e6f1eaeec20bc6f2644a10926697da93446e1f8e24d6d32d45a922a" ], - "markers": "python_version >= '3.6'", - "version": "==2.22.0" + "markers": "python_version >= '3.7'", + "version": "==2.23.0" }, "google-cloud-core": { "hashes": [ @@ -502,39 +504,39 @@ }, "google-cloud-datastore": { "hashes": [ - "sha256:1beb0cfbfa577218d9a7f95949068ae0afb7dfdd311ec3640a92d06b45367abb", - "sha256:feafad874ed376a9d1f0899eafc9650d21fdb2228f90de9b8741a806c213b422" + "sha256:369df0653128fe63f0fb1b5a1cd5a05edae49a805f79f9b639ca9d58833bc6b5", + "sha256:63b31b676dcb2786a650d23d324d8f886c4e14586afdd0cfe6e260a73f12448e" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.17.0" + "version": "==2.18.0" }, "google-cloud-pubsub": { "hashes": [ - "sha256:af14c79dac59f05592abc7d97f3e8430079e437a49f7a1c97f7324bb9d49cd9d", - "sha256:b40712339e5f81b7a035b0be8b2ae906684a4162e119a2b9ab33bc994ba1b72f" + "sha256:32eb61fd4c1dc6c842f594d69d9afa80544e3b327aa640a164eb6fb0201eaf2d", + "sha256:f32144ad9ed32331a80a2f8379a3ca7526bbc01e7bd76de2e8ab52e492d21f50" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.18.3" + "version": "==2.18.4" }, "google-cloud-storage": { "hashes": [ - "sha256:934b31ead5f3994e5360f9ff5750982c5b6b11604dc072bc452c25965e076dc7", - "sha256:9433cf28801671de1c80434238fb1e7e4a1ba3087470e90f70c928ea77c2b9d7" + "sha256:6fbf62659b83c8f3a0a743af0d661d2046c97c3a5bfb587c4662c4bc68de3e31", + "sha256:88cbd7fb3d701c780c4272bc26952db99f25eb283fb4c2208423249f00b5fe53" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.10.0" + "version": "==2.11.0" }, "google-cloud-tasks": { "hashes": [ - "sha256:b44ff8721f9a36f118892c5d45745393fba0ce0ef27fab3853e07cb5fba91a92", - "sha256:ca1a83df7391a78592dcca751583b22c9074d0a72dc8debdb4a4687ff9958a29" + "sha256:3efb280e7a635f57860a06a346131c117042e820ad483afd256800fae1b4d701", + "sha256:801a671c6dbd2269179037e93490f87e2df59ddfcb59551128bab27e7dd09f94" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.14.1" + "version": "==2.14.2" }, "google-crc32c": { "hashes": [ @@ -612,11 +614,11 @@ }, "google-resumable-media": { "hashes": [ - "sha256:218931e8e2b2a73a58eb354a288e03a0fd5fb1c4583261ac6e4c078666468c93", - "sha256:da1bd943e2e114a56d85d6848497ebf9be6a14d3db23e9fc57581e7c3e8170ec" + "sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7", + "sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b" ], "markers": "python_version >= '3.7'", - "version": "==2.5.0" + "version": "==2.6.0" }, "googleapis-common-protos": { "hashes": [ @@ -630,6 +632,7 @@ "hashes": [ "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a", "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a", + "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1", "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43", "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33", "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8", @@ -655,6 +658,7 @@ "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91", "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5", "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9", + "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417", "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8", "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b", "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6", @@ -678,8 +682,10 @@ "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7", "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75", "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae", + "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47", "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b", "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470", + "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c", "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564", "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9", "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099", @@ -702,63 +708,63 @@ }, "grpcio": { "hashes": [ - "sha256:00258cbe3f5188629828363ae8ff78477ce976a6f63fb2bb5e90088396faa82e", - "sha256:092fa155b945015754bdf988be47793c377b52b88d546e45c6a9f9579ac7f7b6", - "sha256:0f80bf37f09e1caba6a8063e56e2b87fa335add314cf2b78ebf7cb45aa7e3d06", - "sha256:20ec6fc4ad47d1b6e12deec5045ec3cd5402d9a1597f738263e98f490fe07056", - "sha256:2313b124e475aa9017a9844bdc5eafb2d5abdda9d456af16fc4535408c7d6da6", - "sha256:23e7d8849a0e58b806253fd206ac105b328171e01b8f18c7d5922274958cc87e", - "sha256:2f708a6a17868ad8bf586598bee69abded4996b18adf26fd2d91191383b79019", - "sha256:2f7349786da979a94690cc5c2b804cab4e8774a3cf59be40d037c4342c906649", - "sha256:34950353539e7d93f61c6796a007c705d663f3be41166358e3d88c45760c7d98", - "sha256:40b72effd4c789de94ce1be2b5f88d7b9b5f7379fe9645f198854112a6567d9a", - "sha256:4b089f7ad1eb00a104078bab8015b0ed0ebcb3b589e527ab009c53893fd4e613", - "sha256:4faea2cfdf762a664ab90589b66f416274887641ae17817de510b8178356bf73", - "sha256:5371bcd861e679d63b8274f73ac281751d34bd54eccdbfcd6aa00e692a82cd7b", - "sha256:5613a2fecc82f95d6c51d15b9a72705553aa0d7c932fad7aed7afb51dc982ee5", - "sha256:57b183e8b252825c4dd29114d6c13559be95387aafc10a7be645462a0fc98bbb", - "sha256:5b7a4ce8f862fe32b2a10b57752cf3169f5fe2915acfe7e6a1e155db3da99e79", - "sha256:5e5b58e32ae14658085c16986d11e99abd002ddbf51c8daae8a0671fffb3467f", - "sha256:60fe15288a0a65d5c1cb5b4a62b1850d07336e3ba728257a810317be14f0c527", - "sha256:6907b1cf8bb29b058081d2aad677b15757a44ef2d4d8d9130271d2ad5e33efca", - "sha256:76c44efa4ede1f42a9d5b2fed1fe9377e73a109bef8675fb0728eb80b0b8e8f2", - "sha256:7a635589201b18510ff988161b7b573f50c6a48fae9cb567657920ca82022b37", - "sha256:7b400807fa749a9eb286e2cd893e501b110b4d356a218426cb9c825a0474ca56", - "sha256:82640e57fb86ea1d71ea9ab54f7e942502cf98a429a200b2e743d8672171734f", - "sha256:871f9999e0211f9551f368612460442a5436d9444606184652117d6a688c9f51", - "sha256:9338bacf172e942e62e5889b6364e56657fbf8ac68062e8b25c48843e7b202bb", - "sha256:a8a8e560e8dbbdf29288872e91efd22af71e88b0e5736b0daf7773c1fecd99f0", - "sha256:aed90d93b731929e742967e236f842a4a2174dc5db077c8f9ad2c5996f89f63e", - "sha256:b363bbb5253e5f9c23d8a0a034dfdf1b7c9e7f12e602fc788c435171e96daccc", - "sha256:b4098b6b638d9e0ca839a81656a2fd4bc26c9486ea707e8b1437d6f9d61c3941", - "sha256:b53333627283e7241fcc217323f225c37783b5f0472316edcaa4479a213abfa6", - "sha256:b670c2faa92124b7397b42303e4d8eb64a4cd0b7a77e35a9e865a55d61c57ef9", - "sha256:bb396952cfa7ad2f01061fbc7dc1ad91dd9d69243bcb8110cf4e36924785a0fe", - "sha256:c60b83c43faeb6d0a9831f0351d7787a0753f5087cc6fa218d78fdf38e5acef0", - "sha256:c6ebecfb7a31385393203eb04ed8b6a08f5002f53df3d59e5e795edb80999652", - "sha256:d78d8b86fcdfa1e4c21f8896614b6cc7ee01a2a758ec0c4382d662f2a62cf766", - "sha256:d7f8df114d6b4cf5a916b98389aeaf1e3132035420a88beea4e3d977e5f267a5", - "sha256:e1cb52fa2d67d7f7fab310b600f22ce1ff04d562d46e9e0ac3e3403c2bb4cc16", - "sha256:e3fdf04e402f12e1de8074458549337febb3b45f21076cc02ef4ff786aff687e", - "sha256:e503cb45ed12b924b5b988ba9576dc9949b2f5283b8e33b21dcb6be74a7c58d0", - "sha256:f19ac6ac0a256cf77d3cc926ef0b4e64a9725cc612f97228cd5dc4bd9dbab03b", - "sha256:f1fb0fd4a1e9b11ac21c30c169d169ef434c6e9344ee0ab27cfa6f605f6387b2", - "sha256:fada6b07ec4f0befe05218181f4b85176f11d531911b64c715d1875c4736d73a", - "sha256:fd173b4cf02b20f60860dc2ffe30115c18972d7d6d2d69df97ac38dee03be5bf", - "sha256:fe752639919aad9ffb0dee0d87f29a6467d1ef764f13c4644d212a9a853a078d", - "sha256:fee387d2fab144e8a34e0e9c5ca0f45c9376b99de45628265cfa9886b1dbe62b" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.57.0" + "sha256:002f228d197fea12797a14e152447044e14fb4fdb2eb5d6cfa496f29ddbf79ef", + "sha256:039003a5e0ae7d41c86c768ef8b3ee2c558aa0a23cf04bf3c23567f37befa092", + "sha256:09206106848462763f7f273ca93d2d2d4d26cab475089e0de830bb76be04e9e8", + "sha256:128eb1f8e70676d05b1b0c8e6600320fc222b3f8c985a92224248b1367122188", + "sha256:1c1c5238c6072470c7f1614bf7c774ffde6b346a100521de9ce791d1e4453afe", + "sha256:1ed979b273a81de36fc9c6716d9fb09dd3443efa18dcc8652501df11da9583e9", + "sha256:201e550b7e2ede113b63e718e7ece93cef5b0fbf3c45e8fe4541a5a4305acd15", + "sha256:212f38c6a156862098f6bdc9a79bf850760a751d259d8f8f249fc6d645105855", + "sha256:24765a627eb4d9288ace32d5104161c3654128fe27f2808ecd6e9b0cfa7fc8b9", + "sha256:24edec346e69e672daf12b2c88e95c6f737f3792d08866101d8c5f34370c54fd", + "sha256:2ef8d4a76d2c7d8065aba829f8d0bc0055495c998dce1964ca5b302d02514fb3", + "sha256:2f85f87e2f087d9f632c085b37440a3169fda9cdde80cb84057c2fc292f8cbdf", + "sha256:3886b4d56bd4afeac518dbc05933926198aa967a7d1d237a318e6fbc47141577", + "sha256:3e6bebf1dfdbeb22afd95650e4f019219fef3ab86d3fca8ebade52e4bc39389a", + "sha256:458899d2ebd55d5ca2350fd3826dfd8fcb11fe0f79828ae75e2b1e6051d50a29", + "sha256:4891bbb4bba58acd1d620759b3be11245bfe715eb67a4864c8937b855b7ed7fa", + "sha256:4b12754af201bb993e6e2efd7812085ddaaef21d0a6f0ff128b97de1ef55aa4a", + "sha256:532410c51ccd851b706d1fbc00a87be0f5312bd6f8e5dbf89d4e99c7f79d7499", + "sha256:5b23d75e5173faa3d1296a7bedffb25afd2fddb607ef292dfc651490c7b53c3d", + "sha256:62831d5e251dd7561d9d9e83a0b8655084b2a1f8ea91e4bd6b3cedfefd32c9d2", + "sha256:652978551af02373a5a313e07bfef368f406b5929cf2d50fa7e4027f913dbdb4", + "sha256:6801ff6652ecd2aae08ef994a3e49ff53de29e69e9cd0fd604a79ae4e545a95c", + "sha256:6cba491c638c76d3dc6c191d9c75041ca5b8f5c6de4b8327ecdcab527f130bb4", + "sha256:7e473a7abad9af48e3ab5f3b5d237d18208024d28ead65a459bd720401bd2f8f", + "sha256:8774219e21b05f750eef8adc416e9431cf31b98f6ce9def288e4cea1548cbd22", + "sha256:8f061722cad3f9aabb3fbb27f3484ec9d4667b7328d1a7800c3c691a98f16bb0", + "sha256:92ae871a902cf19833328bd6498ec007b265aabf2fda845ab5bd10abcaf4c8c6", + "sha256:9f13a171281ebb4d7b1ba9f06574bce2455dcd3f2f6d1fbe0fd0d84615c74045", + "sha256:a2d67ff99e70e86b2be46c1017ae40b4840d09467d5455b2708de6d4c127e143", + "sha256:b5e8db0aff0a4819946215f156bd722b6f6c8320eb8419567ffc74850c9fd205", + "sha256:ba0af11938acf8cd4cf815c46156bcde36fa5850518120920d52620cc3ec1830", + "sha256:bc325fed4d074367bebd465a20763586e5e1ed5b943e9d8bc7c162b1f44fd602", + "sha256:bc7ffef430b80345729ff0a6825e9d96ac87efe39216e87ac58c6c4ef400de93", + "sha256:cde11577d5b6fd73a00e6bfa3cf5f428f3f33c2d2878982369b5372bbc4acc60", + "sha256:d4cef77ad2fed42b1ba9143465856d7e737279854e444925d5ba45fc1f3ba727", + "sha256:d79b660681eb9bc66cc7cbf78d1b1b9e335ee56f6ea1755d34a31108b80bd3c8", + "sha256:d81c2b2b24c32139dd2536972f1060678c6b9fbd106842a9fcdecf07b233eccd", + "sha256:dc72e04620d49d3007771c0e0348deb23ca341c0245d610605dddb4ac65a37cb", + "sha256:dcfba7befe3a55dab6fe1eb7fc9359dc0c7f7272b30a70ae0af5d5b063842f28", + "sha256:e9f995a8a421405958ff30599b4d0eec244f28edc760de82f0412c71c61763d2", + "sha256:eb6b92036ff312d5b4182fa72e8735d17aceca74d0d908a7f08e375456f03e07", + "sha256:f0241f7eb0d2303a545136c59bc565a35c4fc3b924ccbd69cb482f4828d6f31c", + "sha256:fad9295fe02455d4f158ad72c90ef8b4bcaadfdb5efb5795f7ab0786ad67dd58", + "sha256:fbcecb6aedd5c1891db1d70efbfbdc126c986645b5dd616a045c07d6bd2dfa86", + "sha256:fe643af248442221db027da43ed43e53b73e11f40c9043738de9a2b4b6ca7697" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.58.0" }, "grpcio-status": { "hashes": [ - "sha256:15d6af055914ebbc4ed17e55ebfb8e6bb17a45a57fea32e6af19978fb7844690", - "sha256:b098da99df1eebe58337f8f78e50df990273ccacc1226fddeb47c590e3df9e02" + "sha256:0b42e70c0405a66a82d9e9867fa255fe59e618964a6099b20568c31dd9099766", + "sha256:36d46072b71a00147709ebce49344ac59b4b8960942acf0f813a8a7d6c1c28e0" ], "markers": "python_version >= '3.6'", - "version": "==1.57.0" + "version": "==1.58.0" }, "gunicorn": { "hashes": [ @@ -848,8 +854,11 @@ "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", @@ -857,6 +866,7 @@ "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", @@ -865,6 +875,7 @@ "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", @@ -872,9 +883,12 @@ "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", @@ -893,7 +907,9 @@ "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" ], "index": "pypi", "markers": "python_version >= '3.7'", @@ -953,22 +969,22 @@ }, "protobuf": { "hashes": [ - "sha256:06437f0d4bb0d5f29e3d392aba69600188d4be5ad1e0a3370e581a9bf75a3081", - "sha256:0b2b224e9541fe9f046dd7317d05f08769c332b7e4c54d93c7f0f372dedb0b1a", - "sha256:302e8752c760549ed4c7a508abc86b25d46553c81989343782809e1a062a2ef9", - "sha256:44837a5ed9c9418ad5d502f89f28ba102e9cd172b6668bc813f21716f9273348", - "sha256:55dd644adc27d2a624339332755fe077c7f26971045b469ebb9732a69ce1f2ca", - "sha256:5906c5e79ff50fe38b2d49d37db5874e3c8010826f2362f79996d83128a8ed9b", - "sha256:5d32363d14aca6e5c9e9d5918ad8fb65b091b6df66740ae9de50ac3916055e43", - "sha256:970c701ee16788d74f3de20938520d7a0aebc7e4fff37096a48804c80d2908cf", - "sha256:bd39b9094a4cc003a1f911b847ab379f89059f478c0b611ba1215053e295132e", - "sha256:d414199ca605eeb498adc4d2ba82aedc0379dca4a7c364ff9bc9a179aa28e71b", - "sha256:d4af4fd9e9418e819be30f8df2a16e72fbad546a7576ac7f3653be92a6966d30", - "sha256:df015c47d6855b8efa0b9be706c70bf7f050a4d5ac6d37fb043fbd95157a0e25", - "sha256:fc361148e902949dcb953bbcb148c99fe8f8854291ad01107e4120361849fd0e" + "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719", + "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d", + "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2", + "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4", + "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1", + "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3", + "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b", + "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76", + "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959", + "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52", + "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b", + "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675", + "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a" ], "markers": "python_version >= '3.7'", - "version": "==4.24.1" + "version": "==4.24.3" }, "pyasn1": { "hashes": [ @@ -1057,14 +1073,16 @@ }, "pytz": { "hashes": [ - "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588", - "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb" + "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", + "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" ], - "version": "==2023.3" + "version": "==2023.3.post1" }, "pyyaml": { "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", @@ -1072,7 +1090,10 @@ "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", @@ -1080,9 +1101,12 @@ "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", @@ -1097,7 +1121,9 @@ "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", @@ -1153,11 +1179,11 @@ }, "setuptools": { "hashes": [ - "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d", - "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b" + "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87", + "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a" ], "markers": "python_version >= '3.8'", - "version": "==68.1.2" + "version": "==68.2.2" }, "simplejson": { "hashes": [ @@ -1461,32 +1487,32 @@ }, "black": { "hashes": [ - "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3", - "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb", - "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087", - "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320", - "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6", - "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3", - "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc", - "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f", - "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587", - "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91", - "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a", - "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad", - "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926", - "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9", - "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be", - "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd", - "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96", - "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491", - "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2", - "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a", - "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f", - "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995" + "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f", + "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7", + "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100", + "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573", + "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d", + "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f", + "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9", + "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300", + "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948", + "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325", + "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9", + "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71", + "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186", + "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f", + "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe", + "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855", + "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80", + "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393", + "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c", + "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204", + "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377", + "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.7.0" + "version": "==23.9.1" }, "blinker": { "hashes": [ @@ -1499,20 +1525,20 @@ }, "boto3": { "hashes": [ - "sha256:b505faa126db84e226f6f8d242a798fae30a725f0cac8a76c6aca9ace4e8eb28", - "sha256:ed787f250ce2562c7744395bdf32b5a7bc9184126ef50a75e97bcb66043dccf3" + "sha256:8149f17587c68e556743018f213f00ece81a0f021adb9b9b697a2ee8802583d7", + "sha256:8860ab54a26d1d596d64fc9de7e40c4d7c53c100311208cbd90d9272c3385513" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.32" + "version": "==1.28.51" }, "botocore": { "hashes": [ - "sha256:7a07d8dc8cc47bf23af39409ada81f388eb78233e1bb2cde0c415756da753664", - "sha256:8992ac186988c4b4cc168e8e479e9472da1442b193c1bf7c9dcd1877ec62d23c" + "sha256:8e133add22f07b55d21e14176b97b82e993d5f9aca70f5b0cd0908cb105ba53a", + "sha256:91dfb38801d45214875a892bd1e908fc7a894c2ed170bacd67e6929af72f2bd2" ], "markers": "python_version >= '3.7'", - "version": "==1.31.32" + "version": "==1.31.51" }, "certifi": { "hashes": [ @@ -1685,90 +1711,90 @@ "toml" ], "hashes": [ - "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34", - "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e", - "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7", - "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b", - "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3", - "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985", - "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95", - "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2", - "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a", - "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74", - "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd", - "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af", - "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54", - "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865", - "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214", - "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54", - "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe", - "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0", - "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321", - "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446", - "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e", - "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527", - "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12", - "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f", - "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f", - "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84", - "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479", - "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e", - "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873", - "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70", - "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0", - "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977", - "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51", - "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28", - "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1", - "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254", - "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1", - "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd", - "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689", - "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d", - "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543", - "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9", - "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637", - "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071", - "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482", - "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1", - "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b", - "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5", - "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a", - "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393", - "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a", - "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba" + "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375", + "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344", + "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e", + "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745", + "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f", + "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194", + "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a", + "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f", + "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760", + "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8", + "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392", + "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d", + "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc", + "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40", + "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981", + "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0", + "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92", + "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3", + "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0", + "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086", + "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7", + "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465", + "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140", + "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952", + "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3", + "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8", + "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f", + "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593", + "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0", + "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204", + "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037", + "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276", + "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9", + "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26", + "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce", + "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7", + "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136", + "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a", + "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4", + "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c", + "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f", + "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832", + "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3", + "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969", + "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520", + "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887", + "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3", + "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6", + "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1", + "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff", + "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981", + "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e" ], "markers": "python_version >= '3.8'", - "version": "==7.3.0" + "version": "==7.3.1" }, "cryptography": { "hashes": [ - "sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306", - "sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84", - "sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47", - "sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d", - "sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116", - "sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207", - "sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81", - "sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087", - "sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd", - "sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507", - "sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858", - "sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae", - "sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34", - "sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906", - "sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd", - "sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922", - "sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7", - "sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4", - "sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574", - "sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1", - "sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c", - "sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e", - "sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.3" + "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", + "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", + "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", + "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", + "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", + "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", + "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", + "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", + "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", + "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", + "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", + "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", + "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", + "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", + "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", + "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", + "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", + "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", + "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", + "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", + "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", + "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", + "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.4" }, "dill": { "hashes": [ @@ -1788,12 +1814,12 @@ }, "fakeredis": { "hashes": [ - "sha256:bc7a82e9caec80096659fd9a810b72d21f833574be26ff7b97955db626d60bac", - "sha256:f9c18d3dba81a470953cc042868b411e334109e065cde53a7a82beef6702a1de" + "sha256:9742d6d4673df0f5f6ade4e4eee763b7f3517178ffa82508310325a6305651ec", + "sha256:d780da2519b2e9d741056cf2b68604a4e59286bc6fde78b40a2b2b1367a51b30" ], "index": "pypi", "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==2.18.0" + "version": "==2.18.1" }, "flake8": { "hashes": [ @@ -1987,8 +2013,11 @@ "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", @@ -1996,6 +2025,7 @@ "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", @@ -2004,6 +2034,7 @@ "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", @@ -2011,9 +2042,12 @@ "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", @@ -2032,7 +2066,9 @@ "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2" + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" ], "index": "pypi", "markers": "python_version >= '3.7'", @@ -2057,12 +2093,12 @@ }, "moto": { "hashes": [ - "sha256:00fbae396fc48c3596e47b4e3267c1a41ca01c968de023beb68e774c63910b58", - "sha256:e4835912f05627b6a53b938562b717122230fb038d023819133f8526f60ed0a7" + "sha256:2e934d834729b274382055e097b166127db829ab4fae00bb08c031c108391a2c", + "sha256:4caab0145d557d102fe79d0ce3b73d6bf1d916d29ad03c14da15f7da66429cdb" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==4.2.0" + "version": "==4.2.3" }, "mypy": { "hashes": [ @@ -2140,11 +2176,11 @@ }, "pluggy": { "hashes": [ - "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", - "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" ], - "markers": "python_version >= '3.7'", - "version": "==1.2.0" + "markers": "python_version >= '3.8'", + "version": "==1.3.0" }, "pycodestyle": { "hashes": [ @@ -2178,6 +2214,13 @@ "markers": "python_full_version >= '3.7.2'", "version": "==2.17.5" }, + "pylint-absolute-imports": { + "hashes": [ + "sha256:e70d96abbad2fe0bdc3dbae05e1a1b7963dea5b7df4cb63e001f7248de46acd3" + ], + "index": "pypi", + "version": "==1.0.1" + }, "pylint-mccabe": { "hashes": [ "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29" @@ -2196,12 +2239,12 @@ }, "pytest": { "hashes": [ - "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", - "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" + "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002", + "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==7.4.0" + "version": "==7.4.2" }, "pytest-cov": { "hashes": [ @@ -2257,7 +2300,9 @@ }, "pyyaml": { "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", @@ -2265,7 +2310,10 @@ "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", @@ -2273,9 +2321,12 @@ "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", @@ -2290,7 +2341,9 @@ "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", @@ -2339,106 +2392,106 @@ }, "rpds-py": { "hashes": [ - "sha256:0173c0444bec0a3d7d848eaeca2d8bd32a1b43f3d3fde6617aac3731fa4be05f", - "sha256:01899794b654e616c8625b194ddd1e5b51ef5b60ed61baa7a2d9c2ad7b2a4238", - "sha256:02938432352359805b6da099c9c95c8a0547fe4b274ce8f1a91677401bb9a45f", - "sha256:03421628f0dc10a4119d714a17f646e2837126a25ac7a256bdf7c3943400f67f", - "sha256:03975db5f103997904c37e804e5f340c8fdabbb5883f26ee50a255d664eed58c", - "sha256:0766babfcf941db8607bdaf82569ec38107dbb03c7f0b72604a0b346b6eb3298", - "sha256:07e2c54bef6838fa44c48dfbc8234e8e2466d851124b551fc4e07a1cfeb37260", - "sha256:0836d71ca19071090d524739420a61580f3f894618d10b666cf3d9a1688355b1", - "sha256:095b460e117685867d45548fbd8598a8d9999227e9061ee7f012d9d264e6048d", - "sha256:0e7521f5af0233e89939ad626b15278c71b69dc1dfccaa7b97bd4cdf96536bb7", - "sha256:0f2996fbac8e0b77fd67102becb9229986396e051f33dbceada3debaacc7033f", - "sha256:1054a08e818f8e18910f1bee731583fe8f899b0a0a5044c6e680ceea34f93876", - "sha256:13b602dc3e8dff3063734f02dcf05111e887f301fdda74151a93dbbc249930fe", - "sha256:141acb9d4ccc04e704e5992d35472f78c35af047fa0cfae2923835d153f091be", - "sha256:14c408e9d1a80dcb45c05a5149e5961aadb912fff42ca1dd9b68c0044904eb32", - "sha256:159fba751a1e6b1c69244e23ba6c28f879a8758a3e992ed056d86d74a194a0f3", - "sha256:190ca6f55042ea4649ed19c9093a9be9d63cd8a97880106747d7147f88a49d18", - "sha256:196cb208825a8b9c8fc360dc0f87993b8b260038615230242bf18ec84447c08d", - "sha256:1fcdee18fea97238ed17ab6478c66b2095e4ae7177e35fb71fbe561a27adf620", - "sha256:207f57c402d1f8712618f737356e4b6f35253b6d20a324d9a47cb9f38ee43a6b", - "sha256:24a81c177379300220e907e9b864107614b144f6c2a15ed5c3450e19cf536fae", - "sha256:29cd8bfb2d716366a035913ced99188a79b623a3512292963d84d3e06e63b496", - "sha256:2d8b3b3a2ce0eaa00c5bbbb60b6713e94e7e0becab7b3db6c5c77f979e8ed1f1", - "sha256:35da5cc5cb37c04c4ee03128ad59b8c3941a1e5cd398d78c37f716f32a9b7f67", - "sha256:44659b1f326214950a8204a248ca6199535e73a694be8d3e0e869f820767f12f", - "sha256:47c5f58a8e0c2c920cc7783113df2fc4ff12bf3a411d985012f145e9242a2764", - "sha256:4bd4dc3602370679c2dfb818d9c97b1137d4dd412230cfecd3c66a1bf388a196", - "sha256:4ea6b73c22d8182dff91155af018b11aac9ff7eca085750455c5990cb1cfae6e", - "sha256:50025635ba8b629a86d9d5474e650da304cb46bbb4d18690532dd79341467846", - "sha256:517cbf6e67ae3623c5127206489d69eb2bdb27239a3c3cc559350ef52a3bbf0b", - "sha256:5855c85eb8b8a968a74dc7fb014c9166a05e7e7a8377fb91d78512900aadd13d", - "sha256:5a46859d7f947061b4010e554ccd1791467d1b1759f2dc2ec9055fa239f1bc26", - "sha256:65a0583c43d9f22cb2130c7b110e695fff834fd5e832a776a107197e59a1898e", - "sha256:674c704605092e3ebbbd13687b09c9f78c362a4bc710343efe37a91457123044", - "sha256:682726178138ea45a0766907957b60f3a1bf3acdf212436be9733f28b6c5af3c", - "sha256:686ba516e02db6d6f8c279d1641f7067ebb5dc58b1d0536c4aaebb7bf01cdc5d", - "sha256:6a5d3fbd02efd9cf6a8ffc2f17b53a33542f6b154e88dd7b42ef4a4c0700fdad", - "sha256:6aa8326a4a608e1c28da191edd7c924dff445251b94653988efb059b16577a4d", - "sha256:700375326ed641f3d9d32060a91513ad668bcb7e2cffb18415c399acb25de2ab", - "sha256:71f2f7715935a61fa3e4ae91d91b67e571aeb5cb5d10331ab681256bda2ad920", - "sha256:745f5a43fdd7d6d25a53ab1a99979e7f8ea419dfefebcab0a5a1e9095490ee5e", - "sha256:79f594919d2c1a0cc17d1988a6adaf9a2f000d2e1048f71f298b056b1018e872", - "sha256:7d68dc8acded354c972116f59b5eb2e5864432948e098c19fe6994926d8e15c3", - "sha256:7f67da97f5b9eac838b6980fc6da268622e91f8960e083a34533ca710bec8611", - "sha256:83b32f0940adec65099f3b1c215ef7f1d025d13ff947975a055989cb7fd019a4", - "sha256:876bf9ed62323bc7dcfc261dbc5572c996ef26fe6406b0ff985cbcf460fc8a4c", - "sha256:890ba852c16ace6ed9f90e8670f2c1c178d96510a21b06d2fa12d8783a905193", - "sha256:8b08605d248b974eb02f40bdcd1a35d3924c83a2a5e8f5d0fa5af852c4d960af", - "sha256:8b2eb034c94b0b96d5eddb290b7b5198460e2d5d0c421751713953a9c4e47d10", - "sha256:8b9ec12ad5f0a4625db34db7e0005be2632c1013b253a4a60e8302ad4d462afd", - "sha256:8c8d7594e38cf98d8a7df25b440f684b510cf4627fe038c297a87496d10a174f", - "sha256:8d3335c03100a073883857e91db9f2e0ef8a1cf42dc0369cbb9151c149dbbc1b", - "sha256:8d70e8f14900f2657c249ea4def963bed86a29b81f81f5b76b5a9215680de945", - "sha256:9039a11bca3c41be5a58282ed81ae422fa680409022b996032a43badef2a3752", - "sha256:91378d9f4151adc223d584489591dbb79f78814c0734a7c3bfa9c9e09978121c", - "sha256:9251eb8aa82e6cf88510530b29eef4fac825a2b709baf5b94a6094894f252387", - "sha256:933a7d5cd4b84f959aedeb84f2030f0a01d63ae6cf256629af3081cf3e3426e8", - "sha256:978fa96dbb005d599ec4fd9ed301b1cc45f1a8f7982d4793faf20b404b56677d", - "sha256:987b06d1cdb28f88a42e4fb8a87f094e43f3c435ed8e486533aea0bf2e53d931", - "sha256:99b1c16f732b3a9971406fbfe18468592c5a3529585a45a35adbc1389a529a03", - "sha256:99e7c4bb27ff1aab90dcc3e9d37ee5af0231ed98d99cb6f5250de28889a3d502", - "sha256:9c439fd54b2b9053717cca3de9583be6584b384d88d045f97d409f0ca867d80f", - "sha256:9ea4d00850ef1e917815e59b078ecb338f6a8efda23369677c54a5825dbebb55", - "sha256:9f30d205755566a25f2ae0382944fcae2f350500ae4df4e795efa9e850821d82", - "sha256:a06418fe1155e72e16dddc68bb3780ae44cebb2912fbd8bb6ff9161de56e1798", - "sha256:a0805911caedfe2736935250be5008b261f10a729a303f676d3d5fea6900c96a", - "sha256:a1f044792e1adcea82468a72310c66a7f08728d72a244730d14880cd1dabe36b", - "sha256:a216b26e5af0a8e265d4efd65d3bcec5fba6b26909014effe20cd302fd1138fa", - "sha256:a987578ac5214f18b99d1f2a3851cba5b09f4a689818a106c23dbad0dfeb760f", - "sha256:aad51239bee6bff6823bbbdc8ad85136c6125542bbc609e035ab98ca1e32a192", - "sha256:ab2299e3f92aa5417d5e16bb45bb4586171c1327568f638e8453c9f8d9e0f020", - "sha256:ab6919a09c055c9b092798ce18c6c4adf49d24d4d9e43a92b257e3f2548231e7", - "sha256:b0c43f8ae8f6be1d605b0465671124aa8d6a0e40f1fb81dcea28b7e3d87ca1e1", - "sha256:b1440c291db3f98a914e1afd9d6541e8fc60b4c3aab1a9008d03da4651e67386", - "sha256:b52e7c5ae35b00566d244ffefba0f46bb6bec749a50412acf42b1c3f402e2c90", - "sha256:bf4151acb541b6e895354f6ff9ac06995ad9e4175cbc6d30aaed08856558201f", - "sha256:c27ee01a6c3223025f4badd533bea5e87c988cb0ba2811b690395dfe16088cfe", - "sha256:c545d9d14d47be716495076b659db179206e3fd997769bc01e2d550eeb685596", - "sha256:c5934e2833afeaf36bd1eadb57256239785f5af0220ed8d21c2896ec4d3a765f", - "sha256:c7671d45530fcb6d5e22fd40c97e1e1e01965fc298cbda523bb640f3d923b387", - "sha256:c861a7e4aef15ff91233751619ce3a3d2b9e5877e0fcd76f9ea4f6847183aa16", - "sha256:d25b1c1096ef0447355f7293fbe9ad740f7c47ae032c2884113f8e87660d8f6e", - "sha256:d55777a80f78dd09410bd84ff8c95ee05519f41113b2df90a69622f5540c4f8b", - "sha256:d576c3ef8c7b2d560e301eb33891d1944d965a4d7a2eacb6332eee8a71827db6", - "sha256:dd9da77c6ec1f258387957b754f0df60766ac23ed698b61941ba9acccd3284d1", - "sha256:de0b6eceb46141984671802d412568d22c6bacc9b230174f9e55fc72ef4f57de", - "sha256:e07e5dbf8a83c66783a9fe2d4566968ea8c161199680e8ad38d53e075df5f0d0", - "sha256:e564d2238512c5ef5e9d79338ab77f1cbbda6c2d541ad41b2af445fb200385e3", - "sha256:ed89861ee8c8c47d6beb742a602f912b1bb64f598b1e2f3d758948721d44d468", - "sha256:ef1f08f2a924837e112cba2953e15aacfccbbfcd773b4b9b4723f8f2ddded08e", - "sha256:f411330a6376fb50e5b7a3e66894e4a39e60ca2e17dce258d53768fea06a37bd", - "sha256:f68996a3b3dc9335037f82754f9cdbe3a95db42bde571d8c3be26cc6245f2324", - "sha256:f7fdf55283ad38c33e35e2855565361f4bf0abd02470b8ab28d499c663bc5d7c", - "sha256:f963c6b1218b96db85fc37a9f0851eaf8b9040aa46dec112611697a7023da535", - "sha256:fa2818759aba55df50592ecbc95ebcdc99917fa7b55cc6796235b04193eb3c55", - "sha256:fae5cb554b604b3f9e2c608241b5d8d303e410d7dfb6d397c335f983495ce7f6", - "sha256:fb39aca7a64ad0c9490adfa719dbeeb87d13be137ca189d2564e596f8ba32c07" + "sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9", + "sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637", + "sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5", + "sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9", + "sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c", + "sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957", + "sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6", + "sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154", + "sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8", + "sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569", + "sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2", + "sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c", + "sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453", + "sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1", + "sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7", + "sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b", + "sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9", + "sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496", + "sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3", + "sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b", + "sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6", + "sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da", + "sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842", + "sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd", + "sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294", + "sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71", + "sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093", + "sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e", + "sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c", + "sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e", + "sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b", + "sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97", + "sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac", + "sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf", + "sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a", + "sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc", + "sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0", + "sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623", + "sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6", + "sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8", + "sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b", + "sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d", + "sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff", + "sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb", + "sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08", + "sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33", + "sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5", + "sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a", + "sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a", + "sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762", + "sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860", + "sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73", + "sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a", + "sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065", + "sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c", + "sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c", + "sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec", + "sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515", + "sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72", + "sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd", + "sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec", + "sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12", + "sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1", + "sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557", + "sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25", + "sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff", + "sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8", + "sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9", + "sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d", + "sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a", + "sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e", + "sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c", + "sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0", + "sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48", + "sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e", + "sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314", + "sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717", + "sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95", + "sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8", + "sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57", + "sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33", + "sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1", + "sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f", + "sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f", + "sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9", + "sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599", + "sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41", + "sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391", + "sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475", + "sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882", + "sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740", + "sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f", + "sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee", + "sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836", + "sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c", + "sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a", + "sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb" ], "markers": "python_version >= '3.8'", - "version": "==0.9.2" + "version": "==0.10.3" }, "s3transfer": { "hashes": [ @@ -2465,11 +2518,11 @@ }, "soupsieve": { "hashes": [ - "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8", - "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea" + "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", + "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" ], - "markers": "python_version >= '3.7'", - "version": "==2.4.1" + "markers": "python_version >= '3.8'", + "version": "==2.5" }, "termcolor": { "hashes": [ @@ -2512,11 +2565,11 @@ }, "types-pytz": { "hashes": [ - "sha256:1a7b8d4aac70981cfa24478a41eadfcd96a087c986d6f150d77e3ceb3c2bdfab", - "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e" + "sha256:8e7d2198cba44a72df7628887c90f68a568e1445f14db64631af50c3cab8c090", + "sha256:a660a38ed86d45970603e4f3b4877c7ba947668386a896fb5d9589c17e7b8407" ], "index": "pypi", - "version": "==2023.3.0.1" + "version": "==2023.3.1.0" }, "types-pyyaml": { "hashes": [ @@ -2527,19 +2580,19 @@ }, "types-redis": { "hashes": [ - "sha256:4f662060247a2363c7a8f0b7e52915d68960870ff16a749a891eabcf87ed0be4", - "sha256:5f179d10bd3ca995a8134aafcddfc3e12d52b208437c4529ef27e68acb301f38" + "sha256:7865a843802937ab2ddca33579c4e255bfe73f87af85824ead7a6729ba92fc52", + "sha256:e0e9dcc530623db3a41ec058ccefdcd5c7582557f02ab5f7aa9a27fe10a78d7e" ], "index": "pypi", - "version": "==4.6.0.5" + "version": "==4.6.0.6" }, "types-requests": { "hashes": [ - "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a", - "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40" + "sha256:938f51653c757716aeca5d72c405c5e2befad8b0d330e3b385ce7f148e1b10dc", + "sha256:d5d7a08965fca12bedf716eaf5430c6e3d0da9f3164a1dba2a7f3885f9ebe3c0" ], "index": "pypi", - "version": "==2.31.0.2" + "version": "==2.31.0.3" }, "types-simplejson": { "hashes": [ @@ -2558,11 +2611,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", - "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2" + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], - "markers": "python_version >= '3.7'", - "version": "==4.7.1" + "markers": "python_version >= '3.8'", + "version": "==4.8.0" }, "urllib3": { "hashes": [ diff --git a/app/cloud_tasks/__init__.py b/app/cloud_tasks/__init__.py index 325f39ba16..5f92f8e9b8 100644 --- a/app/cloud_tasks/__init__.py +++ b/app/cloud_tasks/__init__.py @@ -1,3 +1,6 @@ -from .cloud_task_publishers import CloudTaskPublisher, LogCloudTaskPublisher +from app.cloud_tasks.cloud_task_publishers import ( + CloudTaskPublisher, + LogCloudTaskPublisher, +) __all__ = ["CloudTaskPublisher", "LogCloudTaskPublisher"] diff --git a/app/cloud_tasks/cloud_task_publishers.py b/app/cloud_tasks/cloud_task_publishers.py index 32a7212f9e..a8c02b1958 100644 --- a/app/cloud_tasks/cloud_task_publishers.py +++ b/app/cloud_tasks/cloud_task_publishers.py @@ -4,7 +4,7 @@ from google.cloud.tasks_v2.types.task import Task from structlog import get_logger -from .exceptions import CloudTaskCreationFailed +from app.cloud_tasks.exceptions import CloudTaskCreationFailed logger = get_logger(__name__) diff --git a/app/data_models/__init__.py b/app/data_models/__init__.py index ae4c7beed9..cbe2c9a282 100644 --- a/app/data_models/__init__.py +++ b/app/data_models/__init__.py @@ -1,15 +1,15 @@ -from .answer import Answer, AnswerValueTypes -from .fulfilment_request import FulfilmentRequest -from .progress import CompletionStatus -from .questionnaire_store import ( +from app.data_models.answer import Answer, AnswerValueTypes +from app.data_models.fulfilment_request import FulfilmentRequest +from app.data_models.progress import CompletionStatus +from app.data_models.questionnaire_store import ( AnswerStore, ListStore, ProgressStore, QuestionnaireStore, SupplementaryDataStore, ) -from .session_data import SessionData -from .session_store import SessionStore +from app.data_models.session_data import SessionData +from app.data_models.session_store import SessionStore __all__ = [ "Answer", diff --git a/app/forms/__init__.py b/app/forms/__init__.py index edbeed9d72..2ec81a73fe 100644 --- a/app/forms/__init__.py +++ b/app/forms/__init__.py @@ -1,3 +1,3 @@ -from .error_messages import error_messages +from app.forms.error_messages import error_messages __all__ = ["error_messages"] diff --git a/app/forms/fields/__init__.py b/app/forms/fields/__init__.py index 240938d377..65c1e9c936 100644 --- a/app/forms/fields/__init__.py +++ b/app/forms/fields/__init__.py @@ -12,9 +12,11 @@ from app.forms.fields.multiple_select_field_with_detail_answer import ( MultipleSelectFieldWithDetailAnswer, ) - -from .select_field_with_detail_answer import SelectField, SelectFieldWithDetailAnswer -from .year_date_field import YearDateField +from app.forms.fields.select_field_with_detail_answer import ( + SelectField, + SelectFieldWithDetailAnswer, +) +from app.forms.fields.year_date_field import YearDateField __all__ = [ "DateField", diff --git a/app/helpers/__init__.py b/app/helpers/__init__.py index 0452bc1b3b..560a62b61c 100644 --- a/app/helpers/__init__.py +++ b/app/helpers/__init__.py @@ -1,6 +1,6 @@ -from .address_lookup_api_helper import get_address_lookup_api_auth_token -from .header_helpers import get_span_and_trace -from .url_safe_serializer import url_safe_serializer +from app.helpers.address_lookup_api_helper import get_address_lookup_api_auth_token +from app.helpers.header_helpers import get_span_and_trace +from app.helpers.url_safe_serializer import url_safe_serializer __all__ = [ "get_span_and_trace", diff --git a/app/publisher/__init__.py b/app/publisher/__init__.py index e18d0cdf4a..78e63544d1 100644 --- a/app/publisher/__init__.py +++ b/app/publisher/__init__.py @@ -1,3 +1,3 @@ -from .publisher import LogPublisher, PubSubPublisher +from app.publisher.publisher import LogPublisher, PubSubPublisher __all__ = ["PubSubPublisher", "LogPublisher"] diff --git a/app/questionnaire/__init__.py b/app/questionnaire/__init__.py index 2fb3f32d83..40eeb20605 100644 --- a/app/questionnaire/__init__.py +++ b/app/questionnaire/__init__.py @@ -1,4 +1,7 @@ -from .location import Location -from .questionnaire_schema import QuestionnaireSchema, QuestionSchemaType +from app.questionnaire.location import Location +from app.questionnaire.questionnaire_schema import ( + QuestionnaireSchema, + QuestionSchemaType, +) __all__ = ["QuestionnaireSchema", "Location", "QuestionSchemaType"] diff --git a/app/storage/__init__.py b/app/storage/__init__.py index 70f8db306e..bada4fbfe3 100644 --- a/app/storage/__init__.py +++ b/app/storage/__init__.py @@ -1,5 +1,5 @@ -from .datastore import Datastore -from .dynamodb import Dynamodb -from .redis import Redis +from app.storage.datastore import Datastore +from app.storage.dynamodb import Dynamodb +from app.storage.redis import Redis __all__ = ["Datastore", "Dynamodb", "Redis"] diff --git a/app/storage/dynamodb.py b/app/storage/dynamodb.py index c9eb4c7184..567ab6ed43 100644 --- a/app/storage/dynamodb.py +++ b/app/storage/dynamodb.py @@ -4,8 +4,7 @@ from botocore.exceptions import ClientError from app.storage.errors import ItemAlreadyExistsError - -from .storage import ModelTypes, StorageHandler, StorageModel +from app.storage.storage import ModelTypes, StorageHandler, StorageModel class Dynamodb(StorageHandler): diff --git a/app/storage/redis.py b/app/storage/redis.py index 2253caf16f..74e0dd1f83 100644 --- a/app/storage/redis.py +++ b/app/storage/redis.py @@ -6,10 +6,9 @@ from structlog import get_logger from app.storage.errors import ItemAlreadyExistsError +from app.storage.storage import ModelTypes, StorageHandler, StorageModel from app.utilities.json import json_dumps, json_loads -from .storage import ModelTypes, StorageHandler, StorageModel - logger = get_logger() diff --git a/app/submitter/__init__.py b/app/submitter/__init__.py index 1e32035a9d..ea3b9d76a8 100644 --- a/app/submitter/__init__.py +++ b/app/submitter/__init__.py @@ -1,4 +1,4 @@ -from .submitter import ( +from app.submitter.submitter import ( GCSFeedbackSubmitter, GCSSubmitter, LogFeedbackSubmitter, diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 36393c2636..56af71eee7 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -1,4 +1,4 @@ -from .business_config import ( +from app.survey_config.business_config import ( BusinessSurveyConfig, DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, @@ -9,14 +9,14 @@ NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) -from .census_config import ( +from app.survey_config.census_config import ( CensusNISRASurveyConfig, CensusSurveyConfig, WelshCensusSurveyConfig, ) -from .link import Link -from .social_survey_config import SocialSurveyConfig -from .survey_config import SurveyConfig +from app.survey_config.link import Link +from app.survey_config.social_survey_config import SocialSurveyConfig +from app.survey_config.survey_config import SurveyConfig __all__ = [ "SocialSurveyConfig", diff --git a/app/utilities/__init__.py b/app/utilities/__init__.py index 388b2b1a34..23599a169c 100644 --- a/app/utilities/__init__.py +++ b/app/utilities/__init__.py @@ -1,3 +1,3 @@ -from .strings import safe_content +from app.utilities.strings import safe_content __all__ = ["safe_content"] diff --git a/app/views/contexts/__init__.py b/app/views/contexts/__init__.py index 1acb951d53..e6fbda5740 100644 --- a/app/views/contexts/__init__.py +++ b/app/views/contexts/__init__.py @@ -1,10 +1,12 @@ -from .calculated_summary_context import CalculatedSummaryContext -from .context import Context -from .grand_calculated_summary_context import GrandCalculatedSummaryContext -from .hub_context import HubContext -from .list_context import ListContext -from .section_summary_context import SectionSummaryContext -from .submit_questionnaire_context import SubmitQuestionnaireContext +from app.views.contexts.calculated_summary_context import CalculatedSummaryContext +from app.views.contexts.context import Context +from app.views.contexts.grand_calculated_summary_context import ( + GrandCalculatedSummaryContext, +) +from app.views.contexts.hub_context import HubContext +from app.views.contexts.list_context import ListContext +from app.views.contexts.section_summary_context import SectionSummaryContext +from app.views.contexts.submit_questionnaire_context import SubmitQuestionnaireContext __all__ = [ "CalculatedSummaryContext", diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 1c2f4254fd..90b3e5f94c 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -9,15 +9,14 @@ ProgressStore, SupplementaryDataStore, ) +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.questionnaire_schema import LIST_COLLECTORS_WITH_REPEATING_BLOCKS from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content - -from ...data_models.metadata_proxy import MetadataProxy -from .context import Context -from .summary import Group -from .summary.list_collector_block import ListCollectorBlock +from app.views.contexts.context import Context +from app.views.contexts.summary.group import Group +from app.views.contexts.summary.list_collector_block import ListCollectorBlock class SectionSummaryContext(Context): diff --git a/app/views/contexts/submit_questionnaire_context.py b/app/views/contexts/submit_questionnaire_context.py index 6729aa6284..d19450fa03 100644 --- a/app/views/contexts/submit_questionnaire_context.py +++ b/app/views/contexts/submit_questionnaire_context.py @@ -2,8 +2,8 @@ from flask_babel import lazy_gettext -from .context import Context -from .summary_context import SummaryContext +from app.views.contexts.context import Context +from app.views.contexts.summary_context import SummaryContext class SubmitQuestionnaireContext(Context): diff --git a/app/views/contexts/summary/__init__.py b/app/views/contexts/summary/__init__.py index 2019c3b40b..e9aa8cc742 100644 --- a/app/views/contexts/summary/__init__.py +++ b/app/views/contexts/summary/__init__.py @@ -1,3 +1,3 @@ -from .group import Group +from app.views.contexts.summary.group import Group __all__ = ["Group"] diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index f103e7ffeb..ab187cacc9 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,12 +1,16 @@ from typing import MutableMapping +from app.data_models import ( + AnswerStore, + ListStore, + ProgressStore, + SupplementaryDataStore, +) +from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire.location import Location - -from ...data_models import AnswerStore, ListStore, ProgressStore, SupplementaryDataStore -from ...data_models.metadata_proxy import MetadataProxy -from ...questionnaire import QuestionnaireSchema -from .context import Context -from .section_summary_context import SectionSummaryContext +from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.views.contexts.context import Context +from app.views.contexts.section_summary_context import SectionSummaryContext class SummaryContext(Context): diff --git a/app/views/handlers/__init__.py b/app/views/handlers/__init__.py index 493419cb39..6b9ab0bd99 100644 --- a/app/views/handlers/__init__.py +++ b/app/views/handlers/__init__.py @@ -1,8 +1,7 @@ from flask import url_for from app.data_models import QuestionnaireStore - -from .individual_response import ( +from app.views.handlers.individual_response import ( IndividualResponseHandler, IndividualResponseHowHandler, IndividualResponsePostAddressConfirmHandler, diff --git a/app/views/handlers/relationships/__init__.py b/app/views/handlers/relationships/__init__.py index f1cb95729a..2aa6662dc1 100644 --- a/app/views/handlers/relationships/__init__.py +++ b/app/views/handlers/relationships/__init__.py @@ -1,4 +1,6 @@ -from .relationship_collector import RelationshipCollector -from .unrelated_question import UnrelatedQuestion +from app.views.handlers.relationships.relationship_collector import ( + RelationshipCollector, +) +from app.views.handlers.relationships.unrelated_question import UnrelatedQuestion __all__ = ["RelationshipCollector", "UnrelatedQuestion"] diff --git a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py index 5c144693f0..da3f157214 100644 --- a/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py +++ b/tests/app/views/handlers/test_confirmation_email_fulfilment_request.py @@ -2,8 +2,7 @@ from app.utilities.json import json_loads from app.views.handlers.confirm_email import ConfirmationEmailFulfilmentRequest - -from .conftest import time_to_freeze +from tests.app.views.handlers.conftest import time_to_freeze @freeze_time(time_to_freeze) diff --git a/tests/app/views/handlers/test_feedback_upload.py b/tests/app/views/handlers/test_feedback_upload.py index 89904b4119..35e5a32709 100644 --- a/tests/app/views/handlers/test_feedback_upload.py +++ b/tests/app/views/handlers/test_feedback_upload.py @@ -9,8 +9,7 @@ FeedbackPayload, FeedbackPayloadV2, ) - -from .conftest import ( +from tests.app.views.handlers.conftest import ( case_id, case_ref, case_type, diff --git a/tests/app/views/handlers/test_preview_questions_pdf.py b/tests/app/views/handlers/test_preview_questions_pdf.py index 1b4cd98868..bc8e4a21c4 100644 --- a/tests/app/views/handlers/test_preview_questions_pdf.py +++ b/tests/app/views/handlers/test_preview_questions_pdf.py @@ -3,8 +3,7 @@ from app.data_models import QuestionnaireStore from app.views.contexts.preview_context import PreviewNotEnabledException from app.views.handlers.preview_questions_pdf import PreviewQuestionsPDF - -from .conftest import set_storage_data +from tests.app.views.handlers.conftest import set_storage_data @pytest.mark.usefixtures("app") diff --git a/tests/app/views/handlers/test_question_with_dynamic_answers.py b/tests/app/views/handlers/test_question_with_dynamic_answers.py index 22beb7ffbc..ac02eda5b7 100644 --- a/tests/app/views/handlers/test_question_with_dynamic_answers.py +++ b/tests/app/views/handlers/test_question_with_dynamic_answers.py @@ -7,9 +7,8 @@ from app.questionnaire import Location from app.utilities.schema import load_schema_from_name from app.views.handlers.question import Question - -from ...parser.conftest import get_response_expires_at -from .conftest import set_storage_data +from tests.app.parser.conftest import get_response_expires_at +from tests.app.views.handlers.conftest import set_storage_data @pytest.mark.usefixtures("app") diff --git a/tests/app/views/handlers/test_view_preview_questions.py b/tests/app/views/handlers/test_view_preview_questions.py index 423ab6ec62..db87dbba1f 100644 --- a/tests/app/views/handlers/test_view_preview_questions.py +++ b/tests/app/views/handlers/test_view_preview_questions.py @@ -8,8 +8,7 @@ from app.views.contexts.preview_context import PreviewNotEnabledException from app.views.handlers.preview_questions_pdf import PreviewQuestionsPDF from app.views.handlers.view_preview_questions import ViewPreviewQuestions - -from .conftest import set_storage_data +from tests.app.views.handlers.conftest import set_storage_data @pytest.mark.usefixtures("app") diff --git a/tests/app/views/handlers/test_view_submitted_response_pdf.py b/tests/app/views/handlers/test_view_submitted_response_pdf.py index e031c30f62..f046491d9a 100644 --- a/tests/app/views/handlers/test_view_submitted_response_pdf.py +++ b/tests/app/views/handlers/test_view_submitted_response_pdf.py @@ -6,8 +6,7 @@ from app.data_models import QuestionnaireStore from app.views.handlers.view_submitted_response import ViewSubmittedResponseExpired from app.views.handlers.view_submitted_response_pdf import ViewSubmittedResponsePDF - -from .conftest import set_storage_data +from tests.app.views.handlers.conftest import set_storage_data @pytest.mark.usefixtures("app") diff --git a/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py b/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py index 54cc9292f9..637988aa48 100644 --- a/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py +++ b/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireCustomPageTitles(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 02f08ff857..6385968d6a 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireGrandCalculatedSummary(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py index 8110c9fac1..b3384d45a6 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py +++ b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireListChangeEvaluatesSections(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector.py b/tests/integration/questionnaire/test_questionnaire_list_collector.py index bf04bdac0c..c54717f434 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector.py @@ -1,4 +1,4 @@ -from . import SUBMIT_URL_PATH, QuestionnaireTestCase +from tests.integration.questionnaire import SUBMIT_URL_PATH, QuestionnaireTestCase # pylint: disable=too-many-public-methods diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py index 120ba70755..0f35bdfda1 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase # pylint: disable=too-many-public-methods diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py b/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py index 49750bb9d9..f8c6733397 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py @@ -2,7 +2,7 @@ import re import string -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireListCollector(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py index 5bfe207f5a..c54b543107 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py @@ -1,6 +1,6 @@ from datetime import date -from ..integration_test_case import IntegrationTestCase +from tests.integration.integration_test_case import IntegrationTestCase # pylint: disable=too-many-public-methods diff --git a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py index 05b92b001a..1d84f07220 100644 --- a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py +++ b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireListCollector(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py index d47564c80b..bd22511d3f 100644 --- a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py +++ b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireListCollector(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_relationships.py b/tests/integration/questionnaire/test_questionnaire_relationships.py index 716110265e..361c9fff62 100644 --- a/tests/integration/questionnaire/test_questionnaire_relationships.py +++ b/tests/integration/questionnaire/test_questionnaire_relationships.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireRelationships(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py index b073f3d434..a0fd44c4b7 100644 --- a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py +++ b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py @@ -1,4 +1,4 @@ -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireRelationshipsUnrelated(QuestionnaireTestCase): diff --git a/tests/integration/questionnaire/test_questionnaire_same_name_items.py b/tests/integration/questionnaire/test_questionnaire_same_name_items.py index 94d182321d..133fff2adf 100644 --- a/tests/integration/questionnaire/test_questionnaire_same_name_items.py +++ b/tests/integration/questionnaire/test_questionnaire_same_name_items.py @@ -1,6 +1,5 @@ from app.utilities.json import json_loads - -from . import QuestionnaireTestCase +from tests.integration.questionnaire import QuestionnaireTestCase class TestQuestionnaireSameNameItems(QuestionnaireTestCase): From 9d76ffe56f22d2461a5fa6c72f2b29754be131e3 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:24:20 +0100 Subject: [PATCH 294/567] Update type hint readme for return any cases (#1211) --- doc/python-type-hinting.md | 41 +++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/doc/python-type-hinting.md b/doc/python-type-hinting.md index 42f782e4a3..6b8b559b9c 100644 --- a/doc/python-type-hinting.md +++ b/doc/python-type-hinting.md @@ -138,7 +138,7 @@ of a file, it silences all errors in the file: # type: ignore ``` -`# type: ignore` should only be used when unavoidable. Ensure that a comment is added to explain why it has been used and have a prefix of `Type ignore:` +`# type: ignore` should only be used when unavoidable (or in the return `Any` case detailed below). Ensure that a comment is added to explain why it has been used and have a prefix of `Type ignore:` ```python def format_number(number: int) -> str: @@ -147,6 +147,45 @@ def format_number(number: int) -> str: return formatted_number ``` +The `warn_return_any` flag is turned on to force type hinting the return types for third party libraries and increase the safety of the code base. + +Where type hints aren’t specific enough to identify the return type (e.g. objects like blocks where some keys correspond to strings, others to lists, others to dicts) mypy will complain if you assume the type of any attribute: + +```python +def get_id_from_block(block: dict) -> str: + return block["id"] # Returning Any from function declared to return "str" +``` + +A type ignore can be avoided here, by changing the code to this... + +```python +def get_id_from_block(block: dict) -> str: + block_id: str = block["id"] + return block_id +``` + +...but as this is a common pattern in a number of places, it results in a lot of duplicating the return type, and extra lines of code for the sake of type hinting. In this scenario, it is ok to type ignore it. + +If the value was needed for any other checks e.g. + +```python +def get_first_answer_from_block(block: dict) -> str: + answer = ... + if answer["id"] ... : + ... + return answer +``` + +This would not be suitable to type ignore, and it should use the existing convention of typing the unknown variable: + +```python +def get_first_answer_from_block(block: dict) -> str: + answer: Answer = ... + if answer["id"] ... : + ... + return answer +``` + ## ParamSpec Use to forward the parameter types of one callable to another callable. From 21204d0514caa711b3ad1457f0010b3c1272c24f Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:17:18 +0100 Subject: [PATCH 295/567] Fix functional tests to reflect url change (#1216) --- tests/functional/spec/exit.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/spec/exit.spec.js b/tests/functional/spec/exit.spec.js index fbfd493d29..6289fdf63c 100644 --- a/tests/functional/spec/exit.spec.js +++ b/tests/functional/spec/exit.spec.js @@ -20,7 +20,7 @@ describe("Post submission exit", () => { await click(SubmitPage.submit()); await click(HubPage.submit()); await $(CensusThankYouPage.exit()).click(); - await expect(await browser.getUrl()).to.equal("https://census.gov.uk/en/start"); + await expect(await browser.getUrl()).to.equal("https://www.ons.gov.uk/census"); }); it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", async () => { From fc014d5d13e30e864b6366578e23649c7aaf8b3b Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:21:23 +0100 Subject: [PATCH 296/567] Update runner Readme to warn about installing multiple python versions (#1212) --- README.md | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75f4663a7e..e1b694724c 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ brew install snappy npm pyenv jq wkhtmltopdf ### Setup +#### Application version + Create `.application-version` for local development This file is automatically created and populated with the git revision id during CI for anything other than development, @@ -58,12 +60,32 @@ to `local` removes the implication that any particular revision is used when run ``` shell echo "local" > .application-version ``` +#### Python version It is preferable to use the version of Python locally that matches that used on deployment. This project has a `.python_version` file for this purpose. -Upgrade pip and install dependencies: +#### Pyenv + +It is recommended to install the `pyenv` Python version management tool to easily switch between Python versions. +To install `pyenv` use this command: +```shell +curl https://pyenv.run | bash +``` +After the installation it should tell you to execute a command to add `pyenv` to path. It should look something like this: +```shell +export PYENV_ROOT="$HOME/.pyenv" + +command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" + +eval "$(pyenv init -)" +``` +Python versions can be changed with the `pyenv local` or `pyenv global` commands suffixed with the desired version (e.g. 3.11.4). Different versions of Python can be installed first with the `pyenv install` command. Refer to the pyenv project Readme [here](https://github.com/pyenv/pyenv). To avoid confusion, check the current Python version at any given time using `python --version` or `python3 --version`. + +#### Python & dependencies + +Inside the project directory install python version, upgrade pip and install dependencies: ``` shell pyenv install @@ -76,18 +98,24 @@ pipenv install --dev CPPFLAGS="-I/usr/local/include -L/usr/local/lib" pipenv install --dev ``` +#### Design system templates + To update the design system templates run: ``` shell make load-design-system-templates ``` +#### Schemas + To download the latest schemas from the [Questionnaire Registry](https://github.com/ONSdigital/eq-questionnaire-schemas): ``` shell make load-schemas ``` +#### Run server + Run the server inside the virtual env created by Pipenv with: ``` shell @@ -162,10 +190,14 @@ Or set the `GOOGLE_CLOUD_PROJECT` environment variable to your gcp project id. ## Frontend Tests -The frontend tests use NodeJS to run. You will need to have node version 14.X to run these tests. To do this, do the following commands: +The frontend tests use NodeJS to run. To handle different versions of NodeJS it is recommended to install `Node Version Manager` (`nvm`). It is similar to pyenv but for Node versions. +To install `nvm` use the command below (make sure to replace "v0.39.5" with the current latest version in [releases](https://github.com/nvm-sh/nvm/releases/): +``` shell +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash +``` +You will need to have the correct node version installed to run the tests. To do this, use the following commands: ``` shell -brew install nvm nvm install nvm use ``` From 5f14275bea14b70ba36f7a0655b562986b06c19c Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:55:26 +0100 Subject: [PATCH 297/567] Schemas v3.72.0 (#1218) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 7deeecce8a..800e0bcf9f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.71.0 +v3.72.0 From f0a7de8c894706bf2f15da03b0f20879a66d83aa Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 2 Oct 2023 15:59:16 +0100 Subject: [PATCH 298/567] Add calculated summary negative demo (#1219) * add an extra variable to verify values are correct on the summary page * test signed commits * verify that all values in the summary are correct * add tests that were temporarily removed * Add a minimum value to the schema for the first answer * add tests to verify negative values are accepted in the first answer * Add a minimum value to the schema for the second answers * add tests to verify negative values are accepted by the second answers * Add a minimum value to the schema for the third answer * add tests to verify negative values are accepted by the third answer * add tests to verify negative values are accepted in all answer sections --------- Co-authored-by: Viraj Patel --- schemas/test/en/test_calculated_summary.json | 24 ++++++++-- .../test/en/test_new_calculated_summary.json | 24 ++++++++-- .../calculated_summary.spec.js | 27 +++++++++++ .../calculated_summary_test_case.js | 45 ++++++++++++++++++- 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 199a993cff..90a0524635 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -51,7 +51,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } @@ -70,7 +74,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } }, { "id": "second-number-answer-unit-total", @@ -86,7 +94,11 @@ "mandatory": false, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } @@ -105,7 +117,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index 98b77580d7..cb70fbf9de 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -51,7 +51,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } @@ -70,7 +74,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } }, { "id": "second-number-answer-unit-total", @@ -86,7 +94,11 @@ "mandatory": false, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } @@ -105,7 +117,11 @@ "mandatory": true, "type": "Currency", "currency": "GBP", - "decimal_places": 2 + "decimal_places": 2, + "minimum": { + "value": -1000, + "exclusive": false + } } ] } diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js index 5926b120d4..76d9aa14e8 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js @@ -15,3 +15,30 @@ describe("Feature: Calculated Summary", () => { CalculatedSummaryTestCase.testCrossSectionDependencies("test_new_calculated_summary_cross_section_dependencies.json"); }); }); + +describe("Feature: Calculated Summary with negative values", () => { + describe("Given I enter a negative value in the first section", () => { + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, 2, 3, 0, "£4.00", ["-£1.00", "£2.00", "£3.00", "£0.00"]); + }); + describe("Given I enter a negative value in the second section ", () => { + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 12, -2, 1, 0, "£11.00", ["£12.00", "-£2.00", "£1.00", "£0.00"]); + }); + describe("Given I enter a negative value in the third section ", () => { + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 3, 2, -6, 0, "-£1.00", ["£3.00", "£2.00", "-£6.00", "£0.00"]); + }); + describe("Given I enter negative values in every currency field ", () => { + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, -2, -3, 0, "-£6.00", ["-£1.00", "-£2.00", "-£3.00", "£0.00"]); + }); + describe("Given I enter a negative value in the first section", () => { + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, 2, 3, 0, "£4.00", ["-£1.00", "£2.00", "£3.00", "£0.00"]); + }); + describe("Given I enter a negative value in the second section ", () => { + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 12, -2, 1, 0, "£11.00", ["£12.00", "-£2.00", "£1.00", "£0.00"]); + }); + describe("Given I enter a negative value in the third section ", () => { + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 3, 2, -6, 0, "-£1.00", ["£3.00", "£2.00", "-£6.00", "£0.00"]); + }); + describe("Given I enter negative values in every currency field ", () => { + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, -2, -3, 0, "-£6.00", ["-£1.00", "-£2.00", "-£3.00", "£0.00"]); + }); +}); diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index 42c7a84859..2514aa9a6d 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -27,7 +27,7 @@ import SectionSummarySectionOne from "../../../generated_pages/calculated_summar import SectionSummarySectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; -import { click } from "../../../helpers"; +import { assertSummaryValues, click } from "../../../helpers"; import { expect } from "@wdio/globals"; class TestCase { @@ -422,6 +422,49 @@ class TestCase { ); }); } + + testNegative(schema, firstAnswerValue, secondAnswerValue, thirdAnswerValue, fourthAnswerValue, expectedTotalValue, expectedAnswerValues) { + before("Get to Calculated Summary", async () => { + await browser.openQuestionnaire(schema); + + await $(FirstNumberBlockPage.firstNumber()).setValue(firstAnswerValue); + await click(FirstNumberBlockPage.submit()); + + await $(SecondNumberBlockPage.secondNumber()).setValue(secondAnswerValue); + await $(SecondNumberBlockPage.secondNumberUnitTotal()).setValue(789); + await $(SecondNumberBlockPage.secondNumberAlsoInTotal()).setValue(0); + await click(SecondNumberBlockPage.submit()); + + await $(ThirdNumberBlockPage.thirdNumber()).setValue(thirdAnswerValue); + await click(ThirdNumberBlockPage.submit()); + await $(ThirdAndAHalfNumberBlockPage.thirdAndAHalfNumberUnitTotal()).setValue(678); + await click(ThirdAndAHalfNumberBlockPage.submit()); + + await $(SkipFourthBlockPage.no()).click(); + await click(SkipFourthBlockPage.submit()); + + await $(FourthNumberBlockPage.fourthNumber()).setValue(fourthAnswerValue); + await click(FourthNumberBlockPage.submit()); + await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(0); + await click(FourthAndAHalfNumberBlockPage.submit()); + + await $(FifthNumberBlockPage.fifthPercent()).setValue(56); + await $(FifthNumberBlockPage.fifthNumber()).setValue(78.91); + await click(FifthNumberBlockPage.submit()); + + await $(SixthNumberBlockPage.sixthPercent()).setValue(23); + await $(SixthNumberBlockPage.sixthNumber()).setValue(45); + await click(SixthNumberBlockPage.submit()); + + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + }); + it("Given I have entered a range of positive and negative values, When I reach the calculated summary, Then the total is correct", async () => { + assertSummaryValues(expectedAnswerValues); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + `We calculate the total of currency values entered to be ${expectedTotalValue}. Is this correct?`, + ); + }); + } } export const CalculatedSummaryTestCase = new TestCase(); From 003377fa7a3bf443c67d04edba25e8267144fb3d Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:18:04 +0100 Subject: [PATCH 299/567] Schemas v3.73.0 (#1221) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 800e0bcf9f..865d40f9b1 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.72.0 +v3.73.0 From 09b1c229773e276510a20636f1a51a47d95e7b8b Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 4 Oct 2023 12:31:53 +0100 Subject: [PATCH 300/567] Add type hints to fields module (#1214) --- app/forms/field_handlers/address_handler.py | 3 +- app/forms/field_handlers/date_handlers.py | 26 ++++++++-------- app/forms/field_handlers/dropdown_handler.py | 10 +++--- app/forms/field_handlers/duration_handler.py | 3 +- .../field_handlers/mobile_number_handler.py | 3 +- app/forms/field_handlers/number_handler.py | 3 +- app/forms/field_handlers/select_handlers.py | 17 ++++------ app/forms/field_handlers/string_handler.py | 3 +- app/forms/field_handlers/text_area_handler.py | 3 +- app/forms/fields/date_field.py | 31 ++++++++++++++----- .../fields/decimal_field_with_separator.py | 7 +++-- .../fields/integer_field_with_separator.py | 8 +++-- app/forms/fields/max_text_area_field.py | 12 +++++-- app/forms/fields/month_year_date_field.py | 31 ++++++++++++++----- ...ultiple_select_field_with_detail_answer.py | 22 ++++++++++--- .../fields/select_field_with_detail_answer.py | 24 ++++++++++---- app/forms/fields/year_date_field.py | 31 ++++++++++++++----- app/forms/validators.py | 2 +- app/utilities/types.py | 24 ++++++++++++++ mypy.ini | 2 +- tests/app/forms/field_handlers/conftest.py | 2 +- tests/app/forms/fields/test_date_field.py | 9 +++--- .../fields/test_month_year_date_field.py | 9 +++--- .../app/forms/fields/test_year_date_field.py | 9 +++--- tests/app/forms/test_custom_fields.py | 14 +++++++-- 25 files changed, 215 insertions(+), 93 deletions(-) diff --git a/app/forms/field_handlers/address_handler.py b/app/forms/field_handlers/address_handler.py index 5c3baa3ec3..2ccd5c3985 100644 --- a/app/forms/field_handlers/address_handler.py +++ b/app/forms/field_handlers/address_handler.py @@ -1,6 +1,7 @@ from functools import cached_property from wtforms import FormField +from wtforms.fields.core import UnboundField from wtforms.validators import InputRequired from app.forms.address_form import AddressValidatorTypes, get_address_form @@ -28,7 +29,7 @@ def validators(self) -> AddressValidatorTypes: return validate_with - def get_field(self) -> FormField: + def get_field(self) -> UnboundField | FormField: return FormField( get_address_form(self.validators), label=self.label, diff --git a/app/forms/field_handlers/date_handlers.py b/app/forms/field_handlers/date_handlers.py index 5ea2d6bc1d..6330329aa4 100644 --- a/app/forms/field_handlers/date_handlers.py +++ b/app/forms/field_handlers/date_handlers.py @@ -1,8 +1,9 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Optional, Union +from typing import Any, Optional from dateutil.relativedelta import relativedelta +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import DateField, MonthYearDateField, YearDateField @@ -14,10 +15,7 @@ format_message_with_title, ) from app.questionnaire.rules.utils import parse_datetime - -DateValidatorTypes = list[ - Union[OptionalForm, DateRequired, DateCheck, SingleDatePeriodCheck] -] +from app.utilities.types import DateValidatorType class DateHandler(FieldHandler): @@ -26,8 +24,8 @@ class DateHandler(FieldHandler): DISPLAY_FORMAT = "d MMMM yyyy" @cached_property - def validators(self) -> DateValidatorTypes: - validate_with: DateValidatorTypes = [OptionalForm()] + def validators(self) -> list[DateValidatorType]: + validate_with: list[DateValidatorType] = [OptionalForm()] if self.answer_schema["mandatory"] is True: validate_with = [ @@ -52,8 +50,10 @@ def validators(self) -> DateValidatorTypes: validate_with.append(min_max_validator) return validate_with - def get_field(self) -> DateField: - return DateField(self.validators, label=self.label, description=self.guidance) + def get_field(self) -> UnboundField | DateField: + return DateField( + validators=self.validators, label=self.label, description=self.guidance + ) def get_min_max_validator( self, minimum_date: Optional[datetime], maximum_date: Optional[datetime] @@ -123,9 +123,9 @@ class MonthYearDateHandler(DateHandler): DATE_FORMAT = "yyyy-mm" DISPLAY_FORMAT = "MMMM yyyy" - def get_field(self) -> MonthYearDateField: + def get_field(self) -> UnboundField | MonthYearDateField: return MonthYearDateField( - self.validators, label=self.label, description=self.guidance + validators=self.validators, label=self.label, description=self.guidance ) def get_min_max_validator( @@ -152,9 +152,9 @@ class YearDateHandler(DateHandler): DATE_FORMAT = "yyyy" DISPLAY_FORMAT = "yyyy" - def get_field(self) -> YearDateField: + def get_field(self) -> UnboundField | YearDateField: return YearDateField( - self.validators, label=self.label, description=self.guidance + validators=self.validators, label=self.label, description=self.guidance ) def get_min_max_validator( diff --git a/app/forms/field_handlers/dropdown_handler.py b/app/forms/field_handlers/dropdown_handler.py index ec969293dc..6feec04a9f 100644 --- a/app/forms/field_handlers/dropdown_handler.py +++ b/app/forms/field_handlers/dropdown_handler.py @@ -2,13 +2,11 @@ from flask_babel import lazy_gettext from wtforms import SelectField +from wtforms.fields.core import UnboundField -from app.forms.field_handlers.select_handlers import ( - Choice, - ChoiceWithDetailAnswer, - SelectHandlerBase, -) +from app.forms.field_handlers.select_handlers import SelectHandlerBase from app.questionnaire.questionnaire_schema import InvalidSchemaConfigurationException +from app.utilities.types import Choice, ChoiceWithDetailAnswer class DropdownHandler(SelectHandlerBase): @@ -31,7 +29,7 @@ def choices(self) -> Sequence[Choice]: def _get_placeholder_text(self) -> str: return self.answer_schema.get("placeholder", self.DEFAULT_PLACEHOLDER) - def get_field(self) -> SelectField: + def get_field(self) -> UnboundField | SelectField: return SelectField( label=self.label, description=self.guidance, diff --git a/app/forms/field_handlers/duration_handler.py b/app/forms/field_handlers/duration_handler.py index e8024900a9..8c691d1d41 100644 --- a/app/forms/field_handlers/duration_handler.py +++ b/app/forms/field_handlers/duration_handler.py @@ -1,4 +1,5 @@ from wtforms import FormField +from wtforms.fields.core import UnboundField from app.forms.duration_form import get_duration_form from app.forms.field_handlers.field_handler import FieldHandler @@ -7,7 +8,7 @@ class DurationHandler(FieldHandler): MANDATORY_MESSAGE_KEY = "MANDATORY_DURATION" - def get_field(self) -> FormField: + def get_field(self) -> UnboundField | FormField: return FormField( get_duration_form(self.answer_schema, dict(self.error_messages)), label=self.label, diff --git a/app/forms/field_handlers/mobile_number_handler.py b/app/forms/field_handlers/mobile_number_handler.py index ecf95ddfdf..2344ad5c69 100644 --- a/app/forms/field_handlers/mobile_number_handler.py +++ b/app/forms/field_handlers/mobile_number_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import StringField +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.validators import MobileNumberCheck, ResponseRequired @@ -21,7 +22,7 @@ def validators(self) -> MobileNumberValidatorTypes: return validate_with - def get_field(self) -> StringField: + def get_field(self) -> UnboundField | StringField: return StringField( label=self.label, description=self.guidance, validators=self.validators ) diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index 70c70f57a6..4d71ccb7af 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -2,6 +2,7 @@ from typing import Any, Type, Union from wtforms import DecimalField, IntegerField +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import DecimalFieldWithSeparator, IntegerFieldWithSeparator @@ -64,7 +65,7 @@ def _field_type( else IntegerFieldWithSeparator ) - def get_field(self) -> Union[DecimalField, IntegerField]: + def get_field(self) -> UnboundField | DecimalField | IntegerField: additional_args = ( {"places": self.max_decimals} if self._field_type == DecimalFieldWithSeparator diff --git a/app/forms/field_handlers/select_handlers.py b/app/forms/field_handlers/select_handlers.py index 647110505c..e76e7d65cf 100644 --- a/app/forms/field_handlers/select_handlers.py +++ b/app/forms/field_handlers/select_handlers.py @@ -1,5 +1,6 @@ -from collections import namedtuple -from typing import Any, Optional, Sequence, Union +from typing import Any, Optional, Sequence + +from wtforms.fields.core import UnboundField from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import ( @@ -8,13 +9,7 @@ ) from app.questionnaire.dynamic_answer_options import DynamicAnswerOptions from app.questionnaire.questionnaire_schema import InvalidSchemaConfigurationException - -Choice = namedtuple("Choice", "value label") -ChoiceWithDetailAnswer = namedtuple( - "ChoiceWithDetailAnswer", "value label detail_answer_id" -) - -ChoiceType = Union[Choice, ChoiceWithDetailAnswer] +from app.utilities.types import ChoiceType, ChoiceWithDetailAnswer class SelectHandlerBase(FieldHandler): @@ -75,7 +70,7 @@ def coerce_str_unless_none(value: Optional[str]) -> Optional[str]: # not providing an answer and them selecting the 'None' option otherwise. # https://github.com/ONSdigital/eq-survey-runner/issues/1013 # See related WTForms PR: https://github.com/wtforms/wtforms/pull/288 - def get_field(self) -> SelectFieldWithDetailAnswer: + def get_field(self) -> UnboundField | SelectFieldWithDetailAnswer: return SelectFieldWithDetailAnswer( label=self.label, description=self.guidance, @@ -88,7 +83,7 @@ def get_field(self) -> SelectFieldWithDetailAnswer: class SelectMultipleHandler(SelectHandler): MANDATORY_MESSAGE_KEY = "MANDATORY_CHECKBOX" - def get_field(self) -> MultipleSelectFieldWithDetailAnswer: + def get_field(self) -> UnboundField | MultipleSelectFieldWithDetailAnswer: return MultipleSelectFieldWithDetailAnswer( label=self.label, description=self.guidance, diff --git a/app/forms/field_handlers/string_handler.py b/app/forms/field_handlers/string_handler.py index 661a645c41..161cb9e1ff 100644 --- a/app/forms/field_handlers/string_handler.py +++ b/app/forms/field_handlers/string_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import StringField, validators +from wtforms.fields.core import UnboundField from wtforms.validators import Length from app.forms.field_handlers.field_handler import FieldHandler @@ -33,7 +34,7 @@ def max_length(self) -> int: max_length: int = self.answer_schema.get("max_length", self.MAX_LENGTH) return max_length - def get_field(self) -> StringField: + def get_field(self) -> UnboundField | StringField: return StringField( label=self.label, description=self.guidance, validators=self.validators ) diff --git a/app/forms/field_handlers/text_area_handler.py b/app/forms/field_handlers/text_area_handler.py index 1d9eedfd82..5bc484f10a 100644 --- a/app/forms/field_handlers/text_area_handler.py +++ b/app/forms/field_handlers/text_area_handler.py @@ -2,6 +2,7 @@ from typing import Union from wtforms import validators +from wtforms.fields.core import UnboundField from wtforms.validators import Length from app.forms.field_handlers.field_handler import FieldHandler @@ -32,7 +33,7 @@ def get_length_validator(self) -> Length: return validators.length(-1, self.max_length, message=length_message) - def get_field(self) -> MaxTextAreaField: + def get_field(self) -> UnboundField | MaxTextAreaField: return MaxTextAreaField( label=self.label, description=self.guidance, diff --git a/app/forms/fields/date_field.py b/app/forms/fields/date_field.py index afe1f0f481..cf26d9344a 100644 --- a/app/forms/fields/date_field.py +++ b/app/forms/fields/date_field.py @@ -1,13 +1,17 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class DateForm(Form): # Validation is only ever added to the 1 field that shows in all 3 variants # This is to prevent an error message for each input box @@ -16,7 +20,7 @@ class DateForm(Form): day = StringField() @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -30,11 +34,24 @@ def data(self): class DateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ) -> None: form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=unset_value, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0], "month": substrings[1], "day": substrings[2]} diff --git a/app/forms/fields/decimal_field_with_separator.py b/app/forms/fields/decimal_field_with_separator.py index ea202f0b50..24e574653a 100644 --- a/app/forms/fields/decimal_field_with_separator.py +++ b/app/forms/fields/decimal_field_with_separator.py @@ -1,4 +1,5 @@ from decimal import Decimal, InvalidOperation +from typing import Any, Sequence from wtforms import DecimalField @@ -15,11 +16,11 @@ class DecimalFieldWithSeparator(DecimalField): DecimalPlace validators """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.data = None + self.data: Decimal | None = None - def process_formdata(self, valuelist): + def process_formdata(self, valuelist: Sequence[str] | None = None) -> None: if valuelist: try: self.data = Decimal(sanitise_number(valuelist[0])) diff --git a/app/forms/fields/integer_field_with_separator.py b/app/forms/fields/integer_field_with_separator.py index d218ab29b3..e4af166f22 100644 --- a/app/forms/fields/integer_field_with_separator.py +++ b/app/forms/fields/integer_field_with_separator.py @@ -1,3 +1,5 @@ +from typing import Any, Sequence + from wtforms import IntegerField from app.helpers.form_helpers import sanitise_number @@ -13,11 +15,11 @@ class IntegerFieldWithSeparator(IntegerField): DecimalPlace validators """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.data = None + self.data: int | None = None - def process_formdata(self, valuelist): + def process_formdata(self, valuelist: Sequence[str] | None = None) -> None: if valuelist: try: self.data = int(sanitise_number(valuelist[0])) diff --git a/app/forms/fields/max_text_area_field.py b/app/forms/fields/max_text_area_field.py index 563351c897..a32b8f318d 100644 --- a/app/forms/fields/max_text_area_field.py +++ b/app/forms/fields/max_text_area_field.py @@ -1,8 +1,16 @@ +from typing import Any + from wtforms import TextAreaField class MaxTextAreaField(TextAreaField): - def __init__(self, label="", validators=None, rows=None, maxlength=None, **kwargs): - super().__init__(label, validators, **kwargs) + def __init__( + self, + *, + rows: int, + maxlength: int, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) self.rows = rows self.maxlength = maxlength diff --git a/app/forms/fields/month_year_date_field.py b/app/forms/fields/month_year_date_field.py index 4bee2feb8e..99373b9810 100644 --- a/app/forms/fields/month_year_date_field.py +++ b/app/forms/fields/month_year_date_field.py @@ -1,19 +1,23 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class YearMonthDateForm(Form): year = StringField(validators=validators) month = StringField() @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -27,11 +31,24 @@ def data(self): class MonthYearDateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ) -> None: form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=unset_value, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0], "month": substrings[1]} diff --git a/app/forms/fields/multiple_select_field_with_detail_answer.py b/app/forms/fields/multiple_select_field_with_detail_answer.py index 619ec0b646..4276285041 100644 --- a/app/forms/fields/multiple_select_field_with_detail_answer.py +++ b/app/forms/fields/multiple_select_field_with_detail_answer.py @@ -1,4 +1,8 @@ -from wtforms import SelectMultipleField +from typing import Any, Generator, Sequence + +from wtforms import SelectFieldBase, SelectMultipleField + +from app.utilities.types import ChoiceType, ChoiceWidgetRenderType class MultipleSelectFieldWithDetailAnswer(SelectMultipleField): @@ -7,10 +11,18 @@ class MultipleSelectFieldWithDetailAnswer(SelectMultipleField): This saves us having to later map options with their detail_answer. """ - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__( + self, + *, + choices: Sequence[ChoiceType], + **kwargs: Any, + ) -> None: + super().__init__( + choices=choices, + **kwargs, + ) - def __iter__(self): + def __iter__(self) -> Generator[SelectFieldBase._Option, None, None]: opts = { "widget": self.option_widget, "name": self.name, @@ -26,7 +38,7 @@ def __iter__(self): opt.checked = checked yield opt - def iter_choices(self): + def iter_choices(self) -> Generator[ChoiceWidgetRenderType, None, None]: for value, label, detail_answer_id in self.choices: selected = self.data is not None and self.coerce(value) in self.data yield value, label, selected, detail_answer_id diff --git a/app/forms/fields/select_field_with_detail_answer.py b/app/forms/fields/select_field_with_detail_answer.py index 5a5299939a..a52384d7d1 100644 --- a/app/forms/fields/select_field_with_detail_answer.py +++ b/app/forms/fields/select_field_with_detail_answer.py @@ -1,6 +1,10 @@ -from wtforms import SelectField +from typing import Any, Generator, Sequence + +from wtforms import SelectField, SelectFieldBase from wtforms.validators import ValidationError +from app.utilities.types import ChoiceType, ChoiceWidgetRenderType + class SelectFieldWithDetailAnswer(SelectField): """ @@ -8,10 +12,18 @@ class SelectFieldWithDetailAnswer(SelectField): This saves us having to later map options with their detail_answer. """ - def __init__(self, **kwargs): - super().__init__(**kwargs) + def __init__( + self, + *, + choices: Sequence[ChoiceType], + **kwargs: Any, + ) -> None: + super().__init__( + choices=choices, + **kwargs, + ) - def __iter__(self): + def __iter__(self) -> Generator[SelectFieldBase._Option, None, None]: opts = { "widget": self.option_widget, "name": self.name, @@ -27,11 +39,11 @@ def __iter__(self): opt.checked = checked yield opt - def iter_choices(self): + def iter_choices(self) -> Generator[ChoiceWidgetRenderType, None, None]: for value, label, detail_answer_id in self.choices: yield value, label, self.coerce(value) == self.data, detail_answer_id - def pre_validate(self, _): + def pre_validate(self, _: Any) -> None: for _, _, match, _ in self.iter_choices(): if match: break diff --git a/app/forms/fields/year_date_field.py b/app/forms/fields/year_date_field.py index f78915ef8c..b229bef315 100644 --- a/app/forms/fields/year_date_field.py +++ b/app/forms/fields/year_date_field.py @@ -1,18 +1,22 @@ import logging from functools import cached_property +from typing import Any, Callable, Sequence, Type +from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField -from wtforms.utils import unset_value +from wtforms.utils import UnsetValue, unset_value + +from app.utilities.types import DateValidatorType logger = logging.getLogger(__name__) -def get_form_class(validators): +def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: class YearDateForm(Form): year = StringField(validators=validators) @cached_property - def data(self): + def data(self) -> str | None: # pylint: disable=no-member # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data @@ -26,11 +30,24 @@ def data(self): class YearDateField(FormField): - def __init__(self, validators, **kwargs): + def __init__( + self, + *, + validators: Sequence[DateValidatorType], + **kwargs: Any, + ): form_class = get_form_class(validators) - super().__init__(form_class, **kwargs) - - def process(self, formdata, data=None, extra_filters=None): + super().__init__( + form_class, + **kwargs, + ) + + def process( + self, + formdata: MultiDict | None = None, + data: str | UnsetValue = unset_value, + extra_filters: Sequence[Callable] | None = None, + ) -> None: if data is not unset_value: substrings = data.split("-") data = {"year": substrings[0]} diff --git a/app/forms/validators.py b/app/forms/validators.py index 9d125f6f6c..4a0b479d95 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -127,7 +127,7 @@ def __call__( form: "QuestionnaireForm", field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], ) -> None: - value: Union[int, Decimal] = field.data + value: int | Decimal | None = field.data if value is not None: decimal_limit = ( diff --git a/app/utilities/types.py b/app/utilities/types.py index d0691c00b5..9a0c7d8d2c 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -1,6 +1,12 @@ from typing import TYPE_CHECKING, NamedTuple, TypeAlias, TypedDict, Union if TYPE_CHECKING: + from app.forms.validators import ( # pragma: no cover + DateCheck, + DateRequired, + OptionalForm, + SingleDatePeriodCheck, + ) from app.questionnaire.location import Location # pragma: no cover from app.questionnaire.relationship_location import ( RelationshipLocation, # pragma: no cover @@ -10,6 +16,13 @@ SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] SupplementaryDataValueType: TypeAlias = dict | str | list | None +DateValidatorType: TypeAlias = Union[ + "OptionalForm", "DateRequired", "DateCheck", "SingleDatePeriodCheck" +] + +ChoiceType: TypeAlias = Union["Choice", "ChoiceWithDetailAnswer"] +ChoiceWidgetRenderType: TypeAlias = tuple[str, str, bool, str | None] + class SectionKeyDict(TypedDict): section_id: str @@ -44,3 +57,14 @@ def section_key(self) -> SectionKey: class SupplementaryDataListMapping(TypedDict): identifier: str | int list_item_id: str + + +class Choice(NamedTuple): + value: str + label: str + + +class ChoiceWithDetailAnswer(NamedTuple): + value: str + label: str + detail_answer_id: str | None diff --git a/mypy.ini b/mypy.ini index b318531b70..b86fec5d3b 100644 --- a/mypy.ini +++ b/mypy.ini @@ -46,7 +46,7 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True -[mypy-app.forms.field_handlers.*] +[mypy-app.forms.*] disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 45900567b5..7a29043aaf 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -3,10 +3,10 @@ from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.data_models.list_store import ListStore -from app.forms.field_handlers.select_handlers import Choice, ChoiceWithDetailAnswer from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver +from app.utilities.types import Choice, ChoiceWithDetailAnswer def get_mock_schema(): diff --git a/tests/app/forms/fields/test_date_field.py b/tests/app/forms/fields/test_date_field.py index f1a65d9fe2..7581fb1a23 100644 --- a/tests/app/forms/fields/test_date_field.py +++ b/tests/app/forms/fields/test_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import date_field +from app.forms.validators import OptionalForm def test_generate_date_form_creates_empty_form(): - form_class = date_field.get_form_class([validators.Optional()]) + form_class = date_field.get_form_class([OptionalForm()]) assert hasattr(form_class, "day") assert hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_date_form_creates_empty_form(): def test_date_form_empty_data(): - form = date_field.get_form_class([validators.Optional()]) + form = date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_date_form_format_data(): data = {"field": "2000-01-01"} class TestForm(Form): - field = date_field.DateField([validators.Optional()]) + field = date_field.DateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/fields/test_month_year_date_field.py b/tests/app/forms/fields/test_month_year_date_field.py index b618428b00..1cf89f1951 100644 --- a/tests/app/forms/fields/test_month_year_date_field.py +++ b/tests/app/forms/fields/test_month_year_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import month_year_date_field +from app.forms.validators import OptionalForm def test_generate_month_year_date_form_creates_empty_form(): - form_class = month_year_date_field.get_form_class([validators.Optional()]) + form_class = month_year_date_field.get_form_class([OptionalForm()]) assert not hasattr(form_class, "day") assert hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_month_year_date_form_creates_empty_form(): def test_month_year_date_form_empty_data(): - form = month_year_date_field.get_form_class([validators.Optional()]) + form = month_year_date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_month_year_date_form_format_data(): data = {"field": "2000-01"} class TestForm(Form): - field = month_year_date_field.MonthYearDateField([validators.Optional()]) + field = month_year_date_field.MonthYearDateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/fields/test_year_date_field.py b/tests/app/forms/fields/test_year_date_field.py index d44207b912..813e1127e3 100644 --- a/tests/app/forms/fields/test_year_date_field.py +++ b/tests/app/forms/fields/test_year_date_field.py @@ -1,10 +1,11 @@ -from wtforms import Form, validators +from wtforms import Form from app.forms.fields import year_date_field +from app.forms.validators import OptionalForm def test_generate_year_date_form_creates_empty_form(): - form_class = year_date_field.get_form_class([validators.Optional()]) + form_class = year_date_field.get_form_class([OptionalForm()]) assert not hasattr(form_class, "day") assert not hasattr(form_class, "month") @@ -12,7 +13,7 @@ def test_generate_year_date_form_creates_empty_form(): def test_year_date_form_empty_data(): - form = year_date_field.get_form_class([validators.Optional()]) + form = year_date_field.get_form_class([OptionalForm()]) assert form().data is None @@ -21,7 +22,7 @@ def test_year_date_form_format_data(): data = {"field": "2000"} class TestForm(Form): - field = year_date_field.YearDateField([validators.Optional()]) + field = year_date_field.YearDateField(validators=[OptionalForm()]) test_form = TestForm(data=data) diff --git a/tests/app/forms/test_custom_fields.py b/tests/app/forms/test_custom_fields.py index ef79081356..ad6f48542c 100644 --- a/tests/app/forms/test_custom_fields.py +++ b/tests/app/forms/test_custom_fields.py @@ -11,13 +11,23 @@ def test_text_area_a_wtforms_field(mock_form): - text_area = MaxTextAreaField("LabelText", _form=mock_form, name="aName") + text_area = MaxTextAreaField( + label="LabelText", + _form=mock_form, + name="aName", + rows=0, + maxlength=0, + ) assert isinstance(text_area, Field) def test_text_area_supports_maxlength_property(mock_form): text_area = MaxTextAreaField( - "TestLabel", maxlength=20, _form=mock_form, name="aName" + label="TestLabel", + maxlength=20, + _form=mock_form, + name="aName", + rows=0, ) assert isinstance(text_area, Field) assert text_area.maxlength == 20 From 22f5897d60e2ca896f496b656f8f08ac8d28a3a0 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Wed, 4 Oct 2023 17:42:47 +0100 Subject: [PATCH 301/567] Grand calculated summary inside a repeating section (#1210) * Add return to list params * Grand Calculated Summary inside repeating section * Functional tests * Remove list name parameter and add lookup * Move string method into utilities * Fix params not being preserved when section is incomplete --- app/data_models/list_store.py | 5 + app/questionnaire/router.py | 40 +- app/utilities/strings.py | 7 + .../contexts/calculated_summary_context.py | 63 +- .../grand_calculated_summary_context.py | 22 +- app/views/contexts/summary/answer.py | 4 + app/views/contexts/summary/block.py | 4 + .../summary/calculated_summary_block.py | 20 + app/views/contexts/summary/group.py | 5 + app/views/contexts/summary/question.py | 5 + app/views/handlers/block.py | 3 + app/views/handlers/calculation_summary.py | 1 + app/views/handlers/question.py | 1 + ...ated_summary_inside_repeating_section.json | 817 ++++++++++++++++++ tests/app/data_model/test_list_store.py | 11 + tests/app/questionnaire/conftest.py | 7 + tests/app/questionnaire/test_router.py | 95 +- tests/app/utilities/test_strings.py | 12 + .../app/views/contexts/summary/test_answer.py | 2 + ...d_summary_inside_repeating_section.spec.js | 220 +++++ ..._questionnaire_grand_calculated_summary.py | 41 + 21 files changed, 1334 insertions(+), 51 deletions(-) create mode 100644 schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json create mode 100644 tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index adb713519c..b7ad32bd0d 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -178,6 +178,11 @@ def _list_item_ids(self) -> list[str]: return ids + def get_list_name_for_list_item_id(self, list_item_id: str) -> str | None: + for list_name in self._lists: + if list_item_id in self[list_name].items: + return list_name + @property def is_dirty(self) -> bool: return self._is_dirty diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 6f78e4ae16..0a239ba7f8 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -120,6 +120,7 @@ def get_next_location_url( return_to: str | None = None, return_to_answer_id: str | None = None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, ) -> str: """ Get the next location in the section. If the section is complete, determine where to go next, @@ -137,6 +138,7 @@ def get_next_location_url( is_section_complete=is_section_complete, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, ): return return_to_url @@ -175,6 +177,7 @@ def get_previous_location_url( return_to: str | None = None, return_to_answer_id: str | None = None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, ) -> str | None: """ Returns the previous 'location' to visit given a set of user answers or returns to the summary if @@ -187,6 +190,7 @@ def get_previous_location_url( is_for_previous=True, return_to_answer_id=return_to_answer_id, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, ): return return_to_url @@ -228,6 +232,7 @@ def get_return_to_location_url( is_section_complete: bool | None = None, return_to_answer_id: str | None = None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, ) -> str | None: if not return_to: return None @@ -239,7 +244,9 @@ def get_return_to_location_url( section_key=location.section_key, routing_path=routing_path, is_for_previous=is_for_previous, + location=location, return_to_answer_id=return_to_answer_id, + return_to_list_item_id=return_to_list_item_id, ) ): return url @@ -248,6 +255,7 @@ def get_return_to_location_url( url := self._get_return_to_for_calculated_summary( return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, location=location, routing_path=routing_path, return_to_answer_id=return_to_answer_id, @@ -266,6 +274,7 @@ def get_return_to_location_url( is_for_previous=is_for_previous, return_to_block_id=return_to_block_id, return_to=return_to, + return_to_list_item_id=return_to_list_item_id, routing_path=routing_path, ) @@ -286,7 +295,9 @@ def _get_return_to_for_grand_calculated_summary( section_key: SectionKey, routing_path: RoutingPath, is_for_previous: bool, + location: LocationType, return_to_answer_id: str | None = None, + return_to_list_item_id: str | None = None, ) -> str | None: """ Builds the return url for a grand calculated summary, @@ -299,38 +310,49 @@ def _get_return_to_for_grand_calculated_summary( grand_calculated_summary_section: str = ( self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore ) + list_item_id = location.list_item_id or return_to_list_item_id + list_name = ( + self._list_store.get_list_name_for_list_item_id(list_item_id) + if list_item_id + else None + ) if grand_calculated_summary_section != section_key.section_id: # the grand calculated summary is in a different section which will have a different routing path # but does not go to it unless the section is enabled and the current section is complete if ( - not self._progress_store.is_section_complete( - SectionKey(section_key.section_id) - ) + not self._progress_store.is_section_complete(section_key) or grand_calculated_summary_section not in self.enabled_section_ids ): return None routing_path = self._path_finder.routing_path( - SectionKey(section_id=grand_calculated_summary_section) + SectionKey( + section_id=grand_calculated_summary_section, + list_item_id=list_item_id, + ) ) if self.can_access_location( - # grand calculated summaries do not yet support repeating sections, when they do, this will need to make use of list item id as well Location( block_id=return_to_block_id, section_id=grand_calculated_summary_section, + list_item_id=list_item_id, + list_name=list_name, ), routing_path, ): return url_for( "questionnaire.block", block_id=return_to_block_id, - return_to=return_to, + list_item_id=list_item_id, + list_name=list_name, _anchor=return_to_answer_id, ) - # the above may alter routing_path, so retrieval of the next incomplete block needs to be here instead of returning None and allowing default behaviour + # since the above may define a different routing_path, + # retrieval of the next incomplete block needs to be here instead of returning None and allowing default behaviour return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, return_to_block_id=return_to_block_id, return_to=return_to, + return_to_list_item_id=return_to_list_item_id, routing_path=routing_path, ) @@ -339,6 +361,7 @@ def _get_return_to_for_calculated_summary( *, return_to: str, return_to_block_id: str | None, + return_to_list_item_id: str | None, location: LocationType, routing_path: RoutingPath, return_to_answer_id: str | None = None, @@ -377,6 +400,7 @@ def _get_return_to_for_calculated_summary( list_item_id=location.list_item_id, return_to=return_to_remaining, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, _anchor=return_to_answer_id, ) @@ -386,6 +410,7 @@ def _get_return_url_for_inaccessible_location( is_for_previous: bool, return_to_block_id: str | None, return_to: str | None, + return_to_list_item_id: str | None, routing_path: RoutingPath, ) -> str | None: """ @@ -404,6 +429,7 @@ def _get_return_url_for_inaccessible_location( return next_incomplete_location.url( return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, ) def get_next_location_url_for_end_of_section(self) -> str: diff --git a/app/utilities/strings.py b/app/utilities/strings.py index 7145023f9b..1bfdf0fb37 100644 --- a/app/utilities/strings.py +++ b/app/utilities/strings.py @@ -38,3 +38,10 @@ def safe_content(content: str) -> str: # Strip HTML Tags content = re.sub(r"]+>", "", content) return content + + +def pascal_case_to_hyphenated_lowercase(string: str) -> str: + """ + Changes text from PascalCase to hyphenated-lowercase + """ + return re.sub(r"(? None: super().__init__( language, @@ -56,12 +59,22 @@ def __init__( self.current_location = current_location self.return_to = return_to self.return_to_block_id = return_to_block_id + self.return_to_list_item_id = return_to_list_item_id + + @cached_property + def rendered_block(self) -> dict: + # Type ignore block is guaranteed to exist at this point + block_id: str = self.current_location.block_id # type: ignore + block: ImmutableDict = self._schema.get_block(block_id) # type: ignore + + return self._placeholder_renderer.render( + data_to_render=block, list_item_id=self.current_location.list_item_id + ) def build_groups_for_section( self, *, section: Mapping, - return_to_block_id: str, routing_path_block_ids: Iterable[str], ) -> list[Mapping]: """ @@ -69,6 +82,8 @@ def build_groups_for_section( the details of the grand calculated summary to return to needs to be passed down to the calculated summary answer links """ return_to = "calculated-summary" + # Type ignore: safe to assume block_id is not None + return_to_block_id: str = self.current_location.block_id # type: ignore if self.return_to == "grand-calculated-summary": return_to_block_id += f",{self.return_to_block_id}" return_to += ",grand-calculated-summary" @@ -87,22 +102,20 @@ def build_groups_for_section( supplementary_data_store=self._supplementary_data_store, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=self.return_to_list_item_id, summary_type="CalculatedSummary", ).serialize() for group in section["groups"] ] def build_view_context(self) -> dict[str, dict]: - # type ignores added as block will exist at this point - block_id: str = self.current_location.block_id # type: ignore - block: ImmutableDict = self._schema.get_block(block_id) # type: ignore - - calculated_section: dict = self._build_calculated_summary_section(block) - calculation = block["calculation"] + calculated_section: dict = self._build_calculated_summary_section( + self.rendered_block + ) + calculation = self.rendered_block["calculation"] groups = self.build_groups_for_section( section=calculated_section, - return_to_block_id=block_id, routing_path_block_ids=self.routing_path_block_ids, ) @@ -116,24 +129,18 @@ def build_view_context(self) -> dict[str, dict]: ) return self._build_formatted_summary( - block=block, - groups=groups, - calculation=calculation, - formatted_total=formatted_total, - summary_type="CalculatedSummary", + groups=groups, calculation=calculation, formatted_total=formatted_total ) def _build_formatted_summary( self, *, - block: ImmutableDict, groups: Iterable[Mapping], - calculation: ImmutableDict, + calculation: Mapping, formatted_total: str, - summary_type: str, ) -> dict[str, dict]: - collapsible = block.get("collapsible") or False - block_title = block["title"] + collapsible = self.rendered_block.get("collapsible") or False + block_title = self.rendered_block["title"] sections = [{"id": self.current_location.section_id, "groups": groups}] @@ -142,15 +149,16 @@ def _build_formatted_summary( "sections": sections, "answers_are_editable": True, "calculated_question": self._get_calculated_question( - calculation, formatted_total + calculation_question=calculation, + formatted_total=formatted_total, ), "title": block_title % {"total": formatted_total}, "collapsible": collapsible, - "summary_type": summary_type, + "summary_type": self.rendered_block["type"], } } - def _build_calculated_summary_section(self, rendered_block: ImmutableDict) -> dict: + def _build_calculated_summary_section(self, rendered_block: Mapping) -> dict: """Build up the list of blocks only including blocks / questions / answers which are relevant to the summary""" # type ignores added as block will exist at this point block_id: str = self.current_location.block_id # type: ignore @@ -235,7 +243,7 @@ def _remove_unwanted_questions_answers( def _get_evaluated_total( self, *, - calculation: ImmutableDict, + calculation: Mapping, routing_path_block_ids: Iterable[str], ) -> NumericType: """ @@ -261,7 +269,7 @@ def _get_formatted_total( ) -> str: answer_format, values_to_calculate = self._get_answer_format(groups) - if isinstance(calculation, ImmutableDict): + if isinstance(calculation, Mapping): calculated_total = self._get_evaluated_total( calculation=calculation, routing_path_block_ids=self.routing_path_block_ids, @@ -330,15 +338,14 @@ def _format_total( return format_number(total) - @staticmethod def _get_calculated_question( - calculation_question: ImmutableDict, - formatted_total: str, + self, *, calculation_question: Mapping, formatted_total: str ) -> dict: calculation_title = calculation_question["title"] + block_type = pascal_case_to_hyphenated_lowercase(self.rendered_block["type"]) return { "title": calculation_title, - "id": "calculated-summary-question", - "answers": [{"id": "calculated-summary-answer", "value": formatted_total}], + "id": f"{block_type}-question", + "answers": [{"id": f"{block_type}-answer", "value": formatted_total}], } diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index b88f30fa8d..8e3c9225c4 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -11,9 +11,7 @@ class GrandCalculatedSummaryContext(CalculatedSummaryContext): - def _build_grand_calculated_summary_section( - self, rendered_block: ImmutableDict - ) -> dict[str, str | list]: + def _build_grand_calculated_summary_section(self) -> dict[str, str | list]: """ Build list of calculated summary blocks that the grand calculated summary will be adding up """ @@ -23,7 +21,7 @@ def _build_grand_calculated_summary_section( ) calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( - rendered_block + self.rendered_block ) blocks_to_calculate = [ self._schema.get_block(block_id) for block_id in calculated_summary_ids @@ -68,7 +66,6 @@ def build_groups_for_section( self, *, section: Mapping, - return_to_block_id: str, routing_path_block_ids: Iterable[str], ) -> list[Mapping]: return [ @@ -85,7 +82,8 @@ def build_groups_for_section( progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, return_to="grand-calculated-summary", - return_to_block_id=return_to_block_id, + return_to_block_id=self.current_location.block_id, + return_to_list_item_id=self.current_location.list_item_id, summary_type="GrandCalculatedSummary", ).serialize() for group in section["groups"] @@ -95,20 +93,16 @@ def build_view_context(self) -> dict[str, dict]: """ Build summary section with formatted total and change links for each calculated summary """ - # Type ignore: Block will exist at this point - block: ImmutableDict = self._schema.get_block(self.current_location.block_id) # type: ignore - - calculation = block["calculation"] + calculation = self.rendered_block["calculation"] calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( - block + self.rendered_block ) routing_path_block_ids = self._blocks_on_routing_path(calculated_summary_ids) - calculated_section = self._build_grand_calculated_summary_section(block) + calculated_section = self._build_grand_calculated_summary_section() groups = self.build_groups_for_section( section=calculated_section, - return_to_block_id=block["id"], routing_path_block_ids=routing_path_block_ids, ) total = self._get_evaluated_total( @@ -128,9 +122,7 @@ def build_view_context(self) -> dict[str, dict]: formatted_total = self._format_total(answer_format=answer_format, total=total) return self._build_formatted_summary( - block=block, groups=groups, calculation=calculation, formatted_total=formatted_total, - summary_type="GrandCalculatedSummary", ) diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index 9d272baf75..b1c1fa3ecc 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -23,6 +23,7 @@ def __init__( list_item_id: str | None, return_to: str | None, return_to_block_id: str | None, + return_to_list_item_id: str | None, is_in_repeating_section: bool, ) -> None: self.id = answer_schema["id"] @@ -40,6 +41,7 @@ def __init__( list_item_id=list_item_id, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, is_in_repeating_section=is_in_repeating_section, ) @@ -64,6 +66,7 @@ def _build_link( list_item_id: str | None, return_to: str | None, return_to_block_id: str | None, + return_to_list_item_id: str | None, is_in_repeating_section: bool, ) -> str: return url_for( @@ -78,6 +81,7 @@ def _build_link( is_in_repeating_section=is_in_repeating_section, ), return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, _anchor=self.id, ) diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 78e8109f87..e92f39fe28 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -31,6 +31,7 @@ def __init__( location: LocationType, return_to: str | None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, language: str, @@ -73,6 +74,7 @@ def __init__( response_metadata=response_metadata, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, progress_store=progress_store, supplementary_data_store=supplementary_data_store, language=language, @@ -88,6 +90,7 @@ def get_question( response_metadata: MutableMapping, return_to: str | None, return_to_block_id: str | None, + return_to_list_item_id: str | None, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, language: str, @@ -120,6 +123,7 @@ def get_question( block_id=self.id, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, metadata=metadata, response_metadata=response_metadata, language=language, diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py index d5ba4dbede..8ade52136f 100644 --- a/app/views/contexts/summary/calculated_summary_block.py +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -18,6 +18,7 @@ class CalculatedSummaryBlock: + # pylint: disable=too-many-locals def __init__( self, block_schema: Mapping, @@ -30,20 +31,32 @@ def __init__( location: LocationType, return_to: str | None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, progress_store: ProgressStore, routing_path_block_ids: Iterable[str], supplementary_data_store: SupplementaryDataStore, ) -> None: """ A Calculated summary block that is rendered as part of a grand calculated summary + + If the GCS is in a repeating section, and the calculated summary also is + then the list name and item id need to be used to build the calculated summary change link + but if the GCS is repeating and the CS is not, the list parameters in the change link should be set to None """ self.id = block_schema["id"] self.title = block_schema["calculation"]["title"] self._return_to = return_to self._return_to_block_id = return_to_block_id + self._return_to_list_item_id = return_to_list_item_id self._block_schema = block_schema self._schema = schema + if self._schema.is_block_in_repeating_section(self.id): + self._list_item_id = location.list_item_id + self._list_name = location.list_name + else: + self._list_item_id = None + self._list_name = None self._rule_evaluator = RuleEvaluator( schema=schema, @@ -71,12 +84,19 @@ def __init__( ] def _build_link(self) -> str: + # not required if the calculated summary is in the repeat alongside the GCS + return_to_list_item_id = ( + self._return_to_list_item_id if not self._list_item_id else None + ) return url_for( "questionnaire.block", block_id=self.id, + list_name=self._list_name, + list_item_id=self._list_item_id, return_to=self._return_to, return_to_answer_id=self.id, return_to_block_id=self._return_to_block_id, + return_to_list_item_id=return_to_list_item_id, _anchor=self.id, ) diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 131692647e..dbba25f0a5 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -43,6 +43,7 @@ def __init__( supplementary_data_store: SupplementaryDataStore, return_to: str | None, return_to_block_id: str | None = None, + return_to_list_item_id: str | None = None, summary_type: str | None = None, view_submitted_response: bool | None = False, ) -> None: @@ -66,6 +67,7 @@ def __init__( progress_store=progress_store, language=language, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, view_submitted_response=view_submitted_response, summary_type=summary_type, supplementary_data_store=supplementary_data_store, @@ -100,6 +102,7 @@ def _build_blocks_and_links( supplementary_data_store: SupplementaryDataStore, language: str, return_to_block_id: str | None, + return_to_list_item_id: str | None, view_submitted_response: bool | None = False, summary_type: str | None = None, ) -> list[dict[str, Block]]: @@ -164,6 +167,7 @@ def _build_blocks_and_links( location=location, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, progress_store=progress_store, language=language, supplementary_data_store=supplementary_data_store, @@ -185,6 +189,7 @@ def _build_blocks_and_links( location=location, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, supplementary_data_store=supplementary_data_store, diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 6e2b9b8ed7..77b3f4c427 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -42,6 +42,7 @@ def __init__( block_id: str, return_to: Optional[str], return_to_block_id: Optional[str] = None, + return_to_list_item_id: Optional[str] = None, metadata: MetadataProxy | None, response_metadata: MutableMapping, language: str, @@ -75,6 +76,7 @@ def __init__( list_name=location.list_name if location else None, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, metadata=metadata, response_metadata=response_metadata, language=language, @@ -98,6 +100,7 @@ def _build_answers( list_name: str | None, return_to: str | None, return_to_block_id: str | None, + return_to_list_item_id: str | None, metadata: MetadataProxy | None, response_metadata: MutableMapping, language: str, @@ -111,6 +114,7 @@ def _build_answers( list_item_id=self.list_item_id, return_to=return_to, return_to_answer_id=answer_id if return_to else None, + return_to_list_item_id=return_to_list_item_id, _anchor=question_schema["answers"][0]["id"], ) @@ -149,6 +153,7 @@ def _build_answers( list_item_id=list_item_id or self.list_item_id, return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=return_to_list_item_id, is_in_repeating_section=self._is_in_repeating_section, ).serialize() summary_answers.append(summary_answer) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 3322b8906e..38f8792e9e 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -44,6 +44,7 @@ def __init__( self._return_to = request_args.get("return_to") self._return_to_answer_id = request_args.get("return_to_answer_id") self._return_to_block_id = request_args.get("return_to_block_id") + self._return_to_list_item_id = request_args.get("return_to_list_item_id") self.resume = "resume" in request_args if not self.is_location_valid(): @@ -111,6 +112,7 @@ def get_previous_location_url(self) -> str | None: self._return_to, self._return_to_answer_id, self._return_to_block_id, + self._return_to_list_item_id, ) def get_next_location_url(self) -> str: @@ -120,6 +122,7 @@ def get_next_location_url(self) -> str: self._return_to, self._return_to_answer_id, self._return_to_block_id, + self._return_to_list_item_id, ) def handle_post(self) -> None: diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 109225f377..14395e35e7 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -21,6 +21,7 @@ def get_context(self) -> dict[str, dict]: routing_path=self._routing_path, return_to=self.return_to, return_to_block_id=self.return_to_block_id, + return_to_list_item_id=self._return_to_list_item_id, supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) context = summary_context.build_view_context() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index b25c6ce7d2..1bd79990b4 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -122,6 +122,7 @@ def get_next_location_url(self) -> str: self._return_to, self._return_to_answer_id, self._return_to_block_id, + self._return_to_list_item_id, ) def _get_answers_for_question(self, question_json: Mapping) -> dict: diff --git a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json new file mode 100644 index 0000000000..96c96edf6d --- /dev/null +++ b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json @@ -0,0 +1,817 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Grand Calculated Summary Inside Repeating Section", + "theme": "default", + "description": "A schema to showcase a grand calculated summary inside a repeating section", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": {} + }, + "sections": [ + { + "id": "base-costs-section", + "title": "Vehicle Costs", + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "base-costs-group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-cost", + "for_list": "costs", + "question": { + "type": "General", + "id": "any-cost-question", + "title": "Do you have any outgoing costs for owning a vehicle?", + "guidance": { + "contents": [ + { + "title": "Notes for testing" + }, + { + "description": "For the first iteration, calculated summaries of repeating answers don’t work in a grand calculated summary in a separate repeating section" + }, + { + "description": "So you should answer no to this question and remove this guidance once separate repeating answer calculated summaries are supported" + } + ] + }, + "answers": [ + { + "type": "Radio", + "id": "any-cost-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-cost", + "list_name": "costs" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "finance-cost", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-cost-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector-cost" + } + ] + }, + { + "id": "list-collector-cost", + "type": "ListCollector", + "for_list": "costs", + "question": { + "id": "confirmation-cost-question", + "type": "General", + "title": "Do you need to add other outgoing costs?", + "answers": [ + { + "id": "list-collector-cost-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-cost", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other outgoing costs?", + "question": { + "id": "add-cost-question", + "type": "General", + "title": "What outgoing cost do you have?", + "answers": [ + { + "id": "cost-name", + "label": "Outgoing cost", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Road Tax", + "value": "Road Tax" + }, + { + "label": "Parking Permit", + "value": "Parking Permit" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-cost", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-cost-question", + "type": "General", + "title": "What outgoing cost do you have?", + "answers": [ + { + "id": "cost-name", + "label": "Outgoing cost", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Road Tax", + "value": "Road Tax" + }, + { + "label": "Parking Permit", + "value": "Parking Permit" + } + ] + } + ] + } + }, + "remove_block": { + "id": "remove-cost", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this outgoing cost?", + "question": { + "id": "remove-cost-question", + "type": "General", + "title": "Are you sure you want to remove this outgoing cost?", + "warning": "All of the information about this outgoing cost will be deleted", + "answers": [ + { + "id": "remove-cost-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "cost", + "item_title": { + "text": "{cost_name}", + "placeholders": [ + { + "placeholder": "cost_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "cost-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + }, + { + "type": "Question", + "id": "dynamic-cost-block", + "skip_conditions": { + "when": { + "==": [ + { + "count": [ + { + "source": "list", + "identifier": "costs" + } + ] + }, + 0 + ] + } + }, + "question": { + "id": "dynamic-answer-question", + "title": "How much do you spend per month on the following for a single vehicle?", + "type": "General", + "dynamic_answers": { + "values": { + "source": "list", + "identifier": "costs" + }, + "answers": [ + { + "label": { + "text": "Single vehicle {transformed_value}", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "cost-name" + } + } + ] + }, + "id": "cost-of-cost", + "type": "Currency", + "mandatory": true, + "currency": "GBP", + "decimal_places": 2 + } + ] + } + } + }, + { + "id": "finance-cost", + "type": "Question", + "question": { + "id": "finance-cost-question", + "type": "General", + "title": "What is your monthly expenditure per vehicle on finance?", + "answers": [ + { + "id": "finance-cost-answer", + "label": "Vehicle monthly finance costs", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-base-cost", + "title": "We calculate the total base cost for any owned vehicle to be %(total)s. Is this correct?", + "calculation": { + "title": "Vehicle base cost", + "operation": { + "+": [ + { + "source": "answers", + "identifier": "cost-of-cost" + }, + { + "source": "answers", + "identifier": "finance-cost-answer" + } + ] + } + } + }, + { + "type": "Question", + "id": "base-cost-payment-breakdown", + "question": { + "type": "Calculated", + "id": "base-cost-payment-breakdown-question", + "title": { + "text": "How much of the {total} is paid by debit or credit card?", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-base-cost" + } + } + } + ] + } + ] + }, + "warning": "The sum of these answers must not exceed the total", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "source": "calculated_summary", + "identifier": "calculated-summary-base-cost" + }, + "answers_to_calculate": ["base-credit", "base-debit"], + "conditions": ["less than", "equals"] + } + ], + "answers": [ + { + "id": "base-credit", + "label": "Credit card", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "base-debit", + "label": "Debit card", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + } + ] + } + ] + }, + { + "id": "vehicles-section", + "title": "Vehicle Ownership", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "vehicles", + "title": "Vehicles", + "add_link_text": "Add another vehicle", + "empty_list_text": "There are no vehicles" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "vehicles-group", + "blocks": [ + { + "type": "ListCollectorDrivingQuestion", + "id": "any-vehicle", + "for_list": "vehicles", + "question": { + "type": "General", + "id": "any-vehicle-question", + "title": "Do you own any vehicles?", + "answers": [ + { + "type": "Radio", + "id": "any-vehicle-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-vehicle", + "list_name": "vehicles" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-vehicle-answer" + }, + "No" + ] + } + }, + { + "block": "list-collector" + } + ] + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "vehicles", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Do you need to add more vehicles?", + "answers": [ + { + "id": "list-collector-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-vehicle", + "type": "ListAddQuestion", + "cancel_text": "Don’t need to add any other vehicles?", + "question": { + "id": "add-question", + "type": "General", + "title": "What vehicle do you own?", + "answers": [ + { + "id": "vehicle-name", + "label": "Vehicle", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Car", + "value": "Car" + }, + { + "label": "Motorbike", + "value": "Motorbike" + }, + { + "label": "Van", + "value": "Van" + } + ] + } + ] + } + }, + "edit_block": { + "id": "edit-vehicle", + "type": "ListEditQuestion", + "cancel_text": "Don’t need to change anything?", + "question": { + "id": "edit-question", + "type": "General", + "title": "What vehicle do you own?", + "answers": [ + { + "id": "vehicle-name", + "label": "Vehicle", + "mandatory": true, + "type": "Dropdown", + "options": [ + { + "label": "Car", + "value": "Car" + }, + { + "label": "Motorbike", + "value": "Motorbike" + }, + { + "label": "Van", + "value": "Van" + } + ] + } + ] + } + }, + "remove_block": { + "id": "remove-vehicle", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this vehicle?", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this vehicle?", + "warning": "All of the information about this vehicle will be deleted", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Vehicle", + "item_title": { + "text": "{vehicle_name}", + "placeholders": [ + { + "placeholder": "vehicle_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "vehicle-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "vehicle-details-section", + "title": "Vehicle Details", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "vehicles", + "title": { + "text": "{vehicle_name} details", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + } + }, + "groups": [ + { + "id": "vehicle-details-group", + "blocks": [ + { + "id": "vehicle-maintenance-block", + "type": "Question", + "question": { + "id": "vehicle-maintenance-question", + "type": "General", + "title": { + "text": "What is your monthly expenditure on maintenance for your {vehicle_name}?", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "answers": [ + { + "id": "vehicle-maintenance-cost", + "label": { + "text": "{vehicle_name} maintenance costs", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "id": "vehicle-fuel-block", + "type": "Question", + "question": { + "id": "vehicle-fuel-question", + "type": "General", + "title": { + "text": "What is your monthly expenditure on fuel for your {vehicle_name}?", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "answers": [ + { + "id": "vehicle-fuel-cost", + "label": { + "text": "{vehicle_name} fuel costs", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "calculated-summary-running-cost", + "title": { + "text": "We calculate the monthly running costs of your {vehicle_name} to be %(total)s. Is this correct?", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "calculation": { + "title": { + "text": "Monthly {vehicle_name} costs", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "operation": { + "+": [ + { + "source": "answers", + "identifier": "vehicle-maintenance-cost" + }, + { + "source": "answers", + "identifier": "vehicle-fuel-cost" + } + ] + } + } + }, + { + "type": "GrandCalculatedSummary", + "id": "grand-calculated-summary-vehicle", + "title": { + "text": "The total cost of owning and running your {vehicle_name} is calculated to be %(total)s. Is this correct?", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calculated-summary-base-cost" + }, + { + "source": "calculated_summary", + "identifier": "calculated-summary-running-cost" + } + ] + }, + "title": { + "text": "Grand total {vehicle_name} expenditure", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + } + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/data_model/test_list_store.py b/tests/app/data_model/test_list_store.py index 7240b6f39d..8765686c05 100644 --- a/tests/app/data_model/test_list_store.py +++ b/tests/app/data_model/test_list_store.py @@ -169,3 +169,14 @@ def test_get_item_using_method(): item = store.get("people") assert item.items[0] == first_id + + +def test_lookup_list_items(): + store = ListStore() + + person_id = store.add_list_item("people") + item_id = store.add_list_item("items") + + assert store.get_list_name_for_list_item_id(person_id) == "people" + assert store.get_list_name_for_list_item_id(item_id) == "items" + assert store.get_list_name_for_list_item_id("not-a-list-item-id") is None diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index dd2875c106..bb050ac1f7 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -1339,6 +1339,13 @@ def grand_calculated_summary_schema(): return load_schema_from_name("test_grand_calculated_summary") +@pytest.fixture +def grand_calculated_summary_in_repeating_section_schema(): + return load_schema_from_name( + "test_grand_calculated_summary_inside_repeating_section" + ) + + @pytest.fixture def grand_calculated_summary_progress_store(): return ProgressStore( diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index c97d13b171..b1085c584b 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -790,13 +790,106 @@ def test_return_to_grand_calculated_summary_from_calculated_summary( expected_previous_url = url_for( "questionnaire.block", - return_to="grand-calculated-summary", block_id="distance-grand-calculated-summary", _anchor="distance-calculated-summary-1", ) assert expected_previous_url == next_location_url + @pytest.mark.parametrize( + "section_id,block_id,list_name,list_item_id,return_to_list_item_id", + [ + ( + "base-costs-section", + "calculated-summary-base-cost", + None, + None, + "ZIrqqR", + ), + ( + "vehicle-details-section", + "calculated-summary-running-costs", + "vehicles", + "ZIrqqR", + None, + ), + ], + ) + @pytest.mark.usefixtures("app") + def test_return_to_repeating_grand_calculated_summary_from_calculated_summary( + self, + section_id, + block_id, + list_name, + list_item_id, + return_to_list_item_id, + grand_calculated_summary_in_repeating_section_schema, + ): + """ + This tests that if you use a change link from a repeating GCS to return to: + either a non-repeating CS in another section or a repeating CS in the same section, + the continue button for the CS has a next location url of the original repeating GCS. + """ + self.schema = grand_calculated_summary_in_repeating_section_schema + self.list_store = ListStore([{"items": ["ZIrqqR"], "name": "vehicles"}]) + + self.progress_store = ProgressStore( + [ + ProgressDict( + section_id="base-costs-section", + block_ids=[ + "any-cost", + "finance-cost", + "calculated-summary-base-cost", + ], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="vehicle-details-section", + block_ids=[ + "vehicle-maintenance-block", + "vehicle-fuel-block", + "calculated-summary-running-cost", + "grand-calculated-summary-vehicle", + ], + status=CompletionStatus.COMPLETED, + list_item_id="ZIrqqR", + ), + ] + ) + current_location = Location( + section_id=section_id, + block_id=block_id, + list_name=list_name, + list_item_id=list_item_id, + ) + routing_path = RoutingPath( + block_ids=[ + "vehicle-maintenance-block", + "vehicle-fuel-block", + "calculated-summary-running-cost", + "grand-calculated-summary-vehicle", + ], + section_id="vehicle-details-section", + list_name="vehicles", + list_item_id="ZIrqqR", + ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_to="grand-calculated-summary", + return_to_block_id="grand-calculated-summary-vehicle", + return_to_list_item_id=return_to_list_item_id, + ) + expected_next_url = url_for( + "questionnaire.block", + list_name="vehicles", + list_item_id="ZIrqqR", + block_id="grand-calculated-summary-vehicle", + ) + + assert expected_next_url == next_location_url + @pytest.mark.parametrize( "return_to_block_id", ("grand-calculated-summary-1", "grand-calculated-summary-2"), diff --git a/tests/app/utilities/test_strings.py b/tests/app/utilities/test_strings.py index 326371c306..a07b97640c 100644 --- a/tests/app/utilities/test_strings.py +++ b/tests/app/utilities/test_strings.py @@ -27,3 +27,15 @@ def test_to_bytes(input_str, bytes_str): ) def test_to_str(input_str, bytes_str): assert strings.to_str(input_str) == bytes_str + + +@pytest.mark.parametrize( + "input_str, expected_result", + ( + ("CalculatedSummary", "calculated-summary"), + ("GrandCalculatedSummary", "grand-calculated-summary"), + ("Block", "block"), + ), +) +def test_pascal_case_to_hyphenated_lowercase(input_str, expected_result): + assert strings.pascal_case_to_hyphenated_lowercase(input_str) == expected_result diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index ab36c64bc9..9dcee02ad6 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -24,6 +24,7 @@ def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): list_item_id="answer-item-id", return_to=return_to, return_to_block_id=return_to_block_id, + return_to_list_item_id=None, is_in_repeating_section=is_in_repeating_section, ) @@ -62,6 +63,7 @@ def test_date_answer_type(): list_item_id="answer-item-id", return_to="section-summary", return_to_block_id=None, + return_to_list_item_id=None, is_in_repeating_section=False, ) diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js new file mode 100644 index 0000000000..e73ff38be2 --- /dev/null +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -0,0 +1,220 @@ +import { assertSummaryValues, click, listItemIds } from "../../../helpers"; +import { expect } from "@wdio/globals"; +import AddVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-add.page.js"; +import AnyCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/any-cost.page.js"; +import AnyVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/any-vehicle.page.js"; +import BaseCostsSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-costs-section-summary.page.js"; +import CalculatedSummaryBaseCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/calculated-summary-base-cost.page.js"; +import CalculatedSummaryRunningCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/calculated-summary-running-cost.page.js"; +import GrandCalculatedSummaryVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/grand-calculated-summary-vehicle.page.js"; +import HubPage from "../../../base_pages/hub.page"; +import ListCollectorPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector.page.js"; +import VehicleDetailsSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-details-section-summary.page.js"; +import VehicleFuelBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-fuel-block.page.js"; +import VehicleMaintenanceBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-maintenance-block.page.js"; +import VehiclesSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicles-section-summary.page.js"; +import FinanceCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/finance-cost.page"; +import BaseCostPaymentBreakdownPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-cost-payment-breakdown.page"; + +describe("Grand Calculated Summary inside a repeating section", () => { + let vehicleListItemIds = []; + before("Load the survey", async () => { + await browser.openQuestionnaire("test_grand_calculated_summary_inside_repeating_section.json"); + }); + + it("Given I have a Grand Calculated Summary inside a repeating section, When I reach it for the first list item, Then I see placeholder content rendered correctly", async () => { + await click(HubPage.submit()); + await $(AnyCostPage.no()).click(); + await click(AnyCostPage.submit()); + await $(FinanceCostPage.answer()).setValue(90); + await click(FinanceCostPage.submit()); + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toContain( + "We calculate the total base cost for any owned vehicle to be £90.00. Is this correct?", + ); + await click(CalculatedSummaryBaseCostPage.submit()); + await $(BaseCostPaymentBreakdownPage.baseCredit()).setValue(30); + await $(BaseCostPaymentBreakdownPage.baseDebit()).setValue(40); + await click(BaseCostPaymentBreakdownPage.submit()); + await click(BaseCostsSectionPage.submit()); + await click(HubPage.submit()); + await $(AnyVehiclePage.yes()).click(); + await click(AnyVehiclePage.submit()); + await $(AddVehiclePage.vehicleName()).selectByAttribute("value", "Car"); + await click(AddVehiclePage.submit()); + await $(ListCollectorPage.yes()).click(); + await click(ListCollectorPage.submit()); + await $(AddVehiclePage.vehicleName()).selectByAttribute("value", "Van"); + await click(AddVehiclePage.submit()); + vehicleListItemIds = await listItemIds(); + await $(ListCollectorPage.no()).click(); + await click(ListCollectorPage.submit()); + await click(VehiclesSectionPage.submit()); + await click(HubPage.submit()); + await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(100); + await click(VehicleMaintenanceBlockPage.submit()); + await $(VehicleFuelBlockPage.vehicleFuelCost()).setValue(125); + await click(VehicleFuelBlockPage.submit()); + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + "We calculate the monthly running costs of your Car to be £225.00. Is this correct?", + ); + await click(CalculatedSummaryRunningCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Car is calculated to be £315.00. Is this correct?", + ); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toContain("Vehicle base cost"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toContain("Monthly Car costs"); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toContain("Grand total Car expenditure"); + assertSummaryValues(["£90.00", "£225.00", "£315.00"]); + }); + + it("Given I have a Grand Calculated Summary inside a repeating section, When I reach it for the second list item, Then I see placeholder content rendered correctly", async () => { + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await click(HubPage.submit()); + await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(50); + await click(VehicleMaintenanceBlockPage.submit()); + await $(VehicleFuelBlockPage.vehicleFuelCost()).setValue(45); + await click(VehicleFuelBlockPage.submit()); + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + "We calculate the monthly running costs of your Van to be £95.00. Is this correct?", + ); + await click(CalculatedSummaryRunningCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Van is calculated to be £185.00. Is this correct?", + ); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toContain("Vehicle base cost"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toContain("Monthly Van costs"); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toContain("Grand total Van expenditure"); + assertSummaryValues(["£90.00", "£95.00", "£185.00"]); + }); + + it("Given I am at a Grand Summary inside a repeating section, When I click the change link for a repeating calculated summary, Then I am taken to the correct page", async () => { + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostEdit()).click(); + await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I have used a change link for a repeating calculated summary, When I click the continue button, Then I am taken to the Grand Calculated Summary", async () => { + await click(CalculatedSummaryRunningCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I am at a Grand Summary inside a repeating section, When I click the change link for a non repeating calculated summary, Then I am taken to the correct page", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); + }); + + it("Given I have used a change link for a non repeating calculated summary from a repeating section, When I click the continue button, Then I am taken to the Grand Calculated Summary for the correct list item", async () => { + await click(CalculatedSummaryBaseCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I use a change link for a repeating calculated summary, When I use a change link there, Then pressing continue twice takes me back to the correct grand calculated summary", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostEdit()).click(); + await $(CalculatedSummaryRunningCostPage.vehicleMaintenanceCostEdit()).click(); + await expect(browser).toHaveUrlContaining(VehicleMaintenanceBlockPage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await click(VehicleMaintenanceBlockPage.submit()); + await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await click(CalculatedSummaryRunningCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I use a change link for a non repeating calculated summary, When I use a change link there, Then pressing continue twice takes me back to the correct grand calculated summary", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await $(CalculatedSummaryBaseCostPage.financeCostAnswerEdit()).click(); + await expect(browser).toHaveUrlContaining(FinanceCostPage.pageName); + await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + await click(FinanceCostPage.submit()); + await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); + await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + await click(CalculatedSummaryBaseCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I change a non repeating answer which results in the section being incomplete, When I press continue, Then I go to the next incomplete location with the list item id preserved", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await $(CalculatedSummaryBaseCostPage.financeCostAnswerEdit()).click(); + await $(FinanceCostPage.answer()).setValue(100); + await click(FinanceCostPage.submit()); + await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toContain( + "We calculate the total base cost for any owned vehicle to be £100.00. Is this correct?", + ); + await click(CalculatedSummaryBaseCostPage.submit()); + await expect(browser).toHaveUrlContaining(BaseCostPaymentBreakdownPage.pageName); + await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + }); + + it("Given I have changed a non repeating answer, When I return to the Grand Calculated Summary, Then I see the correctly updated values", async () => { + await click(BaseCostPaymentBreakdownPage.submit()); + + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Van is calculated to be £195.00. Is this correct?", + ); + }); + + it("Given I change a repeating answer, When I return to the Grand Calculated Summary, Then I see the correctly updated values", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostEdit()).click(); + await $(CalculatedSummaryRunningCostPage.vehicleMaintenanceCostEdit()).click(); + await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(75); + await click(VehicleMaintenanceBlockPage.submit()); + await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + "We calculate the monthly running costs of your Van to be £120.00. Is this correct?", + ); + await click(CalculatedSummaryRunningCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Van is calculated to be £220.00. Is this correct?", + ); + }); + + it("Given I edit the non-repeating calculated summary, When I return to the Hub, Then I see repeating sections are incomplete", async () => { + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await $(HubPage.summaryRowLink("vehicle-details-section-1")).click(); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toContain("Completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toContain("Completed"); + await $(HubPage.summaryRowLink("base-costs-section")).click(); + await $(BaseCostsSectionPage.financeCostAnswerEdit()).click(); + await $(FinanceCostPage.answer()).setValue(110); + await click(FinanceCostPage.submit()); + await click(CalculatedSummaryBaseCostPage.submit()); + await click(BaseCostPaymentBreakdownPage.submit()); + await click(BaseCostsSectionPage.submit()); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toContain("Partially completed"); + }); + + it("Given I have two partially complete repeating sections, When I press continue, Then I am taken straight to the grand calculated summary as it is the first incomplete block", async () => { + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[0]}/`); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Car is calculated to be £335.00. Is this correct?", + ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + }); + + it("Given I've completed the first repeating section, When I press continue, I am taken straight to the grand calculated summary of the second repeat", async () => { + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + "The total cost of owning and running your Van is calculated to be £230.00. Is this correct?", + ); + }); +}); diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 6385968d6a..465a75fded 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -215,3 +215,44 @@ def test_grand_calculated_summary_with_varying_decimal_places(self): self.assertInBody( "We calculate the grand total to be £230.58985. Is this correct?" ) + + def test_grand_calculated_summary_inside_repeating_section(self): + """ + Happy path for a grand calculated summary inside a repeating section + """ + self.launchSurvey("test_grand_calculated_summary_inside_repeating_section") + self.post() + self.post({"any-cost-answer": "No"}) + self.post({"finance-cost-answer": "150"}) + self.post() + self.post({"base-credit": "20", "base-debit": "30"}) + self.post() + self.post() + self.post({"any-vehicle-answer": "Yes"}) + self.post({"vehicle-name": "Car"}) + self.post({"list-collector-answer": "Yes"}) + self.post({"vehicle-name": "Motorbike"}) + self.post({"list-collector-answer": "No"}) + self.post() + self.post() + self.post({"vehicle-maintenance-cost": "100"}) + self.post({"vehicle-fuel-cost": "80"}) + self.assertInBody( + "We calculate the monthly running costs of your Car to be £180.00. Is this correct?" + ) + self.post() + self.assertInBody( + "The total cost of owning and running your Car is calculated to be £330.00. Is this correct?" + ) + self.post() + self.post() + self.post() + self.post({"vehicle-maintenance-cost": "40"}) + self.post({"vehicle-fuel-cost": "35"}) + self.assertInBody( + "We calculate the monthly running costs of your Motorbike to be £75.00. Is this correct?" + ) + self.post() + self.assertInBody( + "The total cost of owning and running your Motorbike is calculated to be £225.00. Is this correct?" + ) From d1f0a986771c97b1b9f15279b8401e405dd3a7b3 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:33:08 +0100 Subject: [PATCH 302/567] Schemas v3.73.1 (#1222) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 865d40f9b1..e8a603bd9e 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.73.0 +v3.73.1 From bb3f26c50f1a42c5219047d25d4785667372efed Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Mon, 9 Oct 2023 14:20:22 +0100 Subject: [PATCH 303/567] Return to list collector summary when navigating back from submit page (#1223) --- app/questionnaire/router.py | 7 ++++-- tests/app/questionnaire/test_router.py | 23 ++++++++++++++++++++ tests/functional/spec/list_collector.spec.js | 6 +++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 0a239ba7f8..4da768ab39 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -69,8 +69,11 @@ def get_first_incomplete_location_in_questionnaire_url(self) -> str: return self.get_next_location_url_for_end_of_section() def get_last_location_in_questionnaire_url(self) -> str | None: - section_key = self._get_last_complete_section_key() - if section_key: + if section_key := self._get_last_complete_section_key(): + if self.can_display_section_summary(section_key): + return url_for( + "questionnaire.get_section", section_id=section_key.section_id + ) routing_path = self.routing_path(section_key) return self.get_last_location_in_section(routing_path).url() diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index b1085c584b..a4ac2e6dc2 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -1781,6 +1781,29 @@ def test_last_block_not_on_path(self): assert completed_block_not_on_path == last_completed_block_in_progress_store assert expected_location_url == last_location_url + @pytest.mark.usefixtures("app") + def test_list_collector_final_summary_returns_to_section_summary(self): + self.schema = load_schema_from_name("test_list_collector_list_summary") + + self.progress_store = ProgressStore( + [ + ProgressDict( + section_id="section", + block_ids=[ + "introduction", + "primary-person-list-collector", + "list-collector", + "visitor-list-collector", + ], + status=CompletionStatus.COMPLETED, + ) + ] + ) + + last_location_url = self.router.get_last_location_in_questionnaire_url() + + assert "/questionnaire/sections/section/" == last_location_url + class TestRouterSectionResume(RouterTestCase): @pytest.mark.usefixtures("app") diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index a446b6d816..977b913957 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -231,5 +231,11 @@ describe("List Collector", () => { await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); await expect(await $(SectionSummaryListCollectorRemovePage.individualResponseGuidance()).isExisting()).to.equal(true); }); + + it("When the user reaches the submit page and navigates back, They should see the Section Summary", async () => { + await click(PeopleListSectionSummaryPage.submit()); + await click(SubmitPage.previous()); + await expect(await browser.getUrl()).to.contain(PeopleListSectionSummaryPage.pageName); + }); }); }); From 95d3149e3d49795f0c571dc00a51aaf0996c3f51 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:34:52 +0100 Subject: [PATCH 304/567] Add UKHSA ONS theme (#1225) * Add UKHSA ONS theme * Swap logos round * Change logo right padding to extra small --- app/helpers/template_helpers.py | 2 + app/survey_config/__init__.py | 6 +- app/survey_config/social_survey_config.py | 12 +- app/survey_config/survey_type.py | 1 + schemas/test/en/test_theme_ukhsa_ons.json | 75 ++ templates/assets/images/ons-logo-stacked.svg | 12 + .../assets/images/ukhsa-logo-stacked.svg | 717 ++++++++++++++++++ templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 35 + tests/functional/spec/theme_ukhsa_ons.spec.js | 16 + 10 files changed, 875 insertions(+), 3 deletions(-) create mode 100644 schemas/test/en/test_theme_ukhsa_ons.json create mode 100644 templates/assets/images/ons-logo-stacked.svg create mode 100644 templates/assets/images/ukhsa-logo-stacked.svg create mode 100644 tests/functional/spec/theme_ukhsa_ons.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 4a5e8ec402..9fbe96b2f0 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -27,6 +27,7 @@ ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, + UKHSAONSSocialSurveyConfig, WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType @@ -197,6 +198,7 @@ def survey_config_mapping( WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig ), SurveyType.CENSUS_NISRA: CensusNISRASurveyConfig, + SurveyType.UKHSA_ONS: UKHSAONSSocialSurveyConfig, } return survey_type_to_config[theme]( diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 56af71eee7..1f8425383c 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -15,11 +15,15 @@ WelshCensusSurveyConfig, ) from app.survey_config.link import Link -from app.survey_config.social_survey_config import SocialSurveyConfig +from app.survey_config.social_survey_config import ( + SocialSurveyConfig, + UKHSAONSSocialSurveyConfig, +) from app.survey_config.survey_config import SurveyConfig __all__ = [ "SocialSurveyConfig", + "UKHSAONSSocialSurveyConfig", "SurveyConfig", "CensusSurveyConfig", "CensusNISRASurveyConfig", diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 30bd716b82..7ec2abbd96 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -3,7 +3,7 @@ from flask_babel import lazy_gettext -from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL, ONS_URL, ONS_URL_CY +from app.settings import ACCOUNT_SERVICE_BASE_URL_SOCIAL, ONS_URL, ONS_URL_CY, read_file from app.survey_config.link import Link from app.survey_config.survey_config import SurveyConfig @@ -63,3 +63,13 @@ def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]] ] return None + + +@dataclass +class UKHSAONSSocialSurveyConfig(SocialSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/ukhsa-logo-stacked.svg" + ) + read_file("./templates/assets/images/ons-logo-stacked.svg") + masthead_logo_mobile: str = read_file( + "./templates/assets/images/ukhsa-logo-stacked.svg" + ) + read_file("./templates/assets/images/ons-logo-stacked.svg") diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index ae3b3d978a..bd56ef7102 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -16,3 +16,4 @@ class SurveyType(Enum): DESNZ_NI = "desnz-ni" CENSUS = "census" CENSUS_NISRA = "census-nisra" + UKHSA_ONS = "ukhsa-ons" diff --git a/schemas/test/en/test_theme_ukhsa_ons.json b/schemas/test/en/test_theme_ukhsa_ons.json new file mode 100644 index 0000000000..9c72466d79 --- /dev/null +++ b/schemas/test/en/test_theme_ukhsa_ons.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test UKHSA ONS Survey", + "theme": "ukhsa-ons", + "description": "A questionnaire to demo the UKHSA ONS survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "UKHSA ONS Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/ons-logo-stacked.svg b/templates/assets/images/ons-logo-stacked.svg new file mode 100644 index 0000000000..898fecb407 --- /dev/null +++ b/templates/assets/images/ons-logo-stacked.svg @@ -0,0 +1,12 @@ + diff --git a/templates/assets/images/ukhsa-logo-stacked.svg b/templates/assets/images/ukhsa-logo-stacked.svg new file mode 100644 index 0000000000..543064cfcb --- /dev/null +++ b/templates/assets/images/ukhsa-logo-stacked.svg @@ -0,0 +1,717 @@ + + UK Health Security Agency + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/errors/_base.html b/templates/errors/_base.html index 06a5a47cc0..b7d8e95fe5 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -3,5 +3,5 @@ {% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr", "desnz", "desnz-ni"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} -{% set SURVEY_TYPES_HEALTH = ["health"] %} +{% set SURVEY_TYPES_HEALTH = ["health", "ukhsa-ons"] %} {% set SURVEY_TYPES_ALL = SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT + SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index b7449148d8..54bf7bddf2 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines from typing import Type import pytest @@ -28,6 +29,7 @@ ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, + UKHSAONSSocialSurveyConfig, WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType @@ -363,6 +365,18 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): None, ], ), + ( + SurveyType.UKHSA_ONS, + "Test", + UKHSAONSSocialSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/ukhsa-logo-stacked.svg") + + read_file("./templates/assets/images/ons-logo-stacked.svg"), + read_file("./templates/assets/images/ukhsa-logo-stacked.svg") + + read_file("./templates/assets/images/ons-logo-stacked.svg"), + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): @@ -542,6 +556,11 @@ def test_service_links_context( "cy", f"{ONS_URL_CY}/aboutus/contactus/surveyenquiries/", ), + ( + UKHSAONSSocialSurveyConfig(), + "en", + f"{ONS_URL}/aboutus/contactus/surveyenquiries/", + ), ], ) def test_contact_us_url_context( @@ -641,6 +660,11 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cy/cookies/", ), + ( + UKHSAONSSocialSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/cookies/", + ), (SurveyConfig(), False, None), ], ) @@ -720,6 +744,11 @@ def test_cookie_settings_url_context( "cy", ACCOUNT_SERVICE_BASE_URL_SOCIAL, ), + ( + UKHSAONSSocialSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ), ], ) def test_cookie_domain_context( @@ -752,6 +781,7 @@ def test_cookie_domain_context( DBTDSITBusinessSurveyConfig(), DBTDSITNIBusinessSurveyConfig(), ORRBusinessSurveyConfig(), + UKHSAONSSocialSurveyConfig(), ], ) def test_cookie_domain_context_cookie_not_provided( @@ -861,6 +891,10 @@ def test_account_service_my_todo_url_context( SocialSurveyConfig(language_code="cy"), f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/cy/start/", ), + ( + UKHSAONSSocialSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", + ), ], ) def test_account_service_log_out_url_context( @@ -890,6 +924,7 @@ def test_account_service_log_out_url_context( (SurveyType.CENSUS, "en", CensusSurveyConfig), (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), (SurveyType.CENSUS_NISRA, "en", CensusNISRASurveyConfig), + (SurveyType.UKHSA_ONS, "en", UKHSAONSSocialSurveyConfig), (None, None, BusinessSurveyConfig), ], ) diff --git a/tests/functional/spec/theme_ukhsa_ons.spec.js b/tests/functional/spec/theme_ukhsa_ons.spec.js new file mode 100644 index 0000000000..fb0b6d6836 --- /dev/null +++ b/tests/functional/spec/theme_ukhsa_ons.spec.js @@ -0,0 +1,16 @@ +import RadioPage from "../generated_pages/theme_dbt_ni/radio.page"; +import { expect } from "@wdio/globals"; + +describe("Theme UKHSA-ONS", () => { + describe("Given I launch a UKHSA-ONS themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_ukhsa_ons.json"); + }); + + it("When I navigate to the radio page, Then I should see UKHSA-ONS theme content", async () => { + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics logo"); + await expect(await $("#ukhsa-logo-alt").getHTML()).toContain("UK Health Security Agency"); + }); + }); +}); From e8f4c476c6d1f6debd648b5003d3acdde875bab1 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:04:44 +0100 Subject: [PATCH 305/567] Schemas v3.74.0 (#1227) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e8a603bd9e..b07259f5dc 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.73.1 +v3.74.0 From 4b4e76faa666d69827a6320a218bf1235ccf43cd Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:23:44 +0100 Subject: [PATCH 306/567] Feat: Grand calculated summary value sources (#1215) * Add support for grand calculated summary value sources * Move string method into utilities * Fix params not being preserved when section is incomplete * Add value source resolver test for GCS --- app/questionnaire/placeholder_transforms.py | 14 +- app/questionnaire/value_source_resolver.py | 18 +- ...ated_summary_inside_repeating_section.json | 241 +++++++++++++++++ ..._calculated_summary_repeating_answers.json | 248 ++++++++++++++++++ .../test_value_source_resolver.py | 110 ++++++++ ...d_summary_inside_repeating_section.spec.js | 32 ++- ...lculated_summary_repeating_answers.spec.js | 54 ++++ ..._questionnaire_grand_calculated_summary.py | 12 + 8 files changed, 718 insertions(+), 11 deletions(-) diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index af9a2a6898..e12d3e67ab 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -6,8 +6,12 @@ from babel.dates import format_datetime from dateutil.relativedelta import relativedelta from flask_babel import ngettext +from werkzeug.datastructures import ImmutableDict -from app.questionnaire.questionnaire_schema import QuestionnaireSchema +from app.questionnaire.questionnaire_schema import ( + QuestionnaireSchema, + get_calculation_block_ids_for_grand_calculated_summary, +) from app.questionnaire.rules.operations import DateOffset from app.questionnaire.rules.operations_helper import OperationHelper from app.questionnaire.rules.utils import parse_datetime @@ -73,6 +77,14 @@ def _get_decimal_limit(self, unresolved_arguments: Mapping) -> int | None: decimal_limit = self.schema.get_decimal_limit_from_calculated_summaries( [identifier] ) + elif source == "grand_calculated_summary": + # Type ignore: Validator will have checked the id so the block is guaranteed to exist + grand_calculated_summary_block: ImmutableDict = self.schema.get_block(identifier) # type: ignore + decimal_limit = self.schema.get_decimal_limit_from_calculated_summaries( + get_calculation_block_ids_for_grand_calculated_summary( + grand_calculated_summary_block + ) + ) return decimal_limit diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 5de135c697..ee16e4baf3 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -230,20 +230,20 @@ def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: return list(list_model) - def _resolve_calculated_summary_value_source( + def _resolve_summary_with_calculation( self, value_source: Mapping, *, assess_routing_path: bool ) -> IntOrDecimal | None: - """Calculates the value for the 'calculation' used by the provided Calculated Summary. + """Calculates the value for the 'calculation' used by the provided Calculated or Grand Calculated Summary. - The caller is responsible for ensuring the provided Calculated Summary and its answers are on the path, + The caller is responsible for ensuring the provided summary and its components are on the path or providing routing_path_block_ids when initialising the value source resolver. """ - calculated_summary_block: ImmutableDict = self.schema.get_block(value_source["identifier"]) # type: ignore - - if not self._is_block_on_path(calculated_summary_block["id"]): + summary_block: ImmutableDict = self.schema.get_block(value_source["identifier"]) # type: ignore + if not self._is_block_on_path(summary_block["id"]): return None - calculation = calculated_summary_block["calculation"] + calculation = summary_block["calculation"] + # the calculation object for the old type of calculated summary block may contain answers_to_calculate instead of operation if calculation.get("answers_to_calculate"): operator = self.get_calculation_operator(calculation["calculation_type"]) list_item_id = self._resolve_list_item_id_for_value_source(value_source) @@ -319,8 +319,8 @@ def resolve( ) -> ValueSourceEscapedTypes | ValueSourceTypes: source = value_source["source"] - if source == "calculated_summary": - return self._resolve_calculated_summary_value_source( + if source in {"calculated_summary", "grand_calculated_summary"}: + return self._resolve_summary_with_calculation( value_source=value_source, assess_routing_path=True ) resolve_method_mapping = { diff --git a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json index 96c96edf6d..a2618836e6 100644 --- a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json +++ b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json @@ -808,6 +808,247 @@ ] } } + }, + { + "type": "Question", + "id": "gcs-breakdown-block", + "question": { + "id": "gcs-breakdown-question", + "title": { + "text": "How do you pay for the monthly fees of {vehicle_cost}?", + "placeholders": [ + { + "placeholder": "vehicle_cost", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "grand-calculated-summary-vehicle", + "source": "grand_calculated_summary" + } + } + } + ] + } + ] + }, + "type": "Calculated", + "warning": "These answers must add up to the total owning and running cost", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "identifier": "grand-calculated-summary-vehicle", + "source": "grand_calculated_summary" + }, + "answers_to_calculate": ["pay-debit", "pay-credit", "pay-other"], + "conditions": ["equals"] + } + ], + "answers": [ + { + "id": "pay-debit", + "label": "Amount paid by debit card", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "pay-credit", + "label": "Amount paid by credit card", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "pay-other", + "label": "Amount paid by other means", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Interstitial", + "id": "gcs-piping", + "content": { + "title": { + "text": "You have provided the following information about monthly expenditure for your {vehicle_name}.", + "placeholders": [ + { + "placeholder": "vehicle_name", + "value": { + "source": "answers", + "identifier": "vehicle-name" + } + } + ] + }, + "contents": [ + { + "list": [ + { + "text": "Monthly maintenance cost: {total_maintenance}", + "placeholders": [ + { + "placeholder": "total_maintenance", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "vehicle-maintenance-cost", + "source": "answers" + } + } + } + ] + } + ] + }, + { + "text": "Monthly fuel cost: {total_fuel}", + "placeholders": [ + { + "placeholder": "total_fuel", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "vehicle-fuel-cost", + "source": "answers" + } + } + } + ] + } + ] + }, + { + "text": "Total base cost: {total_base}", + "placeholders": [ + { + "placeholder": "total_base", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "calculated-summary-base-cost", + "source": "calculated_summary" + } + } + } + ] + } + ] + }, + { + "text": "Total running cost: {total_running}", + "placeholders": [ + { + "placeholder": "total_running", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "calculated-summary-running-cost", + "source": "calculated_summary" + } + } + } + ] + } + ] + }, + { + "text": "Total owning and running cost: {total}", + "placeholders": [ + { + "placeholder": "total", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "grand-calculated-summary-vehicle", + "source": "grand_calculated_summary" + } + } + } + ] + } + ] + }, + { + "text": "Paid by debit card: {debit}", + "placeholders": [ + { + "placeholder": "debit", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "pay-debit", + "source": "answers" + } + } + } + ] + } + ] + }, + { + "text": "Paid by credit card: {credit}", + "placeholders": [ + { + "placeholder": "credit", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "pay-credit", + "source": "answers" + } + } + } + ] + } + ] + }, + { + "text": "Paid by other means: {other}", + "placeholders": [ + { + "placeholder": "other", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "pay-other", + "source": "answers" + } + } + } + ] + } + ] + } + ] + } + ] + } } ] } diff --git a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json index 58e0f183b7..45fbd983b1 100644 --- a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json +++ b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json @@ -1093,6 +1093,9 @@ ] } }, + "summary": { + "show_on_completion": true + }, "groups": [ { "id": "group-7", @@ -1169,6 +1172,251 @@ ] } } + }, + { + "type": "Question", + "id": "internet-breakdown-block", + "question": { + "id": "internet-breakdown-question", + "title": { + "text": "How did you use the {internet_usage} across your devices?", + "placeholders": [ + { + "placeholder": "internet_usage", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "grand_calculated_summary", + "identifier": "grand-calculated-summary-4" + }, + "unit": "digital-gigabyte", + "unit_length": "short" + } + } + ] + } + ] + }, + "type": "Calculated", + "warning": "These answers must add up to the total internet usage", + "calculations": [ + { + "calculation_type": "sum", + "value": { + "identifier": "grand-calculated-summary-4", + "source": "grand_calculated_summary" + }, + "answers_to_calculate": ["internet-pc", "internet-phone"], + "conditions": ["equals"] + } + ], + "answers": [ + { + "id": "internet-pc", + "label": "Amount of internet usage via PC", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "digital-gigabyte", + "decimal_places": 2 + }, + { + "id": "internet-phone", + "label": "Amount of internet usage via Phone", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "digital-gigabyte", + "decimal_places": 2 + } + ] + } + } + ] + } + ] + }, + { + "id": "section-7", + "title": "Personal Expenditure", + "enabled": { + "when": { + "==": [ + "COMPLETED", + { + "source": "progress", + "selector": "section", + "identifier": "section-6" + } + ] + } + }, + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "group-8", + "blocks": [ + { + "type": "Question", + "id": "personal-expenditure-block", + "question": { + "id": "personal-expenditure-question", + "title": { + "text": "How much of the {total_expenditure} household expenditure do you contribute personally?", + "placeholders": [ + { + "placeholder": "total_expenditure", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "grand-calculated-summary-5", + "source": "grand_calculated_summary" + } + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "personal-expenditure-answer", + "label": "Personal contribution", + "mandatory": true, + "description": "Cannot exceed the total expenditure from section 6", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "identifier": "grand-calculated-summary-5", + "source": "grand_calculated_summary" + } + } + } + ] + } + }, + { + "type": "Interstitial", + "id": "grand-calculated-summary-piping", + "content": { + "title": "You have provided the following information about household expenditure and internet use.", + "contents": [ + { + "list": [ + { + "text": "Total household expenditure: {total_expenditure}", + "placeholders": [ + { + "placeholder": "total_expenditure", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "grand-calculated-summary-5", + "source": "grand_calculated_summary" + } + } + } + ] + } + ] + }, + { + "text": "Personal contribution: {personal_contribution}", + "placeholders": [ + { + "placeholder": "personal_contribution", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "identifier": "personal-expenditure-answer", + "source": "answers" + } + } + } + ] + } + ] + }, + { + "text": "Total internet usage: {internet_usage}", + "placeholders": [ + { + "placeholder": "internet_usage", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "grand_calculated_summary", + "identifier": "grand-calculated-summary-4" + }, + "unit": "digital-gigabyte", + "unit_length": "short" + } + } + ] + } + ] + }, + { + "text": "Usage by phone: {internet_phone}", + "placeholders": [ + { + "placeholder": "internet_phone", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "answers", + "identifier": "internet-phone" + }, + "unit": "digital-gigabyte", + "unit_length": "short" + } + } + ] + } + ] + }, + { + "text": "Usage by PC: {internet_pc}", + "placeholders": [ + { + "placeholder": "internet_pc", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "answers", + "identifier": "internet-pc" + }, + "unit": "digital-gigabyte", + "unit_length": "short" + } + } + ] + } + ] + } + ] + } + ] + } } ] } diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 8f91f01ca0..078b81c48a 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -41,6 +41,26 @@ def get_mock_schema(): return schema +def get_calculation_block( + block_id: str, summary_type: str, source_type: str, identifiers: list[str] +) -> dict: + return { + "id": block_id, + "type": summary_type, + "calculation": { + "operation": { + "+": [ + { + "source": source_type, + "identifier": identifier, + } + for identifier in identifiers + ] + } + }, + } + + def get_value_source_resolver( schema: QuestionnaireSchema = None, answer_store: AnswerStore = AnswerStore(), @@ -735,6 +755,96 @@ def test_new_calculated_summary_nested_value_source(mocker, list_item_id): ) +@pytest.mark.parametrize( + "gcs_list_item_id, cs_list_item_id_1, cs_list_item_id_2", + [ + (None, None, None), + ("item-1", "item-1", "item-1"), + ("item-1", "item-1", None), + ("item-1", None, None), + ], +) +def test_grand_calculated_summary_value_source( + mocker, gcs_list_item_id, cs_list_item_id_1, cs_list_item_id_2 +): + """ + Mocks out the grand calculated summary block and its child calculated summary blocks and tests + that the value source resolver correctly sums up all child answers when + 1) The GCS is in a repeat alongside both CS + 2) The GCS is in a repeat alongside one CS + 3) The GCS is in a repeat but neither CS is + 3) The GCS is not in a repeat and neither CS is + """ + schema = mocker.MagicMock() + + def mock_get_block(block_id: str) -> dict: + blocks = { + "number-total": get_calculation_block( + "number-total", + "GrandCalculatedSummary", + "calculated_summary", + ["calculated-summary-1", "calculated-summary-2"], + ), + "calculated-summary-1": get_calculation_block( + "calculated-summary-1", + "CalculatedSummary", + "answers", + ["answer-1", "answer-2"], + ), + "calculated-summary-2": get_calculation_block( + "calculated-summary-2", + "CalculatedSummary", + "answers", + ["answer-3", "answer-4"], + ), + } + return blocks[block_id] + + def mock_is_answer_repeating(answer_id: str) -> bool: + return (answer_id in {"answer-1", "answer-2"} and cs_list_item_id_1) or ( + answer_id in {"answer-3", "answer-4"} and cs_list_item_id_2 + ) + + schema.get_block = Mock(side_effect=mock_get_block) + schema.is_repeating_answer = Mock(side_effect=mock_is_answer_repeating) + schema.is_answer_dynamic = Mock(return_value=False) + schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) + + location = Location( + section_id="test-section", + block_id="test-block", + list_item_id=gcs_list_item_id, + ) + + value_source_resolver = get_value_source_resolver( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="answer-1", value=10, list_item_id=cs_list_item_id_1 + ), + AnswerDict( + answer_id="answer-2", value=5, list_item_id=cs_list_item_id_1 + ), + AnswerDict( + answer_id="answer-3", value=20, list_item_id=cs_list_item_id_2 + ), + AnswerDict( + answer_id="answer-4", value=30, list_item_id=cs_list_item_id_2 + ), + ] + ), + schema=schema, + list_item_id=gcs_list_item_id, + location=location, + ) + assert ( + value_source_resolver.resolve( + {"source": "grand_calculated_summary", "identifier": "number-total"} + ) + == 65 + ) + + @pytest.mark.parametrize( "answer_value, escaped_value", [ diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index e73ff38be2..1bb9cd6139 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -15,6 +15,8 @@ import VehicleMaintenanceBlockPage from "../../../generated_pages/grand_calculat import VehiclesSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicles-section-summary.page.js"; import FinanceCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/finance-cost.page"; import BaseCostPaymentBreakdownPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-cost-payment-breakdown.page"; +import GcsBreakdownBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-breakdown-block.page"; +import GcsPipingPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-piping.page"; describe("Grand Calculated Summary inside a repeating section", () => { let vehicleListItemIds = []; @@ -68,8 +70,31 @@ describe("Grand Calculated Summary inside a repeating section", () => { assertSummaryValues(["£90.00", "£225.00", "£315.00"]); }); - it("Given I have a Grand Calculated Summary inside a repeating section, When I reach it for the second list item, Then I see placeholder content rendered correctly", async () => { + it("Given I immediately use that Grand Calculated Summary for validation, When I enter a sum of values too high, Then I see an error message", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); + await $(GcsBreakdownBlockPage.payDebit()).setValue(100); + await $(GcsBreakdownBlockPage.payCredit()).setValue(115); + await $(GcsBreakdownBlockPage.payOther()).setValue(200); + await click(GcsBreakdownBlockPage.submit()); + await expect(await $(GcsBreakdownBlockPage.errorNumber()).getText()).toContain("Enter answers that add up to 315"); + }); + + it("Given I enter a valid value for the Grand Calculated Summary breakdown, When I press continue, Then I see an Interstitial page with my values correctly piped in", async () => { + await $(GcsBreakdownBlockPage.payOther()).setValue(100); + await click(GcsBreakdownBlockPage.submit()); + await expect(browser).toHaveUrlContaining(GcsPipingPage.pageName); + await expect(await $("body").getText()).toContain("Monthly maintenance cost: £100.00"); + await expect(await $("body").getText()).toContain("Monthly fuel cost: £125.00"); + await expect(await $("body").getText()).toContain("Total base cost: £90.00"); + await expect(await $("body").getText()).toContain("Total running cost: £225.00"); + await expect(await $("body").getText()).toContain("Total owning and running cost: £315.00"); + await expect(await $("body").getText()).toContain("Paid by debit card: £100.00"); + await expect(await $("body").getText()).toContain("Paid by credit card: £115.00"); + await expect(await $("body").getText()).toContain("Paid by other means: £100.00"); + }); + + it("Given I have a Grand Calculated Summary inside a repeating section, When I reach it for the second list item, Then I see placeholder content rendered correctly", async () => { + await click(GcsPipingPage.submit()); await click(VehicleDetailsSectionPage.submit()); await click(HubPage.submit()); await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(50); @@ -181,6 +206,11 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I edit the non-repeating calculated summary, When I return to the Hub, Then I see repeating sections are incomplete", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); + await $(GcsBreakdownBlockPage.payDebit()).setValue(100); + await $(GcsBreakdownBlockPage.payCredit()).setValue(110); + await $(GcsBreakdownBlockPage.payOther()).setValue(10); + await click(GcsBreakdownBlockPage.submit()); + await click(GcsPipingPage.submit()); await click(VehicleDetailsSectionPage.submit()); await $(HubPage.summaryRowLink("vehicle-details-section-1")).click(); await click(GrandCalculatedSummaryVehiclePage.submit()); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 7248e5f112..f82c147691 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -32,8 +32,14 @@ import Section4SummaryPage from "../../../generated_pages/grand_calculated_summa import Section5SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-5-summary.page"; import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; import { expect } from "@wdio/globals"; +import InternetBreakdownBlockPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/internet-breakdown-block.page"; +import Section6SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-6-summary.page"; +import PersonalExpenditureBlockPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/personal-expenditure-block.page"; +import GrandCalculatedSummaryPipingPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/grand-calculated-summary-piping.page"; describe("Feature: Grand Calculated Summary", () => { + const summaryRowTitles = ".ons-summary__row-title"; + describe("Given I have a Grand Calculated Summary across multiple sections", () => { before("Reaching the grand calculated summary section", async () => { await browser.openQuestionnaire("test_grand_calculated_summary_repeating_answers.json"); @@ -337,7 +343,25 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", ); + }); + + it("Given I pipe the grand calculated summary into the next question, When I press continue, Then I see the correct title", async () => { await click(GrandCalculatedSummary5Page.submit()); + await expect(await $(InternetBreakdownBlockPage.questionTitle()).getText()).toContain("How did you use the 100 GB across your devices?"); + }); + + it("Given I use the grand calculated summary for validation, When I enter values with too large a sum, Then I see a validation error", async () => { + await $(InternetBreakdownBlockPage.internetPc()).setValue(60); + await $(InternetBreakdownBlockPage.internetPhone()).setValue(60); + await click(InternetBreakdownBlockPage.submit()); + await expect(await $(InternetBreakdownBlockPage.errorNumber(1)).getText()).toContain("Enter answers that add up to 100"); + }); + + it("Given I use the grand calculated summary for validation, When I enter values with the correct sum, Then I progress to the summary page", async () => { + await $(InternetBreakdownBlockPage.internetPhone()).setValue(40); + await click(InternetBreakdownBlockPage.submit()); + await expect(browser).toHaveUrlContaining(Section6SummaryPage.pageName); + await click(Section6SummaryPage.submit()); }); it("Given I have a grand calculated summary featuring dynamic answers, When I add an item to the list collector and return to the hub, Then I see the section with dynamic answers is in progress, and the grand calculated summary section is not available", async () => { @@ -381,6 +405,9 @@ describe("Feature: Grand Calculated Summary", () => { "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); + await expect(browser).toHaveUrlContaining(Section6SummaryPage.pageName); + await expect(await $$(summaryRowTitles)[0].getText()).toContain("How did you use the 100 GB across your devices?"); + await click(Section6SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Completed"); }); @@ -428,6 +455,7 @@ describe("Feature: Grand Calculated Summary", () => { "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); + await click(Section6SummaryPage.submit()); }); it("Given I remove a list item involved in the grand calculated summary, When I confirm, Then I am taken to each affected calculated summary to reconfirm, and when I return to the Hub the grand calculated summary is in progress", async () => { @@ -466,5 +494,31 @@ describe("Feature: Grand Calculated Summary", () => { ); await click(GrandCalculatedSummary5Page.submit()); }); + + it("Given I have a further section depending on the grand calculated summary section, When I return to the Hub, Then I see the new section is available", async () => { + await click(Section6SummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-7")).getText()).toContain("Not started"); + await click(HubPage.submit()); + }); + + it("Given I use a grand calculated summary value as a maximum, When I enter a value that is too large, Then I see a validation error", async () => { + await expect(await $(PersonalExpenditureBlockPage.questionTitle()).getText()).toContain( + "How much of the £1,199.00 household expenditure do you contribute personally?", + ); + await $(PersonalExpenditureBlockPage.personalExpenditure()).setValue(1200); + await click(PersonalExpenditureBlockPage.submit()); + await expect(await $(PersonalExpenditureBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £1,199.00"); + }); + + it("Given I display multiple grand calculated summaries on an Interstitial page, When I reach the page, Then I see the correct values piped in", async () => { + await $(PersonalExpenditureBlockPage.personalExpenditure()).setValue(1100); + await click(PersonalExpenditureBlockPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryPipingPage.pageName); + await expect(await $("body").getText()).toContain("Total household expenditure: £1,199.00"); + await expect(await $("body").getText()).toContain("Personal contribution: £1,100.00"); + await expect(await $("body").getText()).toContain("Total internet usage: 85 GB"); + await expect(await $("body").getText()).toContain("Usage by phone: 40 GB"); + await expect(await $("body").getText()).toContain("Usage by PC: 60 GB"); + }); }); }); diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 465a75fded..e71e366ca8 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -245,6 +245,16 @@ def test_grand_calculated_summary_inside_repeating_section(self): "The total cost of owning and running your Car is calculated to be £330.00. Is this correct?" ) self.post() + self.post({"pay-debit": "110", "pay-credit": "120", "pay-other": "100"}) + self.assertInBody("Monthly maintenance cost: £100.00") + self.assertInBody("Monthly fuel cost: £80.00") + self.assertInBody("Total base cost: £150.00") + self.assertInBody("Total running cost: £180.00") + self.assertInBody("Total owning and running cost: £330.00") + self.assertInBody("Paid by debit card: £110.00") + self.assertInBody("Paid by credit card: £120.00") + self.assertInBody("Paid by other means: £100.00") + self.post() self.post() self.post() self.post({"vehicle-maintenance-cost": "40"}) @@ -256,3 +266,5 @@ def test_grand_calculated_summary_inside_repeating_section(self): self.assertInBody( "The total cost of owning and running your Motorbike is calculated to be £225.00. Is this correct?" ) + self.post() + self.post({"pay-debit": "25", "pay-credit": "120", "pay-other": "80"}) From d45d636b4cd7fe326f477a0f519c4bf0382287d1 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Fri, 20 Oct 2023 15:22:07 +0100 Subject: [PATCH 307/567] Updating makefile commands (#1226) * add single schema validation and the option to run a single functional test * Allow lint and tests to be run in one command for Python and JS * Allow functional tests to be run headless * allow dev folders such as venv and contents of gitignore to be ignored when calling make format and make lint * allow a chosen suite to be run * Exclude venv and tests directory to ensure python lint runs without errors * add a command that runs JS lint and test headless and made commands names consistent * Rename the commands to clarify the order of linting and testing when calling the command on JS/Python * Add extra commands README that developers can utilise * Remove venv from being ignored * Refactor commands to clarify the purpose and to ensure consistency in names * Alter commands that developers find useful * Improve the description of commands used for functional tests * Fix config and ignore files * Replace yarn commands with make commands * Update the file to make use of the new Make commands * Add new commands to generate pages and to generate new specs given a schema name * Replace yarn commands with Make commands * Refactor issues with running make commands * Remove incorrect pipenv commands * Clarify which suites are available to run using the test-functional-suite command * Correct the make commands to ensure the PR tests pass correctly * Update commands to use yarn commands directly rather than shell scripts * Remove --suite from generate_pages command * Temporary error to test if pullrequest.yml works as intended * Correct deliberate error to ensure pullrequest.yml works as intended * Delete unused file * Remove the .json to be required when validating a single schema --- .github/workflows/pull_request.yml | 10 +++---- .prettierignore | 3 ++- Makefile | 39 ++++++++++++++++++++++----- README.md | 42 ++++++++++++++++-------------- package.json | 2 +- scripts/run_tests_functional.sh | 31 ---------------------- setup.cfg | 5 ++++ tox.ini | 5 ---- 8 files changed, 67 insertions(+), 70 deletions(-) delete mode 100755 scripts/run_tests_functional.sh delete mode 100644 tox.ini diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 4fd86c8c19..30ea49f6e7 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -73,7 +73,7 @@ jobs: - name: Running translation tests run: pipenv run python -m scripts.extract_translation_templates --test - name: Python linting - run: pipenv run ./scripts/run_lint_python.sh + run: make lint-python - name: Get yarn cache id: get-yarn-cache run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT @@ -88,7 +88,7 @@ jobs: - name: Functional tests spec lint run: ./scripts/lint_functional_test_specs.sh - name: Javascript linting - run: yarn lint + run: make lint-js test-unit: needs: python-dependencies runs-on: ubuntu-22.04 @@ -121,7 +121,7 @@ jobs: - name: Link env vars run: ln -sf .development.env .env - name: Running unit tests - run: pipenv run ./scripts/run_tests_unit.sh + run: make test-unit validate-schemas: runs-on: ubuntu-22.04 steps: @@ -129,7 +129,7 @@ jobs: - name: Run validator run: ./scripts/run_validator.sh - name: Running schema tests - run: ./scripts/validate_test_schemas.sh + run: make validate-test-schemas test-functional: needs: [python-dependencies, node-dependencies] strategy: @@ -169,7 +169,7 @@ jobs: - name: Docker compose run: docker-compose --version && RUNNER_ENV_FILE=.functional-tests.env docker-compose up --build -d - name: Functional tests - run: ./scripts/run_tests_functional.sh ${{ matrix.suite }} + run: make test-functional-suite SUITE=${{ matrix.suite }} - name: Docker compose shutdown run: RUNNER_ENV_FILE=.functional-tests.env docker-compose kill docker-push: diff --git a/.prettierignore b/.prettierignore index b3bd224028..8eb11fc6b6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ -tests/functional/generated_pages/ \ No newline at end of file +# Exclude generated_pages directory +tests/functional/generated_pages/ diff --git a/Makefile b/Makefile index 670c25ee31..9be19e4b91 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ SCHEMAS_VERSION=`cat .schemas-version` DESIGN_SYSTEM_VERSION=`cat .design-system-version` RUNNER_ENV_FILE?=.development.env +SCHEMA_PATH=./schemas/test/en/ clean: find schemas/* -prune | grep -v "schemas/test" | xargs rm -r @@ -16,14 +17,17 @@ load-design-system-templates: build: load-design-system-templates load-schemas translate -lint: lint-python - yarn lint +generate-pages: + yarn generate_pages + +lint: lint-python lint-js lint-python: pipenv run ./scripts/run_lint_python.sh -format: format-python - yarn format +lint-test-python: lint-python test-unit + +format: format-python format-js format-python: pipenv run isort . @@ -35,11 +39,32 @@ test: test-unit: pipenv run ./scripts/run_tests_unit.sh -test-functional: - pipenv run ./scripts/run_tests_functional.sh +test-functional: generate-pages + yarn test_functional + +test-functional-headless: generate-pages + EQ_RUN_FUNCTIONAL_TESTS_HEADLESS='True' make test-functional + +test-functional-spec: generate-pages + yarn test_functional --spec ./tests/functional/spec/$(SPEC) + +test-functional-suite: generate-pages + yarn test_functional --suite $(SUITE) + +lint-js: + yarn lint + +format-js: + yarn format + +generate-spec: + pipenv run python -m tests.functional.generate_pages schemas/test/en/$(SCHEMA).json ./tests/functional/generated_pages/$(patsubst test_%,%,$(SCHEMA)) -r '../../base_pages' -s tests/functional/spec/$(SCHEMA).spec.js validate-test-schemas: - pipenv run ./scripts/validate_test_schemas.sh + ./scripts/validate_test_schemas.sh + +validate-test-schema: + ./scripts/validate_test_schemas.sh $(SCHEMA_PATH)$(SCHEMA).json translation-templates: pipenv run python -m scripts.extract_translation_templates diff --git a/README.md b/README.md index e1b694724c..bd4d45d318 100644 --- a/README.md +++ b/README.md @@ -217,11 +217,11 @@ yarn Available commands: | Command | Task | -| ---------------------- | --------------------------------------------------------------------------------------------------------- | -| `yarn test_functional` | Runs the functional tests through Webdriver (requires app running on localhost:5000 and generated pages). | -| `yarn generate_pages` | Generates the functional test pages. | -| `yarn lint` | Lints the JS, reporting errors/warnings. | -| `yarn format` | Format the json schemas. | +|------------------------| --------------------------------------------------------------------------------------------------------- | +| `make test-functional` | Runs the functional tests through Webdriver (requires app running on localhost:5000 and generated pages). | +| `make generate-pages` | Generates the functional test pages. | +| `make lint-js` | Lints the JS, reporting errors/warnings. | +| `make format-js` | Format the json schemas. | --- @@ -241,21 +241,18 @@ RUNNER_ENV_FILE=.functional-tests.env make run This will set the correct environment variables for running the functional tests. -Then you can run: +Then you can run either: ``` shell make test-functional ``` - -This will delete the `tests/functional/generated_pages` directory and regenerate all the files in it from the schemas. - -You can also individually run the `generate_pages` and `test_functional` yarn scripts: +or ``` shell -yarn generate_pages -yarn test_functional +make test-functional-headless ``` +This will delete the `tests/functional/generated_pages` directory and regenerate all the files in it from the schemas. To generate the pages manually you can run the `generate_pages` scripts with the schema directory. Run it from the `tests/functional` directory as follows: @@ -263,22 +260,21 @@ To generate the pages manually you can run the `generate_pages` scripts with the ./generate_pages.py ../../schemas/test/en/ ./generated_pages -r "../../base_pages" ``` -To generate a spec file with the imports included, you can use the `generate_pages.py` script on a single schema with the `-s` argument. - +To generate a spec file with the imports included, you can pass the schema name as an argument without the file extension, e.g. `SCHEMA=test_address`: ``` shell -./generate_pages.py ../../schemas/test/en/test_multiple_piping.json ./temp_directory -r "../../base_pages" -s spec/test_multiple_piping.spec.js +make generate-spec SCHEMA= ``` If you have already built the generated pages, then the functional tests can be executed with: ``` shell -yarn test_functional +make test-functional ``` -This can be limited to a single spec using: +This can be limited to a single spec where argument needed is the remainder of the path after `./tests/functional/spec/` (which is included in the command): ``` shell -yarn test_functional --spec ./tests/functional/spec/exit.spec.js +make test-functional-spec SPEC= ``` To run a single test, add `.only` into the name of any `describe` or `it` function: @@ -288,10 +284,16 @@ To run a single test, add `.only` into the name of any `describe` or `it` functi `it.only('Given this is a test', function() {...}` Test suites are configured in the `wdio.conf.js` file. -An individual test suite can be run using: +An individual test suite can be run using the suite names as the argument to this command. The suites that can be used with command below are: +* timeout_modal_expired +* timeout_modal_extended +* timeout_modal_extended_new_window +* features +* general +* components ``` shell -yarn test_functional --suite +make test-functional-suite SUITE= ``` To run the tests against a remote deployment you will need to specify the environment variable of EQ_FUNCTIONAL_TEST_ENV eg: diff --git a/package.json b/package.json index a51657cbd3..7470ecc575 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint": "yarn generate_pages && yarn lint:tests && yarn lint:test-schemas", "lint:tests": "prettier --check \"tests/functional/**/*.js\" && eslint \"tests/functional/**/*.js\"", "lint:test-schemas": "prettier --check \"schemas/test/*/*.json\" && eslint \"schemas/test/**/*.json\"", - "test_functional": "./node_modules/.bin/wdio tests/functional/wdio.conf.js --suite $1", + "test_functional": "./node_modules/.bin/wdio tests/functional/wdio.conf.js", "generate_pages": "rm -rf ./tests/functional/generated_pages && pipenv run python -m tests.functional.generate_pages schemas/test/en/ ./tests/functional/generated_pages -r '../../base_pages'", "format": "yarn format:tests && yarn format:test-schemas", "format:tests": "prettier \"tests/functional/**/*.js\" --write && eslint --fix \"tests/functional/**/*.js\"", diff --git a/scripts/run_tests_functional.sh b/scripts/run_tests_functional.sh deleted file mode 100755 index f02dfa5069..0000000000 --- a/scripts/run_tests_functional.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# -# Run functional tests -# -# NOTE: This script expects to be run from the project root with -# ./scripts/run_tests_functional.sh - -set -o pipefail - -function display_result { - RESULT=$1 - EXIT_STATUS=$2 - TEST=$3 - - if [ $RESULT -ne 0 ]; then - echo -e "\033[31m$TEST failed\033[0m" - exit $EXIT_STATUS - else - echo -e "\033[32m$TEST passed\033[0m" - fi -} - -# Run Functional tests -echo "Generating functional test pages" -yarn generate_pages - -echo "Running front end functional tests" -yarn test_functional $1 - -display_result $? 5 "Front end functional tests" - diff --git a/setup.cfg b/setup.cfg index 8af620026f..2518f8258d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,3 +6,8 @@ filterwarnings= ignore:.*formatargspec.*:DeprecationWarning ignore:.*isAlive.*:PendingDeprecationWarning +[flake8] +# Ignore node_modules and cloned repos when not in a virtual environment +exclude = node_modules/*,tests/*,src/* +max-line-length = 160 +ignore = C815,C816,W503,E203 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index a60575d8f7..0000000000 --- a/tox.ini +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -# Ignore node_modules and cloned repos when not in a virtual environment -exclude = node_modules/*,tests/*,src/* -max-line-length = 160 -ignore = C815,C816,W503,E203 From 265c704f6df4b5b69e7e01485f300d54fb668669 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:04:00 +0100 Subject: [PATCH 308/567] Schemas v3.75.0 (#1236) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b07259f5dc..0b2010c272 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.74.0 +v3.75.0 From 965dae059c939b4142ec5e1dc7a86d8ad7b3e00b Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 25 Oct 2023 09:59:37 +0100 Subject: [PATCH 309/567] Create ReturnLocation object to store return params (#1229) --- app/questionnaire/return_location.py | 25 ++ app/questionnaire/router.py | 126 ++++------ .../contexts/calculated_summary_context.py | 21 +- .../grand_calculated_summary_context.py | 9 +- app/views/contexts/section_summary_context.py | 8 +- app/views/contexts/summary/answer.py | 21 +- app/views/contexts/summary/block.py | 17 +- .../summary/calculated_summary_block.py | 18 +- app/views/contexts/summary/group.py | 28 +-- .../summary/list_collector_base_block.py | 10 +- .../contexts/summary/list_collector_block.py | 6 +- .../summary/list_collector_content_block.py | 2 +- app/views/contexts/summary/question.py | 23 +- app/views/handlers/block.py | 31 +-- app/views/handlers/calculation_summary.py | 4 +- app/views/handlers/list_action.py | 40 ++- app/views/handlers/list_add_question.py | 12 +- app/views/handlers/list_collector.py | 10 +- app/views/handlers/list_edit_question.py | 4 +- app/views/handlers/list_repeating_question.py | 16 +- app/views/handlers/primary_person_question.py | 2 +- app/views/handlers/question.py | 7 +- .../relationships/relationship_question.py | 4 +- .../app/questionnaire/test_return_location.py | 26 ++ tests/app/questionnaire/test_router.py | 238 +++++++++++++----- .../app/views/contexts/summary/test_answer.py | 18 +- .../app/views/contexts/summary/test_block.py | 5 +- .../views/contexts/summary/test_question.py | 33 +-- .../test_calculated_summary_context.py | 9 +- .../test_grand_calculated_summary_context.py | 4 +- 30 files changed, 426 insertions(+), 351 deletions(-) create mode 100644 app/questionnaire/return_location.py create mode 100644 tests/app/questionnaire/test_return_location.py diff --git a/app/questionnaire/return_location.py b/app/questionnaire/return_location.py new file mode 100644 index 0000000000..b86382384e --- /dev/null +++ b/app/questionnaire/return_location.py @@ -0,0 +1,25 @@ +from dataclasses import asdict, dataclass + + +@dataclass(kw_only=True, frozen=True) +class ReturnLocation: + """ + Used to store return locations in the questionnaire. + + return_to: The name of the type of summary page to return to + return_to_block_id: The block_id of the block to return to + return_to_answer_id: The answer_id of the answer to return to + return_to_list_item_id: The list_item_id to return to if the location is associated with a list + """ + + return_to: str | None = None + return_to_block_id: str | None = None + return_to_answer_id: str | None = None + return_to_list_item_id: str | None = None + + def to_dict(self, answer_id_is_anchor: bool = False) -> dict: + attributes = asdict(self) + if answer_id_is_anchor: + attributes["_anchor"] = attributes["return_to_answer_id"] + del attributes["return_to_answer_id"] + return {k: v for k, v in attributes.items() if v is not None} diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 4da768ab39..5c99d25041 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -12,6 +12,7 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey from app.questionnaire.path_finder import PathFinder +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.utilities.types import LocationType @@ -120,10 +121,7 @@ def get_next_location_url( self, location: LocationType, routing_path: RoutingPath, - return_to: str | None = None, - return_to_answer_id: str | None = None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, ) -> str: """ Get the next location in the section. If the section is complete, determine where to go next, @@ -135,13 +133,10 @@ def get_next_location_url( if return_to_url := self.get_return_to_location_url( location=location, - return_to=return_to, + return_location=return_location, routing_path=routing_path, is_for_previous=False, is_section_complete=is_section_complete, - return_to_answer_id=return_to_answer_id, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, ): return return_to_url @@ -160,9 +155,7 @@ def get_next_location_url( return self._get_next_block_url( location, routing_path, - return_to=return_to, - return_to_answer_id=return_to_answer_id, - return_to_block_id=return_to_block_id, + return_location, ) def _get_next_location_url_for_complete_section( @@ -177,23 +170,17 @@ def get_previous_location_url( self, location: LocationType, routing_path: RoutingPath, - return_to: str | None = None, - return_to_answer_id: str | None = None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, ) -> str | None: """ Returns the previous 'location' to visit given a set of user answers or returns to the summary if - the `return_to` var is set and the section is complete. + the `return_location.return_to` var is set and the section is complete. """ if return_to_url := self.get_return_to_location_url( location=location, - return_to=return_to, + return_location=return_location, routing_path=routing_path, is_for_previous=True, - return_to_answer_id=return_to_answer_id, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, ): return return_to_url @@ -207,17 +194,14 @@ def get_previous_location_url( return url_for( "questionnaire.relationships", last=True, - return_to=return_to, - _anchor=return_to_answer_id, + **return_location.to_dict(answer_id_is_anchor=True), ) return url_for( "questionnaire.block", block_id=previous_block_id, list_name=routing_path.list_name, list_item_id=routing_path.list_item_id, - return_to=return_to, - return_to_block_id=return_to_block_id, - _anchor=return_to_answer_id, + **return_location.to_dict(answer_id_is_anchor=True), ) if self.can_access_hub(): @@ -229,39 +213,30 @@ def get_return_to_location_url( self, *, location: LocationType, - return_to: str | None, + return_location: ReturnLocation, routing_path: RoutingPath, is_for_previous: bool, is_section_complete: bool | None = None, - return_to_answer_id: str | None = None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, ) -> str | None: - if not return_to: + if not return_location.return_to: return None - if return_to == "grand-calculated-summary" and ( + if return_location.return_to == "grand-calculated-summary" and ( url := self._get_return_to_for_grand_calculated_summary( - return_to=return_to, - return_to_block_id=return_to_block_id, + return_location=return_location, section_key=location.section_key, routing_path=routing_path, is_for_previous=is_for_previous, location=location, - return_to_answer_id=return_to_answer_id, - return_to_list_item_id=return_to_list_item_id, ) ): return url - if return_to.startswith("calculated-summary") and ( + if return_location.return_to.startswith("calculated-summary") and ( url := self._get_return_to_for_calculated_summary( - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, location=location, routing_path=routing_path, - return_to_answer_id=return_to_answer_id, + return_location=return_location, ) ): return url @@ -275,45 +250,49 @@ def get_return_to_location_url( # go to the next incomplete item in the section whilst preserving return to parameters return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, - return_to_block_id=return_to_block_id, - return_to=return_to, - return_to_list_item_id=return_to_list_item_id, routing_path=routing_path, + return_location=return_location, ) - if return_to == "section-summary": + if return_location.return_to == "section-summary": return self._get_section_url( - location.section_key, return_to_answer_id=return_to_answer_id + location.section_key, + return_to_answer_id=return_location.return_to_answer_id, ) - if return_to == "final-summary" and self.is_questionnaire_complete: + if ( + return_location.return_to == "final-summary" + and self.is_questionnaire_complete + ): return url_for( - "questionnaire.submit_questionnaire", _anchor=return_to_answer_id + "questionnaire.submit_questionnaire", + _anchor=return_location.return_to_answer_id, ) def _get_return_to_for_grand_calculated_summary( self, *, - return_to: str | None, - return_to_block_id: str | None, + return_location: ReturnLocation, section_key: SectionKey, routing_path: RoutingPath, is_for_previous: bool, location: LocationType, - return_to_answer_id: str | None = None, - return_to_list_item_id: str | None = None, ) -> str | None: """ Builds the return url for a grand calculated summary, and accounts for it possibly being in a different section to the calculated summaries it references """ - if not (return_to_block_id and self._schema.is_block_valid(return_to_block_id)): + if not ( + return_location.return_to_block_id + and self._schema.is_block_valid(return_location.return_to_block_id) + ): return None + return_to_block_id = return_location.return_to_block_id # Type ignore: if the block is valid, then we'll be able to find a section for it grand_calculated_summary_section: str = ( self._schema.get_section_id_for_block_id(return_to_block_id) # type: ignore ) - list_item_id = location.list_item_id or return_to_list_item_id + list_item_id = location.list_item_id or return_location.return_to_list_item_id list_name = ( self._list_store.get_list_name_for_list_item_id(list_item_id) if list_item_id @@ -335,7 +314,7 @@ def _get_return_to_for_grand_calculated_summary( ) if self.can_access_location( Location( - block_id=return_to_block_id, + block_id=return_location.return_to_block_id, section_id=grand_calculated_summary_section, list_item_id=list_item_id, list_name=list_name, @@ -344,30 +323,25 @@ def _get_return_to_for_grand_calculated_summary( ): return url_for( "questionnaire.block", - block_id=return_to_block_id, + block_id=return_location.return_to_block_id, list_item_id=list_item_id, list_name=list_name, - _anchor=return_to_answer_id, + _anchor=return_location.return_to_answer_id, ) # since the above may define a different routing_path, # retrieval of the next incomplete block needs to be here instead of returning None and allowing default behaviour return self._get_return_url_for_inaccessible_location( is_for_previous=is_for_previous, - return_to_block_id=return_to_block_id, - return_to=return_to, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, routing_path=routing_path, ) def _get_return_to_for_calculated_summary( self, *, - return_to: str, - return_to_block_id: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, location: LocationType, routing_path: RoutingPath, - return_to_answer_id: str | None = None, ) -> str | None: """ The return url for a calculated summary varies based on whether it's standalone or part of a grand calculated summary @@ -378,10 +352,10 @@ def _get_return_to_for_calculated_summary( block_id = None remaining: list[str] = [] # for a calculated summary this might have multiple items, e.g. a calculated summary to go to and then a grand calculated one - if return_to_block_id: + if return_location.return_to_block_id: # the first item is the block id to route to (e.g. a calculated summary to go back to first) # anything remaining forms where to go next (e.g. a grand calculated summary) - block_id, *remaining = return_to_block_id.split(",") + block_id, *remaining = return_location.return_to_block_id.split(",") if self.can_access_location( Location( @@ -393,8 +367,10 @@ def _get_return_to_for_calculated_summary( ): # if the next location is valid, the new url is that location, and the new 'return to block id' is just what remains return_to_block_id = ",".join(remaining) if remaining else None + # remove first item and return the remaining ones - return_to_remaining = ",".join(return_to.split(",")[1:]) or None + # Type ignore: return_location.return_to will always be populated at this point + return_to_remaining = ",".join(return_location.return_to.split(",")[1:]) or None # type: ignore return url_for( "questionnaire.block", @@ -403,17 +379,15 @@ def _get_return_to_for_calculated_summary( list_item_id=location.list_item_id, return_to=return_to_remaining, return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, - _anchor=return_to_answer_id, + return_to_list_item_id=return_location.return_to_list_item_id, + _anchor=return_location.return_to_answer_id, ) def _get_return_url_for_inaccessible_location( self, *, is_for_previous: bool, - return_to_block_id: str | None, - return_to: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, routing_path: RoutingPath, ) -> str | None: """ @@ -422,7 +396,7 @@ def _get_return_url_for_inaccessible_location( """ if ( not is_for_previous - and return_to + and return_location.return_to and ( next_incomplete_location := self._get_first_incomplete_location_in_section( routing_path @@ -430,9 +404,7 @@ def _get_return_url_for_inaccessible_location( ) ): return next_incomplete_location.url( - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + **return_location.to_dict(), ) def get_next_location_url_for_end_of_section(self) -> str: @@ -575,7 +547,7 @@ def _is_section_enabled(self, section: Mapping) -> bool: def _get_next_block_url( location: LocationType, routing_path: RoutingPath, - **kwargs: str | None, + return_location: ReturnLocation, ) -> str: # Type ignore: the location will have a block next_block_id = routing_path[routing_path.index(location.block_id) + 1] # type: ignore @@ -585,7 +557,7 @@ def _get_next_block_url( list_name=routing_path.list_name, list_item_id=routing_path.list_item_id, _external=False, - **kwargs, + **return_location.to_dict(), ) @staticmethod diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 3daac5e6db..cb64aaf312 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -15,6 +15,7 @@ QuestionnaireSchema, get_calculated_summary_answer_ids, ) +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.schema_utils import get_answer_ids_in_block @@ -41,9 +42,7 @@ def __init__( routing_path: RoutingPath, current_location: LocationType, supplementary_data_store: SupplementaryDataStore, - return_to: str | None = None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, ) -> None: super().__init__( language, @@ -57,9 +56,7 @@ def __init__( ) self.routing_path_block_ids = routing_path.block_ids self.current_location = current_location - self.return_to = return_to - self.return_to_block_id = return_to_block_id - self.return_to_list_item_id = return_to_list_item_id + self.return_location = return_location @cached_property def rendered_block(self) -> dict: @@ -84,8 +81,8 @@ def build_groups_for_section( return_to = "calculated-summary" # Type ignore: safe to assume block_id is not None return_to_block_id: str = self.current_location.block_id # type: ignore - if self.return_to == "grand-calculated-summary": - return_to_block_id += f",{self.return_to_block_id}" + if self.return_location.return_to == "grand-calculated-summary": + return_to_block_id += f",{self.return_location.return_to_block_id}" return_to += ",grand-calculated-summary" return [ Group( @@ -100,10 +97,12 @@ def build_groups_for_section( language=self._language, progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=self.return_to_list_item_id, summary_type="CalculatedSummary", + return_location=ReturnLocation( + return_to=return_to, + return_to_block_id=return_to_block_id, + return_to_list_item_id=self.return_location.return_to_list_item_id, + ), ).serialize() for group in section["groups"] ] diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 8e3c9225c4..337d5eac09 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -6,6 +6,7 @@ from app.questionnaire.questionnaire_schema import ( get_calculation_block_ids_for_grand_calculated_summary, ) +from app.questionnaire.return_location import ReturnLocation from app.views.contexts.calculated_summary_context import CalculatedSummaryContext from app.views.contexts.summary.group import Group @@ -81,10 +82,12 @@ def build_groups_for_section( language=self._language, progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, - return_to="grand-calculated-summary", - return_to_block_id=self.current_location.block_id, - return_to_list_item_id=self.current_location.list_item_id, summary_type="GrandCalculatedSummary", + return_location=ReturnLocation( + return_to="grand-calculated-summary", + return_to_block_id=self.current_location.block_id, + return_to_list_item_id=self.current_location.list_item_id, + ), ).serialize() for group in section["groups"] ] diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 90b3e5f94c..2d9d2f7d3f 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -12,6 +12,7 @@ from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.questionnaire_schema import LIST_COLLECTORS_WITH_REPEATING_BLOCKS +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content from app.views.contexts.context import Context @@ -154,8 +155,9 @@ def build_summary( language=self._language, progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, - return_to=return_to, - return_to_block_id=None, + return_location=ReturnLocation( + return_to=return_to, + ), view_submitted_response=view_submitted_response, ).serialize() for group in refactored_groups @@ -189,7 +191,7 @@ def _custom_summary_elements( location=self.current_location, language=self._language, supplementary_data_store=self._supplementary_data_store, - return_to="section-summary", + return_location=ReturnLocation(return_to="section-summary"), ) yield list_collector_block.list_summary_element(summary_element) diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index b1c1fa3ecc..ea11a130b2 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -3,6 +3,7 @@ from flask import url_for from app.data_models.answer import AnswerValueEscapedTypes +from app.questionnaire.return_location import ReturnLocation RadioCheckboxTypes = dict[str, str | AnswerValueEscapedTypes | None] DateRangeTypes = dict[str, AnswerValueEscapedTypes | None] @@ -21,9 +22,7 @@ def __init__( block_id: str, list_name: str | None, list_item_id: str | None, - return_to: str | None, - return_to_block_id: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, is_in_repeating_section: bool, ) -> None: self.id = answer_schema["id"] @@ -39,9 +38,7 @@ def __init__( block_id=block_id, list_name=list_name, list_item_id=list_item_id, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, is_in_repeating_section=is_in_repeating_section, ) @@ -64,9 +61,7 @@ def _build_link( block_id: str, list_name: str | None, list_item_id: str | None, - return_to: str | None, - return_to_block_id: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, is_in_repeating_section: bool, ) -> str: return url_for( @@ -74,14 +69,14 @@ def _build_link( list_name=list_name, block_id=block_id, list_item_id=list_item_id, - return_to=return_to, + return_to=return_location.return_to, return_to_answer_id=self._return_to_answer_id( - return_to=return_to, + return_to=return_location.return_to, list_item_id=list_item_id, is_in_repeating_section=is_in_repeating_section, ), - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_to_block_id=return_location.return_to_block_id, + return_to_list_item_id=return_location.return_to_list_item_id, _anchor=self.id, ) diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index e92f39fe28..82760387ac 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -10,6 +10,7 @@ ) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.schema_utils import find_pointers_containing from app.questionnaire.value_source_resolver import ValueSourceResolver @@ -29,9 +30,7 @@ def __init__( response_metadata: MutableMapping, schema: QuestionnaireSchema, location: LocationType, - return_to: str | None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, language: str, @@ -72,9 +71,7 @@ def __init__( list_store=list_store, metadata=metadata, response_metadata=response_metadata, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, progress_store=progress_store, supplementary_data_store=supplementary_data_store, language=language, @@ -88,9 +85,7 @@ def get_question( list_store: ListStore, metadata: MetadataProxy | None, response_metadata: MutableMapping, - return_to: str | None, - return_to_block_id: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, language: str, @@ -121,9 +116,7 @@ def get_question( value_source_resolver=self._value_source_resolver, location=self.location, block_id=self.id, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, metadata=metadata, response_metadata=response_metadata, language=language, diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py index 8ade52136f..b7ee16ae80 100644 --- a/app/views/contexts/summary/calculated_summary_block.py +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -11,6 +11,7 @@ ) from app.data_models.metadata_proxy import MetadataProxy from app.questionnaire import QuestionnaireSchema +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.utilities.types import LocationType @@ -18,7 +19,6 @@ class CalculatedSummaryBlock: - # pylint: disable=too-many-locals def __init__( self, block_schema: Mapping, @@ -29,9 +29,7 @@ def __init__( response_metadata: MutableMapping, schema: QuestionnaireSchema, location: LocationType, - return_to: str | None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, progress_store: ProgressStore, routing_path_block_ids: Iterable[str], supplementary_data_store: SupplementaryDataStore, @@ -46,9 +44,7 @@ def __init__( self.id = block_schema["id"] self.title = block_schema["calculation"]["title"] - self._return_to = return_to - self._return_to_block_id = return_to_block_id - self._return_to_list_item_id = return_to_list_item_id + self._return_location = return_location self._block_schema = block_schema self._schema = schema if self._schema.is_block_in_repeating_section(self.id): @@ -86,16 +82,18 @@ def __init__( def _build_link(self) -> str: # not required if the calculated summary is in the repeat alongside the GCS return_to_list_item_id = ( - self._return_to_list_item_id if not self._list_item_id else None + self._return_location.return_to_list_item_id + if not self._list_item_id + else None ) return url_for( "questionnaire.block", block_id=self.id, list_name=self._list_name, list_item_id=self._list_item_id, - return_to=self._return_to, + return_to=self._return_location.return_to, return_to_answer_id=self.id, - return_to_block_id=self._return_to_block_id, + return_to_block_id=self._return_location.return_to_block_id, return_to_list_item_id=return_to_list_item_id, _anchor=self.id, ) diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index dbba25f0a5..303bad3947 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -15,6 +15,7 @@ LIST_COLLECTORS_WITH_REPEATING_BLOCKS, is_list_collector_block_editable, ) +from app.questionnaire.return_location import ReturnLocation from app.survey_config.link import Link from app.utilities.types import LocationType from app.views.contexts.summary.block import Block @@ -26,7 +27,6 @@ class Group: - # pylint: disable=too-many-locals def __init__( self, *, @@ -41,9 +41,7 @@ def __init__( language: str, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, - return_to: str | None, - return_to_block_id: str | None = None, - return_to_list_item_id: str | None = None, + return_location: ReturnLocation, summary_type: str | None = None, view_submitted_response: bool | None = False, ) -> None: @@ -63,11 +61,9 @@ def __init__( response_metadata=response_metadata, schema=schema, location=self.location, - return_to=return_to, + return_location=return_location, progress_store=progress_store, language=language, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, view_submitted_response=view_submitted_response, summary_type=summary_type, supplementary_data_store=supplementary_data_store, @@ -97,12 +93,10 @@ def _build_blocks_and_links( response_metadata: MutableMapping, schema: QuestionnaireSchema, location: LocationType, - return_to: str | None, + return_location: ReturnLocation, progress_store: ProgressStore, supplementary_data_store: SupplementaryDataStore, language: str, - return_to_block_id: str | None, - return_to_list_item_id: str | None, view_submitted_response: bool | None = False, summary_type: str | None = None, ) -> list[dict[str, Block]]: @@ -139,8 +133,7 @@ def _build_blocks_and_links( location=location, language=language, supplementary_data_store=supplementary_data_store, - return_to=return_to, - return_to_block_id=return_to_block_id, + return_location=return_location, ) repeating_answer_blocks = ( list_collector_block.get_repeating_block_related_answer_blocks( @@ -165,9 +158,7 @@ def _build_blocks_and_links( response_metadata=response_metadata, schema=schema, location=location, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, progress_store=progress_store, language=language, supplementary_data_store=supplementary_data_store, @@ -187,9 +178,7 @@ def _build_blocks_and_links( response_metadata=response_metadata, schema=schema, location=location, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, supplementary_data_store=supplementary_data_store, @@ -221,9 +210,8 @@ def _build_blocks_and_links( schema=schema, location=location, language=language, - return_to=return_to, + return_location=return_location, supplementary_data_store=supplementary_data_store, - return_to_block_id=return_to_block_id, ) list_summary_element = list_collector_block.list_summary_element( summary_item diff --git a/app/views/contexts/summary/list_collector_base_block.py b/app/views/contexts/summary/list_collector_base_block.py index 6d4c39967c..c28cc988e0 100644 --- a/app/views/contexts/summary/list_collector_base_block.py +++ b/app/views/contexts/summary/list_collector_base_block.py @@ -9,6 +9,7 @@ from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import is_list_collector_block_editable +from app.questionnaire.return_location import ReturnLocation from app.utilities.types import LocationType from app.views.contexts import list_context from app.views.contexts.summary.block import Block @@ -28,8 +29,7 @@ def __init__( location: LocationType, language: str, supplementary_data_store: SupplementaryDataStore, - return_to: str | None, - return_to_block_id: str | None = None, + return_location: ReturnLocation, ) -> None: self._location = location self._placeholder_renderer = PlaceholderRenderer( @@ -55,8 +55,7 @@ def __init__( self._routing_path_block_ids = routing_path_block_ids self._progress_store = progress_store self._supplementary_data_store = supplementary_data_store - self._return_to = return_to - self._return_to_block_id = return_to_block_id + self._return_location = return_location @property def list_context(self) -> list_context.ListContext: @@ -136,8 +135,7 @@ def _get_related_answer_blocks_by_list_item_id( list_item_id=list_id, section_id=section_id, ), - return_to=self._return_to, - return_to_block_id=self._return_to_block_id, + return_location=self._return_location, progress_store=self._progress_store, supplementary_data_store=self._supplementary_data_store, language=self._language, diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 04b4ccdd08..4a5a6da971 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -61,7 +61,7 @@ def list_summary_element(self, summary: Mapping) -> dict: for_list=list_collector_block["for_list"], section_id=self._location.section_id, has_repeating_blocks=bool(list_collector_block.get("repeating_blocks")), - return_to=self._return_to, + return_to=self._return_location.return_to, edit_block_id=edit_block_id, remove_block_id=remove_block_id, primary_person_edit_block_id=primary_person_edit_block_id, @@ -90,7 +90,7 @@ def _add_link( "questionnaire.block", list_name=summary["for_list"], block_id=list_collector_block["add_block"]["id"], - return_to=self._return_to, + return_to=self._return_location.return_to, ) if driving_question_block := self._schema.get_driving_question_for_list( @@ -99,5 +99,5 @@ def _add_link( return url_for( "questionnaire.block", block_id=driving_question_block["id"], - return_to=self._return_to, + return_to=self._return_location.return_to, ) diff --git a/app/views/contexts/summary/list_collector_content_block.py b/app/views/contexts/summary/list_collector_content_block.py index b0e0ff5a8e..d4317b5e68 100644 --- a/app/views/contexts/summary/list_collector_content_block.py +++ b/app/views/contexts/summary/list_collector_content_block.py @@ -38,7 +38,7 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: for_list=list_collector_block["for_list"], section_id=self._location.section_id, has_repeating_blocks=bool(list_collector_block.get("repeating_blocks")), - return_to=self._return_to, + return_to=self._return_location.return_to, ) return { diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 77b3f4c427..250a3b161e 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -15,6 +15,7 @@ from app.forms.field_handlers.select_handlers import DynamicAnswerOptions from app.questionnaire import QuestionnaireSchema, QuestionSchemaType from app.questionnaire.placeholder_renderer import PlaceholderRenderer +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.utilities.types import LocationType @@ -40,9 +41,7 @@ def __init__( value_source_resolver: ValueSourceResolver, location: LocationType, block_id: str, - return_to: Optional[str], - return_to_block_id: Optional[str] = None, - return_to_list_item_id: Optional[str] = None, + return_location: ReturnLocation, metadata: MetadataProxy | None, response_metadata: MutableMapping, language: str, @@ -74,9 +73,7 @@ def __init__( question_schema=question_schema, block_id=block_id, list_name=location.list_name if location else None, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, metadata=metadata, response_metadata=response_metadata, language=language, @@ -98,9 +95,7 @@ def _build_answers( question_schema: QuestionSchemaType, block_id: str, list_name: str | None, - return_to: str | None, - return_to_block_id: str | None, - return_to_list_item_id: str | None, + return_location: ReturnLocation, metadata: MetadataProxy | None, response_metadata: MutableMapping, language: str, @@ -112,9 +107,9 @@ def _build_answers( list_name=list_name, block_id=block_id, list_item_id=self.list_item_id, - return_to=return_to, - return_to_answer_id=answer_id if return_to else None, - return_to_list_item_id=return_to_list_item_id, + return_to=return_location.return_to, + return_to_answer_id=answer_id if return_location.return_to else None, + return_to_list_item_id=return_location.return_to_list_item_id, _anchor=question_schema["answers"][0]["id"], ) @@ -151,9 +146,7 @@ def _build_answers( block_id=block_id, list_name=list_name, list_item_id=list_item_id or self.list_item_id, - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=return_to_list_item_id, + return_location=return_location, is_in_repeating_section=self._is_in_repeating_section, ).serialize() summary_answers.append(summary_answer) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 38f8792e9e..4bf47e072b 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -11,6 +11,7 @@ from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.relationship_location import RelationshipLocation +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities import safe_content @@ -41,10 +42,14 @@ def __init__( self.block: ImmutableDict = self._schema.get_block(self._current_location.block_id) # type: ignore self._routing_path = self._get_routing_path() self.page_title: Optional[str] = None - self._return_to = request_args.get("return_to") - self._return_to_answer_id = request_args.get("return_to_answer_id") - self._return_to_block_id = request_args.get("return_to_block_id") - self._return_to_list_item_id = request_args.get("return_to_list_item_id") + + self._return_location = ReturnLocation( + return_to=request_args.get("return_to"), + return_to_block_id=request_args.get("return_to_block_id"), + return_to_answer_id=request_args.get("return_to_answer_id"), + return_to_list_item_id=request_args.get("return_to_list_item_id"), + ) + self.resume = "resume" in request_args if not self.is_location_valid(): @@ -57,12 +62,8 @@ def current_location(self) -> LocationType: return self._current_location @property - def return_to(self) -> str | None: - return self._return_to - - @property - def return_to_block_id(self) -> str | None: - return self._return_to_block_id + def return_location(self) -> ReturnLocation: + return self._return_location @cached_property def questionnaire_store_updater(self) -> QuestionnaireStoreUpdater: @@ -109,20 +110,14 @@ def get_previous_location_url(self) -> str | None: return self.router.get_previous_location_url( self._current_location, self._routing_path, - self._return_to, - self._return_to_answer_id, - self._return_to_block_id, - self._return_to_list_item_id, + self.return_location, ) def get_next_location_url(self) -> str: return self.router.get_next_location_url( self._current_location, self._routing_path, - self._return_to, - self._return_to_answer_id, - self._return_to_block_id, - self._return_to_list_item_id, + self.return_location, ) def handle_post(self) -> None: diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 14395e35e7..816c2abc45 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -19,9 +19,7 @@ def get_context(self) -> dict[str, dict]: response_metadata=self._questionnaire_store.response_metadata, current_location=self._current_location, routing_path=self._routing_path, - return_to=self.return_to, - return_to_block_id=self.return_to_block_id, - return_to_list_item_id=self._return_to_list_item_id, + return_location=self._return_location, supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) context = summary_context.build_view_context() diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index 97ec85c38d..de73ca753a 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -2,6 +2,7 @@ from werkzeug.datastructures import ImmutableDict from app.questionnaire.location import Location +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.views.handlers.question import Question @@ -39,15 +40,12 @@ def get_previous_location_url(self) -> str: block_id = self._request_args.get("previous") return self._get_location_url( - block_id=block_id, - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + block_id=block_id, return_location=self.return_location ) def get_section_or_final_summary_url(self) -> str | None: if ( - self._return_to == "section-summary" + self.return_location.return_to == "section-summary" and self.router.can_display_section_summary( self.parent_location.section_key ) @@ -55,11 +53,15 @@ def get_section_or_final_summary_url(self) -> str | None: return url_for( "questionnaire.get_section", section_id=self.parent_location.section_id, - _anchor=self._return_to_answer_id, + _anchor=self.return_location.return_to_answer_id, ) - if self._return_to == "final-summary" and self.router.is_questionnaire_complete: + if ( + self.return_location.return_to == "final-summary" + and self.router.is_questionnaire_complete + ): return url_for( - "questionnaire.submit_questionnaire", _anchor=self._return_to_answer_id + "questionnaire.submit_questionnaire", + _anchor=self.return_location.return_to_answer_id, ) def get_next_location_url(self) -> str: @@ -74,15 +76,13 @@ def get_next_location_url(self) -> str: return self.router.get_next_location_url( self.parent_location, self._routing_path, - self._return_to, - self._return_to_answer_id, - self._return_to_block_id, + self.return_location, ) return self.parent_location.url( - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + return_to=self.return_location.return_to, + return_to_answer_id=self.return_location.return_to_answer_id, + return_to_block_id=self.return_location.return_to_block_id, ) def handle_post(self) -> None: @@ -101,24 +101,18 @@ def _get_location_url( self, *, block_id: str | None = None, - return_to: str | None = None, - return_to_answer_id: str | None = None, - return_to_block_id: str | None = None, + return_location: ReturnLocation, anchor: str | None = None, ) -> str: if block_id and self._schema.is_block_valid(block_id): # Type ignore: the above line check that block_id exists and is valid and therefore section exists section_id: str = self._schema.get_section_id_for_block_id(block_id) # type: ignore return Location(section_id=section_id, block_id=block_id).url( - return_to=return_to, - return_to_answer_id=return_to_answer_id, - return_to_block_id=return_to_block_id, + **return_location.to_dict(), _anchor=anchor, ) return self.parent_location.url( - return_to=return_to, - return_to_answer_id=return_to_answer_id, - return_to_block_id=return_to_block_id, + **return_location.to_dict(), _anchor=anchor, ) diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index 1a87db7dd3..2fa530368d 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -24,15 +24,15 @@ def get_next_location_url(self) -> str: list_name=self.parent_block["for_list"], list_item_id=self._list_item_id, block_id=repeating_blocks[0]["id"], - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + return_to=self.return_location.return_to, + return_to_answer_id=self.return_location.return_to_answer_id, + return_to_block_id=self.return_location.return_to_block_id, ) return self.parent_location.url( - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + return_to=self.return_location.return_to, + return_to_answer_id=self.return_location.return_to_answer_id, + return_to_block_id=self.return_location.return_to_block_id, ) def handle_post(self) -> None: diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 5ec262621a..542734bc15 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -28,9 +28,7 @@ def get_next_location_url(self) -> str: "questionnaire.block", list_name=self.rendered_block["for_list"], block_id=self.rendered_block["add_block"]["id"], - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) return add_url @@ -44,9 +42,7 @@ def get_next_location_url(self) -> str: list_name=self.list_name, list_item_id=incomplete_block.list_item_id, block_id=incomplete_block.block_id, - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) return repeating_block_url @@ -69,7 +65,7 @@ def _get_list_context(self) -> dict[str, dict]: for_list=self.list_name, edit_block_id=self.rendered_block.get("edit_block", {}).get("id"), remove_block_id=self.rendered_block.get("remove_block", {}).get("id"), - return_to=self._return_to, + return_to=self.return_location.return_to, section_id=self.current_location.section_id, has_repeating_blocks=bool(self.repeating_block_ids), ) diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index a9cc1ae08a..04c4cada20 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -43,9 +43,7 @@ def get_next_location_url(self) -> str: list_name=first_incomplete_block.list_name, list_item_id=first_incomplete_block.list_item_id, block_id=first_incomplete_block.block_id, - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) return super().get_next_location_url() diff --git a/app/views/handlers/list_repeating_question.py b/app/views/handlers/list_repeating_question.py index bf2901ef02..d2bef07d6e 100644 --- a/app/views/handlers/list_repeating_question.py +++ b/app/views/handlers/list_repeating_question.py @@ -16,11 +16,9 @@ def get_previous_location_url(self) -> str: # since the list collector won't be in a repeating section, use the parent location which doesn't have a list item id if url := self.router.get_return_to_location_url( location=self.parent_location, - return_to=self._return_to, + return_location=self.return_location, routing_path=self._routing_path, is_for_previous=True, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, ): return url @@ -37,9 +35,7 @@ def get_previous_location_url(self) -> str: list_name=self.current_location.list_name, list_item_id=self.current_location.list_item_id, block_id=previous_repeating_block_id, - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) if edit_block := self._schema.get_edit_block_for_list_collector( @@ -50,15 +46,11 @@ def get_previous_location_url(self) -> str: list_name=self.current_location.list_name, list_item_id=self.current_location.list_item_id, block_id=edit_block["id"], - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) return self.parent_location.url( - return_to=self._return_to, - return_to_answer_id=self._return_to_answer_id, - return_to_block_id=self._return_to_block_id, + **self.return_location.to_dict(), ) def handle_post(self) -> None: diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index 45965f6a74..2d0bbe802a 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -40,7 +40,7 @@ def get_previous_location_url(self) -> str: def get_next_location_url(self) -> str: return self.router.get_next_location_url( - self.parent_location, self._routing_path, self._return_to + self.parent_location, self._routing_path, self.return_location ) def handle_post(self) -> None: diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 1bd79990b4..ecfe162df1 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -117,12 +117,7 @@ def get_next_location_url(self) -> str: return location_url return self.router.get_next_location_url( - self._current_location, - self._routing_path, - self._return_to, - self._return_to_answer_id, - self._return_to_block_id, - self._return_to_list_item_id, + self._current_location, self._routing_path, self._return_location ) def _get_answers_for_question(self, question_json: Mapping) -> dict: diff --git a/app/views/handlers/relationships/relationship_question.py b/app/views/handlers/relationships/relationship_question.py index 1022afed35..95b4654182 100644 --- a/app/views/handlers/relationships/relationship_question.py +++ b/app/views/handlers/relationships/relationship_question.py @@ -107,7 +107,7 @@ def get_previous_location_url(self) -> str: return previous_location.url() return self.router.get_previous_location_url( # type: ignore - self.parent_location, self._routing_path + self.parent_location, self._routing_path, self.return_location ) def get_next_location_url(self) -> str: @@ -119,5 +119,5 @@ def get_next_location_url(self) -> str: return next_location.url() return self.router.get_next_location_url( - self.parent_location, self._routing_path, self._return_to + self.parent_location, self._routing_path, self.return_location ) diff --git a/tests/app/questionnaire/test_return_location.py b/tests/app/questionnaire/test_return_location.py new file mode 100644 index 0000000000..352f6fd905 --- /dev/null +++ b/tests/app/questionnaire/test_return_location.py @@ -0,0 +1,26 @@ +from app.questionnaire.return_location import ReturnLocation + +TEST_RETURN_LOCATION_OBJECT = ReturnLocation( + return_to="test-return-to", + return_to_block_id="test-return-to-block-id", + return_to_answer_id="test-return-to-answer-id", + return_to_list_item_id="return-to-list-item-id", +) + + +def test_return_location_to_dict(): + assert TEST_RETURN_LOCATION_OBJECT.to_dict() == { + "return_to": "test-return-to", + "return_to_block_id": "test-return-to-block-id", + "return_to_answer_id": "test-return-to-answer-id", + "return_to_list_item_id": "return-to-list-item-id", + } + + +def test_return_location_to_dict_with_anchor_return_to_answer_id(): + assert TEST_RETURN_LOCATION_OBJECT.to_dict(answer_id_is_anchor=True) == { + "return_to": "test-return-to", + "return_to_block_id": "test-return-to-block-id", + "_anchor": "test-return-to-answer-id", + "return_to_list_item_id": "return-to-list-item-id", + } diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index a4ac2e6dc2..620aeec302 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -12,6 +12,7 @@ from app.data_models.progress_store import ProgressStore from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location, SectionKey +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name @@ -362,8 +363,12 @@ def test_within_section(self): ], section_id="default-section", ) + return_location = ReturnLocation() + next_location = self.router.get_next_location_url( - current_location, routing_path + current_location, + routing_path, + return_location, ) expected_location = Location( @@ -393,8 +398,12 @@ def test_last_block_in_section_but_section_is_not_complete_when_routing_backward routing_path = RoutingPath( block_ids=["block-1", "block-2", "block-1"], section_id="section-1" ) + return_location = ReturnLocation() + next_location = self.router.get_next_location_url( - current_location, routing_path + current_location, + routing_path, + return_location, ) assert "questionnaire/block-2/" in next_location @@ -424,8 +433,10 @@ def test_return_to_section_summary_section_is_complete(self): block_ids=["insurance-type", "insurance-address", "listed"], section_id="property-details-section", ) + return_location = ReturnLocation(return_to="section-summary") + next_location = self.router.get_next_location_url( - current_location, routing_path, return_to="section-summary" + current_location, routing_path, return_location ) assert "/questionnaire/sections/property-details-section/" in next_location @@ -462,8 +473,10 @@ def test_return_to_section_summary_section_is_in_progress(self): ], section_id="property-details-section", ) + return_location = ReturnLocation(return_to="section-summary") + next_location = self.router.get_next_location_url( - current_location, routing_path, return_to="section-summary" + current_location, routing_path, return_location ) assert ( @@ -488,8 +501,11 @@ def test_section_summary_on_completion_true(self): section_id="accommodation-section", block_id="proxy" ) routing_path = RoutingPath(block_ids=["proxy"], section_id="default-section") + + return_location = ReturnLocation() + next_location = self.router.get_next_location_url( - current_location, routing_path + current_location, routing_path, return_location ) assert "questionnaire/sections/accommodation-section/" in next_location @@ -514,8 +530,10 @@ def test_section_summary_on_completion_false(self): block_ids=["employment-status", "employment-type"], section_id="employment-section", ) + return_location = ReturnLocation() + next_location = self.router.get_next_location_url( - current_location, routing_path + current_location, routing_path, return_location ) expected_location_url = url_for("questionnaire.get_questionnaire") @@ -560,13 +578,16 @@ def test_return_to_calculated_summary(self, schema): section_id="default-section", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + return_location = ReturnLocation( return_to_answer_id="first-number-answer", return_to="calculated-summary", return_to_block_id="currency-total-playback", ) + + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + expected_location = Location( section_id="default-section", block_id="currency-total-playback", @@ -617,14 +638,18 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): section_id="default-section", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + return_location = ReturnLocation( return_to_answer_id="answer-3", return_to="calculated-summary", return_to_block_id="calculated-summary-block", ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_location, + ) + expected_location = Location( section_id="default-section", block_id="block-4", @@ -634,8 +659,9 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): "questionnaire.block", list_item_id=expected_location.list_item_id, block_id=expected_location.block_id, - return_to="calculated-summary", - return_to_block_id="calculated-summary-block", + return_to=return_location.return_to, + return_to_block_id=return_location.return_to_block_id, + return_to_answer_id=return_location.return_to_answer_id, ) assert expected_location_url == next_location_url @@ -678,11 +704,15 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( block_ids=["fifth-number-block", "sixth-number-block"], section_id="default-section", ) + + return_location = ReturnLocation( + return_to="calculated-summary", + return_to_block_id=return_to_block_id, + ) next_location_url = self.router.get_next_location_url( current_location, routing_path, - return_to="calculated-summary", - return_to_block_id=return_to_block_id, + return_location, ) assert expected_url == next_location_url @@ -712,13 +742,16 @@ def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schem block_ids=["fifth-number-block", "sixth-number-block"], section_id="default-section", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="calculated-summary", return_to_block_id="fourth-number-block", ) + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + # return_to_block_id is still passed here as although it is not currently on the path it may be in future once incomplete questions are # answered so needs to be preserved assert ( @@ -744,14 +777,17 @@ def test_return_to_grand_calculated_summary_from_answer( block_ids=["distance-calculated-summary-1"], section_id="section-1", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", return_to_answer_id="distance-calculated-summary-1", return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", ) + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + expected_previous_url = url_for( "questionnaire.block", return_to="grand-calculated-summary", @@ -780,13 +816,17 @@ def test_return_to_grand_calculated_summary_from_calculated_summary( block_ids=["distance-calculated-summary-1"], section_id="section-1", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="grand-calculated-summary", return_to_answer_id="distance-calculated-summary-1", return_to_block_id="distance-grand-calculated-summary", ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_location, + ) expected_previous_url = url_for( "questionnaire.block", @@ -874,13 +914,14 @@ def test_return_to_repeating_grand_calculated_summary_from_calculated_summary( list_name="vehicles", list_item_id="ZIrqqR", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + return_location = ReturnLocation( return_to="grand-calculated-summary", return_to_block_id="grand-calculated-summary-vehicle", return_to_list_item_id=return_to_list_item_id, ) + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) expected_next_url = url_for( "questionnaire.block", list_name="vehicles", @@ -918,7 +959,7 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( "block-3", "calculated-summary-2", ], - status="IN_PROGRESS", + status=CompletionStatus.IN_PROGRESS, ) ] ) @@ -936,13 +977,16 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( ], section_id="section-1", ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + return_location = ReturnLocation( return_to="grand-calculated-summary", return_to_answer_id="calculated-summary-1", return_to_block_id=return_to_block_id, ) + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_location, + ) # because calculated summary 3 isn't done, should go there before jumping to the grand calculated summary # test from grand-calculated-summary-1 which is in the same section, and grand-calculated-summary-2 which is in another @@ -950,6 +994,7 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( "questionnaire.block", return_to="grand-calculated-summary", return_to_block_id=return_to_block_id, + return_to_answer_id=return_location.return_to_answer_id, block_id="calculated-summary-3", ) @@ -990,13 +1035,16 @@ def test_return_to_calculated_summary_from_incomplete_section( ], section_id="section-1", ) + return_location = ReturnLocation( + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="first-number-block", + return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + ) # the test is being done as part of a two-step return to but its identical functionally next_location_url = self.router.get_next_location_url( current_location, routing_path, - return_to="calculated-summary,grand-calculated-summary", - return_to_answer_id="first-number-block", - return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + return_location, ) # should take you to the second-number-block before going back to the calculated summary @@ -1004,6 +1052,7 @@ def test_return_to_calculated_summary_from_incomplete_section( "questionnaire.block", return_to="calculated-summary,grand-calculated-summary", return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", + return_to_answer_id="first-number-block", block_id="second-number-block", ) @@ -1108,13 +1157,15 @@ def test_return_to_inaccessible_summary_routes_to_next_incomplete_block( ] ) - next_location_url = self.router.get_next_location_url( - current_location, - routing_path, + return_location = ReturnLocation( return_to=return_to, return_to_block_id=return_to_block_id, ) + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + expected_next_url = url_for( "questionnaire.block", return_to=return_to, @@ -1146,8 +1197,9 @@ def test_redirects_to_submit_page_when_questionnaire_complete( routing_path = RoutingPath( block_ids=["name-block"], section_id="default-section" ) + return_location = ReturnLocation() next_location = self.router.get_next_location_url( - current_location, routing_path + current_location, routing_path, return_location ) assert url_for("questionnaire.submit_questionnaire") == next_location @@ -1169,8 +1221,9 @@ def test_return_to_final_summary_questionnaire_and_section_is_complete(self): ) current_location = Location(section_id="test-section", block_id="test-forced") routing_path = RoutingPath(block_ids=["test-forced"], section_id="test-section") + return_location = ReturnLocation(return_to="final-summary") next_location = self.router.get_next_location_url( - current_location, routing_path, return_to="final-summary" + current_location, routing_path, return_location ) assert url_for("questionnaire.submit_questionnaire") == next_location @@ -1195,8 +1248,9 @@ def test_return_to_final_summary_section_is_in_progress(self): block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) + return_location = ReturnLocation(return_to="final-summary") next_location = self.router.get_next_location_url( - current_location, routing_path, return_to="final-summary" + current_location, routing_path, return_location ) assert "/questionnaire/numbers/?return_to=final-summary" in next_location @@ -1220,8 +1274,9 @@ def test_return_to_final_summary_questionnaire_is_not_complete(self): current_location = Location(section_id="test-section", block_id="test-forced") routing_path = RoutingPath(block_ids=["test-forced"], section_id="test-section") + return_location = ReturnLocation(return_to="final-summary") next_location = self.router.get_next_location_url( - current_location, routing_path, return_to="final-summary" + current_location, routing_path, return_location ) expected_location = Location( section_id="test-section-2", @@ -1245,8 +1300,9 @@ def test_within_section(self): block_ids=["mandatory-checkbox", "non-mandatory-checkbox"], section_id="default-section", ) + return_location = ReturnLocation() previous_location_url = self.router.get_previous_location_url( - current_location, routing_path + current_location, routing_path, return_location ) expected_location_url = Location( section_id="default-section", block_id="mandatory-checkbox" @@ -1268,14 +1324,19 @@ def test_return_to_calculated_summary(self): ], section_id="default-section", ) - previous_location_url = self.router.get_previous_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="calculated-summary", return_to_answer_id="first-number-answer", return_to_block_id="currency-total-playback-skipped-fourth", ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_location, + ) + expected_location = Location( section_id="default-section", block_id="currency-total-playback-skipped-fourth", @@ -1328,13 +1389,17 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( ], section_id="section-1", ) - previous_location_url = self.router.get_previous_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", return_to_answer_id="second-number-block", return_to_block_id="number-calculated-summary-1,number-grand-calculated-summary", ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_location, + ) # return to can't go to the distance calculated summary, so go to previous block with return params preserved expected_previous_url = url_for( "questionnaire.block", @@ -1384,13 +1449,15 @@ def test_return_to_grand_calculated_summary_from_calculated_summary_incomplete_s ], section_id="section-1", ) - previous_location_url = self.router.get_previous_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to="grand-calculated-summary", return_to_answer_id="distance-calculated-summary-1", return_to_block_id="distance-grand-calculated-summary", ) + previous_location_url = self.router.get_previous_location_url( + current_location, routing_path, return_location + ) # return to can't go to the grand calculated summary, so routing is just to the previous block in the section with return params preserved expected_previous_url = url_for( "questionnaire.block", @@ -1447,13 +1514,17 @@ def test_return_to_grand_calculated_summary_invalid_url( ], section_id="section-1", ) - previous_location_url = self.router.get_previous_location_url( - current_location, - routing_path, + + return_location = ReturnLocation( return_to=return_to, return_to_answer_id="distance-calculated-summary-1", return_to_block_id=return_to_block_id, ) + previous_location_url = self.router.get_previous_location_url( + current_location, + routing_path, + return_location, + ) assert expected_url == previous_location_url @@ -1478,13 +1549,17 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( block_ids=["calculated-summary-6"], section_id="section-5", ) - next_location_url = self.router.get_previous_location_url( - parent_location, - routing_path, + + return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", return_to_answer_id="calculated-summary-6", return_to_block_id="calculated-summary-6,grand-calculated-summary-3", ) + next_location_url = self.router.get_previous_location_url( + parent_location, + routing_path, + return_location, + ) expected_previous_url = url_for( "questionnaire.block", @@ -1517,11 +1592,14 @@ def test_return_to_section_summary_section_is_complete(self): block_ids=["insurance-type", "insurance-address", "listed"], section_id="default-section", ) + return_location = ReturnLocation( + return_to="section-summary", + return_to_answer_id="insurance-address-answer", + ) previous_location_url = self.router.get_previous_location_url( current_location, routing_path, - return_to="section-summary", - return_to_answer_id="insurance-address-answer", + return_location, ) assert ( @@ -1550,11 +1628,14 @@ def test_return_to_section_summary_section_is_in_progress(self): block_ids=["insurance-type", "insurance-address", "listed"], section_id="default-section", ) + return_location = ReturnLocation( + return_to="section-summary", + return_to_answer_id="insurance-address-answer", + ) previous_location_url = self.router.get_previous_location_url( current_location, routing_path, - return_to="section-summary", - return_to_answer_id="insurance-address-answer", + return_location, ) assert ( @@ -1586,11 +1667,14 @@ def test_return_to_final_summary_section_is_complete(self): block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) + return_location = ReturnLocation( + return_to="final-summary", + return_to_answer_id="dessert-answer", + ) previous_location = self.router.get_previous_location_url( current_location, routing_path, - return_to="final-summary", - return_to_answer_id="dessert-answer", + return_location, ) assert "/questionnaire/submit/#dessert-answer" in previous_location @@ -1619,11 +1703,14 @@ def test_return_to_final_summary_section_is_in_progress(self): block_ids=["radio", "dessert", "dessert-confirmation", "numbers"], section_id="default-section", ) + return_location = ReturnLocation( + return_to="final-summary", + return_to_answer_id="dessert-answer", + ) previous_location = self.router.get_previous_location_url( current_location, routing_path, - return_to="final-summary", - return_to_answer_id="dessert-answer", + return_location, ) assert ( @@ -1658,8 +1745,12 @@ def test_is_none_on_first_block_single_section(self): current_location = Location( section_id="default-section", block_id="mandatory-checkbox" ) + return_location = ReturnLocation() + previous_location_url = self.router.get_previous_location_url( - current_location, routing_path + current_location, + routing_path, + return_location, ) assert previous_location_url is None @@ -1688,8 +1779,12 @@ def test_is_none_on_first_block_second_section(self): routing_path = RoutingPath( block_ids=["house-type"], section_id="house-details-section" ) + return_location = ReturnLocation() + previous_location_url = self.router.get_previous_location_url( - current_location, routing_path + current_location, + routing_path, + return_location, ) assert previous_location_url is None @@ -1708,8 +1803,13 @@ def test_is_not_none_on_first_block_in_section(self): block_ids=["employment-status", "employment-type"], section_id="employment-section", ) + + return_location = ReturnLocation() + previous_location_url = self.router.get_previous_location_url( - current_location, routing_path + current_location, + routing_path, + return_location, ) assert url_for("questionnaire.get_questionnaire") == previous_location_url diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index 9dcee02ad6..14e22ecef4 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -1,5 +1,6 @@ import pytest +from app.questionnaire.return_location import ReturnLocation from app.views.contexts.summary.answer import Answer @@ -16,15 +17,18 @@ ], ) def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): + return_location = ReturnLocation( + return_to=return_to, + return_to_block_id=return_to_block_id, + ) + answer = Answer( answer_schema={"id": "answer-id", "label": "Answer Label", "type": "date"}, answer_value="An answer", block_id="house-type", list_name="answer-list", list_item_id="answer-item-id", - return_to=return_to, - return_to_block_id=return_to_block_id, - return_to_list_item_id=None, + return_location=return_location, is_in_repeating_section=is_in_repeating_section, ) @@ -55,15 +59,17 @@ def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): @pytest.mark.usefixtures("app") def test_date_answer_type(): # When + return_location = ReturnLocation( + return_to="section-summary", + ) + answer = Answer( answer_schema={"id": "answer-id", "label": "", "type": "date"}, answer_value=None, block_id="house-type", list_name="answer-list", list_item_id="answer-item-id", - return_to="section-summary", - return_to_block_id=None, - return_to_list_item_id=None, + return_location=return_location, is_in_repeating_section=False, ) diff --git a/tests/app/views/contexts/summary/test_block.py b/tests/app/views/contexts/summary/test_block.py index 60ef08903f..a2e53eac3b 100644 --- a/tests/app/views/contexts/summary/test_block.py +++ b/tests/app/views/contexts/summary/test_block.py @@ -1,5 +1,6 @@ from app.data_models import ProgressStore from app.questionnaire.location import Location +from app.questionnaire.return_location import ReturnLocation from app.views.contexts.summary.block import Block @@ -13,6 +14,8 @@ def test_create_block(mocker): } location = Location(section_id="a-section") + return_location = ReturnLocation(return_to="final-summary") + question = mocker.MagicMock() question.serialize = mocker.MagicMock(return_value="A Question") @@ -29,7 +32,7 @@ def test_create_block(mocker): response_metadata=mocker.MagicMock(), schema=mocker.MagicMock(), location=location, - return_to="final-summary", + return_location=return_location, progress_store=ProgressStore(), language="en", supplementary_data_store=mocker.MagicMock(), diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index 7f38634742..e0437d506f 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -4,6 +4,7 @@ from app.data_models import Answer, ListStore, ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore from app.questionnaire import QuestionnaireSchema +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver from app.utilities.schema import load_schema_from_name @@ -147,7 +148,7 @@ def address_question( rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), location=None, block_id="address-block", - return_to=None, + return_location=ReturnLocation(), value_source_resolver=get_value_source_resolver( answer_store, list_store, schema ), @@ -209,7 +210,7 @@ def test_create_question( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -367,7 +368,7 @@ def test_concatenate_number_and_checkbox_answers( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -417,7 +418,7 @@ def test_merge_date_range_answers( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -472,7 +473,7 @@ def test_merge_multiple_date_range_answers( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -525,7 +526,7 @@ def test_create_question_with_multiple_answers( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -581,7 +582,7 @@ def test_checkbox_button_options( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -639,7 +640,7 @@ def test_checkbox_button_detail_answer_empty( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -732,7 +733,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -792,7 +793,7 @@ def test_checkbox_button_other_option_text( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -884,7 +885,7 @@ def test_radio_answer_with_detail_answers_returns_correct_value( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -969,7 +970,7 @@ def test_answer_types_selected_option_label( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -1024,7 +1025,7 @@ def test_dynamic_checkbox_answer_options( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -1104,7 +1105,7 @@ def test_dynamic_answer_options( ), location=None, block_id="house-type", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -1177,7 +1178,7 @@ def test_get_answer( ), location=None, block_id="address-group", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, @@ -1339,7 +1340,7 @@ def test_dynamic_answers( ), location=None, block_id="group", - return_to=None, + return_location=ReturnLocation(), language="en", metadata={}, response_metadata={}, diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 657ccade3a..43904b8610 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -2,6 +2,7 @@ from app.data_models import AnswerStore, ListStore from app.questionnaire import Location +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.views.contexts.calculated_summary_context import CalculatedSummaryContext from tests.app.views.contexts import assert_summary_context @@ -123,6 +124,7 @@ def test_build_view_context_for_currency_calculated_summary( routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), supplementary_data_store=supplementary_data_store, + return_location=ReturnLocation(return_to_answer_id=return_to_answer_id), ) context = calculated_summary_context.build_view_context() @@ -221,9 +223,10 @@ def test_build_view_context_for_return_to_calculated_summary( response_metadata={}, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), - return_to=return_to, - return_to_block_id=return_to_block_id, supplementary_data_store=supplementary_data_store, + return_location=ReturnLocation( + return_to=return_to, return_to_block_id=return_to_block_id + ), ) context = calculated_summary_context.build_view_context() @@ -301,6 +304,7 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), current_location=Location(section_id="section-1", block_id=block_id), supplementary_data_store=supplementary_data_store, + return_location=ReturnLocation(), ) context = calculated_summary_context.build_view_context() @@ -409,6 +413,7 @@ def test_build_view_context_for_calculated_summary_with_answers_from_repeating_b response_metadata={}, routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), current_location=Location(section_id="section-1", block_id=block_id), + return_location=ReturnLocation(), ) context = calculated_summary_context.build_view_context() diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index f6fd32e49c..5544d6bd8e 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -3,6 +3,7 @@ from app.data_models import CompletionStatus from app.data_models.progress_store import ProgressStore from app.questionnaire import Location +from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.views.contexts.grand_calculated_summary_context import ( GrandCalculatedSummaryContext, @@ -88,9 +89,8 @@ def test_build_view_context_for_grand_calculated_summary( response_metadata={}, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), - return_to=None, - return_to_block_id=None, supplementary_data_store=supplementary_data_store, + return_location=ReturnLocation(), ) context = grand_calculated_summary_context.build_view_context() From 19745c38a9db043adb4b755a9a974ea4ee0728ef Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:23:50 +0100 Subject: [PATCH 310/567] Update to DS v66 (#1233) --- .design-system-version | 2 +- templates/layouts/_base.html | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.design-system-version b/.design-system-version index 4eed309c20..259bcce324 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -65.2.2 +66.0.1 diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 17f9e92af1..edb739abce 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -3,13 +3,6 @@ {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %} {% from "components/timeout-modal/_macro.njk" import onsTimeoutModal %} -{% set form = { - "attributes": { - "autocomplete": "off", - "novalidate": null - } -} %} - {% if previous_location_url %} {% set breadcrumbs = { "ariaLabel": 'Previous', @@ -109,6 +102,9 @@ {% endif %} {% endblock %} +{% block bodyStart %} +
      +{% endblock %} {% block skipLink %} {{ @@ -119,6 +115,9 @@ }} {% endblock %} +{% block bodyEnd %} +
      +{% endblock %} {% block scripts %} {% if config['EQ_ENABLE_LIVE_RELOAD'] %} From fc30a18ed9cf1a049ec572375faff8aebbefe60c Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:08:04 +0100 Subject: [PATCH 311/567] Schemas v3.76.0 (#1239) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0b2010c272..d7dbe78603 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.75.0 +v3.76.0 From bc728f36a8a42cf872bcc9e02ee9b77d0c66e7a4 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:00:22 +0100 Subject: [PATCH 312/567] Remove question text from ga label (#1232) --- templates/partials/introduction/preview.html | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index 5eb1c8837a..ae1fcc27c8 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -23,7 +23,6 @@

      {{ intro.title }}

      "data-ga": "click", "data-ga-category": "Preview Survey", "data-ga-action": "Open panel", - "data-ga-label": question.question } } %} From 643329d03cd6d4068cc12137d59d900088f09e1e Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:16:05 +0100 Subject: [PATCH 313/567] Update benchmark journey to test complex scenarios (#1203) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> --- schemas/test/en/test_benchmark_business.json | 1688 +++++++++++++++++- 1 file changed, 1626 insertions(+), 62 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index 63ab6312e7..f33ba8da81 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -2,18 +2,21 @@ "language": "en", "mime_type": "application/json/ons/eq", "schema_version": "0.0.1", - "data_version": "0.0.1", - "survey_id": "017", - "form_type": "0070", - "theme": "business", + "data_version": "0.0.3", + "survey_id": "0", + "theme": "default", "legal_basis": "Notice is given under section 1 of the Statistics of Trade Act 1947.", - "title": "Quarterly Stocks Survey", + "title": "Benchmark-Survey", "questionnaire_flow": { - "type": "Linear", + "type": "Hub", "options": { - "summary": { - "collapsible": false - } + "required_completed_sections": [ + "stock-section", + "household-section", + "questions-section", + "section-companies", + "grand-calculated-summary-section-1" + ] } }, "post_submission": { @@ -22,8 +25,9 @@ }, "sections": [ { - "id": "section91", + "id": "stock-section", "title": "Quarterly Stocks Survey", + "show_on_hub": true, "groups": [ { "id": "group-questionnaire-introduction", @@ -123,13 +127,13 @@ ] }, { - "id": "group91", + "id": "group-91", "blocks": [ { - "id": "block379", + "id": "block-379", "type": "Question", "question": { - "id": "question379", + "id": "question-379", "title": { "text": "Are you able to report for the period {ref_p_start_date} to {ref_p_end_date}?", "placeholders": [ @@ -168,8 +172,7 @@ "type": "General", "answers": [ { - "id": "answer434", - "q_code": "123", + "id": "answer-434", "mandatory": true, "type": "Radio", "options": [ @@ -187,30 +190,35 @@ }, "routing_rules": [ { - "block": "block381", + "block": "block-381", "when": { - "==": [{ "identifier": "answer434", "source": "answers" }, "Yes"] + "==": [ + { + "identifier": "answer-434", + "source": "answers" + }, + "Yes" + ] } }, { - "block": "block380" + "block": "block-380" } ] }, { - "id": "block380", + "id": "block-380", "type": "Question", "question": { - "id": "question380", + "id": "question-380", "title": "For which period are you able to report?", "type": "DateRange", "answers": [ { - "id": "answerfrom", + "id": "answer-from", "type": "Date", "mandatory": true, "label": "Period from", - "q_code": "11", "minimum": { "value": { "source": "metadata", @@ -222,11 +230,10 @@ } }, { - "id": "answerto", + "id": "answer-to", "type": "Date", "mandatory": true, "label": "Period to", - "q_code": "12", "maximum": { "value": { "source": "metadata", @@ -249,10 +256,10 @@ } }, { - "id": "block381", + "id": "block-381", "type": "Question", "question": { - "id": "question381", + "id": "question-381", "title": "What was the total value of stocks held (net of progress payments on long-term contracts)?", "guidance": { "contents": [ @@ -286,22 +293,20 @@ "type": "General", "answers": [ { - "id": "answer436", + "id": "answer-436", "mandatory": true, "type": "Currency", "label": "Total value of stocks held at start of period", "description": "Enter the full value (e.g. 56,234.33) or a value to the nearest £thousand (e.g. 56,000). Do not enter ‘56’ for £56,000.", - "q_code": "598", "decimal_places": 2, "currency": "GBP" }, { - "id": "answer437", + "id": "answer-437", "mandatory": true, "type": "Currency", "label": "Total value of stocks held at end of period", "description": "Enter the full value (e.g. 56,234.33) or a value to the nearest £thousand (e.g. 56,000). Do not enter ‘56’ for £56,000.", - "q_code": "599", "decimal_places": 2, "currency": "GBP" } @@ -309,18 +314,17 @@ } }, { - "id": "block4616", + "id": "block-4616", "type": "Question", "question": { - "id": "question4616", + "id": "question-4616", "title": "Are the end of period figures you have provided estimated?", "type": "General", "answers": [ { - "id": "answer5873", + "id": "answer-5873", "mandatory": true, "type": "Radio", - "q_code": "15", "options": [ { "label": "Yes", @@ -336,10 +340,10 @@ } }, { - "id": "block4952", + "id": "block-4952", "type": "Question", "question": { - "id": "question4952", + "id": "question-4952", "title": { "text": "Did any significant changes occur to the total value of stocks for {trad_as}?", "placeholders": [ @@ -395,10 +399,9 @@ "type": "General", "answers": [ { - "id": "answer6287", + "id": "answer-6287", "mandatory": true, "type": "Radio", - "q_code": "146a", "options": [ { "label": "Yes", @@ -414,21 +417,27 @@ }, "routing_rules": [ { - "block": "block4953", + "block": "block-4953", "when": { - "==": [{ "identifier": "answer6287", "source": "answers" }, "Yes"] + "==": [ + { + "identifier": "answer-6287", + "source": "answers" + }, + "Yes" + ] } }, { - "block": "block383" + "block": "block-383" } ] }, { - "id": "block4953", + "id": "block-4953", "type": "Question", "question": { - "id": "question4953", + "id": "question-4953", "title": { "text": "Please indicate the reasons for any changes in the total value of stocks for {trad_as}", "placeholders": [ @@ -457,44 +466,37 @@ "type": "General", "answers": [ { - "id": "answer6288", + "id": "answer-6288", "mandatory": true, "type": "Checkbox", "options": [ { "label": "Change of business structure, merger or takeover", - "value": "Change of business structure, merger or takeover", - "q_code": "146e" + "value": "Change of business structure, merger or takeover" }, { "label": "End of accounting period or financial year", - "value": "End of accounting period or financial year", - "q_code": "146c" + "value": "End of accounting period or financial year" }, { "label": "Introduction or removal of new legislation or incentive", - "value": "Introduction or removal of new legislation or incentive", - "q_code": "146g" + "value": "Introduction or removal of new legislation or incentive" }, { "label": "Normal movement for the time of year", - "value": "Normal movement for the time of year", - "q_code": "146d" + "value": "Normal movement for the time of year" }, { "label": "One-off increase in stocks", - "value": "One-off increase in stocks", - "q_code": "146f" + "value": "One-off increase in stocks" }, { "label": "Start or end of long term project", - "value": "Start or end of long term project", - "q_code": "146b" + "value": "Start or end of long term project" }, { "label": "Other (for example, end of the EU transition period, leaving the EU or other global economic conditions.", - "value": "Other (for example, end of the EU transition period, leaving the EU or other global economic conditions.", - "q_code": "146h" + "value": "Other (for example, end of the EU transition period, leaving the EU or other global economic conditions." } ] } @@ -502,20 +504,19 @@ } }, { - "id": "block383", + "id": "block-383", "type": "Question", "question": { - "id": "question383", + "id": "question-383", "title": "Explain any differences between this quarter's opening value and the previously returned closing value", "description": ["

      Include any unusual fluctuations in figures

      "], "type": "General", "answers": [ { - "id": "answer439", + "id": "answer-439", "mandatory": false, "type": "TextArea", "label": "Comments", - "q_code": "146", "max_length": 2000 } ] @@ -524,6 +525,1569 @@ ] } ] + }, + { + "id": "household-section", + "title": "Household", + "show_on_hub": true, + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": "Do you live here?", + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "questions-section", + "title": "Questions", + "summary": { + "show_on_completion": true + }, + "show_on_hub": true, + "groups": [ + { + "id": "radio", + "title": "Questions", + "blocks": [ + { + "type": "Question", + "id": "skip-first-block", + "question": { + "type": "General", + "id": "skip-first-block-question", + "title": "Skip First Block so it doesn’t appear in Total?", + "answers": [ + { + "type": "Radio", + "id": "skip-first-block-answer", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-number-block", + "question": { + "id": "first-number-question", + "title": "First Number Question Title", + "type": "General", + "answers": [ + { + "id": "first-number-answer", + "label": "First answer label (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "skip_conditions": { + "when": { + "==": [ + { + "identifier": "skip-first-block-answer", + "source": "answers" + }, + "Yes" + ] + } + }, + "type": "Question", + "id": "first-and-a-half-number-block", + "question": { + "id": "first-and-a-half-number-question-also-in-total", + "title": "First Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "first-and-a-half-number-answer-also-in-total", + "label": "First answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "Question", + "id": "second-number-block", + "question": { + "id": "second-number-question-also-in-total", + "title": "Second Number Additional Question Title", + "type": "General", + "answers": [ + { + "id": "second-number-answer-also-in-total", + "label": "Second answer label also in total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-1", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "first-number-answer" + }, + { + "source": "answers", + "identifier": "first-and-a-half-number-answer-also-in-total" + }, + { + "source": "answers", + "identifier": "second-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + } + ] + } + ] + }, + { + "id": "calculated-summary-section", + "title": "Calculated Summary", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "grand-calculated-summary-third-number-block", + "question": { + "id": "grand-calculated-summary-third-number-question", + "title": "Third Number Question Title", + "type": "General", + "answers": [ + { + "id": "third-number-answer", + "label": "Third answer in currency label", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + }, + { + "id": "third-number-answer-also-in-total", + "label": "Third answer label also in currency total (optional)", + "mandatory": false, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "currency-total-playback-2", + "title": "We calculate the total of currency values entered to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "third-number-answer" + }, + { + "source": "answers", + "identifier": "third-number-answer-also-in-total" + } + ] + }, + "title": "Grand total of previous values" + } + }, + { + "type": "Question", + "id": "mutually-exclusive-checkbox", + "question": { + "id": "mutually-exclusive-checkbox-question", + "type": "MutuallyExclusive", + "title": "Which answer did you give to question 4 and a half?", + "mandatory": false, + "answers": [ + { + "id": "checkbox-answer", + "instruction": "Select an answer", + "type": "Checkbox", + "mandatory": false, + "options": [ + { + "label": { + "placeholders": [ + { + "placeholder": "answer_value_1", + "value": { + "identifier": "first-and-a-half-number-answer-also-in-total", + "source": "answers" + } + } + ], + "text": "{answer_value_1} - first and a half answer" + }, + "value": "{answer_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_1", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_1} - calculated summary answer (previous section)" + }, + "value": "{calc_value_1}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "calc_value_2", + "value": { + "identifier": "currency-total-playback-2", + "source": "calculated_summary" + } + } + ], + "text": "{calc_value_2} - calculated summary answer (current section)" + }, + "value": "{calc_value_2}" + }, + { + "label": { + "placeholders": [ + { + "placeholder": "third_answer_value", + "value": { + "identifier": "third-number-answer", + "source": "answers" + } + } + ], + "text": "{third_answer_value} - third answer" + }, + "value": "{third_answer_value}" + } + ] + }, + { + "id": "checkbox-exclusive-answer", + "mandatory": false, + "type": "Checkbox", + "options": [ + { + "label": "I prefer not to say", + "description": "Some description", + "value": "I prefer not to say" + } + ] + } + ] + } + }, + { + "type": "Question", + "id": "set-min-max-block", + "question": { + "answers": [ + { + "id": "set-minimum-answer", + "label": "Set a value greater than the total above", + "mandatory": true, + "description": "This is a description of the minimum value", + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "minimum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + }, + { + "id": "set-maximum-answer", + "description": "This is a description of the maximum value", + "label": "Set a value less than the total above", + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2, + "maximum": { + "value": { + "source": "calculated_summary", + "identifier": "currency-total-playback-1" + } + } + } + ], + "id": "set-min-question", + "title": { + "placeholders": [ + { + "placeholder": "calculated_summary_answer", + "value": { + "identifier": "currency-total-playback-1", + "source": "calculated_summary" + } + } + ], + "text": "Set minimum and maximum values based on your calculated summary total of £{calculated_summary_answer}" + }, + "type": "General" + } + } + ], + "id": "calculated-summary" + } + ] + }, + { + "id": "grand-calculated-summary-section-1", + "title": "Commuting", + "show_on_hub": true, + "summary": { + "show_on_completion": true + }, + "groups": [ + { + "id": "grand-calculated-summary-group", + "title": "Commuting", + "blocks": [ + { + "type": "Question", + "id": "grand-calculated-summary-first-number-block", + "question": { + "id": "grand-calculated-summary-first-number-question", + "title": "How much do you walk per week?", + "type": "General", + "answers": [ + { + "id": "q1-a1", + "label": "Weekly distance travelled on foot", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q1-a2", + "label": "Number of walks per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "grand-calculated-summary-second-number-block", + "question": { + "id": "grand-calculated-summary-second-number-question", + "title": "How much do you drive per week?", + "type": "General", + "answers": [ + { + "id": "q2-a1", + "label": "Weekly distance travelled by car", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q2-a2", + "label": "Number of car journeys per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "distance-calculated-summary-1", + "title": "We calculate the total of distance travelled by foot and car to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a1" + }, + { + "source": "answers", + "identifier": "q2-a1" + } + ] + }, + "title": "Calculated distance on foot and driving" + } + }, + { + "type": "CalculatedSummary", + "id": "number-calculated-summary-1", + "title": "We calculate the total number of journeys on foot and in a car to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q1-a2" + }, + { + "source": "answers", + "identifier": "q2-a2" + } + ] + }, + "title": "Calculated journeys on foot and driving" + } + } + ] + } + ] + }, + { + "id": "grand-calculated-summary-section-2", + "title": "Alternative Transport", + "groups": [ + { + "id": "transport-group", + "title": "Alternative Transport", + "blocks": [ + { + "type": "Question", + "id": "third-number-block", + "question": { + "id": "third-number-question", + "title": "How much do you cycle per week?", + "type": "General", + "answers": [ + { + "id": "q3-a1", + "label": "Weekly distance travelled by bike", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q3-a2", + "label": "Number of bicycle journeys per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "Question", + "id": "fourth-number-block", + "question": { + "id": "fourth-number-question", + "title": "How much do you voi per week?", + "type": "General", + "answers": [ + { + "id": "q4-a1", + "label": "Weekly distance travelled on a Voi", + "mandatory": true, + "type": "Unit", + "unit_length": "short", + "unit": "length-mile", + "decimal_places": 2 + }, + { + "id": "q4-a2", + "label": "Number of scooter trips per week", + "mandatory": true, + "type": "Number" + } + ] + } + }, + { + "type": "CalculatedSummary", + "id": "distance-calculated-summary-2", + "title": "We calculate the total of distance travelled by bike and voi to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a1" + }, + { + "source": "answers", + "identifier": "q4-a1" + } + ] + }, + "title": "Calculated weekly distance on bike and scooter" + } + }, + { + "type": "CalculatedSummary", + "id": "number-calculated-summary-2", + "title": "We calculate the total number of journeys on bike and on a voi to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "answers", + "identifier": "q3-a2" + }, + { + "source": "answers", + "identifier": "q4-a2" + } + ] + }, + "title": "Calculated journeys on bike and scooter" + } + } + ] + } + ] + }, + { + "id": "grand-calculated-summary-section-3", + "title": "Grand calculated summaries", + "groups": [ + { + "id": "summary-group", + "title": "Grand calculated summary group", + "blocks": [ + { + "type": "GrandCalculatedSummary", + "id": "distance-grand-calculated-summary", + "title": "We calculate the grand total weekly distance travelled to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "distance-calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "distance-calculated-summary-2" + } + ] + }, + "title": "Grand calculated summary of distance travelled" + } + }, + { + "type": "GrandCalculatedSummary", + "id": "number-grand-calculated-summary", + "title": "We calculate the grand total journeys per week to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "number-calculated-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "number-calculated-summary-2" + } + ] + }, + "title": "Grand calculated summary of journeys" + } + } + ] + } + ] + }, + { + "id": "section-companies", + "title": "General insurance companies", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "companies", + "title": "Companies or UK branches", + "item_anchor_answer_id": "company-or-branch-name", + "item_label": "Name of UK company or branch", + "add_link_text": "Add another UK company or branch", + "empty_list_text": "No UK company or branch added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-companies", + "blocks": [ + { + "type": "Question", + "id": "responsible-party", + "question": { + "type": "General", + "id": "responsible-party-question", + "title": "Are you the responsible party for reporting trading details for a company of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "any-companies-or-branches", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-companies-or-branches", + "for_list": "companies", + "question": { + "type": "General", + "id": "any-companies-or-branches-question", + "title": "Do any companies or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-companies-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-company", + "list_name": "companies" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-companies-or-branches-answer" + }, + "Yes" + ] + }, + "block": "any-other-companies-or-branches" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-companies-or-branches", + "type": "ListCollector", + "for_list": "companies", + "question": { + "id": "any-other-companies-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK companies or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-companies-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-company", + "type": "ListAddQuestion", + "question": { + "id": "add-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "repeating_blocks": [ + { + "id": "companies-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-number", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-date", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "companies-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "companies-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {company_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-date" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this company been trading in the UK? (Mandatory)", + "id": "authorised-trader-uk-radio", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this company been trading in the EU? (Not mandatory)", + "id": "authorised-trader-eu-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ], + "edit_block": { + "id": "edit-company", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-companies", + "type": "General", + "title": "What is the name and registration number of the company?", + "answers": [ + { + "id": "company-or-branch-name", + "label": "Name of UK company or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-company", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-companies", + "type": "General", + "title": "Are you sure you want to remove this company or UK branch?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Companies or UK branches", + "item_title": { + "text": "{company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "answers", + "identifier": "company-or-branch-name" + } + } + ] + } + } + }, + { + "id": "any-other-trading-details", + "type": "Question", + "question": { + "id": "any-other-trading-details-question", + "type": "General", + "title": "Do you have any other details about the trading you have reported for?", + "answers": [ + { + "id": "any-other-trading-details-answer", + "label": "Additional details", + "mandatory": false, + "type": "TextField" + } + ] + } + } + ] + } + ] + }, + { + "id": "section-businesses", + "title": "General insurance business", + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "businesses", + "title": "Businesses or UK branches", + "item_anchor_answer_id": "business-or-branch-name", + "item_label": "Name of UK business or branch", + "add_link_text": "Add another UK business or branch", + "empty_list_text": "No UK business or branch added" + } + ], + "show_non_item_answers": true + }, + "groups": [ + { + "id": "group-businesses", + "blocks": [ + { + "type": "Question", + "id": "responsible-party-business", + "question": { + "type": "General", + "id": "responsible-party-business-question", + "title": "Are you the responsible party for reporting trading details for a business of branch?", + "answers": [ + { + "type": "Radio", + "id": "responsible-party-business-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "block": "any-businesses-or-branches", + "when": { + "==": [ + "Yes", + { + "source": "answers", + "identifier": "responsible-party-business-answer" + } + ] + } + }, + { + "section": "End" + } + ] + }, + { + "type": "ListCollectorDrivingQuestion", + "id": "any-businesses-or-branches", + "for_list": "businesses", + "question": { + "type": "General", + "id": "any-businesses-or-branches-question", + "title": "Do any businesses or branches within your United Kingdom group undertake general insurance business?", + "answers": [ + { + "type": "Radio", + "id": "any-businesses-or-branches-answer", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-business", + "list_name": "businesses" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "when": { + "==": [ + { + "source": "answers", + "identifier": "any-businesses-or-branches-answer" + }, + "Yes" + ] + }, + "block": "any-other-business-businesses-or-branches" + }, + { + "section": "End" + } + ] + }, + { + "id": "any-other-business-businesses-or-branches", + "type": "ListCollector", + "for_list": "businesses", + "question": { + "id": "any-other-business-businesses-or-branches-question", + "type": "General", + "title": "Do you need to add any other UK businesses or branches that undertake general insurance business?", + "answers": [ + { + "id": "any-other-business-businesses-or-branches-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-business", + "type": "ListAddQuestion", + "question": { + "id": "add-question-businesses", + "type": "General", + "title": "What is the name and registration number of the business?", + "answers": [ + { + "id": "business-or-branch-name", + "label": "Name of UK business or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "repeating_blocks": [ + { + "id": "businesses-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "businesses-repeating-block-1-question", + "type": "General", + "title": { + "text": "Give details about {business_name}", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + } + ] + }, + "answers": [ + { + "id": "registration-business-number", + "label": "Registration number (Mandatory)", + "mandatory": true, + "type": "Number", + "maximum": { + "value": 999, + "exclusive": false + }, + "decimal_places": 0 + }, + { + "id": "registration-business-date", + "label": "Date of Registration (Mandatory)", + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + } + } + ] + } + }, + { + "id": "businesses-repeating-block-2", + "type": "ListRepeatingQuestion", + "question": { + "id": "businesses-repeating-block-2-question", + "type": "General", + "title": { + "text": "Give details about how {business_name} has been trading over the past {date_difference}.", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + }, + { + "placeholder": "date_difference", + "transforms": [ + { + "transform": "calculate_date_difference", + "arguments": { + "first_date": { + "source": "answers", + "identifier": "registration-business-date" + }, + "second_date": { + "value": "now" + } + } + } + ] + } + ] + }, + "answers": [ + { + "type": "Radio", + "label": "Has this business been trading in the UK? (Mandatory)", + "id": "authorised-business-trader-uk-radio", + "mandatory": true, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + }, + { + "type": "Radio", + "label": "Has this business been trading in the EU? (Not mandatory)", + "id": "authorised-business-trader-eu-radio", + "mandatory": false, + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ], + "edit_block": { + "id": "edit-business", + "type": "ListEditQuestion", + "question": { + "id": "edit-question-businesses", + "type": "General", + "title": "What is the name and registration number of the business?", + "answers": [ + { + "id": "business-or-branch-name", + "label": "Name of UK business or branch (Mandatory)", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-business", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question-businesses", + "type": "General", + "title": "Are you sure you want to remove this business or UK branch?", + "answers": [ + { + "id": "remove-confirmation-business", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Businesses or UK branches", + "item_title": { + "text": "{business_name}", + "placeholders": [ + { + "placeholder": "business_name", + "value": { + "source": "answers", + "identifier": "business-or-branch-name" + } + } + ] + } + } + }, + { + "id": "any-other-business-trading-details", + "type": "Question", + "question": { + "id": "any-other-business-trading-details-question", + "type": "General", + "title": "Do you have any other details about the trading you have reported for?", + "answers": [ + { + "id": "any-other-business-trading-details-answer", + "label": "Additional details", + "mandatory": false, + "type": "TextField" + } + ] + } + } + ] + } + ] } ], "navigation": { From 5efaba336cba39f78a2b702bca986eabbaf5cc19 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:06:06 +0000 Subject: [PATCH 314/567] Schemas v3.77.0 (#1243) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d7dbe78603..0049caba3f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.76.0 +v3.77.0 From e192597e544ed7d7852759aa1c9541f81e1d1253 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 1 Nov 2023 11:54:30 +0000 Subject: [PATCH 315/567] Update test_variants_question schema question to make Number type mandatory (#1241) --- schemas/test/en/test_variants_question.json | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/schemas/test/en/test_variants_question.json b/schemas/test/en/test_variants_question.json index bfca17b3b2..9bedff415e 100644 --- a/schemas/test/en/test_variants_question.json +++ b/schemas/test/en/test_variants_question.json @@ -136,7 +136,7 @@ "answers": [ { "id": "age-answer", - "mandatory": false, + "mandatory": true, "type": "Number", "label": "Age" } @@ -443,18 +443,6 @@ } ], "routing_rules": [ - { - "block": "age-block", - "when": { - "==": [ - { - "source": "answers", - "identifier": "age-confirm-answer" - }, - "No" - ] - } - }, { "section": "End" } From 9ae46d207b4c39c06048cb94168eaba5c22dcbd2 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:03:31 +0000 Subject: [PATCH 316/567] Migrate to wdio assertions library (#1237) * Convert the assertions library from Chai to WDIO in this file * Remove the use of Chai across the specs * Remove the use of Chai across the specs in the Address suite * Remove the use of Chai across the specs in the Checkbox folder * Remove the use of Chai across the specs in the dropdown and radio folder * Change Chai syntax to Webdriver across all the specs in the features folder * Change Chai syntax to Webdriver across all the specs in the timeout folder * Change Chai syntax to Webdriver across all the specs in the remainder of the spec folder and refactor files * Refactor tests to improve accuracy * Refactor tests to improve accuracy in the features folder * Refactor general functional tests to improve accuracy * Refactor tests to use wdio instead of chai and lint tests * Refactor tests to use correct syntax * Change number of max instances to 2 * Refactor to clean syntax and readd deleted tests by tool * Format tests * Revert max instances to 1 * Increase max instances to 2 * Revert max instances to 1 * Increase max instances to 2 * Revert change made to suites defined * Revert max instances to 1 * Increase max instances to 2 * Refactor test to improve accuracy and revert max instances back to 1 * Format and lint tests * Remove chai dependencies and linter * Remove Chai references --- .eslintrc | 4 +- package.json | 2 - tests/functional/helpers.js | 10 +- ...edirect_to_list_add_block_checkbox.spec.js | 24 +- ...n_redirect_to_list_add_block_radio.spec.js | 24 +- .../functional/spec/census_thank_you.spec.js | 4 +- tests/functional/spec/checkbox.spec.js | 32 +-- .../spec/components/address/address.spec.js | 26 +-- .../checkbox/checkbox_detail_answer.spec.js | 10 +- .../checkbox_detail_answer_dropdown.spec.js | 20 +- .../checkbox_detail_answer_multiple.spec.js | 22 +- .../checkbox_detail_answer_numeric.spec.js | 28 +-- .../checkbox/checkbox_label.spec.js | 10 +- .../mutually_exclusive_checkbox.spec.js | 72 +++--- .../mutually_exclusive_currency.spec.js | 42 ++-- ...ally_exclusive_day_month_year_date.spec.js | 68 +++--- .../mutually_exclusive_duration.spec.js | 56 ++--- ...mutually_exclusive_month_year_date.spec.js | 56 ++--- ...tually_exclusive_multiple_checkbox.spec.js | 136 ++++++------ ...usive_multiple_day_month_year_date.spec.js | 144 ++++++------ ...ually_exclusive_multiple_textfield.spec.js | 116 +++++----- .../mutually_exclusive_number.spec.js | 44 ++-- .../mutually_exclusive_percentage.spec.js | 44 ++-- .../mutually_exclusive_textarea.spec.js | 24 +- .../mutually_exclusive_textfield.spec.js | 44 ++-- .../mutually_exclusive_unit.spec.js | 44 ++-- .../mutually_exclusive_year_date.spec.js | 44 ++-- .../spec/components/dropdown/dropdown.spec.js | 28 +-- .../functional/spec/components/radio/radio.js | 32 +-- .../radio_detail_answer_dropdown.spec.js | 18 +- .../radio_detail_answer_multiple.spec.js | 20 +- .../radio/radio_detail_answer_numeric.spec.js | 28 +-- .../radio/radio_visible_answers.spec.js | 8 +- .../radio/radio_voluntary_answers.spec.js | 12 +- .../spec/conditional_combined_routing.spec.js | 8 +- .../spec/confirmation_email.spec.js | 52 ++--- .../functional/spec/content_variants.spec.js | 4 +- tests/functional/spec/cookie_banner.spec.js | 18 +- .../spec/custom_page_titles.spec.js | 26 +-- tests/functional/spec/dates.spec.js | 38 ++-- tests/functional/spec/dob_date.spec.js | 4 +- tests/functional/spec/durations.spec.js | 34 +-- tests/functional/spec/error_messages.spec.js | 74 +++---- tests/functional/spec/exit.spec.js | 8 +- .../calculated_summary_test_case.js | 126 +++++------ ...mmary_repeating_and_static_answers.spec.js | 20 +- ...alculated_summary_repeating_blocks.spec.js | 2 +- ...lculated_summary_repeating_section.spec.js | 128 +++++------ .../conditional_checkbox_title.spec.js | 16 +- .../features/confirmation_question.spec.js | 10 +- ..._question_within_repeating_section.spec.js | 12 +- .../default_value/default_value.spec.js | 20 +- .../dynamic_answer_options/function_driven.js | 64 +++--- .../function_driven_mandatory.js | 24 +- .../radio_options_from_checkbox_answers.js | 30 +-- .../dynamic_answers_list_value_source.spec.js | 4 +- .../enabled_section_checkbox.spec.js | 14 +- .../enabled_section_hub.spec.js | 28 +-- .../enabled_section_radio.spec.js | 12 +- ...summary_cross_section_dependencies.spec.js | 20 +- ...d_summary_inside_repeating_section.spec.js | 44 ++-- ...ulated_summary_overlapping_answers.spec.js | 12 +- ...lculated_summary_repeating_answers.spec.js | 80 +++---- .../choose_another_section.spec.js | 8 +- .../hub_and_spoke/hub_and_spoke.spec.js | 82 +++---- .../hub_and_spoke_custom_content.spec.js | 16 +- .../hub_and_spoke_required_enable.spec.js | 6 +- .../features/hub_and_spoke/previous.spec.js | 8 +- .../last_viewed_guidance.spec.js | 20 +- .../last_viewed_guidance_hub.spec.js | 44 ++-- ...option_based_on_first_item_in_list.spec.js | 4 +- .../placeholder_date_difference.spec.js | 8 +- .../placeholder_date_ranges.spec.js | 4 +- .../placeholder_default_value.spec.js | 8 +- .../placeholder/placeholder_metadata.spec.js | 6 +- ...laceholder_option_label_from_value.spec.js | 6 +- .../placeholder/playback_confirmation.spec.js | 5 +- .../progress/progress_value_source_blocks.js | 58 ++--- .../progress_value_source_repeating.js | 48 ++-- .../custom_question_summary.spec.js | 10 +- .../list_collector_repeating_blocks.spec.js | 34 +-- ...eating_sections_with_hub_and_spoke.spec.js | 90 ++++---- .../spec/features/routing/all_in.spec.js | 6 +- .../spec/features/routing/and.spec.js | 8 +- .../routing/answer_comparison_routing.spec.js | 6 +- .../routing/answer_not_on_path.spec.js | 6 +- .../routing/answered_unanswered.spec.js | 36 +-- .../spec/features/routing/any_in.spec.js | 6 +- .../features/routing/checkbox_count.spec.js | 16 +- .../spec/features/routing/date.spec.js | 44 ++-- .../spec/features/routing/in.spec.js | 4 +- .../spec/features/routing/not.spec.js | 4 +- .../spec/features/routing/number.spec.js | 32 +-- .../spec/features/routing/or.spec.js | 8 +- .../routing/removes_completed_block.spec.js | 2 +- .../section_summary/section_summary.spec.js | 48 ++-- ...section_summary_repeating_sections.spec.js | 8 +- ...show_section_summary_on_completion.spec.js | 8 +- .../answer_comparison_skip_conditions.spec.js | 6 +- .../features/submit_page/submit_page.spec.js | 6 +- .../submit_page_collapsible_summary.spec.js | 14 +- ...submit_page_custom_submission_text.spec.js | 8 +- .../submit_page/submit_page_summary.spec.js | 36 +-- ...bmit_with_summary_return_to_answer.spec.js | 20 +- .../supplementary_data.spec.js | 100 ++++----- tests/functional/spec/features/units.spec.js | 36 +-- .../date-combined-mm-yyyy.spec.js | 24 +- .../date-combined-yyyy.spec.js | 12 +- .../date_validation/date-combined.spec.js | 12 +- .../date_validation/date-range.spec.js | 12 +- .../date_validation/date-single.spec.js | 8 +- .../features/validation/sum/dynamic.spec.js | 42 ++-- .../features/validation/sum/equal.spec.js | 12 +- .../validation/sum/equal_multiple.spec.js | 10 +- .../validation/sum/equal_or_less_than.spec.js | 10 +- ...qual_total_in_separate_section_hub.spec.js | 28 +-- ...otal_in_separate_section_repeating.spec.js | 48 ++-- .../features/validation/sum/less_than.spec.js | 8 +- .../validation/sum/value_source.spec.js | 28 +-- .../view_submitted_response.spec.js | 78 +++---- tests/functional/spec/feedback.spec.js | 26 +-- .../spec/interstitial_definition.spec.js | 8 +- .../functional/spec/interviewer_note.spec.js | 8 +- tests/functional/spec/introduction.spec.js | 16 +- tests/functional/spec/language_code.spec.js | 64 +++--- tests/functional/spec/list_collector.spec.js | 44 ++-- .../spec/list_collector_content.spec.js | 22 +- .../list_collector_driving_question.spec.js | 4 +- ...ollector_driving_question_checkbox.spec.js | 6 +- .../list_collector_primary_person.spec.js | 30 +-- .../list_collector_section_summary.spec.js | 208 +++++++++--------- .../spec/list_collector_variants.spec.js | 24 +- ..._collector_variants_primary_person.spec.js | 32 +-- tests/functional/spec/mobile_number.spec.js | 6 +- .../functional/spec/multiple_answers.spec.js | 64 +++--- tests/functional/spec/multiple_piping.spec.js | 4 +- .../spec/my_account_header_link.spec.js | 4 +- tests/functional/spec/numbers.spec.js | 36 +-- tests/functional/spec/page_layout.spec.js | 2 +- .../spec/percentage_decimal.spec.js | 8 +- .../placeholder_answers_on_the_path.spec.js | 18 +- tests/functional/spec/preview.spec.js | 43 ++-- .../spec/question_definitions.spec.js | 8 +- .../question_definitions_array_type.spec.js | 8 +- .../spec/question_description.spec.js | 14 +- .../functional/spec/question_variants.spec.js | 18 +- ...estion_variants_first_item_in_list.spec.js | 4 +- .../spec/radio_checkbox_descriptions.spec.js | 12 +- ...tional_with_detail_answer_optional.spec.js | 10 +- .../spec/relationships-unrelated.spec.js | 26 +-- tests/functional/spec/relationships.spec.js | 42 ++-- .../spec/relationships_primary.spec.js | 6 +- ..._and_skipping_section_dependencies.spec.js | 86 ++++---- ...on_dependencies_calculated_summary.spec.js | 50 ++--- .../spec/routing_checkbox_contains.spec.js | 16 +- tests/functional/spec/save_sign_out.spec.js | 38 ++-- .../spec/skip_condition_block.spec.js | 4 +- .../spec/skip_condition_group.spec.js | 4 +- .../spec/skip_condition_list.spec.js | 8 +- .../spec/skip_conditions_not_set.spec.js | 4 +- .../spec/skip_conditions_set.spec.js | 4 +- ...submit_with_custom_submission_text.spec.js | 6 +- tests/functional/spec/textarea.spec.js | 14 +- tests/functional/spec/textfield.spec.js | 8 +- .../spec/textfield_suggestions.spec.js | 6 +- tests/functional/spec/thank_you.spec.js | 40 ++-- tests/functional/spec/theme_dbt.spec.js | 4 +- tests/functional/spec/theme_dbt_dsit.spec.js | 6 +- .../functional/spec/theme_dbt_dsit_ni.spec.js | 8 +- tests/functional/spec/theme_dbt_ni.spec.js | 6 +- tests/functional/spec/theme_desnz.spec.js | 4 +- tests/functional/spec/theme_desnz_ni.spec.js | 6 +- .../spec/theme_northernireland.spec.js | 4 +- tests/functional/spec/theme_orr.spec.js | 4 +- .../functional/spec/timeout/timeout_modal.js | 29 ++- tests/functional/wdio.conf.js | 3 - 176 files changed, 2325 insertions(+), 2355 deletions(-) diff --git a/.eslintrc b/.eslintrc index b2de38fc80..2c85ab3b2a 100755 --- a/.eslintrc +++ b/.eslintrc @@ -22,15 +22,13 @@ } }, "plugins": [ - "json", - "chai-friendly" + "json" ], "rules": { "no-loss-of-precision": 0, "no-nonoctal-decimal-escape": 0, "no-unsafe-optional-chaining": 0, "no-useless-backreference": 0, - "chai-friendly/no-unused-expressions": 2, "consistent-return": 1, "indent": [ 2, diff --git a/package.json b/package.json index 7470ecc575..59c5b48f79 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,9 @@ "@wdio/local-runner": "^8.14.3", "@wdio/mocha-framework": "^8.14.0", "@wdio/spec-reporter": "^8.14.0", - "chai": "^4.3.7", "eslint": "^8.46.0", "eslint-cli": "^1.1.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-chai-friendly": "^0.7.2", "eslint-plugin-import": "^2.28.0", "eslint-plugin-json": "^3.1.0", "eslint-plugin-n": "^16.0.1", diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 08e113495b..17f3050f70 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -2,24 +2,24 @@ export const checkItemsInList = async (itemsExpected, listLabel) => { await $(listLabel(1)).waitForDisplayed(); for (let i = 1; i <= itemsExpected.length; i++) { - await expect(await $(listLabel(i)).getText()).to.equal(itemsExpected[i - 1]); + await expect(await $(listLabel(i)).getText()).toEqual(itemsExpected[i - 1]); } }; export const checkListItemComplete = async (listItemLabel) => { - await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).to.be.true; + await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).toBe(true); }; export const checkListItemIncomplete = async (listItemLabel) => { - await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).to.be.false; + await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).toBe(false); }; const assertSummaryFunction = (selector) => { return async (entities) => { // check each summary value/item/title is present and that the number of them matches what is on the page await entities.map(async (entity, index) => { - await expect(await $$(selector)[index].getText()).to.equal(entity); + await expect(await $$(selector)[index].getText()).toEqual(entity); }); - await expect(await $$(selector).length).to.equal(entities.length); + await expect(await $$(selector).length).toEqual(entities.length); }; }; diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js index f93a3b2334..1d216b69f2 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -13,20 +13,20 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { it('When the user selects "No", Then, they should be taken to the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); }); it('When the user selects "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.iThinkSo()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); - await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(browser).toHaveUrlContaining("?previous=anyone-usually-live-at"); }); it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { @@ -34,7 +34,7 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); await click(AnyoneLiveAtListCollectorAddPage.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); @@ -42,30 +42,30 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneLiveAtListCollectorRemovePage.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).toBe(false); }); it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js index db643cb71d..ab7f4ebdad 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js @@ -13,20 +13,20 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { it('When the user answers "No", Then, they should be taken to straight the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); }); it('When the user answers "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.yes()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); - await expect(await browser.getUrl()).to.contain("?previous=anyone-usually-live-at"); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(browser).toHaveUrlContaining("?previous=anyone-usually-live-at"); }); it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { @@ -34,7 +34,7 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); await click(AnyoneLiveAtListCollectorAddPage.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); @@ -42,30 +42,30 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneLiveAtListCollectorRemovePage.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); - await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).toBe(false); }); it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollector.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAt.pageName); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); await click(AnyoneUsuallyLiveAt.submit()); - await expect(await browser.getUrl()).to.contain(AnyoneLiveAtListCollectorAddPage.pageName); + await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/census_thank_you.spec.js b/tests/functional/spec/census_thank_you.spec.js index 478fa0033f..0b3b25acdb 100644 --- a/tests/functional/spec/census_thank_you.spec.js +++ b/tests/functional/spec/census_thank_you.spec.js @@ -13,8 +13,8 @@ describe("Thank You Census Household", () => { it("When I navigate to the thank you page, Then I should not see the feedback call to action", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.feedback()).isExisting()).to.equal(false); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.feedback()).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index 38a634bb6b..e0892dbc86 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -10,26 +10,26 @@ describe('Checkbox with "other" option', () => { }); it("Given a label has not been provided in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default label should be visible", async () => { - await expect(await $("body").getText()).to.have.string("Select all that apply"); + await expect(await $("body").getText()).toContain("Select all that apply"); }); it("Given a label has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the label should be visible", async () => { await $(MandatoryCheckboxPage.none()).click(); await click(MandatoryCheckboxPage.submit()); - await expect(await $("body").getText()).to.have.string("Select any answers that apply"); + await expect(await $("body").getText()).toContain("Select any answers that apply"); }); it("Given that there is only one checkbox, When the checkbox answer is displayed, Then no label should be present", async () => { await $(MandatoryCheckboxPage.none()).click(); await click(MandatoryCheckboxPage.submit()); await click(NonMandatoryCheckboxPage.submit()); - await expect(await $("body").getText()).to.not.have.string("Select all that apply"); + await expect(await $("body").getText()).not.toBe("Select all that apply"); }); it('Given an "other" option is available, when the user clicks the "other" option the other input should be visible.', async () => { - await expect(await $(MandatoryCheckboxPage.otherLabelDescription()).getText()).to.have.string("Choose any other topping"); + await expect(await $(MandatoryCheckboxPage.otherLabelDescription()).getText()).toBe("Choose any other topping"); await $(MandatoryCheckboxPage.other()).click(); - await expect(await $(MandatoryCheckboxPage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.otherDetail()).isDisplayed()).toBe(true); }); it("Given a mandatory checkbox answer, When I select the other option, leave the input field empty and submit, Then an error should be displayed.", async () => { @@ -37,14 +37,14 @@ describe('Checkbox with "other" option', () => { await $(MandatoryCheckboxPage.other()).click(); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).toBe(true); }); it("Given a mandatory checkbox answer, When I leave the input field empty and submit, Then the question text should be hidden in the error message using a span element.", async () => { // When await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(MandatoryCheckboxPage.error()).getHTML()).to.contain( + await expect(await $(MandatoryCheckboxPage.error()).getHTML()).toContain( 'Select at least one answer to ‘Which pizza toppings would you like?’', ); }); @@ -52,12 +52,12 @@ describe('Checkbox with "other" option', () => { it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.s", async () => { await $(MandatoryCheckboxPage.other()).click(); await click(MandatoryCheckboxPage.submit()); - await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).toBe(true); // When await $(MandatoryCheckboxPage.otherDetail()).setValue("Other Text"); await click(MandatoryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(NonMandatoryCheckboxPage.pageName); + await expect(browser).toHaveUrlContaining(NonMandatoryCheckboxPage.pageName); }); it('Given a non-mandatory checkbox answer, when the user does not select an option, then "No answer provided" should be displayed on the summary screen', async () => { @@ -68,7 +68,7 @@ describe('Checkbox with "other" option', () => { await click(NonMandatoryCheckboxPage.submit()); await click(singleCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).toBe("No answer provided"); }); it('Given a non-mandatory checkbox answer, when the user selects Other but does not supply a value, then "Other" should be displayed on the summary screen', async () => { @@ -80,7 +80,7 @@ describe('Checkbox with "other" option', () => { await click(NonMandatoryCheckboxPage.submit()); await click(singleCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).toBe("Other"); }); it("Given a non-mandatory checkbox answer, when the user selects Other and supplies a value, then the supplied value should be displayed on the summary screen", async () => { @@ -93,7 +93,7 @@ describe('Checkbox with "other" option', () => { await click(NonMandatoryCheckboxPage.submit()); await click(singleCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).to.contain("The other value"); + await expect(await $(SubmitPage.nonMandatoryCheckboxAnswer()).getText()).toContain("The other value"); }); it("Given that there is an escaped character in an answer label, when the user selects the answer, then the label should be displayed on the summary screen", async () => { @@ -105,7 +105,7 @@ describe('Checkbox with "other" option', () => { await click(NonMandatoryCheckboxPage.submit()); await click(singleCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.contain("Ham & Cheese"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).toBe("Ham & Cheese"); }); it("Given I have previously added text in other textfield and saved, when I uncheck other options and select a different checkbox as answer, then the text entered in other field must be wiped.", async () => { @@ -120,7 +120,7 @@ describe('Checkbox with "other" option', () => { await $(NonMandatoryCheckboxPage.previous()).click(); // Then await $(MandatoryCheckboxPage.other()).click(); - await expect(await $(MandatoryCheckboxPage.otherDetail()).getValue()).to.equal(""); + await expect(await $(MandatoryCheckboxPage.otherDetail()).getValue()).toBe(""); }); it("Given a mandatory checkbox answer, when the user selects only one option, then the answer should not be displayed as a list on the summary screen", async () => { @@ -133,7 +133,7 @@ describe('Checkbox with "other" option', () => { const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; // Then - await expect(listLength).to.equal(0); + await expect(listLength).toBe(0); }); it("Given a mandatory checkbox answer, when the user selects more than one option, then the answer should be displayed as a list on the summary screen", async () => { @@ -147,6 +147,6 @@ describe('Checkbox with "other" option', () => { const listLength = await $$(`${SubmitPage.mandatoryCheckboxAnswer()} li`).length; // Then - await expect(listLength).to.equal(2); + await expect(listLength).toBe(2); }); }); diff --git a/tests/functional/spec/components/address/address.spec.js b/tests/functional/spec/components/address/address.spec.js index 436ab0904e..272fe0a97b 100644 --- a/tests/functional/spec/components/address/address.spec.js +++ b/tests/functional/spec/components/address/address.spec.js @@ -19,9 +19,9 @@ describe("Address Answer Type", () => { await click(AddressMandatory.submit()); await click(AddressOptional.submit()); await click(AddressConfirmation.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); - await expect(await $(SubmitPage.addressMandatory()).getHTML()).to.contain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.addressMandatory()).getText()).toBe("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); + await expect(await $(SubmitPage.addressMandatory()).getHTML()).toContain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); }); }); @@ -32,15 +32,15 @@ describe("Address Answer Type", () => { await click(AddressMandatory.submit()); await click(AddressOptional.submit()); await click(AddressConfirmation.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.addressMandatory()).getText()).to.equal("Evelyn Street"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.addressMandatory()).getText()).toBe("Evelyn Street"); }); }); describe("Given the user is on an mandatory address input question", () => { it("When the user submits the page without entering address line 1, Then an error is displayed", async () => { await click(AddressMandatory.submit()); - await expect(await $(AddressMandatory.error()).getText()).to.equal("Enter an address"); + await expect(await $(AddressMandatory.error()).getText()).toBe("Enter an address"); }); }); @@ -52,7 +52,7 @@ describe("Address Answer Type", () => { await click(AddressOptional.submit()); await click(AddressConfirmation.submit()); - await expect(await $(SubmitPage.addressOptional()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.addressOptional()).getText()).toBe("No answer provided"); }); }); @@ -64,14 +64,14 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); await click(AddressMandatory.submit()); - await expect(await browser.getUrl()).to.contain(AddressOptional.pageName); + await expect(browser).toHaveUrlContaining(AddressOptional.pageName); await browser.url(AddressMandatory.url()); - await expect(await $(AddressMandatory.Line1()).getValue()).to.contain("Evelyn Street"); - await expect(await $(AddressMandatory.Line2()).getValue()).to.contain("Apt 7"); - await expect(await $(AddressMandatory.Town()).getValue()).to.contain("Barry"); - await expect(await $(AddressMandatory.Postcode()).getValue()).to.contain("CF63 4JG"); + await expect(await $(AddressMandatory.Line1()).getValue()).toBe("Evelyn Street"); + await expect(await $(AddressMandatory.Line2()).getValue()).toBe("Apt 7"); + await expect(await $(AddressMandatory.Town()).getValue()).toBe("Barry"); + await expect(await $(AddressMandatory.Postcode()).getValue()).toBe("CF63 4JG"); }); }); describe("Given the user has submitted an address answer type question", () => { @@ -82,7 +82,7 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); await click(AddressMandatory.submit()); await click(AddressOptional.submit()); - await expect(await $(AddressConfirmation.questionText()).getText()).to.equal("Please confirm the first line of your address is Evelyn Street"); + await expect(await $(AddressConfirmation.questionText()).getText()).toBe("Please confirm the first line of your address is Evelyn Street"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js index b644c913ce..4ebb80789d 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer.spec.js @@ -9,23 +9,23 @@ describe("Given the checkbox detail_answer questionnaire,", () => { await browser.openQuestionnaire("test_checkbox_detail_answer_textfield.json"); }); it("When a checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", async () => { - await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).toBe(true); }); it("When a checkbox has a detail_answer with visible set to true and another answer is checked, then the detail answer write-in field should still be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); - await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(CheckboxVisibleTruePage.otherDetail()).isDisplayed()).toBe(true); }); it("When a checkbox has a detail_answer with visible set to false, Then the detail answer write-in field should not be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); await click(CheckboxVisibleTruePage.submit()); - await expect(await $(CheckboxVisibleFalsePage.otherDetail()).isDisplayed()).to.be.false; + await expect(await $(CheckboxVisibleFalsePage.otherDetail()).isDisplayed()).toBe(false); }); it("When a checkbox has a detail_answer with visible not set, Then the detail answer write-in field should not be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); await click(CheckboxVisibleTruePage.submit()); await $(CheckboxVisibleFalsePage.iceCream()).click(); await click(CheckboxVisibleFalsePage.submit()); - await expect(await $(CheckboxVisibleNonePage.otherDetail()).isDisplayed()).to.be.false; + await expect(await $(CheckboxVisibleNonePage.otherDetail()).isDisplayed()).toBe(false); }); it("When a mutually exclusive checkbox has a detail_answer with visible set to true, Then the detail answer write-in field should be shown", async () => { await $(CheckboxVisibleTruePage.coffee()).click(); @@ -34,6 +34,6 @@ describe("Given the checkbox detail_answer questionnaire,", () => { await click(CheckboxVisibleFalsePage.submit()); await $(CheckboxVisibleNonePage.blue()).click(); await click(CheckboxVisibleNonePage.submit()); - await expect(await $(MutuallyExclusivePage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(MutuallyExclusivePage.otherDetail()).isDisplayed()).toBe(true); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js index 2efb1b2ae1..4b8b8dc462 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_dropdown.spec.js @@ -11,26 +11,26 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", async () => { await $(CheckboxDropdownPage.fruit()).click(); - await expect(await $(CheckboxDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); + await expect(await $(CheckboxDropdownPage.fruitDetail()).getText()).toContain("Select fruit"); }); it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", async () => { await $(CheckboxDropdownPage.jam()).click(); - await expect(await $(CheckboxDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); + await expect(await $(CheckboxDropdownPage.jamDetail()).getText()).toContain("Select an answer"); }); it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("No answer provided"); }); it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { await $(CheckboxDropdownPage.fruit()).click(); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("Fruit"); }); it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { @@ -38,7 +38,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("Fruit\nMango"); }); it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { @@ -49,14 +49,14 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("Fruit"); }); it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { await $(CheckboxDropdownPage.jam()).click(); await click(CheckboxDropdownPage.submit()); - await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).toBe("Please select the type of Jam"); }); it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its details", async () => { @@ -64,7 +64,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("Jam\nStrawberry"); }); it("When the user removes a previously submitted detail answer, Then the summary should not display the removed detail answer", async () => { @@ -75,7 +75,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.fruit()).click(); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("No answer provided"); }); it("When the user selects multiple options with detail answers and submits, Then the summary should display all the chosen options and their detail answer", async () => { @@ -85,7 +85,7 @@ describe("Optional Checkbox with a Dropdown detail answer", () => { await $(CheckboxDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); await click(CheckboxDropdownPage.submit()); - await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango\nJam\nStrawberry"); + await expect(await $(SubmitPage.optionalCheckboxWithDropdownDetailAnswer()).getText()).toBe("Fruit\nMango\nJam\nStrawberry"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js index 5ca268c37b..81c20d5b2a 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js @@ -7,9 +7,9 @@ describe('Checkbox with multiple "detail_answer" options', () => { it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { await browser.openQuestionnaire(checkboxSchema); await $(MandatoryCheckboxPage.yourChoice()).click(); - await expect(await $(MandatoryCheckboxPage.yourChoiceDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.yourChoiceDetail()).isDisplayed()).toBe(true); await $(MandatoryCheckboxPage.cheese()).click(); - await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).toBe(true); }); it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", async () => { @@ -23,8 +23,8 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(MandatoryCheckboxPage.yourChoice()).click(); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; - await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Enter your topping choice to continue"); + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).toBe(true); + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).toBe("Enter your topping choice to continue"); }); it("Given a selected checkbox answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", async () => { @@ -32,12 +32,12 @@ describe('Checkbox with multiple "detail_answer" options', () => { await browser.openQuestionnaire(checkboxSchema); await $(MandatoryCheckboxPage.yourChoice()).click(); await click(MandatoryCheckboxPage.submit()); - await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).toBe(true); // When await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); await click(MandatoryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { @@ -45,10 +45,10 @@ describe('Checkbox with multiple "detail_answer" options', () => { await browser.openQuestionnaire(checkboxSchema); // When await $(MandatoryCheckboxPage.cheese()).click(); - await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).isDisplayed()).toBe(true); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).toBe("Cheese"); }); it("Given multiple detail answers, When the user provides text for all, Then that text should be displayed on the summary screen", async () => { @@ -61,7 +61,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Cheese\nMozzarella\nYour choice\nBacon"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).toBe("Cheese\nMozzarella\nYour choice\nBacon"); }); it("Given multiple detail answers, When the user provides text for just one, Then that text should be displayed on the summary screen", async () => { @@ -72,7 +72,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).to.equal("Your choice\nBacon"); + await expect(await $(SubmitPage.mandatoryCheckboxAnswer()).getText()).toBe("Your choice\nBacon"); }); it("Given I have previously added text in a detail answer and saved, When I uncheck the detail answer option and select a different checkbox, Then the text entered in the detail answer field should be empty.", async () => { @@ -89,6 +89,6 @@ describe('Checkbox with multiple "detail_answer" options', () => { await $(SubmitPage.previous()).click(); // Then await $(MandatoryCheckboxPage.cheese()).click(); - await expect(await $(MandatoryCheckboxPage.cheeseDetail()).getValue()).to.equal(""); + await expect(await $(MandatoryCheckboxPage.cheeseDetail()).getValue()).toBe(""); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js index 59a77740c4..b1dd29fb35 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_numeric.spec.js @@ -8,15 +8,15 @@ describe('Checkbox with a numeric "detail_answer" option', () => { }); it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { - await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).toBe(true); }); it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When - await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(CheckboxNumericDetailPage.otherDetail()).isDisplayed()).toBe(true); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).toBe("Other"); }); it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { @@ -24,7 +24,7 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("15"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("15"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).toContain("15"); }); it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { @@ -32,8 +32,8 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).toBe("Please enter an integer"); }); it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { @@ -41,8 +41,8 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("250"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).toBe("Number is too large"); }); it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { @@ -50,8 +50,8 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("-1"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).toBe("Number cannot be less than zero"); }); it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { @@ -59,10 +59,10 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("biscuits"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(CheckboxNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(CheckboxNumericDetailPage.errorNumber(1)).getText()).toBe("Please enter an integer"); await browser.pause(1000); - await expect(await $(CheckboxNumericDetailPage.otherDetail()).getValue()).to.equal("biscuits"); + await expect(await $(CheckboxNumericDetailPage.otherDetail()).getValue()).toBe("biscuits"); }); it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { @@ -70,6 +70,6 @@ describe('Checkbox with a numeric "detail_answer" option', () => { await $(CheckboxNumericDetailPage.otherDetail()).setValue("0"); await click(CheckboxNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).to.contain("0"); + await expect(await $(SubmitPage.checkboxNumericDetailAnswer()).getText()).toContain("0"); }); }); diff --git a/tests/functional/spec/components/checkbox/checkbox_label.spec.js b/tests/functional/spec/components/checkbox/checkbox_label.spec.js index 7cc7e15df8..71e74bf249 100644 --- a/tests/functional/spec/components/checkbox/checkbox_label.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_label.spec.js @@ -7,19 +7,19 @@ describe("Given the checkbox label variants questionnaire,", () => { await browser.openQuestionnaire("test_checkbox_instruction.json"); }); it("Given an instruction has not been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the default instruction should be visible", async () => { - await expect(await $("body").getText()).to.have.string("Select all that apply"); + await expect(await $("body").getText()).toContain("Select all that apply"); }); it("Given an instruction has been set to null in the schema for a checkbox answer, When the checkbox answer is displayed, Then the instruction should not be visible", async () => { await $(DefaultInstructionPage.red()).click(); await click(DefaultInstructionPage.submit()); - await expect(await $("body").getText()).to.not.have.string("Select all that apply"); + await expect(await $("body").getText()).not.toBe("Select all that apply"); }); it("Given a custom instruction has been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then the custom instruction should be visible", async () => { await $(DefaultInstructionPage.red()).click(); await click(DefaultInstructionPage.submit()); await $(NoInstructionPage.rugby()).click(); await click(NoInstructionPage.submit()); - await expect(await $("body").getText()).to.have.string("Select your answer"); + await expect(await $("body").getText()).toContain("Select your answer"); }); it("Given a label and custom instruction have been set in the schema for a checkbox answer, When the checkbox answer is displayed, Then both the custom instruction and label should be visible", async () => { await $(DefaultInstructionPage.red()).click(); @@ -28,7 +28,7 @@ describe("Given the checkbox label variants questionnaire,", () => { await click(NoInstructionPage.submit()); await $(CustomInstructionPage.monday()).click(); await click(CustomInstructionPage.submit()); - await expect(await $("body").getText()).to.have.string("Days of the Week"); - await expect(await $("body").getText()).to.have.string("Select your answer"); + await expect(await $("body").getText()).toContain("Days of the Week"); + await expect(await $("body").getText()).toContain("Select your answer"); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js index 4c16f9a8a1..c9644aecf4 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_checkbox.spec.js @@ -15,25 +15,25 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await $(MandatoryCheckboxPage.checkboxOther()).click(); await $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).toBe("The other option"); // When await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).toBe(""); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); }); @@ -47,7 +47,7 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", await $(SummaryPage.previous()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); }); }); @@ -55,77 +55,77 @@ describe("Component: Mutually Exclusive Checkbox With Single Checkbox Override", it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(MandatoryCheckboxPage.checkboxBritish()).click(); await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).toBe("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive option", () => { it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(MandatoryCheckboxPage.checkboxBritish()).click(); await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).toBe("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked any of the non-exclusive options", () => { it("When the user clicks the mutually exclusive option, Then only the exclusive option should be checked.", async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); // When await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); }); describe("Given the user has not clicked any options and the question is mandatory", () => { it("When the user clicks the Continue button, Then a validation error message should be displayed.", async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).toContain("Select at least one answer"); + await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).toBe(true); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js index 4c1638eb62..f570d6c5f3 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_currency.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(CurrencyPage.currency()).setValue("123"); - await expect(await $(CurrencyPage.currency()).getValue()).to.contain("123"); + await expect(await $(CurrencyPage.currency()).getValue()).toBe("123"); // When await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(CurrencyPage.currency()).getValue()).toBe(""); await click(CurrencyPage.submit()); - await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).not.toBe("123"); }); }); @@ -33,69 +33,69 @@ describe("Component: Mutually Exclusive Currency With Single Checkbox Override", it("When the user enters a value for the non-exclusive currency answer and removes focus, Then only the non-exclusive currency answer should be answered.", async () => { // Given await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(CurrencyPage.currency()).setValue("123"); // Then await $(CurrencyPage.currency()).getValue(); - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(CurrencyPage.submit()); - await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); - await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).toBe("£123"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive currency answer, Then only the non-exclusive currency answer should be answered.", async () => { // Given - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(CurrencyPage.currency()).setValue("123"); // Then - await expect(await $(CurrencyPage.currency()).getValue()).to.contain("123"); - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currency()).getValue()).toBe("123"); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(CurrencyPage.submit()); - await expect(await $(SummaryPage.currencyAnswer()).getText()).to.have.string("123"); - await expect(await $(SummaryPage.currencyAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).toBe("£123"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive currency answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); + await expect(await $(CurrencyPage.currency()).getValue()).toBe(""); // When await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).click(); - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(CurrencyPage.submit()); - await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.currencyExclusiveAnswer()).getText()).not.toBe("123"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(CurrencyPage.currency()).getValue()).to.contain(""); - await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(CurrencyPage.currency()).getValue()).toBe(""); + await expect(await $(CurrencyPage.currencyExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(CurrencyPage.submit()); // Then - await expect(await $(SummaryPage.currencyAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.currencyAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js index 131463a979..5a8869797f 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_day_month_year_date.spec.js @@ -15,23 +15,23 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await $(DatePage.dateday()).setValue("17"); await $(DatePage.datemonth()).setValue("3"); await $(DatePage.dateyear()).setValue("2018"); - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); // When await $(DatePage.dateExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); }); @@ -39,7 +39,7 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given await $(DatePage.dateExclusiveIPreferNotToSay()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(DatePage.dateday()).setValue("17"); @@ -47,23 +47,23 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await $(DatePage.dateyear()).setValue("2018"); // Then - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(DatePage.dateday()).setValue("17"); @@ -71,49 +71,49 @@ describe("Component: Mutually Exclusive Day Month Year Date With Single Checkbox await $(DatePage.dateyear()).setValue("2018"); // Then - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); // When await $(DatePage.dateExclusiveIPreferNotToSay()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(DatePage.submit()); // Then - await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js index b39f61756b..acb887c4cc 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_duration.spec.js @@ -15,21 +15,21 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", await $(DurationPage.durationYears()).setValue("1"); await $(DurationPage.durationMonths()).setValue("7"); - await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); + await expect(await $(DurationPage.durationYears()).getValue()).toBe("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe("7"); // When await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DurationPage.durationYears()).getValue()).toBe(""); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe(""); await click(DurationPage.submit()); - await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).not.toBe("1 year 7 months"); }); }); @@ -37,42 +37,42 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", it("When the user enters a value for the non-exclusive duration answer and removes focus, Then only the non-exclusive duration answer should be answered.", async () => { // Given await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(DurationPage.durationYears()).setValue("1"); await $(DurationPage.durationMonths()).setValue("7"); // Then - await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationYears()).getValue()).toBe("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe("7"); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(DurationPage.submit()); - await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); - await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationAnswer()).getText()).toBe("1 year 7 months"); + await expect(await $(SummaryPage.durationAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive duration answer, Then only the non-exclusive duration answer should be answered.", async () => { // Given - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(DurationPage.durationYears()).setValue("1"); await $(DurationPage.durationMonths()).setValue("7"); // Then - await expect(await $(DurationPage.durationYears()).getValue()).to.contain("1"); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain("7"); - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationYears()).getValue()).toBe("1"); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe("7"); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(DurationPage.submit()); - await expect(await $(SummaryPage.durationAnswer()).getText()).to.have.string("1 year 7 months"); - await expect(await $(SummaryPage.durationAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.durationAnswer()).getText()).toBe("1 year 7 months"); + await expect(await $(SummaryPage.durationAnswer()).getText()).not.toBe("I prefer not to say"); }); }); @@ -80,18 +80,18 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given await browser.url("/questionnaire/mutually-exclusive-duration"); - await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); + await expect(await $(DurationPage.durationYears()).getValue()).toBe(""); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe(""); // When await $(DurationPage.durationExclusiveIPreferNotToSay()).click(); - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(DurationPage.submit()); - await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).to.not.have.string("1 year 7 months"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.durationExclusiveAnswer()).getText()).not.toBe("1 year 7 months"); }); }); @@ -99,15 +99,15 @@ describe("Component: Mutually Exclusive Duration With Single Checkbox Override", it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given await browser.url("/questionnaire/mutually-exclusive-duration"); - await expect(await $(DurationPage.durationYears()).getValue()).to.contain(""); - await expect(await $(DurationPage.durationMonths()).getValue()).to.contain(""); - await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DurationPage.durationYears()).getValue()).toBe(""); + await expect(await $(DurationPage.durationMonths()).getValue()).toBe(""); + await expect(await $(DurationPage.durationExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(DurationPage.submit()); // Then - await expect(await $(SummaryPage.durationAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.durationAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js index 2f9f3d5ee9..d1c2befe03 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js @@ -14,21 +14,21 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove // Given await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe("2018"); // When await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe(""); await click(MonthYearDatePage.submit()); - await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).not.toBe("March 2018"); }); }); @@ -36,76 +36,76 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); // Then - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe("2018"); - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(MonthYearDatePage.submit()); - await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); - await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).toBe("March 2018"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); // Then - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain("3"); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain("2018"); - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe("3"); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe("2018"); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(MonthYearDatePage.submit()); - await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.have.string("March 2018"); - await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).toBe("March 2018"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe(""); // When await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).click(); - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(MonthYearDatePage.submit()); - await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).to.not.have.string("March 2018"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.monthYearDateExclusiveAnswer()).getText()).not.toBe("March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).to.contain(""); - await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).to.contain(""); - await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MonthYearDatePage.monthYearDateMonth()).getValue()).toBe(""); + await expect(await $(MonthYearDatePage.monthYearDateYear()).getValue()).toBe(""); + await expect(await $(MonthYearDatePage.monthYearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(MonthYearDatePage.submit()); // Then - await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.monthYearDateAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js index 910b180f43..fc505570af 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_checkbox.spec.js @@ -15,46 +15,46 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(MandatoryCheckboxPage.checkboxOther()).click(); await $(MandatoryCheckboxPage.checkboxOtherDetail()).setValue("The other option"); - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain("The other option"); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).toBe("The other option"); }); it("When then user clicks the first mutually exclusive option, Then only the first mutually exclusive option should be checked.", async () => { // When await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); // Then - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).toBe(""); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); it("When then user clicks the second mutually exclusive option, Then only the second mutually exclusive option should be checked.", async () => { // When await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); // Then - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).to.contain(""); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOtherDetail()).getValue()).toBe(""); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); }); @@ -68,7 +68,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(SummaryPage.previous()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); }); }); @@ -82,7 +82,7 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", await $(SummaryPage.previous()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(true); }); }); @@ -90,21 +90,21 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(MandatoryCheckboxPage.checkboxBritish()).click(); await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).toBe("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I prefer not to say"); }); }); @@ -112,76 +112,76 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", it("When the user clicks the non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(true); // When await $(MandatoryCheckboxPage.checkboxBritish()).click(); await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).toBe("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I am an alien"); }); }); describe("Given the user has not clicked a mutually exclusive option", () => { it("When the user clicks multiple non-exclusive options, Then only the non-exclusive options should be checked.", async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); // When await $(MandatoryCheckboxPage.checkboxBritish()).click(); await $(MandatoryCheckboxPage.checkboxIrish()).click(); // Then - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.have.string("British\nIrish"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxAnswer()).getText()).to.not.have.string("I am an alien"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).toBe("British\nIrish"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxAnswer()).getText()).not.toBe("I am an alien"); }); }); describe("Given the user has not clicked any of the non-exclusive options", () => { beforeEach(async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); }); it("When the user clicks the first mutually exclusive option, Then only the first exclusive option should be checked.", async () => { // When await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I am an alien"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); it("When the user clicks the second mutually exclusive option, Then only the second exclusive option should be checked.", async () => { // When await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); }); @@ -189,37 +189,37 @@ describe("Component: Mutually Exclusive Checkbox With Multiple Radio Override", it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); // When await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).click(); - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.true; + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(true); await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.have.string("I am an alien"); - await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).toBe("I am an alien"); + await expect(await $(SummaryPage.checkboxExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked any options and the question is mandatory", () => { it("When the user clicks the Continue button, Then a validation error message should be displayed.", async () => { // Given - await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).to.be.false; + await expect(await $(MandatoryCheckboxPage.checkboxBritish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxIrish()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxOther()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(MandatoryCheckboxPage.checkboxExclusiveIAmAnAlien()).isSelected()).toBe(false); // When await click(MandatoryCheckboxPage.submit()); // Then - await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(MandatoryCheckboxPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(MandatoryCheckboxPage.errorNumber(1)).getText()).toContain("Select at least one answer"); + await expect(await $(MandatoryCheckboxPage.questionErrorPanel()).isExisting()).toBe(true); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js index 2d9c91b75e..036c53daa5 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_day_month_year_date.spec.js @@ -14,26 +14,26 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateday()).setValue("17"); await $(DatePage.datemonth()).setValue("3"); await $(DatePage.dateyear()).setValue("2018"); - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); }); it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When await $(DatePage.dateExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); it("When then user clicks the second mutually exclusive radio answer, Then only the second mutually exclusive radio should be answered.", async () => { @@ -41,17 +41,17 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); // Then - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); }); @@ -59,8 +59,8 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given await $(DatePage.dateExclusiveIPreferNotToSay()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); // When await $(DatePage.dateday()).setValue("17"); @@ -68,17 +68,17 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateyear()).setValue("2018"); // Then - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I have never worked"); }); }); @@ -86,8 +86,8 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio it("When the user enters a value for the non-exclusive month year date answer and removes focus, Then only the non-exclusive month year date answer should be answered.", async () => { // Given await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(DatePage.dateday()).setValue("17"); @@ -95,25 +95,25 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateyear()).setValue("2018"); // Then - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I have never worked"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive month year date answer, Then only the non-exclusive month year date answer should be answered.", async () => { // Given - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); // When await $(DatePage.dateday()).setValue("17"); @@ -121,69 +121,69 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio await $(DatePage.dateyear()).setValue("2018"); // Then - await expect(await $(DatePage.dateday()).getValue()).to.contain("17"); - await expect(await $(DatePage.datemonth()).getValue()).to.contain("3"); - await expect(await $(DatePage.dateyear()).getValue()).to.contain("2018"); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).toBe("17"); + await expect(await $(DatePage.datemonth()).getValue()).toBe("3"); + await expect(await $(DatePage.dateyear()).getValue()).toBe("2018"); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); await click(DatePage.submit()); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.have.string("17 March 2018"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateAnswer()).getText()).to.not.have.string("I have never worked"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("17 March 2018"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateAnswer()).getText()).not.toBe("I have never worked"); }); }); describe("Given the user has not answered the non-exclusive month year date answer", () => { beforeEach(async () => { // Given - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); }); it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", async () => { // When await $(DatePage.dateExclusiveIPreferNotToSay()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); // Then await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I have never worked"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", async () => { // When await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // Then await click(DatePage.submit()); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("17 March 2018"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("17 March 2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(DatePage.dateday()).getValue()).to.contain(""); - await expect(await $(DatePage.datemonth()).getValue()).to.contain(""); - await expect(await $(DatePage.dateyear()).getValue()).to.contain(""); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateday()).getValue()).toBe(""); + await expect(await $(DatePage.datemonth()).getValue()).toBe(""); + await expect(await $(DatePage.dateyear()).getValue()).toBe(""); + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); // When await click(DatePage.submit()); // Then - await expect(await $(SummaryPage.dateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.dateAnswer()).getText()).toBe("No answer provided"); }); }); @@ -191,18 +191,18 @@ describe("Component: Mutually Exclusive Day Month Year Date With Multiple Radio it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given await $(DatePage.dateExclusiveIPreferNotToSay()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.false; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(false); // When await $(DatePage.dateExclusiveIHaveNeverWorked()).click(); - await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).to.be.true; + await expect(await $(DatePage.dateExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(DatePage.dateExclusiveIHaveNeverWorked()).isSelected()).toBe(true); await click(DatePage.submit()); // Then - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.have.string("I have never worked"); - await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).toBe("I have never worked"); + await expect(await $(SummaryPage.dateExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js index f521c45453..0ea9f981c9 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_multiple_textfield.spec.js @@ -13,37 +13,37 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", beforeEach(async () => { // Given await $(TextFieldPage.textfield()).setValue("Blue"); - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); }); it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); it("When then user clicks the first mutually exclusive radio answer, Then only the first mutually exclusive radio should be answered.", async () => { // When await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); // Then - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); }); @@ -51,22 +51,22 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); // When await $(TextFieldPage.textfield()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I dont have a favorite colour"); }); }); @@ -74,92 +74,92 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(true); // When await $(TextFieldPage.textfield()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I dont have a favorite colour"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); // When await $(TextFieldPage.textfield()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I dont have a favorite colour"); }); }); describe("Given the user has not answered the non-exclusive textfield answer", () => { beforeEach(async () => { // Given - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); }); it("When the user clicks the first mutually exclusive radio answer, Then only the first exclusive radio should be answered.", async () => { // When await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); // Then await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I dont have a favorite colour"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); it("When the user clicks the second mutually exclusive radio answer, Then only the second exclusive radio should be answered.", async () => { // When await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); // Then await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); // When await click(TextFieldPage.submit()); // Then - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("No answer provided"); }); }); @@ -167,18 +167,18 @@ describe("Component: Mutually Exclusive Textfield With Multiple Radio Override", it("When the user clicks another mutually exclusive option, Then only the most recently clicked mutually exclusive option should be checked.", async () => { // Given await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(false); // When await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; - await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); + await expect(await $(TextFieldPage.textfieldExclusiveIDontHaveAFavoriteColour()).isSelected()).toBe(true); await click(TextFieldPage.submit()); // Then - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I dont have a favorite colour"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I dont have a favorite colour"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("I prefer not to say"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js index 4e371beff7..a0295cb499 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_number.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(NumberPage.number()).setValue("123"); - await expect(await $(NumberPage.number()).getValue()).to.contain("123"); + await expect(await $(NumberPage.number()).getValue()).toBe("123"); // When await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(NumberPage.number()).getValue()).to.contain(""); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(NumberPage.number()).getValue()).toBe(""); await click(NumberPage.submit()); - await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).not.toBe("123"); }); }); @@ -33,69 +33,69 @@ describe("Component: Mutually Exclusive Number With Single Checkbox Override", ( it("When the user enters a value for the non-exclusive number answer and removes focus, Then only the non-exclusive number answer should be answered.", async () => { // Given await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(NumberPage.number()).setValue("123"); // Then - await expect(await $(NumberPage.number()).getValue()).to.contain("123"); - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).toBe("123"); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(NumberPage.submit()); - await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); - await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberAnswer()).getText()).toBe("123"); + await expect(await $(SummaryPage.numberAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive number answer, Then only the non-exclusive number answer should be answered.", async () => { // Given - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(NumberPage.number()).setValue("123"); // Then - await expect(await $(NumberPage.number()).getValue()).to.contain("123"); - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).toBe("123"); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(NumberPage.submit()); - await expect(await $(SummaryPage.numberAnswer()).getText()).to.have.string("123"); - await expect(await $(SummaryPage.numberAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.numberAnswer()).getText()).toBe("123"); + await expect(await $(SummaryPage.numberAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive number answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(NumberPage.number()).getValue()).to.contain(""); + await expect(await $(NumberPage.number()).getValue()).toBe(""); // When await $(NumberPage.numberExclusiveIPreferNotToSay()).click(); - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(NumberPage.submit()); - await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).to.not.have.string("123"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.numberExclusiveAnswer()).getText()).not.toBe("123"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(NumberPage.number()).getValue()).to.contain(""); - await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(NumberPage.number()).getValue()).toBe(""); + await expect(await $(NumberPage.numberExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(NumberPage.submit()); // Then - await expect(await $(SummaryPage.numberAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.numberAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js index 03908d8fb2..c12e9413a9 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_percentage.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(PercentagePage.percentage()).setValue("99"); - await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); + await expect(await $(PercentagePage.percentage()).getValue()).toBe("99"); // When await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(PercentagePage.percentage()).getValue()).toBe(""); await click(PercentagePage.submit()); - await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("99"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).not.toBe("99"); }); }); @@ -34,69 +34,69 @@ describe("Component: Mutually Exclusive Percentage With Single Checkbox Override // Given await browser.url("/questionnaire/mutually-exclusive-percentage"); await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(PercentagePage.percentage()).setValue("99"); // Then - await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).toBe("99"); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(PercentagePage.submit()); - await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); - await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).toBe("99%"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive percentage answer, Then only the non-exclusive percentage answer should be answered.", async () => { // Given - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(PercentagePage.percentage()).setValue("99"); // Then - await expect(await $(PercentagePage.percentage()).getValue()).to.contain("99"); - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).toBe("99"); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(PercentagePage.submit()); - await expect(await $(SummaryPage.percentageAnswer()).getText()).to.have.string("99"); - await expect(await $(SummaryPage.percentageAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).toBe("99%"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive percentage answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); + await expect(await $(PercentagePage.percentage()).getValue()).toBe(""); // When await $(PercentagePage.percentageExclusiveIPreferNotToSay()).click(); - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(PercentagePage.submit()); - await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).to.not.have.string("British\nIrish"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.percentageExclusiveAnswer()).getText()).not.toBe("British\nIrish"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(PercentagePage.percentage()).getValue()).to.contain(""); - await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(PercentagePage.percentage()).getValue()).toBe(""); + await expect(await $(PercentagePage.percentageExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(PercentagePage.submit()); // Then - await expect(await $(SummaryPage.percentageAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.percentageAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js index 6002cdf797..08cb99c00c 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textarea.spec.js @@ -12,50 +12,50 @@ describe("Component: Mutually Exclusive TextArea With Single Checkbox Override", describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive textarea answer, Then only the non-exclusive textarea answer should be answered.", async () => { // Given - await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(TextFieldPage.textarea()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textarea()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textarea()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textareaAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textareaAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive textarea answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(TextFieldPage.textarea()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textarea()).getValue()).toBe(""); // When await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.textareaExclusiveAnswer()).getText()).not.toBe("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(TextFieldPage.textarea()).getValue()).to.contain(""); - await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textarea()).getValue()).toBe(""); + await expect(await $(TextFieldPage.textareaExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(TextFieldPage.submit()); // Then - await expect(await $(SummaryPage.textareaAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textareaAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js index 932f64a742..cf7c1a44ed 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_textfield.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(TextFieldPage.textfield()).setValue("Blue"); - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); // When await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); }); @@ -33,69 +33,69 @@ describe("Component: Mutually Exclusive Textfield With Single Checkbox Override" it("When the user enters a value for the non-exclusive textfield answer and removes focus, Then only the non-exclusive textfield answer should be answered.", async () => { // Given await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(TextFieldPage.textfield()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive textfield answer, Then only the non-exclusive textfield answer should be answered.", async () => { // Given - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(TextFieldPage.textfield()).setValue("Blue"); // Then - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain("Blue"); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe("Blue"); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.have.string("Blue"); - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("Blue"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive textfield answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); // When await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).click(); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(TextFieldPage.submit()); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).to.not.have.string("Blue"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.textfieldExclusiveAnswer()).getText()).not.toBe("Blue"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(TextFieldPage.textfield()).getValue()).to.contain(""); - await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(TextFieldPage.textfield()).getValue()).toBe(""); + await expect(await $(TextFieldPage.textfieldExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(TextFieldPage.submit()); // Then - await expect(await $(SummaryPage.textfieldAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.textfieldAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js index c8e741b64e..b4e953a77c 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_unit.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(UnitPage.unit()).setValue("10"); - await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); + await expect(await $(UnitPage.unit()).getValue()).toBe("10"); // When await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(UnitPage.unit()).getValue()).to.contain(""); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(UnitPage.unit()).getValue()).toBe(""); await click(UnitPage.submit()); - await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).not.toBe("10"); }); }); @@ -33,69 +33,69 @@ describe("Component: Mutually Exclusive Unit With Single Checkbox Override", () it("When the user enters a value for the non-exclusive unit answer and removes focus, Then only the non-exclusive unit answer should be answered.", async () => { // Given await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(UnitPage.unit()).setValue("10"); // Then - await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).toContain("10"); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(UnitPage.submit()); - await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); - await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitAnswer()).getText()).toContain("10"); + await expect(await $(SummaryPage.unitAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive unit answer, Then only the non-exclusive unit answer should be answered.", async () => { // Given - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(UnitPage.unit()).setValue("10"); // Then - await expect(await $(UnitPage.unit()).getValue()).to.contain("10"); - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).toBe("10"); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(UnitPage.submit()); - await expect(await $(SummaryPage.unitAnswer()).getText()).to.have.string("10"); - await expect(await $(SummaryPage.unitAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SummaryPage.unitAnswer()).getText()).toContain("10"); + await expect(await $(SummaryPage.unitAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive unit answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(UnitPage.unit()).getValue()).to.contain(""); + await expect(await $(UnitPage.unit()).getValue()).toBe(""); // When await $(UnitPage.unitExclusiveIPreferNotToSay()).click(); - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(UnitPage.submit()); - await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).to.not.have.string("10"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SummaryPage.unitExclusiveAnswer()).getText()).not.toBe("10"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(UnitPage.unit()).getValue()).to.contain(""); - await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(UnitPage.unit()).getValue()).toBe(""); + await expect(await $(UnitPage.unitExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(UnitPage.submit()); // Then - await expect(await $(SummaryPage.unitAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SummaryPage.unitAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js index 8a82f80707..14a7d8f69c 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_year_date.spec.js @@ -13,19 +13,19 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(YearDatePage.yearDateYear()).setValue("2018"); - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe("2018"); // When await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); // Then - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe(""); await click(YearDatePage.submit()); - await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).not.toBe("2018"); }); }); @@ -33,69 +33,69 @@ describe("Component: Mutually Exclusive Year Date With Single Checkbox Override" it("When the user enters a value for the non-exclusive year date answer and removes focus, Then only the non-exclusive year date answer should be answered.", async () => { // Given await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // When await $(YearDatePage.yearDateYear()).setValue("2018"); // Then - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe("2018"); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(YearDatePage.submit()); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).toBe("2018"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not clicked the mutually exclusive checkbox answer", () => { it("When the user enters a value for the non-exclusive year date answer, Then only the non-exclusive year date answer should be answered.", async () => { // Given - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await $(YearDatePage.yearDateYear()).setValue("2018"); // Then - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain("2018"); - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe("2018"); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); await click(YearDatePage.submit()); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.have.string("2018"); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.not.have.string("I prefer not to say"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).toBe("2018"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).not.toBe("I prefer not to say"); }); }); describe("Given the user has not answered the non-exclusive year date answer", () => { it("When the user clicks the mutually exclusive checkbox answer, Then only the exclusive checkbox should be answered.", async () => { // Given - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe(""); // When await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).click(); - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.true; + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(true); // Then await click(YearDatePage.submit()); - await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.have.string("I prefer not to say"); - await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).to.not.have.string("2018"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).toBe("I prefer not to say"); + await expect(await $(SubmitPage.yearDateExclusiveAnswer()).getText()).not.toBe("2018"); }); }); describe("Given the user has not answered the question and the question is optional", () => { it("When the user clicks the Continue button, Then it should display `No answer provided`", async () => { // Given - await expect(await $(YearDatePage.yearDateYear()).getValue()).to.contain(""); - await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).to.be.false; + await expect(await $(YearDatePage.yearDateYear()).getValue()).toBe(""); + await expect(await $(YearDatePage.yearDateExclusiveIPreferNotToSay()).isSelected()).toBe(false); // When await click(YearDatePage.submit()); // Then - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/dropdown/dropdown.spec.js b/tests/functional/spec/components/dropdown/dropdown.spec.js index 7f750c7a7c..cdc1986718 100644 --- a/tests/functional/spec/components/dropdown/dropdown.spec.js +++ b/tests/functional/spec/components/dropdown/dropdown.spec.js @@ -15,46 +15,46 @@ describe("Component: Dropdown", () => { it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Rugby is better!"); }); it("When I have not selected a dropdown option and click Continue, Then the default error message should be displayed", async () => { await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.contain("Select an answer"); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).toBe("Select an answer"); }); it("When I have selected a dropdown option and I try to select a default (disabled) dropdown option, Then the already selected option should be displayed in summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); await $(DropdownMandatoryPage.answer()).selectByAttribute("value", ""); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Liverpool"); }); it("When I click the dropdown label, Then the dropdown should be focused", async () => { await $(DropdownMandatoryPage.answerLabel()).click(); - await expect(await $(DropdownMandatoryPage.answer()).isFocused()).to.be.true; + await expect(await $(DropdownMandatoryPage.answer()).isFocused()).toBe(true); }); it("When I'm on the summary page and I click Edit then Continue, Then the answer on the summary page should be unchanged", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Rugby is better!"); }); it("When I'm on the summary page and I click Edit and change the answer, Then the newly selected answer should be displayed in the summary", async () => { await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Rugby is better!"); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Rugby is better!"); await $(DropdownMandatorySummary.dropdownMandatoryAnswerEdit()).click(); await $(DropdownMandatoryPage.answer()).selectByAttribute("value", "Liverpool"); await click(DropdownMandatoryPage.submit()); - await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).to.contain("Liverpool"); + await expect(await $(DropdownMandatorySummary.dropdownMandatoryAnswer()).getText()).toBe("Liverpool"); }); }); @@ -65,7 +65,7 @@ describe("Component: Dropdown", () => { it("When I have not selected a dropdown option and click Continue, Then the overridden error message should be displayed", async () => { await click(DropdownMandatoryOverriddenPage.submit()); - await expect(await $(DropdownMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Overridden test error message."); + await expect(await $(DropdownMandatoryOverriddenPage.errorNumber(1)).getText()).toBe("Overridden test error message."); }); }); @@ -77,23 +77,23 @@ describe("Component: Dropdown", () => { it('When I have not selected a dropdown option, Then the summary should display "No answer provided"', async () => { await click(DropdownOptionalPage.submit()); - await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).toBe("No answer provided"); }); it("When I have selected a dropdown option, Then the selected option should be displayed in the summary", async () => { await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Rugby is better!"); await click(DropdownOptionalPage.submit()); - await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Rugby is better!"); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).toBe("Rugby is better!"); }); it('When I have selected a dropdown option and I reselect the default option (Select an answer), Then the summary should display "No answer provided"', async () => { await $(DropdownOptionalPage.answer()).selectByAttribute("value", "Chelsea"); await click(DropdownOptionalPage.submit()); - await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("Chelsea"); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).toBe("Chelsea"); await $(DropdownOptionalSummary.dropdownOptionalAnswerEdit()).click(); await $(DropdownOptionalPage.answer()).selectByAttribute("value", ""); await click(DropdownOptionalPage.submit()); - await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(DropdownOptionalSummary.dropdownOptionalAnswer()).getText()).toBe("No answer provided"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio.js b/tests/functional/spec/components/radio/radio.js index 28af6e2e1f..a077ed88e1 100644 --- a/tests/functional/spec/components/radio/radio.js +++ b/tests/functional/spec/components/radio/radio.js @@ -25,8 +25,8 @@ describe("Component: Radio", () => { it("When I have selected a radio option that contains an escaped character, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.teaCoffee()).click(); await click(RadioMandatoryPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); - await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Tea & Coffee"); + await expect(browser).toHaveUrlContaining(RadioMandatorySummary.pageName); + await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).toBe("Tea & Coffee"); }); }); @@ -38,8 +38,8 @@ describe("Component: Radio", () => { it("When I have selected a radio option, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.coffee()).click(); await click(RadioMandatoryPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioMandatorySummary.pageName); - await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).to.contain("Coffee"); + await expect(browser).toHaveUrlContaining(RadioMandatorySummary.pageName); + await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).toBe("Coffee"); }); }); @@ -50,7 +50,7 @@ describe("Component: Radio", () => { it("When I have submitted the page without any option, Then the question text is hidden in the error message using a span element", async () => { await click(RadioMandatoryOverriddenPage.submit()); - await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).to.contain( + await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getHTML()).toContain( 'Select an answer to ‘What do you prefer for breakfast?’', ); }); @@ -65,8 +65,8 @@ describe("Component: Radio", () => { await $(RadioMandatoryOptionalDetailAnswerPage.other()).click(); await $(RadioMandatoryOptionalDetailAnswerPage.otherDetail()).setValue("Hello World"); await click(RadioMandatoryOptionalDetailAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); - await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("Hello World"); + await expect(browser).toHaveUrlContaining(RadioMandatoryOptionDetailAnswerSummary.pageName); + await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).toContain("Hello World"); }); }); @@ -78,7 +78,7 @@ describe("Component: Radio", () => { it("When I submit without any data in the other text field it should Then throw an overridden error", async () => { await $(RadioMandatoryDetailAnswerOverriddenPage.other()).click(); await click(RadioMandatoryDetailAnswerOverriddenPage.submit()); - await expect(await $(RadioMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + await expect(await $(RadioMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).toBe("Test error message is overridden"); }); }); @@ -89,8 +89,8 @@ describe("Component: Radio", () => { it("When I submit without any data in the other text field is selected, Then the selected option should be displayed in the summary", async () => { await click(RadioMandatoryOptionalDetailAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioMandatoryOptionDetailAnswerSummary.pageName); - await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).to.contain("No answer provided"); + await expect(browser).toHaveUrlContaining(RadioMandatoryOptionDetailAnswerSummary.pageName); + await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).toContain("No answer provided"); }); }); @@ -101,7 +101,7 @@ describe("Component: Radio", () => { it("When I have submitted the page without any option, Then an overridden error is displayed", async () => { await click(RadioMandatoryOverriddenPage.submit()); - await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + await expect(await $(RadioMandatoryOverriddenPage.errorNumber(1)).getText()).toBe("Test error message is overridden"); }); }); @@ -112,8 +112,8 @@ describe("Component: Radio", () => { it("When I have selected no option, Then the selected option should be displayed in the summary", async () => { await click(RadioNonMandatoryPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioNonMandatorySummary.pageName); - await expect(await $(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); + await expect(browser).toHaveUrlContaining(RadioNonMandatorySummary.pageName); + await expect(await $(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).toBe("No answer provided"); }); }); @@ -125,7 +125,7 @@ describe("Component: Radio", () => { it("When I have submitted an other option with an empty text field, Then an overridden error is displayed", async () => { await $(RadioNonMandatoryDetailAnswerOverriddenPage.other()).click(); await click(RadioNonMandatoryDetailAnswerOverriddenPage.submit()); - await expect(await $(RadioNonMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).to.contain("Test error message is overridden"); + await expect(await $(RadioNonMandatoryDetailAnswerOverriddenPage.errorNumber(1)).getText()).toBe("Test error message is overridden"); }); }); @@ -138,8 +138,8 @@ describe("Component: Radio", () => { await $(RadioNonMandatoryDetailAnswerPage.other()).click(); await $(RadioNonMandatoryDetailAnswerPage.otherDetail()).setValue("Hello World"); await click(RadioNonMandatoryDetailAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(RadioNonMandatoryDetailAnswerSummary.pageName); - await expect(await $(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).to.contain("Hello World"); + await expect(browser).toHaveUrlContaining(RadioNonMandatoryDetailAnswerSummary.pageName); + await expect(await $(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).toContain("Hello World"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js index 654d0006ca..c7f26ad488 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_dropdown.spec.js @@ -11,26 +11,26 @@ describe("Optional Radio with a Dropdown detail answer", () => { it("When a placeholder is set for the detail answer, Then that value should be displayed as the first option", async () => { await $(RadioDropdownPage.fruit()).click(); - await expect(await $(RadioDropdownPage.fruitDetail()).getText()).to.contain("Select fruit"); + await expect(await $(RadioDropdownPage.fruitDetail()).getText()).toContain("Select fruit"); }); it("When a placeholder is not set for the detail answer, Then the default placeholder should be displayed as the first option", async () => { await $(RadioDropdownPage.jam()).click(); - await expect(await $(RadioDropdownPage.jamDetail()).getText()).to.contain("Select an answer"); + await expect(await $(RadioDropdownPage.jamDetail()).getText()).toContain("Select an answer"); }); it("When the user does not provide an answer and submits, Then the summary should display 'No answer provided'", async () => { await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("No answer provided"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("No answer provided"); }); it("When the user selects an option with an optional detail answer but does not provide a detail answer, Then the summary should display the chosen option without the detail answer", async () => { await $(RadioDropdownPage.fruit()).click(); await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("Fruit"); }); it("When the user selects an option with an optional detail answer and provides a detail answer, Then the summary should display the chosen option and the detail answer", async () => { @@ -38,7 +38,7 @@ describe("Optional Radio with a Dropdown detail answer", () => { await $(RadioDropdownPage.fruitDetail()).selectByAttribute("value", "Mango"); await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit\nMango"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("Fruit\nMango"); }); it("When the user selects the default dropdown option after submitting a detail answer, Then the summary should not display the detail answer", async () => { @@ -49,14 +49,14 @@ describe("Optional Radio with a Dropdown detail answer", () => { await $(RadioDropdownPage.fruitDetail()).selectByVisibleText("Select fruit"); await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("Fruit"); }); it("When the user selects an option with an mandatory detail answer but does not provide a detail answer, Then an error should be displayed when the user submits", async () => { await $(RadioDropdownPage.jam()).click(); await click(RadioDropdownPage.submit()); - await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).to.equal("Please select the type of Jam"); + await expect(await $(DropdownMandatoryPage.errorNumber(1)).getText()).toBe("Please select the type of Jam"); }); it("When the user selects an option with an mandatory detail answer and provides a detail answer, Then the summary should display the chosen option and its detail answer", async () => { @@ -64,7 +64,7 @@ describe("Optional Radio with a Dropdown detail answer", () => { await $(RadioDropdownPage.jamDetail()).selectByAttribute("value", "Strawberry"); await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Jam\nStrawberry"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("Jam\nStrawberry"); }); it("When the user removes a previously submitted detail answer by selecting another radio option, Then the summary should only display the new radio option", async () => { @@ -75,7 +75,7 @@ describe("Optional Radio with a Dropdown detail answer", () => { await $(RadioDropdownPage.fruit()).click(); await click(RadioDropdownPage.submit()); - await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).to.equal("Fruit"); + await expect(await $(SubmitPage.optionalRadioWithDropdownDetailAnswer()).getText()).toBe("Fruit"); }); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js index beb5cd0514..d52cad891b 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js @@ -7,9 +7,9 @@ describe('Radio with multiple "detail_answer" options', () => { it("Given detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { await browser.openQuestionnaire(radioSchema); await $(MandatoryRadioPage.eggs()).click(); - await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).toBe(true); await $(MandatoryRadioPage.favouriteNotListed()).click(); - await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).isDisplayed()).toBe(true); }); it("Given a mandatory detail answer, When I select the option but leave the input field empty and submit, Then an error should be displayed.", async () => { @@ -19,8 +19,8 @@ describe('Radio with multiple "detail_answer" options', () => { await $(MandatoryRadioPage.favouriteNotListed()).click(); await click(MandatoryRadioPage.submit()); // Then - await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; - await expect(await $(MandatoryRadioPage.errorNumber(1)).getText()).to.contain("Enter your favourite to continue"); + await expect(await $(MandatoryRadioPage.error()).isDisplayed()).toBe(true); + await expect(await $(MandatoryRadioPage.errorNumber(1)).getText()).toBe("Enter your favourite to continue"); }); it("Given a selected radio answer with an error for a mandatory detail answer, When I enter valid value and submit the page, Then the error is cleared and I navigate to next page.", async () => { @@ -28,12 +28,12 @@ describe('Radio with multiple "detail_answer" options', () => { await browser.openQuestionnaire(radioSchema); await $(MandatoryRadioPage.favouriteNotListed()).click(); await click(MandatoryRadioPage.submit()); - await expect(await $(MandatoryRadioPage.error()).isDisplayed()).to.be.true; + await expect(await $(MandatoryRadioPage.error()).isDisplayed()).toBe(true); // When await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); await click(MandatoryRadioPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { @@ -41,10 +41,10 @@ describe('Radio with multiple "detail_answer" options', () => { await browser.openQuestionnaire(radioSchema); // When await $(MandatoryRadioPage.eggs()).click(); - await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).to.be.true; + await expect(await $(MandatoryRadioPage.eggsDetail()).isDisplayed()).toBe(true); await click(MandatoryRadioPage.submit()); // Then - await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs"); + await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).toBe("Eggs"); }); it("Given a detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { @@ -55,7 +55,7 @@ describe('Radio with multiple "detail_answer" options', () => { await $(MandatoryRadioPage.eggsDetail()).setValue("Scrambled"); await click(MandatoryRadioPage.submit()); // Then - await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).to.equal("Eggs\nScrambled"); + await expect(await $(SubmitPage.radioMandatoryAnswer()).getText()).toBe("Eggs\nScrambled"); }); it("Given I have previously added text in a detail answer and saved, When I select a different radio and save, Then the text entered in the detail answer field should be empty.", async () => { @@ -71,6 +71,6 @@ describe('Radio with multiple "detail_answer" options', () => { await $(SubmitPage.previous()).click(); // Then await $(MandatoryRadioPage.favouriteNotListed()).click(); - await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).getValue()).to.equal(""); + await expect(await $(MandatoryRadioPage.favouriteNotListedDetail()).getValue()).toBe(""); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js index 6c71912857..22afcb6fa2 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_numeric.spec.js @@ -8,15 +8,15 @@ describe('Radio with a numeric "detail_answer" option', () => { }); it("Given a numeric detail answer options are available, When the user clicks an option, Then the detail answer input should be visible.", async () => { - await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).toBe(true); }); it("Given a numeric detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { // When - await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).to.be.true; + await expect(await $(RadioNumericDetailPage.otherDetail()).isDisplayed()).toBe(true); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).toBe("Other"); }); it("Given a numeric detail answer, When the user provides text, Then that text should be displayed on the summary screen", async () => { @@ -24,7 +24,7 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("15"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("15"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).toContain("15"); }); it("Given a numeric detail answer, When the user provides text, An error should be displayed", async () => { @@ -32,8 +32,8 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("fhdjkshfjkds"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).toBe("Please enter an integer"); }); it("Given a numeric detail answer, When the user provides a number larger than 20, An error should be displayed", async () => { @@ -41,8 +41,8 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("250"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number is too large"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).toBe("Number is too large"); }); it("Given a numeric detail answer, When the user provides a number less than 0, An error should be displayed", async () => { @@ -50,8 +50,8 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("-1"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Number cannot be less than zero"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).toBe("Number cannot be less than zero"); }); it("Given a numeric detail answer, When the user provides text, An error should be displayed and the text in the textbox should be kept", async () => { @@ -59,9 +59,9 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("biscuits"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).to.be.true; - await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).to.contain("Please enter an integer"); - await expect(await $(RadioNumericDetailPage.otherDetail()).getValue()).to.contain("biscuits"); + await expect(await $(RadioNumericDetailPage.error()).isDisplayed()).toBe(true); + await expect(await $(RadioNumericDetailPage.errorNumber(1)).getText()).toBe("Please enter an integer"); + await expect(await $(RadioNumericDetailPage.otherDetail()).getValue()).toBe("biscuits"); }); it('Given a numeric detail answer, When the user enters "0" and submits, Then "0" should be displayed on the summary screen', async () => { @@ -69,6 +69,6 @@ describe('Radio with a numeric "detail_answer" option', () => { await $(RadioNumericDetailPage.otherDetail()).setValue("0"); await click(RadioNumericDetailPage.submit()); // Then - await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).to.contain("0"); + await expect(await $(SubmitPage.radioAnswerNumericDetail()).getText()).toContain("0"); }); }); diff --git a/tests/functional/spec/components/radio/radio_visible_answers.spec.js b/tests/functional/spec/components/radio/radio_visible_answers.spec.js index d9bc8202de..9f5d8271c8 100644 --- a/tests/functional/spec/components/radio/radio_visible_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_visible_answers.spec.js @@ -8,18 +8,18 @@ describe("Given I start a Radio survey with a write-in option", () => { }); it("When I view a write-in radio and the visible option is set to true, Then the detail answer label should be displayed", async () => { - await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); + await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).toBe(true); }); it("When I view a write-in radio and the visible option is set to true, Then after choosing non write-in option the detail answer label should be displayed", async () => { await $(RadioVisibleTruePage.coffee()).click(); - await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).to.equal(true); + await expect(await $(RadioVisibleTruePage.otherDetail()).isDisplayed()).toBe(true); }); it("When I view a write-in radio and the visible option is set to false, Then the detail answer label should not be displayed", async () => { await $(RadioVisibleTruePage.coffee()).click(); await click(RadioVisibleTruePage.submit()); - await expect(await $(RadioVisibleFalsePage.otherDetail()).isDisplayed()).to.equal(false); + await expect(await $(RadioVisibleFalsePage.otherDetail()).isDisplayed()).toBe(false); }); it("When I view a write-in radio and the visible option is not set, Then the detail answer label should not be displayed", async () => { @@ -27,6 +27,6 @@ describe("Given I start a Radio survey with a write-in option", () => { await click(RadioVisibleFalsePage.submit()); await $(RadioVisibleFalsePage.iceCream()).click(); await click(RadioVisibleFalsePage.submit()); - await expect(await $(RadioVisibleNonePage.otherDetail()).isDisplayed()).to.equal(false); + await expect(await $(RadioVisibleNonePage.otherDetail()).isDisplayed()).toBe(false); }); }); diff --git a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js index ba666fcdf1..0fe2c8b8bd 100644 --- a/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js +++ b/tests/functional/spec/components/radio/radio_voluntary_answers.spec.js @@ -9,14 +9,14 @@ describe("Component: Radio", () => { it("When I select a voluntary radio option, Then the clear button should be displayed", async () => { await $(RadioVoluntaryTruePage.coffee()).click(); - await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(true); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).toBe(true); }); it("When I select a voluntary radio option and click the clear button, Then the radio option should not be selected and the clear button should not be displayed", async () => { await $(RadioVoluntaryTruePage.coffee()).click(); await $(RadioVoluntaryTruePage.clearSelectionButton()).click(); - await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); - await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); + await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).toBe(false); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).toBe(false); }); it("When I clear a previously saved voluntary radio option and submit, Then when returning to the page the radio option is no longer selected", async () => { @@ -26,14 +26,14 @@ describe("Component: Radio", () => { await $(RadioVoluntaryTruePage.clearSelectionButton()).click(); await click(RadioVoluntaryTruePage.submit()); await $(RadioVoluntaryTruePage.previous()).click(); - await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).to.equal(false); - await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).to.equal(false); + await expect(await $(RadioVoluntaryTruePage.coffee()).isSelected()).toBe(false); + await expect(await $(RadioVoluntaryTruePage.clearSelectionButton()).isDisplayed()).toBe(false); }); it("When I select a non-voluntary radio option, Then the clear button should not be displayed on the page", async () => { await click(RadioVoluntaryTruePage.submit()); await $(RadioVoluntaryFalsePage.iceCream()).click(); - await expect(await $(RadioVoluntaryFalsePage.clearSelectionButton()).isDisplayed()).to.equal(false); + await expect(await $(RadioVoluntaryFalsePage.clearSelectionButton()).isDisplayed()).toBe(false); }); }); }); diff --git a/tests/functional/spec/conditional_combined_routing.spec.js b/tests/functional/spec/conditional_combined_routing.spec.js index 0907db5481..9a82287153 100644 --- a/tests/functional/spec/conditional_combined_routing.spec.js +++ b/tests/functional/spec/conditional_combined_routing.spec.js @@ -14,7 +14,7 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.yes()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); + await expect(browser).toHaveUrlContaining(ResponseAny.pageName); // Or await $(ResponseAny.previous()).click(); @@ -24,7 +24,7 @@ describe("Conditional combined routing.", () => { await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(await browser.getUrl()).to.contain(ResponseAny.pageName); + await expect(browser).toHaveUrlContaining(ResponseAny.pageName); }); it('Given a list of radio options, when I choose the option "No, I prefer tea" then I should be routed to the relevant page', async () => { @@ -32,7 +32,7 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.noIPreferTea()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(await browser.getUrl()).to.contain(ResponseNotAny.pageName); + await expect(browser).toHaveUrlContaining(ResponseNotAny.pageName); }); it('Given a list of radio options, when I choose the option "No, I don\'t drink any hot drinks" then I should be routed to the submit page', async () => { @@ -40,6 +40,6 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.noIDonTDrinkAnyHotDrinks()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index 7257be8242..0f41117fae 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -15,60 +15,60 @@ describe("Email confirmation", () => { it("When I complete the survey and am on the thank you page, Then there is option to enter an email address", async () => { await click(SubmitPage.submit()); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.email()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.email()).isExisting()).toBe(true); }); it("When I submit the form without providing an email address, Then I get an error message", async () => { await click(ThankYouPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(ThankYouPage.errorPanel()).getText()).toContain("Enter an email address"); }); it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ThankYouPage.email()).setValue("incorrect-format"); await click(ThankYouPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(ThankYouPage.errorPanel()).getText()).to.contain("Enter an email address in a valid format, for example name@example.com"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(ThankYouPage.errorPanel()).getText()).toContain("Enter an email address in a valid format, for example name@example.com"); }); it("When I submit the form with a valid email address, Then I go to the confirm email page", async () => { await $(ThankYouPage.email()).setValue("name@example.com"); await click(ThankYouPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); - await expect(await $(ConfirmEmailPage.questionTitle()).getText()).to.equal("Is this email address correct?"); + await expect(browser).toHaveUrlContaining("confirmation-email/confirm"); + await expect(await $(ConfirmEmailPage.questionTitle()).getText()).toBe("Is this email address correct?"); }); it("When I submit the confirm email page without providing an answer, Then I get an error message", async () => { await click(ConfirmEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/confirm"); - await expect(await $(ConfirmEmailPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(ConfirmEmailPage.errorPanel()).getText()).to.contain("Select an answer"); + await expect(browser).toHaveUrlContaining("confirmation-email/confirm"); + await expect(await $(ConfirmEmailPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(ConfirmEmailPage.errorPanel()).getText()).toContain("Select an answer"); }); it("When I answer 'Yes' and submit the confirm email page, Then I go to email sent page", async () => { await $(ConfirmEmailPage.yes()).click(); await click(ConfirmEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); - await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); + await expect(browser).toHaveUrlContaining("confirmation-email/sent"); + await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).toBe("A confirmation email has been sent to name@example.com"); }); it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", async () => { await $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); await click(ConfirmationEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/send"); - await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address"); + await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).toBe("Enter an email address"); }); it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ConfirmationEmailPage.email()).setValue("incorrect-format"); await click(ConfirmationEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/send"); - await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).to.equal("Enter an email address in a valid format, for example name@example.com"); + await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).toBe("Enter an email address in a valid format, for example name@example.com"); }); it("When I submit the form with a valid email and confirm it is correct, Then I go to the email confirmation page", async () => { @@ -76,8 +76,8 @@ describe("Email confirmation", () => { await click(ConfirmationEmailPage.submit()); await $(ConfirmEmailPage.yes()).click(); await click(ConfirmEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/sent"); - await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).to.equal("A confirmation email has been sent to name@example.com"); + await expect(browser).toHaveUrlContaining("confirmation-email/sent"); + await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).toBe("A confirmation email has been sent to name@example.com"); }); }); describe("Given I launch the test email confirmation survey", () => { @@ -91,8 +91,8 @@ describe("Email confirmation", () => { await click(ThankYouPage.submit()); await $(ConfirmEmailPage.no()).click(); await click(ConfirmEmailPage.submit()); - await expect(await browser.getUrl()).to.contain("confirmation-email/send"); - await expect(await $(ConfirmationEmailPage.email()).getValue()).to.equal("name@example.com"); + await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await expect(await $(ConfirmationEmailPage.email()).getValue()).toBe("name@example.com"); }); }); }); @@ -107,7 +107,7 @@ describe("Email confirmation", () => { await click(SubmitPage.submit()); await $(ThankYouPage.email()).setValue("name@example.com"); await click(ThankYouPage.submit()); - await expect(await $(ConfirmationEmailSentPage.feedbackLink()).isExisting()).to.equal(false); + await expect(await $(ConfirmationEmailSentPage.feedbackLink()).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/content_variants.spec.js b/tests/functional/spec/content_variants.spec.js index 5c7b1e3008..28fe119254 100644 --- a/tests/functional/spec/content_variants.spec.js +++ b/tests/functional/spec/content_variants.spec.js @@ -9,12 +9,12 @@ describe("QuestionVariants", () => { it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { await $(ageQuestionBlock.age()).setValue(12); await click(ageQuestionBlock.submit()); - await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or younger"); + await expect(await $("main.ons-page__main h1").getText()).toBe("You are 16 or younger"); }); it("Given I am completing the survey, then the correct content is shown based on my previous answers when i am under 16", async () => { await $(ageQuestionBlock.age()).setValue(22); await click(ageQuestionBlock.submit()); - await expect(await $("main.ons-page__main h1").getText()).to.contain("You are 16 or older"); + await expect(await $("main.ons-page__main h1").getText()).toBe("You are 16 or older"); }); }); diff --git a/tests/functional/spec/cookie_banner.spec.js b/tests/functional/spec/cookie_banner.spec.js index 66d3f7d7fe..0cbe1fd9ec 100644 --- a/tests/functional/spec/cookie_banner.spec.js +++ b/tests/functional/spec/cookie_banner.spec.js @@ -4,7 +4,7 @@ import HubPage from "../base_pages/hub.page.js"; describe("Given I am not authenticated and have no cookie,", () => { it("When I visit a page in runner, Then the cookie banner shouldn‘t be displayed", async () => { await browser.url("/"); - await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).toBe(false); }); }); @@ -13,22 +13,22 @@ describe("Given I start a survey,", () => { await browser.openQuestionnaire("test_checkbox.json"); }); it("When I open the page, Then the cookie banner should be displayed", async () => { - await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).toBe(true); }); it("When I delete all cookies from the browser and refresh the page, Then the cookie banner shouldn‘t be displayed", async () => { await browser.deleteAllCookies(); await browser.refresh(); - await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).toBe(false); }); it("When I sign out and click the browser back button, Then the cookie banner should be displayed", async () => { await $(InitialPage.saveSignOut()).click(); await browser.back(); - await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.true; + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).toBe(true); }); it("When I accept the cookies and refresh the page, Then the cookie banner shouldn‘t be displayed", async () => { await $(InitialPage.acceptCookies()).click(); await browser.refresh(); - await expect(await $(InitialPage.acceptCookies()).isDisplayed()).to.be.false; + await expect(await $(InitialPage.acceptCookies()).isDisplayed()).toBe(false); }); }); @@ -40,20 +40,20 @@ describe("Given I start a survey with multiple languages,", () => { await browser.openQuestionnaire("test_language.json", { language: "en", }); - await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Accept additional cookies"); + await expect(await $(HubPage.acceptCookies()).getText()).toBe("Accept additional cookies"); }); it("When I open the page in welsh, Then the cookie banner should be displayed in welsh", async () => { await browser.openQuestionnaire("test_language.json", { language: "cy", }); - await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Derbyn cwcis ychwanegol"); + await expect(await $(HubPage.acceptCookies()).getText()).toBe("Derbyn cwcis ychwanegol"); }); it("When I open the page in english, Then change the language to welsh the cookie banner should be displayed in welsh", async () => { await browser.openQuestionnaire("test_language.json", { language: "en", }); - await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Accept additional cookies"); + await expect(await $(HubPage.acceptCookies()).getText()).toBe("Accept additional cookies"); await $(HubPage.switchLanguage("cy")).click(); - await expect(await $(HubPage.acceptCookies()).getText()).to.contain("Derbyn cwcis ychwanegol"); + await expect(await $(HubPage.acceptCookies()).getText()).toBe("Derbyn cwcis ychwanegol"); }); }); diff --git a/tests/functional/spec/custom_page_titles.spec.js b/tests/functional/spec/custom_page_titles.spec.js index d3759caa3e..135e7b1af9 100644 --- a/tests/functional/spec/custom_page_titles.spec.js +++ b/tests/functional/spec/custom_page_titles.spec.js @@ -19,14 +19,14 @@ describe("Feature: Custom Page Titles", () => { it("When I navigate to the list collector page, Then I should see the custom page title", async () => { await click(HubPage.submit()); const expectedPageTitle = await browser.getTitle(); - await expect(expectedPageTitle).to.equal("Custom page title - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Custom page title - Test Custom Page Titles"); }); it("When I navigate to the add person page, Then I should see the custom page title", async () => { await $(ListCollectorPage.yes()).click(); await click(ListCollectorPage.submit()); let expectedPageTitle = await browser.getTitle(); - await expect(expectedPageTitle).to.equal("Add person 1 - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Add person 1 - Test Custom Page Titles"); await $(ListCollectorAddPage.firstName()).setValue("Marcus"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); @@ -34,7 +34,7 @@ describe("Feature: Custom Page Titles", () => { await $(ListCollectorPage.yes()).click(); await click(ListCollectorPage.submit()); expectedPageTitle = await browser.getTitle(); - await expect(expectedPageTitle).to.equal("Add person 2 - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Add person 2 - Test Custom Page Titles"); }); it("When I navigate to relationship collector pages, Then I should see the custom page titles", async () => { @@ -49,44 +49,44 @@ describe("Feature: Custom Page Titles", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); let expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 2 - Test Custom Page Titles"); + await expect(await expectedPageTitle).toBe("How Person 1 is related to Person 2 - Test Custom Page Titles"); await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); expectedPageTitle = browser.getTitle(); - await expect(await expectedPageTitle).to.equal("How Person 1 is related to Person 3 - Test Custom Page Titles"); + await expect(await expectedPageTitle).toBe("How Person 1 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).to.equal("How Person 2 is related to Person 3 - Test Custom Page Titles"); + await expect(await expectedPageTitle).toBe("How Person 2 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).to.equal("Custom section summary page title - Test Custom Page Titles"); + await expect(await expectedPageTitle).toBe("Custom section summary page title - Test Custom Page Titles"); }); it("When I navigate to list edit and remove pages Then I should see the custom page titles", async () => { await $(ListCollectorPage.listEditLink(1)).click(); let expectedPageTitle = await browser.getTitle(); - await expect(expectedPageTitle).to.equal("Edit person 1 - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Edit person 1 - Test Custom Page Titles"); await $(ListCollectorEditPage.previous()).click(); await $(ListCollectorPage.listRemoveLink(1)).click(); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).to.equal("Remove person 1 - Test Custom Page Titles"); + await expect(await expectedPageTitle).toBe("Remove person 1 - Test Custom Page Titles"); }); it("When I navigate to a repeating section which has custom page title, Then all page titles in the section should have the correct prefix", async () => { await browser.url(HubPage.url()); await click(HubPage.submit()); - await expect(await browser.getTitle()).to.equal("Individual interstitial: Person 1 - Test Custom Page Titles"); + await expect(await browser.getTitle()).toBe("Individual interstitial: Person 1 - Test Custom Page Titles"); await click(IndividualInterstitialPage.submit()); - await expect(await browser.getTitle()).to.equal("Proxy question: Person 1 - Test Custom Page Titles"); + await expect(await browser.getTitle()).toBe("Proxy question: Person 1 - Test Custom Page Titles"); await click(ProxyPage.submit()); - await expect(await browser.getTitle()).to.equal("What is your date of birth?: Person 1 - Test Custom Page Titles"); + await expect(await browser.getTitle()).toBe("What is your date of birth?: Person 1 - Test Custom Page Titles"); await click(DateOfBirthPage.submit()); - await expect(await browser.getTitle()).to.equal("Summary: Person 1 - Test Custom Page Titles"); + await expect(await browser.getTitle()).toBe("Summary: Person 1 - Test Custom Page Titles"); }); }); }); diff --git a/tests/functional/spec/dates.spec.js b/tests/functional/spec/dates.spec.js index 894a00058c..8785af9e2c 100644 --- a/tests/functional/spec/dates.spec.js +++ b/tests/functional/spec/dates.spec.js @@ -12,7 +12,7 @@ describe("Date checks", () => { }); it("Given an answer label is provided for a date question then the label should be displayed ", async () => { - await expect(await $(DateRangePage.legend()).getText()).to.contain("Period from"); + await expect(await $(DateRangePage.legend()).getText()).toBe("Period from"); }); it("Given an answer label is not provided for a date question then the question title should be used within the legend ", async () => { @@ -26,7 +26,7 @@ describe("Date checks", () => { await click(DateRangePage.submit()); - await expect(await $(DateMonthYearPage.legend()).getText()).to.contain("Date with month and year"); + await expect(await $(DateMonthYearPage.legend()).getText()).toBe("Date with month and year"); }); it("Given the test_dates survey is selected when dates are entered then the summary screen shows the dates entered formatted", async () => { @@ -58,14 +58,14 @@ describe("Date checks", () => { await click(DateYearDatePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); // Then the summary screen shows the dates entered formatted - await expect(await $(SubmitPage.dateRangeFromAnswer()).getText()).to.contain("1 January 1901 to 3 May 2017"); - await expect(await $(SubmitPage.monthYearAnswer()).getText()).to.contain("April 2018"); - await expect(await $(SubmitPage.singleDateAnswer()).getText()).to.contain("4 January 1999"); - await expect(await $(SubmitPage.nonMandatoryDateAnswer()).getText()).to.contain("No answer provided"); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.contain("2005"); + await expect(await $(SubmitPage.dateRangeFromAnswer()).getText()).toBe("1 January 1901 to 3 May 2017"); + await expect(await $(SubmitPage.monthYearAnswer()).getText()).toBe("April 2018"); + await expect(await $(SubmitPage.singleDateAnswer()).getText()).toBe("4 January 1999"); + await expect(await $(SubmitPage.nonMandatoryDateAnswer()).getText()).toBe("No answer provided"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).toBe("2005"); }); it("Given the test_dates survey is selected when the from date is greater than the to date then an error message is shown", async () => { @@ -81,12 +81,12 @@ describe("Date checks", () => { await click(DateRangePage.submit()); // Then an error message is shown and the question panel is highlighted - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); - await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).toBe(true); // Then clicking error should focus on first input field await $(DateRangePage.errorNumber(1)).click(); - await expect(await $(DateRangePage.dateRangeFromday()).isFocused()).to.be.true; + await expect(await $(DateRangePage.dateRangeFromday()).isFocused()).toBe(true); }); it("Given the test_dates survey is selected when the from date and the to date are the same then an error message is shown", async () => { @@ -102,8 +102,8 @@ describe("Date checks", () => { await click(DateRangePage.submit()); // Then an error message is shown and the question panel is highlighted - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); - await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.dateRangeQuestionErrorPanel()).isExisting()).toBe(true); }); it("Given the test_dates survey is selected when an invalid date is entered in a date range then an error message is shown", async () => { @@ -119,7 +119,7 @@ describe("Date checks", () => { await click(DateRangePage.submit()); // Then an error message is shown - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a valid date"); }); it("Given the test_dates survey is selected when the year (month year type) is left empty then an error message is shown", async () => { @@ -138,7 +138,7 @@ describe("Date checks", () => { await click(DateMonthYearPage.submit()); // Then an error message is shown - await expect(await $(DateMonthYearPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateMonthYearPage.errorNumber(1)).getText()).toBe("Enter a valid date"); }); it("Given the test_dates survey is selected, " + "When an error message is shown and it is corrected, " + "Then the next question is displayed", async () => { @@ -155,13 +155,13 @@ describe("Date checks", () => { await $(DateMonthYearPage.Year()).setValue(""); await click(DateMonthYearPage.submit()); - await expect(await $(DateMonthYearPage.error()).getText()).to.contain("Enter a valid date"); + await expect(await $(DateMonthYearPage.error()).getText()).toBe("Enter a valid date"); // Then when it is corrected, it goes to the next question await $(DateMonthYearPage.Year()).setValue(2018); await click(DateMonthYearPage.submit()); - await expect(await browser.getUrl()).to.contain(DateSinglePage.url()); + await expect(browser).toHaveUrlContaining(DateSinglePage.url()); }); it("Given the test_dates survey is selected when an error message is shown then when it is corrected, it goes to the summary page and the information is correct", async () => { @@ -188,7 +188,7 @@ describe("Date checks", () => { await click(DateNonMandatoryPage.submit()); // Then an error message is shown - await expect(await $(DateNonMandatoryPage.errorNumber(1)).getText()).to.contain("Enter a valid date"); + await expect(await $(DateNonMandatoryPage.errorNumber(1)).getText()).toBe("Enter a valid date"); }); it("Given the test_dates survey is selected, when a user clicks the day label then the day subfield should gain the focus", async () => { @@ -208,6 +208,6 @@ describe("Date checks", () => { await $(DateSinglePage.dayLabel()).click(); // Then the day subfield should gain the focus - await expect(await $(DateSinglePage.day()).isFocused()).to.be.true; + await expect(await $(DateSinglePage.day()).isFocused()).toBe(true); }); }); diff --git a/tests/functional/spec/dob_date.spec.js b/tests/functional/spec/dob_date.spec.js index be5d9889aa..73b71c0836 100644 --- a/tests/functional/spec/dob_date.spec.js +++ b/tests/functional/spec/dob_date.spec.js @@ -11,13 +11,13 @@ describe("Date of birth check", () => { await $(DateOfBirthPage.month()).setValue(4); await $(DateOfBirthPage.year()).setValue(2021); await click(DateOfBirthPage.submit()); - await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are under 16!"); + await expect(await $(UnderSixteenPage.legend()).getText()).toBe("You are under 16!"); }); it("Given I am completing a date question, When I enter a value less than 16 years, Then I am routed to over 16 page", async () => { await $(DateOfBirthPage.day()).setValue(12); await $(DateOfBirthPage.month()).setValue(4); await $(DateOfBirthPage.year()).setValue(1980); await click(DateOfBirthPage.submit()); - await expect(await $(UnderSixteenPage.legend()).getText()).to.contain("You are over 16!"); + await expect(await $(UnderSixteenPage.legend()).getText()).toBe("You are over 16!"); }); }); diff --git a/tests/functional/spec/durations.spec.js b/tests/functional/spec/durations.spec.js index 87d0ea3402..48fb87b6e9 100644 --- a/tests/functional/spec/durations.spec.js +++ b/tests/functional/spec/durations.spec.js @@ -8,10 +8,10 @@ describe("Durations", () => { }); it("Given the test_durations survey is selected durations suffixes are visible", async () => { - await expect(await $(DurationPage.yearMonthYearsSuffix()).getText()).to.contain("Years"); - await expect(await $(DurationPage.mandatoryYearMonthMonthsSuffix()).getText()).to.contain("Months"); - await expect(await $(DurationPage.yearYearsSuffix()).getText()).to.contain("Years"); - await expect(await $(DurationPage.mandatoryMonthMonthsSuffix()).getText()).to.contain("Months"); + await expect(await $(DurationPage.yearMonthYearsSuffix()).getText()).toBe("Years"); + await expect(await $(DurationPage.mandatoryYearMonthMonthsSuffix()).getText()).toBe("Months"); + await expect(await $(DurationPage.yearYearsSuffix()).getText()).toBe("Years"); + await expect(await $(DurationPage.mandatoryMonthMonthsSuffix()).getText()).toBe("Months"); }); it("Given the test_durations survey is selected when durations are entered then the summary screen shows the durations entered formatted", async () => { @@ -23,8 +23,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("1 year 2 months"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("1 year 2 months"); await click(SubmitPage.submit()); }); @@ -37,8 +37,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("2 months"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("2 months"); await click(SubmitPage.submit()); }); @@ -49,8 +49,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.yearMonthAnswer()).getText()).to.equal("No answer provided"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("No answer provided"); await click(SubmitPage.submit()); }); @@ -61,8 +61,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).toBe("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).toBe("Enter a valid duration"); }); it("Given the test_durations survey is selected when one of the units not a number an error is shown", async () => { @@ -74,8 +74,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).toBe("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).toBe("Enter a valid duration"); }); it("Given the test_durations survey is selected when the number of months is more than 11 an error is shown", async () => { @@ -87,8 +87,8 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a valid duration"); - await expect(await $(DurationPage.errorNumber(2)).getText()).to.contain("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).toBe("Enter a valid duration"); + await expect(await $(DurationPage.errorNumber(2)).getText()).toBe("Enter a valid duration"); }); it("Given the test_durations survey is selected when the mandatory duration is missing an error is shown", async () => { @@ -96,6 +96,6 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(await $(DurationPage.errorNumber(1)).getText()).to.contain("Enter a duration"); + await expect(await $(DurationPage.errorNumber(1)).getText()).toBe("Enter a duration"); }); }); diff --git a/tests/functional/spec/error_messages.spec.js b/tests/functional/spec/error_messages.spec.js index e0421c0d6f..2f8ea17e9c 100644 --- a/tests/functional/spec/error_messages.spec.js +++ b/tests/functional/spec/error_messages.spec.js @@ -46,95 +46,95 @@ describe("Error Messages", () => { }; await click(AboutYou.submit()); - await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There are 14 problems with your answer"); + await expect(await $(AboutYou.errorHeader()).getText()).toBe("There are 14 problems with your answer"); for (const [index, errorMessage] of Object.entries(errorMessageMap)) { - await expect(await $(AboutYou.errorNumber(index)).getText()).to.contain(errorMessage); + await expect(await $(AboutYou.errorNumber(index)).getText()).toContain(errorMessage); } }); it("Given a question has errors, When errors are displayed, Then the error message for each answer is correct", async () => { await click(AboutYou.submit()); - await expect(await $(AboutYou.textfieldErrorItem()).getText()).to.equal("Enter an answer"); - await expect(await $(AboutYou.dateErrorItem()).getText()).to.equal("Enter a date"); - await expect(await $(AboutYou.checkboxErrorItem()).getText()).to.contain("Select at least one answer"); - await expect(await $(AboutYou.radioErrorItem()).getText()).to.contain("Select an answer"); - await expect(await $(AboutYou.currencyErrorItem()).getText()).to.equal("Enter an answer"); - await expect(await $(AboutYou.monthYearDateErrorItem()).getText()).to.equal("Enter a date"); - await expect(await $(AboutYou.dropdownErrorItem()).getText()).to.equal("Select an answer"); - await expect(await $(AboutYou.unitErrorItem()).getText()).to.equal("Enter an answer"); - await expect(await $(AboutYou.durationErrorItem()).getText()).to.equal("Enter a duration"); - await expect(await $(AboutYou.yearDateErrorItem()).getText()).to.equal("Enter a date"); - await expect(await $(AboutYou.numberErrorItem()).getText()).to.equal("Enter an answer"); - await expect(await $(AboutYou.percentageErrorItem()).getText()).to.equal("Enter an answer"); - await expect(await $(AboutYou.mobileNumberErrorItem()).getText()).to.equal("Enter a UK mobile number"); - await expect(await $(AboutYou.textareaErrorItem()).getText()).to.equal("Enter an answer"); + await expect(await $(AboutYou.textfieldErrorItem()).getText()).toBe("Enter an answer"); + await expect(await $(AboutYou.dateErrorItem()).getText()).toBe("Enter a date"); + await expect(await $(AboutYou.checkboxErrorItem()).getText()).toContain("Select at least one answer"); + await expect(await $(AboutYou.radioErrorItem()).getText()).toContain("Select an answer"); + await expect(await $(AboutYou.currencyErrorItem()).getText()).toBe("Enter an answer"); + await expect(await $(AboutYou.monthYearDateErrorItem()).getText()).toBe("Enter a date"); + await expect(await $(AboutYou.dropdownErrorItem()).getText()).toBe("Select an answer"); + await expect(await $(AboutYou.unitErrorItem()).getText()).toBe("Enter an answer"); + await expect(await $(AboutYou.durationErrorItem()).getText()).toBe("Enter a duration"); + await expect(await $(AboutYou.yearDateErrorItem()).getText()).toBe("Enter a date"); + await expect(await $(AboutYou.numberErrorItem()).getText()).toBe("Enter an answer"); + await expect(await $(AboutYou.percentageErrorItem()).getText()).toBe("Enter an answer"); + await expect(await $(AboutYou.mobileNumberErrorItem()).getText()).toBe("Enter a UK mobile number"); + await expect(await $(AboutYou.textareaErrorItem()).getText()).toBe("Enter an answer"); }); it("Given a question has multiple errors, When the errors are displayed, Then the error messages are in a numbered list", async () => { await click(AboutYou.submit()); - await expect(await $(AboutYou.errorList()).isDisplayed()).to.be.true; + await expect(await $(AboutYou.errorList()).isDisplayed()).toBe(true); }); it("Given a question has 1 error, When the error is displayed, Then error message isn't in a numbered list", async () => { await answerAllButOne(); await click(AboutYou.submit()); - await expect(await $(AboutYou.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(AboutYou.singleErrorLink()).isDisplayed()).toBe(true); }); it("Given a question has 1 error, When the error is displayed, Then error header is correct", async () => { await answerAllButOne(); await click(AboutYou.submit()); - await expect(await $(AboutYou.errorHeader()).getText()).to.equal("There is a problem with your answer"); + await expect(await $(AboutYou.errorHeader()).getText()).toBe("There is a problem with your answer"); }); it("Given a question has errors, When an error message is clicked, Then the correct answer is focused", async () => { await click(AboutYou.submit()); await $(AboutYou.errorNumber(1)).click(); - await expect(await $(AboutYou.textfield()).isFocused()).to.be.true; + await expect(await $(AboutYou.textfield()).isFocused()).toBe(true); await $(AboutYou.errorNumber(2)).click(); - await expect(await $(AboutYou.dateday()).isFocused()).to.be.true; + await expect(await $(AboutYou.dateday()).isFocused()).toBe(true); await $(AboutYou.errorNumber(3)).click(); - await expect(await $(AboutYou.checkboxBmw()).isFocused()).to.be.true; + await expect(await $(AboutYou.checkboxBmw()).isFocused()).toBe(true); await $(AboutYou.errorNumber(4)).click(); - await expect(await $(AboutYou.radioYes()).isFocused()).to.be.true; + await expect(await $(AboutYou.radioYes()).isFocused()).toBe(true); await $(AboutYou.errorNumber(5)).click(); - await expect(await $(AboutYou.currency()).isFocused()).to.be.true; + await expect(await $(AboutYou.currency()).isFocused()).toBe(true); await $(AboutYou.errorNumber(6)).click(); - await expect(await $(AboutYou.monthYearDateMonth()).isFocused()).to.be.true; + await expect(await $(AboutYou.monthYearDateMonth()).isFocused()).toBe(true); await $(AboutYou.errorNumber(7)).click(); - await expect(await $(AboutYou.dropdown()).isFocused()).to.be.true; + await expect(await $(AboutYou.dropdown()).isFocused()).toBe(true); await $(AboutYou.errorNumber(8)).click(); - await expect(await $(AboutYou.unit()).isFocused()).to.be.true; + await expect(await $(AboutYou.unit()).isFocused()).toBe(true); await $(AboutYou.errorNumber(9)).click(); - await expect(await $(AboutYou.durationYears()).isFocused()).to.be.true; + await expect(await $(AboutYou.durationYears()).isFocused()).toBe(true); await $(AboutYou.errorNumber(10)).click(); - await expect(await $(AboutYou.yearDateYear()).isFocused()).to.be.true; + await expect(await $(AboutYou.yearDateYear()).isFocused()).toBe(true); await $(AboutYou.errorNumber(11)).click(); - await expect(await $(AboutYou.number()).isFocused()).to.be.true; + await expect(await $(AboutYou.number()).isFocused()).toBe(true); await $(AboutYou.errorNumber(12)).click(); - await expect(await $(AboutYou.percentage()).isFocused()).to.be.true; + await expect(await $(AboutYou.percentage()).isFocused()).toBe(true); await $(AboutYou.errorNumber(13)).click(); - await expect(await $(AboutYou.mobileNumber()).isFocused()).to.be.true; + await expect(await $(AboutYou.mobileNumber()).isFocused()).toBe(true); await $(AboutYou.errorNumber(14)).click(); - await expect(await $(AboutYou.textarea()).isFocused()).to.be.true; + await expect(await $(AboutYou.textarea()).isFocused()).toBe(true); }); }); describe("Error Message NaN value", () => { @@ -144,13 +144,13 @@ describe("Error Message NaN value", () => { it("Given a NaN value was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { await $(BlockPage.answer()).setValue("NaN"); await click(BlockPage.submit()); - await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); - await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); + await expect(await $(BlockPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(BlockPage.answerErrorItem()).getText()).toBe("Enter a number"); }); it("Given a NaN value with separators was entered on percentage question, When the error is displayed, Then the error message is correct", async () => { await $(BlockPage.answer()).setValue(",NaN_"); await click(BlockPage.submit()); - await expect(await $(BlockPage.errorHeader()).getText()).to.equal("There is a problem with your answer"); - await expect(await $(BlockPage.answerErrorItem()).getText()).to.equal("Enter a number"); + await expect(await $(BlockPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(BlockPage.answerErrorItem()).getText()).toBe("Enter a number"); }); }); diff --git a/tests/functional/spec/exit.spec.js b/tests/functional/spec/exit.spec.js index 6289fdf63c..54da5684ce 100644 --- a/tests/functional/spec/exit.spec.js +++ b/tests/functional/spec/exit.spec.js @@ -13,14 +13,14 @@ describe("Post submission exit", () => { await click(HubPage.submit()); await browser.deleteAllCookies(); await $(CensusThankYouPage.exit()).click(); - await expect(await browser.getUrl()).to.equal("https://surveys.ons.gov.uk/sign-in/"); + await expect(browser).toHaveUrlContaining("https://surveys.ons.gov.uk/sign-in/"); }); it("Given I click the exit button from the thank you page, When I am redirected, Then I should be redirected to the correct log out url", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); await $(CensusThankYouPage.exit()).click(); - await expect(await browser.getUrl()).to.equal("https://www.ons.gov.uk/census"); + await expect(browser).toHaveUrlContaining("https://www.ons.gov.uk/census"); }); it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", async () => { @@ -28,7 +28,7 @@ describe("Post submission exit", () => { await click(HubPage.submit()); await $(CensusThankYouPage.exit()).click(); await browser.back(); - await expect(await browser.getUrl()).to.contain("submitted/thank-you"); - await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); + await expect(browser).toHaveUrlContaining("submitted/thank-you"); + await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); }); }); diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index 2514aa9a6d..dbad6a7ea6 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -73,29 +73,29 @@ class TestCase { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £20.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toContain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toContain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toContain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toContain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toBe("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toBe("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toBe("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toBe("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toBe( "Second answer label also in currency total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toContain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toContain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toContain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toContain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toContain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toBe("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toBe("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toBe("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toBe("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toBe("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toBe( "Fourth answer label also in total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toBe("£2.34"); // Answers not included in calculation should not be shown await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).toHaveLength(0); @@ -113,13 +113,13 @@ class TestCase { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); + await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); + await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -128,10 +128,10 @@ class TestCase { await click(FourthNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { @@ -140,11 +140,11 @@ class TestCase { await click(FourthAndAHalfNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toBe("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { @@ -163,26 +163,26 @@ class TestCase { await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.36. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(CurrencyTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("1,467 cm"); // Answers included in calculation should be shown - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toContain("Second answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toContain("789 cm"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toContain("Third answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toContain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toBe("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toBe("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toBe("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toBe("678 cm"); }); it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { @@ -192,33 +192,33 @@ class TestCase { it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("79%"); // Answers included in calculation should be shown - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toContain("Fifth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toContain("56%"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toContain("Sixth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toContain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toBe("Fifth answer label percentage total"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toBe("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toBe("Sixth answer label percentage total"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toBe("23%"); }); it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of number values entered to be 124.58. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("124.58"); // Answers included in calculation should be shown - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toContain("Fifth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toContain("78.91"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toContain("Sixth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toContain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toBe("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toBe("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toBe("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toBe("45.67"); }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { @@ -244,7 +244,7 @@ class TestCase { await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); }); @@ -254,7 +254,7 @@ class TestCase { await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); await click(SetMinMaxBlockPage.submit()); }); @@ -267,7 +267,7 @@ class TestCase { // first incomplete block await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); @@ -288,13 +288,13 @@ class TestCase { await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £15.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); await expect(browser).toHaveUrlContaining(SubmitPage.pageName); @@ -306,13 +306,13 @@ class TestCase { await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £6.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); await expect(browser).toHaveUrlContaining(SubmitPage.pageName); @@ -348,7 +348,7 @@ class TestCase { "Set minimum and maximum values based on your calculated summary total of £25.92", ); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £25.92"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £25.92"); await $(SetMinMaxBlockPage.setMinimum()).setValue(30.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); @@ -383,12 +383,8 @@ class TestCase { it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( - "60 - calculated summary answer (previous section)", - ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( - "40 - calculated summary answer (current section)", - ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("60 - calculated summary answer (previous section)"); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { @@ -398,7 +394,7 @@ class TestCase { await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); - await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £60.00"); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); await click(MinMaxSectionTwo.submit()); @@ -414,12 +410,8 @@ class TestCase { await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await expect(await $("body").getText()).toContain("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( - "30 - calculated summary answer (previous section)", - ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( - "40 - calculated summary answer (current section)", - ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("30 - calculated summary answer (previous section)"); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); } @@ -460,7 +452,7 @@ class TestCase { }); it("Given I have entered a range of positive and negative values, When I reach the calculated summary, Then the total is correct", async () => { assertSummaryValues(expectedAnswerValues); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( `We calculate the total of currency values entered to be ${expectedTotalValue}. Is this correct?`, ); }); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index 28472d1aea..6878d3bb3c 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -46,13 +46,13 @@ describe("Calculated summary with repeating answers", () => { it("Given I complete all list collector dynamic answers for two calculated summaries one of which also has static answers, I'm taken to each one in turn, showing the correct answers", async () => { await click(ExtraSpendingBlockPage.submit()); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?", ); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).toContain("£550.00"); + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).toBe("£550.00"); await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 6. Is this correct?", ); await assertSummaryValues(["4", "2"]); @@ -70,7 +70,7 @@ describe("Calculated summary with repeating answers", () => { await $$(DynamicAnswerPage.inputs())[5].setValue(3); await click(DynamicAnswerPage.submit()); await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 7. Is this correct?", ); await assertSummaryValues(["4", "3"]); @@ -89,7 +89,7 @@ describe("Calculated summary with repeating answers", () => { // then calculated summary await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?", ); @@ -114,14 +114,14 @@ describe("Calculated summary with repeating answers", () => { // first calc summary await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?", ); await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); // second calculated summary await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 14. Is this correct?", ); await assertSummaryValues(["4", "3", "2"]); @@ -145,12 +145,12 @@ describe("Calculated summary with repeating answers", () => { await click(DynamicAnswerPage.submit()); // Tesco is now gone - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?", ); await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); await click(CalculatedSummarySpendingPage.submit()); - await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 10. Is this correct?", ); await assertSummaryValues(["3", "7"]); @@ -166,7 +166,7 @@ describe("Calculated summary with repeating answers", () => { await click(HubPage.submit()); await $(SupermarketTransportPage.weeklyCarTrips()).setValue(11); await click(SupermarketTransportPage.submit()); - await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).toContain("Enter an answer less than or equal to 10"); + await expect(await $(SupermarketTransportPage.singleErrorLink()).getText()).toBe("Enter an answer less than or equal to 10"); }); it("Given I change my answer to a value less than the calculated summary from the previous section, I am able to proceed", async () => { diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index 37b472bdaa..e58eb0efb6 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -222,7 +222,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(SectionOnePage.previous()).click(); // other calculated summary should not be on the path, so go straight back to the spending one which now has none of the list items await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); - await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?", ); await assertSummaryValues(["£100.00"]); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 543099f0d0..0b6e252a40 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -61,29 +61,29 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the currency summary, Then I should see the correct total", async () => { // Totals and titles should be shown - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £20.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£20.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£20.71"); // Answers included in calculation should be shown - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toContain("First answer label"); - await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toContain("£1.23"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toContain("Second answer in currency label"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toContain("£4.56"); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswerLabel()).getText()).toBe("First answer label"); + await expect(await $(CurrencyTotalPlaybackPage.firstNumberAnswer()).getText()).toBe("£1.23"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerLabel()).getText()).toBe("Second answer in currency label"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswer()).getText()).toBe("£4.56"); + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotalLabel()).getText()).toBe( "Second answer label also in currency total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toContain("£0.12"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toContain("Third answer label"); - await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toContain("£3.45"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toContain("Fourth answer label (optional)"); - await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toContain("£9.01"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.secondNumberAnswerAlsoInTotal()).getText()).toBe("£0.12"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswerLabel()).getText()).toBe("Third answer label"); + await expect(await $(CurrencyTotalPlaybackPage.thirdNumberAnswer()).getText()).toBe("£3.45"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswerLabel()).getText()).toBe("Fourth answer label (optional)"); + await expect(await $(CurrencyTotalPlaybackPage.fourthNumberAnswer()).getText()).toBe("£9.01"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotalLabel()).getText()).toBe( "Fourth answer label also in total (optional)", ); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("£2.34"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toBe("£2.34"); // Answers not included in calculation should not be shown await expect(await $$(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal())).toHaveLength(0); @@ -101,13 +101,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); + await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(await browser.getUrl()).toContain("currency-total-playback/#third-number-answer"); + await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -116,10 +116,10 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(FourthNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£30.71"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£30.71"); }); it("Given I leave an answer empty, When I get to the currency summary, Then I should see no answer provided and new total", async () => { @@ -128,11 +128,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(FourthAndAHalfNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£28.37"); - await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toContain("No answer provided"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£28.37"); + await expect(await $(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal()).getText()).toBe("No answer provided"); }); it("Given I skip the fourth page, When I get to the playback, Then I can should not see it in the total", async () => { @@ -151,26 +151,26 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.36. Is this correct?", ); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("£9.36"); + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£9.36"); }); it("Given I complete every question, When I get to the unit summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(CurrencyTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of unit values entered to be 1,467 cm. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("1,467 cm"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("1,467 cm"); // Answers included in calculation should be shown - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toContain("Second answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toContain("789 cm"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toContain("Third answer label in unit total"); - await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toContain("678 cm"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotalLabel()).getText()).toBe("Second answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.secondNumberAnswerUnitTotal()).getText()).toBe("789 cm"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotalLabel()).getText()).toBe("Third answer label in unit total"); + await expect(await $(UnitTotalPlaybackPage.thirdAndAHalfNumberAnswerUnitTotal()).getText()).toBe("678 cm"); }); it("Given the calculated summary has a custom title, When I am on the unit calculated summary, Then the page title should use the custom title", async () => { @@ -180,17 +180,17 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the percentage summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of percentage values entered to be 79%. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("79%"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("79%"); // Answers included in calculation should be shown - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toContain("Fifth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toContain("56%"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toContain("Sixth answer label percentage tota"); - await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toContain("23%"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswerLabel()).getText()).toBe("Fifth answer label percentage total"); + await expect(await $(PercentageTotalPlaybackPage.fifthPercentAnswer()).getText()).toBe("56%"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswerLabel()).getText()).toBe("Sixth answer label percentage total"); + await expect(await $(PercentageTotalPlaybackPage.sixthPercentAnswer()).getText()).toBe("23%"); }); it("Given the calculated summary has a custom title with the list item position, When I am on the percentage calculated summary, Then the page title should use the custom title with the list item position", async () => { @@ -200,17 +200,17 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I complete every question, When I get to the number summary, Then I should see the correct total", async () => { // Totals and titles should be shown await click(UnitTotalPlaybackPage.submit()); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of number values entered to be 124.58. Is this correct?", ); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toContain("Grand total of previous values"); - await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryQuestion()).getText()).toBe("Grand total of previous values"); + await expect(await $(UnitTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("124.58"); // Answers included in calculation should be shown - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toContain("Fifth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toContain("78.91"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toContain("Sixth answer label number total"); - await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toContain("45.67"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswerLabel()).getText()).toBe("Fifth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.fifthNumberAnswer()).getText()).toBe("78.91"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswerLabel()).getText()).toBe("Sixth answer label number total"); + await expect(await $(NumberTotalPlaybackPage.sixthNumberAnswer()).getText()).toBe("45.67"); }); it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", async () => { @@ -220,11 +220,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(BreakdownPage.answer2()).setValue(24.58); await click(BreakdownPage.submit()); await expect(browser).toHaveUrlContaining(SecondCurrencyTotalPlaybackPage.pageName); - await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of number values entered to be £124.58. Is this correct?", ); await expect(await $("body").getText()).toContain("Enter two values that add up to the previous calculated summary total of £124.58"); - await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toContain("124.58"); + await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryAnswer()).getText()).toBe("£124.58"); }); it("Given I complete every calculated summary, When I go to a page with calculated summary piping, Then I should the see the piped calculated summary total for each summary", async () => { @@ -233,7 +233,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { const content = $("h1 + ul").getText(); const textsToAssert = ["Total currency values: £9.36", "Total unit values: 1,467", "Total percentage values: 79", "Total number values: 124.58"]; - textsToAssert.forEach(async (text) => await expect(content).toContain(text)); + textsToAssert.forEach(async (text) => await expect(content).toBe(text)); }); it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { @@ -241,14 +241,14 @@ describe("Feature: Calculated Summary Repeating Section", () => { await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £9.36"); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); }); it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £9.36"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £9.36"); await $(SetMinMaxBlockPage.setMaximum()).setValue(7.0); await click(SetMinMaxBlockPage.submit()); }); @@ -261,7 +261,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { // first incomplete block await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); @@ -282,13 +282,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £15.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); await expect(browser).toHaveUrlContaining(SubmitPage.pageName); @@ -300,13 +300,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); - await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); - await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £6.91"); + await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); await expect(browser).toHaveUrlContaining(SubmitPage.pageName); @@ -412,12 +412,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( - "60 - calculated summary answer (previous section)", - ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( - "40 - calculated summary answer (current section)", - ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("60 - calculated summary answer (previous section)"); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { @@ -427,7 +423,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); - await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toContain("Enter an answer more than or equal to £60.00"); + await expect(await $(MinMaxSectionTwo.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £60.00"); await $(MinMaxSectionTwo.setMinimum()).setValue(61.0); await $(MinMaxSectionTwo.setMaximum()).setValue(40.0); await click(MinMaxSectionTwo.submit()); @@ -443,12 +439,8 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(HubPage.summaryRowLink("calculated-summary-section-1")).click(); await expect(await $("body").getText()).toContain("30 - calculated summary answer (previous section)"); await $(SectionSummarySectionTwo.checkboxAnswerEdit()).click(); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toContain( - "30 - calculated summary answer (previous section)", - ); - await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toContain( - "40 - calculated summary answer (current section)", - ); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("30 - calculated summary answer (previous section)"); + await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); }); }); diff --git a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js index 774ad71884..1b8f1fa8aa 100644 --- a/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js +++ b/tests/functional/spec/features/conditional_question_title/conditional_checkbox_title.spec.js @@ -12,35 +12,35 @@ describe("Feature: Conditional checkbox and radio question titles", () => { it("When I enter an expected name and submit", async () => { await $(NameEntryPage.name()).setValue("Peter"); await click(NameEntryPage.submit()); - await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Peter make changes to this business?"); + await expect(await $(CheckBoxPage.questionText()).getText()).toBe("Did Peter make changes to this business?"); }); it("When I enter an unknown name and go to the checkbox page", async () => { await $(NameEntryPage.name()).setValue("Fred"); await click(NameEntryPage.submit()); - await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); + await expect(await $(CheckBoxPage.questionText()).getText()).toBe("Did this business make major changes in the following areas?"); await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategies()).click(); - await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Did this business make major changes in the following areas"); + await expect(await $(RadioButtonsPage.questionText()).getText()).toBe("Did this business make major changes in the following areas?"); }); it("When I enter another known name page title should include selected title", async () => { await $(NameEntryPage.name()).setValue("Mary"); await click(NameEntryPage.submit()); - await expect(await browser.getTitle()).to.contain("Did Mary make changes to this business? - Test Survey - Checkbox and Radio titles"); + await expect(await browser.getTitle()).toBe("Did Mary make changes to this business? - Test Survey - Checkbox and Radio titles"); }); it("When I enter another known name and go to the summary", async () => { await $(NameEntryPage.name()).setValue("Mary"); await click(NameEntryPage.submit()); - await expect(await $(CheckBoxPage.questionText()).getText()).to.contain("Did Mary make changes to this business"); + await expect(await $(CheckBoxPage.questionText()).getText()).toBe("Did Mary make changes to this business?"); await $(CheckBoxPage.checkboxImplementationOfChangesToMarketingConceptsOrStrategiesLabel()).click(); await click(CheckBoxPage.submit()); - await expect(await $(RadioButtonsPage.questionText()).getText()).to.contain("Is Mary the boss?"); + await expect(await $(RadioButtonsPage.questionText()).getText()).toBe("Is Mary the boss?"); await $(RadioButtonsPage.radioMaybe()).click(); await click(RadioButtonsPage.submit()); - await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Mary"); - await expect(await $(SubmitPage.checkboxQuestion()).getText()).to.contain("Did Mary make changes to this business?"); + await expect(await $(SubmitPage.nameAnswer()).getText()).toBe("Mary"); + await expect(await $(SubmitPage.checkboxQuestion()).getText()).toBe("Did Mary make changes to this business?"); }); }); }); diff --git a/tests/functional/spec/features/confirmation_question.spec.js b/tests/functional/spec/features/confirmation_question.spec.js index 7a735f9353..9e00a59da5 100644 --- a/tests/functional/spec/features/confirmation_question.spec.js +++ b/tests/functional/spec/features/confirmation_question.spec.js @@ -14,9 +14,9 @@ describe("Feature: Confirmation Question", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); - await expect(await $$(SubmitPage.confirmZeroEmployeesAnswer())).to.be.empty; + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).toBe("0"); + await expect(await $$(SubmitPage.confirmZeroEmployeesAnswer())).toHaveLength(0); }); }); describe("Given a confirmation Question", () => { @@ -25,7 +25,7 @@ describe("Feature: Confirmation Question", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(NumberOfEmployeesTotalBlockPage.pageName); + await expect(browser).toHaveUrlContaining(NumberOfEmployeesTotalBlockPage.pageName); }); }); describe("Given a number of employees Question", () => { @@ -34,7 +34,7 @@ describe("Feature: Confirmation Question", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).to.contain("0"); + await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).toBe("0"); }); }); }); diff --git a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js index abcf271e63..cda3562830 100644 --- a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js +++ b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js @@ -16,7 +16,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await click(AddPersonPage.submit()); await $(DoesAnyoneLiveHerePage.no()).click(); await click(DoesAnyoneLiveHerePage.submit()); - await expect(await browser.getUrl()).to.contain(DateOfBirthPage.url().split("/").slice(-1)[0]); + await expect(browser).toHaveUrlContaining(DateOfBirthPage.url().split("/").slice(-1)[0]); }); describe("Given a confirmation question", () => { @@ -30,7 +30,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { // Answer 'No' to confirmation question await $(ConfirmDateOfBirthPage.noINeedToChangeTheirDateOfBirth()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(await browser.getUrl()).to.contain(DateOfBirthPage.pageName); + await expect(browser).toHaveUrlContaining(DateOfBirthPage.pageName); }); }); @@ -44,8 +44,8 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(await browser.getUrl()).to.contain("sections/default-section/"); - await expect(await $(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining("sections/default-section/"); + await expect(await $(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).toBe(false); }); }); @@ -56,8 +56,8 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(DateOfBirthPage.year()).setValue("2000"); await click(DateOfBirthPage.submit()); - await expect(await browser.getUrl()).to.contain(CarerPage.pageName); - await expect(await $(CarerPage.questionText()).getText()).to.contain("Does John Doe look"); + await expect(browser).toHaveUrlContaining(CarerPage.pageName); + await expect(await $(CarerPage.questionText()).getText()).toContain("Does John Doe look"); }); }); }); diff --git a/tests/functional/spec/features/default_value/default_value.spec.js b/tests/functional/spec/features/default_value/default_value.spec.js index 03bfb11a5c..fa4ad123ba 100644 --- a/tests/functional/spec/features/default_value/default_value.spec.js +++ b/tests/functional/spec/features/default_value/default_value.spec.js @@ -8,31 +8,31 @@ describe("Feature: Default Value", () => { it('Given I start default schema, When I do not answer a question, Then "no answer provided" is displayed on the Summary page', async () => { await browser.openQuestionnaire("test_default.json"); await click(QuestionPageOne.submit()); - await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); await click(QuestionPageTwo.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.answerOne()).getText()).to.contain("0"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.answerOne()).getText()).toBe("0"); }); it("Given I have not answered a question containing a default value, When I return to the question, Then no value should be displayed", async () => { await browser.openQuestionnaire("test_default.json"); await click(QuestionPageOne.submit()); - await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); await click(QuestionPageTwo.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(QuestionPageTwo.pageName); + await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); await $(QuestionPageTwo.previous()).click(); - await expect(await browser.getUrl()).to.contain(QuestionPageOne.pageName); - await expect(await $(QuestionPageOne.one()).getValue()).to.equal(""); + await expect(browser).toHaveUrlContaining(QuestionPageOne.pageName); + await expect(await $(QuestionPageOne.one()).getValue()).toBe(""); }); it("Given I have not answered a question containing a default value, When a skip condition checks for the default value, Then I should skip the next question", async () => { await browser.openQuestionnaire("test_default_with_skip.json"); await click(QuestionPageOneSkip.submit()); - await expect(await browser.getUrl()).to.contain(QuestionPageThreeSkip.pageName); - await expect(await $(QuestionPageThreeSkip.questionText()).getText()).to.contain("Question Three"); + await expect(browser).toHaveUrlContaining(QuestionPageThreeSkip.pageName); + await expect(await $(QuestionPageThreeSkip.questionText()).getText()).toBe("Question Three"); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 2a34b1fb54..05e11b7ebd 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -41,20 +41,20 @@ testCases.forEach(async (testCase) => { it("When I click a checkbox option, then the checkbox should be selected", async () => { for (let i = 0; i < testCase.answerOptionCount; i++) { await $(DynamicCheckboxPage.answerByIndex(i)).click(); - await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.true; + await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).toBe(true); } }); it("When I click a selected option, then it should be deselected", async () => { for (let i = 0; i < testCase.answerOptionCount; i++) { await $(DynamicCheckboxPage.answerByIndex(i)).click(); - await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).to.be.false; + await expect(await $(DynamicCheckboxPage.answerByIndex(i)).isSelected()).toBe(false); } }); it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicRadioPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicRadioPage.pageName); }); }); @@ -62,13 +62,13 @@ testCases.forEach(async (testCase) => { it("When I click a radio option, then the radio should be selected", async () => { for (let i = 0; i < (await testCase.answerOptionCount); i++) { await $(DynamicRadioPage.answerByIndex(i)).click(); - await expect(await $(DynamicRadioPage.answerByIndex(i)).isSelected()).to.be.true; + await expect(await $(DynamicRadioPage.answerByIndex(i)).isSelected()).toBe(true); } }); it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicRadioPage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicDropdownPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicDropdownPage.pageName); }); }); @@ -76,13 +76,13 @@ testCases.forEach(async (testCase) => { it("When I select a dropdown option, then the option should be selected", async () => { for (const value of testCase.dropdownOptionValues) { await $(DynamicDropdownPage.answer()).selectByAttribute("value", value); - await expect(await $(DynamicDropdownPage.answer()).getValue()).to.equal(value); + await expect(await $(DynamicDropdownPage.answer()).getValue()).toBe(value); } }); it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicDropdownPage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicMutuallyExclusivePage.pageName); + await expect(browser).toHaveUrlContaining(DynamicMutuallyExclusivePage.pageName); }); }); @@ -90,27 +90,27 @@ testCases.forEach(async (testCase) => { it("When I click a dynamic checkbox option, then the checkbox should be selected", async () => { for (let i = 0; i < (await testCase.answerOptionCount); i++) { await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); - await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.true; + await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).toBe(true); } }); it("When I click a selected option, then it should be deselected", async () => { for (let i = 0; i < (await testCase.answerOptionCount); i++) { await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); - await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).to.be.false; + await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).toBe(false); } }); it("When I click the static checkbox option, then the static checkbox should be selected", async () => { // Test exclusive option (Static option) await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).toBe(true); }); it("When I click the selected static checkbox option, then that checkbox should be deselected", async () => { // Test exclusive option (Static option) await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); - await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.false; + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).toBe(false); }); }); }); @@ -127,11 +127,11 @@ testCases.forEach(async (testCase) => { await click(DynamicRadioPage.submit()); await click(DynamicMutuallyExclusivePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("No answer provided"); - await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("No answer provided"); - await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("No answer provided"); - await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("No answer provided"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("No answer provided"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("No answer provided"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("No answer provided"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).toBe("No answer provided"); }); it("When I select a dynamically generated answer option for each question, then my selected answers should be displayed on the summary", async () => { @@ -153,11 +153,11 @@ testCases.forEach(async (testCase) => { await $(DynamicMutuallyExclusivePage.answerByIndex(6)).click(); // Sunday 3 January 2021 await click(DynamicMutuallyExclusivePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("Wednesday 30 December 2020\nThursday 31 December 2020"); - await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("Tuesday 29 December 2020"); - await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("Saturday 2 January 2021"); - await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("Monday 28 December 2020\nSunday 3 January 2021"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("Wednesday 30 December 2020\nThursday 31 December 2020"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("Tuesday 29 December 2020"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("Saturday 2 January 2021"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).toBe("Monday 28 December 2020\nSunday 3 January 2021"); }); }); }); @@ -188,18 +188,18 @@ describe(`Feature: Dynamically generated answer options driven by a function wit // Test static option for mutually exclusive from non exclusive choices await $(DynamicMutuallyExclusivePage.answerByIndex(7)).click(); await click(DynamicMutuallyExclusivePage.submit()); - await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).to.equal("None of the above"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveDynamicAnswer()).getText()).toBe("None of the above"); // Test exclusive static choice await $(SubmitPage.previous()).click(); await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); await click(DynamicMutuallyExclusivePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).to.equal("I did not work"); - await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).to.equal("I did not work"); - await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).to.equal("I did not work"); - await expect(await $(SubmitPage.dynamicMutuallyExclusiveStaticAnswer()).getText()).to.equal("I did not work"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("I did not work"); + await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("I did not work"); + await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("I did not work"); + await expect(await $(SubmitPage.dynamicMutuallyExclusiveStaticAnswer()).getText()).toBe("I did not work"); }); it("When I edit and change the reference date which the other questions are dependent on, then all dependent answers are removed", async () => { @@ -207,26 +207,26 @@ describe(`Feature: Dynamically generated answer options driven by a function wit await $(ReferenceDatePage.day()).setValue("2"); await click(ReferenceDatePage.submit()); - await expect(await $(DynamicCheckboxPage.answerByIndex(7)).isSelected()).to.be.false; + await expect(await $(DynamicCheckboxPage.answerByIndex(7)).isSelected()).toBe(false); await $(DynamicCheckboxPage.answerByIndex(7)).click(); await click(DynamicCheckboxPage.submit()); - await expect(await $(DynamicRadioPage.answerByIndex(7)).isSelected()).to.be.false; + await expect(await $(DynamicRadioPage.answerByIndex(7)).isSelected()).toBe(false); await $(DynamicRadioPage.answerByIndex(7)).click(); await click(DynamicRadioPage.submit()); - await expect(await $(DynamicDropdownPage.answer()).getText()).to.contain("Select an answer"); + await expect(await $(DynamicDropdownPage.answer()).getText()).toContain("Select an answer"); await $(DynamicDropdownPage.answer()).selectByAttribute("value", "I did not work"); await click(DynamicDropdownPage.submit()); // The Mutually exclusive answer is not removed as it is a different answer_id to the dependent, however the block must be re-submitted - await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).to.be.true; + await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).toBe(true); await click(DynamicMutuallyExclusivePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js index 2109103837..d88ac70d7e 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven_mandatory.js @@ -17,9 +17,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu it("When I do not answer the Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { await click(DynamicCheckboxPage.submit()); - await expect(await $(DynamicCheckboxPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(DynamicCheckboxPage.answerErrorItem()).getText()).to.contain("Select at least one answer"); - await expect(await $(DynamicCheckboxPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DynamicCheckboxPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(DynamicCheckboxPage.answerErrorItem()).getText()).toContain("Select at least one answer"); + await expect(await $(DynamicCheckboxPage.questionErrorPanel()).isExisting()).toBe(true); }); it("When I do not answer the Radio question and submit, then an error message and the question error panel should be displayed.", async () => { @@ -28,9 +28,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu await click(DynamicCheckboxPage.submit()); await click(DynamicRadioPage.submit()); - await expect(await $(DynamicRadioPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(DynamicRadioPage.answerErrorItem()).getText()).to.contain("Select an answer"); - await expect(await $(DynamicRadioPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DynamicRadioPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(DynamicRadioPage.answerErrorItem()).getText()).toContain("Select an answer"); + await expect(await $(DynamicRadioPage.questionErrorPanel()).isExisting()).toBe(true); }); it("When I do not answer the Dropdown question and submit, then an error message and the question error panel should be displayed.", async () => { @@ -39,9 +39,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu await click(DynamicRadioPage.submit()); await click(DynamicDropdownPage.submit()); - await expect(await $(DynamicDropdownPage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(DynamicDropdownPage.answerErrorItem()).getText()).to.contain("Select an answer"); - await expect(await $(DynamicDropdownPage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DynamicDropdownPage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(DynamicDropdownPage.answerErrorItem()).getText()).toBe("Select an answer"); + await expect(await $(DynamicDropdownPage.questionErrorPanel()).isExisting()).toBe(true); }); it("When I do not answer the Mutually Exclusive Checkbox question and submit, then an error message and the question error panel should be displayed.", async () => { @@ -50,9 +50,9 @@ describe(`Feature: Dynamically generated mandatory answer options driven by a fu await click(DynamicDropdownPage.submit()); await click(DynamicMutuallyExclusivePage.submit()); - await expect(await $(DynamicMutuallyExclusivePage.errorHeader()).getText()).to.contain("There is a problem with your answer"); - await expect(await $(DynamicMutuallyExclusivePage.errorNumber(1)).getText()).to.contain("Select at least one answer"); - await expect(await $(DynamicMutuallyExclusivePage.questionErrorPanel()).isExisting()).to.be.true; + await expect(await $(DynamicMutuallyExclusivePage.errorHeader()).getText()).toBe("There is a problem with your answer"); + await expect(await $(DynamicMutuallyExclusivePage.errorNumber(1)).getText()).toContain("Select at least one answer"); + await expect(await $(DynamicMutuallyExclusivePage.questionErrorPanel()).isExisting()).toBe(true); }); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js index 69a6751465..e795ac7a99 100644 --- a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js +++ b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js @@ -11,31 +11,31 @@ describe("Dynamic radio options from checkbox answers", () => { await $(InjurySustainedPage.body()).click(); await click(InjurySustainedPage.submit()); - await expect(await browser.getUrl()).to.contain(MostSeriousInjuryPage.pageName); - await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).to.contain("Head"); - await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(1)).getText()).to.contain("Body"); - await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(2)).getText()).to.contain("They were of equal severity (static option)"); - await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(3)).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(MostSeriousInjuryPage.pageName); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).toBe("Head"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(1)).getText()).toBe("Body"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(2)).getText()).toBe("They were of equal severity (static option)"); + await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(3)).isExisting()).toBe(false); }); it("When the respondent answers the radio question and submits, Then the next radio question should show only the answers from the first checkbox as options", async () => { await $(MostSeriousInjuryPage.answerLabelByIndex(0)).click(); await click(MostSeriousInjuryPage.submit()); - await expect(await browser.getUrl()).to.contain(HealedTheQuickestPage.pageName); - await expect(await $(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).to.contain("Head"); - await expect(await $(HealedTheQuickestPage.answerLabelByIndex(1)).getText()).to.contain("Body"); - await expect(await $(HealedTheQuickestPage.answerLabelByIndex(2)).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(HealedTheQuickestPage.pageName); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).toBe("Head"); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(1)).getText()).toBe("Body"); + await expect(await $(HealedTheQuickestPage.answerLabelByIndex(2)).isExisting()).toBe(false); }); it("When the respondent answers the radio question and submits, then the summary should display all the answers correctly", async () => { await $(HealedTheQuickestPage.answerLabelByIndex(1)).click(); await click(HealedTheQuickestPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.injurySustainedAnswer()).getText()).to.contain("Head\nBody"); - await expect(await $(SubmitPage.mostSeriousInjuryAnswer()).getText()).to.contain("Head"); - await expect(await $(SubmitPage.healedTheQuickestAnswer()).getText()).to.contain("Body"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.injurySustainedAnswer()).getText()).toBe("Head\nBody"); + await expect(await $(SubmitPage.mostSeriousInjuryAnswer()).getText()).toBe("Head"); + await expect(await $(SubmitPage.healedTheQuickestAnswer()).getText()).toBe("Body"); }); it("When I edit and change the answer which the dynamic options is dependent on, then my selected answers are removed", async () => { @@ -43,8 +43,8 @@ describe("Dynamic radio options from checkbox answers", () => { await $(InjurySustainedPage.arms()).click(); await click(InjurySustainedPage.submit()); - await expect(await $(MostSeriousInjuryPage.answerByIndex(0)).isSelected()).to.be.false; - await expect(await $(MostSeriousInjuryPage.answerByIndex(1)).isSelected()).to.be.false; + await expect(await $(MostSeriousInjuryPage.answerByIndex(0)).isSelected()).toBe(false); + await expect(await $(MostSeriousInjuryPage.answerByIndex(1)).isSelected()).toBe(false); }); }); }); diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index 120616bd54..e3d53f1bdf 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -152,8 +152,8 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); await click(DriverPage.submit()); - await expect(await $("body").getText()).not.toContain("Percentage of shopping at Tesco"); - await expect(await $("body").getText()).not.toContain("Percentage of shopping at Aldi"); + await expect(await $("body").getText()).not.toBe("Percentage of shopping at Tesco"); + await expect(await $("body").getText()).not.toBe("Percentage of shopping at Aldi"); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await click(DriverPage.submit()); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js index 18ccd6e39c..89a692e914 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js @@ -11,14 +11,14 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { await $(sectionOne.section1Section2()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain("section-2-block"); + await expect(browser).toHaveUrlContaining("section-2-block"); }); it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain("section-3-block"); + await expect(browser).toHaveUrlContaining("section-3-block"); }); it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { @@ -26,17 +26,17 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain("section-2-block"); + await expect(browser).toHaveUrlContaining("section-2-block"); await click(sectionTwo.submit()); - await expect(await browser.getUrl()).to.contain("section-3-block"); + await expect(browser).toHaveUrlContaining("section-3-block"); }); it("When the user selects `Neither` and submits, Then they should be taken straight to the summary", async () => { await $(sectionOne.section1ExclusiveNeither()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); - await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; - await expect(await $(SubmitPage.section3Question()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await expect(await $(SubmitPage.section2Question()).isExisting()).toBe(false); + await expect(await $(SubmitPage.section3Question()).isExisting()).toBe(false); }); }); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js index 470b173938..07ba9d7f92 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js @@ -10,20 +10,20 @@ describe("Feature: Section Enabled With Hub", () => { await $(sectionOne.section1Section2()).click(); await click(sectionOne.submit()); - await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; - await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).toBe(true); + await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).toBe("Section 2"); - await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).toBe(false); }); it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; - await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).toBe(true); + await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).toBe("Section 3"); - await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).toBe(false); }); it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { @@ -31,21 +31,21 @@ describe("Feature: Section Enabled With Hub", () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.true; - await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).to.equal("Section 2"); + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).toBe(true); + await expect(await $(hubPage.summaryRowTitle("section-2")).getText()).toBe("Section 2"); - await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.true; - await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).to.equal("Section 3"); + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).toBe(true); + await expect(await $(hubPage.summaryRowTitle("section-3")).getText()).toBe("Section 3"); }); it("When the user selects `Neither` and submits, Then hub should not display any other section and should be in the `Completed` state.", async () => { await $(sectionOne.section1ExclusiveNeither()).click(); await click(sectionOne.submit()); - await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).to.be.false; - await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).to.be.false; + await expect(await $(hubPage.summaryRowState("section-2")).isDisplayed()).toBe(false); + await expect(await $(hubPage.summaryRowState("section-3")).isDisplayed()).toBe(false); - await expect(await $(hubPage.submit()).getText()).to.equal("Submit survey"); - await expect(await $(hubPage.heading()).getText()).to.equal("Submit survey"); + await expect(await $(hubPage.submit()).getText()).toBe("Submit survey"); + await expect(await $(hubPage.heading()).getText()).toBe("Submit survey"); }); }); diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js index 10b9e674b7..b69f0720d6 100644 --- a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js +++ b/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js @@ -10,15 +10,15 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { await $(sectionOne.yesEnableSection2()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain("section-2-block"); + await expect(browser).toHaveUrlContaining("section-2-block"); }); it("When the user answers `No, disable section 2` and submits, Then they should be taking straight to the summary", async () => { await $(sectionOne.noDisableSection2()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); - await expect(await $(SubmitPage.section2Question()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await expect(await $(SubmitPage.section2Question()).isExisting()).toBe(false); }); describe("Given that section 2 is enabled", () => { @@ -26,16 +26,16 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { await $(sectionOne.yesEnableSection2()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain("section-2-block"); + await expect(browser).toHaveUrlContaining("section-2-block"); }); it("When the user changes the answers and disables section 2, Then they should be taken straight to the summary", async () => { await browser.back(); - await expect(await browser.getUrl()).to.contain("section-1-block"); + await expect(browser).toHaveUrlContaining("section-1-block"); await $(sectionOne.noDisableSection2()).click(); await click(sectionOne.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index c8adcd7deb..e31770decf 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -42,9 +42,9 @@ describe("Feature: Grand Calculated Summary", () => { await click(tvChoiceBlockPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); await click(HubPage.submit()); - await expect(await $(CurrencyAllPage.currencySection1()).getText()).toContain("£330.00"); - await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).toContain("£70.00"); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.currencySection1()).getText()).toBe("£330.00"); + await expect(await $(CurrencyAllPage.currencyQuestion3()).getText()).toBe("£70.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £400.00. Is this correct?", ); await click(CurrencyAllPage.submit()); @@ -58,7 +58,7 @@ describe("Feature: Grand Calculated Summary", () => { // Currently the grand calculated summary remains 'Completed' because none of the answers have changed await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £330.00. Is this correct?", ); await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).toBe(false); @@ -73,7 +73,7 @@ describe("Feature: Grand Calculated Summary", () => { // Although the calculated summary is not on the path, the answer is still a grand calculated summary dependency, so it updates progress await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £330.00. Is this correct?", ); await expect(await $(CurrencyAllPage.currencyQuestion3()).isExisting()).toBe(false); @@ -88,7 +88,7 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I confirm the calculated summary and the blocks following it are already complete, When I press submit, Then I am returned to the section summary anchored to the answer I edited initially", async () => { await click(CurrencyQuestion3Page.submit()); - await expect(await browser.getUrl()).toContain("calculated-summary-section/#skip-answer-2"); + await expect(browser).toHaveUrlContaining("calculated-summary-section/#skip-answer-2"); }); it("Given I change an answer, When I press previous from the now incomplete calculated summary, Then I am routed to the block before the calculated summary", async () => { await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); @@ -104,7 +104,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("grand-calculated-summary-section")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £450.00. Is this correct?", ); }); @@ -119,7 +119,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); await click(CurrencyQuestion3Page.submit()); await expect(browser).toHaveUrlContaining(CurrencyAllPage.pageName); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £461.00. Is this correct?", ); await click(CurrencyAllPage.submit()); @@ -131,8 +131,8 @@ describe("Feature: Grand Calculated Summary", () => { await click(SkipFirstBlockPage.submit()); await click(QuestionsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("grand-calculated-summary-section")).click(); - await expect(await $(CurrencyAllPage.currencySection1()).getText()).toContain("£30.00"); - await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(CurrencyAllPage.currencySection1()).getText()).toBe("£30.00"); + await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £161.00. Is this correct?", ); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index 1bb9cd6139..5d54c91aae 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -30,7 +30,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(AnyCostPage.submit()); await $(FinanceCostPage.answer()).setValue(90); await click(FinanceCostPage.submit()); - await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total base cost for any owned vehicle to be £90.00. Is this correct?", ); await click(CalculatedSummaryBaseCostPage.submit()); @@ -56,17 +56,17 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(VehicleMaintenanceBlockPage.submit()); await $(VehicleFuelBlockPage.vehicleFuelCost()).setValue(125); await click(VehicleFuelBlockPage.submit()); - await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the monthly running costs of your Car to be £225.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £315.00. Is this correct?", ); - await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toContain("Vehicle base cost"); - await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toContain("Monthly Car costs"); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toContain("Grand total Car expenditure"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toBe("Vehicle base cost"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toBe("Monthly Car costs"); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toBe("Grand total Car expenditure"); assertSummaryValues(["£90.00", "£225.00", "£315.00"]); }); @@ -76,7 +76,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(GcsBreakdownBlockPage.payCredit()).setValue(115); await $(GcsBreakdownBlockPage.payOther()).setValue(200); await click(GcsBreakdownBlockPage.submit()); - await expect(await $(GcsBreakdownBlockPage.errorNumber()).getText()).toContain("Enter answers that add up to 315"); + await expect(await $(GcsBreakdownBlockPage.errorNumber()).getText()).toBe("Enter answers that add up to 315"); }); it("Given I enter a valid value for the Grand Calculated Summary breakdown, When I press continue, Then I see an Interstitial page with my values correctly piped in", async () => { @@ -101,17 +101,17 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(VehicleMaintenanceBlockPage.submit()); await $(VehicleFuelBlockPage.vehicleFuelCost()).setValue(45); await click(VehicleFuelBlockPage.submit()); - await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the monthly running costs of your Van to be £95.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £185.00. Is this correct?", ); - await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toContain("Vehicle base cost"); - await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toContain("Monthly Van costs"); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toContain("Grand total Van expenditure"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toBe("Vehicle base cost"); + await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toBe("Monthly Van costs"); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toBe("Grand total Van expenditure"); assertSummaryValues(["£90.00", "£95.00", "£185.00"]); }); @@ -171,7 +171,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(FinanceCostPage.answer()).setValue(100); await click(FinanceCostPage.submit()); await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); - await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total base cost for any owned vehicle to be £100.00. Is this correct?", ); await click(CalculatedSummaryBaseCostPage.submit()); @@ -183,7 +183,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(BaseCostPaymentBreakdownPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £195.00. Is this correct?", ); }); @@ -194,12 +194,12 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(75); await click(VehicleMaintenanceBlockPage.submit()); await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); - await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the monthly running costs of your Van to be £120.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £220.00. Is this correct?", ); }); @@ -215,8 +215,8 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(HubPage.summaryRowLink("vehicle-details-section-1")).click(); await click(GrandCalculatedSummaryVehiclePage.submit()); await click(VehicleDetailsSectionPage.submit()); - await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toContain("Completed"); - await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toContain("Completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("base-costs-section")).click(); await $(BaseCostsSectionPage.financeCostAnswerEdit()).click(); await $(FinanceCostPage.answer()).setValue(110); @@ -224,15 +224,15 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(CalculatedSummaryBaseCostPage.submit()); await click(BaseCostPaymentBreakdownPage.submit()); await click(BaseCostsSectionPage.submit()); - await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toContain("Partially completed"); - await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Partially completed"); }); it("Given I have two partially complete repeating sections, When I press continue, Then I am taken straight to the grand calculated summary as it is the first incomplete block", async () => { await click(HubPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[0]}/`); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £335.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); @@ -243,7 +243,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(HubPage.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); - await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £230.00. Is this correct?", ); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index bea7b81751..308918051f 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -34,8 +34,8 @@ describe("Feature: Grand Calculated Summary", () => { await click(CalculatedSummary4Page.submit()); await click(Section1SummaryPage.submit()); await click(HubPage.submit()); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( - "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?", + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toBe( + "Grand Calculated Summary of purchases this week comes to £360.00. Is this correct?.", ); await click(GrandCalculatedSummaryShoppingPage.submit()); }); @@ -53,8 +53,8 @@ describe("Feature: Grand Calculated Summary", () => { // then grand calculated summary await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); - await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( - "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?", + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toBe( + "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?.", ); }); @@ -66,7 +66,7 @@ describe("Feature: Grand Calculated Summary", () => { // taken back to the FIRST calculated summary which uses it await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); - await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toBe( "Total of eggs and cheese is calculated to be £700.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); @@ -93,7 +93,7 @@ describe("Feature: Grand Calculated Summary", () => { // taken back to the FIRST calculated summary which uses it await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); - await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toBe( "Total of eggs and cheese is calculated to be £800.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index f82c147691..84eeec3702 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -71,7 +71,7 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I click on the change link for a calculated summary, When I press continue, Then I am taken back to the grand calculated summary", async () => { - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for shopping and entertainment is calculated to be £415.00. Is this correct?", ); await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); @@ -95,7 +95,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary4Edit()).click(); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for games expenditure is calculated to be £15.00. Is this correct?", ); await $(CalculatedSummary4Page.q4A1Edit()).click(); @@ -106,21 +106,21 @@ describe("Feature: Grand Calculated Summary", () => { // first taken back to the calculated summary which has updated await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); - await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for games expenditure is calculated to be £60.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then taken back to the grand calculated summary which has also been updated correctly await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for shopping and entertainment is calculated to be £460.00. Is this correct?", ); }); it("Given I go back to another calculated summary and edit multiple answers, I am still correctly routed back to the grand calculated summary", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for food expenditure is calculated to be £100.00. Is this correct?", ); @@ -132,7 +132,7 @@ describe("Feature: Grand Calculated Summary", () => { // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for food expenditure is calculated to be £190.00. Is this correct?", ); @@ -143,7 +143,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block2Page.submit()); // back at updated calculated summary - await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for food expenditure is calculated to be £550.00. Is this correct?", ); @@ -154,7 +154,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(browser).toHaveUrlContaining(GrandCalculatedSummary1Page.pageName); await click(GrandCalculatedSummary1Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); - await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for shopping and entertainment is calculated to be £910.00. Is this correct?", ); }); @@ -259,7 +259,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have a calculated summary of repeating answers and a calculated summary of dynamic answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { await click(HubPage.submit()); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £280.00. Is this correct?", ); assertSummaryValues(["£250.00", "£30.00", "£280.00"]); @@ -272,7 +272,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have 2 calculated summaries of list repeating block answers, When I reach the grand calculated summary of both, Then I see the correct total and items", async () => { await click(GrandCalculatedSummary3Page.submit()); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); assertSummaryValues(["45 GB", "55 GB", "100 GB"]); @@ -281,7 +281,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have multiple calculated summaries of static, repeating and dynamic answers, When I reach the grand calculated summary of them all, Then I see the correct total and items", async () => { await click(GrandCalculatedSummary4Page.submit()); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,130.00. Is this correct?", ); assertSummaryValues(["£550.00", "£300.00", "£0.00", "£250.00", "£30.00", "£1,130.00"]); @@ -310,12 +310,12 @@ describe("Feature: Grand Calculated Summary", () => { await click(DynamicAnswerPage.submit()); await click(CalculatedSummary5Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £305.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,155.00. Is this correct?", ); }); @@ -335,12 +335,12 @@ describe("Feature: Grand Calculated Summary", () => { await click(StreamingServiceRepeatingBlock1Page.submit()); await click(CalculatedSummary5Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £309.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", ); }); @@ -354,7 +354,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(InternetBreakdownBlockPage.internetPc()).setValue(60); await $(InternetBreakdownBlockPage.internetPhone()).setValue(60); await click(InternetBreakdownBlockPage.submit()); - await expect(await $(InternetBreakdownBlockPage.errorNumber(1)).getText()).toContain("Enter answers that add up to 100"); + await expect(await $(InternetBreakdownBlockPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 100"); }); it("Given I use the grand calculated summary for validation, When I enter values with the correct sum, Then I progress to the summary page", async () => { @@ -374,41 +374,41 @@ describe("Feature: Grand Calculated Summary", () => { await $$(DynamicAnswerPage.inputs())[3].setValue("40"); await click(DynamicAnswerPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-4")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-4")).getText()).toBe("Partially completed"); await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); }); it("Given I complete the in progress section, When I return to the Hub, Then I see the grand calculated summary section re-enabled, and partially completed", async () => { await $(HubPage.summaryRowLink("section-4")).click(); - await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary5Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for monthly spending on utility bills is calculated to be £315.00. Is this correct?", ); await click(CalculatedSummary5Page.submit()); await click(Section4SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); }); it("Given I return to the grand calculated summary section, When I go to each grand calculated summary, Then I see the correct new values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); await expect(browser).toHaveUrlContaining(Section6SummaryPage.pageName); - await expect(await $$(summaryRowTitles)[0].getText()).toContain("How did you use the 100 GB across your devices?"); + await expect(await $$(summaryRowTitles)[0].getText()).toBe("How did you use the 100 GB across your devices?"); await click(Section6SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); }); it("Given I add a list item for the section with list repeating blocks, When I return to the hub before and after completing the section, Then I see the grand calculated summary go from unavailable, to enabled and in progress", async () => { @@ -422,36 +422,36 @@ describe("Feature: Grand Calculated Summary", () => { await click(StreamingServiceRepeatingBlock2Page.submit()); await $(AnyOtherStreamingServicesPage.no()).click(); await click(AnyOtherStreamingServicesPage.submit()); - await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for monthly expenditure on streaming services is calculated to be £44.00. Is this correct?", ); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-5")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-5")).getText()).toBe("Partially completed"); await expect(await $(HubPage.summaryRowLink("section-6")).isExisting()).toBe(false); await $(HubPage.summaryRowLink("section-5")).click(); await click(CalculatedSummary6Page.submit()); - await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toBe( "Total monthly internet usage on streaming services is calculated to be 50 GB. Is this correct?", ); await click(CalculatedSummary7Page.submit()); await click(Section5SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); }); it("Given I the grand calculated summary section is now incomplete, When I return to the section, Then I am taken to each updated grand calculated summary to confirm the new total", async () => { await $(HubPage.summaryRowLink("section-6")).click(); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £359.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 105 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); @@ -459,37 +459,37 @@ describe("Feature: Grand Calculated Summary", () => { }); it("Given I remove a list item involved in the grand calculated summary, When I confirm, Then I am taken to each affected calculated summary to reconfirm, and when I return to the Hub the grand calculated summary is in progress", async () => { - await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-5")).click(); await $(Section5SummaryPage.streamingServicesListRemoveLink(1)).click(); await $(RemoveStreamingServicePage.yes()).click(); await click(RemoveStreamingServicePage.submit()); - await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary6Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for monthly expenditure on streaming services is calculated to be £34.00. Is this correct?", ); await click(CalculatedSummary6Page.submit()); - await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummary7Page.calculatedSummaryTitle()).getText()).toBe( "Total monthly internet usage on streaming services is calculated to be 30 GB. Is this correct?", ); await click(CalculatedSummary7Page.submit()); await click(Section5SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-6")).getText()).toContain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); }); it("Given the section has reverted to partially complete, When I go back to the section, Then I am taken to each grand calculated summary to reconfirm with correct values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); - await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); - await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 85 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); - await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toContain( + await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); @@ -497,7 +497,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I have a further section depending on the grand calculated summary section, When I return to the Hub, Then I see the new section is available", async () => { await click(Section6SummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-7")).getText()).toContain("Not started"); + await expect(await $(HubPage.summaryRowState("section-7")).getText()).toBe("Not started"); await click(HubPage.submit()); }); @@ -507,7 +507,7 @@ describe("Feature: Grand Calculated Summary", () => { ); await $(PersonalExpenditureBlockPage.personalExpenditure()).setValue(1200); await click(PersonalExpenditureBlockPage.submit()); - await expect(await $(PersonalExpenditureBlockPage.errorNumber(1)).getText()).toContain("Enter an answer less than or equal to £1,199.00"); + await expect(await $(PersonalExpenditureBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £1,199.00"); }); it("Given I display multiple grand calculated summaries on an Interstitial page, When I reach the page, Then I see the correct values piped in", async () => { diff --git a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js index 22280aecad..83e92c5230 100644 --- a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js @@ -5,12 +5,12 @@ import { click } from "../../../helpers"; describe("Choose another section link", () => { it("When a user first views the Hub, then the link should not be displayed", async () => { await browser.openQuestionnaire("test_hub_and_spoke.json"); - await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); + await expect(await $("body").getText()).not.toBe("Choose another section and return to this later"); }); it("When a user views the first question and the hub is not available, then the link should not be displayed", async () => { await browser.openQuestionnaire("test_hub_complete_sections.json"); - await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); + await expect(await $("body").getText()).not.toBe("Choose another section and return to this later"); }); it("When a user starts a new section and the hub is available, then the link should be displayed", async () => { @@ -18,7 +18,7 @@ describe("Choose another section link", () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); await click(EmploymentStatusBlockPage.submit()); await $(HubPage.summaryRowLink("accommodation-section")).click(); - await expect(await $("body").getText()).to.contain("Choose another section and return to this later"); + await expect(await $("body").getText()).toContain("Choose another section and return to this later"); }); it("When a user gets to a section summary and the hub is available, then the link should not be displayed", async () => { @@ -28,6 +28,6 @@ describe("Choose another section link", () => { await $(HubPage.summaryRowLink("accommodation-section")).click(); await $(ProxyPage.noIMAnsweringForMyself()).click(); await click(ProxyPage.submit()); - await expect(await $("body").getText()).to.not.have.string("Choose another section and return to this later"); + await expect(await $("body").getText()).not.toBe("Choose another section and return to this later"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 969596a980..d70363b693 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -18,21 +18,21 @@ describe("Feature: Hub and Spoke", () => { }); it("When a user first views the Hub, The Hub should be in a continue state", async () => { - await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); - await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); - await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); - await expect(await $(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.submit()).getText()).toBe("Continue"); + await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("household-section")).getText()).toBe("Not started"); }); it("When a user views the Hub, any section with show_on_hub set to true should appear", async () => { - await expect(await $(HubPage.summaryItems()).getText()).to.contain("Employment"); - await expect(await $(HubPage.summaryItems()).getText()).to.contain("Accommodation"); - await expect(await $(HubPage.summaryItems()).getText()).to.contain("Household residents"); + await expect(await $(HubPage.summaryItems()).getText()).toContain("Employment"); + await expect(await $(HubPage.summaryItems()).getText()).toContain("Accommodation"); + await expect(await $(HubPage.summaryItems()).getText()).toContain("Household residents"); }); it("When a user views the Hub, any section with show_on_hub set to false should not appear", async () => { - await expect(await $(HubPage.summaryItems()).getText()).not.to.contain("Relationships"); + await expect(await $(HubPage.summaryItems()).getText()).not.toBe("Relationships"); }); it("When the user click the 'Save and sign out' button then they should be redirected to the correct log out url", async () => { @@ -40,34 +40,34 @@ describe("Feature: Hub and Spoke", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain("/signed-out"); + await expect(expectedUrl).toContain("/signed-out"); }); it("When a user views the Hub, Then the page title should be Choose another section to complete", async () => { const pageTitle = await browser.getTitle(); - await expect(pageTitle).to.equal("Choose another section to complete - Hub & Spoke"); + await expect(pageTitle).toBe("Choose another section to complete - Hub & Spoke"); }); }); describe("Given a user has not started a section", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire(hubAndSpokeSchema); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Not started"); - await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).to.contain("Not started"); - await expect(await $(HubPage.summaryRowState("household-section")).getText()).to.contain("Not started"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("household-section")).getText()).toBe("Not started"); }); it("When the user starts a section, Then the first question in the section should be displayed", async () => { await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); + await expect(expectedUrl).toContain(EmploymentStatusBlockPage.url()); }); it("When the user starts a section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { await click(HubPage.submit()); await $(EmploymentStatusBlockPage.previous()).click(); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HubPage.url()); + await expect(expectedUrl).toContain(HubPage.url()); }); }); @@ -81,20 +81,20 @@ describe("Feature: Hub and Spoke", () => { it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { await browser.url(HubPage.url()); - await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); - await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + await expect(await $(HubPage.submit()).getText()).toBe("Continue"); + await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); }); it("When the user returns to the Hub, Then the section should be marked as 'Partially completed'", async () => { await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Partially completed"); }); it("When the user returns to the Hub and restarts the same section, Then they should be redirected to the first incomplete block", async () => { await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("employment-section")).click(); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); + await expect(expectedUrl).toContain(EmploymentTypeBlockPage.url()); }); }); @@ -110,33 +110,33 @@ describe("Feature: Hub and Spoke", () => { it("When the user clicks the 'Continue' button, it should return them to the hub", async () => { await click(EmploymentTypeBlockPage.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HubPage.url()); + await expect(expectedUrl).toContain(HubPage.url()); }); it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { await click(EmploymentTypeBlockPage.submit()); - await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); - await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); + await expect(await $(HubPage.submit()).getText()).toBe("Continue"); + await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); }); it("When the user returns to the Hub, Then the section should be marked as 'Completed'", async () => { await click(EmploymentTypeBlockPage.submit()); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Completed"); }); it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", async () => { await click(EmploymentTypeBlockPage.submit()); await $(HubPage.summaryRowLink("employment-section")).click(); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(EmploymentStatusBlockPage.url()); + await expect(expectedUrl).toContain(EmploymentStatusBlockPage.url()); }); it("When the user returns to the Hub and continues, Then they should progress to the next section", async () => { await click(EmploymentTypeBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(browser).toHaveUrlContaining(HubPage.url()); await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(ProxyPage.url()); + await expect(expectedUrl).toContain(ProxyPage.url()); }); }); @@ -147,27 +147,27 @@ describe("Feature: Hub and Spoke", () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); await click(EmploymentStatusBlockPage.submit()); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Completed"); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Completed"); }); it("When the user clicks the 'View answers' link and incompletes the section, Then they the should be taken to the next incomplete question on 'Continue", async () => { await $(HubPage.summaryRowLink("employment-section")).click(); - await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); await click(EmploymentStatusBlockPage.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(EmploymentTypeBlockPage.url()); + await expect(expectedUrl).toContain(EmploymentTypeBlockPage.url()); }); it("When the user clicks the 'View answers' link and incompletes the section and returns to the hub, Then the section should be marked as 'Partially completed'", async () => { await $(HubPage.summaryRowLink("employment-section")).click(); - await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); await click(EmploymentStatusBlockPage.submit()); await browser.url(HubPage.url()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HubPage.url()); - await expect(await $(HubPage.summaryRowState("employment-section")).getText()).to.contain("Partially completed"); + await expect(expectedUrl).toContain(HubPage.url()); + await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Partially completed"); }); }); @@ -195,18 +195,18 @@ describe("Feature: Hub and Spoke", () => { it("It should return them to the hub", async () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HubPage.url()); + await expect(expectedUrl).toContain(HubPage.url()); }); it("When the user returns to the Hub, Then the Hub should be in a completed state", async () => { - await expect(await $(HubPage.submit()).getText()).to.contain("Submit survey"); - await expect(await $(HubPage.heading()).getText()).to.contain("Submit survey"); + await expect(await $(HubPage.submit()).getText()).toBe("Submit survey"); + await expect(await $(HubPage.heading()).getText()).toBe("Submit survey"); }); it("When the user submits, it should show the thankyou page", async () => { await click(HubPage.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain("thank-you"); + await expect(expectedUrl).toContain("thank-you"); }); }); @@ -216,7 +216,7 @@ describe("Feature: Hub and Spoke", () => { }); it("The hub should not show first of all", async () => { - await expect(await browser.getUrl()).to.contain(EmploymentStatusBlockPage.url()); + await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); }); it("The hub should only display when required sections are complete", async () => { @@ -224,7 +224,7 @@ describe("Feature: Hub and Spoke", () => { await click(EmploymentStatusBlockPage.submit()); await $(EmploymentTypeBlockPage.studying()).click(); await click(EmploymentTypeBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(browser).toHaveUrlContaining(HubPage.url()); }); }); @@ -241,7 +241,7 @@ describe("Feature: Hub and Spoke", () => { await $(HubPage.summaryRowLink("household-section")).click(); await click(HouseholdSummary.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HubPage.url()); + await expect(expectedUrl).toContain(HubPage.url()); }); it("When there are changes, which would set the section to in_progress it routes accordingly", async () => { @@ -251,7 +251,7 @@ describe("Feature: Hub and Spoke", () => { await click(DoesAnyoneLiveHere.submit()); await click(HouseholdSummary.submit()); const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(HowManyPeopleLiveHere.url()); + await expect(expectedUrl).toContain(HowManyPeopleLiveHere.url()); }); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js index d2449609e8..b805b39419 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js @@ -8,10 +8,10 @@ describe("Feature: Hub and Spoke with custom content", () => { it("When the questionnaire is incomplete, then custom content should be displayed correctly", async () => { await browser.openQuestionnaire(hubAndSpokeSchema); - await expect(await $(HubPage.heading()).getText()).to.contain("Choose another section to complete"); - await expect(await $(HubPage.guidance()).isExisting()).to.be.false; - await expect(await $(HubPage.submit()).getText()).to.contain("Continue"); - await expect(await $(HubPage.warning()).isExisting()).to.be.false; + await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); + await expect(await $(HubPage.guidance()).isExisting()).toBe(false); + await expect(await $(HubPage.submit()).getText()).toBe("Continue"); + await expect(await $(HubPage.warning()).isExisting()).toBe(false); }); it("When the questionnaire is complete, then custom content should be displayed correctly", async () => { @@ -22,9 +22,9 @@ describe("Feature: Hub and Spoke with custom content", () => { await $(HowManyPeopleLiveHere.answer1()).click(); await click(HowManyPeopleLiveHere.submit()); await click(HouseholdSummary.submit()); - await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); - await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); - await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); - await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); + await expect(await $(HubPage.heading()).getText()).toBe("Submission title"); + await expect(await $(HubPage.guidance()).getText()).toBe("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).toBe("Submission button"); + await expect(await $(HubPage.warning()).getText()).toBe("Submission warning"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js index 97fba7567d..b15893849d 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js @@ -9,13 +9,13 @@ describe("Hub and spoke section required and enabled", () => { it("Given a relationship question in household, When I answer 'Yes', meaning the second section is enabled, Then I am routed to second section", async () => { await $(HouseholdRelationshipsBlockPage.yes()).click(); await click(HouseholdRelationshipsBlockPage.submit()); - await expect(await $(RelationshipsCountPage.legend()).getText()).to.contain("How many people are related"); + await expect(await $(RelationshipsCountPage.legend()).getText()).toBe("How many people are related?"); }); it("Given a relationship question in household, When I answer 'No', Then I am redirected to the hub and can submit my answers without completing the other section", async () => { await $(HouseholdRelationshipsBlockPage.no()).click(); await click(HouseholdRelationshipsBlockPage.submit()); - await expect(await $("body").getText()).to.contain("Submit survey"); + await expect(await $("body").getText()).toContain("Submit survey"); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); }); diff --git a/tests/functional/spec/features/hub_and_spoke/previous.spec.js b/tests/functional/spec/features/hub_and_spoke/previous.spec.js index 2206ea7974..d3c3e66961 100644 --- a/tests/functional/spec/features/hub_and_spoke/previous.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/previous.spec.js @@ -10,25 +10,25 @@ describe("Choose another section link", () => { }); it("When a user gets to initial question, then the previous location link should not be displayed", async () => { - await expect(await $(EmploymentStatusBlockPage.previous()).isExisting()).to.be.false; + await expect(await $(EmploymentStatusBlockPage.previous()).isExisting()).toBe(false); }); it("When a user gets to the hub, then the previous location link should not be displayed", async () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); await click(EmploymentStatusBlockPage.submit()); - await expect(await $(HubPage.previous()).isExisting()).to.be.false; + await expect(await $(HubPage.previous()).isExisting()).toBe(false); }); it("When a user gets to subsequent question, then the previous location link should be displayed", async () => { await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); await click(EmploymentStatusBlockPage.submit()); - await expect(await $(EmploymentTypePage.previous()).isExisting()).to.be.true; + await expect(await $(EmploymentTypePage.previous()).isExisting()).toBe(true); }); it("When a user gets to subsequent questions past the hub, then the previous location link should be displayed", async () => { await $(EmploymentStatusBlockPage.workingAsAnEmployee()).click(); await click(EmploymentStatusBlockPage.submit()); await $(HubPage.summaryRowLink("accommodation-section")).click(); - await expect(await $(ProxyPage.previous()).isExisting()).to.be.true; + await expect(await $(ProxyPage.previous()).isExisting()).toBe(true); }); }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js index 7fb20c8e6a..eae51ad5f0 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js @@ -15,16 +15,16 @@ describe("Last viewed question guidance", () => { }); it("When the respondent first launches the survey, then last question guidance is not shown", async () => { - await expect(await browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); - await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(HouseholdInterstitialPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent resumes on the first block of a section, then last question guidance is not shown", async () => { await $(HouseholdInterstitialPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); await browser.pause(100); - await expect(await browser.getUrl()).to.contain(HouseholdInterstitialPage.url()); - await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(HouseholdInterstitialPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { @@ -32,22 +32,22 @@ describe("Last viewed question guidance", () => { await $(AddressConfirmationPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); await browser.pause(100); - await expect(await browser.getUrl()).to.contain(AddressConfirmationPage.url()); - await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(HouseholdInterstitialPage.url()); - await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(AddressConfirmationPage.url()); + await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(HouseholdInterstitialPage.url()); + await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); it("When the respondent answers the question and saves and continues, then last question guidance is not shown on the next question", async () => { await $(AddressConfirmationPage.yes()).click(); await click(AddressConfirmationPage.submit()); - await expect(await browser.getUrl()).to.contain(PrimaryPersonListCollectorPage.url()); - await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(PrimaryPersonListCollectorPage.url()); + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent uses the previous link from the next question, then last question guidance is not shown", async () => { await click(AddressConfirmationPage.submit()); await $(PrimaryPersonListCollectorPage.previous()).click(); - await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js index 4a9426cd19..b4e0a699ad 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js @@ -21,24 +21,24 @@ describe("Last viewed question guidance", () => { }); it("When the respondent launches the survey, then last question guidance is not shown", async () => { - await expect(await browser.getUrl()).to.contain(WorkInterstitialPage.url()); - await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(WorkInterstitialPage.url()); + await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent saves and resumes from a section which is not started, then last question guidance is not shown", async () => { await $(WorkInterstitialPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); await browser.pause(100); - await expect(await browser.getUrl()).to.contain(WorkInterstitialPage.url()); - await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(WorkInterstitialPage.url()); + await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent saves and resumes from a section which is in progress, then last question guidance is shown", async () => { await click(WorkInterstitialPage.submit()); await $(PaidWorkPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); - await expect(await $(PaidWorkPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(WorkInterstitialPage.url()); - await expect(await $(PaidWorkPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + await expect(await $(PaidWorkPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(WorkInterstitialPage.url()); + await expect(await $(PaidWorkPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); }); @@ -54,8 +54,8 @@ describe("Last viewed question guidance", () => { it("When the respondent selects a section which is not started, then last question guidance is not shown", async () => { await $(HubPage.summaryRowLink("education-section")).click(); - await expect(await browser.getUrl()).to.contain(GcsesPage.url()); - await expect(await $(GcsesPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(GcsesPage.url()); + await expect(await $(GcsesPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the respondent selects a section which is in progress, then last question guidance is shown", async () => { @@ -64,28 +64,28 @@ describe("Last viewed question guidance", () => { await click(GcsesPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("education-section")).click(); - await expect(await browser.getUrl()).to.contain(ALevelsPage.url()); - await expect(await $(ALevelsPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(GcsesPage.url()); - await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(ALevelsPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(GcsesPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); it("When the respondent selects a section which is complete , then last question guidance is not shown on the summary or any link clicked from the summary", async () => { await $(ALevelsPage.yes()).click(); await click(ALevelsPage.submit()); - await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); - await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(EducationSectionSummaryPage.url()); + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); await click(EducationSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("education-section")).click(); - await expect(await browser.getUrl()).to.contain(EducationSectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(EducationSectionSummaryPage.url()); await $(EducationSectionSummaryPage.alevelsAnswerEdit()).click(); - await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the user clicks continue on the hub and it takes you to a section which is not started, then last question guidance is not shown", async () => { await browser.url(HubPage.url()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(SportsPage.url()); - await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SportsPage.url()); + await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); it("When the user clicks continue on the hub and it takes you to a section which is in progress, then last question guidance is shown", async () => { @@ -94,17 +94,17 @@ describe("Last viewed question guidance", () => { await click(SportsPage.submit()); await browser.url(HubPage.url()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(HobbiesPage.url()); - await expect(await $(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).to.contain(SportsPage.url()); - await expect(await $(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(HobbiesPage.url()); + await expect(await $(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(SportsPage.url()); + await expect(await $(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); it("When the user clicks continue on the hub and it takes you to a section which is complete but doesnt have a summary, then last question guidance is not shown", async () => { await $(HobbiesPage.yes()).click(); await click(HobbiesPage.submit()); await $(HubPage.summaryRowLink("interests-section")).click(); - await expect(await browser.getUrl()).to.contain(SportsPage.url()); - await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SportsPage.url()); + await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js index f6c1cf8c95..85d4c1c8eb 100644 --- a/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js +++ b/tests/functional/spec/features/placeholder/answer_option_based_on_first_item_in_list.spec.js @@ -30,7 +30,7 @@ describe("Component: Definition", () => { await click(FavouriteDrinkQuestion.submit()); // Then - await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Tea"); + await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).toBe("Tea"); }); it("Given I am not the first person in the list, When I get to the question page, Then I should see the correct answer option", async () => { @@ -63,7 +63,7 @@ describe("Component: Definition", () => { await click(FavouriteDrinkQuestion.submit()); // Then - await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).to.contain("Orange Juice"); + await expect(await $(ListStatusQuestion.listStatus2TeaLabel()).getText()).toBe("Orange Juice"); }); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js index f9ecf026ab..11e466e624 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_difference.spec.js @@ -17,7 +17,7 @@ describe("Difference check (years)", () => { await $(AgeBlockYearPage.month()).setValue(1); await $(AgeBlockYearPage.year()).setValue(1990); await click(AgeBlockYearPage.submit()); - await expect(await $(AgeTestYearPage.heading()).getText()).to.equal(`You are ${getYears("1990/01/01")} years old. Is this correct?`); + await expect(await $(AgeTestYearPage.heading()).getText()).toBe(`You are ${getYears("1990/01/01")} years old. Is this correct?`); }); }); @@ -32,7 +32,7 @@ describe("Difference check (months and years)", () => { await click(AgeBlockMonthYearPage.submit()); - await expect(await $(AgeTestMonthYearPage.heading()).getText()).to.equal( + await expect(await $(AgeTestMonthYearPage.heading()).getText()).toBe( `It has been ${getYears("1990/01/01")} years since you last went on holiday. Is this correct?`, ); }); @@ -51,7 +51,7 @@ describe("Difference check (months and years range)", () => { await click(AgeBlockMonthYearRangePage.submit()); - await expect(await $(AgeTestMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); + await expect(await $(AgeTestMonthYearRangePage.heading()).getText()).toBe("You were out of the UK for 1 year. Is this correct?"); }); }); @@ -71,7 +71,7 @@ describe("Difference check (years range)", () => { await click(AgeBlockDayMonthYearRangePage.submit()); - await expect(await $(AgeTestDayMonthYearRangePage.heading()).getText()).to.have.string("You were out of the UK for 1 year. Is this correct?"); + await expect(await $(AgeTestDayMonthYearRangePage.heading()).getText()).toBe("You were out of the UK for 1 year. Is this correct?"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js index 9fb84a6528..ae50636dde 100644 --- a/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_date_ranges.spec.js @@ -15,7 +15,7 @@ describe("Date checks", () => { await click(DateQuestionPage.submit()); - await expect(await $(DaysQuestionBlockPage.questionText()).getText()).to.contain("Monday 30 August to Monday 13 September 2021"); + await expect(await $(DaysQuestionBlockPage.questionText()).getText()).toContain("Monday 30 August to Monday 13 September 2021"); await click(DaysQuestionBlockPage.submit()); }); @@ -37,6 +37,6 @@ describe("Date checks", () => { await click(Block0Page.submit()); - await expect(await $(RangeQuestionBlockPage.questionText()).getText()).to.contain("Wednesday 1 to Sunday 19 May 2019"); + await expect(await $(RangeQuestionBlockPage.questionText()).getText()).toContain("Wednesday 1 to Sunday 19 May 2019"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js index 042ef0723a..ed490ff9dc 100644 --- a/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_default_value.spec.js @@ -9,25 +9,25 @@ describe("Placeholder default value check", () => { it("Given a question with default answer, When I do not enter any number and click submit, Then the interstitial page shows default employees number as 0", async () => { await click(EmployeesNumberBlockPage.submit()); - await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 0"); + await expect(await $("#main-content > p").getText()).toContain("The total number of employees confirmed are 0"); }); it("Given a question with default answer, When I enter a number of employee and click submit, Then the interstitial page shows me the employees number entered", async () => { await $(EmployeesNumberBlockPage.employeesNo()).setValue("54"); await click(EmployeesNumberBlockPage.submit()); - await expect(await $("#main-content > p").getText()).to.contain("The total number of employees confirmed are 54"); + await expect(await $("#main-content > p").getText()).toContain("The total number of employees confirmed are 54"); }); it("Given a training budget question with default answer, When I do not enter any amount and click submit, Then the interstitial page shows default amount as 250.00", async () => { await click(EmployeesNumberBlockPage.submit()); await click(EmployeesNumberInterstitialPage.submit()); await click(EmployeesTrainingBlockPage.submit()); - await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £250.00"); + await expect(await $("#main-content > p").getText()).toBe("The average training budget per employee is £250.00"); }); it("Given a training budget question with default answer, When I enter an amount and click submit, Then the interstitial page shows amount entered", async () => { await click(EmployeesNumberBlockPage.submit()); await click(EmployeesNumberInterstitialPage.submit()); await $(EmployeesTrainingBlockPage.employeesAvgTraining()).setValue("100"); await click(EmployeesTrainingBlockPage.submit()); - await expect(await $("#main-content > p").getText()).to.contain("The average training budget per employee is £100.00"); + await expect(await $("#main-content > p").getText()).toBe("The average training budget per employee is £100.00"); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js index 3bfe6df62c..a875f725d2 100644 --- a/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_metadata.spec.js @@ -7,14 +7,14 @@ describe("Placeholder metadata check", () => { await browser.openQuestionnaire("test_placeholder_metadata.json"); }); it("When I see responding unit question, Then I see radio options with first option as metadata placeholder (ru_name)", async () => { - await expect(await $(MandatoryRadioPage.answerRuNameLabel()).getText()).to.equal("Apple"); + await expect(await $(MandatoryRadioPage.answerRuNameLabel()).getText()).toBe("Apple"); }); it("When I answer responding unit question, Then I see confirmation page with my selected placeholder metadata option (ru_name)", async () => { await $(MandatoryRadioPage.answerRuName()).click(); await click(MandatoryRadioPage.submit()); - await expect(await $(SubmitPage.mandatoryRadioAnswer()).getText()).to.equal("Apple"); - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + await expect(await $(SubmitPage.mandatoryRadioAnswer()).getText()).toBe("Apple"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Please submit this survey to complete it"); }); }); }); diff --git a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js index 07f643c226..fc792c4b5c 100644 --- a/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js +++ b/tests/functional/spec/features/placeholder/placeholder_option_label_from_value.spec.js @@ -7,17 +7,17 @@ describe("Option label value check", () => { }); it("Given radio options are provided, when I select first answer (piped from metadata) and go to the next page, then the question title contains the label text of the answer I selected", async () => { - await expect(await $(MandatoryRadioPage.answerBusinessNamePipedLabel()).getText()).to.contain("Apple (piped)"); + await expect(await $(MandatoryRadioPage.answerBusinessNamePipedLabel()).getText()).toContain("Apple (piped)"); await $(MandatoryRadioPage.answerBusinessNamePiped()).click(); await $(MandatoryRadioPage.submit()).scrollIntoView(); await click(MandatoryRadioPage.submit()); - await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Apple (piped)"); + await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).toContain("Apple (piped)"); }); it("Given radio options are provided, when I select an answer (static) and go to the next page, then the question title contains the label text of the answer I selected", async () => { await $(MandatoryRadioPage.googleLtd()).click(); await $(MandatoryRadioPage.submit()).scrollIntoView(); await click(MandatoryRadioPage.submit()); - await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).to.contain("Google LTD"); + await expect(await $(ConfirmationQuestionRadioBlockPage.questionText()).getText()).toContain("Google LTD"); }); }); diff --git a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js index fa4786feab..fe5017b2df 100644 --- a/tests/functional/spec/features/placeholder/playback_confirmation.spec.js +++ b/tests/functional/spec/features/placeholder/playback_confirmation.spec.js @@ -10,8 +10,7 @@ describe("Feature: Playback Confirmation", () => { await $(MandatoryCheckboxPage.ham()).click(); await click(MandatoryCheckboxPage.submit()); - await expect(await $("#confirm-answers-question ul").getHTML()) - .to.contain("Cheese") - .to.contain("Ham"); + await expect(await $("#confirm-answers-question ul").getHTML()).toContain("Ham"); + await expect(await $("#confirm-answers-question ul").getHTML()).toContain("Cheese"); }); }); diff --git a/tests/functional/spec/features/progress/progress_value_source_blocks.js b/tests/functional/spec/features/progress/progress_value_source_blocks.js index 449bd81ee3..7bb57f6b21 100644 --- a/tests/functional/spec/features/progress/progress_value_source_blocks.js +++ b/tests/functional/spec/features/progress/progress_value_source_blocks.js @@ -19,7 +19,7 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); await click(ThirdQuestionPage.submit()); @@ -29,9 +29,9 @@ describe("Feature: Routing based on progress value sources using block identifi await $(SeventhQuestionPage.q1A1()).setValue("3"); await click(SeventhQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $("body").getText()).to.not.have.string("Section 1 Question 2"); - await expect(await $("body").getText()).to.not.have.string("Section 1 Question 4"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $("body").getText()).not.toBe("Section 1 Question 2"); + await expect(await $("body").getText()).not.toBe("Section 1 Question 4"); }); }); @@ -40,30 +40,30 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); await click(ThirdQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); await click(FifthQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); await click(SeventhQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); - await expect(await $("body").getText()).to.have.string("Section 1 Question 6"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $("body").getText()).toContain("Section 1 Question 4"); + await expect(await $("body").getText()).toContain("Section 1 Question 6"); }); }); @@ -72,7 +72,7 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(ThirdQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); await click(ThirdQuestionPage.submit()); @@ -83,29 +83,29 @@ describe("Feature: Routing based on progress value sources using block identifi await click(SeventhQuestionPage.submit()); await $(SubmitPage.s1B1Q1A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await click(ThirdQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await click(FifthQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("3"); await click(SixthQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $("body").getText()).to.have.string("Section 1 Question 4"); - await expect(await $("body").getText()).to.have.string("Section 1 Question 6"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $("body").getText()).toContain("Section 1 Question 4"); + await expect(await $("body").getText()).toContain("Section 1 Question 6"); }); }); @@ -114,35 +114,35 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SecondQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); await click(ThirdQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(FourthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); await click(FifthQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SixthQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); await click(SeventhQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.s1B1Q1A1Edit()).click(); - await expect(await browser.getUrl()).to.contain(FirstQuestionPage.pageName); + await expect(browser).toHaveUrlContaining(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); - await expect(await $("body").getText()).to.not.have.string("Section 1 Question 4"); - await expect(await $("body").getText()).to.not.have.string("Section 1 Question 6"); + await expect(await $("body").getText()).not.toBe("Section 1 Question 4"); + await expect(await $("body").getText()).not.toBe("Section 1 Question 6"); }); }); }); @@ -158,7 +158,7 @@ describe("Feature: Section enabled based on progress value sources using block i await click(FirstQuestionPage.submit()); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(ThirdQuestionSectionTwoPage.pageName); + await expect(browser).toHaveUrlContaining(ThirdQuestionSectionTwoPage.pageName); }); }); }); @@ -175,7 +175,7 @@ describe("Feature: Section enabled based on progress value sources using section await click(FirstQuestionPage.submit()); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Not started"); }); }); @@ -186,8 +186,8 @@ describe("Feature: Section enabled based on progress value sources using section await click(FirstQuestionPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); - await expect(await $("body").getText()).to.not.have.string("Section 2"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); + await expect(await $("body").getText()).not.toBe("Section 2"); }); }); }); diff --git a/tests/functional/spec/features/progress/progress_value_source_repeating.js b/tests/functional/spec/features/progress/progress_value_source_repeating.js index 0fc3193e01..94f95adb0c 100644 --- a/tests/functional/spec/features/progress/progress_value_source_repeating.js +++ b/tests/functional/spec/features/progress/progress_value_source_repeating.js @@ -27,14 +27,14 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(QuestionBlockPage.pageName); + await expect(browser).toHaveUrlContaining(QuestionBlockPage.pageName); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("section-2-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SectionTwoSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SectionTwoSummaryPage.pageName); }); }); @@ -53,11 +53,11 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(RandomQuestionEnablerBlockPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-2-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); + await expect(browser).toHaveUrlContaining(OtherQuestionBlockPage.pageName); }); }); @@ -77,22 +77,22 @@ describe("Feature: Routing rules based on progress value sources in repeating se await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).toBe("Not started"); await $(HubPage.summaryRowLink("section-2-1")).click(); await click(DOBQuestionBlockPage.submit()); await click(SectionTwoSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).toBe("Not started"); await click(HubPage.submit()); await click(QuestionBlockPage.submit()); await $(RandomQuestionEnablerBlockPage.randomQuestionEnabler()).setValue(1); await click(RandomQuestionEnablerBlockPage.submit()); - await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section-2-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2-2")).getText()).toBe("Not started"); }); }); }); @@ -118,15 +118,15 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(browser).toHaveUrlContaining(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); await click(SectionThreeSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).toBe("Completed"); }); }); @@ -146,15 +146,15 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(browser).toHaveUrlContaining(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); await click(SectionThreeSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-1")).click(); await $(SecondNumberBlockPage.secondNumber()).setValue(2); @@ -162,9 +162,9 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(CalculatedSummaryBlockPage.submit()); await browser.url(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("section-2")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-2")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-3-1")).getText()).toBe("Partially completed"); }); }); @@ -189,11 +189,11 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.pageName); + await expect(browser).toHaveUrlContaining(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(OtherQuestionBlockPage.pageName); + await expect(browser).toHaveUrlContaining(OtherQuestionBlockPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js index e736288912..3d50a6cc04 100644 --- a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js +++ b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js @@ -10,17 +10,17 @@ describe("Summary Screen", () => { it("Given a survey has question summary concatenations and has been completed when on the summary page then the correct response should be displayed formatted correctly", async () => { completeAllQuestions(); - await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("John Smith"); - await expect(await $(SubmitPage.summaryRowState("address-question-concatenated-answer")).getText()).to.contain("Cardiff Road\nNewport\nNP10 8XG"); - await expect(await $(SubmitPage.summaryRowState("age-question-concatenated-answer")).getText()).to.contain("7\nThis age is an estimate"); + await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).toBe("John Smith"); + await expect(await $(SubmitPage.summaryRowState("address-question-concatenated-answer")).getText()).toBe("Cardiff Road\nNewport\nNP10 8XG"); + await expect(await $(SubmitPage.summaryRowState("age-question-concatenated-answer")).getText()).toBe("7\nThis age is an estimate"); }); it("Given no values are entered in a question with multiple answers and concatenation set, when on the summary screen then the correct response should be displayed", async () => { await click(NameBlockPage.submit()); await click(AddressBlockPage.submit()); await click(AgeBlock.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).to.contain("No answer provided"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).toBe("No answer provided"); }); async function completeAllQuestions() { diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 4b2d32c42a..c7c829cedd 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -108,7 +108,7 @@ describe("List Collector Repeating Blocks", () => { }); it("The list collector does not show 'GOV' anymore.", async () => { - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toContain("Government"); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toBe("Government"); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("MOD"); }); @@ -209,18 +209,18 @@ describe("List Collector Repeating Blocks", () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); }); it("Editing an answer from the section summary which does not affect progress and pressing continue returns the user to the section summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("No"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).toContain("section-companies/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); }); it("Clicking a change link from the final summary and pressing previous or submit without changing an answer returns the user to the final summary anchored to the answer they clicked on", async () => { @@ -228,18 +228,18 @@ describe("List Collector Repeating Blocks", () => { await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); }); it("Editing an answer from the final summary which does not affect progress and pressing continue returns the user to the final summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("Yes"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(await browser.getUrl()).toContain("submit/#any-other-trading-details-answer"); + await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); }); it("The list collector can now be submitted.", async () => { @@ -264,30 +264,30 @@ describe("List Collector Repeating Blocks", () => { }); it("Edit each type of answer on different items from the section summary.", async () => { - await expect(await $$(summaryValues)[8].getText()).toContain("456"); + await expect(await $$(summaryValues)[8].getText()).toBe("456"); await repeatingAnswerChangeLink(8).click(); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[8].getText()).toContain("789"); + await expect(await $$(summaryValues)[8].getText()).toBe("789"); - await expect(await $$(summaryValues)[4].getText()).toContain("1 January 2023"); + await expect(await $$(summaryValues)[4].getText()).toBe("1 January 2023"); await repeatingAnswerChangeLink(4).click(); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("4"); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("4"); await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[4].getText()).toContain("4 April 2023"); + await expect(await $$(summaryValues)[4].getText()).toBe("4 April 2023"); - await expect(await $$(summaryValues)[5].getText()).toContain("Yes"); + await expect(await $$(summaryValues)[5].getText()).toBe("Yes"); await repeatingAnswerChangeLink(5).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[5].getText()).toContain("No"); + await expect(await $$(summaryValues)[5].getText()).toBe("No"); - await expect(await $$(summaryValues)[11].getText()).toContain("No answer provided"); + await expect(await $$(summaryValues)[11].getText()).toBe("No answer provided"); await repeatingAnswerChangeLink(11).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[11].getText()).toContain("Yes"); + await expect(await $$(summaryValues)[11].getText()).toBe("Yes"); }); it("The list collector can then be submitted", async () => { @@ -313,7 +313,7 @@ describe("Given a journey through the list collector with repeating blocks, in a it("Navigating to the root of the questionnaire will redirect to the incomplete list collector, which we can then complete.", async () => { await browser.url("questionnaire/"); - await expect(await browser.getUrl()).toContain(AnyOtherCompaniesOrBranchesPage.url()); + await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); @@ -338,7 +338,7 @@ describe("Given a journey through the list collector with repeating blocks, in a it("Navigating to the submit page of the section will redirect to the incomplete list collector, which we can then complete.", async () => { await browser.url("questionnaire/sections/section-companies/"); - await expect(await browser.getUrl()).toContain(AnyOtherCompaniesOrBranchesPage.url()); + await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.url()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index b7cc5ea238..8f22372d72 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -25,9 +25,9 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // is pushing the submit button outside window await $(HubPage.acceptCookies()).click(); // Ensure we are on the Hub - await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(browser).toHaveUrlContaining(HubPage.url()); // Ensure the first section is not started - await expect(await $(HubPage.summaryRowState("section")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("section")).getText()).toBe("Not started"); // Start first section to add household members await $(HubPage.summaryRowLink("section")).click(); @@ -73,26 +73,26 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { beforeEach("Navigate to the Hub", async () => await browser.url(HubPage.url())); it("Then a section for each household member should be displayed", async () => { - await expect(await browser.getUrl()).to.contain(HubPage.url()); - - await expect(await $(HubPage.summaryRowState("section")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowTitle("personal-details-section-1")).getText()).to.equal("Marcus Twin"); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowTitle("personal-details-section-2")).getText()).to.equal("Jean Clemens"); - await expect(await $(HubPage.summaryRowState("personal-details-section-3")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowTitle("personal-details-section-3")).getText()).to.equal("Samuel Clemens"); - await expect(await $(HubPage.summaryRowState("personal-details-section-4")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowTitle("personal-details-section-4")).getText()).to.equal("John Doe"); - - await expect(await $(HubPage.summaryRowState("section-5")).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(HubPage.url()); + + await expect(await $(HubPage.summaryRowState("section")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-1")).getText()).toBe("Marcus Twin"); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-2")).getText()).toBe("Jean Clemens"); + await expect(await $(HubPage.summaryRowState("personal-details-section-3")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-3")).getText()).toBe("Samuel Clemens"); + await expect(await $(HubPage.summaryRowState("personal-details-section-4")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowTitle("personal-details-section-4")).getText()).toBe("John Doe"); + + await expect(await $(HubPage.summaryRowState("section-5")).isExisting()).toBe(false); }); it("When the user starts a repeating section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); await $(ProxyPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(browser).toHaveUrlContaining(HubPage.url()); }); it("When the user partially completes a repeating section, Then that section should be marked as 'Partially completed' on the Hub", async () => { @@ -110,14 +110,14 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await browser.url(HubPage.url()); - await expect(await browser.getUrl()).to.contain(HubPage.url()); - await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).to.equal("Partially completed"); + await expect(browser).toHaveUrlContaining(HubPage.url()); + await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Partially completed"); }); it("When the user continues with a partially completed repeating section, Then they are taken to the first incomplete block", async () => { await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(await $(SexPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); + await expect(await $(SexPage.questionText()).getText()).toBe("What is Marcus Twin’s sex?"); }); it("When the user completes a repeating section, Then that section should be marked as 'Completed' on the Hub", async () => { @@ -138,24 +138,24 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await click(PersonalDetailsSummaryPage.submit()); - await expect(await browser.getUrl()).to.contain(HubPage.url()); - await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).to.equal("Completed"); + await expect(browser).toHaveUrlContaining(HubPage.url()); + await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); it("When the user clicks 'View answers' for a completed repeating section, Then they are taken to the summary", async () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); - await expect(await browser.getUrl()).to.contain("/sections/personal-details-section"); + await expect(browser).toHaveUrlContaining("/sections/personal-details-section"); }); it("When the user views the summary for a repeating section, Then the page title is shown", async () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); - await expect(await browser.getTitle()).to.equal("… - Hub & Spoke"); + await expect(await browser.getTitle()).toBe("… - Hub & Spoke"); }); it("When the user adds 2 visitors to the household then a section for each visitor should be display on the hub", async () => { // Ensure no other sections exist - await expect(await $(HubPage.summaryRowState("personal-details-section-5")).isExisting()).to.be.false; - await expect(await $(HubPage.summaryRowState("visitors-section-1")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("personal-details-section-5")).isExisting()).toBe(false); + await expect(await $(HubPage.summaryRowState("visitors-section-1")).isExisting()).toBe(false); // Start section for first visitor await $(HubPage.summaryRowLink("section")).click(); @@ -165,7 +165,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); await $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); await click(VisitorsListCollectorAddPage.submit()); - await expect(await browser.getUrl()).to.contain("/questionnaire/visitors-block"); + await expect(browser).toHaveUrlContaining("/questionnaire/visitors-block"); // Add second visitor await $(VisitorsListCollectorPage.yes()).click(); @@ -180,17 +180,17 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await click(SectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("visitors-section-1")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowTitle("visitors-section-1")).getText()).to.equal("Joe Public"); - await expect(await $(HubPage.summaryRowState("visitors-section-2")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowTitle("visitors-section-2")).getText()).to.equal("Yvonne Yoe"); + await expect(await $(HubPage.summaryRowState("visitors-section-1")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowTitle("visitors-section-1")).getText()).toBe("Joe Public"); + await expect(await $(HubPage.summaryRowState("visitors-section-2")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowTitle("visitors-section-2")).getText()).toBe("Yvonne Yoe"); - await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).toBe(false); }); it("When the user clicks 'Continue' from the Hub, Then they should progress to the first incomplete section", async () => { await click(HubPage.submit()); - await expect(await $(ConfirmDateOfBirthPage.questionText()).getText()).to.equal("What is Marcus Twin’s sex?"); + await expect(await $(ConfirmDateOfBirthPage.questionText()).getText()).toBe("What is Marcus Twin’s sex?"); }); it("When the user answers on their behalf, Then they are shown the non proxy question variant", async () => { @@ -206,7 +206,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesIAmAgeOld()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(await $(SexPage.questionText()).getText()).to.equal("What is your sex?"); + await expect(await $(SexPage.questionText()).getText()).toBe("What is your sex?"); }); it("When the user answers on on behalf of someone else, Then they are shown the proxy question variant for the relevant repeating section", async () => { @@ -221,7 +221,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(ConfirmDateOfBirthPage.confirmDateOfBirthYesPersonNameIsAgeOld()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(await $(SexPage.questionText()).getText()).to.equal("What is Samuel Clemens’ sex?"); + await expect(await $(SexPage.questionText()).getText()).toBe("What is Samuel Clemens’ sex?"); }); it("When the user completes all sections, Then the Hub should be in the completed state", async () => { @@ -252,8 +252,8 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(VisitorsDateOfBirthPage.year()).setValue("1999"); await click(VisitorsDateOfBirthPage.submit()); - await expect(await $(HubPage.submit()).getText()).to.equal("Submit survey"); - await expect(await $(HubPage.heading()).getText()).to.equal("Submit survey"); + await expect(await $(HubPage.submit()).getText()).toBe("Submit survey"); + await expect(await $(HubPage.heading()).getText()).toBe("Submit survey"); }); it("When the user adds a new visitor, Then the Hub should not be in the completed state", async () => { @@ -271,19 +271,19 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await click(SectionSummaryPage.submit()); // New visitor added to hub - await expect(await $(HubPage.summaryRowState("visitors-section-3")).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).toBe(true); - await expect(await $(HubPage.submit()).getText()).to.not.equal("Submit survey"); - await expect(await $(HubPage.submit()).getText()).to.equal("Continue"); + await expect(await $(HubPage.submit()).getText()).not.toBe("Submit survey"); + await expect(await $(HubPage.submit()).getText()).toBe("Continue"); - await expect(await $(HubPage.heading()).getText()).to.not.equal("Submit survey"); - await expect(await $(HubPage.heading()).getText()).to.equal("Choose another section to complete"); + await expect(await $(HubPage.heading()).getText()).not.toBe("Submit survey"); + await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); }); it("When the user removes a visitor, Then their section is not longer displayed on he Hub", async () => { // Ensure final householder exists - await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).toBe(true); await $(HubPage.summaryRowLink("section")).click(); @@ -295,12 +295,12 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await click(SectionSummaryPage.submit()); // Ensure final householder no longer exists - await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowState("visitors-section-3")).isExisting()).toBe(false); }); it("When the user submits, it should show the thank you page", async () => { await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); }); }); diff --git a/tests/functional/spec/features/routing/all_in.spec.js b/tests/functional/spec/features/routing/all_in.spec.js index de3c6a87ce..124f90549d 100644 --- a/tests/functional/spec/features/routing/all_in.spec.js +++ b/tests/functional/spec/features/routing/all_in.spec.js @@ -14,18 +14,18 @@ describe("Feature: Routing - ALL-IN Operator", () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); }); it("When I do select India only, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/and.spec.js b/tests/functional/spec/features/routing/and.spec.js index d24edd0ee9..2bcf19c927 100644 --- a/tests/functional/spec/features/routing/and.spec.js +++ b/tests/functional/spec/features/routing/and.spec.js @@ -16,7 +16,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the incorrect page", async () => { @@ -24,7 +24,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the incorrect page", async () => { @@ -32,7 +32,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { @@ -40,7 +40,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js index 095475e2b3..2a453e33c5 100644 --- a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js +++ b/tests/functional/spec/features/routing/answer_comparison_routing.spec.js @@ -12,7 +12,7 @@ describe("Test routing skip", () => { await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(2); await click(RouteComparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("This page should never be skipped"); + await expect(await $("#main-content > p").getText()).toBe("This page should never be skipped"); }); it("Given we start the routing test survey, When we enter a high number then a low number, Then, we should be routed to the third page", async () => { @@ -20,7 +20,7 @@ describe("Test routing skip", () => { await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(0); await click(RouteComparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); + await expect(await $("#main-content > p").getText()).toBe("This page should be skipped if your second answer was higher than your first"); }); it("Given we start the routing test survey, When we enter an equal number on both questions, Then, we should be routed to the third page", async () => { @@ -28,6 +28,6 @@ describe("Test routing skip", () => { await click(RouteComparison1Page.submit()); await $(RouteComparison2Page.answer()).setValue(1); await click(RouteComparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("This page should be skipped if your second answer was higher than your first"); + await expect(await $("#main-content > p").getText()).toBe("This page should be skipped if your second answer was higher than your first"); }); }); diff --git a/tests/functional/spec/features/routing/answer_not_on_path.spec.js b/tests/functional/spec/features/routing/answer_not_on_path.spec.js index 20da2e9bad..36f95e9f4f 100644 --- a/tests/functional/spec/features/routing/answer_not_on_path.spec.js +++ b/tests/functional/spec/features/routing/answer_not_on_path.spec.js @@ -13,13 +13,13 @@ describe("Answers not on path are not considered when routing", () => { await $(InitialChoicePage.goHereFirst()).click(); await click(InitialChoicePage.submit()); - await expect(await browser.getUrl()).to.contain(InvalidPathPage.pageName); + await expect(browser).toHaveUrlContaining(InvalidPathPage.pageName); await $(InvalidPathPage.answer()).setValue(123); await click(InvalidPathPage.submit()); // We now have an answer in the store on the 'invalid' path - await expect(await browser.getUrl()).to.contain(InvalidPathInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(InvalidPathInterstitialPage.pageName); await $(InvalidPathInterstitialPage.previous()).click(); await $(InvalidPathPage.previous()).click(); @@ -32,6 +32,6 @@ describe("Answers not on path are not considered when routing", () => { await click(ValidPathPage.submit()); // We should be routed to the valid interstitial page since the invalid path answer should not be considered whilst routing. - await expect(await browser.getUrl()).to.contain(ValidFinalInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(ValidFinalInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/features/routing/answered_unanswered.spec.js b/tests/functional/spec/features/routing/answered_unanswered.spec.js index 9965197a86..7387072cd4 100644 --- a/tests/functional/spec/features/routing/answered_unanswered.spec.js +++ b/tests/functional/spec/features/routing/answered_unanswered.spec.js @@ -19,20 +19,20 @@ describe("Test routing question answered/unanswered", () => { it("When I select any answer and submit, Then I should see a page saying I have answered the first question", async () => { await $(QuestionOne.ham()).click(); await click(QuestionOne.submit()); - await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); - await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You answered the first question!"); + await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionOne.cheese()).click(); await click(QuestionOne.submit()); - await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You answered the first question!"); - await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You answered the first question!"); + await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the first question", async () => { await click(QuestionOne.submit()); - await expect(await $(QuestionOneAnswered.heading()).getText()).to.contain("You did not answer the first question!"); - await expect(await browser.getUrl()).to.contain(QuestionOneAnswered.pageName); + await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You did not answer the first question!"); + await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); }); }); @@ -46,20 +46,20 @@ describe("Test routing question answered/unanswered", () => { it("When I select any answer and submit, Then I should see a page saying I have answered the second question", async () => { await $(QuestionTwo.pizzaHut()).click(); await click(QuestionTwo.submit()); - await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); - await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You answered the second question!"); + await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionTwo.dominoS()).click(); await click(QuestionTwo.submit()); - await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You answered the second question!"); - await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You answered the second question!"); + await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the second question", async () => { await click(QuestionTwo.submit()); - await expect(await $(QuestionTwoUnanswered.heading()).getText()).to.contain("You did not answer the second question!"); - await expect(await browser.getUrl()).to.contain(QuestionTwoAnswered.pageName); + await expect(await $(QuestionTwoUnanswered.heading()).getText()).toBe("You did not answer the second question!"); + await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); }); }); @@ -74,21 +74,21 @@ describe("Test routing question answered/unanswered", () => { it("When I do not answer the question or answer `0` and submit, Then I should see a page saying I did not answer the question or that I chose `0`", async () => { await click(QuestionThree.submit()); - await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); - await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); + await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).toBe("You did not answer the question or chose 0 slices"); + await expect(browser).toHaveUrlContaining(QuestionThreeUnansweredOrAnswerZero.pageName); await $(QuestionThreeUnansweredOrAnswerZero.previous()).click(); await $(QuestionThree.answer3()).setValue("0"); await click(QuestionThree.submit()); - await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).to.contain("You did not answer the question or chose 0 slices"); - await expect(await browser.getUrl()).to.contain(QuestionThreeUnansweredOrAnswerZero.pageName); + await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).toBe("You did not answer the question or chose 0 slices"); + await expect(browser).toHaveUrlContaining(QuestionThreeUnansweredOrAnswerZero.pageName); }); it("When I enter an answer greater than 0 and submit, Then I should see a page saying I chose at least one", async () => { await $(QuestionThree.answer3()).setValue("2"); await click(QuestionThree.submit()); - await expect(await $(QuestionTwoAnswered.heading()).getText()).to.contain("You chose at least 1 slice"); - await expect(await browser.getUrl()).to.contain(QuestionThreeAnsweredOrNotZero.pageName); + await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You chose at least 1 slice"); + await expect(browser).toHaveUrlContaining(QuestionThreeAnsweredOrNotZero.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/any_in.spec.js b/tests/functional/spec/features/routing/any_in.spec.js index 6ab4cef38f..5a910993c6 100644 --- a/tests/functional/spec/features/routing/any_in.spec.js +++ b/tests/functional/spec/features/routing/any_in.spec.js @@ -13,18 +13,18 @@ describe("Feature: Routing - ANY-IN Operator", () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); }); it("When I do select India or Malta, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/checkbox_count.spec.js b/tests/functional/spec/features/routing/checkbox_count.spec.js index f40f6c5dd5..49e20925a6 100644 --- a/tests/functional/spec/features/routing/checkbox_count.spec.js +++ b/tests/functional/spec/features/routing/checkbox_count.spec.js @@ -12,23 +12,23 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.ham()).click(); await click(ToppingCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); - await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await expect(await $(CorrectAnswerPage.questionText()).getText()).toBe("You selected 2 or more toppings"); }); it("Given a user selects no checkboxes, When they submit, Then they should be routed to the incorrect page", async () => { await click(ToppingCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); - await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await expect(await $(IncorrectAnswerPage.questionText()).getText()).toBe("You did not select 2 or more toppings"); }); it("Given a user selects 1 checkbox, When they submit, Then they should be routed to the incorrect page", async () => { await $(ToppingCheckboxPage.cheese()).click(); await click(ToppingCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); - await expect(await $(IncorrectAnswerPage.questionText()).getText()).to.have.string("You did not select 2 or more toppings"); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await expect(await $(IncorrectAnswerPage.questionText()).getText()).toBe("You did not select 2 or more toppings"); }); it("Given a user selects 3 checkbox, When they submit, Then they should be routed to the correct page", async () => { @@ -37,7 +37,7 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.pineapple()).click(); await click(ToppingCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); - await expect(await $(CorrectAnswerPage.questionText()).getText()).to.have.string("You selected 2 or more toppings"); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await expect(await $(CorrectAnswerPage.questionText()).getText()).toBe("You selected 2 or more toppings"); }); }); diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/features/routing/date.spec.js index fddbcab619..db7dca2226 100644 --- a/tests/functional/spec/features/routing/date.spec.js +++ b/tests/functional/spec/features/routing/date.spec.js @@ -42,7 +42,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the yesterday date, Then I should be routed to the correct page", async () => { @@ -50,7 +50,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the tomorrow date, Then I should be routed to the correct page", async () => { @@ -58,7 +58,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the last month date, Then I should be routed to the correct page", async () => { @@ -66,7 +66,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(2); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the next month date, Then I should be routed to the correct page", async () => { @@ -74,7 +74,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the last year date, Then I should be routed to the correct page", async () => { @@ -82,7 +82,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2019); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter the next year date, Then I should be routed to the correct page", async () => { @@ -90,7 +90,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2021); await click(DateEqualsQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter an incorrect date, Then I should be routed to the incorrect page", async () => { @@ -98,7 +98,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsComparisonQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); }); }); @@ -116,7 +116,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter February 2018, Then I should be routed to the incorrect page", async () => { @@ -126,7 +126,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); @@ -145,7 +145,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter the 1st March 2017, Then I should be routed to the incorrect page", async () => { @@ -156,7 +156,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date less than the 1st March 2017, Then I should be routed to the incorrect page", async () => { @@ -167,7 +167,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); @@ -184,7 +184,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter 2017, Then I should be routed to the correct page", async () => { @@ -193,7 +193,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(CorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date less than March 2017, Then I should be routed to the incorrect page", async () => { @@ -202,7 +202,7 @@ describe("Feature: Routing on a Date", () => { const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); @@ -221,7 +221,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(await browserUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date equal to today, Then I should be routed to the incorrect page", async () => { @@ -232,7 +232,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -243,7 +243,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); @@ -262,7 +262,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(browserUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date equal to today, Then I should be routed to the correct page", async () => { @@ -273,7 +273,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(CorrectAnswerPage.pageName); + await expect(await browserUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -284,7 +284,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).to.contain(IncorrectAnswerPage.pageName); + await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/in.spec.js b/tests/functional/spec/features/routing/in.spec.js index 93619ab486..c5e0fad396 100644 --- a/tests/functional/spec/features/routing/in.spec.js +++ b/tests/functional/spec/features/routing/in.spec.js @@ -12,13 +12,13 @@ describe("Feature: Routing - IN Operator", () => { it("When I do select India, Then I should be routed to the the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); }); it("When I do not select India, Then I should be routed to the the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialOtherPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/not.spec.js b/tests/functional/spec/features/routing/not.spec.js index dd8bb7fee5..85465d18e5 100644 --- a/tests/functional/spec/features/routing/not.spec.js +++ b/tests/functional/spec/features/routing/not.spec.js @@ -12,13 +12,13 @@ describe("Feature: Routing - Not Operator", () => { it("When I do not select India, Then I should be routed to the not India interstitial page", async () => { await $(CountryCheckboxPage.azerbaijan()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(CountryInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); }); it("When I select India, Then I should be routed to the India interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(await browser.getUrl()).to.contain(IndiaInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(IndiaInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/number.spec.js b/tests/functional/spec/features/routing/number.spec.js index ed343b4e4b..ba16e485d0 100644 --- a/tests/functional/spec/features/routing/number.spec.js +++ b/tests/functional/spec/features/routing/number.spec.js @@ -12,14 +12,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter a number that isn't 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); @@ -33,14 +33,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number that isn't 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(987); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); @@ -54,21 +54,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); @@ -82,21 +82,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(77); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); it("When I enter a number greater than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(765); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); @@ -110,21 +110,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); @@ -138,21 +138,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(23); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I enter a number larger than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(546); await click(NumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/or.spec.js b/tests/functional/spec/features/routing/or.spec.js index dbab536988..c2ba961847 100644 --- a/tests/functional/spec/features/routing/or.spec.js +++ b/tests/functional/spec/features/routing/or.spec.js @@ -15,7 +15,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the correct page", async () => { @@ -23,7 +23,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the correct page", async () => { @@ -31,7 +31,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(CorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { @@ -39,7 +39,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); await click(SecondNumberQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(IncorrectAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/routing/removes_completed_block.spec.js b/tests/functional/spec/features/routing/removes_completed_block.spec.js index ff0b8760da..b2aef75bdc 100644 --- a/tests/functional/spec/features/routing/removes_completed_block.spec.js +++ b/tests/functional/spec/features/routing/removes_completed_block.spec.js @@ -10,7 +10,7 @@ describe("Feature: Routing incompletes block if routing backwards", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yes()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary.spec.js b/tests/functional/spec/features/section_summary/section_summary.spec.js index 1562ea3201..e13a484581 100644 --- a/tests/functional/spec/features/section_summary/section_summary.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary.spec.js @@ -18,50 +18,50 @@ describe("Section Summary", () => { await click(InsuranceTypePage.submit()); await click(InsuranceAddressPage.submit()); await click(ListedPage.submit()); - await expect(await $(PropertyDetailsSummaryPage.insuranceTypeAnswer()).getText()).to.contain("Both"); + await expect(await $(PropertyDetailsSummaryPage.insuranceTypeAnswer()).getText()).toBe("Both"); }); it("When I get to the section summary page, Then the submit button should read 'Continue'", async () => { - await expect(await $(PropertyDetailsSummaryPage.submit()).getText()).to.contain("Continue"); + await expect(await $(PropertyDetailsSummaryPage.submit()).getText()).toBe("Continue"); }); it("When I have selected an answer to edit and edit it, Then I should return to the section summary with new value displayed", async () => { await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.answer()).setValue("Test Address"); await click(InsuranceAddressPage.submit()); - await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); + await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer()).getText()).toBe("Test Address"); }); it("When I select edit from the section summary and click previous on the question page, Then I should be taken back to the section summary", async () => { await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.url()); + await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.url()); }); it("When I continue on the section summary page, Then I should be taken to the next section", async () => { await click(PropertyDetailsSummaryPage.submit()); - await expect(await browser.getUrl()).to.contain(HouseType.pageName); + await expect(browser).toHaveUrlContaining(HouseType.pageName); }); it("When I select edit from Section Summary but change routing, Then I should step through the section and be returned to the Section Summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); - await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.pageName); }); it("When I select edit from Section Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(await browser.getUrl()).to.contain(PropertyDetailsSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.pageName); }); }); @@ -78,14 +78,14 @@ describe("Section Summary", () => { await $(NumberOfPeoplePage.answer()).setValue(3); await click(NumberOfPeoplePage.submit()); await click(HouseholdCountSectionSummaryPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("When I select edit from Final Summary and don't change an answer, Then I should be taken to the Final Summary", async () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await click(InsuranceAddressPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("When I select edit from Final Summary and change an answer that doesn't affect completeness, Then I should be taken to the Final Summary", async () => { @@ -93,7 +93,7 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.answer()).setValue("Test Address"); await click(InsuranceAddressPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("When I select edit from Final Summary but change routing, Then I should step through the section and be returned to the Final Summary once all new questions have been answered", async () => { @@ -101,9 +101,9 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I select edit from Final Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { @@ -111,12 +111,12 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(await browser.getUrl()).to.contain(AddressDurationPage.pageName); + await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Since I cannot return to the section summary yet I return to the previous block in the section", async () => { await $(SubmitPage.summaryShowAllButton()).click(); @@ -124,18 +124,18 @@ describe("Section Summary", () => { await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); await $(AddressDurationPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); }); it("When I change an answer, Then the final summary should display the updated value", async () => { await $(SubmitPage.summaryShowAllButton()).click(); - await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).toBe("No answer provided"); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); - await expect(await browser.getUrl()).to.contain(InsuranceAddressPage.pageName); + await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); await $(InsuranceAddressPage.answer()).setValue("Test Address"); await click(InsuranceAddressPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); - await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).to.contain("Test Address"); + await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).toBe("Test Address"); }); }); describe("Given I start the Test Section Summary questionnaire", () => { @@ -147,13 +147,13 @@ describe("Section Summary", () => { await click(InsuranceTypePage.submit()); await click(InsuranceAddressPage.submit()); await click(ListedPage.submit()); - await expect(await $(PropertyDetailsSummaryPage.heading()).getText()).to.contain("Property Details Section"); + await expect(await $(PropertyDetailsSummaryPage.heading()).getText()).toBe("Property Details Section"); }); it("When there is a title set in the sections summary, it is used for the section summary title", async () => { await click(PropertyDetailsSummaryPage.submit()); await $(HouseType.semiDetached()).click(); await click(HouseType.submit()); - await expect(await $(HouseholdDetailsSummaryPage.heading()).getText()).to.contain("Household Summary - Semi-detached"); + await expect(await $(HouseholdDetailsSummaryPage.heading()).getText()).toBe("Household Summary - Semi-detached"); }); }); }); diff --git a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js index cd65e29248..f06f255b86 100644 --- a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js +++ b/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js @@ -15,7 +15,7 @@ describe("Feature: Repeating Section Summaries", () => { // Ensure the questionnaire fully loads await browser.pause(100); // Ensure we are on the Hub - await expect(await browser.getUrl()).to.contain(HubPage.url()); + await expect(browser).toHaveUrlContaining(HubPage.url()); // Start first section to add household members await $(HubPage.summaryRowLink("section")).click(); @@ -56,15 +56,15 @@ describe("Feature: Repeating Section Summaries", () => { }); it("the title set in the repeating block is used for the section summary title", async () => { - await expect(await $(PersonalSummaryPage.heading()).getText()).to.contain("Mark Twain"); + await expect(await $(PersonalSummaryPage.heading()).getText()).toBe("Mark Twain"); }); it("renders their name as part of the question title on the section summary", async () => { - await expect(await $(PersonalSummaryPage.dateOfBirthQuestion()).getText()).to.contain("Mark Twain’s"); + await expect(await $(PersonalSummaryPage.dateOfBirthQuestion()).getText()).toContain("Mark Twain’s"); }); it("renders the correct date of birth answer", async () => { - await expect(await $(PersonalSummaryPage.dateOfBirthAnswer()).getText()).to.contain("30 November 1835"); + await expect(await $(PersonalSummaryPage.dateOfBirthAnswer()).getText()).toBe("30 November 1835"); }); }); }); diff --git a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js index 512e047c31..fe70621269 100644 --- a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js +++ b/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js @@ -17,7 +17,7 @@ describe("Feature: Show section summary on completion", () => { await $(employmentStatusBlockPage.workingAsAnEmployee()).click(); await click(employmentStatusBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(hubPage.url()); + await expect(browser).toHaveUrlContaining(hubPage.url()); }); }); @@ -25,7 +25,7 @@ describe("Feature: Show section summary on completion", () => { it("When I return to a completed section from the hub, Then I am returned to that section summary", async () => { await $(hubPage.summaryRowLink("employment-section")).click(); - await expect(await browser.getUrl()).to.contain(employmentSectionSummary.url()); + await expect(browser).toHaveUrlContaining(employmentSectionSummary.url()); }); }); @@ -39,7 +39,7 @@ describe("Feature: Show section summary on completion", () => { await $(proxyQuestionPage.noIMAnsweringForMyself()).click(); await click(proxyQuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(accommodationSectionSummary.url()); + await expect(browser).toHaveUrlContaining(accommodationSectionSummary.url()); }); }); @@ -51,7 +51,7 @@ describe("Feature: Show section summary on completion", () => { it("When I return to a completed section from the hub, Then I am returned to the correct section summary", async () => { await $(hubPage.summaryRowLink("accommodation-section")).click(); - await expect(await browser.getUrl()).to.contain(accommodationSectionSummary.url()); + await expect(browser).toHaveUrlContaining(accommodationSectionSummary.url()); }); }); }); diff --git a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js index 42f636517c..952388e5fe 100644 --- a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js +++ b/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js @@ -12,20 +12,20 @@ describe("Test skip condition answer comparisons", () => { await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(1); await click(Comparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("Your second number was equal to your first number"); + await expect(await $("#main-content > p").getText()).toBe("Your second number was equal to your first number"); }); it("Given we start the skip condition survey, when we enter a high number then a low number, then the interstitial should show that the answers are low then high", async () => { await $(Comparison1Page.answer()).setValue(3); await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(2); await click(Comparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("Your first answer was greater than your second number"); + await expect(await $("#main-content > p").getText()).toBe("Your first answer was greater than your second number"); }); it("Given we start the skip condition survey, when we enter a low number then a high number, then the interstitial should show that the answers are high then low", async () => { await $(Comparison1Page.answer()).setValue(1); await click(Comparison1Page.submit()); await $(Comparison2Page.answer()).setValue(2); await click(Comparison2Page.submit()); - await expect(await $("#main-content > p").getText()).to.contain("Your first answer was less than your second number"); + await expect(await $("#main-content > p").getText()).toBe("Your first answer was less than your second number"); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page.spec.js b/tests/functional/spec/features/submit_page/submit_page.spec.js index a206b88549..629d0dc3dd 100644 --- a/tests/functional/spec/features/submit_page/submit_page.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page.spec.js @@ -12,13 +12,13 @@ describe("Given I launch a linear flow questionnaire without summary", () => { it("When I complete the questionnaire, then I should be taken to the submit page without a summary", async () => { await $(BreakfastPage.answer()).setValue("Bacon"); await click(BreakfastPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); - await expect(await $(SubmitPage.summary()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await expect(await $(SubmitPage.summary()).isExisting()).toBe(false); }); it("When I complete the questionnaire and submit the questionnaire, then the submission is successful", async () => { await click(BreakfastPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); await click(SubmitPage.submit()); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js index e9246b645e..c7e83b7353 100644 --- a/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_collapsible_summary.spec.js @@ -26,20 +26,20 @@ describe("Collapsible Summary", () => { }); it("When I am on the submit page, Then a collapsed summary should be displayed with the group title and questions should not be displayed", async () => { - await expect(await $(SubmitPage.collapsibleSummary()).isDisplayed()).to.be.true; + await expect(await $(SubmitPage.collapsibleSummary()).isDisplayed()).toBe(true); - await expect(await $(SubmitPage.collapsibleSummary()).getText()).to.contain("Property Details"); - await expect(await $(SubmitPage.collapsibleSummary()).getText()).to.contain("House Details"); + await expect(await $(SubmitPage.collapsibleSummary()).getText()).toContain("Property Details"); + await expect(await $(SubmitPage.collapsibleSummary()).getText()).toContain("House Details"); - await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).to.equal(""); - await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).to.equal(""); + await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).toBe(""); + await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).toBe(""); }); it("When I click the Show all button, Then the summary should be expanded and questions should be displayed", async () => { await $(SubmitPage.summaryShowAllButton()).click(); - await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).to.contain("What is the address you would like to insure?"); - await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).to.contain("Title"); + await expect(await $(SubmitPage.insuranceAddressQuestion()).getText()).toBe("What is the address you would like to insure?"); + await expect(await $(SubmitPage.numberOfPeopleQuestion()).getText()).toBe("Title"); }); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js index d8e43fb64d..db91730754 100644 --- a/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_custom_submission_text.spec.js @@ -9,9 +9,9 @@ describe("Summary Screen", () => { it("Given a questionnaire with a summary and custom submission content has been completed, then the correct submission content should be displayed", async () => { await $(DessertBlockPage.dessert()).setValue("Crème Brûlée"); await click(DessertBlockPage.submit()); - await expect(await $(SubmitPage.heading()).getText()).to.contain("Submission title"); - await expect(await $(SubmitPage.warning()).getText()).to.contain("Submission warning"); - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Submission guidance"); - await expect(await $(SubmitPage.submit()).getText()).to.contain("Submission button"); + await expect(await $(SubmitPage.heading()).getText()).toBe("Submission title"); + await expect(await $(SubmitPage.warning()).getText()).toBe("Submission warning"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Submission guidance"); + await expect(await $(SubmitPage.submit()).getText()).toBe("Submission button"); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js index 35e28ab92a..5f07fd8d84 100644 --- a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js @@ -12,20 +12,20 @@ describe("Submit Page with Summary", () => { it("Given a questionnaire with a summary has been completed when the submit page is displayed, then it should contain a summary of all answers", async () => { await completeAllQuestions(); - await expect(await $(SubmitPage.radioAnswer()).getText()).to.contain("Bacon"); - await expect(await $(SubmitPage.dessertGroupTitle()).getText()).to.contain("Dessert"); - await expect(await $(SubmitPage.dessertAnswer()).getText()).to.contain("Crème Brûlée"); - await expect(await $(SubmitPage.dessertConfirmationAnswer()).getText()).to.contain("Yes"); - await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£1,234.00"); - await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).to.contain("123,456 km²"); - await expect(await $(SubmitPage.numbersDecimalAnswer()).getText()).to.contain("123,456.78"); + await expect(await $(SubmitPage.radioAnswer()).getText()).toBe("Bacon"); + await expect(await $(SubmitPage.dessertGroupTitle()).getText()).toBe("Dessert"); + await expect(await $(SubmitPage.dessertAnswer()).getText()).toBe("Crème Brûlée"); + await expect(await $(SubmitPage.dessertConfirmationAnswer()).getText()).toBe("Yes"); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).toBe("£1,234.00"); + await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).toBe("123,456 km²"); + await expect(await $(SubmitPage.numbersDecimalAnswer()).getText()).toBe("123,456.78"); }); it("Given a questionnaire with a summary has been completed when the submit page is displayed then I should be able to submit the answers", async () => { await completeAllQuestions(); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question", async () => { @@ -33,7 +33,7 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.radioAnswerEdit()).click(); - await expect(await $(RadioPage.bacon()).isSelected()).to.be.true; + await expect(await $(RadioPage.bacon()).isSelected()).toBe(true); }); it("Given a questionnaire with a summary has been completed and a summary page edit link is clicked, when I click previous, then it should return to the summary", async () => { @@ -42,7 +42,7 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.radioAnswerEdit()).click(); await $(RadioPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question then back to summary", async () => { @@ -51,17 +51,17 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.radioAnswerEdit()).click(); await $(RadioPage.sausage()).click(); await click(RadioPage.submit()); - await expect(await $(SubmitPage.radioAnswer()).getText()).to.contain("Sausage"); + await expect(await $(SubmitPage.radioAnswer()).getText()).toBe("Sausage"); }); it("Given the edit link is used when a question is updated then the submit page summary should show the new answer", async () => { await completeAllQuestions(); await $(SubmitPage.numbersUnitAnswerEdit()).click(); - await expect(await $(NumbersPage.unit()).isFocused()).to.be.true; + await expect(await $(NumbersPage.unit()).isFocused()).toBe(true); await $(NumbersPage.unit()).setValue("654321"); await click(NumbersPage.submit()); - await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).to.contain("654,321 km²"); + await expect(await $(SubmitPage.numbersUnitAnswer()).getText()).toBe("654,321 km²"); }); it("Given a number value of zero is entered when on the submit page then formatted 0 should be displayed on the summary", async () => { @@ -72,7 +72,7 @@ describe("Submit Page with Summary", () => { await click(DessertConfirmationPage.submit()); await $(NumbersPage.currency()).setValue("0"); await click(NumbersPage.submit()); - await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("£0.00"); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).toBe("£0.00"); }); it("Given no value is entered when on the submit page summary then the correct response should be displayed", async () => { @@ -82,14 +82,14 @@ describe("Submit Page with Summary", () => { await $(DessertConfirmationPage.yes()).click(); await click(DessertConfirmationPage.submit()); await click(NumbersPage.submit()); - await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.numbersCurrencyAnswer()).getText()).toBe("No answer provided"); }); it("Given a questionnaire with a summary has been completed, when submission content has not been set in the schema, then the default content should be displayed", async () => { await completeAllQuestions(); - await expect(await $(SubmitPage.heading()).getText()).to.contain("Check your answers and submit"); - await expect(await $(SubmitPage.submit()).getText()).to.contain("Submit answers"); + await expect(await $(SubmitPage.heading()).getText()).toBe("Check your answers and submit"); + await expect(await $(SubmitPage.submit()).getText()).toBe("Submit answers"); }); async function completeAllQuestions() { @@ -104,6 +104,6 @@ describe("Submit Page with Summary", () => { await $(NumbersPage.decimal()).setValue("123456.78"); await click(NumbersPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); } }); diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index e4ed903e12..0178aeaf7d 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -17,15 +17,15 @@ describe("Summary Anchor Scrolling", () => { }); it("When I have provided an answer and click through to the next question, Then the Previous link url shouldn't contain any anchors or reference to return_to or return_to_answer_id", async () => { - await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("#"); - await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to"); - await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.to.contain("return_to_answer_id"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.toContain("#"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.toContain("return_to"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).not.toContain("return_to_answer_id"); }); it("When I reach the section summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( + await expect(await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).getAttribute("href")).toContain( "insurance-address/?return_to=section-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", ); }); @@ -33,7 +33,7 @@ describe("Summary Anchor Scrolling", () => { it("When I reach the section summary page, Then the Change link url for a concatenated answer should contain return_to, return_to_answer_id query params", async () => { await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(await $(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).to.contain( + await expect(await $(PropertyDetailsSummaryPage.summaryRowState("name-question-concatenated-answer-edit")).getAttribute("href")).toContain( "name/?return_to=section-summary&return_to_answer_id=name-question-concatenated-answer#first-name", ); }); @@ -42,7 +42,7 @@ describe("Summary Anchor Scrolling", () => { await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); - await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).to.contain("property-details-section/#insurance-address-answer2"); + await expect(await $(InsuranceAddressPage.previous()).getAttribute("href")).toContain("property-details-section/#insurance-address-answer2"); }); it("When I edit an answer from the section summary page and click the Previous link, Then the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { @@ -50,7 +50,7 @@ describe("Summary Anchor Scrolling", () => { await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await $(InsuranceAddressPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); + await expect(browser).toHaveUrlContaining("property-details-section/#insurance-address-answer2"); }); it("When I edit an answer from the section summary page and click the Submit button, Then I am taken to the summary page and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { @@ -58,7 +58,7 @@ describe("Summary Anchor Scrolling", () => { await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await click(InsuranceAddressPage.submit()); - await expect(await browser.getUrl()).to.contain("property-details-section/#insurance-address-answer2"); + await expect(browser).toHaveUrlContaining("property-details-section/#insurance-address-answer2"); }); it("When I am on the final summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { @@ -68,7 +68,7 @@ describe("Summary Anchor Scrolling", () => { await click(HouseType.submit()); await click(HouseholdDetailsSummaryPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); - await expect(await $(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).to.contain( + await expect(await $(SubmitPage.insuranceAddressAnswer2Edit()).getAttribute("href")).toContain( "?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2", ); }); @@ -81,7 +81,7 @@ describe("Summary Anchor Scrolling", () => { await click(HouseholdDetailsSummaryPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswer2Edit()).click(); - await expect(await browser.getUrl()).to.contain("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); + await expect(browser).toHaveUrlContaining("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); }); }); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index cbb25a4c92..8fb84e08da 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -80,7 +80,7 @@ describe("Using supplementary data", () => { it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); await click(SalesBreakdownBlockPage.submit()); - await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).toBe( "Total value of sales from Bristol and London is calculated to be £444,000.00. Is this correct?", ); }); @@ -105,13 +105,13 @@ describe("Using supplementary data", () => { it("Given I have a section summary enabled, When I reach the section summary, Then I see it rendered correctly with supplementary data", async () => { await click(Section1InterstitialPage.submit()); - await expect(await $(Section1Page.emailQuestion()).getText()).toContain("Is contact@tesco.org still the correct contact email for Tesco?"); - await expect(await $(Section1Page.sameEmailAnswer()).getText()).toContain("Yes"); - await expect(await $(Section1Page.tradingQuestion()).getText()).toContain("When did Tesco begin trading?"); - await expect(await $(Section1Page.tradingAnswer()).getText()).toContain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).toContain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(Section1Page.salesBristolAnswer()).getText()).toContain("£333,000.00"); - await expect(await $(Section1Page.salesLondonAnswer()).getText()).toContain("£111,000.00"); + await expect(await $(Section1Page.emailQuestion()).getText()).toBe("Is contact@tesco.org still the correct contact email for Tesco?"); + await expect(await $(Section1Page.sameEmailAnswer()).getText()).toBe("Yes"); + await expect(await $(Section1Page.tradingQuestion()).getText()).toBe("When did Tesco begin trading?"); + await expect(await $(Section1Page.tradingAnswer()).getText()).toBe("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).toBe("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(Section1Page.salesBristolAnswer()).getText()).toBe("£333,000.00"); + await expect(await $(Section1Page.salesLondonAnswer()).getText()).toBe("£111,000.00"); }); it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { @@ -128,8 +128,8 @@ describe("Using supplementary data", () => { it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { await click(HubPage.submit()); - await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).toContain("Harry Potter"); - await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).toContain("Clark Kent"); + await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).toBe("Harry Potter"); + await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).toBe("Clark Kent"); await click(ListCollectorEmployeesPage.submit()); }); @@ -165,14 +165,14 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.month()).setValue(1); await $(LengthOfEmploymentPage.year()).setValue(1930); await click(LengthOfEmploymentPage.submit()); - await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).toContain("Enter a date after 26 November 1947"); + await expect(await $(LengthOfEmploymentPage.singleErrorLink()).getText()).toBe("Enter a date after 26 November 1947"); }); it("Given I have validation on the start date in the repeating section, When I enter a date after the incorporation date, Then I see that date on the summary page for the section", async () => { await $(LengthOfEmploymentPage.year()).setValue(1990); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Harry Potter start working for Tesco?"); - await expect(await $(Section4Page.employmentStart()).getText()).toContain("1 January 1990"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toBe("When did Harry Potter start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).toBe("1 January 1990"); }); it("Given I complete the repeating section for the other supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { @@ -183,39 +183,39 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.month()).setValue(6); await $(LengthOfEmploymentPage.year()).setValue(2011); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Clark Kent start working for Tesco?"); - await expect(await $(Section4Page.employmentStart()).getText()).toContain("5 June 2011"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toBe("When did Clark Kent start working for Tesco?"); + await expect(await $(Section4Page.employmentStart()).getText()).toBe("5 June 2011"); }); it("Given I move onto the dynamic list items, When I start a repeating section for a dynamic list item, Then I see static supplementary data correctly piped in and the same validation and summary", async () => { await click(Section4Page.submit()); await click(HubPage.submit()); await expect(await $(AdditionalLengthOfEmploymentPage.questionTitle()).getText()).toContain("When did Jane Doe start working for Tesco?"); - await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).toContain("Start date at Tesco"); + await expect(await $(AdditionalLengthOfEmploymentPage.additionalEmploymentStartLegend()).getText()).toBe("Start date at Tesco"); await $(AdditionalLengthOfEmploymentPage.day()).setValue(1); await $(AdditionalLengthOfEmploymentPage.month()).setValue(1); await $(AdditionalLengthOfEmploymentPage.year()).setValue(1930); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).toContain("Enter a date after 26 November 1947"); + await expect(await $(AdditionalLengthOfEmploymentPage.singleErrorLink()).getText()).toBe("Enter a date after 26 November 1947"); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2000); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toContain("When did Jane Doe start working for Tesco?"); - await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toContain("1 January 2000"); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toBe("When did Jane Doe start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toBe("1 January 2000"); await click(Section5Page.submit()); await click(HubPage.submit()); await $(AdditionalLengthOfEmploymentPage.day()).setValue(3); await $(AdditionalLengthOfEmploymentPage.month()).setValue(3); await $(AdditionalLengthOfEmploymentPage.year()).setValue(2010); await click(AdditionalLengthOfEmploymentPage.submit()); - await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toContain("When did John Smith start working for Tesco?"); - await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toContain("3 March 2010"); + await expect(await $(Section5Page.additionalLengthEmploymentQuestion()).getText()).toBe("When did John Smith start working for Tesco?"); + await expect(await $(Section5Page.additionalEmploymentStart()).getText()).toBe("3 March 2010"); await click(Section5Page.submit()); }); it("Given I have some repeating blocks with supplementary data, When I begin the section, Then I see the supplementary names rendered correctly", async () => { await click(HubPage.submit()); - await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).toContain("Articles and equipment for sports or outdoor games"); - await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).toContain("Kitchen Equipment"); + await expect(await $(ListCollectorProductsPage.listLabel(1)).getText()).toBe("Articles and equipment for sports or outdoor games"); + await expect(await $(ListCollectorProductsPage.listLabel(2)).getText()).toBe("Kitchen Equipment"); await click(ListCollectorProductsPage.submit()); }); @@ -230,10 +230,10 @@ describe("Using supplementary data", () => { await expect(await $("body").getHTML()).toContain( "
    • for skiing, water sports, golf, fishing', for skiing, water sports, golf, fishing, table tennis, PE, gymnastics, athletics
    • ", ); - await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toContain( + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toBe( "Volume of sales for Articles and equipment for sports or outdoor games", ); - await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toContain( + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toBe( "Total volume produced for Articles and equipment for sports or outdoor games", ); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(100); @@ -245,9 +245,9 @@ describe("Using supplementary data", () => { await click(ListCollectorProductsPage.submit()); await expect(await $("body").getText()).toContain("Include"); await expect(await $("body").getText()).toContain("pots and pans"); - await expect(await $("body").getText()).not.toContain("Exclude"); - await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toContain("Volume of sales for Kitchen Equipment"); - await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toContain("Total volume produced for Kitchen Equipment"); + await expect(await $("body").getText()).not.toBe("Exclude"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeSalesLabel()).getText()).toBe("Volume of sales for Kitchen Equipment"); + await expect(await $(ProductRepeatingBlock1Page.productVolumeTotalLabel()).getText()).toBe("Total volume produced for Kitchen Equipment"); await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(50); await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(300); await click(ProductRepeatingBlock1Page.submit()); @@ -255,8 +255,8 @@ describe("Using supplementary data", () => { it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { await click(ListCollectorProductsPage.submit()); - await expect(await browser.getUrl()).toContain(CalculatedSummaryVolumeSalesPage.pageName); - await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toContain( + await expect(browser).toHaveUrlContaining(CalculatedSummaryVolumeSalesPage.pageName); + await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); @@ -265,7 +265,7 @@ describe("Using supplementary data", () => { }); it("Given I have another calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { - await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?", ); assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); @@ -274,9 +274,9 @@ describe("Using supplementary data", () => { }); it("Given I have dynamic answers using a supplementary list, When I reach the dynamic answer page, Then I see the correct supplementary data in the answer labels", async () => { - await expect(await $$(DynamicProductsPage.labels())[0].getText()).toContain("Value of sales for Articles and equipment for sports or outdoor games"); - await expect(await $$(DynamicProductsPage.labels())[1].getText()).toContain("Value of sales for Kitchen Equipment"); - await expect(await $$(DynamicProductsPage.labels())[2].getText()).toContain("Value of sales from other categories"); + await expect(await $$(DynamicProductsPage.labels())[0].getText()).toBe("Value of sales for Articles and equipment for sports or outdoor games"); + await expect(await $$(DynamicProductsPage.labels())[1].getText()).toBe("Value of sales for Kitchen Equipment"); + await expect(await $$(DynamicProductsPage.labels())[2].getText()).toBe("Value of sales from other categories"); await $$(DynamicProductsPage.inputs())[0].setValue(110); await $$(DynamicProductsPage.inputs())[1].setValue(220); await $$(DynamicProductsPage.inputs())[2].setValue(330); @@ -284,7 +284,7 @@ describe("Using supplementary data", () => { }); it("Given I have a calculated summary of dynamic answers for a supplementary list, When I reach the calculated summary, Then I see the correct supplementary data in the title and labels", async () => { - await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).toContain( + await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?", ); assertSummaryItems([ @@ -297,7 +297,7 @@ describe("Using supplementary data", () => { }); it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { - await expect(await $$(summaryRowTitles)[0].getText()).toContain("Sales during the previous quarter"); + await expect(await $$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); assertSummaryItems([ "Articles and equipment for sports or outdoor games", "Volume of sales for Articles and equipment for sports or outdoor games", @@ -327,7 +327,7 @@ describe("Using supplementary data", () => { await expect(await $(HubPage.summaryRowState("section-4-2")).getText()).toBe("Not started"); await expect(await $(HubPage.summaryRowState("section-5-1")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowState("section-5-2")).getText()).toBe("Completed"); - await expect(await $("body").getText()).not.toContain("Clark Kent"); + await expect(await $("body").getText()).not.toBe("Clark Kent"); }); it("Given I now have a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { @@ -336,8 +336,8 @@ describe("Using supplementary data", () => { await $(LengthOfEmploymentPage.month()).setValue(10); await $(LengthOfEmploymentPage.year()).setValue(1999); await click(LengthOfEmploymentPage.submit()); - await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toContain("When did Bruce Wayne start working for Lidl?"); - await expect(await $(Section4Page.employmentStart()).getText()).toContain("10 October 1999"); + await expect(await $(Section4Page.lengthEmploymentQuestion()).getText()).toBe("When did Bruce Wayne start working for Lidl?"); + await expect(await $(Section4Page.employmentStart()).getText()).toBe("10 October 1999"); await click(Section4Page.submit()); }); @@ -348,19 +348,19 @@ describe("Using supplementary data", () => { assertSummaryTitles(["Company Details", "Employees", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); // Company details - await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).toContain("Is contact@lidl.org still the correct contact email for Lidl?"); - await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).toContain("No"); - await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).toContain("What is the new contact email for Lidl?"); - await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).toContain("new.contact@gmail.com"); - await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).toContain("When did Lidl begin trading?"); - await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).toContain("Sunday 30 November 1947"); - await expect(await $$(summaryRowTitles)[0].getText()).toContain("How much of the £555,000.00 total UK sales was from Bristol and London?"); - await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).toContain("£333,000.00"); - await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).toContain("£111,000.00"); + await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).toBe("Is contact@lidl.org still the correct contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.sameEmailAnswer()).getText()).toBe("No"); + await expect(await $(ViewSubmittedResponsePage.newEmailQuestion()).getText()).toBe("What is the new contact email for Lidl?"); + await expect(await $(ViewSubmittedResponsePage.newEmailAnswer()).getText()).toBe("new.contact@gmail.com"); + await expect(await $(ViewSubmittedResponsePage.tradingQuestion()).getText()).toBe("When did Lidl begin trading?"); + await expect(await $(ViewSubmittedResponsePage.tradingAnswer()).getText()).toBe("Sunday 30 November 1947"); + await expect(await $$(summaryRowTitles)[0].getText()).toBe("How much of the £555,000.00 total UK sales was from Bristol and London?"); + await expect(await $(ViewSubmittedResponsePage.salesBristolAnswer()).getText()).toBe("£333,000.00"); + await expect(await $(ViewSubmittedResponsePage.salesLondonAnswer()).getText()).toBe("£111,000.00"); // Additional Employees - await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).toContain("Do you have any additional employees to report on?"); - await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).toContain("Yes"); + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeQuestion()).getText()).toBe("Do you have any additional employees to report on?"); + await expect(await $(ViewSubmittedResponsePage.anyAdditionalEmployeeAnswer()).getText()).toBe("Yes"); await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[0].getText()).toBe("Jane Doe"); await expect(await $(ViewSubmittedResponsePage.additionalEmployeeReportingContent(1)).$$(summaryItems)[1].getText()).toBe("John Smith"); diff --git a/tests/functional/spec/features/units.spec.js b/tests/functional/spec/features/units.spec.js index d1202b6b0a..1b13079f3e 100644 --- a/tests/functional/spec/features/units.spec.js +++ b/tests/functional/spec/features/units.spec.js @@ -9,24 +9,24 @@ describe("Units", () => { it("Given we do not set a language code and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); await click(SetLengthUnitsBlockPage.submit()); - await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("hours"); - await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("years"); + await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).toBe("hours"); + await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).toBe("years"); await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); await click(SetDurationUnitsBlockPage.submit()); await click(SetAreaUnitsBlockPage.submit()); await click(SetVolumeUnitsBlockPage.submit()); await click(SetWeightUnitsBlockPage.submit()); - await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 hours"); - await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 years"); + await expect(await $(SubmitPage.durationHour()).getText()).toBe("6 hours"); + await expect(await $(SubmitPage.durationYear()).getText()).toBe("20 years"); }); it("Given we set a language code for welsh and run the questionnaire, when we enter values for durations, they should be displayed on the summary with their units.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "cy" }); await $(SetLengthUnitsBlockPage.submit()).scrollIntoView(); await click(SetLengthUnitsBlockPage.submit()); - await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).to.equal("awr"); - await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).to.equal("flynedd"); + await expect(await $(SetDurationUnitsBlockPage.durationHourUnit()).getText()).toBe("awr"); + await expect(await $(SetDurationUnitsBlockPage.durationYearUnit()).getText()).toBe("flynedd"); await $(SetDurationUnitsBlockPage.durationHour()).setValue(6); await $(SetDurationUnitsBlockPage.durationYear()).setValue(20); await $(SetDurationUnitsBlockPage.submit()).scrollIntoView(); @@ -34,16 +34,16 @@ describe("Units", () => { await click(SetAreaUnitsBlockPage.submit()); await click(SetVolumeUnitsBlockPage.submit()); await click(SetWeightUnitsBlockPage.submit()); - await expect(await $(SubmitPage.durationHour()).getText()).to.equal("6 awr"); - await expect(await $(SubmitPage.durationYear()).getText()).to.equal("20 mlynedd"); + await expect(await $(SubmitPage.durationHour()).getText()).toBe("6 awr"); + await expect(await $(SubmitPage.durationYear()).getText()).toBe("20 mlynedd"); }); it("Given we open a questionnaire with unit labels, when the label is highlighted by the tooltip, then the long unit label should be displayed.", async () => { await browser.openQuestionnaire("test_unit_patterns.json", { language: "en" }); - await expect(await $(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).to.equal("centimetres"); - await expect(await $(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).to.equal("metres"); - await expect(await $(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).to.equal("kilometres"); - await expect(await $(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).to.equal("miles"); + await expect(await $(SetLengthUnitsBlockPage.centimetresUnit()).getAttribute("title")).toBe("centimetres"); + await expect(await $(SetLengthUnitsBlockPage.metresUnit()).getAttribute("title")).toBe("metres"); + await expect(await $(SetLengthUnitsBlockPage.kilometresUnit()).getAttribute("title")).toBe("kilometres"); + await expect(await $(SetLengthUnitsBlockPage.milesUnit()).getAttribute("title")).toBe("miles"); }); it("Given we open a questionnaire with unit labels, when the weight unit label is highlighted by the tooltip, then the correct unit label should be displayed.", async () => { @@ -52,7 +52,7 @@ describe("Units", () => { await click(SetDurationUnitsBlockPage.submit()); await click(SetAreaUnitsBlockPage.submit()); await click(SetVolumeUnitsBlockPage.submit()); - await expect(await $("body").getText()).to.have.string("tonnes"); + await expect(await $("body").getText()).toContain("tonnes"); }); it("Given we open a questionnaire with unit inputs, when the unit allows a maximum of 6 decimal places, then the correct number of decimal places should be displayed on the summary.", async () => { @@ -67,10 +67,10 @@ describe("Units", () => { await $(SetVolumeUnitsBlockPage.megalitres()).setValue("1.10000"); await $(SetVolumeUnitsBlockPage.submit()).click(); await $(SetWeightUnitsBlockPage.submit()).click(); - await expect(await $(SubmitPage.cubicCentimetres()).getText()).to.equal("1.1 cm³"); - await expect(await $(SubmitPage.cubicMetres()).getText()).to.equal("1.12 m³"); - await expect(await $(SubmitPage.litres()).getText()).to.equal("1.123 l"); - await expect(await $(SubmitPage.hectolitres()).getText()).to.equal("1.1234 hl"); - await expect(await $(SubmitPage.megalitres()).getText()).to.equal("1.10000 Ml"); + await expect(await $(SubmitPage.cubicCentimetres()).getText()).toBe("1.1 cm³"); + await expect(await $(SubmitPage.cubicMetres()).getText()).toBe("1.12 m³"); + await expect(await $(SubmitPage.litres()).getText()).toBe("1.123 l"); + await expect(await $(SubmitPage.hectolitres()).getText()).toBe("1.1234 hl"); + await expect(await $(SubmitPage.megalitres()).getText()).toBe("1.10000 Ml"); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js index d7348c8936..89f1669f7c 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-mm-yyyy.spec.js @@ -14,8 +14,8 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); - await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).toBe(false); }); it("When I enter a year but no month, Then I should see only a single invalid date error", async () => { @@ -25,8 +25,8 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a valid date"); - await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a valid date"); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).toBe(false); }); it("When I enter a year of 0, Then I should see only a single invalid date error", async () => { @@ -36,8 +36,8 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter the year in a valid format. For example, 2023."); - await expect(await $(DateRangePage.errorNumber(2)).isExisting()).to.be.false; + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter the year in a valid format. For example, 2023."); + await expect(await $(DateRangePage.errorNumber(2)).isExisting()).toBe(false); }); it("When I enter a single dates that are too early/late, Then I should see a single validation errors", async () => { @@ -47,8 +47,8 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(6); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after November 2016"); - await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before June 2017"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a date after November 2016"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).toBe("Enter a date before June 2017"); }); it("When I enter a range too large, Then I should see a range validation error", async () => { @@ -58,7 +58,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(5); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 months"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period less than or equal to 3 months"); }); it("When I enter a range too small, Then I should see a range validation error", async () => { @@ -68,7 +68,7 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(1); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 months"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period greater than or equal to 2 months"); }); it("When I enter valid dates, Then I should see the summary page", async () => { @@ -79,14 +79,14 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeToMonth()).setValue(3); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to March 2017"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("January 2017 to March 2017"); // Max range await $(SubmitPage.dateRangeFromEdit()).click(); await $(DateRangePage.dateRangeToMonth()).setValue(4); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("January 2017 to April 2017"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("January 2017 to April 2017"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js index b95caaea73..2ac4a34260 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined-yyyy.spec.js @@ -12,22 +12,22 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat await $(DateRangePage.dateRangeFromYear()).setValue(2015); await $(DateRangePage.dateRangeToYear()).setValue(2021); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 2015"); - await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 2021"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a date after 2015"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).toBe("Enter a date before 2021"); }); it("When I enter a range too large, Then I should see a range validation error", async () => { await $(DateRangePage.dateRangeFromYear()).setValue(2016); await $(DateRangePage.dateRangeToYear()).setValue(2020); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 3 years"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period less than or equal to 3 years"); }); it("When I enter a range too small, Then I should see a range validation error", async () => { await $(DateRangePage.dateRangeFromYear()).setValue(2016); await $(DateRangePage.dateRangeToYear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 2 years"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period greater than or equal to 2 years"); }); it("When I enter valid dates, Then I should see the summary page", async () => { @@ -35,13 +35,13 @@ describe("Feature: Combined question level and single validation for MM-YYYY dat // Min range await $(DateRangePage.dateRangeToYear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2018"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("2016 to 2018"); // Max range await $(SubmitPage.dateRangeFromEdit()).click(); await $(DateRangePage.dateRangeToYear()).setValue(2019); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("2016 to 2019"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("2016 to 2019"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js index abbfef8650..c108a6b6ed 100644 --- a/tests/functional/spec/features/validation/date_validation/date-combined.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-combined.spec.js @@ -18,8 +18,8 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); - await expect(await $(DateRangePage.errorNumber(2)).getText()).to.contain("Enter a date before 22 February 2017"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a date after 12 December 2016"); + await expect(await $(DateRangePage.errorNumber(2)).getText()).toBe("Enter a date before 22 February 2017"); }); it("When I enter a range too large, Then I should see a range validation error", async () => { @@ -31,7 +31,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 50 days"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period less than or equal to 50 days"); }); it("When I enter a range too small, Then I should see a range validation error", async () => { @@ -43,7 +43,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 10 days"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period greater than or equal to 10 days"); }); it("When I enter valid dates, Then I should see the summary page", async () => { @@ -56,7 +56,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 11 January 2017"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("1 January 2017 to 11 January 2017"); // Max range await $(SubmitPage.dateRangeFromEdit()).click(); @@ -64,7 +64,7 @@ describe("Feature: Combined question level and single validation for dates", () await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2017); await click(DateRangePage.submit()); - await expect(await $(SubmitPage.dateRangeFrom()).getText()).to.contain("1 January 2017 to 20 February 2017"); + await expect(await $(SubmitPage.dateRangeFrom()).getText()).toBe("1 January 2017 to 20 February 2017"); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-range.spec.js b/tests/functional/spec/features/validation/date_validation/date-range.spec.js index 516d191034..2b7a2d8531 100644 --- a/tests/functional/spec/features/validation/date_validation/date-range.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-range.spec.js @@ -17,7 +17,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(3); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period less than or equal to 1 month, 20 days"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period less than or equal to 1 month, 20 days"); }); }); @@ -31,7 +31,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a reporting period greater than or equal to 23 days"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a reporting period greater than or equal to 23 days"); }); }); @@ -45,7 +45,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); }); @@ -61,7 +61,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a 'period to' date later than the 'period from' date"); }); }); @@ -75,7 +75,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(1); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await $(DateRangePage.errorNumber(1)).getText()).to.contain("Enter a 'period to' date later than the 'period from' date"); + await expect(await $(DateRangePage.errorNumber(1)).getText()).toBe("Enter a 'period to' date later than the 'period from' date"); }); }); @@ -89,7 +89,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-single.spec.js b/tests/functional/spec/features/validation/date_validation/date-single.spec.js index d33b6776b1..95d46e315b 100644 --- a/tests/functional/spec/features/validation/date_validation/date-single.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-single.spec.js @@ -19,7 +19,7 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeTomonth()).setValue(3); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); await click(DatePeriodPage.submit()); - await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date after 12 December 2016"); + await expect(await $(DatePeriodPage.errorNumber(1)).getText()).toBe("Enter a date after 12 December 2016"); }); }); @@ -34,7 +34,7 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeTomonth()).setValue(3); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); await click(DatePeriodPage.submit()); - await expect(await $(DatePeriodPage.errorNumber(1)).getText()).to.contain("Enter a date before 2 July 2017"); + await expect(await $(DatePeriodPage.errorNumber(1)).getText()).toBe("Enter a date before 2 July 2017"); }); }); @@ -49,7 +49,7 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeTomonth()).setValue(1); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); await click(DatePeriodPage.submit()); - await expect(await $(DatePeriodPage.errorNumber(2)).getText()).to.contain("Enter a date after 10 February 2018"); + await expect(await $(DatePeriodPage.errorNumber(2)).getText()).toBe("Enter a date after 10 February 2018"); }); }); @@ -64,7 +64,7 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeTomonth()).setValue(2); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); await click(DatePeriodPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index b64fa33f01..61f12cf4d9 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -21,65 +21,65 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ it("When I continue and enter numbers on dynamic and static answers page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { await $(TotalBlockPage.acceptCookies()).click(); await addTwoSupermarkets(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(33); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); await click(DynamicAnswerPage.submit()); - await expect(await $(DynamicAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 100"); + await expect(await $(DynamicAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 100"); }); }); describe("Given I add list items with hardcoded total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic and static answers page that add up to that total, Then I should be able to get to the subsequent question", async () => { await addTwoSupermarkets(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); await click(DynamicAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(TotalBlockOtherPage.pageName); + await expect(browser).toHaveUrlContaining(TotalBlockOtherPage.pageName); }); }); describe("Given I add list items with custom total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic answers only page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { await addTwoSupermarkets(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); await click(DynamicAnswerPage.submit()); await $(TotalBlockOtherPage.totalOther()).setValue(100); await click(TotalBlockOtherPage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicAnswerOnlyPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerOnlyPage.pageName); await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(0); await click(DynamicAnswerOnlyPage.submit()); - await expect(await $(DynamicAnswerOnlyPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £100.00"); + await expect(await $(DynamicAnswerOnlyPage.errorNumber(1)).getText()).toBe("Enter answers that add up to £100.00"); }); }); describe("Given I add list items with custom total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic answers only page that add up to that total, Then I should be able to get to the summary", async () => { await addTwoSupermarkets(); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await fillDynamicAnswers(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); }); }); describe("Given I add list items and fill all the dynamic answers", () => { it("When I continue and add another list item, Then I should be revisiting dynamic answers which should be updated to reflect the changes", async () => { await addTwoSupermarkets(); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(3); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await fillDynamicAnswers(); await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Morrisons"); await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(4); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); }); }); describe("Given I add list items and fill all the dynamic answers", () => { @@ -89,8 +89,8 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); - await expect(await browser.getUrl()).to.contain(DynamicAnswerPage.pageName); - await expect(await $$(DynamicAnswerPage.labels()).length).to.equal(2); + await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await expect(await $$(DynamicAnswerPage.labels()).length).toBe(2); }); }); describe("Given I add list items and fill all the dynamic answers", () => { @@ -102,22 +102,22 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await click(ListCollectorEditPage.submit()); await click(DynamicAnswerPage.submit()); await click(DynamicAnswerOnlyPage.submit()); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: 2000 }); - await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).to.equal("Percentage of shopping at Aldi"); + await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); }); }); describe("Given I add list items and fill all the dynamic answers", () => { it("When I journey backwards, Then I should be revisiting all the previous blocks", async () => { await addTwoSupermarkets(); await fillDynamicAnswers(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(TotalBlockOtherPage.previous()).click(); await $(DynamicAnswerPage.previous()).click(); await $(ListCollectorPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(DriverPage.pageName); + await expect(browser).toHaveUrlContaining(DriverPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal.spec.js b/tests/functional/spec/features/validation/sum/equal.spec.js index 1c6b6c1be4..a0067437a9 100644 --- a/tests/functional/spec/features/validation/sum/equal.spec.js +++ b/tests/functional/spec/features/validation/sum/equal.spec.js @@ -22,7 +22,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -37,14 +37,14 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await click(TotalAnswerPage.submit()); await browser.url(SubmitPage.url()); - await expect(await browser.getUrl()).to.contain(BreakdownAnswerPage.pageName); + await expect(browser).toHaveUrlContaining(BreakdownAnswerPage.pageName); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 15"); await answerAndSubmitBreakdownQuestion("6", "3", "3", "3"); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -54,7 +54,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("5", "", "", ""); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -64,7 +64,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 5"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js index a082d35993..cb4d045c4e 100644 --- a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js @@ -12,7 +12,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(TotalAnswerPage.total()).setValue("10"); await click(TotalAnswerPage.submit()); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.previous()).click(); await $(BreakdownAnswerPage.breakdown1()).setValue("0"); @@ -20,7 +20,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown3()).setValue("0"); await $(BreakdownAnswerPage.breakdown4()).setValue("0"); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); await $(SubmitPage.previous()).click(); await $(BreakdownAnswerPage.breakdown1()).setValue("1"); @@ -28,7 +28,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("4"); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -39,13 +39,13 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown1()).setValue("1"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 10"); await $(BreakdownAnswerPage.breakdown2()).setValue("2"); await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("5"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 10"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 10"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js index 2858f38dd4..f261fe04b5 100644 --- a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js @@ -16,7 +16,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -29,7 +29,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -42,7 +42,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -55,7 +55,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("4"); await $(BreakdownAnswerPage.breakdown4()).setValue("4"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 12"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to or are less than 12"); }); }); @@ -68,7 +68,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to or are less than 5"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to or are less than 5"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js index db733ec3a1..7928433900 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js @@ -43,28 +43,28 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitTotalEmployeesQuestion(10); await click(CompanySectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(companyOverviewSectionID)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(companyOverviewSectionID)).getText()).toBe("Completed"); }); it("When I am on the hub, Then the breakdown section should be marked as 'Not Started'", async () => { - await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Not started"); }); it("When I start the breakdown section and enter an answer that is not equal to the total for the turnover question, Then I should see a validation error", async () => { await click(HubPage.submit()); await answerAndSubmitTurnoverBreakdownQuestion(1000, 250, 250); - await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); + await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).toBe("Enter answers that add up to £1,000.00"); }); it("When I start the breakdown section and enter answers that are equal the total, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { await answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); await answerAndSubmitEmployeeBreakdownQuestion(5, 5); - await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Completed"); }); }); @@ -83,7 +83,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitEmployeeBreakdownQuestion(5, 5); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Completed"); }); it("When I change my answer for the total turnover question, Then the breakdown section should be marked as 'Partially completed'", async () => { @@ -92,35 +92,35 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitTotalTurnoverQuestion(1500); await click(CompanySectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Partially completed"); }); it("When I click 'Continue with section' on the breakdown section, Then I should be taken to the turnover breakdown question and my previous answers should be prefilled", async () => { await $(HubPage.summaryRowLink(breakdownSectionId)).click(); - await expect(await $(TurnoverBreakdownPage.turnoverBreakdown1()).getValue()).to.equal("500.00"); - await expect(await $(TurnoverBreakdownPage.turnoverBreakdown2()).getValue()).to.equal("250.00"); - await expect(await $(TurnoverBreakdownPage.turnoverBreakdown3()).getValue()).to.equal("250.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown1()).getValue()).toBe("500.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown2()).getValue()).toBe("250.00"); + await expect(await $(TurnoverBreakdownPage.turnoverBreakdown3()).getValue()).toBe("250.00"); }); it("When I submit the turnover breakdown question with no changes, Then I should see a validation error", async () => { await click(TurnoverBreakdownPage.submit()); - await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,500.00"); + await expect(await $(TurnoverBreakdownPage.errorNumber(1)).getText()).toBe("Enter answers that add up to £1,500.00"); }); it("When I update my answers to equal the new total turnover, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { await answerAndSubmitTurnoverBreakdownQuestion(500, 500, 500); - await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Completed"); }); it("When I submit the questionnaire, Then I should see the thank you page", async () => { await $(HubPage.submit()).scrollIntoView(); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index 3c1ee36200..3b60ec97ea 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -46,9 +46,9 @@ const answerAndSubmitSpendingBreakdownQuestion = async (breakdown1, breakdown2, }; const assertSpendingBreakdownAnswer = async (breakdown1, breakdown2, breakdown3) => { - await expect(await $(SpendingBreakdownPage.spendingBreakdown1()).getValue()).to.equal(breakdown1); - await expect(await $(SpendingBreakdownPage.spendingBreakdown2()).getValue()).to.equal(breakdown2); - await expect(await $(SpendingBreakdownPage.spendingBreakdown3()).getValue()).to.equal(breakdown3); + await expect(await $(SpendingBreakdownPage.spendingBreakdown1()).getValue()).toBe(breakdown1); + await expect(await $(SpendingBreakdownPage.spendingBreakdown2()).getValue()).toBe(breakdown2); + await expect(await $(SpendingBreakdownPage.spendingBreakdown3()).getValue()).toBe(breakdown3); }; const answerAndSubmitEntertainmentBreakdownQuestion = async (breakdown1, breakdown2, breakdown3) => { @@ -68,15 +68,15 @@ const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, cu it("When I submit the spending breakdown question with no changes, Then I should see a validation error", async () => { await click(SpendingBreakdownPage.submit()); - await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain(`Enter answers that add up to £${newTotal}`); + await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).toBe(`Enter answers that add up to £${newTotal}`); }); it("When I update my answers to equal the new total spending, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { await answerAndSubmitSpendingBreakdownQuestion(newTotal, 0, 0); - await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).toBe("Completed"); }); }; @@ -98,13 +98,13 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitEntertainmentSpendingQuestion(500); await click(HouseholdOverviewSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(householderSectionId)).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState(householdOverviewSectionId)).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(householderSectionId)).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState(householdOverviewSectionId)).getText()).toBe("Completed"); }); it("When I am on the hub, Then the two repeating breakdown sections should be marked as 'Not Started'", async () => { - await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Not started"); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Not started"); }); it("When I start a repeating section and don't skip the calculated question, and enter an answer that is not equal to the total for the spending question, Then I should see a validation error", async () => { @@ -114,17 +114,17 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitSpendingBreakdownQuestion(500, 500, 500); - await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to £1,000.00"); + await expect(await $(SpendingBreakdownPage.errorNumber(1)).getText()).toBe("Enter answers that add up to £1,000.00"); }); it("When I enter an answer that is equal to the total for the spending question, Then I should be able to get to the section summary and the repeating section should be marked as 'Completed'", async () => { await answerAndSubmitSpendingBreakdownQuestion(500, 250, 250); await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); - await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Completed"); }); it("When I start another repeating section and answer 'No' to the driving question, Then I should not have to answer the breakdown question and the section is marked as 'Completed'", async () => { @@ -132,10 +132,10 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(BreakdownDrivingPage.no()).click(); await click(BreakdownDrivingPage.submit()); - await expect(await browser.getUrl()).to.contain(BreakdownSectionSummary.pageName); + await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Completed"); }); it("When I change my answer for the total spending question, Then the first repeating section should be marked as 'Partially completed' and section repeating section should stay as 'Completed'", async () => { @@ -144,11 +144,11 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitTotalSpendingQuestion(1500); await click(HouseholdOverviewSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Partially completed"); // The 2nd repeating section skipped the breakdown question, therefore progress should updated for sections that have // calculated questions on the path. - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Completed"); }); assertRepeatingSectionOnChange(1, "500.00", "250.00", "250.00", "1,500.00"); @@ -162,7 +162,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitSpendingBreakdownQuestion(1000, 500, 0); await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); await click(BreakdownSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Completed"); }); it("When I change my answer for the total spending question, Then both repeating section should be marked as 'Partially completed'", async () => { @@ -171,10 +171,10 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitTotalSpendingQuestion(2500); await click(HouseholdOverviewSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Partially completed"); // The 2nd repeating section is now on the path, therefore, its status should have been updated. - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Partially completed"); }); assertRepeatingSectionOnChange(1, "1500.00", "0.00", "0.00", "2,500.00"); @@ -184,18 +184,18 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(HubPage.summaryRowLink(householdOverviewSectionId)).click(); await $(HouseholdOverviewSectionSummary.totalSpendingAnswerEdit()).click(); - await expect(await $(TotalSpendingPage.totalSpending()).getValue()).to.equal("2500.00"); + await expect(await $(TotalSpendingPage.totalSpending()).getValue()).toBe("2500.00"); await click(TotalSpendingPage.submit()); await click(HouseholdOverviewSectionSummary.submit()); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Completed"); }); it("When I submit the questionnaire, Then I should see the thank you page", async () => { await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/less_than.spec.js b/tests/functional/spec/features/validation/sum/less_than.spec.js index a55ffb08ad..be821d34d9 100644 --- a/tests/functional/spec/features/validation/sum/less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/less_than.spec.js @@ -16,7 +16,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -29,7 +29,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); await click(BreakdownAnswerPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -42,7 +42,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £12.00"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to less than £12.00"); }); }); @@ -55,7 +55,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to less than £5.00"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to less than £5.00"); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js index 95ce212095..ea2d7c8697 100644 --- a/tests/functional/spec/features/validation/sum/value_source.spec.js +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -40,7 +40,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(await browser.getUrl()).to.contain(TotalPlaybackPage.pageName); + await expect(browser).toHaveUrlContaining(TotalPlaybackPage.pageName); }); }); @@ -51,7 +51,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -68,13 +68,13 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await click(BreakdownAnswerPage.submit()); - await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(true); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 15"); await answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -91,13 +91,13 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerAndSubmitBreakdownQuestion("0", "3", "3", "3"); - await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(BreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(true); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 15"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 15"); await answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -116,15 +116,15 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await click(SecondBreakdownAnswerPage.submit()); - await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.true; + await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(true); - await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 9"); + await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 9"); await answerAndSubmitSecondBreakdownQuestion("5", "4", "0", "0"); - await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).to.be.false; + await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(false); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); }); @@ -135,7 +135,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 5"); + await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 5"); }); }); @@ -146,7 +146,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["2", "1", "1", "1"], ["3", "3", "3", "3"]); - await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).to.contain("Enter answers that add up to 3"); + await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 3"); }); }); }); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 73aafa0250..c30d9ab826 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -30,32 +30,32 @@ describe("View Submitted Response", () => { await $(AddressBlockPage.answer()).setValue("NP10 8XG"); await click(AddressBlockPage.submit()); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); + await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); }); it("Given I have completed a questionnaire with view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { - await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.false; - await expect(await $(ViewSubmittedResponsePage.printButton()).isDisplayed()).to.be.true; - await expect(await $(ViewSubmittedResponsePage.heading()).getText()).to.equal("Answers submitted for Apple"); - await expect(await $(ViewSubmittedResponsePage.metadataTerm(1)).getText()).to.equal("Submitted on:"); - await expect(await $(ViewSubmittedResponsePage.metadataTerm(2)).getText()).to.equal("Submission reference:"); - await expect(await $(ViewSubmittedResponsePage.personalDetailsGroupTitle()).getText()).to.equal("Personal Details"); - await expect(await $(ViewSubmittedResponsePage.nameQuestion()).getText()).to.equal("What is your name?"); - await expect(await $(ViewSubmittedResponsePage.nameAnswer()).getText()).to.equal("John Smith"); - await expect(await $(ViewSubmittedResponsePage.addressDetailsGroupTitle()).getText()).to.equal("Address Details"); - await expect(await $(ViewSubmittedResponsePage.addressQuestion()).getText()).to.equal("What is your address?"); - await expect(await $(ViewSubmittedResponsePage.addressAnswer()).getText()).to.equal("NP10 8XG"); + await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).toBe(false); + await expect(await $(ViewSubmittedResponsePage.printButton()).isDisplayed()).toBe(true); + await expect(await $(ViewSubmittedResponsePage.heading()).getText()).toBe("Answers submitted for Apple"); + await expect(await $(ViewSubmittedResponsePage.metadataTerm(1)).getText()).toBe("Submitted on:"); + await expect(await $(ViewSubmittedResponsePage.metadataTerm(2)).getText()).toBe("Submission reference:"); + await expect(await $(ViewSubmittedResponsePage.personalDetailsGroupTitle()).getText()).toBe("Personal Details"); + await expect(await $(ViewSubmittedResponsePage.nameQuestion()).getText()).toBe("What is your name?"); + await expect(await $(ViewSubmittedResponsePage.nameAnswer()).getText()).toBe("John Smith"); + await expect(await $(ViewSubmittedResponsePage.addressDetailsGroupTitle()).getText()).toBe("Address Details"); + await expect(await $(ViewSubmittedResponsePage.addressQuestion()).getText()).toBe("What is your address?"); + await expect(await $(ViewSubmittedResponsePage.addressAnswer()).getText()).toBe("NP10 8XG"); }); describe("Given I am on the view submitted response page and I submitted over 45 minutes ago", () => { it("When I click the Download as PDF button, Then I should be redirected to a page informing me that I can no longer view or get a copy of my answers", async () => { await browser.pause(40000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) await $(ViewSubmittedResponsePage.downloadButton()).click(); - await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).to.be.true; - await expect(await $(ViewSubmittedResponsePage.informationPanel()).getHTML()).to.contain( + await expect(await $(ViewSubmittedResponsePage.informationPanel()).isDisplayed()).toBe(true); + await expect(await $(ViewSubmittedResponsePage.informationPanel()).getHTML()).toContain( "For security, you can no longer view or get a copy of your answers", ); }); @@ -123,32 +123,32 @@ describe("View Submitted Response Summary Page With Repeating Sections", () => { await click(SectionSummarySectionTwo.submit()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); + await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); }); it("Given I have completed a questionnaire with a repeating section and view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { - await expect(await $(ViewSubmittedResponseRepeatingPage.informationPanel()).isDisplayed()).to.be.false; - await expect(await $(ViewSubmittedResponseRepeatingPage.printButton()).isDisplayed()).to.be.true; - await expect(await $(ViewSubmittedResponseRepeatingPage.heading()).getText()).to.equal("Answers submitted for Apple"); - await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(1)).getText()).to.equal("Submitted on:"); - await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(2)).getText()).to.equal("Submission reference:"); - await expect(await $(ViewSubmittedResponseRepeatingPage.personalDetailsGroupTitle()).getText()).to.equal("Personal Details"); - await expect(await $(ViewSubmittedResponseRepeatingPage.nameQuestion()).getText()).to.equal("What is your name?"); - await expect(await $(ViewSubmittedResponseRepeatingPage.nameAnswer()).getText()).to.equal("John Smith"); - await expect(await $(ViewSubmittedResponseRepeatingPage.addressDetailsGroupTitle()).getText()).to.equal("Address Details"); - await expect(await $(ViewSubmittedResponseRepeatingPage.addressQuestion()).getText()).to.equal("What is your address?"); - await expect(await $(ViewSubmittedResponseRepeatingPage.addressAnswer()).getText()).to.equal("NP10 8XG"); - await expect(await $("body").getHTML()).to.contain("Marcus Twin"); - await expect(await $(firstGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); - await expect(await $(firstGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("40 - calculated summary answer (current section)"); - await expect(await $("body").getHTML()).to.contain("How much did Marcus Twin spend on fruit?"); - await expect(await $(firstGroup).$$(skippableRepeatingSectionAnswer)[0].getText()).to.equal("£100"); - await expect(await $("body").getHTML()).to.contain("John Doe"); - await expect(await $(secondGroup).$$(groupTitle)[0].getText()).to.equal("Calculated Summary Group"); - await expect(await $(secondGroup).$$(repeatingSectionAnswer)[0].getText()).to.equal("80 - calculated summary answer (current section)"); - await expect(await $("body").getHTML()).to.not.contain("How much did John Doe spend on fruit?"); + await expect(await $(ViewSubmittedResponseRepeatingPage.informationPanel()).isDisplayed()).toBe(false); + await expect(await $(ViewSubmittedResponseRepeatingPage.printButton()).isDisplayed()).toBe(true); + await expect(await $(ViewSubmittedResponseRepeatingPage.heading()).getText()).toBe("Answers submitted for Apple"); + await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(1)).getText()).toBe("Submitted on:"); + await expect(await $(ViewSubmittedResponseRepeatingPage.metadataTerm(2)).getText()).toBe("Submission reference:"); + await expect(await $(ViewSubmittedResponseRepeatingPage.personalDetailsGroupTitle()).getText()).toBe("Personal Details"); + await expect(await $(ViewSubmittedResponseRepeatingPage.nameQuestion()).getText()).toBe("What is your name?"); + await expect(await $(ViewSubmittedResponseRepeatingPage.nameAnswer()).getText()).toBe("John Smith"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressDetailsGroupTitle()).getText()).toBe("Address Details"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressQuestion()).getText()).toBe("What is your address?"); + await expect(await $(ViewSubmittedResponseRepeatingPage.addressAnswer()).getText()).toBe("NP10 8XG"); + await expect(await $("body").getHTML()).toContain("Marcus Twin"); + await expect(await $(firstGroup).$$(groupTitle)[0].getText()).toBe("Calculated Summary Group"); + await expect(await $(firstGroup).$$(repeatingSectionAnswer)[0].getText()).toBe("40 - calculated summary answer (current section)"); + await expect(await $("body").getHTML()).toContain("How much did Marcus Twin spend on fruit?"); + await expect(await $(firstGroup).$$(skippableRepeatingSectionAnswer)[0].getText()).toBe("£100"); + await expect(await $("body").getHTML()).toContain("John Doe"); + await expect(await $(secondGroup).$$(groupTitle)[0].getText()).toBe("Calculated Summary Group"); + await expect(await $(secondGroup).$$(repeatingSectionAnswer)[0].getText()).toBe("80 - calculated summary answer (current section)"); + await expect(await $("body").getHTML()).not.toContain("How much did John Doe spend on fruit?"); }); }); diff --git a/tests/functional/spec/feedback.spec.js b/tests/functional/spec/feedback.spec.js index 1ddf12ed95..02fcb667f0 100644 --- a/tests/functional/spec/feedback.spec.js +++ b/tests/functional/spec/feedback.spec.js @@ -15,20 +15,20 @@ describe("Feedback", () => { }); it("When I view the thank you page, Then I can see the feedback call to action", async () => { - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.feedback()).getText()).to.contain("What do you think about this service?"); - await expect(await $(ThankYouPage.feedbackLink()).getText()).to.equal("Give feedback"); - await expect(await $(ThankYouPage.feedbackLink()).getAttribute("href")).to.contain("/submitted/feedback/send"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.feedback()).getText()).toContain("What do you think about this service?"); + await expect(await $(ThankYouPage.feedbackLink()).getText()).toBe("Give feedback"); + await expect(await $(ThankYouPage.feedbackLink()).getAttribute("href")).toContain("/submitted/feedback/send"); }); it("When I try to submit without providing feedback, then I stay on the feedback page and get an error message", async () => { await browser.url(FeedbackPage.url()); - await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); - await expect(await $(FeedbackPage.feedbackTitle()).getText()).to.contain("Give feedback about this service"); + await expect(browser).toHaveUrlContaining(FeedbackPage.pageName); + await expect(await $(FeedbackPage.feedbackTitle()).getText()).toBe("Give feedback about this service"); await click(FeedbackPage.submit()); - await expect(await browser.getUrl()).to.contain(FeedbackPage.pageName); - await expect(await $(FeedbackPage.errorPanel()).isExisting()).to.be.true; - await expect(await $(FeedbackPage.errorPanel()).getText()).to.contain( + await expect(browser).toHaveUrlContaining(FeedbackPage.pageName); + await expect(await $(FeedbackPage.errorPanel()).isExisting()).toBe(true); + await expect(await $(FeedbackPage.errorPanel()).getText()).toBe( "There are 2 problems with your feedback\nSelect what your feedback is about\nEnter your feedback", ); }); @@ -38,8 +38,8 @@ describe("Feedback", () => { await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); await $(FeedbackPage.feedbackText()).setValue("Well done!"); await click(FeedbackPage.submit()); - await expect(await browser.getUrl()).to.contain(FeedbackSentPage.pageName); - await expect(await $(FeedbackSentPage.feedbackThankYouText()).getText()).to.contain("Thank you for your feedback"); + await expect(browser).toHaveUrlContaining(FeedbackSentPage.pageName); + await expect(await $(FeedbackSentPage.feedbackThankYouText()).getText()).toBe("Thank you for your feedback"); }); it("When I click the done button on the feedback sent page, Then I am taken to the thank you page", async () => { @@ -48,8 +48,8 @@ describe("Feedback", () => { await $(FeedbackPage.feedbackText()).setValue("Well done!"); await click(FeedbackPage.submit()); await $(FeedbackSentPage.doneButton()).click(); - await expect(await browser.getUrl()).to.contain("thank-you"); - await expect(await $(ThankYouPage.title()).getText()).to.contain("Thank you for completing the Feedback test schema"); + await expect(browser).toHaveUrlContaining("thank-you"); + await expect(await $(ThankYouPage.title()).getText()).toBe("Thank you for completing the Feedback test schema"); }); }); }); diff --git a/tests/functional/spec/interstitial_definition.spec.js b/tests/functional/spec/interstitial_definition.spec.js index 8cde0becf8..2f40919e90 100644 --- a/tests/functional/spec/interstitial_definition.spec.js +++ b/tests/functional/spec/interstitial_definition.spec.js @@ -7,15 +7,15 @@ describe("Component: Interstitial Definition", () => { }); it("When there is a definition on an interstitial, then the page is displayed correctly", async () => { - await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).toBe(true); + await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).toBe(""); }); it("When I click on a definition title, the content is displayed for just that definition", async () => { await $(InterstitialDefinitionPage.definitionTitle()).click(); - await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).to.be.true; - await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).to.equal("In a way that accomplishes a desired aim or result"); + await expect(await $(InterstitialDefinitionPage.definitionTitle()).isDisplayed()).toBe(true); + await expect(await $(InterstitialDefinitionPage.definitionContent()).getText()).toBe("In a way that accomplishes a desired aim or result"); }); }); }); diff --git a/tests/functional/spec/interviewer_note.spec.js b/tests/functional/spec/interviewer_note.spec.js index 4bb8f4201b..ecb36a8520 100644 --- a/tests/functional/spec/interviewer_note.spec.js +++ b/tests/functional/spec/interviewer_note.spec.js @@ -10,20 +10,20 @@ describe("Given I start a survey", () => { }); it("When I view interstitial page and the interviewer_note is set to true then I should be able to see interviewer note", async () => { - await expect(await $(InitialInterstitialPage.questionText()).getText()).to.contain("Interviewer note"); + await expect(await $(InitialInterstitialPage.questionText()).getText()).toContain("Interviewer note"); }); it("When I view question page and the interviewer_note is set to true then I should be able to see interviewer note", async () => { await click(InitialInterstitialPage.submit()); - await expect(await $(FavouriteTeamPage.questionText()).getText()).to.contain("Interviewer note"); + await expect(await $(FavouriteTeamPage.questionText()).getText()).toContain("Interviewer note"); }); it("When I view question page and the interviewer_note is set to false then I should not be able to see interviewer note", async () => { await $(FavouriteTeamPage.favouriteTeam()).setValue("TNS"); await click(FavouriteTeamPage.submit()); - await expect(await $(ConfirmPage.questionText()).getText()).to.not.contain("Interviewer note"); + await expect(await $(ConfirmPage.questionText()).getText()).not.toBe("Interviewer note"); }); it("When I view interstitial page and the interviewer_note is not set then I should not be able to see interviewer note", async () => { await $(ConfirmPage.yes()).click(); await click(ConfirmPage.submit()); - await expect(await $(FinalInterstitialPage.questionText()).getText()).to.not.contain("Interviewer note"); + await expect(await $(FinalInterstitialPage.questionText()).getText()).not.toBe("Interviewer note"); }); }); diff --git a/tests/functional/spec/introduction.spec.js b/tests/functional/spec/introduction.spec.js index 1c7dd64431..80b5653493 100644 --- a/tests/functional/spec/introduction.spec.js +++ b/tests/functional/spec/introduction.spec.js @@ -8,21 +8,21 @@ describe("Introduction page", () => { it("Given I start a survey, When I view the introduction page, Then I should be able to see introduction information", async () => { await browser.openQuestionnaire(introductionSchema); - await expect(await $(IntroductionPage.useOfData()).getText()).to.contain("How we use your data"); - await expect(await $(IntroductionPage.useOfInformation()).getText()).to.contain( + await expect(await $(IntroductionPage.useOfData()).getText()).toContain("How we use your data"); + await expect(await $(IntroductionPage.useOfInformation()).getText()).toContain( "Data should relate to all sites in England, Scotland and Wales unless otherwise stated.", ); - await expect(await $(IntroductionPage.legalResponse()).getText()).to.contain("Your response is legally required"); - await expect(await $(IntroductionPage.legalBasis()).getText()).to.contain("Notice is given under section 999 of the Test Act 2000"); - await expect(await $(IntroductionPage.introDescription()).getText()).to.contain( + await expect(await $(IntroductionPage.legalResponse()).getText()).toBe("Your response is legally required"); + await expect(await $(IntroductionPage.legalBasis()).getText()).toBe("Notice is given under section 999 of the Test Act 2000"); + await expect(await $(IntroductionPage.introDescription()).getText()).toBe( "To take part, all you need to do is check that you have the information you need to answer the survey questions.", ); }); it("Given I start a survey with introduction guidance set, When I view the introduction page, Then I should be able to see introduction guidance", async () => { await browser.openQuestionnaire(introductionSchema); - await expect(await $(IntroductionPage.guidancePanel(1)).isDisplayed()).to.be.true; - await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain("Coronavirus (COVID-19) guidance"); - await expect(await $(IntroductionPage.guidancePanel(1)).getText()).to.contain( + await expect(await $(IntroductionPage.guidancePanel(1)).isDisplayed()).toBe(true); + await expect(await $(IntroductionPage.guidancePanel(1)).getText()).toContain("Coronavirus (COVID-19) guidance"); + await expect(await $(IntroductionPage.guidancePanel(1)).getText()).toContain( "Explain your figures in the comment section to minimise us contacting you and to help us tell an industry story", ); }); diff --git a/tests/functional/spec/language_code.spec.js b/tests/functional/spec/language_code.spec.js index 79d946759f..8b91be4da7 100644 --- a/tests/functional/spec/language_code.spec.js +++ b/tests/functional/spec/language_code.spec.js @@ -102,7 +102,7 @@ describe("Language Code", () => { language: "cy", }); await click(HubPage.submit()); - await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + await expect(await $(NamePage.questionText()).getText()).toBe("Rhowch enw"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); @@ -118,13 +118,13 @@ describe("Language Code", () => { await $(ConfirmNumberOfPeoplePage.yes()).click(); await click(ConfirmNumberOfPeoplePage.submit()); - await expect(await $(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); - await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); - await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); - await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); + await expect(await $(HubPage.heading()).getText()).toBe("Teitl cyflwyno"); + await expect(await $(HubPage.warning()).getText()).toBe("Rhybudd cyflwyno"); + await expect(await $(HubPage.guidance()).getText()).toBe("Canllawiau cyflwyno"); + await expect(await $(HubPage.submit()).getText()).toBe("Botwm cyflwyno"); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); it("Given a launch language of English, I should see English text", async () => { @@ -133,7 +133,7 @@ describe("Language Code", () => { }); await click(HubPage.submit()); - await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await expect(await $(NamePage.questionText()).getText()).toBe("Please enter a name"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); await click(NamePage.submit()); @@ -148,13 +148,13 @@ describe("Language Code", () => { await $(ConfirmNumberOfPeoplePage.yes()).click(); await click(ConfirmNumberOfPeoplePage.submit()); - await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); - await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); - await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); - await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); + await expect(await $(HubPage.heading()).getText()).toBe("Submission title"); + await expect(await $(HubPage.warning()).getText()).toBe("Submission warning"); + await expect(await $(HubPage.guidance()).getText()).toBe("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).toBe("Submission button"); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); it("Given a launch language of English, When I select Cymraeg, Then the language should be switched to Welsh", async () => { @@ -163,11 +163,11 @@ describe("Language Code", () => { }); await click(HubPage.submit()); - await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); - await expect(await $("header").getText()).to.contain("Test Language Survey"); + await expect(await $(NamePage.questionText()).getText()).toBe("Please enter a name"); + await expect(await $("header").getText()).toContain("Test Language Survey"); await $(NamePage.switchLanguage("cy")).click(); - await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); - await expect(await $("header").getText()).to.contain("Arolwg Iaith Prawf"); + await expect(await $(NamePage.questionText()).getText()).toBe("Rhowch enw"); + await expect(await $("header").getText()).toContain("Arolwg Iaith Prawf"); await $(NamePage.switchLanguage("en")).click(); await $(NamePage.firstName()).setValue("Catherine"); @@ -184,18 +184,18 @@ describe("Language Code", () => { await $(ConfirmNumberOfPeoplePage.yes()).click(); await click(ConfirmNumberOfPeoplePage.submit()); - await expect(await $(HubPage.heading()).getText()).to.contain("Submission title"); - await expect(await $(HubPage.warning()).getText()).to.contain("Submission warning"); - await expect(await $(HubPage.guidance()).getText()).to.contain("Submission guidance"); - await expect(await $(HubPage.submit()).getText()).to.contain("Submission button"); + await expect(await $(HubPage.heading()).getText()).toBe("Submission title"); + await expect(await $(HubPage.warning()).getText()).toBe("Submission warning"); + await expect(await $(HubPage.guidance()).getText()).toBe("Submission guidance"); + await expect(await $(HubPage.submit()).getText()).toBe("Submission button"); await $(HubPage.switchLanguage("cy")).click(); - await expect(await $(HubPage.heading()).getText()).to.contain("Teitl cyflwyno"); - await expect(await $(HubPage.warning()).getText()).to.contain("Rhybudd cyflwyno"); - await expect(await $(HubPage.guidance()).getText()).to.contain("Canllawiau cyflwyno"); - await expect(await $(HubPage.submit()).getText()).to.contain("Botwm cyflwyno"); + await expect(await $(HubPage.heading()).getText()).toBe("Teitl cyflwyno"); + await expect(await $(HubPage.warning()).getText()).toBe("Rhybudd cyflwyno"); + await expect(await $(HubPage.guidance()).getText()).toBe("Canllawiau cyflwyno"); + await expect(await $(HubPage.submit()).getText()).toBe("Botwm cyflwyno"); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); it("Given a launch language of Welsh, When I select English, Then the language should be switched to English", async () => { @@ -204,9 +204,9 @@ describe("Language Code", () => { }); await click(HubPage.submit()); - await expect(await $(NamePage.questionText()).getText()).to.contain("Rhowch enw"); + await expect(await $(NamePage.questionText()).getText()).toBe("Rhowch enw"); await $(NamePage.switchLanguage("en")).click(); - await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await expect(await $(NamePage.questionText()).getText()).toBe("Please enter a name"); }); describe("Given a launch language of English and a question with plural forms, When I select switch languages, Then the plural forms are displayed correctly for the chosen language", () => { @@ -219,7 +219,7 @@ describe("Language Code", () => { }); await click(HubPage.submit()); - await expect(await $(NamePage.questionText()).getText()).to.contain("Please enter a name"); + await expect(await $(NamePage.questionText()).getText()).toBe("Please enter a name"); await $(NamePage.firstName()).setValue("Catherine"); await $(NamePage.lastName()).setValue("Zeta-Jones"); await click(NamePage.submit()); @@ -232,13 +232,13 @@ describe("Language Code", () => { await $(NumberOfPeoplePage.numberOfPeople()).setValue(numberOfPeople); await click(NumberOfPeoplePage.submit()); - await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.en); - await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.en); + await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).toEqual(dataSet.question_title.en); + await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).toEqual(dataSet.answer.en); await $(ConfirmNumberOfPeoplePage.switchLanguage("cy")).click(); - await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).to.contain(dataSet.question_title.cy); - await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).to.contain(dataSet.answer.cy); + await expect(await $(ConfirmNumberOfPeoplePage.questionText()).getText()).toEqual(dataSet.question_title.cy); + await expect(await $(ConfirmNumberOfPeoplePage.yesLabel()).getText()).toEqual(dataSet.answer.cy); await $(ConfirmNumberOfPeoplePage.yes()).click(); await click(ConfirmNumberOfPeoplePage.submit()); diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index 977b913957..a62242ffdb 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -61,7 +61,7 @@ describe("List Collector", () => { await $(ListCollectorEditPage.firstName()).setValue("Mark"); await $(ListCollectorEditPage.lastName()).setValue("Twain"); await click(ListCollectorEditPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twain"); }); it("The questionnaire allows me to remove the first person (Mark Twain) from the summary", async () => { @@ -71,14 +71,14 @@ describe("List Collector", () => { }); it("The collector summary does not show Mark Twain anymore.", async () => { - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.not.have.string("Mark Twain"); - await expect(await $(ListCollectorPage.listLabel(3)).getText()).to.equal("Suzy Clemens"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).not.toBe("Mark Twain"); + await expect(await $(ListCollectorPage.listLabel(3)).getText()).toBe("Suzy Clemens"); }); it("The questionnaire allows more people to be added", async () => { await $(ListCollectorPage.yes()).click(); await click(ListCollectorPage.submit()); - await expect(await $(ListCollectorAddPage.questionText()).getText()).to.contain("What is the name of the person"); + await expect(await $(ListCollectorAddPage.questionText()).getText()).toBe("What is the name of the person?"); await $(ListCollectorAddPage.firstName()).setValue("Clara"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); @@ -95,7 +95,7 @@ describe("List Collector", () => { await $(ListCollectorAddPage.firstName()).setValue("Someone"); await $(ListCollectorAddPage.lastName()).setValue("Else"); await $(ListCollectorAddPage.cancelAndReturn()).click(); - await expect(await browser.getUrl()).to.contain(ListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(ListCollectorPage.pageName); }); it("The user is returned to the list collector when the cancel link is clicked on the edit page.", async () => { @@ -106,7 +106,7 @@ describe("List Collector", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.listEditLink(1)).click(); await $(ListCollectorEditPage.cancelAndReturn()).click(); - await expect(await browser.getUrl()).to.contain(ListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(ListCollectorPage.pageName); }); it("The collector shows everyone on the summary", async () => { @@ -117,12 +117,12 @@ describe("List Collector", () => { it("When No is answered on the list collector the user sees an interstitial", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(NextInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(NextInterstitialPage.pageName); await click(NextInterstitialPage.submit()); }); it("After the interstitial, the user should be on the second list collector page", async () => { - await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); }); it("The collector still shows the same list of people on the summary", async () => { @@ -136,7 +136,7 @@ describe("List Collector", () => { await $(AnotherListCollectorAddPage.firstName()).setValue("Someone"); await $(AnotherListCollectorAddPage.lastName()).setValue("Else"); await click(AnotherListCollectorAddPage.submit()); - await expect(await $(AnotherListCollectorPage.listLabel(6)).getText()).to.equal("Someone Else"); + await expect(await $(AnotherListCollectorPage.listLabel(6)).getText()).toBe("Someone Else"); }); it("The collector allows the user to remove a person again", async () => { @@ -148,26 +148,26 @@ describe("List Collector", () => { it("The user is returned to the list collector when the previous link is clicked.", async () => { await $(AnotherListCollectorPage.listRemoveLink(1)).click(); await $(AnotherListCollectorRemovePage.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); await $(AnotherListCollectorPage.listEditLink(1)).click(); await $(AnotherListCollectorEditPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); await $(AnotherListCollectorPage.yes()).click(); await click(AnotherListCollectorPage.submit()); await $(AnotherListCollectorEditPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(AnotherListCollectorPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); }); it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(AnotherListCollectorPage.no()).click(); await click(AnotherListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); }); it("The questionnaire allows submission", async () => { await click(SummaryPage.submit()); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); }); @@ -197,9 +197,9 @@ describe("List Collector", () => { }); it("The section summary should display contents of the list collector", async () => { - await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Marcus Twin (You)"); - await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(2)).getText()).to.contain("Samuel Clemens"); - await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(1)).getText()).to.contain("Olivia Clemens"); + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).toBe("Marcus Twin (You)"); + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(2)).getText()).toBe("Samuel Clemens"); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(1)).getText()).toBe("Olivia Clemens"); }); it("When the user adds an item to the list, They should return to the section summary and it should display the updated list", async () => { @@ -209,14 +209,14 @@ describe("List Collector", () => { await click(VisitorListCollectorAddPage.submit()); await $(VisitorListCollectorPage.no()).click(); await click(VisitorListCollectorPage.submit()); - await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).to.contain("Joe Bloggs"); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).getText()).toBe("Joe Bloggs"); }); it("When the user removes an item from the list, They should return to the section summary and it should display the updated list", async () => { await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); await $(SectionSummaryListCollectorRemovePage.yes()).click(); await click(SectionSummaryListCollectorRemovePage.submit()); - await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).isExisting()).to.equal(false); + await expect(await $(PeopleListSectionSummaryPage.visitorsListLabel(2)).isExisting()).toBe(false); }); it("When the user updates the list, They should return to the section summary and it should display the updated list", async () => { @@ -224,18 +224,18 @@ describe("List Collector", () => { await $(SectionSummaryListCollectorEditPage.firstName()).setValue("Mark"); await $(SectionSummaryListCollectorEditPage.lastName()).setValue("Twain"); await click(SectionSummaryListCollectorEditPage.submit()); - await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).to.contain("Mark Twain (You)"); + await expect(await $(PeopleListSectionSummaryPage.peopleListLabel(1)).getText()).toBe("Mark Twain (You)"); }); it("When the user removes an item from the list, They should see the individual response guidance", async () => { await $(PeopleListSectionSummaryPage.peopleListRemoveLink(2)).click(); - await expect(await $(SectionSummaryListCollectorRemovePage.individualResponseGuidance()).isExisting()).to.equal(true); + await expect(await $(SectionSummaryListCollectorRemovePage.individualResponseGuidance()).isExisting()).toBe(true); }); it("When the user reaches the submit page and navigates back, They should see the Section Summary", async () => { await click(PeopleListSectionSummaryPage.submit()); await click(SubmitPage.previous()); - await expect(await browser.getUrl()).to.contain(PeopleListSectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(PeopleListSectionSummaryPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector_content.spec.js b/tests/functional/spec/list_collector_content.spec.js index 5c7bf96c9d..705f803af1 100644 --- a/tests/functional/spec/list_collector_content.spec.js +++ b/tests/functional/spec/list_collector_content.spec.js @@ -16,25 +16,25 @@ describe("List Collector Section Summary and Summary Items", () => { }); it("When I get to the Hub, Then from there the next block in list collector content section should be list collector content page.", async () => { await fillInListCollectorSection(); - await expect(await browser.getUrl()).to.contain(HubPage.url()); - await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Not started"); + await expect(browser).toHaveUrlContaining(HubPage.url()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Not started"); await $(HubPage.submit()).click(); await $(ResponsiblePartyQuestionPage.yes()).click(); await $(ResponsiblePartyQuestionPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(ListCollectorContentPage.url()); + await expect(browser).toHaveUrlContaining(ListCollectorContentPage.url()); }); it("When I get to the list collector content page, Then the relevant content and button is displayed.", async () => { await fillInListCollectorSection(); await $(HubPage.submit()).click(); await $(ResponsiblePartyQuestionPage.yes()).click(); await $(ResponsiblePartyQuestionPage.submit()).click(); - await expect(await $(ListCollectorContentPage.heading()).getHTML()).to.contain("Companies"); - await expect(await $("#main-content > p").getText()).to.contain( + await expect(await $(ListCollectorContentPage.heading()).getHTML()).toContain("Companies"); + await expect(await $("#main-content > p").getText()).toBe( "You have previously reported the following companies. Press continue to updated registration and trading information.", ); - await expect(await $("#main-content > #guidance-1").getText()).to.contain("Include all companies"); - await expect(await $("#main-content > #definition").getText()).to.contain("Companies definition"); - await expect(await $(ListCollectorContentPage.submit()).getText()).to.equal("Continue"); + await expect(await $("#main-content > #guidance-1").getText()).toContain("Include all companies"); + await expect(await $("#main-content > #definition").getText()).toBe("Companies definition"); + await expect(await $(ListCollectorContentPage.submit()).getText()).toBe("Continue"); }); it("When I get to list collector content block section, Then I should be able to complete repeating blocks and get to the summary.", async () => { await fillInListCollectorSection(); @@ -60,7 +60,7 @@ describe("List Collector Section Summary and Summary Items", () => { await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); await $(ListCollectorContentPage.submit()).click(); - await expect(await browser.getUrl()).to.contain(ListCollectorContentSectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.url()); }); it("When I fill in first item repeating blocks, Then after going back to the hub the section should be in progress.", async () => { await fillInListCollectorSection(); @@ -78,7 +78,7 @@ describe("List Collector Section Summary and Summary Items", () => { await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); await $(ListCollectorContentPage.previous()).click(); await $(ResponsiblePartyQuestionPage.previous()).click(); - await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); }); it("When I fill in both items repeating blocks, Then after going back to the hub the section should be completed.", async () => { await fillInListCollectorSection(); @@ -107,7 +107,7 @@ describe("List Collector Section Summary and Summary Items", () => { await $(ListCollectorContentSectionSummaryPage.previous()).click(); await $(ListCollectorContentPage.previous()).click(); await $(ResponsiblePartyQuestionPage.previous()).click(); - await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).to.contain("Completed"); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); }); }); }); diff --git a/tests/functional/spec/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector_driving_question.spec.js index 2ed53402d6..6bcccff737 100644 --- a/tests/functional/spec/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector_driving_question.spec.js @@ -38,7 +38,7 @@ describe("List Collector Driving Question", () => { await $(AnyoneUsuallyLiveAtPage.no()).click(); await click(AnyoneUsuallyLiveAtPage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneUsuallyLiveAtPage.url()); + await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAtPage.url()); }); }); @@ -55,7 +55,7 @@ describe("List Collector Driving Question", () => { await $(AnyoneElseLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneElseLiveAtListCollectorRemovePage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); - await expect(await browser.getUrl()).to.contain(AnyoneElseLiveAtListCollectorAddPage.pageName); + await expect(browser).toHaveUrlContaining(AnyoneElseLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js index 3747422f2f..8d0daba5b6 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector_driving_question_checkbox.spec.js @@ -82,7 +82,7 @@ describe("Given the user says no one else lives in the house", () => { await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); await click(AnyoneUsuallyLiveAtPage.submit()); - await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( + await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).toBe( "You said 1 person lives at 12 Lovely Villas. Do you need to add anyone?", ); }); @@ -93,11 +93,11 @@ describe("Given a person does not live in the house", () => { it("The user is asked whether they live there", async () => { await $(PrimaryPersonListCollectorPage.noIDonTUsuallyLiveHere()).click(); await click(PrimaryPersonListCollectorPage.submit()); - await expect(await $(AnyoneUsuallyLiveAtPage.questionText()).getText()).to.equal("Do any of the following usually live at 12 Lovely Villas on 21 March?"); + await expect(await $(AnyoneUsuallyLiveAtPage.questionText()).getText()).toBe("Do any of the following usually live at 12 Lovely Villas on 21 March?"); await $(AnyoneUsuallyLiveAtPage.exclusiveNoneOfTheseApplyNoOneUsuallyLivesHere()).click(); await click(AnyoneUsuallyLiveAtPage.submit()); - await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).to.equal( + await expect(await $(ListCollectorTemporaryAwayPage.questionText()).getText()).toBe( "You said 0 people lives at 12 Lovely Villas. Do you need to add anyone?", ); diff --git a/tests/functional/spec/list_collector_primary_person.spec.js b/tests/functional/spec/list_collector_primary_person.spec.js index de7913cf12..5e49bd5a98 100644 --- a/tests/functional/spec/list_collector_primary_person.spec.js +++ b/tests/functional/spec/list_collector_primary_person.spec.js @@ -22,7 +22,7 @@ describe("Primary Person List Collector Survey", () => { await $(PrimaryPersonListCollectorPage.yesLabel()).click(); await click(PrimaryPersonListCollectorPage.submit()); await browser.url("questionnaire/list-collector"); - await expect(await $(PrimaryPersonListCollectorPage.questionText()).getText()).to.contain("Do you live here"); + await expect(await $(PrimaryPersonListCollectorPage.questionText()).getText()).toBe("Do you live here"); }); }); @@ -37,7 +37,7 @@ describe("Primary Person List Collector Survey", () => { await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); await click(PrimaryPersonListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twin (You)"); }); it("When the user adds another person, they are shown in the summary", async () => { @@ -46,7 +46,7 @@ describe("Primary Person List Collector Survey", () => { await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).toBe("Samuel Clemens"); }); it("When the user goes back and answers No, the primary person is not shown", async () => { @@ -55,7 +55,7 @@ describe("Primary Person List Collector Survey", () => { await click(PrimaryPersonListCollectorPage.submit()); await $(AnyoneUsuallyLiveAtPage.no()).click(); await click(AnyoneUsuallyLiveAtPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Samuel Clemens"); }); it("When the user adds the primary person again, then the primary person is first in the list", async () => { @@ -66,12 +66,12 @@ describe("Primary Person List Collector Survey", () => { await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Mark"); await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); await click(PrimaryPersonListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twin (You)"); }); it("When the user views the summary, then it does not show the remove link for the primary person", async () => { - await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; - await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; + await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).toBe(false); + await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).toBe(true); }); it("When the user changes the primary person's name on the summary, then the name should be updated", async () => { @@ -79,24 +79,24 @@ describe("Primary Person List Collector Survey", () => { await $(ListCollectorEditPage.firstName()).setValue("Mark"); await $(ListCollectorEditPage.lastName()).setValue("Twain"); await click(ListCollectorEditPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); - await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twain (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).toBe("Samuel Clemens"); }); it("When the user views the summary, then it does not show the does anyone usually live here question", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect($("body").getText()).to.not.equal("usually live here"); + await expect($("body").getText()).not.toBe("usually live here"); }); it("When the user attempts to submit, then they are shown the confirmation page", async () => { await click(SectionSummaryPage.submit()); - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, do you wish to submit"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Thank you for your answers, do you wish to submit"); }); it("When the user submits, then they are allowed to submit the survey", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); @@ -109,7 +109,7 @@ describe("Primary Person List Collector Survey", () => { await $(PrimaryPersonListCollectorPage.no()).click(); await click(PrimaryPersonListCollectorPage.submit()); await $(AnyoneUsuallyLiveAtPage.no()).click(); - await expect(await $(ListCollectorPage.listLabel(1)).isExisting()).to.be.false; + await expect(await $(ListCollectorPage.listLabel(1)).isExisting()).toBe(false); }); it("When the user clicks on the add person button multiple times, then only one person is added", async () => { @@ -120,8 +120,8 @@ describe("Primary Person List Collector Survey", () => { await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twain"); await click(PrimaryPersonListCollectorPage.submit()); await click(PrimaryPersonListCollectorPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twain (You)"); - await expect(await $(ListCollectorPage.listLabel(2)).isExisting()).to.be.false; + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twain (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector_section_summary.spec.js index 31d523d04f..1adb0ad1a8 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector_section_summary.spec.js @@ -21,24 +21,24 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).to.contain("Yes"); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).toBe(true); + await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).toBe("Yes"); }); it("When I add my own item, Then the item should be visible on the section summary and have correct values", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(SectionSummaryPage.companiesListLabel(1)).getText()).to.contain("Name of UK company or branch"); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); + await expect(await $(SectionSummaryPage.companiesListLabel(1)).getText()).toContain("Name of UK company or branch"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); const listItemId = (await listItemIds())[0]; - await expect(await $(companiesListRowItemAnchor(1)).getHTML()).to.contain( + await expect(await $(companiesListRowItemAnchor(1)).getHTML()).toContain( `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name`, ); - await expect(await $(companiesListRowItemAnchor(2)).getHTML()).to.contain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); - await expect(await $(companiesListRowItemAnchor(3)).getHTML()).to.contain( + await expect(await $(companiesListRowItemAnchor(2)).getHTML()).toContain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); + await expect(await $(companiesListRowItemAnchor(3)).getHTML()).toContain( `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio`, ); }); @@ -51,33 +51,33 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company C", "789", true); await anyMoreCompaniesNo(); await answerUkBasedQuestion(); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Yes"); - await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); - await expect(await $(companiesListRowItem(2, 3)).getText()).to.contain("No"); - await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("789"); - await expect(await $(companiesListRowItem(3, 3)).getText()).to.contain("Yes"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("No"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("789"); + await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Yes"); }); it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $("body").getText()).to.not.have.string("Company A"); - await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $("body").getText()).not.toBe("Company A"); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); }); it("When I remove an item but the list collector is still on the path, Then the placeholder text should be visible on the section summary.", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $("body").getText()).to.contain("No UK company or branch added"); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $("body").getText()).toContain("No UK company or branch added"); }); it("When I have multiple items in the list and I remove the first item, Then only the item that was not deleted should be visible on the section summary.", async () => { await drivingQuestionYes(); @@ -86,25 +86,25 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company B", "234", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $("body").getText()).to.not.have.string("Company A"); - await expect(await $("body").getText()).to.contain("Company B"); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $("body").getText()).not.toBe("Company A"); + await expect(await $("body").getText()).toContain("Company B"); }); it("When I add an item and relevant data and answer No on the additional items page, Then I should get to the section summary page.", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(true); }); it("When I add an item and relevant data and answer Yes on the additional items page, Then I should be able to and add a new item and relevant data.", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesYes(); - await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).isExisting()).to.be.true; - await expect(await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).to.be.true; - await expect(await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).to.be.true; - await expect(await $(AnyCompaniesOrBranchesAddPage.heading()).getText()).to.contain( + await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).isExisting()).toBe(true); + await expect(await $(AnyCompaniesOrBranchesAddPage.registrationNumber()).isExisting()).toBe(true); + await expect(await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioYes()).isExisting()).toBe(true); + await expect(await $(AnyCompaniesOrBranchesAddPage.heading()).getText()).toBe( "Give details about the company or branch that undertakes general insurance business", ); }); @@ -112,70 +112,70 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); - await expect(await browser.getUrl()).to.contain("edit-company/?return_to=section-summary"); - await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).to.equal("Company A"); + await expect(browser).toHaveUrlContaining("edit-company/?return_to=section-summary"); + await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).toBe("Company A"); }); it("When I edit an item after adding it, Then I should be redirected to the summary page", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); await click(AnyCompaniesOrBranchesAddPage.submit()); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Changed Company"); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Changed Company"); }); it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", async () => { await drivingQuestionNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; - await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(false); await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(true); + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(true); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(true); }); it("When I add an item and relevant data but change my answer to the driving question to No, Then I should see the original item on the summary if change the answer back to Yes.", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); await drivingQuestionNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.false; - await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.false; - await expect(await $("body").getText()).to.not.have.string("No UK company or branch added"); - await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); + await expect(await $("body").getText()).not.toBe("No UK company or branch added"); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(false); await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); await drivingQuestionYes(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).to.be.true; - await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).to.be.true; + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(true); + await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(true); + await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(true); }); it("When I add another company from the summary page, Then I am asked if I want to add any more company before accessing the section summary", async () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await $(SectionSummaryPage.companiesListAddLink()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await expect(browser).toHaveUrlContaining("/questionnaire/companies/add-company"); + await expect(browser).toHaveUrlContaining("?return_to=section-summary"); await addCompany("Company B", "456", true); - await expect(await browser.getUrl()).to.contain(AnyCompaniesOrBranchesPage.url()); - await expect(await $("body").getText()).to.have.string("Company A"); - await expect(await $("body").getText()).to.have.string("Company B"); + await expect(browser).toHaveUrlContaining(AnyCompaniesOrBranchesPage.url()); + await expect(await $("body").getText()).toContain("Company A"); + await expect(await $("body").getText()).toContain("Company B"); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); }); it("When I add three companies, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -185,7 +185,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "789", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await expect(browser).toHaveUrlContaining(UkBasedPage.url()); }); it("When I add less than 3 companies, Then I am not prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -193,7 +193,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company B", "456", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); }); it("When I add more than 3 companies, Then I am not prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -205,7 +205,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company D", "135", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); }); it("When I add another company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -213,15 +213,15 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company B", "456", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); await $(SectionSummaryPage.companiesListAddLink()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/companies/add-company"); - await expect(await browser.getUrl()).to.contain("?return_to=section-summary"); + await expect(browser).toHaveUrlContaining("/questionnaire/companies/add-company"); + await expect(browser).toHaveUrlContaining("?return_to=section-summary"); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await expect(browser).toHaveUrlContaining(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); }); it("When I remove a company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -233,11 +233,11 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company D", "345", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); await removeFirstCompany(); - await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await expect(browser).toHaveUrlContaining(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); }); it("When I get to the summary page, Then the summary should be displayed as expected with change links", async () => { @@ -248,9 +248,9 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await expect(browser).toHaveUrlContaining(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); @@ -258,19 +258,19 @@ describe("List Collector Section Summary and Summary Items", () => { await click(HouseholderCheckboxPage.submit()); await click(SectionSummaryTwoPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).to.contain("Change"); - await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); - await expect(await $(companiesListRowItem(2, 3)).getText()).to.contain("Change"); - await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("234"); - await expect(await $(companiesListRowItem(3, 3)).getText()).to.contain("Change"); - await expect(await $(SubmitPage.householderCheckboxAnswer()).getText()).to.contain("No"); - await expect(await $("body").getHTML()).to.contain("Add another UK company or branch"); - await expect(await $("body").getHTML()).to.contain("Remove"); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Change"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("Change"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); + await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Change"); + await expect(await $(SubmitPage.householderCheckboxAnswer()).getText()).toContain("No"); + await expect(await $("body").getHTML()).toContain("Add another UK company or branch"); + await expect(await $("body").getHTML()).toContain("Remove"); }); it("When I get to the view submitted response page, Then the summary should be displayed as expected without any change or remove links", async () => { @@ -281,9 +281,9 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(await browser.getUrl()).to.contain(UkBasedPage.url()); + await expect(browser).toHaveUrlContaining(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(await browser.getUrl()).to.contain(SectionSummaryPage.url()); + await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); @@ -291,19 +291,19 @@ describe("List Collector Section Summary and Summary Items", () => { await click(HouseholderCheckboxPage.submit()); await click(SectionSummaryTwoPage.submit()); await click(SubmitPage.submit()); - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test"); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(await browser.getUrl()).to.contain(ViewSubmittedResponsePage.pageName); - await expect(await $(companiesListRowItem(1, 1)).getText()).to.contain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).to.contain("123"); - await expect(await $(companiesListRowItem(2, 1)).getText()).to.contain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).to.contain("456"); - await expect(await $(companiesListRowItem(3, 1)).getText()).to.contain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).to.contain("234"); - await expect(await $("body").getHTML()).to.not.contain("Change"); - await expect(await $("body").getHTML()).to.not.contain("Remove"); - await expect(await $("body").getHTML()).to.not.contain("Add another UK company or branch"); + await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); + await expect(await $("body").getHTML()).not.toContain("Change"); + await expect(await $("body").getHTML()).not.toContain("Remove"); + await expect(await $("body").getHTML()).not.toContain("Add another UK company or branch"); }); }); }); diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector_variants.spec.js index 92df8229fc..2b4b4056c3 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector_variants.spec.js @@ -16,13 +16,13 @@ describe("List Collector With Variants", () => { it("The user is asked questions about whether they live there", async () => { await $(YouLiveHerePage.yes()).click(); await click(YouLiveHerePage.submit()); - await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone else live at 1 Pleasant Lane?"); + await expect(await $(ListCollectorPage.questionText()).getText()).toBe("Does anyone else live at 1 Pleasant Lane?"); }); it("The user is able to add members of the household", async () => { await $(ListCollectorPage.anyoneElseYes()).click(); await click(ListCollectorPage.submit()); - await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); + await expect(await $(ListCollectorAddPage.questionText()).getText()).toBe("What is the name of the person?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); @@ -35,22 +35,22 @@ describe("List Collector With Variants", () => { it("The questionnaire has the correct question text on the change and remove pages", async () => { await $(ListCollectorPage.listEditLink(1)).click(); - await expect(await $(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person?"); + await expect(await $(ListCollectorEditPage.questionText()).getText()).toBe("What is the name of the person?"); await $(ListCollectorEditPage.previous()).click(); await $(ListCollectorPage.listRemoveLink(1)).click(); - await expect(await $(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person?"); + await expect(await $(ListCollectorRemovePage.questionText()).getText()).toBe("Are you sure you want to remove this person?"); await $(ListCollectorRemovePage.previous()).click(); }); it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); }); @@ -62,13 +62,13 @@ describe("List Collector With Variants", () => { it("The user is asked questions about whether they live there", async () => { await $(YouLiveHerePage.no()).click(); await click(YouLiveHerePage.submit()); - await expect(await $(ListCollectorPage.questionText()).getText()).to.equal("Does anyone live at 1 Pleasant Lane?"); + await expect(await $(ListCollectorPage.questionText()).getText()).toBe("Does anyone live at 1 Pleasant Lane?"); }); it("The user is able to add members of the household", async () => { await $(ListCollectorPage.anyoneElseYes()).click(); await click(ListCollectorPage.submit()); - await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); + await expect(await $(ListCollectorAddPage.questionText()).getText()).toBe("What is the name of the person who isn’t you?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); @@ -81,22 +81,22 @@ describe("List Collector With Variants", () => { it("The questionnaire has the correct question text on the change and remove pages", async () => { await $(ListCollectorPage.listEditLink(1)).click(); - await expect(await $(ListCollectorEditPage.questionText()).getText()).to.equal("What is the name of the person who isn’t you?"); + await expect(await $(ListCollectorEditPage.questionText()).getText()).toBe("What is the name of the person who isn’t you?"); await $(ListCollectorEditPage.previous()).click(); await $(ListCollectorPage.listRemoveLink(1)).click(); - await expect(await $(ListCollectorRemovePage.questionText()).getText()).to.equal("Are you sure you want to remove this person who isn’t you?"); + await expect(await $(ListCollectorRemovePage.questionText()).getText()).toBe("Are you sure you want to remove this person who isn’t you?"); await $(ListCollectorRemovePage.previous()).click(); }); it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.url()); + await expect(browser).toHaveUrlContaining(ThankYouPage.url()); }); }); }); diff --git a/tests/functional/spec/list_collector_variants_primary_person.spec.js b/tests/functional/spec/list_collector_variants_primary_person.spec.js index 1c111aff44..1d01495bf0 100644 --- a/tests/functional/spec/list_collector_variants_primary_person.spec.js +++ b/tests/functional/spec/list_collector_variants_primary_person.spec.js @@ -14,7 +14,7 @@ describe("List collector with variants primary person", () => { it("When the user is asked questions about whether they like variant, Then they are routed to section asking if they live in the house", async () => { await $(VariantBlockPage.yes()).click(); await click(VariantBlockPage.submit()); - await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here? (variant)"); + await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).toBe("Do you live here? (variant)"); }); }); }); @@ -30,7 +30,7 @@ describe("List collector with variants primary person", () => { await $(ListCollectorAddPage.firstName()).setValue("John"); await $(ListCollectorAddPage.lastName()).setValue("Doe"); await click(ListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("John Doe (You)"); }); it("When the user adds another person, Then they are shown in the list collector summary", async () => { await $(ListCollectorPage.yesLabel()).click(); @@ -38,19 +38,19 @@ describe("List collector with variants primary person", () => { await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).toBe("Samuel Clemens"); }); it("When the user goes back and answers 'No' for 'Do you live here' question, Then the primary person is not shown", async () => { await $(ListCollectorPage.previous()).click(); await $(PrimaryPersonListCollectorPage.youLiveHereNo()).click(); await click(PrimaryPersonListCollectorPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Samuel Clemens"); }); it("When the user adds another person, Then the user is able to add members of the household", async () => { await $(ListCollectorPage.yes()).click(); await click(ListCollectorPage.submit()); - await expect(await $(ListCollectorAddPage.questionText()).getText()).to.equal("What is the name of the person?"); + await expect(await $(ListCollectorAddPage.questionText()).getText()).toBe("What is the name of the person?"); await $(ListCollectorAddPage.firstName()).setValue("Samuel"); await $(ListCollectorAddPage.lastName()).setValue("Clemens"); await click(ListCollectorAddPage.submit()); @@ -62,41 +62,41 @@ describe("List collector with variants primary person", () => { await $(ListCollectorAddPage.firstName()).setValue("Mark"); await $(ListCollectorAddPage.lastName()).setValue("Twin"); await click(ListCollectorAddPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("Mark Twin (You)"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("Mark Twin (You)"); }); it("When the user views the summary, Then it does not show the remove link for the primary person", async () => { - await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).to.be.false; - await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).to.be.true; + await expect(await $(ListCollectorPage.listRemoveLink(1)).isExisting()).toBe(false); + await expect(await $(ListCollectorPage.listRemoveLink(2)).isExisting()).toBe(true); }); it("When the user changes the primary person's name on the summary, Then the name should be updated", async () => { await $(ListCollectorPage.listEditLink(1)).click(); await $(EditPersonPage.firstName()).setValue("John"); await $(EditPersonPage.lastName()).setValue("Doe"); await click(EditPersonPage.submit()); - await expect(await $(ListCollectorPage.listLabel(1)).getText()).to.equal("John Doe (You)"); - await expect(await $(ListCollectorPage.listLabel(2)).getText()).to.equal("Samuel Clemens"); + await expect(await $(ListCollectorPage.listLabel(1)).getText()).toBe("John Doe (You)"); + await expect(await $(ListCollectorPage.listLabel(2)).getText()).toBe("Samuel Clemens"); }); it("When the user answers 'no' to add any person, Then the questionnaire shows the confirmation page", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); it("When the user attempts to submit, Then they are shown the confirmation page", async () => { - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Please submit this survey to complete it"); }); it("When user updates the variant answer, Then it should come back to summary screen with updated answer", async () => { await $(SubmitPage.variantAnswerEdit()).click(); await $(VariantBlockPage.no()).click(); await click(VariantBlockPage.submit()); - await expect(await $(SubmitPage.variantAnswer()).getText()).to.equal("No"); + await expect(await $(SubmitPage.variantAnswer()).getText()).toBe("No"); }); it("When the user submits, Then they are allowed to submit the survey", async () => { await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); }); @@ -108,7 +108,7 @@ describe("Given the user starts on the 'Do you like variant' question", () => { it("When the user answers 'No' for variant question, Then they are routed to section asking if they live in the house", async () => { await $(VariantBlockPage.no()).click(); await click(VariantBlockPage.submit()); - await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).to.contain("Do you live here?"); + await expect(await $(PrimaryPersonListCollectorPage.legend()).getText()).toBe("Do you live here?"); }); it("When the user says they do not live there and anyone else, Then confirmation screen is displayed", async () => { @@ -117,6 +117,6 @@ describe("Given the user starts on the 'Do you like variant' question", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Please submit this survey to complete it"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Please submit this survey to complete it"); }); }); diff --git a/tests/functional/spec/mobile_number.spec.js b/tests/functional/spec/mobile_number.spec.js index e0ccea6981..f3ab63b2c3 100644 --- a/tests/functional/spec/mobile_number.spec.js +++ b/tests/functional/spec/mobile_number.spec.js @@ -9,16 +9,16 @@ describe("Mobile number validation", () => { it("Given I am asked to enter Mobile no, When I enter a valid mobile number with no prefix and submit, Then confirmation section is displayed with entered mobile number", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue(7712345678); await click(MobileNumberBlockPage.submit()); - await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("7712345678"); + await expect(await $(submitPage.mobileNumberAnswer()).getText()).toBe("7712345678"); }); it("Given I am asked to enter Mobile no, When I enter a valid mobile number with prefix (+44) and submit, Then confirmation section is displayed with entered mobile number", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue("+447712345678"); await click(MobileNumberBlockPage.submit()); - await expect(await $(submitPage.mobileNumberAnswer()).getText()).to.contain("+447712345678"); + await expect(await $(submitPage.mobileNumberAnswer()).getText()).toBe("+447712345678"); }); it("Given I am asked to enter Mobile no, When I enter an invalid mobile number and submit, Then an error screen with invalid number information is displayed", async () => { await $(MobileNumberBlockPage.mobileNumber()).setValue("12345678"); await click(MobileNumberBlockPage.submit()); - await expect(await $("body").getText()).to.contain("Enter a UK mobile number in a valid format"); + await expect(await $("body").getText()).toContain("Enter a UK mobile number in a valid format"); }); }); diff --git a/tests/functional/spec/multiple_answers.spec.js b/tests/functional/spec/multiple_answers.spec.js index d3e4666c61..ff716bff13 100644 --- a/tests/functional/spec/multiple_answers.spec.js +++ b/tests/functional/spec/multiple_answers.spec.js @@ -37,30 +37,30 @@ describe("Multiple Answers", () => { }); it("When I am on the summary, Then all answers are displayed", async () => { - await expect(await $(SubmitPage.textfieldAnswer()).getText()).to.equal("John Doe"); - await expect(await $(SubmitPage.dateAnswer()).getText()).to.equal("1 January 1995"); - await expect(await $(SubmitPage.checkboxAnswer()).getText()).to.equal("BMW"); - await expect(await $(SubmitPage.radioAnswer()).getText()).to.equal("Yes"); - await expect(await $(SubmitPage.currencyAnswer()).getText()).to.equal("£50,000.00"); - await expect(await $(SubmitPage.monthYearDateAnswer()).getText()).to.equal("October 2021"); - await expect(await $(SubmitPage.dropdownAnswer()).getText()).to.equal("Silver"); - await expect(await $(SubmitPage.unitAnswer()).getText()).to.equal("10,000 mi"); - await expect(await $(SubmitPage.durationAnswer()).getText()).to.equal("3 years 3 months"); - await expect(await $(SubmitPage.yearDateAnswer()).getText()).to.equal("2019"); - await expect(await $(SubmitPage.numberAnswer()).getText()).to.equal("5"); - await expect(await $(SubmitPage.percentageAnswer()).getText()).to.equal("3%"); - await expect(await $(SubmitPage.mobileNumberAnswer()).getText()).to.equal("07700900111"); - await expect(await $(SubmitPage.textareaAnswer()).getText()).to.equal("Fuel type petrol"); + await expect(await $(SubmitPage.textfieldAnswer()).getText()).toBe("John Doe"); + await expect(await $(SubmitPage.dateAnswer()).getText()).toBe("1 January 1995"); + await expect(await $(SubmitPage.checkboxAnswer()).getText()).toBe("BMW"); + await expect(await $(SubmitPage.radioAnswer()).getText()).toBe("Yes"); + await expect(await $(SubmitPage.currencyAnswer()).getText()).toBe("£50,000.00"); + await expect(await $(SubmitPage.monthYearDateAnswer()).getText()).toBe("October 2021"); + await expect(await $(SubmitPage.dropdownAnswer()).getText()).toBe("Silver"); + await expect(await $(SubmitPage.unitAnswer()).getText()).toBe("10,000 mi"); + await expect(await $(SubmitPage.durationAnswer()).getText()).toBe("3 years 3 months"); + await expect(await $(SubmitPage.yearDateAnswer()).getText()).toBe("2019"); + await expect(await $(SubmitPage.numberAnswer()).getText()).toBe("5"); + await expect(await $(SubmitPage.percentageAnswer()).getText()).toBe("3%"); + await expect(await $(SubmitPage.mobileNumberAnswer()).getText()).toBe("07700900111"); + await expect(await $(SubmitPage.textareaAnswer()).getText()).toBe("Fuel type petrol"); - await expect(await $(SubmitPage.ageAnswer()).getText()).to.equal("10"); - await expect(await $(SubmitPage.ageEstimateAnswer()).getText()).to.equal("This age is an estimate"); + await expect(await $(SubmitPage.ageAnswer()).getText()).toBe("10"); + await expect(await $(SubmitPage.ageEstimateAnswer()).getText()).toBe("This age is an estimate"); }); it("When I click 'Change' an answer, Then I should be taken to the correct page and the answer input should be focused", async () => { await $(SubmitPage.currencyAnswerEdit()).click(); - await expect(await browser.getUrl()).to.contain(AboutYou.url()); - await expect(await browser.getUrl()).to.contain(AboutYou.currency()); - await expect(await $(AboutYou.currency()).isFocused()).to.be.true; + await expect(browser).toHaveUrlContaining(AboutYou.url()); + await expect(browser).toHaveUrlContaining(AboutYou.currency()); + await expect(await $(AboutYou.currency()).isFocused()).toBe(true); }); }); @@ -70,19 +70,19 @@ describe("Multiple Answers", () => { }); it("When I am on the question page, Then all answers should have a label/legend", async () => { - await expect(await $(AboutYou.dateLegend()).getText()).to.equal("What is your date of birth?"); - await expect(await $(AboutYou.monthYearDateLegend()).getText()).to.equal("When would you like the car by?"); - await expect(await $(AboutYou.radioLegend()).getText()).to.equal("Would you like the sports package?"); - await expect(await $(AboutYou.durationLegend()).getText()).to.equal("How long have you had your licence?"); - await expect(await $(AboutYou.checkboxLegend()).getText()).to.equal("What are your favourite car brands?"); - await expect(await $(AboutYou.textfieldLabel()).getText()).to.equal("Your name"); - await expect(await $(AboutYou.currencyLabel()).getText()).to.equal("What is your budget?"); - await expect(await $(AboutYou.dropdownLabel()).getText()).to.equal("Select a colour"); - await expect(await $(AboutYou.unitLabel()).getText()).to.equal("Max mileage"); - await expect(await $(AboutYou.numberLabel()).getText()).to.equal("How many seats?"); - await expect(await $(AboutYou.percentageLabel()).getText()).to.equal("Max CO2 emissions"); - await expect(await $(AboutYou.mobileNumberLabel()).getText()).to.equal("What is your mobile number?"); - await expect(await $(AboutYou.textareaLabel()).getText()).to.equal("Other comments"); + await expect(await $(AboutYou.dateLegend()).getText()).toBe("What is your date of birth?"); + await expect(await $(AboutYou.monthYearDateLegend()).getText()).toBe("When would you like the car by?"); + await expect(await $(AboutYou.radioLegend()).getText()).toBe("Would you like the sports package?"); + await expect(await $(AboutYou.durationLegend()).getText()).toBe("How long have you had your licence?"); + await expect(await $(AboutYou.checkboxLegend()).getText()).toBe("What are your favourite car brands?"); + await expect(await $(AboutYou.textfieldLabel()).getText()).toBe("Your name"); + await expect(await $(AboutYou.currencyLabel()).getText()).toBe("What is your budget?"); + await expect(await $(AboutYou.dropdownLabel()).getText()).toBe("Select a colour"); + await expect(await $(AboutYou.unitLabel()).getText()).toBe("Max mileage"); + await expect(await $(AboutYou.numberLabel()).getText()).toBe("How many seats?"); + await expect(await $(AboutYou.percentageLabel()).getText()).toBe("Max CO2 emissions"); + await expect(await $(AboutYou.mobileNumberLabel()).getText()).toBe("What is your mobile number?"); + await expect(await $(AboutYou.textareaLabel()).getText()).toBe("Other comments"); }); }); }); diff --git a/tests/functional/spec/multiple_piping.spec.js b/tests/functional/spec/multiple_piping.spec.js index 3bf58bd178..6520d65f72 100644 --- a/tests/functional/spec/multiple_piping.spec.js +++ b/tests/functional/spec/multiple_piping.spec.js @@ -20,7 +20,7 @@ describe("Piping", () => { await $(TextfieldPage.firstText()).setValue("Fireman"); await $(TextfieldPage.secondText()).setValue("Sam"); await click(TextfieldPage.submit()); - await expect(await $(MultiplePipingPage.answerAddressLabel()).getText()).to.contain("1 The ONS, Newport, NP10 8XG, Wales"); + await expect(await $(MultiplePipingPage.answerAddressLabel()).getText()).toBe("1 The ONS, Newport, NP10 8XG, Wales"); }); it("Given I enter values in multiple questions, When I navigate to the multiple piping question, Then I should see both values piped into the question", async () => { @@ -29,7 +29,7 @@ describe("Piping", () => { await $(TextfieldPage.firstText()).setValue("Fireman"); await $(TextfieldPage.secondText()).setValue("Sam"); await click(TextfieldPage.submit()); - await expect(await $(MultiplePipingPage.questionText()).getText()).to.contain("Does Fireman Sam live at 1 The ONS"); + await expect(await $(MultiplePipingPage.questionText()).getText()).toBe("Does Fireman Sam live at 1 The ONS"); }); }); }); diff --git a/tests/functional/spec/my_account_header_link.spec.js b/tests/functional/spec/my_account_header_link.spec.js index e94c94f742..a721b3b301 100644 --- a/tests/functional/spec/my_account_header_link.spec.js +++ b/tests/functional/spec/my_account_header_link.spec.js @@ -4,7 +4,7 @@ describe("My Account header link", () => { it("Given I start a survey, When I visit a page then I should not see the My account button", async () => { await browser.openQuestionnaire("test_introduction.json"); await browser.pause(100); - await expect(await browser.getUrl()).to.contain("introduction"); - await expect(await $(IntroductionPage.myAccountLink()).isExisting()).to.be.false; + await expect(browser).toHaveUrlContaining("introduction"); + await expect(await $(IntroductionPage.myAccountLink()).isExisting()).toBe(false); }); }); diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index 8694bf1b8d..e1e7f0036f 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -14,8 +14,8 @@ describe("Number validation", () => { }); describe("Given I am completing the test numbers questionnaire,", () => { it("When I am on the set minimum and maximum page, Then each field has a label", async () => { - await expect(await $(SetMinMax.setMinimumLabelDescription()).getText()).to.contain("This is a description of the minimum value"); - await expect(await $(SetMinMax.setMaximumLabelDescription()).getText()).to.contain("This is a description of the maximum value"); + await expect(await $(SetMinMax.setMinimumLabelDescription()).getText()).toBe("This is a description of the minimum value"); + await expect(await $(SetMinMax.setMaximumLabelDescription()).getText()).toBe("This is a description of the maximum value"); }); it("When I enter values outside of the set range, Then the correct error messages are displayed", async () => { @@ -33,14 +33,14 @@ describe("Number validation", () => { await $(TestMinMax.testDecimal()).setValue("5.4"); await click(TestMinMax.submit()); - await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than or equal to 10"); - await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter an answer more than 10"); - await expect(await $(TestMinMax.errorNumber(3)).getText()).to.contain("Enter an answer more than or equal to -123"); - await expect(await $(TestMinMax.errorNumber(4)).getText()).to.contain("Enter an answer less than or equal to 1,234"); - await expect(await $(TestMinMax.errorNumber(5)).getText()).to.contain("Enter an answer more than 123"); - await expect(await $(TestMinMax.errorNumber(6)).getText()).to.contain("Enter an answer less than 1,234"); - await expect(await $(TestMinMax.errorNumber(7)).getText()).to.contain("Enter an answer less than or equal to 100"); - await expect(await $(TestMinMax.errorNumber(8)).getText()).to.contain("Enter an answer more than or equal to £10.00"); + await expect(await $(TestMinMax.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to 10"); + await expect(await $(TestMinMax.errorNumber(2)).getText()).toBe("Enter an answer more than 10"); + await expect(await $(TestMinMax.errorNumber(3)).getText()).toBe("Enter an answer more than or equal to -123"); + await expect(await $(TestMinMax.errorNumber(4)).getText()).toBe("Enter an answer less than or equal to 1,234"); + await expect(await $(TestMinMax.errorNumber(5)).getText()).toBe("Enter an answer more than 123"); + await expect(await $(TestMinMax.errorNumber(6)).getText()).toBe("Enter an answer less than 1,234"); + await expect(await $(TestMinMax.errorNumber(7)).getText()).toBe("Enter an answer less than or equal to 100"); + await expect(await $(TestMinMax.errorNumber(8)).getText()).toBe("Enter an answer more than or equal to £10.00"); }); it("When I enter values inside the set range but provide too many decimal places, Then the correct error messages are displayed", async () => { @@ -55,8 +55,8 @@ describe("Number validation", () => { await $(TestMinMax.testDecimal()).setValue("11.123456"); await click(TestMinMax.submit()); - await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter a number rounded to 2 decimal places"); - await expect(await $(TestMinMax.errorNumber(2)).getText()).to.contain("Enter a number rounded to 5 decimal places"); + await expect(await $(TestMinMax.errorNumber(1)).getText()).toBe("Enter a number rounded to 2 decimal places"); + await expect(await $(TestMinMax.errorNumber(2)).getText()).toBe("Enter a number rounded to 5 decimal places"); }); it("When I enter values inside the set range, Then I should be able to submit the survey", async () => { @@ -75,7 +75,7 @@ describe("Number validation", () => { await click(secondNumberBlock.submit()); await click(currencySectionSummary.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", async () => { @@ -86,14 +86,14 @@ describe("Number validation", () => { await click(TestMinMax.submit()); await click(DetailAnswer.submit()); - await expect(await $(DetailAnswer.errorNumber(1)).getText()).to.contain("Enter an answer less than or equal to 1,018"); + await expect(await $(DetailAnswer.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to 1,018"); await $(DetailAnswer.otherDetail()).setValue("1001"); await click(DetailAnswer.submit()); await click(secondNumberBlock.submit()); await click(currencySectionSummary.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I edit and change the minimum value, Then I must re-validate and submit any dependent answers again before I can return to the summary", async () => { @@ -102,16 +102,16 @@ describe("Number validation", () => { await click(SetMinMax.submit()); await click(TestMinMax.submit()); - await expect(await $(TestMinMax.errorNumber(1)).getText()).to.contain("Enter an answer more than 11"); + await expect(await $(TestMinMax.errorNumber(1)).getText()).toBe("Enter an answer more than 11"); await $(TestMinMax.testRangeExclusive()).setValue("12"); await click(TestMinMax.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When a number with more than 3 decimal places has been entered, Then it should be displayed correctly on the summary", async () => { - await expect(await $(SubmitPage.testDecimal()).getText()).to.equal("£11.10000"); + await expect(await $(SubmitPage.testDecimal()).getText()).toBe("£11.10000"); }); }); }); diff --git a/tests/functional/spec/page_layout.spec.js b/tests/functional/spec/page_layout.spec.js index 8b0045a1e2..b28c0752ec 100644 --- a/tests/functional/spec/page_layout.spec.js +++ b/tests/functional/spec/page_layout.spec.js @@ -5,6 +5,6 @@ describe("Page Layout", () => { await browser.url(HubPage.url()); const cssWidthSelector = await $('div[class*="ons-col-"][class*="@m"]').getAttribute("class"); - await expect(cssWidthSelector).to.contain("ons-col-8@m"); + await expect(cssWidthSelector).toContain("ons-col-8@m"); }); }); diff --git a/tests/functional/spec/percentage_decimal.spec.js b/tests/functional/spec/percentage_decimal.spec.js index 4c531e437f..e6d0807bad 100644 --- a/tests/functional/spec/percentage_decimal.spec.js +++ b/tests/functional/spec/percentage_decimal.spec.js @@ -10,8 +10,8 @@ describe("Decimal places", () => { await $(PercentageDecimalPage.decimal()).setValue("3.333"); await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); - await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.333"); + await expect(browser).toHaveUrlContaining(PercentageDecimalPage.pageName); + await expect(await $(PercentageDecimalPage.decimal()).getValue()).toBe("3.333"); }); it("Given an answer allows 3 decimal places, When I enter a value to 1 decimal place and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { @@ -20,7 +20,7 @@ describe("Decimal places", () => { await $(PercentageDecimalPage.decimal()).setValue("3.3"); await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(PercentageDecimalPage.pageName); - await expect(await $(PercentageDecimalPage.decimal()).getValue()).to.equal("3.300"); + await expect(browser).toHaveUrlContaining(PercentageDecimalPage.pageName); + await expect(await $(PercentageDecimalPage.decimal()).getValue()).toBe("3.300"); }); }); diff --git a/tests/functional/spec/placeholder_answers_on_the_path.spec.js b/tests/functional/spec/placeholder_answers_on_the_path.spec.js index 66a60dc1fc..1dc9dbff1c 100644 --- a/tests/functional/spec/placeholder_answers_on_the_path.spec.js +++ b/tests/functional/spec/placeholder_answers_on_the_path.spec.js @@ -30,8 +30,8 @@ describe("First Non Empty Item Transform", () => { await $(DateEntryBlockPage.previous()).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); await click(DateQuestionBlockPage.submit()); - expect(await browser.getUrl()).to.contain(TotalTurnoverBlockPage.pageName); - expect(await $(TotalTurnoverBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + expect(await browser.getUrl()).toContain(TotalTurnoverBlockPage.pageName); + expect(await $(TotalTurnoverBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); @@ -55,15 +55,15 @@ describe("First Non Empty Item Transform Cross Section", () => { // Check date changed and then change to original dates await click(HubPage.submit()); - expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); + expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).toContain("5 January 2017 to 25 January 2017"); await $(FoodQuestionBlockPage.previous()).click(); await $(HubPage.summaryRowLink("default-section")).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); await click(DateQuestionBlockPage.submit()); // Check the next section if the metadata date is shown await click(HubPage.submit()); - expect(await browser.getUrl()).to.contain(FoodQuestionBlockPage.pageName); - expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + expect(await browser.getUrl()).toContain(FoodQuestionBlockPage.pageName); + expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); @@ -96,8 +96,8 @@ describe("First Non Empty Item Transform Repeating Sections", () => { await click(ListCollectorPage.submit()); // Check Repeating Section has the set dates await click(HubPage.submit()); - expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); - expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("5 January 2017 to 25 January 2017"); + expect(await browser.getUrl()).toContain(PersonalDetailsBlockPage.pageName); + expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).toContain("5 January 2017 to 25 January 2017"); await $(PersonalDetailsBlockPage.previous()).click(); // Change to original dates await $(HubPage.summaryRowLink("date-section")).click(); @@ -105,7 +105,7 @@ describe("First Non Empty Item Transform Repeating Sections", () => { await click(DateQuestionBlockPage.submit()); await click(HubPage.submit()); // Check the list collector has metadata dates in the title - expect(await browser.getUrl()).to.contain(PersonalDetailsBlockPage.pageName); - expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).to.contain("1 January 2017 to 1 February 2017"); + expect(await browser.getUrl()).toContain(PersonalDetailsBlockPage.pageName); + expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index f264602ef1..8e44bbcc3f 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -1,6 +1,5 @@ import IntroductionPageHub from "../generated_pages/introduction_hub/introduction.page"; import IntroductionPageLinear from "../generated_pages/introduction/introduction.page"; -import { expect } from "chai"; describe("Introduction preview questions", () => { const introductionSchemaHub = "test_introduction_hub.json"; @@ -25,24 +24,24 @@ describe("Introduction preview questions", () => { async function testPreview(schema, page) { await browser.openQuestionnaire(schema); await $(page.previewQuestions()).click(); - expect(await browser.getUrl()).to.contain("questionnaire/preview"); + expect(await browser.getUrl()).toContain("questionnaire/preview"); if (schema === "test_introduction.json") { - expect(await $(previewSectionTitle).getText()).to.equal("Main section"); + expect(await $(previewSectionTitle).getText()).toBe("Main section"); } else { await $(showButton).click(); } // :TODO: Add data attributes to elements below so we don't rely on tags or classes that are subject to DS changes - expect(await $(previewQuestion).$("h3").getText()).to.equal("Are you able to report for the calendar month 1 January 2017 to 1 February 2017?"); - expect(await $(previewQuestion).$(".ons-question__description").getText()).to.equal("Your return should relate to the calendar year 2021."); - expect(await $(previewQuestion).$$(".ons-panel__body")[0].getText()).to.equal("Please provide figures for the period in which you were trading."); - expect(await $(showButton).length).to.be.undefined; - expect(await $(printButton).isClickable()).to.be.true; - expect(await $(pdfButton).isClickable()).to.be.true; + expect(await $(previewQuestion).$("h3").getText()).toBe("Are you able to report for the calendar month 1 January 2017 to 1 February 2017?"); + expect(await $(previewQuestion).$(".ons-question__description").getText()).toBe("Your return should relate to the calendar year 2021."); + expect(await $(previewQuestion).$$(".ons-panel__body")[0].getText()).toBe("Please provide figures for the period in which you were trading."); + expect(await $(showButton).length).toBeUndefined(); + expect(await $(printButton).isClickable()).toBe(true); + expect(await $(pdfButton).isClickable()).toBe(true); // answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 // $(detailsHeading).click(); // expect($(previewQuestion).$("#answer-guidance--content div p").getText()).to.equal("For example select `yes` if you can report for this period"); - expect(await $(previewQuestion).$$("p")[2].getText()).to.equal("You can answer with one of the following options:"); - expect(await $(previewQuestion).$$("ul")[0].getText()).to.equal("Yes\nNo"); + expect(await $(previewQuestion).$$("p")[2].getText()).toBe("You can answer with one of the following options:"); + expect(await $(previewQuestion).$$("ul")[0].getText()).toBe("Yes\nNo"); } it("Given I start a survey, When I view the preview page, Then all preview elements should be visible and any metadata piped answers are resolved", async () => { @@ -62,12 +61,12 @@ describe("Introduction preview questions", () => { await $(answerToMonth).setValue(12); await $(answerToYear).setValue(2016); await $(submitButton).click(); - expect(await $("h1").getText()).to.equal("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); + expect(await $("h1").getText()).toBe("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); await browser.url("questionnaire/introduction/"); await $(IntroductionPageLinear.previewQuestions()).click(); - expect(await browser.getUrl()).to.contain("questionnaire/preview"); - expect(await $(previewSectionTitle).getText()).to.equal("Main section"); - expect(await $$(previewQuestion)[2].$("h3").getText()).to.equal( + expect(await browser.getUrl()).toContain("questionnaire/preview"); + expect(await $(previewSectionTitle).getText()).toBe("Main section"); + expect(await $$(previewQuestion)[2].$("h3").getText()).toBe( "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", ); }); @@ -75,15 +74,15 @@ describe("Introduction preview questions", () => { it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", async () => { await browser.openQuestionnaire(introductionSchemaHub); await $(IntroductionPageHub.previewQuestions()).click(); - expect(await browser.getUrl()).to.contain("questionnaire/preview"); - expect(await $(printButton).isClickable()).to.be.true; - expect(await $(pdfButton).isClickable()).to.be.true; - expect(await $(showButton).getText()).to.equal("Show all"); - expect(await $(previewSummaryContent).isClickable()).to.be.false; + expect(await browser.getUrl()).toContain("questionnaire/preview"); + expect(await $(printButton).isClickable()).toBe(true); + expect(await $(pdfButton).isClickable()).toBe(true); + expect(await $(showButton).getText()).toBe("Show all"); + expect(await $(previewSummaryContent).isClickable()).toBe(false); it("and if the twisty button is clicked, Then the twisty button should read 'Hide all' and the answers should be visible", async () => { await $(showButton).click(); - expect(await $(showButton).getText()).to.equal("Hide all"); - expect(await $(previewSummaryContent).isClickable()).to.be.true; + expect(await $(showButton).getText()).toBe("Hide all"); + expect(await $(previewSummaryContent).isClickable()).toBe(true); }); }); }); diff --git a/tests/functional/spec/question_definitions.spec.js b/tests/functional/spec/question_definitions.spec.js index abec9c374d..3c0ee372bc 100644 --- a/tests/functional/spec/question_definitions.spec.js +++ b/tests/functional/spec/question_definitions.spec.js @@ -7,26 +7,26 @@ describe("Component: Definition", () => { }); it("When I click the title link, then the description should be visible", async () => { - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); // When await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( + await expect(await $(DefinitionPage.definitionContent()).getText()).toContain( "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power.", ); }); it("When I click the title link twice, then the description should not be visible", async () => { - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); // When await $(DefinitionPage.definitionTitle()).click(); await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); }); }); }); diff --git a/tests/functional/spec/question_definitions_array_type.spec.js b/tests/functional/spec/question_definitions_array_type.spec.js index 66a8b9b899..8e25937295 100644 --- a/tests/functional/spec/question_definitions_array_type.spec.js +++ b/tests/functional/spec/question_definitions_array_type.spec.js @@ -7,26 +7,26 @@ describe("Component: Definition", () => { }); it("When I click the title link, then the description should be visible", async () => { - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); // When await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent()).getText()).to.contain( + await expect(await $(DefinitionPage.definitionContent()).getText()).toContain( "A typical photovoltaic system employs solar panels, each comprising a number of solar cells, which generate electrical power.", ); }); it("When I click the title link twice, then the description should not be visible", async () => { - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); // When await $(DefinitionPage.definitionTitle()).click(); await $(DefinitionPage.definitionTitle()).click(); // Then - await expect(await $(DefinitionPage.definitionContent()).getText()).to.equal(""); + await expect(await $(DefinitionPage.definitionContent()).getText()).toBe(""); }); }); }); diff --git a/tests/functional/spec/question_description.spec.js b/tests/functional/spec/question_description.spec.js index 720c674f45..ebc4fdf3a0 100644 --- a/tests/functional/spec/question_description.spec.js +++ b/tests/functional/spec/question_description.spec.js @@ -9,7 +9,7 @@ import { click } from "../helpers"; describe("Question description", () => { it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", async () => { await browser.openQuestionnaire("test_question_description.json"); - await expect(await $(NameBlockPage.questionTitle()).getHTML()).to.contain("

      Answer the question

      Go on

      "); + await expect(await $(NameBlockPage.questionTitle()).getHTML()).toContain("

      Answer the question

      Go on

      "); }); }); @@ -17,12 +17,12 @@ describe("Optional question description and guidance", () => { it("Given a question description has been set in the schema, When the value to be displayed is None, Then it is not rendered on the page", async () => { await browser.openQuestionnaire("test_optional_guidance_and_description.json"); await click(DescriptionBlockPage.submit()); - await expect(await $(RadioPage.questionTitle()).getHTML()).to.not.contain("

      ''

      "); - await expect(await $(RadioPage.guidance()).isExisting()).to.be.false; + await expect(await $(RadioPage.questionTitle()).getHTML()).not.toContain("

      ''

      "); + await expect(await $(RadioPage.guidance()).isExisting()).toBe(false); await $(RadioPage.no()).click(); await click(RadioPage.submit()); - await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.contain("
    • List item one
    • "); - await expect(await $(RadioPageTwo.questionTitle()).getHTML()).to.not.contain("
    • "); + await expect(await $(RadioPageTwo.questionTitle()).getHTML()).toContain("
    • List item one
    • "); + await expect(await $(RadioPageTwo.questionTitle()).getHTML()).not.toContain("
    • "); }); }); @@ -30,7 +30,7 @@ describe("Question guidance", () => { it("Given a question guidance with multiple content items, When it is rendered, Then there should only be one guidance box", async () => { await browser.openQuestionnaire("test_question_guidance.json"); await click(IntroductionPage.submit()); - await expect(await browser.getUrl()).to.contain(GuidancePage.pageName); - await expect(await $$("#question-guidance-question-test-guidance-title").length).to.equal(1); + await expect(browser).toHaveUrlContaining(GuidancePage.pageName); + await expect(await $$("#question-guidance-question-test-guidance-title").length).toBe(1); }); }); diff --git a/tests/functional/spec/question_variants.spec.js b/tests/functional/spec/question_variants.spec.js index 0b484b0681..9a136747bf 100644 --- a/tests/functional/spec/question_variants.spec.js +++ b/tests/functional/spec/question_variants.spec.js @@ -19,30 +19,30 @@ describe("QuestionVariants", () => { await $(nameBlock.lastName()).setValue("van Rossum"); await click(nameBlock.submit()); - await expect(await $(proxyBlock.questionText()).getText()).to.contain("Are you Guido van Rossum?"); + await expect(await $(proxyBlock.questionText()).getText()).toBe("Are you Guido van Rossum?"); await $(proxyBlock.noIAmAnsweringOnTheirBehalf()).click(); await click(proxyBlock.submit()); - await expect(await $(ageBlock.questionText()).getText()).to.contain("What age is Guido van Rossum"); + await expect(await $(ageBlock.questionText()).getText()).toBe("What age is Guido van Rossum?"); await $(ageBlock.age()).setValue(63); await click(ageBlock.submit()); - await expect(await $(ageConfirmationBlock.questionText()).getText()).to.contain("Guido van Rossum is over 16?"); + await expect(await $(ageConfirmationBlock.questionText()).getText()).toBe("Guido van Rossum is over 16?"); await $(ageConfirmationBlock.ageConfirmYes()).click(); await click(ageConfirmationBlock.submit()); - await expect(await $(basicVariantsSummary.ageQuestion()).getText()).to.contain("What age is Guido van Rossum"); - await expect(await $(basicVariantsSummary.ageAnswer()).getText()).to.contain("63"); + await expect(await $(basicVariantsSummary.ageQuestion()).getText()).toBe("What age is Guido van Rossum?"); + await expect(await $(basicVariantsSummary.ageAnswer()).getText()).toBe("63"); await click(basicVariantsSummary.submit()); await $(currencyBlock.sterling()).click(); await click(currencyBlock.submit()); - await expect(await $(firstNumberBlock.firstNumberLabel()).getText()).to.contain("First answer in GBP"); + await expect(await $(firstNumberBlock.firstNumberLabel()).getText()).toBe("First answer in GBP"); await $(firstNumberBlock.firstNumber()).setValue(123); await click(firstNumberBlock.submit()); @@ -50,13 +50,13 @@ describe("QuestionVariants", () => { await $(secondNumberBlock.secondNumber()).setValue(321); await click(secondNumberBlock.submit()); - await expect(await $(currencySectionSummary.currencyAnswer()).getText()).to.contain("Sterling"); - await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("£"); + await expect(await $(currencySectionSummary.currencyAnswer()).getText()).toBe("Sterling"); + await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).toContain("£"); await $(currencySectionSummary.currencyAnswerEdit()).click(); await $(currencyBlock.usDollars()).click(); await click(currencyBlock.submit()); - await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).to.contain("$"); + await expect(await $(currencySectionSummary.firstNumberAnswer()).getText()).toContain("$"); }); }); diff --git a/tests/functional/spec/question_variants_first_item_in_list.spec.js b/tests/functional/spec/question_variants_first_item_in_list.spec.js index 8597d6ee38..a878f350e4 100644 --- a/tests/functional/spec/question_variants_first_item_in_list.spec.js +++ b/tests/functional/spec/question_variants_first_item_in_list.spec.js @@ -16,7 +16,7 @@ describe("Question Variants First Item in List", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await click(HubPage.submit()); - await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are the first person in the list"); + await expect(await $(ListStatusQuestion.questionText()).getText()).toBe("You are the first person in the list"); }); it("Given I am the second person on the list, When the when rule is set, Then I should the correct question variant", async () => { @@ -35,6 +35,6 @@ describe("Question Variants First Item in List", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(HubPage.summaryRowLink("personal-details-section-2")).click(); - await expect(await $(ListStatusQuestion.questionText()).getText()).to.contain("You are not the first person in the list"); + await expect(await $(ListStatusQuestion.questionText()).getText()).toBe("You are not the first person in the list"); }); }); diff --git a/tests/functional/spec/radio_checkbox_descriptions.spec.js b/tests/functional/spec/radio_checkbox_descriptions.spec.js index 5a820c1a9d..2531f1a874 100644 --- a/tests/functional/spec/radio_checkbox_descriptions.spec.js +++ b/tests/functional/spec/radio_checkbox_descriptions.spec.js @@ -9,17 +9,17 @@ describe("Checkbox and Radio item descriptions", () => { }); it("When the schema defines a description for a checkbox option, then that description is displayed", async () => { - await expect( - await $(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText(), - ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); + await expect(await $(CheckboxBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText()).toBe( + "For example first use of alliances, partnerships, outsourcing or sub-contracting", + ); }); it("When the schema defines a description for a radio option, then that description is displayed", async () => { await $(CheckboxBlockPage.newBusinessPracticesForOrganisingProcedures()).click(); await click(CheckboxBlockPage.submit()); - await expect( - await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText(), - ).to.contain("For example first use of alliances, partnerships, outsourcing or sub-contracting"); + await expect(await $(RadioBlockPage.newMethodsOfOrganisingExternalRelationshipsWithOtherFirmsOrPublicInstitutionsLabelDescription()).getText()).toBe( + "For example first use of alliances, partnerships, outsourcing or sub-contracting", + ); }); }); }); diff --git a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js index 4f7992dfaa..32e0647a09 100644 --- a/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js +++ b/tests/functional/spec/radio_optional_with_detail_answer_optional.spec.js @@ -10,26 +10,26 @@ describe("Checkbox and Radio item descriptions", () => { describe("Given the user is presented with an optional radio answer with optional detail answer", () => { it("When no answer is provided, Then the expected answer is displayed", async () => { await click(RadioNonMandatoryPage.submit()); - await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("No answer provided"); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).toBe("No answer provided"); }); it("When Toast is selected and no detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.toast()).click(); await click(RadioNonMandatoryPage.submit()); - await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).toBe("Toast"); }); it("When Other is selected and no detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.other()).click(); await click(RadioNonMandatoryPage.submit()); - await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Other"); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).toBe("Other"); }); it("When Other is selected and detail answer is provided, Then the expected answer is displayed", async () => { await $(RadioNonMandatoryPage.other()).click(); await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); await click(RadioNonMandatoryPage.submit()); - await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Eggs"); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).toContain("Eggs"); }); it("When Other is selected and detail answer is provided and the answer is changed, Then the expected answer is displayed", async () => { @@ -37,7 +37,7 @@ describe("Checkbox and Radio item descriptions", () => { await $(RadioNonMandatoryPage.otherDetail()).setValue("Eggs"); await $(RadioNonMandatoryPage.toast()).click(); await click(RadioNonMandatoryPage.submit()); - await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).to.contain("Toast"); + await expect(await $(SubmitPage.radioNonMandatoryAnswer()).getText()).toBe("Toast"); }); }); }); diff --git a/tests/functional/spec/relationships-unrelated.spec.js b/tests/functional/spec/relationships-unrelated.spec.js index 650409666f..466d253b45 100644 --- a/tests/functional/spec/relationships-unrelated.spec.js +++ b/tests/functional/spec/relationships-unrelated.spec.js @@ -29,41 +29,41 @@ describe("Unrelated Relationships", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); await click(RelationshipsPage.submit()); - await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); - await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).toBe("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).toBe("Daniel Davis"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).toBe("Eve Elliot"); }); it("When I click previous, Then I will go back to the previous relationship", async () => { await $(RelatedToAnyoneElsePage.previous()).click(); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Carla Clark is unrelated to Andrew Austin"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Carla Clark is unrelated to Andrew Austin"); }); it("When I return to the 'related to anyone else' question and select 'Yes', Then I will be taken to the next relationship for the first person", async () => { await click(RelationshipsPage.submit()); await $(RelatedToAnyoneElsePage.yes()).click(); await click(RelatedToAnyoneElsePage.submit()); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Andrew Austin, Daniel Davis is their"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Thinking about Andrew Austin, Daniel Davis is their"); }); it("When I click previous, Then I will go back to the 'related to anyone else' question", async () => { await $(RelationshipsPage.previous()).click(); - await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - await expect(await $(RelatedToAnyoneElsePage.yes()).isSelected()).to.be.true; + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).toBe("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.yes()).isSelected()).toBe(true); }); it("When I select 'No' to the 'related to anyone else' question, Then I will be taken to the first relationship for the second person", async () => { await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).click(); await click(RelatedToAnyoneElsePage.submit()); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Thinking about Betty Burns, Carla Clark is their"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Thinking about Betty Burns, Carla Clark is their"); }); it("When I click previous, Then I will go back to the 'related to anyone else' question for the first person", async () => { await $(RelationshipsPage.previous()).click(); - await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).to.contain("Are any of these people related to you?"); - await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).to.equal("Daniel Davis"); - await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).to.equal("Eve Elliot"); - await expect(await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).isSelected()).to.be.true; + await expect(await $(RelatedToAnyoneElsePage.questionText()).getText()).toBe("Are any of these people related to you?"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(1)).getText()).toBe("Daniel Davis"); + await expect(await $(RelatedToAnyoneElsePage.listLabel(2)).getText()).toBe("Eve Elliot"); + await expect(await $(RelatedToAnyoneElsePage.noNoneOfThesePeopleAreRelatedToMe()).isSelected()).toBe(true); }); it("When I click complete the remaining relationships, Then I will go to the relationships section complete page", async () => { @@ -80,7 +80,7 @@ describe("Unrelated Relationships", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); await click(RelationshipsPage.submit()); - await expect(await browser.getUrl()).to.contain(RelationshipsInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(RelationshipsInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/relationships.spec.js b/tests/functional/spec/relationships.spec.js index ba8ee11f60..eed441f469 100644 --- a/tests/functional/spec/relationships.spec.js +++ b/tests/functional/spec/relationships.spec.js @@ -23,7 +23,7 @@ describe("Relationships", () => { await $(ListCollectorPage.no()).click(); // eslint-disable-next-line no-undef await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); }); it("When I add two household members, Then I will be asked about one relationship", async () => { @@ -40,11 +40,11 @@ describe("Relationships", () => { await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); + await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); }); describe("When I add three household members,", () => { @@ -62,20 +62,20 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); }); it("And go to the first relationship, Then the previous link should return to the list collector", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(RelationshipsPage.previous()).click(); - await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); + await expect(browser).toHaveUrlContaining("/questionnaire/list-collector/"); }); it("And go to the first relationship, Then the 'Brother or Sister' option should have the text 'Including half brother or half sister'", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await $(RelationshipsPage.brotherOrSisterLabelDescription()).getText()).to.contain("Including half brother or half sister"); + await expect(await $(RelationshipsPage.brotherOrSisterLabelDescription()).getText()).toBe("Including half brother or half sister"); }); it("And go to the second relationship, Then the previous link should return to the first relationship", async () => { @@ -85,8 +85,8 @@ describe("Relationships", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.previous()).click(); await click(RelationshipsInterstitialPage.submit()); - await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Marcus"); + await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Marcus"); }); it("And go to the section summary, Then the previous link should return to the last relationship Interstitial", async () => { @@ -99,11 +99,11 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); - await expect(await browser.getUrl()).to.contain(RelationshipsPage.pageName); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Olivia"); + await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Olivia"); }); it("When I add all relationships and return to the relationships, Then the relationships should be populated", async () => { @@ -116,26 +116,26 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); - await expect(await $(RelationshipsPage.husbandOrWife()).isSelected()).to.be.true; + await expect(await $(RelationshipsPage.husbandOrWife()).isSelected()).toBe(true); await $(RelationshipsPage.previous()).click(); - await expect(await $(RelationshipsPage.legallyRegisteredCivilPartner()).isSelected()).to.be.true; + await expect(await $(RelationshipsPage.legallyRegisteredCivilPartner()).isSelected()).toBe(true); }); it("And go to the first relationship, Then the person's name should be in the question title and playback text", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await $(ListCollectorPage.questionText()).getText()).to.contain("Marcus Twin"); - await expect(await $(RelationshipsPage.playback()).getText()).to.contain("Marcus Twin"); + await expect(await $(ListCollectorPage.questionText()).getText()).toContain("Marcus Twin"); + await expect(await $(RelationshipsPage.playback()).getText()).toContain("Marcus Twin"); }); it("And go to the first relationship and submit without selecting an option, Then an error should be displayed", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await click(RelationshipsPage.submit()); - await expect(await $(RelationshipsPage.error()).isDisplayed()).to.be.true; + await expect(await $(RelationshipsPage.error()).isDisplayed()).toBe(true); }); it("And go to the first relationship and click 'Save and sign out', Then I should be signed out", async () => { @@ -143,14 +143,14 @@ describe("Relationships", () => { await click(ListCollectorPage.submit()); await $(RelationshipsPage.husbandOrWife()).click(); await $(RelationshipsPage.saveSignOut()).click(); - await expect(await browser.getUrl()).to.not.contain("questionnaire"); + await expect(await browser.getUrl()).not.toContain("questionnaire"); }); it("And go to the first relationship, select a relationship and click 'Save and sign out', Then I should be signed out", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(RelationshipsPage.saveSignOut()).click(); - await expect(await browser.getUrl()).to.not.contain("questionnaire"); + await expect(await browser.getUrl()).not.toContain("questionnaire"); }); }); @@ -163,7 +163,7 @@ describe("Relationships", () => { await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); - await expect(await browser.getUrl()).to.contain("/sections/section/"); + await expect(browser).toHaveUrlContaining("/sections/section/"); }); it("Then I add another household member I will be redirected to parent list collector", async () => { @@ -171,7 +171,7 @@ describe("Relationships", () => { await $(ListCollectorAddPage.firstName()).setValue("Tom"); await $(ListCollectorAddPage.lastName()).setValue("Bowden"); await click(ListCollectorAddPage.submit()); - await expect(await browser.getUrl()).to.contain("/questionnaire/list-collector/"); + await expect(browser).toHaveUrlContaining("/questionnaire/list-collector/"); }); }); diff --git a/tests/functional/spec/relationships_primary.spec.js b/tests/functional/spec/relationships_primary.spec.js index ee22c938a3..44b758c19d 100644 --- a/tests/functional/spec/relationships_primary.spec.js +++ b/tests/functional/spec/relationships_primary.spec.js @@ -18,7 +18,7 @@ describe("Relationships - Primary Person", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is your"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("is your"); }); it("When I add household members, Then non-primary relationships will be asked as a non primary person", async () => { @@ -30,7 +30,7 @@ describe("Relationships - Primary Person", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.relationshipSonOrDaughter()).click(); await click(RelationshipsPage.submit()); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("is their"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("is their"); }); it("When I add household members And add their relationships And remove the primary person And add a new primary person then I will be asked for the relationships again", async () => { @@ -51,7 +51,7 @@ describe("Relationships - Primary Person", () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await $(RelationshipsPage.questionText()).getText()).to.contain("Samuel Clemens is your"); + await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Samuel Clemens is your"); }); async function addPrimaryAndTwoOthersAndCompleteRelationships() { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js index 992153844f..f70896c05e 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js @@ -96,8 +96,8 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsSmokerPage.no()).click(); await click(RepeatingIsSmokerPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("45"); await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); @@ -108,8 +108,8 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsDependentPage.yes()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("10"); }); it("When I answer 'Yes' to skipping the age question and populate the household, Then in each repeating section I am not asked their age", async () => { @@ -122,8 +122,8 @@ describe("Routing and skipping section dependencies", () => { await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.no()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).toBe(false); await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); @@ -132,8 +132,8 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsDependentPage.yes()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).toBe(false); }); }); @@ -144,12 +144,12 @@ describe("Routing and skipping section dependencies", () => { it("When I answer 'No' to skipping the section question and 'Yes' to enable the section question, Then the household summary will be visible on the hub", async () => { await answerNoToSkipEnableQuestionAndYesToEnableSection(); - await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).toBe(true); }); it("When I answer 'No' to skipping the section question and 'No' to enable the section question, Then the household summary will not be visible on the hub", async () => { await answerNoToSkipEnableQuestionAndNoToEnableSection(); - await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).toBe(false); }); }); @@ -161,7 +161,7 @@ describe("Routing and skipping section dependencies", () => { await answerNoToSkipEnableQuestionAndYesToEnableSection(); await changeSkipEnableQuestionToYes(); - await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("household-section")).isExisting()).toBe(false); }); }); @@ -187,7 +187,7 @@ describe("Routing and skipping section dependencies", () => { await expectPersonalDetailsName(); await expectPersonalDetailsAge(); - await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).toBe( "I did, but it was removed from the path as I changed my answer to No on the skip question", ); }); @@ -205,17 +205,17 @@ describe("Routing and skipping section dependencies", () => { await answerAndSubmitReasonForNoConfirmationQuestion(); await click(PrimaryPersonSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).toBe("Completed"); await editNoToSkipAgeQuestion(); - await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).toBe("Partially completed"); }); it("When I change my answer back to skipping age to 'Yes', Then the Primary Person section status is changed back to Completed", async () => { await editYesToSkipAgeQuestion(); - await expect(await $(HubPage.summaryRowState("primary-person")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("primary-person")).getText()).toBe("Completed"); }); }); @@ -236,15 +236,15 @@ describe("Routing and skipping section dependencies", () => { await editNoToSkipAgeQuestion(); - await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Partially completed"); - await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).toBe("Not started"); }); it("When I change my answer back to skipping age to 'Yes', Then the Partially completed household member status is changed back to Completed and the other stays as not started", async () => { await editYesToSkipAgeQuestion(); - await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).to.equal("Completed"); - await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).to.equal("Not started"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("household-personal-details-section-2")).getText()).toBe("Not started"); }); }); @@ -268,9 +268,9 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsSmokerPage.no()).click(); await click(RepeatingIsSmokerPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("45"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).to.contain("No"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("45"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).toBe("No"); await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); @@ -283,9 +283,9 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsSmokerPage.no()).click(); await click(RepeatingIsSmokerPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("19"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).to.contain("No"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("19"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).getText()).toBe("No"); }); it("When I answer 'No' to skipping the age question and populate the household with Repeating Age < 18, Then in each repeating section I am not asked if they are smoker", async () => { @@ -301,9 +301,9 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsDependentPage.yes()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("15"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("15"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).toBe(false); await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); @@ -314,9 +314,9 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsDependentPage.yes()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).to.contain("10"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).getText()).toBe("10"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).toBe(false); }); it("When I answer 'Yes' to skipping the age question and populate the household, Then in each repeating section I am not asked if they are smoker", async () => { @@ -329,9 +329,9 @@ describe("Routing and skipping section dependencies", () => { await click(RepeatingSexPage.submit()); await $(RepeatingIsDependentPage.no()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Female"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Female"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).toBe(false); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).toBe(false); await click(HouseHoldPersonalDetailsSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("household-personal-details-section-2")).click(); @@ -340,9 +340,9 @@ describe("Routing and skipping section dependencies", () => { await $(RepeatingIsDependentPage.yes()).click(); await click(RepeatingIsDependentPage.submit()); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).to.contain("Male"); - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).to.be.false; - await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).to.be.false; + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingSexAnswer()).getText()).toBe("Male"); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingAgeAnswer()).isExisting()).toBe(false); + await expect(await $(HouseHoldPersonalDetailsSectionSummaryPage.repeatingIsSmokerAnswer()).isExisting()).toBe(false); }); }); }); @@ -470,23 +470,21 @@ const answerAndSubmitReasonForNoConfirmationQuestion = async () => { }; const expectPersonalDetailsName = async () => { - await expect(await $(PrimaryPersonSummaryPage.nameAnswer()).getText()).to.contain("John Smith"); + await expect(await $(PrimaryPersonSummaryPage.nameAnswer()).getText()).toBe("John Smith"); }; const expectPersonalDetailsAge = async () => { - await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).getText()).to.contain("50"); + await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).getText()).toBe("50"); }; const expectReasonNoConfirmationAnswer = async () => { - await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).to.contain( - "I did not visit section 2, so confirmation was not needed", - ); + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).getText()).toBe("I did not visit section 2, so confirmation was not needed"); }; const expectPersonalDetailsAgeExistingFalse = async () => { - await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).isExisting()).to.be.false; + await expect(await $(PrimaryPersonSummaryPage.ageAnswer()).isExisting()).toBe(false); }; const expectReasonNoConfirmationExistingFalse = async () => { - await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).isExisting()).to.be.false; + await expect(await $(PrimaryPersonSummaryPage.reasonNoConfirmationAnswer()).isExisting()).toBe(false); }; diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 7ed73fa8e0..59e5bf2aef 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -18,9 +18,9 @@ describe("Routing and skipping section dependencies based on calculated summarie }); it("When the calculated summary total has not been set, Then the dependent section should not be enabled", async () => { - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(false); }); it("When the calculated summary total is equal to £100, Then the dependent section should be enabled", async () => { @@ -35,9 +35,9 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(true); }); it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is not met (total less than £10), then the dependent question should be displayed", async () => { @@ -53,7 +53,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); - await expect(await browser.getUrl()).to.contain(FruitPage.pageName); + await expect(browser).toHaveUrlContaining(FruitPage.pageName); }); it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is met (total greater than £10), then the dependent question should not be displayed", async () => { @@ -69,7 +69,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); - await expect(await browser.getUrl()).to.contain(VegetablesPage.pageName); + await expect(browser).toHaveUrlContaining(VegetablesPage.pageName); }); it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is greater than £100, then we should be routed to the second question block", async () => { @@ -87,7 +87,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); await click(VegetablesPage.submit()); - await expect(await browser.getUrl()).to.contain(SecondQuestionBlockPage.pageName); + await expect(browser).toHaveUrlContaining(SecondQuestionBlockPage.pageName); }); it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is less than £100, then we should be routed to the section summary", async () => { @@ -105,7 +105,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); await click(VegetablesPage.submit()); - await expect(await browser.getUrl()).to.contain(DependentQuestionSectionSummaryPage.pageName); + await expect(browser).toHaveUrlContaining(DependentQuestionSectionSummaryPage.pageName); }); it("When a question in another section has a dependency on a calculated summary total, and both sections are complete, and I go back and edit the calculated summary total, then the dependent section status should be in progress", async () => { @@ -124,16 +124,16 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(VegetablesPage.yes()).click(); await click(VegetablesPage.submit()); await click(DependentQuestionSectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Completed"); + await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); await $(FirstQuestionBlockPage.milk()).setValue(100); await click(FirstQuestionBlockPage.submit()); - await expect(await browser.getUrl()).to.contain(CurrencyTotalPlaybackPage.pageName); + await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).to.equal("Partially completed"); + await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).toBe("Partially completed"); }); it("When the calculated summary total is less than £100 but additional answers on the path are opened up as a result of editing an answer, Then the dependent section should be enabled", async () => { @@ -148,9 +148,9 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(false); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); @@ -161,9 +161,9 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(true); }); it("When the calculated summary total is equal to £100 but answers on the path are remove as a result of an answer edit, Then the dependent section should be enabled", async () => { @@ -180,9 +180,9 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.true; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(true); await $(HubPage.summaryRowLink("calculated-summary-section")).click(); await $(CalculatedSummarySectionSummaryPage.skipButterBlockAnswerEdit()).click(); @@ -190,9 +190,9 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(SkipQuestionPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); - await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).to.be.true; - await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).to.be.false; + await expect(await $(HubPage.summaryRowLink("calculated-summary-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-question-section")).isExisting()).toBe(true); + await expect(await $(HubPage.summaryRowLink("dependent-enabled-section")).isExisting()).toBe(false); }); }); }); diff --git a/tests/functional/spec/routing_checkbox_contains.spec.js b/tests/functional/spec/routing_checkbox_contains.spec.js index ca42bbec2f..be317ee262 100644 --- a/tests/functional/spec/routing_checkbox_contains.spec.js +++ b/tests/functional/spec/routing_checkbox_contains.spec.js @@ -10,12 +10,12 @@ describe("Routing Checkbox Contains Condition.", () => { it('Given a list of checkbox options, when I have don\'t select "Liechtenstein" and select the option "India" or the option "Azerbaijan" or both then I should be routed to the "contains any" condition page', async () => { // When - await expect(await $(RoutingCheckboxContains.liechtenstein()).isSelected()).to.be.false; + await expect(await $(RoutingCheckboxContains.liechtenstein()).isSelected()).toBe(false); await $(RoutingCheckboxContains.india()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -26,7 +26,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -36,7 +36,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(ContainsAnyPage.pageName); + await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); }); it('Given a list of checkbox options, when I select the option "Malta" or the option "Liechtenstein" or both then I should be routed to the summary condition page', async () => { @@ -44,7 +44,7 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.liechtenstein()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -55,7 +55,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -65,7 +65,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it('Given a list of checkbox options, when I select the options "India", "Azerbaijan" and "Liechtenstein" then I should be routed to the "contains all" condition page', async () => { @@ -75,6 +75,6 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.liechtenstein()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(await browser.getUrl()).to.contain(ContainsAllPage.pageName); + await expect(browser).toHaveUrlContaining(ContainsAllPage.pageName); }); }); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index a41ff7de45..be0ef695cc 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -19,10 +19,10 @@ describe("Save sign out / Exit", () => { await browser.openQuestionnaire("test_introduction.json"); await $(IntroductionPage.exitButton()).click(); - await expect(await browser.getUrl()).to.contain("/surveys/todo"); + await expect(browser).toHaveUrlContaining("/surveys/todo"); await browser.back(); - await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); + await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); }); it("Given I am completing a questionnaire, when I select save and sign out, then I am redirected to the signed out page", async () => { @@ -32,10 +32,10 @@ describe("Save sign out / Exit", () => { await click(SetMinMax.submit()); await $(TestMinMax.saveSignOut()).click(); - await expect(await browser.getUrl()).to.contain("/signed-out"); + await expect(browser).toHaveUrlContaining("/signed-out"); await browser.back(); - await expect(await $("body").getHTML()).to.contain("Sorry, you need to sign in again"); + await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); }); it("Given I have started a questionnaire, when I return to the questionnaire, then I am returned to the page I was on and can then complete the questionnaire", async () => { @@ -57,49 +57,49 @@ describe("Save sign out / Exit", () => { await click(currencySectionSummary.submit()); await click(SubmitPage.submit()); - await expect(await browser.getUrl()).to.contain("thank-you"); + await expect(browser).toHaveUrlContaining("thank-you"); }); it("Given a I have started a social questionnaire, when I select save and sign out, then I am redirected to the signed out page and the correct access code link is shown", async () => { await browser.openQuestionnaire("test_theme_social.json", { version: "v2", theme: "social" }); await $(SubmitPage.saveSignOut()).click(); - await expect(await browser.getUrl()).to.contain("/signed-out"); - await expect(await $("body").getHTML()).to.contain("Your progress has been saved"); - await expect(await $("body").getHTML()).to.contain("To resume the survey,"); - await expect(await $("body").getHTML()).to.contain("/en/start"); + await expect(browser).toHaveUrlContaining("/signed-out"); + await expect(await $("body").getHTML()).toContain("Your progress has been saved"); + await expect(await $("body").getHTML()).toContain("To resume the survey,"); + await expect(await $("body").getHTML()).toContain("/en/start"); }); it("Given a I have started a business questionnaire, when I select save and sign out, then I am redirected to the signed out page and the correct access code link is shown", async () => { await browser.openQuestionnaire("test_introduction.json"); await $(IntroductionPage.getStarted()).click(); await $(IntroInterstitialPage.saveSignOut()).click(); - await expect(await browser.getUrl()).to.contain("/signed-out"); - await expect(await $("body").getHTML()).to.contain("Your progress has been saved"); - await expect(await $("body").getHTML()).to.contain("To find further information or resume the survey,"); - await expect(await $("body").getHTML()).to.contain("/surveys/todo"); + await expect(browser).toHaveUrlContaining("/signed-out"); + await expect(await $("body").getHTML()).toContain("Your progress has been saved"); + await expect(await $("body").getHTML()).toContain("To find further information or resume the survey,"); + await expect(await $("body").getHTML()).toContain("/surveys/todo"); }); it("Given a business questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { await browser.openQuestionnaire("test_introduction.json"); - await expect(await $(IntroductionPage.exitButton()).getText()).to.contain("Exit"); + await expect(await $(IntroductionPage.exitButton()).getText()).toBe("Exit"); await $(IntroductionPage.getStarted()).click(); - await expect(await $(IntroInterstitialPage.saveSignOut()).getText()).to.contain("Save and exit survey"); + await expect(await $(IntroInterstitialPage.saveSignOut()).getText()).toBe("Save and exit survey"); await click(IntroInterstitialPage.submit()); - await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and exit survey"); + await expect(await $(SubmitPage.saveSignOut()).getText()).toBe("Save and exit survey"); await click(SubmitPage.submit()); - await expect(await $(IntroThankYouPagePage.exitButton()).isExisting()).to.be.false; + await expect(await $(IntroThankYouPagePage.exitButton()).isExisting()).toBe(false); }); it("Given a Census questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { await browser.openQuestionnaire("test_thank_you_census_household.json"); - await expect(await $(HouseHolderConfirmationPage.saveSignOut()).getText()).to.contain("Save and complete later"); + await expect(await $(HouseHolderConfirmationPage.saveSignOut()).getText()).toBe("Save and complete later"); await click(HouseHolderConfirmationPage.submit()); - await expect(await $(SubmitPage.saveSignOut()).getText()).to.contain("Save and complete later"); + await expect(await $(SubmitPage.saveSignOut()).getText()).toBe("Save and complete later"); }); }); diff --git a/tests/functional/spec/skip_condition_block.spec.js b/tests/functional/spec/skip_condition_block.spec.js index 2fa483db9d..2eb93ab759 100644 --- a/tests/functional/spec/skip_condition_block.spec.js +++ b/tests/functional/spec/skip_condition_block.spec.js @@ -13,13 +13,13 @@ describe("Skip Conditions - Block", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); await click(QuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); await click(QuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SkipPage.pageName); + await expect(browser).toHaveUrlContaining(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_condition_group.spec.js b/tests/functional/spec/skip_condition_group.spec.js index cc4e18b56d..8473ced58f 100644 --- a/tests/functional/spec/skip_condition_group.spec.js +++ b/tests/functional/spec/skip_condition_group.spec.js @@ -13,13 +13,13 @@ describe("Skip Conditions - Group", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); await click(QuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); await click(QuestionPage.submit()); - await expect(await browser.getUrl()).to.contain(SkipPage.pageName); + await expect(browser).toHaveUrlContaining(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_condition_list.spec.js b/tests/functional/spec/skip_condition_list.spec.js index 95bfee02d1..2ec01c2ece 100644 --- a/tests/functional/spec/skip_condition_list.spec.js +++ b/tests/functional/spec/skip_condition_list.spec.js @@ -13,7 +13,7 @@ describe("Feature: Routing on lists", () => { it("When I don't add a person to the list, Then the less than two people skippable page should be shown", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(LessThanTwoInterstitialPage.pageName); }); it("When I add one person to the list, Then the less than two people skippable page should be shown", async () => { @@ -24,7 +24,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(LessThanTwoInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(LessThanTwoInterstitialPage.pageName); }); it("When I add two people to the list, Then the two people skippable page should be shown", async () => { @@ -40,7 +40,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(TwoInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(TwoInterstitialPage.pageName); }); it("When I add three people to the list, Then the more than two people skippable page should be shown", async () => { @@ -61,7 +61,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(await browser.getUrl()).to.contain(MoreThanTwoInterstitialPage.pageName); + await expect(browser).toHaveUrlContaining(MoreThanTwoInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/skip_conditions_not_set.spec.js b/tests/functional/spec/skip_conditions_not_set.spec.js index 5f7f56b67b..5bd576b804 100644 --- a/tests/functional/spec/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/skip_conditions_not_set.spec.js @@ -9,12 +9,12 @@ describe("Skip Conditions - Not Set", () => { it("Given I do not complete the first page, Then I should see the summary page", async () => { await click(FoodPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I complete the first page, Then I should see the drink page", async () => { await $(FoodPage.bacon()).click(); await click(FoodPage.submit()); - await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); + await expect(browser).toHaveUrlContaining(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/skip_conditions_set.spec.js b/tests/functional/spec/skip_conditions_set.spec.js index 99d4aca253..491c6e344f 100644 --- a/tests/functional/spec/skip_conditions_set.spec.js +++ b/tests/functional/spec/skip_conditions_set.spec.js @@ -10,11 +10,11 @@ describe("Skip Conditions - Set", () => { it("Given I complete the first page, Then I should see the summary page", async () => { await $(FoodPage.bacon()).click(); await click(FoodPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); }); it("Given I do not complete the first page, Then I should see the drink page", async () => { await click(FoodPage.submit()); - await expect(await browser.getUrl()).to.contain(DrinkPage.pageName); + await expect(browser).toHaveUrlContaining(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/submit_with_custom_submission_text.spec.js b/tests/functional/spec/submit_with_custom_submission_text.spec.js index f933804061..569bb90c8a 100644 --- a/tests/functional/spec/submit_with_custom_submission_text.spec.js +++ b/tests/functional/spec/submit_with_custom_submission_text.spec.js @@ -11,8 +11,8 @@ describe("Submit with custom submission text", () => { await $(IntroductionPage.getStarted()).click(); await $(BreakfastPage.answer()).setValue("Eggs"); await click(BreakfastPage.submit()); - await expect(await $(SubmitPage.heading()).getText()).to.contain("Submit your questionnaire"); - await expect(await $(SubmitPage.warning()).getText()).to.contain("You cannot view your answers after submission"); - await expect(await $(SubmitPage.guidance()).getText()).to.contain("Thank you for your answers, submit this to complete it"); + await expect(await $(SubmitPage.heading()).getText()).toBe("Submit your questionnaire"); + await expect(await $(SubmitPage.warning()).getText()).toBe("You cannot view your answers after submission"); + await expect(await $(SubmitPage.guidance()).getText()).toBe("Thank you for your answers, submit this to complete it"); }); }); diff --git a/tests/functional/spec/textarea.spec.js b/tests/functional/spec/textarea.spec.js index 0de7384c0c..495fe5d1bb 100644 --- a/tests/functional/spec/textarea.spec.js +++ b/tests/functional/spec/textarea.spec.js @@ -10,40 +10,40 @@ describe("Textarea", () => { }); it("Given a textarea option, a user should be able to click the label of the textarea to focus", async () => { await $(TextareaBlock.answerLabel()).click(); - await expect(await $(TextareaBlock.answer()).isFocused()).to.be.true; + await expect(await $(TextareaBlock.answer()).isFocused()).toBe(true); }); it('Given a textarea option, When no text is entered, Then the summary should display "No answer provided"', async () => { await click(TextareaBlock.submit()); - await expect(await $(TextareaSummary.answer()).getText()).to.contain("No answer provided"); + await expect(await $(TextareaSummary.answer()).getText()).toBe("No answer provided"); }); it("Given a textarea option, When some text is entered, Then the summary should display the text", async () => { await $(TextareaBlock.answer()).setValue("Some text"); await click(TextareaBlock.submit()); - await expect(await $(TextareaSummary.answer()).getText()).to.contain("Some text"); + await expect(await $(TextareaSummary.answer()).getText()).toBe("Some text"); }); it("Given a text entered in textarea , When user submits and revisits the textarea, Then the textarea must contain the text entered previously", async () => { await $(TextareaBlock.answer()).setValue("'Twenty><&Five'"); await click(TextareaBlock.submit()); - await expect(await $(TextareaSummary.answer()).getText()).to.contain("'Twenty><&Five'"); + await expect(await $(TextareaSummary.answer()).getText()).toBe("'Twenty><&Five'"); await $(TextareaSummary.answerEdit()).click(); await $(TextareaBlock.answer()).getValue(); }); it("Displays the number of characters remaining", async () => { - await expect(await $(textareaLimit).getText()).to.contain("20"); + await expect(await $(textareaLimit).getText()).toContain("20"); }); it("Updates the number of characters remaining when the user adds content", async () => { await $(TextareaBlock.answer()).setValue("Banjo"); - await expect(await $(textareaLimit).getText()).to.contain("15"); + await expect(await $(textareaLimit).getText()).toContain("15"); }); it("The user is unable to add more characters when the limit is reached", async () => { await $(TextareaBlock.answer()).setValue("This sentence is over twenty characters long"); - await expect(await $(textareaLimit).getText()).to.contain("0"); + await expect(await $(textareaLimit).getText()).toContain("0"); await $(TextareaBlock.answer()).getValue(); }); }); diff --git a/tests/functional/spec/textfield.spec.js b/tests/functional/spec/textfield.spec.js index b842e004c2..cb53365171 100644 --- a/tests/functional/spec/textfield.spec.js +++ b/tests/functional/spec/textfield.spec.js @@ -5,15 +5,15 @@ describe("Textfield", () => { it("Given a textfield option, a user should be able to click the label of the textfield to focus", async () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.nameLabel()).click(); - await expect(await $(TextFieldPage.name()).isFocused()).to.be.true; + await expect(await $(TextFieldPage.name()).isFocused()).toBe(true); }); it("Given a text entered in textfield , When user submits and revisits the textfield, Then the textfield must contain the text entered previously", async () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.name()).setValue("'Twenty><&Five'"); await click(TextFieldPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.pageName); - await expect(await $(SubmitPage.nameAnswer()).getText()).to.contain("Twenty><&Five'"); + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(await $(SubmitPage.nameAnswer()).getText()).toBe("'Twenty><&Five'"); await $(SubmitPage.nameAnswerEdit()).click(); await $(TextFieldPage.name()).getValue(); }); @@ -22,6 +22,6 @@ describe("Textfield", () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.name()).setValue("This string is too long"); await click(TextFieldPage.submit()); - await expect(await $(TextFieldPage.errorNumber(1)).getText()).to.contain("You have entered too many characters. Enter up to 20 characters"); + await expect(await $(TextFieldPage.errorNumber(1)).getText()).toBe("You have entered too many characters. Enter up to 20 characters"); }); }); diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index 8aea9ebfb7..3ad986fcbd 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -7,7 +7,7 @@ describe("Suggestions", () => { await browser.openQuestionnaire("test_textfield_suggestions.json"); await $(SuggestionsPage.country()).setValue("Uni"); $("#country-answer-listbox li").waitForDisplayed(); - await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); + await expect(await $$(".ons-js-autosuggest-listbox li").length).not.toBe(0); }); }); @@ -30,10 +30,10 @@ describe("Suggestions", () => { await browser.pause(500); await browser.keys(" United"); await suggestionsList.waitForExist(); - await expect(await $$(".ons-js-autosuggest-listbox li").length).to.not.equal(0); + await expect(await $$(".ons-js-autosuggest-listbox li").length).not.toBe(0); // TODO there is an issue with the load-time of the auto-suggest dropdown causing this test to fail. Uncomment when this has been resolved. // await suggestionsOption.click(); await click(MultipleSuggestionsPage.submit()); - await expect(await browser.getUrl()).to.contain(SubmitPage.url()); + await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); }); diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index 4596278662..7e97ca44be 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -14,11 +14,11 @@ describe("Thank You Social", () => { it("When I navigate to the thank you page, Then I should see social theme content", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Social Survey"); - await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted"); - await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); - await expect(await $(ThankYouPage.metadata()).getHTML()).to.not.contain("Submission reference:"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test Social Survey"); + await expect(await $(ThankYouPage.guidance()).getHTML()).toContain("Your answers have been submitted"); + await expect(await $(ThankYouPage.metadata()).getHTML()).toContain("Submitted on:"); + await expect(await $(ThankYouPage.metadata()).getHTML()).not.toContain("Submission reference:"); }); }); }); @@ -33,11 +33,11 @@ describe("Thank You Default", () => { await $(CheckboxPage.good()).click(); await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Question Title Test"); - await expect(await $(ThankYouPage.guidance()).getHTML()).to.contain("Your answers have been submitted for"); - await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submitted on:"); - await expect(await $(ThankYouPage.metadata()).getHTML()).to.contain("Submission reference:"); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Question Title Test"); + await expect(await $(ThankYouPage.guidance()).getHTML()).toContain("Your answers have been submitted for"); + await expect(await $(ThankYouPage.metadata()).getHTML()).toContain("Submitted on:"); + await expect(await $(ThankYouPage.metadata()).getHTML()).toContain("Submission reference:"); }); }); }); @@ -49,24 +49,22 @@ describe("Thank You Default View Response Enabled", () => { await $(DidYouKnowPage.yes()).click(); await click(DidYouKnowPage.submit()); await click(ThankYouSubmitPage.submit()); - await expect(await browser.getUrl()).to.contain(ThankYouPage.pageName); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); it("When I navigate to the thank you page, and I have submitted less than 40 seconds ago, Then I should see the countdown timer and option to view my answers", async () => { - await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; - await expect(await $(ThankYouPage.title()).getHTML()).to.contain("Thank you for completing the Test Thank You"); - await expect(await $(ThankYouPage.viewAnswersTitle()).getHTML()).to.contain("Get a copy of your answers"); - await expect(await $(ThankYouPage.viewAnswersLink()).getText()).to.contain("save or print your answers"); - await expect(await $(ThankYouPage.viewSubmittedCountdown()).getHTML()).to.contain( - "For security, your answers will only be available to view for another", - ); + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).toBe(false); + await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test Thank You"); + await expect(await $(ThankYouPage.viewAnswersTitle()).getHTML()).toContain("Get a copy of your answers"); + await expect(await $(ThankYouPage.viewAnswersLink()).getText()).toContain("save or print your answers"); + await expect(await $(ThankYouPage.viewSubmittedCountdown()).getHTML()).toContain("For security, your answers will only be available to view for another"); }); it("When I navigate to the thank you page, and I have submitted more than 40 seconds ago, Then I shouldn't see the option to view my answers", async () => { - await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.false; + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).toBe(false); await browser.pause(46000); // Waiting 40 seconds for the timeout to expire (45 minute timeout changed to 35 seconds by overriding VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS for the purpose of the functional test) - await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).to.be.true; - await expect(await $(ThankYouPage.viewSubmittedGuidance()).getHTML()).to.contain("For security, you can no longer view or get a copy of your answers"); + await expect(await $(ThankYouPage.viewSubmittedGuidance()).isDisplayed()).toBe(true); + await expect(await $(ThankYouPage.viewSubmittedGuidance()).getHTML()).toContain("For security, you can no longer view or get a copy of your answers"); }); }); }); diff --git a/tests/functional/spec/theme_dbt.spec.js b/tests/functional/spec/theme_dbt.spec.js index cab192f328..5ad48e9826 100644 --- a/tests/functional/spec/theme_dbt.spec.js +++ b/tests/functional/spec/theme_dbt.spec.js @@ -7,8 +7,8 @@ describe("Theme DBT", () => { }); it("When I navigate to the radio page, Then I should see DBT theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade"); }); }); }); diff --git a/tests/functional/spec/theme_dbt_dsit.spec.js b/tests/functional/spec/theme_dbt_dsit.spec.js index 7f129e36ae..1b3740ec80 100644 --- a/tests/functional/spec/theme_dbt_dsit.spec.js +++ b/tests/functional/spec/theme_dbt_dsit.spec.js @@ -7,9 +7,9 @@ describe("Theme DBT-DSIT", () => { }); it("When I navigate to the radio page, Then I should see DBT-DSIT theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade logo"); - await expect(await $("#dsit-logo-alt").getHTML()).to.contain("Department for Science, Innovation and Technology logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade logo"); + await expect(await $("#dsit-logo-alt").getHTML()).toContain("Department for Science, Innovation and Technology logo"); }); }); }); diff --git a/tests/functional/spec/theme_dbt_dsit_ni.spec.js b/tests/functional/spec/theme_dbt_dsit_ni.spec.js index 80e78bb78c..3f47a86ebe 100644 --- a/tests/functional/spec/theme_dbt_dsit_ni.spec.js +++ b/tests/functional/spec/theme_dbt_dsit_ni.spec.js @@ -7,10 +7,10 @@ describe("Theme DBT-DSIT-NI", () => { }); it("When I navigate to the radio page, Then I should see DBT-DSIT-NI theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade logo"); - await expect(await $("#dsit-logo-alt").getHTML()).to.contain("Department for Science, Innovation and Technology logo"); - await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade logo"); + await expect(await $("#dsit-logo-alt").getHTML()).toContain("Department for Science, Innovation and Technology logo"); + await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_dbt_ni.spec.js b/tests/functional/spec/theme_dbt_ni.spec.js index 3cb462124f..1dbe188732 100644 --- a/tests/functional/spec/theme_dbt_ni.spec.js +++ b/tests/functional/spec/theme_dbt_ni.spec.js @@ -7,9 +7,9 @@ describe("Theme DBT-NI", () => { }); it("When I navigate to the radio page, Then I should see DBT-NI theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#dbt-logo-alt").getHTML()).to.contain("Department for Business and Trade"); - await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade"); + await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_desnz.spec.js b/tests/functional/spec/theme_desnz.spec.js index e67af59233..953d7a9c68 100644 --- a/tests/functional/spec/theme_desnz.spec.js +++ b/tests/functional/spec/theme_desnz.spec.js @@ -7,8 +7,8 @@ describe("Theme DESNZ", () => { }); it("When I navigate to the radio page, Then I should see DESNZ theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#desnz-logo-alt").getHTML()).to.contain("Department for Energy Security and Net Zero"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#desnz-logo-alt").getHTML()).toContain("Department for Energy Security and Net Zero"); }); }); }); diff --git a/tests/functional/spec/theme_desnz_ni.spec.js b/tests/functional/spec/theme_desnz_ni.spec.js index 7896f46ef5..4e92750325 100644 --- a/tests/functional/spec/theme_desnz_ni.spec.js +++ b/tests/functional/spec/theme_desnz_ni.spec.js @@ -7,9 +7,9 @@ describe("Theme DESNZ-NI", () => { }); it("When I navigate to the radio page, Then I should see DESNZ-NI theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#desnz-logo-alt").getHTML()).to.contain("Department for Energy Security and Net Zero"); - await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#desnz-logo-alt").getHTML()).toContain("Department for Energy Security and Net Zero"); + await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_northernireland.spec.js b/tests/functional/spec/theme_northernireland.spec.js index 81e1a1a7a4..e6d7b0e9cc 100644 --- a/tests/functional/spec/theme_northernireland.spec.js +++ b/tests/functional/spec/theme_northernireland.spec.js @@ -7,8 +7,8 @@ describe("Theme Northern Ireland", () => { }); it("When I navigate to the radio page, Then I should see Northern Ireland theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#finance-ni-logo-alt").getHTML()).to.contain("Northern Ireland Department of Finance logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); }); }); diff --git a/tests/functional/spec/theme_orr.spec.js b/tests/functional/spec/theme_orr.spec.js index 15f5ad86e1..e4a035246a 100644 --- a/tests/functional/spec/theme_orr.spec.js +++ b/tests/functional/spec/theme_orr.spec.js @@ -7,8 +7,8 @@ describe("Theme Rail and Road", () => { }); it("When I navigate to the radio page, Then I should see Rail and Road theme content", async () => { - await expect(await browser.getUrl()).to.contain(RadioPage.pageName); - await expect(await $("#orr-logo-mobile-alt").getHTML()).to.contain("Office of Rail and Road logo"); + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#orr-logo-mobile-alt").getHTML()).toContain("Office of Rail and Road logo"); }); }); }); diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index 7325e97135..a6102847ba 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -6,16 +6,15 @@ class TestCase { it("When the timeout modal is displayed, and I do not extend my session, Then I will be redirected to the session expired page", async () => { await this.checkTimeoutModal(); await browser.pause(65000); // We are waiting for the session to expire - await expect(await browser.getUrl()).to.contain("/session-expired"); - await expect(await $("body").getHTML()) - .to.include( - "Sorry, you need to sign in again", - "This is because you have either:", - "been inactive for 45 minutes and your session has timed out to protect your information", - "followed a link to a page you are not signed in to", - "followed a link to a survey that has already been submitted", - ) - .to.not.include("To protect your information, your progress will be saved and you will be signed out in"); + await expect(browser).toHaveUrlContaining("/session-expired"); + await expect(await $("body").getHTML()).toContain( + "Sorry, you need to sign in again", + "This is because you have either:", + "been inactive for 45 minutes and your session has timed out to protect your information", + "followed a link to a page you are not signed in to", + "followed a link to a survey that has already been submitted", + ); + await expect(await $("body").getHTML()).not.toContain("To protect your information, your progress will be saved and you will be signed out in"); }).timeout(140000); } @@ -23,11 +22,11 @@ class TestCase { it("When the timeout modal is displayed, and I click the “Continue survey” button, Then my session will be extended", async () => { await this.checkTimeoutModal(); await click(TimeoutModalPage.submit()); - await expect(await $(TimeoutModalPage.timer()).getText()).to.equal(""); + await expect(await $(TimeoutModalPage.timer()).getText()).toBe(""); await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired await browser.refresh(); - await expect(await browser.getUrl()).to.contain(await page.pageName); - await expect(await $("body").getHTML()).to.not.include("Sorry, you need to sign in again"); + await expect(browser).toHaveUrlContaining(await page.pageName); + await expect(await $("body").getHTML()).not.toContain("Sorry, you need to sign in again"); }).timeout(140000); } @@ -38,13 +37,13 @@ class TestCase { await browser.switchWindow(await page.pageName); await browser.refresh(); await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired - await expect(await browser.getUrl()).to.contain(await page.pageName); + await expect(browser).toHaveUrlContaining(await page.pageName); }).timeout(140000); } async checkTimeoutModal() { await $(TimeoutModalPage.timer()).waitForDisplayed({ timeout: 70000 }); - await expect(await $(TimeoutModalPage.timer()).getText()).to.equal( + await expect(await $(TimeoutModalPage.timer()).getText()).toBe( "To protect your information, your progress will be saved and you will be signed out in 59 seconds.", ); } diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index bbaa35304c..0f0432c0a4 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -194,11 +194,8 @@ export const config = { * @param {Array} args arguments that command would receive */ before: async function (capabilities, specs) { - const chai = require("chai"); const JwtHelper = require("./jwt_helper"); - global.expect = chai.expect; - await browser.addCommand( "openQuestionnaire", async function ( From 0bb7547b2a998e310e75fba5dca66bd262a746ad Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:44:38 +0000 Subject: [PATCH 317/567] Schemas v3.77.1 (#1244) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0049caba3f..3fa0a38d0d 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.77.0 +v3.77.1 From 9e2750e70fde3ba2a3be1604c2d9c524b1a75794 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:57:24 +0000 Subject: [PATCH 318/567] Add extra logging around session store (#1245) --- app/authentication/authenticator.py | 8 ++++++++ app/globals.py | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 5f2ac068e2..21b2cd5621 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -118,6 +118,14 @@ def load_user(extend_session: bool = True) -> Optional[User]: return user + if not session_store: + logger.info("session store doesn't exist") # pragma: no cover + + elif not _is_session_valid(session_store): + logger.info( + "session has expired", session_expiry=session_store.expiration_time + ) # pragma: no cover + logger.info("session does not exist") cookie_session.pop(USER_IK, None) diff --git a/app/globals.py b/app/globals.py index 1323d60928..b470e235f0 100644 --- a/app/globals.py +++ b/app/globals.py @@ -33,6 +33,12 @@ def get_questionnaire_store(user_id: str, user_ik: str) -> QuestionnaireStore: def get_session_store() -> SessionStore | None: if USER_IK not in cookie_session or EQ_SESSION_ID not in cookie_session: + if USER_IK not in cookie_session: + logger.info("user ik not found in cookie session") # pragma: no cover + + if EQ_SESSION_ID not in cookie_session: + logger.info("eq session id not found in cookie session") # pragma: no cover + return None # Sets up a single SessionStore instance per request context. @@ -48,6 +54,9 @@ def get_session_store() -> SessionStore | None: cookie_session[USER_IK], pepper, cookie_session[EQ_SESSION_ID] ) + if not store.session_data: + logger.info("session data not found") # pragma: no cover + return store if store.session_data else None @@ -57,7 +66,11 @@ def get_session_timeout_in_seconds(schema: QuestionnaireSchema) -> int: :return: Timeout in seconds """ default_session_timeout = current_app.config["EQ_SESSION_TIMEOUT_SECONDS"] + logger.info("default session timeout", timeout=default_session_timeout) + schema_session_timeout = schema.json.get("session_timeout_in_seconds") + logger.info("schema session timeout", timeout=schema_session_timeout) + timeout = ( schema_session_timeout if schema_session_timeout and schema_session_timeout < default_session_timeout From 131c18ef6278d850304107887519d69b485f921f Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 7 Nov 2023 09:45:34 +0000 Subject: [PATCH 319/567] Refactor the list collector repeating blocks functional test spec (#1240) --- .../list_collector_repeating_blocks.spec.js | 273 ++++++++---------- 1 file changed, 127 insertions(+), 146 deletions(-) diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index c7c829cedd..2a31cd218a 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -13,6 +13,7 @@ import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, che import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; import { expect } from "@wdio/globals"; +import ThankYouPage from "../../../base_pages/thank-you.page"; const summaryValues = 'dd[class="ons-summary__values"]'; async function proceedToListCollector() { @@ -52,13 +53,13 @@ async function addCompany( } describe("List Collector Repeating Blocks", () => { - describe("Given a normal journey through the list collector with repeating blocks, the answers can be submitted.", () => { + describe("Given a normal journey through the list collector with repeating blocks", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); // These tests sometimes fail when a button is on the screen, but right on the very edge, accept cookies to increase screen space await $(ResponsiblePartyPage.acceptCookies()).click(); }); - it("The user is able to add companies, complete repeating blocks, and submit.", async () => { + it("When the user adds items and completes all of the repeating blocks, Then they are able to successfully submit the questionnaire.", async () => { await proceedToListCollector(); await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); @@ -71,14 +72,15 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherTradingDetailsPage.submit()); await click(SectionCompaniesPage.submit()); await click(SubmitPage.submit()); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); - describe("Given a journey through the list collector with repeating blocks, the companies can be added, removed and edited.", () => { + describe("Given a journey through the list collector with repeating blocks where items need to be updated", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); }); - it("The user is able to add companies and complete repeating blocks.", async () => { + it("When the user adds items to the list and completes the repeating blocks, Then the completed items are displayed on the list collector page.", async () => { await proceedToListCollector(); await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); @@ -87,57 +89,48 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("MOD", "789", "3", "3", "2023", true); + checkItemsInList(["ONS", "GOV", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); }); - it("The list collector shows all of the companies.", async () => { - const companiesExpected = ["ONS", "GOV", "MOD"]; - checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); - }); - - it("The list collector allows the name of 'GOV' to be changed", async () => { + it("When the user edits an item, Then the name of the item is able to be changed", async () => { await $(AnyOtherCompaniesOrBranchesPage.listEditLink(2)).click(); await $(EditCompanyPage.companyOrBranchName()).setValue("Government"); await click(EditCompanyPage.submit()); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("Government"); }); - it("The list collector allows removal of 'Government'", async () => { + it("When the user clicks the remove link, Then the item selected is removed", async () => { await $(AnyOtherCompaniesOrBranchesPage.listRemoveLink(2)).click(); await $(RemoveCompanyPage.yes()).click(); await click(RemoveCompanyPage.submit()); - }); - - it("The list collector does not show 'GOV' anymore.", async () => { - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toBe("Government"); + checkItemsInList(["ONS", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); + await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toContain("Government"); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("MOD"); }); - it("The list collector can add more companies.", async () => { + it("When a user has finished editing or removing from the list, Then they are still able to add additional companies", async () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("Council", "101", "4", "4", "2023", false, true); + checkItemsInList(["ONS", "MOD", "Council"], AnyOtherCompaniesOrBranchesPage.listLabel); }); - it("The list collector shows all of the companies.", async () => { - const companiesExpected = ["ONS", "MOD", "Council", "another one"]; - checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); - }); - - it("The list collector can then be submitted", async () => { + it("When a user has finished making changes to the list, Then section can be completed and the questionnaire submitted", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(AnyOtherTradingDetailsPage.submit()); await click(SectionCompaniesPage.submit()); await click(SubmitPage.submit()); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); - describe("Given a journey through the list collector with repeating blocks, any incomplete repeating_block will be revisited before navigating to the next block after the list collector.", () => { + describe("Given a journey that test routes through the list collector with repeating blocks.", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); }); - it("The user is able to add companies complete some repeating blocks and leave others incomplete.", async () => { + it("When the user only completes some of the repeating blocks and leaves others incomplete, Then on the list collector page only completed items should display the completed checkmark icon.", async () => { await proceedToListCollector(); await addCompany("ONS", "123", "1", "1", "2023", true, true); @@ -164,20 +157,23 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("NAV", "101", "4", "4", "2023", true, true); - }); - it("The list collector shows all of the companies as well as checkmarks on the complete items 1 and 4, but not on the incomplete items 3 and 4.", async () => { - const companiesExpected = ["ONS", "GOV", "MOD", "NAV"]; - checkItemsInList(companiesExpected, AnyOtherCompaniesOrBranchesPage.listLabel); + // Only the ONS and NAV items should be complete + checkItemsInList(["ONS", "GOV", "MOD", "NAV"], AnyOtherCompaniesOrBranchesPage.listLabel); checkListItemComplete(`dt[data-qa="list-item-1-label"]`); checkListItemIncomplete(`dt[data-qa="list-item-2-label"]`); checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); checkListItemComplete(`dt[data-qa="list-item-1-label"]`); }); - it("Attempting to complete the list collector will navigate the user to the first incomplete block of the second list item.", async () => { + it("When an item has incomplete repeating blocks, Then using submit on the list collector page will navigate the user to the first incomplete repeating block.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); + await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock1Page.pageName); + }); + + it("When there are multiple incomplete items and only the first incomplete item is completed, Then attempting using Submit on the list collector page will navigate the user to the next incomplete item.", async () => { + // Complete the first incomplete list item await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("456"); await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("2"); await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("2"); @@ -186,23 +182,24 @@ describe("List Collector Repeating Blocks", () => { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - }); - it("Attempting to complete the list collector will navigate the user to the first incomplete block of the third list item.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await click(CompaniesRepeatingBlock2Page.submit()); + + // The user is taken to the next incomplete repeating block + await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock2Page.pageName); }); - it("All items are now marked as completed with the checkmark icon.", async () => { + it("When the last remaining incomplete repeating block is completed, Then all items are marked as completed with the checkmark icon.", async () => { + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); checkListItemComplete(`dt[data-qa="list-item-1-label"]`); checkListItemComplete(`dt[data-qa="list-item-2-label"]`); checkListItemComplete(`dt[data-qa="list-item-3-label"]`); checkListItemComplete(`dt[data-qa="list-item-4-label"]`); }); - it("Clicking a change link from the section summary and pressing previous or submit without changing an answer returns the user to the section summary anchored to the answer they clicked on", async () => { + it("When the user clicks a change link from the section summary and submits without changing an answer, Then the user is returned to the section summary anchored to the answer they clicked on", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(AnyOtherTradingDetailsPage.submit()); @@ -216,14 +213,14 @@ describe("List Collector Repeating Blocks", () => { await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); }); - it("Editing an answer from the section summary which does not affect progress and pressing continue returns the user to the section summary anchored to the answer they edited", async () => { + it("When an answer is edited from the section summary which does not affect progress, Then pressing continue returns the user to the section summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("No"); await click(AnyOtherTradingDetailsPage.submit()); await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); }); - it("Clicking a change link from the final summary and pressing previous or submit without changing an answer returns the user to the final summary anchored to the answer they clicked on", async () => { + it("When a user clicks a change link from the final summary and submits without changing an answer, Then the user is returned to the final summary anchored to the answer they clicked on", async () => { await click(SectionCompaniesPage.submit()); await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); @@ -235,23 +232,24 @@ describe("List Collector Repeating Blocks", () => { await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); }); - it("Editing an answer from the final summary which does not affect progress and pressing continue returns the user to the final summary anchored to the answer they edited", async () => { + it("When an an answer is edited from the final summary which does not affect progress, Then pressing continue returns the user to the final summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("Yes"); await click(AnyOtherTradingDetailsPage.submit()); await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); }); - it("The list collector can now be submitted.", async () => { + it("When all items are completed by the user, Then the questionnaire is able to be submitted.", async () => { await click(SubmitPage.submit()); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); }); }); - describe("Given a journey through the list collector with repeating blocks, the answers from repeating blocks can be edited.", () => { + describe("Given a journey through the list collector with repeating blocks", () => { before("Load the survey", async () => { await browser.openQuestionnaire("test_list_collector_repeating_blocks_section_summary.json"); }); - it("The user is able to add companies, complete repeating blocks and navigate to the section summary.", async () => { + it("When the user adds and completes items, Then they are able to see the items on the section summary page.", async () => { await proceedToListCollector(); await addCompany("ONS", "123", "1", "1", "2023", true, true); await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); @@ -261,125 +259,108 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(AnyOtherTradingDetailsPage.submit()); await click(SectionCompaniesPage.submit()); + await expect(await $$(summaryValues)[2].getText()).toContain("ONS"); + await expect(await $$(summaryValues)[4].getText()).toContain("1 January 2023"); + await expect(await $$(summaryValues)[5].getText()).toContain("Yes"); + await expect(await $$(summaryValues)[7].getText()).toContain("GOV"); + await expect(await $$(summaryValues)[8].getText()).toContain("456"); + await expect(await $$(summaryValues)[11].getText()).toContain("No answer provided"); }); - it("Edit each type of answer on different items from the section summary.", async () => { - await expect(await $$(summaryValues)[8].getText()).toBe("456"); + it("When an item is edited from the section summary page, Then the correct value is displayed when the user returns to the summary.", async () => { + await expect(await $$(summaryValues)[8].getText()).toContain("456"); await repeatingAnswerChangeLink(8).click(); await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[8].getText()).toBe("789"); + await expect(await $$(summaryValues)[8].getText()).toContain("789"); + }); + }); - await expect(await $$(summaryValues)[4].getText()).toBe("1 January 2023"); - await repeatingAnswerChangeLink(4).click(); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("4"); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("4"); - await click(CompaniesRepeatingBlock1Page.submit()); - await expect(await $$(summaryValues)[4].getText()).toBe("4 April 2023"); + describe("Given the user is completing a list collector with repeating blocks in a mandatory section of a hub based questionnaire.", () => { + before("Load the survey", async () => { + await browser.openQuestionnaire("test_list_collector_repeating_blocks_with_hub.json"); + }); + it("When the user adds complete and incomplete items and returns to the hub, Then the user should be taken to first incomplete repeating block when pressing Continue.", async () => { + await proceedToListCollector(); - await expect(await $$(summaryValues)[5].getText()).toBe("Yes"); - await repeatingAnswerChangeLink(5).click(); - await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[5].getText()).toBe("No"); + await addCompany("ONS", "123", "1", "1", "2023", true, true); + await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); + await click(AddCompanyPage.submit()); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); + await browser.url("questionnaire/"); + await click(HubPage.submit()); + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock1Page.pageName); + }); - await expect(await $$(summaryValues)[11].getText()).toBe("No answer provided"); - await repeatingAnswerChangeLink(11).click(); - await $(CompaniesRepeatingBlock2Page.authorisedTraderEuRadioYes()).click(); + it("When the user completes the incomplete blocks and returns to the list collector Page, Then the completed items should display the checkmark icon", async () => { + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("456"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("2"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); + await click(CompaniesRepeatingBlock1Page.submit()); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(await $$(summaryValues)[11].getText()).toBe("Yes"); + await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); + checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + checkListItemComplete(`dt[data-qa="list-item-2-label"]`); }); - it("The list collector can then be submitted", async () => { - await click(SubmitPage.submit()); - }); - }); -}); + it("When another incomplete item is added via the section summary, Then navigating to the submit page of the section will redirect to the list collector page.", async () => { + // Add another item and partially complete + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(AnyOtherTradingDetailsPage.submit()); + await $(SectionCompaniesPage.companiesListAddLink()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); + await click(AddCompanyPage.submit()); + await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); -describe("Given a journey through the list collector with repeating blocks, in a mandatory section of hub questionnaire, the incomplete repeating blocks mark the list collector incomplete and thus navigate back there.", () => { - before("Load the survey", async () => { - await browser.openQuestionnaire("test_list_collector_repeating_blocks_with_hub.json"); - }); - it("The user is able to add a compete company and an incomplete company.", async () => { - await proceedToListCollector(); - - await addCompany("ONS", "123", "1", "1", "2023", true, true); - await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(AddCompanyPage.companyOrBranchName()).setValue("GOV"); - await click(AddCompanyPage.submit()); - await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); - }); + // Navigating to the section summary will redirect to the list collector page + await browser.url("questionnaire/sections/section-companies/"); + await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); + }); - it("Navigating to the root of the questionnaire will redirect to the incomplete list collector, which we can then complete.", async () => { - await browser.url("questionnaire/"); - await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.url()); - - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("456"); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("2"); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("2"); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); - await click(CompaniesRepeatingBlock1Page.submit()); - await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); - await click(CompaniesRepeatingBlock2Page.submit()); - }); + it("When the incomplete repeating blocks are completed, Then the user is able to complete the section and is taken to the hub page.", async () => { + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); + await click(CompaniesRepeatingBlock1Page.submit()); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(SectionCompaniesPage.submit()); + await expect(browser).toHaveUrlContaining(HubPage.pageName); + }); - it("Another incomplete item can be added via the section summary.", async () => { - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await click(AnyOtherTradingDetailsPage.submit()); - await $(SectionCompaniesPage.companiesListAddLink()).click(); - await $(AddCompanyPage.companyOrBranchName()).setValue("MOD"); - await click(AddCompanyPage.submit()); - await $(CompaniesRepeatingBlock1Page.cancelAndReturn()).click(); - }); + it("When the user is on the Hub page and has completed the section, Then they are able to add additional companies using the Add link", async () => { + await $(HubPage.summaryRowLink("section-companies")).click(); + await $(SectionCompaniesPage.companiesListAddLink()).click(); + await $(AddCompanyPage.companyOrBranchName()).setValue("MOJ"); + await click(AddCompanyPage.submit()); + await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); + await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); + await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); + await click(CompaniesRepeatingBlock1Page.submit()); + await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); + await click(CompaniesRepeatingBlock2Page.submit()); + await $(AnyOtherCompaniesOrBranchesPage.no()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); + await click(SectionCompaniesPage.submit()); + await expect(browser).toHaveUrlContaining(HubPage.pageName); + }); - it("Navigating to the submit page of the section will redirect to the incomplete list collector, which we can then complete.", async () => { - await browser.url("questionnaire/sections/section-companies/"); - await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.url()); - - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); - await click(CompaniesRepeatingBlock1Page.submit()); - await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); - await click(CompaniesRepeatingBlock2Page.submit()); - }); - it("The user is able to add additional company via a list collector, edit this newly added list item and return to list collector page", async () => { - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await click(SectionCompaniesPage.submit()); - await $(HubPage.summaryRowLink("section-companies")).click(); - await $(SectionCompaniesPage.companiesListAddLink()).click(); - await $(AddCompanyPage.companyOrBranchName()).setValue("MOJ"); - await click(AddCompanyPage.submit()); - await $(CompaniesRepeatingBlock1Page.previous()).click(); - await $(EditCompanyPage.previous()).click(); - await $(AnyOtherCompaniesOrBranchesPage.listEditLink(4)).click(); - await expect(browser).toHaveUrlContaining(EditCompanyPage.pageName); - await click(EditCompanyPage.submit()); - await $(CompaniesRepeatingBlock1Page.previous()).click(); - await $(EditCompanyPage.previous()).click(); - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await $(CompaniesRepeatingBlock1Page.registrationNumber()).setValue("789"); - await $(CompaniesRepeatingBlock1Page.registrationDateday()).setValue("3"); - await $(CompaniesRepeatingBlock1Page.registrationDatemonth()).setValue("3"); - await $(CompaniesRepeatingBlock1Page.registrationDateyear()).setValue("2023"); - await click(CompaniesRepeatingBlock1Page.submit()); - await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioYes()).click(); - await click(CompaniesRepeatingBlock2Page.submit()); - await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await click(AnyOtherCompaniesOrBranchesPage.submit()); - await click(SectionCompaniesPage.submit()); - }); - it("The section can now be submitted and and hub will redirect user to the next section.", async () => { - await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ResponsiblePartyHubPage.pageName); + it("When the user has completed the list collector section and uses Submit on the hub page, Then the user will be redirected to the next section.", async () => { + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(ResponsiblePartyHubPage.pageName); + }); }); }); From fb456f035c7e51981abe859acfd768e677bd2080 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:47:53 +0000 Subject: [PATCH 320/567] Schemas v3.77.2 (#1249) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 3fa0a38d0d..9ec9a51169 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.77.1 +v3.77.2 From 6418a9d552b495359f7ede7918f45341c54e9906 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 8 Nov 2023 08:34:14 +0000 Subject: [PATCH 321/567] Schemas v3.77.3 (#1250) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 9ec9a51169..325bce58d4 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.77.2 +v3.77.3 From 01c155af8703295c0028870b52499972293be451 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:15:20 +0000 Subject: [PATCH 322/567] Update github actions to use nvmrc file for node versioning (#1247) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- .github/workflows/pull_request.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 30ea49f6e7..bb30ac1f1b 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: "18.14.0" + node-version-file : ".nvmrc" - name: Get yarn cache id: get-yarn-cache run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT @@ -61,7 +61,7 @@ jobs: cache: 'pipenv' - uses: actions/setup-node@v3 with: - node-version: "18.14.0" + node-version-file: ".nvmrc" - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv @@ -144,7 +144,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: "18.14.0" + node-version-file: ".nvmrc" - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - uses: actions/setup-python@v4 From ec1b054f3491569884604729c86505531e09c40b Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 9 Nov 2023 15:27:52 +0000 Subject: [PATCH 323/567] Update yarn.lock (#1254) --- yarn.lock | 57 ------------------------------------------------------- 1 file changed, 57 deletions(-) diff --git a/yarn.lock b/yarn.lock index e4dd914e58..04379303a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1686,11 +1686,6 @@ asn1.js@^5.0.1: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -1958,19 +1953,6 @@ caniuse-lite@^1.0.30001517: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== -chai@^4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" - pathval "^1.1.1" - type-detect "^4.0.5" - chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -2021,11 +2003,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== - chokidar@3.5.3, chokidar@^3.5.0, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -2304,13 +2281,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -deep-eql@^4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" - integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== - dependencies: - type-detect "^4.0.0" - deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -2631,11 +2601,6 @@ eslint-module-utils@^2.8.0: dependencies: debug "^3.2.7" -eslint-plugin-chai-friendly@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz#0ebfbb2c1244f5de2997f3963d155758234f2b0f" - integrity sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg== - eslint-plugin-es-x@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.2.0.tgz#5779d742ad31f8fd780b9481331481e142b72311" @@ -3144,11 +3109,6 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== - get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" @@ -4149,13 +4109,6 @@ long@^5.2.0: resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== - dependencies: - get-func-name "^2.0.0" - lowercase-keys@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" @@ -4761,11 +4714,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -5667,11 +5615,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" From ff5249687646dbfe8026834a8a32a09ebb51e2e1 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:30:11 +0000 Subject: [PATCH 324/567] Add DataStores class to questionnaire store (#1228) --- app/data_models/data_stores.py | 20 + app/data_models/questionnaire_store.py | 61 +-- app/forms/questionnaire_form.py | 106 +--- app/globals.py | 2 +- app/questionnaire/path_finder.py | 50 +- app/questionnaire/placeholder_parser.py | 39 +- app/questionnaire/placeholder_renderer.py | 52 +- .../questionnaire_store_updater.py | 10 +- app/questionnaire/router.py | 66 +-- app/questionnaire/rules/rule_evaluator.py | 31 +- app/questionnaire/value_source_resolver.py | 47 +- app/questionnaire/variants.py | 78 +-- app/routes/dump.py | 14 +- app/routes/flush.py | 11 +- app/routes/individual_response.py | 2 +- app/routes/questionnaire.py | 23 +- app/routes/session.py | 7 +- app/submitter/convert_payload_0_0_1.py | 34 +- app/submitter/converter.py | 19 +- app/submitter/converter_v2.py | 51 +- .../contexts/calculated_summary_context.py | 55 +-- app/views/contexts/context.py | 38 +- .../grand_calculated_summary_context.py | 12 +- app/views/contexts/hub_context.py | 6 +- app/views/contexts/list_context.py | 6 +- app/views/contexts/preview_context.py | 34 +- app/views/contexts/section_preview_context.py | 24 +- app/views/contexts/section_summary_context.py | 41 +- .../contexts/submit_questionnaire_context.py | 7 +- app/views/contexts/summary/block.py | 74 +-- .../summary/calculated_summary_block.py | 25 +- app/views/contexts/summary/group.py | 67 +-- .../summary/list_collector_base_block.py | 51 +- .../contexts/summary/list_collector_block.py | 2 +- .../summary/list_collector_content_block.py | 2 +- app/views/contexts/summary/question.py | 67 +-- app/views/contexts/summary_context.py | 33 +- .../view_submitted_response_context.py | 9 +- app/views/handlers/__init__.py | 2 +- app/views/handlers/block.py | 24 +- app/views/handlers/calculation_summary.py | 7 +- app/views/handlers/confirm_email.py | 9 +- app/views/handlers/content.py | 7 +- app/views/handlers/feedback.py | 11 +- app/views/handlers/individual_response.py | 51 +- app/views/handlers/list_action.py | 2 +- app/views/handlers/list_add_question.py | 2 +- app/views/handlers/list_collector.py | 7 +- app/views/handlers/list_edit_question.py | 2 +- app/views/handlers/list_remove_question.py | 4 +- app/views/handlers/primary_person_question.py | 2 +- app/views/handlers/question.py | 44 +- .../relationships/relationship_collector.py | 2 +- .../relationships/relationship_question.py | 6 +- .../relationships/unrelated_question.py | 2 +- app/views/handlers/section.py | 14 +- app/views/handlers/submission.py | 2 +- app/views/handlers/submit_questionnaire.py | 14 +- app/views/handlers/view_preview_questions.py | 7 +- tests/app/conftest.py | 8 +- tests/app/data_model/conftest.py | 8 +- .../data_model/test_questionnaire_store.py | 98 ++-- tests/app/forms/field_handlers/conftest.py | 18 +- .../forms/field_handlers/test_date_handler.py | 12 +- .../field_handlers/test_number_handler.py | 2 +- tests/app/forms/test_field_factory.py | 20 +- tests/app/forms/test_questionnaire_form.py | 460 ++++-------------- tests/app/helpers/test_schema_helper.py | 2 +- tests/app/questionnaire/conftest.py | 35 +- .../rules/test_rule_evaluator.py | 285 ++++++----- .../test_dynamic_answer_options.py | 23 +- tests/app/questionnaire/test_path_finder.py | 238 +++------ .../questionnaire/test_placeholder_parser.py | 198 ++------ .../test_placeholder_renderer.py | 75 +-- .../test_questionnaire_store_updater.py | 85 ++-- tests/app/questionnaire/test_router.py | 131 +++-- tests/app/questionnaire/test_schema_utils.py | 111 ++--- .../test_value_source_resolver.py | 397 ++++++++------- tests/app/submitter/conftest.py | 12 +- .../submitter/test_convert_payload_0_0_1.py | 166 ++----- .../submitter/test_convert_payload_0_0_3.py | 282 +++-------- tests/app/submitter/test_converter.py | 36 +- tests/app/views/contexts/conftest.py | 8 +- .../app/views/contexts/summary/test_block.py | 8 +- .../views/contexts/summary/test_question.py | 297 ++--------- .../test_calculated_summary_context.py | 74 ++- .../test_grand_calculated_summary_context.py | 57 +-- tests/app/views/contexts/test_hub_context.py | 130 +---- tests/app/views/contexts/test_list_context.py | 86 +--- .../views/contexts/test_preview_context.py | 41 +- .../contexts/test_section_summary_context.py | 180 +++---- .../app/views/contexts/test_submit_context.py | 30 +- .../test_submitted_response_context.py | 6 +- .../views/contexts/test_summary_context.py | 13 +- tests/app/views/handlers/conftest.py | 87 ++-- .../test_question_with_dynamic_answers.py | 4 +- 96 files changed, 1645 insertions(+), 3575 deletions(-) create mode 100644 app/data_models/data_stores.py diff --git a/app/data_models/data_stores.py b/app/data_models/data_stores.py new file mode 100644 index 0000000000..7c1f5e8dbf --- /dev/null +++ b/app/data_models/data_stores.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass, field +from typing import MutableMapping + +from app.data_models.answer_store import AnswerStore +from app.data_models.list_store import ListStore +from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.progress_store import ProgressStore +from app.data_models.supplementary_data_store import SupplementaryDataStore + + +@dataclass +class DataStores: + metadata: MetadataProxy | None = None + response_metadata: MutableMapping = field(default_factory=dict) + list_store: ListStore = field(default_factory=ListStore) + answer_store: AnswerStore = field(default_factory=AnswerStore) + progress_store: ProgressStore = field(default_factory=ProgressStore) + supplementary_data_store: SupplementaryDataStore = field( + default_factory=SupplementaryDataStore + ) diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index ae9fef59d6..4a89af045b 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, MutableMapping, Optional from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore @@ -29,13 +30,8 @@ def __init__( version = self.get_latest_version_number() self.version = version self._metadata: MutableMapping = {} - # self.metadata is a read-only view over self._metadata - self.metadata: MetadataProxy | None = None - self.response_metadata: MutableMapping = {} - self.list_store = ListStore() - self.answer_store = AnswerStore() - self.progress_store = ProgressStore() - self.supplementary_data_store = SupplementaryDataStore() + self._stores = DataStores() + self.data_stores = self._stores self.submitted_at: Optional[datetime] self.collection_exercise_sid: Optional[str] @@ -60,7 +56,7 @@ def set_metadata(self, to_set: MutableMapping) -> QuestionnaireStore: Metadata should normally be read only. """ self._metadata = to_set - self.metadata = MetadataProxy.from_dict(self._metadata) + self._stores.metadata = MetadataProxy.from_dict(self._metadata) return self @@ -72,7 +68,7 @@ def set_supplementary_data(self, to_set: MutableMapping) -> None: this updates ListStore to add/update any lists for supplementary data and stores the identifier -> list_item_id mappings in the supplementary data store to use in the payload at the end """ - if self.supplementary_data_store.list_mappings: + if self._stores.supplementary_data_store.list_mappings: self._remove_old_supplementary_lists_and_answers(new_data=to_set) list_mappings = { @@ -82,7 +78,7 @@ def set_supplementary_data(self, to_set: MutableMapping) -> None: for list_name, list_data in to_set.get("items", {}).items() } - self.supplementary_data_store = SupplementaryDataStore( + self._stores.supplementary_data_store = SupplementaryDataStore( supplementary_data=to_set, list_mappings=list_mappings ) @@ -99,11 +95,11 @@ def _create_supplementary_list( # if any pre-existing supplementary data already has a mapping for this list item # then its already in the list store and doesn't require adding if not ( - list_item_id := self.supplementary_data_store.list_lookup.get( + list_item_id := self._stores.supplementary_data_store.list_lookup.get( list_name, {} ).get(identifier) ): - list_item_id = self.list_store.add_list_item(list_name) + list_item_id = self._stores.list_store.add_list_item(list_name) list_mappings.append( SupplementaryDataListMapping( identifier=identifier, list_item_id=list_item_id @@ -120,48 +116,55 @@ def _remove_old_supplementary_lists_and_answers( :param new_data - the new supplementary data for comparison """ deleted_list_item_ids: set[str] = set() - for list_name, mappings in self.supplementary_data_store.list_lookup.items(): + for ( + list_name, + mappings, + ) in self._stores.supplementary_data_store.list_lookup.items(): if list_name in new_data.get("items", {}): new_identifiers = [ item["identifier"] for item in new_data["items"][list_name] ] for identifier, list_item_id in mappings.items(): if identifier not in new_identifiers: - self.list_store.delete_list_item(list_name, list_item_id) + self._stores.list_store.delete_list_item( + list_name, list_item_id + ) deleted_list_item_ids.add(list_item_id) else: - self.list_store.delete_list(list_name) + self._stores.list_store.delete_list(list_name) deleted_list_item_ids.update(mappings.values()) - self.answer_store.remove_all_answers_for_list_item_ids(*deleted_list_item_ids) + self._stores.answer_store.remove_all_answers_for_list_item_ids( + *deleted_list_item_ids + ) def _deserialize(self, data: str) -> None: json_data = json_loads(data) - self.progress_store = ProgressStore(json_data.get("PROGRESS")) + self._stores.progress_store = ProgressStore(json_data.get("PROGRESS")) self.set_metadata(json_data.get("METADATA", {})) - self.supplementary_data_store = SupplementaryDataStore.deserialize( + self._stores.supplementary_data_store = SupplementaryDataStore.deserialize( json_data.get("SUPPLEMENTARY_DATA", {}) ) - self.answer_store = AnswerStore(json_data.get("ANSWERS")) - self.list_store = ListStore.deserialize(json_data.get("LISTS")) - self.response_metadata = json_data.get("RESPONSE_METADATA", {}) + self._stores.answer_store = AnswerStore(json_data.get("ANSWERS")) + self._stores.list_store = ListStore.deserialize(json_data.get("LISTS")) + self._stores.response_metadata = json_data.get("RESPONSE_METADATA", {}) def serialize(self) -> str: data = { "METADATA": self._metadata, - "ANSWERS": list(self.answer_store), - "SUPPLEMENTARY_DATA": self.supplementary_data_store.serialize(), - "LISTS": self.list_store.serialize(), - "PROGRESS": self.progress_store.serialize(), - "RESPONSE_METADATA": self.response_metadata, + "ANSWERS": list(self._stores.answer_store), + "SUPPLEMENTARY_DATA": self._stores.supplementary_data_store.serialize(), + "LISTS": self._stores.list_store.serialize(), + "PROGRESS": self._stores.progress_store.serialize(), + "RESPONSE_METADATA": self._stores.response_metadata, } return json_dumps(data) def delete(self) -> None: self._storage.delete() self._metadata.clear() - self.response_metadata = {} - self.answer_store.clear() - self.progress_store.clear() + self._stores.response_metadata = {} + self._stores.answer_store.clear() + self._stores.progress_store.clear() def save(self) -> None: data = self.serialize() diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 833e1c5aae..373f02e002 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -5,20 +5,14 @@ from collections.abc import Callable from datetime import datetime, timedelta, timezone from decimal import Decimal -from typing import Any, Mapping, MutableMapping, Optional, Sequence, Union +from typing import Any, Mapping, Optional, Sequence, Union from dateutil.relativedelta import relativedelta from flask_wtf import FlaskForm from werkzeug.datastructures import ImmutableMultiDict, MultiDict from wtforms import validators -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.forms import error_messages from app.forms.field_handlers import DateHandler, FieldHandler, get_field_handler from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck @@ -50,37 +44,23 @@ def __init__( self, schema: QuestionnaireSchema, question_schema: QuestionSchemaType, - answer_store: AnswerStore, - list_store: ListStore, - metadata: Optional[MetadataProxy], - response_metadata: MutableMapping, + data_stores: DataStores, location: Union[None, Location, RelationshipLocation], - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, **kwargs: Union[MultiDict, Mapping, None], ): self.schema = schema self.question = question_schema - self.answer_store = answer_store - self.list_store = list_store - self.metadata = metadata - self.response_metadata = response_metadata self.location = location self.question_errors: dict[str, str] = {} self.options_with_detail_answer: dict = {} self.question_title = self.question.get("title", "") - self.progress_store = progress_store - self.supplementary_data_store = supplementary_data_store + self.data_stores = data_stores + self.value_source_resolver = ValueSourceResolver( - answer_store=self.answer_store, schema=self.schema, - metadata=self.metadata, - response_metadata=self.response_metadata, - list_store=self.list_store, + data_stores=data_stores, location=self.location, list_item_id=self.location.list_item_id if self.location else None, - progress_store=self.progress_store, - supplementary_data_store=self.supplementary_data_store, ) super().__init__(**kwargs) @@ -219,7 +199,7 @@ def _get_target_total_and_currency( target_answer = self.schema.get_answers_by_answer_id( calculation["answer_id"] )[0] - target_total = self.answer_store.get_answer( + target_total = self.data_stores.answer_store.get_answer( calculation["answer_id"] ).value # type: ignore # expect not None @@ -331,27 +311,17 @@ def _get_period_range_for_single_date( ) -> timedelta: list_item_id = self.location.list_item_id if self.location else None value_source_resolver = ValueSourceResolver( - answer_store=self.answer_store, - list_store=self.list_store, - metadata=self.metadata, - response_metadata=self.response_metadata, + data_stores=self.data_stores, schema=self.schema, location=self.location, list_item_id=list_item_id, escape_answer_values=False, - progress_store=self.progress_store, - supplementary_data_store=self.supplementary_data_store, ) rule_evaluator = RuleEvaluator( + data_stores=self.data_stores, schema=self.schema, - answer_store=self.answer_store, - list_store=self.list_store, - metadata=self.metadata, - response_metadata=self.response_metadata, location=self.location, - progress_store=self.progress_store, - supplementary_data_store=self.supplementary_data_store, ) handler = DateHandler( @@ -417,7 +387,7 @@ def _get_formatted_calculation_values( block_id = self.location.block_id if self.location else None if block_id and block_id in self.schema.dynamic_answers_parent_block_ids: list_name = self.schema.get_list_name_for_dynamic_answer(block_id) - list_item_ids = self.list_store[list_name] + list_item_ids = self.data_stores.list_store[list_name] for answer_id in answers_sequence: if self.schema.is_answer_dynamic(answer_id): answers_list.extend( @@ -492,32 +462,19 @@ def get_answer_fields( question: QuestionSchemaType, data: MultiDict[str, Any] | Mapping[str, Any] | None, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, location: LocationType | None, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> dict[str, FieldHandler]: list_item_id = location.list_item_id if location else None block_ids_by_section: dict[SectionKey, tuple[str, ...]] = {} - if location and progress_store: + if location and data_stores.progress_store: block_ids_by_section = ( get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( location=location, - progress_store=progress_store, - path_finder=PathFinder( - schema=schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - metadata=metadata, - response_metadata=response_metadata, - supplementary_data_store=supplementary_data_store, - ), + progress_store=data_stores.progress_store, + path_finder=PathFinder(schema=schema, data_stores=data_stores), data=question, ignore_keys=["when"], schema=schema, @@ -529,29 +486,19 @@ def get_answer_fields( def _get_value_source_resolver(list_item: str | None = None) -> ValueSourceResolver: return ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, + data_stores=data_stores, schema=schema, location=location, list_item_id=list_item, escape_answer_values=False, - response_metadata=response_metadata, routing_path_block_ids=block_ids, assess_routing_path=False, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) rule_evaluator = RuleEvaluator( schema=schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=data_stores, location=location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) answer_fields = {} @@ -653,12 +600,7 @@ def generate_form( *, schema: QuestionnaireSchema, question_schema: QuestionSchemaType, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, + data_stores: DataStores, location: LocationType | None = None, data: dict[str, Any] | None = None, form_data: MultiDict | None = None, @@ -674,13 +616,8 @@ class DynamicForm(QuestionnaireForm): question_schema, input_data, schema, - answer_store, - list_store, - metadata, - response_metadata, + data_stores, location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) for answer_id, field in answer_fields.items(): @@ -689,13 +626,8 @@ class DynamicForm(QuestionnaireForm): return DynamicForm( schema, question_schema, - answer_store, - list_store, - metadata, - response_metadata, + data_stores, location, data=data, formdata=form_data, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) diff --git a/app/globals.py b/app/globals.py index b470e235f0..5b0b98c8b1 100644 --- a/app/globals.py +++ b/app/globals.py @@ -109,7 +109,7 @@ def get_metadata(user: User) -> MetadataProxy | None: return None questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) - return questionnaire_store.metadata + return questionnaire_store.data_stores.metadata def get_view_submitted_response_expiration_time(submitted_at: datetime) -> datetime: diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 5af6280c3f..4b7e82d099 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -1,13 +1,9 @@ -from typing import Iterable, Mapping, MutableMapping, Sequence +from typing import Iterable, Mapping, Sequence from werkzeug.datastructures import ImmutableDict from app.data_models import CompletionStatus -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import ProgressStore -from app.data_models.supplementary_data_store import SupplementaryDataStore +from app.data_models.data_stores import DataStores from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.routing_path import RoutingPath @@ -16,23 +12,9 @@ class PathFinder: - def __init__( - self, - schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - supplementary_data_store: SupplementaryDataStore, - ): - self.answer_store = answer_store - self.metadata = metadata - self.response_metadata = response_metadata + def __init__(self, schema: QuestionnaireSchema, data_stores: DataStores): + self.data_stores = data_stores self.schema = schema - self.progress_store = progress_store - self.list_store = list_store - self.supplementary_data_store = supplementary_data_store def routing_path(self, section_key: SectionKey) -> RoutingPath: """ @@ -80,7 +62,7 @@ def get_when_rules_block_dependencies(self, section_id: str) -> list[str]: for dependent_section in dependencies_for_section for block_id in self.routing_path(SectionKey(dependent_section)) if SectionKey(dependent_section) - in self.progress_store.started_section_keys() + in self.data_stores.progress_store.started_section_keys() ] def _get_not_skipped_blocks_in_section( @@ -200,14 +182,9 @@ def _evaluate_routing_rules( when_rule_evaluator = RuleEvaluator( self.schema, - self.answer_store, - self.list_store, - self.metadata, - self.response_metadata, - progress_store=self.progress_store, + self.data_stores, location=this_location, routing_path_block_ids=routing_path_block_ids, - supplementary_data_store=self.supplementary_data_store, ) for rule in routing_rules: rule_valid = ( @@ -254,14 +231,9 @@ def evaluate_skip_conditions( when_rule_evaluator = RuleEvaluator( schema=self.schema, - answer_store=self.answer_store, - list_store=self.list_store, - metadata=self.metadata, - response_metadata=self.response_metadata, - progress_store=self.progress_store, + data_stores=self.data_stores, location=current_location, routing_path_block_ids=routing_path_block_ids, - supplementary_data_store=self.supplementary_data_store, ) return when_rule_evaluator.evaluate(skip_conditions["when"]) @@ -285,8 +257,10 @@ def _remove_current_blocks_answers_for_backwards_routing( rule["when"], answer_ids_for_current_block ) - self.progress_store.remove_location_for_backwards_routing(this_location) - self.progress_store.update_section_status( + self.data_stores.progress_store.remove_location_for_backwards_routing( + this_location + ) + self.data_stores.progress_store.update_section_status( CompletionStatus.IN_PROGRESS, this_location.section_key ) @@ -300,7 +274,7 @@ def _remove_block_answers_for_backward_routing_according_to_when_rule( "identifier" in rule and rule["identifier"] in answer_ids_for_current_block ): - self.answer_store.remove_answer(rule["identifier"]) + self.data_stores.answer_store.remove_answer(rule["identifier"]) if QuestionnaireSchema.has_operator(rule): return self._remove_block_answers_for_backward_routing_according_to_when_rule( diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index f37e341ea5..d5b6fbd75e 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -11,10 +11,7 @@ TypeAlias, ) -from app.data_models import ProgressStore, SupplementaryDataStore -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire import path_finder as pf from app.questionnaire.dependencies import ( @@ -39,6 +36,7 @@ PlaceholderRenderer, # pragma: no cover ) + TransformedValueTypes: TypeAlias = None | str | int | Decimal | bool @@ -51,14 +49,9 @@ class PlaceholderParser: def __init__( self, language: str, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, renderer: PlaceholderRenderer, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, list_item_id: str | None = None, location: LocationType | None = None, placeholder_preview_mode: bool | None = False, @@ -67,25 +60,14 @@ def __init__( self._placeholder_map: MutableMapping[ str, ValueSourceEscapedTypes | ValueSourceTypes | None ] = {} - self._answer_store = answer_store - self._list_store = list_store - self._metadata = metadata - self._response_metadata = response_metadata + self._data_stores = data_stores self._list_item_id = list_item_id self._schema = schema self._location = location - self._progress_store = progress_store - self._supplementary_data_store = supplementary_data_store self._placeholder_preview_mode = placeholder_preview_mode self._path_finder = pf.PathFinder( - schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - supplementary_data_store=self._supplementary_data_store, + schema=self._schema, data_stores=self._data_stores ) self._value_source_resolver = self._get_value_source_resolver() @@ -126,19 +108,14 @@ def _get_value_source_resolver( assess_routing_path: bool | None = False, ) -> ValueSourceResolver: return ValueSourceResolver( - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, + data_stores=self._data_stores, schema=self._schema, location=self._location, list_item_id=self._list_item_id, escape_answer_values=True, - response_metadata=self._response_metadata, use_default_answer=True, assess_routing_path=assess_routing_path, routing_path_block_ids=routing_path_block_ids, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, ) def _parse_placeholder(self, placeholder: Mapping) -> Any: @@ -201,7 +178,7 @@ def _get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( return ( get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( location=self._location, - progress_store=self._progress_store, + progress_store=self._data_stores.progress_store, sections_to_ignore=sections_to_ignore, data=data, path_finder=self._path_finder, @@ -228,7 +205,7 @@ def _get_value_source_resolver_for_transform( ) block_ids = get_routing_path_block_ids_by_section_for_dependent_sections( location=self._location, - progress_store=self._progress_store, + progress_store=self._data_stores.progress_store, sections_to_ignore=["when"], path_finder=self._path_finder, data=transform, diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 838e91a084..a4506f71c0 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -3,11 +3,8 @@ from jsonpointer import resolve_pointer, set_pointer -from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer import AnswerValueTypes -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_parser import PlaceholderParser from app.questionnaire.plural_forms import get_plural_form_key @@ -24,26 +21,16 @@ class PlaceholderRenderer: def __init__( self, language: str, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, location: LocationType | None = None, placeholder_preview_mode: bool | None = False, ): self._placeholder_preview_mode = placeholder_preview_mode self._language = language - self._answer_store = answer_store - self._list_store = list_store - self._metadata = metadata - self._response_metadata = response_metadata + self._data_stores = data_stores self._schema = schema self._location = location - self._progress_store = progress_store - self._supplementary_data_store = supplementary_data_store def render_pointer( self, @@ -64,12 +51,16 @@ def get_plural_count( source_id = schema_partial["identifier"] if source == "answers": - answer = self._answer_store.get_answer(source_id) + answer = self._data_stores.answer_store.get_answer(source_id) return answer.value if answer else None if source == "list": - return len(self._list_store[source_id]) + return len(self._data_stores.list_store[source_id]) - return self._metadata[source_id] if self._metadata else None + return ( + self._data_stores.metadata[source_id] + if self._data_stores.metadata + else None + ) def render_placeholder( self, @@ -80,17 +71,12 @@ def render_placeholder( if not placeholder_parser: placeholder_parser = PlaceholderParser( language=self._language, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, list_item_id=list_item_id, location=self._location, renderer=self, - progress_store=self._progress_store, placeholder_preview_mode=self._placeholder_preview_mode, - supplementary_data_store=self._supplementary_data_store, ) placeholder_data = QuestionnaireSchema.get_mutable_deepcopy(placeholder_data) @@ -137,17 +123,12 @@ def render( placeholder_parser = PlaceholderParser( language=self._language, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, list_item_id=list_item_id, location=self._location, renderer=self, placeholder_preview_mode=self._placeholder_preview_mode, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, ) for pointer in pointers: @@ -188,7 +169,7 @@ def resolve_dynamic_answers_ids( dynamic_answers: dict, ) -> None: list_name = dynamic_answers["values"]["identifier"] - list_items = self._list_store[list_name].items + list_items = self._data_stores.list_store[list_name].items resolved_dynamic_answers = [] @@ -210,17 +191,12 @@ def resolve_dynamic_answers( for answer in dynamic_answers["answers"]: placeholder_parser = PlaceholderParser( language=self._language, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, list_item_id=answer["list_item_id"], location=self._location, renderer=self, placeholder_preview_mode=self._placeholder_preview_mode, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, ) pointers = find_pointers_containing(answer, "placeholders") diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 6f5143e1ff..079bf67785 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -32,9 +32,9 @@ def __init__( self._current_question = current_question or {} self._schema = schema self._questionnaire_store = questionnaire_store - self._answer_store = self._questionnaire_store.answer_store - self._list_store = self._questionnaire_store.list_store - self._progress_store = self._questionnaire_store.progress_store + self._answer_store = self._questionnaire_store.data_stores.answer_store + self._list_store = self._questionnaire_store.data_stores.list_store + self._progress_store = self._questionnaire_store.data_stores.progress_store self._router = router self.dependent_block_id_by_section_key: Mapping[ @@ -206,10 +206,10 @@ def update_same_name_items( same_name_items = set() people_names: dict[str, str] = {} - list_model = self._questionnaire_store.list_store[list_name] + list_model = self._list_store[list_name] for current_list_item_id in list_model: - answers = self._questionnaire_store.answer_store.get_answers_by_answer_id( + answers = self._answer_store.get_answers_by_answer_id( answer_ids=same_name_answer_ids, list_item_id=current_list_item_id ) current_names = [answer.value.casefold() for answer in answers if answer] # type: ignore diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 5c99d25041..07b0d20522 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -1,14 +1,8 @@ -from typing import Generator, Mapping, MutableMapping +from typing import Generator, Mapping from flask import url_for -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey from app.questionnaire.path_finder import PathFinder @@ -22,29 +16,14 @@ class Router: def __init__( self, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - supplementary_data_store: SupplementaryDataStore, + data_stores: DataStores, ): self._schema = schema - self._answer_store = answer_store - self._list_store = list_store - self._progress_store = progress_store - self._metadata = metadata - self._response_metadata = response_metadata - self._supplementary_data_store = supplementary_data_store + self._data_stores = data_stores self._path_finder = PathFinder( self._schema, - self._answer_store, - self._list_store, - self._progress_store, - self._metadata, - self._response_metadata, - self._supplementary_data_store, + self._data_stores, ) @property @@ -79,7 +58,7 @@ def get_last_location_in_questionnaire_url(self) -> str | None: return self.get_last_location_in_section(routing_path).url() def _is_list_item_in_list_store(self, list_item_id: str, list_name: str) -> bool: - return list_item_id in self._list_store[list_name] + return list_item_id in self._data_stores.list_store[list_name] def can_access_location( self, location: LocationType, routing_path: RoutingPath @@ -104,7 +83,7 @@ def can_access_location( def can_access_hub(self) -> bool: return self._schema.is_flow_hub and all( - self._progress_store.is_section_complete(SectionKey(section_id)) + self._data_stores.progress_store.is_section_complete(SectionKey(section_id)) for section_id in self._schema.get_section_ids_required_for_hub() if section_id in self.enabled_section_ids ) @@ -112,7 +91,7 @@ def can_access_hub(self) -> bool: def can_display_section_summary(self, section_key: SectionKey) -> bool: return bool( self._schema.get_summary_for_section(section_key.section_id) - ) and self._progress_store.is_section_complete(section_key) + ) and self._data_stores.progress_store.is_section_complete(section_key) def routing_path(self, section_key: SectionKey) -> RoutingPath: return self._path_finder.routing_path(section_key) @@ -127,7 +106,7 @@ def get_next_location_url( Get the next location in the section. If the section is complete, determine where to go next, whether it be a summary, the hub or the next incomplete location. """ - is_section_complete = self._progress_store.is_section_complete( + is_section_complete = self._data_stores.progress_store.is_section_complete( location.section_key ) @@ -242,7 +221,7 @@ def get_return_to_location_url( return url if is_section_complete is None: - is_section_complete = self._progress_store.is_section_complete( + is_section_complete = self._data_stores.progress_store.is_section_complete( location.section_key ) @@ -294,7 +273,7 @@ def _get_return_to_for_grand_calculated_summary( ) list_item_id = location.list_item_id or return_location.return_to_list_item_id list_name = ( - self._list_store.get_list_name_for_list_item_id(list_item_id) + self._data_stores.list_store.get_list_name_for_list_item_id(list_item_id) if list_item_id else None ) @@ -302,7 +281,7 @@ def _get_return_to_for_grand_calculated_summary( # the grand calculated summary is in a different section which will have a different routing path # but does not go to it unless the section is enabled and the current section is complete if ( - not self._progress_store.is_section_complete(section_key) + not self._data_stores.progress_store.is_section_complete(section_key) or grand_calculated_summary_section not in self.enabled_section_ids ): return None @@ -417,7 +396,7 @@ def get_next_location_url_for_end_of_section(self) -> str: return self.get_first_incomplete_location_in_questionnaire_url() def get_section_resume_url(self, routing_path: RoutingPath) -> str: - if routing_path.section_key in self._progress_store: + if routing_path.section_key in self._data_stores.progress_store: location = self._get_first_incomplete_location_in_section(routing_path) if location: return location.url(resume=True) @@ -451,7 +430,7 @@ def full_routing_path(self) -> list[RoutingPath]: repeating_list = self._schema.get_repeating_list_for_section(section_id) if repeating_list: - for list_item_id in self._list_store[repeating_list]: + for list_item_id in self._data_stores.list_store[repeating_list]: full_routing_path.append( self._path_finder.routing_path( SectionKey(section_id, list_item_id) @@ -467,7 +446,7 @@ def _get_first_incomplete_location_in_section( self, routing_path: RoutingPath ) -> Location | None: for block_id in routing_path: - if not self._progress_store.is_block_complete( + if not self._data_stores.progress_store.is_block_complete( block_id=block_id, section_key=routing_path.section_key, ): @@ -488,7 +467,7 @@ def _get_allowable_path(self, routing_path: RoutingPath) -> list[str]: for block_id in routing_path: allowable_path.append(block_id) - if not self._progress_store.is_block_complete( + if not self._data_stores.progress_store.is_block_complete( block_id=block_id, section_key=routing_path.section_key, ): @@ -503,19 +482,19 @@ def _get_enabled_section_keys( if repeating_list := self._schema.get_repeating_list_for_section( section_id ): - for list_item_id in self._list_store[repeating_list]: + for list_item_id in self._data_stores.list_store[repeating_list]: yield SectionKey(section_id, list_item_id) else: yield SectionKey(section_id) def _get_first_incomplete_section_key(self) -> SectionKey | None: for section_key in self._get_enabled_section_keys(): - if not self._progress_store.is_section_complete(section_key): + if not self._data_stores.progress_store.is_section_complete(section_key): return section_key def _get_last_complete_section_key(self) -> SectionKey | None: for section_key in list(self._get_enabled_section_keys())[::-1]: - if self._progress_store.is_section_complete(section_key): + if self._data_stores.progress_store.is_section_complete(section_key): return section_key def _is_section_enabled(self, section: Mapping) -> bool: @@ -530,15 +509,10 @@ def _is_section_enabled(self, section: Mapping) -> bool: ) when_rule_evaluator = RuleEvaluator( + data_stores=self._data_stores, schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - progress_store=self._progress_store, location=Location(section_id=section_id), routing_path_block_ids=routing_path_block_ids, - supplementary_data_store=self._supplementary_data_store, ) return bool(when_rule_evaluator.evaluate(enabled["when"])) diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 438898b075..dd82915afd 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -1,15 +1,9 @@ from dataclasses import dataclass from datetime import date from decimal import Decimal -from typing import Generator, Iterable, MutableMapping, Sequence, TypeAlias +from typing import Generator, Iterable, Sequence, TypeAlias -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE @@ -30,13 +24,8 @@ @dataclass class RuleEvaluator: schema: QuestionnaireSchema - answer_store: AnswerStore - list_store: ListStore - metadata: MetadataProxy | None - response_metadata: MutableMapping + data_stores: DataStores location: LocationType | None - progress_store: ProgressStore - supplementary_data_store: SupplementaryDataStore routing_path_block_ids: Iterable[str] | None = None language: str = DEFAULT_LANGUAGE_CODE @@ -44,29 +33,19 @@ class RuleEvaluator: def __post_init__(self) -> None: list_item_id = self.location.list_item_id if self.location else None self.value_source_resolver = ValueSourceResolver( - answer_store=self.answer_store, - list_store=self.list_store, - metadata=self.metadata, - response_metadata=self.response_metadata, + data_stores=self.data_stores, schema=self.schema, location=self.location, list_item_id=list_item_id, routing_path_block_ids=self.routing_path_block_ids, - progress_store=self.progress_store, use_default_answer=True, - supplementary_data_store=self.supplementary_data_store, ) renderer: PlaceholderRenderer = PlaceholderRenderer( language=self.language, - answer_store=self.answer_store, - list_store=self.list_store, - metadata=self.metadata, - response_metadata=self.response_metadata, + data_stores=self.data_stores, schema=self.schema, location=self.location, - progress_store=self.progress_store, - supplementary_data_store=self.supplementary_data_store, ) self.operations = Operations( language=self.language, schema=self.schema, renderer=renderer diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index ee16e4baf3..aae26c336d 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -1,19 +1,18 @@ from dataclasses import dataclass from decimal import Decimal -from typing import Callable, Iterable, Mapping, MutableMapping, TypeAlias +from typing import Callable, Iterable, Mapping, TypeAlias from markupsafe import Markup from werkzeug.datastructures import ImmutableDict -from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer import ( AnswerValueEscapedTypes, AnswerValueTypes, escape_answer_value, ) -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListModel, ListStore -from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException +from app.data_models.data_stores import DataStores +from app.data_models.list_store import ListModel +from app.data_models.metadata_proxy import NoMetadataException from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException, SectionKey from app.questionnaire.rules import rule_evaluator @@ -27,15 +26,10 @@ @dataclass class ValueSourceResolver: - answer_store: AnswerStore - list_store: ListStore - metadata: MetadataProxy | None - response_metadata: MutableMapping + data_stores: DataStores schema: QuestionnaireSchema location: LocationType | None list_item_id: str | None - progress_store: ProgressStore - supplementary_data_store: SupplementaryDataStore routing_path_block_ids: Iterable[str] | None = None use_default_answer: bool = False escape_answer_values: bool = False @@ -73,7 +67,7 @@ def _get_answer_value( if assess_routing_path and not self._is_answer_on_path(answer_id): return None - if answer := self.answer_store.get_answer(answer_id, list_item_id): + if answer := self.data_stores.answer_store.get_answer(answer_id, list_item_id): return answer.value if self.use_default_answer and ( @@ -95,14 +89,14 @@ def _resolve_list_item_id_for_value_source( if list_item_selector["source"] == "list": return getattr( # type: ignore - self.list_store[list_item_selector["identifier"]], + self.data_stores.list_store[list_item_selector["identifier"]], list_item_selector["selector"], ) if value_source["source"] == "supplementary_data": return ( self.list_item_id - if self.supplementary_data_store.is_data_repeating( + if self.data_stores.supplementary_data_store.is_data_repeating( value_source["identifier"] ) else None @@ -120,7 +114,7 @@ def _resolve_repeating_answers_for_list( ) -> ResolvedAnswerList: """Return the list of answers in answer store that correspond to the given list name and dynamic/repeating answer_id""" answer_values: ResolvedAnswerList = [] - for list_item_id in self.list_store[list_name]: + for list_item_id in self.data_stores.list_store[list_name]: answer_value = self._get_answer_value( answer_id=answer_id, list_item_id=list_item_id ) @@ -198,7 +192,9 @@ def _resolve_progress_value_source( if selector == "section": # List item id is set to None here as we do not support checking progress value sources for # repeating sections - return self.progress_store.get_section_status(SectionKey(identifier)) + return self.data_stores.progress_store.get_section_status( + SectionKey(identifier) + ) if selector == "block": if not self.location: @@ -210,7 +206,7 @@ def _resolve_progress_value_source( # Type ignore: Section id will exist at this point section_id_for_block: str = self.schema.get_section_id_for_block_id(identifier) # type: ignore - return self.progress_store.get_block_status( + return self.data_stores.progress_store.get_block_status( block_id=identifier, section_key=SectionKey( section_id=section_id_for_block, @@ -222,7 +218,7 @@ def _resolve_progress_value_source( def _resolve_list_value_source(self, value_source: Mapping) -> int | str | list: identifier = value_source["identifier"] - list_model: ListModel = self.list_store[identifier] + list_model: ListModel = self.data_stores.list_store[identifier] if selector := value_source.get("selector"): value: str | list | int = getattr(list_model, selector) @@ -259,12 +255,7 @@ def _resolve_summary_with_calculation( evaluator = rule_evaluator.RuleEvaluator( self.schema, - self.answer_store, - self.list_store, - self.metadata, - self.response_metadata, - progress_store=self.progress_store, - supplementary_data_store=self.supplementary_data_store, + data_stores=self.data_stores, location=self.location, routing_path_block_ids=self.routing_path_block_ids, ) @@ -272,17 +263,17 @@ def _resolve_summary_with_calculation( return evaluator.evaluate(calculation["operation"]) # type: ignore def _resolve_metadata_source(self, value_source: Mapping) -> str | None: - if not self.metadata: + if not self.data_stores.metadata: raise NoMetadataException identifier = value_source["identifier"] - return self.metadata[identifier] + return self.data_stores.metadata[identifier] def _resolve_location_source(self, value_source: Mapping) -> str | None: if value_source.get("identifier") == "list_item_id": return self.list_item_id def _resolve_response_metadata_source(self, value_source: Mapping) -> str | None: - return self.response_metadata.get(value_source.get("identifier")) + return self.data_stores.response_metadata.get(value_source.get("identifier")) def resolve_list(self, value_source_list: list[Mapping]) -> list[ValueSourceTypes]: values: list[ValueSourceTypes] = [] @@ -299,7 +290,7 @@ def _resolve_supplementary_data_source( ) -> ValueSourceTypes: list_item_id = self._resolve_list_item_id_for_value_source(value_source) - return self.supplementary_data_store.get_data( + return self.data_stores.supplementary_data_store.get_data( identifier=value_source["identifier"], selectors=value_source.get("selectors"), list_item_id=list_item_id, diff --git a/app/questionnaire/variants.py b/app/questionnaire/variants.py index b9b79bcc0c..971b9a6fd0 100644 --- a/app/questionnaire/variants.py +++ b/app/questionnaire/variants.py @@ -1,12 +1,8 @@ -from typing import Mapping, MutableMapping +from typing import Mapping from werkzeug.datastructures import ImmutableDict -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import ProgressStore -from app.data_models.supplementary_data_store import SupplementaryDataStore +from app.data_models.data_stores import DataStores from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.utilities.types import LocationType @@ -16,15 +12,10 @@ def choose_variant( # type: ignore block: Mapping, schema: QuestionnaireSchema, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, variants_key: str, single_key: str, current_location: LocationType, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> dict: if block.get(single_key): # Type ignore: the key passed in will be for a dictionary @@ -34,13 +25,8 @@ def choose_variant( # type: ignore when_rule_evaluator = RuleEvaluator( schema, - answer_store, - list_store, - metadata, - response_metadata, + data_stores=data_stores, location=current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) if when_rule_evaluator.evaluate(when_rules): @@ -51,78 +37,48 @@ def choose_variant( # type: ignore def choose_question_to_display( block: ImmutableDict, schema: QuestionnaireSchema, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, current_location: LocationType, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> dict: return choose_variant( block, schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, variants_key="question_variants", single_key="question", current_location=current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) def choose_content_to_display( block: ImmutableDict, schema: QuestionnaireSchema, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, current_location: LocationType, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> dict: return choose_variant( block, schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, variants_key="content_variants", single_key="content", current_location=current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) def transform_variants( block: ImmutableDict, schema: QuestionnaireSchema, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, current_location: LocationType, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> ImmutableDict: output_block = dict(block) if "question_variants" in block: question = choose_question_to_display( block, schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) output_block.pop("question_variants", None) output_block.pop("question", None) @@ -133,13 +89,8 @@ def transform_variants( content = choose_content_to_display( block, schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) output_block.pop("content_variants", None) output_block.pop("content", None) @@ -153,13 +104,8 @@ def transform_variants( output_block[list_operation] = transform_variants( block[list_operation], schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) return ImmutableDict(output_block) diff --git a/app/routes/dump.py b/app/routes/dump.py index cfafa6cd12..db5dc0f1f9 100644 --- a/app/routes/dump.py +++ b/app/routes/dump.py @@ -34,12 +34,7 @@ def dump_routing( ) -> tuple[str, int]: router = Router( schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) response = [ @@ -64,12 +59,7 @@ def dump_submission( ) -> tuple[str, int]: router = Router( schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) routing_path = router.full_routing_path() diff --git a/app/routes/flush.py b/app/routes/flush.py index efc5b4c25a..76ec8d3422 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -72,9 +72,9 @@ def flush_data() -> Response: def _submit_data(user: User) -> bool: questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) - if questionnaire_store and questionnaire_store.answer_store: + if questionnaire_store and questionnaire_store.data_stores.answer_store: # Type ignore: The presence of an answer_store implicitly verifies that there must be metadata populated and thus can safely be used non-optionally. - metadata: MetadataProxy = questionnaire_store.metadata # type: ignore + metadata: MetadataProxy = questionnaire_store.data_stores.metadata # type: ignore submitted_at = datetime.now(timezone.utc) schema = load_schema_from_metadata( metadata=metadata, language_code=metadata.language_code # type: ignore @@ -82,12 +82,7 @@ def _submit_data(user: User) -> bool: router = Router( schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) full_routing_path = router.full_routing_path() diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index f835f64c56..9b9afc4eec 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -197,7 +197,7 @@ def individual_response_post_address_confirmation( return redirect(url_for("questionnaire.get_questionnaire")) # Type ignore: @with_schema guarantees that metadata is present via QuestionnaireStore - metadata: MetadataProxy = questionnaire_store.metadata # type: ignore + metadata: MetadataProxy = questionnaire_store.data_stores.metadata # type: ignore return render_template( template="individual_response/confirmation-post", diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index af9f21508c..6d7e1a2f04 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -130,7 +130,7 @@ def before_post_submission_request() -> None: if not questionnaire_store.submitted_at: raise NotFound - handle_language(questionnaire_store.metadata) + handle_language(questionnaire_store.data_stores.metadata) # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( @@ -158,12 +158,7 @@ def get_questionnaire( ) -> Response | str: router = Router( schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) if not router.can_access_hub(): @@ -184,12 +179,7 @@ def get_questionnaire( hub_context = HubContext( language=flask_babel.get_locale().language, schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) context = hub_context( survey_complete=router.is_questionnaire_complete, @@ -243,12 +233,7 @@ def get_preview( preview_context = PreviewContext( language=flask_babel.get_locale().language, schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=questionnaire_store.data_stores, ) except PreviewNotEnabledException as exc: raise NotFound from exc diff --git a/app/routes/session.py b/app/routes/session.py index 5f595310c7..1fc4eb7453 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -154,8 +154,9 @@ def _set_questionnaire_supplementary_data( as it is possible that the survey has changed but the dataset hasn't so the validity could have changed. """ existing_sds_dataset_id = ( - questionnaire_store.metadata.survey_metadata["sds_dataset_id"] - if questionnaire_store.metadata and questionnaire_store.metadata.survey_metadata + questionnaire_store.data_stores.metadata.survey_metadata["sds_dataset_id"] + if questionnaire_store.data_stores.metadata + and questionnaire_store.data_stores.metadata.survey_metadata else None ) @@ -165,7 +166,7 @@ def _set_questionnaire_supplementary_data( ): # no need to fetch: either no supplementary data or it hasn't changed, just validate lists _validate_supplementary_data_lists( - supplementary_data=questionnaire_store.supplementary_data_store.raw_data, + supplementary_data=questionnaire_store.data_stores.supplementary_data_store.raw_data, schema=schema, ) return diff --git a/app/submitter/convert_payload_0_0_1.py b/app/submitter/convert_payload_0_0_1.py index 9932ed2533..64ce678da7 100644 --- a/app/submitter/convert_payload_0_0_1.py +++ b/app/submitter/convert_payload_0_0_1.py @@ -4,14 +4,9 @@ from werkzeug.datastructures import ImmutableDict -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) +from app.data_models import AnswerStore from app.data_models.answer import AnswerValueTypes, ListAnswer -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location from app.questionnaire.routing_path import RoutingPath @@ -23,14 +18,9 @@ # pylint: disable=too-many-locals,too-many-nested-blocks def convert_answers_to_payload_0_0_1( *, - metadata: MetadataProxy, - response_metadata: MetadataType, - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, schema: QuestionnaireSchema, full_routing_path: Iterable[RoutingPath], - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> OrderedDict[str, Any]: """ Convert answers into the data format below @@ -39,21 +29,16 @@ def convert_answers_to_payload_0_0_1( '001': '01-01-2016', '002': '30-03-2016' } - :param metadata: questionnaire metadata - :param response_metadata: response metadata - :param answer_store: questionnaire answers - :param list_store: list store + :param data_stores: questionnaire data stores :param schema: QuestionnaireSchema class with populated schema json :param full_routing_path: a list of section routing paths followed in the questionnaire - :param progress_store: progress store - :param supplementary_data_store: supplementary data store :return: data in a formatted form """ data = OrderedDict() for routing_path in full_routing_path: for block_id in routing_path: answer_ids = schema.get_answer_ids_for_block(block_id) - answers_in_block = answer_store.get_answers_by_answer_id( + answers_in_block = data_stores.answer_store.get_answers_by_answer_id( answer_ids, routing_path.list_item_id ) @@ -69,13 +54,8 @@ def convert_answers_to_payload_0_0_1( question = choose_question_to_display( block, schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, current_location=current_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) for answer_id, answer in schema.get_answers_for_question_by_id( question @@ -90,7 +70,7 @@ def convert_answers_to_payload_0_0_1( if answer_schema["type"] == "Checkbox": data.update( _get_checkbox_answer_data( - answer_store, answer_schema, value # type: ignore + data_stores.answer_store, answer_schema, value # type: ignore ) ) elif "q_code" in answer_schema: diff --git a/app/submitter/converter.py b/app/submitter/converter.py index cb7e4e07f8..30a76343a6 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -62,15 +62,11 @@ def convert_answers( Returns: Data payload """ - metadata = questionnaire_store.metadata + metadata = questionnaire_store.data_stores.metadata if not metadata: raise NoMetadataException - response_metadata = questionnaire_store.response_metadata - answer_store = questionnaire_store.answer_store - list_store = questionnaire_store.list_store - progress_store = questionnaire_store.progress_store - supplementary_data_store = questionnaire_store.supplementary_data_store + data_stores = questionnaire_store.data_stores survey_id = schema.json["survey_id"] @@ -88,17 +84,14 @@ def convert_answers( "launch_language_code": metadata.language_code or DEFAULT_LANGUAGE_CODE, } - optional_properties = get_optional_payload_properties(metadata, response_metadata) + optional_properties = get_optional_payload_properties( + metadata, data_stores.response_metadata + ) payload["data"] = get_payload_data( - answer_store=answer_store, - list_store=list_store, + data_stores=data_stores, schema=schema, full_routing_path=full_routing_path, - metadata=metadata, - response_metadata=response_metadata, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) return payload | optional_properties diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index dacb484b83..91931643f7 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -4,9 +4,9 @@ from structlog import get_logger from app.authentication.auth_payload_versions import AuthPayloadVersion -from app.data_models import AnswerStore, ListStore, ProgressStore, QuestionnaireStore +from app.data_models import QuestionnaireStore +from app.data_models.data_stores import DataStores from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException -from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.questionnaire_schema import ( DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, @@ -49,15 +49,11 @@ def convert_answers_v2( Returns: Data payload """ - metadata = questionnaire_store.metadata + metadata = questionnaire_store.data_stores.metadata if not metadata: raise NoMetadataException - response_metadata = questionnaire_store.response_metadata - answer_store = questionnaire_store.answer_store - list_store = questionnaire_store.list_store - progress_store = questionnaire_store.progress_store - supplementary_data_store = questionnaire_store.supplementary_data_store + data_stores = questionnaire_store.data_stores survey_id = schema.json["survey_id"] @@ -75,21 +71,18 @@ def convert_answers_v2( "launch_language_code": metadata.language_code or DEFAULT_LANGUAGE_CODE, } - optional_properties = get_optional_payload_properties(metadata, response_metadata) + optional_properties = get_optional_payload_properties( + metadata, data_stores.response_metadata + ) payload["survey_metadata"] = {"survey_id": survey_id} if metadata.survey_metadata: payload["survey_metadata"].update(metadata.survey_metadata.data) payload["data"] = get_payload_data( - answer_store=answer_store, - list_store=list_store, + data_stores=data_stores, schema=schema, full_routing_path=full_routing_path, - metadata=metadata, - response_metadata=response_metadata, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) logger.info("converted answer ready for submission") @@ -113,45 +106,37 @@ def get_optional_payload_properties( # pylint: disable=too-many-locals def get_payload_data( - answer_store: AnswerStore, - list_store: ListStore, + data_stores: DataStores, schema: QuestionnaireSchema, full_routing_path: Iterable[RoutingPath], - metadata: MetadataProxy, - response_metadata: MutableMapping, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, ) -> OrderedDict | dict[str, list | dict]: if schema.json["data_version"] == "0.0.1": return convert_answers_to_payload_0_0_1( - metadata=metadata, - response_metadata=response_metadata, - answer_store=answer_store, - list_store=list_store, + data_stores=data_stores, schema=schema, full_routing_path=full_routing_path, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) if schema.json["data_version"] == "0.0.3": answers = convert_answers_to_payload_0_0_3( - answer_store=answer_store, - list_store=list_store, + answer_store=data_stores.answer_store, + list_store=data_stores.list_store, schema=schema, full_routing_path=full_routing_path, ) - lists: list = list_store.serialize() + lists: list = data_stores.list_store.serialize() for list_ in lists: # for any lists that were populated by supplementary data, provide the identifier -> list_item_id mappings - if mapping := supplementary_data_store.list_mappings.get(list_["name"]): + if mapping := data_stores.supplementary_data_store.list_mappings.get( + list_["name"] + ): list_["supplementary_data_mappings"] = mapping data: dict[str, list | dict] = {"answers": answers, "lists": lists} - if supplementary_data_store.raw_data: - data["supplementary_data"] = supplementary_data_store.raw_data + if data_stores.supplementary_data_store.raw_data: + data["supplementary_data"] = data_stores.supplementary_data_store.raw_data if answer_codes := schema.json.get("answer_codes"): answer_ids_to_filter = {answer.answer_id for answer in answers} diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index cb64aaf312..95e3375c1b 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,15 +1,9 @@ from functools import cached_property -from typing import Callable, Iterable, Mapping, MutableMapping, Tuple +from typing import Callable, Iterable, Mapping, Tuple from werkzeug.datastructures import ImmutableDict -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.jinja_filters import format_number, format_percentage, format_unit from app.questionnaire.questionnaire_schema import ( QuestionnaireSchema, @@ -34,26 +28,17 @@ def __init__( self, language: str, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, routing_path: RoutingPath, current_location: LocationType, - supplementary_data_store: SupplementaryDataStore, return_location: ReturnLocation, ) -> None: super().__init__( language, schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, - supplementary_data_store, + data_stores, ) + self._data_stores = data_stores self.routing_path_block_ids = routing_path.block_ids self.current_location = current_location self.return_location = return_location @@ -88,15 +73,10 @@ def build_groups_for_section( Group( group_schema=group, routing_path_block_ids=routing_path_block_ids, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, schema=self._schema, + data_stores=self._data_stores, location=self.current_location, language=self._language, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, summary_type="CalculatedSummary", return_location=ReturnLocation( return_to=return_to, @@ -202,13 +182,8 @@ def _remove_unwanted_questions_answers( block_to_transform: ImmutableDict = transform_variants( block, self._schema, - self._metadata, - self._response_metadata, - self._answer_store, - self._list_store, + self._data_stores, self.current_location, - self._progress_store, - self._supplementary_data_store, ) transformed_block: dict = QuestionnaireSchema.get_mutable_deepcopy( block_to_transform @@ -249,15 +224,10 @@ def _get_evaluated_total( For a calculation in the new style and the list of involved block ids (possibly across sections) evaluate the total """ evaluate_calculated_summary = RuleEvaluator( - self._schema, - self._answer_store, - self._list_store, - self._metadata, - self._response_metadata, + data_stores=self._data_stores, + schema=self._schema, routing_path_block_ids=routing_path_block_ids, location=self.current_location, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, ) # Type ignore: in the case of a calculated summation it will always be a numeric type calculated_total: NumericType = evaluate_calculated_summary.evaluate(calculation) # type: ignore @@ -287,13 +257,8 @@ def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: question = choose_question_to_display( block, self._schema, - self._metadata, - self._response_metadata, - self._answer_store, - self._list_store, + self._data_stores, current_location=self.current_location, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, ) for answer in question["answers"]: if not answer_format["type"]: diff --git a/app/views/contexts/context.py b/app/views/contexts/context.py index 0ad55db4a5..dadde15ee8 100644 --- a/app/views/contexts/context.py +++ b/app/views/contexts/context.py @@ -1,11 +1,6 @@ from abc import ABC -from typing import MutableMapping -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore -from app.data_models.metadata_proxy import MetadataProxy -from app.data_models.progress_store import ProgressStore -from app.data_models.supplementary_data_store import SupplementaryDataStore +from app.data_models.data_stores import DataStores from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.router import Router @@ -16,42 +11,19 @@ def __init__( self, language: str, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, - supplementary_data_store: SupplementaryDataStore, + data_stores: DataStores, placeholder_preview_mode: bool = False, ) -> None: self._language = language self._schema = schema - self._answer_store = answer_store - self._list_store = list_store - self._progress_store = progress_store - self._supplementary_data_store = supplementary_data_store - self._metadata = metadata - self._response_metadata = response_metadata + self._data_stores = data_stores self._placeholder_preview_mode = placeholder_preview_mode - self._router = Router( - schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, - supplementary_data_store=supplementary_data_store, - ) + self._router = Router(schema=self._schema, data_stores=self._data_stores) self._placeholder_renderer = PlaceholderRenderer( + data_stores=data_stores, language=self._language, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, schema=self._schema, - progress_store=self._progress_store, placeholder_preview_mode=self._placeholder_preview_mode, - supplementary_data_store=supplementary_data_store, ) diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 337d5eac09..c3f0f1a21f 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -48,7 +48,10 @@ def _blocks_on_routing_path( } # find any sections involved in the grand calculated summary (but only if they have started, to avoid evaluating the path if not necessary) started_sections = [ - key for key, _ in self._progress_store.started_section_keys(section_ids) + key + for key, _ in self._data_stores.progress_store.started_section_keys( + section_ids + ) ] routing_path_block_ids: list[str] = [] @@ -73,15 +76,10 @@ def build_groups_for_section( Group( group_schema=group, routing_path_block_ids=routing_path_block_ids, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, location=self.current_location, language=self._language, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, summary_type="GrandCalculatedSummary", return_location=ReturnLocation( return_to="grand-calculated-summary", diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index dd72784b30..10cf142795 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -158,7 +158,7 @@ def _get_row_for_section( ) -> dict[str, Union[str, list]]: row_id = f"{section_id}-{list_item_index}" if list_item_index else section_id - section_status = self._progress_store.get_section_status( + section_status = self._data_stores.progress_store.get_section_status( SectionKey(section_id, list_item_id) ) @@ -183,7 +183,7 @@ def _get_rows( if repeating_list: for list_item_index, list_item_id in enumerate( - self._list_store[repeating_list].items, start=1 + self._data_stores.list_store[repeating_list].items, start=1 ): rows.append( self._get_row_for_repeating_section( @@ -202,7 +202,7 @@ def _individual_response_enabled(self) -> bool: for_list = self._schema.json["individual_response"]["for_list"] - if not self._list_store[for_list].non_primary_people: + if not self._data_stores.list_store[for_list].non_primary_people: return False return True diff --git a/app/views/contexts/list_context.py b/app/views/contexts/list_context.py index 6daf7babd5..20bce2146e 100644 --- a/app/views/contexts/list_context.py +++ b/app/views/contexts/list_context.py @@ -60,14 +60,14 @@ def _build_list_items_context( primary_person_edit_block_id: str | None, for_list_item_ids: Sequence[str] | None, ) -> Generator[dict, None, None]: - list_item_ids = self._list_store[for_list] + list_item_ids = self._data_stores.list_store[for_list] if for_list_item_ids: list_item_ids = [ list_item_id # type: ignore for list_item_id in list_item_ids if list_item_id in for_list_item_ids ] - primary_person = self._list_store[for_list].primary_person + primary_person = self._data_stores.list_store[for_list].primary_person for list_item_id in list_item_ids: partial_url_for = partial( @@ -86,7 +86,7 @@ def _build_list_items_context( ), "primary_person": is_primary, "list_item_id": list_item_id, - "is_complete": self._progress_store.is_section_complete( + "is_complete": self._data_stores.progress_store.is_section_complete( SectionKey(section_id, list_item_id) ), "repeating_blocks": has_repeating_blocks, diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py index cf7990511c..f3c58b9e00 100644 --- a/app/views/contexts/preview_context.py +++ b/app/views/contexts/preview_context.py @@ -1,14 +1,8 @@ -from typing import Generator, MutableMapping, Optional, Union +from typing import Generator, Union from flask_babel import lazy_gettext -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.views.contexts import Context from app.views.contexts.section_preview_context import SectionPreviewContext @@ -20,15 +14,7 @@ class PreviewNotEnabledException(Exception): class PreviewContext(Context): def __init__( - self, - language: str, - schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: Optional[MetadataProxy], - response_metadata: MutableMapping[str, Union[str, int, list]], - supplementary_data_store: SupplementaryDataStore, + self, language: str, schema: QuestionnaireSchema, data_stores: DataStores ): if not schema.preview_enabled: raise PreviewNotEnabledException @@ -36,12 +22,7 @@ def __init__( super().__init__( language, schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, - supplementary_data_store, + data_stores, placeholder_preview_mode=True, ) @@ -59,12 +40,7 @@ def build_all_sections(self) -> Generator[dict, None, None]: section_preview_context = SectionPreviewContext( language=self._language, schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, section_id=section_id, ) diff --git a/app/views/contexts/section_preview_context.py b/app/views/contexts/section_preview_context.py index c4048c2577..0b5e6d1942 100644 --- a/app/views/contexts/section_preview_context.py +++ b/app/views/contexts/section_preview_context.py @@ -1,12 +1,4 @@ -from typing import MutableMapping, Optional - -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.views.contexts.context import Context from app.views.contexts.preview import PreviewGroup @@ -18,23 +10,13 @@ def __init__( *, language: str, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, - metadata: Optional[MetadataProxy], - response_metadata: MutableMapping, + data_stores: DataStores, section_id: str, ): super().__init__( language, schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, - supplementary_data_store, + data_stores, placeholder_preview_mode=True, ) self._section_id = section_id diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 2d9d2f7d3f..335cce9b59 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -1,15 +1,9 @@ from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, MutableMapping, Optional, Union +from typing import Any, Generator, Iterable, Mapping, Union from werkzeug.datastructures import ImmutableDict -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.questionnaire_schema import LIST_COLLECTORS_WITH_REPEATING_BLOCKS from app.questionnaire.return_location import ReturnLocation @@ -25,27 +19,18 @@ def __init__( self, language: str, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: Optional[MetadataProxy], - response_metadata: MutableMapping, + data_stores: DataStores, routing_path: RoutingPath, current_location: Location, - supplementary_data_store: SupplementaryDataStore, ) -> None: super().__init__( language, schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, - supplementary_data_store, + data_stores, ) self.routing_path = routing_path self.current_location = current_location + self.data_stores = data_stores def __call__( self, @@ -106,7 +91,7 @@ def get_page_title(self, title_for_location: Union[Mapping, str]) -> str: page_title = f"{page_title}: {section_repeating_page_title}" if self.current_location.list_item_id and self.current_location.list_name: - list_item_position = self._list_store.list_item_position( + list_item_position = self._data_stores.list_store.list_item_position( self.current_location.list_name, self.current_location.list_item_id ) page_title = page_title.format(list_item_position=list_item_position) @@ -146,15 +131,10 @@ def build_summary( Group( group_schema=group, routing_path_block_ids=self.routing_path.block_ids, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, schema=self._schema, + data_stores=self._data_stores, location=self.current_location, language=self._language, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, return_location=ReturnLocation( return_to=return_to, ), @@ -182,15 +162,10 @@ def _custom_summary_elements( if summary_element["type"] == "List": list_collector_block = ListCollectorBlock( routing_path_block_ids=self.routing_path.block_ids, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, location=self.current_location, language=self._language, - supplementary_data_store=self._supplementary_data_store, return_location=ReturnLocation(return_to="section-summary"), ) yield list_collector_block.list_summary_element(summary_element) diff --git a/app/views/contexts/submit_questionnaire_context.py b/app/views/contexts/submit_questionnaire_context.py index d19450fa03..2325f64acd 100644 --- a/app/views/contexts/submit_questionnaire_context.py +++ b/app/views/contexts/submit_questionnaire_context.py @@ -34,13 +34,8 @@ def __call__(self) -> dict[str, Union[str, dict]]: summary_context = SummaryContext( language=self._language, schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, view_submitted_response=False, - supplementary_data_store=self._supplementary_data_store, ) context["summary"] = summary_context( answers_are_editable=True, return_to="final-summary" diff --git a/app/views/contexts/summary/block.py b/app/views/contexts/summary/block.py index 82760387ac..998594c793 100644 --- a/app/views/contexts/summary/block.py +++ b/app/views/contexts/summary/block.py @@ -1,19 +1,11 @@ -from typing import Mapping, MutableMapping +from typing import Mapping from jsonpointer import resolve_pointer -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.return_location import ReturnLocation -from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.schema_utils import find_pointers_containing -from app.questionnaire.value_source_resolver import ValueSourceResolver from app.questionnaire.variants import choose_variant from app.utilities.types import LocationType from app.views.contexts.summary.question import Question @@ -24,15 +16,10 @@ def __init__( self, block_schema: Mapping, *, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, location: LocationType, return_location: ReturnLocation, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, language: str, ) -> None: self.id = block_schema["id"] @@ -40,54 +27,21 @@ def __init__( self.number = block_schema.get("number") self.location = location self.schema = schema - - self._rule_evaluator = RuleEvaluator( - schema=self.schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, - location=self.location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, - ) - - self._value_source_resolver = ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, - schema=self.schema, - location=self.location, - list_item_id=self.location.list_item_id if self.location else None, - use_default_answer=True, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, - ) + self.data_stores = data_stores self.question = self.get_question( block_schema=block_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=self.data_stores, return_location=return_location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, language=language, ) def get_question( self, *, + data_stores: DataStores, block_schema: Mapping, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, return_location: ReturnLocation, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, language: str, ) -> dict[str, Question]: """Taking question variants into account, return the question which was displayed to the user""" @@ -95,30 +49,18 @@ def get_question( variant = choose_variant( block_schema, self.schema, - metadata, - response_metadata, - answer_store, - list_store, + data_stores, variants_key="question_variants", single_key="question", current_location=self.location, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) return Question( variant, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, + data_stores=self.data_stores, schema=self.schema, - rule_evaluator=self._rule_evaluator, - value_source_resolver=self._value_source_resolver, location=self.location, block_id=self.id, return_location=return_location, - metadata=metadata, - response_metadata=response_metadata, language=language, ).serialize() diff --git a/app/views/contexts/summary/calculated_summary_block.py b/app/views/contexts/summary/calculated_summary_block.py index b7ee16ae80..fb96b44970 100644 --- a/app/views/contexts/summary/calculated_summary_block.py +++ b/app/views/contexts/summary/calculated_summary_block.py @@ -1,15 +1,9 @@ from decimal import Decimal -from typing import Iterable, Mapping, MutableMapping, TypeAlias +from typing import Iterable, Mapping, TypeAlias from flask import url_for -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.return_location import ReturnLocation from app.questionnaire.rules.rule_evaluator import RuleEvaluator @@ -23,16 +17,11 @@ def __init__( self, block_schema: Mapping, *, - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, location: LocationType, return_location: ReturnLocation, - progress_store: ProgressStore, routing_path_block_ids: Iterable[str], - supplementary_data_store: SupplementaryDataStore, ) -> None: """ A Calculated summary block that is rendered as part of a grand calculated summary @@ -47,6 +36,7 @@ def __init__( self._return_location = return_location self._block_schema = block_schema self._schema = schema + self._data_stores = data_stores if self._schema.is_block_in_repeating_section(self.id): self._list_item_id = location.list_item_id self._list_name = location.list_name @@ -56,14 +46,9 @@ def __init__( self._rule_evaluator = RuleEvaluator( schema=schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=self._data_stores, location=location, - progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, - supplementary_data_store=supplementary_data_store, ) # Type ignore: for a calculated summary the resolved answer would only ever be one of these 3 diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 303bad3947..290bbe8f61 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,14 +1,8 @@ -from typing import Iterable, Mapping, MutableMapping, Type +from typing import Iterable, Mapping, Type from werkzeug.datastructures import ImmutableDict -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import ( @@ -32,15 +26,10 @@ def __init__( *, group_schema: Mapping, routing_path_block_ids: Iterable[str], - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, schema: QuestionnaireSchema, + data_stores: DataStores, location: LocationType, language: str, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, return_location: ReturnLocation, summary_type: str | None = None, view_submitted_response: bool | None = False, @@ -51,34 +40,25 @@ def __init__( self.location = location self.placeholder_text = None self.links: dict[str, Link] = {} + self.data_stores = data_stores self.blocks = self._build_blocks_and_links( group_schema=group_schema, routing_path_block_ids=routing_path_block_ids, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=self.data_stores, schema=schema, location=self.location, return_location=return_location, - progress_store=progress_store, language=language, view_submitted_response=view_submitted_response, summary_type=summary_type, - supplementary_data_store=supplementary_data_store, ) self.placeholder_renderer = PlaceholderRenderer( language=language, - answer_store=answer_store, - list_store=list_store, + data_stores=data_stores, location=self.location, - metadata=metadata, - response_metadata=response_metadata, schema=schema, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) # pylint: disable=too-many-locals @@ -87,15 +67,10 @@ def _build_blocks_and_links( *, group_schema: Mapping, routing_path_block_ids: Iterable[str], - answer_store: AnswerStore, - list_store: ListStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, location: LocationType, return_location: ReturnLocation, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, language: str, view_submitted_response: bool | None = False, summary_type: str | None = None, @@ -124,15 +99,10 @@ def _build_blocks_and_links( list_collector_block = list_collector_block_class( routing_path_block_ids=routing_path_block_ids, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=data_stores, schema=schema, location=location, language=language, - supplementary_data_store=supplementary_data_store, return_location=return_location, ) repeating_answer_blocks = ( @@ -152,16 +122,11 @@ def _build_blocks_and_links( [ Block( block, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=data_stores, schema=schema, location=location, return_location=return_location, - progress_store=progress_store, language=language, - supplementary_data_store=supplementary_data_store, ).serialize() ] ) @@ -172,16 +137,11 @@ def _build_blocks_and_links( [ CalculatedSummaryBlock( block, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=self.data_stores, schema=schema, location=location, return_location=return_location, - progress_store=progress_store, routing_path_block_ids=routing_path_block_ids, - supplementary_data_store=supplementary_data_store, ).serialize() ] ) @@ -201,17 +161,12 @@ def _build_blocks_and_links( else ListCollectorContentBlock ) list_collector_block = list_collector_block_class( + data_stores=self.data_stores, routing_path_block_ids=routing_path_block_ids, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - metadata=metadata, - response_metadata=response_metadata, schema=schema, location=location, language=language, return_location=return_location, - supplementary_data_store=supplementary_data_store, ) list_summary_element = list_collector_block.list_summary_element( summary_item diff --git a/app/views/contexts/summary/list_collector_base_block.py b/app/views/contexts/summary/list_collector_base_block.py index c28cc988e0..a6c1f8de1e 100644 --- a/app/views/contexts/summary/list_collector_base_block.py +++ b/app/views/contexts/summary/list_collector_base_block.py @@ -1,11 +1,10 @@ from collections import defaultdict -from typing import Iterable, Mapping, MutableMapping, Sequence +from typing import Iterable, Mapping, Sequence from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerStore, ProgressStore, SupplementaryDataStore -from app.data_models.list_store import ListModel, ListStore -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores +from app.data_models.list_store import ListModel from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.questionnaire_schema import is_list_collector_block_editable @@ -20,55 +19,28 @@ def __init__( self, *, routing_path_block_ids: Iterable[str], - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, schema: QuestionnaireSchema, location: LocationType, language: str, - supplementary_data_store: SupplementaryDataStore, return_location: ReturnLocation, ) -> None: self._location = location + self._data_stores = data_stores self._placeholder_renderer = PlaceholderRenderer( - language=language, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, - schema=schema, - progress_store=progress_store, - location=location, - supplementary_data_store=supplementary_data_store, + data_stores=data_stores, language=language, schema=schema, location=location ) - self._list_store = list_store self._schema = schema self._location = location # type ignore added as section should exist self._section: ImmutableDict = self._schema.get_section(self._location.section_id) # type: ignore self._language = language - self._answer_store = answer_store - self._metadata = metadata - self._response_metadata = response_metadata self._routing_path_block_ids = routing_path_block_ids - self._progress_store = progress_store - self._supplementary_data_store = supplementary_data_store self._return_location = return_location @property def list_context(self) -> list_context.ListContext: - return list_context.ListContext( - self._language, - self._schema, - self._answer_store, - self._list_store, - self._progress_store, - self._metadata, - self._response_metadata, - self._supplementary_data_store, - ) + return list_context.ListContext(self._language, self._schema, self._data_stores) def _list_collector_block_on_path(self, for_list: str) -> list[ImmutableDict]: list_collector_blocks = list( @@ -125,10 +97,7 @@ def _get_related_answer_blocks_by_list_item_id( self._placeholder_renderer.render( data_to_render=Block( block, - answer_store=self._answer_store, - list_store=self._list_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, schema=self._schema, location=Location( list_name=list_model.name, @@ -136,8 +105,6 @@ def _get_related_answer_blocks_by_list_item_id( section_id=section_id, ), return_location=self._return_location, - progress_store=self._progress_store, - supplementary_data_store=self._supplementary_data_store, language=self._language, ).serialize(), list_item_id=list_id, @@ -192,7 +159,7 @@ def get_repeating_block_related_answer_blocks( return the list of rendered question blocks for each list item id """ list_name = self._schema.list_names_by_list_repeating_block_id[block["id"]] - list_model = self._list_store[list_name] + list_model = self._data_stores.list_store[list_name] blocks: list[dict] = [] if answer_blocks_by_list_item_id := self._get_related_answer_blocks_by_list_item_id( list_model=list_model, repeating_blocks=[block] diff --git a/app/views/contexts/summary/list_collector_block.py b/app/views/contexts/summary/list_collector_block.py index 4a5a6da971..8d8a3baa27 100644 --- a/app/views/contexts/summary/list_collector_block.py +++ b/app/views/contexts/summary/list_collector_block.py @@ -17,7 +17,7 @@ def list_summary_element(self, summary: Mapping) -> dict: item_label, item_anchor, ) = (None, None, None, None, None, None) - list_model = self._list_store[summary["for_list"]] + list_model = self._data_stores.list_store[summary["for_list"]] add_link = self._add_link(summary, list_collector_block) diff --git a/app/views/contexts/summary/list_collector_content_block.py b/app/views/contexts/summary/list_collector_content_block.py index d4317b5e68..0d4bad2110 100644 --- a/app/views/contexts/summary/list_collector_content_block.py +++ b/app/views/contexts/summary/list_collector_content_block.py @@ -10,7 +10,7 @@ def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: item_label = None - current_list = self._list_store[summary["for_list"]] + current_list = self._data_stores.list_store[summary["for_list"]] list_collector_blocks_on_path = self._list_collector_block_on_path( summary["for_list"] diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index 250a3b161e..cbb35a69df 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -1,17 +1,12 @@ -from typing import Any, Mapping, MutableMapping, Optional +from typing import Any, Mapping, Optional from flask import url_for from markupsafe import Markup, escape from werkzeug.datastructures import ImmutableDict -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) +from app.data_models import AnswerStore from app.data_models.answer import AnswerValueEscapedTypes, escape_answer_value -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.forms.field_handlers.select_handlers import DynamicAnswerOptions from app.questionnaire import QuestionnaireSchema, QuestionSchemaType from app.questionnaire.placeholder_renderer import PlaceholderRenderer @@ -32,51 +27,52 @@ def __init__( self, question_schema: QuestionSchemaType, *, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - supplementary_data_store: SupplementaryDataStore, + data_stores: DataStores, schema: QuestionnaireSchema, - rule_evaluator: RuleEvaluator, - value_source_resolver: ValueSourceResolver, location: LocationType, block_id: str, return_location: ReturnLocation, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, language: str, ) -> None: self.list_item_id = location.list_item_id if location else None self.id = question_schema["id"] self.type = question_schema["type"] self.schema = schema + self.data_stores = data_stores self.answer_schemas = iter(question_schema.get("answers", [])) - self.answer_store = answer_store - self.list_store = list_store - self.progress_store = progress_store - self.supplementary_data_store = supplementary_data_store + self.location = location self.summary = question_schema.get("summary") self.title = ( question_schema.get("title") or question_schema["answers"][0]["label"] ) self.number = question_schema.get("number", None) - self.rule_evaluator = rule_evaluator - self.value_source_resolver = value_source_resolver + self._rule_evaluator = RuleEvaluator( + schema=self.schema, + data_stores=data_stores, + location=self.location, + ) + + self._value_source_resolver = ValueSourceResolver( + data_stores=data_stores, + schema=self.schema, + location=self.location, + list_item_id=self.list_item_id, + use_default_answer=True, + ) + # no need to call the method if no list item id self._is_in_repeating_section = bool( self.list_item_id and self.schema.is_block_in_repeating_section(block_id) ) self.answers = self._build_answers( - answer_store=answer_store, + answer_store=self.data_stores.answer_store, question_schema=question_schema, block_id=block_id, list_name=location.list_name if location else None, - return_location=return_location, - metadata=metadata, - response_metadata=response_metadata, language=language, + return_location=return_location, ) def get_answer( @@ -96,8 +92,6 @@ def _build_answers( block_id: str, list_name: str | None, return_location: ReturnLocation, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, language: str, ) -> list[dict[str, Any]]: if self.summary: @@ -128,8 +122,6 @@ def _build_answers( for answer_schema in self._get_resolved_answers( question_schema=question_schema, language=language, - metadata=metadata, - response_metadata=response_metadata, ): list_item_id = answer_schema.get("list_item_id") answer_id = answer_schema.get("original_answer_id") or answer_schema["id"] @@ -223,8 +215,8 @@ def _get_dynamic_answer_options( dynamic_options = DynamicAnswerOptions( dynamic_options_schema=dynamic_options_schema, - rule_evaluator=self.rule_evaluator, - value_source_resolver=self.value_source_resolver, + rule_evaluator=self._rule_evaluator, + value_source_resolver=self._value_source_resolver, ) return dynamic_options.evaluate() @@ -294,21 +286,12 @@ def _get_resolved_answers( *, question_schema: QuestionSchemaType, language: str, - metadata: MetadataProxy | None = None, - response_metadata: MutableMapping, ) -> Any: resolved_question = ImmutableDict({"answers": self.answer_schemas}) if "dynamic_answers" in question_schema: placeholder_renderer = PlaceholderRenderer( - answer_store=self.answer_store, - list_store=self.list_store, - progress_store=self.progress_store, - schema=self.schema, - language=language, - metadata=metadata, - response_metadata=response_metadata, - supplementary_data_store=self.supplementary_data_store, + data_stores=self.data_stores, language=language, schema=self.schema ) resolved_question = ImmutableDict( diff --git a/app/views/contexts/summary_context.py b/app/views/contexts/summary_context.py index ab187cacc9..1bd5919009 100644 --- a/app/views/contexts/summary_context.py +++ b/app/views/contexts/summary_context.py @@ -1,12 +1,4 @@ -from typing import MutableMapping - -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.views.contexts.context import Context @@ -18,23 +10,13 @@ def __init__( self, language: str, schema: QuestionnaireSchema, - answer_store: AnswerStore, - list_store: ListStore, - progress_store: ProgressStore, - metadata: MetadataProxy | None, - response_metadata: MutableMapping, + data_stores: DataStores, view_submitted_response: bool, - supplementary_data_store: SupplementaryDataStore, ) -> None: super().__init__( language, schema, - answer_store, - list_store, - progress_store, - metadata, - response_metadata, - supplementary_data_store, + data_stores, ) self.view_submitted_response = view_submitted_response self.summaries: list[dict] = [] @@ -58,7 +40,7 @@ def __call__( def _build_all_groups(self, return_to: str | None) -> None: for section_id in self._router.enabled_section_ids: if repeat := self._schema.get_repeat_for_section(section_id): - for_repeat = self._list_store[repeat["for_list"]] + for_repeat = self._data_stores.list_store[repeat["for_list"]] if for_repeat.count > 0: for item in for_repeat.items: @@ -84,14 +66,9 @@ def build_summary_item( section_summary_context = SectionSummaryContext( language=self._language, schema=self._schema, - answer_store=self._answer_store, - list_store=self._list_store, - progress_store=self._progress_store, - metadata=self._metadata, - response_metadata=self._response_metadata, + data_stores=self._data_stores, current_location=location, routing_path=self._router.routing_path(location.section_key), - supplementary_data_store=self._supplementary_data_store, ) summary = section_summary_context( diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index 2c7433ef39..a4f8d0cf40 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -25,7 +25,7 @@ def build_view_submitted_response_context( questionnaire_store.submitted_at # type: ignore ) - metadata = questionnaire_store.metadata + metadata = questionnaire_store.data_stores.metadata if not metadata: raise NoMetadataException @@ -58,13 +58,8 @@ def build_view_submitted_response_context( summary_context = SummaryContext( language=language, schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, + data_stores=questionnaire_store.data_stores, view_submitted_response=True, - supplementary_data_store=questionnaire_store.supplementary_data_store, ) context["summary"] = summary_context() context["pdf_url"] = url_for("post_submission.get_view_submitted_response_pdf") diff --git a/app/views/handlers/__init__.py b/app/views/handlers/__init__.py index 6b9ab0bd99..12202065dc 100644 --- a/app/views/handlers/__init__.py +++ b/app/views/handlers/__init__.py @@ -24,7 +24,7 @@ def individual_response_url( if individual_response_for_list: if ( list_item_id - != questionnaire_store.list_store[ + != questionnaire_store.data_stores.list_store[ individual_response_for_list ].primary_person ): diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 4bf47e072b..b46b63cae2 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -79,26 +79,16 @@ def questionnaire_store_updater(self) -> QuestionnaireStoreUpdater: def placeholder_renderer(self) -> PlaceholderRenderer: return PlaceholderRenderer( self._language, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, schema=self._schema, location=self._current_location, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property def router(self) -> Router: return Router( schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) def is_location_valid(self) -> bool: @@ -142,7 +132,7 @@ def _update_section_completeness( ) def _set_started_at_metadata(self) -> str | None: - response_metadata = self._questionnaire_store.response_metadata + response_metadata = self._questionnaire_store.data_stores.response_metadata if not response_metadata.get("started_at"): started_at = datetime.now(timezone.utc).isoformat() logger.info("Survey started", started_at=started_at) @@ -154,9 +144,11 @@ def _get_safe_page_title(self, page_title: Mapping | str) -> str: def _resolve_custom_page_title_vars(self) -> MutableMapping: # Type ignore: list_item_id and list_name are populated at this stage - list_item_position = self._questionnaire_store.list_store.list_item_position( - self.current_location.list_name, # type: ignore - self.current_location.list_item_id, # type: ignore + list_item_position = ( + self._questionnaire_store.data_stores.list_store.list_item_position( + self.current_location.list_name, # type: ignore + self.current_location.list_item_id, # type: ignore + ) ) return {"list_item_position": list_item_position} diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 816c2abc45..999cce80bb 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -12,15 +12,10 @@ def get_context(self) -> dict[str, dict]: summary_context = self.summary_class( language=self._language, schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, current_location=self._current_location, routing_path=self._routing_path, return_location=self._return_location, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) context = summary_context.build_view_context() diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index b0b4af444f..42a29bb953 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -78,13 +78,8 @@ def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=self.question_schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, form_data=self._form_data, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -151,7 +146,7 @@ def _publish_fulfilment_request(self) -> Task | None: # Type ignore: session data would be populated at this point self._session_store.session_data, # type: ignore # Type ignore: metadata will be populated as we reach this stage - self._questionnaire_store.metadata, # type: ignore + self._questionnaire_store.data_stores.metadata, # type: ignore self._schema, ) diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index c189cef9db..55cb3c6c01 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -13,13 +13,8 @@ def rendered_block(self) -> dict: transformed_block = transform_variants( self.block, self._schema, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, + self._questionnaire_store.data_stores, self._current_location, - self._questionnaire_store.progress_store, - self._questionnaire_store.supplementary_data_store, ) content_page_title = transformed_block.get( diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 9044ba50eb..6d579a4de6 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -65,14 +65,9 @@ def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=self.question_schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, data=None, form_data=self._form_data, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_context(self) -> Mapping[str, Union[str, bool, dict]]: @@ -92,7 +87,7 @@ def handle_post(self) -> None: session_data: SessionData = self._session_store.session_data # type: ignore session_data.feedback_count += 1 - metadata = self._questionnaire_store.metadata + metadata = self._questionnaire_store.data_stores.metadata if not metadata: raise NoMetadataException # pragma: no cover @@ -108,7 +103,7 @@ def handle_post(self) -> None: ) feedback_message = feedback_converter( metadata=metadata, - response_metadata=self._questionnaire_store.response_metadata, + response_metadata=self._questionnaire_store.data_stores.response_metadata, schema=self._schema, case_id=case_id, submission_language_code=session_data.language_code, diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index c7fc62c0fb..eacb33d2fd 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -125,21 +125,23 @@ def __init__( # Type ignore: in individual_response for_list is required which is where list_name comes from self._list_name: str = self._schema.get_individual_response_list() # type: ignore - self._metadata = self._questionnaire_store.metadata + self._metadata = self._questionnaire_store.data_stores.metadata - self._response_metadata = self._questionnaire_store.response_metadata + self._response_metadata = ( + self._questionnaire_store.data_stores.response_metadata + ) if not self._is_location_valid(): raise NotFound @cached_property def _list_model(self) -> ListModel: - return self._questionnaire_store.list_store[self._list_name] + return self._questionnaire_store.data_stores.list_store[self._list_name] @cached_property def _list_item_position(self) -> int: # Type ignore: Current usages of this cached property occur when List Name and List Item ID exist and be not None - return self._questionnaire_store.list_store.list_item_position( + return self._questionnaire_store.data_stores.list_store.list_item_position( self._list_name, self._list_item_id # type: ignore ) @@ -174,26 +176,16 @@ def placeholder_renderer(self) -> PlaceholderRenderer: return PlaceholderRenderer( # Type ignore: Language is defaulted via handle_language in the individual_response blueprint before_request which triggers this language=self._language, # type: ignore - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, schema=self._schema, location=None, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property def router(self) -> Router: return Router( schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) @cached_property @@ -206,14 +198,9 @@ def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=self.rendered_block["question"], - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, data=self._answers, form_data=self._form_data, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) def get_context(self) -> dict: @@ -239,7 +226,7 @@ def _publish_fulfilment_request(self, mobile_number: str | None = None) -> None: def _check_individual_response_count(self) -> None: if ( - self._questionnaire_store.response_metadata.get( + self._questionnaire_store.data_stores.response_metadata.get( "individual_response_count", 0 ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] @@ -249,7 +236,7 @@ def _check_individual_response_count(self) -> None: ) def _update_individual_response_count(self) -> None: - response_metadata = self._questionnaire_store.response_metadata + response_metadata = self._questionnaire_store.data_stores.response_metadata # Type ignore: if response_metadata.get("individual_response_count"): @@ -275,7 +262,7 @@ def handle_get(self) -> str: def _get_next_location_url(self) -> str: # Type ignore: Current usages of this method occur when List Name exists and is not None - list_model = self._questionnaire_store.list_store[self._list_name] # type: ignore + list_model = self._questionnaire_store.data_stores.list_store[self._list_name] # type: ignore if self._list_item_id: return url_for( @@ -323,7 +310,7 @@ def _render_block(self) -> dict[str, Any]: ) def _update_section_status(self, status: CompletionStatus) -> None: - self._questionnaire_store.progress_store.update_section_status( + self._questionnaire_store.data_stores.progress_store.update_section_status( status, SectionKey(self.individual_section_id, self._list_item_id), ) @@ -587,7 +574,7 @@ def handle_post(self) -> Response | None: ) def _update_section_completeness(self) -> None: - if not self._questionnaire_store.progress_store.get_completed_block_ids( + if not self._questionnaire_store.data_stores.progress_store.get_completed_block_ids( section_key := SectionKey( section_id=self.individual_section_id, list_item_id=self._list_item_id ) @@ -601,7 +588,7 @@ def _update_section_completeness(self) -> None: else CompletionStatus.IN_PROGRESS ) self._update_section_status(status) - if self._questionnaire_store.progress_store.is_dirty: + if self._questionnaire_store.data_stores.progress_store.is_dirty: self._questionnaire_store.save() @@ -725,7 +712,7 @@ def __init__( form_data: ImmutableMultiDict[str, str], ): self._list_name: str = schema.get_individual_response_list() # type: ignore - list_model = questionnaire_store.list_store[self._list_name] + list_model = questionnaire_store.data_stores.list_store[self._list_name] self.non_primary_people_names = {} if list_model.same_name_items: @@ -734,8 +721,10 @@ def __init__( name_answer_ids = ["first-name", "last-name"] for list_item_id in list_model.non_primary_people: - name_answers = questionnaire_store.answer_store.get_answers_by_answer_id( - name_answer_ids, list_item_id=list_item_id + name_answers = ( + questionnaire_store.data_stores.answer_store.get_answers_by_answer_id( + name_answer_ids, list_item_id=list_item_id + ) ) # Type ignore: AnswerValues can be any type, however name_answers in this context will always be strings name = " ".join(name_answer.value for name_answer in name_answers) # type: ignore diff --git a/app/views/handlers/list_action.py b/app/views/handlers/list_action.py index de73ca753a..5546db5335 100644 --- a/app/views/handlers/list_action.py +++ b/app/views/handlers/list_action.py @@ -68,7 +68,7 @@ def get_next_location_url(self) -> str: if url := self.get_section_or_final_summary_url(): return url - if self._questionnaire_store.progress_store.is_block_complete( + if self._questionnaire_store.data_stores.progress_store.is_block_complete( # Type ignore: block_id would exist at this point block_id=self.parent_location.block_id, # type: ignore section_key=self.parent_location.section_key, diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index 2fa530368d..e86ad59983 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -63,7 +63,7 @@ def _resolve_custom_page_title_vars(self) -> dict[str, int]: # For list add blocks, no list item id is yet available. Instead, we resolve # `list_item_position` to the position in the list it would be if added. list_length = len( - self._questionnaire_store.list_store[self._current_location.list_name] # type: ignore + self._questionnaire_store.data_stores.list_store[self._current_location.list_name] # type: ignore ) return {"list_item_position": list_length + 1} diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 542734bc15..2dd729aada 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -52,12 +52,7 @@ def _get_list_context(self) -> dict[str, dict]: list_context = ListContext( self._language, self._schema, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, - self._questionnaire_store.progress_store, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, - self._questionnaire_store.supplementary_data_store, + self._questionnaire_store.data_stores, ) return list_context( diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index 04c4cada20..670c180545 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -16,7 +16,7 @@ def repeating_block_ids(self) -> list[str]: def is_location_valid(self) -> bool: list_item_doesnt_exist = ( self._current_location.list_item_id - not in self._questionnaire_store.list_store[ + not in self._questionnaire_store.data_stores.list_store[ # Type ignore: list_name/list_item_id already exist self._current_location.list_name # type: ignore ].items diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index 1e42fd2519..cb3d9f113f 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -6,13 +6,13 @@ class ListRemoveQuestion(ListAction): def is_location_valid(self) -> bool: list_item_doesnt_exist = ( self._current_location.list_item_id - not in self._questionnaire_store.list_store[ + not in self._questionnaire_store.data_stores.list_store[ # Type ignore: list_name will exist within the remove block self._current_location.list_name # type: ignore ].items ) is_primary = ( - self._questionnaire_store.list_store[ + self._questionnaire_store.data_stores.list_store[ # Type ignore: list_name will exist within the remove block self._current_location.list_name # type: ignore ].primary_person diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index 2d0bbe802a..751e1d78f7 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -23,7 +23,7 @@ def _get_routing_path(self) -> RoutingPath: return self.router.routing_path(self.parent_location.section_key) def is_location_valid(self) -> bool: - primary_person_list_item_id = self._questionnaire_store.list_store[ + primary_person_list_item_id = self._questionnaire_store.data_stores.list_store[ # Type ignore: list_name will exist by this point self.current_location.list_name # type: ignore ].primary_person diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index ecfe162df1..81c362f453 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -28,28 +28,18 @@ def form(self) -> QuestionnaireForm: return generate_form( schema=self._schema, question_schema=question_json, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, location=self._current_location, form_data=self._form_data, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) answers = self._get_answers_for_question(question_json) return generate_form( schema=self._schema, question_schema=question_json, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, + data_stores=self._questionnaire_store.data_stores, location=self._current_location, data=answers, - progress_store=self._questionnaire_store.progress_store, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, ) @cached_property @@ -67,13 +57,8 @@ def rendered_block(self) -> dict: transformed_block = transform_variants( self.block, self._schema, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, + self._questionnaire_store.data_stores, self._current_location, - self._questionnaire_store.progress_store, - self._questionnaire_store.supplementary_data_store, ) page_title = transformed_block.get("page_title") or self._get_safe_page_title( transformed_block["question"]["title"] @@ -99,12 +84,7 @@ def list_context(self) -> ListContext: return ListContext( language=self._language, schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) def get_next_location_url(self) -> str: @@ -132,7 +112,7 @@ def _get_answers_for_question(self, question_json: Mapping) -> dict: or self._current_location.list_item_id ) answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id - if answer := self._questionnaire_store.answer_store.get_answer( + if answer := self._questionnaire_store.data_stores.answer_store.get_answer( answer_id=answer_id_to_use, list_item_id=list_item_id ): answer_value_by_answer_id[answer_id] = answer.value @@ -142,7 +122,7 @@ def _get_answers_for_question(self, question_json: Mapping) -> dict: def _get_list_add_question_url(self, params: dict) -> str | None: block_id = params["block_id"] list_name = params["list_name"] - list_items = self._questionnaire_store.list_store[list_name].items + list_items = self._questionnaire_store.data_stores.list_store[list_name].items section_id = self._schema.get_section_id_for_block_id(block_id) if self._is_list_just_primary(list_items, list_name) or not list_items: @@ -157,7 +137,9 @@ def _is_list_just_primary(self, list_items: list[str], list_name: str) -> bool: return ( len(list_items) == 1 and list_items[0] - == self._questionnaire_store.list_store[list_name].primary_person + == self._questionnaire_store.data_stores.list_store[ + list_name + ].primary_person ) def _get_answer_action(self) -> dict | None: @@ -278,7 +260,7 @@ def get_first_incomplete_list_repeating_block_location( if not repeating_block_ids: return None - list_model = self._questionnaire_store.list_store.get(list_name) + list_model = self._questionnaire_store.data_stores.list_store.get(list_name) for list_item_id in list_model.items: if incomplete_location := self.get_first_incomplete_list_repeating_block_location_for_list_item( repeating_block_ids=repeating_block_ids, @@ -294,11 +276,13 @@ def get_first_incomplete_list_repeating_block_location_for_list_item( section_key: SectionKey, list_name: str, ) -> Location | None: - if self._questionnaire_store.progress_store.is_section_complete(section_key): + if self._questionnaire_store.data_stores.progress_store.is_section_complete( + section_key + ): return None for repeating_block_id in repeating_block_ids: - if not self._questionnaire_store.progress_store.is_block_complete( + if not self._questionnaire_store.data_stores.progress_store.is_block_complete( block_id=repeating_block_id, section_key=section_key, ): diff --git a/app/views/handlers/relationships/relationship_collector.py b/app/views/handlers/relationships/relationship_collector.py index 83b2271ae3..89b93c18bc 100644 --- a/app/views/handlers/relationships/relationship_collector.py +++ b/app/views/handlers/relationships/relationship_collector.py @@ -61,7 +61,7 @@ def _resolve_custom_page_title_vars(self) -> MutableMapping: if to_list_item_position := self.current_location.to_list_item_id: # type: ignore page_title_vars[ "to_list_item_position" - ] = self._questionnaire_store.list_store.list_item_position( + ] = self._questionnaire_store.data_stores.list_store.list_item_position( # Type ignore: list_name populated at this stage self.current_location.list_name, # type: ignore to_list_item_position, diff --git a/app/views/handlers/relationships/relationship_question.py b/app/views/handlers/relationships/relationship_question.py index 95b4654182..698d025b72 100644 --- a/app/views/handlers/relationships/relationship_question.py +++ b/app/views/handlers/relationships/relationship_question.py @@ -50,7 +50,7 @@ def unrelated_no_answer_values(self) -> list[str] | None: @cached_property def relationship_store(self) -> RelationshipStore: - answer = self._questionnaire_store.answer_store.get_answer( + answer = self._questionnaire_store.data_stores.answer_store.get_answer( self.relationships_answer_id ) if answer: @@ -63,10 +63,10 @@ def relationship_store(self) -> RelationshipStore: def relationship_router(self) -> RelationshipRouter: # Type ignore: list will be populated at this point as it is required to build relationship list_name = self.relationships_block["for_list"] # type: ignore - list_items = self._questionnaire_store.list_store[list_name].items + list_items = self._questionnaire_store.data_stores.list_store[list_name].items return RelationshipRouter( - answer_store=self._questionnaire_store.answer_store, + answer_store=self._questionnaire_store.data_stores.answer_store, relationship_store=self.relationship_store, section_id=self._current_location.section_id, list_name=list_name, diff --git a/app/views/handlers/relationships/unrelated_question.py b/app/views/handlers/relationships/unrelated_question.py index 5f2f90f6fb..aadf543206 100644 --- a/app/views/handlers/relationships/unrelated_question.py +++ b/app/views/handlers/relationships/unrelated_question.py @@ -37,7 +37,7 @@ def get_remaining_relationships_for_individual(self) -> list[str]: These relationships won't be on the path if the user has selected "No" to the unrelated question, so we get them from the list store. """ - list_model = self._questionnaire_store.list_store[self.list_name] + list_model = self._questionnaire_store.data_stores.list_store[self.list_name] previous_location = self.relationship_router.get_previous_location( # Type ignore: block will determine type of location to be relationship location self.current_location # type: ignore diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index f4b0665698..d5cb189417 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -23,12 +23,7 @@ def __init__( self._language = language self._router = Router( schema=schema, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, - progress_store=questionnaire_store.progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) if not self._is_valid_location(): raise InvalidLocationException(f"location {self._section_id} is not valid") @@ -47,14 +42,9 @@ def get_context(self) -> Mapping: section_summary_context = SectionSummaryContext( self._language, self._schema, - self._questionnaire_store.answer_store, - self._questionnaire_store.list_store, - self._questionnaire_store.progress_store, - self._questionnaire_store.metadata, - self._questionnaire_store.response_metadata, + self._questionnaire_store.data_stores, self._routing_path, self.current_location, - self._questionnaire_store.supplementary_data_store, ) return section_summary_context() diff --git a/app/views/handlers/submission.py b/app/views/handlers/submission.py index 72203704f2..27e0fe9d45 100644 --- a/app/views/handlers/submission.py +++ b/app/views/handlers/submission.py @@ -45,7 +45,7 @@ def __init__( self._full_routing_path = full_routing_path self._session_store = get_session_store() # Type ignore: It isn't possible to not have metadata at this point - self._metadata: MetadataProxy = questionnaire_store.metadata # type: ignore + self._metadata: MetadataProxy = questionnaire_store.data_stores.metadata # type: ignore @cached_property def submitted_at(self) -> datetime: diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index 2425ee9a8c..36f21c34d1 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -28,24 +28,14 @@ def __init__( def router(self) -> Router: return Router( schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) def get_context(self) -> dict[str, str | dict]: submit_questionnaire_context = SubmitQuestionnaireContext( language=self._language, schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) return submit_questionnaire_context() diff --git a/app/views/handlers/view_preview_questions.py b/app/views/handlers/view_preview_questions.py index cd2691afe2..bff73ac906 100644 --- a/app/views/handlers/view_preview_questions.py +++ b/app/views/handlers/view_preview_questions.py @@ -21,12 +21,7 @@ def get_context(self) -> dict[str, object]: preview_context = PreviewContext( language=self._language, schema=self._schema, - answer_store=self._questionnaire_store.answer_store, - list_store=self._questionnaire_store.list_store, - progress_store=self._questionnaire_store.progress_store, - metadata=self._questionnaire_store.metadata, - response_metadata=self._questionnaire_store.response_metadata, - supplementary_data_store=self._questionnaire_store.supplementary_data_store, + data_stores=self._questionnaire_store.data_stores, ) context = { "hide_sign_out_button": True, diff --git a/tests/app/conftest.py b/tests/app/conftest.py index e8123efb4f..8fd43951c8 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -12,6 +12,7 @@ from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore @@ -100,7 +101,7 @@ def fake_questionnaire_store(): storage.get_user_data = MagicMock(return_value=("{}", "ce_sid", 1, None)) storage.add_or_update = MagicMock() store = QuestionnaireStore(storage) - store.metadata = MetadataProxy.from_dict( + store.data_stores.metadata = MetadataProxy.from_dict( { "schema_name": "test_checkbox", "display_address": "68 Abingdon Road, Goathill", @@ -172,6 +173,11 @@ def supplementary_data_store(): return SupplementaryDataStore() +@pytest.fixture +def data_stores(): + return DataStores() + + @pytest.fixture def publisher(mocker): mocker.patch( diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index 9fa4459e33..d4f9aec115 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -165,13 +165,15 @@ def questionnaire_store_with_supplementary_data( questionnaire_store, supplementary_data_store_with_data ): questionnaire_store = QuestionnaireStore(questionnaire_store.storage) - questionnaire_store.supplementary_data_store = supplementary_data_store_with_data - questionnaire_store.list_store = ListStore( + questionnaire_store.data_stores.supplementary_data_store = ( + supplementary_data_store_with_data + ) + questionnaire_store.data_stores.list_store = ListStore( [{"items": ["item-1", "item-2"], "name": "products"}] ) # Mock the identifier generation in list store so the ids are item-1, item-2, ... # pylint: disable=protected-access - questionnaire_store.list_store._generate_identifier = Mock( + questionnaire_store.data_stores.list_store._generate_identifier = Mock( side_effect=(f"item-{i}" for i in range(3, 100)) ) return questionnaire_store diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index c1b9b022e1..d051ef7c52 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -22,10 +22,11 @@ def test_questionnaire_store_json_loads( questionnaire_store.input_data = json_dumps(basic_input) # When store = QuestionnaireStore(questionnaire_store.storage) + data_stores = store.data_stores # Then - assert store.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) - assert store.response_metadata == basic_input["RESPONSE_METADATA"] - assert store.answer_store == AnswerStore(basic_input["ANSWERS"]) + assert data_stores.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) + assert data_stores.response_metadata == basic_input["RESPONSE_METADATA"] + assert data_stores.answer_store == AnswerStore(basic_input["ANSWERS"]) assert not hasattr(store, "NOT_A_LEGAL_TOP_LEVEL_KEY") assert not hasattr(store, "not_a_legal_top_level_key") @@ -33,14 +34,14 @@ def test_questionnaire_store_json_loads( assert ( len( - store.progress_store.get_completed_block_ids( + data_stores.progress_store.get_completed_block_ids( SectionKey("a-test-section", "abc123") ) ) == 1 ) assert ( - store.progress_store.get_completed_block_ids( + data_stores.progress_store.get_completed_block_ids( SectionKey("a-test-section", "abc123") )[0] == expected_completed_block_ids @@ -53,20 +54,22 @@ def test_questionnaire_store_missing_keys(questionnaire_store, basic_input): questionnaire_store.input_data = json_dumps(basic_input) # When store = QuestionnaireStore(questionnaire_store.storage) + data_stores = store.data_stores # Then - assert store.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) - assert store.response_metadata == basic_input["RESPONSE_METADATA"] - assert store.answer_store == AnswerStore(basic_input["ANSWERS"]) - assert not store.progress_store.serialize() + assert data_stores.metadata == MetadataProxy.from_dict(basic_input["METADATA"]) + assert data_stores.response_metadata == basic_input["RESPONSE_METADATA"] + assert data_stores.answer_store == AnswerStore(basic_input["ANSWERS"]) + assert not data_stores.progress_store.serialize() def test_questionnaire_store_updates_storage(questionnaire_store, basic_input): # Given store = QuestionnaireStore(questionnaire_store.storage) + data_stores = store.data_stores store.set_metadata(basic_input["METADATA"]) - store.answer_store = AnswerStore(basic_input["ANSWERS"]) - store.response_metadata = basic_input["RESPONSE_METADATA"] - store.progress_store = ProgressStore(basic_input["PROGRESS"]) + data_stores.answer_store = AnswerStore(basic_input["ANSWERS"]) + data_stores.response_metadata = basic_input["RESPONSE_METADATA"] + data_stores.progress_store = ProgressStore(basic_input["PROGRESS"]) store.supplementary_data_store = SupplementaryDataStore.deserialize( basic_input["SUPPLEMENTARY_DATA"] ) @@ -87,9 +90,10 @@ class NotSerializable: store = QuestionnaireStore(questionnaire_store.storage) store.set_metadata(non_serializable_metadata) - store.response_metadata = basic_input["RESPONSE_METADATA"] - store.answer_store = AnswerStore(basic_input["ANSWERS"]) - store.progress_store = ProgressStore(basic_input["PROGRESS"]) + data_stores = store.data_stores + data_stores.response_metadata = basic_input["RESPONSE_METADATA"] + data_stores.answer_store = AnswerStore(basic_input["ANSWERS"]) + data_stores.progress_store = ProgressStore(basic_input["PROGRESS"]) # When / Then with pytest.raises(TypeError): @@ -100,24 +104,25 @@ def test_questionnaire_store_deletes(questionnaire_store, basic_input): # Given store = QuestionnaireStore(questionnaire_store.storage) store.set_metadata(basic_input["METADATA"]) - store.response_metadata = basic_input["RESPONSE_METADATA"] - store.answer_store = AnswerStore(basic_input["ANSWERS"]) - store.progress_store = ProgressStore(basic_input["PROGRESS"]) + data_stores = store.data_stores + data_stores.response_metadata = basic_input["RESPONSE_METADATA"] + data_stores.answer_store = AnswerStore(basic_input["ANSWERS"]) + data_stores.progress_store = ProgressStore(basic_input["PROGRESS"]) # When store.delete() # Then - assert "a-test-section" not in store.progress_store - assert len(store.answer_store) == 0 - assert store.response_metadata == {} + assert "a-test-section" not in data_stores.progress_store + assert len(data_stores.answer_store) == 0 + assert data_stores.response_metadata == {} def test_questionnaire_store_raises_when_writing_to_metadata(questionnaire_store): store = QuestionnaireStore(questionnaire_store.storage) with pytest.raises(TypeError): - store.metadata["no"] = "writing" + store.data_stores.metadata["no"] = "writing" class TestQuestionnaireStoreWithSupplementaryData: @@ -125,9 +130,9 @@ class TestQuestionnaireStoreWithSupplementaryData: def assert_list_store_data(self, list_name: str, list_item_ids: list[str]): """Helper function to check that ListStore contains the given list with matching list_item_ids""" - lists = [list_model.name for list_model in self.store.list_store] + lists = [list_model.name for list_model in self.store.data_stores.list_store] assert list_name in lists - assert self.store.list_store[list_name].items == list_item_ids + assert self.store.data_stores.list_store[list_name].items == list_item_ids def test_adding_new_supplementary_data( self, questionnaire_store, supplementary_data @@ -137,9 +142,11 @@ def test_adding_new_supplementary_data( """ self.store = QuestionnaireStore(questionnaire_store.storage) self.store.set_supplementary_data(supplementary_data) - assert "products" in self.store.supplementary_data_store.list_lookup + assert "products" in self.store.data_stores.supplementary_data_store.list_lookup supplementary_list_item_ids = list( - self.store.supplementary_data_store.list_lookup["products"].values() + self.store.data_stores.supplementary_data_store.list_lookup[ + "products" + ].values() ) # check list mapping ids match list store ids self.assert_list_store_data("products", supplementary_list_item_ids) @@ -155,17 +162,20 @@ def test_updating_supplementary_data( supplementary_data["items"]["products"].append({"identifier": "12345"}) self.store.set_supplementary_data(supplementary_data) - assert self.store.supplementary_data_store.list_mappings == make_immutable( - { - "products": [ - {"identifier": 89929001, "list_item_id": "item-1"}, - {"identifier": "201630601", "list_item_id": "item-2"}, - {"identifier": "12345", "list_item_id": "item-3"}, - ], - "supermarkets": [ - {"identifier": "54321", "list_item_id": "item-4"}, - ], - } + assert ( + self.store.data_stores.supplementary_data_store.list_mappings + == make_immutable( + { + "products": [ + {"identifier": 89929001, "list_item_id": "item-1"}, + {"identifier": "201630601", "list_item_id": "item-2"}, + {"identifier": "12345", "list_item_id": "item-3"}, + ], + "supermarkets": [ + {"identifier": "54321", "list_item_id": "item-4"}, + ], + } + ) ) self.assert_list_store_data("products", ["item-1", "item-2", "item-3"]) @@ -190,7 +200,7 @@ def test_removing_all_supplementary_data( """Checks that removing all supplementary data clears out the list store""" self.store = questionnaire_store_with_supplementary_data self.store.set_supplementary_data({}) - assert len(list(self.store.list_store)) == 0 + assert len(list(self.store.data_stores.list_store)) == 0 def test_removing_supplementary_lists_with_answers( self, questionnaire_store_with_supplementary_data, supplementary_data @@ -200,7 +210,7 @@ def test_removing_supplementary_lists_with_answers( self.store = questionnaire_store_with_supplementary_data # add some answers for the supplementary list items - self.store.answer_store = AnswerStore( + self.store.data_stores.answer_store = AnswerStore( [ { "answer_id": "product-sales-answer", @@ -222,13 +232,13 @@ def test_removing_supplementary_lists_with_answers( # item-1 should be gone self.assert_list_store_data("products", ["item-2"]) # the answer for it should be too - answers = list(self.store.answer_store.answer_map.keys()) + answers = list(self.store.data_stores.answer_store.answer_map.keys()) assert len(answers) == 1 assert answers[0] == ("product-sales-answer", "item-2") # remove all answers self.store.set_supplementary_data({}) - assert not self.store.answer_store.answer_map + assert not self.store.data_stores.answer_store.answer_map def test_removing_supplementary_data_ignores_non_supplementary_data( self, questionnaire_store_with_supplementary_data @@ -236,7 +246,7 @@ def test_removing_supplementary_data_ignores_non_supplementary_data( """Tests that removing supplementary data does not affect other lists and answers""" self.store = questionnaire_store_with_supplementary_data # unrelated - self.store.answer_store = AnswerStore( + self.store.data_stores.answer_store = AnswerStore( [ { "answer_id": "unrelated-answer", @@ -249,11 +259,11 @@ def test_removing_supplementary_data_ignores_non_supplementary_data( }, ] ) - self.store.list_store.add_list_item("supermarkets") + self.store.data_stores.list_store.add_list_item("supermarkets") self.assert_list_store_data("products", ["item-1", "item-2"]) self.assert_list_store_data("supermarkets", ["item-3"]) self.store.set_supplementary_data({}) self.assert_list_store_data("supermarkets", ["item-3"]) - answers = list(self.store.answer_store.answer_map.keys()) + answers = list(self.store.data_stores.answer_store.answer_map.keys()) assert answers == [("unrelated-answer", "JxSW21"), ("sales", None)] diff --git a/tests/app/forms/field_handlers/conftest.py b/tests/app/forms/field_handlers/conftest.py index 7a29043aaf..ff1a04e61d 100644 --- a/tests/app/forms/field_handlers/conftest.py +++ b/tests/app/forms/field_handlers/conftest.py @@ -1,8 +1,6 @@ import pytest -from app.data_models import ProgressStore, SupplementaryDataStore -from app.data_models.answer_store import AnswerStore -from app.data_models.list_store import ListStore +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver @@ -27,14 +25,9 @@ def get_mock_response_metadata(): @pytest.fixture def rule_evaluator(): evaluator = RuleEvaluator( - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=None, - response_metadata=get_mock_response_metadata(), + data_stores=DataStores(response_metadata=get_mock_response_metadata()), schema=get_mock_schema(), location=None, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) return evaluator @@ -43,16 +36,11 @@ def rule_evaluator(): @pytest.fixture def value_source_resolver(): resolver = ValueSourceResolver( - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=None, - response_metadata=get_mock_response_metadata(), + data_stores=DataStores(response_metadata=get_mock_response_metadata()), schema=get_mock_schema(), location=None, list_item_id=None, escape_answer_values=False, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) return resolver diff --git a/tests/app/forms/field_handlers/test_date_handler.py b/tests/app/forms/field_handlers/test_date_handler.py index 994d2917cf..036ca62553 100644 --- a/tests/app/forms/field_handlers/test_date_handler.py +++ b/tests/app/forms/field_handlers/test_date_handler.py @@ -51,7 +51,7 @@ def test_generate_date_form_validates_single_date_period( ): schema = load_schema_from_name("test_date_validation_single") value_source_resolver.schema = schema - value_source_resolver.metadata = {"ref_p_start_date": "2017-02-20"} + value_source_resolver.data_stores.metadata = {"ref_p_start_date": "2017-02-20"} handler = DateHandler( schema.get_answers_by_answer_id("date-range-from")[0], @@ -115,7 +115,7 @@ def test_get_referenced_offset_value_for_now_value( def test_get_referenced_offset_value_for_meta( app, value_source_resolver, rule_evaluator ): - value_source_resolver.metadata = {"date": "2018-02-20"} + value_source_resolver.data_stores.metadata = {"date": "2018-02-20"} answer = {"minimum": {"value": {"identifier": "date", "source": "metadata"}}} handler = DateHandler(answer, value_source_resolver, rule_evaluator, error_messages) @@ -135,7 +135,7 @@ def test_get_referenced_offset_value_for_answer_id( 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 + value_source_resolver.data_stores.answer_store = answer_store answer = {"maximum": {"value": {"identifier": "date", "source": "answers"}}} handler = DateHandler(answer, value_source_resolver, rule_evaluator, error_messages) @@ -163,7 +163,7 @@ def test_get_referenced_offset_value_with_list_item_id( answer_store = AnswerStore() answer_store.add_or_update(test_answer_id) - value_source_resolver.answer_store = answer_store + value_source_resolver.data_stores.answer_store = answer_store value_source_resolver.location = location value_source_resolver.list_item_id = list_item_id answer = { @@ -195,12 +195,12 @@ def test_get_referenced_offset_value_with_no_offset( "app.utilities.schema.load_schema_from_name", return_value=QuestionnaireSchema({}) ) def test_minimum_and_maximum_offset_dates(app, value_source_resolver, rule_evaluator): - value_source_resolver.metadata = {"date": "2018-02-20"} + value_source_resolver.data_stores.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 + value_source_resolver.data_stores.answer_store = answer_store answer = { "id": "date_answer", "type": "Date", diff --git a/tests/app/forms/field_handlers/test_number_handler.py b/tests/app/forms/field_handlers/test_number_handler.py index ecdbd1eb7d..d542ceb13c 100644 --- a/tests/app/forms/field_handlers/test_number_handler.py +++ b/tests/app/forms/field_handlers/test_number_handler.py @@ -385,7 +385,7 @@ def test_get_schema_value_answer_store(value_source_resolver, rule_evaluator): answer_store.add_or_update(Answer(answer_id="set-maximum", value=10)) answer_store.add_or_update(Answer(answer_id="set-minimum", value=1)) - value_source_resolver.answer_store = answer_store + value_source_resolver.data_stores.answer_store = answer_store number_handler = NumberHandler( answer_schema, value_source_resolver, rule_evaluator, error_messages ) diff --git a/tests/app/forms/test_field_factory.py b/tests/app/forms/test_field_factory.py index c8c9f214ab..8b0b125fbb 100644 --- a/tests/app/forms/test_field_factory.py +++ b/tests/app/forms/test_field_factory.py @@ -1,6 +1,6 @@ import pytest -from app.data_models import ProgressStore, SupplementaryDataStore +from app.data_models.data_stores import DataStores from app.forms import error_messages from app.forms.field_handlers import get_field_handler from app.questionnaire import QuestionnaireSchema @@ -8,7 +8,7 @@ from app.questionnaire.value_source_resolver import ValueSourceResolver -def test_invalid_field_type_raises_on_invalid(answer_store, list_store): +def test_invalid_field_type_raises_on_invalid(): schema = QuestionnaireSchema( { "questionnaire_flow": { @@ -33,30 +33,18 @@ def test_invalid_field_type_raises_on_invalid(answer_store, list_store): "case_ref": "1000000000000001", } - response_metadata = {} - value_source_resolver = ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata, response_metadata={}), schema=schema, location=None, list_item_id=None, escape_answer_values=False, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) rule_evaluator = RuleEvaluator( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(), schema=schema, location=None, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) # Given diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 02a2b3dd24..9e5027b0b0 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -4,8 +4,9 @@ import pytest from werkzeug.datastructures import MultiDict -from app.data_models import ListStore, ProgressStore, SupplementaryDataStore +from app.data_models import ListStore from app.data_models.answer_store import Answer, AnswerStore +from app.data_models.data_stores import DataStores from app.forms import error_messages from app.forms.questionnaire_form import generate_form from app.forms.validators import ( @@ -27,7 +28,7 @@ def error_exists(answer_id, msg, mapped_errors): ) -def test_form_ids_match_block_answer_ids(app, answer_store, list_store): +def test_form_ids_match_block_answer_ids(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_textfield") @@ -36,19 +37,14 @@ def test_form_ids_match_block_answer_ids(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) for answer_id in schema.get_answer_ids_for_block("name-block"): assert hasattr(form, answer_id) -def test_form_date_range_populates_data(app, answer_store, list_store): +def test_form_date_range_populates_data(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") @@ -74,19 +70,14 @@ def test_form_date_range_populates_data(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) assert form.data == expected_form_data -def test_date_range_matching_dates_raises_question_error(app, answer_store, list_store): +def test_date_range_matching_dates_raises_question_error(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") @@ -112,13 +103,8 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) form.validate() @@ -129,9 +115,7 @@ def test_date_range_matching_dates_raises_question_error(app, answer_store, list ) -def test_date_range_to_precedes_from_raises_question_error( - app, answer_store, list_store -): +def test_date_range_to_precedes_from_raises_question_error(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") @@ -157,13 +141,8 @@ def test_date_range_to_precedes_from_raises_question_error( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) form.validate() @@ -174,9 +153,7 @@ def test_date_range_to_precedes_from_raises_question_error( ) -def test_date_range_too_large_period_raises_question_error( - app, answer_store, list_store -): +def test_date_range_too_large_period_raises_question_error(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -202,13 +179,8 @@ def test_date_range_too_large_period_raises_question_error( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -218,9 +190,7 @@ def test_date_range_too_large_period_raises_question_error( ] % {"max": "1 month, 20 days"} -def test_date_range_too_small_period_raises_question_error( - app, answer_store, list_store -): +def test_date_range_too_small_period_raises_question_error(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -246,13 +216,8 @@ def test_date_range_too_small_period_raises_question_error( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -262,7 +227,7 @@ def test_date_range_too_small_period_raises_question_error( ] % {"min": "23 days"} -def test_date_range_valid_period(app, answer_store, list_store): +def test_date_range_valid_period(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -288,20 +253,15 @@ def test_date_range_valid_period(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) form.validate() assert form.data == expected_form_data -def test_date_combined_single_validation(app, answer_store, list_store): +def test_date_combined_single_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_combined") @@ -325,8 +285,6 @@ def test_date_combined_single_validation(app, answer_store, list_store): metadata = get_metadata(test_metadata) - response_metadata = {} - expected_form_data = { "csrf_token": None, "date-range-from": "2017-01-01", @@ -336,13 +294,8 @@ def test_date_combined_single_validation(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(metadata=metadata), ) form.validate() @@ -356,7 +309,7 @@ def test_date_combined_single_validation(app, answer_store, list_store): ] % {"max": "14 March 2017"} -def test_date_combined_range_too_small_validation(app, answer_store, list_store): +def test_date_combined_range_too_small_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_combined") @@ -389,13 +342,8 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -405,7 +353,7 @@ def test_date_combined_range_too_small_validation(app, answer_store, list_store) ] % {"min": "10 days"} -def test_date_combined_range_too_large_validation(app, answer_store, list_store): +def test_date_combined_range_too_large_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_combined") @@ -429,8 +377,6 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) metadata = get_metadata(test_metadata) - response_metadata = {} - expected_form_data = { "csrf_token": None, "date-range-from": "2017-01-01", @@ -440,13 +386,8 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -456,7 +397,7 @@ def test_date_combined_range_too_large_validation(app, answer_store, list_store) ] % {"max": "50 days"} -def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): +def test_date_mm_yyyy_combined_single_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_mm_yyyy_combined") @@ -478,8 +419,6 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): metadata = get_metadata(test_metadata) - response_metadata = {} - expected_form_data = { "csrf_token": None, "date-range-from": "2016-11", @@ -489,13 +428,8 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -509,9 +443,7 @@ def test_date_mm_yyyy_combined_single_validation(app, answer_store, list_store): ] % {"max": "June 2017"} -def test_date_mm_yyyy_combined_range_too_small_validation( - app, answer_store, list_store -): +def test_date_mm_yyyy_combined_range_too_small_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_mm_yyyy_combined") @@ -542,13 +474,8 @@ def test_date_mm_yyyy_combined_range_too_small_validation( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -558,9 +485,7 @@ def test_date_mm_yyyy_combined_range_too_small_validation( ] % {"min": "2 months"} -def test_date_mm_yyyy_combined_range_too_large_validation( - app, answer_store, list_store -): +def test_date_mm_yyyy_combined_range_too_large_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_mm_yyyy_combined") @@ -582,8 +507,6 @@ def test_date_mm_yyyy_combined_range_too_large_validation( metadata = get_metadata(test_metadata) - response_metadata = {} - expected_form_data = { "csrf_token": None, "date-range-from": "2017-01", @@ -593,13 +516,8 @@ def test_date_mm_yyyy_combined_range_too_large_validation( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -609,7 +527,7 @@ def test_date_mm_yyyy_combined_range_too_large_validation( ] % {"max": "3 months"} -def test_date_yyyy_combined_single_validation(app, answer_store, list_store): +def test_date_yyyy_combined_single_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_yyyy_combined") @@ -635,13 +553,8 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -655,7 +568,7 @@ def test_date_yyyy_combined_single_validation(app, answer_store, list_store): ] % {"max": "2021"} -def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_store): +def test_date_yyyy_combined_range_too_small_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_yyyy_combined") @@ -681,13 +594,8 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -697,7 +605,7 @@ def test_date_yyyy_combined_range_too_small_validation(app, answer_store, list_s ] % {"min": "2 years"} -def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_store): +def test_date_yyyy_combined_range_too_large_validation(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_yyyy_combined") @@ -723,13 +631,8 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -739,9 +642,7 @@ def test_date_yyyy_combined_range_too_large_validation(app, answer_store, list_s ] % {"max": "3 years"} -def test_date_raises_ValueError_when_any_date_range_parts_are_falsy( - app, answer_store, list_store -): +def test_date_raises_ValueError_when_any_date_range_parts_are_falsy(app): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_combined") @@ -760,27 +661,18 @@ def test_date_raises_ValueError_when_any_date_range_parts_are_falsy( metadata = get_metadata() - response_metadata = {} - form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(ValueError): form.validate() -def test_bespoke_message_for_date_validation_range( - app, answer_store, list_store, mocker -): +def test_bespoke_message_for_date_validation_range(app, data_stores, mocker): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -819,13 +711,8 @@ def test_bespoke_message_for_date_validation_range( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) mocker.patch( @@ -836,9 +723,7 @@ def test_bespoke_message_for_date_validation_range( assert form.question_errors["date-range-question"] == "Test Message" -def test_invalid_minimum_period_limit_and_single_date_periods( - app, answer_store, list_store, mocker -): +def test_invalid_minimum_period_limit_and_single_date_periods(app, data_stores, mocker): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -878,13 +763,8 @@ def test_invalid_minimum_period_limit_and_single_date_periods( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -898,9 +778,7 @@ def test_invalid_minimum_period_limit_and_single_date_periods( ) -def test_invalid_maximum_period_limit_and_single_date_periods( - app, answer_store, list_store, mocker -): +def test_invalid_maximum_period_limit_and_single_date_periods(app, data_stores, mocker): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -940,13 +818,8 @@ def test_invalid_maximum_period_limit_and_single_date_periods( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -962,7 +835,7 @@ def test_invalid_maximum_period_limit_and_single_date_periods( def test_period_limits_minimum_not_set_and_single_date_periods( - app, answer_store, list_store, mocker + app, data_stores, mocker ): with app.test_request_context(): schema = load_schema_from_name("test_date_validation_range") @@ -1003,13 +876,8 @@ def test_period_limits_minimum_not_set_and_single_date_periods( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with mocker.patch( @@ -1021,9 +889,7 @@ def test_period_limits_minimum_not_set_and_single_date_periods( assert len(form.question_errors) == 0 -def test_invalid_date_range_and_single_date_periods( - app, answer_store, list_store, mocker -): +def test_invalid_date_range_and_single_date_periods(app, answer_store, mocker): with app.test_request_context(): test_answer_id = Answer(answer_id="date", value="2017-03-20") answer_store.add_or_update(test_answer_id) @@ -1070,13 +936,8 @@ def test_invalid_date_range_and_single_date_periods( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(metadata), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, metadata=metadata), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -1091,7 +952,7 @@ def test_invalid_date_range_and_single_date_periods( ) -def test_invalid_calculation_type(app, answer_store, list_store, mocker): +def test_invalid_calculation_type(app, answer_store, mocker): answer_total = Answer(answer_id="total-answer", value=10) answer_store.add_or_update(answer_total) @@ -1117,13 +978,8 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) with pytest.raises(Exception) as exc: @@ -1135,7 +991,7 @@ def test_invalid_calculation_type(app, answer_store, list_store, mocker): assert "Invalid calculation_type: subtraction" == str(exc.value) -def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocker): +def test_bespoke_message_for_sum_validation(app, answer_store, mocker): answer_total = Answer(answer_id="total-answer", value=10) answer_store.add_or_update(answer_total) @@ -1156,13 +1012,8 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) mocker.patch( @@ -1335,7 +1186,6 @@ def test_bespoke_message_for_sum_validation(app, answer_store, list_store, mocke def test_calculated_field( app, answer_store, - list_store, schema_name, block, answers, @@ -1359,21 +1209,14 @@ def test_calculated_field( list_item_id=None, ) - metadata = get_metadata() - form_data = MultiDict(breakdowns) form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, + data_stores=DataStores(answer_store=answer_store), location=location, - metadata=metadata, - response_metadata={}, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1423,13 +1266,8 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, list_store=list_store), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1438,7 +1276,7 @@ def test_sum_calculated_field_value_source_calculated_summary_repeat_not_equal_v ] == schema.error_messages["TOTAL_SUM_NOT_EQUALS"] % {"total": "10"} -def test_multi_calculation(app, answer_store, list_store): +def test_multi_calculation(app, answer_store): answer_total = Answer(answer_id="total-answer", value=Decimal("10.00")) answer_store.add_or_update(answer_total) @@ -1457,17 +1295,14 @@ def test_multi_calculation(app, answer_store, list_store): } ) + data_stores = DataStores(answer_store=answer_store) + # With no answers question validation should pass form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1479,13 +1314,8 @@ def test_multi_calculation(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1497,13 +1327,8 @@ def test_multi_calculation(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1512,7 +1337,7 @@ def test_multi_calculation(app, answer_store, list_store): ] % {"total": "10.00"} -def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_store): +def test_generate_form_with_title_and_no_answer_label(app, answer_store): """ Checks that the form is still generated when there is no answer label but there is a question title """ @@ -1532,20 +1357,15 @@ def test_generate_form_with_title_and_no_answer_label(app, answer_store, list_st form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() assert form.data == expected_form_data -def test_form_errors_are_correctly_mapped(app, answer_store, list_store): +def test_form_errors_are_correctly_mapped(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_numbers") @@ -1554,12 +1374,7 @@ def test_form_errors_are_correctly_mapped(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) form.validate() @@ -1570,7 +1385,7 @@ def test_form_errors_are_correctly_mapped(app, answer_store, list_store): ) -def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store): +def test_form_subfield_errors_are_correctly_mapped(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") @@ -1579,12 +1394,7 @@ def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) form.validate() @@ -1603,9 +1413,7 @@ def test_form_subfield_errors_are_correctly_mapped(app, answer_store, list_store ) -def test_detail_answer_mandatory_only_checked_if_option_selected( - app, answer_store, list_store -): +def test_detail_answer_mandatory_only_checked_if_option_selected(app, data_stores): # The detail_answer can only be mandatory if the option it is associated with is answered with app.test_request_context(): schema = load_schema_from_name("test_checkbox_detail_answer_multiple") @@ -1616,13 +1424,8 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=MultiDict({"mandatory-checkbox-answer": "Your choice"}), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") @@ -1632,22 +1435,15 @@ def test_detail_answer_mandatory_only_checked_if_option_selected( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, data={"mandatory-checkbox-answer": "Ham"}, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") assert detail_answer_field.validators == () -def test_answer_with_detail_answer_errors_are_correctly_mapped( - app, answer_store, list_store -): +def test_answer_with_detail_answer_errors_are_correctly_mapped(app, data_stores): with app.test_request_context(): schema = load_schema_from_name( "test_radio_mandatory_with_detail_answer_mandatory" @@ -1658,13 +1454,8 @@ def test_answer_with_detail_answer_errors_are_correctly_mapped( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=MultiDict({"radio-mandatory-answer": "Other"}), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1682,7 +1473,7 @@ def test_answer_with_detail_answer_errors_are_correctly_mapped( ) -def test_answer_errors_are_interpolated(app, answer_store, list_store): +def test_answer_errors_are_interpolated(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_numbers") @@ -1691,13 +1482,8 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=MultiDict({"set-minimum": "-10001"}), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() @@ -1709,7 +1495,7 @@ def test_answer_errors_are_interpolated(app, answer_store, list_store): def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( - app, answer_store, list_store + app, data_stores ): with app.test_request_context(): schema = load_schema_from_name("test_mutually_exclusive") @@ -1721,13 +1507,8 @@ def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=MultiDict(), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1738,9 +1519,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_when_not_answered( ) -def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( - app, list_store -): +def test_mandatory_mutually_exclusive_question_raises_error_with_question_text(app): with app.test_request_context(): schema = load_schema_from_name("test_question_title_in_error") @@ -1753,14 +1532,9 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( renderer = PlaceholderRenderer( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=schema, - progress_store=ProgressStore(), location=Location(section_id="mutually-exclusive-checkbox-section"), - supplementary_data_store=SupplementaryDataStore(), ) rendered_schema = renderer.render( data_to_render=question_schema, list_item_id=None @@ -1769,13 +1543,8 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( form = generate_form( schema=schema, question_schema=rendered_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), form_data=MultiDict(), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) error = form.question_errors["mutually-exclusive-checkbox-question"] @@ -1786,9 +1555,7 @@ def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( ) -def test_mutually_exclusive_question_raises_error_when_both_answered( - app, answer_store, list_store -): +def test_mutually_exclusive_question_raises_error_when_both_answered(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_mutually_exclusive") @@ -1806,13 +1573,8 @@ def test_mutually_exclusive_question_raises_error_when_both_answered( form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) form.validate_mutually_exclusive_question(question_schema) @@ -1822,7 +1584,7 @@ def test_mutually_exclusive_question_raises_error_when_both_answered( ) -def test_date_range_form(app, answer_store, list_store): +def test_date_range_form(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") @@ -1830,12 +1592,7 @@ def test_date_range_form(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=data_stores, ) assert hasattr(form, "date-range-from-answer") @@ -1848,7 +1605,7 @@ def test_date_range_form(app, answer_store, list_store): assert isinstance(period_to_field.year.validators[0], DateRequired) -def test_date_range_form_with_data(app, answer_store, list_store): +def test_date_range_form_with_data(app, data_stores): with app.test_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") @@ -1867,13 +1624,8 @@ def test_date_range_form_with_data(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) assert hasattr(form, "date-range-from-answer") @@ -1889,7 +1641,7 @@ def test_date_range_form_with_data(app, answer_store, list_store): assert period_to_field.data == "2017-09-01" -def test_form_for_radio_other_not_selected(app, answer_store, list_store): +def test_form_for_radio_other_not_selected(app, data_stores): with app.test_request_context(): schema = load_schema_from_name( "test_radio_mandatory_with_detail_answer_mandatory" @@ -1907,13 +1659,8 @@ def test_form_for_radio_other_not_selected(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) assert hasattr(form, "radio-mandatory-answer") @@ -1921,7 +1668,7 @@ def test_form_for_radio_other_not_selected(app, answer_store, list_store): assert other_text_field.data == "" -def test_form_for_radio_other_selected(app, answer_store, list_store): +def test_form_for_radio_other_selected(app, data_stores): with app.test_request_context(): schema = load_schema_from_name( "test_radio_mandatory_with_detail_answer_mandatory" @@ -1939,13 +1686,8 @@ def test_form_for_radio_other_selected(app, answer_store, list_store): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=data_stores, form_data=form_data, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) other_text_field = getattr(form, "other-answer-mandatory") @@ -1957,7 +1699,7 @@ def test_dynamic_answers_question_validates(app): schema = load_schema_from_name( "test_validation_sum_against_total_dynamic_answers" ) - answer_store = AnswerStore([{"answer_id": "total-answer", "value": 100}]) + question_schema = schema.get_block("dynamic-answer").get("question") question_schema = QuestionnaireSchema.get_mutable_deepcopy(question_schema) question_schema["answers"].append( @@ -1973,8 +1715,6 @@ def test_dynamic_answers_question_validates(app): } ) - list_store = ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]) - form_data = MultiDict( { "percentage-of-shopping-lCIZsS": "25", @@ -1991,19 +1731,17 @@ def test_dynamic_answers_question_validates(app): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, - form_data=form_data, - progress_store=ProgressStore(), + data_stores=DataStores( + answer_store=AnswerStore([{"answer_id": "total-answer", "value": 100}]), + list_store=ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]), + ), location=Location( section_id="section", block_id="dynamic-answer", list_name=None, list_item_id=None, ), - supplementary_data_store=SupplementaryDataStore(), + form_data=form_data, ) form.validate() @@ -2015,7 +1753,7 @@ def test_dynamic_answers_question_raises_validation_error(app): schema = load_schema_from_name( "test_validation_sum_against_total_dynamic_answers" ) - answer_store = AnswerStore([{"answer_id": "total-answer", "value": 100}]) + question_schema = schema.get_block("dynamic-answer").get("question") question_schema = QuestionnaireSchema.get_mutable_deepcopy(question_schema) question_schema["answers"].append( @@ -2031,8 +1769,6 @@ def test_dynamic_answers_question_raises_validation_error(app): } ) - list_store = ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]) - form_data = MultiDict( { "percentage-of-shopping-lCIZsS": "25", @@ -2043,19 +1779,17 @@ def test_dynamic_answers_question_raises_validation_error(app): form = generate_form( schema=schema, question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores( + answer_store=AnswerStore([{"answer_id": "total-answer", "value": 100}]), + list_store=ListStore([{"name": "supermarkets", "items": ["lCIZsS"]}]), + ), form_data=form_data, - progress_store=ProgressStore(), location=Location( section_id="section", block_id="dynamic-answer", list_name=None, list_item_id=None, ), - supplementary_data_store=SupplementaryDataStore(), ) form.validate() diff --git a/tests/app/helpers/test_schema_helper.py b/tests/app/helpers/test_schema_helper.py index d4200ad8b5..d7fb2a7234 100644 --- a/tests/app/helpers/test_schema_helper.py +++ b/tests/app/helpers/test_schema_helper.py @@ -11,7 +11,7 @@ def test_questionnaire_schema_passed_into_function( ): mocker.patch( "app.helpers.schema_helpers.get_metadata", - return_value=fake_questionnaire_store.metadata, + return_value=fake_questionnaire_store.data_stores.metadata, ) mocker.patch( diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index bb050ac1f7..8bdea2989b 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -4,6 +4,7 @@ from app.data_models import CompletionStatus, QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress import ProgressDict @@ -58,15 +59,10 @@ def response_metadata(): def parser(answer_store, location, mock_schema, mock_renderer): return PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, metadata=get_metadata()), schema=mock_schema, location=location, renderer=mock_renderer, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) @@ -1011,14 +1007,12 @@ def placeholder_renderer(option_label_from_value_schema): ) renderer = PlaceholderRenderer( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata({"trad_as": "ESSENTIAL SERVICES LTD"}), - response_metadata={}, + data_stores=DataStores( + answer_store=answer_store, + metadata=get_metadata({"trad_as": "ESSENTIAL SERVICES LTD"}), + ), schema=option_label_from_value_schema, - progress_store=ProgressStore(), location=Location(section_id="checkbox-section"), - supplementary_data_store=SupplementaryDataStore(), ) return renderer @@ -1027,13 +1021,8 @@ def placeholder_renderer(option_label_from_value_schema): def mock_renderer(mock_schema): return PlaceholderRenderer( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(), schema=mock_schema, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) @@ -1197,10 +1186,12 @@ def mock_questionnaire_store( return mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=mock_empty_progress_store, - supplementary_data_store=mock_empty_supplementary_data_store, + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, + ), ) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index e52157d628..8ca282a57d 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import Mapping, Optional, Union +from typing import Optional, Union import pytest from freezegun import freeze_time @@ -12,7 +12,7 @@ SupplementaryDataStore, ) from app.data_models.answer import Answer -from app.data_models.metadata_proxy import MetadataProxy +from app.data_models.data_stores import DataStores from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.relationship_location import RelationshipLocation @@ -46,16 +46,11 @@ def get_rule_evaluator( *, language="en", schema: QuestionnaireSchema = None, - answer_store: AnswerStore = AnswerStore(), - list_store: ListStore = ListStore(), - metadata: Optional[MetadataProxy] = None, - response_metadata: Mapping = None, + data_stores: DataStores = None, location: Union[Location, RelationshipLocation] = Location( section_id="test-section", block_id="test-block" ), routing_path_block_ids: Optional[list] = None, - progress: ProgressStore = ProgressStore(), - supplementary_data_store: SupplementaryDataStore = SupplementaryDataStore(), ): if not schema: schema = get_mock_schema() @@ -67,14 +62,9 @@ def get_rule_evaluator( return RuleEvaluator( language=language, schema=schema, - metadata=metadata or {}, - response_metadata=response_metadata, - answer_store=answer_store, - list_store=list_store, + data_stores=data_stores, location=location, routing_path_block_ids=routing_path_block_ids, - progress_store=progress, - supplementary_data_store=supplementary_data_store, ) @@ -120,7 +110,11 @@ def test_boolean_operators_as_rule(rule, expected_result): ) def test_answer_source(answer_value, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore([{"answer_id": "some-answer", "value": answer_value}]), + data_stores=DataStores( + answer_store=AnswerStore( + [{"answer_id": "some-answer", "value": answer_value}] + ) + ), ) assert ( @@ -139,14 +133,16 @@ def test_answer_source(answer_value, expected_result): ) def test_answer_source_with_list_item_selector_location(answer_value, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "list_item_id": "item-1", - "value": answer_value, - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "list_item_id": "item-1", + "value": answer_value, + } + ] + ) ), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" @@ -181,16 +177,18 @@ def test_answer_source_with_list_item_selector_list_first_item( answer_value, expected_result ): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "list_item_id": "item-1", - "value": answer_value, - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "list_item_id": "item-1", + "value": answer_value, + } + ] + ), + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), ), - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), ) assert ( @@ -220,14 +218,16 @@ def test_answer_source_with_list_item_selector_list_first_item( ) def test_answer_source_with_dict_answer_selector(answer_value, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": {"years": answer_value, "months": 10}, - } - ] - ), + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": {"years": answer_value, "months": 10}, + } + ] + ), + ) ) assert ( @@ -253,7 +253,7 @@ def test_answer_source_with_dict_answer_selector(answer_value, expected_result): ) def test_metadata_source(metadata_value, expected_result): rule_evaluator = get_rule_evaluator( - metadata=get_metadata({"some_key": metadata_value}) + data_stores=DataStores(metadata=get_metadata({"some_key": metadata_value})) ) assert ( @@ -287,7 +287,7 @@ def test_progress_source(identifier, selector, expected_result): rule_evaluator = get_rule_evaluator( schema=schema, location=Location(section_id="section-1", block_id="s1-b3", list_item_id=None), - progress=ProgressStore(in_progress_sections), + data_stores=DataStores(progress_store=ProgressStore(in_progress_sections)), routing_path_block_ids=["s1-b1", "s1-b2", "s1-b3"], ) @@ -317,7 +317,9 @@ def test_progress_source(identifier, selector, expected_result): ) def test_response_metadata_source(response_metadata_value, expected_result): rule_evaluator = get_rule_evaluator( - response_metadata={"started_at": response_metadata_value}, + data_stores=DataStores( + response_metadata={"started_at": response_metadata_value}, + ) ) assert ( @@ -339,9 +341,11 @@ def test_response_metadata_source(response_metadata_value, expected_result): ) def test_list_source(list_count, expected_result): rule_evaluator = get_rule_evaluator( - list_store=ListStore( - [{"name": "some-list", "items": get_list_items(list_count)}] - ), + data_stores=DataStores( + list_store=ListStore( + [{"name": "some-list", "items": get_list_items(list_count)}] + ), + ) ) assert ( @@ -363,7 +367,9 @@ def test_list_source(list_count, expected_result): ) def test_list_source_with_id_selector_first(list_item_id, expected_result): rule_evaluator = get_rule_evaluator( - list_store=ListStore([{"name": "some-list", "items": get_list_items(1)}]), + data_stores=DataStores( + list_store=ListStore([{"name": "some-list", "items": get_list_items(1)}]), + ) ) assert ( @@ -389,15 +395,17 @@ def test_list_source_with_id_selector_first(list_item_id, expected_result): ) def test_list_source_with_id_selector_same_name_items(list_item_id, expected_result): rule_evaluator = get_rule_evaluator( - list_store=ListStore( - [ - { - "name": "some-list", - "items": get_list_items(5), - "same_name_items": get_list_items(3), - } - ] - ), + data_stores=DataStores( + list_store=ListStore( + [ + { + "name": "some-list", + "items": get_list_items(5), + "same_name_items": get_list_items(3), + } + ] + ), + ) ) assert ( @@ -433,14 +441,16 @@ def test_list_source_id_selector_primary_person( ) rule_evaluator = get_rule_evaluator( - list_store=ListStore( - [ - { - "name": "some-list", - "primary_person": primary_person_list_item_id, - "items": get_list_items(3), - } - ] + data_stores=DataStores( + list_store=ListStore( + [ + { + "name": "some-list", + "primary_person": primary_person_list_item_id, + "items": get_list_items(3), + } + ] + ) ), location=location, ) @@ -471,6 +481,7 @@ def test_current_location_source(list_item_id, expected_result): location=Location( section_id="some-section", block_id="some-block", list_item_id=list_item_id ), + data_stores=DataStores(), ) assert ( @@ -559,29 +570,31 @@ def test_current_location_source(list_item_id, expected_result): ) def test_nested_rules(operator, operands, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "answer-1", - "list_item_id": "item-1", - "value": "Yes, I do", - }, - { - "answer_id": "answer-2", - "list_item_id": "item-1", - "value": 10, - }, - ] - ), - metadata=get_metadata({"region_code": "GB-NIR", "language_code": "en"}), - list_store=ListStore( - [ - { - "name": "some-list", - "items": get_list_items(5), - "same_name_items": get_list_items(3), - } - ], + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "answer-1", + "list_item_id": "item-1", + "value": "Yes, I do", + }, + { + "answer_id": "answer-2", + "list_item_id": "item-1", + "value": 10, + }, + ] + ), + metadata=get_metadata({"region_code": "GB-NIR", "language_code": "en"}), + list_store=ListStore( + [ + { + "name": "some-list", + "items": get_list_items(5), + "same_name_items": get_list_items(3), + } + ], + ), ), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" @@ -612,7 +625,7 @@ def test_nested_rules(operator, operands, expected_result): ], ) def test_comparison_operator_rule_with_nonetype_operands(operator_name, operands): - rule_evaluator = get_rule_evaluator(metadata=get_metadata()) + rule_evaluator = get_rule_evaluator(data_stores=DataStores(metadata=get_metadata())) assert rule_evaluator.evaluate(rule={operator_name: operands}) is False @@ -632,7 +645,7 @@ def test_comparison_operator_rule_with_nonetype_operands(operator_name, operands ) def test_array_operator_rule_with_nonetype_operands(operator_name, operands): rule_evaluator = get_rule_evaluator( - metadata=get_metadata(), + data_stores=DataStores(metadata=get_metadata()), ) assert ( rule_evaluator.evaluate( @@ -756,15 +769,17 @@ def test_array_operator_rule_with_nonetype_operands(operator_name, operands): ) def test_date_value(rule, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": current_date_as_yyyy_mm_dd, - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": current_date_as_yyyy_mm_dd, + } + ] + ), + metadata=get_metadata({"some-metadata": current_date_as_yyyy_mm_dd}), ), - metadata=get_metadata({"some-metadata": current_date_as_yyyy_mm_dd}), ) assert ( @@ -783,7 +798,7 @@ def test_answer_source_outside_of_repeating_section(): rule_evaluator = get_rule_evaluator( schema=schema, - answer_store=answer_store, + data_stores=DataStores(answer_store=answer_store), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" ), @@ -821,7 +836,7 @@ def test_answer_source_not_on_path_non_repeating_section(is_answer_on_path): rule_evaluator = get_rule_evaluator( schema=schema, - answer_store=AnswerStore([answer.to_dict()]), + data_stores=DataStores(answer_store=AnswerStore([answer.to_dict()])), location=location, routing_path_block_ids=["block-on-path"], ) @@ -860,7 +875,7 @@ def test_answer_source_not_on_path_repeating_section(is_answer_on_path): rule_evaluator = get_rule_evaluator( schema=schema, - answer_store=AnswerStore([answer.to_dict()]), + data_stores=DataStores(answer_store=AnswerStore([answer.to_dict()])), location=location, routing_path_block_ids=["block-on-path"], ) @@ -889,7 +904,9 @@ def test_answer_source_default_answer_used_when_no_answer( rule_evaluator = get_rule_evaluator( schema=schema, - answer_store=AnswerStore([{"answer_id": "some-answer", "value": "No"}]), + data_stores=DataStores( + answer_store=AnswerStore([{"answer_id": "some-answer", "value": "No"}]) + ), ) assert ( @@ -907,7 +924,7 @@ def test_answer_source_default_answer_used_when_no_answer( def test_raises_exception_when_bad_operand_type(): with pytest.raises(TypeError): - rule_evaluator = get_rule_evaluator() + rule_evaluator = get_rule_evaluator(data_stores=DataStores()) rule_evaluator.evaluate(rule={Operator.EQUAL: {1, 2}}) @@ -931,14 +948,16 @@ def test_raises_exception_when_bad_operand_type(): ) def test_answer_source_count(rule, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": ["array element 1", "array element 2"], - } - ] - ), + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": ["array element 1", "array element 2"], + } + ] + ), + ) ) assert rule_evaluator.evaluate(rule=rule) is expected_result @@ -976,14 +995,16 @@ def test_answer_source_count(rule, expected_result): ) def test_format_date(rule, expected_result): rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": "2021-01-01", - } - ] - ), + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": "2021-01-01", + } + ] + ), + ) ) assert rule_evaluator.evaluate(rule=rule) == expected_result @@ -1010,8 +1031,10 @@ def test_map_without_nested_date_operator(source): date_map = {"started_at": datetime.now(timezone.utc).isoformat()} rule_evaluator = get_rule_evaluator( - response_metadata=date_map, - supplementary_data_store=SupplementaryDataStore(date_map), + data_stores=DataStores( + response_metadata=date_map, + supplementary_data_store=SupplementaryDataStore(date_map), + ) ) assert rule_evaluator.evaluate(rule=rule) == [ @@ -1048,13 +1071,15 @@ def test_map_with_nested_date_operator(offset, expected_result): } rule_evaluator = get_rule_evaluator( - answer_store=AnswerStore( - [ - { - "answer_id": "checkbox-answer", - "value": ["2021-01-01", "2021-01-02", "2021-01-03"], - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "checkbox-answer", + "value": ["2021-01-01", "2021-01-02", "2021-01-03"], + } + ] + ) ) ) @@ -1078,8 +1103,10 @@ def test_supplementary_data_source( rule_evaluator = get_rule_evaluator( schema=schema, - answer_store=answer_store, - supplementary_data_store=supplementary_data_store_with_data, + data_stores=DataStores( + answer_store=answer_store, + supplementary_data_store=supplementary_data_store_with_data, + ), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" ), diff --git a/tests/app/questionnaire/test_dynamic_answer_options.py b/tests/app/questionnaire/test_dynamic_answer_options.py index 90ba59898a..5a3db0196b 100644 --- a/tests/app/questionnaire/test_dynamic_answer_options.py +++ b/tests/app/questionnaire/test_dynamic_answer_options.py @@ -1,13 +1,8 @@ # pylint: disable=redefined-outer-name import pytest -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) from app.data_models.answer_store import Answer +from app.data_models.data_stores import DataStores from app.questionnaire.dynamic_answer_options import DynamicAnswerOptions from app.questionnaire.rules.rule_evaluator import RuleEvaluator from app.questionnaire.value_source_resolver import ValueSourceResolver @@ -16,14 +11,9 @@ @pytest.fixture def rule_evaluator(mock_schema, response_metadata): evaluator = RuleEvaluator( - answer_store=AnswerStore(), - list_store=ListStore(), - metadata={}, - response_metadata=response_metadata, + data_stores=DataStores(response_metadata=response_metadata), schema=mock_schema, location=None, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) return evaluator @@ -32,17 +22,12 @@ def rule_evaluator(mock_schema, response_metadata): @pytest.fixture def value_source_resolver(mock_schema, response_metadata): resolver = ValueSourceResolver( - answer_store=AnswerStore(), - list_store=ListStore(), - metadata={}, - response_metadata=response_metadata, + data_stores=DataStores(response_metadata=response_metadata), schema=mock_schema, location=None, list_item_id=None, routing_path_block_ids=None, use_default_answer=True, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ) return resolver @@ -130,7 +115,7 @@ def test_dynamic_answer_options_answer_source( ) if checkbox_answer: - value_source_resolver.answer_store.add_or_update( + value_source_resolver.data_stores.answer_store.add_or_update( Answer(answer_id="injury-sustained-answer", value=checkbox_answer) ) diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index a7105ee659..1a84a43d16 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -2,6 +2,7 @@ from app.data_models import CompletionStatus, ListStore from app.data_models.answer_store import Answer, AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.progress import ProgressDict from app.data_models.progress_store import ProgressStore from app.questionnaire.path_finder import PathFinder @@ -11,7 +12,7 @@ from tests.app.questionnaire.conftest import get_metadata -def test_simple_path(answer_store, list_store, supplementary_data_store): +def test_simple_path(): schema = load_schema_from_name("test_textfield") progress_store = ProgressStore( [ @@ -24,13 +25,7 @@ def test_simple_path(answer_store, list_store, supplementary_data_store): ] ) path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + schema, data_stores=DataStores(progress_store=progress_store) ) section_id = schema.get_section_id_for_block_id("name-block") @@ -43,46 +38,20 @@ def test_simple_path(answer_store, list_store, supplementary_data_store): assert routing_path == assumed_routing_path -def test_introduction_in_path_when_in_schema( - answer_store, - list_store, - progress_store, - supplementary_data_store, -): +def test_introduction_in_path_when_in_schema(data_stores): schema = load_schema_from_name("test_introduction") current_section = schema.get_section("introduction-section") - path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, - ) + path_finder = PathFinder(schema, data_stores=data_stores) routing_path = path_finder.routing_path(SectionKey(current_section["id"])) assert "introduction" in routing_path -def test_introduction_not_in_path_when_not_in_schema( - answer_store, - list_store, - progress_store, - supplementary_data_store, -): +def test_introduction_not_in_path_when_not_in_schema(data_stores): schema = load_schema_from_name("test_checkbox") current_section = schema.get_section("default-section") - path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, - ) + path_finder = PathFinder(schema, data_stores=data_stores) routing_path = path_finder.routing_path(SectionKey(current_section["id"])) @@ -91,9 +60,6 @@ def test_introduction_not_in_path_when_not_in_schema( def test_routing_basic_and_conditional_path( answer_store, - list_store, - progress_store, - supplementary_data_store, ): # Given schema = load_schema_from_name("test_routing_number_equals") @@ -108,24 +74,14 @@ def test_routing_basic_and_conditional_path( answer_store.add_or_update(answer_1) # When - path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, - ) + path_finder = PathFinder(schema, data_stores=DataStores(answer_store=answer_store)) routing_path = path_finder.routing_path(SectionKey(section_id)) # Then assert routing_path == expected_path -def test_routing_path_with_complete_introduction( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_with_complete_introduction(): schema = load_schema_from_name("test_introduction") section_id = schema.get_section_id_for_block_id("introduction") progress_store = ProgressStore( @@ -154,20 +110,14 @@ def test_routing_path_with_complete_introduction( ) path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + schema, data_stores=DataStores(progress_store=progress_store) ) routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_routing_path -def test_routing_path(answer_store, list_store, supplementary_data_store): +def test_routing_path(): schema = load_schema_from_name("test_submit_with_summary") section_id = schema.get_section_id_for_block_id("dessert") expected_path = RoutingPath( @@ -191,22 +141,14 @@ def test_routing_path(answer_store, list_store, supplementary_data_store): ] ) path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + schema, data_stores=DataStores(progress_store=progress_store) ) routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_path -def test_routing_path_with_repeating_sections( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_with_repeating_sections(): schema = load_schema_from_name("test_repeating_sections_with_hub_and_spoke") progress_store = ProgressStore( @@ -224,13 +166,7 @@ def test_routing_path_with_repeating_sections( ] ) path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + schema, data_stores=DataStores(progress_store=progress_store) ) repeating_section_id = "personal-details-section" @@ -246,9 +182,7 @@ def test_routing_path_with_repeating_sections( assert routing_path == expected_path -def test_routing_path_empty_routing_rules( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_empty_routing_rules(answer_store): schema = load_schema_from_name("test_checkbox") section_id = schema.get_section_id_for_block_id("mandatory-checkbox") expected_path = RoutingPath( @@ -277,21 +211,16 @@ def test_routing_path_empty_routing_rules( path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) assert routing_path == expected_path -def test_routing_path_with_conditional_value_not_in_metadata( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_with_conditional_value_not_in_metadata(answer_store): schema = load_schema_from_name("test_metadata_routing") section_id = schema.get_section_id_for_block_id("block1") expected_path = RoutingPath( @@ -311,12 +240,11 @@ def test_routing_path_with_conditional_value_not_in_metadata( path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - get_metadata(), - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, + answer_store=answer_store, + metadata=get_metadata(), + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -324,9 +252,7 @@ def test_routing_path_with_conditional_value_not_in_metadata( assert routing_path == expected_path -def test_routing_path_should_skip_block( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_should_skip_block(answer_store): # Given schema = load_schema_from_name("test_skip_condition_block") section_id = schema.get_section_id_for_block_id("should-skip") @@ -348,12 +274,9 @@ def test_routing_path_should_skip_block( # When path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -367,9 +290,7 @@ def test_routing_path_should_skip_block( assert routing_path == expected_routing_path -def test_routing_path_should_skip_group( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_should_skip_group(answer_store): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -391,12 +312,9 @@ def test_routing_path_should_skip_group( # When path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -409,9 +327,7 @@ def test_routing_path_should_skip_group( assert routing_path == expected_routing_path -def test_routing_path_should_not_skip_group( - answer_store, list_store, supplementary_data_store -): +def test_routing_path_should_not_skip_group(answer_store): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -433,12 +349,9 @@ def test_routing_path_should_not_skip_group( # When path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -453,9 +366,6 @@ def test_routing_path_should_not_skip_group( def test_get_routing_path_when_first_block_in_group_skipped( answer_store, - list_store, - progress_store, - supplementary_data_store, ): # Given schema = load_schema_from_name("test_skip_condition_group") @@ -464,15 +374,7 @@ def test_get_routing_path_when_first_block_in_group_skipped( ) # When - path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - None, - {}, - supplementary_data_store=supplementary_data_store, - ) + path_finder = PathFinder(schema, data_stores=DataStores(answer_store=answer_store)) # Then expected_route = RoutingPath( @@ -485,9 +387,6 @@ def test_get_routing_path_when_first_block_in_group_skipped( def test_build_path_with_group_routing( answer_store, - list_store, - progress_store, - supplementary_data_store, ): # Given i have answered the routing question schema = load_schema_from_name("test_routing_group") @@ -496,15 +395,7 @@ def test_build_path_with_group_routing( answer_store.add_or_update(Answer(answer_id="which-group-answer", value="group2")) # When i build the path - path_finder = PathFinder( - schema, - answer_store, - list_store, - progress_store, - {}, - {}, - supplementary_data_store=supplementary_data_store, - ) + path_finder = PathFinder(schema, data_stores=DataStores(answer_store=answer_store)) path = path_finder.routing_path(SectionKey(section_id)) # Then it should route me straight to Group2 and not Group1 @@ -512,9 +403,7 @@ def test_build_path_with_group_routing( assert "group2-block" in path -def test_remove_answer_and_block_if_routing_backwards( - list_store, supplementary_data_store -): +def test_remove_answer_and_block_if_routing_backwards(): schema = load_schema_from_name("test_confirmation_question_backwards_routing") section_id = schema.get_section_id_for_block_id("confirm-zero-employees-block") @@ -546,23 +435,20 @@ def test_remove_answer_and_block_if_routing_backwards( path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - {}, - {}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) assert ( len( - path_finder.progress_store.get_completed_block_ids( + path_finder.data_stores.progress_store.get_completed_block_ids( SectionKey("default-section") ) ) == 3 ) - assert len(path_finder.answer_store) == 3 + assert len(path_finder.data_stores.answer_store) == 3 routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -576,14 +462,18 @@ def test_remove_answer_and_block_if_routing_backwards( section_id="default-section", ) assert routing_path == expected_path - assert path_finder.progress_store.get_completed_block_ids( + assert path_finder.data_stores.progress_store.get_completed_block_ids( SectionKey("default-section") ) == progress_store.get_completed_block_ids(SectionKey("default-section")) - assert len(path_finder.answer_store) == 2 - assert not path_finder.answer_store.get_answer("confirm-zero-employees-answer") + assert len(path_finder.data_stores.answer_store) == 2 + assert not path_finder.data_stores.answer_store.get_answer( + "confirm-zero-employees-answer" + ) assert ( - path_finder.progress_store.get_section_status(SectionKey("default-section")) + path_finder.data_stores.progress_store.get_section_status( + SectionKey("default-section") + ) == CompletionStatus.IN_PROGRESS ) @@ -650,13 +540,11 @@ def test_remove_answer_and_block_if_routing_backwards( ), ) def test_routing_path_block_ids_dependent_on_other_sections_when_rules( - list_store, skip_age_answer, skip_confirmation_answer, section_id, expected_route, answer_store, - supplementary_data_store, ): # Given a schema which has when rules in a section which has dependencies on other sections answers schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") @@ -693,12 +581,9 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( # When I build the path path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - metadata=None, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, answer_store=answer_store + ), ) routing_path = path_finder.routing_path(SectionKey(section_id)) @@ -733,7 +618,7 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules( ), ) def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating( - skip_age_answer, expected_route, answer_store, supplementary_data_store + skip_age_answer, expected_route, answer_store ): # Given a schema with repeating sections which has when rules dependent on another section schema = load_schema_from_name("test_routing_and_skipping_section_dependencies") @@ -771,12 +656,11 @@ def test_routing_path_block_ids_dependent_on_other_sections_when_rules_repeating # When I build the path path_finder = PathFinder( schema, - answer_store, - list_store, - progress_store, - metadata=None, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + progress_store=progress_store, + answer_store=answer_store, + list_store=list_store, + ), ) routing_path = path_finder.routing_path( SectionKey( diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index 07760e4ae7..aa14516725 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -3,8 +3,9 @@ import pytest from mock import Mock -from app.data_models import ProgressStore, SupplementaryDataStore +from app.data_models import ProgressStore from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire import Location @@ -39,15 +40,10 @@ def test_metadata_placeholder(mock_renderer, mock_schema, mock_location): metadata = get_metadata({"period_str": period_str}) parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -82,15 +78,10 @@ def test_previous_answer_transform_placeholder(mock_renderer, mock_location): parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -120,15 +111,10 @@ def test_metadata_transform_placeholder(mock_renderer, mock_schema, mock_locatio parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -162,15 +148,10 @@ def test_response_metadata_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata=response_metadata, + data_stores=DataStores(metadata=metadata, response_metadata=response_metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -207,15 +188,10 @@ def test_multiple_answer_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -247,15 +223,10 @@ def test_first_non_empty_item_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -289,15 +260,10 @@ def test_format_list_answer_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -334,15 +300,10 @@ def test_placeholder_parser_escapes_answers(mock_renderer, mock_schema, mock_loc parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -385,15 +346,10 @@ def test_multiple_metadata_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -428,15 +384,10 @@ def test_multiple_metadata_list_transform_placeholder( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -469,15 +420,10 @@ def test_checkbox_transform_placeholder(mock_renderer, mock_schema, mock_locatio parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -514,15 +460,10 @@ def test_mixed_transform_placeholder(mock_renderer, mock_schema, mock_location): parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -554,15 +495,10 @@ def test_mixed_transform_placeholder_value(mock_renderer, mock_schema, mock_loca parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -583,15 +519,10 @@ def test_list_source_count(mock_renderer, mock_schema, mock_location): parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -623,15 +554,10 @@ def test_list_source_count_in_transform(mock_renderer, mock_schema, mock_locatio parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -670,15 +596,10 @@ def test_chain_transform_placeholder(mock_renderer, mock_schema, mock_location): parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -717,15 +638,10 @@ def test_placeholder_resolves_answer_value_based_on_first_item_in_list( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store, answer_store=answer_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -750,15 +666,10 @@ def test_placeholder_resolves_list_item_value_based_on_first_item_in_list( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -791,16 +702,11 @@ def test_placeholder_resolves_same_name_items( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store), schema=mock_schema, renderer=mock_renderer, list_item_id="abc123", - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -898,16 +804,11 @@ def test_placeholder_resolves_name_is_duplicate_chain( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store, answer_store=answer_store), schema=mock_schema, list_item_id="abc123", renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -916,16 +817,11 @@ def test_placeholder_resolves_name_is_duplicate_chain( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(list_store=list_store, answer_store=answer_store), schema=mock_schema, list_item_id="cde456", renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1019,16 +915,11 @@ def test_placeholder_resolves_list_has_items_chain( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, list_store=list_store), schema=mock_schema, list_item_id="abc123", renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1037,16 +928,11 @@ def test_placeholder_resolves_list_has_items_chain( parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=list_store, - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(answer_store=answer_store, list_store=list_store), schema=mock_schema, list_item_id="cde456", renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_transforms) @@ -1073,15 +959,10 @@ def test_placeholder_default_value(default_placeholder_value_schema, mock_render parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores(), schema=default_placeholder_value_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) @@ -1157,15 +1038,12 @@ def test_placeholder_parser_calculated_summary_dependencies_cache( placeholder_parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores( + answer_store=answer_store, progress_store=progress_store + ), schema=schema, renderer=mock_renderer, - progress_store=progress_store, location=location, - supplementary_data_store=SupplementaryDataStore(), ) placeholder_1 = placeholder_parser(placeholder_list=placeholder_list_1) @@ -1265,15 +1143,14 @@ def test_placeholder_dependencies_cache(mocker, mock_renderer): ) placeholder_parser = PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=get_metadata(), - response_metadata={}, + data_stores=DataStores( + answer_store=answer_store, + progress_store=progress_store, + metadata=get_metadata(), + ), schema=schema, renderer=mock_renderer, - progress_store=progress_store, location=location, - supplementary_data_store=SupplementaryDataStore(), ) placeholder_1 = placeholder_parser(placeholder_list=placeholder_list_1) @@ -1324,15 +1201,10 @@ def test_format_currency_placeholder_total_with_previous_transform( parser = PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=metadata, - response_metadata={}, + data_stores=DataStores(metadata=metadata), schema=mock_schema, renderer=mock_renderer, - progress_store=ProgressStore(), location=mock_location, - supplementary_data_store=SupplementaryDataStore(), ) placeholders = parser(placeholder_list) diff --git a/tests/app/questionnaire/test_placeholder_renderer.py b/tests/app/questionnaire/test_placeholder_renderer.py index 7c4c0ee42f..6786e41212 100644 --- a/tests/app/questionnaire/test_placeholder_renderer.py +++ b/tests/app/questionnaire/test_placeholder_renderer.py @@ -1,8 +1,8 @@ import pytest from mock import MagicMock -from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.questionnaire import Location from app.questionnaire.placeholder_parser import PlaceholderParser @@ -41,22 +41,17 @@ def test_renders_pointer( ] ) - renderer = get_placeholder_render(answer_store=answer_store) + renderer = get_placeholder_render(data_stores=DataStores(answer_store=answer_store)) rendered = renderer.render_pointer( dict_to_render=placholder_transform_question_json, pointer_to_render="/answers/0/options/0/label", list_item_id=None, placeholder_parser=PlaceholderParser( language="en", - answer_store=answer_store, - list_store=ListStore(), - metadata=None, - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, location=mock_location, renderer=mock_renderer, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ), ) @@ -87,7 +82,7 @@ def test_renders_json(placholder_transform_question_json): ] ) - renderer = get_placeholder_render(answer_store=answer_store) + renderer = get_placeholder_render(data_stores=DataStores(answer_store=answer_store)) rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label = rendered_schema["answers"][0]["options"][0]["label"] @@ -118,7 +113,9 @@ def test_renders_json_uses_language(placholder_transform_question_json): ] ) - renderer = get_placeholder_render(language="cy", answer_store=answer_store) + renderer = get_placeholder_render( + language="cy", data_stores=DataStores(answer_store=answer_store) + ) rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label = rendered_schema["answers"][0]["options"][0]["label"] @@ -135,15 +132,10 @@ def test_errors_on_invalid_pointer(placholder_transform_question_json, mock_sche list_item_id=None, placeholder_parser=PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=None, - response_metadata={}, + data_stores=DataStores(), schema=mock_schema, location=None, renderer=renderer, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ), ) @@ -158,15 +150,10 @@ def test_errors_on_invalid_json(mock_schema): list_item_id=None, placeholder_parser=PlaceholderParser( language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=None, - response_metadata={}, + data_stores=DataStores(), schema=mock_schema, location=None, renderer=renderer, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), ), ) @@ -174,7 +161,7 @@ def test_errors_on_invalid_json(mock_schema): def test_renders_text_plural_from_answers(): answer_store = AnswerStore([{"answer_id": "number-of-people", "value": 1}]) - renderer = get_placeholder_render(answer_store=answer_store) + renderer = get_placeholder_render(data_stores=DataStores(answer_store=answer_store)) rendered_text = renderer.render_placeholder( { "text_plural": { @@ -228,7 +215,7 @@ def test_renders_text_plural_from_list(): def test_renders_text_plural_from_metadata(): metadata = {"some_value": 100} - renderer = get_placeholder_render(metadata=metadata) + renderer = get_placeholder_render(data_stores=DataStores(metadata=metadata)) rendered_text = renderer.render_placeholder( { @@ -277,7 +264,7 @@ def test_renders_json_dynamic_answers( list_store = ListStore([{"items": ["yWKpNY", "vtbSnC"], "name": "supermarkets"}]) renderer = get_placeholder_render_dynamic_answers( - answer_store=answer_store, list_store=list_store + data_stores=DataStores(answer_store=answer_store, list_store=list_store) ) rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label_first = rendered_schema["answers"][0]["label"] @@ -316,7 +303,7 @@ def test_renders_json_dynamic_answers_pointer( list_store = ListStore([{"items": ["yWKpNY", "vtbSnC"], "name": "supermarkets"}]) renderer = get_placeholder_render_dynamic_answers( - answer_store=answer_store, list_store=list_store + data_stores=DataStores(answer_store=answer_store, list_store=list_store) ) rendered_schema = renderer.render(data_to_render=json_to_render, list_item_id=None) rendered_label_first = rendered_schema["question"]["answers"][0]["label"] @@ -330,49 +317,25 @@ def test_renders_json_dynamic_answers_pointer( assert rendered_id_second == "percentage-of-shopping-vtbSnC" -def get_placeholder_render_dynamic_answers( - *, - language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), - metadata=None, - response_metadata=None, -): +def get_placeholder_render_dynamic_answers(*, language="en", data_stores=None): + data_stores = data_stores or DataStores() schema = load_schema_from_name("test_dynamic_answers_list_source") return PlaceholderRenderer( language=language, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - metadata=metadata or {}, - response_metadata=response_metadata or {}, + data_stores=data_stores, schema=schema, - supplementary_data_store=supplementary_data_store, ) -def get_placeholder_render( - *, - language="en", - answer_store=AnswerStore(), - list_store=ListStore(), - metadata=None, - response_metadata=None, -): +def get_placeholder_render(*, language="en", data_stores=None): + data_stores = data_stores or DataStores() schema = MagicMock() schema.is_answer_dynamic = MagicMock(return_value=False) schema.is_answer_in_list_collector_repeating_block = MagicMock(return_value=False) renderer = PlaceholderRenderer( language=language, - answer_store=answer_store, - list_store=list_store, - metadata=metadata or {}, - response_metadata=response_metadata or {}, + data_stores=data_stores, schema=schema, - progress_store=ProgressStore(), location=Location(section_id="default-section"), - supplementary_data_store=SupplementaryDataStore(), ) return renderer diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index fa1808cf45..43155e5993 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -8,6 +8,7 @@ from app.data_models import CompletionStatus, QuestionnaireStore, SupplementaryDataStore from app.data_models.answer_store import AnswerDict, AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.progress import ProgressDict from app.data_models.progress_store import ProgressStore @@ -78,7 +79,7 @@ def test_update_dynamic_answers( "percentage-of-shopping": {} } - mock_questionnaire_store.answer_store = AnswerStore( + mock_questionnaire_store.data_stores.answer_store = AnswerStore( [ {"answer_id": "any-supermarket-answer", "value": "Yes"}, { @@ -116,7 +117,7 @@ def test_update_dynamic_answers( ) questionnaire_store_updater.update_answers(form_data, list_item_id="vhECeh") - assert mock_questionnaire_store.answer_store == AnswerStore( + assert mock_questionnaire_store.data_stores.answer_store == AnswerStore( [ {"answer_id": "any-supermarket-answer", "value": "Yes"}, { @@ -290,10 +291,12 @@ def test_remove_all_answers_with_list_item_id( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=mocker.MagicMock(spec=ListStore), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=mocker.MagicMock(spec=ListStore), + progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -323,10 +326,12 @@ def test_remove_primary_person( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -347,10 +352,12 @@ def test_add_primary_person( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -376,10 +383,12 @@ def test_remove_completed_relationship_locations_for_list_name( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=mock_empty_progress_store, - supplementary_data_store=mock_empty_supplementary_data_store, + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -417,10 +426,12 @@ def test_remove_completed_relationship_locations_for_list_name_no_locations( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=mock_empty_progress_store, - supplementary_data_store=mock_empty_supplementary_data_store, + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -449,10 +460,12 @@ def test_update_relationship_question_completeness_no_relationship_collectors( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=mock_empty_progress_store, - supplementary_data_store=mock_empty_supplementary_data_store, + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=mock_empty_progress_store, + supplementary_data_store=mock_empty_supplementary_data_store, + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( mock_location, mock_empty_schema, mock_questionnaire_store, mock_router, None @@ -501,10 +514,12 @@ def test_update_same_name_items( mock_questionnaire_store = mocker.MagicMock( spec=QuestionnaireStore, completed_blocks=[], - answer_store=mock_empty_answer_store, - list_store=populated_list_store, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=mock_empty_answer_store, + list_store=populated_list_store, + progress_store=ProgressStore(), + supplementary_data_store=SupplementaryDataStore(), + ), ) questionnaire_store_updater = QuestionnaireStoreUpdater( @@ -1090,10 +1105,12 @@ def get_questionnaire_store_updater( mock_questionnaire_store = MagicMock( spec=QuestionnaireStore, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + answer_store=answer_store, + list_store=list_store, + progress_store=progress_store, + supplementary_data_store=supplementary_data_store, + ), ) current_question = current_question or {} diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 620aeec302..d05d0b9302 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -1,4 +1,5 @@ # pylint: disable=too-many-lines + from functools import cached_property import pytest @@ -7,44 +8,34 @@ from app.data_models import CompletionStatus from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore from app.data_models.progress import ProgressDict from app.data_models.progress_store import ProgressStore -from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location, SectionKey from app.questionnaire.return_location import ReturnLocation from app.questionnaire.router import Router from app.questionnaire.routing_path import RoutingPath from app.utilities.schema import load_schema_from_name -from tests.app.questionnaire.conftest import get_metadata class RouterTestCase: schema = None - answer_store = AnswerStore() - list_store = ListStore() - progress_store = ProgressStore() - metadata = get_metadata() - supplementary_data_store = SupplementaryDataStore() - response_metadata = {} + data_stores: DataStores + + @pytest.fixture(autouse=True) + def setup(self): + self.data_stores = DataStores() @cached_property def router(self): - return Router( - self.schema, - self.answer_store, - self.list_store, - self.progress_store, - self.metadata, - self.response_metadata, - self.supplementary_data_store, - ) + return Router(self.schema, self.data_stores) class TestRouter(RouterTestCase): def test_enabled_section_ids(self): self.schema = load_schema_from_name("test_section_enabled_checkbox") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -53,7 +44,7 @@ def test_enabled_section_ids(self): ) ] ) - self.answer_store = AnswerStore( + self.data_stores.answer_store = AnswerStore( [{"answer_id": "section-1-answer", "value": ["Section 2"]}] ) @@ -82,7 +73,7 @@ def test_full_routing_path_with_repeating_sections(self): self.schema = load_schema_from_name( "test_repeating_sections_with_hub_and_spoke" ) - self.list_store = ListStore( + self.data_stores.list_store = ListStore( [ { "items": ["abc123", "123abc"], @@ -125,7 +116,7 @@ def test_full_routing_path_with_repeating_sections(self): class TestRouterPathCompletion(RouterTestCase): def test_is_complete(self): self.schema = load_schema_from_name("test_textfield") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -153,7 +144,7 @@ def test_is_not_complete(self): class TestRouterQuestionnaireCompletion(RouterTestCase): def test_is_complete(self): self.schema = load_schema_from_name("test_textfield") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -179,7 +170,7 @@ def test_is_complete_with_repeating_sections(self): self.schema = load_schema_from_name( "test_repeating_sections_with_hub_and_spoke" ) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "section", @@ -199,7 +190,7 @@ def test_is_complete_with_repeating_sections(self): }, ] ) - self.list_store = ListStore( + self.data_stores.list_store = ListStore( [{"items": ["abc123"], "name": "people", "primary_person": "abc123"}] ) @@ -211,7 +202,7 @@ def test_is_not_complete_with_repeating_sections(self): self.schema = load_schema_from_name( "test_repeating_sections_with_hub_and_spoke" ) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "default-section", @@ -220,7 +211,7 @@ def test_is_not_complete_with_repeating_sections(self): } ] ) - self.list_store = ListStore( + self.data_stores.list_store = ListStore( [ { "items": ["abc123", "123abc"], @@ -272,7 +263,7 @@ def test_cant_access(self): "test_repeating_sections_with_hub_and_spoke" ) - self.list_store = ListStore( + self.data_stores.list_store = ListStore( [ { "items": ["abc123", "123abc"], @@ -341,7 +332,7 @@ class TestRouterNextLocation(RouterTestCase): @pytest.mark.usefixtures("app") def test_within_section(self): self.schema = load_schema_from_name("test_checkbox") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -383,7 +374,7 @@ def test_last_block_in_section_but_section_is_not_complete_when_routing_backward ): self.schema = Mock() self.schema.get_block.return_value = {"type": "Question"} - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -411,7 +402,7 @@ def test_last_block_in_section_but_section_is_not_complete_when_routing_backward @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "property-details-section", @@ -444,14 +435,14 @@ def test_return_to_section_summary_section_is_complete(self): @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_section_summary") - self.answer_store = AnswerStore( + self.data_stores.answer_store = AnswerStore( [ {"answer_id": "insurance-type-answer", "value": "Both"}, {"answer_id": "insurance-address-answer", "value": "Address"}, {"answer_id": "listed-answer", "value": "No"}, ] ) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="property-details-section", @@ -487,7 +478,7 @@ def test_return_to_section_summary_section_is_in_progress(self): @pytest.mark.usefixtures("app") def test_section_summary_on_completion_true(self): self.schema = load_schema_from_name("test_show_section_summary_on_completion") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="accommodation-section", @@ -513,7 +504,7 @@ def test_section_summary_on_completion_true(self): @pytest.mark.usefixtures("app") def test_section_summary_on_completion_false(self): self.schema = load_schema_from_name("test_show_section_summary_on_completion") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="employment-section", @@ -552,7 +543,7 @@ def test_return_to_calculated_summary(self, schema): self.schema = load_schema_from_name(schema) # for the purposes of this test, assume the routing path consists only of the first two blocks and the calculated summary # and that those two blocks are complete - this will be a sufficient condition to return to the calculated summary - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -616,7 +607,7 @@ def test_return_to_calculated_summary_not_on_allowable_path(self, schema): then you are instead routed to the first incomplete block of the section """ self.schema = load_schema_from_name(schema) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -686,7 +677,7 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( self, schema, return_to_block_id, expected_url ): self.schema = load_schema_from_name(schema) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -724,7 +715,7 @@ def test_return_to_calculated_summary_invalid_return_to_block_id( ) def test_return_to_calculated_summary_return_to_block_id_not_on_path(self, schema): self.schema = load_schema_from_name(schema) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -767,7 +758,7 @@ def test_return_to_grand_calculated_summary_from_answer( If going from GCS -> CS -> answer -> CS -> GCS this tests going from CS -> GCS having just come from an answer """ self.schema = grand_calculated_summary_schema - self.progress_store = grand_calculated_summary_progress_store + self.data_stores.progress_store = grand_calculated_summary_progress_store current_location = Location( section_id="section-1", block_id="first-number-block" @@ -806,7 +797,7 @@ def test_return_to_grand_calculated_summary_from_calculated_summary( If going from GCS -> CS -> GCS this tests going from CS -> GCS having just come from the grand calculated summary """ self.schema = grand_calculated_summary_schema - self.progress_store = grand_calculated_summary_progress_store + self.data_stores.progress_store = grand_calculated_summary_progress_store current_location = Location( section_id="section-1", block_id="distance-calculated-summary-1" @@ -871,9 +862,11 @@ def test_return_to_repeating_grand_calculated_summary_from_calculated_summary( the continue button for the CS has a next location url of the original repeating GCS. """ self.schema = grand_calculated_summary_in_repeating_section_schema - self.list_store = ListStore([{"items": ["ZIrqqR"], "name": "vehicles"}]) + self.data_stores.list_store = ListStore( + [{"items": ["ZIrqqR"], "name": "vehicles"}] + ) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="base-costs-section", @@ -948,7 +941,7 @@ def test_return_to_grand_calculated_summary_from_incomplete_section( "test_grand_calculated_summary_repeating_answers" ) # calculated summary 3 is not complete yet - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -1010,7 +1003,7 @@ def test_return_to_calculated_summary_from_incomplete_section( """ self.schema = grand_calculated_summary_schema # second-number block not complete yet - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -1147,7 +1140,7 @@ def test_return_to_inaccessible_summary_routes_to_next_incomplete_block( completed_block_ids = [*routing_path_block_ids] completed_block_ids.remove(next_incomplete_block_id) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id=section, @@ -1182,7 +1175,7 @@ def test_redirects_to_submit_page_when_questionnaire_complete( self, ): self.schema = load_schema_from_name("test_textfield") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -1209,7 +1202,7 @@ def test_return_to_final_summary_questionnaire_and_section_is_complete(self): self.schema = load_schema_from_name( "test_routing_to_questionnaire_end_single_section" ) - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="test-section", @@ -1231,7 +1224,7 @@ def test_return_to_final_summary_questionnaire_and_section_is_complete(self): @pytest.mark.usefixtures("app") def test_return_to_final_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_submit_with_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -1260,8 +1253,10 @@ def test_return_to_final_summary_questionnaire_is_not_complete(self): self.schema = load_schema_from_name( "test_routing_to_questionnaire_end_multiple_sections" ) - self.answer_store = AnswerStore([{"answer_id": "test-answer", "value": "Yes"}]) - self.progress_store = ProgressStore( + self.data_stores.answer_store = AnswerStore( + [{"answer_id": "test-answer", "value": "Yes"}] + ) + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="test-section", @@ -1362,7 +1357,7 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( """ self.schema = grand_calculated_summary_schema # trying to go to number-calculated-summary-1 but distance-calculated-summary-1 which comes before is not complete yet - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -1422,7 +1417,7 @@ def test_return_to_grand_calculated_summary_from_calculated_summary_incomplete_s """ self.schema = grand_calculated_summary_schema # number calculated summary is not complete, so the section is not complete - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section-1", @@ -1538,7 +1533,9 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( Test returning to a calculated summary from a list repeating question as part of a grand calculated summary change link """ self.schema = grand_calculated_summary_repeating_answers_schema - self.progress_store = grand_calculated_summary_repeating_answers_progress_store + self.data_stores.progress_store = ( + grand_calculated_summary_repeating_answers_progress_store + ) parent_location = Location( section_id="section-5", @@ -1574,7 +1571,7 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_complete(self): self.schema = load_schema_from_name("test_section_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="property-details-section", @@ -1610,7 +1607,7 @@ def test_return_to_section_summary_section_is_complete(self): @pytest.mark.usefixtures("app") def test_return_to_section_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_section_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="property-details-section", @@ -1646,7 +1643,7 @@ def test_return_to_section_summary_section_is_in_progress(self): @pytest.mark.usefixtures("app") def test_return_to_final_summary_section_is_complete(self): self.schema = load_schema_from_name("test_submit_with_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "default-section", @@ -1682,7 +1679,7 @@ def test_return_to_final_summary_section_is_complete(self): @pytest.mark.usefixtures("app") def test_return_to_final_summary_section_is_in_progress(self): self.schema = load_schema_from_name("test_submit_with_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "default-section", @@ -1723,7 +1720,7 @@ class TestRouterPreviousLocationLinearFlow(RouterTestCase): @pytest.mark.usefixtures("app") def test_is_none_on_first_block_single_section(self): self.schema = load_schema_from_name("test_checkbox") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -1758,7 +1755,7 @@ def test_is_none_on_first_block_single_section(self): @pytest.mark.usefixtures("app") def test_is_none_on_first_block_second_section(self): self.schema = load_schema_from_name("test_section_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ { "section_id": "property-details-section", @@ -1819,7 +1816,7 @@ class TestRouterLastLocationLinearFlow(RouterTestCase): @pytest.mark.usefixtures("app") def test_block_on_path(self): self.schema = load_schema_from_name("test_checkbox") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="default-section", @@ -1844,7 +1841,7 @@ def test_last_block_not_on_path(self): self.schema = load_schema_from_name( "test_routing_to_questionnaire_end_multiple_sections" ) - self.answer_store = AnswerStore( + self.data_stores.answer_store = AnswerStore( [ {"answer_id": "test-answer", "value": "No"}, { @@ -1856,7 +1853,7 @@ def test_last_block_not_on_path(self): section_id = "test-section" last_block_on_path = "test-forced" completed_block_not_on_path = "test-optional" - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id=section_id, @@ -1873,7 +1870,9 @@ def test_last_block_not_on_path(self): ).url() last_completed_block_in_progress_store = ( - self.progress_store.get_completed_block_ids(SectionKey(section_id))[-1] + self.data_stores.progress_store.get_completed_block_ids( + SectionKey(section_id) + )[-1] ) last_location_url = self.router.get_last_location_in_questionnaire_url() @@ -1885,7 +1884,7 @@ def test_last_block_not_on_path(self): def test_list_collector_final_summary_returns_to_section_summary(self): self.schema = load_schema_from_name("test_list_collector_list_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="section", @@ -1910,7 +1909,7 @@ class TestRouterSectionResume(RouterTestCase): def test_section_in_progress_returns_url_for_first_incomplete_location(self): self.schema = load_schema_from_name("test_section_summary") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="property-details-section", @@ -1937,7 +1936,7 @@ def test_section_complete_returns_url_for_first_location( self, ): self.schema = load_schema_from_name("test_hub_complete_sections") - self.progress_store = ProgressStore( + self.data_stores.progress_store = ProgressStore( [ ProgressDict( section_id="employment-section", diff --git a/tests/app/questionnaire/test_schema_utils.py b/tests/app/questionnaire/test_schema_utils.py index cee9be9196..9ebc6b1c3d 100644 --- a/tests/app/questionnaire/test_schema_utils.py +++ b/tests/app/questionnaire/test_schema_utils.py @@ -1,6 +1,5 @@ -from app.data_models import ProgressStore, SupplementaryDataStore from app.data_models.answer_store import Answer, AnswerStore -from app.data_models.list_store import ListStore +from app.data_models.data_stores import DataStores from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.variants import ( @@ -20,8 +19,6 @@ 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 = {} - response_metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) @@ -29,13 +26,8 @@ def test_transform_variants_with_question_variants(question_variant_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block(block, transformed_block, "Question 1, No") @@ -45,13 +37,8 @@ def test_transform_variants_with_question_variants(question_variant_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block(block, transformed_block, "Question 1, Yes") @@ -61,8 +48,6 @@ def test_transform_variants_with_content(content_variant_schema): schema = QuestionnaireSchema(content_variant_schema) answer_store = AnswerStore({}) answer_store.add_or_update(Answer(answer_id="age-answer", value=18)) - metadata = {} - response_metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) @@ -70,13 +55,8 @@ def test_transform_variants_with_content(content_variant_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert transformed_block != block @@ -87,8 +67,6 @@ def test_transform_variants_with_content(content_variant_schema): def test_transform_variants_with_no_variants(question_schema): schema = QuestionnaireSchema(question_schema) answer_store = AnswerStore({}) - metadata = {} - response_metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) @@ -96,13 +74,8 @@ def test_transform_variants_with_no_variants(question_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert transformed_block == block @@ -112,8 +85,6 @@ 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"]) @@ -121,13 +92,8 @@ def test_transform_variants_list_collector(list_collector_variant_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block( @@ -145,13 +111,8 @@ def test_transform_variants_list_collector(list_collector_variant_schema): transformed_block = transform_variants( block, schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block( @@ -178,13 +139,12 @@ def test_choose_content_to_display(content_variant_schema): content_to_display = choose_content_to_display( schema.get_block("block1"), schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=answer_store, + metadata=metadata, + response_metadata=response_metadata, + ), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert content_to_display["title"] == "You are over 16" @@ -194,13 +154,12 @@ def test_choose_content_to_display(content_variant_schema): content_to_display = choose_content_to_display( schema.get_block("block1"), schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores( + answer_store=answer_store, + metadata=metadata, + response_metadata=response_metadata, + ), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert content_to_display["title"] == "You are ageless" @@ -210,8 +169,6 @@ 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 = {} - response_metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) @@ -219,13 +176,8 @@ def test_choose_question_to_display(question_variant_schema): question_to_display = choose_question_to_display( schema.get_block("block1"), schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert question_to_display["title"] == "Question 1, Yes" @@ -235,13 +187,8 @@ def test_choose_question_to_display(question_variant_schema): question_to_display = choose_question_to_display( schema.get_block("block1"), schema, - metadata, - response_metadata, - answer_store, - ListStore({}), - Location(section_id=section_id, block_id=block["id"]), - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), + data_stores=DataStores(answer_store=answer_store), + current_location=Location(section_id=section_id, block_id=block["id"]), ) assert question_to_display["title"] == "Question 1, No" diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 078b81c48a..f75cc6ada8 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1,17 +1,13 @@ -from typing import Mapping, Optional, Union +from typing import Optional, Union import pytest from mock import MagicMock, Mock from app.authentication.auth_payload_versions import AuthPayloadVersion -from app.data_models import ( - AnswerStore, - ListStore, - ProgressStore, - SupplementaryDataStore, -) +from app.data_models import AnswerStore, ListStore, ProgressStore from app.data_models.answer import Answer, AnswerDict -from app.data_models.metadata_proxy import MetadataProxy, NoMetadataException +from app.data_models.data_stores import DataStores +from app.data_models.metadata_proxy import NoMetadataException from app.data_models.supplementary_data_store import InvalidSupplementaryDataSelector from app.questionnaire import Location, QuestionnaireSchema from app.questionnaire.location import InvalidLocationException @@ -63,10 +59,7 @@ def get_calculation_block( def get_value_source_resolver( schema: QuestionnaireSchema = None, - answer_store: AnswerStore = AnswerStore(), - list_store: ListStore = ListStore(), - metadata: MetadataProxy = None, - response_metadata: Mapping = None, + data_stores: DataStores = None, location: Union[Location, RelationshipLocation] = Location( section_id="test-section", block_id="test-block" ), @@ -74,9 +67,8 @@ def get_value_source_resolver( routing_path_block_ids: Optional[list] = None, use_default_answer=False, escape_answer_values=False, - progress_store: ProgressStore | None = None, - supplementary_data_store: SupplementaryDataStore | None = None, ): + data_stores = data_stores or DataStores() if not schema: schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=bool(list_item_id)) @@ -87,24 +79,21 @@ def get_value_source_resolver( schema.get_default_answer = Mock(return_value=None) return ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata=metadata, - response_metadata=response_metadata, + data_stores=data_stores, schema=schema, location=location, list_item_id=list_item_id, routing_path_block_ids=routing_path_block_ids, use_default_answer=use_default_answer, escape_answer_values=escape_answer_values, - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, ) def test_answer_source(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]), + data_stores=DataStores( + answer_store=AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) + ), ) assert ( @@ -117,14 +106,16 @@ def test_answer_source(): def test_answer_source_with_dict_answer_selector(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": {"years": 1, "months": 10}, - } - ] - ), + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": {"years": 1, "months": 10}, + } + ] + ), + ) ) assert ( @@ -141,8 +132,10 @@ def test_answer_source_with_dict_answer_selector(): def test_answer_source_with_list_item_id_no_list_item_selector(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [{"answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes"}] + data_stores=DataStores( + answer_store=AnswerStore( + [{"answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes"}] + ) ), list_item_id="item-1", ) @@ -161,7 +154,9 @@ def test_list_item_id_ignored_if_answer_not_in_list_collector_or_repeat(): value_source_resolver = get_value_source_resolver( schema=schema, - answer_store=AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]), + data_stores=DataStores( + answer_store=AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) + ), list_item_id="item-1", ) @@ -175,14 +170,16 @@ def test_list_item_id_ignored_if_answer_not_in_list_collector_or_repeat(): def test_answer_source_with_list_item_selector_location(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "list_item_id": "item-1", - "value": "Yes", - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "list_item_id": "item-1", + "value": "Yes", + } + ] + ) ), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" @@ -206,14 +203,16 @@ def test_answer_source_with_list_item_selector_location(): def test_answer_source_with_list_item_selector_location_none(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "list_item_id": "item-1", - "value": "Yes", - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "list_item_id": "item-1", + "value": "Yes", + } + ] + ) ), location=None, ) @@ -229,16 +228,18 @@ def test_answer_source_with_list_item_selector_location_none(): def test_answer_source_with_list_item_selector_list_first_item(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "list_item_id": "item-1", - "value": "Yes", - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "list_item_id": "item-1", + "value": "Yes", + } + ] + ), + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), ), - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), ) assert ( @@ -265,8 +266,10 @@ def test_answer_source_outside_of_repeating_section(): value_source_resolver = get_value_source_resolver( schema=schema, - answer_store=answer_store, - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + data_stores=DataStores( + answer_store=answer_store, + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + ), location=Location( section_id="some-section", block_id="some-block", list_item_id="item-1" ), @@ -299,8 +302,10 @@ def test_answer_source_not_on_path_non_repeating_section(is_answer_on_path): value_source_resolver = get_value_source_resolver( schema=schema, - answer_store=AnswerStore([answer.to_dict()]), - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + data_stores=DataStores( + answer_store=AnswerStore([answer.to_dict()]), + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + ), location=location, list_item_id=location.list_item_id, routing_path_block_ids=["block-on-path"], @@ -335,8 +340,10 @@ def test_answer_source_not_on_path_repeating_section(is_answer_on_path): value_source_resolver = get_value_source_resolver( schema=schema, - answer_store=AnswerStore([answer.to_dict()]), - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + data_stores=DataStores( + answer_store=AnswerStore([answer.to_dict()]), + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + ), location=location, list_item_id=location.list_item_id, routing_path_block_ids=["block-on-path"], @@ -394,17 +401,19 @@ def test_answer_source_dynamic_answer( ) list_item_ids = get_list_items(len(answer_values)) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="percentage-of-shopping", - value=value, - list_item_id=list_item_id, - ) - for list_item_id, value in zip(list_item_ids, answer_values) - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="percentage-of-shopping", + value=value, + list_item_id=list_item_id, + ) + for list_item_id, value in zip(list_item_ids, answer_values) + ] + ), + list_store=ListStore([{"name": "supermarkets", "items": list_item_ids}]), ), - list_store=ListStore([{"name": "supermarkets", "items": list_item_ids}]), schema=schema, escape_answer_values=escape_answer_values, ) @@ -433,17 +442,19 @@ def test_answer_source_repeating_block_answers( ) list_item_ids = get_list_items(len(answer_values)) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="transport-cost", - value=value, - list_item_id=list_item_id, - ) - for list_item_id, value in zip(list_item_ids, answer_values) - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="transport-cost", + value=value, + list_item_id=list_item_id, + ) + for list_item_id, value in zip(list_item_ids, answer_values) + ] + ), + list_store=ListStore([{"name": "transport", "items": list_item_ids}]), ), - list_store=ListStore([{"name": "transport", "items": list_item_ids}]), schema=schema, ) assert ( @@ -460,7 +471,7 @@ def test_answer_source_repeating_block_answers( ) def test_metadata_source(metadata_identifier, expected_result): value_source_resolver = get_value_source_resolver( - metadata=get_metadata({"region_code": "GB-ENG"}) + data_stores=DataStores(metadata=get_metadata({"region_code": "GB-ENG"})) ) source = {"source": "metadata", "identifier": metadata_identifier} @@ -468,7 +479,7 @@ def test_metadata_source(metadata_identifier, expected_result): def test_resolve_metadata_source_with_no_metadata_raises_exception(): - value_source_resolver = get_value_source_resolver(metadata=None) + value_source_resolver = get_value_source_resolver() source = {"source": "metadata", "identifier": "identifier"} @@ -495,7 +506,9 @@ def test_metadata_source_v2_metadata_structure(metadata_identifier, expected_res } ) - value_source_resolver = get_value_source_resolver(metadata=metadata) + value_source_resolver = get_value_source_resolver( + data_stores=DataStores(metadata=metadata) + ) source = {"source": "metadata", "identifier": metadata_identifier} assert value_source_resolver.resolve(source) == expected_result @@ -507,8 +520,10 @@ def test_metadata_source_v2_metadata_structure(metadata_identifier, expected_res ) def test_list_source(list_count): value_source_resolver = get_value_source_resolver( - list_store=ListStore( - [{"name": "some-list", "items": get_list_items(list_count)}] + data_stores=DataStores( + list_store=ListStore( + [{"name": "some-list", "items": get_list_items(list_count)}] + ) ), ) @@ -522,7 +537,9 @@ def test_list_source(list_count): def test_list_source_with_id_selector_first(): value_source_resolver = get_value_source_resolver( - list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]), + data_stores=DataStores( + list_store=ListStore([{"name": "some-list", "items": get_list_items(3)}]) + ), ) assert ( @@ -535,15 +552,17 @@ def test_list_source_with_id_selector_first(): def test_list_source_with_id_selector_same_name_items(): value_source_resolver = get_value_source_resolver( - list_store=ListStore( - [ - { - "name": "some-list", - "items": get_list_items(5), - "same_name_items": get_list_items(3), - } - ] - ), + data_stores=DataStores( + list_store=ListStore( + [ + { + "name": "some-list", + "items": get_list_items(5), + "same_name_items": get_list_items(3), + } + ] + ), + ) ) assert value_source_resolver.resolve( @@ -561,15 +580,17 @@ def test_list_source_with_id_selector_same_name_items(): ) def test_list_source_id_selector_primary_person(primary_person_list_item_id): value_source_resolver = get_value_source_resolver( - list_store=ListStore( - [ - { - "name": "some-list", - "primary_person": primary_person_list_item_id, - "items": get_list_items(3), - } - ] - ), + data_stores=DataStores( + list_store=ListStore( + [ + { + "name": "some-list", + "primary_person": primary_person_list_item_id, + "items": get_list_items(3), + } + ] + ), + ) ) assert ( @@ -596,7 +617,9 @@ def test_location_source(): def test_response_metadata_source(): value_source_resolver = get_value_source_resolver( - response_metadata={"started_at": "2021-10-11T09:40:11.220038+00:00"} + data_stores=DataStores( + response_metadata={"started_at": "2021-10-11T09:40:11.220038+00:00"} + ) ) assert ( value_source_resolver.resolve( @@ -624,15 +647,17 @@ def test_calculated_summary_value_source(mocker, list_item_id): ) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="number-answer-1", value=10, list_item_id=list_item_id - ), - AnswerDict( - answer_id="number-answer-2", value=5, list_item_id=list_item_id - ), - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="number-answer-1", value=10, list_item_id=list_item_id + ), + AnswerDict( + answer_id="number-answer-2", value=5, list_item_id=list_item_id + ), + ] + ) ), schema=schema, list_item_id=list_item_id, @@ -673,17 +698,19 @@ def test_new_calculated_summary_value_source(mocker, list_item_id): ) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="number-answer-1", - value=10, - list_item_id=location.list_item_id, - ), - AnswerDict( - answer_id="number-answer-2", value=5, list_item_id=list_item_id - ), - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="number-answer-1", + value=10, + list_item_id=location.list_item_id, + ), + AnswerDict( + answer_id="number-answer-2", value=5, list_item_id=list_item_id + ), + ] + ) ), schema=schema, list_item_id=list_item_id, @@ -730,18 +757,20 @@ def test_new_calculated_summary_nested_value_source(mocker, list_item_id): ) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="number-answer-1", value=10, list_item_id=list_item_id - ), - AnswerDict( - answer_id="number-answer-2", value=5, list_item_id=list_item_id - ), - AnswerDict( - answer_id="number-answer-3", value=5, list_item_id=list_item_id - ), - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="number-answer-1", value=10, list_item_id=list_item_id + ), + AnswerDict( + answer_id="number-answer-2", value=5, list_item_id=list_item_id + ), + AnswerDict( + answer_id="number-answer-3", value=5, list_item_id=list_item_id + ), + ] + ) ), schema=schema, list_item_id=list_item_id, @@ -817,21 +846,23 @@ def mock_is_answer_repeating(answer_id: str) -> bool: ) value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - AnswerDict( - answer_id="answer-1", value=10, list_item_id=cs_list_item_id_1 - ), - AnswerDict( - answer_id="answer-2", value=5, list_item_id=cs_list_item_id_1 - ), - AnswerDict( - answer_id="answer-3", value=20, list_item_id=cs_list_item_id_2 - ), - AnswerDict( - answer_id="answer-4", value=30, list_item_id=cs_list_item_id_2 - ), - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="answer-1", value=10, list_item_id=cs_list_item_id_1 + ), + AnswerDict( + answer_id="answer-2", value=5, list_item_id=cs_list_item_id_1 + ), + AnswerDict( + answer_id="answer-3", value=20, list_item_id=cs_list_item_id_2 + ), + AnswerDict( + answer_id="answer-4", value=30, list_item_id=cs_list_item_id_2 + ), + ] + ) ), schema=schema, list_item_id=gcs_list_item_id, @@ -856,13 +887,15 @@ def mock_is_answer_repeating(answer_id: str) -> bool: ) def test_answer_value_can_be_escaped(answer_value, escaped_value): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": answer_value, - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": answer_value, + } + ] + ) ), escape_answer_values=True, ) @@ -876,13 +909,15 @@ def test_answer_value_can_be_escaped(answer_value, escaped_value): def test_answer_value_with_selector_can_be_escaped(): value_source_resolver = get_value_source_resolver( - answer_store=AnswerStore( - [ - { - "answer_id": "some-answer", - "value": {"key_1": HTML_CONTENT, "key_2": 1}, - } - ] + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "some-answer", + "value": {"key_1": HTML_CONTENT, "key_2": 1}, + } + ] + ) ), escape_answer_values=True, ) @@ -896,7 +931,7 @@ def test_answer_value_with_selector_can_be_escaped(): def test_progress_values_source_throws_if_no_location_given(): value_source_resolver = get_value_source_resolver( - progress_store=ProgressStore(), location=None + data_stores=DataStores(progress_store=ProgressStore()), location=None ) with pytest.raises(ValueError): value_source_resolver.resolve( @@ -952,10 +987,12 @@ def test_supplementary_data_value_source_non_list_items( else Location(section_id="section", block_id="block-id") ) value_source_resolver = get_value_source_resolver( - supplementary_data_store=supplementary_data_store_with_data, + data_stores=DataStores( + supplementary_data_store=supplementary_data_store_with_data, + list_store=list_store, + ), location=location, list_item_id=location.list_item_id, - list_store=list_store, ) assert ( value_source_resolver.resolve( @@ -1027,10 +1064,12 @@ def test_supplementary_data_value_source_list_items( list_item_id=list_item_id, ) value_source_resolver = get_value_source_resolver( - supplementary_data_store=supplementary_data_store_with_data, + data_stores=DataStores( + supplementary_data_store=supplementary_data_store_with_data, + list_store=list_store, + ), location=location, list_item_id=list_item_id, - list_store=list_store, ) assert ( value_source_resolver.resolve( @@ -1051,7 +1090,9 @@ def test_supplementary_data_invalid_selector_raises_exception( block_id="block-id", ) value_source_resolver = get_value_source_resolver( - supplementary_data_store=supplementary_data_store_with_data, + data_stores=DataStores( + supplementary_data_store=supplementary_data_store_with_data + ), location=location, ) with pytest.raises(InvalidSupplementaryDataSelector) as e: @@ -1075,9 +1116,11 @@ def test_supplementary_data_list_item_outside_repeating_section_raises_exception block_id="block-id", ) value_source_resolver = get_value_source_resolver( - supplementary_data_store=supplementary_data_store_with_data, + data_stores=DataStores( + supplementary_data_store=supplementary_data_store_with_data, + list_store=list_store, + ), location=location, - list_store=list_store, ) with pytest.raises(InvalidSupplementaryDataSelector) as e: value_source_resolver.resolve( diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 7b9302885a..42adc25b92 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -86,11 +86,15 @@ def get_questionnaire_store(version): store = QuestionnaireStore(storage) - store.answer_store = AnswerStore() + store.data_stores.answer_store = AnswerStore() store.supplementary_data_store = SupplementaryDataStore() - store.answer_store.add_or_update(user_answer) - store.metadata = METADATA_V2 if version is AuthPayloadVersion.V2 else METADATA_V1 - store.response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} + store.data_stores.answer_store.add_or_update(user_answer) + store.data_stores.metadata = ( + METADATA_V2 if version is AuthPayloadVersion.V2 else METADATA_V1 + ) + store.data_stores.response_metadata = { + "started_at": "2018-07-04T14:49:33.448608+00:00" + } return store diff --git a/tests/app/submitter/test_convert_payload_0_0_1.py b/tests/app/submitter/test_convert_payload_0_0_1.py index 71b3e8b9f3..bb850cc22f 100644 --- a/tests/app/submitter/test_convert_payload_0_0_1.py +++ b/tests/app/submitter/test_convert_payload_0_0_1.py @@ -29,7 +29,7 @@ def create_answer(answer_id, value): def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("ABC", "2016-01-01").to_dict(), Answer("DEF", "2016-03-30").to_dict(), @@ -53,14 +53,9 @@ def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): RoutingPath(block_ids=["block-1"], section_id="section-1", list_item_id=None) ] answer_object = convert_answers_to_payload_0_0_1( - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - answer_store=questionnaire_store.answer_store, - list_store=questionnaire_store.list_store, + data_stores=questionnaire_store.data_stores, schema=QuestionnaireSchema(questionnaire), full_routing_path=full_routing_path, - progress_store=questionnaire_store.progress_store, - supplementary_data_store=questionnaire_store.supplementary_data_store, ) assert answer_object["002"] == "2016-03-30" assert len(answer_object) == 1 @@ -76,7 +71,9 @@ def test_convert_answers_v2_to_payload_0_0_1_with_key_error(version): def test_answer_with_zero(version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.answer_store = AnswerStore([Answer("GHI", 0).to_dict()]) + questionnaire_store.data_stores.answer_store = AnswerStore( + [Answer("GHI", 0).to_dict()] + ) question = { "id": "question-2", @@ -93,14 +90,9 @@ def test_answer_with_zero(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert data_payload["003"] == "0" @@ -116,7 +108,9 @@ def test_answer_with_zero(version): def test_answer_with_float(version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.answer_store = AnswerStore([Answer("GHI", 10.02).to_dict()]) + questionnaire_store.data_stores.answer_store = AnswerStore( + [Answer("GHI", 10.02).to_dict()] + ) question = { "id": "question-2", @@ -133,14 +127,9 @@ def test_answer_with_float(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Check the converter correctly @@ -157,7 +146,7 @@ def test_answer_with_float(version): def test_answer_with_string(version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("GHI", "String test + !").to_dict()] ) @@ -176,14 +165,9 @@ def test_answer_with_string(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Check the converter correctly @@ -200,7 +184,7 @@ def test_answer_with_string(version): def test_answer_without_qcode(version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("GHI", "String test + !").to_dict()] ) @@ -219,14 +203,9 @@ def test_answer_without_qcode(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert not data_payload @@ -243,7 +222,7 @@ def test_converter_checkboxes_with_q_codes(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -287,14 +266,9 @@ def test_converter_checkboxes_with_q_codes(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -319,7 +293,7 @@ def test_converter_checkboxes_with_q_codes_and_other_value( full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("crisps-answer", ["Ready salted", "Other"]).to_dict(), Answer("other-answer-mandatory", "Bacon").to_dict(), @@ -367,14 +341,9 @@ def test_converter_checkboxes_with_q_codes_and_other_value( schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -399,7 +368,7 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(v full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("crisps-answer", ["Ready salted", "Other"]).to_dict(), ] @@ -445,14 +414,9 @@ def test_converter_checkboxes_with_missing_detail_answer_value_in_answer_store(v schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -473,7 +437,7 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) @@ -518,14 +482,9 @@ def test_converter_checkboxes_with_missing_q_codes_uses_answer_q_code(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -544,7 +503,7 @@ def test_converter_q_codes_for_empty_strings(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [RoutingPath(block_ids=["crisps"], section_id="food")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("crisps-answer", "").to_dict(), Answer("other-crisps-answer", "Ready salted").to_dict(), @@ -573,14 +532,9 @@ def test_converter_q_codes_for_empty_strings(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -603,7 +557,7 @@ def test_radio_answer(version): block_ids=["radio-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("radio-answer", "Coffee").to_dict(), Answer("other-answer-mandatory", "Water").to_dict(), @@ -644,14 +598,9 @@ def test_radio_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -675,7 +624,7 @@ def test_number_answer(version): block_ids=["number-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("number-answer", 0.9999).to_dict()] ) @@ -693,14 +642,9 @@ def test_number_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -723,7 +667,7 @@ def test_percentage_answer(version): block_ids=["percentage-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("percentage-answer", 100).to_dict()] ) @@ -741,14 +685,9 @@ def test_percentage_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -771,7 +710,7 @@ def test_textarea_answer(version): block_ids=["textarea-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("textarea-answer", "example text.").to_dict()] ) @@ -789,14 +728,9 @@ def test_textarea_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -819,7 +753,7 @@ def test_currency_answer(version): block_ids=["currency-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("currency-answer", 99.99).to_dict()] ) @@ -837,14 +771,9 @@ def test_currency_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -867,7 +796,7 @@ def test_dropdown_answer(version): block_ids=["dropdown-block"], section_id="section-1", list_item_id=None ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("dropdown-answer", "Liverpool").to_dict()] ) @@ -896,14 +825,9 @@ def test_dropdown_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -923,7 +847,7 @@ def test_date_answer(version): full_routing_path = [RoutingPath(block_ids=["date-block"], section_id="section-1")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ create_answer("single-date-answer", "1990-02-01"), create_answer("month-year-answer", "1990-01"), @@ -947,14 +871,9 @@ def test_date_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -974,7 +893,7 @@ def test_unit_answer(version): questionnaire_store = get_questionnaire_store(version) full_routing_path = [RoutingPath(block_ids=["unit-block"], section_id="section-1")] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [Answer("unit-answer", 10).to_dict()] ) @@ -992,14 +911,9 @@ def test_unit_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index 75e101c384..e7d46ced03 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -36,7 +36,7 @@ def test_convert_answers_v2_to_payload_0_0_3(version): ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("name", "Joe Bloggs", None).to_dict(), Answer("address", "62 Somewhere", None).to_dict(), @@ -87,14 +87,9 @@ def test_convert_answers_v2_to_payload_0_0_3(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -117,7 +112,7 @@ def test_convert_payload_0_0_3_multiple_answers(version): answers = AnswerStore( [Answer("crisps-answer", ["Ready salted", "Sweet chilli"]).to_dict()] ) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -145,14 +140,9 @@ def test_convert_payload_0_0_3_multiple_answers(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -172,7 +162,7 @@ def test_radio_answer(version): full_routing_path = [RoutingPath(block_ids=["radio-block"], section_id="section-1")] answers = AnswerStore([Answer("radio-answer", "Coffee").to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -198,14 +188,9 @@ def test_radio_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -226,7 +211,7 @@ def test_number_answer(version): RoutingPath(block_ids=["number-block"], section_id="section-1") ] answers = AnswerStore([Answer("number-answer", 1.755).to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -243,14 +228,9 @@ def test_number_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -271,7 +251,7 @@ def test_percentage_answer(version): RoutingPath(block_ids=["percentage-block"], section_id="section-1") ] answers = AnswerStore([Answer("percentage-answer", 99).to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -288,14 +268,9 @@ def test_percentage_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -318,7 +293,7 @@ def test_textarea_answer(version): answers = AnswerStore( [Answer("textarea-answer", "This is an example text!").to_dict()] ) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -335,14 +310,9 @@ def test_textarea_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -363,7 +333,7 @@ def test_currency_answer(version): RoutingPath(block_ids=["currency-block"], section_id="section-1") ] answers = AnswerStore([Answer("currency-answer", 100).to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -380,14 +350,9 @@ def test_currency_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -408,7 +373,7 @@ def test_dropdown_answer(version): RoutingPath(block_ids=["dropdown-block"], section_id="section-1") ] answers = AnswerStore([Answer("dropdown-answer", "Rugby is better!").to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -435,14 +400,9 @@ def test_dropdown_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -467,7 +427,7 @@ def test_date_answer(version): Answer("month-year-answer", "01-1990").to_dict(), ] ) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -484,14 +444,9 @@ def test_date_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -516,7 +471,7 @@ def test_month_year_date_answer(version): Answer("month-year-answer", "01-1990").to_dict(), ] ) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -533,14 +488,9 @@ def test_month_year_date_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -560,7 +510,7 @@ def test_unit_answer(version): full_routing_path = [RoutingPath(block_ids=["unit-block"], section_id="section-1")] answers = AnswerStore([Answer("unit-answer", 10).to_dict()]) - questionnaire_store.answer_store = answers + questionnaire_store.data_stores.answer_store = answers questionnaire = make_schema( "0.0.3", @@ -577,14 +527,9 @@ def test_unit_answer(version): schema = QuestionnaireSchema(questionnaire) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert len(data_payload["answers"]) == 1 @@ -629,20 +574,15 @@ def test_primary_person_list_item_conversion(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_list_collector_primary_person") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["answers"])) @@ -687,20 +627,15 @@ def test_list_item_conversion(version): list_store = ListStore(items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}]) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_list_collector") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) del answer_objects[-1] @@ -742,20 +677,15 @@ def test_list_item_conversion_empty_list(version): {"answer_id": "extraneous-answer", "value": "Bad", "list_item_id": "123"}, ] - questionnaire_store.answer_store = AnswerStore(answer_objects) - questionnaire_store.list_store = ListStore() + questionnaire_store.data_stores.answer_store = AnswerStore(answer_objects) + questionnaire_store.data_stores.list_store = ListStore() schema = load_schema_from_name("test_list_collector") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Answers not on the routing path @@ -785,8 +715,8 @@ def test_default_answers_not_present_when_not_answered(version): answer_objects = [{"answer_id": "number-question-two", "value": "12"}] - questionnaire_store.answer_store = AnswerStore(answer_objects) - questionnaire_store.list_store = ListStore() + questionnaire_store.data_stores.answer_store = AnswerStore(answer_objects) + questionnaire_store.data_stores.list_store = ListStore() routing_path = [ RoutingPath( @@ -796,14 +726,9 @@ def test_default_answers_not_present_when_not_answered(version): ] output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -850,20 +775,15 @@ def test_list_structure_in_payload_is_as_expected(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_list_collector_primary_person") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -908,20 +828,15 @@ def test_primary_person_not_in_payload_when_not_answered(version): list_store = ListStore(items=[{"name": "people", "items": ["xJlKBy", "RfAGDc"]}]) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_list_collector") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data_dict = json_loads(json_dumps(output["lists"])) @@ -986,20 +901,15 @@ def test_relationships_in_payload(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_relationships") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1064,20 +974,15 @@ def test_no_relationships_in_payload(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1159,20 +1064,15 @@ def test_unrelated_block_answers_in_payload(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1271,20 +1171,15 @@ def test_unrelated_block_answers_not_on_path_not_in_payload(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1373,20 +1268,15 @@ def test_relationship_answers_not_on_path_in_payload(version): ] ) - questionnaire_store.answer_store = answers - questionnaire_store.list_store = list_store + questionnaire_store.data_stores.answer_store = answers + questionnaire_store.data_stores.list_store = list_store schema = load_schema_from_name("test_relationships_unrelated") output = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) data = json_loads(json_dumps(output["answers"])) @@ -1438,7 +1328,7 @@ def test_answers_codes_only_present_for_answered_questions(version): ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("name-answer", "Joe Bloggs", None).to_dict(), ] @@ -1447,14 +1337,9 @@ def test_answers_codes_only_present_for_answered_questions(version): schema = load_schema_from_name("test_answer_codes") data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -1477,7 +1362,7 @@ def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(ve RoutingPath(block_ids=["mandatory-checkbox"], section_id="default-section") ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("mandatory-checkbox-answer", ["Ham"]).to_dict(), ] @@ -1486,14 +1371,9 @@ def test_all_answers_codes_for_answer_options_in_payload_when_one_is_answered(ve schema = load_schema_from_name("test_answer_codes") data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -1518,19 +1398,14 @@ def test_no_answers_codes_in_payload_when_no_questions_answered(version): RoutingPath(block_ids=["mandatory-checkbox"], section_id="default-section") ] - questionnaire_store.answer_store = AnswerStore() + questionnaire_store.data_stores.answer_store = AnswerStore() schema = load_schema_from_name("test_answer_codes") data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -1551,7 +1426,7 @@ def test_payload_dynamic_answers(version): ) ] - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("any-supermarket-answer", "Yes", None).to_dict(), Answer("supermarket-name", "Tesco", "tUJzGV").to_dict(), @@ -1562,21 +1437,16 @@ def test_payload_dynamic_answers(version): ] ) - questionnaire_store.list_store = ListStore( + questionnaire_store.data_stores.list_store = ListStore( [{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}] ) schema = load_schema_from_name("test_dynamic_answers_list_source") data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) # Then @@ -1614,22 +1484,17 @@ def test_repeating_block_answers_present( ) ] - questionnaire_store.answer_store = repeating_blocks_answer_store - questionnaire_store.list_store = repeating_blocks_list_store + questionnaire_store.data_stores.answer_store = repeating_blocks_answer_store + questionnaire_store.data_stores.list_store = repeating_blocks_list_store schema = load_schema_from_name( "test_list_collector_repeating_blocks_section_summary" ) data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) expected_answer_codes = [ @@ -1731,14 +1596,14 @@ def test_payload_supplementary_data(): ] list_item_ids = ["tUJzGV", "vhECeh"] - questionnaire_store.supplementary_data_store = SupplementaryDataStore( + questionnaire_store.data_stores.supplementary_data_store = SupplementaryDataStore( supplementary_data=supplementary_data, list_mappings={"supermarkets": supermarkets_list_mappings}, ) - questionnaire_store.list_store = ListStore( + questionnaire_store.data_stores.list_store = ListStore( [{"items": list_item_ids, "name": "supermarkets"}] ) - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("percentage-of-shopping", 12, list_item_ids[0]).to_dict(), Answer("percentage-of-shopping", 21, list_item_ids[1]).to_dict(), @@ -1748,14 +1613,9 @@ def test_payload_supplementary_data(): schema = load_schema_from_name("test_supplementary_data") data_payload = get_payload_data( - questionnaire_store.answer_store, - questionnaire_store.list_store, + questionnaire_store.data_stores, schema, full_routing_path, - questionnaire_store.metadata, - questionnaire_store.response_metadata, - questionnaire_store.progress_store, - questionnaire_store.supplementary_data_store, ) assert "supplementary_data" in data_payload diff --git a/tests/app/submitter/test_converter.py b/tests/app/submitter/test_converter.py index d9634a4593..53fcb56cac 100644 --- a/tests/app/submitter/test_converter.py +++ b/tests/app/submitter/test_converter.py @@ -88,7 +88,7 @@ def test_started_at_should_be_set_in_payload_if_present_in_response_metadata( assert ( answer_object["started_at"] - == questionnaire_store.response_metadata["started_at"] + == questionnaire_store.data_stores.response_metadata["started_at"] ) @@ -104,7 +104,7 @@ def test_started_at_should_not_be_set_in_payload_if_absent_in_response_metadata( ): del fake_response_metadata["started_at"] questionnaire_store = get_questionnaire_store(version) - questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store.data_stores.response_metadata = fake_response_metadata converter = ( convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers @@ -156,7 +156,7 @@ def test_case_id_should_be_set_in_payload(fake_questionnaire_schema, version): fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) - assert answer_object["case_id"] == questionnaire_store.metadata.case_id + assert answer_object["case_id"] == questionnaire_store.data_stores.metadata.case_id @pytest.mark.parametrize( @@ -180,9 +180,13 @@ def test_case_ref_should_be_set_in_payload(fake_questionnaire_schema, version): if version is AuthPayloadVersion.V2: assert answer_object["survey_metadata"][ "case_ref" - ], questionnaire_store.metadata["survey_metadata"]["data"]["case_ref"] + ], questionnaire_store.data_stores.metadata["survey_metadata"]["data"][ + "case_ref" + ] else: - assert answer_object["case_ref"], questionnaire_store.metadata["case_ref"] + assert answer_object["case_ref"], questionnaire_store.data_stores.metadata[ + "case_ref" + ] @pytest.mark.parametrize( @@ -208,11 +212,13 @@ def test_display_address_should_be_set_in_payload_metadata( if version is AuthPayloadVersion.V2: assert payload["survey_metadata"][ "display_address" - ], questionnaire_store.metadata["survey_metadata"]["data"]["display_address"] - else: - assert payload["metadata"]["display_address"], questionnaire_store.metadata[ + ], questionnaire_store.data_stores.metadata["survey_metadata"]["data"][ "display_address" ] + else: + assert payload["metadata"][ + "display_address" + ], questionnaire_store.data_stores.metadata["display_address"] @pytest.mark.parametrize( @@ -252,7 +258,7 @@ def test_converter_language_code_not_set_in_payload( fake_questionnaire_schema, fake_response_metadata, version ): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store.data_stores.response_metadata = fake_response_metadata converter = ( convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers @@ -262,7 +268,7 @@ def test_converter_language_code_not_set_in_payload( fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT ) - assert questionnaire_store.metadata["language_code"] is None + assert questionnaire_store.data_stores.metadata["language_code"] is None assert answer_object["launch_language_code"] == "en" @@ -278,8 +284,8 @@ def test_converter_language_code_set_in_payload( fake_questionnaire_schema, fake_response_metadata, version ): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.metadata = get_metadata({"language_code": "ga"}) - questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store.data_stores.metadata = get_metadata({"language_code": "ga"}) + questionnaire_store.data_stores.response_metadata = fake_response_metadata converter = ( convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers @@ -302,7 +308,7 @@ def test_converter_language_code_set_in_payload( def test_no_metadata_raises_exception(fake_questionnaire_schema, version): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.metadata = None + questionnaire_store.data_stores.metadata = None converter = ( convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers @@ -323,7 +329,7 @@ def test_data_object_set_in_payload( fake_questionnaire_schema, fake_response_metadata, version ): questionnaire_store = get_questionnaire_store(version) - questionnaire_store.response_metadata = fake_response_metadata + questionnaire_store.data_stores.response_metadata = fake_response_metadata converter = ( convert_answers_v2 if version is AuthPayloadVersion.V2 else convert_answers @@ -341,7 +347,7 @@ def test_instrument_id_is_not_in_payload_collection_if_form_type_absent_in_metad ): questionnaire_store = get_questionnaire_store("v1") - questionnaire_store.metadata = get_metadata({"form_type": None}) + questionnaire_store.data_stores.metadata = get_metadata({"form_type": None}) payload = convert_answers( fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 6df95e9334..0b2724df81 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -480,8 +480,8 @@ def questionnaire_store(): store = QuestionnaireStore(storage) - store.answer_store = AnswerStore() - store.metadata = { + store.data_stores.answer_store = AnswerStore() + store.data_stores.metadata = { "ru_name": "ESSENTIAL ENTERPRISE LTD.", "ref_p_start_date": "2016-02-02", "ref_p_end_date": "2016-03-03", @@ -490,6 +490,8 @@ def questionnaire_store(): "ru_ref": "12346789012A", } - store.response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} + store.data_stores.response_metadata = { + "started_at": "2018-07-04T14:49:33.448608+00:00" + } return store diff --git a/tests/app/views/contexts/summary/test_block.py b/tests/app/views/contexts/summary/test_block.py index a2e53eac3b..3ae281acd1 100644 --- a/tests/app/views/contexts/summary/test_block.py +++ b/tests/app/views/contexts/summary/test_block.py @@ -1,4 +1,3 @@ -from app.data_models import ProgressStore from app.questionnaire.location import Location from app.questionnaire.return_location import ReturnLocation from app.views.contexts.summary.block import Block @@ -26,16 +25,11 @@ def test_create_block(mocker): ) block = Block( block_schema, - answer_store=mocker.MagicMock(), - list_store=mocker.MagicMock(), - metadata=mocker.MagicMock(), - response_metadata=mocker.MagicMock(), + data_stores=mocker.MagicMock(), schema=mocker.MagicMock(), location=location, return_location=return_location, - progress_store=ProgressStore(), language="en", - supplementary_data_store=mocker.MagicMock(), ) # Then diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index e0437d506f..f2480ffdbc 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -1,45 +1,15 @@ # pylint: disable=too-many-lines import pytest -from app.data_models import Answer, ListStore, ProgressStore, SupplementaryDataStore +from app.data_models import Answer, ListStore from app.data_models.answer_store import AnswerStore +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.questionnaire.return_location import ReturnLocation -from app.questionnaire.rules.rule_evaluator import RuleEvaluator -from app.questionnaire.value_source_resolver import ValueSourceResolver from app.utilities.schema import load_schema_from_name from app.views.contexts.summary.question import Question -def get_rule_evaluator(answer_store, list_store, schema, response_metadata=None): - return RuleEvaluator( - schema=schema, - answer_store=answer_store, - list_store=list_store, - metadata={}, - response_metadata=response_metadata or {}, - location=None, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), - ) - - -def get_value_source_resolver(answer_store, list_store, schema, response_metadata=None): - return ValueSourceResolver( - answer_store=answer_store, - list_store=list_store, - metadata={}, - response_metadata=response_metadata or {}, - schema=schema, - location=None, - list_item_id=None, - routing_path_block_ids=None, - use_default_answer=True, - progress_store=ProgressStore(), - supplementary_data_store=SupplementaryDataStore(), - ) - - def get_question_schema(answer_schema): return { "id": "question_id", @@ -132,30 +102,18 @@ def address_questionnaire_schema(concatenation_type): def address_question( - answer_store, - list_store, - progress_store, schema, - supplementary_data_store, + data_stores, ): question_schema = schema.get_questions("what-is-your-address-question")[0] return Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=data_stores, schema=schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), location=None, block_id="address-block", return_location=ReturnLocation(), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, schema - ), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) @@ -183,11 +141,8 @@ def test_create_question( answers, expected_title, expected_len, - answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, + data_stores, ): # Given question_schema = { @@ -200,21 +155,12 @@ def test_create_question( # When question = Question( question_schema=question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), + data_stores=data_stores, location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + schema=mock_schema, ) # Then @@ -234,10 +180,7 @@ def test_create_question( def test_concatenate_textfield_answers( concatenation_type, concatenation_character, - list_store, - progress_store, answer_store, - supplementary_data_store, ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -250,11 +193,8 @@ def test_concatenate_textfield_answers( answer_store.add_or_update(answer) question = address_question( - answer_store, - list_store, - progress_store, schema, - supplementary_data_store, + DataStores(answer_store=answer_store), ) # Then assert ( @@ -273,12 +213,7 @@ def test_concatenate_textfield_answers( ), ) def test_concatenate_textfield_answers_default( - concatenation_type, - concatenation_character, - list_store, - answer_store, - progress_store, - supplementary_data_store, + concatenation_type, concatenation_character, answer_store ): # Given schema = address_questionnaire_schema(concatenation_type) @@ -291,11 +226,8 @@ def test_concatenate_textfield_answers_default( # When question = address_question( - answer_store, - list_store, - progress_store, schema, - supplementary_data_store, + data_stores=DataStores(answer_store=answer_store), ) # Then @@ -317,11 +249,8 @@ def test_concatenate_textfield_answers_default( def test_concatenate_number_and_checkbox_answers( concatenation_type, concatenation_character, - list_store, answer_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="age", value=7)) @@ -358,21 +287,12 @@ def test_concatenate_number_and_checkbox_answers( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), + data_stores=DataStores(answer_store=answer_store), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -386,10 +306,7 @@ def test_concatenate_number_and_checkbox_answers( @pytest.mark.usefixtures("app") def test_merge_date_range_answers( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value="13/02/2016")) @@ -408,21 +325,12 @@ def test_merge_date_range_answers( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -434,10 +342,7 @@ def test_merge_date_range_answers( @pytest.mark.usefixtures("app") def test_merge_multiple_date_range_answers( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given for answer in ( @@ -463,21 +368,12 @@ def test_merge_multiple_date_range_answers( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -491,10 +387,7 @@ def test_merge_multiple_date_range_answers( @pytest.mark.usefixtures("app") def test_create_question_with_multiple_answers( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given for answer in ( @@ -516,21 +409,12 @@ def test_create_question_with_multiple_answers( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -542,10 +426,7 @@ def test_create_question_with_multiple_answers( @pytest.mark.usefixtures("app") def test_checkbox_button_options( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given answer_store.add_or_update( @@ -572,21 +453,12 @@ def test_checkbox_button_options( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -598,10 +470,7 @@ def test_checkbox_button_options( @pytest.mark.usefixtures("app") def test_checkbox_button_detail_answer_empty( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given answer_store.add_or_update(Answer(answer_id="answer_1", value=["other", ""])) @@ -630,21 +499,12 @@ def test_checkbox_button_detail_answer_empty( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -697,10 +557,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( expected_len, expected_value, answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given for answer in answers: @@ -723,21 +580,12 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -748,10 +596,7 @@ def test_checkbox_answer_with_detail_answer_returns_the_value( @pytest.mark.usefixtures("app") def test_checkbox_button_other_option_text( answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given answer_store.add_or_update( @@ -783,21 +628,12 @@ def test_checkbox_button_other_option_text( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -849,10 +685,7 @@ def test_radio_answer_with_detail_answers_returns_correct_value( answers, expected, answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): # Given for answer in answers: @@ -875,21 +708,12 @@ def test_radio_answer_with_detail_answers_returns_correct_value( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -935,10 +759,7 @@ def test_answer_types_selected_option_label( answers, expected, answer_store, - list_store, - progress_store, mock_schema, - supplementary_data_store, ): for answer in answers: answer_store.add_or_update(answer) @@ -960,21 +781,12 @@ def test_answer_types_selected_option_label( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=mock_schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, mock_schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema - ), location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -984,11 +796,8 @@ def test_answer_types_selected_option_label( @pytest.mark.usefixtures("app") def test_dynamic_checkbox_answer_options( answer_store, - list_store, - progress_store, mock_schema, dynamic_answer_options_schema, - supplementary_data_store, ): # Given answer_schema = { @@ -1013,23 +822,14 @@ def test_dynamic_checkbox_answer_options( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - schema=mock_schema, - rule_evaluator=get_rule_evaluator( - answer_store, list_store, mock_schema, response_metadata - ), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema, response_metadata + data_stores=DataStores( + answer_store=answer_store, response_metadata=response_metadata ), + schema=mock_schema, location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -1070,10 +870,7 @@ def test_dynamic_checkbox_answer_options( ) def test_dynamic_answer_options( answer_type, - list_store, - progress_store, answer_store_value, - supplementary_data_store, expected, dynamic_answer_options_schema, mock_schema, @@ -1093,23 +890,14 @@ def test_dynamic_answer_options( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, - schema=mock_schema, - rule_evaluator=get_rule_evaluator( - answer_store, list_store, mock_schema, response_metadata - ), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, mock_schema, response_metadata + data_stores=DataStores( + answer_store=answer_store, response_metadata=response_metadata ), + schema=mock_schema, location=None, block_id="house-type", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -1156,9 +944,6 @@ def test_get_answer( answer_schema, answer_store, expected, - list_store, - progress_store, - supplementary_data_store, ): schema = address_questionnaire_schema("Newline") @@ -1168,21 +953,12 @@ def test_get_answer( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store), schema=schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, schema - ), location=None, block_id="address-group", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then @@ -1268,13 +1044,7 @@ def test_get_answer( ), ), ) -def test_dynamic_answers( - answer_store, - list_store, - progress_store, - expected, - supplementary_data_store, -): +def test_dynamic_answers(expected, list_store, answer_store): schema = load_schema_from_name("test_dynamic_answers_list_source", "en") # Given @@ -1330,21 +1100,12 @@ def test_dynamic_answers( # When question = Question( question_schema, - answer_store=answer_store, - list_store=list_store, - progress_store=progress_store, + data_stores=DataStores(answer_store=answer_store, list_store=list_store), schema=schema, - rule_evaluator=get_rule_evaluator(answer_store, list_store, schema), - value_source_resolver=get_value_source_resolver( - answer_store, list_store, schema - ), location=None, block_id="group", return_location=ReturnLocation(), language="en", - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, ) # Then diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index 43904b8610..a845121917 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -1,6 +1,7 @@ import pytest from app.data_models import AnswerStore, ListStore +from app.data_models.data_stores import DataStores from app.questionnaire import Location from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath @@ -75,9 +76,6 @@ def test_build_view_context_for_currency_calculated_summary( test_calculated_summary_schema, test_calculated_summary_answers, test_calculated_summary_answers_skipped_fourth, - list_store, - progress_store, - supplementary_data_store, mocker, return_to_answer_id, skip_fourth, @@ -114,16 +112,13 @@ def test_build_view_context_for_currency_calculated_summary( calculated_summary_context = CalculatedSummaryContext( language=language, schema=test_calculated_summary_schema, - answer_store=test_calculated_summary_answers_skipped_fourth - if skip_fourth - else test_calculated_summary_answers, - list_store=list_store, - progress_store=progress_store, - metadata=None, - response_metadata={}, + data_stores=DataStores( + answer_store=test_calculated_summary_answers_skipped_fourth + if skip_fourth + else test_calculated_summary_answers + ), routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), - supplementary_data_store=supplementary_data_store, return_location=ReturnLocation(return_to_answer_id=return_to_answer_id), ) @@ -185,9 +180,6 @@ def test_build_view_context_for_currency_calculated_summary( def test_build_view_context_for_return_to_calculated_summary( test_grand_calculated_summary_schema, test_grand_calculated_summary_answers, - list_store, - progress_store, - supplementary_data_store, mocker, block_id, return_to_answer_id, @@ -216,14 +208,9 @@ def test_build_view_context_for_return_to_calculated_summary( calculated_summary_context = CalculatedSummaryContext( language="en", schema=test_grand_calculated_summary_schema, - answer_store=test_grand_calculated_summary_answers, - list_store=list_store, - progress_store=progress_store, - metadata=None, - response_metadata={}, + data_stores=DataStores(answer_store=test_grand_calculated_summary_answers), routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), - supplementary_data_store=supplementary_data_store, return_location=ReturnLocation( return_to=return_to, return_to_block_id=return_to_block_id ), @@ -269,9 +256,6 @@ def test_build_view_context_for_return_to_calculated_summary( ) def test_build_view_context_for_calculated_summary_with_dynamic_answers( test_calculated_summary_repeating_and_static_answers_schema, - answer_store, - progress_store, - supplementary_data_store, mocker, block_id, expected_answer_ids, @@ -296,14 +280,13 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( calculated_summary_context = CalculatedSummaryContext( language="en", schema=test_calculated_summary_repeating_and_static_answers_schema, - answer_store=answer_store, - list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "supermarkets"}]), - progress_store=progress_store, - metadata=None, - response_metadata={}, + data_stores=DataStores( + list_store=ListStore( + [{"items": ["CHKtQS", "laFWcs"], "name": "supermarkets"}] + ) + ), routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), current_location=Location(section_id="section-1", block_id=block_id), - supplementary_data_store=supplementary_data_store, return_location=ReturnLocation(), ) @@ -376,8 +359,6 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( ) def test_build_view_context_for_calculated_summary_with_answers_from_repeating_blocks( test_calculated_summary_repeating_blocks, - progress_store, - supplementary_data_store, mocker, block_id, expected_answer_ids, @@ -395,22 +376,29 @@ def test_build_view_context_for_calculated_summary_with_answers_from_repeating_b ) block_ids = ["block-car", "list-collector"] - answer_store = AnswerStore( - [ - {"answer_id": "transport-name", "value": "Train", "list_item_id": "CHKtQS"}, - {"answer_id": "transport-name", "value": "Bus", "list_item_id": "laFWcs"}, - ] - ) calculated_summary_context = CalculatedSummaryContext( language="en", schema=test_calculated_summary_repeating_blocks, - answer_store=answer_store, - list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "transport"}]), - progress_store=progress_store, - supplementary_data_store=supplementary_data_store, - metadata=None, - response_metadata={}, + data_stores=DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "transport-name", + "value": "Train", + "list_item_id": "CHKtQS", + }, + { + "answer_id": "transport-name", + "value": "Bus", + "list_item_id": "laFWcs", + }, + ] + ), + list_store=ListStore( + [{"items": ["CHKtQS", "laFWcs"], "name": "transport"}] + ), + ), routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), current_location=Location(section_id="section-1", block_id=block_id), return_location=ReturnLocation(), diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index 5544d6bd8e..f0736e0095 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -1,6 +1,7 @@ import pytest from app.data_models import CompletionStatus +from app.data_models.data_stores import DataStores from app.data_models.progress_store import ProgressStore from app.questionnaire import Location from app.questionnaire.return_location import ReturnLocation @@ -35,8 +36,6 @@ def test_build_view_context_for_grand_calculated_summary( value, test_grand_calculated_summary_schema, test_grand_calculated_summary_answers, - list_store, - supplementary_data_store, mocker, return_to_answer_id, ): @@ -59,37 +58,35 @@ def test_build_view_context_for_grand_calculated_summary( grand_calculated_summary_context = GrandCalculatedSummaryContext( language="en", schema=test_grand_calculated_summary_schema, - answer_store=test_grand_calculated_summary_answers, - list_store=list_store, - progress_store=ProgressStore( - progress=[ - { - "section_id": "section-1", - "status": CompletionStatus.COMPLETED, - "block_ids": [ - "first-number-block", - "second-number-block", - "distance-calculated-summary-1", - "number-calculated-summary-1", - ], - }, - { - "section_id": "section-2", - "status": CompletionStatus.COMPLETED, - "block_ids": [ - "third-number-block", - "fourth-number-block", - "distance-calculated-summary-2", - "number-calculated-summary-2", - ], - }, - ] + data_stores=DataStores( + answer_store=test_grand_calculated_summary_answers, + progress_store=ProgressStore( + progress=[ + { + "section_id": "section-1", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + }, + { + "section_id": "section-2", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "third-number-block", + "fourth-number-block", + "distance-calculated-summary-2", + "number-calculated-summary-2", + ], + }, + ] + ), ), - metadata=None, - response_metadata={}, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), - supplementary_data_store=supplementary_data_store, return_location=ReturnLocation(), ) diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 5b1172ddb6..088137f5d4 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -5,35 +5,14 @@ from app.questionnaire.router import Router from app.utilities.schema import load_schema_from_name from app.views.contexts import HubContext -from tests.app.questionnaire.conftest import get_metadata @pytest.fixture -def router( - schema, - answer_store, - list_store, - progress_store, - supplementary_data_store, -): - return Router( - schema, - answer_store, - list_store, - progress_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) +def router(schema, data_stores): + return Router(schema, data_stores) -def test_get_not_started_row_for_section( - schema, - progress_store, - answer_store, - list_store, - supplementary_data_store, -): +def test_get_not_started_row_for_section(schema, data_stores): expected = { "rowItems": [ { @@ -53,16 +32,7 @@ def test_get_not_started_row_for_section( ] } - hub = HubContext( - language=None, - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub = HubContext(language=None, schema=schema, data_stores=data_stores) actual = hub.get_row_context_for_section( section_name="Breakfast", @@ -74,13 +44,7 @@ def test_get_not_started_row_for_section( assert expected == actual -def test_get_completed_row_for_section( - schema, - progress_store, - answer_store, - list_store, - supplementary_data_store, -): +def test_get_completed_row_for_section(schema, data_stores): expected = { "rowItems": [ { @@ -101,16 +65,7 @@ def test_get_completed_row_for_section( ] } - hub = HubContext( - language=None, - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub = HubContext(language=None, schema=schema, data_stores=data_stores) actual = hub.get_row_context_for_section( section_name="Breakfast", @@ -122,24 +77,9 @@ def test_get_completed_row_for_section( assert expected == actual -def test_get_context( - progress_store, - answer_store, - list_store, - router, - supplementary_data_store, -): +def test_get_context(router, data_stores): schema = load_schema_from_name("test_hub_and_spoke") - hub = HubContext( - language="en", - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub = HubContext(language="en", schema=schema, data_stores=data_stores) expected_context = { "individual_response_enabled": False, @@ -156,24 +96,9 @@ def test_get_context( ) -def test_get_context_custom_content_incomplete( - progress_store, - answer_store, - list_store, - router, - supplementary_data_store, -): +def test_get_context_custom_content_incomplete(router, data_stores): schema = load_schema_from_name("test_hub_and_spoke_custom_content") - hub_context = HubContext( - language="en", - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub_context = HubContext(language="en", schema=schema, data_stores=data_stores) expected_context = { "individual_response_enabled": False, @@ -190,24 +115,9 @@ def test_get_context_custom_content_incomplete( ) -def test_get_context_custom_content_complete( - progress_store, - answer_store, - list_store, - router, - supplementary_data_store, -): +def test_get_context_custom_content_complete(data_stores, router): schema = load_schema_from_name("test_hub_and_spoke_custom_content") - hub_context = HubContext( - language="en", - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub_context = HubContext(language="en", schema=schema, data_stores=data_stores) expected_context = { "individual_response_enabled": False, @@ -225,23 +135,11 @@ def test_get_context_custom_content_complete( def test_get_context_no_list_items_survey_incomplete_individual_response_disabled( - progress_store, - answer_store, - list_store, + data_stores, router, - supplementary_data_store, ): schema = load_schema_from_name("test_individual_response") - hub_context = HubContext( - language="en", - progress_store=progress_store, - list_store=list_store, - schema=schema, - answer_store=answer_store, - metadata=get_metadata(), - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + hub_context = HubContext(language="en", schema=schema, data_stores=data_stores) assert not ( hub_context( diff --git a/tests/app/views/contexts/test_list_context.py b/tests/app/views/contexts/test_list_context.py index 2040d82408..36f9aa0dca 100644 --- a/tests/app/views/contexts/test_list_context.py +++ b/tests/app/views/contexts/test_list_context.py @@ -1,6 +1,7 @@ import pytest from app.data_models import CompletionStatus, ProgressStore +from app.data_models.data_stores import DataStores from app.data_models.progress import ProgressDict from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name @@ -13,18 +14,11 @@ def test_build_list_collector_context( schema, people_answer_store, people_list_store, - progress_store, - supplementary_data_store, ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - people_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + DataStores(answer_store=people_answer_store, list_store=people_list_store), ) list_context = list_context( @@ -42,18 +36,11 @@ def test_build_list_summary_context_no_summary_block( schema, people_answer_store, people_list_store, - progress_store, - supplementary_data_store, ): list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - people_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + DataStores(answer_store=people_answer_store, list_store=people_list_store), ) list_context = list_context( @@ -70,8 +57,6 @@ def test_build_list_summary_context( list_collector_block, people_answer_store, people_list_store, - progress_store, - supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") expected = [ @@ -98,12 +83,7 @@ def test_build_list_summary_context( list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - people_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + DataStores(answer_store=people_answer_store, list_store=people_list_store), ) list_context = list_context( @@ -123,21 +103,16 @@ def test_assert_primary_person_string_appended( list_collector_block, people_answer_store, people_list_store, - progress_store, - supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") people_list_store["people"].primary_person = "PlwgoG" list_context = ListContext( language=DEFAULT_LANGUAGE_CODE, - progress_store=progress_store, - list_store=people_list_store, schema=schema, - answer_store=people_answer_store, - metadata=None, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + answer_store=people_answer_store, list_store=people_list_store + ), ) list_context = list_context( summary_definition=list_collector_block["summary"], @@ -156,20 +131,15 @@ def test_for_list_item_ids( list_collector_block, people_answer_store, people_list_store, - progress_store, - supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") list_context = ListContext( language=DEFAULT_LANGUAGE_CODE, - progress_store=progress_store, - list_store=people_list_store, schema=schema, - answer_store=people_answer_store, - metadata=None, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + answer_store=people_answer_store, list_store=people_list_store + ), ) list_context = list_context( summary_definition=list_collector_block["summary"], @@ -197,7 +167,6 @@ def test_list_context_items_complete_without_repeating_blocks( people_answer_store, people_list_store, list_collector_block, - supplementary_data_store, ): schema = load_schema_from_name("test_list_collector_primary_person") expected = [ @@ -241,12 +210,11 @@ def test_list_context_items_complete_without_repeating_blocks( list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - people_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores=DataStores( + answer_store=people_answer_store, + list_store=people_list_store, + progress_store=progress_store, + ), ) list_context = list_context( @@ -265,8 +233,6 @@ def test_list_context_items_complete_without_repeating_blocks( def test_list_context_items_incomplete_with_repeating_blocks( repeating_blocks_answer_store, repeating_blocks_list_store, - progress_store, - supplementary_data_store, ): schema = load_schema_from_name( "test_list_collector_repeating_blocks_section_summary" @@ -296,12 +262,10 @@ def test_list_context_items_incomplete_with_repeating_blocks( list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - repeating_blocks_answer_store, - repeating_blocks_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + DataStores( + answer_store=repeating_blocks_answer_store, + list_store=repeating_blocks_list_store, + ), ) list_context = list_context( @@ -365,12 +329,12 @@ def test_list_context_items_complete_with_repeating_blocks( list_context = ListContext( DEFAULT_LANGUAGE_CODE, schema, - repeating_blocks_answer_store, - repeating_blocks_list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + DataStores( + answer_store=repeating_blocks_answer_store, + list_store=repeating_blocks_list_store, + supplementary_data_store=supplementary_data_store, + progress_store=progress_store, + ), ) list_context = list_context( diff --git a/tests/app/views/contexts/test_preview_context.py b/tests/app/views/contexts/test_preview_context.py index ae6dda20f5..d1a3de3849 100644 --- a/tests/app/views/contexts/test_preview_context.py +++ b/tests/app/views/contexts/test_preview_context.py @@ -1,6 +1,7 @@ import pytest from flask_babel import lazy_gettext +from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema from app.views.contexts.preview_context import ( PreviewContext, @@ -11,20 +12,16 @@ def test_build_preview_rendering_context( test_introduction_preview_linear_schema, - answer_store, - list_store, - progress_store, questionnaire_store, ): preview_context = PreviewContext( "en", test_introduction_preview_linear_schema, - answer_store, - list_store, - progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=DataStores( + supplementary_data_store=questionnaire_store.data_stores.supplementary_data_store, + metadata=questionnaire_store.data_stores.metadata, + response_metadata=questionnaire_store.data_stores.response_metadata, + ), ) preview_context = preview_context() @@ -34,20 +31,16 @@ def test_build_preview_rendering_context( def test_build_preview_context( test_introduction_preview_linear_schema, - answer_store, - list_store, - progress_store, questionnaire_store, ): preview_context = PreviewContext( "en", test_introduction_preview_linear_schema, - answer_store, - list_store, - progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores=DataStores( + supplementary_data_store=questionnaire_store.data_stores.supplementary_data_store, + metadata=questionnaire_store.data_stores.metadata, + response_metadata=questionnaire_store.data_stores.response_metadata, + ), ) context = preview_context() @@ -273,20 +266,12 @@ def test_build_preview_context( def test_preview_questions_disabled_raises_exception( - answer_store, - list_store, - progress_store, - questionnaire_store, + data_stores, ): schema = QuestionnaireSchema({"preview_questions": False}) with pytest.raises(PreviewNotEnabledException): PreviewContext( "en", schema, - answer_store, - list_store, - progress_store, - metadata=questionnaire_store.metadata, - response_metadata=questionnaire_store.response_metadata, - supplementary_data_store=questionnaire_store.supplementary_data_store, + data_stores, ) diff --git a/tests/app/views/contexts/test_section_summary_context.py b/tests/app/views/contexts/test_section_summary_context.py index 2c4185f540..0c1d007784 100644 --- a/tests/app/views/contexts/test_section_summary_context.py +++ b/tests/app/views/contexts/test_section_summary_context.py @@ -2,9 +2,8 @@ from mock import MagicMock from app.data_models.answer_store import Answer, AnswerStore +from app.data_models.data_stores import DataStores from app.data_models.list_store import ListStore -from app.data_models.progress_store import ProgressStore -from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import Location from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.questionnaire.routing_path import RoutingPath @@ -17,23 +16,15 @@ def test_build_summary_rendering_context( test_section_summary_schema, - answer_store, - list_store, - progress_store, + data_stores, mocker, - supplementary_data_store, ): section_summary_context = SectionSummaryContext( "en", test_section_summary_schema, - answer_store, - list_store, - progress_store, - metadata=get_metadata(), - response_metadata={}, + data_stores, current_location=Location(section_id="property-details-section"), routing_path=mocker.MagicMock(), - supplementary_data_store=supplementary_data_store, ) single_section_context = section_summary_context() @@ -43,26 +34,18 @@ def test_build_summary_rendering_context( def test_build_view_context_for_section_summary( test_section_summary_schema, - answer_store, - list_store, - progress_store, + data_stores, mocker, - supplementary_data_store, ): summary_context = SectionSummaryContext( "en", test_section_summary_schema, - answer_store, - list_store, - progress_store, - metadata=None, - response_metadata={}, + data_stores, current_location=Location( section_id="property-details-section", block_id="property-details-summary", ), routing_path=mocker.MagicMock(), - supplementary_data_store=supplementary_data_store, ) context = summary_context() @@ -114,10 +97,7 @@ def test_custom_section( expected_title, test_section_summary_schema, answer_store, - list_store, - progress_store, mocker, - supplementary_data_store, ): for answer in answers: answer_store.add_or_update(answer) @@ -125,38 +105,31 @@ def test_custom_section( summary_context = SectionSummaryContext( "en", test_section_summary_schema, - answer_store, - list_store, - progress_store, - metadata=None, - response_metadata={}, + data_stores=DataStores(answer_store=answer_store), current_location=location, routing_path=mocker.MagicMock(), - supplementary_data_store=supplementary_data_store, ) context = summary_context() assert context["summary"][title_key] == expected_title @pytest.mark.usefixtures("app") -def test_context_for_section_list_summary( - people_answer_store, progress_store, supplementary_data_store -): +def test_context_for_section_list_summary(people_answer_store): schema = load_schema_from_name("test_list_collector_list_summary") summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, schema=schema, - answer_store=people_answer_store, - list_store=ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] + data_stores=DataStores( + answer_store=people_answer_store, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, + {"items": ["gTrlio"], "name": "visitors"}, + ] + ), + metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), ), - progress_store=progress_store, - metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), - response_metadata={}, current_location=Location(section_id="section"), routing_path=RoutingPath( block_ids=[ @@ -166,7 +139,6 @@ def test_context_for_section_list_summary( ], section_id="section", ), - supplementary_data_store=supplementary_data_store, ) context = summary_context() @@ -278,8 +250,6 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( item_label, answer_1_label, answer_2_label, - progress_store, - supplementary_data_store, request, ): schema = load_schema_from_name(test_schema) @@ -287,15 +257,14 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( summary_context = SectionSummaryContext( language=DEFAULT_LANGUAGE_CODE, schema=schema, - answer_store=answer_store, - list_store=ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, - ] + data_stores=DataStores( + answer_store=answer_store, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "companies"}, + ] + ), ), - progress_store=progress_store, - metadata=None, - response_metadata={}, current_location=Location(section_id="section-companies"), routing_path=RoutingPath( block_ids=[ @@ -303,7 +272,6 @@ def test_context_for_section_summary_with_list_summary_and_first_variant( ], section_id="section-companies", ), - supplementary_data_store=supplementary_data_store, ) context = summary_context() expected = { @@ -477,16 +445,15 @@ def test_context_for_driving_question_summary_empty_list(): summary_context = SectionSummaryContext( DEFAULT_LANGUAGE_CODE, schema, - AnswerStore([{"answer_id": "anyone-usually-live-at-answer", "value": "No"}]), - ListStore(), - ProgressStore(), - metadata=None, - response_metadata={}, + DataStores( + answer_store=AnswerStore( + [{"answer_id": "anyone-usually-live-at-answer", "value": "No"}] + ) + ), current_location=Location(section_id="section"), routing_path=RoutingPath( block_ids=["anyone-usually-live-at"], section_id="section" ), - supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -517,13 +484,10 @@ def test_context_for_driving_question_summary_empty_list(): @pytest.mark.usefixtures("app") -def test_context_for_driving_question_summary(progress_store): +def test_context_for_driving_question_summary(): schema = load_schema_from_name("test_list_collector_driving_question") - - summary_context = SectionSummaryContext( - DEFAULT_LANGUAGE_CODE, - schema, - AnswerStore( + data_stores = DataStores( + answer_store=AnswerStore( [ {"answer_id": "anyone-usually-live-at-answer", "value": "Yes"}, {"answer_id": "first-name", "value": "Toni", "list_item_id": "PlwgoG"}, @@ -534,16 +498,18 @@ def test_context_for_driving_question_summary(progress_store): }, ] ), - ListStore([{"items": ["PlwgoG"], "name": "people"}]), - progress_store, - metadata=None, - response_metadata={}, + list_store=ListStore([{"items": ["PlwgoG"], "name": "people"}]), + ) + + summary_context = SectionSummaryContext( + DEFAULT_LANGUAGE_CODE, + schema, + data_stores=data_stores, current_location=Location(section_id="section"), routing_path=RoutingPath( block_ids=["anyone-usually-live-at", "anyone-else-live-at"], section_id="section", ), - supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -594,23 +560,21 @@ def test_titles_for_repeating_section_summary(people_answer_store): section_summary_context = SectionSummaryContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] + data_stores=DataStores( + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, + {"items": ["gTrlio"], "name": "visitors"}, + ] + ), + answer_store=people_answer_store, ), - ProgressStore(), - metadata=None, - response_metadata={}, current_location=Location( section_id="personal-details-section", list_name="people", list_item_id="PlwgoG", ), routing_path=MagicMock(), - supplementary_data_store=SupplementaryDataStore(), ) context = section_summary_context() @@ -620,16 +584,15 @@ def test_titles_for_repeating_section_summary(people_answer_store): section_summary_context = SectionSummaryContext( DEFAULT_LANGUAGE_CODE, schema, - people_answer_store, - ListStore( - [ - {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, - {"items": ["gTrlio"], "name": "visitors"}, - ] + data_stores=DataStores( + answer_store=people_answer_store, + list_store=ListStore( + [ + {"items": ["PlwgoG", "UHPLbX"], "name": "people"}, + {"items": ["gTrlio"], "name": "visitors"}, + ] + ), ), - ProgressStore(), - metadata=None, - response_metadata={}, current_location=Location( block_id="personal-summary", section_id="personal-details-section", @@ -637,7 +600,6 @@ def test_titles_for_repeating_section_summary(people_answer_store): list_item_id="UHPLbX", ), routing_path=MagicMock(), - supplementary_data_store=SupplementaryDataStore(), ) context = section_summary_context() @@ -647,17 +609,25 @@ def test_titles_for_repeating_section_summary(people_answer_store): @pytest.mark.usefixtures("app") def test_primary_only_links_for_section_summary(people_answer_store): schema = load_schema_from_name("test_list_collector_list_summary") - - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, + data_stores = DataStores( answer_store=people_answer_store, list_store=ListStore( - [{"items": ["PlwgoG"], "name": "people", "primary_person": "PlwgoG"}] + [ + { + "items": ["PlwgoG"], + "name": "people", + "primary_person": "PlwgoG", + } + ] ), - progress_store=ProgressStore(), metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), response_metadata={}, + ) + + summary_context = SectionSummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + data_stores=data_stores, current_location=Location(section_id="section"), routing_path=RoutingPath( block_ids=[ @@ -667,7 +637,6 @@ def test_primary_only_links_for_section_summary(people_answer_store): ], section_id="section", ), - supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() @@ -681,9 +650,7 @@ def test_primary_only_links_for_section_summary(people_answer_store): def test_primary_links_for_section_summary(people_answer_store): schema = load_schema_from_name("test_list_collector_list_summary") - summary_context = SectionSummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, + data_stores = DataStores( answer_store=people_answer_store, list_store=ListStore( [ @@ -694,9 +661,13 @@ def test_primary_links_for_section_summary(people_answer_store): } ] ), - progress_store=ProgressStore(), metadata=get_metadata({"display_address": "70 Abingdon Road, Goathill"}), - response_metadata={}, + ) + + summary_context = SectionSummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + data_stores=data_stores, current_location=Location(section_id="section"), routing_path=RoutingPath( block_ids=[ @@ -706,7 +677,6 @@ def test_primary_links_for_section_summary(people_answer_store): ], section_id="section", ), - supplementary_data_store=SupplementaryDataStore(), ) context = summary_context() diff --git a/tests/app/views/contexts/test_submit_context.py b/tests/app/views/contexts/test_submit_context.py index 78cc34d190..6a695002b5 100644 --- a/tests/app/views/contexts/test_submit_context.py +++ b/tests/app/views/contexts/test_submit_context.py @@ -31,21 +31,13 @@ def test_custom_submission_content( schema_name, expected, - answer_store, - list_store, - progress_store, - supplementary_data_store, + data_stores, ): schema = load_schema_from_name(schema_name) submit_questionnaire_context = SubmitQuestionnaireContext( "en", schema, - answer_store, - list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, + data_stores, ) context = submit_questionnaire_context() @@ -58,23 +50,9 @@ def test_custom_submission_content( @pytest.mark.usefixtures("app") -def test_questionnaire_context( - answer_store, - list_store, - progress_store, - supplementary_data_store, -): +def test_questionnaire_context(data_stores): schema = load_schema_from_name("test_submit_with_summary") - submit_questionnaire_context = SubmitQuestionnaireContext( - "en", - schema, - answer_store, - list_store, - progress_store, - metadata={}, - response_metadata={}, - supplementary_data_store=supplementary_data_store, - ) + submit_questionnaire_context = SubmitQuestionnaireContext("en", schema, data_stores) context = submit_questionnaire_context() assert_summary_context(context) diff --git a/tests/app/views/contexts/test_submitted_response_context.py b/tests/app/views/contexts/test_submitted_response_context.py index 10676bd021..fb885d85f5 100644 --- a/tests/app/views/contexts/test_submitted_response_context.py +++ b/tests/app/views/contexts/test_submitted_response_context.py @@ -138,7 +138,7 @@ def test_no_metadata_raises_error( with app.app_context(): questionnaire_store = fake_questionnaire_store({}, SUBMITTED_AT) - questionnaire_store.metadata = None + questionnaire_store.data_stores.metadata = None with pytest.raises(NoMetadataException): build_view_submitted_response_context( @@ -150,9 +150,9 @@ def fake_questionnaire_store(metadata, submitted_at): storage = Mock() storage.get_user_data = Mock(return_value=("{}", "ce_sid", 1, None)) questionnaire_store = QuestionnaireStore(storage) - questionnaire_store.metadata = get_metadata(metadata) + questionnaire_store.data_stores.metadata = get_metadata(metadata) questionnaire_store.submitted_at = submitted_at - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ Answer("name-answer", "John Smith", None).to_dict(), Answer("address-answer", "NP10 8XG", None).to_dict(), diff --git a/tests/app/views/contexts/test_summary_context.py b/tests/app/views/contexts/test_summary_context.py index 54a98fac2e..30409f91ae 100644 --- a/tests/app/views/contexts/test_summary_context.py +++ b/tests/app/views/contexts/test_summary_context.py @@ -7,6 +7,7 @@ ProgressStore, SupplementaryDataStore, ) +from app.data_models.data_stores import DataStores from app.data_models.progress import CompletionStatus, ProgressDict from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.utilities.schema import load_schema_from_name @@ -102,17 +103,21 @@ def test_context_for_summary(): ] ) - summary_context = SummaryContext( - language=DEFAULT_LANGUAGE_CODE, - schema=schema, + data_stores = DataStores( answer_store=answer_store, list_store=list_store, progress_store=progress_store, metadata=None, response_metadata={}, - view_submitted_response=False, supplementary_data_store=SupplementaryDataStore(), ) + + summary_context = SummaryContext( + language=DEFAULT_LANGUAGE_CODE, + schema=schema, + data_stores=data_stores, + view_submitted_response=False, + ) context = summary_context() expected = { "answers_are_editable": False, diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index 8ac724a725..88ad452438 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -7,6 +7,7 @@ from app.authentication.auth_payload_versions import AuthPayloadVersion from app.data_models import QuestionnaireStore +from app.data_models.data_stores import DataStores from app.data_models.metadata_proxy import MetadataProxy from app.data_models.session_data import SessionData from app.data_models.session_store import SessionStore @@ -203,19 +204,21 @@ def mock_questionnaire_store(mocker): storage_ = mocker.Mock() storage_.get_user_data = mocker.Mock(return_value=("{}", "ce_id", 1, None)) questionnaire_store = QuestionnaireStore(storage_) - questionnaire_store.metadata = MetadataProxy.from_dict( - { - "tx_id": "tx_id", - "case_id": "case_id", - "ru_ref": ru_ref, - "user_id": user_id, - "collection_exercise_sid": collection_exercise_sid, - "period_id": period_id, - "schema_name": schema_name, - "account_service_url": "account_service_url", - "response_id": "response_id", - "response_expires_at": get_response_expires_at(), - } + questionnaire_store.data_stores = DataStores( + metadata=MetadataProxy.from_dict( + { + "tx_id": "tx_id", + "case_id": "case_id", + "ru_ref": ru_ref, + "user_id": user_id, + "collection_exercise_sid": collection_exercise_sid, + "period_id": period_id, + "schema_name": schema_name, + "account_service_url": "account_service_url", + "response_id": "response_id", + "response_expires_at": get_response_expires_at(), + } + ) ) return questionnaire_store @@ -225,33 +228,35 @@ def mock_questionnaire_store_v2(mocker): storage_ = mocker.Mock() storage_.get_user_data = mocker.Mock(return_value=("{}", "ce_id", 1, None)) questionnaire_store = QuestionnaireStore(storage_) - questionnaire_store.metadata = MetadataProxy.from_dict( - { - "version": AuthPayloadVersion.V2, - "tx_id": "tx_id", - "case_id": case_id, - "schema_name": schema_name, - "collection_exercise_sid": collection_exercise_sid, - "response_id": response_id, - "channel": channel, - "region_code": region_code, - "account_service_url": "account_service_url", - "response_expires_at": get_response_expires_at(), - "survey_metadata": { - "data": { - "period_id": period_id, - "period_str": period_str, - "ref_p_start_date": ref_p_start_date, - "ref_p_end_date": ref_p_end_date, - "ru_ref": ru_ref, - "ru_name": ru_name, - "case_type": case_type, - "form_type": form_type, - "case_ref": case_ref, - "display_address": display_address, - "user_id": user_id, - } - }, - } + questionnaire_store.data_stores = DataStores( + metadata=MetadataProxy.from_dict( + { + "version": AuthPayloadVersion.V2, + "tx_id": "tx_id", + "case_id": case_id, + "schema_name": schema_name, + "collection_exercise_sid": collection_exercise_sid, + "response_id": response_id, + "channel": channel, + "region_code": region_code, + "account_service_url": "account_service_url", + "response_expires_at": get_response_expires_at(), + "survey_metadata": { + "data": { + "period_id": period_id, + "period_str": period_str, + "ref_p_start_date": ref_p_start_date, + "ref_p_end_date": ref_p_end_date, + "ru_ref": ru_ref, + "ru_name": ru_name, + "case_type": case_type, + "form_type": form_type, + "case_ref": case_ref, + "display_address": display_address, + "user_id": user_id, + } + }, + } + ) ) return questionnaire_store diff --git a/tests/app/views/handlers/test_question_with_dynamic_answers.py b/tests/app/views/handlers/test_question_with_dynamic_answers.py index ac02eda5b7..16d57e0107 100644 --- a/tests/app/views/handlers/test_question_with_dynamic_answers.py +++ b/tests/app/views/handlers/test_question_with_dynamic_answers.py @@ -18,7 +18,7 @@ def test_question_with_dynamic_answers(storage, language, mocker): set_storage_data(storage, submitted_at=submitted_at) questionnaire_store = QuestionnaireStore(storage) - questionnaire_store.answer_store = AnswerStore( + questionnaire_store.data_stores.answer_store = AnswerStore( [ { "answer_id": "supermarket-name", @@ -32,7 +32,7 @@ def test_question_with_dynamic_answers(storage, language, mocker): }, ] ) - questionnaire_store.list_store = ListStore( + questionnaire_store.data_stores.list_store = ListStore( [{"items": ["tUJzGV", "vhECeh"], "name": "supermarkets"}] ) questionnaire_store.set_metadata({"response_expires_at": get_response_expires_at()}) From 7e65ef5062e0d616a0c514b05779d08a4db5c125 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 15 Nov 2023 08:59:34 +0000 Subject: [PATCH 325/567] Remove code added to fix functional tests for older chromedriver version (#1251) --- .../dynamic_answers_list_value_source.spec.js | 57 +++++++------------ .../features/validation/sum/dynamic.spec.js | 1 - .../spec/textfield_suggestions.spec.js | 6 +- 3 files changed, 22 insertions(+), 42 deletions(-) diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index e3d53f1bdf..b2511bddfb 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -15,13 +15,12 @@ describe("Dynamic answers list value source", () => { const summaryTitles = ".ons-summary__item-title"; const summaryValues = ".ons-summary__values"; const summaryActions = ".ons-summary__actions"; - const timeout = 2000; beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_dynamic_answers_list_source.json"); }); it("Given list items have been added, When the dynamic answers are displayed, Then the correct answers should be visible", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); @@ -34,29 +33,26 @@ describe("Dynamic answers list value source", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(2); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.supermarketsListAddLink()).click(); await $(ListCollectorAddPage.supermarketName()).setValue("Aldi"); await $(ListCollectorAddPage.setMaximum()).setValue(10000); await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); }); it("Given list items have been added and the dynamic answers are submitted, When the summary is displayed, Then the correct answers should be visible and have correct values", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); - await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); + await setMinimumAndGetSectionSummary(); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("12%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[1].getText()).toBe("Percentage of shopping at Aldi"); @@ -67,88 +63,80 @@ describe("Dynamic answers list value source", () => { await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).toBe(8); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are revisited, Then they should be visible and have correct values", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); - await $(DynamicAnswerPage.labels()).waitForExist({ timeout }); await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).toBe("12"); await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).toBe("21"); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $$(DynamicAnswerPage.labels())[1].getText()).toBe("Percentage of shopping at Aldi"); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with different values, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); - await $$(DynamicAnswerPage.inputs())[0].waitForExist({ timeout }); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await $$(DynamicAnswerPage.inputs())[1].setValue(12); await click(DynamicAnswerPage.submit()); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("12%"); }); it("Given list items have been added and the dynamic answers are submitted, When the summary edit answer link is used for dynamic answer, Then the focus is on correct answer option", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).toBe(true); await click(DynamicAnswerPage.submit()); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[1].$("a").click(); await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).toBe(true); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with answers updated, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); await $$(DynamicAnswerPage.inputs())[0].setValue(21); await click(DynamicAnswerPage.submit()); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[1].getText()).toBe("21%"); }); it("Given list items have been added and the dynamic answers are submitted, When the list items are removed and answers updated, Then they should be displayed correctly on summary", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); - await setMinimumAndGetSectionSummary(timeout); - await $(SectionSummaryPage.supermarketsListRemoveLink(1)).waitForExist({ timeout }); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); await click(DynamicAnswerPage.submit()); await click(DynamicAnswerOnlyPage.submit()); await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).toBe(5); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues).length).toBe(5); }); it("Given list items have been added and the dynamic answers are submitted, When the driving question is changed to 'No' and subsequently changed back to 'Yes', Then all answers should re-appear on summary", async () => { - await addTwoSupermarkets(timeout); + await addTwoSupermarkets(); await $$(DynamicAnswerPage.inputs())[0].setValue(12); await $$(DynamicAnswerPage.inputs())[1].setValue(21); await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.no()).click(); await click(DriverPage.submit()); @@ -157,7 +145,6 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.anySupermarketAnswerEdit()).click(); await $(DriverPage.yes()).click(); await click(DriverPage.submit()); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Tesco"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("12%"); @@ -170,14 +157,14 @@ describe("Dynamic answers list value source", () => { }); it("Given list items have been added, When the dynamic answers are displayed in a separate section, Then the correct answers should be visible", async () => { - await addTwoSupermarketsAndGetToNextSection(timeout); + await addTwoSupermarketsAndGetToNextSection(); await expect(await $$(OnlineShoppingPage.labels())[0].getText()).toBe("Percentage of online shopping at Tesco"); await expect(await $$(OnlineShoppingPage.labels())[1].getText()).toBe("Percentage of online shopping at Aldi"); await expect(await $$(OnlineShoppingPage.labels()).length).toBe(4); }); }); -async function addTwoSupermarkets(timeout) { +async function addTwoSupermarkets() { await $(DriverPage.yes()).click(); await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); @@ -190,10 +177,9 @@ async function addTwoSupermarkets(timeout) { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await $(DynamicAnswerPage.inputs()).waitForExist({ timeout }); } -async function addTwoSupermarketsAndGetToNextSection(timeout) { +async function addTwoSupermarketsAndGetToNextSection() { await $(DriverPage.yes()).click(); await click(DriverPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); @@ -210,15 +196,14 @@ async function addTwoSupermarketsAndGetToNextSection(timeout) { await $$(DynamicAnswerPage.inputs())[1].setValue(21); await $$(DynamicAnswerPage.inputs())[2].setValue(3); await $$(DynamicAnswerPage.inputs())[3].setValue(7); - await setMinimumAndGetSectionSummary(timeout); + await setMinimumAndGetSectionSummary(); await click(SectionSummaryPage.submit()); await click(HubPage.submit()); } -async function setMinimumAndGetSectionSummary(timeout) { +async function setMinimumAndGetSectionSummary() { await click(DynamicAnswerPage.submit()); await $(SetMinimumPage.setMinimum()).setValue(2); await click(SetMinimumPage.submit()); await click(DynamicAnswerOnlyPage.submit()); - await $(SectionSummaryPage.listCollectorGroupContent(2)).waitForExist({ timeout }); } diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 61f12cf4d9..877583943a 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -103,7 +103,6 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await click(DynamicAnswerPage.submit()); await click(DynamicAnswerOnlyPage.submit()); await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); - await $(SectionSummaryPage.groupContent(2)).waitForExist({ timeout: 2000 }); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); }); }); diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index 3ad986fcbd..d587a6f4dd 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -14,7 +14,6 @@ describe("Suggestions", () => { describe("Suggestions", () => { it("Given I open a textfield with a suggestions url that allows multiple suggestions, when I have entered text and picked suggestion from a list, then after typing more text it will show new suggestions", async () => { await browser.openQuestionnaire("test_textfield_suggestions.json"); - const suggestionsList = $("#multiple-country-answer-listbox li"); const suggestionsOption = $("#multiple-country-answer-listbox__option--0"); await $(SuggestionsPage.country()).setValue("United States of America"); @@ -23,16 +22,13 @@ describe("Suggestions", () => { // Browser needs to pause before typing starts to allow for the autosuggest Javascript to initialise await browser.pause(500); await browser.keys("Ita"); - await suggestionsList.waitForExist(); await suggestionsOption.click(); await $(MultipleSuggestionsPage.multipleCountry()).click(); // Browser needs to pause before typing starts to allow for the autosuggest Javascript to initialise await browser.pause(500); await browser.keys(" United"); - await suggestionsList.waitForExist(); await expect(await $$(".ons-js-autosuggest-listbox li").length).not.toBe(0); - // TODO there is an issue with the load-time of the auto-suggest dropdown causing this test to fail. Uncomment when this has been resolved. - // await suggestionsOption.click(); + await suggestionsOption.click(); await click(MultipleSuggestionsPage.submit()); await expect(browser).toHaveUrlContaining(SubmitPage.url()); }); From ef12715428eceb0e797668dc5213d7b79236c31a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 15 Nov 2023 11:40:55 +0000 Subject: [PATCH 326/567] Support for multiple return to answer ids (#1242) --- app/questionnaire/router.py | 13 +- .../contexts/calculated_summary_context.py | 3 + app/views/contexts/summary/answer.py | 24 ++- tests/app/questionnaire/test_router.py | 143 +++++++++++++++++- .../app/views/contexts/summary/test_answer.py | 67 +++++--- ...ulated_summary_overlapping_answers.spec.js | 6 +- 6 files changed, 215 insertions(+), 41 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index 07b0d20522..c1902c597c 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -348,18 +348,23 @@ def _get_return_to_for_calculated_summary( return_to_block_id = ",".join(remaining) if remaining else None # remove first item and return the remaining ones - # Type ignore: return_location.return_to will always be populated at this point - return_to_remaining = ",".join(return_location.return_to.split(",")[1:]) or None # type: ignore + # Type ignore: return_location.return_to and return_location.return_to_answer_id will always be populated at this point + return_to = ",".join(return_location.return_to.split(",")[1:]) or None # type: ignore + anchor, *return_to_answer_ids = return_location.return_to_answer_id.split(",") # type: ignore + return_to_answer_id = ( + ",".join(return_to_answer_ids) if return_to_answer_ids else None + ) return url_for( "questionnaire.block", block_id=block_id, list_name=location.list_name, list_item_id=location.list_item_id, - return_to=return_to_remaining, + return_to=return_to, return_to_block_id=return_to_block_id, return_to_list_item_id=return_location.return_to_list_item_id, - _anchor=return_location.return_to_answer_id, + return_to_answer_id=return_to_answer_id, + _anchor=anchor, ) def _get_return_url_for_inaccessible_location( diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 95e3375c1b..a6c0c61b9f 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -82,6 +82,9 @@ def build_groups_for_section( return_to=return_to, return_to_block_id=return_to_block_id, return_to_list_item_id=self.return_location.return_to_list_item_id, + return_to_answer_id=self.return_location.return_to_answer_id + if self.return_location.return_to == "grand-calculated-summary" + else None, ), ).serialize() for group in section["groups"] diff --git a/app/views/contexts/summary/answer.py b/app/views/contexts/summary/answer.py index ea11a130b2..226ac9c043 100644 --- a/app/views/contexts/summary/answer.py +++ b/app/views/contexts/summary/answer.py @@ -64,17 +64,19 @@ def _build_link( return_location: ReturnLocation, is_in_repeating_section: bool, ) -> str: + return_to_answer_id = self._return_to_answer_id( + return_to=return_location.return_to, + list_item_id=list_item_id, + is_in_repeating_section=is_in_repeating_section, + return_to_answer_id=return_location.return_to_answer_id, + ) return url_for( endpoint="questionnaire.block", list_name=list_name, block_id=block_id, list_item_id=list_item_id, return_to=return_location.return_to, - return_to_answer_id=self._return_to_answer_id( - return_to=return_location.return_to, - list_item_id=list_item_id, - is_in_repeating_section=is_in_repeating_section, - ), + return_to_answer_id=return_to_answer_id, return_to_block_id=return_location.return_to_block_id, return_to_list_item_id=return_location.return_to_list_item_id, _anchor=self.id, @@ -86,16 +88,24 @@ def _return_to_answer_id( return_to: str | None, list_item_id: str | None, is_in_repeating_section: bool, + return_to_answer_id: str | None, ) -> str | None: """ If the summary page using this answer has repeating answers, but it is not in a repeating section, then the answer ids will be suffixed with list item id, so the return to answer id link also needs this to work correctly """ + answer_id = None if return_to: if ( list_item_id and not is_in_repeating_section and not self._original_answer_id # original answer would mean id has already been suffixed ): - return f"{self.id}-{list_item_id}" - return self.id + answer_id = f"{self.id}-{list_item_id}" + else: + answer_id = self.id + + if return_to_answer_id: + answer_id += f",{return_to_answer_id}" + + return answer_id diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index d05d0b9302..f39658b53f 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -771,7 +771,7 @@ def test_return_to_grand_calculated_summary_from_answer( return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", - return_to_answer_id="distance-calculated-summary-1", + return_to_answer_id="q1-a1,distance-calculated-summary-1", return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", ) @@ -784,11 +784,137 @@ def test_return_to_grand_calculated_summary_from_answer( return_to="grand-calculated-summary", block_id="distance-calculated-summary-1", return_to_block_id="distance-grand-calculated-summary", - _anchor="distance-calculated-summary-1", + return_to_answer_id="distance-calculated-summary-1", + _anchor="q1-a1", ) assert expected_previous_url == next_location_url + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary_from_answer_when_multiple_answers(self): + """ + If going from GCS -> CS -> answer -> CS -> GCS this tests going from CS -> GCS having just come from an answer + """ + self.schema = load_schema_from_name( + "test_grand_calculated_summary_overlapping_answers" + ) + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="introduction-section", + block_ids=[ + "introduction-block", + ], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-1", + block_ids=[ + "block-1", + "block-2", + "calculated-summary-1", + "calculated-summary-2", + "block-3", + "calculated-summary-3", + ], + status=CompletionStatus.COMPLETED, + ), + ] + ) + + current_location = Location(section_id="section-1", block_id="block-1") + + routing_path = RoutingPath( + block_ids=[ + "block-1", + "block-2", + "calculated-summary-1", + "calculated-summary-2", + "block-3", + "calculated-summary-3", + ], + section_id="section-1", + ) + + return_location = ReturnLocation( + return_to="calculated-summary,grand-calculated-summary", + return_to_answer_id="q1-a1,calculated-summary-1", + return_to_block_id="calculated-summary-1,grand-calculated-summary-shopping", + ) + + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + + assert ( + next_location_url + == "/questionnaire/calculated-summary-1/?return_to=grand-calculated-summary&return_to_block_id=grand-calculated-summary-shopping&" + "return_to_answer_id=calculated-summary-1#q1-a1" + ) + + @pytest.mark.usefixtures("app") + def test_return_to_grand_calculated_summary_from_answer_when_multiple_answers(self): + """ + If going from GCS -> CS -> answer -> CS -> GCS this tests going from CS -> GCS having just come from an answer + """ + self.schema = load_schema_from_name( + "test_grand_calculated_summary_overlapping_answers" + ) + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="introduction-section", + block_ids=[ + "introduction-block", + ], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-1", + block_ids=[ + "block-1", + "block-2", + "calculated-summary-1", + "calculated-summary-2", + "block-3", + "calculated-summary-3", + ], + status=CompletionStatus.COMPLETED, + ), + ] + ) + + current_location = Location( + section_id="section-1", block_id="calculated-summary-1" + ) + + routing_path = RoutingPath( + block_ids=[ + "block-1", + "block-2", + "calculated-summary-1", + "calculated-summary-2", + "block-3", + "calculated-summary-3", + ], + section_id="section-1", + ) + + return_location = ReturnLocation( + return_to="grand-calculated-summary", + return_to_answer_id="calculated-summary-1", + return_to_block_id="grand-calculated-summary-shopping", + ) + + next_location_url = self.router.get_next_location_url( + current_location, routing_path, return_location + ) + + assert ( + next_location_url + == "/questionnaire/grand-calculated-summary-shopping/#calculated-summary-1" + ) + @pytest.mark.usefixtures("app") def test_return_to_grand_calculated_summary_from_calculated_summary( self, grand_calculated_summary_progress_store, grand_calculated_summary_schema @@ -1030,7 +1156,7 @@ def test_return_to_calculated_summary_from_incomplete_section( ) return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", - return_to_answer_id="first-number-block", + return_to_answer_id="q1-a1,distance-calculated-summary-1", return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", ) # the test is being done as part of a two-step return to but its identical functionally @@ -1045,7 +1171,7 @@ def test_return_to_calculated_summary_from_incomplete_section( "questionnaire.block", return_to="calculated-summary,grand-calculated-summary", return_to_block_id="distance-calculated-summary-1,distance-grand-calculated-summary", - return_to_answer_id="first-number-block", + return_to_answer_id="q1-a1,distance-calculated-summary-1", block_id="second-number-block", ) @@ -1387,7 +1513,7 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", - return_to_answer_id="second-number-block", + return_to_answer_id="q2-a1", return_to_block_id="number-calculated-summary-1,number-grand-calculated-summary", ) previous_location_url = self.router.get_previous_location_url( @@ -1401,7 +1527,7 @@ def test_return_to_grand_calculated_summary_from_answer_incomplete_section( return_to="calculated-summary,grand-calculated-summary", return_to_block_id="number-calculated-summary-1,number-grand-calculated-summary", block_id="first-number-block", - _anchor="second-number-block", + _anchor="q2-a1", ) assert expected_previous_url == previous_location_url @@ -1549,7 +1675,7 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( return_location = ReturnLocation( return_to="calculated-summary,grand-calculated-summary", - return_to_answer_id="calculated-summary-6", + return_to_answer_id="streaming-service-monthly-cost-JSfZqh,calculated-summary-6", return_to_block_id="calculated-summary-6,grand-calculated-summary-3", ) next_location_url = self.router.get_previous_location_url( @@ -1563,7 +1689,8 @@ def test_return_to_grand_calculated_summary_from_repeating_answer( return_to="grand-calculated-summary", block_id="calculated-summary-6", return_to_block_id="grand-calculated-summary-3", - _anchor="calculated-summary-6", + return_to_answer_id="calculated-summary-6", + _anchor="streaming-service-monthly-cost-JSfZqh", ) assert expected_previous_url == next_location_url diff --git a/tests/app/views/contexts/summary/test_answer.py b/tests/app/views/contexts/summary/test_answer.py index 14e22ecef4..4eaa47078e 100644 --- a/tests/app/views/contexts/summary/test_answer.py +++ b/tests/app/views/contexts/summary/test_answer.py @@ -6,20 +6,58 @@ @pytest.mark.usefixtures("app") @pytest.mark.parametrize( - "return_to, return_to_block_id, is_in_repeating_section", + "return_to, return_to_block_id, is_in_repeating_section, return_to_answer_id, query_string", [ - ("section-summary", None, False), - (None, None, False), - ("calculated-summary", "total", False), - ("section-summary", None, True), - (None, None, True), - ("calculated-summary", "total", True), + ( + "section-summary", + None, + False, + "answer-id", + "?return_to=section-summary&return_to_answer_id=answer-id-answer-item-id,answer-id", + ), + (None, None, False, "answer-id", ""), + ( + "calculated-summary", + "total", + False, + "answer-id", + "?return_to=calculated-summary&return_to_answer_id=answer-id-answer-item-id,answer-id&return_to_block_id=total", + ), + ( + "section-summary", + None, + True, + "answer-id-answer-item-id", + "?return_to=section-summary&return_to_answer_id=answer-id,answer-id-answer-item-id", + ), + (None, None, True, "answer-id-answer-item-id", ""), + ( + "calculated-summary", + "total", + True, + "answer-id-answer-item-id", + "?return_to=calculated-summary&return_to_answer_id=answer-id,answer-id-answer-item-id&return_to_block_id=total", + ), + ( + "calculated-summary", + "total", + True, + "calculated-summary-1", + "?return_to=calculated-summary&return_to_answer_id=answer-id,calculated-summary-1&return_to_block_id=total", + ), ], ) -def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): +def test_create_answer( + return_to, + return_to_block_id, + is_in_repeating_section, + return_to_answer_id, + query_string, +): return_location = ReturnLocation( return_to=return_to, return_to_block_id=return_to_block_id, + return_to_answer_id=return_to_answer_id, ) answer = Answer( @@ -37,19 +75,6 @@ def test_create_answer(return_to, return_to_block_id, is_in_repeating_section): assert answer.value == "An answer" assert answer.type == "date" - return_to_answer_id = answer.id - if not is_in_repeating_section: - return_to_answer_id += "-answer-item-id" - - if return_to and return_to_block_id: - query_string = f"?return_to={return_to}&return_to_answer_id={return_to_answer_id}&return_to_block_id={return_to_block_id}" - elif return_to: - query_string = ( - f"?return_to={return_to}&return_to_answer_id={return_to_answer_id}" - ) - else: - query_string = "" - assert ( answer.link == f"/questionnaire/answer-list/answer-item-id/house-type/{query_string}#{answer.id}" diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index 308918051f..2d4041779f 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -40,7 +40,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(GrandCalculatedSummaryShoppingPage.submit()); }); - it("Given I edit an answer that is only used in a single calculated summary, I am routed back to the calculated summary and then the grand calculated summary", async () => { + it("Given I edit an answer that is only used in a single calculated summary, I am routed back to the calculated summary and then the grand calculated summary and the correct fields are focused", async () => { await $(HubPage.summaryRowLink("section-3")).click(); await $(GrandCalculatedSummaryShoppingPage.calculatedSummary2Edit()).click(); await $(CalculatedSummary2Page.q1A2Edit()).click(); @@ -49,6 +49,9 @@ describe("Feature: Grand Calculated Summary", () => { // taken back to calculated summary await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); + await expect(await browser.getUrl()).toContain( + "/questionnaire/calculated-summary-2/?return_to=grand-calculated-summary&return_to_block_id=grand-calculated-summary-shopping&return_to_answer_id=calculated-summary-2#q1-a2", + ); await click(CalculatedSummary2Page.submit()); // then grand calculated summary @@ -56,6 +59,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?.", ); + await expect(await browser.getUrl()).toContain("/questionnaire/grand-calculated-summary-shopping/#calculated-summary-2"); }); it("Given I edit an answer that is used in two calculated summaries, if I edit it from the first calculated summary change link, I taken through each block between the question and the second calculated summary before returning to the grand calculated summary", async () => { From f76b31851c6ed7a2ae3bfc51c5859fe1534ea65c Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 16 Nov 2023 09:16:30 +0000 Subject: [PATCH 327/567] Refactor dependencies splitting answers and lists (#1235) * Split list and answer dependencies * Fix code coverage issue by simplifying utility method * Update docstrings and fix progress capturing for lists * Extra test and minor simplification * Ignore question and content variants in when rule dependencies * Dont enforce key in mapping methods --- app/questionnaire/placeholder_parser.py | 7 +- app/questionnaire/questionnaire_schema.py | 412 +++++++----------- .../questionnaire_store_updater.py | 122 +++--- app/utilities/mappings.py | 15 +- app/views/handlers/list_add_question.py | 6 +- app/views/handlers/list_remove_question.py | 4 +- .../handlers/primary_person_list_collector.py | 18 +- app/views/handlers/question.py | 22 - tests/app/questionnaire/conftest.py | 114 ++++- .../test_questionnaire_schema.py | 129 ++++-- .../test_questionnaire_store_updater.py | 12 +- .../features/validation/sum/dynamic.spec.js | 4 +- 12 files changed, 465 insertions(+), 400 deletions(-) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index d5b6fbd75e..4a3039b648 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -28,7 +28,7 @@ ValueSourceResolver, ValueSourceTypes, ) -from app.utilities.mappings import get_flattened_mapping_values +from app.utilities.mappings import get_flattened_mapping_values, get_values_for_key from app.utilities.types import LocationType, SectionKey if TYPE_CHECKING: @@ -187,8 +187,9 @@ def _get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( ) ) - def _all_value_sources_metadata(self, placeholder: Mapping) -> bool: - sources = self._schema.get_values_for_key(placeholder, key="source") + @staticmethod + def _all_value_sources_metadata(placeholder: Mapping) -> bool: + sources = get_values_for_key("source", data=placeholder) return all(source == "metadata" for source in sources) def _get_value_source_resolver_for_transform( diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 65c5a9d02c..9b9318ac94 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -15,7 +15,11 @@ from app.questionnaire.schema_utils import get_answers_from_question from app.settings import MAX_NUMBER from app.utilities.make_immutable import make_immutable -from app.utilities.mappings import get_flattened_mapping_values, get_mappings_with_key +from app.utilities.mappings import ( + get_flattened_mapping_values, + get_mappings_with_key, + get_values_for_key, +) DEFAULT_LANGUAGE_CODE = "en" @@ -58,8 +62,8 @@ class InvalidSchemaConfigurationException(Exception): @dataclass(frozen=True) -class AnswerDependent: - """Represents a dependent belonging to some answer. +class Dependent: + """Represents a dependent belonging to some answer or list. The dependent can be a reference to another answer, or just the parent block of the answer. If the dependent has an answer_id, then the dependent answer is removed @@ -79,13 +83,11 @@ def __init__( self, questionnaire_json: Mapping, language_code: str = DEFAULT_LANGUAGE_CODE ): self._parent_id_map: dict[str, str] = {} - self._section_dependencies_by_list_name: dict[str, list[str]] = {} self._list_collector_section_ids_by_list_name: dict[ str, list[str] ] = defaultdict(list) - self._answer_dependencies_map: dict[str, set[AnswerDependent]] = defaultdict( - set - ) + self._answer_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) + self._list_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) self._when_rules_section_dependencies_by_section: dict[str, set[str]] = {} self._when_rules_section_dependencies_by_section_for_progress_value_source: defaultdict[ str, OrderedSet[str] @@ -103,6 +105,9 @@ def __init__( self._when_rules_section_dependencies_by_answer: dict[ str, set[str] ] = defaultdict(set) + self._when_rules_section_dependencies_by_list: dict[ + str, set[str] + ] = defaultdict(set) self._placeholder_transform_section_dependencies_by_block: dict[ str, dict[str, set[str]] ] = defaultdict(lambda: defaultdict(set)) @@ -122,10 +127,9 @@ def __init__( self._questions_by_id = self._get_questions_by_id() self._answers_by_id = self._get_answers_by_id() self._dynamic_answer_ids: set[str] = set() - self._list_dependent_block_additional_dependencies: dict[str, set[str]] = {} # Post schema parsing. - self._populate_answer_dependencies() + self._populate_answer_and_list_dependencies() self._populate_when_rules_section_dependencies() self._populate_calculated_summary_section_dependencies() self._populate_min_max_for_numeric_answers() @@ -138,9 +142,13 @@ def placeholder_transform_section_dependencies_by_block( return self._placeholder_transform_section_dependencies_by_block @cached_property - def answer_dependencies(self) -> ImmutableDict[str, set[AnswerDependent]]: + def answer_dependencies(self) -> ImmutableDict[str, set[Dependent]]: return ImmutableDict(self._answer_dependencies_map) + @cached_property + def list_dependencies(self) -> ImmutableDict[str, set[Dependent]]: + return ImmutableDict(self._list_dependencies_map) + @cached_property # Type ignore: make_immutable uses generic types so return type is manually specified def min_and_max_map(self) -> ImmutableDict[str, ImmutableDict[str, int]]: @@ -327,6 +335,11 @@ def list_names_by_list_repeating_block_id(self) -> ImmutableDict[str, str]: def list_collector_repeating_block_ids(self) -> list[str]: return list(self._list_names_by_list_repeating_block_id.keys()) + @cached_property + def list_collector_section_ids_by_list_name(self) -> ImmutableDict[str, tuple[str]]: + # Type ignore: make_immutable is generic so type is manually specified + return make_immutable(self._list_collector_section_ids_by_list_name) # type: ignore + def get_all_when_rules_section_dependencies_for_section( self, section_id: str ) -> set[str]: @@ -341,6 +354,10 @@ def get_all_when_rules_section_dependencies_for_section( return all_section_dependencies + def get_when_rule_section_dependencies_for_list(self, list_name: str) -> set[str]: + """Gets the set of all sections which reference the list in a when rule somewhere""" + return self._when_rules_section_dependencies_by_list.get(list_name, set()) + def _get_sections_by_id(self) -> dict[str, ImmutableDict]: return { section["id"]: section @@ -431,94 +448,57 @@ def _get_answers_by_id(self) -> dict[str, list[ImmutableDict]]: return answers_by_id - def _populate_answer_dependencies(self) -> None: + def _populate_answer_and_list_dependencies(self) -> None: for block in self.get_blocks(): if block["type"] in {"CalculatedSummary", "GrandCalculatedSummary"}: - self._update_answer_dependencies_for_summary(block) + self._update_dependencies_for_summary(block) continue - if block["type"] == "ListCollector" and block.get("repeating_blocks"): - self._update_dependencies_for_list_repeating_blocks(block) - for question in self.get_all_questions_for_block(block): self.update_dependencies_for_dynamic_answers( question=question, block_id=block["id"] ) if question["type"] == "Calculated": - self._update_answer_dependencies_for_calculations( + self._update_dependencies_for_calculations( question["calculations"], block_id=block["id"] ) continue for answer in question.get("answers", []): - self._update_answer_dependencies_for_answer( - answer, block_id=block["id"] - ) + self._update_dependencies_for_answer(answer, block_id=block["id"]) for option in answer.get("options", []): if "detail_answer" in option: - self._update_answer_dependencies_for_answer( + self._update_dependencies_for_answer( option["detail_answer"], block_id=block["id"] ) - def _update_dependencies_for_list_repeating_blocks( - self, list_collector_block: ImmutableDict - ) -> None: - """Blocks depending on repeating questions may need to depend on adding/removing items from the parent list collector, so update the map""" - list_block_dependencies: set[str] = set() - for child in ("add_block", "remove_block"): - if child_block := list_collector_block.get(child): - list_block_dependencies.update( - self.get_answer_ids_for_block(child_block["id"]) - ) - - if list_block_dependencies: - for repeating_block in list_collector_block["repeating_blocks"]: - self._list_dependent_block_additional_dependencies[ - repeating_block["id"] - ] = list_block_dependencies - - def _update_answer_dependencies_for_summary(self, block: ImmutableDict) -> None: + def _update_dependencies_for_summary(self, block: ImmutableDict) -> None: if block["type"] == "CalculatedSummary": - self._update_answer_dependencies_for_calculated_summary_dependency( + self._update_dependencies_for_calculated_summary_dependency( calculated_summary_block=block, dependent_block=block ) elif block["type"] == "GrandCalculatedSummary": - self._update_answer_dependencies_for_grand_calculated_summary(block) + self._update_dependencies_for_grand_calculated_summary(block) - def _update_answer_dependencies_for_calculated_summary_dependency( + def _update_dependencies_for_calculated_summary_dependency( self, *, calculated_summary_block: ImmutableDict, dependent_block: ImmutableDict ) -> None: """ - update all calculated summary answers to be dependencies of the dependent block - - in the case that one of the calculated summary answers is dynamic/repeating, so has multiple answers for a particular list - the calculated summary block needs to depend on the `remove_block` and `add_block` for the list - so that adding/removing items requires re-confirming the calculated summary + For a block that depends on a calculated summary block, add the block as a dependency of each of the calculated summary answers + Similarly if the calculated summary depends on a list, then add the block as a dependency of the list """ calculated_summary_answer_ids = get_calculated_summary_answer_ids( calculated_summary_block ) - answer_dependent = self._get_answer_dependent_for_block_id( - block_id=dependent_block["id"] - ) + dependent = self._get_dependent_for_block_id(block_id=dependent_block["id"]) for answer_id in calculated_summary_answer_ids: - if answer_id in [ - *self._dynamic_answer_ids, - *self._repeating_block_answer_ids, - ]: - # Type ignore: answer_id is valid so block must exist - block_id: str = self.get_block_for_answer_id(answer_id)["id"] # type: ignore - if block_id in self._list_dependent_block_additional_dependencies: - for ( - list_block_id - ) in self._list_dependent_block_additional_dependencies[block_id]: - self._answer_dependencies_map[list_block_id].add( - answer_dependent - ) - self._answer_dependencies_map[answer_id].add(answer_dependent) - - def _update_answer_dependencies_for_grand_calculated_summary( + if list_name := self.get_list_name_for_answer_id(answer_id): + # dynamic/repeating answers means the calculated summary also depends on the list those answers loop over + self._list_dependencies_map[list_name].add(dependent) + self._answer_dependencies_map[answer_id].add(dependent) + + def _update_dependencies_for_grand_calculated_summary( self, grand_calculated_summary_block: ImmutableDict ) -> None: grand_calculated_summary_calculated_summary_ids = ( @@ -529,18 +509,18 @@ def _update_answer_dependencies_for_grand_calculated_summary( for calculated_summary_id in grand_calculated_summary_calculated_summary_ids: # Type ignore: safe to assume block exists calculated_summary_block: ImmutableDict = self.get_block(calculated_summary_id) # type: ignore - self._update_answer_dependencies_for_calculated_summary_dependency( + self._update_dependencies_for_calculated_summary_dependency( calculated_summary_block=calculated_summary_block, dependent_block=grand_calculated_summary_block, ) - def _update_answer_dependencies_for_calculations( + def _update_dependencies_for_calculations( self, calculations: tuple[ImmutableDict, ...], *, block_id: str ) -> None: for calculation in calculations: if source_answer_id := calculation.get("answer_id"): dependents = { - self._get_answer_dependent_for_block_id( + self._get_dependent_for_block_id( block_id=self.get_block_for_answer_id(answer_id)["id"] # type: ignore ) for answer_id in calculation["answers_to_calculate"] @@ -548,52 +528,54 @@ def _update_answer_dependencies_for_calculations( self._answer_dependencies_map[source_answer_id] |= dependents elif isinstance(calculation.get("value"), dict): - self._update_answer_dependencies_for_value_source( + self._update_dependencies_for_value_source( calculation["value"], block_id=block_id, ) - def _update_answer_dependencies_for_answer( + def _update_dependencies_for_answer( self, answer: Mapping, *, block_id: str ) -> None: for key in ["minimum", "maximum"]: value = answer.get(key, {}).get("value") if isinstance(value, dict): - self._update_answer_dependencies_for_value_source( + self._update_dependencies_for_value_source( value, block_id=block_id, ) if dynamic_options_values := answer.get("dynamic_options", {}).get("values"): - self._update_answer_dependencies_for_dynamic_options( + self._update_dependencies_for_dynamic_options( dynamic_options_values, block_id=block_id, answer_id=answer["id"] ) - def _update_answer_dependencies_for_dynamic_options( + def _update_dependencies_for_dynamic_options( self, dynamic_options_values: Mapping, *, block_id: str, answer_id: str, ) -> None: - value_sources = get_mappings_with_key("source", dynamic_options_values) + value_sources = get_mappings_with_key("source", data=dynamic_options_values) for value_source in value_sources: - self._update_answer_dependencies_for_value_source( + self._update_dependencies_for_value_source( value_source, block_id=block_id, answer_id=answer_id ) - def _update_answer_dependencies_for_value_source( + def _update_dependencies_for_value_source( self, value_source: Mapping, *, block_id: str, answer_id: str | None = None, ) -> None: + """ + For a given value source, get the answer ids it consists of, or the list it references, + and add the given block (and optionally answer) as a dependency of those answers or lists + """ if value_source["source"] == "answers": self._answer_dependencies_map[value_source["identifier"]] |= { - self._get_answer_dependent_for_block_id( - block_id=block_id, answer_id=answer_id - ) + self._get_dependent_for_block_id(block_id=block_id, answer_id=answer_id) } if value_source["source"] == "calculated_summary": identifier = value_source["identifier"] @@ -601,69 +583,34 @@ def _update_answer_dependencies_for_value_source( answer_ids_for_block = get_calculated_summary_answer_ids( calculated_summary_block ) - + dependent = self._get_dependent_for_block_id( + block_id=block_id, answer_id=answer_id + ) for answer_id_for_block in answer_ids_for_block: - self._answer_dependencies_map[answer_id_for_block] |= { - self._get_answer_dependent_for_block_id( - block_id=block_id, answer_id=answer_id - ) - } + self._answer_dependencies_map[answer_id_for_block].add(dependent) + # if the answer is repeating, then the calculated summary also depends on the list it loops over + if list_name := self.get_list_name_for_answer_id( + answer_id_for_block + ): + self._list_dependencies_map[list_name].add(dependent) + if value_source["source"] == "list": - self._update_answer_dependencies_for_list_source( - block_id=block_id, list_name=value_source["identifier"] + self._list_dependencies_map[value_source["identifier"]].add( + self._get_dependent_for_block_id(block_id=block_id) ) - def _update_answer_dependencies_for_list_source( - self, *, block_id: str, list_name: str - ) -> None: - """Updates dependencies for a block depending on a list collector - - This method also stores a map of { block_depending_on_list_source -> {add_block, remove_block} }, because: - blocks like dynamic_answers, don't directly need to depend on the add_block/remove_block, - but a block depending on the dynamic answers might (such as a calculated summary) - """ - list_collectors = self.get_list_collectors_for_list( - for_list=list_name, - ) - - for list_collector in list_collectors: - if add_block := self.get_add_block_for_list_collector( # type: ignore - list_collector["id"] - ): - add_block_question = add_block["question"] - answer_ids_for_block = list( - self.get_answers_for_question_by_id(add_block_question) - ) - for block_answer_id in answer_ids_for_block: - self._answer_dependencies_map[block_answer_id] |= { - self._get_answer_dependent_for_block_id( - block_id=block_id, for_list=list_name - ) - if self.is_block_in_repeating_section(block_id) - # non-repeating blocks such as dynamic-answers could depend on the list - else self._get_answer_dependent_for_block_id(block_id=block_id) - } - self._list_dependent_block_additional_dependencies[block_id] = set( - answer_ids_for_block - ) - # removing an item from a list will require any dependent calculated summaries to be re-confirmed, so cache dependencies - if remove_block_id := self.get_remove_block_id_for_list(list_name): - self._list_dependent_block_additional_dependencies[block_id].update( - self.get_answer_ids_for_block(remove_block_id) - ) - - def _get_answer_dependent_for_block_id( + def _get_dependent_for_block_id( self, *, block_id: str, answer_id: str | None = None, for_list: str | None = None, - ) -> AnswerDependent: + ) -> Dependent: section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore if not for_list: for_list = self.get_repeating_list_for_section(section_id) - return AnswerDependent( + return Dependent( block_id=block_id, section_id=section_id, for_list=for_list, @@ -692,14 +639,6 @@ def get_section_ids(self) -> Iterable[str]: def get_section(self, section_id: str) -> ImmutableDict | None: return self._sections_by_id.get(section_id) - def get_section_ids_dependent_on_list(self, list_name: str) -> list[str]: - try: - return self._section_dependencies_by_list_name[list_name] - except KeyError: - section_ids = self._section_ids_associated_to_list_name(list_name) - self._section_dependencies_by_list_name[list_name] = section_ids - return section_ids - def get_submission(self) -> ImmutableDict: schema: ImmutableDict = self.json.get("submission", ImmutableDict({})) return schema @@ -708,47 +647,12 @@ def get_post_submission(self) -> ImmutableDict: schema: ImmutableDict = self.json.get("post_submission", ImmutableDict({})) return schema - def _is_list_name_in_rule( - self, when_rule: Mapping[str, list], list_name: str - ) -> bool: - if not QuestionnaireSchema.has_operator(when_rule): - return False - - operands = self.get_operands(when_rule) - - for operand in operands: - if not isinstance(operand, Mapping): - continue - if "source" in operand: - return bool( - operand.get("source") == "list" - and operand.get("identifier") == list_name - ) - - # Nested rules - if QuestionnaireSchema.has_operator(operand): - return self._is_list_name_in_rule(operand, list_name) - - return False - @staticmethod def get_operands(rules: Mapping) -> Sequence: operator = next(iter(rules)) operands: Sequence = rules[operator] return operands - def _section_ids_associated_to_list_name(self, list_name: str) -> list[str]: - section_ids: list[str] = [] - - for section in self.get_sections(): - ignore_keys = ["question_variants", "content_variants"] - when_rules = self.get_values_for_key(section, "when", ignore_keys) - - rule: Mapping = next(when_rules, {}) - if self._is_list_name_in_rule(rule, list_name): - section_ids.append(section["id"]) - return section_ids - @staticmethod def get_blocks_for_section( section: Mapping, @@ -914,6 +818,20 @@ def is_repeating_answer( or self.is_answer_dynamic(answer_id) ) + def get_list_name_for_answer_id(self, answer_id: str) -> str | None: + """ + if the answer is dynamic or in a repeating block or section, return the name of the list it repeats over, otherwise None. + """ + # Type ignore: safe to assume block exists, same for section below. + block_id: str = self.get_block_for_answer_id(answer_id)["id"] # type: ignore + if self.is_answer_dynamic(answer_id): + return self.get_list_name_for_dynamic_answer(block_id) + if self.is_answer_in_list_collector_repeating_block(answer_id): + return self._list_names_by_list_repeating_block_id[block_id] + if self.is_answer_in_repeating_section(answer_id): + section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore + return self.get_repeating_list_for_section(section_id) + def get_answers_by_answer_id(self, answer_id: str) -> list[ImmutableDict]: """Return answers matching answer id, including all matching answers inside variants @@ -1180,23 +1098,6 @@ def has_operator(rule: Any) -> bool: operator in rule for operator in OPERATION_MAPPING ) - def get_values_for_key( - self, block: Mapping, key: str, ignore_keys: list[str] | None = None - ) -> Generator: - ignore_keys = ignore_keys or [] - for k, v in block.items(): - if k in ignore_keys: - continue - if k == key: - yield v - if isinstance(v, dict): - yield from self.get_values_for_key(v, key, ignore_keys) - elif isinstance(v, (list, tuple)): - for d in v: - # in the case of a when_rule "==": {dict, "Yes"} d could be a string - if isinstance(d, dict): - yield from self.get_values_for_key(d, key, ignore_keys) - def _get_parent_section_id_for_block(self, block_id: str) -> str: parent_block_id = self._parent_id_map[block_id] group_id = self._parent_id_map[parent_block_id] @@ -1244,13 +1145,20 @@ def _populate_when_rules_section_dependencies(self) -> None: Progress section dependencies by section are directly populated in this method. Progress section dependencies by block are populated in the `self._populate_block_dependencies_for_progress_value_source` called here. + + Question variants and content variants don't need including, since the answer ids, block ids, and question ids + remain the same, so a change in the variant, does not impact questionnaire progress. """ progress_section_dependencies = ( self._when_rules_section_dependencies_by_section_for_progress_value_source ) for section in self.get_sections(): - when_rules = self.get_values_for_key(section, "when") + when_rules = get_values_for_key( + "when", + data=section, + ignore_keys=["question_variants", "content_variants"], + ) rules: list = list(when_rules) ( @@ -1295,14 +1203,20 @@ def _populate_block_dependencies_for_progress_value_source( def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( self, current_section_id: str, rule: Mapping - ) -> tuple[set[str], dict[str, dict[str, OrderedSet[str] | DependencyDictType]]]: + ) -> tuple[ + set[str], set[str], dict[str, dict[str, OrderedSet[str] | DependencyDictType]] + ]: """ - For a given rule, returns a set of dependent answer ids and any dependent sections for progress value sources. + For a given rule, returns: + - a set of dependent answer ids + - a set of dependent lists + - any dependent sections for progress value sources. Progress dependencies are keyed both by section and by block e.g. sections: {"section-1": {"section-2"}} blocks: {"section-1": {"block-1": {"section-2"}}} """ - answer_id_list: set[str] = set() + dependent_answer_ids: set[str] = set() + dependent_list_names: set[str] = set() dependencies_ids_for_progress_value_source: dict[ str, dict[str, OrderedSet[str] | DependencyDictType] ] = { @@ -1314,14 +1228,16 @@ def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_f selector: str | None = rule.get("selector") if source == "answers" and identifier: - answer_id_list.add(identifier) + dependent_answer_ids.add(identifier) elif source == "calculated_summary" and identifier: calculated_summary_block = self.get_block(identifier) # Type Ignore: Calculated summary block will exist at this point calculated_summary_answer_ids = get_calculated_summary_answer_ids( calculated_summary_block # type: ignore ) - answer_id_list.update(calculated_summary_answer_ids) + dependent_answer_ids.update(calculated_summary_answer_ids) + elif source == "list" and identifier: + dependent_list_names.add(identifier) elif source == "progress" and identifier: if selector == "section" and identifier != current_section_id: # Type ignore: Added as this will be a set rather than a dict at this point @@ -1342,7 +1258,31 @@ def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_f identifier # type: ignore ].append(current_section_id) - return answer_id_list, dependencies_ids_for_progress_value_source + return ( + dependent_answer_ids, + dependent_list_names, + dependencies_ids_for_progress_value_source, + ) + + def _get_section_dependencies_for_dependent_answers( + self, current_section_id: str, dependent_answer_ids: Iterable[str] + ) -> set[str]: + """ + For a set of answer ids dependent on a rule in the current section, add the current section + as a dependency of the answer and return the set of sections those answers reside in. + """ + section_dependencies: set[str] = set() + # Type Ignore: Added to this method as the block will exist at this point + for answer_id in dependent_answer_ids: + block = self.get_block_for_answer_id(answer_id) # type: ignore + section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore + + if section_id != current_section_id: + self._when_rules_section_dependencies_by_answer[answer_id].add( + current_section_id + ) + section_dependencies.add(section_id) # type: ignore + return section_dependencies def _get_rules_section_dependencies( self, current_section_id: str, rules: Mapping | Sequence @@ -1362,7 +1302,8 @@ def _get_rules_section_dependencies( continue [ - answer_id_list, + dependent_answer_ids, + dependent_list_names, dependencies_for_progress_value_source, ] = self._get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( current_section_id, rule @@ -1375,16 +1316,16 @@ def _get_rules_section_dependencies( dependencies_for_progress_value_source["blocks"] ) - # Type Ignore: Added to this method as the block will exist at this point - for answer_id in answer_id_list: - block = self.get_block_for_answer_id(answer_id) # type: ignore - section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore + section_dependencies |= ( + self._get_section_dependencies_for_dependent_answers( + current_section_id, dependent_answer_ids + ) + ) - if section_id != current_section_id: - self._when_rules_section_dependencies_by_answer[answer_id].add( - current_section_id - ) - section_dependencies.add(section_id) # type: ignore + for list_name in dependent_list_names: + self._when_rules_section_dependencies_by_list[list_name].add( + current_section_id + ) if QuestionnaireSchema.has_operator(rule): ( @@ -1407,51 +1348,28 @@ def _get_rules_section_dependencies( ) def _populate_calculated_summary_section_dependencies(self) -> None: + """ + For each block, find all the calculated summary value source dependencies + and make sure all involved sections are added as dependencies for that block and its section. + + Since calculated summaries can only contain answers from the section they are in, + it is sufficient to check the section for the calculated summary only. + """ for section in self.get_sections(): for block in self.get_blocks_for_section(section): - sources = get_mappings_with_key("source", block, ignore_keys=["when"]) - - calculated_summary_sources = [ - source + sources = get_mappings_with_key( + "source", data=block, ignore_keys=["when"] + ) + section_dependencies: set[str] = { + # Type ignore: Validator ensures a valid identifier so section will exist + self.get_section_id_for_block_id(source["identifier"]) # type: ignore for source in sources if source["source"] == "calculated_summary" - ] - - section_dependencies = ( - self._get_calculated_summary_section_dependencies( - sources=calculated_summary_sources, - ) - ) - + } self.calculated_summary_section_dependencies_by_block[section["id"]][ block["id"] ].update(section_dependencies) - def _get_calculated_summary_section_dependencies( - self, - sources: list[Mapping], - ) -> set[str]: - # Type ignore: Added to this method as the block will exist at this point - section_dependencies: set[str] = set() - - for source in sources: - answer_id_list: list = [] - identifier: str = source["identifier"] - - calculated_summary_block = self.get_block(identifier) # type: ignore - calculated_summary_answer_ids = get_calculated_summary_answer_ids( - calculated_summary_block # type: ignore - ) - answer_id_list.extend(calculated_summary_answer_ids) - - for answer_id in answer_id_list: - block = self.get_block_for_answer_id(answer_id) # type: ignore - section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore - - section_dependencies.add(section_id) # type: ignore - - return section_dependencies - def _get_section_ids_for_answer_ids(self, answer_ids: set[str]) -> OrderedSet[str]: section_dependencies: OrderedSet[str] = OrderedSet() for answer_id in answer_ids: @@ -1491,7 +1409,7 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None: def _populate_placeholder_transform_section_dependencies(self) -> None: for block in self.get_blocks(): - transforms = get_mappings_with_key("transform", block) + transforms = get_mappings_with_key("transform", data=block) placeholder_answer_ids = { item["identifier"] for transform in transforms @@ -1518,7 +1436,7 @@ def update_dependencies_for_dynamic_answers( self.dynamic_answers_parent_block_ids.add(block_id) for answer in dynamic_answers["answers"]: value_source = dynamic_answers["values"] - self._update_answer_dependencies_for_value_source( + self._update_dependencies_for_value_source( value_source, block_id=block_id, answer_id=answer["id"], @@ -1526,7 +1444,7 @@ def update_dependencies_for_dynamic_answers( self._dynamic_answer_ids.add(answer["id"]) - self._update_answer_dependencies_for_answer(answer, block_id=block_id) + self._update_dependencies_for_answer(answer, block_id=block_id) def is_summary_with_calculation(summary_type: str) -> bool: @@ -1539,7 +1457,7 @@ def get_sources_for_type_from_data( data: MultiDict | Mapping | Sequence, ignore_keys: list | None = None, ) -> list: - sources = get_mappings_with_key("source", data, ignore_keys=ignore_keys) + sources = get_mappings_with_key(key="source", data=data, ignore_keys=ignore_keys) return [source for source in sources if source["source"] == source_type] diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 079bf67785..a1bfa9f006 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -10,7 +10,7 @@ from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey -from app.questionnaire.questionnaire_schema import AnswerDependent +from app.questionnaire.questionnaire_schema import Dependent from app.questionnaire.router import Router from app.utilities.types import DependentSection, LocationType @@ -159,27 +159,28 @@ def remove_list_item_data(self, list_name: str, list_item_id: str) -> None: self._update_relationship_question_completeness(list_name) self._progress_store.remove_progress_for_list_item_id(list_item_id=list_item_id) - self._capture_dependencies_for_list_item_removal(list_name) - def _capture_dependencies_for_list_item_removal(self, list_name: str) -> None: + def capture_dependencies_for_list_change(self, list_name: str) -> None: """ - If an item is removed from a list, dependencies of any child blocks need to be captured and their progress updated. - (E.g. dynamic-answers depending on a child block could have validation, so need revisiting if an item is removed) + Captures the dependencies when an item is added to or removed from the given list. + Any list collector sections will be affected by the change as well as other sections using when rules """ - for list_collector in self._schema.get_list_collectors_for_list( - for_list=list_name - ): - child_blocks = ( - list_collector.get("add_block"), - list_collector.get("remove_block"), - *list_collector.get("repeating_blocks", []), - ) - for child_block in child_blocks: - if child_block: - for answer_id in self._schema.get_answer_ids_for_block( - child_block["id"] - ): - self._capture_block_dependencies_for_answer(answer_id) + self._capture_block_dependencies_for_list(list_name) + section_ids = self._schema.get_when_rule_section_dependencies_for_list( + list_name + ) + section_ids.update( + self._schema.list_collector_section_ids_by_list_name[list_name] + ) + + for section_key in self.started_section_keys(section_ids=section_ids): + # Only add sections which are repeated sections for this list, or the section in which this list is collected + # Prevents list item progresses being added as dependants as these are captured by started_section_keys(section_ids=section_ids) + if section_key.list_item_id and not self._schema.get_repeat_for_section( + section_key.section_id + ): + continue + self.dependent_sections.add(DependentSection(**section_key.to_dict())) def _get_relationship_answers_for_list_name( self, list_name: str @@ -280,9 +281,25 @@ def _update_answer( ) ) + def _capture_block_dependencies_for_list(self, list_name: str) -> None: + """Captures a list of block ids that are dependents of the given list""" + dependencies: set[Dependent] = self._schema.list_dependencies.get( + list_name, set() + ) + self._capture_block_dependencies(dependencies) + def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: - """Captures a unique list of block ids that are dependents of the provided answer id. + """Captures a unique list of block ids that are dependents of the provided answer id.""" + dependencies: set[Dependent] = self._schema.answer_dependencies.get( + answer_id, set() + ) + is_repeating_answer = self._schema.is_answer_in_repeating_section(answer_id) + self._capture_block_dependencies(dependencies, is_repeating_answer) + def _capture_block_dependencies( + self, dependencies: set[Dependent], is_repeating_answer: bool | None = None + ) -> None: + """ The block_ids are mapped to the section key. Dependencies in a repeating section use the list items for the repeating list when creating the section key. @@ -290,12 +307,6 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: multiples times, as you may have multiple dependencies for one block which may also apply to each item in the list. However, when updating the progress store, the block ids are checked to ensure they exist in the progress store. """ - dependencies: set[AnswerDependent] = self._schema.answer_dependencies.get( - answer_id, set() - ) - - is_repeating_answer = self._schema.is_answer_in_repeating_section(answer_id) - for dependency in dependencies: for list_item_id in self._get_list_item_ids_for_dependency( dependency, is_repeating_answer @@ -304,27 +315,24 @@ def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: self._answer_store.remove_answer( dependency.answer_id, list_item_id=list_item_id ) - self.dependent_block_id_by_section_key[ SectionKey(dependency.section_id, list_item_id) ].add(dependency.block_id) def _get_list_item_ids_for_dependency( - self, dependency: AnswerDependent, is_repeating_answer: bool | None = False + self, dependency: Dependent, is_repeating_answer: bool | None = False ) -> list[str] | list[None]: + """ + Gets the list item ids that relate to the dependency. If the dependency is repeating, and the dependent is also repeating + then we must be in that repeating section, so the only relevant list item id is the current one. + If the dependent is not repeating, but the dependency is, then all list item ids need including. + """ if dependency.for_list: - list_item_ids: list[str] | list[None] - if is_repeating_answer: - # If the source answer is repeating, then we must be in the current repeating section. - # A repeating answer should only ever be depended on by itself. - list_item_ids = [self._current_location.list_item_id] # type: ignore - else: - list_item_ids = self._list_store[dependency.for_list].items - else: - list_item_ids = [None] - - return list_item_ids + # Type ignore: in this scenario the list item id must exist + return [self._current_location.list_item_id] # type: ignore + return self._list_store[dependency.for_list].items + return [None] def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: """Captures a unique list of section ids that are dependents of the provided answer id.""" @@ -344,30 +352,29 @@ def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: else: self.dependent_sections.add(DependentSection(section_id)) - def _capture_section_dependencies_progress_value_source_for_current_section( + def _capture_section_dependencies_progress_value_source_for_section( self, + section_id: str, ) -> None: """ - Captures a unique list of section ids that are dependents of the current section, for progress value sources. + Captures a unique list of section ids that are dependents of the provided section, for progress value sources. """ dependent_sections: Iterable = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( - self._current_location.section_id, set() + section_id, set() ) self._update_section_dependencies(dependent_sections) - def _capture_section_dependencies_progress_value_source_for_current_block( - self, + def _capture_section_dependencies_progress_value_source_for_block( + self, *, section_id: str, block_id: str ) -> None: """ - Captures a unique list of section ids that are dependents of the current block, for progress value sources. + Captures a unique list of section ids that are dependents of the provided block, for progress value sources. """ - # Type ignore: Added as block_id will exist at this point dependent_sections: Iterable = self._schema.when_rules_block_dependencies_by_section_for_progress_value_source.get( - self._current_location.section_id, {} + section_id, {} ).get( - self._current_location.block_id, set() # type: ignore + block_id, set() ) - self._update_section_dependencies(dependent_sections) def _update_section_dependencies(self, dependent_sections: Iterable) -> None: @@ -401,11 +408,22 @@ def update_answers( if self._update_answer(answer_id_to_use, list_item_id_to_use, answer_value): self._capture_section_dependencies_for_answer(answer_id_to_use) self._capture_block_dependencies_for_answer(answer_id_to_use) - self.capture_progress_section_dependencies() + + if self._answer_store.is_dirty: + self.capture_progress_section_dependencies() def capture_progress_section_dependencies(self) -> None: - self._capture_section_dependencies_progress_value_source_for_current_block() - self._capture_section_dependencies_progress_value_source_for_current_section() + """ + Captures a unique list of section ids that are dependents of the current section or block, for progress value sources. + """ + self._capture_section_dependencies_progress_value_source_for_section( + self._current_location.section_id + ) + # Type ignore: block id will exist when at any time this is called + self._capture_section_dependencies_progress_value_source_for_block( + section_id=self._current_location.section_id, + block_id=self._current_location.block_id, # type: ignore + ) def update_progress_for_dependent_sections(self) -> None: """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. diff --git a/app/utilities/mappings.py b/app/utilities/mappings.py index f0dd2e17fb..efbe0b1aee 100644 --- a/app/utilities/mappings.py +++ b/app/utilities/mappings.py @@ -12,14 +12,16 @@ def get_flattened_mapping_values( def get_mappings_with_key( # noqa: C901 pylint: disable=too-complex - key: str, data: Mapping | Sequence, ignore_keys: list[str] | None = None + key: str, *, data: Mapping | Sequence, ignore_keys: list[str] | None = None ) -> Generator[Mapping, None, None]: ignore_keys = ignore_keys or [] def _handle_sequence(value: Sequence) -> Generator[Mapping, None, None]: for element in value: if isinstance(element, Mapping): - yield from get_mappings_with_key(key, element, ignore_keys) + yield from get_mappings_with_key( + key, data=element, ignore_keys=ignore_keys + ) if isinstance(data, Sequence): yield from _handle_sequence(data) @@ -32,6 +34,13 @@ def _handle_sequence(value: Sequence) -> Generator[Mapping, None, None]: if k in ignore_keys: continue if isinstance(v, Mapping): - yield from get_mappings_with_key(key, v, ignore_keys) + yield from get_mappings_with_key(key, data=v, ignore_keys=ignore_keys) if isinstance(v, Sequence): yield from _handle_sequence(v) + + +def get_values_for_key( + key: str, *, data: Mapping | Sequence, ignore_keys: list[str] | None = None +) -> Generator: + for mapping in get_mappings_with_key(key, data=data, ignore_keys=ignore_keys): + yield mapping[key] diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index e86ad59983..8ab905660c 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -38,7 +38,7 @@ def get_next_location_url(self) -> str: def handle_post(self) -> None: # Ensure the section is in progress when user adds an item self._list_item_id = self.questionnaire_store_updater.add_list_item( - self.parent_block["for_list"] + list_name := self.parent_block["for_list"] ) # Clear the answer from the confirmation question on the list collector question @@ -54,9 +54,7 @@ def handle_post(self) -> None: self.questionnaire_store_updater.update_answers( self.form.data, self._list_item_id ) - - self.capture_dependent_sections_for_list(self.parent_block["for_list"]) - + self.questionnaire_store_updater.capture_dependencies_for_list_change(list_name) return super().handle_post() def _resolve_custom_page_title_vars(self) -> dict[str, int]: diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index cb3d9f113f..d29207bd13 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -32,7 +32,9 @@ def handle_post(self) -> None: list_name, self._current_location.list_item_id, # type: ignore ) - self.capture_dependent_sections_for_list(self.parent_block["for_list"]) + self.questionnaire_store_updater.capture_dependencies_for_list_change( + list_name + ) return super().handle_post() diff --git a/app/views/handlers/primary_person_list_collector.py b/app/views/handlers/primary_person_list_collector.py index fa8c31c209..ebb10e563b 100644 --- a/app/views/handlers/primary_person_list_collector.py +++ b/app/views/handlers/primary_person_list_collector.py @@ -35,21 +35,23 @@ def handle_post(self) -> None: self._primary_person_id = ( self.questionnaire_store_updater.add_primary_person(list_name) ) - - self.capture_dependent_sections_for_list(list_name) + self.questionnaire_store_updater.capture_dependencies_for_list_change( + list_name + ) self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self.questionnaire_store_updater.update_progress_for_dependent_sections() self.questionnaire_store_updater.save() else: - self.questionnaire_store_updater.remove_primary_person(list_name) - - self.questionnaire_store_updater.update_same_name_items( - list_name, self.rendered_block.get("same_name_answer_ids") - ) # This method could determine the current section's status incorrectly, as # the call to update the answer store takes place in # `super().handle_post()`. The section status will eventually get # determined correctly when the parent class' `update_section_status` # method is called. - self.capture_dependent_sections_for_list(list_name) + self.questionnaire_store_updater.remove_primary_person(list_name) + self.questionnaire_store_updater.update_same_name_items( + list_name, self.rendered_block.get("same_name_answer_ids") + ) + self.questionnaire_store_updater.capture_dependencies_for_list_change( + list_name + ) super().handle_post() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 81c362f453..9952dcb058 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -9,7 +9,6 @@ from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.questionnaire.variants import transform_variants -from app.utilities.types import DependentSection from app.views.contexts import ListContext from app.views.contexts.question import build_question_context from app.views.handlers.block import BlockHandler @@ -221,27 +220,6 @@ def get_return_to_hub_url(self) -> str | None: ): return url_for(".get_questionnaire") - def capture_dependent_sections_for_list(self, list_name: str) -> None: - section_ids = self._schema.get_section_ids_dependent_on_list(list_name) - section_ids.append(self.current_location.section_id) - - for ( - section_id, - list_item_id, - ) in self.questionnaire_store_updater.started_section_keys( - section_ids=section_ids - ): - # Only add sections which are repeated sections for this list, or the section in which this list is collected - # Prevents list item progresses being added as dependants as these are captured by started_section_keys(section_ids=section_ids) - if section_id == self.current_location.section_id and list_item_id: - continue - self.questionnaire_store_updater.dependent_sections.add( - DependentSection( - section_id, - list_item_id, - ) - ) - def clear_radio_answers(self) -> None: answer_ids_to_remove = [] for answer in self.rendered_block["question"]["answers"]: diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 8bdea2989b..5ffc9d9f54 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -203,8 +203,8 @@ def list_collector_variant_schema(): "id": "section", "groups": [ { - "id": "group", - "title": "List", + "id": "when-group", + "title": "When Group", "blocks": [ { "type": "Question", @@ -225,7 +225,18 @@ def list_collector_variant_schema(): } ], }, - }, + } + ], + } + ], + }, + { + "id": "list-section", + "groups": [ + { + "id": "list-group", + "title": "List", + "blocks": [ { "id": "block1", "type": "ListCollector", @@ -437,7 +448,79 @@ def list_collector_variant_schema(): ], } ], - } + }, + { + "id": "content-section", + "groups": [ + { + "id": "group-content", + "blocks": [ + { + "type": "Interstitial", + "id": "household-occupancy", + "content_variants": [ + { + "content": { + "title": "Household Occupancy", + "contents": [ + { + "description": "According to your answer this household is occupied" + } + ], + }, + "when": { + ">": [ + { + "source": "list", + "identifier": "people", + "selector": "count", + }, + 0, + ] + }, + }, + { + "content": { + "title": "Household Occupancy", + "contents": [ + { + "description": "According to your answer this household is unoccupied" + } + ], + }, + "when": { + "==": [ + { + "source": "list", + "identifier": "people", + "selector": "count", + }, + 0, + ] + }, + }, + ], + }, + { + "type": "Question", + "id": "block-occupancy", + "question": { + "answers": [ + { + "id": "answer-occupancy", + "mandatory": True, + "type": "General", + } + ], + "id": "question-occupancy", + "title": {"text": "Does anyone else live here?"}, + "type": "General", + }, + }, + ], + } + ], + }, ] } @@ -714,6 +797,29 @@ def sections_dependent_on_list_schema(): } ], }, + { + "id": "section6", + "enabled": { + "when": { + ">": [ + {"count": [{"source": "list", "identifier": "list"}]}, + 0, + ] + } + }, + "groups": [ + { + "id": "group6", + "blocks": [ + { + "type": "Question", + "id": "block6", + "question": {}, + } + ], + } + ], + }, ] } diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 5df0784446..062a8b114b 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -4,7 +4,7 @@ from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict -from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema +from app.questionnaire.questionnaire_schema import Dependent, QuestionnaireSchema from app.utilities.schema import load_schema_from_name @@ -221,14 +221,6 @@ def test_get_all_questions_for_block_question(): assert all_questions[0]["answers"][0]["id"] == "answer1" -def test_get_section_ids_by_list_name(sections_dependent_on_list_schema): - schema = QuestionnaireSchema(sections_dependent_on_list_schema) - when_blocks = schema.get_section_ids_dependent_on_list("list") - - assert len(when_blocks) == 2 - assert ["section2", "section4"] == when_blocks - - def test_get_all_questions_for_block_question_variants(): block = { "id": "block1", @@ -409,7 +401,7 @@ def test_answer_dependencies_for_calculated_question_non_repeating( assert schema.answer_dependencies == { "total-employees-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="employees-breakdown-block", for_list=None, @@ -417,7 +409,7 @@ def test_answer_dependencies_for_calculated_question_non_repeating( ) }, "total-turnover-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="turnover-breakdown-block", for_list=None, @@ -434,7 +426,7 @@ def test_answer_dependencies_for_calculated_question_repeating( assert schema.answer_dependencies == { "entertainment-spending-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="second-spending-breakdown-block", for_list="people", @@ -442,7 +434,7 @@ def test_answer_dependencies_for_calculated_question_repeating( ) }, "total-spending-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="spending-breakdown-block", for_list="people", @@ -459,13 +451,13 @@ def test_answer_dependencies_for_calculated_question_value_source( assert schema.answer_dependencies == { "breakdown-1": { - AnswerDependent( + Dependent( section_id="default-section", block_id="number-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="second-breakdown-block", for_list=None, @@ -473,13 +465,13 @@ def test_answer_dependencies_for_calculated_question_value_source( ), }, "breakdown-2": { - AnswerDependent( + Dependent( section_id="default-section", block_id="number-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="second-breakdown-block", for_list=None, @@ -487,7 +479,7 @@ def test_answer_dependencies_for_calculated_question_value_source( ), }, "total-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="breakdown-block", for_list=None, @@ -504,13 +496,13 @@ def test_answer_dependencies_for_calculated_summary( expected_dependencies = { "first-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -518,13 +510,13 @@ def test_answer_dependencies_for_calculated_summary( ), }, "second-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -532,13 +524,13 @@ def test_answer_dependencies_for_calculated_summary( ), }, "second-number-answer-also-in-total": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -546,13 +538,13 @@ def test_answer_dependencies_for_calculated_summary( ), }, "third-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -560,13 +552,13 @@ def test_answer_dependencies_for_calculated_summary( ), }, "fourth-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -574,13 +566,13 @@ def test_answer_dependencies_for_calculated_summary( ), }, "fourth-and-a-half-number-answer-also-in-total": { - AnswerDependent( + Dependent( section_id="default-section", block_id="currency-total-playback", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="set-min-max-block", for_list=None, @@ -588,7 +580,7 @@ def test_answer_dependencies_for_calculated_summary( ), }, "second-number-answer-unit-total": { - AnswerDependent( + Dependent( section_id="default-section", block_id="unit-total-playback", for_list=None, @@ -596,7 +588,7 @@ def test_answer_dependencies_for_calculated_summary( ) }, "third-and-a-half-number-answer-unit-total": { - AnswerDependent( + Dependent( section_id="default-section", block_id="unit-total-playback", for_list=None, @@ -604,7 +596,7 @@ def test_answer_dependencies_for_calculated_summary( ) }, "fifth-percent-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="percentage-total-playback", for_list=None, @@ -612,7 +604,7 @@ def test_answer_dependencies_for_calculated_summary( ) }, "sixth-percent-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="percentage-total-playback", for_list=None, @@ -620,7 +612,7 @@ def test_answer_dependencies_for_calculated_summary( ) }, "fifth-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="number-total-playback", for_list=None, @@ -628,7 +620,7 @@ def test_answer_dependencies_for_calculated_summary( ) }, "sixth-number-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="number-total-playback", for_list=None, @@ -645,7 +637,7 @@ def test_answer_dependencies_for_min_max(numbers_schema): assert schema.answer_dependencies == { "set-minimum": { - AnswerDependent( + Dependent( section_id="default-section", block_id="test-min-max-block", for_list=None, @@ -653,13 +645,13 @@ def test_answer_dependencies_for_min_max(numbers_schema): ) }, "set-maximum": { - AnswerDependent( + Dependent( section_id="currency-section", block_id="second-number-block", for_list=None, answer_id=None, ), - AnswerDependent( + Dependent( section_id="default-section", block_id="test-min-max-block", for_list=None, @@ -667,7 +659,7 @@ def test_answer_dependencies_for_min_max(numbers_schema): ), }, "test-range": { - AnswerDependent( + Dependent( section_id="default-section", block_id="detail-answer-block", for_list=None, @@ -675,7 +667,7 @@ def test_answer_dependencies_for_min_max(numbers_schema): ) }, "first-number-answer": { - AnswerDependent( + Dependent( section_id="currency-section", block_id="second-number-block", for_list=None, @@ -692,13 +684,13 @@ def test_answer_dependencies_for_dynamic_options( assert schema.answer_dependencies == { "injury-sustained-answer": { - AnswerDependent( + Dependent( section_id="injury-sustained-section", block_id="most-serious-injury", for_list=None, answer_id="most-serious-injury-answer", ), - AnswerDependent( + Dependent( section_id="injury-sustained-section", block_id="healed-the-quickest", for_list=None, @@ -715,25 +707,25 @@ def test_answer_dependencies_for_dynamic_options_function_driven( assert schema.answer_dependencies == { "reference-date-answer": { - AnswerDependent( + Dependent( section_id="default-section", block_id="dynamic-mutually-exclusive", for_list=None, answer_id="dynamic-mutually-exclusive-dynamic-answer", ), - AnswerDependent( + Dependent( section_id="default-section", block_id="dynamic-checkbox", for_list=None, answer_id="dynamic-checkbox-answer", ), - AnswerDependent( + Dependent( section_id="default-section", block_id="dynamic-dropdown", for_list=None, answer_id="dynamic-dropdown-answer", ), - AnswerDependent( + Dependent( section_id="default-section", block_id="dynamic-radio", for_list=None, @@ -743,6 +735,25 @@ def test_answer_dependencies_for_dynamic_options_function_driven( } +def test_list_dependencies_for_calculated_summary_with_repeating_answers(): + """ + Tests list dependencies for list value sources, calculated summaries involving a repeat + and calculated summary value sources where the calculated summary includes a repeating answer. + """ + schema = load_schema_from_name( + "test_new_calculated_summary_repeating_and_static_answers" + ) + + assert schema.list_dependencies == { + "supermarkets": { + Dependent(section_id="section-1", block_id="dynamic-answer"), + Dependent(section_id="section-1", block_id="calculated-summary-spending"), + Dependent(section_id="section-1", block_id="calculated-summary-visits"), + Dependent(section_id="section-2", block_id="supermarket-transport"), + } + } + + def test_when_rules_section_dependencies_by_section( skipping_section_dependencies_schema, ): @@ -804,6 +815,17 @@ def test_when_rules_section_dependencies_new_calculated_summary( } == schema.when_rules_section_dependencies_by_answer +def test_when_rule_section_dependencies_for_list(sections_dependent_on_list_schema): + """Tests when rule dependencies for lists when used in a section, a block, and nested in a conditional question""" + schema = QuestionnaireSchema(sections_dependent_on_list_schema) + + assert schema.get_when_rule_section_dependencies_for_list("list") == { + "section2", + "section4", + "section6", + } + + def test_progress_block_dependencies( progress_block_dependencies_schema, ): @@ -947,3 +969,16 @@ def test_get_block_for_answer_id_returns_repeating_block_for_repeating_block_ans assert block1["id"] == "companies-repeating-block-1" assert block2["id"] == "companies-repeating-block-2" + + +def test_when_rule_dependencies_dont_include_variants(list_collector_variant_schema): + """ + Since question variants and content variants still have the same question ids, answer ids, etc. + A change to the variant does not affect progress, + therefore section progress does not need to be re-evaluated when a when rule reference is updated + This test ensures that when rule dependencies don't include keys within variants + """ + schema = QuestionnaireSchema(list_collector_variant_schema) + assert not schema.when_rules_section_dependencies_by_answer + assert not schema.when_rules_section_dependencies_by_section + assert not schema.get_when_rule_section_dependencies_for_list("people") diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index 43155e5993..bd4283db26 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -13,7 +13,7 @@ from app.data_models.progress import ProgressDict from app.data_models.progress_store import ProgressStore from app.questionnaire.location import Location -from app.questionnaire.questionnaire_schema import AnswerDependent, QuestionnaireSchema +from app.questionnaire.questionnaire_schema import Dependent, QuestionnaireSchema from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdater from app.utilities.schema import load_schema_from_name from app.utilities.types import DependentSection, SectionKey @@ -66,7 +66,7 @@ def test_update_dynamic_answers( ] mock_empty_schema.answer_dependencies = { "supermarket-name": { - AnswerDependent( + Dependent( section_id="section", block_id="dynamic-answer", for_list="supermarkets", @@ -537,7 +537,7 @@ def test_update_same_name_items( def get_answer_dependencies(for_list=None): return { "total-employees-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="employees-breakdown-block", for_list=for_list, @@ -545,7 +545,7 @@ def get_answer_dependencies(for_list=None): ) }, "total-turnover-answer": { - AnswerDependent( + Dependent( section_id="breakdown-section", block_id="turnover-breakdown-block", for_list=for_list, @@ -724,7 +724,7 @@ def test_update_answers_with_answer_dependents( mock_schema.get_answer_ids_for_question.return_value = ["first-answer"] mock_schema.answer_dependencies = { "first-answer": { - AnswerDependent( + Dependent( section_id="section", block_id="second-block", for_list=None, @@ -813,7 +813,7 @@ def test_update_repeating_answers_with_answer_dependents( mock_schema.get_answer_ids_for_question.return_value = ["first-answer"] mock_schema.answer_dependencies = { "first-answer": { - AnswerDependent( + Dependent( section_id="section", block_id="second-block", for_list="list-name", diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 877583943a..4e103b60e5 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -94,14 +94,12 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ }); }); describe("Given I add list items and fill all the dynamic answers", () => { - it("When I continue and edit existing list item, Then I should be revisiting dynamic answers which should be updated to reflect the changes", async () => { + it("When I continue and edit existing list item, Then I should return straight to the summary because the dynamic answers do not depend on the supermarket name", async () => { await addTwoSupermarkets(); await fillDynamicAnswers(); await $(SectionSummaryPage.supermarketsListEditLink(1)).click(); await $(ListCollectorEditPage.supermarketName()).setValue("Aldi"); await click(ListCollectorEditPage.submit()); - await click(DynamicAnswerPage.submit()); - await click(DynamicAnswerOnlyPage.submit()); await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); }); From dd4a3b9f0b428b8e32ea9721f278f443638199e6 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:05:48 +0000 Subject: [PATCH 328/567] Update python packages to resolve warnings (#1255) --- Pipfile | 4 +- Pipfile.lock | 2245 ++++++++++---------- app/translations/messages.pot | 249 +-- package.json | 8 +- tests/app/conftest.py | 3 +- tests/app/submitter/conftest.py | 2 +- tests/app/utilities/test_schema.py | 12 +- tests/integration/integration_test_case.py | 2 +- yarn.lock | 1001 +++++---- 9 files changed, 1842 insertions(+), 1684 deletions(-) diff --git a/Pipfile b/Pipfile index e8420b8a72..911d87b829 100644 --- a/Pipfile +++ b/Pipfile @@ -24,7 +24,7 @@ moto = "*" freezegun = "*" pytest-xdist = "*" fakeredis = "*" -mypy = "*" +mypy = "==1.5.1" black = "*" pytest-flask = "*" pytest = "*" @@ -45,7 +45,7 @@ flask = "*" flask-babel = "*" flask-login = "*" flask-wtf = "*" -gevent = { version = "==23.7.0", platform_python_implementation = "=='CPython'" } +gevent = { version = "==23.9.0", platform_python_implementation = "=='CPython'" } google-cloud-datastore = "*" grpcio = "*" gunicorn = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 6a0dcdec38..41062d7aa3 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "93bae4a3d9d1e963132a771d87f74d22c50452836a32d4b5f620797e209c4b71" + "sha256": "a95c972a7f46999f235a45301b06d933f74b856dcf39858ee8251dc35ddae14a" }, "pipfile-spec": 6, "requires": { @@ -18,37 +18,37 @@ "default": { "babel": { "hashes": [ - "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610", - "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455" + "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900", + "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed" ], "markers": "python_version >= '3.7'", - "version": "==2.12.1" + "version": "==2.13.1" }, "blinker": { "hashes": [ - "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", - "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.6.2" + "markers": "python_version >= '3.8'", + "version": "==1.7.0" }, "boto3": { "hashes": [ - "sha256:8149f17587c68e556743018f213f00ece81a0f021adb9b9b697a2ee8802583d7", - "sha256:8860ab54a26d1d596d64fc9de7e40c4d7c53c100311208cbd90d9272c3385513" + "sha256:1d10691911c4b8b9443d3060257ba32b68b6e3cad0eebbb9f69fd1c52a78417f", + "sha256:489c4967805b677b7a4030460e4c06c0903d6bc0f6834453611bf87efbd8d8a3" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.51" + "version": "==1.28.83" }, "botocore": { "hashes": [ - "sha256:8e133add22f07b55d21e14176b97b82e993d5f9aca70f5b0cd0908cb105ba53a", - "sha256:91dfb38801d45214875a892bd1e908fc7a894c2ed170bacd67e6929af72f2bd2" + "sha256:40914b0fb28f13d709e1f8a4481e278350b77a3987be81acd23715ec8d5fedca", + "sha256:c742069e8bfd06d212d712228258ff09fb481b6ec02358e539381ce0fcad065a" ], "markers": "python_version >= '3.7'", - "version": "==1.31.51" + "version": "==1.31.83" }, "brotli": { "hashes": [ @@ -141,12 +141,12 @@ }, "cachetools": { "hashes": [ - "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590", - "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b" + "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2", + "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==5.3.1" + "version": "==5.3.2" }, "certifi": { "hashes": [ @@ -158,153 +158,157 @@ }, "cffi": { "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "version": "==1.15.1" + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" }, "charset-normalizer": { "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.2.0" + "version": "==3.3.2" }, "click": { "hashes": [ @@ -334,32 +338,32 @@ }, "cryptography": { "hashes": [ - "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", - "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", - "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", - "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", - "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", - "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", - "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", - "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", - "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", - "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", - "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", - "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", - "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", - "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", - "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", - "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", - "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", - "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", - "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", - "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", - "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", - "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", - "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.4" + "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", + "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84", + "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", + "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", + "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", + "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", + "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", + "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", + "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", + "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81", + "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", + "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", + "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d", + "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", + "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", + "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", + "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", + "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936", + "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", + "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", + "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", + "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", + "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.5" }, "deprecated": { "hashes": [ @@ -379,30 +383,30 @@ }, "email-validator": { "hashes": [ - "sha256:1ff6e86044200c56ae23595695c54e9614f4a9551e0e393614f764860b3d7900", - "sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c" + "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44", + "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.0.0.post2" + "markers": "python_version >= '3.8'", + "version": "==2.1.0.post1" }, "flask": { "hashes": [ - "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc", - "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" + "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", + "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.3.3" + "version": "==3.0.0" }, "flask-babel": { "hashes": [ - "sha256:be015772c5d7f046f3b99c508dcf618636eb93d21b713b356db79f3e79f69f39", - "sha256:deb3ee272d5adf97f5974ed09ab501243d63e7fb4a047501a00de4bd4aca4830" + "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625", + "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593" ], "index": "pypi", - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==3.1.0" + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==4.0.0" }, "flask-compress": { "hashes": [ @@ -414,12 +418,12 @@ }, "flask-login": { "hashes": [ - "sha256:1ef79843f5eddd0f143c2cd994c1b05ac83c0401dc6234c143495af9a939613f", - "sha256:c0a7baa9fdc448cdd3dd6f0939df72eec5177b2f7abe6cb82fc934d29caac9c3" + "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", + "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.6.2" + "version": "==0.6.3" }, "flask-talisman": { "hashes": [ @@ -431,68 +435,66 @@ }, "flask-wtf": { "hashes": [ - "sha256:41c4244e9ae626d63bed42ae4785b90667b885b1535d5a4095e1f63060d12aa9", - "sha256:7887d6f1ebb3e17bf648647422f0944c9a469d0fcf63e3b66fb9a83037e38b2c" + "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695", + "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.1.1" + "markers": "python_version >= '3.8'", + "version": "==1.2.1" }, "gevent": { "hashes": [ - "sha256:11b9bb0bce45170ff992760385a86e6955ccb88dba4a82a64d5ce9459290d8d6", - "sha256:1234849b0bc4df560924aa92f7c01ca3f310677735fb508a2b0d7a61bb946916", - "sha256:34086bcc1252ae41e1cb81cf13c4a7678031595c12f4e9a1c3d0ab433f20826a", - "sha256:369241d1a6a3fe3ef4eba454b71e0168026560c5344fc4bc37196867041982ac", - "sha256:3e0d76a7848726e0646324a1adc011355dcd91875e7913badd1ada2e5eeb8a6e", - "sha256:5b230007a665d2cf5cf8878c9f56a2b8bacbdc4fe0235afc5269b71cd00528e5", - "sha256:5da07d65dfa23fe419c37cea110bf951b42af6bf3a1fff244043a75c9185dbd5", - "sha256:6a51a8e3cdaa6901e47d56f84cb5f92b1bf3deea920bce69cf7a245df16159ac", - "sha256:6b3dd449c80814357f6568eb095a2be2421b805d59fa97c65094707e04a181f9", - "sha256:6bd9ea1b5fbdc7e5921a9e515f34a450eb3927a902253a33caedcce2d19d7d96", - "sha256:746a1e12f280dab07389e6709164b1e1a6caaf50493ea5b1dcaa73cff005174c", - "sha256:769e8811ded08fe7d8b09ad8ebb72d47aecc112411e0726e7296b7ed187ed629", - "sha256:76ca6f893953ab898ebbff5d772103318a85044e55d0bad401d6b49d71bb76e7", - "sha256:83b6d61a8e9da25edb304ca7fba19ee57bb1ffa801f9df3e668bfed7bb8386cb", - "sha256:8c284390f0f6d0b5be3bf805fa8e0ae1329065f2b0ac5af5423c67183197deb8", - "sha256:919423e803939726c99ab2d29ea46b8676af549cee72d263f2b24758ec607b2c", - "sha256:94b013587f7c4697d620c129627f7b12d7d9f6e40ab198635891ca2098cd8556", - "sha256:9c7c349aa23d67cf5cc3b2c87aaedcfead976d0577b1cfcd07ffeba63baba79c", - "sha256:a1d2f1e67d04fde47ca2deac89733df28ef3a7ec1d7359a79f57d4778cced16d", - "sha256:a226b42cb9a49580ca7729572a4f8289d1fa28cd2529c9f4eed3e14b995d1c9c", - "sha256:a8f62e8d37913512823923e05607a296389aeb50ccca8a271ae7cedb5b17faeb", - "sha256:add904a7ef960cd4e133e61eb7413982c5e4203928160be1c09752ac06a25e71", - "sha256:aeb1511cf0786152af741c47ee462dac81b57bbd1fbbe08ab562b6c8c9ad75ed", - "sha256:c1dba07207b15b371e50372369edf256a142cb5cdf8599849cbf8660327efa06", - "sha256:c92b837b60e850c50fc6d723d1e363e786d37fd9d51e564e07df52ad5e8a86d4", - "sha256:cea93f4f77badbddc711620cca164ad75c74056603908e621a5ba1b97adbc39c", - "sha256:d0d3630674c1b344b256a298ab1ff43220f840b12af768131b5d74e485924237", - "sha256:debc177e88a8c876cb1a4d974f985d03670177bdc61e1c084a8d525f1a50b12d", - "sha256:dec7b08daf08385fb281b81ec2e7e703243975d867f40ae0a8a3e30b380eb9ea", - "sha256:df4d7be3352126458cc818309ca6a3b678c209b1ae33e56b6975c6a8309f2068", - "sha256:f522b6b015f1bfa9d8d3716ddffb23e3d4a8933df3e4ebf0a29a65a9fa74382b" + "sha256:03d7452ec45c71ef84cacbf199dac582af2948fb37c638aa6e7232fe5c2840f1", + "sha256:06a927940f9348b504888f93c8d85c2d8e2277ffd2105734148c0082ade17c20", + "sha256:117904bdb139adeb3378a0631e81fc5939b54edce16ddf21288bde111ab31d47", + "sha256:1578b5c6da4e88f5cade5682b13635d17231ade6c549d8498cc95776954675c4", + "sha256:169784ef1594ddac0a773e85e5f996f6f8512abdbe248bff16ed8a22fafecadb", + "sha256:17abeca1a8b61a0ce135127f8328265a317efaa3eda047199d65129ca68f0268", + "sha256:1b9ff212cddf245d44651600970c5370c4168f710d4856d8b2350d734dfff1a6", + "sha256:1ecd70495749ba1ece8e312e41e98d3371f5aa1e98922b47b3635c8263157938", + "sha256:259438ec75b31b199d99941294e50f705d92d20d59578244a882a57c2f0f52b3", + "sha256:2894d82111ed27b7f80dcbf8c713bb9c9c2c7e8890606cf548f6320fb2ae328f", + "sha256:291c380fd708c5e04d63fcb8427da21600fda202ffe14ba59a454efd1715f8d5", + "sha256:2bb65683df8a0a1427d42985158cee7dad8a3211247d5824302e276d9fd5bf65", + "sha256:3778609919477ae6eccbf3b10308015f5620c428dbf0dbc6cbeb8a135c9d603a", + "sha256:41199f10877f368822058b47aca3276e5a4c453e2c4df814a44f23c1bf059488", + "sha256:436d0e9c1185b15f0204f7e2a687af2fc5307b1fed8ce011b86732ad53edc136", + "sha256:5afcf6402981f33c84567e6a0a8dcf05e52e8c0b03d80b699644c5f251221e02", + "sha256:70bd1e7a6f571837beb190e3a330f289d10ba4d84fa980ffe778b97fb887455c", + "sha256:863a244311e986e924baa1516d821c37fcdb4e091e42f86f9d17d13323893c6d", + "sha256:91e1e96d50dc4a296dce77b9e28c29a7401fbaca6809989b2029266db6172967", + "sha256:951d95f1066fb84f661f2bb2c1d6423855a63922abf3e5bd1001711cfb1d8f38", + "sha256:96a3ce33e77277995436656e6f7712f4dcbe9dafa527f245ebbe3de44fd3d4bb", + "sha256:9b6eecefb35ceaa33693b971d18e98bfc7e63c9488f1b83eddedb8087a567066", + "sha256:b6179e5419044f8e49ab1fd2165391ffd94071362e03a04d2e499a64b619e105", + "sha256:bb645e9f68cb4b009d667507195e07314c594b2dac962ae797bd05bccd4076eb", + "sha256:d55cf22d6b29593d256bad9fade978fbdc998d6f3bf3aa3399a81feaa91b5a86", + "sha256:d76871f16df200c9454d3f9695b6cd97e7b80102fdf3e4b69b1e2900f9bd0eee", + "sha256:e9e3f81cc36eb623e8716515bd7e28f50ccca5fd930ba493275e60d5fbc69786", + "sha256:f44f1d3a05ce1d041d514b8151aec82e4649e5f8f6e2d837eb9bfc2607d7b64d", + "sha256:faad7ed513672083eabe1e45d1dbfbbcd7bde9c0da6ef3598e975128c8aee427" ], "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", - "version": "==23.7.0" + "version": "==23.9.0" }, "google-api-core": { "extras": [ "grpc" ], "hashes": [ - "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a", - "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a" + "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41", + "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952" ], "markers": "python_version >= '3.7'", - "version": "==2.11.1" + "version": "==2.14.0" }, "google-auth": { "hashes": [ - "sha256:2cec41407bd1e207f5b802638e32bb837df968bb5c05f413d0fa526fac4cf7a7", - "sha256:753a26312e6f1eaeec20bc6f2644a10926697da93446e1f8e24d6d32d45a922a" + "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3", + "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2" ], "markers": "python_version >= '3.7'", - "version": "==2.23.0" + "version": "==2.23.4" }, "google-cloud-core": { "hashes": [ @@ -522,12 +524,12 @@ }, "google-cloud-storage": { "hashes": [ - "sha256:6fbf62659b83c8f3a0a743af0d661d2046c97c3a5bfb587c4662c4bc68de3e31", - "sha256:88cbd7fb3d701c780c4272bc26952db99f25eb283fb4c2208423249f00b5fe53" + "sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d", + "sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.11.0" + "version": "==2.13.0" }, "google-cloud-tasks": { "hashes": [ @@ -621,150 +623,155 @@ "version": "==2.6.0" }, "googleapis-common-protos": { + "extras": [ + "grpc" + ], "hashes": [ - "sha256:69f9bbcc6acde92cab2db95ce30a70bd2b81d20b12eff3f1aabaffcbe8a93918", - "sha256:e73ebb404098db405ba95d1e1ae0aa91c3e15a71da031a2eeb6b2e23e7bc3708" + "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0", + "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b" ], "markers": "python_version >= '3.7'", - "version": "==1.60.0" + "version": "==1.61.0" }, "greenlet": { "hashes": [ - "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a", - "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a", - "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1", - "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43", - "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33", - "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8", - "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088", - "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca", - "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343", - "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645", - "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db", - "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df", - "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3", - "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86", - "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2", - "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a", - "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf", - "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7", - "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394", - "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40", - "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3", - "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6", - "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74", - "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0", - "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3", - "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91", - "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5", - "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9", - "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417", - "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8", - "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b", - "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6", - "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb", - "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73", - "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b", - "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df", - "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9", - "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f", - "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0", - "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857", - "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a", - "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249", - "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30", - "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292", - "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b", - "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d", - "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b", - "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c", - "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca", - "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7", - "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75", - "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae", - "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47", - "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b", - "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470", - "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c", - "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564", - "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9", - "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099", - "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0", - "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5", - "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19", - "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1", - "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526" + "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", + "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", + "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", + "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", + "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", + "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", + "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", + "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", + "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", + "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", + "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", + "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", + "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", + "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", + "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", + "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", + "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", + "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", + "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", + "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", + "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", + "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", + "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", + "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", + "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", + "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", + "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", + "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", + "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", + "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", + "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", + "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", + "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", + "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", + "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", + "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", + "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", + "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", + "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", + "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", + "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", + "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", + "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", + "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", + "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", + "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", + "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", + "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", + "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", + "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", + "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", + "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", + "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", + "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", + "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", + "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", + "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" ], "markers": "python_version < '3.12' and platform_python_implementation == 'CPython'", - "version": "==2.0.2" + "version": "==3.0.1" }, "grpc-google-iam-v1": { "hashes": [ - "sha256:2bc4b8fdf22115a65d751c9317329322602c39b7c86a289c9b72d228d960ef5f", - "sha256:5c10f3d8dc2d88678ab1a9b0cb5482735c5efee71e6c0cd59f872eef22913f5c" + "sha256:009197a7f1eaaa22149c96e5e054ac5934ba7241974e92663d8d3528a21203d1", + "sha256:834da89f4c4a2abbe842a793ed20fc6d9a77011ef2626755b1b89116fb9596d7" ], "markers": "python_version >= '3.7'", - "version": "==0.12.6" + "version": "==0.12.7" }, "grpcio": { "hashes": [ - "sha256:002f228d197fea12797a14e152447044e14fb4fdb2eb5d6cfa496f29ddbf79ef", - "sha256:039003a5e0ae7d41c86c768ef8b3ee2c558aa0a23cf04bf3c23567f37befa092", - "sha256:09206106848462763f7f273ca93d2d2d4d26cab475089e0de830bb76be04e9e8", - "sha256:128eb1f8e70676d05b1b0c8e6600320fc222b3f8c985a92224248b1367122188", - "sha256:1c1c5238c6072470c7f1614bf7c774ffde6b346a100521de9ce791d1e4453afe", - "sha256:1ed979b273a81de36fc9c6716d9fb09dd3443efa18dcc8652501df11da9583e9", - "sha256:201e550b7e2ede113b63e718e7ece93cef5b0fbf3c45e8fe4541a5a4305acd15", - "sha256:212f38c6a156862098f6bdc9a79bf850760a751d259d8f8f249fc6d645105855", - "sha256:24765a627eb4d9288ace32d5104161c3654128fe27f2808ecd6e9b0cfa7fc8b9", - "sha256:24edec346e69e672daf12b2c88e95c6f737f3792d08866101d8c5f34370c54fd", - "sha256:2ef8d4a76d2c7d8065aba829f8d0bc0055495c998dce1964ca5b302d02514fb3", - "sha256:2f85f87e2f087d9f632c085b37440a3169fda9cdde80cb84057c2fc292f8cbdf", - "sha256:3886b4d56bd4afeac518dbc05933926198aa967a7d1d237a318e6fbc47141577", - "sha256:3e6bebf1dfdbeb22afd95650e4f019219fef3ab86d3fca8ebade52e4bc39389a", - "sha256:458899d2ebd55d5ca2350fd3826dfd8fcb11fe0f79828ae75e2b1e6051d50a29", - "sha256:4891bbb4bba58acd1d620759b3be11245bfe715eb67a4864c8937b855b7ed7fa", - "sha256:4b12754af201bb993e6e2efd7812085ddaaef21d0a6f0ff128b97de1ef55aa4a", - "sha256:532410c51ccd851b706d1fbc00a87be0f5312bd6f8e5dbf89d4e99c7f79d7499", - "sha256:5b23d75e5173faa3d1296a7bedffb25afd2fddb607ef292dfc651490c7b53c3d", - "sha256:62831d5e251dd7561d9d9e83a0b8655084b2a1f8ea91e4bd6b3cedfefd32c9d2", - "sha256:652978551af02373a5a313e07bfef368f406b5929cf2d50fa7e4027f913dbdb4", - "sha256:6801ff6652ecd2aae08ef994a3e49ff53de29e69e9cd0fd604a79ae4e545a95c", - "sha256:6cba491c638c76d3dc6c191d9c75041ca5b8f5c6de4b8327ecdcab527f130bb4", - "sha256:7e473a7abad9af48e3ab5f3b5d237d18208024d28ead65a459bd720401bd2f8f", - "sha256:8774219e21b05f750eef8adc416e9431cf31b98f6ce9def288e4cea1548cbd22", - "sha256:8f061722cad3f9aabb3fbb27f3484ec9d4667b7328d1a7800c3c691a98f16bb0", - "sha256:92ae871a902cf19833328bd6498ec007b265aabf2fda845ab5bd10abcaf4c8c6", - "sha256:9f13a171281ebb4d7b1ba9f06574bce2455dcd3f2f6d1fbe0fd0d84615c74045", - "sha256:a2d67ff99e70e86b2be46c1017ae40b4840d09467d5455b2708de6d4c127e143", - "sha256:b5e8db0aff0a4819946215f156bd722b6f6c8320eb8419567ffc74850c9fd205", - "sha256:ba0af11938acf8cd4cf815c46156bcde36fa5850518120920d52620cc3ec1830", - "sha256:bc325fed4d074367bebd465a20763586e5e1ed5b943e9d8bc7c162b1f44fd602", - "sha256:bc7ffef430b80345729ff0a6825e9d96ac87efe39216e87ac58c6c4ef400de93", - "sha256:cde11577d5b6fd73a00e6bfa3cf5f428f3f33c2d2878982369b5372bbc4acc60", - "sha256:d4cef77ad2fed42b1ba9143465856d7e737279854e444925d5ba45fc1f3ba727", - "sha256:d79b660681eb9bc66cc7cbf78d1b1b9e335ee56f6ea1755d34a31108b80bd3c8", - "sha256:d81c2b2b24c32139dd2536972f1060678c6b9fbd106842a9fcdecf07b233eccd", - "sha256:dc72e04620d49d3007771c0e0348deb23ca341c0245d610605dddb4ac65a37cb", - "sha256:dcfba7befe3a55dab6fe1eb7fc9359dc0c7f7272b30a70ae0af5d5b063842f28", - "sha256:e9f995a8a421405958ff30599b4d0eec244f28edc760de82f0412c71c61763d2", - "sha256:eb6b92036ff312d5b4182fa72e8735d17aceca74d0d908a7f08e375456f03e07", - "sha256:f0241f7eb0d2303a545136c59bc565a35c4fc3b924ccbd69cb482f4828d6f31c", - "sha256:fad9295fe02455d4f158ad72c90ef8b4bcaadfdb5efb5795f7ab0786ad67dd58", - "sha256:fbcecb6aedd5c1891db1d70efbfbdc126c986645b5dd616a045c07d6bd2dfa86", - "sha256:fe643af248442221db027da43ed43e53b73e11f40c9043738de9a2b4b6ca7697" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.58.0" + "sha256:023088764012411affe7db183d1ada3ad9daf2e23ddc719ff46d7061de661340", + "sha256:08d77e682f2bf730a4961eea330e56d2f423c6a9b91ca222e5b1eb24a357b19f", + "sha256:0a4a3833c0e067f3558538727235cd8a49709bff1003200bbdefa2f09334e4b1", + "sha256:0a754aff9e3af63bdc4c75c234b86b9d14e14a28a30c4e324aed1a9b873d755f", + "sha256:11168ef43e4a43ff1b1a65859f3e0ef1a173e277349e7fb16923ff108160a8cd", + "sha256:128e20f57c5f27cb0157e73756d1586b83c1b513ebecc83ea0ac37e4b0e4e758", + "sha256:1f9524d1d701e399462d2c90ba7c193e49d1711cf429c0d3d97c966856e03d00", + "sha256:1ff16d68bf453275466a9a46739061a63584d92f18a0f5b33d19fc97eb69867c", + "sha256:2067274c88bc6de89c278a672a652b4247d088811ece781a4858b09bdf8448e3", + "sha256:2171c39f355ba5b551c5d5928d65aa6c69807fae195b86ef4a7d125bcdb860a9", + "sha256:242adc47725b9a499ee77c6a2e36688fa6c96484611f33b1be4c57ab075a92dd", + "sha256:27f879ae604a7fcf371e59fba6f3ff4635a4c2a64768bd83ff0cac503142fef4", + "sha256:2b230028a008ae1d0f430acb227d323ff8a619017415cf334c38b457f814119f", + "sha256:3059668df17627f0e0fa680e9ef8c995c946c792612e9518f5cc1503be14e90b", + "sha256:31176aa88f36020055ace9adff2405a33c8bdbfa72a9c4980e25d91b2f196873", + "sha256:36f53c2b3449c015880e7d55a89c992c357f176327b0d2873cdaaf9628a37c69", + "sha256:3b4368b33908f683a363f376dfb747d40af3463a6e5044afee07cf9436addf96", + "sha256:3c61d641d4f409c5ae46bfdd89ea42ce5ea233dcf69e74ce9ba32b503c727e29", + "sha256:4abb717e320e74959517dc8e84a9f48fbe90e9abe19c248541e9418b1ce60acd", + "sha256:4c93f4abbb54321ee6471e04a00139c80c754eda51064187963ddf98f5cf36a4", + "sha256:535561990e075fa6bd4b16c4c3c1096b9581b7bb35d96fac4650f1181e428268", + "sha256:53c9aa5ddd6857c0a1cd0287225a2a25873a8e09727c2e95c4aebb1be83a766a", + "sha256:5d573e70a6fe77555fb6143c12d3a7d3fa306632a3034b4e7c59ca09721546f8", + "sha256:6009386a2df66159f64ac9f20425ae25229b29b9dd0e1d3dd60043f037e2ad7e", + "sha256:686e975a5d16602dc0982c7c703948d17184bd1397e16c8ee03511ecb8c4cdda", + "sha256:6959fb07e8351e20501ffb8cc4074c39a0b7ef123e1c850a7f8f3afdc3a3da01", + "sha256:6b25ed37c27e652db01be341af93fbcea03d296c024d8a0e680017a268eb85dd", + "sha256:6da6dea3a1bacf99b3c2187e296db9a83029ed9c38fd4c52b7c9b7326d13c828", + "sha256:72ca2399097c0b758198f2ff30f7178d680de8a5cfcf3d9b73a63cf87455532e", + "sha256:73abb8584b0cf74d37f5ef61c10722adc7275502ab71789a8fe3cb7ef04cf6e2", + "sha256:74100fecaec8a535e380cf5f2fb556ff84957d481c13e54051c52e5baac70541", + "sha256:75c6ecb70e809cf1504465174343113f51f24bc61e22a80ae1c859f3f7034c6d", + "sha256:7cf05053242f61ba94014dd3a986e11a083400a32664058f80bf4cf817c0b3a1", + "sha256:9411e24328a2302e279e70cae6e479f1fddde79629fcb14e03e6d94b3956eabf", + "sha256:a213acfbf186b9f35803b52e4ca9addb153fc0b67f82a48f961be7000ecf6721", + "sha256:bb7e0fe6ad73b7f06d7e2b689c19a71cf5cc48f0c2bf8608469e51ffe0bd2867", + "sha256:c2504eed520958a5b77cc99458297cb7906308cb92327f35fb7fbbad4e9b2188", + "sha256:c35aa9657f5d5116d23b934568e0956bd50c615127810fffe3ac356a914c176a", + "sha256:c5f09cffa619adfb44799fa4a81c2a1ad77c887187613fb0a8f201ab38d89ba1", + "sha256:c978f864b35f2261e0819f5cd88b9830b04dc51bcf055aac3c601e525a10d2ba", + "sha256:cbe946b3e6e60a7b4618f091e62a029cb082b109a9d6b53962dd305087c6e4fd", + "sha256:cc3e4cd087f07758b16bef8f31d88dbb1b5da5671d2f03685ab52dece3d7a16e", + "sha256:cf0dead5a2c5a3347af2cfec7131d4f2a2e03c934af28989c9078f8241a491fa", + "sha256:d2794f0e68b3085d99b4f6ff9c089f6fdd02b32b9d3efdfbb55beac1bf22d516", + "sha256:d2fa68a96a30dd240be80bbad838a0ac81a61770611ff7952b889485970c4c71", + "sha256:d6f70406695e3220f09cd7a2f879333279d91aa4a8a1d34303b56d61a8180137", + "sha256:d8f9cd4ad1be90b0cf350a2f04a38a36e44a026cac1e036ac593dc48efe91d52", + "sha256:da2d94c15f88cd40d7e67f7919d4f60110d2b9d5b1e08cf354c2be773ab13479", + "sha256:e1727c1c0e394096bb9af185c6923e8ea55a5095b8af44f06903bcc0e06800a2", + "sha256:e420ced29b5904cdf9ee5545e23f9406189d8acb6750916c2db4793dada065c6", + "sha256:e82c5cf1495244adf5252f925ac5932e5fd288b3e5ab6b70bec5593074b7236c", + "sha256:f1ef0d39bc1feb420caf549b3c657c871cad4ebbcf0580c4d03816b0590de0cf", + "sha256:f8753a6c88d1d0ba64302309eecf20f70d2770f65ca02d83c2452279085bfcd3", + "sha256:f93dbf58f03146164048be5426ffde298b237a5e059144847e4940f5b80172c3" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.59.2" }, "grpcio-status": { "hashes": [ - "sha256:0b42e70c0405a66a82d9e9867fa255fe59e618964a6099b20568c31dd9099766", - "sha256:36d46072b71a00147709ebce49344ac59b4b8960942acf0f813a8a7d6c1c28e0" + "sha256:24bdf3b3b83b9112f43bd0626f82510d12cc1d919a45028ac20eb6919218e508", + "sha256:a2c2b146e66b73ba80d021ab34fce5db4dd9be67ca4566cda40d36b185ce54f4" ], "markers": "python_version >= '3.6'", - "version": "==1.58.0" + "version": "==1.59.2" }, "gunicorn": { "hashes": [ @@ -935,11 +942,11 @@ }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==23.2" }, "pdfkit": { "hashes": [ @@ -969,22 +976,20 @@ }, "protobuf": { "hashes": [ - "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719", - "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d", - "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2", - "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4", - "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1", - "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3", - "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b", - "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76", - "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959", - "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52", - "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b", - "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675", - "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a" + "sha256:1a3ba712877e6d37013cdc3476040ea1e313a6c2e1580836a94f76b3c176d575", + "sha256:1a53d6f64b00eecf53b65ff4a8c23dc95df1fa1e97bb06b8122e5a64f49fc90a", + "sha256:32ac2100b0e23412413d948c03060184d34a7c50b3e5d7524ee96ac2b10acf51", + "sha256:5c1203ac9f50e4853b0a0bfffd32c67118ef552a33942982eeab543f5c634395", + "sha256:63714e79b761a37048c9701a37438aa29945cd2417a97076048232c1df07b701", + "sha256:683dc44c61f2620b32ce4927de2108f3ebe8ccf2fd716e1e684e5a50da154054", + "sha256:68f7caf0d4f012fd194a301420cf6aa258366144d814f358c5b32558228afa7c", + "sha256:b2cf8b5d381f9378afe84618288b239e75665fe58d0f3fd5db400959274296e9", + "sha256:c40ff8f00aa737938c5378d461637d15c442a12275a81019cc2fef06d81c9419", + "sha256:cf21faba64cd2c9a3ed92b7a67f226296b10159dbb8fbc5e854fc90657d908e4", + "sha256:d94a33db8b7ddbd0af7c467475fb9fde0c705fb315a8433c0e2020942b863a1f" ], - "markers": "python_version >= '3.7'", - "version": "==4.24.3" + "markers": "python_version >= '3.8'", + "version": "==4.25.0" }, "pyasn1": { "hashes": [ @@ -1137,12 +1142,12 @@ }, "redis": { "hashes": [ - "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0", - "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120" + "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", + "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==5.0.0" + "version": "==5.0.1" }, "requests": { "hashes": [ @@ -1163,11 +1168,11 @@ }, "s3transfer": { "hashes": [ - "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084", - "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861" + "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", + "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" ], "markers": "python_version >= '3.7'", - "version": "==0.6.2" + "version": "==0.7.0" }, "sdc-cryptography": { "hashes": [ @@ -1187,95 +1192,108 @@ }, "simplejson": { "hashes": [ - "sha256:081ea6305b3b5e84ae7417e7f45956db5ea3872ec497a584ec86c3260cda049e", - "sha256:08be5a241fdf67a8e05ac7edbd49b07b638ebe4846b560673e196b2a25c94b92", - "sha256:0c16ec6a67a5f66ab004190829eeede01c633936375edcad7cbf06d3241e5865", - "sha256:0ccb2c1877bc9b25bc4f4687169caa925ffda605d7569c40e8e95186e9a5e58b", - "sha256:17a963e8dd4d81061cc05b627677c1f6a12e81345111fbdc5708c9f088d752c9", - "sha256:199a0bcd792811c252d71e3eabb3d4a132b3e85e43ebd93bfd053d5b59a7e78b", - "sha256:1cb19eacb77adc5a9720244d8d0b5507421d117c7ed4f2f9461424a1829e0ceb", - "sha256:203412745fed916fc04566ecef3f2b6c872b52f1e7fb3a6a84451b800fb508c1", - "sha256:2098811cd241429c08b7fc5c9e41fcc3f59f27c2e8d1da2ccdcf6c8e340ab507", - "sha256:22b867205cd258050c2625325fdd9a65f917a5aff22a23387e245ecae4098e78", - "sha256:23fbb7b46d44ed7cbcda689295862851105c7594ae5875dce2a70eeaa498ff86", - "sha256:2541fdb7467ef9bfad1f55b6c52e8ea52b3ce4a0027d37aff094190a955daa9d", - "sha256:3231100edee292da78948fa0a77dee4e5a94a0a60bcba9ed7a9dc77f4d4bb11e", - "sha256:344a5093b71c1b370968d0fbd14d55c9413cb6f0355fdefeb4a322d602d21776", - "sha256:37724c634f93e5caaca04458f267836eb9505d897ab3947b52f33b191bf344f3", - "sha256:3844305bc33d52c4975da07f75b480e17af3558c0d13085eaa6cc2f32882ccf7", - "sha256:390f4a8ca61d90bcf806c3ad644e05fa5890f5b9a72abdd4ca8430cdc1e386fa", - "sha256:3a4480e348000d89cf501b5606415f4d328484bbb431146c2971123d49fd8430", - "sha256:3b652579c21af73879d99c8072c31476788c8c26b5565687fd9db154070d852a", - "sha256:3e0902c278243d6f7223ba3e6c5738614c971fd9a887fff8feaa8dcf7249c8d4", - "sha256:412e58997a30c5deb8cab5858b8e2e5b40ca007079f7010ee74565cc13d19665", - "sha256:44cdb4e544134f305b033ad79ae5c6b9a32e7c58b46d9f55a64e2a883fbbba01", - "sha256:46133bc7dd45c9953e6ee4852e3de3d5a9a4a03b068bd238935a5c72f0a1ce34", - "sha256:46e89f58e4bed107626edce1cf098da3664a336d01fc78fddcfb1f397f553d44", - "sha256:4710806eb75e87919b858af0cba4ffedc01b463edc3982ded7b55143f39e41e1", - "sha256:476c8033abed7b1fd8db62a7600bf18501ce701c1a71179e4ce04ac92c1c5c3c", - "sha256:48600a6e0032bed17c20319d91775f1797d39953ccfd68c27f83c8d7fc3b32cb", - "sha256:4d3025e7e9ddb48813aec2974e1a7e68e63eac911dd5e0a9568775de107ac79a", - "sha256:547ea86ca408a6735335c881a2e6208851027f5bfd678d8f2c92a0f02c7e7330", - "sha256:54fca2b26bcd1c403146fd9461d1da76199442297160721b1d63def2a1b17799", - "sha256:5673d27806085d2a413b3be5f85fad6fca4b7ffd31cfe510bbe65eea52fff571", - "sha256:58ee5e24d6863b22194020eb62673cf8cc69945fcad6b283919490f6e359f7c5", - "sha256:5ca922c61d87b4c38f37aa706520328ffe22d7ac1553ef1cadc73f053a673553", - "sha256:5db86bb82034e055257c8e45228ca3dbce85e38d7bfa84fa7b2838e032a3219c", - "sha256:6277f60848a7d8319d27d2be767a7546bc965535b28070e310b3a9af90604a4c", - "sha256:6424d8229ba62e5dbbc377908cfee9b2edf25abd63b855c21f12ac596cd18e41", - "sha256:65dafe413b15e8895ad42e49210b74a955c9ae65564952b0243a18fb35b986cc", - "sha256:66389b6b6ee46a94a493a933a26008a1bae0cfadeca176933e7ff6556c0ce998", - "sha256:66d780047c31ff316ee305c3f7550f352d87257c756413632303fc59fef19eac", - "sha256:69a8b10a4f81548bc1e06ded0c4a6c9042c0be0d947c53c1ed89703f7e613950", - "sha256:6a561320485017ddfc21bd2ed5de2d70184f754f1c9b1947c55f8e2b0163a268", - "sha256:6aa7ca03f25b23b01629b1c7f78e1cd826a66bfb8809f8977a3635be2ec48f1a", - "sha256:6b79642a599740603ca86cf9df54f57a2013c47e1dd4dd2ae4769af0a6816900", - "sha256:6e7c70f19405e5f99168077b785fe15fcb5f9b3c0b70b0b5c2757ce294922c8c", - "sha256:70128fb92932524c89f373e17221cf9535d7d0c63794955cc3cd5868e19f5d38", - "sha256:73d0904c2471f317386d4ae5c665b16b5c50ab4f3ee7fd3d3b7651e564ad74b1", - "sha256:74bf802debe68627227ddb665c067eb8c73aa68b2476369237adf55c1161b728", - "sha256:79c748aa61fd8098d0472e776743de20fae2686edb80a24f0f6593a77f74fe86", - "sha256:79d46e7e33c3a4ef853a1307b2032cfb7220e1a079d0c65488fbd7118f44935a", - "sha256:7e78d79b10aa92f40f54178ada2b635c960d24fc6141856b926d82f67e56d169", - "sha256:8090e75653ea7db75bc21fa5f7bcf5f7bdf64ea258cbbac45c7065f6324f1b50", - "sha256:87b190e6ceec286219bd6b6f13547ca433f977d4600b4e81739e9ac23b5b9ba9", - "sha256:889328873c35cb0b2b4c83cbb83ec52efee5a05e75002e2c0c46c4e42790e83c", - "sha256:8f8d179393e6f0cf6c7c950576892ea6acbcea0a320838c61968ac7046f59228", - "sha256:919bc5aa4d8094cf8f1371ea9119e5d952f741dc4162810ab714aec948a23fe5", - "sha256:926957b278de22797bfc2f004b15297013843b595b3cd7ecd9e37ccb5fad0b72", - "sha256:93f5ac30607157a0b2579af59a065bcfaa7fadeb4875bf927a8f8b6739c8d910", - "sha256:96ade243fb6f3b57e7bd3b71e90c190cd0f93ec5dce6bf38734a73a2e5fa274f", - "sha256:9f14ecca970d825df0d29d5c6736ff27999ee7bdf5510e807f7ad8845f7760ce", - "sha256:a755f7bfc8adcb94887710dc70cc12a69a454120c6adcc6f251c3f7b46ee6aac", - "sha256:a79b439a6a77649bb8e2f2644e6c9cc0adb720fc55bed63546edea86e1d5c6c8", - "sha256:aa9d614a612ad02492f704fbac636f666fa89295a5d22b4facf2d665fc3b5ea9", - "sha256:ad071cd84a636195f35fa71de2186d717db775f94f985232775794d09f8d9061", - "sha256:b0e9a5e66969f7a47dc500e3dba8edc3b45d4eb31efb855c8647700a3493dd8a", - "sha256:b438e5eaa474365f4faaeeef1ec3e8d5b4e7030706e3e3d6b5bee6049732e0e6", - "sha256:b46aaf0332a8a9c965310058cf3487d705bf672641d2c43a835625b326689cf4", - "sha256:c39fa911e4302eb79c804b221ddec775c3da08833c0a9120041dd322789824de", - "sha256:ca56a6c8c8236d6fe19abb67ef08d76f3c3f46712c49a3b6a5352b6e43e8855f", - "sha256:cb502cde018e93e75dc8fc7bb2d93477ce4f3ac10369f48866c61b5e031db1fd", - "sha256:cd4d50a27b065447c9c399f0bf0a993bd0e6308db8bbbfbc3ea03b41c145775a", - "sha256:d125e754d26c0298715bdc3f8a03a0658ecbe72330be247f4b328d229d8cf67f", - "sha256:d300773b93eed82f6da138fd1d081dc96fbe53d96000a85e41460fe07c8d8b33", - "sha256:d396b610e77b0c438846607cd56418bfc194973b9886550a98fd6724e8c6cfec", - "sha256:d61482b5d18181e6bb4810b4a6a24c63a490c3a20e9fbd7876639653e2b30a1a", - "sha256:d9f2c27f18a0b94107d57294aab3d06d6046ea843ed4a45cae8bd45756749f3a", - "sha256:dc2b3f06430cbd4fac0dae5b2974d2bf14f71b415fb6de017f498950da8159b1", - "sha256:dc935d8322ba9bc7b84f99f40f111809b0473df167bf5b93b89fb719d2c4892b", - "sha256:e333c5b62e93949f5ac27e6758ba53ef6ee4f93e36cc977fe2e3df85c02f6dc4", - "sha256:e765b1f47293dedf77946f0427e03ee45def2862edacd8868c6cf9ab97c8afbd", - "sha256:ed18728b90758d171f0c66c475c24a443ede815cf3f1a91e907b0db0ebc6e508", - "sha256:eff87c68058374e45225089e4538c26329a13499bc0104b52b77f8428eed36b2", - "sha256:f05d05d99fce5537d8f7a0af6417a9afa9af3a6c4bb1ba7359c53b6257625fcb", - "sha256:f253edf694ce836631b350d758d00a8c4011243d58318fbfbe0dd54a6a839ab4", - "sha256:f41915a4e1f059dfad614b187bc06021fefb5fc5255bfe63abf8247d2f7a646a", - "sha256:f96def94576f857abf58e031ce881b5a3fc25cbec64b2bc4824824a8a4367af9" + "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137", + "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a", + "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae", + "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a", + "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba", + "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087", + "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b", + "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41", + "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d", + "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd", + "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d", + "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e", + "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a", + "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9", + "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6", + "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb", + "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664", + "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2", + "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b", + "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5", + "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835", + "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17", + "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69", + "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd", + "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6", + "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2", + "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2", + "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428", + "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f", + "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2", + "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f", + "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7", + "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f", + "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637", + "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3", + "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816", + "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565", + "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378", + "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0", + "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b", + "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693", + "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358", + "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9", + "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23", + "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf", + "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a", + "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832", + "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414", + "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f", + "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48", + "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad", + "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290", + "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3", + "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917", + "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402", + "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0", + "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc", + "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867", + "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f", + "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589", + "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb", + "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b", + "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3", + "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb", + "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c", + "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46", + "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672", + "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c", + "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e", + "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0", + "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b", + "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4", + "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4", + "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c", + "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c", + "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8", + "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b", + "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb", + "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80", + "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2", + "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374", + "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc", + "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50", + "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c", + "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f", + "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28", + "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5", + "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734", + "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a", + "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d", + "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4", + "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c", + "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13", + "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973", + "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a", + "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e", + "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff", + "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded" ], "index": "pypi", "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.19.1" + "version": "==3.19.2" }, "six": { "hashes": [ @@ -1287,12 +1305,12 @@ }, "structlog": { "hashes": [ - "sha256:270d681dd7d163c11ba500bc914b2472d2b50a8ef00faa999ded5ff83a2f906b", - "sha256:79b9e68e48b54e373441e130fa447944e6f87a05b35de23138e475c05d0f7e0e" + "sha256:16a167e87b9fa7fae9a972d5d12805ef90e04857a93eba479d4be3801a6a1482", + "sha256:334666b94707f89dbc4c81a22a8ccd34449f0201d5b1ee097a030b577fa8c858" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==23.1.0" + "markers": "python_version >= '3.8'", + "version": "==23.2.0" }, "ua-parser": { "hashes": [ @@ -1304,115 +1322,110 @@ }, "urllib3": { "hashes": [ - "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", - "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.16" + "markers": "python_version >= '3.7'", + "version": "==2.0.7" }, "uwsgi": { "hashes": [ - "sha256:4cc4727258671ac5fa17ab422155e9aaef8a2008ebb86e4404b66deaae965db2" + "sha256:0cafda0c16f921db7fe42cfaf81b167cf884ee17350efbdd87d1ecece2d7de37" ], "index": "pypi", - "version": "==2.0.22" + "version": "==2.0.23" }, "werkzeug": { "hashes": [ - "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8", - "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528" + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" ], "markers": "python_version >= '3.8'", - "version": "==2.3.7" + "version": "==3.0.1" }, "wrapt": { "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" + "markers": "python_version >= '3.6'", + "version": "==1.16.0" }, "wtforms": { "hashes": [ - "sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc", - "sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b" + "sha256:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620", + "sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0" ], - "markers": "python_version >= '3.7'", - "version": "==3.0.1" + "markers": "python_version >= '3.8'", + "version": "==3.1.1" }, "zope.event": { "hashes": [ @@ -1424,49 +1437,55 @@ }, "zope.interface": { "hashes": [ - "sha256:042f2381118b093714081fd82c98e3b189b68db38ee7d35b63c327c470ef8373", - "sha256:0ec9653825f837fbddc4e4b603d90269b501486c11800d7c761eee7ce46d1bbb", - "sha256:12175ca6b4db7621aedd7c30aa7cfa0a2d65ea3a0105393e05482d7a2d367446", - "sha256:1592f68ae11e557b9ff2bc96ac8fc30b187e77c45a3c9cd876e3368c53dc5ba8", - "sha256:23ac41d52fd15dd8be77e3257bc51bbb82469cf7f5e9a30b75e903e21439d16c", - "sha256:424d23b97fa1542d7be882eae0c0fc3d6827784105264a8169a26ce16db260d8", - "sha256:4407b1435572e3e1610797c9203ad2753666c62883b921318c5403fb7139dec2", - "sha256:48f4d38cf4b462e75fac78b6f11ad47b06b1c568eb59896db5b6ec1094eb467f", - "sha256:4c3d7dfd897a588ec27e391edbe3dd320a03684457470415870254e714126b1f", - "sha256:5171eb073474a5038321409a630904fd61f12dd1856dd7e9d19cd6fe092cbbc5", - "sha256:5a158846d0fca0a908c1afb281ddba88744d403f2550dc34405c3691769cdd85", - "sha256:6ee934f023f875ec2cfd2b05a937bd817efcc6c4c3f55c5778cbf78e58362ddc", - "sha256:790c1d9d8f9c92819c31ea660cd43c3d5451df1df61e2e814a6f99cebb292788", - "sha256:809fe3bf1a91393abc7e92d607976bbb8586512913a79f2bf7d7ec15bd8ea518", - "sha256:87b690bbee9876163210fd3f500ee59f5803e4a6607d1b1238833b8885ebd410", - "sha256:89086c9d3490a0f265a3c4b794037a84541ff5ffa28bb9c24cc9f66566968464", - "sha256:99856d6c98a326abbcc2363827e16bd6044f70f2ef42f453c0bd5440c4ce24e5", - "sha256:aab584725afd10c710b8f1e6e208dbee2d0ad009f57d674cb9d1b3964037275d", - "sha256:af169ba897692e9cd984a81cb0f02e46dacdc07d6cf9fd5c91e81f8efaf93d52", - "sha256:b39b8711578dcfd45fc0140993403b8a81e879ec25d53189f3faa1f006087dca", - "sha256:b3f543ae9d3408549a9900720f18c0194ac0fe810cecda2a584fd4dca2eb3bb8", - "sha256:d0583b75f2e70ec93f100931660328965bb9ff65ae54695fb3fa0a1255daa6f2", - "sha256:dfbbbf0809a3606046a41f8561c3eada9db811be94138f42d9135a5c47e75f6f", - "sha256:e538f2d4a6ffb6edfb303ce70ae7e88629ac6e5581870e66c306d9ad7b564a58", - "sha256:eba51599370c87088d8882ab74f637de0c4f04a6d08a312dce49368ba9ed5c2a", - "sha256:ee4b43f35f5dc15e1fec55ccb53c130adb1d11e8ad8263d68b1284b66a04190d", - "sha256:f2363e5fd81afb650085c6686f2ee3706975c54f331b426800b53531191fdf28", - "sha256:f299c020c6679cb389814a3b81200fe55d428012c5e76da7e722491f5d205990", - "sha256:f72f23bab1848edb7472309e9898603141644faec9fd57a823ea6b4d1c4c8995", - "sha256:fa90bac61c9dc3e1a563e5babb3fd2c0c1c80567e815442ddbe561eadc803b30" - ], - "markers": "python_version >= '3.7'", - "version": "==6.0" + "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", + "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", + "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", + "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", + "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", + "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", + "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", + "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", + "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", + "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", + "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", + "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", + "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", + "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", + "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", + "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", + "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", + "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", + "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", + "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", + "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", + "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", + "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", + "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", + "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", + "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", + "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", + "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", + "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", + "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", + "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", + "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", + "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", + "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", + "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", + "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" + ], + "markers": "python_version >= '3.7'", + "version": "==6.1" } }, "develop": { "astroid": { "hashes": [ - "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c", - "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd" + "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", + "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.6" + "version": "==2.15.8" }, "attrs": { "hashes": [ @@ -1487,58 +1506,54 @@ }, "black": { "hashes": [ - "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f", - "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7", - "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100", - "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573", - "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d", - "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f", - "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9", - "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300", - "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948", - "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325", - "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9", - "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71", - "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186", - "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f", - "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe", - "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855", - "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80", - "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393", - "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c", - "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204", - "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377", - "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301" + "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4", + "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b", + "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f", + "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07", + "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187", + "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6", + "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05", + "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06", + "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e", + "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5", + "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244", + "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f", + "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221", + "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055", + "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479", + "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394", + "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911", + "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==23.9.1" + "version": "==23.11.0" }, "blinker": { "hashes": [ - "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", - "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.6.2" + "markers": "python_version >= '3.8'", + "version": "==1.7.0" }, "boto3": { "hashes": [ - "sha256:8149f17587c68e556743018f213f00ece81a0f021adb9b9b697a2ee8802583d7", - "sha256:8860ab54a26d1d596d64fc9de7e40c4d7c53c100311208cbd90d9272c3385513" + "sha256:1d10691911c4b8b9443d3060257ba32b68b6e3cad0eebbb9f69fd1c52a78417f", + "sha256:489c4967805b677b7a4030460e4c06c0903d6bc0f6834453611bf87efbd8d8a3" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.51" + "version": "==1.28.83" }, "botocore": { "hashes": [ - "sha256:8e133add22f07b55d21e14176b97b82e993d5f9aca70f5b0cd0908cb105ba53a", - "sha256:91dfb38801d45214875a892bd1e908fc7a894c2ed170bacd67e6929af72f2bd2" + "sha256:40914b0fb28f13d709e1f8a4481e278350b77a3987be81acd23715ec8d5fedca", + "sha256:c742069e8bfd06d212d712228258ff09fb481b6ec02358e539381ce0fcad065a" ], "markers": "python_version >= '3.7'", - "version": "==1.31.51" + "version": "==1.31.83" }, "certifi": { "hashes": [ @@ -1550,153 +1565,157 @@ }, "cffi": { "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "version": "==1.15.1" + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" }, "charset-normalizer": { "hashes": [ - "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", - "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", - "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", - "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", - "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", - "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", - "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", - "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", - "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", - "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", - "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", - "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", - "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", - "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", - "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", - "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", - "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", - "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", - "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", - "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", - "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", - "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", - "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", - "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", - "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", - "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", - "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", - "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", - "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", - "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", - "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", - "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", - "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", - "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", - "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", - "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", - "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", - "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", - "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", - "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", - "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", - "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", - "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", - "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", - "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", - "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", - "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", - "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", - "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", - "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", - "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", - "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", - "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", - "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", - "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", - "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", - "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", - "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", - "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", - "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", - "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", - "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", - "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", - "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", - "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", - "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", - "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", - "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", - "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", - "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", - "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", - "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", - "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", - "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", - "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.2.0" + "version": "==3.3.2" }, "click": { "hashes": [ @@ -1711,90 +1730,90 @@ "toml" ], "hashes": [ - "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375", - "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344", - "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e", - "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745", - "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f", - "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194", - "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a", - "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f", - "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760", - "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8", - "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392", - "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d", - "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc", - "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40", - "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981", - "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0", - "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92", - "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3", - "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0", - "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086", - "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7", - "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465", - "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140", - "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952", - "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3", - "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8", - "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f", - "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593", - "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0", - "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204", - "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037", - "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276", - "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9", - "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26", - "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce", - "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7", - "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136", - "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a", - "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4", - "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c", - "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f", - "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832", - "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3", - "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969", - "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520", - "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887", - "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3", - "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6", - "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1", - "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff", - "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981", - "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e" + "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", + "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", + "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", + "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", + "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", + "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", + "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", + "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", + "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", + "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", + "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", + "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", + "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", + "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", + "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", + "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", + "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", + "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", + "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", + "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", + "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", + "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", + "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", + "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", + "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", + "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", + "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", + "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", + "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", + "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", + "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", + "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", + "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", + "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", + "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", + "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", + "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", + "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", + "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", + "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", + "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", + "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", + "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", + "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", + "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", + "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", + "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", + "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", + "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", + "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", + "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", + "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" ], "markers": "python_version >= '3.8'", - "version": "==7.3.1" + "version": "==7.3.2" }, "cryptography": { "hashes": [ - "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", - "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", - "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", - "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", - "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", - "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", - "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", - "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", - "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", - "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", - "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", - "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", - "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", - "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", - "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", - "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", - "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", - "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", - "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", - "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", - "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", - "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", - "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.4" + "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", + "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84", + "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", + "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", + "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", + "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", + "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", + "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", + "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", + "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81", + "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", + "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", + "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d", + "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", + "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", + "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", + "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", + "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936", + "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", + "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", + "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", + "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", + "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.5" }, "dill": { "hashes": [ @@ -1814,12 +1833,12 @@ }, "fakeredis": { "hashes": [ - "sha256:9742d6d4673df0f5f6ade4e4eee763b7f3517178ffa82508310325a6305651ec", - "sha256:d780da2519b2e9d741056cf2b68604a4e59286bc6fde78b40a2b2b1367a51b30" + "sha256:69987928d719d1ae1665ae8ebb16199d22a5ebae0b7d0d0d6586fc3a1a67428c", + "sha256:c9baf3c7fd2ebf40db50db4c642c7c76b712b1eed25d91efcc175bba9bc40ca3" ], "index": "pypi", "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==2.18.1" + "version": "==2.20.0" }, "flake8": { "hashes": [ @@ -1875,12 +1894,12 @@ }, "flask": { "hashes": [ - "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc", - "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" + "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", + "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.3.3" + "version": "==3.0.0" }, "freezegun": { "hashes": [ @@ -1950,12 +1969,12 @@ }, "jsonschema": { "hashes": [ - "sha256:043dc26a3845ff09d20e4420d6012a9c91c9aa8999fa184e7efcfeccb41e32cb", - "sha256:6e1e7569ac13be8139b2dd2c21a55d350066ee3f80df06c608b398cdc6f30e8f" + "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392", + "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.19.0" + "version": "==4.19.2" }, "jsonschema-specifications": { "hashes": [ @@ -2093,12 +2112,12 @@ }, "moto": { "hashes": [ - "sha256:2e934d834729b274382055e097b166127db829ab4fae00bb08c031c108391a2c", - "sha256:4caab0145d557d102fe79d0ce3b73d6bf1d916d29ad03c14da15f7da66429cdb" + "sha256:1298006aaa6996b886658eb194cac0e3a5679c9fcce6cb13e741ccc5a7247abb", + "sha256:3e0ef388900448485cd6eff18e9f7fcaa6cf4560b6fb536ba2e2e1278a5ecc59" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==4.2.3" + "version": "==4.2.7" }, "mypy": { "hashes": [ @@ -2144,11 +2163,11 @@ }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==23.2" }, "pathspec": { "hashes": [ @@ -2168,11 +2187,11 @@ }, "platformdirs": { "hashes": [ - "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d", - "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d" + "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", + "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" ], "markers": "python_version >= '3.7'", - "version": "==3.10.0" + "version": "==3.11.0" }, "pluggy": { "hashes": [ @@ -2184,11 +2203,11 @@ }, "pycodestyle": { "hashes": [ - "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0", - "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8" + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" ], "markers": "python_version >= '3.8'", - "version": "==2.11.0" + "version": "==2.11.1" }, "pycparser": { "hashes": [ @@ -2207,12 +2226,12 @@ }, "pylint": { "hashes": [ - "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413", - "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252" + "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", + "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad" ], "index": "pypi", "markers": "python_full_version >= '3.7.2'", - "version": "==2.17.5" + "version": "==2.17.7" }, "pylint-absolute-imports": { "hashes": [ @@ -2239,12 +2258,12 @@ }, "pytest": { "hashes": [ - "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002", - "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069" + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==7.4.2" + "version": "==7.4.3" }, "pytest-cov": { "hashes": [ @@ -2257,21 +2276,21 @@ }, "pytest-flask": { "hashes": [ - "sha256:46fde652f77777bf02dc91205aec4ce20cdf2acbbbd66a918ab91f5c14693d3d", - "sha256:fe25b39ad0db09c3d1fe728edecf97ced85e774c775db259a6d25f0270a4e7c9" + "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", + "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" ], "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==1.2.0" + "markers": "python_version >= '3.7'", + "version": "==1.3.0" }, "pytest-mock": { "hashes": [ - "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39", - "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f" + "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f", + "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.11.1" + "markers": "python_version >= '3.8'", + "version": "==3.12.0" }, "pytest-sugar": { "hashes": [ @@ -2357,12 +2376,12 @@ }, "redis": { "hashes": [ - "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0", - "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120" + "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", + "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==5.0.0" + "version": "==5.0.1" }, "referencing": { "hashes": [ @@ -2383,123 +2402,125 @@ }, "responses": { "hashes": [ - "sha256:205029e1cb334c21cb4ec64fc7599be48b859a0fd381a42443cdd600bfe8b16a", - "sha256:e6fbcf5d82172fecc0aa1860fd91e58cbfd96cee5e96da5b63fa6eb3caa10dd3" + "sha256:060be153c270c06fa4d22c1ef8865fdef43902eb595204deeef736cddb62d353", + "sha256:3df82f7d4dcd3e5f61498181aadb4381f291da25c7506c47fe8cb68ce29203e7" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.23.3" + "markers": "python_version >= '3.8'", + "version": "==0.24.0" }, "rpds-py": { "hashes": [ - "sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9", - "sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637", - "sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5", - "sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9", - "sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c", - "sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957", - "sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6", - "sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154", - "sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8", - "sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569", - "sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2", - "sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c", - "sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453", - "sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1", - "sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7", - "sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b", - "sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9", - "sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496", - "sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3", - "sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b", - "sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6", - "sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da", - "sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842", - "sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd", - "sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294", - "sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71", - "sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093", - "sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e", - "sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c", - "sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e", - "sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b", - "sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97", - "sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac", - "sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf", - "sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a", - "sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc", - "sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0", - "sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623", - "sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6", - "sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8", - "sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b", - "sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d", - "sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff", - "sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb", - "sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08", - "sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33", - "sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5", - "sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a", - "sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a", - "sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762", - "sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860", - "sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73", - "sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a", - "sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065", - "sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c", - "sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c", - "sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec", - "sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515", - "sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72", - "sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd", - "sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec", - "sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12", - "sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1", - "sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557", - "sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25", - "sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff", - "sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8", - "sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9", - "sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d", - "sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a", - "sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e", - "sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c", - "sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0", - "sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48", - "sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e", - "sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314", - "sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717", - "sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95", - "sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8", - "sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57", - "sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33", - "sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1", - "sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f", - "sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f", - "sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9", - "sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599", - "sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41", - "sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391", - "sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475", - "sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882", - "sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740", - "sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f", - "sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee", - "sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836", - "sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c", - "sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a", - "sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb" + "sha256:0525847f83f506aa1e28eb2057b696fe38217e12931c8b1b02198cfe6975e142", + "sha256:05942656cb2cb4989cd50ced52df16be94d344eae5097e8583966a1d27da73a5", + "sha256:0831d3ecdea22e4559cc1793f22e77067c9d8c451d55ae6a75bf1d116a8e7f42", + "sha256:0853da3d5e9bc6a07b2486054a410b7b03f34046c123c6561b535bb48cc509e1", + "sha256:08e6e7ff286254016b945e1ab632ee843e43d45e40683b66dd12b73791366dd1", + "sha256:0a38612d07a36138507d69646c470aedbfe2b75b43a4643f7bd8e51e52779624", + "sha256:0bedd91ae1dd142a4dc15970ed2c729ff6c73f33a40fa84ed0cdbf55de87c777", + "sha256:0c5441b7626c29dbd54a3f6f3713ec8e956b009f419ffdaaa3c80eaf98ddb523", + "sha256:0e9e976e0dbed4f51c56db10831c9623d0fd67aac02853fe5476262e5a22acb7", + "sha256:0fadfdda275c838cba5102c7f90a20f2abd7727bf8f4a2b654a5b617529c5c18", + "sha256:1096ca0bf2d3426cbe79d4ccc91dc5aaa73629b08ea2d8467375fad8447ce11a", + "sha256:171d9a159f1b2f42a42a64a985e4ba46fc7268c78299272ceba970743a67ee50", + "sha256:188912b22b6c8225f4c4ffa020a2baa6ad8fabb3c141a12dbe6edbb34e7f1425", + "sha256:1b4cf9ab9a0ae0cb122685209806d3f1dcb63b9fccdf1424fb42a129dc8c2faa", + "sha256:1e04581c6117ad9479b6cfae313e212fe0dfa226ac727755f0d539cd54792963", + "sha256:1fa73ed22c40a1bec98d7c93b5659cd35abcfa5a0a95ce876b91adbda170537c", + "sha256:2124f9e645a94ab7c853bc0a3644e0ca8ffbe5bb2d72db49aef8f9ec1c285733", + "sha256:240687b5be0f91fbde4936a329c9b7589d9259742766f74de575e1b2046575e4", + "sha256:25740fb56e8bd37692ed380e15ec734be44d7c71974d8993f452b4527814601e", + "sha256:27ccc93c7457ef890b0dd31564d2a05e1aca330623c942b7e818e9e7c2669ee4", + "sha256:281c8b219d4f4b3581b918b816764098d04964915b2f272d1476654143801aa2", + "sha256:2d34a5450a402b00d20aeb7632489ffa2556ca7b26f4a63c35f6fccae1977427", + "sha256:301bd744a1adaa2f6a5e06c98f1ac2b6f8dc31a5c23b838f862d65e32fca0d4b", + "sha256:30e5ce9f501fb1f970e4a59098028cf20676dee64fc496d55c33e04bbbee097d", + "sha256:33ab498f9ac30598b6406e2be1b45fd231195b83d948ebd4bd77f337cb6a2bff", + "sha256:35585a8cb5917161f42c2104567bb83a1d96194095fc54a543113ed5df9fa436", + "sha256:389c0e38358fdc4e38e9995e7291269a3aead7acfcf8942010ee7bc5baee091c", + "sha256:3acadbab8b59f63b87b518e09c4c64b142e7286b9ca7a208107d6f9f4c393c5c", + "sha256:3b7a64d43e2a1fa2dd46b678e00cabd9a49ebb123b339ce799204c44a593ae1c", + "sha256:3c8c0226c71bd0ce9892eaf6afa77ae8f43a3d9313124a03df0b389c01f832de", + "sha256:429349a510da82c85431f0f3e66212d83efe9fd2850f50f339341b6532c62fe4", + "sha256:466030a42724780794dea71eb32db83cc51214d66ab3fb3156edd88b9c8f0d78", + "sha256:47aeceb4363851d17f63069318ba5721ae695d9da55d599b4d6fb31508595278", + "sha256:48aa98987d54a46e13e6954880056c204700c65616af4395d1f0639eba11764b", + "sha256:4b2416ed743ec5debcf61e1242e012652a4348de14ecc7df3512da072b074440", + "sha256:4d0a675a7acbbc16179188d8c6d0afb8628604fc1241faf41007255957335a0b", + "sha256:4eb74d44776b0fb0782560ea84d986dffec8ddd94947f383eba2284b0f32e35e", + "sha256:4f8a1d990dc198a6c68ec3d9a637ba1ce489b38cbfb65440a27901afbc5df575", + "sha256:513ccbf7420c30e283c25c82d5a8f439d625a838d3ba69e79a110c260c46813f", + "sha256:5210a0018c7e09c75fa788648617ebba861ae242944111d3079034e14498223f", + "sha256:54cdfcda59251b9c2f87a05d038c2ae02121219a04d4a1e6fc345794295bdc07", + "sha256:56dd500411d03c5e9927a1eb55621e906837a83b02350a9dc401247d0353717c", + "sha256:57ec6baec231bb19bb5fd5fc7bae21231860a1605174b11585660236627e390e", + "sha256:5f1519b080d8ce0a814f17ad9fb49fb3a1d4d7ce5891f5c85fc38631ca3a8dc4", + "sha256:6174d6ad6b58a6bcf67afbbf1723420a53d06c4b89f4c50763d6fa0a6ac9afd2", + "sha256:68172622a5a57deb079a2c78511c40f91193548e8ab342c31e8cb0764d362459", + "sha256:6915fc9fa6b3ec3569566832e1bb03bd801c12cea030200e68663b9a87974e76", + "sha256:6b75b912a0baa033350367a8a07a8b2d44fd5b90c890bfbd063a8a5f945f644b", + "sha256:6f5dcb658d597410bb7c967c1d24eaf9377b0d621358cbe9d2ff804e5dd12e81", + "sha256:6f8d7fe73d1816eeb5378409adc658f9525ecbfaf9e1ede1e2d67a338b0c7348", + "sha256:7036316cc26b93e401cedd781a579be606dad174829e6ad9e9c5a0da6e036f80", + "sha256:7188ddc1a8887194f984fa4110d5a3d5b9b5cd35f6bafdff1b649049cbc0ce29", + "sha256:761531076df51309075133a6bc1db02d98ec7f66e22b064b1d513bc909f29743", + "sha256:7979d90ee2190d000129598c2b0c82f13053dba432b94e45e68253b09bb1f0f6", + "sha256:8015835494b21aa7abd3b43fdea0614ee35ef6b03db7ecba9beb58eadf01c24f", + "sha256:81c4d1a3a564775c44732b94135d06e33417e829ff25226c164664f4a1046213", + "sha256:81cf9d306c04df1b45971c13167dc3bad625808aa01281d55f3cf852dde0e206", + "sha256:88857060b690a57d2ea8569bca58758143c8faa4639fb17d745ce60ff84c867e", + "sha256:8c567c664fc2f44130a20edac73e0a867f8e012bf7370276f15c6adc3586c37c", + "sha256:91bd2b7cf0f4d252eec8b7046fa6a43cee17e8acdfc00eaa8b3dbf2f9a59d061", + "sha256:9620650c364c01ed5b497dcae7c3d4b948daeae6e1883ae185fef1c927b6b534", + "sha256:9b007c2444705a2dc4a525964fd4dd28c3320b19b3410da6517cab28716f27d3", + "sha256:9bf9acce44e967a5103fcd820fc7580c7b0ab8583eec4e2051aec560f7b31a63", + "sha256:a239303acb0315091d54c7ff36712dba24554993b9a93941cf301391d8a997ee", + "sha256:a2baa6be130e8a00b6cbb9f18a33611ec150b4537f8563bddadb54c1b74b8193", + "sha256:a54917b7e9cd3a67e429a630e237a90b096e0ba18897bfb99ee8bd1068a5fea0", + "sha256:a689e1ded7137552bea36305a7a16ad2b40be511740b80748d3140614993db98", + "sha256:a952ae3eb460c6712388ac2ec706d24b0e651b9396d90c9a9e0a69eb27737fdc", + "sha256:aa32205358a76bf578854bf31698a86dc8b2cb591fd1d79a833283f4a403f04b", + "sha256:b2287c09482949e0ca0c0eb68b2aca6cf57f8af8c6dfd29dcd3bc45f17b57978", + "sha256:b6b0e17d39d21698185097652c611f9cf30f7c56ccec189789920e3e7f1cee56", + "sha256:b710bf7e7ae61957d5c4026b486be593ed3ec3dca3e5be15e0f6d8cf5d0a4990", + "sha256:b8e11715178f3608874508f08e990d3771e0b8c66c73eb4e183038d600a9b274", + "sha256:b92aafcfab3d41580d54aca35a8057341f1cfc7c9af9e8bdfc652f83a20ced31", + "sha256:bec29b801b4adbf388314c0d050e851d53762ab424af22657021ce4b6eb41543", + "sha256:c694bee70ece3b232df4678448fdda245fd3b1bb4ba481fb6cd20e13bb784c46", + "sha256:c6b52b7028b547866c2413f614ee306c2d4eafdd444b1ff656bf3295bf1484aa", + "sha256:cb41ad20064e18a900dd427d7cf41cfaec83bcd1184001f3d91a1f76b3fcea4e", + "sha256:cd316dbcc74c76266ba94eb021b0cc090b97cca122f50bd7a845f587ff4bf03f", + "sha256:ced40cdbb6dd47a032725a038896cceae9ce267d340f59508b23537f05455431", + "sha256:d1c562a9bb72244fa767d1c1ab55ca1d92dd5f7c4d77878fee5483a22ffac808", + "sha256:d389ff1e95b6e46ebedccf7fd1fadd10559add595ac6a7c2ea730268325f832c", + "sha256:d56b1cd606ba4cedd64bb43479d56580e147c6ef3f5d1c5e64203a1adab784a2", + "sha256:d72a4315514e5a0b9837a086cb433b004eea630afb0cc129de76d77654a9606f", + "sha256:d9e7f29c00577aff6b318681e730a519b235af292732a149337f6aaa4d1c5e31", + "sha256:dbc25baa6abb205766fb8606f8263b02c3503a55957fcb4576a6bb0a59d37d10", + "sha256:e57919c32ee295a2fca458bb73e4b20b05c115627f96f95a10f9f5acbd61172d", + "sha256:e5bbe011a2cea9060fef1bb3d668a2fd8432b8888e6d92e74c9c794d3c101595", + "sha256:e6aea5c0eb5b0faf52c7b5c4a47c8bb64437173be97227c819ffa31801fa4e34", + "sha256:e888be685fa42d8b8a3d3911d5604d14db87538aa7d0b29b1a7ea80d354c732d", + "sha256:eebaf8c76c39604d52852366249ab807fe6f7a3ffb0dd5484b9944917244cdbe", + "sha256:efbe0b5e0fd078ed7b005faa0170da4f72666360f66f0bb2d7f73526ecfd99f9", + "sha256:efddca2d02254a52078c35cadad34762adbae3ff01c6b0c7787b59d038b63e0d", + "sha256:f05450fa1cd7c525c0b9d1a7916e595d3041ac0afbed2ff6926e5afb6a781b7f", + "sha256:f12d69d568f5647ec503b64932874dade5a20255736c89936bf690951a5e79f5", + "sha256:f45321224144c25a62052035ce96cbcf264667bcb0d81823b1bbc22c4addd194", + "sha256:f62581d7e884dd01ee1707b7c21148f61f2febb7de092ae2f108743fcbef5985", + "sha256:f8832a4f83d4782a8f5a7b831c47e8ffe164e43c2c148c8160ed9a6d630bc02a", + "sha256:fa35ad36440aaf1ac8332b4a4a433d4acd28f1613f0d480995f5cfd3580e90b7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.12.0" }, "s3transfer": { "hashes": [ - "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084", - "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861" + "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", + "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" ], "markers": "python_version >= '3.7'", - "version": "==0.6.2" + "version": "==0.7.0" }, "six": { "hashes": [ @@ -2534,26 +2555,28 @@ }, "tomlkit": { "hashes": [ - "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86", - "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899" + "sha256:df32fab589a81f0d7dc525a4267b6d7a64ee99619cbd1eeb0fae32c1dd426977", + "sha256:eeea7ac7563faeab0a1ed8fe12c2e5a51c61f933f2502f7e9db0241a65163ad0" ], "markers": "python_version >= '3.7'", - "version": "==0.12.1" + "version": "==0.12.2" }, "types-cachetools": { "hashes": [ - "sha256:595f0342d246c8ba534f5a762cf4c2f60ecb61e8002b8b2277fd5cf791d4e851", - "sha256:f7f8a25bfe306f2e6bc2ad0a2f949d9e72f2d91036d509c36d3810bf728bc6e1" + "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199", + "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0" ], "index": "pypi", - "version": "==5.3.0.6" + "markers": "python_version >= '3.7'", + "version": "==5.3.0.7" }, "types-pyopenssl": { "hashes": [ - "sha256:19536aa3debfbe25a918cf0d898e9f5fbbe6f3594a429da7914bf331deb1b342", - "sha256:6a010dac9ecd42b582d7dd2cc3e9e40486b79b3b64bb2fffba1474ff96af906d" + "sha256:00171433653265843b7469ddb9f3c86d698668064cc33ef10537822156130ebf", + "sha256:5ffb077fe70b699c88d5caab999ae80e192fe28bf6cda7989b7e79b1e4e2dcd3" ], - "version": "==23.2.0.2" + "markers": "python_version >= '3.7'", + "version": "==23.3.0.0" }, "types-python-dateutil": { "hashes": [ @@ -2565,34 +2588,36 @@ }, "types-pytz": { "hashes": [ - "sha256:8e7d2198cba44a72df7628887c90f68a568e1445f14db64631af50c3cab8c090", - "sha256:a660a38ed86d45970603e4f3b4877c7ba947668386a896fb5d9589c17e7b8407" + "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", + "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" ], "index": "pypi", - "version": "==2023.3.1.0" + "version": "==2023.3.1.1" }, "types-pyyaml": { "hashes": [ - "sha256:7d340b19ca28cddfdba438ee638cd4084bde213e501a3978738543e27094775b", - "sha256:a461508f3096d1d5810ec5ab95d7eeecb651f3a15b71959999988942063bf01d" + "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", + "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" ], - "version": "==6.0.12.11" + "version": "==6.0.12.12" }, "types-redis": { "hashes": [ - "sha256:7865a843802937ab2ddca33579c4e255bfe73f87af85824ead7a6729ba92fc52", - "sha256:e0e9dcc530623db3a41ec058ccefdcd5c7582557f02ab5f7aa9a27fe10a78d7e" + "sha256:00f003da884ec3d1d54633186b4cbd587b39782595c5603330cc46a51f9bcf6e", + "sha256:aa7fb5f743534500f274ddf11ab1c910aae1020481865a36b799e1d67de2aaf3" ], "index": "pypi", - "version": "==4.6.0.6" + "markers": "python_version >= '3.7'", + "version": "==4.6.0.10" }, "types-requests": { "hashes": [ - "sha256:938f51653c757716aeca5d72c405c5e2befad8b0d330e3b385ce7f148e1b10dc", - "sha256:d5d7a08965fca12bedf716eaf5430c6e3d0da9f3164a1dba2a7f3885f9ebe3c0" + "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", + "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" ], "index": "pypi", - "version": "==2.31.0.3" + "markers": "python_version >= '3.7'", + "version": "==2.31.0.10" }, "types-simplejson": { "hashes": [ @@ -2602,13 +2627,6 @@ "index": "pypi", "version": "==3.19.0.2" }, - "types-urllib3": { - "hashes": [ - "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f", - "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e" - ], - "version": "==1.26.25.14" - }, "typing-extensions": { "hashes": [ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", @@ -2619,100 +2637,95 @@ }, "urllib3": { "hashes": [ - "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", - "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.16" + "markers": "python_version >= '3.7'", + "version": "==2.0.7" }, "werkzeug": { "hashes": [ - "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8", - "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528" + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" ], "markers": "python_version >= '3.8'", - "version": "==2.3.7" + "version": "==3.0.1" }, "wrapt": { "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" + "markers": "python_version >= '3.6'", + "version": "==1.16.0" }, "xmltodict": { "hashes": [ diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 85ac35f5e2..ca60c4d81c 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,40 +8,40 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-05-18 10:40+0100\n" +"POT-Creation-Date: 2023-11-10 11:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.12.1\n" +"Generated-By: Babel 2.13.1\n" -#: app/forms/validators.py:377 app/jinja_filters.py:119 +#: app/forms/validators.py:389 app/jinja_filters.py:111 #, python-format msgid "%(num)s year" msgid_plural "%(num)s years" msgstr[0] "" msgstr[1] "" -#: app/forms/validators.py:381 app/jinja_filters.py:127 +#: app/forms/validators.py:393 app/jinja_filters.py:119 #, python-format msgid "%(num)s month" msgid_plural "%(num)s months" msgstr[0] "" msgstr[1] "" -#: app/jinja_filters.py:169 +#: app/jinja_filters.py:161 #, python-format msgid "%(date)s at %(time)s" msgstr "" -#: app/jinja_filters.py:183 +#: app/jinja_filters.py:175 #, python-format msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:716 +#: app/jinja_filters.py:733 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:25 msgid "No answer provided" @@ -58,7 +58,7 @@ msgid "Select an answer to ‘%(question_title)s’leave this page or close your " "browser if using a shared device" msgstr "" -#: app/questionnaire/placeholder_transforms.py:158 +#: app/questionnaire/placeholder_transforms.py:202 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:164 +#: app/questionnaire/placeholder_transforms.py:208 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:169 +#: app/questionnaire/placeholder_transforms.py:213 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:155 +#: app/routes/errors.py:160 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:158 +#: app/routes/errors.py:163 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:176 +#: app/routes/errors.py:181 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:179 +#: app/routes/errors.py:184 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:215 +#: app/routes/errors.py:232 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:222 +#: app/routes/errors.py:239 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:225 app/routes/errors.py:250 app/routes/errors.py:272 +#: app/routes/errors.py:242 app/routes/errors.py:267 app/routes/errors.py:289 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:246 +#: app/routes/errors.py:263 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:247 +#: app/routes/errors.py:264 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:268 templates/errors/403.html:3 +#: app/routes/errors.py:285 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:269 +#: app/routes/errors.py:286 msgid "You can try to submit your feedback again." msgstr "" @@ -396,75 +396,76 @@ msgstr "" msgid "Save and exit survey" msgstr "" -#: app/views/contexts/hub_context.py:15 +#: app/views/contexts/hub_context.py:16 msgid "Completed" msgstr "" -#: app/views/contexts/hub_context.py:17 +#: app/views/contexts/hub_context.py:18 msgid "View answers" msgstr "" -#: app/views/contexts/hub_context.py:18 +#: app/views/contexts/hub_context.py:19 msgid "View answers for {section_name}" msgstr "" -#: app/views/contexts/hub_context.py:22 +#: app/views/contexts/hub_context.py:23 msgid "Partially completed" msgstr "" -#: app/views/contexts/hub_context.py:24 +#: app/views/contexts/hub_context.py:25 msgid "Continue with section" msgstr "" -#: app/views/contexts/hub_context.py:25 +#: app/views/contexts/hub_context.py:26 msgid "Continue with {section_name} section" msgstr "" -#: app/views/contexts/hub_context.py:29 +#: app/views/contexts/hub_context.py:30 msgid "Not started" msgstr "" -#: app/views/contexts/hub_context.py:31 +#: app/views/contexts/hub_context.py:32 msgid "Start section" msgstr "" -#: app/views/contexts/hub_context.py:32 +#: app/views/contexts/hub_context.py:33 msgid "Start {section_name} section" msgstr "" -#: app/views/contexts/hub_context.py:36 +#: app/views/contexts/hub_context.py:37 msgid "Separate census requested" msgstr "" -#: app/views/contexts/hub_context.py:38 app/views/contexts/hub_context.py:39 +#: app/views/contexts/hub_context.py:39 app/views/contexts/hub_context.py:40 msgid "Change or resend" msgstr "" -#: app/views/contexts/hub_context.py:51 app/views/contexts/hub_context.py:52 +#: app/views/contexts/hub_context.py:52 app/views/contexts/hub_context.py:53 msgid "Submit survey" msgstr "" -#: app/views/contexts/hub_context.py:56 +#: app/views/contexts/hub_context.py:57 msgid "You must submit this survey to complete it" msgstr "" -#: app/views/contexts/hub_context.py:63 +#: app/views/contexts/hub_context.py:64 msgid "Choose another section to complete" msgstr "" -#: app/views/contexts/hub_context.py:64 templates/confirm-email.html:25 +#: app/views/contexts/hub_context.py:65 templates/confirm-email.html:25 #: templates/individual_response/confirmation-post.html:21 #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/sectionsummary.html:10 templates/sectionsummary.html:48 +#: templates/listcollectorcontent.html:6 templates/sectionsummary.html:10 +#: templates/sectionsummary.html:48 msgid "Continue" msgstr "" -#: app/views/contexts/list_context.py:109 +#: app/views/contexts/list_context.py:125 msgid " (You)" msgstr "" -#: app/views/contexts/preview_context.py:66 +#: app/views/contexts/preview_context.py:75 msgid "Preview survey questions" msgstr "" @@ -526,27 +527,27 @@ msgstr "" msgid "You can answer with one of the following options:" msgstr "" -#: app/views/handlers/confirm_email.py:38 +#: app/views/handlers/confirm_email.py:44 msgid "Yes, send the confirmation email" msgstr "" -#: app/views/handlers/confirm_email.py:68 +#: app/views/handlers/confirm_email.py:74 msgid "Confirm your email address" msgstr "" -#: app/views/handlers/confirm_email.py:89 +#: app/views/handlers/confirm_email.py:95 msgid "Is this email address correct?" msgstr "" -#: app/views/handlers/confirm_email.py:102 -#: app/views/handlers/confirm_email.py:103 -#: app/views/handlers/individual_response.py:905 +#: app/views/handlers/confirm_email.py:108 +#: app/views/handlers/confirm_email.py:109 +#: app/views/handlers/individual_response.py:930 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:129 +#: app/views/handlers/confirm_email.py:135 #: app/views/handlers/confirmation_email.py:67 -#: app/views/handlers/feedback.py:84 app/views/handlers/question.py:185 +#: app/views/handlers/feedback.py:85 app/views/handlers/question.py:196 msgid "Error: {page_title}" msgstr "" @@ -558,176 +559,176 @@ msgstr "" msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:140 +#: app/views/handlers/feedback.py:141 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:146 app/views/handlers/feedback.py:170 +#: app/views/handlers/feedback.py:147 app/views/handlers/feedback.py:171 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:149 app/views/handlers/feedback.py:150 +#: app/views/handlers/feedback.py:150 app/views/handlers/feedback.py:151 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:151 +#: app/views/handlers/feedback.py:152 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:157 +#: app/views/handlers/feedback.py:157 app/views/handlers/feedback.py:158 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:163 +#: app/views/handlers/feedback.py:161 app/views/handlers/feedback.py:164 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:178 app/views/handlers/feedback.py:188 +#: app/views/handlers/feedback.py:179 app/views/handlers/feedback.py:189 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:179 +#: app/views/handlers/feedback.py:180 msgid "Do not include confidential information, such as your contact details" msgstr "" -#: app/views/handlers/individual_response.py:148 +#: app/views/handlers/individual_response.py:149 msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:267 +#: app/views/handlers/individual_response.py:272 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:343 +#: app/views/handlers/individual_response.py:346 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:367 +#: app/views/handlers/individual_response.py:370 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:369 +#: app/views/handlers/individual_response.py:372 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:377 +#: app/views/handlers/individual_response.py:380 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:379 +#: app/views/handlers/individual_response.py:382 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:388 +#: app/views/handlers/individual_response.py:391 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:390 +#: app/views/handlers/individual_response.py:393 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:393 +#: app/views/handlers/individual_response.py:396 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:439 +#: app/views/handlers/individual_response.py:442 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:470 +#: app/views/handlers/individual_response.py:473 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:486 +#: app/views/handlers/individual_response.py:489 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:492 +#: app/views/handlers/individual_response.py:495 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:496 +#: app/views/handlers/individual_response.py:499 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:502 +#: app/views/handlers/individual_response.py:505 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:545 +#: app/views/handlers/individual_response.py:557 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:611 +#: app/views/handlers/individual_response.py:622 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:620 +#: app/views/handlers/individual_response.py:631 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:627 +#: app/views/handlers/individual_response.py:638 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:640 +#: app/views/handlers/individual_response.py:651 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:646 +#: app/views/handlers/individual_response.py:657 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:679 +#: app/views/handlers/individual_response.py:694 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:734 -#: app/views/handlers/individual_response.py:772 +#: app/views/handlers/individual_response.py:757 +#: app/views/handlers/individual_response.py:795 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:738 +#: app/views/handlers/individual_response.py:761 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:796 +#: app/views/handlers/individual_response.py:819 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:809 +#: app/views/handlers/individual_response.py:832 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:810 +#: app/views/handlers/individual_response.py:833 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:843 +#: app/views/handlers/individual_response.py:868 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:892 +#: app/views/handlers/individual_response.py:917 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:901 +#: app/views/handlers/individual_response.py:926 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:939 +#: app/views/handlers/individual_response.py:968 msgid "Confirm mobile number" msgstr "" -#: app/views/handlers/thank_you.py:26 templates/census-thank-you.html:24 +#: app/views/handlers/thank_you.py:27 templates/census-thank-you.html:24 msgid "Thank you for completing the census" msgstr "" @@ -863,7 +864,7 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:23 templates/partials/question.html:49 +#: templates/interstitial.html:23 templates/partials/question.html:50 msgid "If you can’t answer questions for this person" msgstr "" @@ -921,14 +922,14 @@ msgstr "" msgid "Save questions as PDF" msgstr "" -#: templates/partials/introduction/preview.html:37 -#: templates/partials/summary/collapsible-summary.html:51 +#: templates/partials/introduction/preview.html:36 +#: templates/partials/summary/collapsible-summary.html:50 #: templates/preview.html:92 msgid "Show all" msgstr "" -#: templates/partials/introduction/preview.html:38 -#: templates/partials/summary/collapsible-summary.html:52 +#: templates/partials/introduction/preview.html:37 +#: templates/partials/summary/collapsible-summary.html:51 #: templates/preview.html:93 msgid "Hide all" msgstr "" @@ -993,19 +994,19 @@ msgid "" "your answers for your records." msgstr "" -#: templates/layouts/_base.html:157 templates/thank-you.html:71 +#: templates/layouts/_base.html:143 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/layouts/_base.html:158 templates/thank-you.html:72 +#: templates/layouts/_base.html:144 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:159 templates/thank-you.html:73 +#: templates/layouts/_base.html:145 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/layouts/_base.html:160 templates/thank-you.html:74 +#: templates/layouts/_base.html:146 templates/thank-you.html:74 msgid "seconds" msgstr "" @@ -1255,63 +1256,63 @@ msgid "" "href='{contact_us_url}'>contact us" msgstr "" -#: templates/layouts/_base.html:20 +#: templates/layouts/_base.html:13 msgid "Previous" msgstr "" -#: templates/layouts/_base.html:69 +#: templates/layouts/_base.html:62 msgid "Tell us whether you accept cookies" msgstr "" -#: templates/layouts/_base.html:70 +#: templates/layouts/_base.html:63 msgid "" "We use cookies to collect information" " about how you use {cookie_domain}. We use this information to make the " "website work as well as possible and improve our services." msgstr "" -#: templates/layouts/_base.html:71 +#: templates/layouts/_base.html:64 msgid "" "You’ve accepted all cookies. You can change your cookie preferences at any " "time." msgstr "" -#: templates/layouts/_base.html:72 +#: templates/layouts/_base.html:65 msgid "Accept all cookies" msgstr "" -#: templates/layouts/_base.html:73 +#: templates/layouts/_base.html:66 msgid "Set cookie preferences" msgstr "" -#: templates/layouts/_base.html:74 +#: templates/layouts/_base.html:67 msgid "Hide" msgstr "" -#: templates/layouts/_base.html:130 +#: templates/layouts/_base.html:113 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:152 +#: templates/layouts/_base.html:138 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:153 +#: templates/layouts/_base.html:139 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:154 +#: templates/layouts/_base.html:140 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:155 +#: templates/layouts/_base.html:141 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:156 +#: templates/layouts/_base.html:142 msgid "Continue survey" msgstr "" @@ -1331,7 +1332,7 @@ msgstr "" msgid "Exit" msgstr "" -#: templates/macros/helpers.html:13 +#: templates/macros/helpers.html:15 msgid "Interviewer note:" msgstr "" @@ -1382,7 +1383,7 @@ msgid "" msgstr "" #: templates/partials/preview-question.html:34 -#: templates/partials/question.html:85 +#: templates/partials/question.html:93 msgid "Or" msgstr "" @@ -1390,19 +1391,19 @@ msgstr "" msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:76 +#: templates/partials/question.html:84 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:77 +#: templates/partials/question.html:85 msgid "cleared" msgstr "" -#: templates/partials/question.html:80 +#: templates/partials/question.html:88 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:81 templates/partials/question.html:89 +#: templates/partials/question.html:89 templates/partials/question.html:97 msgid "deselected" msgstr "" diff --git a/package.json b/package.json index 59c5b48f79..83740d94d6 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,11 @@ "wdio": "wdio run ./tests/functional/wdio.conf.js" }, "devDependencies": { - "@babel/core": "^7.22.9", - "@babel/plugin-transform-runtime": "^7.22.9", - "@babel/preset-env": "^7.22.9", + "@babel/core": "^7.23.2", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", "@babel/register": "^7.22.5", - "@babel/runtime": "^7.22.6", + "@babel/runtime": "^7.23.2", "@wdio/cli": "^8.14.4", "@wdio/local-runner": "^8.14.3", "@wdio/mocha-framework": "^8.14.0", diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 8fd43951c8..2d8275cadb 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -8,7 +8,8 @@ from mock import MagicMock from mock.mock import Mock from requests.adapters import ConnectTimeoutError, ReadTimeoutError -from urllib3.connectionpool import HTTPConnectionPool, HTTPResponse +from urllib3.connectionpool import HTTPConnectionPool +from urllib3.response import HTTPResponse from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 42adc25b92..e36b72d913 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -146,7 +146,7 @@ def patch_gcs_client(mocker): @pytest.fixture def gcs_blob_with_retry(mocker): - blob = Blob(name="some-blob", bucket=mocker.Mock()) + blob = Blob(name="some-blob", bucket=mocker.MagicMock()) response_503 = Response() response_503.status_code = 503 diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 2df35289c2..04b06cc7b2 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -5,7 +5,8 @@ import responses from mock import Mock, patch from requests import RequestException -from urllib3.connectionpool import HTTPConnectionPool, HTTPResponse +from urllib3.connectionpool import HTTPConnectionPool +from urllib3.response import HTTPResponse from app.questionnaire import QuestionnaireSchema from app.setup import create_app @@ -257,18 +258,11 @@ def get_mocked_make_request(mocker, status_codes): mocked_responses.append(response) - patched_make_request = mocker.patch.object( + return mocker.patch.object( HTTPConnectionPool, "_make_request", side_effect=mocked_responses, ) - mocker.patch.object( - HTTPResponse, - "from_httplib", - side_effect=mocked_responses, - ) - - return patched_make_request def test_load_schema_from_url_retries_timeout_error(mocked_make_request_with_timeout): diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 2ab0066a91..1bbfef0255 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -340,7 +340,7 @@ def deleteCookie(self): """ Deletes the test client cookie """ - self._client.delete_cookie("localhost", "session") + self._client.delete_cookie(domain="localhost", key="session") def getHtmlSoup(self): """ diff --git a/yarn.lock b/yarn.lock index 04379303a1..07ff888752 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,38 +27,51 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" - integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== +"@babel/compat-data@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" + integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== + +"@babel/core@^7.23.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" + integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.9" - "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.6" - "@babel/parser" "^7.22.7" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.8" - "@babel/types" "^7.22.5" - convert-source-map "^1.7.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.3" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.3" + "@babel/types" "^7.23.3" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" + json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.22.7", "@babel/generator@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" - integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== +"@babel/generator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" + integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.23.3" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -70,14 +83,25 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" - integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" -"@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": +"@babel/helper-compilation-targets@^7.22.6": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== @@ -88,15 +112,15 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" - integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== +"@babel/helper-create-class-features-plugin@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -112,10 +136,19 @@ regexpu-core "^5.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" - integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== +"@babel/helper-create-regexp-features-plugin@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz#a71c10f7146d809f4a256c373f462d9bba8cf6ba" + integrity sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -123,6 +156,11 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" @@ -136,6 +174,14 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -143,6 +189,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-member-expression-to-functions@^7.22.15": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== + dependencies: + "@babel/types" "^7.23.0" + "@babel/helper-member-expression-to-functions@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" @@ -150,23 +203,23 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" - integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -180,16 +233,25 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" - integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== +"@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.9" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" + +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" -"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": +"@babel/helper-replace-supers@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== @@ -224,33 +286,52 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== -"@babel/helper-wrap-function@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" - integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== dependencies: "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" -"@babel/helpers@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" - integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== +"@babel/helpers@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" + integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.6" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" "@babel/highlight@^7.22.5": version "7.22.5" @@ -261,40 +342,45 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.22.5", "@babel/parser@^7.22.7": +"@babel/parser@^7.22.15", "@babel/parser@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" + integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== + +"@babel/parser@^7.22.5": version "7.22.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" - integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" - integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz#20c60d4639d18f7da8602548512e9d3a4c8d7098" + integrity sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -330,17 +416,17 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" - integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== +"@babel/plugin-syntax-import-assertions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-attributes@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" - integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== +"@babel/plugin-syntax-import-attributes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" + integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -422,211 +508,211 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" - integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.7": - version "7.22.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b" - integrity sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg== +"@babel/plugin-transform-async-generator-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.3.tgz#9df2627bad7f434ed13eef3e61b2b65cafd4885b" + integrity sha512-59GsVNavGxAXCDDbakWSMJhajASb4kBCqDjqJsv+p5nKdbz7istmZ3HrX3L2LuiI80+zsOADCvooqQH3qGCucQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-async-to-generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" - integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== +"@babel/plugin-transform-async-to-generator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== dependencies: - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" -"@babel/plugin-transform-block-scoped-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" - integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== +"@babel/plugin-transform-block-scoped-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" - integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== +"@babel/plugin-transform-block-scoping@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz#e99a3ff08f58edd28a8ed82481df76925a4ffca7" + integrity sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" - integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== +"@babel/plugin-transform-class-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" + integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" - integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== +"@babel/plugin-transform-class-static-block@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.3.tgz#56f2371c7e5bf6ff964d84c5dc4d4db5536b5159" + integrity sha512-PENDVxdr7ZxKPyi5Ffc0LjXdnJyrJxyqF5T5YjlVg4a0VFfQHW0r8iAtRiDXkfHlu1wwcvdtnndGYIeJLSuRMQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" - integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== +"@babel/plugin-transform-classes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz#73380c632c095b03e8503c24fd38f95ad41ffacb" + integrity sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" - integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== +"@babel/plugin-transform-computed-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.5" + "@babel/template" "^7.22.15" -"@babel/plugin-transform-destructuring@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" - integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== +"@babel/plugin-transform-destructuring@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" - integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== +"@babel/plugin-transform-dotall-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-duplicate-keys@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" - integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" - integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== +"@babel/plugin-transform-dynamic-import@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.3.tgz#82625924da9ed5fb11a428efb02e43bc9a3ab13e" + integrity sha512-vTG+cTGxPFou12Rj7ll+eD5yWeNl5/8xvQvF08y5Gv3v4mZQoyFf8/n9zg4q5vvCWt5jmgymfzMAldO7orBn7A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-transform-exponentiation-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" - integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== +"@babel/plugin-transform-exponentiation-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" - integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== +"@babel/plugin-transform-export-namespace-from@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.3.tgz#dcd066d995f6ac6077e5a4ccb68322a01e23ac49" + integrity sha512-yCLhW34wpJWRdTxxWtFZASJisihrfyMOTOQexhVzA78jlU+dH7Dw+zQgcPepQ5F3C6bAIiblZZ+qBggJdHiBAg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" - integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== +"@babel/plugin-transform-for-of@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz#afe115ff0fbce735e02868d41489093c63e15559" + integrity sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" - integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== +"@babel/plugin-transform-function-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== dependencies: - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" - integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== +"@babel/plugin-transform-json-strings@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.3.tgz#489724ab7d3918a4329afb4172b2fd2cf3c8d245" + integrity sha512-H9Ej2OiISIZowZHaBwF0tsJOih1PftXJtE8EWqlEIwpc7LMTGq0rPOrywKLQ4nefzx8/HMR0D3JGXoMHYvhi0A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" - integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== +"@babel/plugin-transform-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" - integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== +"@babel/plugin-transform-logical-assignment-operators@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.3.tgz#3a406d6083feb9487083bca6d2334a3c9b6c4808" + integrity sha512-+pD5ZbxofyOygEp+zZAfujY2ShNCXRpDRIPOiBmTO693hhyOEteZgl876Xs9SAHPQpcV0vz8LvA/T+w8AzyX8A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" - integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== +"@babel/plugin-transform-member-expression-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-amd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" - integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== +"@babel/plugin-transform-modules-amd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== +"@babel/plugin-transform-modules-commonjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" - integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== +"@babel/plugin-transform-modules-systemjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" + integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" -"@babel/plugin-transform-modules-umd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" - integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== +"@babel/plugin-transform-modules-umd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": @@ -637,210 +723,211 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-new-target@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" - integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== +"@babel/plugin-transform-new-target@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" - integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== +"@babel/plugin-transform-nullish-coalescing-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.3.tgz#8a613d514b521b640344ed7c56afeff52f9413f8" + integrity sha512-xzg24Lnld4DYIdysyf07zJ1P+iIfJpxtVFOzX4g+bsJ3Ng5Le7rXx9KwqKzuyaUeRnt+I1EICwQITqc0E2PmpA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" - integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== +"@babel/plugin-transform-numeric-separator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.3.tgz#2f8da42b75ba89e5cfcd677afd0856d52c0c2e68" + integrity sha512-s9GO7fIBi/BLsZ0v3Rftr6Oe4t0ctJ8h4CCXfPoEJwmvAPMyNrfkOOJzm6b9PX9YXcCJWWQd/sBF/N26eBiMVw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" - integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== +"@babel/plugin-transform-object-rest-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.3.tgz#509373753b5f7202fe1940e92fd075bd7874955f" + integrity sha512-VxHt0ANkDmu8TANdE9Kc0rndo/ccsmfe2Cx2y5sI4hu3AukHQ5wAu4cM7j3ba8B9548ijVyclBU+nuDQftZsog== dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/compat-data" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-parameters" "^7.23.3" -"@babel/plugin-transform-object-super@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" - integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== +"@babel/plugin-transform-object-super@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" -"@babel/plugin-transform-optional-catch-binding@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" - integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== +"@babel/plugin-transform-optional-catch-binding@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.3.tgz#362c0b545ee9e5b0fa9d9e6fe77acf9d4c480027" + integrity sha512-LxYSb0iLjUamfm7f1D7GpiS4j0UAC8AOiehnsGAP8BEsIX8EOi3qV6bbctw8M7ZvLtcoZfZX5Z7rN9PlWk0m5A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" - integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== +"@babel/plugin-transform-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz#92fc83f54aa3adc34288933fa27e54c13113f4be" + integrity sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" - integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== +"@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-methods@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" - integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== +"@babel/plugin-transform-private-methods@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" + integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" - integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== +"@babel/plugin-transform-private-property-in-object@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.3.tgz#5cd34a2ce6f2d008cc8f91d8dcc29e2c41466da6" + integrity sha512-a5m2oLNFyje2e/rGKjVfAELTVI5mbA0FeZpBnkOWWV7eSmKQ+T/XW0Vf+29ScLzSxX+rnsarvU0oie/4m6hkxA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" - integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== +"@babel/plugin-transform-property-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-regenerator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" - integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== +"@babel/plugin-transform-regenerator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - regenerator-transform "^0.15.1" + regenerator-transform "^0.15.2" -"@babel/plugin-transform-reserved-words@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" - integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== +"@babel/plugin-transform-reserved-words@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-runtime@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz#a87b11e170cbbfb018e6a2bf91f5c6e533b9e027" - integrity sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ== +"@babel/plugin-transform-runtime@^7.23.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.3.tgz#0aa7485862b0b5cb0559c1a5ec08b4923743ee3b" + integrity sha512-XcQ3X58CKBdBnnZpPaQjgVMePsXtSZzHoku70q9tUAQp02ggPQNM04BF3RvlW1GSM/McbSOQAzEK4MXbS7/JFg== dependencies: - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.4" - babel-plugin-polyfill-corejs3 "^0.8.2" - babel-plugin-polyfill-regenerator "^0.5.1" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" - integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== +"@babel/plugin-transform-shorthand-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" - integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== +"@babel/plugin-transform-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" -"@babel/plugin-transform-sticky-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" - integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== +"@babel/plugin-transform-sticky-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-template-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" - integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== +"@babel/plugin-transform-template-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typeof-symbol@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" - integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== +"@babel/plugin-transform-typeof-symbol@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-escapes@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" - integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== +"@babel/plugin-transform-unicode-escapes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-property-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" - integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== +"@babel/plugin-transform-unicode-property-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" + integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" - integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== +"@babel/plugin-transform-unicode-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-sets-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" - integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== +"@babel/plugin-transform-unicode-sets-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" + integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.9.tgz#57f17108eb5dfd4c5c25a44c1977eba1df310ac7" - integrity sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g== +"@babel/preset-env@^7.23.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.3.tgz#d299e0140a7650684b95c62be2db0ef8c975143e" + integrity sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q== dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.9" + "@babel/compat-data" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.3" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.22.5" - "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-assertions" "^7.23.3" + "@babel/plugin-syntax-import-attributes" "^7.23.3" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -852,70 +939,67 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.7" - "@babel/plugin-transform-async-to-generator" "^7.22.5" - "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.5" - "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.5" - "@babel/plugin-transform-classes" "^7.22.6" - "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.5" - "@babel/plugin-transform-dotall-regex" "^7.22.5" - "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.5" - "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.5" - "@babel/plugin-transform-for-of" "^7.22.5" - "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.5" - "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" - "@babel/plugin-transform-member-expression-literals" "^7.22.5" - "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.5" - "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + "@babel/plugin-transform-async-generator-functions" "^7.23.3" + "@babel/plugin-transform-async-to-generator" "^7.23.3" + "@babel/plugin-transform-block-scoped-functions" "^7.23.3" + "@babel/plugin-transform-block-scoping" "^7.23.3" + "@babel/plugin-transform-class-properties" "^7.23.3" + "@babel/plugin-transform-class-static-block" "^7.23.3" + "@babel/plugin-transform-classes" "^7.23.3" + "@babel/plugin-transform-computed-properties" "^7.23.3" + "@babel/plugin-transform-destructuring" "^7.23.3" + "@babel/plugin-transform-dotall-regex" "^7.23.3" + "@babel/plugin-transform-duplicate-keys" "^7.23.3" + "@babel/plugin-transform-dynamic-import" "^7.23.3" + "@babel/plugin-transform-exponentiation-operator" "^7.23.3" + "@babel/plugin-transform-export-namespace-from" "^7.23.3" + "@babel/plugin-transform-for-of" "^7.23.3" + "@babel/plugin-transform-function-name" "^7.23.3" + "@babel/plugin-transform-json-strings" "^7.23.3" + "@babel/plugin-transform-literals" "^7.23.3" + "@babel/plugin-transform-logical-assignment-operators" "^7.23.3" + "@babel/plugin-transform-member-expression-literals" "^7.23.3" + "@babel/plugin-transform-modules-amd" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.3" + "@babel/plugin-transform-modules-umd" "^7.23.3" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" - "@babel/plugin-transform-numeric-separator" "^7.22.5" - "@babel/plugin-transform-object-rest-spread" "^7.22.5" - "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.6" - "@babel/plugin-transform-parameters" "^7.22.5" - "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.5" - "@babel/plugin-transform-property-literals" "^7.22.5" - "@babel/plugin-transform-regenerator" "^7.22.5" - "@babel/plugin-transform-reserved-words" "^7.22.5" - "@babel/plugin-transform-shorthand-properties" "^7.22.5" - "@babel/plugin-transform-spread" "^7.22.5" - "@babel/plugin-transform-sticky-regex" "^7.22.5" - "@babel/plugin-transform-template-literals" "^7.22.5" - "@babel/plugin-transform-typeof-symbol" "^7.22.5" - "@babel/plugin-transform-unicode-escapes" "^7.22.5" - "@babel/plugin-transform-unicode-property-regex" "^7.22.5" - "@babel/plugin-transform-unicode-regex" "^7.22.5" - "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.4" - babel-plugin-polyfill-corejs3 "^0.8.2" - babel-plugin-polyfill-regenerator "^0.5.1" + "@babel/plugin-transform-new-target" "^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.3" + "@babel/plugin-transform-numeric-separator" "^7.23.3" + "@babel/plugin-transform-object-rest-spread" "^7.23.3" + "@babel/plugin-transform-object-super" "^7.23.3" + "@babel/plugin-transform-optional-catch-binding" "^7.23.3" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-private-methods" "^7.23.3" + "@babel/plugin-transform-private-property-in-object" "^7.23.3" + "@babel/plugin-transform-property-literals" "^7.23.3" + "@babel/plugin-transform-regenerator" "^7.23.3" + "@babel/plugin-transform-reserved-words" "^7.23.3" + "@babel/plugin-transform-shorthand-properties" "^7.23.3" + "@babel/plugin-transform-spread" "^7.23.3" + "@babel/plugin-transform-sticky-regex" "^7.23.3" + "@babel/plugin-transform-template-literals" "^7.23.3" + "@babel/plugin-transform-typeof-symbol" "^7.23.3" + "@babel/plugin-transform-unicode-escapes" "^7.23.3" + "@babel/plugin-transform-unicode-property-regex" "^7.23.3" + "@babel/plugin-transform-unicode-regex" "^7.23.3" + "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" core-js-compat "^3.31.0" semver "^6.3.1" -"@babel/preset-modules@^0.1.5": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6.tgz#31bcdd8f19538437339d17af00d177d854d9d458" - integrity sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg== +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" esutils "^2.0.2" @@ -935,13 +1019,29 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.22.6", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.8.4": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== dependencies: regenerator-runtime "^0.13.11" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/template@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -951,22 +1051,31 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": - version "7.22.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" - integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== +"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" + integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.7" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.3" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.7" - "@babel/types" "^7.22.5" + "@babel/parser" "^7.23.3" + "@babel/types" "^7.23.3" debug "^4.1.0" globals "^11.1.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.23.0", "@babel/types@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" + integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@babel/types@^7.22.5", "@babel/types@^7.4.4": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -1718,29 +1827,29 @@ b4a@^1.6.4: resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== -babel-plugin-polyfill-corejs2@^0.4.4: - version "0.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" - integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== +babel-plugin-polyfill-corejs2@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz#b2df0251d8e99f229a8e60fc4efa9a68b41c8313" + integrity sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.4.3" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" - integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== +babel-plugin-polyfill-corejs3@^0.8.5: + version "0.8.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz#25c2d20002da91fe328ff89095c85a391d6856cf" + integrity sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" - core-js-compat "^3.31.0" + "@babel/helper-define-polyfill-provider" "^0.4.3" + core-js-compat "^3.33.1" -babel-plugin-polyfill-regenerator@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" - integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== +babel-plugin-polyfill-regenerator@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz#d4c49e4b44614607c13fb769bcd85c72bb26a4a5" + integrity sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.4.3" balanced-match@^1.0.0: version "1.0.2" @@ -1836,6 +1945,16 @@ browserslist@^4.21.9: node-releases "^2.0.13" update-browserslist-db "^1.0.11" +browserslist@^4.22.1: + version "4.22.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -1953,6 +2072,11 @@ caniuse-lite@^1.0.30001517: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== +caniuse-lite@^1.0.30001541: + version "1.0.30001561" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" + integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== + chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -1976,7 +2100,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2169,10 +2293,10 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== core-js-compat@^3.31.0: version "3.32.0" @@ -2181,6 +2305,13 @@ core-js-compat@^3.31.0: dependencies: browserslist "^4.21.9" +core-js-compat@^3.33.1: + version "3.33.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.2.tgz#3ea4563bfd015ad4e4b52442865b02c62aba5085" + integrity sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw== + dependencies: + browserslist "^4.22.1" + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -2436,6 +2567,11 @@ electron-to-chromium@^1.4.477: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.482.tgz#77c5ed37b93d4dda860e27538e0e2a01d6a19e02" integrity sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA== +electron-to-chromium@^1.4.535: + version "1.4.580" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.580.tgz#2f8f70f70733a6be1fb6f31de1224e6dc4bb196d" + integrity sha512-T5q3pjQon853xxxHUq3ZP68ZpvJHuSMY2+BZaW3QzjS4HvNuvsMmZ/+lU+nCrftre1jFZ+OSlExynXWBihnXzw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3858,7 +3994,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.2: +json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -4997,10 +5133,15 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" @@ -5749,6 +5890,14 @@ update-browserslist-db@^1.0.11: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" From 653811f41a8ac264589e770c0fcde91c2b279188 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:56:09 +0000 Subject: [PATCH 329/567] Revert "Add extra logging around session store" (#1248) This reverts commit 9e2750e70fde3ba2a3be1604c2d9c524b1a75794. --- app/authentication/authenticator.py | 8 -------- app/globals.py | 13 ------------- 2 files changed, 21 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 21b2cd5621..5f2ac068e2 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -118,14 +118,6 @@ def load_user(extend_session: bool = True) -> Optional[User]: return user - if not session_store: - logger.info("session store doesn't exist") # pragma: no cover - - elif not _is_session_valid(session_store): - logger.info( - "session has expired", session_expiry=session_store.expiration_time - ) # pragma: no cover - logger.info("session does not exist") cookie_session.pop(USER_IK, None) diff --git a/app/globals.py b/app/globals.py index 5b0b98c8b1..98ab3006a5 100644 --- a/app/globals.py +++ b/app/globals.py @@ -33,12 +33,6 @@ def get_questionnaire_store(user_id: str, user_ik: str) -> QuestionnaireStore: def get_session_store() -> SessionStore | None: if USER_IK not in cookie_session or EQ_SESSION_ID not in cookie_session: - if USER_IK not in cookie_session: - logger.info("user ik not found in cookie session") # pragma: no cover - - if EQ_SESSION_ID not in cookie_session: - logger.info("eq session id not found in cookie session") # pragma: no cover - return None # Sets up a single SessionStore instance per request context. @@ -54,9 +48,6 @@ def get_session_store() -> SessionStore | None: cookie_session[USER_IK], pepper, cookie_session[EQ_SESSION_ID] ) - if not store.session_data: - logger.info("session data not found") # pragma: no cover - return store if store.session_data else None @@ -66,11 +57,7 @@ def get_session_timeout_in_seconds(schema: QuestionnaireSchema) -> int: :return: Timeout in seconds """ default_session_timeout = current_app.config["EQ_SESSION_TIMEOUT_SECONDS"] - logger.info("default session timeout", timeout=default_session_timeout) - schema_session_timeout = schema.json.get("session_timeout_in_seconds") - logger.info("schema session timeout", timeout=schema_session_timeout) - timeout = ( schema_session_timeout if schema_session_timeout and schema_session_timeout < default_session_timeout From 6d70a2ce44d3120dfc5c92bcd71f44c0c44cf4b2 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:29:18 +0000 Subject: [PATCH 330/567] Fix test number schema (#1258) --- schemas/test/en/test_numbers.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/test/en/test_numbers.json b/schemas/test/en/test_numbers.json index d111304925..85353843d1 100644 --- a/schemas/test/en/test_numbers.json +++ b/schemas/test/en/test_numbers.json @@ -115,7 +115,7 @@ } ] }, - "mandatory": false, + "mandatory": true, "type": "Number", "decimal_places": 2, "maximum": { From 51d13e92067101e40ed79ff867ff9881a93dcec1 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:00:08 +0000 Subject: [PATCH 331/567] Add extra logging when loading user (#1259) --- app/authentication/authenticator.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 5f2ac068e2..289a26a3fd 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -118,7 +118,15 @@ def load_user(extend_session: bool = True) -> Optional[User]: return user - logger.info("session does not exist") + logger.info( + "session does not exist", + user_ik_present=USER_IK in cookie_session, + eq_session_id_present=EQ_SESSION_ID in cookie_session, + session_store_exists=bool(session_store), + session_expiration=session_store.expiration_time.isoformat() + if session_store and session_store.expiration_time + else None, + ) cookie_session.pop(USER_IK, None) From 2be4307b4ee2581f6ee2b81a6d93651adbfd9ec4 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:38:49 +0000 Subject: [PATCH 332/567] Schemas v3.78.0 (#1262) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 325bce58d4..8852be9f45 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.77.3 +v3.78.0 From ceadf3454c7b6d96c6497526f09bb2f66f6fa61e Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:48:12 +0000 Subject: [PATCH 333/567] Add support for CIR (#1246) * Add support for CIR * Reuse load schema from url instead of separate service * Add metdata validation for too many options * Make load_schema_from_url generic * Add logs test * Move binding to shared utility method * Update README to include CIR and SDS base urls --- .development.env | 1 + .functional-tests.env | 1 + README.md | 80 +++++++++++----------- app/data_models/metadata_proxy.py | 28 ++++---- app/routes/flush.py | 7 +- app/routes/individual_response.py | 7 +- app/routes/questionnaire.py | 15 +---- app/routes/session.py | 1 + app/settings.py | 2 + app/submitter/converter_v2.py | 10 ++- app/utilities/bind_context.py | 17 +++++ app/utilities/metadata_parser_v2.py | 21 ++++-- app/utilities/schema.py | 55 ++++++++------- tests/app/parser/test_metadata_parser.py | 44 +++++++++++- tests/app/questionnaire/conftest.py | 2 +- tests/app/submitter/conftest.py | 73 ++++++++++++-------- tests/app/submitter/test_converter.py | 35 ++++++++++ tests/app/utilities/conftest.py | 13 ++++ tests/app/utilities/test_schema.py | 86 +++++++++++++++++++++--- tests/integration/create_token.py | 20 +++++- tests/integration/session/test_login.py | 55 ++++++++++++++- tests/integration/test_flush_data.py | 41 ++++++++++- 22 files changed, 461 insertions(+), 153 deletions(-) create mode 100644 app/utilities/bind_context.py create mode 100644 tests/app/utilities/conftest.py diff --git a/.development.env b/.development.env index 80623dfb5e..86a1116505 100644 --- a/.development.env +++ b/.development.env @@ -28,4 +28,5 @@ ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log SDS_API_BASE_URL=http://localhost:5003 +CIR_API_BASE_URL=http://localhost:5004 OIDC_TOKEN_BACKEND=local diff --git a/.functional-tests.env b/.functional-tests.env index b40db06e04..0faec74514 100644 --- a/.functional-tests.env +++ b/.functional-tests.env @@ -29,4 +29,5 @@ COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS=35 SDS_API_BASE_URL=http://localhost:5003 +CIR_API_BASE_URL=http://localhost:5004 OIDC_TOKEN_BACKEND=local diff --git a/README.md b/README.md index bd4d45d318..e430932e49 100644 --- a/README.md +++ b/README.md @@ -390,45 +390,47 @@ Once we have the translated .po files they can be added to the source code and u The following env variables can be used -| Variable Name | Default | Description | -| ----------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------- | -| EQ_SESSION_TIMEOUT_SECONDS | 2700 (45 mins) | The duration of the flask session | -| EQ_PROFILING | False | Enables or disables profiling (True/False) Default False/Disabled | -| EQ_GOOGLE_TAG_MANAGER_ID | | The Google Tag Manger ID - Specifies the GTM account | -| EQ_GOOGLE_TAG_MANAGER_AUTH | | The Google Tag Manger Auth - Ties the GTM container with the whole environment | -| EQ_ENABLE_HTML_MINIFY | True | Enable minification of html | -| EQ_ENABLE_SECURE_SESSION_COOKIE | True | Set secure session cookies | -| EQ_MAX_HTTP_POST_CONTENT_LENGTH | 65536 | The maximum http post content length that the system wil accept | -| EQ_MINIMIZE_ASSETS | True | Should JS and CSS be minimized | -| MAX_CONTENT_LENGTH | 65536 | max request payload size in bytes | -| EQ_APPLICATION_VERSION_PATH | .application-version | the location of a file containing the application version number | -| EQ_ENABLE_LIVE_RELOAD | False | Enable livereload of browser when scripts, styles or templates are updated | -| EQ_SECRETS_FILE | secrets.yml | The location of the secrets file | -| EQ_KEYS_FILE | keys.yml | The location of the keys file | -| EQ_SUBMISSION_BACKEND | | Which submission backend to use ( gcs, rabbitmq, log ) | -| EQ_GCS_SUBMISSION_BUCKET_ID | | The bucket name in GCP to store the submissions in | -| EQ_GCS_FEEDBACK_BUCKET_ID | | The bucket name in GCP to store the feedback in | -| EQ_RABBITMQ_HOST | | | -| EQ_RABBITMQ_HOST_SECONDARY | | | -| EQ_RABBITMQ_PORT | 5672 | | -| EQ_RABBITMQ_QUEUE_NAME | submit_q | The name of the submission queue | -| EQ_SERVER_SIDE_STORAGE_USER_ID_ITERATIONS | 10000 | | -| EQ_STORAGE_BACKEND | datastore | | -| EQ_DYNAMODB_ENDPOINT | | | -| EQ_REDIS_HOST | | Hostname of Redis instance used for ephemeral storage | -| EQ_REDIS_PORT | | Port number of Redis instance used for ephemeral storage | -| EQ_DYNAMODB_MAX_RETRIES | 5 | | -| EQ_DYNAMODB_MAX_POOL_CONNECTIONS | 30 | | -| EQ_QUESTIONNAIRE_STATE_TABLE_NAME | | | -| EQ_SESSION_TABLE_NAME | | | -| EQ_USED_JTI_CLAIM_TABLE_NAME | | | -| WEB_SERVER_TYPE | | Web server type used to run the application. This also determines the worker class which can be async/threaded | -| WEB_SERVER_WORKERS | | The number of worker processes | -| WEB_SERVER_THREADS | | The number of worker threads per worker | -| WEB_SERVER_UWSGI_ASYNC_CORES | | The number of cores to initialise when using "uwsgi-async" web server worker type | -| DATASTORE_USE_GRPC | False | Determines whether to use gRPC for Datastore. gRPC is currently only supported for threaded web servers | -| ACCOUNT_SERVICE_BASE_URL | `https://surveys.ons.gov.uk` | The base URL of the account service used to launch the survey | -| ONS_URL | `https://www.ons.gov.uk` | The URL of the ONS website where static content is sourced, e.g. accessibility info | +| Variable Name | Default | Description | +|-------------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------| +| EQ_SESSION_TIMEOUT_SECONDS | 2700 (45 mins) | The duration of the flask session | +| EQ_PROFILING | False | Enables or disables profiling (True/False) Default False/Disabled | +| EQ_GOOGLE_TAG_MANAGER_ID | | The Google Tag Manger ID - Specifies the GTM account | +| EQ_GOOGLE_TAG_MANAGER_AUTH | | The Google Tag Manger Auth - Ties the GTM container with the whole environment | +| EQ_ENABLE_HTML_MINIFY | True | Enable minification of html | +| EQ_ENABLE_SECURE_SESSION_COOKIE | True | Set secure session cookies | +| EQ_MAX_HTTP_POST_CONTENT_LENGTH | 65536 | The maximum http post content length that the system wil accept | +| EQ_MINIMIZE_ASSETS | True | Should JS and CSS be minimized | +| MAX_CONTENT_LENGTH | 65536 | max request payload size in bytes | +| EQ_APPLICATION_VERSION_PATH | .application-version | the location of a file containing the application version number | +| EQ_ENABLE_LIVE_RELOAD | False | Enable livereload of browser when scripts, styles or templates are updated | +| EQ_SECRETS_FILE | secrets.yml | The location of the secrets file | +| EQ_KEYS_FILE | keys.yml | The location of the keys file | +| EQ_SUBMISSION_BACKEND | | Which submission backend to use ( gcs, rabbitmq, log ) | +| EQ_GCS_SUBMISSION_BUCKET_ID | | The bucket name in GCP to store the submissions in | +| EQ_GCS_FEEDBACK_BUCKET_ID | | The bucket name in GCP to store the feedback in | +| EQ_RABBITMQ_HOST | | | +| EQ_RABBITMQ_HOST_SECONDARY | | | +| EQ_RABBITMQ_PORT | 5672 | | +| EQ_RABBITMQ_QUEUE_NAME | submit_q | The name of the submission queue | +| EQ_SERVER_SIDE_STORAGE_USER_ID_ITERATIONS | 10000 | | +| EQ_STORAGE_BACKEND | datastore | | +| EQ_DYNAMODB_ENDPOINT | | | +| EQ_REDIS_HOST | | Hostname of Redis instance used for ephemeral storage | +| EQ_REDIS_PORT | | Port number of Redis instance used for ephemeral storage | +| EQ_DYNAMODB_MAX_RETRIES | 5 | | +| EQ_DYNAMODB_MAX_POOL_CONNECTIONS | 30 | | +| EQ_QUESTIONNAIRE_STATE_TABLE_NAME | | | +| EQ_SESSION_TABLE_NAME | | | +| EQ_USED_JTI_CLAIM_TABLE_NAME | | | +| WEB_SERVER_TYPE | | Web server type used to run the application. This also determines the worker class which can be async/threaded | +| WEB_SERVER_WORKERS | | The number of worker processes | +| WEB_SERVER_THREADS | | The number of worker threads per worker | +| WEB_SERVER_UWSGI_ASYNC_CORES | | The number of cores to initialise when using "uwsgi-async" web server worker type | +| DATASTORE_USE_GRPC | False | Determines whether to use gRPC for Datastore. gRPC is currently only supported for threaded web servers | +| ACCOUNT_SERVICE_BASE_URL | `https://surveys.ons.gov.uk` | The base URL of the account service used to launch the survey | +| ONS_URL | `https://www.ons.gov.uk` | The URL of the ONS website where static content is sourced, e.g. accessibility info | +| SDS_API_BASE_URL | | The base URL of the SDS API used for fetching supplementary data | +| CIR_API_BASE_URL | | The base URL of the CIR API used for fetching collection instruments | The following env variables can be used when running tests diff --git a/app/data_models/metadata_proxy.py b/app/data_models/metadata_proxy.py index a3b2414674..7dfce0b8d4 100644 --- a/app/data_models/metadata_proxy.py +++ b/app/data_models/metadata_proxy.py @@ -3,7 +3,7 @@ from copy import deepcopy from dataclasses import dataclass from datetime import datetime -from typing import Any, Mapping, Optional +from typing import Any, Mapping from werkzeug.datastructures import ImmutableDict @@ -26,6 +26,7 @@ class NoMetadataException(Exception): "language_code", "schema_name", "schema_url", + "cir_instrument_id", "channel", "region_code", "roles", @@ -35,9 +36,9 @@ class NoMetadataException(Exception): @dataclass(frozen=True) class SurveyMetadata: data: ImmutableDict - receipting_keys: Optional[tuple] = None + receipting_keys: tuple | None = None - def __getitem__(self, key: str) -> Optional[Any]: + def __getitem__(self, key: str) -> Any: return self.data.get(key) @@ -50,16 +51,17 @@ class MetadataProxy: collection_exercise_sid: str response_id: str response_expires_at: datetime - survey_metadata: Optional[SurveyMetadata] = None - schema_url: Optional[str] = None - schema_name: Optional[str] = None - language_code: Optional[str] = None - channel: Optional[str] = None - region_code: Optional[str] = None - version: Optional[AuthPayloadVersion] = None - roles: Optional[list] = None - - def __getitem__(self, key: str) -> Optional[Any]: + survey_metadata: SurveyMetadata | None = None + schema_url: str | None = None + schema_name: str | None = None + cir_instrument_id: str | None = None + language_code: str | None = None + channel: str | None = None + region_code: str | None = None + version: AuthPayloadVersion | None = None + roles: list | None = None + + def __getitem__(self, key: str) -> Any | None: if self.survey_metadata and key in self.survey_metadata.data: return self.survey_metadata[key] diff --git a/app/routes/flush.py b/app/routes/flush.py index 76ec8d3422..c31ffa63be 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -21,6 +21,7 @@ from app.submitter.converter import convert_answers from app.submitter.converter_v2 import convert_answers_v2 from app.submitter.submission_failed import SubmissionFailedException +from app.utilities.bind_context import bind_contextvars_schema_from_metadata from app.utilities.json import json_dumps from app.utilities.schema import load_schema_from_metadata from app.views.handlers.submission import get_receipting_metadata @@ -59,10 +60,8 @@ def flush_data() -> Response: tx_id=metadata.tx_id, ce_id=metadata.collection_exercise_sid, ) - if schema_name := metadata.schema_name: - contextvars.bind_contextvars(schema_name=schema_name) - if schema_url := metadata.schema_url: - contextvars.bind_contextvars(schema_url=schema_url) + bind_contextvars_schema_from_metadata(metadata) + if _submit_data(user): return Response(status=200) return Response(status=404) diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index 9b9afc4eec..e3690bd5e8 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -18,6 +18,7 @@ from app.helpers.session_helpers import with_questionnaire_store from app.helpers.template_helpers import render_template from app.questionnaire import QuestionnaireSchema +from app.utilities.bind_context import bind_contextvars_schema_from_metadata from app.utilities.schema import load_schema_from_metadata from app.views.handlers.individual_response import ( IndividualResponseChangeHandler, @@ -59,11 +60,7 @@ def before_individual_response_request() -> Response | None: ce_id=metadata.collection_exercise_sid, ) - if schema_name := metadata.schema_name: - contextvars.bind_contextvars(schema_name=schema_name) - - if schema_url := metadata.schema_url: - contextvars.bind_contextvars(schema_url=schema_url) # pragma: no cover + bind_contextvars_schema_from_metadata(metadata) logger.info( "individual-response request", method=request.method, url_path=request.full_path diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 6d7e1a2f04..068c883a8f 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -31,6 +31,7 @@ from app.questionnaire.location import InvalidLocationException from app.questionnaire.router import Router from app.submitter.previously_submitted_exception import PreviouslySubmittedException +from app.utilities.bind_context import bind_contextvars_schema_from_metadata from app.utilities.schema import load_schema_from_metadata from app.views.contexts import HubContext from app.views.contexts.preview_context import ( @@ -94,12 +95,7 @@ def before_questionnaire_request() -> Response | None: tx_id=metadata.tx_id, ce_id=metadata.collection_exercise_sid, ) - - if schema_name := metadata.schema_name: - contextvars.bind_contextvars(schema_name=schema_name) - - if schema_url := metadata.schema_url: - contextvars.bind_contextvars(schema_url=schema_url) + bind_contextvars_schema_from_metadata(metadata) logger.info( "questionnaire request", method=request.method, url_path=request.full_path @@ -138,12 +134,7 @@ def before_post_submission_request() -> None: ) contextvars.bind_contextvars(tx_id=metadata.tx_id) - - if schema_name := metadata.schema_name: - contextvars.bind_contextvars(schema_name=schema_name) - - if schema_url := metadata.schema_url: - contextvars.bind_contextvars(schema_url=schema_url) + bind_contextvars_schema_from_metadata(metadata) logger.info( "questionnaire request", method=request.method, url_path=request.full_path diff --git a/app/routes/session.py b/app/routes/session.py index 1fc4eb7453..80b0887e9e 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -87,6 +87,7 @@ def login() -> Response: "case_id": decrypted_token.get("case_id"), "schema_name": decrypted_token.get("schema_name"), "schema_url": decrypted_token.get("schema_url"), + "cir_instrument_id": decrypted_token.get("cir_instrument_id"), "ru_ref": ru_ref, "qid": qid, }.items() diff --git a/app/settings.py b/app/settings.py index 55d1f1fa02..4f061c8678 100644 --- a/app/settings.py +++ b/app/settings.py @@ -146,6 +146,8 @@ def utcoffset_or_fail(date_value, key): SDS_API_BASE_URL = os.getenv("SDS_API_BASE_URL") +CIR_API_BASE_URL = os.getenv("CIR_API_BASE_URL") + OIDC_TOKEN_BACKEND = os.getenv("OIDC_TOKEN_BACKEND") OIDC_TOKEN_VALIDITY_IN_SECONDS = int( diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index 91931643f7..7687583170 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -65,17 +65,23 @@ def convert_answers_v2( "data_version": schema.json["data_version"], "origin": "uk.gov.ons.edc.eq", "collection_exercise_sid": metadata.collection_exercise_sid, - "schema_name": metadata.schema_name, "flushed": flushed, "submitted_at": submitted_at.isoformat(), "launch_language_code": metadata.language_code or DEFAULT_LANGUAGE_CODE, + "survey_metadata": {"survey_id": survey_id}, } optional_properties = get_optional_payload_properties( metadata, data_stores.response_metadata ) - payload["survey_metadata"] = {"survey_id": survey_id} + if metadata.schema_name: + payload["schema_name"] = metadata.schema_name + elif metadata.schema_url: + payload["schema_url"] = metadata.schema_url + elif metadata.cir_instrument_id: + payload["cir_instrument_id"] = metadata.cir_instrument_id + if metadata.survey_metadata: payload["survey_metadata"].update(metadata.survey_metadata.data) diff --git a/app/utilities/bind_context.py b/app/utilities/bind_context.py new file mode 100644 index 0000000000..199fb09447 --- /dev/null +++ b/app/utilities/bind_context.py @@ -0,0 +1,17 @@ +from structlog import contextvars + +from app.data_models.metadata_proxy import MetadataProxy + + +def bind_contextvars_schema_from_metadata(metadata: MetadataProxy) -> None: + """ + Metadata always contains exactly one way of identifying a schema, bind the reference to contextvars + """ + if schema_name := metadata.schema_name: + contextvars.bind_contextvars(schema_name=schema_name) + + if schema_url := metadata.schema_url: + contextvars.bind_contextvars(schema_url=schema_url) + + if cir_instrument_id := metadata.cir_instrument_id: + contextvars.bind_contextvars(cir_instrument_id=cir_instrument_id) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 41ff03a10c..eabcb68ae2 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -81,6 +81,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): ) # type:ignore schema_name = VALIDATORS["string"](required=False) # type:ignore schema_url = VALIDATORS["url"](required=False) # type:ignore + cir_instrument_id = VALIDATORS["uuid"](required=False) # type:ignore response_id = VALIDATORS["string"](required=True) # type:ignore account_service_url = VALIDATORS["url"](required=True) # type:ignore @@ -100,12 +101,22 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): survey_metadata = fields.Nested(SurveyMetadata, required=False) @validates_schema - def validate_schema_name_is_set(self, data, **kwargs): + def validate_schema_options(self, data, **kwargs): # pylint: disable=no-self-use, unused-argument - if data and not (data.get("schema_name") or data.get("schema_url")): - raise ValidationError( - "Neither schema_name or schema_url has been set in metadata" - ) + if data: + options = [ + option + for option in ["schema_name", "schema_url", "cir_instrument_id"] + if data.get(option) + ] + if len(options) == 0: + raise ValidationError( + "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + ) + if len(options) > 1: + raise ValidationError( + f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata, but {', '.join(options)} were provided" + ) def validate_questionnaire_claims( diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 4845a7df33..eaad4a51e7 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -1,10 +1,11 @@ import os -import time from functools import lru_cache from glob import glob from pathlib import Path from typing import Any +from urllib.parse import urlencode +from flask import current_app from requests import RequestException from structlog import get_logger @@ -20,6 +21,7 @@ SCHEMA_DIR = "schemas" LANGUAGE_CODES = ("en", "cy") +CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL = "/v2/retrieve_collection_instrument" LANGUAGES_MAP = { "test_language": [["en", "cy"]], @@ -109,26 +111,15 @@ def load_schema_from_metadata( metadata: MetadataProxy, *, language_code: str | None ) -> QuestionnaireSchema: if schema_url := metadata.schema_url: - # :TODO: Remove before production uses schema_url - # This is temporary and is only for development/integration purposes. - # This should not be used in production. - - start = time.time() - schema = load_schema_from_url(schema_url, language_code) - duration_in_milliseconds = (time.time() - start) * 1_000 - - cache_info = ( - load_schema_from_url.cache_info() # pylint: disable=no-value-for-parameter + return load_schema_from_url( + url=schema_url, + language_code=language_code, ) - logger.info( - f"load_schema_from_url took {duration_in_milliseconds:.6f} milliseconds", - schema_url=schema_url, - currsize=cache_info.currsize, - hits=cache_info.hits, - misses=cache_info.misses, - pid=os.getpid(), + + if cir_instrument_id := metadata.cir_instrument_id: + return load_schema_from_instrument_id( + cir_instrument_id=cir_instrument_id, language_code=language_code ) - return schema return load_schema_from_name( # Type ignore: Metadata is validated to have either schema_name or schema_url populated. @@ -145,6 +136,14 @@ def load_schema_from_name( return _load_schema_from_name(schema_name, language_code) +def load_schema_from_instrument_id( + *, cir_instrument_id: str, language_code: str | None +) -> QuestionnaireSchema: + parameters = {"guid": cir_instrument_id} + cir_url = f"{current_app.config['CIR_API_BASE_URL']}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}?{urlencode(parameters)}" + return load_schema_from_url(url=cir_url, language_code=language_code) + + @lru_cache(maxsize=None) def _load_schema_from_name(schema_name: str, language_code: str) -> QuestionnaireSchema: schema_json = _load_schema_file(schema_name, language_code) @@ -195,20 +194,20 @@ def _load_schema_file(schema_name: str, language_code: str) -> Any: @lru_cache(maxsize=None) -def load_schema_from_url( - schema_url: str, language_code: str | None -) -> QuestionnaireSchema: +def load_schema_from_url(url: str, *, language_code: str | None) -> QuestionnaireSchema: + """ + Fetches a schema from the provided url. + The caller is responsible for including any required query parameters in the url + """ language_code = language_code or DEFAULT_LANGUAGE_CODE pid = os.getpid() logger.info( "loading schema from URL", - schema_url=schema_url, + schema_url=url, language_code=language_code, pid=pid, ) - constructed_schema_url = f"{schema_url}?language={language_code}" - session = get_retryable_session( max_retries=SCHEMA_REQUEST_MAX_RETRIES, retry_status_codes=SCHEMA_REQUEST_RETRY_STATUS_CODES, @@ -216,11 +215,11 @@ def load_schema_from_url( ) try: - req = session.get(constructed_schema_url, timeout=SCHEMA_REQUEST_TIMEOUT) + req = session.get(url, timeout=SCHEMA_REQUEST_TIMEOUT) except RequestException as exc: logger.exception( "schema request errored", - schema_url=constructed_schema_url, + schema_url=url, ) raise SchemaRequestFailed from exc @@ -238,7 +237,7 @@ def load_schema_from_url( logger.error( "got a non-200 response for schema url request", status_code=req.status_code, - schema_url=constructed_schema_url, + schema_url=url, ) raise SchemaRequestFailed diff --git a/tests/app/parser/test_metadata_parser.py b/tests/app/parser/test_metadata_parser.py index 10b44e1350..59f401617b 100644 --- a/tests/app/parser/test_metadata_parser.py +++ b/tests/app/parser/test_metadata_parser.py @@ -396,13 +396,53 @@ def test_deserialisation_iso_8601_datetime_bad_datetime_raises_ValidationError( validator(metadata) -def test_empty_schema_name_and_schema_url_not_valid_v2(): +def test_empty_schema_name_and_schema_url_and_cir_instrument_id_not_valid_v2(): metadata = get_metadata_full(AuthPayloadVersion.V2) del metadata["schema_name"] - with pytest.raises(ValidationError): + with pytest.raises(ValidationError) as exc: + validate_runner_claims_v2(metadata) + + assert ( + "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + in str(exc) + ) + + +@pytest.mark.parametrize( + "options", + [ + { + "schema_name": "test_name", + "cir_instrument_id": "f0519981-426c-8b93-75c0-bfc40c66fe25", + }, + { + "schema_url": "http://test.json", + "cir_instrument_id": "f0519981-426c-8b93-75c0-bfc40c66fe25", + }, + { + "schema_name": "test_name", + "schema_url": "http://test.json", + "cir_instrument_id": "f0519981-426c-8b93-75c0-bfc40c66fe25", + }, + {"schema_name": "test_name", "schema_url": "http://test.json"}, + ], +) +def test_too_many_of_schema_name_schema_url_and_cir_instrument_id_not_valid_v2(options): + metadata = get_metadata_full(AuthPayloadVersion.V2) + del metadata["schema_name"] + + metadata.update(options) + provided = ", ".join(options) + + with pytest.raises(ValidationError) as exc: validate_runner_claims_v2(metadata) + assert ( + f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata, but {provided} were provided" + in str(exc) + ) + def test_valid_v2_social_claims(): metadata = get_metadata_social() diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 5ffc9d9f54..22ad9938c0 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -22,7 +22,7 @@ from app.utilities.schema import load_schema_from_name -def get_metadata(extra_metadata: dict = None): +def get_metadata(extra_metadata: dict | None = None): extra_metadata = extra_metadata or {} metadata = { "response_id": "1", diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index e36b72d913..d18ceaba2b 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -45,36 +45,35 @@ } ) -METADATA_V2 = MetadataProxy.from_dict( - { - "version": AuthPayloadVersion.V2.value, - "tx_id": str(uuid.uuid4()), - "schema_name": "1_0000", - "collection_exercise_sid": "test-sid", - "account_service_url": ACCOUNT_SERVICE_BASE_URL_SOCIAL, - "survey_metadata": { - "data": { - "period_id": "2016-02-01", - "period_str": "2016-01-01", - "ref_p_start_date": "2016-02-02", - "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", - "ru_name": "Apple", - "case_type": "SPG", - "form_type": "I", - "case_ref": "1000000000000001", - "display_address": "68 Abingdon Road, Goathill", - "user_id": "789473423", - }, +RAW_METADATA_V2 = { + "version": AuthPayloadVersion.V2.value, + "tx_id": str(uuid.uuid4()), + "schema_name": "1_0000", + "collection_exercise_sid": "test-sid", + "account_service_url": ACCOUNT_SERVICE_BASE_URL_SOCIAL, + "survey_metadata": { + "data": { + "period_id": "2016-02-01", + "period_str": "2016-01-01", + "ref_p_start_date": "2016-02-02", + "ref_p_end_date": "2016-03-03", + "ru_ref": "432423423423", + "ru_name": "Apple", + "case_type": "SPG", + "form_type": "I", + "case_ref": "1000000000000001", + "display_address": "68 Abingdon Road, Goathill", + "user_id": "789473423", }, - "response_id": "1234567890123456", - "case_id": str(uuid.uuid4()), - "region_code": "GB-ENG", - "channel": "RH", - "jti": str(uuid.uuid4()), - "response_expires_at": get_response_expires_at(), - } -) + }, + "response_id": "1234567890123456", + "case_id": str(uuid.uuid4()), + "region_code": "GB-ENG", + "channel": "RH", + "jti": str(uuid.uuid4()), + "response_expires_at": get_response_expires_at(), +} +METADATA_V2 = MetadataProxy.from_dict(RAW_METADATA_V2) def get_questionnaire_store(version): @@ -109,6 +108,22 @@ def fake_metadata_v2(): return METADATA_V2 +@pytest.fixture +def fake_metadata_v2_schema_url(): + copy = RAW_METADATA_V2.copy() + copy["schema_url"] = "https://schema_url.com" + del copy["schema_name"] + return MetadataProxy.from_dict(copy) + + +@pytest.fixture +def fake_metadata_v2_cir_instrument_id(): + copy = RAW_METADATA_V2.copy() + copy["cir_instrument_id"] = "f0519981-426c-8b93-75c0-bfc40c66fe25" + del copy["schema_name"] + return MetadataProxy.from_dict(copy) + + @pytest.fixture def fake_response_metadata(): response_metadata = {"started_at": "2018-07-04T14:49:33.448608+00:00"} diff --git a/tests/app/submitter/test_converter.py b/tests/app/submitter/test_converter.py index 53fcb56cac..091304fe7e 100644 --- a/tests/app/submitter/test_converter.py +++ b/tests/app/submitter/test_converter.py @@ -366,3 +366,38 @@ def test_instrument_id_should_be_set_in_payload_collection_if_form_type_in_metad ) assert payload["collection"]["instrument_id"], "I" + + +def test_schema_url_in_metadata_should_be_in_payload( + fake_metadata_v2_schema_url, fake_questionnaire_schema +): + questionnaire_store = get_questionnaire_store("v2") + questionnaire_store.data_stores.metadata = fake_metadata_v2_schema_url + + payload = convert_answers_v2( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert "schema_url" in payload + assert "schema_name" not in payload + assert "cir_instrument_id" not in payload + assert payload["schema_url"] == fake_metadata_v2_schema_url["schema_url"] + + +def test_cir_instrument_id_in_metadata_should_be_in_payload( + fake_metadata_v2_cir_instrument_id, fake_questionnaire_schema +): + questionnaire_store = get_questionnaire_store("v2") + questionnaire_store.data_stores.metadata = fake_metadata_v2_cir_instrument_id + + payload = convert_answers_v2( + fake_questionnaire_schema, questionnaire_store, {}, SUBMITTED_AT + ) + + assert "schema_url" not in payload + assert "schema_name" not in payload + assert "cir_instrument_id" in payload + assert ( + payload["cir_instrument_id"] + == fake_metadata_v2_cir_instrument_id["cir_instrument_id"] + ) diff --git a/tests/app/utilities/conftest.py b/tests/app/utilities/conftest.py new file mode 100644 index 0000000000..3bbbeb7c51 --- /dev/null +++ b/tests/app/utilities/conftest.py @@ -0,0 +1,13 @@ +import pytest + +from app.data_models.metadata_proxy import MetadataProxy + + +@pytest.fixture +def metadata_with_cir_instrument_id(): + return MetadataProxy.from_dict( + { + "cir_instrument_id": "f0519981-426c-8b93-75c0-bfc40c66fe25", + "language_code": "cy", + }, + ) diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 04b06cc7b2..c6bee3737f 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -11,6 +11,7 @@ from app.questionnaire import QuestionnaireSchema from app.setup import create_app from app.utilities.schema import ( + CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, SCHEMA_REQUEST_MAX_RETRIES, SchemaRequestFailed, _load_schema_from_name, @@ -26,6 +27,7 @@ from tests.app.questionnaire.conftest import get_metadata TEST_SCHEMA_URL = "http://test.domain/schema.json" +TEST_CIR_URL = "http://cir.domain" def test_valid_schema_names_from_params(): @@ -70,7 +72,7 @@ def test_get_schema_list(): assert get_schema_list() == expected_output -# pylint: disable=no-value-for-parameter +# pylint: disable=no-value-for-parameter,missing-kwoa,too-many-function-args def test_schema_cache_on_function_call(): _load_schema_from_name.cache_clear() @@ -150,7 +152,7 @@ def test_load_schema_from_url_200(): mock_schema = QuestionnaireSchema({}, language_code="cy") responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) - loaded_schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") + loaded_schema = load_schema_from_url(url=TEST_SCHEMA_URL, language_code="cy") assert loaded_schema.json == mock_schema.json assert loaded_schema.language_code == mock_schema.language_code @@ -174,7 +176,7 @@ def test_load_schema_from_url_non_200(status_code): ) with pytest.raises(SchemaRequestFailed) as exc: - load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + load_schema_from_url(url=TEST_SCHEMA_URL, language_code="en") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 0 @@ -188,7 +190,7 @@ def test_load_schema_from_url_non_200(status_code): def test_load_schema_from_url_request_failed(): responses.add(responses.GET, TEST_SCHEMA_URL, body=RequestException()) with pytest.raises(SchemaRequestFailed) as exc: - load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + load_schema_from_url(url=TEST_SCHEMA_URL, language_code="en") assert str(exc.value) == "schema request failed" @@ -201,7 +203,7 @@ def test_load_schema_from_url_uses_cache(): responses.add(responses.GET, TEST_SCHEMA_URL, json=mock_schema.json, status=200) # First load: Add to cache, no hits - load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") + load_schema_from_url(url=TEST_SCHEMA_URL, language_code="cy") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 1 @@ -209,7 +211,7 @@ def test_load_schema_from_url_uses_cache(): assert cache_info.hits == 0 # Second load: Read from cache, 1 hit - load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="cy") + load_schema_from_url(url=TEST_SCHEMA_URL, language_code="cy") cache_info = load_schema_from_url.cache_info() assert cache_info.currsize == 1 @@ -250,6 +252,72 @@ def test_load_schema_from_metadata_with_schema_url_and_override_language_code(): assert loaded_schema.language_code == language_code +@responses.activate +def test_load_schema_from_metadata_with_cir_instrument_id_200( + app, metadata_with_cir_instrument_id +): + load_schema_from_url.cache_clear() + mock_schema = QuestionnaireSchema({}, language_code="cy") + responses.add( + responses.GET, + f"{TEST_CIR_URL}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}", + json=mock_schema.json, + status=200, + ) + + with app.app_context(): + app.config["CIR_API_BASE_URL"] = TEST_CIR_URL + loaded_schema = load_schema_from_metadata( + metadata=metadata_with_cir_instrument_id, language_code="cy" + ) + + assert loaded_schema.json == mock_schema.json + assert loaded_schema.language_code == mock_schema.language_code + + +@responses.activate +def test_load_schema_from_metadata_with_cir_instrument_id_request_failed( + app, metadata_with_cir_instrument_id +): + load_schema_from_url.cache_clear() + responses.add( + responses.GET, + f"{TEST_CIR_URL}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}", + body=RequestException(), + ) + with app.app_context(): + with pytest.raises(SchemaRequestFailed): + app.config["CIR_API_BASE_URL"] = TEST_CIR_URL + load_schema_from_metadata( + metadata=metadata_with_cir_instrument_id, language_code="cy" + ) + + +@pytest.mark.parametrize( + "status_code", + [401, 403, 404, 501, 511], +) +@responses.activate +def test_load_schema_from_metadata_with_cir_instrument_id_non_200( + app, status_code, metadata_with_cir_instrument_id +): + load_schema_from_url.cache_clear() + mock_schema = QuestionnaireSchema({}, language_code="cy") + responses.add( + responses.GET, + f"{TEST_CIR_URL}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}", + json=mock_schema.json, + status=status_code, + ) + with app.app_context(): + with pytest.raises(SchemaRequestFailed) as exc: + app.config["CIR_API_BASE_URL"] = TEST_CIR_URL + load_schema_from_metadata( + metadata=metadata_with_cir_instrument_id, language_code="cy" + ) + assert str(exc.value) == "schema request failed" + + def get_mocked_make_request(mocker, status_codes): mocked_responses = [] for status_code in status_codes: @@ -269,7 +337,7 @@ def test_load_schema_from_url_retries_timeout_error(mocked_make_request_with_tim load_schema_from_url.cache_clear() try: - schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + schema = load_schema_from_url(url=TEST_SCHEMA_URL, language_code="en") except SchemaRequestFailed: return pytest.fail("Schema request unexpectedly failed") @@ -285,7 +353,7 @@ def test_load_schema_from_url_retries_transient_error(mocker): load_schema_from_url.cache_clear() try: - schema = load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + schema = load_schema_from_url(url=TEST_SCHEMA_URL, language_code="en") except SchemaRequestFailed: return pytest.fail("Schema request unexpectedly failed") @@ -302,7 +370,7 @@ def test_load_schema_from_url_max_retries(mocker): load_schema_from_url.cache_clear() with pytest.raises(SchemaRequestFailed) as exc: - load_schema_from_url(schema_url=TEST_SCHEMA_URL, language_code="en") + load_schema_from_url(url=TEST_SCHEMA_URL, language_code="en") assert str(exc.value) == "schema request failed" assert mocked_make_request.call_count == 3 diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 87c43a0df1..e696bf18f0 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -102,7 +102,12 @@ def __init__(self, key_store, upstream_kid, sr_public_kid): @staticmethod def _get_payload_with_params( - schema_name=None, schema_url=None, payload=PAYLOAD, **extra_payload + *, + schema_name=None, + schema_url=None, + cir_instrument_id=None, + payload=PAYLOAD, + **extra_payload, ): # pylint: disable=dangerous-default-value payload_vars = payload.copy() payload_vars["tx_id"] = str(uuid4()) @@ -110,6 +115,8 @@ def _get_payload_with_params( payload_vars["schema_name"] = schema_name if schema_url: payload_vars["schema_url"] = schema_url + if cir_instrument_id: + payload_vars["cir_instrument_id"] = cir_instrument_id payload_vars["iat"] = time.time() payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now @@ -204,6 +211,17 @@ def create_token_with_schema_url(self, schema_name, schema_url, **extra_payload) return self.generate_token(payload_vars) + def create_token_with_cir_instrument_id( + self, cir_instrument_id, payload=None, **extra_payload + ): + payload_vars = self._get_payload_with_params( + cir_instrument_id=cir_instrument_id, + payload=payload or PAYLOAD_V2_BUSINESS, + **extra_payload, + ) + + return self.generate_token(payload_vars) + def create_token_with_none_language_code(self, schema_name, **extra_payload): payload_vars = self._get_payload_with_params( schema_name=schema_name, **extra_payload diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index 819ba25817..8a2d2737d5 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -2,7 +2,10 @@ from httmock import HTTMock, response, urlmatch -from app.utilities.schema import get_schema_path_map +from app.utilities.schema import ( + CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, + get_schema_path_map, +) from tests.integration.create_token import PAYLOAD from tests.integration.integration_test_case import IntegrationTestCase @@ -356,6 +359,37 @@ def test_login_without_case_id_in_token_is_unauthorised(self): # Then self.assertStatusForbidden() + def test_login_token_with_cir_instrument_id_should_redirect_to_survey(self): + cir_instrument_id = "f0519981-426c-8b93-75c0-bfc40c66fe25" + + # Given + token = self.token_generator.create_token_with_cir_instrument_id( + cir_instrument_id=cir_instrument_id + ) + + # When + with HTTMock(self.cir_url_mock): + self.post(url=f"/session?token={token}") + + # Then + self.assertStatusOK() + self.assertInUrl("/questionnaire") + + def test_login_token_with_invalid_cir_instrument_id_results_in_500(self): + cir_instrument_id = "a0df1208-dff5-4a3d-b35d-f9620c4a48ef" + + # Given + token = self.token_generator.create_token_with_cir_instrument_id( + cir_instrument_id=cir_instrument_id + ) + + # When + with HTTMock(self.cir_url_mock_500): + self.post(url=f"/session?token={token}") + + # Then + self.assertException() + @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") def schema_url_mock(_url, _request): @@ -364,6 +398,25 @@ def schema_url_mock(_url, _request): with open(schema_path, encoding="utf8") as json_data: return json_data.read() + @staticmethod + @urlmatch( + path=CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, + query="guid=f0519981-426c-8b93-75c0-bfc40c66fe25", + ) + def cir_url_mock(_url, _request): + schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] + + with open(schema_path, encoding="utf8") as json_data: + return json_data.read() + + @staticmethod + @urlmatch( + path=CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, + query="guid=a0df1208-dff5-4a3d-b35d-f9620c4a48ef", + ) + def cir_url_mock_500(_url, _request): + return response(500) + @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") def schema_url_mock_500(_url, _request): diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 121c9ad9e1..309eb392c8 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -4,7 +4,10 @@ from httmock import HTTMock, urlmatch from mock import patch -from app.utilities.schema import get_schema_path_map +from app.utilities.schema import ( + CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, + get_schema_path_map, +) from tests.app.parser.conftest import get_response_expires_at from tests.integration.integration_test_case import IntegrationTestCase @@ -42,6 +45,17 @@ def schema_url_mock(_url, _request): with open(schema_path, encoding="utf8") as json_data: return json_data.read() + @staticmethod + @urlmatch( + path=CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, + query="guid=f0519981-426c-8b93-75c0-bfc40c66fe25", + ) + def cir_url_mock(_url, _request): + schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] + + with open(schema_path, encoding="utf8") as json_data: + return json_data.read() + def test_flush_data_successful(self): self.post( url="/flush?token=" @@ -233,10 +247,33 @@ def test_flush_logs_output_schema_url(self): url=f"/flush?token={self.token_generator.create_token_with_schema_url('test_textfield', schema_url, payload=self.get_payload())}" ) - flush_log = logs.output[6] + flush_log = logs.output[5] self.assertIn("successfully flushed answers", flush_log) self.assertIn("tx_id", flush_log) self.assertIn("ce_id", flush_log) self.assertIn("schema_name", flush_log) self.assertIn("schema_url", flush_log) + + def test_flush_logs_output_cir_instrument_id(self): + token = self.token_generator.create_token_with_cir_instrument_id( + cir_instrument_id="f0519981-426c-8b93-75c0-bfc40c66fe25" + ) + with HTTMock(self.cir_url_mock): + self.get(url=f"/session?token={token}") + self.assertStatusOK() + with self.assertLogs() as logs: + token_with_flush_role = ( + self.token_generator.create_token_with_cir_instrument_id( + cir_instrument_id="f0519981-426c-8b93-75c0-bfc40c66fe25", + payload=self.get_payload(), + ) + ) + self.post(url=f"/flush?token={token_with_flush_role}") + + flush_log = logs.output[6] + + self.assertIn("successfully flushed answers", flush_log) + self.assertIn("tx_id", flush_log) + self.assertIn("ce_id", flush_log) + self.assertIn("cir_instrument_id", flush_log) From 39338ab31d101cf40e3555a59a432e7a29557042 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 28 Nov 2023 08:38:58 +0000 Subject: [PATCH 334/567] Add no return_to_answer_id handling when generating return_to for calculated summary (#1266) --- app/questionnaire/router.py | 12 +- ...t_validation_sum_against_value_source.json | 10 ++ .../test_questionnaire_schema.py | 106 +++++++++++------- tests/app/questionnaire/test_router.py | 59 ++++++++++ .../validation/sum/value_source.spec.js | 46 +++++++- 5 files changed, 187 insertions(+), 46 deletions(-) diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index c1902c597c..fb8faac566 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -348,9 +348,17 @@ def _get_return_to_for_calculated_summary( return_to_block_id = ",".join(remaining) if remaining else None # remove first item and return the remaining ones - # Type ignore: return_location.return_to and return_location.return_to_answer_id will always be populated at this point + # Type ignore: return_location.return_to will always be populated at this point return_to = ",".join(return_location.return_to.split(",")[1:]) or None # type: ignore - anchor, *return_to_answer_ids = return_location.return_to_answer_id.split(",") # type: ignore + return_to_answer_ids = [] + anchor = None + + if return_location.return_to_answer_id: + ( + anchor, + *return_to_answer_ids, + ) = return_location.return_to_answer_id.split(",") + return_to_answer_id = ( ",".join(return_to_answer_ids) if return_to_answer_ids else None ) diff --git a/schemas/test/en/test_validation_sum_against_value_source.json b/schemas/test/en/test_validation_sum_against_value_source.json index 64726559f7..b394a70198 100644 --- a/schemas/test/en/test_validation_sum_against_value_source.json +++ b/schemas/test/en/test_validation_sum_against_value_source.json @@ -171,6 +171,16 @@ } ] } + }, + { + "type": "CalculatedSummary", + "id": "another-number-total-playback", + "title": "We calculate the total of number values entered to be %(total)s. Is this correct?", + "calculation": { + "calculation_type": "sum", + "answers_to_calculate": ["breakdown-1", "breakdown-2", "breakdown-3", "breakdown-4"], + "title": "Another grand total of previous values" + } } ] } diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 062a8b114b..baaf937498 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -449,44 +449,74 @@ def test_answer_dependencies_for_calculated_question_value_source( ): schema = calculated_question_with_dependent_sections_schema - assert schema.answer_dependencies == { - "breakdown-1": { - Dependent( - section_id="default-section", - block_id="number-total-playback", - for_list=None, - answer_id=None, - ), - Dependent( - section_id="default-section", - block_id="second-breakdown-block", - for_list=None, - answer_id=None, - ), - }, - "breakdown-2": { - Dependent( - section_id="default-section", - block_id="number-total-playback", - for_list=None, - answer_id=None, - ), - Dependent( - section_id="default-section", - block_id="second-breakdown-block", - for_list=None, - answer_id=None, - ), - }, - "total-answer": { - Dependent( - section_id="default-section", - block_id="breakdown-block", - for_list=None, - answer_id=None, - ) - }, - } + assert schema.answer_dependencies == ImmutableDict( + { + "total-answer": { + Dependent( + section_id="default-section", + block_id="breakdown-block", + for_list=None, + answer_id=None, + ) + }, + "breakdown-1": { + Dependent( + section_id="default-section", + block_id="number-total-playback", + for_list=None, + answer_id=None, + ), + Dependent( + section_id="default-section", + block_id="second-breakdown-block", + for_list=None, + answer_id=None, + ), + Dependent( + section_id="default-section", + block_id="another-number-total-playback", + for_list=None, + answer_id=None, + ), + }, + "breakdown-2": { + Dependent( + section_id="default-section", + block_id="number-total-playback", + for_list=None, + answer_id=None, + ), + Dependent( + section_id="default-section", + block_id="second-breakdown-block", + for_list=None, + answer_id=None, + ), + Dependent( + section_id="default-section", + block_id="another-number-total-playback", + for_list=None, + answer_id=None, + ), + }, + "breakdown-3": { + Dependent( + section_id="default-section", + block_id="another-number-total-playback", + for_list=None, + answer_id=None, + ) + }, + "breakdown-4": { + Dependent( + section_id="default-section", + block_id="another-number-total-playback", + for_list=None, + answer_id=None, + ) + }, + } + ) def test_answer_dependencies_for_calculated_summary( diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index f39658b53f..02ef686ddb 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -2083,3 +2083,62 @@ def test_section_complete_returns_url_for_first_location( ) assert "questionnaire/employment-status/" in section_resume_url + + @pytest.mark.usefixtures("app") + def test_return_to_calculated_summary_no_return_to_answer_id(self): + self.schema = load_schema_from_name("test_validation_sum_against_value_source") + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="default-section", + block_ids=[ + "total-block", + "breakdown-block", + "number-total-playback", + ], + status=CompletionStatus.IN_PROGRESS, + ) + ] + ) + + current_location = Location( + section_id="default-section", block_id="second-breakdown-block" + ) + + routing_path = RoutingPath( + block_ids=[ + "total-block", + "breakdown-block", + "number-total-playback", + "second-breakdown-block", + "another-number-total-playback", + ], + section_id="default-section", + ) + + return_location = ReturnLocation( + return_to_answer_id=None, + return_to="calculated-summary", + return_to_block_id="another-number-total-playback", + ) + + next_location_url = self.router.get_next_location_url( + current_location, + routing_path, + return_location, + ) + + expected_location = Location( + section_id="default-section", + block_id="second-breakdown-block", + ) + + expected_location_url = url_for( + "questionnaire.block", + block_id=expected_location.block_id, + return_to=return_location.return_to, + return_to_block_id=return_location.return_to_block_id, + return_to_answer_id=return_location.return_to_answer_id, + ) + + assert expected_location_url == next_location_url diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js index ea2d7c8697..f6acefef1b 100644 --- a/tests/functional/spec/features/validation/sum/value_source.spec.js +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -3,6 +3,7 @@ import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_agai import TotalPlaybackPage from "../../../../generated_pages/validation_sum_against_value_source/number-total-playback.page"; import SecondBreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/second-breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; +import AnotherTotalPlaybackPage from "../../../../generated_pages/validation_sum_against_value_source/another-number-total-playback.page"; import { click } from "../../../../helpers"; const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); @@ -45,23 +46,25 @@ describe("Feature: Sum of grouped answers equal to validation against value sour }); describe("Given I have a calculated summary value of 12", () => { - it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the summary page", async () => { + it("When I continue to second breakdown and enter values equal to calculated summary total, Then I should be able to get to the next calculated summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); }); }); describe("Given I completed both grouped answer validation questions and I am on the summary", () => { - it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the summary", async () => { + it("When I go back from the summary and change the total, Then I must reconfirm both breakdown questions with valid answers before I can get to the next calculated summary", async () => { await $(TotalAnswerPage.total()).setValue("12"); await click(TotalAnswerPage.submit()); await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await click(AnotherTotalPlaybackPage.submit()); + await $(SubmitPage.totalAnswerEdit()).click(); await $(TotalAnswerPage.total()).setValue("15"); await click(TotalAnswerPage.submit()); @@ -74,7 +77,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); }); }); @@ -85,6 +88,8 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await click(AnotherTotalPlaybackPage.submit()); + await $(SubmitPage.totalAnswerEdit()).click(); await $(TotalAnswerPage.total()).setValue("15"); await click(TotalAnswerPage.submit()); @@ -97,7 +102,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); }); }); @@ -108,6 +113,8 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); + await click(AnotherTotalPlaybackPage.submit()); + await $(SubmitPage.breakdown1Edit()).click(); await answerAndSubmitBreakdownQuestion("6", "3", "2", "1"); @@ -124,7 +131,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(false); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); }); }); @@ -149,4 +156,31 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await expect(await $(SecondBreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 3"); }); }); + describe("Given I edit a question from a Calculated Summary page", () => { + it("When I change the answer and there is a question that needs to be revisited before I can return to the Calculated Summary Page, Then I revisit the relevant page before I route back to the Calculated Summary page", async () => { + await $(TotalAnswerPage.total()).setValue("5"); + await click(TotalAnswerPage.submit()); + + await answerBothBreakdownQuestions(["2", "1", "1", "1"], ["1", "2", "0", "0"]); + + await $(AnotherTotalPlaybackPage.breakdown1Edit()).click(); + + await $(BreakdownAnswerPage.breakdown1()).setValue("1"); + await $(BreakdownAnswerPage.breakdown2()).setValue("2"); + + await click(BreakdownAnswerPage.submit()); + + await click(TotalPlaybackPage.previous()); + + await click(BreakdownAnswerPage.submit()); + + await click(TotalPlaybackPage.submit()); + + await click(SecondBreakdownAnswerPage.submit()); + + await click(AnotherTotalPlaybackPage.submit()); + + await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + }); + }); }); From 99c73cbca291a2f1b233d42b90e70f2915782270 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 30 Nov 2023 08:29:46 +0000 Subject: [PATCH 335/567] Add confirmation email support to non census thank-you page (#1253) --- app/translations/messages.pot | 175 +++++++----------- app/views/contexts/email_form_context.py | 13 +- app/views/contexts/thank_you_context.py | 25 +-- app/views/handlers/thank_you.py | 35 +--- schemas/test/en/test_confirmation_email.json | 4 +- .../en/test_feedback_email_confirmation.json | 4 +- templates/census-thank-you.html | 64 ------- templates/confirmation-email.html | 2 +- ...form.html => confirmation-email-form.html} | 0 templates/thank-you.html | 9 + .../spec/confirmation_email.spec.js | 10 +- tests/functional/spec/exit.spec.js | 34 ---- ...st_questionnaire_schema_theme_thank_you.py | 48 ----- .../routes/test_confirmation_email.py | 30 ++- 14 files changed, 121 insertions(+), 332 deletions(-) delete mode 100644 templates/census-thank-you.html rename templates/partials/{email-form.html => confirmation-email-form.html} (100%) delete mode 100644 tests/functional/spec/exit.spec.js delete mode 100644 tests/integration/questionnaire/test_questionnaire_schema_theme_thank_you.py diff --git a/app/translations/messages.pot b/app/translations/messages.pot index ca60c4d81c..46c0e2aec3 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-11-10 11:23+0000\n" +"POT-Creation-Date: 2023-11-16 13:49+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -465,7 +465,7 @@ msgstr "" msgid " (You)" msgstr "" -#: app/views/contexts/preview_context.py:75 +#: app/views/contexts/preview_context.py:51 msgid "Preview survey questions" msgstr "" @@ -493,17 +493,17 @@ msgstr "" msgid "Please submit this survey to complete it" msgstr "" -#: app/views/contexts/thank_you_context.py:29 +#: app/views/contexts/thank_you_context.py:30 msgid "" "Your answers have been submitted for {company_name} " "({trading_name})" msgstr "" -#: app/views/contexts/thank_you_context.py:36 +#: app/views/contexts/thank_you_context.py:37 msgid "Your answers have been submitted for {company_name}" msgstr "" -#: app/views/contexts/thank_you_context.py:40 +#: app/views/contexts/thank_you_context.py:41 msgid "Your answers have been submitted." msgstr "" @@ -535,19 +535,19 @@ msgstr "" msgid "Confirm your email address" msgstr "" -#: app/views/handlers/confirm_email.py:95 +#: app/views/handlers/confirm_email.py:90 msgid "Is this email address correct?" msgstr "" -#: app/views/handlers/confirm_email.py:108 -#: app/views/handlers/confirm_email.py:109 -#: app/views/handlers/individual_response.py:930 +#: app/views/handlers/confirm_email.py:103 +#: app/views/handlers/confirm_email.py:104 +#: app/views/handlers/individual_response.py:919 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:135 +#: app/views/handlers/confirm_email.py:130 #: app/views/handlers/confirmation_email.py:67 -#: app/views/handlers/feedback.py:85 app/views/handlers/question.py:196 +#: app/views/handlers/feedback.py:80 app/views/handlers/question.py:177 msgid "Error: {page_title}" msgstr "" @@ -559,236 +559,183 @@ msgstr "" msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:141 +#: app/views/handlers/feedback.py:136 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:147 app/views/handlers/feedback.py:171 +#: app/views/handlers/feedback.py:142 app/views/handlers/feedback.py:166 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:150 app/views/handlers/feedback.py:151 +#: app/views/handlers/feedback.py:145 app/views/handlers/feedback.py:146 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:152 +#: app/views/handlers/feedback.py:147 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:157 app/views/handlers/feedback.py:158 +#: app/views/handlers/feedback.py:152 app/views/handlers/feedback.py:153 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:161 app/views/handlers/feedback.py:164 +#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:159 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:179 app/views/handlers/feedback.py:189 +#: app/views/handlers/feedback.py:174 app/views/handlers/feedback.py:184 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:180 +#: app/views/handlers/feedback.py:175 msgid "Do not include confidential information, such as your contact details" msgstr "" -#: app/views/handlers/individual_response.py:149 +#: app/views/handlers/individual_response.py:151 msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:272 +#: app/views/handlers/individual_response.py:259 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:346 +#: app/views/handlers/individual_response.py:333 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:370 +#: app/views/handlers/individual_response.py:357 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:372 +#: app/views/handlers/individual_response.py:359 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:380 +#: app/views/handlers/individual_response.py:367 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:382 +#: app/views/handlers/individual_response.py:369 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:391 +#: app/views/handlers/individual_response.py:378 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:393 +#: app/views/handlers/individual_response.py:380 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:396 +#: app/views/handlers/individual_response.py:383 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:442 +#: app/views/handlers/individual_response.py:429 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:473 +#: app/views/handlers/individual_response.py:460 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:489 +#: app/views/handlers/individual_response.py:476 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:495 +#: app/views/handlers/individual_response.py:482 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:499 +#: app/views/handlers/individual_response.py:486 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:505 +#: app/views/handlers/individual_response.py:492 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:557 +#: app/views/handlers/individual_response.py:544 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:622 +#: app/views/handlers/individual_response.py:609 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:631 +#: app/views/handlers/individual_response.py:618 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:638 +#: app/views/handlers/individual_response.py:625 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:651 +#: app/views/handlers/individual_response.py:638 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:657 +#: app/views/handlers/individual_response.py:644 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:694 +#: app/views/handlers/individual_response.py:681 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:757 -#: app/views/handlers/individual_response.py:795 +#: app/views/handlers/individual_response.py:746 +#: app/views/handlers/individual_response.py:784 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:761 +#: app/views/handlers/individual_response.py:750 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:819 +#: app/views/handlers/individual_response.py:808 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:832 +#: app/views/handlers/individual_response.py:821 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:833 +#: app/views/handlers/individual_response.py:822 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:868 +#: app/views/handlers/individual_response.py:857 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:917 +#: app/views/handlers/individual_response.py:906 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:926 +#: app/views/handlers/individual_response.py:915 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:968 +#: app/views/handlers/individual_response.py:957 msgid "Confirm mobile number" msgstr "" -#: app/views/handlers/thank_you.py:27 templates/census-thank-you.html:24 -msgid "Thank you for completing the census" +#: app/views/handlers/thank_you.py:23 +msgid "Thank you for completing the survey" msgstr "" #: app/views/handlers/view_submitted_response.py:52 msgid "View Submitted Response" msgstr "" -#: templates/census-thank-you.html:9 templates/confirmation-email.html:8 -msgid "There is a problem with this page" -msgstr "" - -#: templates/census-thank-you.html:22 -msgid "Thank you for completing your census" -msgstr "" - -#: templates/census-thank-you.html:26 -msgid "Thank you for completing the survey" -msgstr "" - -#: templates/census-thank-you.html:32 -msgid "" -"Your individual census has been submitted for " -"{display_address}" -msgstr "" - -#: templates/census-thank-you.html:34 -msgid "" -"Your census has been submitted for the household at " -"{display_address}" -msgstr "" - -#: templates/census-thank-you.html:36 -msgid "" -"Your census has been submitted for the accommodation at " -"{display_address}" -msgstr "" - -#: templates/census-thank-you.html:37 -msgid "" -"Anyone staying at this accommodation for at least 6 months needs to fill " -"in their own individual census, including staff. Your Census Officer will" -" provide you with census forms for your residents." -msgstr "" - -#: templates/census-thank-you.html:49 -msgid "" -"Your personal information is protected by law and will be kept " -"confidential" -msgstr "" - -#: templates/census-thank-you.html:55 -msgid "Get confirmation email" -msgstr "" - -#: templates/census-thank-you.html:56 -msgid "" -"If you would like to be sent confirmation that you have completed your " -"census, enter your email address" -msgstr "" - #: templates/confirm-email.html:12 templates/partials/answers/address.html:55 #: templates/question.html:10 #, python-format @@ -822,6 +769,10 @@ msgid "" "id='send-another-email'>send another confirmation email." msgstr "" +#: templates/confirmation-email.html:8 +msgid "There is a problem with this page" +msgstr "" + #: templates/confirmation-email.html:12 msgid "Send a confirmation email" msgstr "" @@ -1014,6 +965,16 @@ msgstr "" msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" +#: templates/thank-you.html:87 +msgid "Get confirmation email" +msgstr "" + +#: templates/thank-you.html:88 +msgid "" +"If you would like to be sent confirmation that you have completed your " +"survey, enter your email address" +msgstr "" + #: templates/view-submitted-response.html:35 msgid "Print answers" msgstr "" diff --git a/app/views/contexts/email_form_context.py b/app/views/contexts/email_form_context.py index 9103e627cc..1b248baec1 100644 --- a/app/views/contexts/email_form_context.py +++ b/app/views/contexts/email_form_context.py @@ -8,19 +8,16 @@ def build_confirmation_email_form_context( email_confirmation_form: EmailForm, ) -> dict[str, Union[bool, str, Any]]: - context = { + return { "hide_sign_out_button": False, "sign_out_url": url_for("session.get_sign_out"), + "form": build_email_form_context(email_confirmation_form), } - context.update(build_email_form_context(email_confirmation_form)) - return context def build_email_form_context(email_confirmation_form: EmailForm) -> dict[str, Any]: return { - "form": { - "mapped_errors": email_confirmation_form.map_errors(), - "email_field": email_confirmation_form.email, - "errors": email_confirmation_form.errors, - } + "mapped_errors": email_confirmation_form.map_errors(), + "email_field": email_confirmation_form.email, + "errors": email_confirmation_form.errors, } diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index 6fffb9ab82..fcad74e705 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -24,6 +24,7 @@ def build_thank_you_context( submitted_at: datetime, survey_type: SurveyType, guidance_content: Optional[dict] = None, + confirmation_email_form: Optional[EmailForm] = None, ) -> dict[str, Any]: if (ru_name := metadata["ru_name"]) and (trad_as := metadata["trad_as"]): submission_text = lazy_gettext( @@ -43,7 +44,8 @@ def build_thank_you_context( submitted_at, metadata.tx_id, ) - return { + + context = { "hide_sign_out_button": True, "submission_text": submission_text, "metadata": context_metadata, @@ -52,6 +54,11 @@ def build_thank_you_context( schema, submitted_at ), } + if confirmation_email_form: + context["confirmation_email_form"] = build_email_form_context( + confirmation_email_form + ) + return context def build_view_submitted_response_context( @@ -74,19 +81,3 @@ def build_view_submitted_response_context( "post_submission.get_view_submitted_response" ) return view_submitted_response - - -def build_census_thank_you_context( - metadata: MetadataProxy, - confirmation_email_form: Optional[EmailForm], - form_type: str, -) -> dict[str, bool | str | None]: - context = { - "display_address": metadata["display_address"], - "form_type": form_type, - "hide_sign_out_button": False, - "sign_out_url": url_for("session.get_sign_out"), - } - if confirmation_email_form: - context.update(build_email_form_context(confirmation_email_form)) - return context diff --git a/app/views/handlers/thank_you.py b/app/views/handlers/thank_you.py index ad466efbab..a9593409dd 100644 --- a/app/views/handlers/thank_you.py +++ b/app/views/handlers/thank_you.py @@ -1,7 +1,6 @@ from datetime import datetime from functools import cached_property -from flask import session as cookie_session from flask_babel import LazyString, gettext from flask_login import current_user @@ -10,10 +9,7 @@ from app.globals import get_metadata from app.helpers.template_helpers import get_survey_type from app.questionnaire import QuestionnaireSchema -from app.views.contexts.thank_you_context import ( - build_census_thank_you_context, - build_thank_you_context, -) +from app.views.contexts.thank_you_context import build_thank_you_context from app.views.handlers.confirmation_email import ( ConfirmationEmail, ConfirmationEmailLimitReached, @@ -23,8 +19,7 @@ class ThankYou: DEFAULT_THANK_YOU_TEMPLATE = "thank-you" - CENSUS_THANK_YOU_TEMPLATE = "census-thank-you" - PAGE_TITLE = gettext("Thank you for completing the census") + PAGE_TITLE = gettext("Thank you for completing the survey") def __init__( self, @@ -36,12 +31,7 @@ def __init__( self._schema: QuestionnaireSchema = schema self._submitted_at = submitted_at - self._is_census_theme = cookie_session.get("theme") == "census" - self.template = ( - self.CENSUS_THANK_YOU_TEMPLATE - if self._is_census_theme - else self.DEFAULT_THANK_YOU_TEMPLATE - ) + self.template = self.DEFAULT_THANK_YOU_TEMPLATE @cached_property def confirmation_email(self) -> ConfirmationEmail | None: @@ -53,25 +43,18 @@ def confirmation_email(self) -> ConfirmationEmail | None: def get_context(self) -> dict: metadata: MetadataProxy = get_metadata(current_user) # type: ignore - if not self._is_census_theme: - guidance_content = self._schema.get_post_submission().get("guidance") - return build_thank_you_context( - self._schema, - metadata, - self._submitted_at, - get_survey_type(), - guidance_content, - ) - confirmation_email_form = ( self.confirmation_email.form if self.confirmation_email else None ) - return build_census_thank_you_context( + guidance_content = self._schema.get_post_submission().get("guidance") + return build_thank_you_context( + self._schema, metadata, + self._submitted_at, + get_survey_type(), + guidance_content, confirmation_email_form, - # Type ignore: form_type will already be populated at this stage - self._schema.form_type, # type: ignore ) def get_page_title(self) -> str | LazyString: diff --git a/schemas/test/en/test_confirmation_email.json b/schemas/test/en/test_confirmation_email.json index cc7f2c51d3..835b153691 100644 --- a/schemas/test/en/test_confirmation_email.json +++ b/schemas/test/en/test_confirmation_email.json @@ -7,7 +7,7 @@ "form_type": "H", "region_code": "GB-WLS", "title": "Confirmation email test schema", - "theme": "census", + "theme": "default", "description": "A questionnaire to test confirmation email", "metadata": [ { @@ -19,7 +19,7 @@ "type": "string" }, { - "name": "display_address", + "name": "ru_name", "type": "string" } ], diff --git a/schemas/test/en/test_feedback_email_confirmation.json b/schemas/test/en/test_feedback_email_confirmation.json index 85c2aff16d..0a18b7b9e7 100644 --- a/schemas/test/en/test_feedback_email_confirmation.json +++ b/schemas/test/en/test_feedback_email_confirmation.json @@ -5,7 +5,7 @@ "data_version": "0.0.3", "survey_id": "0", "title": "Feedback test schema", - "theme": "census", + "theme": "default", "description": "A questionnaire to test feedback", "metadata": [ { @@ -17,7 +17,7 @@ "type": "string" }, { - "name": "display_address", + "name": "ru_name", "type": "string" } ], diff --git a/templates/census-thank-you.html b/templates/census-thank-you.html deleted file mode 100644 index 507a793f87..0000000000 --- a/templates/census-thank-you.html +++ /dev/null @@ -1,64 +0,0 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/panel/_macro.njk' import onsPanel %} -{% set hide_sign_out_button = content.hide_sign_out_button %} -{% set sign_out_url = content.sign_out_url %} - -{% block main %} - {% set form = content.form %} - {% if form and form.mapped_errors %} - {% set error_title = _("There is a problem with this page") %} - {% include 'partials/error-panel.html' %} - {% endif %} - - {% call onsPanel({ - "type": "success", - "classes": "ons-u-mb-s", - "iconType": "check", - "iconSize": "xl" - }) - %} -

      - {% if content.form_type == "I" %} - {{ _("Thank you for completing your census") }} - {% elif content.form_type %} - {{ _("Thank you for completing the census") }} - {% else %} - {{ _("Thank you for completing the survey") }} - {% endif %} -

      - - {% if content.form_type %} - {% if content.form_type == "I" %} -

      {{ _("Your individual census has been submitted for {display_address}").format(display_address = content.display_address) }}

      - {% elif content.form_type == "H" %} -

      {{ _("Your census has been submitted for the household at {display_address}").format(display_address = content.display_address) }}

      - {% elif content.form_type == "C" %} -

      {{ _("Your census has been submitted for the accommodation at {display_address}").format(display_address = content.display_address) }}

      -

      {{ _("Anyone staying at this accommodation for at least 6 months needs to fill in their own individual census, including staff. Your Census Officer will provide you with census forms for your residents.") }}

      - {% endif %} - {% endif %} - {% endcall %} - - - {% call onsPanel({ - "type": "bare", - "classes": "ons-u-mb-s", - "iconType": "lock" - }) - %} -

      {{ _("Your personal information is protected by law and will be kept confidential") }}

      - {% endcall %} - - - {% if form %} -
      -

      {{ _("Get confirmation email") }}

      -

      {{ _("If you would like to be sent confirmation that you have completed your census, enter your email address") }}

      - {% include 'partials/email-form.html' %} - {% endif %} - - {% if content.show_feedback_call_to_action %} - {% include 'partials/feedback-call-to-action.html' %} - {% endif %} - -{% endblock %} diff --git a/templates/confirmation-email.html b/templates/confirmation-email.html index 3fe38c7dfc..f84e2af11f 100644 --- a/templates/confirmation-email.html +++ b/templates/confirmation-email.html @@ -10,5 +10,5 @@ {% endif %}

      {{ _("Send a confirmation email") }}

      - {% include 'partials/email-form.html' %} + {% include 'partials/confirmation-email-form.html' %} {% endblock %} diff --git a/templates/partials/email-form.html b/templates/partials/confirmation-email-form.html similarity index 100% rename from templates/partials/email-form.html rename to templates/partials/confirmation-email-form.html diff --git a/templates/thank-you.html b/templates/thank-you.html index 6a715e17ed..ae7504a6e3 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -80,6 +80,15 @@

      {{ _("Get a copy of your answers") } {% endif %} {% endif %} + {% if content.confirmation_email_form %} +
      +

      {{ _("Get confirmation email") }}

      +

      {{ _("If you would like to be sent confirmation that you have completed your survey, enter your email address") }}

      + {% with form=content.confirmation_email_form %} + {% include 'partials/confirmation-email-form.html' %} + {% endwith %} + {% endif %} + {% if content.show_feedback_call_to_action %} {% include 'partials/feedback-call-to-action.html' %} {% endif %} diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index 0f41117fae..d3947b9a8a 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -6,6 +6,8 @@ import ConfirmationEmailSentPage from "../base_pages/confirmation-email-sent.pag import ConfirmEmailPage from "../base_pages/confirm-email.page"; import { click } from "../helpers"; +const errorPanel = '[data-ga="error"]'; + describe("Email confirmation", () => { describe("Given I launch the test email confirmation survey", () => { before(async () => { @@ -22,16 +24,16 @@ describe("Email confirmation", () => { it("When I submit the form without providing an email address, Then I get an error message", async () => { await click(ThankYouPage.submit()); await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); - await expect(await $(ThankYouPage.errorPanel()).isExisting()).toBe(true); - await expect(await $(ThankYouPage.errorPanel()).getText()).toContain("Enter an email address"); + await expect(await $(errorPanel).isExisting()).toBe(true); + await expect(await $(errorPanel).getText()).toBe("Enter an email address"); }); it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ThankYouPage.email()).setValue("incorrect-format"); await click(ThankYouPage.submit()); await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); - await expect(await $(ThankYouPage.errorPanel()).isExisting()).toBe(true); - await expect(await $(ThankYouPage.errorPanel()).getText()).toContain("Enter an email address in a valid format, for example name@example.com"); + await expect(await $(errorPanel).isExisting()).toBe(true); + await expect(await $(errorPanel).getText()).toBe("Enter an email address in a valid format, for example name@example.com"); }); it("When I submit the form with a valid email address, Then I go to the confirm email page", async () => { diff --git a/tests/functional/spec/exit.spec.js b/tests/functional/spec/exit.spec.js deleted file mode 100644 index 54da5684ce..0000000000 --- a/tests/functional/spec/exit.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -import CensusThankYouPage from "../base_pages/census-thank-you.page.js"; -import HubPage from "../base_pages/hub.page"; -import { SubmitPage } from "../base_pages/submit.page.js"; -import { click } from "../helpers"; - -describe("Post submission exit", () => { - beforeEach("Load the survey", async () => { - await browser.openQuestionnaire("test_thank_you_census_household.json"); - }); - - it("Given I click the exit button from the thank you page which has no session cookie, When I am redirected, Then I should be redirected to the correct log out url", async () => { - await click(SubmitPage.submit()); - await click(HubPage.submit()); - await browser.deleteAllCookies(); - await $(CensusThankYouPage.exit()).click(); - await expect(browser).toHaveUrlContaining("https://surveys.ons.gov.uk/sign-in/"); - }); - - it("Given I click the exit button from the thank you page, When I am redirected, Then I should be redirected to the correct log out url", async () => { - await click(SubmitPage.submit()); - await click(HubPage.submit()); - await $(CensusThankYouPage.exit()).click(); - await expect(browser).toHaveUrlContaining("https://www.ons.gov.uk/census"); - }); - - it("Given I have clicked the exit button, When I navigate back, Then I am taken to the session timed out page", async () => { - await click(SubmitPage.submit()); - await click(HubPage.submit()); - await $(CensusThankYouPage.exit()).click(); - await browser.back(); - await expect(browser).toHaveUrlContaining("submitted/thank-you"); - await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); - }); -}); diff --git a/tests/integration/questionnaire/test_questionnaire_schema_theme_thank_you.py b/tests/integration/questionnaire/test_questionnaire_schema_theme_thank_you.py deleted file mode 100644 index e87138395d..0000000000 --- a/tests/integration/questionnaire/test_questionnaire_schema_theme_thank_you.py +++ /dev/null @@ -1,48 +0,0 @@ -from tests.integration.integration_test_case import IntegrationTestCase - - -class TestSchemaThemeThankYou(IntegrationTestCase): - def test_census_individual(self): - self.launchSurvey( - "test_thank_you_census_individual", - display_address="68 Abingdon Road, Goathill", - ) - self.post({"individual-confirmation": "Yes"}) - self.post() - self.assertInBody("Thank you for completing your census") - self.assertInBody( - "Your individual census has been submitted for 68 Abingdon Road, Goathill" - ) - self.assertInBody( - 'Make sure you leave this page or close your browser if using a shared device' - ) - - def test_census_household(self): - self.launchSurvey( - "test_thank_you_census_household", - display_address="68 Abingdon Road, Goathill", - ) - self.post({"household-confirmation": "Yes"}) - self.post() - self.assertInBody("Thank you for completing the census") - self.assertInBody( - "Your census has been submitted for the household at 68 Abingdon Road, Goathill" - ) - - def test_census_communal_establishment(self): - self.launchSurvey( - "test_thank_you_census_communal_establishment", - display_address="68 Abingdon Road, Goathill", - ) - self.post({"communal-establishment-confirmation": "Yes"}) - self.post() - self.assertInBody("Thank you for completing the census") - self.assertInBody( - "Your census has been submitted for the accommodation at 68 Abingdon Road, Goathill" - ) - - def test_census_theme_schema_name_not_mapped_to_census_type(self): - self.launchSurvey("test_feedback_email_confirmation") - self.post({"schema-confirmation-answer": "Yes"}) - self.post() - self.assertInBody("Thank you for completing the survey") diff --git a/tests/integration/routes/test_confirmation_email.py b/tests/integration/routes/test_confirmation_email.py index 9939a2b39a..42f2874bc8 100644 --- a/tests/integration/routes/test_confirmation_email.py +++ b/tests/integration/routes/test_confirmation_email.py @@ -145,7 +145,7 @@ def test_census_themed_schema_with_confirmation_email_true(self): self.assertInUrl("/submitted/thank-you/") self.assertInBody("Get confirmation email") self.assertEqualPageTitle( - "Thank you for completing the census - Confirmation email test schema" + "We’ve received your answers - Confirmation email test schema" ) def test_census_themed_schema_with_confirmation_email_not_set(self): @@ -255,11 +255,7 @@ def test_thank_you_missing_email(self): # Then I get an error message on the thank you page self.assertInUrl("/submitted/thank-you/") - self.assertInBody("There is a problem with this page") - self.assertInBody("Enter an email address") - self.assertEqualPageTitle( - "Error: Thank you for completing the census - Confirmation email test schema" - ) + self.assertInSelectorCSS("Enter an email address", class_="ons-panel__error") def test_thank_you_incorrect_email_format(self): # Given I launch and complete the test_confirmation_email questionnaire @@ -270,13 +266,9 @@ def test_thank_you_incorrect_email_format(self): # Then I get an error message on the thank you page self.assertInUrl("thank-you") - self.assertInBody("There is a problem with this page") - self.assertInBody( - "Enter an email address in a valid format, for example name@example.com" - ) - - self.assertEqualPageTitle( - "Error: Thank you for completing the census - Confirmation email test schema" + self.assertInSelectorCSS( + "Enter an email address in a valid format, for example name@example.com", + class_="ons-panel__error", ) def test_thank_you_email_invalid_tld(self): @@ -288,9 +280,9 @@ def test_thank_you_email_invalid_tld(self): # Then I get an error message on the thank you page self.assertInUrl("thank-you") - self.assertInBody("There is a problem with this page") - self.assertInBody( - "Enter an email address in a valid format, for example name@example.com" + self.assertInSelectorCSS( + "Enter an email address in a valid format, for example name@example.com", + class_="ons-panel__error", ) def test_thank_you_email_invalid_and_invalid_tld(self): @@ -302,9 +294,9 @@ def test_thank_you_email_invalid_and_invalid_tld(self): # Then I get a single error message on the thank you page self.assertInUrl("thank-you") - self.assertInBody("There is a problem with this page") - self.assertInBody( - "Enter an email address in a valid format, for example name@example.com" + self.assertInSelectorCSS( + "Enter an email address in a valid format, for example name@example.com", + class_="ons-panel__error", ) self.assertNotInBody('data-qa="error-link-2"') From 72e03902dbe2eb055739704d10f85df86faa972e Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:29:21 +0000 Subject: [PATCH 336/567] Update DS to v67 (#1263) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 259bcce324..a76dccdfcc 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -66.0.1 +67.0.0 From eda53b9d68ae20a8f813ed0469266b49ab05c3f7 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Thu, 30 Nov 2023 13:27:19 +0000 Subject: [PATCH 337/567] Add require-await rule for functional test linting (#1267) --- .eslintrc | 3 +- tests/functional/helpers.js | 6 +- ...edirect_to_list_add_block_checkbox.spec.js | 2 +- ...n_redirect_to_list_add_block_radio.spec.js | 2 +- .../calculated_summary.spec.js | 80 +++++++++++++++++-- .../calculated_summary_test_case.js | 2 +- ...mmary_repeating_and_static_answers.spec.js | 14 ++-- ...alculated_summary_repeating_blocks.spec.js | 18 ++--- ...lculated_summary_repeating_section.spec.js | 2 +- .../dynamic_answer_options/function_driven.js | 2 +- ...d_summary_inside_repeating_section.spec.js | 4 +- ...lculated_summary_repeating_answers.spec.js | 13 +-- .../custom_question_summary.spec.js | 2 +- .../list_collector_repeating_blocks.spec.js | 28 +++---- .../supplementary_data.spec.js | 27 ++++--- .../date_validation/date-single.spec.js | 2 +- ...otal_in_separate_section_repeating.spec.js | 2 +- tests/functional/spec/list_collector.spec.js | 6 +- .../spec/list_collector_variants.spec.js | 4 +- .../functional/spec/multiple_answers.spec.js | 2 +- 20 files changed, 148 insertions(+), 73 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2c85ab3b2a..f6110e9fed 100755 --- a/.eslintrc +++ b/.eslintrc @@ -68,6 +68,7 @@ { "blocks": "never" } - ] + ], + "require-await": "error" } } diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 17f3050f70..213369f7f8 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -23,9 +23,9 @@ const assertSummaryFunction = (selector) => { }; }; -export const assertSummaryValues = assertSummaryFunction('dd[class="ons-summary__values"]'); -export const assertSummaryTitles = assertSummaryFunction('dt[class="ons-summary__title"]'); -export const assertSummaryItems = assertSummaryFunction('dd[class="ons-summary__item--text"]'); +export const assertSummaryValues = assertSummaryFunction(".ons-summary__values"); +export const assertSummaryTitles = assertSummaryFunction(".ons-summary__title"); +export const assertSummaryItems = assertSummaryFunction(".ons-summary__item--text"); export const repeatingAnswerChangeLink = (answerIndex) => { return $$('dd[class="ons-summary__actions"]')[answerIndex].$("a"); diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js index 1d216b69f2..05d1d5bf5a 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -37,7 +37,7 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; - checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); + await checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js index ab7f4ebdad..46871e6162 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js @@ -37,7 +37,7 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; - checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); + await checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); }); it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js b/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js index 76d9aa14e8..cee8c0f62c 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js @@ -18,27 +18,91 @@ describe("Feature: Calculated Summary", () => { describe("Feature: Calculated Summary with negative values", () => { describe("Given I enter a negative value in the first section", () => { - CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, 2, 3, 0, "£4.00", ["-£1.00", "£2.00", "£3.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, 2, 3, 0, "£4.00", [ + "-£1.00", + "£2.00", + "£0.00", + "£3.00", + "£0.00", + "£0.00", + "£4.00", + ]); }); describe("Given I enter a negative value in the second section ", () => { - CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 12, -2, 1, 0, "£11.00", ["£12.00", "-£2.00", "£1.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 12, -2, 1, 0, "£11.00", [ + "£12.00", + "-£2.00", + "£0.00", + "£1.00", + "£0.00", + "£0.00", + "£11.00", + ]); }); describe("Given I enter a negative value in the third section ", () => { - CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 3, 2, -6, 0, "-£1.00", ["£3.00", "£2.00", "-£6.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", 3, 2, -6, 0, "-£1.00", [ + "£3.00", + "£2.00", + "£0.00", + "-£6.00", + "£0.00", + "£0.00", + "-£1.00", + ]); }); describe("Given I enter negative values in every currency field ", () => { - CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, -2, -3, 0, "-£6.00", ["-£1.00", "-£2.00", "-£3.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_calculated_summary.json", -1, -2, -3, 0, "-£6.00", [ + "-£1.00", + "-£2.00", + "£0.00", + "-£3.00", + "£0.00", + "£0.00", + "-£6.00", + ]); }); describe("Given I enter a negative value in the first section", () => { - CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, 2, 3, 0, "£4.00", ["-£1.00", "£2.00", "£3.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, 2, 3, 0, "£4.00", [ + "-£1.00", + "£2.00", + "£0.00", + "£3.00", + "£0.00", + "£0.00", + "£4.00", + ]); }); describe("Given I enter a negative value in the second section ", () => { - CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 12, -2, 1, 0, "£11.00", ["£12.00", "-£2.00", "£1.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 12, -2, 1, 0, "£11.00", [ + "£12.00", + "-£2.00", + "£0.00", + "£1.00", + "£0.00", + "£0.00", + "£11.00", + ]); }); describe("Given I enter a negative value in the third section ", () => { - CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 3, 2, -6, 0, "-£1.00", ["£3.00", "£2.00", "-£6.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", 3, 2, -6, 0, "-£1.00", [ + "£3.00", + "£2.00", + "£0.00", + "-£6.00", + "£0.00", + "£0.00", + "-£1.00", + ]); }); describe("Given I enter negative values in every currency field ", () => { - CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, -2, -3, 0, "-£6.00", ["-£1.00", "-£2.00", "-£3.00", "£0.00"]); + CalculatedSummaryTestCase.testNegative("test_new_calculated_summary.json", -1, -2, -3, 0, "-£6.00", [ + "-£1.00", + "-£2.00", + "£0.00", + "-£3.00", + "£0.00", + "£0.00", + "-£6.00", + ]); }); }); diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js index dbad6a7ea6..540711cb4d 100644 --- a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js @@ -451,7 +451,7 @@ class TestCase { await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); }); it("Given I have entered a range of positive and negative values, When I reach the calculated summary, Then the total is correct", async () => { - assertSummaryValues(expectedAnswerValues); + await assertSummaryValues(expectedAnswerValues); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( `We calculate the total of currency values entered to be ${expectedTotalValue}. Is this correct?`, ); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index 6878d3bb3c..7602805482 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -50,12 +50,12 @@ describe("Calculated summary with repeating answers", () => { "We calculate the total cost of your weekly shopping to be £550.00. Is this correct?", ); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryAnswer()).getText()).toBe("£550.00"); - await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00"]); + await assertSummaryValues(["£300.00", "£200.00", "£30.00", "£15.00", "£5.00", "£0.00", "£550.00"]); await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 6. Is this correct?", ); - await assertSummaryValues(["4", "2"]); + await assertSummaryValues(["4", "2", "6"]); }); it("Given I click on a change link, when I use the previous button, I return to the calculated summary", async () => { @@ -73,7 +73,7 @@ describe("Calculated summary with repeating answers", () => { await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 7. Is this correct?", ); - await assertSummaryValues(["4", "3"]); + await assertSummaryValues(["4", "3", "7"]); await click(CalculatedSummaryVisitsPage.submit()); }); @@ -117,14 +117,14 @@ describe("Calculated summary with repeating answers", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?", ); - await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00"]); + await assertSummaryValues(["£300.00", "£200.00", "£100.00", "£30.00", "£15.00", "£10.00", "£5.00", "£0.00", "£710.00"]); // second calculated summary await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 14. Is this correct?", ); - await assertSummaryValues(["4", "3", "2"]); + await assertSummaryValues(["4", "3", "2", "14"]); await click(CalculatedSummaryVisitsPage.submit()); await expect(browser).toHaveUrlContaining(SummaryPage.pageName); }); @@ -148,12 +148,12 @@ describe("Calculated summary with repeating answers", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £380.00. Is this correct?", ); - await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00"]); + await assertSummaryValues(["£200.00", "£100.00", "£15.00", "£10.00", "£5.00", "£50.00", "£380.00"]); await click(CalculatedSummarySpendingPage.submit()); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 10. Is this correct?", ); - await assertSummaryValues(["3", "7"]); + await assertSummaryValues(["3", "7", "10"]); await click(CalculatedSummaryVisitsPage.submit()); await expect(await $(SummaryPage.supermarketsListLabel(1)).getText()).toBe("Lidl"); diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index e58eb0efb6..8f1cc6c465 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -49,7 +49,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £400.00. Is this correct?", ); - await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00"]); + await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£400.00"]); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Monthly expenditure travelling by car"); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Monthly season ticket expenditure for travel by Bus"); await expect(await $(CalculatedSummarySpendingPage.summaryItems()).getText()).toContain("Additional monthly expenditure for travel by Bus"); @@ -62,7 +62,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 12. Is this correct?", ); - await assertSummaryValues(["10", "2"]); + await assertSummaryValues(["10", "2", "12"]); await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).toContain("Monthly journeys by Bus"); await expect(await $(CalculatedSummaryCountPage.summaryItems()).getText()).toContain("Monthly journeys by Plane"); await click(CalculatedSummaryCountPage.submit()); @@ -84,7 +84,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?", ); - await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + await assertSummaryValues(["£100.00", "£30.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00", "£550.00"]); }); it("Given I am on the first calculated summary, When I confirm the total, Then I see the second calculated summary with an updated total", async () => { @@ -93,7 +93,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 18. Is this correct?", ); - await assertSummaryValues(["10", "2", "6"]); + await assertSummaryValues(["10", "2", "6", "18"]); await $(CalculatedSummaryCountPage.previous()).click(); }); @@ -121,7 +121,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?", ); - await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + await assertSummaryValues(["£100.00", "£60.00", "£5.00", "£0.00", "£265.00", "£100.00", "£50.00", "£580.00"]); await click(CalculatedSummarySpendingPage.submit()); }); @@ -133,7 +133,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 24. Is this correct?", ); - await assertSummaryValues(["10", "2", "12"]); + await assertSummaryValues(["10", "2", "12", "24"]); await click(CalculatedSummaryCountPage.submit()); }); @@ -145,7 +145,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?", ); - await assertSummaryValues(["£100.00", "£0.00", "£265.00", "£100.00", "£50.00"]); + await assertSummaryValues(["£100.00", "£0.00", "£265.00", "£100.00", "£50.00", "£515.00"]); }); it("Given I have confirmed the first updated total, When I press continue, Then I see the next calculated summary to confirm that total too", async () => { @@ -154,7 +154,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 14. Is this correct?", ); - await assertSummaryValues(["2", "12"]); + await assertSummaryValues(["2", "12", "14"]); }); it("Given I have a second section, When I begin and answer the first question with a total higher than the calculated summary, Then I see an error message preventing me from continuing", async () => { @@ -225,7 +225,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?", ); - await assertSummaryValues(["£100.00"]); + await assertSummaryValues(["£100.00", "£100.00"]); }); it("Given I confirm the calculated summary and finish the section, When I return to the Hub, Then I see that section 2 is no longer available", async () => { diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js index 0b6e252a40..7c43562bd9 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -319,7 +319,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); }); - describe("Given I have a Calculated Summary in a Repeating Section", async () => { + describe("Given I have a Calculated Summary in a Repeating Section", () => { before("Get to Final Summary", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_section.json"); await click(HubPage.submit()); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 05e11b7ebd..23f5a05498 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -30,7 +30,7 @@ const openQuestionnaireAndSetUp = async (schema) => { await click(ReferenceDatePage.submit()); }; -testCases.forEach(async (testCase) => { +testCases.forEach((testCase) => { describe(`Feature: Dynamically generated answer options driven by a function (${testCase.schemaName})`, () => { describe("Selecting/Deselecting", () => { before("Open questionnaire", async () => { diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index 5d54c91aae..60f26906c2 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -67,7 +67,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toBe("Vehicle base cost"); await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toBe("Monthly Car costs"); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toBe("Grand total Car expenditure"); - assertSummaryValues(["£90.00", "£225.00", "£315.00"]); + await assertSummaryValues(["£90.00", "£225.00", "£315.00"]); }); it("Given I immediately use that Grand Calculated Summary for validation, When I enter a sum of values too high, Then I see an error message", async () => { @@ -112,7 +112,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostLabel()).getText()).toBe("Vehicle base cost"); await expect(await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostLabel()).getText()).toBe("Monthly Van costs"); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryQuestion()).getText()).toBe("Grand total Van expenditure"); - assertSummaryValues(["£90.00", "£95.00", "£185.00"]); + await assertSummaryValues(["£90.00", "£95.00", "£185.00"]); }); it("Given I am at a Grand Summary inside a repeating section, When I click the change link for a repeating calculated summary, Then I am taken to the correct page", async () => { diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 84eeec3702..1f5e2bee3e 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -262,8 +262,8 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £280.00. Is this correct?", ); - assertSummaryValues(["£250.00", "£30.00", "£280.00"]); - assertSummaryItems([ + await assertSummaryValues(["£250.00", "£30.00", "£280.00"]); + await assertSummaryItems([ "Total monthly expenditure on utility bills", "Total monthly expenditure on streaming services", "Total monthly expenditure on bills and streaming services", @@ -275,8 +275,8 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); - assertSummaryValues(["45 GB", "55 GB", "100 GB"]); - assertSummaryItems(["Total internet usage on streaming services", "Total internet usage on other services", "Total internet usage"]); + await assertSummaryValues(["45 GB", "55 GB", "100 GB"]); + await assertSummaryItems(["Total internet usage on streaming services", "Total internet usage on other services", "Total internet usage"]); }); it("Given I have multiple calculated summaries of static, repeating and dynamic answers, When I reach the grand calculated summary of them all, Then I see the correct total and items", async () => { @@ -284,13 +284,14 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,130.00. Is this correct?", ); - assertSummaryValues(["£550.00", "£300.00", "£0.00", "£250.00", "£30.00", "£1,130.00"]); - assertSummaryValues([ + await assertSummaryValues(["£550.00", "£300.00", "£0.00", "£250.00", "£30.00", "£1,130.00"]); + await assertSummaryItems([ "Total monthly food expenditure", "Total monthly clothes expenditure", "Total games expenditure", "Total monthly expenditure on utility bills", "Total monthly expenditure on streaming services", + "Total monthly expenditure", ]); }); diff --git a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js index 3d50a6cc04..caad8184a8 100644 --- a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js +++ b/tests/functional/spec/features/question_summary/custom_question_summary.spec.js @@ -9,7 +9,7 @@ describe("Summary Screen", () => { }); it("Given a survey has question summary concatenations and has been completed when on the summary page then the correct response should be displayed formatted correctly", async () => { - completeAllQuestions(); + await completeAllQuestions(); await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).toBe("John Smith"); await expect(await $(SubmitPage.summaryRowState("address-question-concatenated-answer")).getText()).toBe("Cardiff Road\nNewport\nNP10 8XG"); await expect(await $(SubmitPage.summaryRowState("age-question-concatenated-answer")).getText()).toBe("7\nThis age is an estimate"); diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 2a31cd218a..62340d9f5f 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -89,7 +89,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("MOD", "789", "3", "3", "2023", true); - checkItemsInList(["ONS", "GOV", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); + await checkItemsInList(["ONS", "GOV", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); }); it("When the user edits an item, Then the name of the item is able to be changed", async () => { @@ -103,7 +103,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.listRemoveLink(2)).click(); await $(RemoveCompanyPage.yes()).click(); await click(RemoveCompanyPage.submit()); - checkItemsInList(["ONS", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); + await checkItemsInList(["ONS", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toContain("Government"); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("MOD"); }); @@ -112,7 +112,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await addCompany("Council", "101", "4", "4", "2023", false, true); - checkItemsInList(["ONS", "MOD", "Council"], AnyOtherCompaniesOrBranchesPage.listLabel); + await checkItemsInList(["ONS", "MOD", "Council"], AnyOtherCompaniesOrBranchesPage.listLabel); }); it("When a user has finished making changes to the list, Then section can be completed and the questionnaire submitted", async () => { @@ -159,11 +159,11 @@ describe("List Collector Repeating Blocks", () => { await addCompany("NAV", "101", "4", "4", "2023", true, true); // Only the ONS and NAV items should be complete - checkItemsInList(["ONS", "GOV", "MOD", "NAV"], AnyOtherCompaniesOrBranchesPage.listLabel); - checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - checkListItemIncomplete(`dt[data-qa="list-item-2-label"]`); - checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); - checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkItemsInList(["ONS", "GOV", "MOD", "NAV"], AnyOtherCompaniesOrBranchesPage.listLabel); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkListItemIncomplete(`dt[data-qa="list-item-2-label"]`); + await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); }); it("When an item has incomplete repeating blocks, Then using submit on the list collector page will navigate the user to the first incomplete repeating block.", async () => { @@ -193,10 +193,10 @@ describe("List Collector Repeating Blocks", () => { it("When the last remaining incomplete repeating block is completed, Then all items are marked as completed with the checkmark icon.", async () => { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - checkListItemComplete(`dt[data-qa="list-item-2-label"]`); - checkListItemComplete(`dt[data-qa="list-item-3-label"]`); - checkListItemComplete(`dt[data-qa="list-item-4-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-3-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-4-label"]`); }); it("When the user clicks a change link from the section summary and submits without changing an answer, Then the user is returned to the section summary anchored to the answer they clicked on", async () => { @@ -305,8 +305,8 @@ describe("List Collector Repeating Blocks", () => { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); - checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); }); it("When another incomplete item is added via the section summary, Then navigating to the submit page of the section will redirect to the list collector page.", async () => { diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 8fb84e08da..3781fee978 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -259,8 +259,12 @@ describe("Using supplementary data", () => { await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); - assertSummaryItems(["Volume of sales for Articles and equipment for sports or outdoor games", "Volume of sales for Kitchen Equipment"]); - assertSummaryValues(["100 kg", "50 kg"]); + await assertSummaryItems([ + "Volume of sales for Articles and equipment for sports or outdoor games", + "Volume of sales for Kitchen Equipment", + "Total sales volume", + ]); + await assertSummaryValues(["100 kg", "50 kg", "150 kg"]); await click(CalculatedSummaryVolumeSalesPage.submit()); }); @@ -268,8 +272,12 @@ describe("Using supplementary data", () => { await expect(await $(CalculatedSummaryVolumeTotalPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total volume produced over the previous quarter to be 500 kg. Is this correct?", ); - assertSummaryItems(["Total volume produced for Articles and equipment for sports or outdoor games", "Total volume produced for Kitchen Equipment"]); - assertSummaryValues(["200 kg", "300 kg"]); + await assertSummaryItems([ + "Total volume produced for Articles and equipment for sports or outdoor games", + "Total volume produced for Kitchen Equipment", + "Total volume produced", + ]); + await assertSummaryValues(["200 kg", "300 kg", "500 kg"]); await click(CalculatedSummaryVolumeTotalPage.submit()); }); @@ -287,18 +295,19 @@ describe("Using supplementary data", () => { await expect(await $(CalculatedSummaryValueSalesPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total value of sales over the previous quarter to be £660.00. Is this correct?", ); - assertSummaryItems([ + await assertSummaryItems([ "Value of sales for Articles and equipment for sports or outdoor games", "Value of sales for Kitchen Equipment", "Value of sales from other categories", + "Total sales value", ]); - assertSummaryValues(["£110.00", "£220.00", "£330.00"]); + await assertSummaryValues(["£110.00", "£220.00", "£330.00", "£660.00"]); await click(CalculatedSummaryValueSalesPage.submit()); }); it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { await expect(await $$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); - assertSummaryItems([ + await assertSummaryItems([ "Articles and equipment for sports or outdoor games", "Volume of sales for Articles and equipment for sports or outdoor games", "Total volume produced for Articles and equipment for sports or outdoor games", @@ -309,7 +318,7 @@ describe("Using supplementary data", () => { "Value of sales for Kitchen Equipment", "Value of sales from other categories", ]); - assertSummaryValues(["100 kg", "200 kg", "110 kg", "50 kg", "300 kg", "220 kg", "£110.00", "£220.00", "£330.00"]); + await assertSummaryValues(["100 kg", "200 kg", "50 kg", "300 kg", "£110.00", "£220.00", "£330.00"]); await click(Section6Page.submit()); }); @@ -345,7 +354,7 @@ describe("Using supplementary data", () => { await click(HubPage.submit()); await $(ThankYouPage.savePrintAnswersLink()).click(); - assertSummaryTitles(["Company Details", "Employees", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); + await assertSummaryTitles(["Company Details", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); // Company details await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).toBe("Is contact@lidl.org still the correct contact email for Lidl?"); diff --git a/tests/functional/spec/features/validation/date_validation/date-single.spec.js b/tests/functional/spec/features/validation/date_validation/date-single.spec.js index 95d46e315b..6656f19f76 100644 --- a/tests/functional/spec/features/validation/date_validation/date-single.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-single.spec.js @@ -5,7 +5,7 @@ import { click } from "../../../../helpers"; describe("Feature: Validation for single date periods", () => { beforeEach(async () => { await browser.openQuestionnaire("test_date_validation_single.json"); - completeFirstDatePage(); + await completeFirstDatePage(); }); describe("Given I enter a date before the minimum offset meta date", () => { diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index 3b60ec97ea..1420021d3a 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -58,7 +58,7 @@ const answerAndSubmitEntertainmentBreakdownQuestion = async (breakdown1, breakdo await click(EntertainmentBreakdownPage.submit()); }; -const assertRepeatingSectionOnChange = async (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { +const assertRepeatingSectionOnChange = (repeatIndex, currentBreakdown1, currentBreakdown2, currentBreakdown3, newTotal) => { it(`When I click 'Continue with section' on repeating section ${repeatIndex}, Then I should be taken to the spending breakdown question and my previous answers should be prefilled`, async () => { await $(HubPage.summaryRowLink(repeatingSectionId(repeatIndex))).click(); diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector.spec.js index a62242ffdb..2e53ef29bb 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector.spec.js @@ -53,7 +53,7 @@ describe("List Collector", () => { it("The collector shows all of the household members in the summary", async () => { const peopleExpected = ["Marcus Twin", "Samuel Clemens", "Olivia Clemens", "Suzy Clemens"]; - checkItemsInList(peopleExpected, ListCollectorPage.listLabel); + await checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire allows the name of a person to be changed", async () => { @@ -111,7 +111,7 @@ describe("List Collector", () => { it("The collector shows everyone on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; - checkItemsInList(peopleExpected, ListCollectorPage.listLabel); + await checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("When No is answered on the list collector the user sees an interstitial", async () => { @@ -127,7 +127,7 @@ describe("List Collector", () => { it("The collector still shows the same list of people on the summary", async () => { const peopleExpected = ["Samuel Clemens", "Olivia Clemens", "Suzy Clemens", "Clara Clemens", "Jean Clemens"]; - checkItemsInList(peopleExpected, ListCollectorPage.listLabel); + await checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The collector allows the user to add another person to the same list", async () => { diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector_variants.spec.js index 2b4b4056c3..6bb3610d2a 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector_variants.spec.js @@ -30,7 +30,7 @@ describe("List Collector With Variants", () => { it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; - checkItemsInList(peopleExpected, ListCollectorPage.listLabel); + await checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire has the correct question text on the change and remove pages", async () => { @@ -76,7 +76,7 @@ describe("List Collector With Variants", () => { it("The user can see all household members in the summary", async () => { const peopleExpected = ["Samuel Clemens"]; - checkItemsInList(peopleExpected, ListCollectorPage.listLabel); + await checkItemsInList(peopleExpected, ListCollectorPage.listLabel); }); it("The questionnaire has the correct question text on the change and remove pages", async () => { diff --git a/tests/functional/spec/multiple_answers.spec.js b/tests/functional/spec/multiple_answers.spec.js index ff716bff13..4a9f702e17 100644 --- a/tests/functional/spec/multiple_answers.spec.js +++ b/tests/functional/spec/multiple_answers.spec.js @@ -33,7 +33,7 @@ describe("Multiple Answers", () => { describe("Given I have completed a questionnaire that has multiple answers per question", () => { beforeEach("Load the questionnaire and answer all questions", async () => { await browser.openQuestionnaire("test_multiple_answers.json"); - answerAllQuestions(); + await answerAllQuestions(); }); it("When I am on the summary, Then all answers are displayed", async () => { From a1a0b1028f0a6bcc48c5c28af2afaf7cfff8efd1 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Fri, 1 Dec 2023 10:30:36 +0000 Subject: [PATCH 338/567] Add type hints to the utilities module (#1252) * Add type hints to json.py * Update MyPy ini file to include the utilities module * Add type hints to decimal_places.py * Add type hints to metadata_parser.py * Add type hints to metadata_parser_v2.py * Add type hints to metadata_validators.py * Add type hints to request_session.py * Add type hints to schema.py * Add type hints to supplementary_data_parser.py * Format python code * Fix variable name issue * Tidy up pylint comments * Add more type hint definitions * test new changes to ensure the deserialize method uses default format (iso8601) unless a format is specified * Tidy up type hints and add missing hints for empty collection * Refactor code to improve type hints * Refactor/improve type hints and install new dev package * Refactor more type hints * Update type ignore message * Fix type hinting * Correct type hint for parameter * Remove types-simplejson package * Revert pipfiles * Revert pipfile.lock --- app/utilities/decimal_places.py | 15 ++++--- app/utilities/json.py | 8 ++-- app/utilities/metadata_parser.py | 27 ++++++------ app/utilities/metadata_parser_v2.py | 32 ++++++++------- app/utilities/metadata_validators.py | 16 +++++--- app/utilities/request_session.py | 4 +- app/utilities/schema.py | 8 ++-- app/utilities/supplementary_data_parser.py | 20 +++++---- mypy.ini | 5 +++ .../app/utilities/test_metadata_validators.py | 41 +++++++++++++++++++ 10 files changed, 122 insertions(+), 54 deletions(-) create mode 100644 tests/app/utilities/test_metadata_validators.py diff --git a/app/utilities/decimal_places.py b/app/utilities/decimal_places.py index a9f9eeec0b..e9fee55907 100644 --- a/app/utilities/decimal_places.py +++ b/app/utilities/decimal_places.py @@ -3,7 +3,7 @@ import flask_babel from babel import Locale, numbers, units -from babel.numbers import get_currency_precision +from babel.numbers import NumberPattern, get_currency_precision UnitLengthType: TypeAlias = Literal["short", "long", "narrow"] @@ -27,7 +27,7 @@ def get_formatted_currency( *, value: float | Decimal, currency: str = "GBP", - locale: str | None = None, + locale: str | Locale | None = None, decimal_limit: int | None = None, ) -> str: """ @@ -80,7 +80,7 @@ def custom_format_unit( measurement_unit: str, locale: Locale | str, length: UnitLengthType = "short", -): +) -> str: """ This function provides a wrapper for the numbers `format_unit` method, generating the number format (including the desired number of decimals), based on the value entered by the user and @@ -92,14 +92,17 @@ def custom_format_unit( value=value, measurement_unit=measurement_unit, length=length, - format=number_format, + # Type ignore: babel function has incorrect type hinting, NumberPattern is valid here + format=number_format, # type: ignore locale=locale, ) return formatted_unit -def get_number_format(value: int | float | Decimal, locale: Locale | str) -> str: +def get_number_format( + value: int | float | Decimal, locale: Locale | str +) -> NumberPattern: """ Generates the number format based on the value entered by the user and the locale @@ -111,7 +114,7 @@ def get_number_format(value: int | float | Decimal, locale: Locale | str) -> str """ decimal_places = _get_decimal_places(value) locale = Locale.parse(locale) - locale_decimal_format = locale.decimal_formats[None] + locale_decimal_format: NumberPattern = locale.decimal_formats[None] locale_decimal_format.frac_prec = (decimal_places, decimal_places) return locale_decimal_format diff --git a/app/utilities/json.py b/app/utilities/json.py index 045067b476..6ca7c4a347 100644 --- a/app/utilities/json.py +++ b/app/utilities/json.py @@ -1,13 +1,15 @@ +from typing import IO, Any + import simplejson as json -def json_load(file, **kwargs): +def json_load(file: IO[str], **kwargs: Any) -> Any: return json.load(file, use_decimal=True, **kwargs) -def json_loads(data, **kwargs): +def json_loads(data: str, **kwargs: Any) -> Any: return json.loads(data, use_decimal=True, **kwargs) -def json_dumps(data, **kwargs) -> str: +def json_dumps(data: Any, **kwargs: Any) -> str: return json.dumps(data, for_json=True, use_decimal=True, **kwargs) diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index f0d2681aa1..0a5b96e66a 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -1,6 +1,6 @@ import functools from datetime import datetime, timezone -from typing import Mapping +from typing import Any, Mapping, MutableMapping from marshmallow import ( EXCLUDE, @@ -34,9 +34,9 @@ class StripWhitespaceMixin: @pre_load() - def strip_whitespace( - self, items, **kwargs - ): # pylint: disable=no-self-use, unused-argument + def strip_whitespace( # pylint: disable=no-self-use, unused-argument + self, items: MutableMapping, **kwargs: Any + ) -> MutableMapping: for key, value in items.items(): if isinstance(value, str): items[key] = value.strip() @@ -87,8 +87,9 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): eq_id = VALIDATORS["string"](required=False) # type:ignore @validates_schema - def validate_schema_name(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def validate_schema_name( # pylint: disable=no-self-use, unused-argument + self, data: Mapping, **kwargs: Any + ) -> None: """Function to validate the business schema parameters""" if not data.get("schema_name"): business_schema_claims = ( @@ -101,8 +102,9 @@ def validate_schema_name(self, data, **kwargs): ) @post_load - def update_schema_name(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def update_schema_name( # pylint: disable=no-self-use, unused-argument + self, data: MutableMapping, **kwargs: Any + ) -> MutableMapping: """Function to transform parameters into a business schema""" if data.get("schema_name"): logger.info( @@ -115,9 +117,9 @@ def update_schema_name(self, data, **kwargs): return data @post_load - def update_response_id( - self, data, **kwargs - ): # pylint: disable=no-self-use, unused-argument + def update_response_id( # pylint: disable=no-self-use, unused-argument + self, data: MutableMapping, **kwargs: Any + ) -> MutableMapping: """ If response_id is present : return as it is If response_id is not present : Build response_id from ru_ref,collection_exercise_sid,eq_id and form_type @@ -147,4 +149,5 @@ def update_response_id( def validate_runner_claims(claims: Mapping) -> dict: """Validate claims required for runner to function""" runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) - return runner_metadata_schema.load(claims) + # Type ignore: the load method in the Marshmallow parent schema class doesn't have type hints for return + return runner_metadata_schema.load(claims) # type: ignore diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index eabcb68ae2..90fc55e734 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -1,6 +1,6 @@ import functools from datetime import datetime, timezone -from typing import Callable, Iterable, Mapping +from typing import Any, Callable, Iterable, Mapping, MutableMapping from marshmallow import ( EXCLUDE, @@ -34,9 +34,9 @@ class StripWhitespaceMixin: @pre_load() - def strip_whitespace( - self, items, **kwargs - ): # pylint: disable=no-self-use, unused-argument + def strip_whitespace( # pylint: disable=no-self-use, unused-argument + self, items: MutableMapping, **kwargs: Any + ) -> MutableMapping: for key, value in items.items(): if isinstance(value, str): items[key] = value.strip() @@ -52,8 +52,9 @@ class SurveyMetadata(Schema, StripWhitespaceMixin): receipting_keys = fields.List(fields.String) @validates_schema - def validate_receipting_keys(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument + self, data: Mapping, **kwargs: Any + ) -> None: if data and (receipting_keys := data.get("receipting_keys", {})): missing_receipting_keys = [ receipting_key @@ -101,8 +102,9 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): survey_metadata = fields.Nested(SurveyMetadata, required=False) @validates_schema - def validate_schema_options(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def validate_schema_options( # pylint: disable=no-self-use, unused-argument + self, data: Mapping, **kwargs: Any + ) -> None: if data: options = [ option @@ -122,14 +124,14 @@ def validate_schema_options(self, data, **kwargs): def validate_questionnaire_claims( claims: Mapping, questionnaire_specific_metadata: Iterable[Mapping], - unknown=EXCLUDE, + unknown: str = EXCLUDE, ) -> dict: """Validate any survey specific claims required for a questionnaire""" - dynamic_fields = {} + dynamic_fields: dict[str, fields.String | DateString] = {} for metadata_field in questionnaire_specific_metadata: - field_arguments = {} - validators = [] + field_arguments: dict[str, bool] = {} + validators: list[validate.Validator] = [] if metadata_field.get("optional"): field_arguments["required"] = False @@ -155,10 +157,12 @@ def validate_questionnaire_claims( )(unknown=unknown) # The load method performs validation. - return questionnaire_metadata_schema.load(claims) + # Type ignore: the load method in the Marshmallow parent schema class doesn't have type hints for return + return questionnaire_metadata_schema.load(claims) # type: ignore def validate_runner_claims_v2(claims: Mapping) -> dict: """Validate claims required for runner to function""" runner_metadata_schema = RunnerMetadataSchema(unknown=EXCLUDE) - return runner_metadata_schema.load(claims) + # Type ignore: the load method in the Marshmallow parent schema class doesn't have type hints for return + return runner_metadata_schema.load(claims) # type: ignore diff --git a/app/utilities/metadata_validators.py b/app/utilities/metadata_validators.py index 4fe55ca3cb..d4d10c70d0 100644 --- a/app/utilities/metadata_validators.py +++ b/app/utilities/metadata_validators.py @@ -1,3 +1,5 @@ +from typing import Any + from marshmallow import fields, validate @@ -6,7 +8,7 @@ class RegionCode(validate.Regexp): Currently, this does not validate the subdivision, but only checks length """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__("^GB-[A-Z]{3}$", *args, **kwargs) @@ -16,7 +18,7 @@ class UUIDString(fields.UUID): This custom field deserializes UUIDs to strings. """ - def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ + def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore # pylint: disable=arguments-differ return str(super()._deserialize(*args, **kwargs)) @@ -26,10 +28,12 @@ class DateString(fields.DateTime): This custom field deserializes Dates to strings. """ - def _deserialize(self, *args, **kwargs): # pylint: disable=arguments-differ - date = super()._deserialize(*args, **kwargs) + DEFAULT_FORMAT = "iso8601" - if self.format == "iso8601": + def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore # pylint: disable=arguments-differ + date = super()._deserialize(*args, **kwargs) + date_format = self.format or self.DEFAULT_FORMAT + if date_format == "iso8601": return date.isoformat() - return date.strftime(self.format) + return date.strftime(date_format) diff --git a/app/utilities/request_session.py b/app/utilities/request_session.py index 4835d06a7a..48d8cb5457 100644 --- a/app/utilities/request_session.py +++ b/app/utilities/request_session.py @@ -1,10 +1,12 @@ +from typing import Sequence + import requests from requests.adapters import HTTPAdapter from urllib3 import Retry def get_retryable_session( - max_retries, retry_status_codes, backoff_factor + max_retries: int, retry_status_codes: Sequence[int], backoff_factor: float ) -> requests.Session: session = requests.Session() diff --git a/app/utilities/schema.py b/app/utilities/schema.py index eaad4a51e7..8e38c53b8d 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -71,7 +71,7 @@ def get_schema_path(language_code: str, schema_name: str) -> str | None: def get_schema_path_map( include_test_schemas: bool = False, ) -> dict[str, dict[str, dict[str, str]]]: - schemas = {} + schemas: dict[str, dict[str, dict[str, str]]] = {} for survey_type in os.listdir(SCHEMA_DIR): if not include_test_schemas and survey_type == "test": continue @@ -99,7 +99,7 @@ def _schema_exists(language_code: str, schema_name: str) -> bool: ) -def get_allowed_languages(schema_name: str | None, launch_language: str): +def get_allowed_languages(schema_name: str | None, launch_language: str) -> list[str]: if schema_name: for language_combination in LANGUAGES_MAP.get(schema_name, []): if launch_language in language_combination: @@ -151,7 +151,7 @@ def _load_schema_from_name(schema_name: str, language_code: str) -> Questionnair return QuestionnaireSchema(schema_json, language_code) -def get_schema_name_from_params(eq_id, form_type) -> str: +def get_schema_name_from_params(eq_id: str | None, form_type: str | None) -> str: return f"{eq_id}_{form_type}" @@ -243,7 +243,7 @@ def load_schema_from_url(url: str, *, language_code: str | None) -> Questionnair raise SchemaRequestFailed -def cache_questionnaire_schemas(): +def cache_questionnaire_schemas() -> None: for schemas_by_language in get_schema_path_map().values(): for language_code, schemas in schemas_by_language.items(): for schema in schemas: diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index f95306ec44..67dd8387c9 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -1,4 +1,4 @@ -from typing import Mapping +from typing import Any, Mapping from marshmallow import ( INCLUDE, @@ -18,8 +18,9 @@ class ItemsSchema(Schema): identifier = fields.Field(required=True) @validates("identifier") - def validate_identifier(self, identifier): - # pylint: disable=no-self-use + def validate_identifier( # pylint: disable=no-self-use + self, identifier: fields.Field + ) -> None: if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): @@ -40,8 +41,9 @@ class SupplementaryData(Schema, StripWhitespaceMixin): items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @validates_schema() - def validate_identifier(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def validate_identifier( # pylint: disable=no-self-use, unused-argument + self, data: Mapping, **kwargs: Any + ) -> None: if data and data["identifier"] != self.context["identifier"]: raise ValidationError( "Supplementary data did not return the specified Identifier" @@ -59,8 +61,9 @@ class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): ) @validates_schema() - def validate_dataset_and_survey_id(self, data, **kwargs): - # pylint: disable=no-self-use, unused-argument + def validate_dataset_and_survey_id( # pylint: disable=no-self-use, unused-argument + self, data: Mapping, **kwargs: Any + ) -> None: if data: if data["dataset_id"] != self.context["dataset_id"]: raise ValidationError( @@ -97,4 +100,5 @@ def validate_supplementary_data_v1( items = [ItemsSchema(unknown=INCLUDE).load(value) for value in values] validated_supplementary_data["data"]["items"][key] = items - return validated_supplementary_data + # Type ignore: the load method in the Marshmallow parent schema class doesn't have type hints for return + return validated_supplementary_data # type: ignore diff --git a/mypy.ini b/mypy.ini index b86fec5d3b..55719acdde 100644 --- a/mypy.ini +++ b/mypy.ini @@ -100,3 +100,8 @@ no_implicit_optional = True disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True + +[mypy-app.utilities.*] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True diff --git a/tests/app/utilities/test_metadata_validators.py b/tests/app/utilities/test_metadata_validators.py new file mode 100644 index 0000000000..ceafe03f51 --- /dev/null +++ b/tests/app/utilities/test_metadata_validators.py @@ -0,0 +1,41 @@ +import pytest + +from app.utilities.metadata_validators import DateString + + +def test_deserialize_defaults_to_iso_format(): + date_string = DateString() + assert ( + date_string._deserialize( # pylint: disable=protected-access + value="2014-12-22T03:12:58.019077+00:00", attr="", data="" + ) + == "2014-12-22T03:12:58.019077+00:00" + ) + + +def test_deserialize_does_not_default_to_iso_format(): + date_string = DateString(format="%Y-%m-%d") + assert ( + date_string._deserialize( # pylint: disable=protected-access + value="2014-12-22", attr="", data="" + ) + == "2014-12-22" + ) + + +def test_deserialize_incorrect_format_errors_with_default_format(): + date_string = DateString() + with pytest.raises(Exception) as e: + date_string._deserialize( # pylint: disable=protected-access + value="2014-12-22", attr="", data="" + ) + assert "Not a valid datetime." in str(e.value) + + +def test_deserialize_given_format_errors_with_wrong_format(): + date_string = DateString("%d-%m-%Y") + with pytest.raises(Exception) as e: + date_string._deserialize( # pylint: disable=protected-access + value="2023-10-22T05:16:58.019477+00:00", attr="", data="" + ) + assert "Not a valid datetime." in str(e.value) From 64cc7b91588c68e17d9ef0276c9ab51a4dae9f07 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:39:16 +0000 Subject: [PATCH 339/567] Update mypy and gevent to latest versions (#1269) --- Pipfile | 4 +- Pipfile.lock | 713 ++++++++++--------- app/questionnaire/questionnaire_schema.py | 15 +- app/submitter/converter.py | 2 +- app/submitter/converter_v2.py | 2 +- app/views/handlers/confirmation_email.py | 3 +- app/views/handlers/feedback.py | 3 +- app/views/handlers/list_collector_content.py | 3 +- 8 files changed, 382 insertions(+), 363 deletions(-) diff --git a/Pipfile b/Pipfile index 911d87b829..0d23dbb97d 100644 --- a/Pipfile +++ b/Pipfile @@ -24,7 +24,7 @@ moto = "*" freezegun = "*" pytest-xdist = "*" fakeredis = "*" -mypy = "==1.5.1" +mypy = "*" black = "*" pytest-flask = "*" pytest = "*" @@ -45,7 +45,7 @@ flask = "*" flask-babel = "*" flask-login = "*" flask-wtf = "*" -gevent = { version = "==23.9.0", platform_python_implementation = "=='CPython'" } +gevent = { version = "*", platform_python_implementation = "=='CPython'" } google-cloud-datastore = "*" grpcio = "*" gunicorn = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 41062d7aa3..02ad875c5a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a95c972a7f46999f235a45301b06d933f74b856dcf39858ee8251dc35ddae14a" + "sha256": "fc45f58bb88b36d9a6136dce2a43825ddb10145475d5110804526ebe2eb4dc05" }, "pipfile-spec": 6, "requires": { @@ -35,20 +35,20 @@ }, "boto3": { "hashes": [ - "sha256:1d10691911c4b8b9443d3060257ba32b68b6e3cad0eebbb9f69fd1c52a78417f", - "sha256:489c4967805b677b7a4030460e4c06c0903d6bc0f6834453611bf87efbd8d8a3" + "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", + "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.83" + "version": "==1.33.1" }, "botocore": { "hashes": [ - "sha256:40914b0fb28f13d709e1f8a4481e278350b77a3987be81acd23715ec8d5fedca", - "sha256:c742069e8bfd06d212d712228258ff09fb481b6ec02358e539381ce0fcad065a" + "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", + "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" ], "markers": "python_version >= '3.7'", - "version": "==1.31.83" + "version": "==1.33.1" }, "brotli": { "hashes": [ @@ -150,11 +150,11 @@ }, "certifi": { "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" ], "markers": "python_version >= '3.6'", - "version": "==2023.7.22" + "version": "==2023.11.17" }, "cffi": { "hashes": [ @@ -338,32 +338,32 @@ }, "cryptography": { "hashes": [ - "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", - "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84", - "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", - "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", - "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", - "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", - "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", - "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", - "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", - "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81", - "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", - "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", - "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d", - "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", - "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", - "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", - "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", - "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936", - "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", - "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", - "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", - "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", - "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.5" + "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", + "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", + "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", + "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", + "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", + "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", + "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", + "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", + "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", + "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", + "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", + "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", + "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", + "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", + "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", + "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", + "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", + "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", + "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", + "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", + "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", + "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", + "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.7" }, "deprecated": { "hashes": [ @@ -444,38 +444,49 @@ }, "gevent": { "hashes": [ - "sha256:03d7452ec45c71ef84cacbf199dac582af2948fb37c638aa6e7232fe5c2840f1", - "sha256:06a927940f9348b504888f93c8d85c2d8e2277ffd2105734148c0082ade17c20", - "sha256:117904bdb139adeb3378a0631e81fc5939b54edce16ddf21288bde111ab31d47", - "sha256:1578b5c6da4e88f5cade5682b13635d17231ade6c549d8498cc95776954675c4", - "sha256:169784ef1594ddac0a773e85e5f996f6f8512abdbe248bff16ed8a22fafecadb", - "sha256:17abeca1a8b61a0ce135127f8328265a317efaa3eda047199d65129ca68f0268", - "sha256:1b9ff212cddf245d44651600970c5370c4168f710d4856d8b2350d734dfff1a6", - "sha256:1ecd70495749ba1ece8e312e41e98d3371f5aa1e98922b47b3635c8263157938", - "sha256:259438ec75b31b199d99941294e50f705d92d20d59578244a882a57c2f0f52b3", - "sha256:2894d82111ed27b7f80dcbf8c713bb9c9c2c7e8890606cf548f6320fb2ae328f", - "sha256:291c380fd708c5e04d63fcb8427da21600fda202ffe14ba59a454efd1715f8d5", - "sha256:2bb65683df8a0a1427d42985158cee7dad8a3211247d5824302e276d9fd5bf65", - "sha256:3778609919477ae6eccbf3b10308015f5620c428dbf0dbc6cbeb8a135c9d603a", - "sha256:41199f10877f368822058b47aca3276e5a4c453e2c4df814a44f23c1bf059488", - "sha256:436d0e9c1185b15f0204f7e2a687af2fc5307b1fed8ce011b86732ad53edc136", - "sha256:5afcf6402981f33c84567e6a0a8dcf05e52e8c0b03d80b699644c5f251221e02", - "sha256:70bd1e7a6f571837beb190e3a330f289d10ba4d84fa980ffe778b97fb887455c", - "sha256:863a244311e986e924baa1516d821c37fcdb4e091e42f86f9d17d13323893c6d", - "sha256:91e1e96d50dc4a296dce77b9e28c29a7401fbaca6809989b2029266db6172967", - "sha256:951d95f1066fb84f661f2bb2c1d6423855a63922abf3e5bd1001711cfb1d8f38", - "sha256:96a3ce33e77277995436656e6f7712f4dcbe9dafa527f245ebbe3de44fd3d4bb", - "sha256:9b6eecefb35ceaa33693b971d18e98bfc7e63c9488f1b83eddedb8087a567066", - "sha256:b6179e5419044f8e49ab1fd2165391ffd94071362e03a04d2e499a64b619e105", - "sha256:bb645e9f68cb4b009d667507195e07314c594b2dac962ae797bd05bccd4076eb", - "sha256:d55cf22d6b29593d256bad9fade978fbdc998d6f3bf3aa3399a81feaa91b5a86", - "sha256:d76871f16df200c9454d3f9695b6cd97e7b80102fdf3e4b69b1e2900f9bd0eee", - "sha256:e9e3f81cc36eb623e8716515bd7e28f50ccca5fd930ba493275e60d5fbc69786", - "sha256:f44f1d3a05ce1d041d514b8151aec82e4649e5f8f6e2d837eb9bfc2607d7b64d", - "sha256:faad7ed513672083eabe1e45d1dbfbbcd7bde9c0da6ef3598e975128c8aee427" + "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", + "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", + "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", + "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", + "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", + "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", + "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", + "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", + "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", + "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", + "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", + "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", + "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", + "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", + "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", + "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", + "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", + "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", + "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", + "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", + "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", + "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", + "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", + "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", + "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", + "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", + "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", + "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", + "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", + "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", + "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", + "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", + "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", + "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", + "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", + "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", + "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", + "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", + "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", + "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" ], "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", - "version": "==23.9.0" + "version": "==23.9.1" }, "google-api-core": { "extras": [ @@ -693,7 +704,7 @@ "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" ], - "markers": "python_version < '3.12' and platform_python_implementation == 'CPython'", + "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", "version": "==3.0.1" }, "grpc-google-iam-v1": { @@ -706,72 +717,72 @@ }, "grpcio": { "hashes": [ - "sha256:023088764012411affe7db183d1ada3ad9daf2e23ddc719ff46d7061de661340", - "sha256:08d77e682f2bf730a4961eea330e56d2f423c6a9b91ca222e5b1eb24a357b19f", - "sha256:0a4a3833c0e067f3558538727235cd8a49709bff1003200bbdefa2f09334e4b1", - "sha256:0a754aff9e3af63bdc4c75c234b86b9d14e14a28a30c4e324aed1a9b873d755f", - "sha256:11168ef43e4a43ff1b1a65859f3e0ef1a173e277349e7fb16923ff108160a8cd", - "sha256:128e20f57c5f27cb0157e73756d1586b83c1b513ebecc83ea0ac37e4b0e4e758", - "sha256:1f9524d1d701e399462d2c90ba7c193e49d1711cf429c0d3d97c966856e03d00", - "sha256:1ff16d68bf453275466a9a46739061a63584d92f18a0f5b33d19fc97eb69867c", - "sha256:2067274c88bc6de89c278a672a652b4247d088811ece781a4858b09bdf8448e3", - "sha256:2171c39f355ba5b551c5d5928d65aa6c69807fae195b86ef4a7d125bcdb860a9", - "sha256:242adc47725b9a499ee77c6a2e36688fa6c96484611f33b1be4c57ab075a92dd", - "sha256:27f879ae604a7fcf371e59fba6f3ff4635a4c2a64768bd83ff0cac503142fef4", - "sha256:2b230028a008ae1d0f430acb227d323ff8a619017415cf334c38b457f814119f", - "sha256:3059668df17627f0e0fa680e9ef8c995c946c792612e9518f5cc1503be14e90b", - "sha256:31176aa88f36020055ace9adff2405a33c8bdbfa72a9c4980e25d91b2f196873", - "sha256:36f53c2b3449c015880e7d55a89c992c357f176327b0d2873cdaaf9628a37c69", - "sha256:3b4368b33908f683a363f376dfb747d40af3463a6e5044afee07cf9436addf96", - "sha256:3c61d641d4f409c5ae46bfdd89ea42ce5ea233dcf69e74ce9ba32b503c727e29", - "sha256:4abb717e320e74959517dc8e84a9f48fbe90e9abe19c248541e9418b1ce60acd", - "sha256:4c93f4abbb54321ee6471e04a00139c80c754eda51064187963ddf98f5cf36a4", - "sha256:535561990e075fa6bd4b16c4c3c1096b9581b7bb35d96fac4650f1181e428268", - "sha256:53c9aa5ddd6857c0a1cd0287225a2a25873a8e09727c2e95c4aebb1be83a766a", - "sha256:5d573e70a6fe77555fb6143c12d3a7d3fa306632a3034b4e7c59ca09721546f8", - "sha256:6009386a2df66159f64ac9f20425ae25229b29b9dd0e1d3dd60043f037e2ad7e", - "sha256:686e975a5d16602dc0982c7c703948d17184bd1397e16c8ee03511ecb8c4cdda", - "sha256:6959fb07e8351e20501ffb8cc4074c39a0b7ef123e1c850a7f8f3afdc3a3da01", - "sha256:6b25ed37c27e652db01be341af93fbcea03d296c024d8a0e680017a268eb85dd", - "sha256:6da6dea3a1bacf99b3c2187e296db9a83029ed9c38fd4c52b7c9b7326d13c828", - "sha256:72ca2399097c0b758198f2ff30f7178d680de8a5cfcf3d9b73a63cf87455532e", - "sha256:73abb8584b0cf74d37f5ef61c10722adc7275502ab71789a8fe3cb7ef04cf6e2", - "sha256:74100fecaec8a535e380cf5f2fb556ff84957d481c13e54051c52e5baac70541", - "sha256:75c6ecb70e809cf1504465174343113f51f24bc61e22a80ae1c859f3f7034c6d", - "sha256:7cf05053242f61ba94014dd3a986e11a083400a32664058f80bf4cf817c0b3a1", - "sha256:9411e24328a2302e279e70cae6e479f1fddde79629fcb14e03e6d94b3956eabf", - "sha256:a213acfbf186b9f35803b52e4ca9addb153fc0b67f82a48f961be7000ecf6721", - "sha256:bb7e0fe6ad73b7f06d7e2b689c19a71cf5cc48f0c2bf8608469e51ffe0bd2867", - "sha256:c2504eed520958a5b77cc99458297cb7906308cb92327f35fb7fbbad4e9b2188", - "sha256:c35aa9657f5d5116d23b934568e0956bd50c615127810fffe3ac356a914c176a", - "sha256:c5f09cffa619adfb44799fa4a81c2a1ad77c887187613fb0a8f201ab38d89ba1", - "sha256:c978f864b35f2261e0819f5cd88b9830b04dc51bcf055aac3c601e525a10d2ba", - "sha256:cbe946b3e6e60a7b4618f091e62a029cb082b109a9d6b53962dd305087c6e4fd", - "sha256:cc3e4cd087f07758b16bef8f31d88dbb1b5da5671d2f03685ab52dece3d7a16e", - "sha256:cf0dead5a2c5a3347af2cfec7131d4f2a2e03c934af28989c9078f8241a491fa", - "sha256:d2794f0e68b3085d99b4f6ff9c089f6fdd02b32b9d3efdfbb55beac1bf22d516", - "sha256:d2fa68a96a30dd240be80bbad838a0ac81a61770611ff7952b889485970c4c71", - "sha256:d6f70406695e3220f09cd7a2f879333279d91aa4a8a1d34303b56d61a8180137", - "sha256:d8f9cd4ad1be90b0cf350a2f04a38a36e44a026cac1e036ac593dc48efe91d52", - "sha256:da2d94c15f88cd40d7e67f7919d4f60110d2b9d5b1e08cf354c2be773ab13479", - "sha256:e1727c1c0e394096bb9af185c6923e8ea55a5095b8af44f06903bcc0e06800a2", - "sha256:e420ced29b5904cdf9ee5545e23f9406189d8acb6750916c2db4793dada065c6", - "sha256:e82c5cf1495244adf5252f925ac5932e5fd288b3e5ab6b70bec5593074b7236c", - "sha256:f1ef0d39bc1feb420caf549b3c657c871cad4ebbcf0580c4d03816b0590de0cf", - "sha256:f8753a6c88d1d0ba64302309eecf20f70d2770f65ca02d83c2452279085bfcd3", - "sha256:f93dbf58f03146164048be5426ffde298b237a5e059144847e4940f5b80172c3" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.59.2" + "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8", + "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea", + "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69", + "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb", + "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07", + "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d", + "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b", + "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354", + "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79", + "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59", + "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86", + "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7", + "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e", + "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281", + "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069", + "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb", + "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d", + "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3", + "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25", + "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c", + "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a", + "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80", + "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf", + "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118", + "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53", + "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e", + "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9", + "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556", + "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b", + "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e", + "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994", + "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc", + "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a", + "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d", + "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e", + "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718", + "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4", + "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab", + "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552", + "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3", + "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391", + "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13", + "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7", + "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3", + "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025", + "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8", + "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6", + "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473", + "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94", + "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0", + "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9", + "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d", + "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881", + "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.59.3" }, "grpcio-status": { "hashes": [ - "sha256:24bdf3b3b83b9112f43bd0626f82510d12cc1d919a45028ac20eb6919218e508", - "sha256:a2c2b146e66b73ba80d021ab34fce5db4dd9be67ca4566cda40d36b185ce54f4" + "sha256:2fd2eb39ca4e9afb3c874c0878ff75b258db0b7dcc25570fc521f16ae0ab942a", + "sha256:65c394ba43380d6bdf8c04c61efc493104b5535552aed35817a1b4dc66598a1f" ], "markers": "python_version >= '3.6'", - "version": "==1.59.2" + "version": "==1.59.3" }, "gunicorn": { "hashes": [ @@ -799,20 +810,20 @@ }, "humanize": { "hashes": [ - "sha256:8bc9e2bb9315e61ec06bf690151ae35aeb65651ab091266941edf97c90836404", - "sha256:9783373bf1eec713a770ecaa7c2d7a7902c98398009dfa3d8a2df91eec9311e8" + "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", + "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.8.0" + "version": "==4.9.0" }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" ], "markers": "python_version >= '3.5'", - "version": "==3.4" + "version": "==3.6" }, "itsdangerous": { "hashes": [ @@ -976,28 +987,28 @@ }, "protobuf": { "hashes": [ - "sha256:1a3ba712877e6d37013cdc3476040ea1e313a6c2e1580836a94f76b3c176d575", - "sha256:1a53d6f64b00eecf53b65ff4a8c23dc95df1fa1e97bb06b8122e5a64f49fc90a", - "sha256:32ac2100b0e23412413d948c03060184d34a7c50b3e5d7524ee96ac2b10acf51", - "sha256:5c1203ac9f50e4853b0a0bfffd32c67118ef552a33942982eeab543f5c634395", - "sha256:63714e79b761a37048c9701a37438aa29945cd2417a97076048232c1df07b701", - "sha256:683dc44c61f2620b32ce4927de2108f3ebe8ccf2fd716e1e684e5a50da154054", - "sha256:68f7caf0d4f012fd194a301420cf6aa258366144d814f358c5b32558228afa7c", - "sha256:b2cf8b5d381f9378afe84618288b239e75665fe58d0f3fd5db400959274296e9", - "sha256:c40ff8f00aa737938c5378d461637d15c442a12275a81019cc2fef06d81c9419", - "sha256:cf21faba64cd2c9a3ed92b7a67f226296b10159dbb8fbc5e854fc90657d908e4", - "sha256:d94a33db8b7ddbd0af7c467475fb9fde0c705fb315a8433c0e2020942b863a1f" + "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd", + "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb", + "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0", + "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7", + "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b", + "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2", + "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510", + "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6", + "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd", + "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10", + "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7" ], "markers": "python_version >= '3.8'", - "version": "==4.25.0" + "version": "==4.25.1" }, "pyasn1": { "hashes": [ - "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", - "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" + "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58", + "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.0" + "version": "==0.5.1" }, "pyasn1-modules": { "hashes": [ @@ -1168,11 +1179,11 @@ }, "s3transfer": { "hashes": [ - "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", - "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" + "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", + "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" ], "markers": "python_version >= '3.7'", - "version": "==0.7.0" + "version": "==0.8.0" }, "sdc-cryptography": { "hashes": [ @@ -1184,11 +1195,11 @@ }, "setuptools": { "hashes": [ - "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87", - "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a" + "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2", + "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6" ], "markers": "python_version >= '3.8'", - "version": "==68.2.2" + "version": "==69.0.2" }, "simplejson": { "hashes": [ @@ -1540,28 +1551,28 @@ }, "boto3": { "hashes": [ - "sha256:1d10691911c4b8b9443d3060257ba32b68b6e3cad0eebbb9f69fd1c52a78417f", - "sha256:489c4967805b677b7a4030460e4c06c0903d6bc0f6834453611bf87efbd8d8a3" + "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", + "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.28.83" + "version": "==1.33.1" }, "botocore": { "hashes": [ - "sha256:40914b0fb28f13d709e1f8a4481e278350b77a3987be81acd23715ec8d5fedca", - "sha256:c742069e8bfd06d212d712228258ff09fb481b6ec02358e539381ce0fcad065a" + "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", + "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" ], "markers": "python_version >= '3.7'", - "version": "==1.31.83" + "version": "==1.33.1" }, "certifi": { "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" ], "markers": "python_version >= '3.6'", - "version": "==2023.7.22" + "version": "==2023.11.17" }, "cffi": { "hashes": [ @@ -1788,32 +1799,32 @@ }, "cryptography": { "hashes": [ - "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf", - "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84", - "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", - "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", - "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", - "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", - "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", - "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", - "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", - "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81", - "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", - "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", - "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d", - "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", - "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", - "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", - "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", - "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936", - "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", - "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", - "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", - "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", - "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.5" + "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", + "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", + "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", + "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", + "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", + "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", + "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", + "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", + "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", + "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", + "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", + "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", + "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", + "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", + "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", + "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", + "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", + "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", + "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", + "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", + "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", + "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", + "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.7" }, "dill": { "hashes": [ @@ -1920,11 +1931,11 @@ }, "idna": { "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" ], "markers": "python_version >= '3.5'", - "version": "==3.4" + "version": "==3.6" }, "iniconfig": { "hashes": [ @@ -1969,20 +1980,20 @@ }, "jsonschema": { "hashes": [ - "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392", - "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc" + "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa", + "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.19.2" + "version": "==4.20.0" }, "jsonschema-specifications": { "hashes": [ - "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", - "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" + "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca", + "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779" ], "markers": "python_version >= '3.8'", - "version": "==2023.7.1" + "version": "==2023.11.1" }, "lazy-object-proxy": { "hashes": [ @@ -2112,46 +2123,46 @@ }, "moto": { "hashes": [ - "sha256:1298006aaa6996b886658eb194cac0e3a5679c9fcce6cb13e741ccc5a7247abb", - "sha256:3e0ef388900448485cd6eff18e9f7fcaa6cf4560b6fb536ba2e2e1278a5ecc59" + "sha256:5cf0736d1f43cb887498d00b00ae522774bfddb7db1f4994fedea65b290b9f0e", + "sha256:92595fe287474a31ac3ef847941ebb097e8ffb0c3d6c106e47cf573db06933b2" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==4.2.7" + "version": "==4.2.10" }, "mypy": { "hashes": [ - "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315", - "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0", - "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373", - "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a", - "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161", - "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275", - "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693", - "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb", - "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65", - "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4", - "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb", - "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243", - "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14", - "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4", - "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1", - "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a", - "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160", - "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25", - "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12", - "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d", - "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92", - "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770", - "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2", - "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70", - "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb", - "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5", - "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f" + "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", + "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", + "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82", + "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", + "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", + "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", + "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", + "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", + "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", + "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", + "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", + "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", + "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6", + "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", + "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", + "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7", + "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", + "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", + "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", + "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", + "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", + "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", + "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", + "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42", + "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", + "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", + "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.5.1" + "version": "==1.7.1" }, "mypy-extensions": { "hashes": [ @@ -2187,11 +2198,11 @@ }, "platformdirs": { "hashes": [ - "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", - "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" + "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", + "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731" ], "markers": "python_version >= '3.7'", - "version": "==3.11.0" + "version": "==4.0.0" }, "pluggy": { "hashes": [ @@ -2302,12 +2313,12 @@ }, "pytest-xdist": { "hashes": [ - "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93", - "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2" + "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a", + "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.3.1" + "version": "==3.5.0" }, "python-dateutil": { "hashes": [ @@ -2385,11 +2396,11 @@ }, "referencing": { "hashes": [ - "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", - "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" + "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882", + "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863" ], "markers": "python_version >= '3.8'", - "version": "==0.30.2" + "version": "==0.31.0" }, "requests": { "hashes": [ @@ -2402,125 +2413,125 @@ }, "responses": { "hashes": [ - "sha256:060be153c270c06fa4d22c1ef8865fdef43902eb595204deeef736cddb62d353", - "sha256:3df82f7d4dcd3e5f61498181aadb4381f291da25c7506c47fe8cb68ce29203e7" + "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9", + "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.24.0" + "version": "==0.24.1" }, "rpds-py": { "hashes": [ - "sha256:0525847f83f506aa1e28eb2057b696fe38217e12931c8b1b02198cfe6975e142", - "sha256:05942656cb2cb4989cd50ced52df16be94d344eae5097e8583966a1d27da73a5", - "sha256:0831d3ecdea22e4559cc1793f22e77067c9d8c451d55ae6a75bf1d116a8e7f42", - "sha256:0853da3d5e9bc6a07b2486054a410b7b03f34046c123c6561b535bb48cc509e1", - "sha256:08e6e7ff286254016b945e1ab632ee843e43d45e40683b66dd12b73791366dd1", - "sha256:0a38612d07a36138507d69646c470aedbfe2b75b43a4643f7bd8e51e52779624", - "sha256:0bedd91ae1dd142a4dc15970ed2c729ff6c73f33a40fa84ed0cdbf55de87c777", - "sha256:0c5441b7626c29dbd54a3f6f3713ec8e956b009f419ffdaaa3c80eaf98ddb523", - "sha256:0e9e976e0dbed4f51c56db10831c9623d0fd67aac02853fe5476262e5a22acb7", - "sha256:0fadfdda275c838cba5102c7f90a20f2abd7727bf8f4a2b654a5b617529c5c18", - "sha256:1096ca0bf2d3426cbe79d4ccc91dc5aaa73629b08ea2d8467375fad8447ce11a", - "sha256:171d9a159f1b2f42a42a64a985e4ba46fc7268c78299272ceba970743a67ee50", - "sha256:188912b22b6c8225f4c4ffa020a2baa6ad8fabb3c141a12dbe6edbb34e7f1425", - "sha256:1b4cf9ab9a0ae0cb122685209806d3f1dcb63b9fccdf1424fb42a129dc8c2faa", - "sha256:1e04581c6117ad9479b6cfae313e212fe0dfa226ac727755f0d539cd54792963", - "sha256:1fa73ed22c40a1bec98d7c93b5659cd35abcfa5a0a95ce876b91adbda170537c", - "sha256:2124f9e645a94ab7c853bc0a3644e0ca8ffbe5bb2d72db49aef8f9ec1c285733", - "sha256:240687b5be0f91fbde4936a329c9b7589d9259742766f74de575e1b2046575e4", - "sha256:25740fb56e8bd37692ed380e15ec734be44d7c71974d8993f452b4527814601e", - "sha256:27ccc93c7457ef890b0dd31564d2a05e1aca330623c942b7e818e9e7c2669ee4", - "sha256:281c8b219d4f4b3581b918b816764098d04964915b2f272d1476654143801aa2", - "sha256:2d34a5450a402b00d20aeb7632489ffa2556ca7b26f4a63c35f6fccae1977427", - "sha256:301bd744a1adaa2f6a5e06c98f1ac2b6f8dc31a5c23b838f862d65e32fca0d4b", - "sha256:30e5ce9f501fb1f970e4a59098028cf20676dee64fc496d55c33e04bbbee097d", - "sha256:33ab498f9ac30598b6406e2be1b45fd231195b83d948ebd4bd77f337cb6a2bff", - "sha256:35585a8cb5917161f42c2104567bb83a1d96194095fc54a543113ed5df9fa436", - "sha256:389c0e38358fdc4e38e9995e7291269a3aead7acfcf8942010ee7bc5baee091c", - "sha256:3acadbab8b59f63b87b518e09c4c64b142e7286b9ca7a208107d6f9f4c393c5c", - "sha256:3b7a64d43e2a1fa2dd46b678e00cabd9a49ebb123b339ce799204c44a593ae1c", - "sha256:3c8c0226c71bd0ce9892eaf6afa77ae8f43a3d9313124a03df0b389c01f832de", - "sha256:429349a510da82c85431f0f3e66212d83efe9fd2850f50f339341b6532c62fe4", - "sha256:466030a42724780794dea71eb32db83cc51214d66ab3fb3156edd88b9c8f0d78", - "sha256:47aeceb4363851d17f63069318ba5721ae695d9da55d599b4d6fb31508595278", - "sha256:48aa98987d54a46e13e6954880056c204700c65616af4395d1f0639eba11764b", - "sha256:4b2416ed743ec5debcf61e1242e012652a4348de14ecc7df3512da072b074440", - "sha256:4d0a675a7acbbc16179188d8c6d0afb8628604fc1241faf41007255957335a0b", - "sha256:4eb74d44776b0fb0782560ea84d986dffec8ddd94947f383eba2284b0f32e35e", - "sha256:4f8a1d990dc198a6c68ec3d9a637ba1ce489b38cbfb65440a27901afbc5df575", - "sha256:513ccbf7420c30e283c25c82d5a8f439d625a838d3ba69e79a110c260c46813f", - "sha256:5210a0018c7e09c75fa788648617ebba861ae242944111d3079034e14498223f", - "sha256:54cdfcda59251b9c2f87a05d038c2ae02121219a04d4a1e6fc345794295bdc07", - "sha256:56dd500411d03c5e9927a1eb55621e906837a83b02350a9dc401247d0353717c", - "sha256:57ec6baec231bb19bb5fd5fc7bae21231860a1605174b11585660236627e390e", - "sha256:5f1519b080d8ce0a814f17ad9fb49fb3a1d4d7ce5891f5c85fc38631ca3a8dc4", - "sha256:6174d6ad6b58a6bcf67afbbf1723420a53d06c4b89f4c50763d6fa0a6ac9afd2", - "sha256:68172622a5a57deb079a2c78511c40f91193548e8ab342c31e8cb0764d362459", - "sha256:6915fc9fa6b3ec3569566832e1bb03bd801c12cea030200e68663b9a87974e76", - "sha256:6b75b912a0baa033350367a8a07a8b2d44fd5b90c890bfbd063a8a5f945f644b", - "sha256:6f5dcb658d597410bb7c967c1d24eaf9377b0d621358cbe9d2ff804e5dd12e81", - "sha256:6f8d7fe73d1816eeb5378409adc658f9525ecbfaf9e1ede1e2d67a338b0c7348", - "sha256:7036316cc26b93e401cedd781a579be606dad174829e6ad9e9c5a0da6e036f80", - "sha256:7188ddc1a8887194f984fa4110d5a3d5b9b5cd35f6bafdff1b649049cbc0ce29", - "sha256:761531076df51309075133a6bc1db02d98ec7f66e22b064b1d513bc909f29743", - "sha256:7979d90ee2190d000129598c2b0c82f13053dba432b94e45e68253b09bb1f0f6", - "sha256:8015835494b21aa7abd3b43fdea0614ee35ef6b03db7ecba9beb58eadf01c24f", - "sha256:81c4d1a3a564775c44732b94135d06e33417e829ff25226c164664f4a1046213", - "sha256:81cf9d306c04df1b45971c13167dc3bad625808aa01281d55f3cf852dde0e206", - "sha256:88857060b690a57d2ea8569bca58758143c8faa4639fb17d745ce60ff84c867e", - "sha256:8c567c664fc2f44130a20edac73e0a867f8e012bf7370276f15c6adc3586c37c", - "sha256:91bd2b7cf0f4d252eec8b7046fa6a43cee17e8acdfc00eaa8b3dbf2f9a59d061", - "sha256:9620650c364c01ed5b497dcae7c3d4b948daeae6e1883ae185fef1c927b6b534", - "sha256:9b007c2444705a2dc4a525964fd4dd28c3320b19b3410da6517cab28716f27d3", - "sha256:9bf9acce44e967a5103fcd820fc7580c7b0ab8583eec4e2051aec560f7b31a63", - "sha256:a239303acb0315091d54c7ff36712dba24554993b9a93941cf301391d8a997ee", - "sha256:a2baa6be130e8a00b6cbb9f18a33611ec150b4537f8563bddadb54c1b74b8193", - "sha256:a54917b7e9cd3a67e429a630e237a90b096e0ba18897bfb99ee8bd1068a5fea0", - "sha256:a689e1ded7137552bea36305a7a16ad2b40be511740b80748d3140614993db98", - "sha256:a952ae3eb460c6712388ac2ec706d24b0e651b9396d90c9a9e0a69eb27737fdc", - "sha256:aa32205358a76bf578854bf31698a86dc8b2cb591fd1d79a833283f4a403f04b", - "sha256:b2287c09482949e0ca0c0eb68b2aca6cf57f8af8c6dfd29dcd3bc45f17b57978", - "sha256:b6b0e17d39d21698185097652c611f9cf30f7c56ccec189789920e3e7f1cee56", - "sha256:b710bf7e7ae61957d5c4026b486be593ed3ec3dca3e5be15e0f6d8cf5d0a4990", - "sha256:b8e11715178f3608874508f08e990d3771e0b8c66c73eb4e183038d600a9b274", - "sha256:b92aafcfab3d41580d54aca35a8057341f1cfc7c9af9e8bdfc652f83a20ced31", - "sha256:bec29b801b4adbf388314c0d050e851d53762ab424af22657021ce4b6eb41543", - "sha256:c694bee70ece3b232df4678448fdda245fd3b1bb4ba481fb6cd20e13bb784c46", - "sha256:c6b52b7028b547866c2413f614ee306c2d4eafdd444b1ff656bf3295bf1484aa", - "sha256:cb41ad20064e18a900dd427d7cf41cfaec83bcd1184001f3d91a1f76b3fcea4e", - "sha256:cd316dbcc74c76266ba94eb021b0cc090b97cca122f50bd7a845f587ff4bf03f", - "sha256:ced40cdbb6dd47a032725a038896cceae9ce267d340f59508b23537f05455431", - "sha256:d1c562a9bb72244fa767d1c1ab55ca1d92dd5f7c4d77878fee5483a22ffac808", - "sha256:d389ff1e95b6e46ebedccf7fd1fadd10559add595ac6a7c2ea730268325f832c", - "sha256:d56b1cd606ba4cedd64bb43479d56580e147c6ef3f5d1c5e64203a1adab784a2", - "sha256:d72a4315514e5a0b9837a086cb433b004eea630afb0cc129de76d77654a9606f", - "sha256:d9e7f29c00577aff6b318681e730a519b235af292732a149337f6aaa4d1c5e31", - "sha256:dbc25baa6abb205766fb8606f8263b02c3503a55957fcb4576a6bb0a59d37d10", - "sha256:e57919c32ee295a2fca458bb73e4b20b05c115627f96f95a10f9f5acbd61172d", - "sha256:e5bbe011a2cea9060fef1bb3d668a2fd8432b8888e6d92e74c9c794d3c101595", - "sha256:e6aea5c0eb5b0faf52c7b5c4a47c8bb64437173be97227c819ffa31801fa4e34", - "sha256:e888be685fa42d8b8a3d3911d5604d14db87538aa7d0b29b1a7ea80d354c732d", - "sha256:eebaf8c76c39604d52852366249ab807fe6f7a3ffb0dd5484b9944917244cdbe", - "sha256:efbe0b5e0fd078ed7b005faa0170da4f72666360f66f0bb2d7f73526ecfd99f9", - "sha256:efddca2d02254a52078c35cadad34762adbae3ff01c6b0c7787b59d038b63e0d", - "sha256:f05450fa1cd7c525c0b9d1a7916e595d3041ac0afbed2ff6926e5afb6a781b7f", - "sha256:f12d69d568f5647ec503b64932874dade5a20255736c89936bf690951a5e79f5", - "sha256:f45321224144c25a62052035ce96cbcf264667bcb0d81823b1bbc22c4addd194", - "sha256:f62581d7e884dd01ee1707b7c21148f61f2febb7de092ae2f108743fcbef5985", - "sha256:f8832a4f83d4782a8f5a7b831c47e8ffe164e43c2c148c8160ed9a6d630bc02a", - "sha256:fa35ad36440aaf1ac8332b4a4a433d4acd28f1613f0d480995f5cfd3580e90b7" + "sha256:0290712eb5603a725769b5d857f7cf15cf6ca93dda3128065bbafe6fdb709beb", + "sha256:032c242a595629aacace44128f9795110513ad27217b091e834edec2fb09e800", + "sha256:08832078767545c5ee12561ce980714e1e4c6619b5b1e9a10248de60cddfa1fd", + "sha256:08b335fb0c45f0a9e2478a9ece6a1bfb00b6f4c4780f9be3cf36479c5d8dd374", + "sha256:0b70c1f800059c92479dc94dda41288fd6607f741f9b1b8f89a21a86428f6383", + "sha256:0d9f8930092558fd15c9e07198625efb698f7cc00b3dc311c83eeec2540226a8", + "sha256:181ee352691c4434eb1c01802e9daa5edcc1007ff15023a320e2693fed6a661b", + "sha256:19f5aa7f5078d35ed8e344bcba40f35bc95f9176dddb33fc4f2084e04289fa63", + "sha256:1a3b2583c86bbfbf417304eeb13400ce7f8725376dc7d3efbf35dc5d7052ad48", + "sha256:1c9a1dc5e898ce30e2f9c0aa57181cddd4532b22b7780549441d6429d22d3b58", + "sha256:1f36a1e80ef4ed1996445698fd91e0d3e54738bf597c9995118b92da537d7a28", + "sha256:20147996376be452cd82cd6c17701daba69a849dc143270fa10fe067bb34562a", + "sha256:249c8e0055ca597707d71c5ad85fd2a1c8fdb99386a8c6c257e1b47b67a9bec1", + "sha256:2647192facf63be9ed2d7a49ceb07efe01dc6cfb083bd2cc53c418437400cb99", + "sha256:264f3a5906c62b9df3a00ad35f6da1987d321a053895bd85f9d5c708de5c0fbf", + "sha256:2abd669a39be69cdfe145927c7eb53a875b157740bf1e2d49e9619fc6f43362e", + "sha256:2b2415d5a7b7ee96aa3a54d4775c1fec140476a17ee12353806297e900eaeddc", + "sha256:2c173f529666bab8e3f948b74c6d91afa22ea147e6ebae49a48229d9020a47c4", + "sha256:2da81c1492291c1a90987d76a47c7b2d310661bf7c93a9de0511e27b796a8b46", + "sha256:2eca04a365be380ca1f8fa48b334462e19e3382c0bb7386444d8ca43aa01c481", + "sha256:37b08df45f02ff1866043b95096cbe91ac99de05936dd09d6611987a82a3306a", + "sha256:37f79f4f1f06cc96151f4a187528c3fd4a7e1065538a4af9eb68c642365957f7", + "sha256:3dd5fb7737224e1497c886fb3ca681c15d9c00c76171f53b3c3cc8d16ccfa7fb", + "sha256:3e3ac5b602fea378243f993d8b707189f9061e55ebb4e56cb9fdef8166060f28", + "sha256:3f55ae773abd96b1de25fc5c3fb356f491bd19116f8f854ba705beffc1ddc3c5", + "sha256:4011d5c854aa804c833331d38a2b6f6f2fe58a90c9f615afdb7aa7cf9d31f721", + "sha256:4145172ab59b6c27695db6d78d040795f635cba732cead19c78cede74800949a", + "sha256:42b9535aa22ab023704cfc6533e968f7e420affe802d85e956d8a7b4c0b0b5ea", + "sha256:46a07a258bda12270de02b34c4884f200f864bba3dcd6e3a37fef36a168b859d", + "sha256:4f13d3f6585bd07657a603780e99beda96a36c86acaba841f131e81393958336", + "sha256:528e2afaa56d815d2601b857644aeb395afe7e59212ab0659906dc29ae68d9a6", + "sha256:545e94c84575057d3d5c62634611858dac859702b1519b6ffc58eca7fb1adfcf", + "sha256:577d40a72550eac1386b77b43836151cb61ff6700adacda2ad4d883ca5a0b6f2", + "sha256:5967fa631d0ed9f8511dede08bc943a9727c949d05d1efac4ac82b2938024fb7", + "sha256:5b769396eb358d6b55dbf78f3f7ca631ca1b2fe02136faad5af74f0111b4b6b7", + "sha256:63c9e2794329ef070844ff9bfc012004aeddc0468dc26970953709723f76c8a5", + "sha256:6574f619e8734140d96c59bfa8a6a6e7a3336820ccd1bfd95ffa610673b650a2", + "sha256:6bfe72b249264cc1ff2f3629be240d7d2fdc778d9d298087cdec8524c91cd11f", + "sha256:736817dbbbd030a69a1faf5413a319976c9c8ba8cdcfa98c022d3b6b2e01eca6", + "sha256:74a2044b870df7c9360bb3ce7e12f9ddf8e72e49cd3a353a1528cbf166ad2383", + "sha256:74be3b215a5695690a0f1a9f68b1d1c93f8caad52e23242fcb8ba56aaf060281", + "sha256:76a8374b294e4ccb39ccaf11d39a0537ed107534139c00b4393ca3b542cc66e5", + "sha256:7ba239bb37663b2b4cd08e703e79e13321512dccd8e5f0e9451d9e53a6b8509a", + "sha256:7c40851b659d958c5245c1236e34f0d065cc53dca8d978b49a032c8e0adfda6e", + "sha256:7cf241dbb50ea71c2e628ab2a32b5bfcd36e199152fc44e5c1edb0b773f1583e", + "sha256:7cfae77da92a20f56cf89739a557b76e5c6edc094f6ad5c090b9e15fbbfcd1a4", + "sha256:7d152ec7bb431040af2500e01436c9aa0d993f243346f0594a15755016bf0be1", + "sha256:80080972e1d000ad0341c7cc58b6855c80bd887675f92871221451d13a975072", + "sha256:82dbcd6463e580bcfb7561cece35046aaabeac5a9ddb775020160b14e6c58a5d", + "sha256:8308a8d49d1354278d5c068c888a58d7158a419b2e4d87c7839ed3641498790c", + "sha256:839676475ac2ccd1532d36af3d10d290a2ca149b702ed464131e450a767550df", + "sha256:83feb0f682d75a09ddc11aa37ba5c07dd9b824b22915207f6176ea458474ff75", + "sha256:88956c993a20201744282362e3fd30962a9d86dc4f1dcf2bdb31fab27821b61f", + "sha256:8a6ad8429340e0a4de89353447c6441329def3632e7b2293a7d6e873217d3c2b", + "sha256:8ba9fbc5d6e36bfeb5292530321cc56c4ef3f98048647fabd8f57543c34174ec", + "sha256:8c1f6c8df23be165eb0cb78f305483d00c6827a191e3a38394c658d5b9c80bbd", + "sha256:91276caef95556faeb4b8f09fe4439670d3d6206fee78d47ddb6e6de837f0b4d", + "sha256:960e7e460fda2d0af18c75585bbe0c99f90b8f09963844618a621b804f8c3abe", + "sha256:9656a09653b18b80764647d585750df2dff8928e03a706763ab40ec8c4872acc", + "sha256:9cd935c0220d012a27c20135c140f9cdcbc6249d5954345c81bfb714071b985c", + "sha256:a2b3c79586636f1fa69a7bd59c87c15fca80c0d34b5c003d57f2f326e5276575", + "sha256:a4b9d3f5c48bbe8d9e3758e498b3c34863f2c9b1ac57a4e6310183740e59c980", + "sha256:a8c2bf286e5d755a075e5e97ba56b3de08cccdad6b323ab0b21cc98875176b03", + "sha256:a90031658805c63fe488f8e9e7a88b260ea121ba3ee9cdabcece9c9ddb50da39", + "sha256:ad666a904212aa9a6c77da7dce9d5170008cda76b7776e6731928b3f8a0d40fa", + "sha256:af2d1648eb625a460eee07d3e1ea3a4a6e84a1fb3a107f6a8e95ac19f7dcce67", + "sha256:b3d4b390ee70ca9263b331ccfaf9819ee20e90dfd0201a295e23eb64a005dbef", + "sha256:ba4432301ad7eeb1b00848cf46fae0e5fecfd18a8cb5fdcf856c67985f79ecc7", + "sha256:bc3179e0815827cf963e634095ae5715ee73a5af61defbc8d6ca79f1bdae1d1d", + "sha256:c5fd099acaee2325f01281a130a39da08d885e4dedf01b84bf156ec2737d78fe", + "sha256:c797ea56f36c6f248656f0223b11307fdf4a1886f3555eba371f34152b07677f", + "sha256:cd4ea56c9542ad0091dfdef3e8572ae7a746e1e91eb56c9e08b8d0808b40f1d1", + "sha256:cdd6f8738e1f1d9df5b1603bb03cb30e442710e5672262b95d0f9fcb4edb0dab", + "sha256:d0580faeb9def6d0beb7aa666294d5604e569c4e24111ada423cf9936768d95c", + "sha256:d11afdc5992bbd7af60ed5eb519873690d921425299f51d80aa3099ed49f2bcc", + "sha256:d1d388d2f5f5a6065cf83c54dd12112b7389095669ff395e632003ae8999c6b8", + "sha256:d20da6b4c7aa9ee75ad0730beaba15d65157f5beeaca54a038bb968f92bf3ce3", + "sha256:d22e0660de24bd8e9ac82f4230a22a5fe4e397265709289d61d5fb333839ba50", + "sha256:d22f2cb82e0b40e427a74a93c9a4231335bbc548aed79955dde0b64ea7f88146", + "sha256:d4fa1eeb9bea6d9b64ac91ec51ee94cc4fc744955df5be393e1c923c920db2b0", + "sha256:d9793d46d3e6522ae58e9321032827c9c0df1e56cbe5d3de965facb311aed6aa", + "sha256:dab979662da1c9fbb464e310c0b06cb5f1d174d09a462553af78f0bfb3e01920", + "sha256:db8d0f0ad92f74feb61c4e4a71f1d573ef37c22ef4dc19cab93e501bfdad8cbd", + "sha256:df2af1180b8eeececf4f819d22cc0668bfadadfd038b19a90bd2fb2ee419ec6f", + "sha256:dfb5d2ab183c0efe5e7b8917e4eaa2e837aacafad8a69b89aa6bc81550eed857", + "sha256:e04f8c76b8d5c70695b4e8f1d0b391d8ef91df00ef488c6c1ffb910176459bc6", + "sha256:e4a45ba34f904062c63049a760790c6a2fa7a4cc4bd160d8af243b12371aaa05", + "sha256:e9be1f7c5f9673616f875299339984da9447a40e3aea927750c843d6e5e2e029", + "sha256:edc91c50e17f5cd945d821f0f1af830522dba0c10267c3aab186dc3dbaab8def", + "sha256:ee70ee5f4144a45a9e6169000b5b525d82673d5dab9f7587eccc92794814e7ac", + "sha256:f1059ca9a51c936c9a8d46fbc2c9a6b4c15ab3f13a97f1ad32f024b39666ba85", + "sha256:f47eef55297799956464efc00c74ae55c48a7b68236856d56183fe1ddf866205", + "sha256:f4ae6f423cb7d1c6256b7482025ace2825728f53b7ac58bcd574de6ee9d242c2", + "sha256:f4b15a163448ec79241fb2f1bc5a8ae1a4a304f7a48d948d208a2935b26bf8a5", + "sha256:f55601fb58f92e4f4f1d05d80c24cb77505dc42103ddfd63ddfdc51d3da46fa2", + "sha256:fa84bbe22ffa108f91631935c28a623001e335d66e393438258501e618fb0dde", + "sha256:faa12a9f34671a30ea6bb027f04ec4e1fb8fa3fb3ed030893e729d4d0f3a9791", + "sha256:fcfd5f91b882eedf8d9601bd21261d6ce0e61a8c66a7152d1f5df08d3f643ab1", + "sha256:fe30ef31172bdcf946502a945faad110e8fff88c32c4bec9a593df0280e64d8a" ], "markers": "python_version >= '3.8'", - "version": "==0.12.0" + "version": "==0.13.1" }, "s3transfer": { "hashes": [ - "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", - "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" + "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", + "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" ], "markers": "python_version >= '3.7'", - "version": "==0.7.0" + "version": "==0.8.0" }, "six": { "hashes": [ @@ -2555,11 +2566,11 @@ }, "tomlkit": { "hashes": [ - "sha256:df32fab589a81f0d7dc525a4267b6d7a64ee99619cbd1eeb0fae32c1dd426977", - "sha256:eeea7ac7563faeab0a1ed8fe12c2e5a51c61f933f2502f7e9db0241a65163ad0" + "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4", + "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba" ], "markers": "python_version >= '3.7'", - "version": "==0.12.2" + "version": "==0.12.3" }, "types-cachetools": { "hashes": [ @@ -2603,12 +2614,12 @@ }, "types-redis": { "hashes": [ - "sha256:00f003da884ec3d1d54633186b4cbd587b39782595c5603330cc46a51f9bcf6e", - "sha256:aa7fb5f743534500f274ddf11ab1c910aae1020481865a36b799e1d67de2aaf3" + "sha256:94fc61118601fb4f79206b33b9f4344acff7ca1d7bba67834987fb0efcf6a770", + "sha256:c8cfc84635183deca2db4a528966c5566445fd3713983f0034fb0f5a09e0890d" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==4.6.0.10" + "version": "==4.6.0.11" }, "types-requests": { "hashes": [ diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 9b9318ac94..b78fe7131f 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -625,10 +625,12 @@ def _get_flattened_questions(self) -> list[ImmutableDict]: ] def get_section_ids_required_for_hub(self) -> tuple[str, ...]: - return self.flow_options.get("required_completed_sections", tuple()) + # Type ignore: the type of the .get() returned value is Any + return self.flow_options.get("required_completed_sections", tuple()) # type: ignore def get_summary_options(self) -> ImmutableDict[str, bool]: - return self.flow_options.get("summary", ImmutableDict({})) + # Type ignore: the type of the .get() returned value is Any + return self.flow_options.get("summary", ImmutableDict({})) # type: ignore def get_sections(self) -> Iterable[ImmutableDict]: return self._sections_by_id.values() @@ -701,7 +703,8 @@ def get_title_for_section(self, section_id: str) -> str | None: def get_show_on_hub_for_section(self, section_id: str) -> bool | None: if section := self.get_section(section_id): - return section.get("show_on_hub", True) + # Type ignore: the type of the .get() returned value is Any + return section.get("show_on_hub", True) # type: ignore def get_summary_for_section(self, section_id: str) -> ImmutableDict | None: if section := self.get_section(section_id): @@ -713,7 +716,8 @@ def get_summary_title_for_section(self, section_id: str) -> str | None: def show_summary_on_completion_for_section(self, section_id: str) -> bool | None: if summary := self.get_summary_for_section(section_id): - return summary.get("show_on_completion", False) + # Type ignore: the type of the .get() returned value is Any + return summary.get("show_on_completion", False) # type: ignore def get_repeat_for_section(self, section_id: str) -> ImmutableDict | None: if section := self.get_section(section_id): @@ -871,7 +875,8 @@ def get_repeating_blocks_for_list_collector( self, list_collector_id: str ) -> list[ImmutableDict] | None: if list_collector := self.get_block(list_collector_id): - return list_collector.get("repeating_blocks", []) + # Type ignore: the type of the .get() returned value is Any + return list_collector.get("repeating_blocks", []) # type: ignore def get_answer_ids_for_list_items(self, list_collector_id: str) -> list[str]: """ diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 30a76343a6..98bef71b6c 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -128,7 +128,7 @@ def build_metadata(metadata: MetadataProxy) -> MetadataType: def get_optional_payload_properties( metadata: MetadataProxy, response_metadata: MutableMapping -) -> MetadataType: +) -> dict: payload = {} for key in ["channel", "case_type", "form_type", "region_code", "case_ref"]: diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index 7687583170..f34aaf4731 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -98,7 +98,7 @@ def convert_answers_v2( def get_optional_payload_properties( metadata: MetadataProxy, response_metadata: MutableMapping -) -> MetadataType: +) -> dict: payload = {} for key in ["channel", "region_code"]: diff --git a/app/views/handlers/confirmation_email.py b/app/views/handlers/confirmation_email.py index c2fb4713e9..e80545b4cc 100644 --- a/app/views/handlers/confirmation_email.py +++ b/app/views/handlers/confirmation_email.py @@ -75,5 +75,6 @@ def is_limit_reached(session_data: SessionData) -> bool: @staticmethod def is_enabled(schema: QuestionnaireSchema) -> bool: if submission_schema := schema.get_post_submission(): - return submission_schema.get("confirmation_email", False) + # Type ignore: the type of the .get() returned value is Any + return submission_schema.get("confirmation_email", False) # type: ignore return False diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 6d579a4de6..7cfe01f83e 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -195,7 +195,8 @@ def is_limit_reached(session_data: SessionData) -> bool: @staticmethod def is_enabled(schema: QuestionnaireSchema) -> bool: if submission_schema := schema.get_post_submission(): - return submission_schema.get("feedback", False) + # Type ignore: the type of the .get() returned value is Any + return submission_schema.get("feedback", False) # type: ignore return False diff --git a/app/views/handlers/list_collector_content.py b/app/views/handlers/list_collector_content.py index a69c8fe322..731a53753d 100644 --- a/app/views/handlers/list_collector_content.py +++ b/app/views/handlers/list_collector_content.py @@ -4,7 +4,8 @@ class ListCollectorContent(ListCollector): def _get_additional_view_context(self) -> dict: - return self.rendered_block.get("content", {}) + # Type ignore: the type of the .get() returned value is Any + return self.rendered_block.get("content", {}) # type: ignore def handle_post(self) -> None: if self._is_list_collector_complete(): From cba0f1c2be16eb0e80d30179b58e0a6bdbeb25f4 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:00:34 +0000 Subject: [PATCH 340/567] Remove unnecessary disabling of linting errors (#1264) --- .pylintrc | 2 +- app/authentication/authenticator.py | 2 +- app/data_models/metadata_proxy.py | 1 - app/forms/address_form.py | 2 -- app/forms/duration_form.py | 1 - app/forms/email_form.py | 2 -- app/forms/fields/date_field.py | 2 -- app/forms/fields/month_year_date_field.py | 2 -- app/forms/fields/year_date_field.py | 2 -- app/forms/questionnaire_form.py | 6 +--- app/globals.py | 4 +-- app/helpers/language_helper.py | 1 - app/jinja_filters.py | 16 ++++----- app/publisher/publisher.py | 3 +- app/questionnaire/questionnaire_schema.py | 9 ++--- .../questionnaire_store_updater.py | 2 +- app/routes/flush.py | 2 +- app/routes/individual_response.py | 1 - app/routes/questionnaire.py | 7 ---- app/routes/session.py | 1 - app/setup.py | 11 +++--- app/submitter/converter_v2.py | 1 - app/utilities/metadata_parser_v2.py | 36 ++++++++----------- app/utilities/metadata_validators.py | 4 +-- app/utilities/supplementary_data_parser.py | 4 +-- .../summary/list_collector_content_block.py | 1 - app/views/handlers/confirm_email.py | 2 -- app/views/handlers/confirmation_email.py | 2 -- app/views/handlers/content.py | 3 +- app/views/handlers/feedback.py | 4 --- app/views/handlers/individual_response.py | 27 +++++--------- app/views/handlers/list_add_question.py | 2 -- app/views/handlers/list_collector.py | 2 -- app/views/handlers/list_edit_question.py | 2 -- app/views/handlers/pdf_response.py | 2 +- .../handlers/primary_person_list_collector.py | 2 -- app/views/handlers/primary_person_question.py | 2 -- app/views/handlers/question.py | 6 ---- .../relationships/relationship_collector.py | 2 -- .../relationships/unrelated_question.py | 2 -- mypy.ini | 1 + tests/app/data_model/test_answer_store.py | 1 - .../app/data_model/test_relationship_store.py | 2 +- .../field_handlers/test_address_handler.py | 4 --- .../field_handlers/test_number_handler.py | 3 -- tests/app/forms/test_questionnaire_form.py | 4 +-- tests/app/parser/conftest.py | 1 - tests/app/storage/conftest.py | 2 +- tests/app/submitter/conftest.py | 1 - tests/app/submitter/test_submitter.py | 4 +-- tests/app/utilities/test_schema.py | 2 +- .../views/contexts/summary/test_question.py | 1 - tests/functional/generate_pages.py | 2 -- ...st_questionnaire_list_collector_content.py | 2 -- ...ress_value_source_in_repeating_sections.py | 1 - .../routes/test_confirmation_email.py | 2 +- tests/integration/test_broken_submission.py | 4 +-- tests/integration/test_flush_data.py | 8 ++--- 58 files changed, 65 insertions(+), 165 deletions(-) diff --git a/.pylintrc b/.pylintrc index a79574a5cb..043074ca1e 100644 --- a/.pylintrc +++ b/.pylintrc @@ -414,7 +414,7 @@ disable= # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member +enable=c-extension-no-member,useless-suppression [METHOD_ARGS] diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 289a26a3fd..0659f9f86a 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -37,7 +37,7 @@ def user_loader(user_id: str) -> Optional[str]: @login_manager.request_loader def request_load_user( - request: Request, # pylint: disable=unused-argument + request: Request, ) -> Optional[User]: logger.debug("load user") diff --git a/app/data_models/metadata_proxy.py b/app/data_models/metadata_proxy.py index 7dfce0b8d4..6e9ec28d3a 100644 --- a/app/data_models/metadata_proxy.py +++ b/app/data_models/metadata_proxy.py @@ -42,7 +42,6 @@ def __getitem__(self, key: str) -> Any: return self.data.get(key) -# pylint: disable=too-many-locals @dataclass(frozen=True) class MetadataProxy: tx_id: str diff --git a/app/forms/address_form.py b/app/forms/address_form.py index 15d8dea278..ffc344713f 100644 --- a/app/forms/address_form.py +++ b/app/forms/address_form.py @@ -24,8 +24,6 @@ class AddressForm(Form): @cached_property def data(self) -> dict[str, Any]: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation data_: dict[str, Any] = super().data return data_ diff --git a/app/forms/duration_form.py b/app/forms/duration_form.py index 62dd671373..eda527ac70 100644 --- a/app/forms/duration_form.py +++ b/app/forms/duration_form.py @@ -10,7 +10,6 @@ # pylint: disable=no-member -# wtforms Form parents are not discoverable in the 2.3.3 implementation class DurationForm(Form): def validate( self, extra_validators: Optional[dict[str, list[Callable]]] = None diff --git a/app/forms/email_form.py b/app/forms/email_form.py index fbe898e4d5..080565ac71 100644 --- a/app/forms/email_form.py +++ b/app/forms/email_form.py @@ -7,8 +7,6 @@ from app.forms.validators import EmailTLDCheck -# pylint: disable=no-member -# wtforms Form parents are not discoverable in the 2.3.3 implementation class EmailForm(FlaskForm): email = StringField( validators=[ diff --git a/app/forms/fields/date_field.py b/app/forms/fields/date_field.py index cf26d9344a..2b9affdca7 100644 --- a/app/forms/fields/date_field.py +++ b/app/forms/fields/date_field.py @@ -21,8 +21,6 @@ class DateForm(Form): @cached_property def data(self) -> str | None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data try: diff --git a/app/forms/fields/month_year_date_field.py b/app/forms/fields/month_year_date_field.py index 99373b9810..75192c45b7 100644 --- a/app/forms/fields/month_year_date_field.py +++ b/app/forms/fields/month_year_date_field.py @@ -18,8 +18,6 @@ class YearMonthDateForm(Form): @cached_property def data(self) -> str | None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data try: diff --git a/app/forms/fields/year_date_field.py b/app/forms/fields/year_date_field.py index b229bef315..c9e29607de 100644 --- a/app/forms/fields/year_date_field.py +++ b/app/forms/fields/year_date_field.py @@ -17,8 +17,6 @@ class YearDateForm(Form): @cached_property def data(self) -> str | None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation data = super().data try: diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 373f02e002..0252952ef9 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -192,7 +192,7 @@ def _get_target_total_and_currency( if "value" in calculation: if isinstance(calculation["value"], dict): - target_total = self.value_source_resolver.resolve(calculation["value"]) # type: ignore + target_total = self.value_source_resolver.resolve(calculation["value"]) else: target_total = calculation["value"] else: @@ -411,8 +411,6 @@ def _get_calculation_total( return result def answers_all_valid(self, answer_id_list: Sequence[str]) -> bool: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation return not set(answer_id_list) & set(self.errors) def map_errors(self) -> list[tuple[str, str]]: @@ -425,8 +423,6 @@ def map_errors(self) -> list[tuple[str, str]]: self.question_errors[self.question["id"]], ) ] - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation for answer in self.question["answers"]: if answer["id"] in self.errors: ordered_errors += map_subfield_errors(self.errors, answer["id"]) diff --git a/app/globals.py b/app/globals.py index 98ab3006a5..1a297fde95 100644 --- a/app/globals.py +++ b/app/globals.py @@ -25,7 +25,6 @@ def get_questionnaire_store(user_id: str, user_ik: str) -> QuestionnaireStore: "EQ_SERVER_SIDE_STORAGE_ENCRYPTION_USER_PEPPER" ) storage = EncryptedQuestionnaireStorage(user_id, user_ik, pepper) - # pylint: disable=assigning-non-slot store = g._questionnaire_store = QuestionnaireStore(storage) return store @@ -43,7 +42,6 @@ def get_session_store() -> SessionStore | None: pepper = secret_store.get_secret_by_name( "EQ_SERVER_SIDE_STORAGE_ENCRYPTION_USER_PEPPER" ) - # pylint: disable=assigning-non-slot store = g._session_store = SessionStore( cookie_session[USER_IK], pepper, cookie_session[EQ_SESSION_ID] ) @@ -82,7 +80,7 @@ def create_session_store( seconds=session_timeout_in_seconds ) - # pylint: disable=protected-access, assigning-non-slot + # pylint: disable=protected-access g._session_store = ( SessionStore(user_ik, pepper) .create(eq_session_id, user_id, session_data, expires_at) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index e35e0756d7..87f9afc70f 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -26,7 +26,6 @@ def handle_language(metadata: MetadataProxy | None = None) -> None: language_code = metadata.language_code if metadata else None launch_language = language_code or DEFAULT_LANGUAGE_CODE - # pylint: disable=assigning-non-slot g.allowed_languages = get_allowed_languages(schema_name, launch_language) request_language = request.args.get("language_code") if request_language and request_language in g.allowed_languages: diff --git a/app/jinja_filters.py b/app/jinja_filters.py index e50eea9b43..8f7920e512 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -149,7 +149,7 @@ def get_format_date(value: Markup) -> str: return f"{date}" -@pass_eval_context # type: ignore +@pass_eval_context @blueprint.app_template_filter() def format_datetime( context: nodes.EvalContext, date_time: datetime @@ -197,13 +197,13 @@ def get_currency_symbol_processor() -> dict[str, Callable]: return {"get_currency_symbol": get_currency_symbol} -@blueprint.app_template_filter() # type: ignore +@blueprint.app_template_filter() def setAttribute(dictionary: dict[str, str], key: str, value: str) -> dict[str, str]: dictionary[key] = value return dictionary -@blueprint.app_template_filter() # type: ignore +@blueprint.app_template_filter() def setAttributes( dictionary: dict[str, str], attributes: dict[str, str] ) -> dict[str, str]: @@ -371,15 +371,13 @@ def __init__( ] else: self.otherType = "input" - self.value = escape( - detail_answer_field._value() - ) # pylint: disable=protected-access + self.value = escape(detail_answer_field._value()) if answer_type == "Number": self.width = get_width_for_number(detail_answer_schema) -@blueprint.app_template_filter() # type: ignore +@blueprint.app_template_filter() def map_select_config(form: FormType, answer: AnswerType) -> list[SelectConfig]: options = form["fields"][answer["id"]] @@ -394,7 +392,7 @@ def map_select_config_processor() -> dict[str, Callable]: return {"map_select_config": map_select_config} -@blueprint.app_template_filter() # type: ignore +@blueprint.app_template_filter() def map_relationships_config( form: Mapping[str, str], answer: Mapping[str, Union[int, slice]] ) -> list[RelationshipRadioConfig]: @@ -643,7 +641,7 @@ def map_summary_item_config_processor() -> dict[str, Callable]: # pylint: disable=too-many-locals -@blueprint.app_template_filter() # type: ignore +@blueprint.app_template_filter() def map_list_collector_config( list_items: list[dict[str, str | int]], editable: bool = True, diff --git a/app/publisher/publisher.py b/app/publisher/publisher.py index 6b909d0c47..141857fcad 100644 --- a/app/publisher/publisher.py +++ b/app/publisher/publisher.py @@ -24,7 +24,6 @@ def __init__(self): def _publish(self, topic_id, message) -> "publisher.futures.Future": logger.info("publishing message", topic_id=topic_id) - # pylint: disable=no-member topic_path = self._client.topic_path(self._project_id, topic_id) response: Future = self._client.publish(topic_path, message) return response @@ -40,7 +39,7 @@ def publish(self, topic_id, message: bytes, fulfilment_request_transaction_id: s message_id=message_id, fulfilment_request_transaction_id=fulfilment_request_transaction_id, ) - except Exception as exc: # pylint:disable=broad-except + except Exception as exc: logger.exception( "message publication failed", topic_id=topic_id, diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index b78fe7131f..2506bf8b84 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -702,8 +702,8 @@ def get_title_for_section(self, section_id: str) -> str | None: return section.get("title") def get_show_on_hub_for_section(self, section_id: str) -> bool | None: + # Type ignore: the type of the .get() returned value is Any if section := self.get_section(section_id): - # Type ignore: the type of the .get() returned value is Any return section.get("show_on_hub", True) # type: ignore def get_summary_for_section(self, section_id: str) -> ImmutableDict | None: @@ -1248,9 +1248,7 @@ def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_f # Type ignore: Added as this will be a set rather than a dict at this point dependencies_ids_for_progress_value_source["sections"][ identifier - ] = OrderedSet( - [current_section_id] - ) # type: ignore + ] = OrderedSet([current_section_id]) elif selector == "block" and ( section_id := self.get_section_id_for_block_id(identifier) ): @@ -1279,9 +1277,8 @@ def _get_section_dependencies_for_dependent_answers( section_dependencies: set[str] = set() # Type Ignore: Added to this method as the block will exist at this point for answer_id in dependent_answer_ids: - block = self.get_block_for_answer_id(answer_id) # type: ignore + block = self.get_block_for_answer_id(answer_id) section_id = self.get_section_id_for_block_id(block["id"]) # type: ignore - if section_id != current_section_id: self._when_rules_section_dependencies_by_answer[answer_id].add( current_section_id diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index a1bfa9f006..ab2ffd243f 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -221,7 +221,7 @@ def update_same_name_items( else: people_names[current_list_item_name] = current_list_item_id - list_model.same_name_items = list(same_name_items) # type: ignore + list_model.same_name_items = list(same_name_items) def _remove_relationship_answers_for_list_item_id( self, list_item_id: str, answers: list diff --git a/app/routes/flush.py b/app/routes/flush.py index c31ffa63be..e959d57340 100644 --- a/app/routes/flush.py +++ b/app/routes/flush.py @@ -76,7 +76,7 @@ def _submit_data(user: User) -> bool: metadata: MetadataProxy = questionnaire_store.data_stores.metadata # type: ignore submitted_at = datetime.now(timezone.utc) schema = load_schema_from_metadata( - metadata=metadata, language_code=metadata.language_code # type: ignore + metadata=metadata, language_code=metadata.language_code ) router = Router( diff --git a/app/routes/individual_response.py b/app/routes/individual_response.py index e3690bd5e8..991acd7893 100644 --- a/app/routes/individual_response.py +++ b/app/routes/individual_response.py @@ -69,7 +69,6 @@ def before_individual_response_request() -> Response | None: # Ensures langauge is set in the SessionStore handle_language(metadata) - # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( metadata=metadata, language_code=get_locale().language, diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 068c883a8f..dbee87a6e2 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -103,7 +103,6 @@ def before_questionnaire_request() -> Response | None: handle_language(metadata) - # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( metadata=metadata, language_code=get_locale().language ) @@ -128,7 +127,6 @@ def before_post_submission_request() -> None: handle_language(questionnaire_store.data_stores.metadata) - # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( metadata=metadata, language_code=get_locale().language ) @@ -281,7 +279,6 @@ def get_section( return redirect(section_handler.get_next_location_url()) -# pylint: disable=too-many-return-statements @questionnaire_blueprint.route("/", methods=["GET", "POST"]) @questionnaire_blueprint.route("//", methods=["GET", "POST"]) @questionnaire_blueprint.route( @@ -412,8 +409,6 @@ def get_thank_you( ) ) - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation logger.info( "email validation error", error_message=str(confirmation_email.form.errors["email"][0]), @@ -537,8 +532,6 @@ def send_confirmation_email( ) ) - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation logger.info( "email validation error", error_message=str(confirmation_email.form.errors["email"][0]), diff --git a/app/routes/session.py b/app/routes/session.py index 80b0887e9e..afa25de574 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -99,7 +99,6 @@ def login() -> Response: metadata = MetadataProxy.from_dict(runner_claims) - # pylint: disable=assigning-non-slot g.schema = load_schema_from_metadata( metadata=metadata, language_code=metadata.language_code ) diff --git a/app/setup.py b/app/setup.py index 7b2be50872..6bda0591aa 100644 --- a/app/setup.py +++ b/app/setup.py @@ -141,7 +141,7 @@ def create_app( # noqa: C901 pylint: disable=too-complex, too-many-statements # before_request hooks. Otherwise any logging by the plugin in their before # request will use the logger context of the previous request. @application.before_request - def before_request(): # pylint: disable=unused-variable + def before_request(): contextvars.clear_contextvars() request_id = str(uuid4()) @@ -206,7 +206,7 @@ def before_request(): # pylint: disable=unused-variable setup_jinja_env(application) @application.after_request - def apply_caching(response): # pylint: disable=unused-variable + def apply_caching(response): if "text/html" in response.content_type: for k, v in CACHE_HEADERS.items(): response.headers[k] = v @@ -216,7 +216,7 @@ def apply_caching(response): # pylint: disable=unused-variable return response @application.after_request - def response_minify(response): # pylint: disable=unused-variable + def response_minify(response): """ minify html response to decrease site traffic """ @@ -237,7 +237,7 @@ def response_minify(response): # pylint: disable=unused-variable return response @application.after_request - def after_request(response): # pylint: disable=unused-variable + def after_request(response): # We're using the stringified version of the Flask session to get a rough # length for the cookie. The real length won't be known yet as Flask # serializes and adds the cookie header after this method is called. @@ -260,7 +260,6 @@ def setup_jinja_env(application): # Switch off flask default autoescaping as schema content can contain html application.jinja_env.autoescape = False - # pylint: disable=no-member application.jinja_env.add_extension("jinja2.ext.do") @@ -489,7 +488,7 @@ def setup_compression(application): def add_safe_health_check(application): @application.route("/status") - def safe_health_check(): # pylint: disable=unused-variable + def safe_health_check(): data = {"status": "OK", "version": application.config["EQ_APPLICATION_VERSION"]} return json_dumps(data) diff --git a/app/submitter/converter_v2.py b/app/submitter/converter_v2.py index f34aaf4731..90905e09b0 100644 --- a/app/submitter/converter_v2.py +++ b/app/submitter/converter_v2.py @@ -110,7 +110,6 @@ def get_optional_payload_properties( return payload -# pylint: disable=too-many-locals def get_payload_data( data_stores: DataStores, schema: QuestionnaireSchema, diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 90fc55e734..081e3c9db5 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -71,32 +71,26 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument class RunnerMetadataSchema(Schema, StripWhitespaceMixin): """Metadata which is required for the operation of runner itself""" - jti = VALIDATORS["uuid"]() # type:ignore - tx_id = VALIDATORS["uuid"]() # type:ignore - case_id = VALIDATORS["uuid"]() # type:ignore - collection_exercise_sid = VALIDATORS["string"]( - validate=validate.Length(min=1) - ) # type:ignore + jti = VALIDATORS["uuid"]() + tx_id = VALIDATORS["uuid"]() + case_id = VALIDATORS["uuid"]() + collection_exercise_sid = VALIDATORS["string"](validate=validate.Length(min=1)) version = VALIDATORS["string"]( required=True, validate=validate.OneOf([AuthPayloadVersion.V2.value]) - ) # type:ignore - schema_name = VALIDATORS["string"](required=False) # type:ignore - schema_url = VALIDATORS["url"](required=False) # type:ignore - cir_instrument_id = VALIDATORS["uuid"](required=False) # type:ignore - response_id = VALIDATORS["string"](required=True) # type:ignore - account_service_url = VALIDATORS["url"](required=True) # type:ignore - - language_code = VALIDATORS["string"](required=False) # type:ignore - channel = VALIDATORS["string"]( - required=False, validate=validate.Length(min=1) - ) # type:ignore + ) + schema_name = VALIDATORS["string"](required=False) + schema_url = VALIDATORS["url"](required=False) + cir_instrument_id = VALIDATORS["uuid"](required=False) + response_id = VALIDATORS["string"](required=True) + account_service_url = VALIDATORS["url"](required=True) + + language_code = VALIDATORS["string"](required=False) + channel = VALIDATORS["string"](required=False, validate=validate.Length(min=1)) response_expires_at = VALIDATORS["iso_8601_date_string"]( required=True, validate=lambda x: parse_iso_8601_datetime(x) > datetime.now(tz=timezone.utc), - ) # type:ignore - region_code = VALIDATORS["string"]( - required=False, validate=RegionCode() - ) # type:ignore + ) + region_code = VALIDATORS["string"](required=False, validate=RegionCode()) roles = fields.List(fields.String(), required=False) survey_metadata = fields.Nested(SurveyMetadata, required=False) diff --git a/app/utilities/metadata_validators.py b/app/utilities/metadata_validators.py index d4d10c70d0..0704fd1ce9 100644 --- a/app/utilities/metadata_validators.py +++ b/app/utilities/metadata_validators.py @@ -18,7 +18,7 @@ class UUIDString(fields.UUID): This custom field deserializes UUIDs to strings. """ - def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore # pylint: disable=arguments-differ + def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore return str(super()._deserialize(*args, **kwargs)) @@ -30,7 +30,7 @@ class DateString(fields.DateTime): DEFAULT_FORMAT = "iso8601" - def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore # pylint: disable=arguments-differ + def _deserialize(self, *args: Any, **kwargs: Any) -> str: # type: ignore date = super()._deserialize(*args, **kwargs) date_format = self.format or self.DEFAULT_FORMAT if date_format == "iso8601": diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 67dd8387c9..766cdd4faa 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -41,7 +41,7 @@ class SupplementaryData(Schema, StripWhitespaceMixin): items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @validates_schema() - def validate_identifier( # pylint: disable=no-self-use, unused-argument + def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data and data["identifier"] != self.context["identifier"]: @@ -61,7 +61,7 @@ class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): ) @validates_schema() - def validate_dataset_and_survey_id( # pylint: disable=no-self-use, unused-argument + def validate_dataset_and_survey_id( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data: diff --git a/app/views/contexts/summary/list_collector_content_block.py b/app/views/contexts/summary/list_collector_content_block.py index 0d4bad2110..03e7ea8974 100644 --- a/app/views/contexts/summary/list_collector_content_block.py +++ b/app/views/contexts/summary/list_collector_content_block.py @@ -4,7 +4,6 @@ class ListCollectorContentBlock(ListCollectorBaseBlock): - # pylint: disable=too-many-locals def list_summary_element(self, summary: Mapping[str, Any]) -> dict[str, Any]: related_answers = None diff --git a/app/views/handlers/confirm_email.py b/app/views/handlers/confirm_email.py index 42a29bb953..45d2c31457 100644 --- a/app/views/handlers/confirm_email.py +++ b/app/views/handlers/confirm_email.py @@ -124,8 +124,6 @@ def get_next_location_url(self) -> str: return url_for(".send_confirmation_email", email=self._serialized_email) def get_page_title(self) -> str: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors: formatted_errors: str = gettext("Error: {page_title}").format( page_title=self.page_title diff --git a/app/views/handlers/confirmation_email.py b/app/views/handlers/confirmation_email.py index e80545b4cc..f6213074a8 100644 --- a/app/views/handlers/confirmation_email.py +++ b/app/views/handlers/confirmation_email.py @@ -61,8 +61,6 @@ def get_url_safe_serialized_email(self) -> str | bytes: return url_safe_serializer().dumps(self.form.email.data) def get_page_title(self) -> str | LazyString | None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors: return gettext("Error: {page_title}").format(page_title=self.page_title) return self.page_title diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index 55cb3c6c01..317eb92c6f 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -29,9 +29,8 @@ def rendered_block(self) -> dict: def get_context(self) -> dict: return { "block": self.rendered_block, - # Type ignore: if block is first in individual response, these two items won't be none "individual_response_url": individual_response_url( - self._schema.get_individual_response_list(), # type: ignore + self._schema.get_individual_response_list(), self._current_location.list_item_id, # type: ignore self._questionnaire_store, ) diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 7cfe01f83e..cc21fc7319 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -74,8 +74,6 @@ def get_context(self) -> Mapping[str, Union[str, bool, dict]]: return build_feedback_context(self.question_schema, self.form) def get_page_title(self) -> str: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors: title: str = gettext("Error: {page_title}").format( page_title=self.PAGE_TITLE @@ -94,8 +92,6 @@ def handle_post(self) -> None: case_id = metadata.case_id tx_id = metadata.tx_id - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation feedback_converter = ( FeedbackPayloadV2 if metadata.version is AuthPayloadVersion.V2 diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index eacb33d2fd..a49bdcfb25 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -174,8 +174,7 @@ def rendered_block(self) -> dict: @cached_property def placeholder_renderer(self) -> PlaceholderRenderer: return PlaceholderRenderer( - # Type ignore: Language is defaulted via handle_language in the individual_response blueprint before_request which triggers this - language=self._language, # type: ignore + language=self._language, data_stores=self._questionnaire_store.data_stores, schema=self._schema, location=None, @@ -261,8 +260,7 @@ def handle_get(self) -> str: ) def _get_next_location_url(self) -> str: - # Type ignore: Current usages of this method occur when List Name exists and is not None - list_model = self._questionnaire_store.data_stores.list_store[self._list_name] # type: ignore + list_model = self._questionnaire_store.data_stores.list_store[self._list_name] if self._list_item_id: return url_for( @@ -286,14 +284,12 @@ def _get_previous_location_url(self) -> str: "questionnaire.block", list_name=self._list_name, list_item_id=self._list_item_id, - # Type ignore: Current usages of this method occur when List Name exists and is not None - block_id=self._schema.get_remove_block_id_for_list(self._list_name), # type: ignore + block_id=self._schema.get_remove_block_id_for_list(self._list_name), ) if self._list_item_id: individual_section_first_block_id = ( - # Type ignore: Current usages of this method occur when Individual Section ID exists and is not None - self._schema.get_first_block_id_for_section(self.individual_section_id) # type: ignore + self._schema.get_first_block_id_for_section(self.individual_section_id) ) return url_for( "questionnaire.block", @@ -334,8 +330,7 @@ def block_definition(self) -> Mapping[str, Any]: "How would you like {person_name} to receive a separate census?" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - # Type ignore: List name will exist and be not None at this point - self._list_name # type: ignore + self._list_name ), }, "description": self._build_question_description(), @@ -461,8 +456,7 @@ def block_definition(self) -> Mapping[str, Any]: "How would you like to answer {person_name_possessive} questions?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( - # Type ignore: List name will exist and be not None at this point - self._list_name # type: ignore + self._list_name ), }, "answers": [ @@ -493,8 +487,7 @@ def block_definition(self) -> Mapping[str, Any]: "I will answer for {person_name}" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - # Type ignore: List name will exist and be not None at this point - self._list_name # type: ignore + self._list_name ), }, "value": "I will answer for {person_name}", @@ -610,8 +603,7 @@ def block_definition(self) -> Mapping: "Do you want to send an individual access code for {person_name} by post?" ), "placeholders": IndividualResponseHandler._person_name_placeholder( - # Type ignore: List name will exist and be not None at this point - self._list_name # type: ignore + self._list_name ), }, "description": [ @@ -809,8 +801,7 @@ def block_definition(self) -> Mapping: "What is {person_name_possessive} mobile number?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( - # Type ignore: List name will exist and be not None at this point - self._list_name # type: ignore + self._list_name ), }, "answers": [ diff --git a/app/views/handlers/list_add_question.py b/app/views/handlers/list_add_question.py index 8ab905660c..599203d6c9 100644 --- a/app/views/handlers/list_add_question.py +++ b/app/views/handlers/list_add_question.py @@ -49,8 +49,6 @@ def handle_post(self) -> None: self.questionnaire_store_updater.remove_answers(answer_ids_to_remove) self.questionnaire_store_updater.remove_completed_location(self.parent_location) - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers( self.form.data, self._list_item_id ) diff --git a/app/views/handlers/list_collector.py b/app/views/handlers/list_collector.py index 2dd729aada..8ba455a74d 100644 --- a/app/views/handlers/list_collector.py +++ b/app/views/handlers/list_collector.py @@ -77,8 +77,6 @@ def handle_post(self) -> None: if answer_action and answer_action["type"] == "RedirectToListAddBlock": self._is_adding = True - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) self.questionnaire_store_updater.save() elif self._is_list_collector_complete(): diff --git a/app/views/handlers/list_edit_question.py b/app/views/handlers/list_edit_question.py index 670c180545..7aa6786e27 100644 --- a/app/views/handlers/list_edit_question.py +++ b/app/views/handlers/list_edit_question.py @@ -49,8 +49,6 @@ def get_next_location_url(self) -> str: return super().get_next_location_url() def handle_post(self) -> None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) return super().handle_post() diff --git a/app/views/handlers/pdf_response.py b/app/views/handlers/pdf_response.py index c1e10ff834..779edc5b33 100644 --- a/app/views/handlers/pdf_response.py +++ b/app/views/handlers/pdf_response.py @@ -56,7 +56,7 @@ def filename(self) -> str: if self._questionnaire_store.submitted_at: formatted_date = self._questionnaire_store.submitted_at.date().isoformat() else: - formatted_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") # type: ignore + formatted_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d") return f"{formatted_title}-{formatted_date}.pdf" def _get_pdf(self, rendered_html: str) -> io.BytesIO: diff --git a/app/views/handlers/primary_person_list_collector.py b/app/views/handlers/primary_person_list_collector.py index ebb10e563b..c67c890b83 100644 --- a/app/views/handlers/primary_person_list_collector.py +++ b/app/views/handlers/primary_person_list_collector.py @@ -29,8 +29,6 @@ def handle_post(self) -> None: if answer_action and answer_action["type"] == "RedirectToListAddBlock": self._is_adding = True - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) self._primary_person_id = ( self.questionnaire_store_updater.add_primary_person(list_name) diff --git a/app/views/handlers/primary_person_question.py b/app/views/handlers/primary_person_question.py index 751e1d78f7..f1af5ec9f9 100644 --- a/app/views/handlers/primary_person_question.py +++ b/app/views/handlers/primary_person_question.py @@ -45,8 +45,6 @@ def get_next_location_url(self) -> str: def handle_post(self) -> None: same_name_answer_ids = self.parent_block.get("same_name_answer_ids") - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) self.questionnaire_store_updater.update_same_name_items( self.parent_block["for_list"], same_name_answer_ids diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 9952dcb058..259bf8d4ed 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -149,8 +149,6 @@ def _get_answer_action(self) -> dict | None: answers = self.rendered_block["question"]["answers"] for answer in answers: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation submitted_answer = self.form.data[answer["id"]] for option in answer.get("options", {}): @@ -171,8 +169,6 @@ def get_context(self) -> dict[str, dict]: if "list_summary" in self.rendered_block: context.update(self.get_list_summary_context()) - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation if self.form.errors or self.form.question_errors: self.page_title = gettext("Error: {page_title}").format( page_title=self.page_title @@ -201,8 +197,6 @@ def get_list_summary_context(self) -> dict: ) def handle_post(self) -> None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers(self.form.data) if self.questionnaire_store_updater.is_dirty(): # We prematurely complete the block, as we need it completed to build the routing path diff --git a/app/views/handlers/relationships/relationship_collector.py b/app/views/handlers/relationships/relationship_collector.py index 89b93c18bc..c5f09cabad 100644 --- a/app/views/handlers/relationships/relationship_collector.py +++ b/app/views/handlers/relationships/relationship_collector.py @@ -15,8 +15,6 @@ def is_location_valid(self) -> bool: return super().is_location_valid() def handle_post(self) -> None: - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation relationship_answer = self.form.data.get(self.relationships_answer_id) relationship = Relationship( # Type ignore: handle_post is only called from relationships endpoint and location class is assigned to RelationshipLocation diff --git a/app/views/handlers/relationships/unrelated_question.py b/app/views/handlers/relationships/unrelated_question.py index aadf543206..dd9476c002 100644 --- a/app/views/handlers/relationships/unrelated_question.py +++ b/app/views/handlers/relationships/unrelated_question.py @@ -49,8 +49,6 @@ def handle_post(self) -> None: if answer_action := self._get_answer_action(): self.handle_answer_action(answer_action["type"]) - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation self.questionnaire_store_updater.update_answers( self.form.data, list_item_id=self.current_location.list_item_id ) diff --git a/mypy.ini b/mypy.ini index 55719acdde..8918828886 100644 --- a/mypy.ini +++ b/mypy.ini @@ -3,6 +3,7 @@ [mypy] ignore_missing_imports = True warn_no_return = False +warn_unused_ignores = True # Per-module options: diff --git a/tests/app/data_model/test_answer_store.py b/tests/app/data_model/test_answer_store.py index 44a68381f9..6ebc1d0536 100644 --- a/tests/app/data_model/test_answer_store.py +++ b/tests/app/data_model/test_answer_store.py @@ -1,4 +1,3 @@ -# pylint: disable=redefined-outer-name import pytest from app.data_models.answer_store import Answer, AnswerStore diff --git a/tests/app/data_model/test_relationship_store.py b/tests/app/data_model/test_relationship_store.py index 1343a7f08d..8bb5f61c1e 100644 --- a/tests/app/data_model/test_relationship_store.py +++ b/tests/app/data_model/test_relationship_store.py @@ -27,7 +27,7 @@ def test_deserialisation(): assert len(relationship_store) == 2 -def test_clear(): # pylint: disable=redefined-outer-name +def test_clear(): relationship_store = RelationshipStore(relationships) relationship_store.clear() diff --git a/tests/app/forms/field_handlers/test_address_handler.py b/tests/app/forms/field_handlers/test_address_handler.py index ba1332ea72..8a32d131cf 100644 --- a/tests/app/forms/field_handlers/test_address_handler.py +++ b/tests/app/forms/field_handlers/test_address_handler.py @@ -69,8 +69,6 @@ def test_no_validation_when_address_not_mandatory( value_source_resolver, ) form.validate() - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation assert not form.errors @@ -84,8 +82,6 @@ def test_mandatory_validation_when_address_line_1_missing( ) form = test_form_class(MultiDict({"test_field": "1"}), value_source_resolver) form.validate() - # pylint: disable=no-member - # wtforms Form parents are not discoverable in the 2.3.3 implementation assert form.errors["test_field"]["line1"][0] == "Enter an address" diff --git a/tests/app/forms/field_handlers/test_number_handler.py b/tests/app/forms/field_handlers/test_number_handler.py index d542ceb13c..d99574e71a 100644 --- a/tests/app/forms/field_handlers/test_number_handler.py +++ b/tests/app/forms/field_handlers/test_number_handler.py @@ -9,9 +9,6 @@ from app.forms.fields import DecimalFieldWithSeparator, IntegerFieldWithSeparator from app.settings import MAX_NUMBER -# pylint: disable=no-member -# wtforms Form parents are not discoverable in the 2.3.3 implementation - def get_test_form_class( answer_schema, value_source_resolver, rule_evaluator, messages=None diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 9e5027b0b0..360af156f4 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1026,7 +1026,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, mocker): @pytest.mark.parametrize( - "schema_name, block, answers, breakdowns, expected_form_data, question, errors_text, value_dict", # pylint: disable=too-many-locals + "schema_name, block, answers, breakdowns, expected_form_data, question, errors_text, value_dict", [ [ "test_validation_sum_against_total_equal", @@ -1180,7 +1180,7 @@ def test_bespoke_message_for_sum_validation(app, answer_store, mocker): "second-breakdown-question", ["TOTAL_SUM_NOT_EQUALS"], {"total": "10"}, - ], # pylint: disable=too-many-locals + ], ], ) def test_calculated_field( diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index e1c13e6e85..2adca52851 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -1,4 +1,3 @@ -# pylint: disable=redefined-outer-name import uuid from datetime import datetime, timedelta, timezone diff --git a/tests/app/storage/conftest.py b/tests/app/storage/conftest.py index fef16c7ece..a34869df51 100644 --- a/tests/app/storage/conftest.py +++ b/tests/app/storage/conftest.py @@ -22,7 +22,7 @@ def dynamodb(): for config in StorageModel.TABLE_CONFIG_BY_TYPE.values(): table_name = current_app.config[config["table_name_key"]] if table_name: - boto3_client.create_table( # pylint: disable=no-member + boto3_client.create_table( TableName=table_name, AttributeDefinitions=[ {"AttributeName": config["key_field"], "AttributeType": "S"} diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index d18ceaba2b..b8cda9bfde 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -1,4 +1,3 @@ -# pylint: disable=redefined-outer-name import uuid import pytest diff --git a/tests/app/submitter/test_submitter.py b/tests/app/submitter/test_submitter.py index 5756f9b783..4e1feb4aa5 100644 --- a/tests/app/submitter/test_submitter.py +++ b/tests/app/submitter/test_submitter.py @@ -375,7 +375,7 @@ def test_double_submission_is_forbidden_when_not_delete_operation_error( def gcs_blob_create_forbidden(mocker): blob = Blob(name="test-blob", bucket=mocker.Mock()) - blob.upload_from_string = mocker.Mock( # pylint: disable=protected-access + blob.upload_from_string = mocker.Mock( side_effect=Forbidden("storage.objects.create") ) @@ -386,7 +386,7 @@ def gcs_blob_create_forbidden(mocker): def gcs_blob_delete_forbidden(mocker): blob = Blob(name="test-blob", bucket=mocker.Mock()) - blob.upload_from_string = mocker.Mock( # pylint: disable=protected-access + blob.upload_from_string = mocker.Mock( side_effect=Forbidden("storage.objects.delete") ) diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index c6bee3737f..652b2c15ac 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -72,7 +72,7 @@ def test_get_schema_list(): assert get_schema_list() == expected_output -# pylint: disable=no-value-for-parameter,missing-kwoa,too-many-function-args +# pylint: disable=no-value-for-parameter,missing-kwoa def test_schema_cache_on_function_call(): _load_schema_from_name.cache_clear() diff --git a/tests/app/views/contexts/summary/test_question.py b/tests/app/views/contexts/summary/test_question.py index f2480ffdbc..00216fee12 100644 --- a/tests/app/views/contexts/summary/test_question.py +++ b/tests/app/views/contexts/summary/test_question.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-lines import pytest from app.data_models import Answer, ListStore diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index db798ebdd9..9a781a3371 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -311,13 +311,11 @@ """ ) -# pylint: disable=line-too-long NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_EDIT_LINK_GETTER = Template( r""" ${list_name}ListEditLink(listItemInstance) { return `a[data-qa="list-item-change-` + listItemInstance + `-link"]`; } """ ) -# pylint: disable=line-too-long NON_ITEM_ANSWERS_LIST_SECTION_SUMMARY_REMOVE_LINK_GETTER = Template( r""" ${list_name}ListRemoveLink(listItemInstance) { return `a[data-qa="list-item-remove-` + listItemInstance + `-link"]`; } diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py index 0f35bdfda1..1d0ce55d64 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py @@ -1,7 +1,5 @@ from tests.integration.questionnaire import QuestionnaireTestCase -# pylint: disable=too-many-public-methods - class TestQuestionnaireListCollectorContent(QuestionnaireTestCase): def get_add_someone_link(self): diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py index db12b2c901..a05257f090 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py @@ -268,7 +268,6 @@ def test_enable_block_in_repeating_section_if_section_source_progress_is_complet # 3. Assert random question shows up self.assertInBody("Random question") - # pylint: disable=locally-disabled, too-many-statements def test_block_progress_dependencies_updated_in_repeating_sections(self): """ Test that dependency blocks inside repeating sections are updated properly diff --git a/tests/integration/routes/test_confirmation_email.py b/tests/integration/routes/test_confirmation_email.py index 42f2874bc8..bb0fa4a7d8 100644 --- a/tests/integration/routes/test_confirmation_email.py +++ b/tests/integration/routes/test_confirmation_email.py @@ -5,7 +5,7 @@ from tests.integration.integration_test_case import IntegrationTestCase -# pylint: disable=too-many-public-methods,too-few-public-methods +# pylint: disable=too-many-public-methods class TestEmailConfirmation(IntegrationTestCase): def setUp(self): settings.CONFIRMATION_EMAIL_LIMIT = 2 diff --git a/tests/integration/test_broken_submission.py b/tests/integration/test_broken_submission.py index 07cb65a2d2..7cf23b00e0 100644 --- a/tests/integration/test_broken_submission.py +++ b/tests/integration/test_broken_submission.py @@ -23,7 +23,5 @@ def test_broken_submitter_results_in_500(self): self.assertStatusOK() self.post() - self.assertEqual( - self.instance.send_message.called, True - ) # pylint: disable=no-member + self.assertEqual(self.instance.send_message.called, True) self.assertStatusCode(500) diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 309eb392c8..ff9ad7001b 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -110,9 +110,7 @@ def test_invalid_token_passed_to_flush(self): self.assertStatusForbidden() def test_flush_errors_when_submission_fails(self): - self.submitter_instance.send_message.return_value = ( - False # pylint: disable=no-member - ) + self.submitter_instance.send_message.return_value = False self.post( url="/flush?token=" @@ -126,8 +124,8 @@ def test_flush_sets_flushed_flag_to_true(self): + self.token_generator.generate_token(self.get_payload()) ) - self.encrypt_instance.assert_called_once() # pylint: disable=no-member - args = self.encrypt_instance.call_args[0] # pylint: disable=no-member + self.encrypt_instance.assert_called_once() + args = self.encrypt_instance.call_args[0] self.assertTrue('"flushed": true' in args[0]) From 33cf6fdfde7683f8377663fde7bb3afa6394da1c Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:21:30 +0000 Subject: [PATCH 341/567] Schemas v3.79.0 (#1273) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 8852be9f45..905878c406 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.78.0 +v3.79.0 From c1295392db752c772bd1a1be82617be5aeac9d36 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:15:45 +0000 Subject: [PATCH 342/567] Schemas v3.80.0 (#1274) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 905878c406..c17f470e03 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.79.0 +v3.80.0 From f5dc0185c8ef317b53680b85b9dfb42c46753642 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:46:41 +0000 Subject: [PATCH 343/567] Upgrade to Node V20 and drop support for Yarn in Runner (#1260) * Update Node to version v20.9.0 * Drop yarn commands across files and test GHA * Remove yarn from run_tests script * Update README and package-lock.json and remove .yarnrc * Fix package-lock.json in order to fix broken test * Update pull_request.yaml to cache when setting up node * Fix missing code in pull_request.yaml * Remove unnecessary caching code from pull_request.yaml * Update README and update node to the latest version * Update package-lock.json --- .github/workflows/pull_request.yml | 43 +- .nvmrc | 2 +- .yarnrc | 1 - Makefile | 12 +- README.md | 16 +- package-lock.json | 11410 +++++++++++++++++++++++++++ package.json | 4 +- scripts/run_tests.sh | 2 +- yarn.lock | 6306 --------------- 9 files changed, 11434 insertions(+), 6362 deletions(-) delete mode 100644 .yarnrc create mode 100644 package-lock.json delete mode 100644 yarn.lock diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index bb30ac1f1b..070829d5b0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -36,18 +36,9 @@ jobs: - uses: actions/setup-node@v3 with: node-version-file : ".nvmrc" - - name: Get yarn cache - id: get-yarn-cache - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - name: Cache yarn modules - id: cache-yarn - uses: actions/cache@v3 - with: - path: ${{ steps.get-yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - - name: Install yarn deps - if: steps.cache-yarn.outputs.cache-hit != 'true' - run: yarn + cache: "npm" + - name: Install npm deps + run: npm install lint: needs: [python-dependencies, node-dependencies] runs-on: ubuntu-22.04 @@ -62,6 +53,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version-file: ".nvmrc" + cache: "npm" - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install pipenv @@ -74,17 +66,8 @@ jobs: run: pipenv run python -m scripts.extract_translation_templates --test - name: Python linting run: make lint-python - - name: Get yarn cache - id: get-yarn-cache - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - name: Cache yarn modules - uses: actions/cache@v3 - id: cache-yarn - with: - path: ${{ steps.get-yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - - name: Install yarn deps - run: yarn install + - name: Install npm deps + run: npm install - name: Functional tests spec lint run: ./scripts/lint_functional_test_specs.sh - name: Javascript linting @@ -145,6 +128,7 @@ jobs: - uses: actions/setup-node@v3 with: node-version-file: ".nvmrc" + cache: "npm" - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - uses: actions/setup-python@v4 @@ -155,17 +139,8 @@ jobs: run: pip install pipenv==2023.8.22 - name: Install virtual environment run: pipenv install --dev - - name: Get yarn cache - id: get-yarn-cache - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - name: Cache yarn modules - uses: actions/cache@v3 - id: cache-yarn - with: - path: ${{ steps.get-yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - - name: Install yarn deps - run: yarn install + - name: Install npm deps + run: npm install - name: Docker compose run: docker-compose --version && RUNNER_ENV_FILE=.functional-tests.env docker-compose up --build -d - name: Functional tests diff --git a/.nvmrc b/.nvmrc index 49991d30ce..790e1105f2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.14.0 +v20.10.0 diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 075332deec..0000000000 --- a/.yarnrc +++ /dev/null @@ -1 +0,0 @@ -strict-ssl true diff --git a/Makefile b/Makefile index 9be19e4b91..9d07aa392b 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ load-design-system-templates: build: load-design-system-templates load-schemas translate generate-pages: - yarn generate_pages + npm run generate_pages lint: lint-python lint-js @@ -40,22 +40,22 @@ test-unit: pipenv run ./scripts/run_tests_unit.sh test-functional: generate-pages - yarn test_functional + npm run test_functional test-functional-headless: generate-pages EQ_RUN_FUNCTIONAL_TESTS_HEADLESS='True' make test-functional test-functional-spec: generate-pages - yarn test_functional --spec ./tests/functional/spec/$(SPEC) + npm run test_functional -- --spec=./tests/functional/spec/$(SPEC) test-functional-suite: generate-pages - yarn test_functional --suite $(SUITE) + npm run test_functional -- --suite=$(SUITE) lint-js: - yarn lint + npm run lint format-js: - yarn format + npm run format generate-spec: pipenv run python -m tests.functional.generate_pages schemas/test/en/$(SCHEMA).json ./tests/functional/generated_pages/$(patsubst test_%,%,$(SCHEMA)) -r '../../base_pages' -s tests/functional/spec/$(SCHEMA).spec.js diff --git a/README.md b/README.md index e430932e49..46d35903c5 100644 --- a/README.md +++ b/README.md @@ -202,16 +202,10 @@ nvm install nvm use ``` -Install yarn with: - -``` shell -npm i -g yarn -``` - Fetch npm dependencies: ``` shell -yarn +npm install ``` Available commands: @@ -299,7 +293,7 @@ make test-functional-suite SUITE= To run the tests against a remote deployment you will need to specify the environment variable of EQ_FUNCTIONAL_TEST_ENV eg: ``` shell -EQ_FUNCTIONAL_TEST_ENV=https://staging-new-surveys.dev.eq.ons.digital/ yarn test_functional +EQ_FUNCTIONAL_TEST_ENV=https://staging-new-surveys.dev.eq.ons.digital/ npm run test_functional ``` --- @@ -488,7 +482,7 @@ To add a new dependency, use `pipenv install [package-name]`, which not only ins NB: both the Pipfile and Pipfile.lock files are required in source control to accurately pin dependencies. ### JavaScript -To add a new dependency, use `yarn add [package-name]` and `yarn` to install all the packages locally. +To add a new dependency, use `npm install [dev dependency] --save-dev` or `npm install [dependency]` then use `npm install` to install all the packages locally. --- @@ -497,8 +491,8 @@ To add a new dependency, use `yarn add [package-name]` and `yarn` to install all ### On [Design System](https://github.com/ONSdigital/design-system) Repo Checkout branch with new changes on -You will need to install the Design System dependencies to do this so run `yarn` in the terminal if you haven't -You will also need to install gulp +You will need to install the Design System dependencies. If you haven't installed Yarn, install it with `npm i -g yarn`. To install the dependencies run `yarn` in the terminal. If you haven't +you will also need to install gulp. Then in the terminal run: diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..0dbb022dfd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11410 @@ +{ + "name": "eq-questionnaire-runner", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eq-questionnaire-runner", + "version": "1.0.0", + "devDependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/register": "^7.22.5", + "@babel/runtime": "^7.23.2", + "@wdio/cli": "^8.14.4", + "@wdio/local-runner": "^8.14.3", + "@wdio/mocha-framework": "^8.14.0", + "@wdio/spec-reporter": "^8.14.0", + "eslint": "^8.46.0", + "eslint-cli": "^1.1.1", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-json": "^3.1.0", + "eslint-plugin-n": "^16.0.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "json-web-key": "^0.4.0", + "jsrsasign": "^10.8.6", + "lint-staged": "^13.2.3", + "livereload": "^0.9.3", + "node-forge": "^1.2.1", + "node-jose": "^2.2.0", + "prettier": "^3.0.1", + "typescript": "^5.1.6", + "uuid": "^9.0.0", + "webdriverio": "^8.15.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", + "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.5", + "@babel/parser": "^7.23.5", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.5.tgz", + "integrity": "sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", + "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", + "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.4.tgz", + "integrity": "sha512-ITwqpb6V4btwUG0YJR82o2QvmWrLgDnx/p2A3CTPYGaRgULkDiC0DRA2C4jlRB9uXGUEfaSS/IGHfVW+ohzYDw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.5.tgz", + "integrity": "sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.4", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.4", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.4", + "@babel/plugin-transform-classes": "^7.23.5", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.4", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.4", + "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.4", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", + "@babel/plugin-transform-numeric-separator": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.4", + "@babel/plugin-transform-optional-chaining": "^7.23.4", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.4", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", + "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@ljharb/through": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", + "integrity": "sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", + "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", + "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@wdio/cli": { + "version": "8.24.13", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.13.tgz", + "integrity": "sha512-UG4dvnT6KfnKDsNVn/GeUidi21Pso6N6eu1O5oin9+fP612zpPFrx3/TuYrAfjJb+qmy1QkKq3zX99y+xlp7og==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.1", + "@wdio/config": "8.24.12", + "@wdio/globals": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "async-exit-hook": "^2.0.1", + "chalk": "^5.2.0", + "chokidar": "^3.5.3", + "cli-spinners": "^2.9.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "execa": "^8.0.1", + "import-meta-resolve": "^4.0.0", + "inquirer": "9.2.12", + "lodash.flattendeep": "^4.4.0", + "lodash.pickby": "^4.6.0", + "lodash.union": "^4.6.0", + "read-pkg-up": "^10.0.0", + "recursive-readdir": "^2.2.3", + "webdriverio": "8.24.12", + "yargs": "^17.7.2" + }, + "bin": { + "wdio": "bin/wdio.js" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/cli/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/config": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.12.tgz", + "integrity": "sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw==", + "dev": true, + "dependencies": { + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/globals": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.12.tgz", + "integrity": "sha512-uF26a89Q+6DdqzSfK9suXJNdWYJnsazjzPuq4Xtz6nKdjgmBufSeX1JHV4LxErEu5b/IdzVcMCUKKEvsZPc9vA==", + "dev": true, + "engines": { + "node": "^16.13 || >=18" + }, + "optionalDependencies": { + "expect-webdriverio": "^4.6.1", + "webdriverio": "8.24.12" + } + }, + "node_modules/@wdio/local-runner": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.12.tgz", + "integrity": "sha512-Q1lfdSPDEgKwuE1gNucJrkVfgOJLTjtnYGb7Fe7oYUHGDwjkudjSBJYmyx30qFZKfZ4zRqXtaEdys54/0TxibA==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/logger": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/runner": "8.24.12", + "@wdio/types": "8.24.12", + "async-exit-hook": "^2.0.1", + "split2": "^4.1.0", + "stream-buffers": "^3.0.2" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/logger": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz", + "integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==", + "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/mocha-framework": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.12.tgz", + "integrity": "sha512-SHN7CYZnDkVUNYxLp8iMV92xcmU/4gq5dqA0pRrK4m5nIU7BoL0flm0kA+ydYUQyNedQh2ru1V63uNyTOyCKAg==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.0", + "@types/node": "^20.1.0", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "mocha": "^10.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/protocols": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", + "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", + "dev": true + }, + "node_modules/@wdio/repl": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", + "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/reporter": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.12.tgz", + "integrity": "sha512-FtLzDTBXdgxXf4T9HJQ2bNpYYSKEw//jojFm9XzB4fPwzPeFY3HC+dbePucVW1SSLrVzVxqIOyHiwCLqQ/4cQw==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "diff": "^5.0.0", + "object-inspect": "^1.12.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/runner": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.12.tgz", + "integrity": "sha512-wiwXZWG12YDe7GCYBnZ1xEg3UKi18Rvh4RNQiumjypDOErJit1hOCppbJ37LqLqQu+tfWGfN73j46yR7fQOCHw==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.24.12", + "@wdio/globals": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "deepmerge-ts": "^5.0.0", + "expect-webdriverio": "^4.6.1", + "gaze": "^1.1.2", + "webdriver": "8.24.12", + "webdriverio": "8.24.12" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/spec-reporter": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.12.tgz", + "integrity": "sha512-Ng3ErWK8eESamCYwIr2Uv49+46RvmT8FnmGaJ6irJoAp101K8zENEs1pyqYHJReucN+ka/wM87blfc2k8NEHCA==", + "dev": true, + "dependencies": { + "@wdio/reporter": "8.24.12", + "@wdio/types": "8.24.12", + "chalk": "^5.1.2", + "easy-table": "^1.2.0", + "pretty-ms": "^7.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/spec-reporter/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/types": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.12.tgz", + "integrity": "sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/@wdio/utils": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.12.tgz", + "integrity": "sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.24.12", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.3.5", + "geckodriver": "^4.2.0", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "dev": true, + "dependencies": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "dev": true + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3", + "core-js-compat": "^3.33.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/basic-ftp": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", + "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/builtins/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001566", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", + "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "dependencies": { + "mitt": "3.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", + "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-shorthand-properties": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", + "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", + "dev": true + }, + "node_modules/css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", + "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1233178", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz", + "integrity": "sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg==", + "dev": true + }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/edge-paths": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", + "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", + "dev": true, + "dependencies": { + "@types/which": "^2.0.1", + "which": "^2.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/shirshak55" + } + }, + "node_modules/edge-paths/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/edge-paths/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/edgedriver": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.8.tgz", + "integrity": "sha512-FWLPDuwJDeGGgtmlqTXb4lQi/HV9yylLo1F9O1g9TLqSemA5T6xH28seUIfyleVirLFtDQyKNUxKsMhMT4IfnA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^8.16.17", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "node-fetch": "^3.3.2", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" + } + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.605", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.605.tgz", + "integrity": "sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-cli": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eslint-cli/-/eslint-cli-1.1.1.tgz", + "integrity": "sha512-Gu+fYzt7M+jIb5szUHLl5Ex0vFY7zErbi78D7ZaaLunvVTxHRvbOlfzmJlIUWsV5WDM4qyu9TD7WnGgDaDgaMA==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1", + "debug": "^2.6.8", + "resolve": "^1.3.3" + }, + "bin": { + "eslint": "bin/eslint.js", + "eslint-cli": "bin/eslint.js" + } + }, + "node_modules/eslint-cli/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-cli/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/eslint-compat-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", + "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", + "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.1.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-json": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz", + "integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "vscode-json-languageservice": "^4.1.6" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.3.1.tgz", + "integrity": "sha512-w46eDIkxQ2FaTHcey7G40eD+FhTXOdKudDXPUO2n9WNcslze/i/HT2qJ3GXjHngYSGDISIgPNhwGtgoix4zeOw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.1.0", + "get-tsconfig": "^4.7.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-n/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect-webdriverio": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.6.1.tgz", + "integrity": "sha512-w6ee91kN3BoxNGVKQheAqFpRGMehdDg7kDiErEk/oM7tbd/WUT4R4v9KYOUtjiaUFHWWCRW2FtcOOjcd0+1pvQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">=16 || >=18 || >=20" + }, + "optionalDependencies": { + "@wdio/globals": "^8.23.1", + "@wdio/logger": "^8.16.17", + "webdriverio": "^8.23.1" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/geckodriver": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", + "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@wdio/logger": "^8.11.0", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.1", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": "^16.13 || >=18 || >=20" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", + "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/get-uri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", + "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.0", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globule": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", + "dev": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "^4.17.21", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/globule/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globule/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "9.2.12", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", + "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", + "dev": true, + "dependencies": { + "@ljharb/through": "^2.3.11", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^5.0.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-web-key": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-web-key/-/json-web-key-0.4.0.tgz", + "integrity": "sha512-4MwQAsadU3y7ZTfhwup/2lYJUEB7mAOMOjfNvuHGJkgkvXaBaiWlFsuWNFpWkTG23ZlMm56TBB3BI9cVQTxjzA==", + "dev": true, + "dependencies": { + "asn1.js": "^5.0.1" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsrsasign": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.9.0.tgz", + "integrity": "sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ==", + "dev": true, + "funding": { + "url": "https://github.com/kjur/jsrsasign#donations" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ky": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lint-staged": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz", + "integrity": "sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", + "lilconfig": "2.1.0", + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "node_modules/listr2": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", + "dev": true + }, + "node_modules/locate-app": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.1.0.tgz", + "integrity": "sha512-rcVo/iLUxrd9d0lrmregK/Z5Y5NCpSwf9KlMbPpOHmKmdxdQY1Fj8NDQ5QymJTryCsBLqwmniFv2f3JKbk9Bvg==", + "dev": true, + "dependencies": { + "n12": "0.4.0", + "type-fest": "2.13.0", + "userhome": "1.0.0" + } + }, + "node_modules/locate-app/node_modules/type-fest": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.0.tgz", + "integrity": "sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.pickby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", + "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", + "dev": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true + }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/log-update/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/log-update/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/log-update/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/loglevel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/n12": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/n12/-/n12-0.4.0.tgz", + "integrity": "sha512-p/hj4zQ8d3pbbFLQuN1K9honUxiDDhueOWyFLw/XgBv+wZCE44bcLH4CIcsolOceJQduh4Jf7m/LfaTxyGmGtQ==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-jose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz", + "integrity": "sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==", + "dev": true, + "dependencies": { + "base64url": "^3.0.1", + "buffer": "^6.0.3", + "es6-promise": "^4.2.8", + "lodash": "^4.17.21", + "long": "^5.2.0", + "node-forge": "^1.2.1", + "pako": "^2.0.4", + "process": "^0.11.10", + "uuid": "^9.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", + "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "dev": true + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", + "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "ip": "^1.1.8", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "error-ex": "^1.3.2", + "json-parse-even-better-errors": "^3.0.0", + "lines-and-columns": "^2.0.3", + "type-fest": "^3.8.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true + }, + "node_modules/puppeteer-core/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/puppeteer-core/node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/puppeteer-core/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", + "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.1", + "normalize-package-data": "^6.0.0", + "parse-json": "^7.0.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", + "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0", + "read-pkg": "^8.1.0", + "type-fest": "^4.2.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz", + "integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz", + "integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", + "integrity": "sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/resq/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rgb2hex": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", + "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safaridriver": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.0.tgz", + "integrity": "sha512-azzzIP3gR1TB9bVPv7QO4Zjw0rR1BWEU/s2aFdUMN48gxDjxEB13grAEuXDmkKPgE74cObymDxmAmZnL3clj4w==", + "dev": true + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-error": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", + "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "dev": true, + "dependencies": { + "type-fest": "^2.12.2" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks/node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "dev": true + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/streamx": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz", + "integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/userhome": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz", + "integrity": "sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz", + "integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.3", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.3" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "node_modules/wait-port": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", + "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.3.0", + "debug": "^4.3.4" + }, + "bin": { + "wait-port": "bin/wait-port.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wait-port/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wait-port/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/wait-port/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wait-port/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wait-port/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/wait-port/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wait-port/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webdriver": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.12.tgz", + "integrity": "sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@types/ws": "^8.5.3", + "@wdio/config": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "deepmerge-ts": "^5.1.0", + "got": "^12.6.1", + "ky": "^0.33.0", + "ws": "^8.8.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/ws": { + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webdriverio": { + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.12.tgz", + "integrity": "sha512-Ddu0NNRMVkTzRzqvm3m0wt2eLUn+Plz2Cj+1QXDnVpddYJvk9J3elZC2hqNyscEtecQ+h2y3r36OcJqkl9jPag==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@wdio/config": "8.24.12", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.24.12", + "@wdio/utils": "8.24.12", + "archiver": "^6.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1233178", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^20.9.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.24.12" + }, + "engines": { + "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } + } + }, + "node_modules/webdriverio/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/webdriverio/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + } + } +} diff --git a/package.json b/package.json index 83740d94d6..9b1717f277 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,12 @@ }, "scripts": { "start": "make dev-compose-up && concurrently \"make run\" \"livereload . -e 'html,njk'\"", - "lint": "yarn generate_pages && yarn lint:tests && yarn lint:test-schemas", + "lint": "npm run generate_pages && npm run lint:tests && npm run lint:test-schemas", "lint:tests": "prettier --check \"tests/functional/**/*.js\" && eslint \"tests/functional/**/*.js\"", "lint:test-schemas": "prettier --check \"schemas/test/*/*.json\" && eslint \"schemas/test/**/*.json\"", "test_functional": "./node_modules/.bin/wdio tests/functional/wdio.conf.js", "generate_pages": "rm -rf ./tests/functional/generated_pages && pipenv run python -m tests.functional.generate_pages schemas/test/en/ ./tests/functional/generated_pages -r '../../base_pages'", - "format": "yarn format:tests && yarn format:test-schemas", + "format": "npm run format:tests && npm run format:test-schemas", "format:tests": "prettier \"tests/functional/**/*.js\" --write && eslint --fix \"tests/functional/**/*.js\"", "format:test-schemas": "prettier \"schemas/test/*/*.json\" --write && eslint --fix \"schemas/test/*/*.json\"", "wdio": "wdio run ./tests/functional/wdio.conf.js" diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh index 9707b3f5df..7c5609a22a 100755 --- a/scripts/run_tests.sh +++ b/scripts/run_tests.sh @@ -15,7 +15,7 @@ echo "Running python lint tests" ./scripts/run_lint_python.sh echo "Running js lint tests" -yarn lint +npm run lint echo "Running unit tests" ./scripts/run_tests_unit.sh diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 07ff888752..0000000000 --- a/yarn.lock +++ /dev/null @@ -1,6306 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@assemblyscript/loader@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06" - integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg== - -"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== - dependencies: - "@babel/highlight" "^7.22.5" - -"@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== - dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" - -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== - -"@babel/compat-data@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" - integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== - -"@babel/core@^7.23.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" - integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.2" - "@babel/parser" "^7.23.3" - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.3" - "@babel/types" "^7.23.3" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" - integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== - dependencies: - "@babel/types" "^7.23.3" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" - integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-compilation-targets@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" - integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== - dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.15" - browserslist "^4.21.9" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-compilation-targets@^7.22.6": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" - integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== - dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.9" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" - integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" - integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" - integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" - semver "^6.3.1" - -"@babel/helper-define-polyfill-provider@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz#a71c10f7146d809f4a256c373f462d9bba8cf6ba" - integrity sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== - -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== - dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-member-expression-to-functions@^7.22.15": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" - integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== - dependencies: - "@babel/types" "^7.23.0" - -"@babel/helper-member-expression-to-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" - integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" - -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - -"@babel/helper-remap-async-to-generator@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" - integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-wrap-function" "^7.22.20" - -"@babel/helper-replace-supers@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" - integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" - -"@babel/helper-replace-supers@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" - integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== - dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== - -"@babel/helper-validator-option@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" - integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== - -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== - -"@babel/helper-wrap-function@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" - integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== - dependencies: - "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.15" - "@babel/types" "^7.22.19" - -"@babel/helpers@^7.23.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" - integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== - dependencies: - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.23.0" - -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.22.15", "@babel/parser@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" - integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== - -"@babel/parser@^7.22.5": - version "7.22.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" - integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" - integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" - integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.23.3" - -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz#20c60d4639d18f7da8602548512e9d3a4c8d7098" - integrity sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-import-assertions@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" - integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-import-attributes@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" - integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" - integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-arrow-functions@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" - integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-async-generator-functions@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.3.tgz#9df2627bad7f434ed13eef3e61b2b65cafd4885b" - integrity sha512-59GsVNavGxAXCDDbakWSMJhajASb4kBCqDjqJsv+p5nKdbz7istmZ3HrX3L2LuiI80+zsOADCvooqQH3qGCucQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.20" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-transform-async-to-generator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" - integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== - dependencies: - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.20" - -"@babel/plugin-transform-block-scoped-functions@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" - integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-block-scoping@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz#e99a3ff08f58edd28a8ed82481df76925a4ffca7" - integrity sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-class-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" - integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-class-static-block@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.3.tgz#56f2371c7e5bf6ff964d84c5dc4d4db5536b5159" - integrity sha512-PENDVxdr7ZxKPyi5Ffc0LjXdnJyrJxyqF5T5YjlVg4a0VFfQHW0r8iAtRiDXkfHlu1wwcvdtnndGYIeJLSuRMQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-transform-classes@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz#73380c632c095b03e8503c24fd38f95ad41ffacb" - integrity sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-split-export-declaration" "^7.22.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" - integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.15" - -"@babel/plugin-transform-destructuring@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" - integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-dotall-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" - integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-duplicate-keys@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" - integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-dynamic-import@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.3.tgz#82625924da9ed5fb11a428efb02e43bc9a3ab13e" - integrity sha512-vTG+cTGxPFou12Rj7ll+eD5yWeNl5/8xvQvF08y5Gv3v4mZQoyFf8/n9zg4q5vvCWt5jmgymfzMAldO7orBn7A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-transform-exponentiation-operator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" - integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-export-namespace-from@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.3.tgz#dcd066d995f6ac6077e5a4ccb68322a01e23ac49" - integrity sha512-yCLhW34wpJWRdTxxWtFZASJisihrfyMOTOQexhVzA78jlU+dH7Dw+zQgcPepQ5F3C6bAIiblZZ+qBggJdHiBAg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-transform-for-of@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz#afe115ff0fbce735e02868d41489093c63e15559" - integrity sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-function-name@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" - integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== - dependencies: - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-json-strings@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.3.tgz#489724ab7d3918a4329afb4172b2fd2cf3c8d245" - integrity sha512-H9Ej2OiISIZowZHaBwF0tsJOih1PftXJtE8EWqlEIwpc7LMTGq0rPOrywKLQ4nefzx8/HMR0D3JGXoMHYvhi0A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-transform-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" - integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-logical-assignment-operators@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.3.tgz#3a406d6083feb9487083bca6d2334a3c9b6c4808" - integrity sha512-+pD5ZbxofyOygEp+zZAfujY2ShNCXRpDRIPOiBmTO693hhyOEteZgl876Xs9SAHPQpcV0vz8LvA/T+w8AzyX8A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-transform-member-expression-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" - integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-modules-amd@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" - integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== - dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-modules-commonjs@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" - integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== - dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - -"@babel/plugin-transform-modules-systemjs@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" - integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== - dependencies: - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - -"@babel/plugin-transform-modules-umd@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" - integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== - dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" - integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-new-target@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" - integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-nullish-coalescing-operator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.3.tgz#8a613d514b521b640344ed7c56afeff52f9413f8" - integrity sha512-xzg24Lnld4DYIdysyf07zJ1P+iIfJpxtVFOzX4g+bsJ3Ng5Le7rXx9KwqKzuyaUeRnt+I1EICwQITqc0E2PmpA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-transform-numeric-separator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.3.tgz#2f8da42b75ba89e5cfcd677afd0856d52c0c2e68" - integrity sha512-s9GO7fIBi/BLsZ0v3Rftr6Oe4t0ctJ8h4CCXfPoEJwmvAPMyNrfkOOJzm6b9PX9YXcCJWWQd/sBF/N26eBiMVw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-transform-object-rest-spread@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.3.tgz#509373753b5f7202fe1940e92fd075bd7874955f" - integrity sha512-VxHt0ANkDmu8TANdE9Kc0rndo/ccsmfe2Cx2y5sI4hu3AukHQ5wAu4cM7j3ba8B9548ijVyclBU+nuDQftZsog== - dependencies: - "@babel/compat-data" "^7.23.3" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.23.3" - -"@babel/plugin-transform-object-super@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" - integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - -"@babel/plugin-transform-optional-catch-binding@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.3.tgz#362c0b545ee9e5b0fa9d9e6fe77acf9d4c480027" - integrity sha512-LxYSb0iLjUamfm7f1D7GpiS4j0UAC8AOiehnsGAP8BEsIX8EOi3qV6bbctw8M7ZvLtcoZfZX5Z7rN9PlWk0m5A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-transform-optional-chaining@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz#92fc83f54aa3adc34288933fa27e54c13113f4be" - integrity sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-transform-parameters@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" - integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-private-methods@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" - integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-private-property-in-object@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.3.tgz#5cd34a2ce6f2d008cc8f91d8dcc29e2c41466da6" - integrity sha512-a5m2oLNFyje2e/rGKjVfAELTVI5mbA0FeZpBnkOWWV7eSmKQ+T/XW0Vf+29ScLzSxX+rnsarvU0oie/4m6hkxA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" - integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-regenerator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" - integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - regenerator-transform "^0.15.2" - -"@babel/plugin-transform-reserved-words@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" - integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-runtime@^7.23.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.3.tgz#0aa7485862b0b5cb0559c1a5ec08b4923743ee3b" - integrity sha512-XcQ3X58CKBdBnnZpPaQjgVMePsXtSZzHoku70q9tUAQp02ggPQNM04BF3RvlW1GSM/McbSOQAzEK4MXbS7/JFg== - dependencies: - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.6" - babel-plugin-polyfill-corejs3 "^0.8.5" - babel-plugin-polyfill-regenerator "^0.5.3" - semver "^6.3.1" - -"@babel/plugin-transform-shorthand-properties@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" - integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-spread@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" - integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - -"@babel/plugin-transform-sticky-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" - integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-template-literals@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" - integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-typeof-symbol@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" - integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-escapes@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" - integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-property-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" - integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" - integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-sets-regex@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" - integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/preset-env@^7.23.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.3.tgz#d299e0140a7650684b95c62be2db0ef8c975143e" - integrity sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q== - dependencies: - "@babel/compat-data" "^7.23.3" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.3" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.23.3" - "@babel/plugin-syntax-import-attributes" "^7.23.3" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.3" - "@babel/plugin-transform-async-to-generator" "^7.23.3" - "@babel/plugin-transform-block-scoped-functions" "^7.23.3" - "@babel/plugin-transform-block-scoping" "^7.23.3" - "@babel/plugin-transform-class-properties" "^7.23.3" - "@babel/plugin-transform-class-static-block" "^7.23.3" - "@babel/plugin-transform-classes" "^7.23.3" - "@babel/plugin-transform-computed-properties" "^7.23.3" - "@babel/plugin-transform-destructuring" "^7.23.3" - "@babel/plugin-transform-dotall-regex" "^7.23.3" - "@babel/plugin-transform-duplicate-keys" "^7.23.3" - "@babel/plugin-transform-dynamic-import" "^7.23.3" - "@babel/plugin-transform-exponentiation-operator" "^7.23.3" - "@babel/plugin-transform-export-namespace-from" "^7.23.3" - "@babel/plugin-transform-for-of" "^7.23.3" - "@babel/plugin-transform-function-name" "^7.23.3" - "@babel/plugin-transform-json-strings" "^7.23.3" - "@babel/plugin-transform-literals" "^7.23.3" - "@babel/plugin-transform-logical-assignment-operators" "^7.23.3" - "@babel/plugin-transform-member-expression-literals" "^7.23.3" - "@babel/plugin-transform-modules-amd" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-modules-systemjs" "^7.23.3" - "@babel/plugin-transform-modules-umd" "^7.23.3" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.23.3" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.3" - "@babel/plugin-transform-numeric-separator" "^7.23.3" - "@babel/plugin-transform-object-rest-spread" "^7.23.3" - "@babel/plugin-transform-object-super" "^7.23.3" - "@babel/plugin-transform-optional-catch-binding" "^7.23.3" - "@babel/plugin-transform-optional-chaining" "^7.23.3" - "@babel/plugin-transform-parameters" "^7.23.3" - "@babel/plugin-transform-private-methods" "^7.23.3" - "@babel/plugin-transform-private-property-in-object" "^7.23.3" - "@babel/plugin-transform-property-literals" "^7.23.3" - "@babel/plugin-transform-regenerator" "^7.23.3" - "@babel/plugin-transform-reserved-words" "^7.23.3" - "@babel/plugin-transform-shorthand-properties" "^7.23.3" - "@babel/plugin-transform-spread" "^7.23.3" - "@babel/plugin-transform-sticky-regex" "^7.23.3" - "@babel/plugin-transform-template-literals" "^7.23.3" - "@babel/plugin-transform-typeof-symbol" "^7.23.3" - "@babel/plugin-transform-unicode-escapes" "^7.23.3" - "@babel/plugin-transform-unicode-property-regex" "^7.23.3" - "@babel/plugin-transform-unicode-regex" "^7.23.3" - "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" - "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.6" - babel-plugin-polyfill-corejs3 "^0.8.5" - babel-plugin-polyfill-regenerator "^0.5.3" - core-js-compat "^3.31.0" - semver "^6.3.1" - -"@babel/preset-modules@0.1.6-no-external-plugins": - version "0.1.6-no-external-plugins" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" - integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/register@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939" - integrity sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.5" - source-map-support "^0.5.16" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - -"@babel/runtime@^7.23.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" - integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/runtime@^7.8.4": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" - integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" - integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.3" - "@babel/types" "^7.23.3" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.23.0", "@babel/types@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" - integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.5", "@babel/types@^7.4.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - to-fast-properties "^2.0.0" - -"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" - integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== - -"@eslint/eslintrc@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.1.tgz#18d635e24ad35f7276e8a49d135c7d3ca6a46f93" - integrity sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@^8.46.0": - version "8.46.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" - integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== - -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@jest/expect-utils@^29.6.2": - version "29.6.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" - integrity sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg== - dependencies: - jest-get-type "^29.4.3" - -"@jest/schemas@^29.6.0": - version "29.6.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" - integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== - dependencies: - "@sinclair/typebox" "^0.27.8" - -"@jest/types@^29.6.1": - version "29.6.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" - integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== - dependencies: - "@jest/schemas" "^29.6.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@ljharb/through@^2.3.9": - version "2.3.9" - resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.9.tgz#85f221eb82f9d555e180e87d6e50fb154af85408" - integrity sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@puppeteer/browsers@1.4.6": - version "1.4.6" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.4.6.tgz#1f70fd23d5d2ccce9d29b038e5039d7a1049ca77" - integrity sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ== - dependencies: - debug "4.3.4" - extract-zip "2.0.1" - progress "2.0.3" - proxy-agent "6.3.0" - tar-fs "3.0.4" - unbzip2-stream "1.4.3" - yargs "17.7.1" - -"@puppeteer/browsers@^1.4.6": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.5.0.tgz#2c445f7e41133d4aa23f776748d70211ea4e98ed" - integrity sha512-za318PweGINh5LnHSph7C4xhs0tmRjCD8EPpzcKlw4nzSPhnULj+LTG3+TGefZvW1ti5gjw2JkdQvQsivBeZlg== - dependencies: - debug "4.3.4" - extract-zip "2.0.1" - progress "2.0.3" - proxy-agent "6.3.0" - tar-fs "3.0.4" - unbzip2-stream "1.4.3" - yargs "17.7.1" - -"@puppeteer/browsers@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.6.0.tgz#d52413a7039e40a5ef72fb13fb6505fd87ce842e" - integrity sha512-R2ib8j329427jtKB/qlz0MJbwfJE/6I8ocJLiajsRqJ2PPI8DbjiNzC3lQZeISXEcjOBVhbG2RafN8SlHdcT+A== - dependencies: - debug "4.3.4" - extract-zip "2.0.1" - progress "2.0.3" - proxy-agent "6.3.0" - tar-fs "3.0.4" - unbzip2-stream "1.4.3" - yargs "17.7.1" - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== - -"@sindresorhus/is@^5.2.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" - integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== - -"@szmarczak/http-timer@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" - integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== - dependencies: - defer-to-connect "^2.0.1" - -"@tootallnate/quickjs-emscripten@^0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" - integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== - -"@types/http-cache-semantics@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/mocha@^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" - integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== - -"@types/node@*", "@types/node@^20.1.0", "@types/node@^20.1.1": - version "20.4.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.6.tgz#b66b66c9bb5d49b199f03399e341c9d6036e9e88" - integrity sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA== - -"@types/normalize-package-data@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/which@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/which/-/which-2.0.2.tgz#54541d02d6b1daee5ec01ac0d1b37cecf37db1ae" - integrity sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw== - -"@types/ws@^8.5.3": - version "8.5.5" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" - integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== - dependencies: - "@types/node" "*" - -"@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== - -"@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== - dependencies: - "@types/yargs-parser" "*" - -"@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== - dependencies: - "@types/node" "*" - -"@wdio/cli@^8.14.4": - version "8.14.4" - resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-8.14.4.tgz#3eba90787fcf697251004e97a6f70ca82000c47a" - integrity sha512-67a/DhDr2jm/mnH+Asvn0vvY9eT9vK4lLecBv20X0hMkLAS2qKLEbJz1+JfewP2ScArlSluG4R7Zq/qU0F+i3g== - dependencies: - "@types/node" "^20.1.1" - "@wdio/config" "8.14.0" - "@wdio/globals" "8.14.3" - "@wdio/logger" "8.11.0" - "@wdio/protocols" "8.11.0" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - async-exit-hook "^2.0.1" - chalk "^5.2.0" - chokidar "^3.5.3" - cli-spinners "^2.9.0" - ejs "^3.1.9" - execa "^7.1.1" - import-meta-resolve "^3.0.0" - inquirer "9.2.9" - lodash.flattendeep "^4.4.0" - lodash.pickby "^4.6.0" - lodash.union "^4.6.0" - read-pkg-up "10.0.0" - recursive-readdir "^2.2.3" - webdriverio "8.14.3" - yargs "^17.7.2" - yarn-install "^1.0.0" - -"@wdio/config@8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.14.0.tgz#0bb0aec83b0972d1118dfcb4d5d5962ecc751025" - integrity sha512-p1bXsoljGuLrw8I2ZKbh6SkmBzyzDZykpHDpCsCsNASVQSVsL7e0PMykLCfKFWq3lQsg7aOX9DPYOtFlOykz/w== - dependencies: - "@wdio/logger" "8.11.0" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - decamelize "^6.0.0" - deepmerge-ts "^5.0.0" - glob "^10.2.2" - import-meta-resolve "^3.0.0" - read-pkg-up "^10.0.0" - -"@wdio/config@8.15.4": - version "8.15.4" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-8.15.4.tgz#850bb69c1d5e9032efb75b220fe8060cbc55012f" - integrity sha512-zCiztDJUVELPEJm6p5jD0ze3noyhABR6I7nSJiiT5RZgYbbQ5lhE7tC38VKfiSL9HgDFoItH0wA3uMq0AYDG+g== - dependencies: - "@wdio/logger" "8.11.0" - "@wdio/types" "8.15.0" - "@wdio/utils" "8.15.4" - decamelize "^6.0.0" - deepmerge-ts "^5.0.0" - glob "^10.2.2" - import-meta-resolve "^3.0.0" - read-pkg-up "^10.0.0" - -"@wdio/globals@8.14.3", "@wdio/globals@^8.13.1": - version "8.14.3" - resolved "https://registry.yarnpkg.com/@wdio/globals/-/globals-8.14.3.tgz#1d5be730f7d36297c2abaa37416b6681bb84bb1c" - integrity sha512-CKQeYnWa8umh7UrSSZWBqHUDIaB/j55xEY0m6lw57kjAAmh6XJxfnqfkddo64EPICZuEHRWrwLJVSRXRCBRxrA== - optionalDependencies: - expect-webdriverio "^4.2.5" - webdriverio "8.14.3" - -"@wdio/local-runner@^8.14.3": - version "8.14.3" - resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-8.14.3.tgz#39fd347a0463fe6e173152cecd26bb42f6ec5767" - integrity sha512-y4pHl8xphE0fEuDI1JXBVH7FQo8YAyEk32lmpOD7rsI/+M+b+ukYcSQtfHfVPVYGNVvipFbiavi8yJPho0jF+w== - dependencies: - "@types/node" "^20.1.0" - "@wdio/logger" "8.11.0" - "@wdio/repl" "8.10.1" - "@wdio/runner" "8.14.3" - "@wdio/types" "8.14.0" - async-exit-hook "^2.0.1" - split2 "^4.1.0" - stream-buffers "^3.0.2" - -"@wdio/logger@8.11.0", "@wdio/logger@^8.11.0": - version "8.11.0" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-8.11.0.tgz#df28cb65d7b9e308a0cd1445a99adc8b5730174c" - integrity sha512-IsuKSaYi7NKEdgA57h8muzlN/MVp1dQG+V4C//7g4m03YJUnNQLvDhJzLjdeNTfvZy61U7foQSyt+3ktNzZkXA== - dependencies: - chalk "^5.1.2" - loglevel "^1.6.0" - loglevel-plugin-prefix "^0.8.4" - strip-ansi "^7.1.0" - -"@wdio/mocha-framework@^8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-8.14.0.tgz#78e1568cdb9b89e11dfcc007720869509962d485" - integrity sha512-j2ngUFGF0T+Zax3Tops6TxyWNy+OxvMRsloZaE+wIMrAMnp6ocGC+W4si2cW5ZuZlvaoM6j406XZOmxW//pUCA== - dependencies: - "@types/mocha" "^10.0.0" - "@types/node" "^20.1.0" - "@wdio/logger" "8.11.0" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - mocha "^10.0.0" - -"@wdio/protocols@8.11.0": - version "8.11.0" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-8.11.0.tgz#78f46d416e68c32effbd60cda1fb317db2c02067" - integrity sha512-eXTMYt/XoaX53H/Q2qmsn1uWthIC5aSTGtX9YyXD/AkagG2hXeX3lLmzNWBaSIvKR+vWXRYbg3Y/7IvL2s25Wg== - -"@wdio/protocols@8.14.6": - version "8.14.6" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-8.14.6.tgz#357db1b304244039e8befb036cdf891d826d69fd" - integrity sha512-KM2taEMUDEt1of0Na/6kIv/aNzX0pmr0etpKRci4QrWPQ1O11dXxWjkatFILQz6qOVKvQ0v+vkTPQRUllmH+uQ== - -"@wdio/repl@8.10.1": - version "8.10.1" - resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-8.10.1.tgz#4f1858335d510b3a73432be163ef303aa79db362" - integrity sha512-VZ1WFHTNKjR8Ga97TtV2SZM6fvRjWbYI2i/f4pJB4PtusorKvONAMJf2LQcUBIyzbVobqr7KSrcjmSwRolI+yw== - dependencies: - "@types/node" "^20.1.0" - -"@wdio/reporter@8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-8.14.0.tgz#1f597392370947f982e415d37cf305fcf9da4330" - integrity sha512-xzsHRlnb35vFB/zs04Zsuh2Y24DRDuLWTYA96PxDNqMIjhkqJbERTJWSlRwiBhyhz6N8NrwAQfghyh93d554eg== - dependencies: - "@types/node" "^20.1.0" - "@wdio/logger" "8.11.0" - "@wdio/types" "8.14.0" - diff "^5.0.0" - object-inspect "^1.12.0" - supports-color "9.4.0" - -"@wdio/runner@8.14.3": - version "8.14.3" - resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-8.14.3.tgz#8171d370074c1e80ca5f7eead9897463cf0a6abf" - integrity sha512-jjDSyQQ+65219E9DAOKNNQ37YHYHpYCJztdfoT6twnA7+KhT/+Or4P9c23TxWNv7KH2CU7HThJKHmmZpHfd7OA== - dependencies: - "@types/node" "^20.1.0" - "@wdio/config" "8.14.0" - "@wdio/globals" "8.14.3" - "@wdio/logger" "8.11.0" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - deepmerge-ts "^5.0.0" - expect-webdriverio "^4.2.5" - gaze "^1.1.2" - webdriver "8.14.3" - webdriverio "8.14.3" - -"@wdio/spec-reporter@^8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-8.14.0.tgz#8f4f6303182fd67632abfebb473689a1697967a9" - integrity sha512-LmDXAcA1Xy5Yd+0lxhsULfZwr0hqxjjwLO4Uys6qUyGGSUJpL8psF26sO8kFYLHBpDN0DNxzvNm7iIvPAtE1dw== - dependencies: - "@wdio/reporter" "8.14.0" - "@wdio/types" "8.14.0" - chalk "^5.1.2" - easy-table "^1.2.0" - pretty-ms "^7.0.0" - -"@wdio/types@8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.14.0.tgz#9050fbcc94423429b13d21105bb82b18fc7e3fc7" - integrity sha512-Bhk3+3Sj47nnGwWjRMYSIp7pUFV/kOQ3o6ycwR4dKtUJpdMYLuQccWJNAp1AfM3g2mVZ07FindBgNkV5bED5zg== - dependencies: - "@types/node" "^20.1.0" - -"@wdio/types@8.15.0": - version "8.15.0" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-8.15.0.tgz#f678c831957a2a05a3bc771df1702eae88a8e07c" - integrity sha512-W37Hogp6fTHg+wAdfPdpcMe2gm7LgL9eacSIuPI54CJ+fMrVlSB1I7x+RM6rMk2mccBXAmKL/cKe7y4YOX1PQA== - dependencies: - "@types/node" "^20.1.0" - -"@wdio/utils@8.14.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.14.0.tgz#70af46c28384e5575648edf8ddf61666add5160c" - integrity sha512-T7Vh4exPsjm8QtxpLbOmb7MeZVOQSFjfTncc+5eEuoYlpbJeJOcp1DSfIoTeoU9ibYToIyWEfW3MSvlDeG5JrA== - dependencies: - "@wdio/logger" "8.11.0" - "@wdio/types" "8.14.0" - import-meta-resolve "^3.0.0" - -"@wdio/utils@8.15.4": - version "8.15.4" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-8.15.4.tgz#4c7789e628f599adba988c7fcd4c535cc80e27ef" - integrity sha512-dYiiSmIR0drNoTDy6zQZanBAG/69PiBRFQsZiF56E+IJ9ikt0Oc88btNBwQP8jOI96q2qNwDlc46Lihei8xPxQ== - dependencies: - "@puppeteer/browsers" "^1.6.0" - "@wdio/logger" "8.11.0" - "@wdio/types" "8.15.0" - chrome-launcher "^1.0.0" - decamelize "^6.0.0" - deepmerge-ts "^5.1.0" - edgedriver "^5.3.3" - geckodriver "^4.2.0" - get-port "^7.0.0" - got "^13.0.0" - import-meta-resolve "^3.0.0" - safaridriver "^0.1.0" - wait-port "^1.0.4" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.9.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" - integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== - -agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" - integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== - dependencies: - debug "^4.3.4" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -ansi-styles@^6.0.0, ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -archiver-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" - integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== - dependencies: - glob "^7.1.4" - graceful-fs "^4.2.0" - lazystream "^1.0.0" - lodash.defaults "^4.2.0" - lodash.difference "^4.5.0" - lodash.flatten "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.union "^4.6.0" - normalize-path "^3.0.0" - readable-stream "^2.0.0" - -archiver@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6" - integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w== - dependencies: - archiver-utils "^2.1.0" - async "^3.2.3" - buffer-crc32 "^0.2.1" - readable-stream "^3.6.0" - readdir-glob "^1.0.0" - tar-stream "^2.2.0" - zip-stream "^4.1.0" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== - dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" - -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" - is-string "^1.0.7" - -array.prototype.findlastindex@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz#bc229aef98f6bd0533a2bc61ff95209875526c9b" - integrity sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" - -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - -arraybuffer.prototype.slice@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" - integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" - is-shared-array-buffer "^1.0.2" - -asn1.js@^5.0.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -ast-types@^0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" - integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== - dependencies: - tslib "^2.0.1" - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async-exit-hook@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" - integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== - -async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -b4a@^1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" - integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== - -babel-plugin-polyfill-corejs2@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz#b2df0251d8e99f229a8e60fc4efa9a68b41c8313" - integrity sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q== - dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.3" - semver "^6.3.1" - -babel-plugin-polyfill-corejs3@^0.8.5: - version "0.8.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz#25c2d20002da91fe328ff89095c85a391d6856cf" - integrity sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.3" - core-js-compat "^3.33.1" - -babel-plugin-polyfill-regenerator@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz#d4c49e4b44614607c13fb769bcd85c72bb26a4a5" - integrity sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.3" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.2.0, base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64url@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - -basic-ftp@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.3.tgz#b14c0fe8111ce001ec913686434fe0c2fb461228" - integrity sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g== - -big-integer@^1.6.17: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -binary@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -bl@^4.0.3, bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@~3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" - integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== - -bn.js@^4.0.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== - dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" - node-releases "^2.0.13" - update-browserslist-db "^1.0.11" - -browserslist@^4.22.1: - version "4.22.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" - integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== - dependencies: - caniuse-lite "^1.0.30001541" - electron-to-chromium "^1.4.535" - node-releases "^2.0.13" - update-browserslist-db "^1.0.13" - -buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer-indexof-polyfill@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" - integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== - -buffer@^5.2.1, buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== - -builtins@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - -cac@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" - integrity sha512-hq4rxE3NT5PlaEiVV39Z45d6MoFcQZG5dsgJqtAUeOz3408LEQAElToDkf9i5IYSCOmK0If/81dLg7nKxqPR0w== - dependencies: - camelcase-keys "^3.0.0" - chalk "^1.1.3" - indent-string "^3.0.0" - minimist "^1.2.0" - read-pkg-up "^1.0.1" - suffix "^0.1.0" - text-table "^0.2.0" - -cacheable-lookup@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" - integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== - -cacheable-request@^10.2.8: - version "10.2.13" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.13.tgz#b7012bb4a2acdb18cb54d2dff751d766b3500842" - integrity sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA== - dependencies: - "@types/http-cache-semantics" "^4.0.1" - get-stream "^6.0.1" - http-cache-semantics "^4.1.1" - keyv "^4.5.3" - mimic-response "^4.0.0" - normalize-url "^8.0.0" - responselike "^3.0.0" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camaro@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camaro/-/camaro-6.2.0.tgz#90a49bc146e41095d39e5c8b109a75882b19239b" - integrity sha512-81zTKgZb2LnkZKtLbIqLqBzQ6stWSlWC3I/lZd5u4NJVljDgMcsZqn9zZ+Yij/yNyiVpko0EhOKdYa6YAbOWrA== - dependencies: - piscina "^3.2.0" - -camelcase-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-3.0.0.tgz#fc0c6c360363f7377e3793b9a16bccf1070c1ca4" - integrity sha512-U4E6A6aFyYnNW+tDt5/yIUKQURKXe3WMFPfX4FxrQFcwZ/R08AUk1xWcUtlr7oq6CV07Ji+aa69V2g7BSpblnQ== - dependencies: - camelcase "^3.0.0" - map-obj "^1.0.0" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg== - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-lite@^1.0.30001517: - version "1.0.30001519" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" - integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== - -caniuse-lite@^1.0.30001541: - version "1.0.30001561" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" - integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== - -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== - dependencies: - traverse ">=0.3.0 <0.4" - -chalk@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3" - integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA== - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.1.2, chalk@^5.2.0, chalk@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chokidar@3.5.3, chokidar@^3.5.0, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chrome-launcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-1.0.0.tgz#c8bc62a89bebfdcb71976a9bd7e3d66e2106ee8b" - integrity sha512-74IMFVfgni/bQ4GotUNJpH2vDR+Sh9cXNPVhPXiedeiB0+5j7/8i8LAqS7WlyNSKqtxJ/CgbOpBDPLkzqDVhlw== - dependencies: - "@types/node" "*" - escape-string-regexp "^4.0.0" - is-wsl "^2.2.0" - lighthouse-logger "^2.0.1" - -chromium-bidi@0.4.16: - version "0.4.16" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.16.tgz#8a67bfdf6bb8804efc22765a82859d20724b46ab" - integrity sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA== - dependencies: - mitt "3.0.0" - -ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.5.0, cli-spinners@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" - integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -cli-truncate@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" - integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== - dependencies: - slice-ansi "^5.0.0" - string-width "^5.0.0" - -cli-width@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" - integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.19: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -commander@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - -commander@^9.3.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - -compress-commons@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" - integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== - dependencies: - buffer-crc32 "^0.2.13" - crc32-stream "^4.0.2" - normalize-path "^3.0.0" - readable-stream "^3.6.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -core-js-compat@^3.31.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" - integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== - dependencies: - browserslist "^4.21.9" - -core-js-compat@^3.33.1: - version "3.33.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.2.tgz#3ea4563bfd015ad4e4b52442865b02c62aba5085" - integrity sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw== - dependencies: - browserslist "^4.22.1" - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -crc-32@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" - integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== - -crc32-stream@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" - integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== - dependencies: - crc-32 "^1.2.0" - readable-stream "^3.4.0" - -cross-fetch@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" - integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== - dependencies: - node-fetch "^2.6.12" - -cross-spawn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA== - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -css-shorthand-properties@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" - integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== - -css-value@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" - integrity sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q== - -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - -data-uri-to-buffer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" - integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== - -debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^2.6.8, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -decamelize@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-6.0.0.tgz#8cad4d916fde5c41a264a43d0ecc56fe3d31749e" - integrity sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA== - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge-ts@^5.0.0, deepmerge-ts@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz#c55206cc4c7be2ded89b9c816cf3608884525d7a" - integrity sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw== - -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - -defer-to-connect@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -degenerator@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" - integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== - dependencies: - ast-types "^0.13.4" - escodegen "^2.1.0" - esprima "^4.0.1" - -dequal@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - -devtools-protocol@0.0.1147663: - version "0.0.1147663" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" - integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== - -devtools-protocol@^0.0.1170846: - version "0.0.1170846" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1170846.tgz#e5822c62c56a8aff7c937bb04e69823f4235fc42" - integrity sha512-GFZiHgvL4JW7+8hIMQgwYNUaIRRCsqfXd11ZbOTdu2VzDeu0Le4l1c3u4FFRWCSvMg82OFip9k/sYyz4M/PJIA== - -devtools-protocol@^0.0.1182435: - version "0.0.1182435" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1182435.tgz#68155464f987d66390fae28b02782a1b9c153a0c" - integrity sha512-EmlkWb62wSbQNE1gRZZsi4KZYRaF5Skpp183LhRU7+sadKR06O1dHCjZmFSEG6Kv7P6S/UYLxcY3NlYwqKM99w== - -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -diff@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" - integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -easy-table@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb" - integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww== - dependencies: - ansi-regex "^5.0.1" - optionalDependencies: - wcwidth "^1.0.1" - -edge-paths@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-3.0.5.tgz#9a35361d701d9b5dc07f641cebe8da01ede80937" - integrity sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg== - dependencies: - "@types/which" "^2.0.1" - which "^2.0.2" - -edgedriver@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/edgedriver/-/edgedriver-5.3.2.tgz#2e1859f347f9373defede0f8dcf788762369d27a" - integrity sha512-jxVSDomvdf8P/vEr5BWfhM+1/hEYeeipwaSWGltBMoJY9OLEf7o/vo+PmM3C2o4+LzGuyL/zSlfXRDAWFNkn4g== - dependencies: - "@wdio/logger" "^8.11.0" - camaro "^6.2.0" - decamelize "^6.0.0" - edge-paths "^3.0.5" - node-fetch "^3.3.1" - unzipper "^0.10.14" - which "^3.0.1" - -edgedriver@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/edgedriver/-/edgedriver-5.3.3.tgz#d26a207305cd42ee9069b976c08132ab0780d8bb" - integrity sha512-Gfb0gzBhpiXaVqLsIkua3RItwfiEGHVt9lXYG4bySoSluDtqIvwFBnMAEHKBS+j16ebEJSQh475e20X2lfzgjg== - dependencies: - "@wdio/logger" "^8.11.0" - camaro "^6.2.0" - decamelize "^6.0.0" - edge-paths "^3.0.5" - node-fetch "^3.3.2" - unzipper "^0.10.14" - which "^3.0.1" - -ejs@^3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== - dependencies: - jake "^10.8.5" - -electron-to-chromium@^1.4.477: - version "1.4.482" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.482.tgz#77c5ed37b93d4dda860e27538e0e2a01d6a19e02" - integrity sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA== - -electron-to-chromium@^1.4.535: - version "1.4.580" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.580.tgz#2f8f70f70733a6be1fb6f31de1224e6dc4bb196d" - integrity sha512-T5q3pjQon853xxxHUq3ZP68ZpvJHuSMY2+BZaW3QzjS4HvNuvsMmZ/+lU+nCrftre1jFZ+OSlExynXWBihnXzw== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -error-ex@^1.2.0, error-ex@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: - version "1.22.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" - integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.1" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.1" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.0" - safe-array-concat "^1.0.0" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.10" - -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== - dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - has-tostringtag "^1.0.0" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es6-promise@^4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" - integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== - -escodegen@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - -eslint-cli@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/eslint-cli/-/eslint-cli-1.1.1.tgz#ae6979edd8ee6e78c6d413b525f4052cb2a94cfd" - integrity sha512-Gu+fYzt7M+jIb5szUHLl5Ex0vFY7zErbi78D7ZaaLunvVTxHRvbOlfzmJlIUWsV5WDM4qyu9TD7WnGgDaDgaMA== - dependencies: - chalk "^2.0.1" - debug "^2.6.8" - resolve "^1.3.3" - -eslint-config-standard@^17.1.0: - version "17.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" - integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== - -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== - dependencies: - debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" - -eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== - dependencies: - debug "^3.2.7" - -eslint-plugin-es-x@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.2.0.tgz#5779d742ad31f8fd780b9481331481e142b72311" - integrity sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA== - dependencies: - "@eslint-community/eslint-utils" "^4.1.2" - "@eslint-community/regexpp" "^4.6.0" - -eslint-plugin-es@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" - integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== - dependencies: - eslint-utils "^2.0.0" - regexpp "^3.0.0" - -eslint-plugin-import@^2.28.0: - version "2.28.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz#8d66d6925117b06c4018d491ae84469eb3cb1005" - integrity sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q== - dependencies: - array-includes "^3.1.6" - array.prototype.findlastindex "^1.2.2" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.8.0" - has "^1.0.3" - is-core-module "^2.12.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.6" - object.groupby "^1.0.0" - object.values "^1.1.6" - resolve "^1.22.3" - semver "^6.3.1" - tsconfig-paths "^3.14.2" - -eslint-plugin-json@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz#251108ba1681c332e0a442ef9513bd293619de67" - integrity sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g== - dependencies: - lodash "^4.17.21" - vscode-json-languageservice "^4.1.6" - -eslint-plugin-n@^16.0.1: - version "16.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.0.1.tgz#baa62bb3af52940a53ba15386348ad9b0b425ada" - integrity sha512-CDmHegJN0OF3L5cz5tATH84RPQm9kG+Yx39wIqIwPR2C0uhBGMWfbbOtetR83PQjjidA5aXMu+LEFw1jaSwvTA== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - builtins "^5.0.1" - eslint-plugin-es-x "^7.1.0" - ignore "^5.2.4" - is-core-module "^2.12.1" - minimatch "^3.1.2" - resolve "^1.22.2" - semver "^7.5.3" - -eslint-plugin-node@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" - integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== - dependencies: - eslint-plugin-es "^3.0.0" - eslint-utils "^2.0.0" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - -eslint-plugin-promise@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" - integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-utils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz#8c2095440eca8c933bedcadf16fefa44dbe9ba5f" - integrity sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw== - -eslint@^8.46.0: - version "8.46.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.46.0.tgz#a06a0ff6974e53e643acc42d1dcf2e7f797b3552" - integrity sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.1" - "@eslint/js" "^8.46.0" - "@humanwhocodes/config-array" "^0.11.10" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.2" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -eventemitter-asyncresource@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz#734ff2e44bf448e627f7748f905d6bdd57bdb65b" - integrity sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ== - -execa@^7.0.0, execa@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" - integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.1" - human-signals "^4.3.0" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^3.0.7" - strip-final-newline "^3.0.0" - -expect-webdriverio@^4.2.5: - version "4.2.7" - resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-4.2.7.tgz#ae7525202e4e02f46b0c9c10e0cd9af4f5234b52" - integrity sha512-ulNspQoTct5/0OBfNSNzBKldSgoQxzmHBi/TLtrsLZY5QCZ7svD7Csdtm+YEW6IBg/v8MI7ibiMcpWY9cjcG6A== - dependencies: - expect "^29.6.1" - jest-matcher-utils "^29.6.1" - optionalDependencies: - "@wdio/globals" "^8.13.1" - webdriverio "^8.13.1" - -expect@^29.6.1: - version "29.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" - integrity sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA== - dependencies: - "@jest/expect-utils" "^29.6.2" - "@types/node" "*" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.6.2" - jest-message-util "^29.6.2" - jest-util "^29.6.2" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-fifo@^1.1.0, fast-fifo@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.0.tgz#03e381bcbfb29932d7c3afde6e15e83e05ab4d8b" - integrity sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== - dependencies: - reusify "^1.0.4" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -figures@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f" - integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg== - dependencies: - escape-string-regexp "^5.0.0" - is-unicode-supported "^1.2.0" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -filelist@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" - integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== - dependencies: - minimatch "^5.0.1" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-up@5.0.0, find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" - integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== - dependencies: - locate-path "^7.1.0" - path-exists "^5.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -foreground-child@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" - integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - -form-data-encoder@^2.1.2: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" - integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== - -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2, functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gaze@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - -geckodriver@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.1.3.tgz#4a9e25a301e53a6fb5a9e1d72e3d29b7b1815727" - integrity sha512-w3vZvibJnDLui3ZcyWz/CjiWET31br7ALakbfEqssSzwBFzolOj6/FnXPgcmxSkdH1iMBOc83dewLEqR8xoilA== - dependencies: - "@wdio/logger" "^8.11.0" - decamelize "^6.0.0" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.1" - node-fetch "^3.3.1" - tar-fs "^3.0.4" - unzipper "^0.10.14" - which "^3.0.1" - -geckodriver@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.2.0.tgz#a385370011ae29fa8c68351b01d054a92b92d945" - integrity sha512-I2BlybeMFMzpxHRrh8X4VwP4ys74JHszyUgfezOrbTR7PEybFneDcuEjKIQxKV6vFPmsdwhwF1x8AshdQo56xA== - dependencies: - "@wdio/logger" "^8.11.0" - decamelize "^6.0.0" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.1" - node-fetch "^3.3.1" - tar-fs "^3.0.4" - unzipper "^0.10.14" - which "^3.0.1" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" - -get-port@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.0.0.tgz#ffcd83da826146529e307a341d7801cae351daff" - integrity sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw== - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -get-uri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.1.tgz#cff2ba8d456c3513a04b70c45de4dbcca5b1527c" - integrity sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q== - dependencies: - basic-ftp "^5.0.2" - data-uri-to-buffer "^5.0.1" - debug "^4.3.4" - fs-extra "^8.1.0" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^10.2.2: - version "10.3.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" - integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.0.3" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - -glob@^7.1.3, glob@^7.1.4: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@~7.1.1: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -globule@^1.0.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.4.tgz#7c11c43056055a75a6e68294453c17f2796170fb" - integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== - dependencies: - glob "~7.1.1" - lodash "^4.17.21" - minimatch "~3.0.2" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -"got@^ 12.6.1": - version "12.6.1" - resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" - integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== - dependencies: - "@sindresorhus/is" "^5.2.0" - "@szmarczak/http-timer" "^5.0.1" - cacheable-lookup "^7.0.0" - cacheable-request "^10.2.8" - decompress-response "^6.0.0" - form-data-encoder "^2.1.2" - get-stream "^6.0.1" - http2-wrapper "^2.1.10" - lowercase-keys "^3.0.0" - p-cancelable "^3.0.0" - responselike "^3.0.0" - -got@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" - integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== - dependencies: - "@sindresorhus/is" "^5.2.0" - "@szmarczak/http-timer" "^5.0.1" - cacheable-lookup "^7.0.0" - cacheable-request "^10.2.8" - decompress-response "^6.0.0" - form-data-encoder "^2.1.2" - get-stream "^6.0.1" - http2-wrapper "^2.1.10" - lowercase-keys "^3.0.0" - p-cancelable "^3.0.0" - responselike "^3.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -grapheme-splitter@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hdr-histogram-js@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz#0b860534655722b6e3f3e7dca7b78867cf43dcb5" - integrity sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g== - dependencies: - "@assemblyscript/loader" "^0.10.1" - base64-js "^1.2.0" - pako "^1.0.3" - -hdr-histogram-percentiles-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz#9409f4de0c2dda78e61de2d9d78b1e9f3cba283c" - integrity sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw== - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -hosted-git-info@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.1.tgz#629442c7889a69c05de604d52996b74fe6f26d58" - integrity sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w== - dependencies: - lru-cache "^7.5.1" - -http-cache-semantics@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" - integrity sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - -http2-wrapper@^2.1.10: - version "2.2.0" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.0.tgz#b80ad199d216b7d3680195077bd7b9060fa9d7f3" - integrity sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.2.0" - -https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" - integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== - dependencies: - agent-base "^7.0.2" - debug "4" - -human-signals@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" - integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== - -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.13, ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-meta-resolve@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz#94a6aabc623874fbc2f3525ec1300db71c6cbc11" - integrity sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inquirer@9.2.9: - version "9.2.9" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-9.2.9.tgz#49c16cae502b773209598f8668d8ddbfe5b07ecb" - integrity sha512-0VXHov2GGwWquYxwxlcIcm3yOHvFb2jh/+HkY8/AUXSTWShpo6QJMlSfHi5Xo74NO40UePBM3rQcI3OkzOF/7A== - dependencies: - "@ljharb/through" "^2.3.9" - ansi-escapes "^4.3.2" - chalk "^5.3.0" - cli-cursor "^3.1.0" - cli-width "^4.0.0" - external-editor "^3.0.3" - figures "^5.0.0" - lodash "^4.17.21" - mute-stream "1.0.0" - ora "^5.4.1" - run-async "^3.0.0" - rxjs "^7.8.1" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wrap-ansi "^6.0.1" - -internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== - dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" - side-channel "^1.0.4" - -ip@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== - -ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== - -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.11.0, is-core-module@^2.12.0, is-core-module@^2.12.1, is-core-module@^2.8.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== - dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-fullwidth-code-point@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" - integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-obj@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== - dependencies: - which-typed-array "^1.1.11" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-unicode-supported@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" - integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -jackspeak@^2.0.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.2.tgz#707c62733924b8dc2a0a629dc6248577788b5385" - integrity sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -jake@^10.8.5: - version "10.8.7" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" - integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== - dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.4" - minimatch "^3.1.2" - -jest-diff@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" - integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" - -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== - -jest-matcher-utils@^29.6.1, jest-matcher-utils@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" - integrity sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ== - dependencies: - chalk "^4.0.0" - jest-diff "^29.6.2" - jest-get-type "^29.4.3" - pretty-format "^29.6.2" - -jest-message-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" - integrity sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.6.2" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-util@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.2.tgz#8a052df8fff2eebe446769fd88814521a517664d" - integrity sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w== - dependencies: - "@jest/types" "^29.6.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@4.1.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-even-better-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" - integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json-web-key@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-web-key/-/json-web-key-0.4.0.tgz#a8e7268d1741c3a87c51c5070ea7ea988e9a78b7" - integrity sha512-4MwQAsadU3y7ZTfhwup/2lYJUEB7mAOMOjfNvuHGJkgkvXaBaiWlFsuWNFpWkTG23ZlMm56TBB3BI9cVQTxjzA== - dependencies: - asn1.js "^5.0.1" - -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonc-parser@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -jsrsasign@^10.8.6: - version "10.8.6" - resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.8.6.tgz#ebf7f3c812c6517af84f0d8a10115e0dbfabe145" - integrity sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw== - -keyv@^4.5.3: - version "4.5.3" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" - integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== - dependencies: - json-buffer "3.0.1" - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -ky@^0.33.0: - version "0.33.3" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.33.3.tgz#bf1ad322a3f2c3428c13cfa4b3af95e6c4a2f543" - integrity sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw== - -lazystream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" - integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== - dependencies: - readable-stream "^2.0.5" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -lighthouse-logger@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz#48895f639b61cca89346bb6f47f7403a3895fa02" - integrity sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ== - dependencies: - debug "^2.6.9" - marky "^1.2.2" - -lilconfig@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" - integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== - -lines-and-columns@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" - integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== - -lint-staged@^13.2.3: - version "13.2.3" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.3.tgz#f899aad6c093473467e9c9e316e3c2d8a28f87a7" - integrity sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg== - dependencies: - chalk "5.2.0" - cli-truncate "^3.1.0" - commander "^10.0.0" - debug "^4.3.4" - execa "^7.0.0" - lilconfig "2.1.0" - listr2 "^5.0.7" - micromatch "^4.0.5" - normalize-path "^3.0.0" - object-inspect "^1.12.3" - pidtree "^0.6.0" - string-argv "^0.3.1" - yaml "^2.2.2" - -listenercount@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" - integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ== - -listr2@^5.0.7: - version "5.0.8" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" - integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.19" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.8.0" - through "^2.3.8" - wrap-ansi "^7.0.0" - -livereload-js@^3.3.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-3.4.1.tgz#ba90fbc708ed1b9a024bb89c4ee12c96ea03d66f" - integrity sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g== - -livereload@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.9.3.tgz#a714816375ed52471408bede8b49b2ee6a0c55b1" - integrity sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw== - dependencies: - chokidar "^3.5.0" - livereload-js "^3.3.1" - opts ">= 1.2.0" - ws "^7.4.3" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -locate-path@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" - integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== - dependencies: - p-locate "^6.0.0" - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== - -lodash.difference@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" - integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA== - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.pickby@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" - integrity sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q== - -lodash.union@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== - -lodash.zip@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" - integrity sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg== - -lodash@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@4.1.0, log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - -loglevel-plugin-prefix@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" - integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== - -loglevel@^1.6.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" - integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== - -long@^5.2.0: - version "5.2.3" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" - integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== - -lowercase-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" - integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru-cache@^7.14.1, lru-cache@^7.5.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -"lru-cache@^9.1.1 || ^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" - integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== - -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -map-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== - -marky@^1.2.2: - version "1.2.5" - resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0" - integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - -mimic-response@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" - integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== - -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1, minimatch@^5.1.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.0, minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimatch@~3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" - integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" - integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== - -mitt@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" - integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== - -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -"mkdirp@>=0.5 0": - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mocha@^10.0.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.2.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - nanoid "3.3.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -mute-stream@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" - integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== - -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -netmask@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" - integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== - -nice-napi@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b" - integrity sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA== - dependencies: - node-addon-api "^3.0.0" - node-gyp-build "^4.2.2" - -node-addon-api@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@^2.6.12: - version "2.6.12" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" - integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^3.3.1, node-fetch@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -node-forge@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - -node-gyp-build@^4.2.2: - version "4.6.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" - integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== - -node-jose@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-2.2.0.tgz#b64f3225ad6bec328509a420800de597ba2bf3ed" - integrity sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw== - dependencies: - base64url "^3.0.1" - buffer "^6.0.3" - es6-promise "^4.2.8" - lodash "^4.17.21" - long "^5.2.0" - node-forge "^1.2.1" - pako "^2.0.4" - process "^0.11.10" - uuid "^9.0.0" - -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== - -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-5.0.0.tgz#abcb8d7e724c40d88462b84982f7cbf6859b4588" - integrity sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q== - dependencies: - hosted-git-info "^6.0.0" - is-core-module "^2.8.1" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a" - integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw== - -npm-run-path@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" - integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== - dependencies: - path-key "^4.0.0" - -object-inspect@^1.12.0, object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.fromentries@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -object.groupby@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.0.tgz#cb29259cf90f37e7bac6437686c1ea8c916d12a9" - integrity sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.21.2" - get-intrinsic "^1.2.1" - -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - -optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== - dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - -"opts@>= 1.2.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/opts/-/opts-2.0.2.tgz#a17e189fbbfee171da559edd8a42423bc5993ce1" - integrity sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg== - -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== - -p-cancelable@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" - integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== - -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-limit@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" - integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== - dependencies: - yocto-queue "^1.0.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-locate@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" - integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== - dependencies: - p-limit "^4.0.0" - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pac-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz#db42120c64292685dafaf2bd921e223c56bfb13b" - integrity sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA== - dependencies: - "@tootallnate/quickjs-emscripten" "^0.23.0" - agent-base "^7.0.2" - debug "^4.3.4" - get-uri "^6.0.1" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" - pac-resolver "^7.0.0" - socks-proxy-agent "^8.0.1" - -pac-resolver@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.0.tgz#79376f1ca26baf245b96b34c339d79bff25e900c" - integrity sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg== - dependencies: - degenerator "^5.0.0" - ip "^1.1.8" - netmask "^2.0.2" - -pako@^1.0.3: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -pako@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" - integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== - dependencies: - error-ex "^1.2.0" - -parse-json@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-7.0.0.tgz#51c0713f233b804eb5adee3ef1e75d3243e0ff06" - integrity sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw== - dependencies: - "@babel/code-frame" "^7.21.4" - error-ex "^1.3.2" - json-parse-even-better-errors "^3.0.0" - lines-and-columns "^2.0.3" - type-fest "^3.8.0" - -parse-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" - integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-exists@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" - integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-scurry@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== - dependencies: - lru-cache "^9.1.1 || ^10.0.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pidtree@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" - integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== - -pirates@^4.0.5: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -piscina@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/piscina/-/piscina-3.2.0.tgz#f5a1dde0c05567775690cccefe59d9223924d154" - integrity sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA== - dependencies: - eventemitter-asyncresource "^1.0.0" - hdr-histogram-js "^2.0.1" - hdr-histogram-percentiles-obj "^3.0.0" - optionalDependencies: - nice-napi "^1.0.2" - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prettier@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.1.tgz#65271fc9320ce4913c57747a70ce635b30beaa40" - integrity sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ== - -pretty-format@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" - integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== - dependencies: - "@jest/schemas" "^29.6.0" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-ms@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" - integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== - dependencies: - parse-ms "^2.1.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -progress@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proxy-agent@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" - integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== - dependencies: - agent-base "^7.0.2" - debug "^4.3.4" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" - lru-cache "^7.14.1" - pac-proxy-agent "^7.0.0" - proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.1" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -puppeteer-core@^20.9.0: - version "20.9.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-20.9.0.tgz#6f4b420001b64419deab38d398a4d9cd071040e6" - integrity sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg== - dependencies: - "@puppeteer/browsers" "1.4.6" - chromium-bidi "0.4.16" - cross-fetch "4.0.0" - debug "4.3.4" - devtools-protocol "0.0.1147663" - ws "8.13.0" - -query-selector-shadow-dom@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz#1c7b0058eff4881ac44f45d8f84ede32e9a2f349" - integrity sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -queue-tick@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" - integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== - -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - -read-pkg-up@10.0.0, read-pkg-up@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-10.0.0.tgz#0542d21ff1001d2bfff1f6eac8b4d1d1dc486617" - integrity sha512-jgmKiS//w2Zs+YbX039CorlkOp8FIVbSAN8r8GJHDsGlmNPXo+VeHkqAwCiQVTTx5/LwLZTcEw59z3DvcLbr0g== - dependencies: - find-up "^6.3.0" - read-pkg "^8.0.0" - type-fest "^3.12.0" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-8.0.0.tgz#78b93774c15a3f151b56d5790d5127a5cb9fc507" - integrity sha512-Ajb9oSjxXBw0YyOiwtQ2dKbAA/vMnUPnY63XcCk+mXo0BwIdQEMgZLZiMWGttQHcUhUgbK0mH85ethMPKXxziw== - dependencies: - "@types/normalize-package-data" "^2.4.1" - normalize-package-data "^5.0.0" - parse-json "^7.0.0" - type-fest "^3.8.0" - -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdir-glob@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" - integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== - dependencies: - minimatch "^5.1.0" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -recursive-readdir@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" - integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== - dependencies: - minimatch "^3.0.5" - -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -regenerator-runtime@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" - integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== - -regenerator-transform@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" - integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== - dependencies: - "@babel/runtime" "^7.8.4" - -regexp.prototype.flags@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - functions-have-names "^1.2.3" - -regexpp@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -resolve-alpn@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.3.3: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== - dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.22.3: - version "1.22.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283" - integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw== - dependencies: - is-core-module "^2.12.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -responselike@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" - integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== - dependencies: - lowercase-keys "^3.0.0" - -resq@^1.9.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/resq/-/resq-1.11.0.tgz#edec8c58be9af800fd628118c0ca8815283de196" - integrity sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw== - dependencies: - fast-deep-equal "^2.0.1" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rgb2hex@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.5.tgz#f82230cd3ab1364fa73c99be3a691ed688f8dbdc" - integrity sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw== - -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -run-async@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-3.0.0.tgz#42a432f6d76c689522058984384df28be379daad" - integrity sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -rxjs@^7.8.0, rxjs@^7.8.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - -safaridriver@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/safaridriver/-/safaridriver-0.1.0.tgz#8ff901e847b003c6a52b534028f57cddc82d6b14" - integrity sha512-azzzIP3gR1TB9bVPv7QO4Zjw0rR1BWEU/s2aFdUMN48gxDjxEB13grAEuXDmkKPgE74cObymDxmAmZnL3clj4w== - -safe-array-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" - integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - has-symbols "^1.0.3" - isarray "^2.0.5" - -safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-regex "^1.1.4" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -"semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^6.1.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.0.0, semver@^7.3.5, semver@^7.5.3: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -serialize-error@^11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-11.0.1.tgz#7cfa2b54f7aca3e4cbfc0137259d94d93793f813" - integrity sha512-B5yw3/Lg+Daspbs0f+iO3Qim0+lALnaLS8aZUAy8Y0tO92tkOoMEuwtKo4jpZ5XO16CTwMi4tYN8cZQI3QF2Qw== - dependencies: - type-fest "^2.12.2" - -serialize-error@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" - integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== - dependencies: - type-fest "^0.20.2" - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -setimmediate@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.2, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" - integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== - dependencies: - ansi-styles "^6.0.0" - is-fullwidth-code-point "^4.0.0" - -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - -socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== - dependencies: - agent-base "^7.0.1" - debug "^4.3.4" - socks "^2.7.1" - -socks@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== - dependencies: - ip "^2.0.0" - smart-buffer "^4.2.0" - -source-map-support@^0.5.16: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.13" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" - integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== - -split2@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" - integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -stream-buffers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - -streamx@^2.15.0: - version "2.15.0" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.0.tgz#f58c92e6f726b5390dcabd6dd9094d29a854d698" - integrity sha512-HcxY6ncGjjklGs1xsP1aR71INYcsXFJet5CU1CHqihQ2J5nOsbd4OjgjHO42w/4QNv9gZb3BueV+Vxok5pLEXg== - dependencies: - fast-fifo "^1.1.0" - queue-tick "^1.0.1" - -string-argv@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" - integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== - -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^7.0.1, strip-ansi@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - -strip-json-comments@3.1.1, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -suffix@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.1.tgz#cc58231646a0ef1102f79478ef3a9248fd9c842f" - integrity sha512-j5uf6MJtMCfC4vBe5LFktSe4bGyNTBk7I2Kdri0jeLrcv5B9pWfxVa5JQpoxgtR8vaVB7bVxsWgnfQbX5wkhAA== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" - integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -tar-fs@3.0.4, tar-fs@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" - integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== - dependencies: - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^3.1.5" - -tar-stream@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar-stream@^3.1.5: - version "3.1.6" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.6.tgz#6520607b55a06f4a2e2e04db360ba7d338cc5bab" - integrity sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg== - dependencies: - b4a "^1.6.4" - fast-fifo "^1.2.0" - streamx "^2.15.0" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== - -tsconfig-paths@^3.14.2: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^2.0.1, tslib@^2.1.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410" - integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig== - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^2.12.2: - version "2.19.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" - integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== - -type-fest@^3.12.0, type-fest@^3.8.0: - version "3.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" - integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== - -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" - -typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - -typescript@^5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -unbzip2-stream@1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unzipper@^0.10.14: - version "0.10.14" - resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" - integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g== - dependencies: - big-integer "^1.6.17" - binary "~0.3.0" - bluebird "~3.4.1" - buffer-indexof-polyfill "~1.0.0" - duplexer2 "~0.1.4" - fstream "^1.0.12" - graceful-fs "^4.2.2" - listenercount "~1.0.1" - readable-stream "~2.3.6" - setimmediate "~1.0.4" - -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -vscode-json-languageservice@^4.1.6: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" - integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== - dependencies: - jsonc-parser "^3.0.0" - vscode-languageserver-textdocument "^1.0.3" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" - vscode-uri "^3.0.3" - -vscode-languageserver-textdocument@^1.0.3: - version "1.0.8" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" - integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== - -vscode-languageserver-types@^3.16.0: - version "3.17.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" - integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== - -vscode-nls@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" - integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== - -vscode-uri@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" - integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== - -wait-port@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-1.0.4.tgz#6f9474645ddbf7701ac100ab6762438edf6e5689" - integrity sha512-w8Ftna3h6XSFWWc2JC5gZEgp64nz8bnaTp5cvzbJSZ53j+omktWTDdwXxEF0jM8YveviLgFWvNGrSvRHnkyHyw== - dependencies: - chalk "^4.1.2" - commander "^9.3.0" - debug "^4.3.4" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - -web-streams-polyfill@^3.0.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== - -webdriver@8.14.3: - version "8.14.3" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.14.3.tgz#9b87f62655d422bce54d17b5fca2cde9aa701442" - integrity sha512-7hAhzXHuNKkowa2yN0KgNiHVxOOWpBjt0Ql7EBQadlF6wWzSFX8qTdtEBuDk9VcR/ijDE2X9UyptNCU1j83TvQ== - dependencies: - "@puppeteer/browsers" "^1.4.6" - "@types/node" "^20.1.0" - "@types/ws" "^8.5.3" - "@wdio/config" "8.14.0" - "@wdio/logger" "8.11.0" - "@wdio/protocols" "8.11.0" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - chrome-launcher "^1.0.0" - decamelize "^6.0.0" - deepmerge-ts "^5.1.0" - edgedriver "^5.3.2" - geckodriver "^4.1.3" - get-port "^7.0.0" - got "^ 12.6.1" - ky "^0.33.0" - safaridriver "^0.1.0" - wait-port "^1.0.4" - ws "^8.8.0" - -webdriver@8.15.4: - version "8.15.4" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-8.15.4.tgz#2cd010229c0021955ffb31243192ab67d4f8b844" - integrity sha512-9fbha7DJeMX7amb/TYtSjpQ2AAsUa7+auI8Riwu0vuP1tm5zEqQvm+xl25TPNiPtmwPHxfn4aZlQvJ589NnWkw== - dependencies: - "@types/node" "^20.1.0" - "@types/ws" "^8.5.3" - "@wdio/config" "8.15.4" - "@wdio/logger" "8.11.0" - "@wdio/protocols" "8.14.6" - "@wdio/types" "8.15.0" - "@wdio/utils" "8.15.4" - deepmerge-ts "^5.1.0" - got "^ 12.6.1" - ky "^0.33.0" - ws "^8.8.0" - -webdriverio@8.14.3, webdriverio@^8.13.1: - version "8.14.3" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.14.3.tgz#490cdd505b6cf57a50d6f67841b8295d6f25c94f" - integrity sha512-NMVap664xbbPKrVWpcxyTEVmkUadS8wu2eBtT8uMhesLCtaCZjxTF5andHpIEFitKhgHf22SeP++1ZrQpMoRCA== - dependencies: - "@types/node" "^20.1.0" - "@wdio/config" "8.14.0" - "@wdio/logger" "8.11.0" - "@wdio/protocols" "8.11.0" - "@wdio/repl" "8.10.1" - "@wdio/types" "8.14.0" - "@wdio/utils" "8.14.0" - archiver "^5.0.0" - aria-query "^5.0.0" - css-shorthand-properties "^1.1.1" - css-value "^0.0.1" - devtools-protocol "^0.0.1170846" - grapheme-splitter "^1.0.2" - import-meta-resolve "^3.0.0" - is-plain-obj "^4.1.0" - lodash.clonedeep "^4.5.0" - lodash.zip "^4.2.0" - minimatch "^9.0.0" - puppeteer-core "^20.9.0" - query-selector-shadow-dom "^1.0.0" - resq "^1.9.1" - rgb2hex "0.2.5" - serialize-error "^8.0.0" - webdriver "8.14.3" - -webdriverio@^8.15.0: - version "8.15.4" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-8.15.4.tgz#b299c4cc3aa4c1fbc1b56582c1c002e3b9566953" - integrity sha512-UkZI2ampTsBzTC+mDu8kZwUd7hKVML/zNhFk2EUyE9HaDXY6GHuVkzT2vU++3IXHbMUhRZTSy7IRaMlG8EAmNA== - dependencies: - "@types/node" "^20.1.0" - "@wdio/config" "8.15.4" - "@wdio/logger" "8.11.0" - "@wdio/protocols" "8.14.6" - "@wdio/repl" "8.10.1" - "@wdio/types" "8.15.0" - "@wdio/utils" "8.15.4" - archiver "^5.0.0" - aria-query "^5.0.0" - css-shorthand-properties "^1.1.1" - css-value "^0.0.1" - devtools-protocol "^0.0.1182435" - grapheme-splitter "^1.0.2" - import-meta-resolve "^3.0.0" - is-plain-obj "^4.1.0" - lodash.clonedeep "^4.5.0" - lodash.zip "^4.2.0" - minimatch "^9.0.0" - puppeteer-core "^20.9.0" - query-selector-shadow-dom "^1.0.0" - resq "^1.9.1" - rgb2hex "0.2.5" - serialize-error "^11.0.1" - webdriver "8.15.4" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-typed-array@^1.1.10, which-typed-array@^1.1.11: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" - integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== - dependencies: - isexe "^2.0.0" - -workerpool@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" - integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@8.13.0, ws@^8.8.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== - -ws@^7.4.3: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^2.2.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@17.7.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yarn-install@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yarn-install/-/yarn-install-1.0.0.tgz#57f45050b82efd57182b3973c54aa05cb5d25230" - integrity sha512-VO1u181msinhPcGvQTVMnHVOae8zjX/NSksR17e6eXHRveDvHCF5mGjh9hkN8mzyfnCqcBe42LdTs7bScuTaeg== - dependencies: - cac "^3.0.3" - chalk "^1.1.3" - cross-spawn "^4.0.2" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== - -zip-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" - integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== - dependencies: - archiver-utils "^2.1.0" - compress-commons "^4.1.0" - readable-stream "^3.6.0" From b2d744ec94647d44c21a02c7579e5001e149e4b6 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Tue, 12 Dec 2023 09:17:08 +0000 Subject: [PATCH 344/567] Schemas v3.80.1 (#1279) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c17f470e03..fbd42358a5 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.80.0 +v3.80.1 From b58802a95ec6e0f0441033748170fed924b56bb8 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:01:16 +0000 Subject: [PATCH 345/567] Supplementary data dependencies fix (#1265) * Copy QuestionnaireStoreUpdater into BaseQuestionnaireStoreUpdater * Split methods between store updaters * Capture dependencies when modifying supplementary data * Refactor to avoid is_repeating_answer flag for lists * Combine store updaters for ease of review * Move supplementary data methods to the base QSU * Use supplementary data property for simplification * Move and use location helpers * Refactor remove dependent blocks and capture sections * Use section key for capture dependent section --- app/data_models/questionnaire_store.py | 78 --- app/questionnaire/questionnaire_schema.py | 8 + .../questionnaire_store_updater.py | 379 ++++++++++----- app/routes/session.py | 28 +- app/views/handlers/block.py | 2 +- app/views/handlers/calculation_summary.py | 2 +- app/views/handlers/question.py | 4 +- schemas/test/en/test_supplementary_data.json | 360 ++++++++++++++ tests/app/conftest.py | 129 ++++- tests/app/data_model/conftest.py | 22 +- .../data_model/test_questionnaire_store.py | 147 +----- tests/app/questionnaire/conftest.py | 5 + .../test_questionnaire_store_updater.py | 6 +- .../test_questionnaire_store_updater_base.py | 458 ++++++++++++++++++ .../supplementary_data.spec.js | 120 ++++- tests/integration/routes/test_session.py | 12 +- 16 files changed, 1365 insertions(+), 395 deletions(-) create mode 100644 tests/app/questionnaire/test_questionnaire_store_updater_base.py diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 4a89af045b..18a5f457b3 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -11,7 +11,6 @@ from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.rules.utils import parse_iso_8601_datetime from app.utilities.json import json_dumps, json_loads -from app.utilities.types import SupplementaryDataListMapping if TYPE_CHECKING: from app.storage.encrypted_questionnaire_storage import ( # pragma: no cover @@ -60,83 +59,6 @@ def set_metadata(self, to_set: MutableMapping) -> QuestionnaireStore: return self - def set_supplementary_data(self, to_set: MutableMapping) -> None: - """ - Used to set or update the supplementary data whenever the sds endpoint is called - (Which should be once per session, but only if the sds_dataset_id has changed) - - this updates ListStore to add/update any lists for supplementary data and stores the - identifier -> list_item_id mappings in the supplementary data store to use in the payload at the end - """ - if self._stores.supplementary_data_store.list_mappings: - self._remove_old_supplementary_lists_and_answers(new_data=to_set) - - list_mappings = { - list_name: self._create_supplementary_list( - list_name=list_name, list_data=list_data - ) - for list_name, list_data in to_set.get("items", {}).items() - } - - self._stores.supplementary_data_store = SupplementaryDataStore( - supplementary_data=to_set, list_mappings=list_mappings - ) - - def _create_supplementary_list( - self, *, list_name: str, list_data: list[dict] - ) -> list[SupplementaryDataListMapping]: - """ - Creates or updates a list in ListStore based off supplementary data - returns the identifier -> list_item_id mappings used - """ - list_mappings: list[SupplementaryDataListMapping] = [] - for list_item in list_data: - identifier = list_item["identifier"] - # if any pre-existing supplementary data already has a mapping for this list item - # then its already in the list store and doesn't require adding - if not ( - list_item_id := self._stores.supplementary_data_store.list_lookup.get( - list_name, {} - ).get(identifier) - ): - list_item_id = self._stores.list_store.add_list_item(list_name) - list_mappings.append( - SupplementaryDataListMapping( - identifier=identifier, list_item_id=list_item_id - ) - ) - return list_mappings - - def _remove_old_supplementary_lists_and_answers( - self, new_data: MutableMapping - ) -> None: - """ - In the case that existing supplementary data is being replaced with new data: any list items in the old data - but not the new data are removed from the list store and related answers are deleted - :param new_data - the new supplementary data for comparison - """ - deleted_list_item_ids: set[str] = set() - for ( - list_name, - mappings, - ) in self._stores.supplementary_data_store.list_lookup.items(): - if list_name in new_data.get("items", {}): - new_identifiers = [ - item["identifier"] for item in new_data["items"][list_name] - ] - for identifier, list_item_id in mappings.items(): - if identifier not in new_identifiers: - self._stores.list_store.delete_list_item( - list_name, list_item_id - ) - deleted_list_item_ids.add(list_item_id) - else: - self._stores.list_store.delete_list(list_name) - deleted_list_item_ids.update(mappings.values()) - self._stores.answer_store.remove_all_answers_for_list_item_ids( - *deleted_list_item_ids - ) - def _deserialize(self, data: str) -> None: json_data = json_loads(data) self._stores.progress_store = ProgressStore(json_data.get("PROGRESS")) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 2506bf8b84..04273cc0d0 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -454,6 +454,14 @@ def _populate_answer_and_list_dependencies(self) -> None: self._update_dependencies_for_summary(block) continue + if block["type"] == "ListCollectorContent" and block.get( + "repeating_blocks" + ): + # Editable list collectors don't need this because the add/remove handlers manage revisiting repeating blocks + self._list_dependencies_map[block["for_list"]].add( + self._get_dependent_for_block_id(block_id=block["id"]) + ) + for question in self.get_all_questions_for_block(block): self.update_dependencies_for_dynamic_answers( question=question, block_id=block["id"] diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index ab2ffd243f..84dccf0040 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -1,35 +1,41 @@ from collections import defaultdict from itertools import combinations -from typing import Iterable, Mapping +from typing import Iterable, Mapping, MutableMapping, Sequence from ordered_set import OrderedSet from werkzeug.datastructures import ImmutableDict -from app.data_models import AnswerValueTypes, CompletionStatus, QuestionnaireStore +from app.data_models import ( + AnswerValueTypes, + CompletionStatus, + QuestionnaireStore, + SupplementaryDataStore, +) from app.data_models.answer_store import Answer from app.data_models.relationship_store import RelationshipDict, RelationshipStore from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey from app.questionnaire.questionnaire_schema import Dependent from app.questionnaire.router import Router -from app.utilities.types import DependentSection, LocationType +from app.utilities.types import ( + DependentSection, + LocationType, + SupplementaryDataListMapping, +) -class QuestionnaireStoreUpdater: - """Component responsible for any actions that need to happen as a result of updating the questionnaire_store""" +class QuestionnaireStoreUpdaterBase: + """Component responsible for any actions that need to happen as a result of updating the questionnaire_store + his should only be used over the QuestionnaireStoreUpdater if location is unknown""" EMPTY_ANSWER_VALUES: tuple[None, list, str, dict] = (None, [], "", {}) def __init__( self, - current_location: LocationType, schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, router: Router, - current_question: Mapping | None, ): - self._current_location = current_location - self._current_question = current_question or {} self._schema = schema self._questionnaire_store = questionnaire_store self._answer_store = self._questionnaire_store.data_stores.answer_store @@ -42,6 +48,14 @@ def __init__( ] = defaultdict(set) self.dependent_sections: set[DependentSection] = set() + @property + def _supplementary_data_store(self) -> SupplementaryDataStore: + return self._questionnaire_store.data_stores.supplementary_data_store + + @_supplementary_data_store.setter + def _supplementary_data_store(self, store: SupplementaryDataStore) -> None: + self._questionnaire_store.data_stores.supplementary_data_store = store + def save(self) -> None: if self.is_dirty(): self._questionnaire_store.save() @@ -122,30 +136,11 @@ def remove_answers( for answer_id in answer_ids: self._answer_store.remove_answer(answer_id, list_item_id=list_item_id) - def add_primary_person(self, list_name: str) -> str: - self.remove_completed_relationship_locations_for_list_name(list_name) - - if primary_person := self._list_store[list_name].primary_person: - return primary_person - - # If a primary person was initially answered negatively, then changed to positive, - # the location must be removed from the progress store. - self.remove_completed_location(self._current_location) - - return self._list_store.add_list_item(list_name, primary_person=True) - def add_list_item(self, list_name: str) -> str: new_list_item_id = self._list_store.add_list_item(list_name) self.remove_completed_relationship_locations_for_list_name(list_name) return new_list_item_id - def remove_primary_person(self, list_name: str) -> None: - """Remove the primary person and all of their answers. - Any context for the primary person will be removed - """ - if list_item_id := self._list_store[list_name].primary_person: - self.remove_list_item_data(list_name, list_item_id) - def remove_list_item_data(self, list_name: str, list_item_id: str) -> None: """Remove answers from the answer store, remove list item progress from the progress store and update the list store to remove it. Any related relationship answers are re-evaluated for completeness. @@ -160,6 +155,13 @@ def remove_list_item_data(self, list_name: str, list_item_id: str) -> None: self._progress_store.remove_progress_for_list_item_id(list_item_id=list_item_id) + def remove_list_data(self, list_name: str) -> None: + """Delete entire list and remove any associated answers""" + self._answer_store.remove_all_answers_for_list_item_ids( + *self._list_store[list_name].items + ) + self._list_store.delete_list(list_name) + def capture_dependencies_for_list_change(self, list_name: str) -> None: """ Captures the dependencies when an item is added to or removed from the given list. @@ -170,7 +172,7 @@ def capture_dependencies_for_list_change(self, list_name: str) -> None: list_name ) section_ids.update( - self._schema.list_collector_section_ids_by_list_name[list_name] + self._schema.list_collector_section_ids_by_list_name.get(list_name, set()) ) for section_key in self.started_section_keys(section_ids=section_ids): @@ -235,13 +237,11 @@ def _remove_relationship_answers_for_list_item_id( answer.value = answers_to_keep self._answer_store.add_or_update(answer) - def add_completed_location(self, location: LocationType | None = None) -> None: + def add_completed_location(self, location: LocationType) -> None: if not self._progress_store.is_routing_backwards: - location = location or self._current_location self._progress_store.add_completed_location(location) - def remove_completed_location(self, location: LocationType | None = None) -> bool: - location = location or self._current_location + def remove_completed_location(self, location: LocationType) -> bool: return self._progress_store.remove_completed_location(location) def update_section_status( @@ -286,53 +286,36 @@ def _capture_block_dependencies_for_list(self, list_name: str) -> None: dependencies: set[Dependent] = self._schema.list_dependencies.get( list_name, set() ) - self._capture_block_dependencies(dependencies) + for dependent in dependencies: + list_item_ids = self._get_list_item_ids_for_list_dependency(dependent) + self._capture_block_dependent(dependent, list_item_ids) - def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: - """Captures a unique list of block ids that are dependents of the provided answer id.""" - dependencies: set[Dependent] = self._schema.answer_dependencies.get( - answer_id, set() - ) - is_repeating_answer = self._schema.is_answer_in_repeating_section(answer_id) - self._capture_block_dependencies(dependencies, is_repeating_answer) + def _get_list_item_ids_for_list_dependency( + self, dependency: Dependent + ) -> list[str] | list[None]: + if dependency.for_list: + return self._list_store[dependency.for_list].items + return [None] - def _capture_block_dependencies( - self, dependencies: set[Dependent], is_repeating_answer: bool | None = None + def _capture_block_dependent( + self, dependent: Dependent, list_item_ids: Sequence[str] | Sequence[None] ) -> None: """ - The block_ids are mapped to the section key. Dependencies in a repeating section use the list items - for the repeating list when creating the section key. + The block_id is mapped to the section key. Dependents in a repeating section should be passed in with the list items + for the repeating list for creating the section key. Blocks are captured regardless of whether they are complete. This avoids fetching the completed blocks multiples times, as you may have multiple dependencies for one block which may also apply to each item in the list. However, when updating the progress store, the block ids are checked to ensure they exist in the progress store. """ - for dependency in dependencies: - for list_item_id in self._get_list_item_ids_for_dependency( - dependency, is_repeating_answer - ): - if dependency.answer_id: - self._answer_store.remove_answer( - dependency.answer_id, list_item_id=list_item_id - ) - self.dependent_block_id_by_section_key[ - SectionKey(dependency.section_id, list_item_id) - ].add(dependency.block_id) - - def _get_list_item_ids_for_dependency( - self, dependency: Dependent, is_repeating_answer: bool | None = False - ) -> list[str] | list[None]: - """ - Gets the list item ids that relate to the dependency. If the dependency is repeating, and the dependent is also repeating - then we must be in that repeating section, so the only relevant list item id is the current one. - If the dependent is not repeating, but the dependency is, then all list item ids need including. - """ - if dependency.for_list: - if is_repeating_answer: - # Type ignore: in this scenario the list item id must exist - return [self._current_location.list_item_id] # type: ignore - return self._list_store[dependency.for_list].items - return [None] + for list_item_id in list_item_ids: + if dependent.answer_id: + self._answer_store.remove_answer( + dependent.answer_id, list_item_id=list_item_id + ) + self.dependent_block_id_by_section_key[ + SectionKey(dependent.section_id, list_item_id) + ].add(dependent.block_id) def _capture_section_dependencies_for_answer(self, answer_id: str) -> None: """Captures a unique list of section ids that are dependents of the provided answer id.""" @@ -389,42 +372,6 @@ def _update_section_dependencies(self, dependent_sections: Iterable) -> None: else: self.dependent_sections.add(DependentSection(section_id)) - def update_answers( - self, form_data: Mapping, list_item_id: str | None = None - ) -> None: - list_item_id = list_item_id or self._current_location.list_item_id - answers_by_answer_id = self._schema.get_answers_for_question_by_id( - self._current_question - ) - - for answer_id, answer_value in form_data.items(): - if answer_id not in answers_by_answer_id: - continue - - resolved_answer = answers_by_answer_id[answer_id] - answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id - list_item_id_to_use = resolved_answer.get("list_item_id") or list_item_id - - if self._update_answer(answer_id_to_use, list_item_id_to_use, answer_value): - self._capture_section_dependencies_for_answer(answer_id_to_use) - self._capture_block_dependencies_for_answer(answer_id_to_use) - - if self._answer_store.is_dirty: - self.capture_progress_section_dependencies() - - def capture_progress_section_dependencies(self) -> None: - """ - Captures a unique list of section ids that are dependents of the current section or block, for progress value sources. - """ - self._capture_section_dependencies_progress_value_source_for_section( - self._current_location.section_id - ) - # Type ignore: block id will exist when at any time this is called - self._capture_section_dependencies_progress_value_source_for_block( - section_id=self._current_location.section_id, - block_id=self._current_location.block_id, # type: ignore - ) - def update_progress_for_dependent_sections(self) -> None: """Removes dependent blocks from the progress store and updates the progress to IN_PROGRESS. Section progress is not updated for the current location as it is handled by `handle_post` on block handlers. @@ -526,19 +473,21 @@ def remove_dependent_blocks_and_capture_dependent_sections(self) -> None: ) blocks_removed |= self.remove_completed_location(location) - if blocks_removed and ( - section_id != self._current_location.section_id - or list_item_id != self._current_location.list_item_id - ): - # Since this section key will be marked as incomplete, any `DependentSection` with is_complete as `None` - # can be removed as we do not need to re-evaluate progress as we already know the section would be incomplete. - dependent = DependentSection(section_id, list_item_id) - if dependent in self.dependent_sections: - self.dependent_sections.remove(dependent) - - self.dependent_sections.add( - DependentSection(section_id, list_item_id, is_complete=False) - ) + if blocks_removed: + self._capture_dependent_section(section_key) + + def _capture_dependent_section(self, section_key: SectionKey) -> None: + """ + Since this section key will be marked as incomplete, any `DependentSection` with is_complete as `None` + can be removed as we do not need to re-evaluate progress as we already know the section would be incomplete. + """ + dependent = DependentSection(**section_key.to_dict()) + if dependent in self.dependent_sections: + self.dependent_sections.remove(dependent) + + self.dependent_sections.add( + DependentSection(**section_key.to_dict(), is_complete=False) + ) def started_section_keys( self, section_ids: Iterable[str] | None = None @@ -550,3 +499,193 @@ def _get_chronological_section_dependents(self) -> list: return sorted( self.dependent_sections, key=lambda x: sections.index(x.section_id) ) + + def set_supplementary_data(self, to_set: MutableMapping) -> None: + """ + Used to set or update the supplementary data whenever the sds endpoint is called + (Which should be once per session, but only if the sds_dataset_id has changed) + + this updates ListStore to add/update any lists for supplementary data and stores the + identifier -> list_item_id mappings in the supplementary data store to use in the payload at the end + """ + list_mappings: dict[str, list[SupplementaryDataListMapping]] = {} + modified_lists: set[str] = set() + + if self._supplementary_data_store.list_mappings: + modified_lists |= self._remove_outdated_supplementary_lists_and_answers( + new_data=to_set + ) + + for list_name, list_data in to_set.get("items", {}).items(): + mappings, lists = self._create_supplementary_list( + list_name=list_name, list_data=list_data + ) + list_mappings[list_name] = mappings + modified_lists |= lists + + for list_name in modified_lists: + self.capture_dependencies_for_list_change(list_name) + + self._supplementary_data_store = SupplementaryDataStore( + supplementary_data=to_set, list_mappings=list_mappings + ) + + def _create_supplementary_list( + self, *, list_name: str, list_data: Sequence[dict] + ) -> tuple[list[SupplementaryDataListMapping], set[str]]: + """ + Creates or updates a list in ListStore based off supplementary data + returns the identifier -> list_item_id mappings used and the lists that were modified in the process + """ + list_mappings: list[SupplementaryDataListMapping] = [] + modified_lists: set[str] = set() + for list_item in list_data: + identifier = list_item["identifier"] + # if any pre-existing supplementary data already has a mapping for this list item + # then its already in the list store and doesn't require adding + if not ( + list_item_id := self._supplementary_data_store.list_lookup.get( + list_name, {} + ).get(identifier) + ): + list_item_id = self.add_list_item(list_name) + modified_lists.add(list_name) + list_mappings.append( + SupplementaryDataListMapping( + identifier=identifier, list_item_id=list_item_id + ) + ) + return list_mappings, modified_lists + + def _remove_outdated_supplementary_lists_and_answers( + self, new_data: MutableMapping + ) -> set[str]: + """ + In the case that existing supplementary data is being replaced with new data: any list items in the old data + but not the new data are removed from the list store and related answers are deleted + :param new_data - the new supplementary data for comparison + :return: any lists that were modified by the change in supplementary data + """ + modified_lists: set[str] = set() + for ( + list_name, + mappings, + ) in self._supplementary_data_store.list_lookup.items(): + if list_name in new_data.get("items", {}): + new_identifiers = [ + item["identifier"] for item in new_data["items"][list_name] + ] + for identifier, list_item_id in mappings.items(): + if identifier not in new_identifiers: + modified_lists.add(list_name) + self.remove_list_item_data(list_name, list_item_id) + else: + self.remove_list_data(list_name) + return modified_lists + + +class QuestionnaireStoreUpdater(QuestionnaireStoreUpdaterBase): + """Component responsible for any actions that need to happen as a result of updating the questionnaire_store""" + + def __init__( + self, + current_location: LocationType, + schema: QuestionnaireSchema, + questionnaire_store: QuestionnaireStore, + router: Router, + current_question: Mapping | None, + ): + self._current_location = current_location + self._current_question = current_question or {} + super().__init__( + schema=schema, questionnaire_store=questionnaire_store, router=router + ) + + def add_primary_person(self, list_name: str) -> str: + self.remove_completed_relationship_locations_for_list_name(list_name) + + if primary_person := self._list_store[list_name].primary_person: + return primary_person + + # If a primary person was initially answered negatively, then changed to positive, + # the location must be removed from the progress store. + self.remove_completed_location(self._current_location) + + return self._list_store.add_list_item(list_name, primary_person=True) + + def remove_primary_person(self, list_name: str) -> None: + """Remove the primary person and all of their answers. + Any context for the primary person will be removed + """ + if list_item_id := self._list_store[list_name].primary_person: + self.remove_list_item_data(list_name, list_item_id) + + def _capture_block_dependencies_for_answer(self, answer_id: str) -> None: + """Captures a unique list of block ids that are dependents of the provided answer id.""" + dependencies: set[Dependent] = self._schema.answer_dependencies.get( + answer_id, set() + ) + is_repeating_answer = self._schema.is_answer_in_repeating_section(answer_id) + + for dependent in dependencies: + list_item_ids = self._get_list_item_ids_for_answer_dependency( + dependent, is_repeating_answer + ) + self._capture_block_dependent(dependent, list_item_ids) + + def _get_list_item_ids_for_answer_dependency( + self, dependency: Dependent, is_repeating_answer: bool | None = False + ) -> list[str] | list[None]: + """ + Gets the list item ids that relate to the dependency of the answer. + If the dependency is repeating, and so is the answer, then we must be in that repeating section, + so the only relevant list item id is the current one. + If the answer is not repeating, but the dependency is, then all list item ids need including. + """ + if dependency.for_list: + if is_repeating_answer: + # Type ignore: in this scenario the list item id must exist + return [self._current_location.list_item_id] # type: ignore + return self._list_store[dependency.for_list].items + return [None] + + def update_answers( + self, form_data: Mapping, list_item_id: str | None = None + ) -> None: + list_item_id = list_item_id or self._current_location.list_item_id + answers_by_answer_id = self._schema.get_answers_for_question_by_id( + self._current_question + ) + + for answer_id, answer_value in form_data.items(): + if answer_id not in answers_by_answer_id: + continue + + resolved_answer = answers_by_answer_id[answer_id] + answer_id_to_use = resolved_answer.get("original_answer_id") or answer_id + list_item_id_to_use = resolved_answer.get("list_item_id") or list_item_id + + if self._update_answer(answer_id_to_use, list_item_id_to_use, answer_value): + self._capture_section_dependencies_for_answer(answer_id_to_use) + self._capture_block_dependencies_for_answer(answer_id_to_use) + + if self._answer_store.is_dirty: + self.capture_progress_section_dependencies() + + def capture_progress_section_dependencies(self) -> None: + """ + Captures a unique list of section ids that are dependents of the current section or block, for progress value sources. + """ + self._capture_section_dependencies_progress_value_source_for_section( + self._current_location.section_id + ) + # Type ignore: block id will exist when at any time this is called + self._capture_section_dependencies_progress_value_source_for_block( + section_id=self._current_location.section_id, + block_id=self._current_location.block_id, # type: ignore + ) + + def _capture_dependent_section(self, section_key: SectionKey) -> None: + """Only capture the dependent section if it is not the current one""" + if section_key != self._current_location.section_key: + super()._capture_dependent_section(section_key) diff --git a/app/routes/session.py b/app/routes/session.py index afa25de574..7625ca3f46 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -27,6 +27,8 @@ ) from app.questionnaire import QuestionnaireSchema from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE +from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdaterBase +from app.questionnaire.router import Router from app.routes.errors import _render_error_page from app.services.supplementary_data import get_supplementary_data_v1 from app.utilities.metadata_parser import validate_runner_claims @@ -185,7 +187,31 @@ def _set_questionnaire_supplementary_data( _validate_supplementary_data_lists( supplementary_data=supplementary_data["data"], schema=schema ) - questionnaire_store.set_supplementary_data(supplementary_data["data"]) + _set_supplementary_data( + questionnaire_store=questionnaire_store, + schema=schema, + supplementary_data=supplementary_data["data"], + ) + + +def _set_supplementary_data( + *, + questionnaire_store: QuestionnaireStore, + schema: QuestionnaireSchema, + supplementary_data: dict, +) -> None: + """ + Adds the supplementary data to the questionnaire store which: + 1) removes any old list items and answers + 2) Updates block and section progress to reflect any newly unlocked questions due to supplementary data list changes + """ + router = Router(schema=schema, data_stores=questionnaire_store.data_stores) + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + questionnaire_store=questionnaire_store, schema=schema, router=router + ) + base_questionnaire_store_updater.set_supplementary_data(to_set=supplementary_data) + base_questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() + base_questionnaire_store_updater.update_progress_for_dependent_sections() def _validate_supplementary_data_lists( diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index b46b63cae2..04ccee7c72 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -112,7 +112,7 @@ def get_next_location_url(self) -> str: def handle_post(self) -> None: self._set_started_at_metadata() - self.questionnaire_store_updater.add_completed_location() + self.questionnaire_store_updater.add_completed_location(self._current_location) self.questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() self._update_section_completeness() self.questionnaire_store_updater.update_progress_for_dependent_sections() diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 999cce80bb..604b5c7257 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -26,7 +26,7 @@ def get_context(self) -> dict[str, dict]: def handle_post(self) -> None: # We prematurely set the current as complete, so that dependent sections can be updated accordingly - self.questionnaire_store_updater.add_completed_location() + self.questionnaire_store_updater.add_completed_location(self.current_location) # Then we update dependent sections self.questionnaire_store_updater.capture_progress_section_dependencies() return super().handle_post() diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index 259bf8d4ed..ea6de60cd2 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -201,7 +201,9 @@ def handle_post(self) -> None: if self.questionnaire_store_updater.is_dirty(): # We prematurely complete the block, as we need it completed to build the routing path # In order to support progress value source references of the previous block - self.questionnaire_store_updater.add_completed_location() + self.questionnaire_store_updater.add_completed_location( + self.current_location + ) self._routing_path = self.router.routing_path( self._current_location.section_key ) diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json index 09f6ba27da..03291f04d9 100644 --- a/schemas/test/en/test_supplementary_data.json +++ b/schemas/test/en/test_supplementary_data.json @@ -1054,6 +1054,88 @@ } ] } + }, + { + "id": "conditional-employee-block", + "type": "Question", + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "employees" + } + ] + }, + 3 + ] + } + }, + "question": { + "id": "conditional-employee-question", + "guidance": { + "contents": [ + { + "description": "This block is enabled because there are 3 employees in the supplementary dataset" + } + ] + }, + "type": "General", + "title": { + "text": "Has {employee_name} been promoted since starting at {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "promoted-yes-no-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } } ] } @@ -1192,6 +1274,86 @@ } ] } + }, + { + "id": "conditional-additional-employee-block", + "type": "Question", + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "additional-employees" + } + ] + }, + 3 + ] + } + }, + "question": { + "id": "conditional-additional-employee-question", + "guidance": { + "contents": [ + { + "description": "This block is enabled because there are 3 additional employees" + } + ] + }, + "type": "General", + "title": { + "text": "Has {employee_name} been promoted since starting at {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "employee-first-name" + }, + { + "source": "answers", + "identifier": "employee-last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "additional-promoted-yes-no-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } } ] } @@ -1544,6 +1706,204 @@ ] } ] + }, + { + "id": "section-7", + "title": "Sales targets", + "groups": [ + { + "id": "value-sales-group", + "blocks": [ + { + "id": "product-sales-interstitial", + "type": "Interstitial", + "content": { + "title": "Product value sales", + "contents": [ + { + "guidance": { + "contents": [ + { + "description": "The next block only shows when there are 2 products in the supplementary dataset." + }, + { + "description": "This is to test that section progress updates when swapping between supplementary datasets which remove or add list items" + } + ] + } + } + ] + } + }, + { + "id": "product-question-2-enabled", + "type": "Question", + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "products" + } + ] + }, + 2 + ] + } + }, + "question": { + "id": "product-2-question", + "guidance": { + "contents": [ + { + "description": "This block is enabled because there are 2 products in the supplementary dataset" + } + ] + }, + "type": "General", + "title": { + "text": "Did the total value sales of {value_sales} over the last quarter meet the target?", + "placeholders": [ + { + "placeholder": "value_sales", + "transforms": [ + { + "transform": "format_currency", + "arguments": { + "number": { + "source": "calculated_summary", + "identifier": "calculated-summary-value-sales" + } + } + } + ] + } + ] + }, + "answers": [ + { + "id": "value-yes-no-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ] + } + ] + }, + { + "id": "section-8", + "title": "Production targets", + "groups": [ + { + "id": "volume-produced-group", + "blocks": [ + { + "id": "product-volume-interstitial", + "type": "Interstitial", + "content": { + "title": "Product volume produced", + "contents": [ + { + "guidance": { + "contents": [ + { + "description": "The next block only shows when there are 3 products in the supplementary dataset." + }, + { + "description": "This is to test that section progress updates when swapping between supplementary datasets which remove or add list items" + } + ] + } + } + ] + } + }, + { + "id": "product-question-3-enabled", + "type": "Question", + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "products" + } + ] + }, + 3 + ] + } + }, + "question": { + "id": "product-3-question", + "guidance": { + "contents": [ + { + "description": "This block is enabled because there are 3 products in the supplementary dataset" + } + ] + }, + "type": "General", + "title": { + "text": "Did the total volume produced of {volume_produced} over the last quarter meet the target?", + "placeholders": [ + { + "placeholder": "volume_produced", + "transforms": [ + { + "transform": "format_unit", + "arguments": { + "value": { + "source": "calculated_summary", + "identifier": "calculated-summary-volume-total" + }, + "unit": "mass-kilogram", + "unit_length": "short" + } + } + ] + } + ] + }, + "answers": [ + { + "id": "volume-yes-no-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ] + } + ] } ] } diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 2d8275cadb..077b000f6b 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,5 +1,5 @@ # pylint: disable=redefined-outer-name - +from copy import deepcopy from datetime import datetime, timedelta, timezone from http.client import HTTPMessage @@ -285,6 +285,98 @@ def supplementary_data(): } +@pytest.fixture +def supplementary_data_with_employees(supplementary_data): + copy = deepcopy(supplementary_data) + copy["items"]["employees"] = [ + { + "identifier": "429001", + "personal_details": { + "forename": "Harry", + "surname": "Potter", + "address": { + "postcode": "BS1 1AJ", + "house_number": "12", + "city": "Bristol", + }, + }, + "employment_details": { + "job_title": "Customer assistant", + "start_date": "2020-01-01", + "salary": { + "payroll_number": "54345", + "value": "25000", + "currency": "GBP", + }, + }, + }, + { + "identifier": "529001", + "personal_details": { + "forename": "Bruce", + "surname": "Wayne", + "address": { + "postcode": "BS1 1HJ", + "house_number": "15", + "city": "Bristol", + }, + }, + "employment_details": { + "job_title": "Customer assistant", + "start_date": "2019-03-01", + "salary": { + "payroll_number": "4345", + "value": "27000", + "currency": "GBP", + }, + }, + }, + { + "identifier": "629011", + "personal_details": { + "forename": "Henry", + "surname": "Green", + "address": { + "postcode": "BS1 1HR", + "house_number": "11", + "city": "Bristol", + }, + }, + "employment_details": { + "job_title": "Warehouse operative", + "start_date": "2022-10-01", + "salary": { + "payroll_number": "28379", + "value": "29000", + "currency": "GBP", + }, + }, + }, + { + "identifier": "729011", + "personal_details": { + "forename": "Fourth", + "surname": "Person", + "address": { + "postcode": "BS1 1HO", + "house_number": "14", + "city": "Bristol", + }, + }, + "employment_details": { + "job_title": "Warehouse operative", + "start_date": "2022-11-01", + "salary": { + "payroll_number": "22238379", + "value": "29500", + "currency": "GBP", + }, + }, + }, + ] + return copy + + @pytest.fixture def supplementary_data_list_mappings(): return { @@ -295,6 +387,26 @@ def supplementary_data_list_mappings(): } +@pytest.fixture +def supplementary_data_employee_list_mappings(): + return { + "employees": [ + SupplementaryDataListMapping( + identifier="429001", list_item_id="employee-1" + ), + SupplementaryDataListMapping( + identifier="529001", list_item_id="employee-2" + ), + SupplementaryDataListMapping( + identifier="629011", list_item_id="employee-3" + ), + SupplementaryDataListMapping( + identifier="729011", list_item_id="employee-4" + ), + ], + } + + @pytest.fixture def supplementary_data_store_with_data( supplementary_data, supplementary_data_list_mappings @@ -303,3 +415,18 @@ def supplementary_data_store_with_data( supplementary_data=supplementary_data, list_mappings=supplementary_data_list_mappings, ) + + +@pytest.fixture +def supplementary_data_store_with_employees( + supplementary_data_with_employees, + supplementary_data_list_mappings, + supplementary_data_employee_list_mappings, +): + return SupplementaryDataStore( + supplementary_data=supplementary_data_with_employees, + list_mappings={ + **supplementary_data_list_mappings, + **supplementary_data_employee_list_mappings, + }, + ) diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index d4f9aec115..d5d94b5313 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -2,9 +2,8 @@ from datetime import datetime, timedelta, timezone import pytest -from mock.mock import Mock -from app.data_models import CompletionStatus, ListStore, QuestionnaireStore +from app.data_models import CompletionStatus from app.data_models.answer_store import Answer from app.data_models.progress import ProgressDict from app.data_models.session_store import SessionStore @@ -158,22 +157,3 @@ def app_session_store_encoded(mocker, session_data): store.user_id, store.user_ik, store.pepper ) return store - - -@pytest.fixture -def questionnaire_store_with_supplementary_data( - questionnaire_store, supplementary_data_store_with_data -): - questionnaire_store = QuestionnaireStore(questionnaire_store.storage) - questionnaire_store.data_stores.supplementary_data_store = ( - supplementary_data_store_with_data - ) - questionnaire_store.data_stores.list_store = ListStore( - [{"items": ["item-1", "item-2"], "name": "products"}] - ) - # Mock the identifier generation in list store so the ids are item-1, item-2, ... - # pylint: disable=protected-access - questionnaire_store.data_stores.list_store._generate_identifier = Mock( - side_effect=(f"item-{i}" for i in range(3, 100)) - ) - return questionnaire_store diff --git a/tests/app/data_model/test_questionnaire_store.py b/tests/app/data_model/test_questionnaire_store.py index d051ef7c52..a7a17b5240 100644 --- a/tests/app/data_model/test_questionnaire_store.py +++ b/tests/app/data_model/test_questionnaire_store.py @@ -1,13 +1,12 @@ import pytest -from app.data_models import QuestionnaireStore from app.data_models.answer_store import AnswerStore from app.data_models.metadata_proxy import MetadataProxy from app.data_models.progress_store import ProgressStore +from app.data_models.questionnaire_store import QuestionnaireStore from app.data_models.supplementary_data_store import SupplementaryDataStore from app.questionnaire.location import SectionKey from app.utilities.json import json_dumps, json_loads -from app.utilities.make_immutable import make_immutable @pytest.mark.parametrize( @@ -123,147 +122,3 @@ def test_questionnaire_store_raises_when_writing_to_metadata(questionnaire_store with pytest.raises(TypeError): store.data_stores.metadata["no"] = "writing" - - -class TestQuestionnaireStoreWithSupplementaryData: - store: QuestionnaireStore - - def assert_list_store_data(self, list_name: str, list_item_ids: list[str]): - """Helper function to check that ListStore contains the given list with matching list_item_ids""" - lists = [list_model.name for list_model in self.store.data_stores.list_store] - assert list_name in lists - assert self.store.data_stores.list_store[list_name].items == list_item_ids - - def test_adding_new_supplementary_data( - self, questionnaire_store, supplementary_data - ): - """Tests that adding supplementary data adds supplementary list items to the list store - this test doesn't mock list item ids, and checks that they match those in list_mappings - """ - self.store = QuestionnaireStore(questionnaire_store.storage) - self.store.set_supplementary_data(supplementary_data) - assert "products" in self.store.data_stores.supplementary_data_store.list_lookup - supplementary_list_item_ids = list( - self.store.data_stores.supplementary_data_store.list_lookup[ - "products" - ].values() - ) - # check list mapping ids match list store ids - self.assert_list_store_data("products", supplementary_list_item_ids) - - def test_updating_supplementary_data( - self, questionnaire_store_with_supplementary_data, supplementary_data - ): - """Test that overwriting supplementary data with additional lists/items adds them to the list store - without duplicating any existing data""" - self.store = questionnaire_store_with_supplementary_data - - supplementary_data["items"]["supermarkets"] = [{"identifier": "54321"}] - supplementary_data["items"]["products"].append({"identifier": "12345"}) - self.store.set_supplementary_data(supplementary_data) - - assert ( - self.store.data_stores.supplementary_data_store.list_mappings - == make_immutable( - { - "products": [ - {"identifier": 89929001, "list_item_id": "item-1"}, - {"identifier": "201630601", "list_item_id": "item-2"}, - {"identifier": "12345", "list_item_id": "item-3"}, - ], - "supermarkets": [ - {"identifier": "54321", "list_item_id": "item-4"}, - ], - } - ) - ) - - self.assert_list_store_data("products", ["item-1", "item-2", "item-3"]) - self.assert_list_store_data("supermarkets", ["item-4"]) - - def test_removing_some_supplementary_data( - self, questionnaire_store_with_supplementary_data, supplementary_data - ): - """Tests that if you overwrite existing supplementary data with data that is missing list item ids - or lists, that the list store is updated to remove that data""" - self.store = questionnaire_store_with_supplementary_data - - del supplementary_data["items"]["products"][0] - self.store.set_supplementary_data(supplementary_data) - - # products item-1 should be gone - self.assert_list_store_data("products", ["item-2"]) - - def test_removing_all_supplementary_data( - self, questionnaire_store_with_supplementary_data - ): - """Checks that removing all supplementary data clears out the list store""" - self.store = questionnaire_store_with_supplementary_data - self.store.set_supplementary_data({}) - assert len(list(self.store.data_stores.list_store)) == 0 - - def test_removing_supplementary_lists_with_answers( - self, questionnaire_store_with_supplementary_data, supplementary_data - ): - """Tests that if you overwrite supplementary data, - related answers for old list/list_item_ids are removed from the answer store""" - self.store = questionnaire_store_with_supplementary_data - - # add some answers for the supplementary list items - self.store.data_stores.answer_store = AnswerStore( - [ - { - "answer_id": "product-sales-answer", - "value": "100", - "list_item_id": "item-1", - }, - { - "answer_id": "product-sales-answer", - "value": "200", - "list_item_id": "item-2", - }, - ] - ) - - # delete the first product and update supplementary data - del supplementary_data["items"]["products"][0] - self.store.set_supplementary_data(supplementary_data) - - # item-1 should be gone - self.assert_list_store_data("products", ["item-2"]) - # the answer for it should be too - answers = list(self.store.data_stores.answer_store.answer_map.keys()) - assert len(answers) == 1 - assert answers[0] == ("product-sales-answer", "item-2") - - # remove all answers - self.store.set_supplementary_data({}) - assert not self.store.data_stores.answer_store.answer_map - - def test_removing_supplementary_data_ignores_non_supplementary_data( - self, questionnaire_store_with_supplementary_data - ): - """Tests that removing supplementary data does not affect other lists and answers""" - self.store = questionnaire_store_with_supplementary_data - # unrelated - self.store.data_stores.answer_store = AnswerStore( - [ - { - "answer_id": "unrelated-answer", - "value": "100", - "list_item_id": "JxSW21", - }, - { - "answer_id": "sales", - "value": "200", - }, - ] - ) - self.store.data_stores.list_store.add_list_item("supermarkets") - self.assert_list_store_data("products", ["item-1", "item-2"]) - self.assert_list_store_data("supermarkets", ["item-3"]) - - self.store.set_supplementary_data({}) - self.assert_list_store_data("supermarkets", ["item-3"]) - answers = list(self.store.data_stores.answer_store.answer_map.keys()) - assert answers == [("unrelated-answer", "JxSW21"), ("sales", None)] diff --git a/tests/app/questionnaire/conftest.py b/tests/app/questionnaire/conftest.py index 22ad9938c0..3603df1202 100644 --- a/tests/app/questionnaire/conftest.py +++ b/tests/app/questionnaire/conftest.py @@ -35,6 +35,11 @@ def get_metadata(extra_metadata: dict | None = None): return MetadataProxy.from_dict(metadata) +@pytest.fixture +def supplementary_data_schema(): + return load_schema_from_name("test_supplementary_data") + + @pytest.fixture def placeholder_list(): return [ diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index bd4283db26..b4ca4f52d9 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -1156,7 +1156,7 @@ def test_dependent_sections_completed_dependant_blocks_removed_and_status_update progress_store=progress_store, router=mock_router, ) - dependent_section_key = ("breakdown-section", None) + dependent_section_key = SectionKey("breakdown-section", None) dependent_block_id = "turnover-breakdown-block" questionnaire_store_updater.dependent_block_id_by_section_key = { @@ -1380,7 +1380,7 @@ def test_repeating_dependent_sections_completed_dependant_blocks_removed_and_sta ) questionnaire_store_updater.dependent_block_id_by_section_key = { - ("breakdown-section", list_item): {"turnover-breakdown-block"} + SectionKey("breakdown-section", list_item): {"turnover-breakdown-block"} for list_item in list_store["some-list"] } questionnaire_store_updater.dependent_sections.add( @@ -1453,7 +1453,7 @@ def test_dependent_sections_added_dependant_block_removed( progress_store=progress_store, router=mock_router, ) - dependent_section_key = ("breakdown-section", None) + dependent_section_key = SectionKey("breakdown-section", None) dependent_block_id = "turnover-breakdown-block" questionnaire_store_updater.dependent_block_id_by_section_key = { diff --git a/tests/app/questionnaire/test_questionnaire_store_updater_base.py b/tests/app/questionnaire/test_questionnaire_store_updater_base.py new file mode 100644 index 0000000000..e7acf7f92e --- /dev/null +++ b/tests/app/questionnaire/test_questionnaire_store_updater_base.py @@ -0,0 +1,458 @@ +# pylint: disable=redefined-outer-name +import pytest +from mock import MagicMock, Mock + +from app.data_models import AnswerStore, ListStore, ProgressStore, QuestionnaireStore +from app.data_models.progress import CompletionStatus, ProgressDict +from app.questionnaire import QuestionnaireSchema +from app.questionnaire.questionnaire_store_updater import QuestionnaireStoreUpdaterBase +from app.utilities.make_immutable import make_immutable +from app.utilities.types import DependentSection, SectionKey + + +@pytest.fixture +def questionnaire_store_with_supplementary_data( + fake_questionnaire_store, supplementary_data_store_with_data +): + fake_questionnaire_store.data_stores.supplementary_data_store = ( + supplementary_data_store_with_data + ) + fake_questionnaire_store.data_stores.list_store = ListStore( + [{"items": ["item-1", "item-2"], "name": "products"}] + ) + # Mock the identifier generation in list store so the ids are item-1, item-2, ... + # pylint: disable=protected-access + fake_questionnaire_store.data_stores.list_store._generate_identifier = Mock( + side_effect=(f"item-{i}" for i in range(3, 100)) + ) + return fake_questionnaire_store + + +@pytest.fixture +def questionnaire_store_with_employee_supplementary_data( + fake_questionnaire_store, + supplementary_data_store_with_employees, +): + """Mock questionnaire store with supplementary data of two products and partial progress""" + fake_questionnaire_store.data_stores.supplementary_data_store = ( + supplementary_data_store_with_employees + ) + fake_questionnaire_store.data_stores.list_store = ListStore( + [ + {"items": ["item-1", "item-2"], "name": "products"}, + { + "items": ["employee-1", "employee-2", "employee-3", "employee-4"], + "name": "employees", + }, + ], + ) + fake_questionnaire_store.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="introduction-section", + block_ids=["loaded-successfully-block", "introduction-block"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-2", + block_ids=["list-collector-employees"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-3", + block_ids=["any-additional-employees"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-4", + block_ids=["length-of-employment"], + list_item_id="employee-1", + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-4", + block_ids=["length-of-employment"], + list_item_id="employee-2", + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-6", + block_ids=["product-repeating-block-1"], + status=CompletionStatus.COMPLETED, + list_item_id="item-1", + ), + ProgressDict( + section_id="section-6", + block_ids=["product-repeating-block-1"], + status=CompletionStatus.COMPLETED, + list_item_id="item-2", + ), + ProgressDict( + section_id="section-6", + block_ids=[ + "list-collector-products", + "calculated-summary-volume-sales", + "calculated-summary-volume-total", + "dynamic-products", + "calculated-summary-value-sales", + ], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="section-8", + block_ids=["product-volume-interstitial"], + status=CompletionStatus.COMPLETED, + ), + ] + ) + # Mock the identifier generation in list store so the ids are item-1, item-2, ... + # pylint: disable=protected-access + fake_questionnaire_store.data_stores.list_store._generate_identifier = Mock( + side_effect=(f"item-{i}" for i in range(3, 100)) + ) + return fake_questionnaire_store + + +def test_removing_list_item_data( + supplementary_data_schema, questionnaire_store_with_employee_supplementary_data +): + """ + Tests that if you remove list item data with the base updater + it removes the list item from the list store and removes the progress for any dependents of the list item + """ + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + schema=supplementary_data_schema, + questionnaire_store=questionnaire_store_with_employee_supplementary_data, + router=MagicMock(), + ) + base_questionnaire_store_updater.remove_list_item_data("products", "item-2") + base_questionnaire_store_updater.capture_dependencies_for_list_change("products") + + assert questionnaire_store_with_employee_supplementary_data.data_stores.list_store[ + "products" + ].items == ["item-1"] + # should not contain item-1 since this should be unaffected + assert base_questionnaire_store_updater.dependent_sections == { + DependentSection(section_id="section-6", list_item_id=None), + DependentSection(section_id="section-8", list_item_id=None), + } + # this should affect all blocks in section-6 + assert base_questionnaire_store_updater.dependent_block_id_by_section_key == { + SectionKey(section_id="section-6", list_item_id=None): { + "list-collector-products", + "calculated-summary-value-sales", + "dynamic-products", + "calculated-summary-volume-sales", + "calculated-summary-volume-total", + }, + } + + +@pytest.mark.parametrize( + "section_id,list_item_id,dependent_blocks,expected_blocks", + [ + ( + "section-6", + None, + { + "calculated-summary-volume-sales", + "calculated-summary-volume-total", + }, + [ + "list-collector-products", + "dynamic-products", + "calculated-summary-value-sales", + ], + ), + ( + "section-6", + None, + { + "list-collector-products", + "calculated-summary-value-sales", + "dynamic-products", + "calculated-summary-volume-sales", + "calculated-summary-volume-total", + }, + [], + ), + ( + "section-6", + "item-1", + {"product-repeating-block-1"}, + [], + ), + ( + "section-8", + None, + {"product-volume-interstitial"}, + [], + ), + ], +) +def test_remove_dependent_blocks_and_capture_dependent_sections( + supplementary_data_schema, + questionnaire_store_with_employee_supplementary_data, + section_id, + list_item_id, + dependent_blocks, + expected_blocks, +): + """ + Tests that the progress store is successfully updated when removing captured dependencies + and that the dependent sections are captured correctly + """ + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + schema=supplementary_data_schema, + questionnaire_store=questionnaire_store_with_employee_supplementary_data, + router=MagicMock(), + ) + section_key = SectionKey(section_id=section_id, list_item_id=list_item_id) + base_questionnaire_store_updater.dependent_block_id_by_section_key = { + section_key: dependent_blocks, + } + base_questionnaire_store_updater.remove_dependent_blocks_and_capture_dependent_sections() + assert ( + base_questionnaire_store_updater._progress_store._progress[ # pylint: disable=protected-access + section_key + ].block_ids + == expected_blocks + ) + assert base_questionnaire_store_updater.dependent_sections == { + DependentSection( + section_id=section_id, list_item_id=list_item_id, is_complete=False + ) + } + + +@pytest.mark.parametrize( + "section_id, list_item_id", + [("section-6", None), ("section-6", "item-1"), ("section-8", None)], +) +def test_update_progress_for_dependent_sections( + supplementary_data_schema, + questionnaire_store_with_employee_supplementary_data, + section_id, + list_item_id, +): + """ + Tests that captured dependent sections get set back to in progress + """ + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + schema=supplementary_data_schema, + questionnaire_store=questionnaire_store_with_employee_supplementary_data, + router=MagicMock(), + ) + base_questionnaire_store_updater.dependent_sections = { + dependent_section := DependentSection( + section_id=section_id, list_item_id=list_item_id, is_complete=False + ) + } + base_questionnaire_store_updater.update_progress_for_dependent_sections() + assert ( + base_questionnaire_store_updater._progress_store._progress[ # pylint: disable=protected-access + dependent_section.section_key + ].status + == CompletionStatus.IN_PROGRESS + ) + + +def test_update_progress_of_repeating_dependent( + supplementary_data_schema, + questionnaire_store_with_employee_supplementary_data, +): + """ + Tests that when a data change unlocks a new question within a repeating section + Each repeating section which has been started is captured + """ + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + schema=supplementary_data_schema, + questionnaire_store=questionnaire_store_with_employee_supplementary_data, + router=MagicMock(), + ) + base_questionnaire_store_updater.remove_list_item_data("employees", "employee-4") + base_questionnaire_store_updater.capture_dependencies_for_list_change("employees") + # employee-3 not affected as section had not yet been started + assert base_questionnaire_store_updater.dependent_sections == { + DependentSection(section_id="section-2"), + DependentSection(section_id="section-4", list_item_id="employee-1"), + DependentSection(section_id="section-4", list_item_id="employee-2"), + } + + +def test_update_supplementary_data_list_in_schema_with_no_list_collector( + question_variant_schema, + fake_questionnaire_store, + supplementary_data_with_employees, +): + """ + Tests that updating supplementary data for a schema with no list collectors is safe + """ + base_questionnaire_store_updater = QuestionnaireStoreUpdaterBase( + schema=QuestionnaireSchema(questionnaire_json=question_variant_schema), + questionnaire_store=fake_questionnaire_store, + router=MagicMock(), + ) + base_questionnaire_store_updater.set_supplementary_data( + supplementary_data_with_employees + ) + base_questionnaire_store_updater.remove_list_item_data("products", "product-1") + base_questionnaire_store_updater.capture_dependencies_for_list_change("products") + # supplementary data has nothing to do with given schema + assert base_questionnaire_store_updater.dependent_sections == set() + + +class TestSettingSupplementaryData: + store: QuestionnaireStore + + def get_questionnaire_store_updater_base(self): + return QuestionnaireStoreUpdaterBase( + schema=MagicMock(), + questionnaire_store=self.store, + router=MagicMock(), + ) + + def assert_list_store_data(self, list_name: str, list_item_ids: list[str]): + """Helper function to check that ListStore contains the given list with matching list_item_ids""" + lists = [list_model.name for list_model in self.store.data_stores.list_store] + assert list_name in lists + assert self.store.data_stores.list_store[list_name].items == list_item_ids + + def test_adding_new_supplementary_data( + self, fake_questionnaire_store, supplementary_data + ): + """Tests that adding supplementary data adds supplementary list items to the list store + this test doesn't mock list item ids, and checks that they match those in list_mappings + """ + self.store = fake_questionnaire_store + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + questionnaire_store_updater_base.set_supplementary_data(supplementary_data) + assert "products" in self.store.data_stores.supplementary_data_store.list_lookup + supplementary_list_item_ids = list( + self.store.data_stores.supplementary_data_store.list_lookup[ + "products" + ].values() + ) + # check list mapping ids match list store ids + self.assert_list_store_data("products", supplementary_list_item_ids) + + def test_updating_supplementary_data( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Test that overwriting supplementary data with additional lists/items adds them to the list store + without duplicating any existing data""" + self.store = questionnaire_store_with_supplementary_data + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + + supplementary_data["items"]["supermarkets"] = [{"identifier": "54321"}] + supplementary_data["items"]["products"].append({"identifier": "12345"}) + questionnaire_store_updater_base.set_supplementary_data(supplementary_data) + + assert ( + self.store.data_stores.supplementary_data_store.list_mappings + == make_immutable( + { + "products": [ + {"identifier": 89929001, "list_item_id": "item-1"}, + {"identifier": "201630601", "list_item_id": "item-2"}, + {"identifier": "12345", "list_item_id": "item-3"}, + ], + "supermarkets": [ + {"identifier": "54321", "list_item_id": "item-4"}, + ], + } + ) + ) + + self.assert_list_store_data("products", ["item-1", "item-2", "item-3"]) + self.assert_list_store_data("supermarkets", ["item-4"]) + + def test_removing_some_supplementary_data( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Tests that if you overwrite existing supplementary data with data that is missing list item ids + or lists, that the list store is updated to remove that data""" + self.store = questionnaire_store_with_supplementary_data + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + + del supplementary_data["items"]["products"][0] + questionnaire_store_updater_base.set_supplementary_data(supplementary_data) + + # products item-1 should be gone + self.assert_list_store_data("products", ["item-2"]) + + def test_removing_all_supplementary_data( + self, questionnaire_store_with_supplementary_data + ): + """Checks that removing all supplementary data clears out the list store""" + self.store = questionnaire_store_with_supplementary_data + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + questionnaire_store_updater_base.set_supplementary_data(to_set={}) + assert len(list(self.store.data_stores.list_store)) == 0 + + def test_removing_supplementary_lists_with_answers( + self, questionnaire_store_with_supplementary_data, supplementary_data + ): + """Tests that if you overwrite supplementary data, + related answers for old list/list_item_ids are removed from the answer store""" + self.store = questionnaire_store_with_supplementary_data + # add some answers for the supplementary list items + self.store.data_stores.answer_store = AnswerStore( + [ + { + "answer_id": "product-sales-answer", + "value": "100", + "list_item_id": "item-1", + }, + { + "answer_id": "product-sales-answer", + "value": "200", + "list_item_id": "item-2", + }, + ] + ) + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + + # delete the first product and update supplementary data + del supplementary_data["items"]["products"][0] + questionnaire_store_updater_base.set_supplementary_data(supplementary_data) + + # item-1 should be gone + self.assert_list_store_data("products", ["item-2"]) + # the answer for it should be too + answers = list(self.store.data_stores.answer_store.answer_map.keys()) + assert len(answers) == 1 + assert answers[0] == ("product-sales-answer", "item-2") + + # remove all answers + questionnaire_store_updater_base.set_supplementary_data({}) + assert not self.store.data_stores.answer_store.answer_map + + def test_removing_supplementary_data_ignores_non_supplementary_data( + self, questionnaire_store_with_supplementary_data + ): + """Tests that removing supplementary data does not affect other lists and answers""" + self.store = questionnaire_store_with_supplementary_data + questionnaire_store_updater_base = self.get_questionnaire_store_updater_base() + # unrelated + self.store.data_stores.answer_store = AnswerStore( + [ + { + "answer_id": "unrelated-answer", + "value": "100", + "list_item_id": "JxSW21", + }, + { + "answer_id": "sales", + "value": "200", + }, + ] + ) + self.store.data_stores.list_store.add_list_item("supermarkets") + self.assert_list_store_data("products", ["item-1", "item-2"]) + self.assert_list_store_data("supermarkets", ["item-3"]) + + questionnaire_store_updater_base.set_supplementary_data({}) + self.assert_list_store_data("supermarkets", ["item-3"]) + answers = list(self.store.data_stores.answer_store.answer_map.keys()) + assert answers == [("unrelated-answer", "JxSW21"), ("sales", None)] diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 3781fee978..671207d24d 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -1,3 +1,6 @@ +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; +import { expect } from "@wdio/globals"; +import { getRandomString } from "../../../jwt_helper"; import AddAdditionalEmployeePage from "../../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; import AdditionalLengthOfEmploymentPage from "../../../generated_pages/supplementary_data/additional-length-of-employment.page.js"; import AnyAdditionalEmployeesPage from "../../../generated_pages/supplementary_data/any-additional-employees.page.js"; @@ -15,7 +18,10 @@ import ListCollectorEmployeesPage from "../../../generated_pages/supplementary_d import ListCollectorProductsPage from "../../../generated_pages/supplementary_data/list-collector-products.page.js"; import LoadedSuccessfullyBlockPage from "../../../generated_pages/supplementary_data/loaded-successfully-block.page.js"; import NewEmailPage from "../../../generated_pages/supplementary_data/new-email.page.js"; +import ProductQuestion3EnabledPage from "../../../generated_pages/supplementary_data/product-question-3-enabled.page"; import ProductRepeatingBlock1Page from "../../../generated_pages/supplementary_data/product-repeating-block-1-repeating-block.page.js"; +import ProductSalesInterstitialPage from "../../../generated_pages/supplementary_data/product-sales-interstitial.page"; +import ProductVolumeInterstitialPage from "../../../generated_pages/supplementary_data/product-volume-interstitial.page"; import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; @@ -26,9 +32,6 @@ import Section6Page from "../../../generated_pages/supplementary_data/section-6- import ThankYouPage from "../../../base_pages/thank-you.page"; import TradingPage from "../../../generated_pages/supplementary_data/trading.page.js"; import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page.js"; -import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, click } from "../../../helpers"; -import { getRandomString } from "../../../jwt_helper"; -import { expect } from "@wdio/globals"; describe("Using supplementary data", () => { const responseId = getRandomString(16); @@ -58,7 +61,7 @@ describe("Using supplementary data", () => { await click(EmailBlockPage.submit()); }); - it("Given I have dynamic answer options based of a supplementary date value, When I reach the block on trading start date, Then I see a correct list of options to choose from", async () => { + it("Given I have dynamic answer options based off a supplementary date value, When I reach the block using them, Then I see a correct list of options to choose from", async () => { await expect(await $(TradingPage.answerLabelByIndex(0)).getText()).toBe("Thursday 27 November 1947"); await expect(await $(TradingPage.answerLabelByIndex(1)).getText()).toBe("Friday 28 November 1947"); await expect(await $(TradingPage.answerLabelByIndex(2)).getText()).toBe("Saturday 29 November 1947"); @@ -70,14 +73,14 @@ describe("Using supplementary data", () => { await click(TradingPage.submit()); }); - it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { + it("Given I have a calculated question validated against a supplementary data value, When I enter a breakdown that exceeds the total, Then I see an error message", async () => { await $(SalesBreakdownBlockPage.salesBristol()).setValue(333000); await $(SalesBreakdownBlockPage.salesLondon()).setValue(444000); await click(SalesBreakdownBlockPage.submit()); await expect(await $(SalesBreakdownBlockPage.errorNumber(1)).getText()).toContain("Enter answers that add up to or are less than 555,000"); }); - it("Given I have answers with a sum validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { + it("Given I have a calculated question validated against a supplementary data value, When I enter a breakdown less than the total, Then I see a calculated summary page with the sum of my previous answers", async () => { await $(SalesBreakdownBlockPage.salesLondon()).setValue(111000); await click(SalesBreakdownBlockPage.submit()); await expect(await $(CalculatedSummarySalesPage.calculatedSummaryTitle()).getText()).toBe( @@ -114,7 +117,7 @@ describe("Using supplementary data", () => { await expect(await $(Section1Page.salesLondonAnswer()).getText()).toBe("£111,000.00"); }); - it("Given I change the email for the company, When I return to the interstitial block, Then I see the email has updated", async () => { + it("Given I add an answer used in a first non empty item transform with supplementary data, When I return to the interstitial block, Then I see the transform value has updated", async () => { await $(Section1Page.sameEmailAnswerEdit()).click(); await $(EmailBlockPage.no()).click(); await click(EmailBlockPage.submit()); @@ -126,14 +129,14 @@ describe("Using supplementary data", () => { await click(Section1Page.submit()); }); - it("Given I have a list collector block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { + it("Given I have a list collector content block using a supplementary list, When I start the section, I see the supplementary list items in the list", async () => { await click(HubPage.submit()); await expect(await $(ListCollectorEmployeesPage.listLabel(1)).getText()).toBe("Harry Potter"); await expect(await $(ListCollectorEmployeesPage.listLabel(2)).getText()).toBe("Clark Kent"); await click(ListCollectorEmployeesPage.submit()); }); - it("Given I add some additional employees via a list collector, When I return to the Hub, Then I see new enabled sections for the supplementary list items, and my added ones", async () => { + it("Given I add some additional employees via another list collector, When I return to the Hub, Then I see new enabled sections for the supplementary list items and my added ones", async () => { await click(HubPage.submit()); await $(AnyAdditionalEmployeesPage.yes()).click(); await click(AnyAdditionalEmployeesPage.submit()); @@ -175,7 +178,7 @@ describe("Using supplementary data", () => { await expect(await $(Section4Page.employmentStart()).getText()).toBe("1 January 1990"); }); - it("Given I complete the repeating section for the other supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { + it("Given I complete the repeating section for another supplementary item, When I reach the summary page, Then I see the correct supplementary data with my answers", async () => { await click(Section4Page.submit()); await click(HubPage.submit()); await expect(await $(LengthOfEmploymentPage.questionTitle()).getText()).toContain("When did Clark Kent start working for Tesco?"); @@ -305,7 +308,7 @@ describe("Using supplementary data", () => { await click(CalculatedSummaryValueSalesPage.submit()); }); - it("Given I have a section summary for product details, When I reach the summary page, Then I see the supplementary data and my answers rendered correctly", async () => { + it("Given I have a section with repeating answers for a supplementary list, When I reach the section summary page, Then I see the supplementary data and my answers rendered correctly", async () => { await expect(await $$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); await assertSummaryItems([ "Articles and equipment for sports or outdoor games", @@ -320,9 +323,17 @@ describe("Using supplementary data", () => { ]); await assertSummaryValues(["100 kg", "200 kg", "50 kg", "300 kg", "£110.00", "£220.00", "£330.00"]); await click(Section6Page.submit()); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); + }); + + it("Given I am using a supplementary dataset where the size of one of the lists skips a question in a section, When I enter the section, Then I only see an interstitial block as the other block is skipped", async () => { + await $(HubPage.summaryRowLink("section-8")).click(); + await expect(browser).toHaveUrlContaining(ProductVolumeInterstitialPage.pageName); + await click(ProductVolumeInterstitialPage.submit()); + await expect(await $(HubPage.summaryRowState("section-8")).getText()).toBe("Completed"); }); - it("Given I relaunch the survey for a newer version of the supplementary data, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not the old ones", async () => { + it("Given I relaunch the survey with new supplementary data and new list items for the repeating section, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not any old ones", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", @@ -336,10 +347,58 @@ describe("Using supplementary data", () => { await expect(await $(HubPage.summaryRowState("section-4-2")).getText()).toBe("Not started"); await expect(await $(HubPage.summaryRowState("section-5-1")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowState("section-5-2")).getText()).toBe("Completed"); - await expect(await $("body").getText()).not.toBe("Clark Kent"); + await expect(await $("body").getText()).not.toContain("Clark Kent"); + }); + + it("Given the survey has been relaunched with new data and more items in the products list, When I am on the Hub, Then I see the products section and section with a new block due to the product list size are both in progress", async () => { + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-8")).getText()).toBe("Partially completed"); + }); + + it("Given I am using a supplementary dataset with a product list size that skips a question in the sales target section, When I enter the section, Then I only see an interstitial block", async () => { + await $(HubPage.summaryRowLink("section-7")).click(); + await expect(browser).toHaveUrlContaining(ProductSalesInterstitialPage.pageName); + await click(ProductSalesInterstitialPage.submit()); + await expect(await $(HubPage.summaryRowState("section-7")).getText()).toBe("Completed"); + }); + + it("Given there is now an additional product, When I resume the Product Details Section, Then I start from the list collector content block and see the new product is incomplete", async () => { + await $(HubPage.summaryRowLink("section-6")).click(); + await expect(browser).toHaveUrlContaining(ListCollectorProductsPage.pageName); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + await click(ListCollectorProductsPage.submit()); + await expect(browser).toHaveUrlContaining(ProductRepeatingBlock1Page.pageName); }); - it("Given I now have a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { + it("Given I complete the section and relaunch with the old data that has fewer items in the products list, When I am on the Hub, Then I see the products section and sales targets sections are now in progress", async () => { + await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(40); + await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(50); + await click(ProductRepeatingBlock1Page.submit()); + await click(ListCollectorProductsPage.submit()); + await click(CalculatedSummaryVolumeSalesPage.submit()); + await click(CalculatedSummaryVolumeTotalPage.submit()); + await $$(DynamicProductsPage.inputs())[2].setValue(115); + await click(DynamicProductsPage.submit()); + await click(CalculatedSummaryValueSalesPage.submit()); + await click(Section6Page.submit()); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); + await browser.openQuestionnaire("test_supplementary_data.json", { + version: "v2", + sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + responseId, + }); + await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("section-7")).getText()).toBe("Partially completed"); + }); + + it("Given I return to the new data resulting in a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { + await browser.openQuestionnaire("test_supplementary_data.json", { + version: "v2", + sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", + responseId, + }); await click(HubPage.submit()); await $(LengthOfEmploymentPage.day()).setValue(10); await $(LengthOfEmploymentPage.month()).setValue(10); @@ -351,10 +410,34 @@ describe("Using supplementary data", () => { }); it("Given I can view my response after submission, When I submit the survey, Then I see the values I've entered and correct rendering with supplementary data", async () => { + await click(HubPage.submit()); + await click(ListCollectorProductsPage.submit()); + await $(ProductRepeatingBlock1Page.productVolumeSales()).setValue(40); + await $(ProductRepeatingBlock1Page.productVolumeTotal()).setValue(50); + await click(ProductRepeatingBlock1Page.submit()); + await click(ListCollectorProductsPage.submit()); + await click(CalculatedSummaryVolumeSalesPage.submit()); + await click(CalculatedSummaryVolumeTotalPage.submit()); + await $$(DynamicProductsPage.inputs())[2].setValue(115); + await click(DynamicProductsPage.submit()); + await click(CalculatedSummaryValueSalesPage.submit()); + await click(Section6Page.submit()); + await click(HubPage.submit()); + await $(ProductQuestion3EnabledPage.yes()).click(); + await click(ProductQuestion3EnabledPage.submit()); await click(HubPage.submit()); await $(ThankYouPage.savePrintAnswersLink()).click(); - await assertSummaryTitles(["Company Details", "Additional Employees", "Harry Potter", "Bruce Wayne", "Jane Doe", "John Smith", "Product details"]); + await assertSummaryTitles([ + "Company Details", + "Additional Employees", + "Harry Potter", + "Bruce Wayne", + "Jane Doe", + "John Smith", + "Product details", + "Production Targets", + ]); // Company details await expect(await $(ViewSubmittedResponsePage.emailQuestion()).getText()).toBe("Is contact@lidl.org still the correct contact email for Lidl?"); @@ -416,6 +499,11 @@ describe("Using supplementary data", () => { ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).toBe("50 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).toBe("300 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[6].getText()).toBe("Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[7].getText()).toBe("Volume of sales for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[8].getText()).toBe("Total volume produced for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[4].getText()).toBe("40 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[5].getText()).toBe("50 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).toBe( "Value of sales for Articles and equipment for sports or outdoor games", @@ -425,7 +513,7 @@ describe("Using supplementary data", () => { await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[3].getText()).toBe("Value of sales from other categories"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[0].getText()).toBe("£110.00"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[1].getText()).toBe("£220.00"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[2].getText()).toBe("No answer provided"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[2].getText()).toBe("£115.00"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryValues)[3].getText()).toBe("£330.00"); }); }); diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 626f324a31..345b36ad31 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -122,7 +122,7 @@ def test_patch_session_expiry_extends_session(self): @patch("app.routes.session.get_supplementary_data_v1") @patch("app.routes.session._validate_supplementary_data_lists") @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( self, @@ -139,7 +139,7 @@ def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( @patch("app.routes.session.get_supplementary_data_v1") @patch("app.routes.session._validate_supplementary_data_lists") @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_is_reloaded_when_changed_sds_dataset_id_in_metadata( self, @@ -161,7 +161,7 @@ def test_supplementary_data_is_reloaded_when_changed_sds_dataset_id_in_metadata( @patch("app.routes.session.get_supplementary_data_v1") @patch("app.routes.session._validate_supplementary_data_lists") @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_is_not_reloaded_when_same_sds_dataset_id_in_metadata( self, @@ -231,7 +231,7 @@ def test_supplementary_data_raises_500_error_when_missing_supplementary_data_key @patch("app.routes.session.get_supplementary_data_v1") @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data", + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_raises_500_error_when_missing_required_lists( self, mock_set, mock_get @@ -245,7 +245,7 @@ def test_supplementary_data_raises_500_error_when_missing_required_lists( @patch("app.routes.session.get_supplementary_data_v1") @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data", + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_is_loaded_when_all_required_lists_present( self, mock_set, mock_get @@ -266,7 +266,7 @@ def test_supplementary_data_is_loaded_when_all_required_lists_present( ], ) @patch( - "app.data_models.questionnaire_store.QuestionnaireStore.set_supplementary_data" + "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) def test_supplementary_data_raises_500_error_when_survey_becomes_invalid_for_same_dataset( self, From 4eef48ebc330f1f3e9ef6919b8d2e870766ee048 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 14 Dec 2023 09:26:30 +0000 Subject: [PATCH 346/567] Remove check letter from SDS identifier (#1280) --- app/helpers/metadata_helpers.py | 3 ++ app/routes/session.py | 9 +++++- app/submitter/converter.py | 2 +- app/survey_config/business_config.py | 3 +- app/views/handlers/feedback.py | 2 +- tests/app/conftest.py | 2 +- tests/app/data_model/test_metadata_proxy.py | 4 +-- .../test_supplementary_data_store.py | 6 ++-- tests/app/forms/test_field_factory.py | 2 +- tests/app/helpers/test_template_helpers.py | 4 +-- tests/app/parser/conftest.py | 2 +- .../parser/test_supplementary_data_parser.py | 30 +++++++++---------- tests/app/services/conftest.py | 4 +-- .../test_request_supplementary_data.py | 16 +++++----- tests/app/submitter/conftest.py | 4 +-- .../submitter/test_convert_payload_0_0_3.py | 2 +- tests/app/views/contexts/conftest.py | 2 +- tests/app/views/handlers/conftest.py | 2 +- .../views/handlers/test_submission_handler.py | 4 +-- tests/functional/jwt_helper.js | 4 +-- tests/functional/wdio.conf.js | 2 +- tests/integration/create_token.py | 6 ++-- .../introduction/test_introduction.py | 4 +-- tests/integration/routes/test_dump.py | 6 ++-- tests/integration/routes/test_errors.py | 2 +- .../routes/test_jwt_authentication.py | 2 +- tests/integration/routes/test_session.py | 9 +++++- tests/integration/test_flush_data.py | 4 +-- 28 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 app/helpers/metadata_helpers.py diff --git a/app/helpers/metadata_helpers.py b/app/helpers/metadata_helpers.py new file mode 100644 index 0000000000..e4eba3bcb7 --- /dev/null +++ b/app/helpers/metadata_helpers.py @@ -0,0 +1,3 @@ +def get_ru_ref_without_check_letter(ru_ref: str) -> str: + """Return the ru_ref without the check-letter""" + return ru_ref[:11] diff --git a/app/routes/session.py b/app/routes/session.py index 7625ca3f46..b0afb58dff 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -20,6 +20,7 @@ from app.data_models import QuestionnaireStore from app.data_models.metadata_proxy import MetadataProxy from app.globals import get_session_store, get_session_timeout_in_seconds +from app.helpers.metadata_helpers import get_ru_ref_without_check_letter from app.helpers.template_helpers import ( DATA_LAYER_KEYS, get_survey_config, @@ -173,10 +174,16 @@ def _set_questionnaire_supplementary_data( ) return + identifier = ( + get_ru_ref_without_check_letter(metadata["ru_ref"]) + if metadata["ru_ref"] + else metadata["qid"] + ) + supplementary_data = get_supplementary_data_v1( # Type ignore: survey_id and either ru_ref or qid are required for schemas that use supplementary data dataset_id=new_sds_dataset_id, - identifier=metadata["ru_ref"] or metadata["qid"], # type: ignore + identifier=identifier, # type: ignore survey_id=metadata["survey_id"], # type: ignore ) logger.info( diff --git a/app/submitter/converter.py b/app/submitter/converter.py index 98bef71b6c..05840442a6 100644 --- a/app/submitter/converter.py +++ b/app/submitter/converter.py @@ -45,7 +45,7 @@ def convert_answers( 'channel': 'RH', 'metadata': { 'user_id': '789473423', - 'ru_ref': '432423423423' + 'ru_ref': '12345678901A' }, 'data': [ ... diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index 8019845eea..a4c3362ed1 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -5,6 +5,7 @@ from flask_babel import lazy_gettext +from app.helpers.metadata_helpers import get_ru_ref_without_check_letter from app.settings import read_file from app.survey_config.link import HeaderLink, Link from app.survey_config.survey_config import SurveyConfig @@ -39,7 +40,7 @@ def _get_account_service_help_url( # The ru_ref currently is concatenated with the check letter. Which upstream currently do not support. # The first 11 characters represents the reporting unit reference. # The 12th character is the check letter identifier. - "ru_ref": ru_ref[:11], + "ru_ref": get_ru_ref_without_check_letter(ru_ref), } return f"{self.base_url}/surveys/surveys-help?{urlencode(request_data)}" diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index cc21fc7319..3df91eb85a 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -226,7 +226,7 @@ class FeedbackPayload: "metadata": { "ref_period_end_date": "2021-03-29", "ref_period_start_date": "2021-03-01", - "ru_ref": "11110000022H", + "ru_ref": "12345678901A", "user_id": "d98d78eb-d23a-494d-b67c-e770399de383" }, "origin": "uk.gov.ons.edc.eq", diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 077b000f6b..1ddb2999d3 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -250,7 +250,7 @@ def mocked_make_request_with_timeout( def supplementary_data(): return { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", "note": { "title": "Volume of total production", "example": { diff --git a/tests/app/data_model/test_metadata_proxy.py b/tests/app/data_model/test_metadata_proxy.py index 93c5cd603c..2964a88c49 100644 --- a/tests/app/data_model/test_metadata_proxy.py +++ b/tests/app/data_model/test_metadata_proxy.py @@ -6,7 +6,7 @@ METADATA_V1 = { "schema_name": "1_0000", - "ru_ref": "432423423423", + "ru_ref": "12345678901A", "response_id": "1", "account_service_url": "account_service_url", "tx_id": "tx_id", @@ -26,7 +26,7 @@ "response_expires_at": "2023-04-24T10:46:32+00:00", "survey_metadata": { "data": { - "ru_ref": "432423423423", + "ru_ref": "12345678901A", }, }, } diff --git a/tests/app/data_model/test_supplementary_data_store.py b/tests/app/data_model/test_supplementary_data_store.py index 8cb7421e29..5491e9795a 100644 --- a/tests/app/data_model/test_supplementary_data_store.py +++ b/tests/app/data_model/test_supplementary_data_store.py @@ -22,7 +22,7 @@ def test_supplementary_data_serialisation( def test_supplementary_data_deserialisation(): raw_data = { - "identifier": "12346789012A", + "identifier": "12345678901", "items": { "products": [ {"identifier": 89929001}, @@ -47,7 +47,7 @@ def test_supplementary_data_deserialisation(): assert deserialized.raw_data == make_immutable(raw_data) assert deserialized.list_mappings == make_immutable(list_mappings) assert deserialized._data_map == { # pylint: disable=protected-access - ("identifier", None): "12346789012A", + ("identifier", None): "12345678901", ("products", "item-1"): {"identifier": 89929001}, ("products", "item-2"): {"identifier": "201630601"}, } @@ -63,7 +63,7 @@ def test_empty_supplementary_data_deserialisation(): @pytest.mark.parametrize( "identifier,list_item_id,selectors,expected", [ - ("identifier", None, None, "12346789012A"), + ("identifier", None, None, "12345678901"), ("note", None, ["title"], "Volume of total production"), ("products", "item-2", ["name"], "Other Minerals"), ("products", "item-1", ["value_sales", "answer_code"], "89929001"), diff --git a/tests/app/forms/test_field_factory.py b/tests/app/forms/test_field_factory.py index 8b0b125fbb..e8c7ca5906 100644 --- a/tests/app/forms/test_field_factory.py +++ b/tests/app/forms/test_field_factory.py @@ -26,7 +26,7 @@ def test_invalid_field_type_raises_on_invalid(): "period_str": "2016-01-01", "ref_p_start_date": "2016-02-02", "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", + "ru_ref": "12345678901A", "ru_name": "Apple", "return_by": "2016-07-07", "case_id": "1234567890", diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 54bf7bddf2..26680414bf 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -443,7 +443,7 @@ def test_header_context(app: Flask, theme, survey_title, survey_config, expected "itemsList": [ { "title": "Help", - "url": f"{ACCOUNT_SERVICE_BASE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=63782964754", + "url": f"{ACCOUNT_SERVICE_BASE_URL}/surveys/surveys-help?survey_ref=001&ru_ref=12345678901", "id": "header-link-help", }, { @@ -480,7 +480,7 @@ def test_service_links_context( if is_authenticated: mocker.patch( "app.helpers.template_helpers.get_metadata", - return_value=get_metadata({"ru_ref": "63782964754U", "tx_id": "tx_id"}), + return_value=get_metadata({"ru_ref": "12345678901A", "tx_id": "tx_id"}), ) result = ContextHelper( diff --git a/tests/app/parser/conftest.py b/tests/app/parser/conftest.py index 2adca52851..ad5e59b783 100644 --- a/tests/app/parser/conftest.py +++ b/tests/app/parser/conftest.py @@ -105,7 +105,7 @@ def fake_metadata_full_v2_business(): "ru_name": "Apple", "return_by": "2016-07-07", "case_ref": "1000000000000001", - "ru_ref": "123456789", + "ru_ref": "12345678901A", "form_type": "I", "response_expires_at": get_response_expires_at(), } diff --git a/tests/app/parser/test_supplementary_data_parser.py b/tests/app/parser/test_supplementary_data_parser.py index 693fb8a89c..cac6f0f75f 100644 --- a/tests/app/parser/test_supplementary_data_parser.py +++ b/tests/app/parser/test_supplementary_data_parser.py @@ -11,7 +11,7 @@ "survey_id": "123", "data": { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", "items": { "local_units": [ { @@ -47,7 +47,7 @@ def test_invalid_supplementary_data_payload_raises_error(): validate_supplementary_data( supplementary_data={}, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -58,7 +58,7 @@ def test_validate_supplementary_data_payload(): validated_payload = validate_supplementary_data_v1( supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -70,7 +70,7 @@ def test_validate_supplementary_data_payload_incorrect_dataset_id(): validate_supplementary_data_v1( supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, dataset_id="331507ca-1039-4624-a342-7cbc3630e217", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -85,7 +85,7 @@ def test_validate_supplementary_data_payload_incorrect_survey_id(): validate_supplementary_data_v1( supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="234", ) @@ -116,14 +116,14 @@ def test_supplementary_data_payload_with_no_items_is_validated(): "survey_id": "123", "data": { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", }, } validated_payload = validate_supplementary_data_v1( supplementary_data=payload, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -135,7 +135,7 @@ def test_validate_supplementary_data_payload_missing_survey_id(): "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", "data": { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", }, } @@ -143,7 +143,7 @@ def test_validate_supplementary_data_payload_missing_survey_id(): validate_supplementary_data_v1( supplementary_data=payload, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -157,14 +157,14 @@ def test_validate_supplementary_data_payload_with_unknown_field(): "some_field": "value", "data": { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", }, } validated_payload = validate_supplementary_data_v1( supplementary_data=payload, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -178,7 +178,7 @@ def test_validate_supplementary_data_invalid_schema_version(): "some_field": "value", "data": { "schema_version": "v2", - "identifier": "12346789012A", + "identifier": "12345678901", }, } @@ -186,7 +186,7 @@ def test_validate_supplementary_data_invalid_schema_version(): validate_supplementary_data_v1( supplementary_data=payload, dataset_id="001", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -201,7 +201,7 @@ def test_validate_supplementary_data_payload_missing_identifier_in_items(): validate_supplementary_data_v1( supplementary_data=payload, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -217,7 +217,7 @@ def test_validate_supplementary_data_payload_invalid_identifier(invalid_identifi validate_supplementary_data_v1( supplementary_data=payload, dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) diff --git a/tests/app/services/conftest.py b/tests/app/services/conftest.py index e6357d380f..8fa7f85adf 100644 --- a/tests/app/services/conftest.py +++ b/tests/app/services/conftest.py @@ -8,7 +8,7 @@ def decrypted_mock_supplementary_data_payload(): "survey_id": "123", "data": { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", "items": { "local_units": [ { @@ -45,7 +45,7 @@ def encrypted_mock_supplementary_data_payload(): "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", "survey_id": "123", # pylint: disable-next=line-too-long - "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJkZjg4ZmRhZDI2MTJhZTFlODA1NzExMjBlNmM2MzcxZjU1ODk2Njk2In0.HXHbiTFa2XCuaKfSX4HfONLAVfZxEZo6_072A-ilMwhQKVhmQzqhDOX9dRID1yeoJnYxcdVGRDPwD5QvVHY2qsIOGkO3Brh59GHvSVASSCR21od7DBvlv0LoCDGAVcPt5bwwMbmziGQNkyfNnZG1EwAFk09lWfXwapJDsKbu1uhbW-F8HOOFZT7vR3paUmeTS0ekITwod-eZTD-B7KwUnDhJSm12cl9ob8MHirCaTE3jB64iQe_GvgdSUs1n5HZnX1f7rDQEpWm0OeuxPbEDrFmU-9wBV6LAjszypxPhQ0pv76TMu-VhjNBgf5Xm0cMO8ycmubdBdyWZiVUcmG0or9Mw0QvDYA9th27RChZPs5Le0w9oTnp5p5qzQexpPdzcS9Niqwabwx-NyUvTYkkFWc9poGNtrane4Ei4AFlV1-nQ4A8wMSIuOOSteYVombw-FEo9FhIhnuU8qyHoy0EaW5D1PMcdsphSb2ybTsiEJfdwwpxWDiuMDUpW6c2It_uSEpLCFHj51pQ8_Ez44gDyRE41NpSVvOB6h-nOggICQKggBDimtAuOyunZ5jhQWlKAeX4vAwM4iUEJ82c9lLTb8EDmQDOj6os9PCmyrZku5FHRXXDDvyuQAEqB-ARRnWT47LZOmaeTn4RI92qcejOqTNSG80mCUMY1PPu1fqxOiG8.5ukQQc5IT8fHgMRJ.jDXFWkIV9PIIrAC6EnVu5joGNdlmM52xgEqXCocUvZflvT8xMNocGGDUD1S0_FbQlpSP8FJcby4-B88yzxLgwil7mBqGtE7kWjALPGgnV9DsjEs-OblFFUIY9dVaKMTwaXhSxpjEt4j9NUOo-uyKOQIg-ZT745_zbkFZhlScuHz0u1YYaZGN04Md-eDI38erkTrS3pe2aovjbrwc9s_FMbrHEKlnuAUwxGPUxLTJXpTavHdmhhWriaI6a9ymng6mcefKqpNfWXc-3MIY2k10JCoo2KlVyYRN0up6T4OW56mAoXf7dUNSC8PtcZ5J3rpJ4XUnLZNVSL6Kz9BAmXY5SpZlU9zIBxHsirG9fS8gV6bI_MeMwbMawMCylcArlINVGVwDnUIvNXIJplVgm1OTHL0TiZzEFvnqkro3mzvVL-ElFeYO0uegqN8cDGkcv_lpAHH33j-IPdG-8CYerTCSVrT9.vWWMrdJFFLTVBSiv3KhbiQ", + "data": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJraWQiOiJkZjg4ZmRhZDI2MTJhZTFlODA1NzExMjBlNmM2MzcxZjU1ODk2Njk2In0.tK1Duhk7FmvMn7X6QaSvrpx5U0wOzOyzFwOaLUksCBrPI2J7Tnz1mRMoQla-ekuG-B0DLaQUWA74vgN44UhWbLd7fYbIwjt9TCgdfVyP7P9tOebe55xEHsbvMmRf7F1F37KjPXXk7usDkFuuzl_fjhLwmrjNn90YA93QLy1PbGiMcC8JFYDwLL-vaWslB-VGgKEdud4LCY80xvwxzxxHYtpEQ2NpBXt9zpodwQCFqn8LHHGN80h8_-TE2gOInWccRd7GsyzvH5hGb-wxmgodJQFrPse4v6VBgTJbixMFvxulZqAyYULzU7LiFGSQZqrhqqL4-UXWRmeJX5aYR9AtzO2kGl6gVDZ2d0jGLuHwOSiJ9CLtvltKU8ai0ZfN-qUY6ZnV2lZ7wq_fZrat2-VdxE-ktMy6owGBD3DwHcd_QwAPlDFDEJeAS1G4JKuyfCb_siM5wLd1_HV_6kG2IcyAtt0usRj2D36N2K2lufJjJJlDh2QJm6r5H1pGci8AR7YCJ7nAbmlyMJ7RLJ65TswcvWT7A8jQnVBZT2NCL3_WZ9o2cGj9HixzATzVPkP54LWPTgDI-Q0-nLaBfkHFcR9-6v9f_DFW_zcj2yFfu93HRSc4GSjGs7x0dZkpY-JLFqcNzxcEeUMODUgeE9QZAIF2VsS7NcodbpqJJlOBbevJeZU.Z6Gte9FcRQfo3vos.kwL0nw0aZj6hYugPWt5oa4j-SwdBhDl-ynd6UDtqf1WFDLZcS_DBaXXT2tB7vh_zHNJaMhkJfZV4iT1jr6Wat8pDhL4c_IrclxMLagU22dST6aZcCBkVtvVUCB5dt0AO64NUBysiDf8nTnJyzLnC-pMXvc1QXSS5OMdrFdLPm3jaTAT7y1NYNtIQyRrLZj7PayRQK5w5JS3clq3BSn780rECVxM4FLuDsO0hft-feDKW700Xe4HAeXqaLwADM3bUXMeriiX2CvdTkBB2gv2uBQdR6FIVwGPDbTvXHf1bvTCLmaIT-Ki3pDh_BIrZhgg9ZO8Pft5ZEWWeQR0kF-2eglkro90AWSzUFrQO5YsYsQ-YdjoajeyyiQTg1zW3LyGM3F7-EDKrAZzUgiUhK9UC1Lfheg7Owg7aG-gYN3N6UU5ngnXegcJtNEnoULOLnck0aLS7Ku0v19tw4ObGPW28KZo.Ewzo-AyJbBMCWPqlhjhhAw", } diff --git a/tests/app/services/test_request_supplementary_data.py b/tests/app/services/test_request_supplementary_data.py index 98546735db..7efe05f690 100644 --- a/tests/app/services/test_request_supplementary_data.py +++ b/tests/app/services/test_request_supplementary_data.py @@ -38,7 +38,7 @@ def test_get_supplementary_data_v1_200( ) loaded_supplementary_data = get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -66,7 +66,7 @@ def test_get_supplementary_data_v1_non_200( with pytest.raises(SupplementaryDataRequestFailed) as exc: get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -82,7 +82,7 @@ def test_get_supplementary_data_v1_request_failed(app: Flask): with pytest.raises(SupplementaryDataRequestFailed) as exc: get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -105,7 +105,7 @@ def test_get_supplementary_data_v1_retries_timeout_error( try: supplementary_data = get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) except SupplementaryDataRequestFailed: @@ -137,7 +137,7 @@ def test_get_supplementary_data_v1_retries_transient_error( try: supplementary_data = get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) except SupplementaryDataRequestFailed: @@ -163,7 +163,7 @@ def test_get_supplementary_data_v1_max_retries(app: Flask, mocker): with pytest.raises(SupplementaryDataRequestFailed) as exc: get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -219,7 +219,7 @@ def test_get_supplementary_data_v1_raises_missing_supplementary_data_key_error_w with pytest.raises(MissingSupplementaryDataKey): get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) @@ -246,7 +246,7 @@ def test_get_supplementary_data_v1_with_gcp_authentication( get_supplementary_data_v1( dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", - identifier="12346789012A", + identifier="12345678901", survey_id="123", ) mock_oidc_service.get_credentials.assert_called_once_with( diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index b8cda9bfde..599a8b0e15 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -28,7 +28,7 @@ "period_str": "2016-01-01", "ref_p_start_date": "2016-02-02", "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", + "ru_ref": "12345678901A", "response_id": "1234567890123456", "ru_name": "Apple", "return_by": "2016-07-07", @@ -56,7 +56,7 @@ "period_str": "2016-01-01", "ref_p_start_date": "2016-02-02", "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", + "ru_ref": "12345678901A", "ru_name": "Apple", "case_type": "SPG", "form_type": "I", diff --git a/tests/app/submitter/test_convert_payload_0_0_3.py b/tests/app/submitter/test_convert_payload_0_0_3.py index e7d46ced03..8c1db11040 100644 --- a/tests/app/submitter/test_convert_payload_0_0_3.py +++ b/tests/app/submitter/test_convert_payload_0_0_3.py @@ -1581,7 +1581,7 @@ def test_payload_supplementary_data(): supplementary_data = { "schema_version": "v1", - "identifier": "12346789012A", + "identifier": "12345678901", "note": {"title": "supermarket test survey", "description": "test data"}, "items": { "supermarkets": [ diff --git a/tests/app/views/contexts/conftest.py b/tests/app/views/contexts/conftest.py index 0b2724df81..6384ba8b35 100644 --- a/tests/app/views/contexts/conftest.py +++ b/tests/app/views/contexts/conftest.py @@ -487,7 +487,7 @@ def questionnaire_store(): "ref_p_end_date": "2016-03-03", "display_address": "68 Abingdon Road, Goathill", "trad_as": "ESSENTIAL ENTERPRISE LTD.", - "ru_ref": "12346789012A", + "ru_ref": "12345678901A", } store.data_stores.response_metadata = { diff --git a/tests/app/views/handlers/conftest.py b/tests/app/views/handlers/conftest.py index 88ad452438..97ada5adcd 100644 --- a/tests/app/views/handlers/conftest.py +++ b/tests/app/views/handlers/conftest.py @@ -21,7 +21,7 @@ period_id = "2016-02-01" ref_p_start_date = "2016-02-02" ref_p_end_date = "2016-03-03" -ru_ref = "432423423423" +ru_ref = "12345678901A" ru_name = "ru_name" user_id = "789473423" schema_name = "1_0000" diff --git a/tests/app/views/handlers/test_submission_handler.py b/tests/app/views/handlers/test_submission_handler.py index 6ce4431ffa..976f8eb5d0 100644 --- a/tests/app/views/handlers/test_submission_handler.py +++ b/tests/app/views/handlers/test_submission_handler.py @@ -99,7 +99,7 @@ def test_submission_payload_structure_v1( "data": {"answers": [], "lists": []}, "flushed": False, "launch_language_code": "en", - "metadata": {"ru_ref": "432423423423", "user_id": "789473423"}, + "metadata": {"ru_ref": "12345678901A", "user_id": "789473423"}, "origin": "uk.gov.ons.edc.eq", "submission_language_code": "cy", "submitted_at": datetime.now(timezone.utc).isoformat(), @@ -150,7 +150,7 @@ def test_submission_payload_structure_v2( "period_str": "2016-01-01", "ref_p_start_date": "2016-02-02", "ref_p_end_date": "2016-03-03", - "ru_ref": "432423423423", + "ru_ref": "12345678901A", "ru_name": "ru_name", "case_type": "I", "form_type": "I", diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index d05713f379..bef8ebb323 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -69,7 +69,7 @@ export function generateToken( surveyId = "123", periodId = "201605", periodStr = "May 2016", - ruRef = "12346789012A", + ruRef = "12345678901A", sdsDatasetId = null, regionCode = "GB-ENG", languageCode = "en", @@ -122,7 +122,7 @@ export function generateToken( exp, user_id: userId, case_id: caseId, - ru_ref: "12346789012A", + ru_ref: "12345678901A", response_id: responseId, ru_name: "Apple", trad_as: "Apple", diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 0f0432c0a4..431b985827 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -209,7 +209,7 @@ export const config = { surveyId = "123", periodId = "201605", periodStr = "May 2016", - ruRef = "12346789012A", + ruRef = "12345678901A", sdsDatasetId = null, region = "GB-ENG", language = "en", diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index e696bf18f0..159e9cb133 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -15,7 +15,7 @@ "period_str": "April 2016", "period_id": "201604", "collection_exercise_sid": "789", - "ru_ref": "123456789012A", + "ru_ref": "12345678901A", "response_id": "1234567890123456", "ru_name": "Integration Testing", "ref_p_start_date": "2016-04-01", @@ -36,7 +36,7 @@ "user_id": "integration-test", "period_str": "April 2016", "period_id": "201604", - "ru_ref": "123456789012A", + "ru_ref": "12345678901A", "ru_name": "Integration Testing", "ref_p_start_date": "2016-04-01", "ref_p_end_date": "2016-04-30", @@ -59,7 +59,7 @@ "user_id": "integration-test", "period_str": "April 2016", "period_id": "201604", - "ru_ref": "123456789012A", + "ru_ref": "12345678901A", "ru_name": "Integration Testing", "ref_p_start_date": "2016-04-01", "ref_p_end_date": "2016-04-30", diff --git a/tests/integration/introduction/test_introduction.py b/tests/integration/introduction/test_introduction.py index 990e635cca..676f3b76eb 100644 --- a/tests/integration/introduction/test_introduction.py +++ b/tests/integration/introduction/test_introduction.py @@ -10,7 +10,7 @@ def test_mail_link_contains_ru_ref_in_subject(self): # When on the introduction page # Then the email link is present with the ru_ref in the subject - self.assertRegexPage(r'"mailto\:.+\?subject\=.+123456789012A"') + self.assertRegexPage(r'"mailto\:.+\?subject\=.+12345678901A"') def test_intro_description_displayed(self): # Given survey containing intro description @@ -82,5 +82,5 @@ def test_contact_links(self): self.launchSurvey("test_introduction") self.assertInBody( 'If the company details or structure have changed contact us on 0300 1234 931 ' - 'or email surveys@ons.gov.uk' + 'or email surveys@ons.gov.uk' ) diff --git a/tests/integration/routes/test_dump.py b/tests/integration/routes/test_dump.py index c0668425d7..883b90a41a 100644 --- a/tests/integration/routes/test_dump.py +++ b/tests/integration/routes/test_dump.py @@ -89,7 +89,7 @@ def test_dump_submission_authenticated_with_role_no_answers(self): "schema_name": "test_radio_mandatory_with_detail_answer_mandatory", }, "data": {"answers": [], "lists": []}, - "metadata": {"ru_ref": "123456789012A", "user_id": "integration-test"}, + "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, "launch_language_code": "en", "submission_language_code": "en", } @@ -137,7 +137,7 @@ def test_dump_submission_authenticated_with_role_with_answers(self): ], "lists": [], }, - "metadata": {"ru_ref": "123456789012A", "user_id": "integration-test"}, + "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, "launch_language_code": "en", "submission_language_code": "en", } @@ -205,7 +205,7 @@ def test_dump_submission_authenticated_with_role_with_lists(self): } ], }, - "metadata": {"ru_ref": "123456789012A", "user_id": "integration-test"}, + "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, "launch_language_code": "en", "submission_language_code": "en", } diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 4d5d2502ac..a230be9c95 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -21,7 +21,7 @@ class TestErrors(IntegrationTestCase): # pylint: disable=too-many-public-method "period_str": "April 2016", "period_id": "201604", "collection_exercise_sid": "789", - "ru_ref": "123456789012A", + "ru_ref": "12345678901A", "response_id": "1234567890123456", "ru_name": "Integration Testing", "ref_p_start_date": "2016-04-01", diff --git a/tests/integration/routes/test_jwt_authentication.py b/tests/integration/routes/test_jwt_authentication.py index 0d1a9eff39..f0683e3cfc 100644 --- a/tests/integration/routes/test_jwt_authentication.py +++ b/tests/integration/routes/test_jwt_authentication.py @@ -81,7 +81,7 @@ def create_payload(): "collection_exercise_sid": "sid", "ref_p_start_date": "2016-01-01", "ref_p_end_date": "2016-09-01", - "ru_ref": "1234", + "ru_ref": "12345678901A", "response_id": response_id, "ru_name": "Test", "return_by": "2016-09-09", diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 345b36ad31..f0b92ba1af 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -7,11 +7,13 @@ from mock.mock import patch from sdc.crypto.key_store import KeyStore +from app.helpers.metadata_helpers import get_ru_ref_without_check_letter from app.questionnaire.questionnaire_schema import DEFAULT_LANGUAGE_CODE from app.services.supplementary_data import SupplementaryDataRequestFailed from app.settings import ACCOUNT_SERVICE_BASE_URL, ACCOUNT_SERVICE_BASE_URL_SOCIAL from app.utilities.json import json_loads from tests.app.services.test_request_supplementary_data import TEST_SDS_URL +from tests.integration.create_token import PAYLOAD_V2_SUPPLEMENTARY_DATA from tests.integration.integration_test_case import ( EQ_SUBMISSION_SDX_PRIVATE_KEY, EQ_SUBMISSION_SR_PRIVATE_SIGNING_KEY, @@ -124,7 +126,7 @@ def test_patch_session_expiry_extends_session(self): @patch( "app.questionnaire.questionnaire_store_updater.QuestionnaireStoreUpdaterBase.set_supplementary_data", ) - def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( + def test_supplementary_data_is_loaded_with_correct_identifier_when_new_sds_dataset_id_in_metadata( self, mock_set, mock_validate, @@ -136,6 +138,11 @@ def test_supplementary_data_is_loaded_when_new_sds_dataset_id_in_metadata( mock_set.assert_called_once() mock_validate.assert_called_once() + used_identifier = mock_get.call_args.kwargs["identifier"] + ru_ref = PAYLOAD_V2_SUPPLEMENTARY_DATA["survey_metadata"]["data"]["ru_ref"] + assert used_identifier == get_ru_ref_without_check_letter(ru_ref) + assert used_identifier != ru_ref + @patch("app.routes.session.get_supplementary_data_v1") @patch("app.routes.session._validate_supplementary_data_lists") @patch( diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index ff9ad7001b..1069acff31 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -158,7 +158,7 @@ def test_flush_data_successful_v1( "schema_name": "test_textfield", "period": "201605", }, - "metadata": {"user_id": "UNKNOWN", "ru_ref": "12346789012A"}, + "metadata": {"user_id": "UNKNOWN", "ru_ref": "12345678901A"}, "launch_language_code": "en", "data": { "answers": [{"answer_id": "name-answer", "value": "sdfsdaf"}], @@ -197,7 +197,7 @@ def test_flush_data_successful_v2( "period_id": "201605", "ru_name": "ESSENTIAL ENTERPRISE LTD.", "user_id": "UNKNOWN", - "ru_ref": "12346789012A", + "ru_ref": "12345678901A", }, "data": { "answers": [{"answer_id": "name-answer", "value": "sdfsdf"}], From 5e954000650fd76fd23b71f2584816826b581fed Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:07:37 +0000 Subject: [PATCH 347/567] List collector content progress tests (#1278) * List collector content progress tests --- app/views/handlers/list_remove_question.py | 1 + .../spec/list_collector_content.spec.js | 167 +++++++++++------- 2 files changed, 106 insertions(+), 62 deletions(-) diff --git a/app/views/handlers/list_remove_question.py b/app/views/handlers/list_remove_question.py index d29207bd13..8ba7c59efa 100644 --- a/app/views/handlers/list_remove_question.py +++ b/app/views/handlers/list_remove_question.py @@ -32,6 +32,7 @@ def handle_post(self) -> None: list_name, self._current_location.list_item_id, # type: ignore ) + # This will result in any list collector content blocks using this list to require revisiting. This is currently the expected behaviour. self.questionnaire_store_updater.capture_dependencies_for_list_change( list_name ) diff --git a/tests/functional/spec/list_collector_content.spec.js b/tests/functional/spec/list_collector_content.spec.js index 705f803af1..5c211c89fb 100644 --- a/tests/functional/spec/list_collector_content.spec.js +++ b/tests/functional/spec/list_collector_content.spec.js @@ -1,5 +1,7 @@ import AnyOtherCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches.page.js"; import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches-add.page.js"; +import AnyCompaniesOrBranchesRemovePage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches-remove.page.js"; + import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-companies-or-branches.page"; import CompaniesSummaryPage from "../generated_pages/list_collector_content_page/section-companies-summary.page"; import HubPage from "../base_pages/hub.page"; @@ -8,6 +10,8 @@ import ListCollectorFirstRepeatingBlockPage from "../generated_pages/list_collec import ListCollectorSecondRepeatingBlockPage from "../generated_pages/list_collector_content_page/companies-repeating-block-2-repeating-block.page"; import ListCollectorContentPage from "../generated_pages/list_collector_content_page/list-collector-content.page"; import ListCollectorContentSectionSummaryPage from "../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; +import ConfirmationCheckboxPage from "../generated_pages/list_collector_content_page/confirmation-checkbox.page"; +import { checkListItemComplete, checkListItemIncomplete, click } from "../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -18,16 +22,16 @@ describe("List Collector Section Summary and Summary Items", () => { await fillInListCollectorSection(); await expect(browser).toHaveUrlContaining(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Not started"); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); - await $(ResponsiblePartyQuestionPage.submit()).click(); + await click(ResponsiblePartyQuestionPage.submit()); await expect(browser).toHaveUrlContaining(ListCollectorContentPage.url()); }); it("When I get to the list collector content page, Then the relevant content and button is displayed.", async () => { await fillInListCollectorSection(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); - await $(ResponsiblePartyQuestionPage.submit()).click(); + await click(ResponsiblePartyQuestionPage.submit()); await expect(await $(ListCollectorContentPage.heading()).getHTML()).toContain("Companies"); await expect(await $("#main-content > p").getText()).toBe( "You have previously reported the following companies. Press continue to updated registration and trading information.", @@ -38,87 +42,126 @@ describe("List Collector Section Summary and Summary Items", () => { }); it("When I get to list collector content block section, Then I should be able to complete repeating blocks and get to the summary.", async () => { await fillInListCollectorSection(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); - await $(ResponsiblePartyQuestionPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); - await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); - await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); - await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(456); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); - await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); + await click(ResponsiblePartyQuestionPage.submit()); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(123, 1, 1, 1990, true, true); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(456, 1, 1, 1990, true, true); + await click(ListCollectorContentPage.submit()); await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.url()); }); it("When I fill in first item repeating blocks, Then after going back to the hub the section should be in progress.", async () => { await fillInListCollectorSection(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); - await $(ResponsiblePartyQuestionPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); - await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); - await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); + await click(ResponsiblePartyQuestionPage.submit()); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(123, 1, 1, 1990, true, true); await $(ListCollectorContentPage.previous()).click(); await $(ResponsiblePartyQuestionPage.previous()).click(); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); }); it("When I fill in both items repeating blocks, Then after going back to the hub the section should be completed.", async () => { await fillInListCollectorSection(); - await $(HubPage.submit()).click(); + await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); - await $(ResponsiblePartyQuestionPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); - await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(123); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); - await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); - await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(456); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(1); - await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(1990); - await $(ListCollectorFirstRepeatingBlockPage.submit()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); - await $(ListCollectorSecondRepeatingBlockPage.submit()).click(); - await $(ListCollectorContentPage.submit()).click(); + await click(ResponsiblePartyQuestionPage.submit()); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(123, 1, 1, 1990, true, true); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(456, 1, 1, 1990, true, true); + await click(ListCollectorContentPage.submit()); await $(ListCollectorContentSectionSummaryPage.previous()).click(); await $(ListCollectorContentPage.previous()).click(); await $(ResponsiblePartyQuestionPage.previous()).click(); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); }); + it("When I complete both sections then add another item, The list collector content block reverts to in progress and the new repeating blocks need completing", async () => { + await completeBothSections(); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); + await $(HubPage.summaryRowLink("section-companies")).click(); + await expect(browser).toHaveUrlContaining(CompaniesSummaryPage.pageName); + await $(CompaniesSummaryPage.companiesListAddLink()).click(); + await addCompany("Company C", "789", false); + await anyMoreCompaniesNo(); + await $(ConfirmationCheckboxPage.yes()).click(); + await click(ConfirmationCheckboxPage.submit()); + await click(CompaniesSummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(ListCollectorContentPage.pageName); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + await click(ListCollectorContentPage.submit()); + await expect(browser).toHaveUrlContaining(ListCollectorFirstRepeatingBlockPage.pageName); + await completeRepeatingBlocks(666, 2, 5, 1995, true, true); + await checkListItemComplete(`dt[data-qa="list-item-3-label"]`); + await click(ListCollectorContentPage.submit()); + await click(ListCollectorContentSectionSummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); + }); + // :TODO: Currently, this is expected behaviour, if list collector content blocks no longer need revisiting after removing items, this test needs updating. + it("When I complete both sections then remove a list item item, Then the list collector content block reverts to in progress the list summary is revisited", async () => { + await completeBothSections(); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); + await $(HubPage.summaryRowLink("section-companies")).click(); + await $(CompaniesSummaryPage.companiesListRemoveLink(1)).click(); + await $(AnyCompaniesOrBranchesRemovePage.yes()).click(); + await click(AnyCompaniesOrBranchesRemovePage.submit()); + await click(CompaniesSummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); + await click(HubPage.submit()); + await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await click(ListCollectorContentPage.submit()); + await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.pageName); + await click(ListCollectorContentSectionSummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); + }); }); }); const fillInListCollectorSection = async () => { await $(AnyCompaniesOrBranchesPage.yes()).click(); - await $(AnyCompaniesOrBranchesPage.submit()).click(); + await click(AnyCompaniesOrBranchesPage.submit()); await addCompany("Company A", "123", true); await anyMoreCompaniesYes(); await addCompany("Company B", "456", true); await anyMoreCompaniesNo(); - await $(CompaniesSummaryPage.submit()).click(); + await click(CompaniesSummaryPage.submit()); +}; + +const completeBothSections = async () => { + await fillInListCollectorSection(); + await click(HubPage.submit()); + await $(ResponsiblePartyQuestionPage.yes()).click(); + await click(ResponsiblePartyQuestionPage.submit()); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(654, 2, 6, 1999, true, true); + await click(ListCollectorContentPage.submit()); + await completeRepeatingBlocks(655, 12, 1, 1989, true, false); + await click(ListCollectorContentPage.submit()); + await click(ListCollectorContentSectionSummaryPage.submit()); +}; + +const completeRepeatingBlocks = async (registrationNumber, day, month, year, authorisedUk, authorisedEu) => { + await $(ListCollectorFirstRepeatingBlockPage.registrationNumberRepeatingBlock()).setValue(registrationNumber); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockday()).setValue(day); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockmonth()).setValue(month); + await $(ListCollectorFirstRepeatingBlockPage.registrationDateRepeatingBlockyear()).setValue(year); + await click(ListCollectorFirstRepeatingBlockPage.submit()); + if (authorisedUk) { + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockYes()).click(); + } else { + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderUkRadioRepeatingBlockNo()).click(); + } + if (authorisedEu) { + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockYes()).click(); + } else { + await $(ListCollectorSecondRepeatingBlockPage.authorisedTraderEuRadioRepeatingBlockNo()).click(); + } + await click(ListCollectorSecondRepeatingBlockPage.submit()); }; const addCompany = async (name, number, authorised) => { await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue(name); @@ -128,15 +171,15 @@ const addCompany = async (name, number, authorised) => { } else { await $(AnyCompaniesOrBranchesAddPage.authorisedInsurerRadioNo()).click(); } - await $(AnyCompaniesOrBranchesAddPage.submit()).click(); + await click(AnyCompaniesOrBranchesAddPage.submit()); }; const anyMoreCompaniesYes = async () => { await $(AnyOtherCompaniesOrBranchesPage.yes()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); }; const anyMoreCompaniesNo = async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); - await $(AnyOtherCompaniesOrBranchesPage.submit()).click(); + await click(AnyOtherCompaniesOrBranchesPage.submit()); }; From d0d2a6c849e252d39aae88a795dc0b2c859f1ea5 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:53:32 +0000 Subject: [PATCH 348/567] Dockerise Mock CIR (#1275) * Dockerise mock CIR * Functionally test CIR --- Makefile | 1 + README.md | 10 ++++++++-- docker-compose-dev-linux.yml | 9 +++++++++ docker-compose-dev-mac.yml | 9 +++++++++ docker-compose.yml | 9 +++++++++ tests/functional/jwt_helper.js | 14 ++++++++++---- tests/functional/spec/launch_with_cir.spec.js | 19 +++++++++++++++++++ tests/functional/wdio.conf.js | 2 ++ 8 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/functional/spec/launch_with_cir.spec.js diff --git a/Makefile b/Makefile index 9d07aa392b..9f01ddaae9 100644 --- a/Makefile +++ b/Makefile @@ -102,6 +102,7 @@ run-uwsgi-async: link-development-env dev-compose-up: docker-compose -f docker-compose-dev-mac.yml pull eq-questionnaire-launcher docker-compose -f docker-compose-dev-mac.yml pull sds + docker-compose -f docker-compose-dev-mac.yml pull cir docker-compose -f docker-compose-dev-mac.yml up -d dev-compose-up-linux: diff --git a/README.md b/README.md index 46d35903c5..b4aa4585a5 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ make run ### Supporting services -Runner requires four supporting services - a questionnaire launcher, a storage backend, a cache and the supplementary data service. +Runner requires five supporting services - a questionnaire launcher, a storage backend, a cache, the supplementary data service and the collection instrument registry. #### Run supporting services with Docker @@ -145,7 +145,7 @@ make dev-compose-up-linux ##### [Questionnaire launcher](https://github.com/ONSDigital/eq-questionnaire-launcher) ``` shell -docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 -e SDS_API_BASE_URL=http://docker.for.mac.host.internal:5003 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest +docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 -e SDS_API_BASE_URL=http://docker.for.mac.host.internal:5003 -e CIR_API_BASE_URL=http://docker.for.mac.host.internal:5004 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest ``` ##### [Mock Supplementary data service](https://github.com/ONSDigital/eq-runner-mock-sds) @@ -154,6 +154,12 @@ docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 docker run -it -p 5003:5003 onsdigital/eq-runner-mock-sds:latest ``` +##### [Mock Collection Instrument Registry](https://github.com/ONSDigital/eq-runner-mock-cir) + +``` shell +docker run -it -p 5004:5004 onsdigital/eq-runner-mock-cir:latest +``` + ##### Storage backends [DynamoDB](https://github.com/ONSDigital/eq-docker-dynamodb) diff --git a/docker-compose-dev-linux.yml b/docker-compose-dev-linux.yml index 00e6b81d18..5e40871664 100644 --- a/docker-compose-dev-linux.yml +++ b/docker-compose-dev-linux.yml @@ -21,6 +21,7 @@ services: SURVEY_RUNNER_URL: http://localhost:5000 SURVEY_RUNNER_SCHEMA_URL: http://host.docker.internal:5000 SDS_API_BASE_URL: http://host.docker.internal:5003 + CIR_API_BASE_URL: http://host.docker.internal:5004 networks: - eq-env restart: always @@ -35,6 +36,14 @@ services: ports: - "5003:5003" + cir: + image: onsdigital/eq-runner-mock-cir:latest + networks: + - eq-env + restart: always + ports: + - "5004:5004" + networks: eq-env: driver: bridge diff --git a/docker-compose-dev-mac.yml b/docker-compose-dev-mac.yml index 536df1aee3..ae4d85612d 100644 --- a/docker-compose-dev-mac.yml +++ b/docker-compose-dev-mac.yml @@ -21,6 +21,7 @@ services: SURVEY_RUNNER_URL: http://localhost:5000 SURVEY_RUNNER_SCHEMA_URL: http://docker.for.mac.host.internal:5000 SDS_API_BASE_URL: http://docker.for.mac.host.internal:5003 + CIR_API_BASE_URL: http://docker.for.mac.host.internal:5004 networks: - eq-env restart: always @@ -35,6 +36,14 @@ services: ports: - "5003:5003" + cir: + image: "onsdigital/eq-runner-mock-cir:latest" + networks: + - eq-env + restart: always + ports: + - "5004:5004" + networks: eq-env: driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index a74ade875e..9cc816b7ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,6 +21,7 @@ services: DATASTORE_EMULATOR_HOST: datastore:8432 EQ_REDIS_HOST: "redis" SDS_API_BASE_URL: http://sds:5003 + CIR_API_BASE_URL: http://cir:5004 restart: always depends_on: - datastore @@ -52,6 +53,14 @@ services: ports: - "5003:5003" + cir: + image: onsdigital/eq-runner-mock-cir:latest + networks: + - eq-env + restart: always + ports: + - "5004:5004" + networks: eq-env: diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index bef8ebb323..8cdd04c27f 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -75,10 +75,16 @@ export function generateToken( languageCode = "en", includeLogoutUrl = true, displayAddress = "", + cirInstrumentId = null, }, ) { - const schemaParts = schemaRegEx.exec(schema); - + let schemaParams = {}; + if (schema) { + const schemaParts = schemaRegEx.exec(schema); + schemaParams = { schema_name: `${schemaParts[1]}_${schemaParts[2]}` }; + } else if (cirInstrumentId) { + schemaParams = { cir_instrument_id: cirInstrumentId }; + } // Header const oHeader = { alg: "RS256", @@ -105,7 +111,7 @@ export function generateToken( exp, case_id: caseId, response_id: responseId, - schema_name: `${schemaParts[1]}_${schemaParts[2]}`, + ...schemaParams, collection_exercise_sid: collectionId, region_code: regionCode, language_code: languageCode, @@ -126,7 +132,7 @@ export function generateToken( response_id: responseId, ru_name: "Apple", trad_as: "Apple", - schema_name: `${schemaParts[1]}_${schemaParts[2]}`, + ...schemaParams, collection_exercise_sid: collectionId, period_id: periodId, period_str: periodStr, diff --git a/tests/functional/spec/launch_with_cir.spec.js b/tests/functional/spec/launch_with_cir.spec.js new file mode 100644 index 0000000000..166efcb73e --- /dev/null +++ b/tests/functional/spec/launch_with_cir.spec.js @@ -0,0 +1,19 @@ +import { expect } from "@wdio/globals"; +import { click } from "../helpers"; +import NameBlockPage from "../generated_pages/textfield/name-block.page.js"; +import HubPage from "../base_pages/hub.page"; +import ThankYouPage from "../base_pages/thank-you.page"; + +describe("Launch a survey from the collection instrument registry", () => { + it("Given I retrieve a Collection Instrument, When I Launch, Then I am able to complete the survey as normal", async () => { + await browser.openQuestionnaire(null, { + version: "v2", + cirInstrumentId: "fd4a527f-c126-da2d-8ee6-51663a43e416", + }); + await expect(browser).toHaveUrlContaining(NameBlockPage.pageName); + await $(NameBlockPage.name()).setValue("Joe"); + await click(NameBlockPage.submit()); + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + }); +}); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 431b985827..3ef40fb081 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -214,6 +214,7 @@ export const config = { region = "GB-ENG", language = "en", includeLogoutUrl = false, + cirInstrumentId = null, } = {}, ) { const token = await JwtHelper.generateToken(schema, { @@ -230,6 +231,7 @@ export const config = { regionCode: region, languageCode: language, includeLogoutUrl, + cirInstrumentId, }); this.url(`/session?token=${token}`); }, From 3a0edb834fbd98c4418aa851d6c8710ea0710812 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:13:40 +0000 Subject: [PATCH 349/567] Remove census survey configurations from Runner (#1256) * Add changes to files * Addd updated translations * Fix metadata after schema theme change * Fix confirmation email error panel test * Translations and remove template census thank you * Remove census survey config and themes in files * Remove census survey config and themes in test files * Remove tests and format tests * Add ru_name to relevant schemas and update messages.pot, point runner to use a different validator branch * Fix tests and schema * Fix metadata ru_name issue in schema * Fix census mention in ThankYou class and update translations * Update translations babel version * Change template name and references * Remove comments * Remove more census related schemas/tests * Update messages.pot * Fix form in context * Remove test that does the same as another and revert tag in run_validator.sh * Remove and fix broken tests * Remove further census survey configs and update schemas/tests/docs * Fix broken test and format json/python files * Revert changes to a historical document * Revert deleted env variable * Revert changes made * Revert broken test to original --------- Co-authored-by: petechd Co-authored-by: petechd <53475968+petechd@users.noreply.github.com> --- .functional-tests.env | 1 - app/helpers/template_helpers.py | 7 - app/survey_config/__init__.py | 8 - app/survey_config/census_config.py | 186 ---------------- app/survey_config/survey_type.py | 2 - app/translations/messages.pot | 70 ++---- app/utilities/metadata_parser.py | 8 - app/utilities/schema.py | 2 +- schemas/test/en/test_individual_response.json | 2 +- ...t_individual_response_on_hub_disabled.json | 2 +- .../en/test_list_collector_list_summary.json | 16 +- schemas/test/en/test_metadata_routing.json | 10 +- .../en/test_routing_on_multiple_select.json | 6 +- ...ank_you_census_communal_establishment.json | 72 ------- .../en/test_thank_you_census_household.json | 69 ------ .../en/test_thank_you_census_individual.json | 72 ------- tests/app/helpers/conftest.py | 204 ------------------ tests/app/helpers/test_template_helpers.py | 109 ---------- .../base_pages/census-thank-you.page.js | 21 -- tests/functional/base_pages/feedback.page.js | 4 - .../functional/spec/census_thank_you.spec.js | 20 -- tests/functional/spec/save_sign_out.spec.js | 10 - .../routes/test_confirmation_email.py | 22 +- tests/integration/routes/test_errors.py | 32 --- tests/integration/routes/test_session.py | 21 -- .../session/test_sign_out_and_exit.py | 25 --- .../integration/test_application_variables.py | 15 -- 27 files changed, 44 insertions(+), 972 deletions(-) delete mode 100644 app/survey_config/census_config.py delete mode 100644 schemas/test/en/test_thank_you_census_communal_establishment.json delete mode 100644 schemas/test/en/test_thank_you_census_household.json delete mode 100644 schemas/test/en/test_thank_you_census_individual.json delete mode 100644 tests/functional/base_pages/census-thank-you.page.js delete mode 100644 tests/functional/spec/census_thank_you.spec.js diff --git a/.functional-tests.env b/.functional-tests.env index 0faec74514..9803a04cc0 100644 --- a/.functional-tests.env +++ b/.functional-tests.env @@ -24,7 +24,6 @@ WEB_SERVER_THREADS=10 WEB_SERVER_UWSGI_ASYNC_CORES=10 CDN_URL=https://cdn.eq.gcp.onsdigital.uk CDN_ASSETS_PATH=/design-system -ADDRESS_LOOKUP_API_URL=https://whitelodge-ai-api.census-gcp.onsdigital.uk COOKIE_SETTINGS_URL=# EQ_SUBMISSION_CONFIRMATION_BACKEND=log VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS=35 diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 9fbe96b2f0..04107b645c 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -15,8 +15,6 @@ from app.settings import ACCOUNT_SERVICE_BASE_URL from app.survey_config import ( BusinessSurveyConfig, - CensusNISRASurveyConfig, - CensusSurveyConfig, DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, @@ -28,7 +26,6 @@ SocialSurveyConfig, SurveyConfig, UKHSAONSSocialSurveyConfig, - WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType from app.utilities.schema import load_schema_from_metadata @@ -194,10 +191,6 @@ def survey_config_mapping( SurveyType.DESNZ: DESNZBusinessSurveyConfig, SurveyType.DESNZ_NI: DESNZNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, - SurveyType.CENSUS: ( - WelshCensusSurveyConfig if language == "cy" else CensusSurveyConfig - ), - SurveyType.CENSUS_NISRA: CensusNISRASurveyConfig, SurveyType.UKHSA_ONS: UKHSAONSSocialSurveyConfig, } diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 1f8425383c..e800b7b03a 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -9,11 +9,6 @@ NIBusinessSurveyConfig, ORRBusinessSurveyConfig, ) -from app.survey_config.census_config import ( - CensusNISRASurveyConfig, - CensusSurveyConfig, - WelshCensusSurveyConfig, -) from app.survey_config.link import Link from app.survey_config.social_survey_config import ( SocialSurveyConfig, @@ -25,9 +20,6 @@ "SocialSurveyConfig", "UKHSAONSSocialSurveyConfig", "SurveyConfig", - "CensusSurveyConfig", - "CensusNISRASurveyConfig", - "WelshCensusSurveyConfig", "BusinessSurveyConfig", "NIBusinessSurveyConfig", "DBTBusinessSurveyConfig", diff --git a/app/survey_config/census_config.py b/app/survey_config/census_config.py deleted file mode 100644 index 8522bed2e6..0000000000 --- a/app/survey_config/census_config.py +++ /dev/null @@ -1,186 +0,0 @@ -from dataclasses import dataclass -from typing import Optional - -from flask_babel import lazy_gettext -from flask_babel.speaklater import LazyString - -from app.survey_config.link import Link -from app.survey_config.survey_config import SurveyConfig - -EN_BASE_URL = "https://census.gov.uk" -CY_BASE_URL = "https://cyfrifiad.gov.uk" -NIR_BASE_URL = f"{EN_BASE_URL}/ni" - - -@dataclass -class CensusSurveyConfig( - SurveyConfig, -): - base_url: str = EN_BASE_URL - account_service_log_out_url: str = f"{base_url}/en/start" - design_system_theme: str = "census" - - survey_title: LazyString = lazy_gettext("Census 2021") - sign_out_button_text: str = lazy_gettext("Save and complete later") - _is_nisra: bool = False - - def get_additional_data_layer_context(self) -> list[dict]: - return [{"nisra": self._is_nisra}] - - def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [ - Link( - lazy_gettext("Help"), - f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", - ).as_dict() - ] - - if cookie_has_theme: - links.append( - Link(lazy_gettext("Contact us"), f"{EN_BASE_URL}/contact-us/").as_dict() - ) - - links.extend( - [ - Link( - lazy_gettext("Languages"), - f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", - ).as_dict(), - Link( - lazy_gettext("BSL and audio videos"), - f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", - ).as_dict(), - ] - ) - - return links - - def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: - if cookie_has_theme: - return [ - Link(lazy_gettext("Cookies"), f"{EN_BASE_URL}/cookies/").as_dict(), - Link( - lazy_gettext("Accessibility statement"), - f"{EN_BASE_URL}/accessibility-statement/", - ).as_dict(), - Link( - lazy_gettext("Privacy and data protection"), - f"{EN_BASE_URL}/privacy-and-data-protection/", - ).as_dict(), - Link( - lazy_gettext("Terms and conditions"), - f"{EN_BASE_URL}/terms-and-conditions/", - ).as_dict(), - ] - - return None - - -@dataclass -class WelshCensusSurveyConfig( - CensusSurveyConfig, -): - base_url: str = CY_BASE_URL - account_service_log_out_url: str = f"{base_url}/en/start" - - def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [ - Link( - lazy_gettext("Help"), - f"{CY_BASE_URL}/help/sut-i-ateb-y-cwestiynau/help-y-cwestiynau-ar-lein/", - ).as_dict() - ] - - if cookie_has_theme: - links.append( - Link( - lazy_gettext("Contact us"), f"{CY_BASE_URL}/cysylltu-a-ni/" - ).as_dict() - ) - links.extend( - [ - Link( - lazy_gettext("Languages"), - f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", - ).as_dict(), - Link( - lazy_gettext("BSL and audio videos"), - f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", - ).as_dict(), - ] - ) - return links - - def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: - if cookie_has_theme: - return [ - Link(lazy_gettext("Cookies"), f"{CY_BASE_URL}/cwcis/").as_dict(), - Link( - lazy_gettext("Accessibility statement"), - f"{CY_BASE_URL}/datganiad-hygyrchedd/", - ).as_dict(), - Link( - lazy_gettext("Privacy and data protection"), - f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", - ).as_dict(), - Link( - lazy_gettext("Terms and conditions"), - f"{CY_BASE_URL}/telerau-ac-amodau/", - ).as_dict(), - ] - - return None - - -# Census and Nisra theme will no longer work as of 23/11/22 -# Theming has been removed from DS in v60 (https://github.com/ONSdigital/eq-questionnaire-runner/pull/951) -@dataclass -class CensusNISRASurveyConfig( - CensusSurveyConfig, -): - base_url: str = NIR_BASE_URL - account_service_log_out_url: str = base_url - copyright_declaration: LazyString = lazy_gettext( - "Crown copyright and database rights 2021 NIMA MOU577.501." - ) - copyright_text: LazyString = lazy_gettext( - "Use of address data is subject to the terms and conditions." - ) - _is_nisra: bool = True - - def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: - links = [ - Link( - lazy_gettext("Help"), - f"{NIR_BASE_URL}/help/help-with-the-questions/online-questions-help/", - ).as_dict() - ] - - if cookie_has_theme: - links.append( - Link( - lazy_gettext("Contact us"), f"{NIR_BASE_URL}/contact-us/" - ).as_dict() - ) - - return links - - def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: - if cookie_has_theme: - return [ - Link(lazy_gettext("Cookies"), f"{NIR_BASE_URL}/cookies/").as_dict(), - Link( - lazy_gettext("Accessibility statement"), - f"{NIR_BASE_URL}/accessibility-statement/", - ).as_dict(), - Link( - lazy_gettext("Privacy and data protection"), - f"{NIR_BASE_URL}/privacy-and-data-protection/", - ).as_dict(), - Link( - lazy_gettext("Terms and conditions"), - f"{NIR_BASE_URL}/terms-and-conditions/", - ).as_dict(), - ] - - return None diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index bd56ef7102..fbfbc16062 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -14,6 +14,4 @@ class SurveyType(Enum): ORR = "orr" DESNZ = "desnz" DESNZ_NI = "desnz-ni" - CENSUS = "census" - CENSUS_NISRA = "census-nisra" UKHSA_ONS = "ukhsa-ons" diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 46c0e2aec3..73959fbe20 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-11-16 13:49+0000\n" +"POT-Creation-Date: 2023-12-04 17:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -213,19 +213,19 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:51 +#: app/helpers/template_helpers.py:48 msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:117 +#: app/helpers/template_helpers.py:114 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:148 +#: app/helpers/template_helpers.py:145 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:173 +#: app/helpers/template_helpers.py:170 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -301,17 +301,15 @@ msgstr "" msgid "You can try to submit your feedback again." msgstr "" -#: app/routes/individual_response.py:206 +#: app/routes/individual_response.py:203 msgid "An individual access code has been sent by post" msgstr "" -#: app/routes/individual_response.py:312 +#: app/routes/individual_response.py:309 msgid "An individual access code has been sent by text" msgstr "" #: app/survey_config/business_config.py:59 -#: app/survey_config/census_config.py:33 app/survey_config/census_config.py:89 -#: app/survey_config/census_config.py:154 msgid "Help" msgstr "" @@ -329,8 +327,6 @@ msgid "What we do" msgstr "" #: app/survey_config/business_config.py:92 -#: app/survey_config/census_config.py:40 app/survey_config/census_config.py:97 -#: app/survey_config/census_config.py:162 #: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "" @@ -341,57 +337,23 @@ msgid "Accessibility" msgstr "" #: app/survey_config/business_config.py:107 -#: app/survey_config/census_config.py:61 app/survey_config/census_config.py:117 -#: app/survey_config/census_config.py:171 #: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "" #: app/survey_config/business_config.py:109 -#: app/survey_config/census_config.py:67 app/survey_config/census_config.py:123 -#: app/survey_config/census_config.py:177 #: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "" -#: app/survey_config/census_config.py:23 -msgid "Census 2021" -msgstr "" - -#: app/survey_config/census_config.py:24 -msgid "Save and complete later" -msgstr "" - -#: app/survey_config/census_config.py:46 app/survey_config/census_config.py:103 -msgid "Languages" -msgstr "" - -#: app/survey_config/census_config.py:50 app/survey_config/census_config.py:107 -msgid "BSL and audio videos" -msgstr "" - -#: app/survey_config/census_config.py:63 app/survey_config/census_config.py:119 -#: app/survey_config/census_config.py:173 -msgid "Accessibility statement" -msgstr "" - -#: app/survey_config/census_config.py:71 app/survey_config/census_config.py:127 -#: app/survey_config/census_config.py:181 -msgid "Terms and conditions" -msgstr "" - -#: app/survey_config/census_config.py:143 -msgid "Crown copyright and database rights 2021 NIMA MOU577.501." +#: app/survey_config/survey_config.py:17 +msgid "Crown copyright and database rights 2020 OS 100019153." msgstr "" -#: app/survey_config/census_config.py:146 app/survey_config/survey_config.py:20 +#: app/survey_config/survey_config.py:20 msgid "Use of address data is subject to the terms and conditions." msgstr "" -#: app/survey_config/survey_config.py:17 -msgid "Crown copyright and database rights 2020 OS 100019153." -msgstr "" - #: app/survey_config/survey_config.py:36 msgid "Save and exit survey" msgstr "" @@ -728,7 +690,7 @@ msgstr "" msgid "Confirm mobile number" msgstr "" -#: app/views/handlers/thank_you.py:23 +#: app/views/handlers/thank_you.py:22 msgid "Thank you for completing the survey" msgstr "" @@ -965,11 +927,11 @@ msgstr "" msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" -#: templates/thank-you.html:87 +#: templates/thank-you.html:85 msgid "Get confirmation email" msgstr "" -#: templates/thank-you.html:88 +#: templates/thank-you.html:86 msgid "" "If you would like to be sent confirmation that you have completed your " "survey, enter your email address" @@ -1297,15 +1259,15 @@ msgstr "" msgid "Interviewer note:" msgstr "" -#: templates/partials/email-form.html:25 +#: templates/partials/confirmation-email-form.html:25 msgid "Email address" msgstr "" -#: templates/partials/email-form.html:26 +#: templates/partials/confirmation-email-form.html:26 msgid "This will not be stored and only used once to send your confirmation" msgstr "" -#: templates/partials/email-form.html:38 +#: templates/partials/confirmation-email-form.html:38 msgid "Send confirmation" msgstr "" diff --git a/app/utilities/metadata_parser.py b/app/utilities/metadata_parser.py index 0a5b96e66a..16fdb44f6d 100644 --- a/app/utilities/metadata_parser.py +++ b/app/utilities/metadata_parser.py @@ -69,15 +69,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): validate=lambda x: parse_iso_8601_datetime(x) > datetime.now(tz=timezone.utc), ) # type:ignore - # Either schema_name OR the three census parameters are required. Should be required after census. schema_name = VALIDATORS["string"](required=False) # type:ignore - - # The following three parameters can be removed after Census - survey = VALIDATORS["string"]( - required=False, - validate=validate.OneOf(("CENSUS", "CCS")), - load_default="CENSUS", - ) # type:ignore region_code = VALIDATORS["string"]( required=False, validate=RegionCode() ) # type:ignore diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 8e38c53b8d..57c9e4e7fa 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -158,7 +158,7 @@ def get_schema_name_from_params(eq_id: str | None, form_type: str | None) -> str def _load_schema_file(schema_name: str, language_code: str) -> Any: """ Load a schema, optionally for a specified language. - :param schema_name: The name of the schema e.g. census_household + :param schema_name: The name of the schema e.g. test_address :param language_code: ISO 2-character code for language e.g. 'en', 'cy' """ if language_code != DEFAULT_LANGUAGE_CODE and not _schema_exists( diff --git a/schemas/test/en/test_individual_response.json b/schemas/test/en/test_individual_response.json index 60a2bf6e01..d4154dbd4b 100644 --- a/schemas/test/en/test_individual_response.json +++ b/schemas/test/en/test_individual_response.json @@ -5,7 +5,7 @@ "data_version": "0.0.3", "survey_id": "census", "title": "Test Individual Response", - "theme": "census", + "theme": "default", "description": "A questionnaire to test individual response", "metadata": [ { diff --git a/schemas/test/en/test_individual_response_on_hub_disabled.json b/schemas/test/en/test_individual_response_on_hub_disabled.json index b8616d5757..69a8f80537 100644 --- a/schemas/test/en/test_individual_response_on_hub_disabled.json +++ b/schemas/test/en/test_individual_response_on_hub_disabled.json @@ -5,7 +5,7 @@ "data_version": "0.0.3", "survey_id": "census", "title": "Test Individual Response", - "theme": "census", + "theme": "default", "description": "A questionnaire to test individual response", "metadata": [ { diff --git a/schemas/test/en/test_list_collector_list_summary.json b/schemas/test/en/test_list_collector_list_summary.json index 82aae3b14d..a7f3a51290 100644 --- a/schemas/test/en/test_list_collector_list_summary.json +++ b/schemas/test/en/test_list_collector_list_summary.json @@ -45,10 +45,10 @@ "type": "List", "for_list": "people", "title": { - "text": "Household members staying overnight on {census_date} at {household_address}", + "text": "Household members staying overnight on {date} at {household_address}", "placeholders": [ { - "placeholder": "census_date", + "placeholder": "date", "transforms": [ { "arguments": { @@ -77,10 +77,10 @@ "type": "List", "for_list": "visitors", "title": { - "text": "Visitors staying overnight on {census_date} at {household_address}", + "text": "Visitors staying overnight on {date} at {household_address}", "placeholders": [ { - "placeholder": "census_date", + "placeholder": "date", "transforms": [ { "arguments": { @@ -297,10 +297,10 @@ }, "summary": { "title": { - "text": "Household members staying overnight on {census_date} at {household_address}", + "text": "Household members staying overnight on {date} at {household_address}", "placeholders": [ { - "placeholder": "census_date", + "placeholder": "date", "transforms": [ { "arguments": { @@ -467,10 +467,10 @@ }, "summary": { "title": { - "text": "Visitors staying overnight on {census_date} at {household_address}", + "text": "Visitors staying overnight on {date} at {household_address}", "placeholders": [ { - "placeholder": "census_date", + "placeholder": "date", "transforms": [ { "arguments": { diff --git a/schemas/test/en/test_metadata_routing.json b/schemas/test/en/test_metadata_routing.json index 4a4269e610..b97f19e384 100644 --- a/schemas/test/en/test_metadata_routing.json +++ b/schemas/test/en/test_metadata_routing.json @@ -4,9 +4,9 @@ "schema_version": "0.0.1", "data_version": "0.0.3", "survey_id": "0", - "title": "Census England Household Schema", - "description": "Census England Household Schema", - "theme": "census", + "title": "Household Schema", + "description": "Household Schema", + "theme": "default", "metadata": [ { "name": "user_id", @@ -19,6 +19,10 @@ { "name": "flag_1", "type": "boolean" + }, + { + "name": "ru_name", + "type": "string" } ], "questionnaire_flow": { diff --git a/schemas/test/en/test_routing_on_multiple_select.json b/schemas/test/en/test_routing_on_multiple_select.json index b198117b8e..312b654f2d 100644 --- a/schemas/test/en/test_routing_on_multiple_select.json +++ b/schemas/test/en/test_routing_on_multiple_select.json @@ -6,7 +6,7 @@ "survey_id": "0", "title": "Test schema for routing on multiple selected answers", "description": "Test schema for routing on multiple selected answers", - "theme": "census", + "theme": "default", "metadata": [ { "name": "user_id", @@ -15,6 +15,10 @@ { "name": "period_id", "type": "string" + }, + { + "name": "ru_name", + "type": "string" } ], "questionnaire_flow": { diff --git a/schemas/test/en/test_thank_you_census_communal_establishment.json b/schemas/test/en/test_thank_you_census_communal_establishment.json deleted file mode 100644 index f689e86667..0000000000 --- a/schemas/test/en/test_thank_you_census_communal_establishment.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "form_type": "C", - "region_code": "GB-WLS", - "title": "Census communal establishment test schema", - "theme": "census", - "description": "A questionnaire to test the thank you page for communal establishment", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "display_address", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "communal-establishment-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "communal-establishment-confirmation", - "question": { - "answers": [ - { - "type": "Radio", - "id": "communal-establishment-confirmation-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ], - "id": "communal-establishment-confirmation-question", - "title": "Are you aware this is an census communal establishment test schema?", - "type": "General" - } - } - ], - "id": "communal-establishment-group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_thank_you_census_household.json b/schemas/test/en/test_thank_you_census_household.json deleted file mode 100644 index 676e0929bf..0000000000 --- a/schemas/test/en/test_thank_you_census_household.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "form_type": "H", - "region_code": "GB-WLS", - "title": "Census household test schema", - "theme": "census", - "description": "A questionnaire to test the thank you page for census household", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "display_address", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Hub", - "options": { "required_completed_sections": ["household-section"] } - }, - "sections": [ - { - "id": "household-section", - "title": "Household Section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "household-confirmation", - "question": { - "answers": [ - { - "type": "Radio", - "id": "household-confirmation-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ], - "id": "household-confirmation-question", - "title": "Are you aware this is an census household test schema?", - "type": "General" - } - } - ], - "id": "household-group" - } - ] - } - ] -} diff --git a/schemas/test/en/test_thank_you_census_individual.json b/schemas/test/en/test_thank_you_census_individual.json deleted file mode 100644 index 34ef182247..0000000000 --- a/schemas/test/en/test_thank_you_census_individual.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "mime_type": "application/json/ons/eq", - "language": "en", - "schema_version": "0.0.1", - "data_version": "0.0.3", - "survey_id": "0", - "form_type": "I", - "region_code": "GB-WLS", - "title": "Census individual test schema", - "theme": "census", - "description": "A questionnaire to test the thank you page for census individual", - "metadata": [ - { - "name": "user_id", - "type": "string" - }, - { - "name": "period_id", - "type": "string" - }, - { - "name": "display_address", - "type": "string" - } - ], - "questionnaire_flow": { - "type": "Linear", - "options": { - "summary": { - "collapsible": false - } - } - }, - "sections": [ - { - "id": "individual-section", - "groups": [ - { - "blocks": [ - { - "type": "Question", - "id": "individual-confirmation", - "question": { - "answers": [ - { - "type": "Radio", - "id": "individual-confirmation-answer", - "mandatory": false, - "options": [ - { - "label": "Yes", - "value": "Yes" - }, - { - "label": "No", - "value": "No" - } - ] - } - ], - "id": "individual-confirmation-question", - "title": "Are you aware this is an census individual test schema?", - "type": "General" - } - } - ], - "id": "individual-group" - } - ] - } - ] -} diff --git a/tests/app/helpers/conftest.py b/tests/app/helpers/conftest.py index 954679a633..e07f4d62c8 100644 --- a/tests/app/helpers/conftest.py +++ b/tests/app/helpers/conftest.py @@ -7,7 +7,6 @@ ONS_URL, ONS_URL_CY, ) -from app.survey_config.census_config import CY_BASE_URL, EN_BASE_URL @fixture @@ -38,91 +37,6 @@ def footer_context(): } -def expected_footer_census_theme(): - census = { - "rows": [ - { - "itemsList": [ - { - "text": "Help", - "url": f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", - "target": "_blank", - }, - { - "text": "Contact us", - "url": f"{EN_BASE_URL}/contact-us/", - "target": "_blank", - }, - { - "text": "Languages", - "url": f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", - "target": "_blank", - }, - { - "text": "BSL and audio videos", - "url": f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", - "target": "_blank", - }, - ] - } - ], - "legal": [ - { - "itemsList": [ - { - "text": "Cookies", - "url": f"{EN_BASE_URL}/cookies/", - "target": "_blank", - }, - { - "text": "Accessibility statement", - "url": f"{EN_BASE_URL}/accessibility-statement/", - "target": "_blank", - }, - { - "text": "Privacy and data protection", - "url": f"{EN_BASE_URL}/privacy-and-data-protection/", - "target": "_blank", - }, - { - "text": "Terms and conditions", - "url": f"{EN_BASE_URL}/terms-and-conditions/", - "target": "_blank", - }, - ] - } - ], - } - return {**footer_context(), **census} - - -def expected_footer_census_theme_no_cookie(): - census = { - "rows": [ - { - "itemsList": [ - { - "text": "Help", - "url": f"{EN_BASE_URL}/help/how-to-answer-questions/online-questions-help/", - "target": "_blank", - }, - { - "text": "Languages", - "url": f"{EN_BASE_URL}/help/languages-and-accessibility/languages/", - "target": "_blank", - }, - { - "text": "BSL and audio videos", - "url": f"{EN_BASE_URL}/help/languages-and-accessibility/accessibility/accessible-videos-with-bsl/", - "target": "_blank", - }, - ] - } - ], - } - return {**footer_context(), **census} - - def expected_footer_business_theme(): business = { "rows": [ @@ -262,121 +176,3 @@ def expected_footer_social_theme_no_cookie(): ], } return {**footer_context(), **social} - - -def expected_footer_nisra_theme(): - return { - "lang": "en", - "crest": True, - "newTabWarning": "The following links open in a new tab", - "copyrightDeclaration": { - "copyright": "Crown copyright and database rights 2021 NIMA MOU577.501.", - "text": "Use of address data is subject to the terms and conditions.", - }, - "rows": [ - { - "itemsList": [ - { - "text": "Help", - "url": f"{EN_BASE_URL}/ni/help/help-with-the-questions/online-questions-help/", - "target": "_blank", - }, - { - "text": "Contact us", - "url": f"{EN_BASE_URL}/ni/contact-us/", - "target": "_blank", - }, - ] - } - ], - "legal": [ - { - "itemsList": [ - { - "text": "Cookies", - "url": f"{EN_BASE_URL}/ni/cookies/", - "target": "_blank", - }, - { - "text": "Accessibility statement", - "url": f"{EN_BASE_URL}/ni/accessibility-statement/", - "target": "_blank", - }, - { - "text": "Privacy and data protection", - "url": f"{EN_BASE_URL}/ni/privacy-and-data-protection/", - "target": "_blank", - }, - { - "text": "Terms and conditions", - "url": f"{EN_BASE_URL}/ni/terms-and-conditions/", - "target": "_blank", - }, - ] - } - ], - } - - -def expected_footer_census_welsh_theme(): - return { - "lang": "en", - "crest": True, - "newTabWarning": "The following links open in a new tab", - "copyrightDeclaration": { - "copyright": "Crown copyright and database rights 2020 OS 100019153.", - "text": "Use of address data is subject to the terms and conditions.", - }, - "rows": [ - { - "itemsList": [ - { - "text": "Help", - "url": f"{CY_BASE_URL}/help/sut-i-ateb-y-cwestiynau/help-y-cwestiynau-ar-lein/", - "target": "_blank", - }, - { - "text": "Contact us", - "url": f"{CY_BASE_URL}/cysylltu-a-ni/", - "target": "_blank", - }, - { - "text": "Languages", - "url": f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/ieithoedd/", - "target": "_blank", - }, - { - "text": "BSL and audio videos", - "url": f"{CY_BASE_URL}/help/ieithoedd-a-hygyrchedd/hygyrchedd/fideos-hygyrch-gyda-bsl/", - "target": "_blank", - }, - ] - } - ], - "legal": [ - { - "itemsList": [ - { - "text": "Cookies", - "url": f"{CY_BASE_URL}/cwcis/", - "target": "_blank", - }, - { - "text": "Accessibility statement", - "url": f"{CY_BASE_URL}/datganiad-hygyrchedd/", - "target": "_blank", - }, - { - "text": "Privacy and data protection", - "url": f"{CY_BASE_URL}/preifatrwydd-a-diogelu-data/", - "target": "_blank", - }, - { - "text": "Terms and conditions", - "url": f"{CY_BASE_URL}/telerau-ac-amodau/", - "target": "_blank", - }, - ] - } - ], - } diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 26680414bf..4cf600edde 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -17,8 +17,6 @@ ) from app.survey_config import ( BusinessSurveyConfig, - CensusNISRASurveyConfig, - CensusSurveyConfig, DBTBusinessSurveyConfig, DBTDSITBusinessSurveyConfig, DBTDSITNIBusinessSurveyConfig, @@ -30,16 +28,11 @@ SocialSurveyConfig, SurveyConfig, UKHSAONSSocialSurveyConfig, - WelshCensusSurveyConfig, ) from app.survey_config.survey_type import SurveyType from tests.app.helpers.conftest import ( expected_footer_business_theme, expected_footer_business_theme_no_cookie, - expected_footer_census_theme, - expected_footer_census_theme_no_cookie, - expected_footer_census_welsh_theme, - expected_footer_nisra_theme, expected_footer_social_theme, expected_footer_social_theme_no_cookie, ) @@ -51,8 +44,6 @@ @pytest.mark.parametrize( "theme, survey_config, language, expected_footer", [ - (SurveyType.CENSUS, CensusSurveyConfig(), "en", expected_footer_census_theme()), - (None, CensusSurveyConfig(), "en", expected_footer_census_theme_no_cookie()), ( SurveyType.BUSINESS, BusinessSurveyConfig(), @@ -78,18 +69,6 @@ "cy", expected_footer_social_theme("cy"), ), - ( - SurveyType.CENSUS_NISRA, - CensusNISRASurveyConfig(), - "en", - expected_footer_nisra_theme(), - ), - ( - SurveyType.CENSUS, - WelshCensusSurveyConfig(), - "en", - expected_footer_census_welsh_theme(), - ), ], ) def test_footer_context(app: Flask, theme, survey_config, language, expected_footer): @@ -108,33 +87,6 @@ def test_footer_context(app: Flask, theme, survey_config, language, expected_foo assert result == expected_footer -def test_footer_warning_in_context_census_theme(app: Flask): - with app.app_context(): - expected = "Make sure you leave this page or close your browser if using a shared device" - - survey_config = CensusSurveyConfig() - - result = ContextHelper( - language="en", - is_post_submission=True, - include_csrf_token=True, - survey_config=survey_config, - ).context["footer"]["footerWarning"] - - assert result == expected - - -def test_footer_warning_not_in_context_census_theme(app: Flask): - with app.app_context(): - with pytest.raises(KeyError): - _ = ContextHelper( - language="en", - is_post_submission=False, - include_csrf_token=True, - survey_config=CensusSurveyConfig(), - ).context["footer"]["footerWarning"] - - @pytest.mark.parametrize( "theme, survey_title, survey_config, expected", ( @@ -186,36 +138,6 @@ def test_footer_warning_not_in_context_census_theme(app: Flask): SurveyConfig(), ["ONS Surveys", None, None], ), - ( - SurveyType.CENSUS, - None, - CensusSurveyConfig(), - ["ONS Surveys", None, None], - ), - ( - SurveyType.CENSUS, - "Test", - CensusSurveyConfig(), - ["Test", None, None], - ), - ( - None, - None, - CensusSurveyConfig(), - ["ONS Surveys", None, None], - ), - ( - SurveyType.CENSUS_NISRA, - None, - CensusNISRASurveyConfig(), - ["ONS Surveys", None, None], - ), - ( - None, - None, - CensusNISRASurveyConfig(), - ["ONS Surveys", None, None], - ), ( None, None, @@ -584,7 +506,6 @@ def test_contact_us_url_context( "survey_config, expected", [ (SurveyConfig(), "Save and exit survey"), - (CensusSurveyConfig(), "Save and complete later"), ], ) def test_sign_out_button_text_context( @@ -848,9 +769,6 @@ def test_account_service_my_todo_url_context( BusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", ), - (CensusSurveyConfig(), "https://census.gov.uk/en/start"), - (WelshCensusSurveyConfig(), "https://cyfrifiad.gov.uk/en/start"), - (CensusNISRASurveyConfig(), "https://census.gov.uk/ni"), ( NIBusinessSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL}/sign-in/logout", @@ -921,9 +839,6 @@ def test_account_service_log_out_url_context( (SurveyType.DBT_DSIT, "en", DBTDSITBusinessSurveyConfig), (SurveyType.DBT_DSIT_NI, "en", DBTDSITNIBusinessSurveyConfig), (SurveyType.ORR, "en", ORRBusinessSurveyConfig), - (SurveyType.CENSUS, "en", CensusSurveyConfig), - (SurveyType.CENSUS, "cy", WelshCensusSurveyConfig), - (SurveyType.CENSUS_NISRA, "en", CensusNISRASurveyConfig), (SurveyType.UKHSA_ONS, "en", UKHSAONSSocialSurveyConfig), (None, None, BusinessSurveyConfig), ], @@ -1032,9 +947,6 @@ def test_context_set_from_app_config(app): (SurveyType.DBT_DSIT, "en", None), (SurveyType.DBT_DSIT_NI, "en", None), (SurveyType.ORR, "en", None), - (SurveyType.CENSUS, "en", "census"), - (SurveyType.CENSUS, "cy", "census"), - (SurveyType.CENSUS_NISRA, "en", "census"), ], ) def test_correct_theme_in_context(app: Flask, theme: str, language: str, expected: str): @@ -1063,9 +975,6 @@ def test_correct_theme_in_context(app: Flask, theme: str, language: str, expecte (SurveyType.DBT_DSIT, "en", "ONS Surveys"), (SurveyType.DBT_DSIT_NI, "en", "ONS Surveys"), (SurveyType.ORR, "en", "ONS Surveys"), - (SurveyType.CENSUS, "en", "ONS Surveys"), - (SurveyType.CENSUS, "cy", "ONS Surveys"), - (SurveyType.CENSUS_NISRA, "en", "ONS Surveys"), ], ) def test_use_default_survey_title_in_context_when_no_cookie( @@ -1169,24 +1078,6 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema({"survey_id": "001"}), [{"survey_id": "001"}], ), - ( - SurveyType.CENSUS, - "en", - QuestionnaireSchema({"survey_id": "001"}), - [{"nisra": False}, {"survey_id": "001"}], - ), - ( - SurveyType.CENSUS, - "cy", - QuestionnaireSchema({"survey_id": "001"}), - [{"nisra": False}, {"survey_id": "001"}], - ), - ( - SurveyType.CENSUS_NISRA, - "en", - QuestionnaireSchema({"survey_id": "001"}), - [{"nisra": True}, {"survey_id": "001"}], - ), ], ) def test_correct_data_layer_in_context( diff --git a/tests/functional/base_pages/census-thank-you.page.js b/tests/functional/base_pages/census-thank-you.page.js deleted file mode 100644 index 07e2b81eda..0000000000 --- a/tests/functional/base_pages/census-thank-you.page.js +++ /dev/null @@ -1,21 +0,0 @@ -import BasePage from "./base.page"; - -class CensusThankYouPage extends BasePage { - title() { - return '[data-qa="title"]'; - } - - exit() { - return '[data-qa="btn-exit"]'; - } - - feedback() { - return ".ons-feedback"; - } - - feedbackLink() { - return ".ons-feedback__link"; - } -} - -export default new CensusThankYouPage("thank-you"); diff --git a/tests/functional/base_pages/feedback.page.js b/tests/functional/base_pages/feedback.page.js index 901b37fd0a..b561de7283 100644 --- a/tests/functional/base_pages/feedback.page.js +++ b/tests/functional/base_pages/feedback.page.js @@ -9,10 +9,6 @@ class FeedbackPage extends FeedbackBasePage { return "#feedback-type"; } - feedbackTypeCensusQuestions() { - return "#feedback-type-0"; - } - feedbackTypePageDesignAndStructure() { return "#feedback-type-1"; } diff --git a/tests/functional/spec/census_thank_you.spec.js b/tests/functional/spec/census_thank_you.spec.js deleted file mode 100644 index 0b3b25acdb..0000000000 --- a/tests/functional/spec/census_thank_you.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -import { SubmitPage } from "../base_pages/submit.page.js"; -import HubPage from "../base_pages/hub.page"; - -import ThankYouPage from "../base_pages/thank-you.page"; -import { click } from "../helpers"; - -describe("Thank You Census Household", () => { - describe("Given I launch a census schema without feedback enabled", () => { - beforeEach(async () => { - await browser.openQuestionnaire("test_thank_you_census_household.json"); - }); - - it("When I navigate to the thank you page, Then I should not see the feedback call to action", async () => { - await click(SubmitPage.submit()); - await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); - await expect(await $(ThankYouPage.feedback()).isExisting()).toBe(false); - }); - }); -}); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index be0ef695cc..a53285d86d 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -5,7 +5,6 @@ import SubmitPage from "../generated_pages/numbers/submit.page"; import IntroductionPage from "../generated_pages/introduction/introduction.page"; import IntroInterstitialPage from "../generated_pages/introduction/general-business-information-completed.page"; import IntroThankYouPagePage from "../base_pages/thank-you.page"; -import HouseHolderConfirmationPage from "../generated_pages/thank_you_census_household/household-confirmation.page"; import currencyBlock from "../generated_pages/variants_question/currency-block.page.js"; import firstNumberBlock from "../generated_pages/variants_question/first-number-block.page.js"; import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; @@ -93,13 +92,4 @@ describe("Save sign out / Exit", () => { await expect(await $(IntroThankYouPagePage.exitButton()).isExisting()).toBe(false); }); - - it("Given a Census questionnaire, when I navigate the questionnaire, then I see the correct sign out buttons", async () => { - await browser.openQuestionnaire("test_thank_you_census_household.json"); - - await expect(await $(HouseHolderConfirmationPage.saveSignOut()).getText()).toBe("Save and complete later"); - await click(HouseHolderConfirmationPage.submit()); - - await expect(await $(SubmitPage.saveSignOut()).getText()).toBe("Save and complete later"); - }); }); diff --git a/tests/integration/routes/test_confirmation_email.py b/tests/integration/routes/test_confirmation_email.py index bb0fa4a7d8..a09ef85e78 100644 --- a/tests/integration/routes/test_confirmation_email.py +++ b/tests/integration/routes/test_confirmation_email.py @@ -69,8 +69,8 @@ def test_missing_email_param_confirm_email(self): self.assertBadRequest() def test_confirm_email_with_confirmation_email_not_set(self): - # Given I launch the test_thank_you_census_individual questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_thank_you_census_individual") + # Given I launch the test_thank_you questionnaire, which doesn't have email confirmation set in the schema + self.launchSurvey("test_thank_you") self.post() self.post() @@ -82,8 +82,8 @@ def test_confirm_email_with_confirmation_email_not_set(self): self.assertNotInBody("Is this email address correct?") def test_confirmation_email_send_with_confirmation_email_not_set(self): - # Given I launch the test_thank_you_census_individual questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_thank_you_census_individual") + # Given I launch the test_thank_you questionnaire, which doesn't have email confirmation set in the schema + self.launchSurvey("test_thank_you") self.post() self.post() @@ -137,7 +137,7 @@ def test_email_confirmation_page_get_not_allowed(self): # Then I get shown a 404 error self.assertStatusNotFound() - def test_census_themed_schema_with_confirmation_email_true(self): + def test_default_themed_schema_with_confirmation_email_true(self): # Given I launch and complete the test_confirmation_email questionnaire self._launch_and_complete_questionnaire() @@ -148,18 +148,6 @@ def test_census_themed_schema_with_confirmation_email_true(self): "We’ve received your answers - Confirmation email test schema" ) - def test_census_themed_schema_with_confirmation_email_not_set(self): - # Given I launch the test_thank_you_census_individual questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_thank_you_census_individual") - - # When I complete the questionnaire - self.post() - self.post() - - # Then on the thank you page I don't get a confirmation email form - self.assertInUrl("/submitted/thank-you/") - self.assertNotInBody("Get confirmation email") - def test_default_themed_schema_with_confirmation_email_not_set(self): # Given I launch the test_checkbox questionnaire, which doesn't have email confirmation set in the schema self.launchSurvey("test_checkbox") diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index a230be9c95..957520afc5 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -314,24 +314,6 @@ def test_500_theme_social_cookie_exists(self): f'

      Contact us if you need to speak to someone about your survey.

      ' ) - def test_500_theme_census_cookie_exists(self): - # Given - self.launchSurvey("test_thank_you_census_household") - - # When - with patch( - "app.routes.questionnaire.get_block_handler", - side_effect=Exception("You broke it"), - ): - self.post({"answer": "test"}) - - # Then - self.assertException() - self.assertInBody( - "

      If you are completing a business survey and you need to speak to someone about your survey," - f' please contact us.

      ' - ) - def test_submission_failed_theme_default_cookie_exists(self): # Given submitter = self._application.eq["submitter"] @@ -368,20 +350,6 @@ def test_submission_failed_theme_social_cookie_exists(self): f'

      If this problem keeps happening, please contact us for help.

      ' ) - def test_submission_failed_theme_census_cookie_exists(self): - # Given - submitter = self._application.eq["submitter"] - submitter.send_message = Mock(return_value=False) - - # When - self.launchAndFailSubmission("test_thank_you_census_individual") - - # Then - self.assertStatusCode(500) - self.assertInBody( - f'

      If you are completing a business survey, please contact us.

      ' - ) - def test_preview_not_enabled_results_in_404(self): self.launchSurvey("test_checkbox") self.post(action="start_questionnaire") diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index f0b92ba1af..02fdb13082 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -293,24 +293,3 @@ def test_supplementary_data_raises_500_error_when_survey_becomes_invalid_for_sam self.launchSupplementaryDataSurvey(response_id="1", sds_dataset_id="same") self.assertStatusCode(500) self.assertEqual(mock_validate.call_count, 2) - - -class TestCensusSession(IntegrationTestCase): - def setUp(self): - # Cache for requests - self.last_url = None - self.last_response = None - self.last_csrf_token = None - self.redirect_url = None - - # Perform setup steps - self._set_up_app(setting_overrides={"SURVEY_TYPE": "census"}) - - def test_session_signed_out_no_cookie_session_census_config(self): - self.launchSurvey(schema_name="test_individual_response") - self.assertInBody("Save and sign out") - - self.deleteCookie() - self.get("/sign-out", follow_redirects=False) - - self.assertInRedirect("census.gov.uk") diff --git a/tests/integration/session/test_sign_out_and_exit.py b/tests/integration/session/test_sign_out_and_exit.py index abc35a002d..ffb05b2767 100644 --- a/tests/integration/session/test_sign_out_and_exit.py +++ b/tests/integration/session/test_sign_out_and_exit.py @@ -94,28 +94,3 @@ def test_redirects_to_account_service_log_out_url_using_base_url_from_claims(sel ) self.signOut() self.assertInRedirect(ACCOUNT_SERVICE_LOG_OUT_URL) - - -class TestCensusSignOut(IntegrationTestCase): - def setUp(self): - self._set_up_app(setting_overrides={"SURVEY_TYPE": "census"}) - - def test_sign_out_url(self): - self.launchSurvey(schema_name="test_individual_response") - self.saveAndSignOut() - self.assertInRedirect(SIGNED_OUT_URL_PATH) - - def test_sign_out_button_text(self): - self.launchSurvey(schema_name="test_individual_response") - self.assertEqual( - "Save and complete later", self.getSignOutButton().text.strip() - ) - - def test_no_session_cookie_redirects_to_default_account_service_log_out_url(self): - self.launchSurvey(schema_name="test_individual_response") - self.assertInBody("Save and sign out") - - self.deleteCookie() - self.signOut() - - self.assertInRedirect("census.gov.uk") diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 49c5e03c20..4ac227be57 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -48,21 +48,6 @@ def test_google_analytics_data_layer_has_no_null_fields(self): f'dataLayer = [{{"survey_id": "001", "title": "Other input fields"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) - def test_google_analytics_data_layer_is_set_to_nisra_false(self): - self.launchSurvey("test_thank_you_census_individual", roles=["dumper"]) - self.get("/dump/debug") - actual = json_loads(self.getResponseData()) - self._client.set_cookie( - domain="localhost", key="ons_cookie_policy", value="'usage':true" - ) - self.get("/questionnaire/individual-confirmation/") - self.assertStatusOK() - self.assertInHead("gtm.start") - # pylint: disable=line-too-long - self.assertInHead( - f'dataLayer = [{{"nisra": false}}, {{"form_type": "I", "survey_id": "0", "title": "Census individual test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' - ) - def test_livereload_script_rendered(self): self.launchSurvey("test_textfield") self.assertStatusOK() From d86ebad1f75928d8a391f891885489451113ee5f Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:48:23 +0000 Subject: [PATCH 350/567] Migrate CI tasks to use registry-image (#1276) --- ci/backup_questionnaire_state.yaml | 2 +- ci/deploy_app.yaml | 74 --------------------- ci/deploy_credentials.yaml | 27 -------- ci/purge_expired_questionnaire_state.yaml | 2 +- ci/purge_expired_sessions.yaml | 2 +- ci/purge_submitted_questionnaire_state.yaml | 2 +- ci/restore_questionnaire_state.yaml | 2 +- ci/wait.yaml | 13 ---- 8 files changed, 5 insertions(+), 119 deletions(-) delete mode 100644 ci/deploy_app.yaml delete mode 100644 ci/deploy_credentials.yaml delete mode 100644 ci/wait.yaml diff --git a/ci/backup_questionnaire_state.yaml b/ci/backup_questionnaire_state.yaml index 39d211688a..e486ca2de0 100644 --- a/ci/backup_questionnaire_state.yaml +++ b/ci/backup_questionnaire_state.yaml @@ -1,6 +1,6 @@ platform: linux image_resource: - type: docker-image + type: registry-image source: repository: gcr.io/google.com/cloudsdktool/cloud-sdk tag: alpine diff --git a/ci/deploy_app.yaml b/ci/deploy_app.yaml deleted file mode 100644 index cdbb8129fe..0000000000 --- a/ci/deploy_app.yaml +++ /dev/null @@ -1,74 +0,0 @@ -platform: linux -image_resource: - type: docker-image - source: - repository: gcr.io/google.com/cloudsdktool/cloud-sdk - tag: slim -inputs: - - name: eq-questionnaire-runner - - name: image-tag - optional: true -params: - SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) - PROJECT_ID: - DOCKER_REGISTRY: - IMAGE_TAG: - REGION: - - CONCURRENCY: - MIN_INSTANCES: - MAX_INSTANCES: - CPU: - MEMORY: - - WEB_SERVER_TYPE: - WEB_SERVER_WORKERS: - WEB_SERVER_THREADS: - WEB_SERVER_UWSGI_ASYNC_CORES: - HTTP_KEEP_ALIVE: - - DATASTORE_USE_GRPC: - EQ_STORAGE_BACKEND: - EQ_ENABLE_SECURE_SESSION_COOKIE: - EQ_RABBITMQ_ENABLED: - EQ_ENABLE_HTML_MINIFY: - EQ_RABBITMQ_HOST: - EQ_RABBITMQ_HOST_SECONDARY: - EQ_QUESTIONNAIRE_STATE_TABLE_NAME: - EQ_SESSION_TABLE_NAME: - EQ_USED_JTI_CLAIM_TABLE_NAME: - EQ_SUBMISSION_BACKEND: - EQ_FEEDBACK_BACKEND: - EQ_PUBLISHER_BACKEND: - EQ_SUBMISSION_CONFIRMATION_BACKEND: - EQ_FULFILMENT_TOPIC_ID: - EQ_INDIVIDUAL_RESPONSE_LIMIT: - EQ_INDIVIDUAL_RESPONSE_POSTAL_DEADLINE: - EQ_FEEDBACK_LIMIT: - CDN_URL: - CDN_ASSETS_PATH: - ADDRESS_LOOKUP_API_URL: - ADDRESS_LOOKUP_API_AUTH_ENABLED: - ADDRESS_LOOKUP_API_AUTH_TOKEN_LEEWAY_IN_SECONDS: - CONFIRMATION_EMAIL_LIMIT: - GOOGLE_TAG_MANAGER_ID: - GOOGLE_TAG_MANAGER_AUTH: - CLOUD_ARMOR_POLICY_NAME: - VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS: - -run: - path: bash - args: - - -exc - - | - export GOOGLE_APPLICATION_CREDENTIALS=/root/gcloud-service-key.json - cat >$GOOGLE_APPLICATION_CREDENTIALS <$GOOGLE_APPLICATION_CREDENTIALS <&1); do sleep 5; printf '.'; done From 36736be42b6f82028595850fe2be34c405daeace Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:43:27 +0000 Subject: [PATCH 351/567] Add support in repeating GCS for static CS with repeating answers (#1268) * Add gcs support for non-repeating cs with repeating answers * Fix test for updated schema --- app/questionnaire/questionnaire_schema.py | 7 +- app/questionnaire/value_source_resolver.py | 28 ++- ...ated_summary_inside_repeating_section.json | 89 +++++++-- ...repeating_sections_with_hub_and_spoke.json | 20 +- .../forms/field_handlers/test_date_handler.py | 4 +- .../rules/test_rule_evaluator.py | 8 +- .../questionnaire/test_placeholder_parser.py | 4 +- .../test_value_source_resolver.py | 111 +++++++++-- ...d_summary_inside_repeating_section.spec.js | 172 +++++++++++++++++- ...eating_sections_with_hub_and_spoke.spec.js | 12 +- .../questionnaire/test_questionnaire_hub.py | 2 +- 11 files changed, 381 insertions(+), 76 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 04273cc0d0..8e7c3a7850 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -835,11 +835,14 @@ def get_list_name_for_answer_id(self, answer_id: str) -> str | None: if the answer is dynamic or in a repeating block or section, return the name of the list it repeats over, otherwise None. """ # Type ignore: safe to assume block exists, same for section below. - block_id: str = self.get_block_for_answer_id(answer_id)["id"] # type: ignore + block: ImmutableDict = self.get_block_for_answer_id(answer_id) # type: ignore + block_id: str = block["id"] if self.is_answer_dynamic(answer_id): return self.get_list_name_for_dynamic_answer(block_id) if self.is_answer_in_list_collector_repeating_block(answer_id): - return self._list_names_by_list_repeating_block_id[block_id] + return self.list_names_by_list_repeating_block_id[block_id] + if self.is_answer_in_list_collector_block(answer_id): + return block["for_list"] # type: ignore if self.is_answer_in_repeating_section(answer_id): section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore return self.get_repeating_list_for_section(section_id) diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index aae26c336d..c4050e6f16 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -75,6 +75,23 @@ def _get_answer_value( ): return answer.value + def _resolve_list_item_id_for_answer_id(self, answer_id: str) -> str | None: + """ + If there's a list item id and the answer is repeating, return the list item id to resolve the instance of the answer + However if the answer is repeating for a different list, return None so that the repeating answer id resolves to a list + """ + if self.list_item_id and ( + list_name_for_answer := self.schema.get_list_name_for_answer_id(answer_id) + ): + # if there is a current list, and it differs to the repeating answer one, return None + if ( + self.location + and self.location.list_name + and self.location.list_name != list_name_for_answer + ): + return None + return self.list_item_id + def _resolve_list_item_id_for_value_source( self, value_source: Mapping ) -> str | None: @@ -102,12 +119,8 @@ def _resolve_list_item_id_for_value_source( else None ) - return ( - self.list_item_id - if self.list_item_id - and self.schema.is_repeating_answer(value_source["identifier"]) - else None - ) + if value_source["source"] == "answers": + return self._resolve_list_item_id_for_answer_id(value_source["identifier"]) def _resolve_repeating_answers_for_list( self, *, answer_id: str, list_name: str @@ -242,11 +255,10 @@ def _resolve_summary_with_calculation( # the calculation object for the old type of calculated summary block may contain answers_to_calculate instead of operation if calculation.get("answers_to_calculate"): operator = self.get_calculation_operator(calculation["calculation_type"]) - list_item_id = self._resolve_list_item_id_for_value_source(value_source) values = [ self._get_answer_value( answer_id=answer_id, - list_item_id=list_item_id, + list_item_id=self._resolve_list_item_id_for_answer_id(answer_id), assess_routing_path=assess_routing_path, ) for answer_id in calculation["answers_to_calculate"] diff --git a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json index a2618836e6..41722556d2 100644 --- a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json +++ b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json @@ -30,7 +30,17 @@ "id": "base-costs-section", "title": "Vehicle Costs", "summary": { - "show_on_completion": true + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "costs", + "title": "Base Costs", + "add_link_text": "Add another base cost", + "empty_list_text": "There are no base costs" + } + ], + "show_non_item_answers": true }, "groups": [ { @@ -44,19 +54,6 @@ "type": "General", "id": "any-cost-question", "title": "Do you have any outgoing costs for owning a vehicle?", - "guidance": { - "contents": [ - { - "title": "Notes for testing" - }, - { - "description": "For the first iteration, calculated summaries of repeating answers don’t work in a grand calculated summary in a separate repeating section" - }, - { - "description": "So you should answer no to this question and remove this guidance once separate repeating answer calculated summaries are supported" - } - ] - }, "answers": [ { "type": "Radio", @@ -216,6 +213,49 @@ ] } }, + "repeating_blocks": [ + { + "id": "cost-repeating-block-1", + "type": "ListRepeatingQuestion", + "question": { + "id": "cost-repeating-block-1-question", + "type": "General", + "title": { + "text": "What is the base monthly rate for {cost_name} for a single vehicle?", + "placeholders": [ + { + "placeholder": "cost_name", + "value": { + "source": "answers", + "identifier": "cost-name" + } + } + ] + }, + "answers": [ + { + "id": "repeating-block-1-cost-base", + "label": { + "text": "Base {transformed_value} expenditure", + "placeholders": [ + { + "placeholder": "transformed_value", + "value": { + "source": "answers", + "identifier": "cost-name" + } + } + ] + }, + "mandatory": true, + "type": "Currency", + "currency": "GBP", + "decimal_places": 2 + } + ] + } + } + ], "summary": { "title": "cost", "item_title": { @@ -262,7 +302,7 @@ }, "question": { "id": "dynamic-answer-question", - "title": "How much do you spend per month on the following for a single vehicle?", + "title": "How much extra do you normally spend per month for a single vehicle?", "type": "General", "dynamic_answers": { "values": { @@ -272,7 +312,7 @@ "answers": [ { "label": { - "text": "Single vehicle {transformed_value}", + "text": "Extra {transformed_value} expenditure", "placeholders": [ { "placeholder": "transformed_value", @@ -283,9 +323,9 @@ } ] }, - "id": "cost-of-cost", + "id": "dynamic-answer-cost-extra", "type": "Currency", - "mandatory": true, + "mandatory": false, "currency": "GBP", "decimal_places": 2 } @@ -322,7 +362,11 @@ "+": [ { "source": "answers", - "identifier": "cost-of-cost" + "identifier": "repeating-block-1-cost-base" + }, + { + "source": "answers", + "identifier": "dynamic-answer-cost-extra" }, { "source": "answers", @@ -814,6 +858,13 @@ "id": "gcs-breakdown-block", "question": { "id": "gcs-breakdown-question", + "guidance": { + "contents": [ + { + "description": "Currently this question is not revisited when the grand calculated summary changes. When grand calculated summary dependencies are implemented, this guidance should be removed, and this block should become incomplete upon the GCS changing." + } + ] + }, "title": { "text": "How do you pay for the monthly fees of {vehicle_cost}?", "placeholders": [ diff --git a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json index 6fd40a7e8b..f746def37d 100644 --- a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json +++ b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json @@ -436,13 +436,13 @@ "title": "What is the name of the person?", "answers": [ { - "id": "first-name", + "id": "forename", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "last-name", + "id": "surname", "label": "Last name", "mandatory": true, "type": "TextField" @@ -459,13 +459,13 @@ "title": "What is the name of the person?", "answers": [ { - "id": "first-name", + "id": "forename", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "last-name", + "id": "surname", "label": "Last name", "mandatory": true, "type": "TextField" @@ -516,11 +516,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "first-name" + "identifier": "forename" }, { "source": "answers", - "identifier": "last-name" + "identifier": "surname" } ] }, @@ -1219,11 +1219,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "first-name" + "identifier": "forename" }, { "source": "answers", - "identifier": "last-name" + "identifier": "surname" } ], "delimiter": " " @@ -1278,11 +1278,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "first-name" + "identifier": "forename" }, { "source": "answers", - "identifier": "last-name" + "identifier": "surname" } ] }, diff --git a/tests/app/forms/field_handlers/test_date_handler.py b/tests/app/forms/field_handlers/test_date_handler.py index 036ca62553..08c941cc2a 100644 --- a/tests/app/forms/field_handlers/test_date_handler.py +++ b/tests/app/forms/field_handlers/test_date_handler.py @@ -146,8 +146,8 @@ def test_get_referenced_offset_value_for_answer_id( @patch( - "app.questionnaire.questionnaire_schema.QuestionnaireSchema.is_repeating_answer", - return_value=True, + "app.questionnaire.questionnaire_schema.QuestionnaireSchema.get_list_name_for_answer_id", + return_value="list", ) def test_get_referenced_offset_value_with_list_item_id( app, value_source_resolver, rule_evaluator diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index 8ca282a57d..4775ba1e2b 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -54,7 +54,7 @@ def get_rule_evaluator( ): if not schema: schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=True) + schema.get_list_name_for_answer_id = Mock(return_value="mock-list") schema.get_default_answer = Mock(return_value=None) schema.is_answer_dynamic = Mock(return_value=False) schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) @@ -793,7 +793,7 @@ def test_date_value(rule, expected_result): def test_answer_source_outside_of_repeating_section(): schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=False) + schema.get_list_name_for_answer_id = Mock(return_value=None) answer_store = AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) rule_evaluator = get_rule_evaluator( @@ -857,7 +857,7 @@ def test_answer_source_not_on_path_non_repeating_section(is_answer_on_path): @pytest.mark.parametrize("is_answer_on_path", [True, False]) def test_answer_source_not_on_path_repeating_section(is_answer_on_path): schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=True) + schema.get_list_name_for_answer_id = Mock(return_value="mock-list") location = Location( section_id="test-section", block_id="test-block", list_item_id="item-1" ) @@ -1098,7 +1098,7 @@ def test_supplementary_data_source( ): """Tests rule evaluation of repeating and non-repeating supplementary data source inside a repeat""" schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=False) + schema.get_list_name_for_answer_id = Mock(return_value=None) answer_store = AnswerStore([{"answer_id": "same-answer", "value": value}]) rule_evaluator = get_rule_evaluator( diff --git a/tests/app/questionnaire/test_placeholder_parser.py b/tests/app/questionnaire/test_placeholder_parser.py index aa14516725..bc2522c439 100644 --- a/tests/app/questionnaire/test_placeholder_parser.py +++ b/tests/app/questionnaire/test_placeholder_parser.py @@ -800,7 +800,7 @@ def test_placeholder_resolves_name_is_duplicate_chain( } ] - mock_schema.is_repeating_answer = Mock(return_value=True) + mock_schema.get_list_name_for_answer_id = Mock(return_value="people") parser = PlaceholderParser( language="en", @@ -911,7 +911,7 @@ def test_placeholder_resolves_list_has_items_chain( } ] - mock_schema.is_repeating_answer = Mock(return_value=True) + mock_schema.get_list_name_for_answer_id = Mock(return_value="people") parser = PlaceholderParser( language="en", diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index f75cc6ada8..7bb2975d3c 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-lines from typing import Optional, Union import pytest @@ -72,6 +73,9 @@ def get_value_source_resolver( if not schema: schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=bool(list_item_id)) + schema.get_list_name_for_answer_id = Mock( + return_value="list" if list_item_id else None + ) schema.is_answer_dynamic = Mock(return_value=False) schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) @@ -150,7 +154,7 @@ def test_answer_source_with_list_item_id_no_list_item_selector(): def test_list_item_id_ignored_if_answer_not_in_list_collector_or_repeat(): schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=False) + schema.get_list_name_for_answer_id = Mock(return_value=None) value_source_resolver = get_value_source_resolver( schema=schema, @@ -261,7 +265,7 @@ def test_answer_source_with_list_item_selector_list_first_item(): def test_answer_source_outside_of_repeating_section(): schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=False) + schema.get_list_name_for_answer_id = Mock(return_value=None) answer_store = AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) value_source_resolver = get_value_source_resolver( @@ -322,7 +326,7 @@ def test_answer_source_not_on_path_non_repeating_section(is_answer_on_path): @pytest.mark.parametrize("is_answer_on_path", [True, False]) def test_answer_source_not_on_path_repeating_section(is_answer_on_path): schema = get_mock_schema() - schema.is_repeating_answer = Mock(return_value=True) + schema.get_list_name_for_answer_id = Mock(return_value="some-list") location = Location( section_id="test-section", block_id="test-block", list_item_id="item-1" ) @@ -426,17 +430,87 @@ def test_answer_source_dynamic_answer( ) -@pytest.mark.parametrize("answer_values", ([], [10, 5], [100, 200, 300])) -def test_answer_source_repeating_block_answers( - mocker, placeholder_transform_question_repeating_block, answer_values +@pytest.mark.parametrize( + "answer_values, list_name, list_item_id, expected", + ( + ([10, 5], "supermarkets", "item-1", 10), + ([10, 5], "cars", "item-1", [10, 5]), + ([10, 5], None, None, [10, 5]), + ), +) +def test_answer_source_dynamic_answer_in_different_repeat( + placeholder_transform_question_dynamic_answers_json, + answer_values, + list_name, + list_item_id, + expected, ): """ - Tests that an answer id from a repeating block resolves to the list of answers for that list and repeating block question + Tests that a dynamic answer id as a value source resolves to the specific instance in the context of a repeat + and the list of all answers when outside a repeat or in a repeat for a different list. """ - schema = mocker.MagicMock() + schema = MagicMock() + schema.is_answer_dynamic = Mock(return_value=True) + schema.get_block_for_answer_id = Mock( + return_value={"question": placeholder_transform_question_dynamic_answers_json} + ) + schema.get_list_name_for_answer_id = Mock(return_value="supermarkets") + list_item_ids = get_list_items(len(answer_values)) + value_source_resolver = get_value_source_resolver( + data_stores=DataStores( + answer_store=AnswerStore( + [ + AnswerDict( + answer_id="percentage-of-shopping", + value=value, + list_item_id=list_item_id, + ) + for list_item_id, value in zip(list_item_ids, answer_values) + ] + ), + list_store=ListStore([{"name": "supermarkets", "items": list_item_ids}]), + ), + location=Location( + section_id="section-1", list_name=list_name, list_item_id=list_item_id + ), + list_item_id=list_item_id, + schema=schema, + ) + assert ( + value_source_resolver.resolve( + {"source": "answers", "identifier": "percentage-of-shopping"} + ) + == expected + ) + + +@pytest.mark.parametrize( + "answer_values, list_name, list_item_id, expected", + ( + ([], None, None, []), + ([10, 5], None, None, [10, 5]), + ([100, 200, 300], None, None, [100, 200, 300]), + ([10, 5], "transport", "item-1", 10), + ([100, 200, 300], "transport", "item-2", 200), + ([10, 5], "shopping", "item-1", [10, 5]), + ([100, 200, 300], "shopping", "item-2", [100, 200, 300]), + ), +) +def test_answer_source_repeating_block_answers_in_repeat( + placeholder_transform_question_repeating_block, + answer_values, + list_name, + list_item_id, + expected, +): + """ + Tests that an answer id from a repeating block resolves to the specific answer in the context of a repeat. + And the list of answers for that list and repeating block question outside a repeat or in a repeat for another list. + """ + schema = MagicMock() schema.list_names_by_list_repeating_block_id = {"repeating-block-1": "transport"} schema.is_answer_dynamic = Mock(return_value=False) - schema.is_answer_in_list_collector_repeating_block = Mock(return_value=True) + schema.get_list_name_for_answer_id = Mock(return_value="transport") schema.get_block_for_answer_id = Mock( return_value=placeholder_transform_question_repeating_block ) @@ -456,12 +530,16 @@ def test_answer_source_repeating_block_answers( list_store=ListStore([{"name": "transport", "items": list_item_ids}]), ), schema=schema, + location=Location( + section_id="section-1", list_name=list_name, list_item_id=list_item_id + ), + list_item_id=list_item_id, ) assert ( value_source_resolver.resolve( {"source": "answers", "identifier": "transport-cost"} ) - == answer_values + == expected ) @@ -829,13 +907,18 @@ def mock_get_block(block_id: str) -> dict: } return blocks[block_id] - def mock_is_answer_repeating(answer_id: str) -> bool: - return (answer_id in {"answer-1", "answer-2"} and cs_list_item_id_1) or ( - answer_id in {"answer-3", "answer-4"} and cs_list_item_id_2 + def mock_get_list_name_for_answer_id(answer_id: str) -> str | None: + return ( + "mock-list" + if (answer_id in {"answer-1", "answer-2"} and cs_list_item_id_1) + or (answer_id in {"answer-3", "answer-4"} and cs_list_item_id_2) + else None ) schema.get_block = Mock(side_effect=mock_get_block) - schema.is_repeating_answer = Mock(side_effect=mock_is_answer_repeating) + schema.get_list_name_for_answer_id = Mock( + side_effect=mock_get_list_name_for_answer_id + ) schema.is_answer_dynamic = Mock(return_value=False) schema.is_answer_in_list_collector_repeating_block = Mock(return_value=False) diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index 60f26906c2..ccd0a024fa 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -3,32 +3,57 @@ import { expect } from "@wdio/globals"; import AddVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-add.page.js"; import AnyCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/any-cost.page.js"; import AnyVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/any-vehicle.page.js"; +import BaseCostPaymentBreakdownPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-cost-payment-breakdown.page"; import BaseCostsSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-costs-section-summary.page.js"; import CalculatedSummaryBaseCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/calculated-summary-base-cost.page.js"; import CalculatedSummaryRunningCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/calculated-summary-running-cost.page.js"; +import CostRepeatingBlock1RepeatingBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/cost-repeating-block-1-repeating-block.page"; +import DynamicCostBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/dynamic-cost-block.page"; +import FinanceCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/finance-cost.page"; +import GcsBreakdownBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-breakdown-block.page"; +import GcsPipingPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-piping.page"; import GrandCalculatedSummaryVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/grand-calculated-summary-vehicle.page.js"; import HubPage from "../../../base_pages/hub.page"; +import ListCollectorCostAddPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-cost-add.page"; +import ListCollectorCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-cost.page"; +import ListCollectorCostRemovePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-cost-remove.page"; import ListCollectorPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector.page.js"; import VehicleDetailsSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-details-section-summary.page.js"; import VehicleFuelBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-fuel-block.page.js"; import VehicleMaintenanceBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicle-maintenance-block.page.js"; import VehiclesSectionPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/vehicles-section-summary.page.js"; -import FinanceCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/finance-cost.page"; -import BaseCostPaymentBreakdownPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/base-cost-payment-breakdown.page"; -import GcsBreakdownBlockPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-breakdown-block.page"; -import GcsPipingPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/gcs-piping.page"; describe("Grand Calculated Summary inside a repeating section", () => { let vehicleListItemIds = []; + let costListItemIds = []; + const summaryActions = 'dd[class="ons-summary__actions"]'; + const dynamicAnswerChangeLink = (answerIndex) => $$(summaryActions)[answerIndex].$("a"); + before("Load the survey", async () => { await browser.openQuestionnaire("test_grand_calculated_summary_inside_repeating_section.json"); }); it("Given I have a Grand Calculated Summary inside a repeating section, When I reach it for the first list item, Then I see placeholder content rendered correctly", async () => { await click(HubPage.submit()); - await $(AnyCostPage.no()).click(); + await $(AnyCostPage.yes()).click(); await click(AnyCostPage.submit()); - await $(FinanceCostPage.answer()).setValue(90); + await $(ListCollectorCostAddPage.costName()).selectByAttribute("value", "Road Tax"); + await click(ListCollectorCostAddPage.submit()); + await $(CostRepeatingBlock1RepeatingBlockPage.repeatingBlock1CostBase()).setValue(5); + await click(CostRepeatingBlock1RepeatingBlockPage.submit()); + await $(ListCollectorCostPage.yes()).click(); + await click(ListCollectorCostPage.submit()); + await $(ListCollectorCostAddPage.costName()).selectByAttribute("value", "Parking Permit"); + await click(ListCollectorCostAddPage.submit()); + await $(CostRepeatingBlock1RepeatingBlockPage.repeatingBlock1CostBase()).setValue(12); + await click(CostRepeatingBlock1RepeatingBlockPage.submit()); + costListItemIds = await listItemIds(); + await $(ListCollectorCostPage.no()).click(); + await click(ListCollectorCostPage.submit()); + await $$(DynamicCostBlockPage.inputs())[0].setValue(5); + await $$(DynamicCostBlockPage.inputs())[1].setValue(8); + await click(DynamicCostBlockPage.submit()); + await $(FinanceCostPage.answer()).setValue(60); await click(FinanceCostPage.submit()); await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total base cost for any owned vehicle to be £90.00. Is this correct?", @@ -168,7 +193,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I change a non repeating answer which results in the section being incomplete, When I press continue, Then I go to the next incomplete location with the list item id preserved", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); await $(CalculatedSummaryBaseCostPage.financeCostAnswerEdit()).click(); - await $(FinanceCostPage.answer()).setValue(100); + await $(FinanceCostPage.answer()).setValue(70); await click(FinanceCostPage.submit()); await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( @@ -219,7 +244,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("base-costs-section")).click(); await $(BaseCostsSectionPage.financeCostAnswerEdit()).click(); - await $(FinanceCostPage.answer()).setValue(110); + await $(FinanceCostPage.answer()).setValue(80); await click(FinanceCostPage.submit()); await click(CalculatedSummaryBaseCostPage.submit()); await click(BaseCostPaymentBreakdownPage.submit()); @@ -247,4 +272,135 @@ describe("Grand Calculated Summary inside a repeating section", () => { "The total cost of owning and running your Van is calculated to be £230.00. Is this correct?", ); }); + + it("Given I go to the non-repeating calculated summary, When I click a change link for a dynamic answer and press continue twice, Then I go back to the Grand Calculated Summary for the correct list item", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await dynamicAnswerChangeLink(2).click(); + await expect(browser).toHaveUrlContaining(DynamicCostBlockPage.pageName); + await click(DynamicCostBlockPage.submit()); + await click(CalculatedSummaryBaseCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I go to the non-repeating calculated summary, When I click a change link for a repeating block answer and press continue twice, Then I go back to the Grand Calculated Summary for the correct list item", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await dynamicAnswerChangeLink(0).click(); + await expect(browser).toHaveUrlContaining(CostRepeatingBlock1RepeatingBlockPage.pageName); + await expect(browser).toHaveUrlContaining(`costs/${costListItemIds[0]}/`); + await click(CostRepeatingBlock1RepeatingBlockPage.submit()); + await click(CalculatedSummaryBaseCostPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + }); + + it("Given I edit a dynamic answer from the non-repeating calculated summary, When I return to the Grand Calculated Summary, Then I see the correct total", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await dynamicAnswerChangeLink(3).click(); + await $$(DynamicCostBlockPage.inputs())[1].setValue(28); + await click(DynamicCostBlockPage.submit()); + await click(CalculatedSummaryBaseCostPage.submit()); + await click(BaseCostPaymentBreakdownPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Van is calculated to be £250.00. Is this correct?", + ); + }); + + it("Given I edit a repeating block answer from the non-repeating calculated summary, When I return to the Grand Calculated Summary, Then I see the correct total", async () => { + await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); + await dynamicAnswerChangeLink(1).click(); + await expect(browser).toHaveUrlContaining(CostRepeatingBlock1RepeatingBlockPage.pageName); + await expect(browser).toHaveUrlContaining(`costs/${costListItemIds[1]}/`); + await $(CostRepeatingBlock1RepeatingBlockPage.repeatingBlock1CostBase()).setValue(22); + await click(CostRepeatingBlock1RepeatingBlockPage.submit()); + await click(CalculatedSummaryBaseCostPage.submit()); + await click(BaseCostPaymentBreakdownPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Van is calculated to be £260.00. Is this correct?", + ); + }); + + it("Given I complete the Grand Calculated Summary, When I return to the Hub, Then I see the other repeating section is incomplete as the static calculated summary changed", async () => { + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Completed"); + }); + + it("Given I go to the other repeating section, When I enter the section, Then I see the grand calculated summary with correctly updated totals", async () => { + await click(HubPage.submit()); + await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Car is calculated to be £365.00. Is this correct?", + ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + }); + + it("Given I remove an item from the costs lists, When I return to the Hub, Then I see both repeating sections revert to partially complete", async () => { + await $(HubPage.summaryRowLink("base-costs-section")).click(); + await $(BaseCostsSectionPage.costsListRemoveLink(1)).click(); + await $(ListCollectorCostRemovePage.yes()).click(); + await click(ListCollectorCostRemovePage.submit()); + await click(DynamicCostBlockPage.submit()); + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( + "We calculate the total base cost for any owned vehicle to be £130.00. Is this correct?", + ); + await click(CalculatedSummaryBaseCostPage.submit()); + await click(BaseCostPaymentBreakdownPage.submit()); + await click(BaseCostsSectionPage.submit()); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Partially completed"); + }); + + it("Given I revisit both repeating sections, When I start each, Then I see the grand calculated summary page with correct values", async () => { + await click(HubPage.submit()); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Car is calculated to be £355.00. Is this correct?", + ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await click(HubPage.submit()); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Van is calculated to be £250.00. Is this correct?", + ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + }); + + it("Given I add an item to the costs lists, When I return to the Hub, Then I see both repeating sections revert to partially complete", async () => { + await $(HubPage.summaryRowLink("base-costs-section")).click(); + await $(BaseCostsSectionPage.costsListAddLink()).click(); + await $(ListCollectorCostAddPage.costName()).selectByAttribute("value", "Road Tax"); + await click(ListCollectorCostAddPage.submit()); + await $(CostRepeatingBlock1RepeatingBlockPage.repeatingBlock1CostBase()).setValue(30); + await click(CostRepeatingBlock1RepeatingBlockPage.submit()); + await $(ListCollectorCostPage.no()).click(); + await click(ListCollectorCostPage.submit()); + await $$(DynamicCostBlockPage.inputs())[1].setValue(20); + await click(DynamicCostBlockPage.submit()); + await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( + "We calculate the total base cost for any owned vehicle to be £180.00. Is this correct?", + ); + await click(CalculatedSummaryBaseCostPage.submit()); + await click(BaseCostPaymentBreakdownPage.submit()); + await click(BaseCostsSectionPage.submit()); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Partially completed"); + }); + + it("Given I revisit both repeating sections with new items, When I start each, Then I see the grand calculated summary page with correct values", async () => { + await click(HubPage.submit()); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Car is calculated to be £405.00. Is this correct?", + ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await click(VehicleDetailsSectionPage.submit()); + await click(HubPage.submit()); + await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( + "The total cost of owning and running your Van is calculated to be £300.00. Is this correct?", + ); + }); }); diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index 8f22372d72..8815cd46b1 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -162,16 +162,16 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // Add first visitor await $(SectionSummaryPage.visitorListAddLink()).click(); - await $(VisitorsListCollectorAddPage.firstName()).setValue("Joe"); - await $(VisitorsListCollectorAddPage.lastName()).setValue("Public"); + await $(VisitorsListCollectorAddPage.forename()).setValue("Joe"); + await $(VisitorsListCollectorAddPage.surname()).setValue("Public"); await click(VisitorsListCollectorAddPage.submit()); await expect(browser).toHaveUrlContaining("/questionnaire/visitors-block"); // Add second visitor await $(VisitorsListCollectorPage.yes()).click(); await click(VisitorsListCollectorPage.submit()); - await $(VisitorsListCollectorAddPage.firstName()).setValue("Yvonne"); - await $(VisitorsListCollectorAddPage.lastName()).setValue("Yoe"); + await $(VisitorsListCollectorAddPage.forename()).setValue("Yvonne"); + await $(VisitorsListCollectorAddPage.surname()).setValue("Yoe"); await click(VisitorsListCollectorAddPage.submit()); // Exit the visitors list collector @@ -261,8 +261,8 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // Add another visitor await $(SectionSummaryPage.visitorListAddLink()).click(); - await $(VisitorsListCollectorAddPage.firstName()).setValue("Anna"); - await $(VisitorsListCollectorAddPage.lastName()).setValue("Doe"); + await $(VisitorsListCollectorAddPage.forename()).setValue("Anna"); + await $(VisitorsListCollectorAddPage.surname()).setValue("Doe"); await click(VisitorsListCollectorAddPage.submit()); await $(VisitorsListCollectorPage.no()).click(); diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index adb4eb8ffd..f6e3f76a3e 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -120,7 +120,7 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): # Visitors self.post({"visitors-anyone-else": "Yes"}) - self.post({"first-name": "Joe", "last-name": "Public"}) + self.post({"forename": "Joe", "surname": "Public"}) # Go back to hub self.post({"visitors-anyone-else": "No"}) From 1d71fd4478e01f090535d17013dd7195921574d5 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Tue, 19 Dec 2023 12:30:09 +0000 Subject: [PATCH 352/567] Prevent duplicate answer_ids across different list collectors (#1272) --- schemas/test/en/test_benchmark_business.json | 2 +- .../test_list_collector_section_summary.json | 2 +- ...repeating_sections_with_hub_and_spoke.json | 20 +++++++++---------- ...eating_sections_with_hub_and_spoke.spec.js | 12 +++++------ .../questionnaire/test_questionnaire_hub.py | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index f33ba8da81..db844fefaf 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -1672,7 +1672,7 @@ "title": "Are you sure you want to remove this company or UK branch?", "answers": [ { - "id": "remove-confirmation", + "id": "remove-confirmation-company", "mandatory": true, "type": "Radio", "options": [ diff --git a/schemas/test/en/test_list_collector_section_summary.json b/schemas/test/en/test_list_collector_section_summary.json index 901fc6a680..f739e7583d 100644 --- a/schemas/test/en/test_list_collector_section_summary.json +++ b/schemas/test/en/test_list_collector_section_summary.json @@ -273,7 +273,7 @@ "title": "Are you sure you want to remove this company or UK branch?", "answers": [ { - "id": "remove-confirmation", + "id": "remove-confirmation-company", "mandatory": true, "type": "Radio", "options": [ diff --git a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json index f746def37d..224a7bb5cc 100644 --- a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json +++ b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json @@ -436,13 +436,13 @@ "title": "What is the name of the person?", "answers": [ { - "id": "forename", + "id": "visitor-first-name", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "surname", + "id": "visitor-last-name", "label": "Last name", "mandatory": true, "type": "TextField" @@ -459,13 +459,13 @@ "title": "What is the name of the person?", "answers": [ { - "id": "forename", + "id": "visitor-first-name", "label": "First name", "mandatory": true, "type": "TextField" }, { - "id": "surname", + "id": "visitor-last-name", "label": "Last name", "mandatory": true, "type": "TextField" @@ -516,11 +516,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "forename" + "identifier": "visitor-first-name" }, { "source": "answers", - "identifier": "surname" + "identifier": "visitor-last-name" } ] }, @@ -1219,11 +1219,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "forename" + "identifier": "visitor-first-name" }, { "source": "answers", - "identifier": "surname" + "identifier": "visitor-last-name" } ], "delimiter": " " @@ -1278,11 +1278,11 @@ "list_to_concatenate": [ { "source": "answers", - "identifier": "forename" + "identifier": "visitor-first-name" }, { "source": "answers", - "identifier": "surname" + "identifier": "visitor-last-name" } ] }, diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index 8815cd46b1..1ade7a4872 100644 --- a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -162,16 +162,16 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // Add first visitor await $(SectionSummaryPage.visitorListAddLink()).click(); - await $(VisitorsListCollectorAddPage.forename()).setValue("Joe"); - await $(VisitorsListCollectorAddPage.surname()).setValue("Public"); + await $(VisitorsListCollectorAddPage.visitorFirstName()).setValue("Joe"); + await $(VisitorsListCollectorAddPage.visitorLastName()).setValue("Public"); await click(VisitorsListCollectorAddPage.submit()); await expect(browser).toHaveUrlContaining("/questionnaire/visitors-block"); // Add second visitor await $(VisitorsListCollectorPage.yes()).click(); await click(VisitorsListCollectorPage.submit()); - await $(VisitorsListCollectorAddPage.forename()).setValue("Yvonne"); - await $(VisitorsListCollectorAddPage.surname()).setValue("Yoe"); + await $(VisitorsListCollectorAddPage.visitorFirstName()).setValue("Yvonne"); + await $(VisitorsListCollectorAddPage.visitorLastName()).setValue("Yoe"); await click(VisitorsListCollectorAddPage.submit()); // Exit the visitors list collector @@ -261,8 +261,8 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // Add another visitor await $(SectionSummaryPage.visitorListAddLink()).click(); - await $(VisitorsListCollectorAddPage.forename()).setValue("Anna"); - await $(VisitorsListCollectorAddPage.surname()).setValue("Doe"); + await $(VisitorsListCollectorAddPage.visitorFirstName()).setValue("Anna"); + await $(VisitorsListCollectorAddPage.visitorLastName()).setValue("Doe"); await click(VisitorsListCollectorAddPage.submit()); await $(VisitorsListCollectorPage.no()).click(); diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index f6e3f76a3e..685476caa1 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -120,7 +120,7 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): # Visitors self.post({"visitors-anyone-else": "Yes"}) - self.post({"forename": "Joe", "surname": "Public"}) + self.post({"visitor-first-name": "Joe", "visitor-last-name": "Public"}) # Go back to hub self.post({"visitors-anyone-else": "No"}) From 5b7d1dcb3c7b4905093ca4e0fbb6f50b96f82893 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:03:34 +0000 Subject: [PATCH 353/567] Schemas v3.81.0 (#1282) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index fbd42358a5..1c0211200b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.80.1 +v3.81.0 From 1041777cd658d03e4b25323afbfed98d1af4cdea Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:13:22 +0000 Subject: [PATCH 354/567] Schemas v3.82.0 (#1286) --- .schemas-version | 2 +- app/translations/messages.pot | 116 +++++++++++++++++----------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/.schemas-version b/.schemas-version index 1c0211200b..8e4ae25dc0 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.81.0 +v3.82.0 diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 73959fbe20..991f75061d 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -1,14 +1,14 @@ # Translations template for PROJECT. -# Copyright (C) 2023 ORGANIZATION +# Copyright (C) 2024 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2023. +# FIRST AUTHOR , 2024. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2023-12-04 17:20+0000\n" +"POT-Creation-Date: 2024-01-02 14:50+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,7 +41,7 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:733 +#: app/jinja_filters.py:731 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:25 msgid "No answer provided" @@ -301,47 +301,47 @@ msgstr "" msgid "You can try to submit your feedback again." msgstr "" -#: app/routes/individual_response.py:203 +#: app/routes/individual_response.py:202 msgid "An individual access code has been sent by post" msgstr "" -#: app/routes/individual_response.py:309 +#: app/routes/individual_response.py:308 msgid "An individual access code has been sent by text" msgstr "" -#: app/survey_config/business_config.py:59 +#: app/survey_config/business_config.py:60 msgid "Help" msgstr "" -#: app/survey_config/business_config.py:73 +#: app/survey_config/business_config.py:74 msgid "My account" msgstr "" -#: app/survey_config/business_config.py:78 +#: app/survey_config/business_config.py:79 msgid "Sign out" msgstr "" -#: app/survey_config/business_config.py:88 +#: app/survey_config/business_config.py:89 #: app/survey_config/social_survey_config.py:39 msgid "What we do" msgstr "" -#: app/survey_config/business_config.py:92 +#: app/survey_config/business_config.py:93 #: app/survey_config/social_survey_config.py:43 msgid "Contact us" msgstr "" -#: app/survey_config/business_config.py:97 +#: app/survey_config/business_config.py:98 #: app/survey_config/social_survey_config.py:48 msgid "Accessibility" msgstr "" -#: app/survey_config/business_config.py:107 +#: app/survey_config/business_config.py:108 #: app/survey_config/social_survey_config.py:58 msgid "Cookies" msgstr "" -#: app/survey_config/business_config.py:109 +#: app/survey_config/business_config.py:110 #: app/survey_config/social_survey_config.py:60 msgid "Privacy and data protection" msgstr "" @@ -503,13 +503,13 @@ msgstr "" #: app/views/handlers/confirm_email.py:103 #: app/views/handlers/confirm_email.py:104 -#: app/views/handlers/individual_response.py:919 +#: app/views/handlers/individual_response.py:910 msgid "No, I need to change it" msgstr "" -#: app/views/handlers/confirm_email.py:130 -#: app/views/handlers/confirmation_email.py:67 -#: app/views/handlers/feedback.py:80 app/views/handlers/question.py:177 +#: app/views/handlers/confirm_email.py:128 +#: app/views/handlers/confirmation_email.py:65 +#: app/views/handlers/feedback.py:78 app/views/handlers/question.py:173 msgid "Error: {page_title}" msgstr "" @@ -521,35 +521,35 @@ msgstr "" msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:136 +#: app/views/handlers/feedback.py:132 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:142 app/views/handlers/feedback.py:166 +#: app/views/handlers/feedback.py:138 app/views/handlers/feedback.py:162 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:145 app/views/handlers/feedback.py:146 +#: app/views/handlers/feedback.py:141 app/views/handlers/feedback.py:142 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:147 +#: app/views/handlers/feedback.py:143 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:152 app/views/handlers/feedback.py:153 +#: app/views/handlers/feedback.py:148 app/views/handlers/feedback.py:149 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:156 app/views/handlers/feedback.py:159 +#: app/views/handlers/feedback.py:152 app/views/handlers/feedback.py:155 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:174 app/views/handlers/feedback.py:184 +#: app/views/handlers/feedback.py:170 app/views/handlers/feedback.py:180 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:175 +#: app/views/handlers/feedback.py:171 msgid "Do not include confidential information, such as your contact details" msgstr "" @@ -557,136 +557,136 @@ msgstr "" msgid "Person {list_item_position}" msgstr "" -#: app/views/handlers/individual_response.py:259 +#: app/views/handlers/individual_response.py:258 msgid "Cannot answer questions for others in your household" msgstr "" -#: app/views/handlers/individual_response.py:333 +#: app/views/handlers/individual_response.py:329 msgid "How would you like {person_name} to receive a separate census?" msgstr "" -#: app/views/handlers/individual_response.py:357 +#: app/views/handlers/individual_response.py:352 msgid "Text message" msgstr "" -#: app/views/handlers/individual_response.py:359 +#: app/views/handlers/individual_response.py:354 msgid "We will need their mobile number for this" msgstr "" -#: app/views/handlers/individual_response.py:367 +#: app/views/handlers/individual_response.py:362 msgid "Post" msgstr "" -#: app/views/handlers/individual_response.py:369 +#: app/views/handlers/individual_response.py:364 msgid "" "We can only send this to an unnamed resident at the registered household " "address" msgstr "" -#: app/views/handlers/individual_response.py:378 +#: app/views/handlers/individual_response.py:373 msgid "It is no longer possible to receive an access code by post." msgstr "" -#: app/views/handlers/individual_response.py:380 +#: app/views/handlers/individual_response.py:375 msgid "Select how to send access code." msgstr "" -#: app/views/handlers/individual_response.py:383 +#: app/views/handlers/individual_response.py:378 msgid "" "For someone to complete a separate census, we need to send them an " "individual access code." msgstr "" -#: app/views/handlers/individual_response.py:429 +#: app/views/handlers/individual_response.py:424 msgid "Send individual access code" msgstr "" -#: app/views/handlers/individual_response.py:460 +#: app/views/handlers/individual_response.py:455 msgid "How would you like to answer {person_name_possessive} questions?" msgstr "" -#: app/views/handlers/individual_response.py:476 +#: app/views/handlers/individual_response.py:470 msgid "I would like to request a separate census for them to complete" msgstr "" -#: app/views/handlers/individual_response.py:482 +#: app/views/handlers/individual_response.py:476 msgid "I will ask them to answer their own questions" msgstr "" -#: app/views/handlers/individual_response.py:486 +#: app/views/handlers/individual_response.py:480 msgid "They will need the household access code from the letter we sent you" msgstr "" -#: app/views/handlers/individual_response.py:492 +#: app/views/handlers/individual_response.py:486 msgid "I will answer for {person_name}" msgstr "" -#: app/views/handlers/individual_response.py:544 +#: app/views/handlers/individual_response.py:537 msgid "How to answer questions" msgstr "" -#: app/views/handlers/individual_response.py:609 +#: app/views/handlers/individual_response.py:602 msgid "Do you want to send an individual access code for {person_name} by post?" msgstr "" -#: app/views/handlers/individual_response.py:618 +#: app/views/handlers/individual_response.py:610 msgid "" "A letter with an individual access code will be sent to your registered " "household address" msgstr "" -#: app/views/handlers/individual_response.py:625 +#: app/views/handlers/individual_response.py:617 msgid "" "The letter will be addressed to Individual Resident " "instead of the name provided" msgstr "" -#: app/views/handlers/individual_response.py:638 +#: app/views/handlers/individual_response.py:630 msgid "Yes, send the access code by post" msgstr "" -#: app/views/handlers/individual_response.py:644 +#: app/views/handlers/individual_response.py:636 msgid "No, send it another way" msgstr "" -#: app/views/handlers/individual_response.py:681 +#: app/views/handlers/individual_response.py:673 msgid "Confirm address" msgstr "" -#: app/views/handlers/individual_response.py:746 -#: app/views/handlers/individual_response.py:784 +#: app/views/handlers/individual_response.py:738 +#: app/views/handlers/individual_response.py:776 msgid "Separate Census" msgstr "" -#: app/views/handlers/individual_response.py:750 +#: app/views/handlers/individual_response.py:742 msgid "Who do you need to request a separate census for?" msgstr "" -#: app/views/handlers/individual_response.py:808 +#: app/views/handlers/individual_response.py:800 msgid "What is {person_name_possessive} mobile number?" msgstr "" -#: app/views/handlers/individual_response.py:821 +#: app/views/handlers/individual_response.py:812 msgid "UK mobile number" msgstr "" -#: app/views/handlers/individual_response.py:822 +#: app/views/handlers/individual_response.py:813 msgid "This will not be stored and only used once to send the access code" msgstr "" -#: app/views/handlers/individual_response.py:857 +#: app/views/handlers/individual_response.py:848 msgid "Mobile number" msgstr "" -#: app/views/handlers/individual_response.py:906 +#: app/views/handlers/individual_response.py:897 msgid "Is this mobile number correct?" msgstr "" -#: app/views/handlers/individual_response.py:915 +#: app/views/handlers/individual_response.py:906 msgid "Yes, send the text" msgstr "" -#: app/views/handlers/individual_response.py:957 +#: app/views/handlers/individual_response.py:948 msgid "Confirm mobile number" msgstr "" From 39e7a5967d36f3e9f3bad2aece80c135a90194a6 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:27:10 +0000 Subject: [PATCH 355/567] Schemas v3.83.1 (#1290) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 8e4ae25dc0..4f139b6c63 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.82.0 +v3.83.1 From 420f6a89e93e481d37f09fb1e145f99353a091b5 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Tue, 9 Jan 2024 10:00:10 +0000 Subject: [PATCH 356/567] Remove redundant awaits found in PyCharm and await-thenable linting (#1287) --- tests/functional/spec/custom_page_titles.spec.js | 10 +++++----- .../features/dynamic_answer_options/function_driven.js | 6 +++--- tests/functional/spec/features/routing/date.spec.js | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/functional/spec/custom_page_titles.spec.js b/tests/functional/spec/custom_page_titles.spec.js index 135e7b1af9..dc04f6fc2c 100644 --- a/tests/functional/spec/custom_page_titles.spec.js +++ b/tests/functional/spec/custom_page_titles.spec.js @@ -53,18 +53,18 @@ describe("Feature: Custom Page Titles", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); - expectedPageTitle = browser.getTitle(); - await expect(await expectedPageTitle).toBe("How Person 1 is related to Person 3 - Test Custom Page Titles"); + expectedPageTitle = await browser.getTitle(); + await expect(expectedPageTitle).toBe("How Person 1 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).toBe("How Person 2 is related to Person 3 - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("How Person 2 is related to Person 3 - Test Custom Page Titles"); await $(RelationshipsPage.sonOrDaughter()).click(); await click(RelationshipsPage.submit()); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).toBe("Custom section summary page title - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Custom section summary page title - Test Custom Page Titles"); }); it("When I navigate to list edit and remove pages Then I should see the custom page titles", async () => { @@ -74,7 +74,7 @@ describe("Feature: Custom Page Titles", () => { await $(ListCollectorEditPage.previous()).click(); await $(ListCollectorPage.listRemoveLink(1)).click(); expectedPageTitle = await browser.getTitle(); - await expect(await expectedPageTitle).toBe("Remove person 1 - Test Custom Page Titles"); + await expect(expectedPageTitle).toBe("Remove person 1 - Test Custom Page Titles"); }); it("When I navigate to a repeating section which has custom page title, Then all page titles in the section should have the correct prefix", async () => { diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 23f5a05498..6c5605edcc 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -60,7 +60,7 @@ testCases.forEach((testCase) => { describe("Given a dynamic answer options questionnaire and I am on the radio answer page", () => { it("When I click a radio option, then the radio should be selected", async () => { - for (let i = 0; i < (await testCase.answerOptionCount); i++) { + for (let i = 0; i < testCase.answerOptionCount; i++) { await $(DynamicRadioPage.answerByIndex(i)).click(); await expect(await $(DynamicRadioPage.answerByIndex(i)).isSelected()).toBe(true); } @@ -88,14 +88,14 @@ testCases.forEach((testCase) => { describe("Given a dynamic answer options questionnaire and I am on the mutually exclusive page", () => { it("When I click a dynamic checkbox option, then the checkbox should be selected", async () => { - for (let i = 0; i < (await testCase.answerOptionCount); i++) { + for (let i = 0; i < testCase.answerOptionCount; i++) { await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).toBe(true); } }); it("When I click a selected option, then it should be deselected", async () => { - for (let i = 0; i < (await testCase.answerOptionCount); i++) { + for (let i = 0; i < testCase.answerOptionCount; i++) { await $(DynamicMutuallyExclusivePage.answerByIndex(i)).click(); await expect(await $(DynamicMutuallyExclusivePage.answerByIndex(i)).isSelected()).toBe(false); } diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/features/routing/date.spec.js index db7dca2226..6202b5e91b 100644 --- a/tests/functional/spec/features/routing/date.spec.js +++ b/tests/functional/spec/features/routing/date.spec.js @@ -221,7 +221,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).toContain(CorrectAnswerPage.pageName); + await expect(browserUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date equal to today, Then I should be routed to the incorrect page", async () => { @@ -232,7 +232,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); + await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -243,7 +243,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); + await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); @@ -273,7 +273,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).toContain(CorrectAnswerPage.pageName); + await expect(browserUrl).toContain(CorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -284,7 +284,7 @@ describe("Feature: Routing on a Date", () => { const browserUrl = await browser.getUrl(); - await expect(await browserUrl).toContain(IncorrectAnswerPage.pageName); + await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); }); }); }); From 12d7eaf79f37e8dd03fa3a090414acd0f3b80ff3 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:33:53 +0000 Subject: [PATCH 357/567] Schemas v3.84.0 (#1296) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 4f139b6c63..338a4a181f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.83.1 +v3.84.0 From 248ce3a121ed11d2404d778bac95c721a8150e8d Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:26:57 +0000 Subject: [PATCH 358/567] Schemas v3.85.0 (#1297) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 338a4a181f..8f8c6ffb94 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.84.0 +v3.85.0 From 524abde0765a5a7d21bb2b1881fd8443bef16b64 Mon Sep 17 00:00:00 2001 From: Katie Gardner_ONS <114991656+katie-gardner@users.noreply.github.com> Date: Thu, 18 Jan 2024 09:34:11 +0000 Subject: [PATCH 359/567] Grand calculated summary dependencies (#1270) * Handle Grand Calculated Summary Dependencies * Tests for GCS Dependencies * Use validator branch * Use correct validator branch * Make names consistent * Update functional test for dependencies * Fix unit test for renamed answer id * Use more generic type for source types * Simplify When rule dependencies (#1271) * Simplify when rule dependencies * Restore validator branch --------- Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- app/forms/questionnaire_form.py | 4 +- app/questionnaire/dependencies.py | 21 +- app/questionnaire/placeholder_parser.py | 6 +- app/questionnaire/placeholder_transforms.py | 4 +- app/questionnaire/questionnaire_schema.py | 341 ++++++++---------- .../grand_calculated_summary_context.py | 10 +- ...alculated_summary_overlapping_answers.json | 24 ++ .../test_questionnaire_schema.py | 78 ++++ ...d_summary_inside_repeating_section.spec.js | 43 ++- ...ulated_summary_overlapping_answers.spec.js | 27 +- ...lculated_summary_repeating_answers.spec.js | 8 +- 11 files changed, 350 insertions(+), 216 deletions(-) diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 0252952ef9..bea1885e14 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -18,7 +18,7 @@ from app.forms.validators import DateRangeCheck, MutuallyExclusiveCheck, SumCheck from app.questionnaire import Location, QuestionnaireSchema, QuestionSchemaType from app.questionnaire.dependencies import ( - get_routing_path_block_ids_by_section_for_calculated_summary_dependencies, + get_routing_path_block_ids_by_section_for_calculation_summary_dependencies, ) from app.questionnaire.path_finder import PathFinder from app.questionnaire.relationship_location import RelationshipLocation @@ -467,7 +467,7 @@ def get_answer_fields( if location and data_stores.progress_store: block_ids_by_section = ( - get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + get_routing_path_block_ids_by_section_for_calculation_summary_dependencies( location=location, progress_store=data_stores.progress_store, path_finder=PathFinder(schema=schema, data_stores=data_stores), diff --git a/app/questionnaire/dependencies.py b/app/questionnaire/dependencies.py index e2a28e40c7..5be9818ae6 100644 --- a/app/questionnaire/dependencies.py +++ b/app/questionnaire/dependencies.py @@ -1,13 +1,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Mapping, Sequence +from typing import TYPE_CHECKING, Iterable, Mapping, Sequence from ordered_set import OrderedSet from werkzeug.datastructures import MultiDict from app.data_models import ProgressStore from app.questionnaire import QuestionnaireSchema -from app.questionnaire.questionnaire_schema import get_sources_for_type_from_data +from app.questionnaire.questionnaire_schema import get_sources_for_types_from_data from app.utilities.mappings import get_flattened_mapping_values from app.utilities.types import LocationType, SectionKey @@ -20,7 +20,7 @@ def get_routing_path_block_ids_by_section_for_dependent_sections( location: LocationType, progress_store: ProgressStore, path_finder: PathFinder, - source_type: str, + source_types: Iterable[str], data: MultiDict | Mapping | Sequence, sections_to_ignore: list | None = None, ignore_keys: list[str] | None = None, @@ -35,8 +35,8 @@ def get_routing_path_block_ids_by_section_for_dependent_sections( else get_flattened_mapping_values(dependent_sections) ) - if dependents and not get_sources_for_type_from_data( - source_type=source_type, data=data, ignore_keys=ignore_keys + if dependents and not get_sources_for_types_from_data( + source_types=source_types, data=data, ignore_keys=ignore_keys ): return block_ids_by_section @@ -55,7 +55,7 @@ def get_routing_path_block_ids_by_section_for_dependent_sections( return block_ids_by_section -def get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( +def get_routing_path_block_ids_by_section_for_calculation_summary_dependencies( *, location: LocationType, progress_store: ProgressStore, @@ -65,7 +65,12 @@ def get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( ignore_keys: list[str] | None = None, schema: QuestionnaireSchema, ) -> dict[SectionKey, tuple[str, ...]]: - dependent_sections = schema.calculated_summary_section_dependencies_by_block[ + """ + If the current location depends on any calculated or grand calculated summaries, + for all the sections that those CS or GCS values depend on, get the blocks on the path for that section. + These routing path block ids are then used to ensure the CS/GCS only includes answers on the path + """ + dependent_sections = schema.calculation_summary_section_dependencies_by_block[ location.section_id ] return get_routing_path_block_ids_by_section_for_dependent_sections( @@ -74,7 +79,7 @@ def get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( sections_to_ignore=sections_to_ignore, data=data, path_finder=path_finder, - source_type="calculated_summary", + source_types={"calculated_summary", "grand_calculated_summary"}, ignore_keys=ignore_keys, dependent_sections=dependent_sections, ) diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 4a3039b648..19407a9256 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -15,7 +15,7 @@ from app.questionnaire import QuestionnaireSchema from app.questionnaire import path_finder as pf from app.questionnaire.dependencies import ( - get_routing_path_block_ids_by_section_for_calculated_summary_dependencies, + get_routing_path_block_ids_by_section_for_calculation_summary_dependencies, get_routing_path_block_ids_by_section_for_dependent_sections, ) from app.questionnaire.placeholder_transforms import PlaceholderTransforms @@ -176,7 +176,7 @@ def _get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( return {} return ( - get_routing_path_block_ids_by_section_for_calculated_summary_dependencies( + get_routing_path_block_ids_by_section_for_calculation_summary_dependencies( location=self._location, progress_store=self._data_stores.progress_store, sections_to_ignore=sections_to_ignore, @@ -210,7 +210,7 @@ def _get_value_source_resolver_for_transform( sections_to_ignore=["when"], path_finder=self._path_finder, data=transform, - source_type="answers", + source_types={"answers"}, dependent_sections=dependent_sections, ) self._routing_path_block_ids_by_section_key.update(block_ids) diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index e12d3e67ab..26abcfd17a 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -10,7 +10,7 @@ from app.questionnaire.questionnaire_schema import ( QuestionnaireSchema, - get_calculation_block_ids_for_grand_calculated_summary, + get_calculated_summary_ids_for_grand_calculated_summary, ) from app.questionnaire.rules.operations import DateOffset from app.questionnaire.rules.operations_helper import OperationHelper @@ -81,7 +81,7 @@ def _get_decimal_limit(self, unresolved_arguments: Mapping) -> int | None: # Type ignore: Validator will have checked the id so the block is guaranteed to exist grand_calculated_summary_block: ImmutableDict = self.schema.get_block(identifier) # type: ignore decimal_limit = self.schema.get_decimal_limit_from_calculated_summaries( - get_calculation_block_ids_for_grand_calculated_summary( + get_calculated_summary_ids_for_grand_calculated_summary( grand_calculated_summary_block ) ) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 8e7c3a7850..a4a0671292 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -88,7 +88,9 @@ def __init__( ] = defaultdict(list) self._answer_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) self._list_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) - self._when_rules_section_dependencies_by_section: dict[str, set[str]] = {} + self._when_rules_section_dependencies_by_section: dict[ + str, set[str] + ] = defaultdict(set) self._when_rules_section_dependencies_by_section_for_progress_value_source: defaultdict[ str, OrderedSet[str] ] = defaultdict( @@ -99,7 +101,7 @@ def __init__( ] = defaultdict( lambda: defaultdict(OrderedSet) ) - self.calculated_summary_section_dependencies_by_block: dict[ + self.calculation_summary_section_dependencies_by_block: dict[ str, DependencyDictType ] = defaultdict(lambda: defaultdict(OrderedSet)) self._when_rules_section_dependencies_by_answer: dict[ @@ -131,7 +133,7 @@ def __init__( # Post schema parsing. self._populate_answer_and_list_dependencies() self._populate_when_rules_section_dependencies() - self._populate_calculated_summary_section_dependencies() + self._populate_calculation_summary_section_dependencies() self._populate_min_max_for_numeric_answers() self._populate_placeholder_transform_section_dependencies() @@ -510,7 +512,7 @@ def _update_dependencies_for_grand_calculated_summary( self, grand_calculated_summary_block: ImmutableDict ) -> None: grand_calculated_summary_calculated_summary_ids = ( - get_calculation_block_ids_for_grand_calculated_summary( + get_calculated_summary_ids_for_grand_calculated_summary( grand_calculated_summary_block ) ) @@ -570,6 +572,27 @@ def _update_dependencies_for_dynamic_options( value_source, block_id=block_id, answer_id=answer_id ) + def _update_dependencies_for_calculated_summary_value_source( + self, *, calculated_summary_id: str, block_id: str, answer_id: str | None + ) -> None: + """ + For the given block (and optionally answer within) set it as a dependency of each calculated summary answer + If the calculated summary depends on a list, make the block depend on it too + """ + # Type ignore: validator checks the validity of value sources. + calculated_summary_block: ImmutableDict = self.get_block(calculated_summary_id) # type: ignore + answer_ids_for_block = get_calculated_summary_answer_ids( + calculated_summary_block + ) + dependent = self._get_dependent_for_block_id( + block_id=block_id, answer_id=answer_id + ) + for answer_id_for_block in answer_ids_for_block: + self._answer_dependencies_map[answer_id_for_block].add(dependent) + # if the answer is repeating, then the calculated summary also depends on the list it loops over + if list_name := self.get_list_name_for_answer_id(answer_id_for_block): + self._list_dependencies_map[list_name].add(dependent) + def _update_dependencies_for_value_source( self, value_source: Mapping, @@ -585,22 +608,26 @@ def _update_dependencies_for_value_source( self._answer_dependencies_map[value_source["identifier"]] |= { self._get_dependent_for_block_id(block_id=block_id, answer_id=answer_id) } - if value_source["source"] == "calculated_summary": + elif value_source["source"] == "calculated_summary": identifier = value_source["identifier"] - if calculated_summary_block := self.get_block(identifier): - answer_ids_for_block = get_calculated_summary_answer_ids( - calculated_summary_block - ) - dependent = self._get_dependent_for_block_id( - block_id=block_id, answer_id=answer_id + self._update_dependencies_for_calculated_summary_value_source( + calculated_summary_id=identifier, block_id=block_id, answer_id=answer_id + ) + + elif value_source["source"] == "grand_calculated_summary": + identifier = value_source["identifier"] + # Type ignore: validator will ensure identifier is valid + grand_calculated_summary_block: ImmutableDict = self.get_block(identifier) # type: ignore + for ( + calculated_summary_id + ) in get_calculated_summary_ids_for_grand_calculated_summary( + grand_calculated_summary_block + ): + self._update_dependencies_for_calculated_summary_value_source( + calculated_summary_id=calculated_summary_id, + block_id=block_id, + answer_id=answer_id, ) - for answer_id_for_block in answer_ids_for_block: - self._answer_dependencies_map[answer_id_for_block].add(dependent) - # if the answer is repeating, then the calculated summary also depends on the list it loops over - if list_name := self.get_list_name_for_answer_id( - answer_id_for_block - ): - self._list_dependencies_map[list_name].add(dependent) if value_source["source"] == "list": self._list_dependencies_map[value_source["identifier"]].add( @@ -1156,127 +1183,88 @@ def _get_error_messages(self) -> dict: def _populate_when_rules_section_dependencies(self) -> None: """ - Populates section dependencies for when rules, including when rules containing - progress value sources. - Progress section dependencies by section are directly populated in this method. - Progress section dependencies by block are populated in the - `self._populate_block_dependencies_for_progress_value_source` called here. + Populates section dependencies for when rules, including when rules containing progress value sources. Question variants and content variants don't need including, since the answer ids, block ids, and question ids remain the same, so a change in the variant, does not impact questionnaire progress. """ - progress_section_dependencies = ( - self._when_rules_section_dependencies_by_section_for_progress_value_source - ) - for section in self.get_sections(): + rules: list[Mapping] = [] when_rules = get_values_for_key( "when", data=section, ignore_keys=["question_variants", "content_variants"], ) - rules: list = list(when_rules) - - ( - rules_section_dependencies, - rule_section_dependencies_for_progress_value_source, - rule_block_dependencies_for_progress_value_source, - ) = self._get_rules_section_dependencies(section["id"], rules) - - if rules_section_dependencies: - self._when_rules_section_dependencies_by_section[ - section["id"] - ] = rules_section_dependencies + for when_rule in when_rules: + rules.extend(get_mappings_with_key("source", data=when_rule)) - for ( - key, - values, - ) in rule_section_dependencies_for_progress_value_source.items(): - progress_section_dependencies[key].update(values) - - self._populate_block_dependencies_for_progress_value_source( - rule_block_dependencies_for_progress_value_source - ) + for rule in rules: + self._populate_dependencies_for_rule( + rule, current_section_id=section["id"] + ) - def _populate_block_dependencies_for_progress_value_source( - self, - rule_block_dependencies_for_progress_value_source: dict[ - str, DependencyDictType - ], + def _populate_dependencies_for_rule( + self, rule: Mapping, *, current_section_id: str ) -> None: """ - Populates section dependencies for progress value sources at the block level + For a given rule, update dependency maps to indicate that the section containing the rule + depends on the answer/block/progress etc. that the rule is referencing. """ - dependencies = ( + identifier: str = rule["identifier"] + source: str = rule["source"] + selector: str | None = rule.get("selector") + + dependent_answer_ids: set[str] = set() + dependent_section_ids: set[str] = set() + + progress_section_dependencies = ( + self._when_rules_section_dependencies_by_section_for_progress_value_source + ) + progress_block_dependencies = ( self._when_rules_block_dependencies_by_section_for_progress_value_source ) - for ( - dependent_section, - section_dependencies_by_block, - ) in rule_block_dependencies_for_progress_value_source.items(): - for block_id, section_ids in section_dependencies_by_block.items(): - dependencies[dependent_section][block_id].update(section_ids) - - def _get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( - self, current_section_id: str, rule: Mapping - ) -> tuple[ - set[str], set[str], dict[str, dict[str, OrderedSet[str] | DependencyDictType]] - ]: - """ - For a given rule, returns: - - a set of dependent answer ids - - a set of dependent lists - - any dependent sections for progress value sources. - Progress dependencies are keyed both by section and by block e.g. - sections: {"section-1": {"section-2"}} - blocks: {"section-1": {"block-1": {"section-2"}}} - """ - dependent_answer_ids: set[str] = set() - dependent_list_names: set[str] = set() - dependencies_ids_for_progress_value_source: dict[ - str, dict[str, OrderedSet[str] | DependencyDictType] - ] = { - "sections": {}, - "blocks": {}, - } - identifier: str | None = rule.get("identifier") - source: str | None = rule.get("source") - selector: str | None = rule.get("selector") - if source == "answers" and identifier: + if source == "answers": dependent_answer_ids.add(identifier) - elif source == "calculated_summary" and identifier: + elif source == "calculated_summary": calculated_summary_block = self.get_block(identifier) # Type Ignore: Calculated summary block will exist at this point calculated_summary_answer_ids = get_calculated_summary_answer_ids( calculated_summary_block # type: ignore ) dependent_answer_ids.update(calculated_summary_answer_ids) - elif source == "list" and identifier: - dependent_list_names.add(identifier) - elif source == "progress" and identifier: + elif source == "grand_calculated_summary": + # grand calculated summary section could differ from cs & answer sections, include it in dependent sections + grand_calculated_summary_section_id: str = self.get_section_id_for_block_id(identifier) # type: ignore + if grand_calculated_summary_section_id != current_section_id: + dependent_section_ids.add(grand_calculated_summary_section_id) + dependent_answer_ids.update( + self.get_answer_ids_for_grand_calculated_summary_id(identifier) + ) + elif source == "list": + self._when_rules_section_dependencies_by_list[identifier].add( + current_section_id + ) + elif source == "progress": if selector == "section" and identifier != current_section_id: - # Type ignore: Added as this will be a set rather than a dict at this point - dependencies_ids_for_progress_value_source["sections"][ - identifier - ] = OrderedSet([current_section_id]) - elif selector == "block" and ( - section_id := self.get_section_id_for_block_id(identifier) + progress_section_dependencies[identifier].add(current_section_id) + elif ( + selector == "block" + and (block_section_id := self.get_section_id_for_block_id(identifier)) + != current_section_id ): # Type ignore: The identifier key will return a list - if section_id != current_section_id: - dependencies_ids_for_progress_value_source["blocks"][section_id] = { - identifier: OrderedSet() - } - dependencies_ids_for_progress_value_source["blocks"][section_id][ - identifier # type: ignore - ].append(current_section_id) - - return ( - dependent_answer_ids, - dependent_list_names, - dependencies_ids_for_progress_value_source, + progress_block_dependencies[block_section_id][identifier].add( # type: ignore + current_section_id + ) + + dependent_section_ids |= self._get_section_dependencies_for_dependent_answers( + current_section_id, dependent_answer_ids ) + if dependent_section_ids: + self._when_rules_section_dependencies_by_section[current_section_id].update( + dependent_section_ids + ) def _get_section_dependencies_for_dependent_answers( self, current_section_id: str, dependent_answer_ids: Iterable[str] @@ -1297,89 +1285,34 @@ def _get_section_dependencies_for_dependent_answers( section_dependencies.add(section_id) # type: ignore return section_dependencies - def _get_rules_section_dependencies( - self, current_section_id: str, rules: Mapping | Sequence - ) -> tuple[set[str], DependencyDictType, dict[str, DependencyDictType]]: - """ - Returns a set of sections ids that the current sections depends on. + def _populate_calculation_summary_section_dependencies(self) -> None: """ - section_dependencies: set[str] = set() - section_dependencies_for_progress_value_source: dict = {} - block_dependencies_for_progress_value_source: dict = {} - - if isinstance(rules, Mapping) and QuestionnaireSchema.has_operator(rules): - rules = self.get_operands(rules) - - for rule in rules: - if not isinstance(rule, Mapping): - continue - - [ - dependent_answer_ids, - dependent_list_names, - dependencies_for_progress_value_source, - ] = self._get_section_and_block_ids_dependencies_for_progress_source_and_answer_ids_from_rule( - current_section_id, rule - ) - - section_dependencies_for_progress_value_source.update( - dependencies_for_progress_value_source["sections"] - ) - block_dependencies_for_progress_value_source.update( - dependencies_for_progress_value_source["blocks"] - ) - - section_dependencies |= ( - self._get_section_dependencies_for_dependent_answers( - current_section_id, dependent_answer_ids - ) - ) - - for list_name in dependent_list_names: - self._when_rules_section_dependencies_by_list[list_name].add( - current_section_id - ) - - if QuestionnaireSchema.has_operator(rule): - ( - nested_section_dependencies, - nested_section_dependencies_for_progress_value_source, - nested_block_dependencies_for_progress_value_source, - ) = self._get_rules_section_dependencies(current_section_id, rule) - section_dependencies.update(nested_section_dependencies) - section_dependencies_for_progress_value_source |= ( - nested_section_dependencies_for_progress_value_source - ) - block_dependencies_for_progress_value_source |= ( - nested_block_dependencies_for_progress_value_source - ) - - return ( - section_dependencies, - section_dependencies_for_progress_value_source, - block_dependencies_for_progress_value_source, - ) - - def _populate_calculated_summary_section_dependencies(self) -> None: - """ - For each block, find all the calculated summary value source dependencies + For each block, find all the calculated and grand calculated summary value source dependencies and make sure all involved sections are added as dependencies for that block and its section. Since calculated summaries can only contain answers from the section they are in, - it is sufficient to check the section for the calculated summary only. + it is sufficient to check the section for the calculated summary only. For grand calculated summaries + Need to include the section of the gcs, and each cs it references. """ for section in self.get_sections(): for block in self.get_blocks_for_section(section): sources = get_mappings_with_key( "source", data=block, ignore_keys=["when"] ) - section_dependencies: set[str] = { - # Type ignore: Validator ensures a valid identifier so section will exist - self.get_section_id_for_block_id(source["identifier"]) # type: ignore - for source in sources - if source["source"] == "calculated_summary" - } - self.calculated_summary_section_dependencies_by_block[section["id"]][ + section_dependencies: set[str] = set() + + for source in sources: + if source["source"] == "calculated_summary": + section_dependencies.add( + self.get_section_id_for_block_id(source["identifier"]) # type: ignore + ) + elif source["source"] == "grand_calculated_summary": + section_dependencies.update( + self.get_section_ids_for_grand_calculated_summary_id( + source["identifier"] + ) + ) + self.calculation_summary_section_dependencies_by_block[section["id"]][ block["id"] ].update(section_dependencies) @@ -1392,6 +1325,36 @@ def _get_section_ids_for_answer_ids(self, answer_ids: set[str]) -> OrderedSet[st section_dependencies.add(section_id) # type: ignore return section_dependencies + def get_answer_ids_for_grand_calculated_summary_id( + self, grand_calculated_summary_id: str + ) -> list[str]: + # Type ignores: can assume the cs and gcs exist + answer_ids: list[str] = [] + block: ImmutableDict = self.get_block(grand_calculated_summary_id) # type: ignore + for ( + calculated_summary_id + ) in get_calculated_summary_ids_for_grand_calculated_summary(block): + calculated_summary_block: ImmutableDict = self.get_block( # type: ignore + calculated_summary_id + ) + answer_ids.extend( + get_calculated_summary_answer_ids(calculated_summary_block) + ) + return answer_ids + + def get_section_ids_for_grand_calculated_summary_id( + self, grand_calculated_summary_id: str + ) -> set[str]: + """ + Returns all sections that the grand calculated summary depends on, + i.e. the grand calculated summary section and the sections for each included calculated summary + """ + # Type ignores: Can assume the block and each section exists + block_ids = {grand_calculated_summary_id} + block: ImmutableDict = self.get_block(grand_calculated_summary_id) # type: ignore + block_ids.update(get_calculated_summary_ids_for_grand_calculated_summary(block)) + return {self.get_section_id_for_block_id(block_id) for block_id in block_ids} # type: ignore + def get_summary_item_for_list_for_section( self, *, section_id: str, list_name: str ) -> ImmutableDict | None: @@ -1464,22 +1427,22 @@ def is_summary_with_calculation(summary_type: str) -> bool: return summary_type in {"GrandCalculatedSummary", "CalculatedSummary"} -def get_sources_for_type_from_data( +def get_sources_for_types_from_data( *, - source_type: str, + source_types: Iterable[str], data: MultiDict | Mapping | Sequence, ignore_keys: list | None = None, ) -> list: sources = get_mappings_with_key(key="source", data=data, ignore_keys=ignore_keys) - return [source for source in sources if source["source"] == source_type] + return [source for source in sources if source["source"] in source_types] def get_identifiers_from_calculation_block( *, calculation_block: Mapping, source_type: str ) -> list[str]: - values = get_sources_for_type_from_data( - source_type=source_type, data=calculation_block["calculation"]["operation"] + values = get_sources_for_types_from_data( + source_types={source_type}, data=calculation_block["calculation"]["operation"] ) return [value["identifier"] for value in values] @@ -1494,7 +1457,7 @@ def get_calculated_summary_answer_ids(calculated_summary_block: Mapping) -> list ) -def get_calculation_block_ids_for_grand_calculated_summary( +def get_calculated_summary_ids_for_grand_calculated_summary( grand_calculated_summary_block: Mapping, ) -> list[str]: return get_identifiers_from_calculation_block( diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index c3f0f1a21f..0931d969e3 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -4,7 +4,7 @@ from app.questionnaire.location import SectionKey from app.questionnaire.questionnaire_schema import ( - get_calculation_block_ids_for_grand_calculated_summary, + get_calculated_summary_ids_for_grand_calculated_summary, ) from app.questionnaire.return_location import ReturnLocation from app.views.contexts.calculated_summary_context import CalculatedSummaryContext @@ -21,8 +21,8 @@ def _build_grand_calculated_summary_section(self) -> dict[str, str | list]: self.current_location.block_id # type: ignore ) - calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( - self.rendered_block + calculated_summary_ids = ( + get_calculated_summary_ids_for_grand_calculated_summary(self.rendered_block) ) blocks_to_calculate = [ self._schema.get_block(block_id) for block_id in calculated_summary_ids @@ -95,8 +95,8 @@ def build_view_context(self) -> dict[str, dict]: Build summary section with formatted total and change links for each calculated summary """ calculation = self.rendered_block["calculation"] - calculated_summary_ids = get_calculation_block_ids_for_grand_calculated_summary( - self.rendered_block + calculated_summary_ids = ( + get_calculated_summary_ids_for_grand_calculated_summary(self.rendered_block) ) routing_path_block_ids = self._blocks_on_routing_path(calculated_summary_ids) diff --git a/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json index 7634df978e..b21dbc9b6e 100644 --- a/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json +++ b/schemas/test/en/test_grand_calculated_summary_overlapping_answers.json @@ -328,6 +328,30 @@ ] } ] + }, + { + "id": "section-4", + "title": "Conditional Section", + "enabled": { + "when": { + ">": [{ "source": "grand_calculated_summary", "identifier": "grand-calculated-summary-shopping" }, 500] + } + }, + "groups": [ + { + "id": "group-3", + "title": "Conditional Group", + "blocks": [ + { + "type": "Interstitial", + "id": "grand-calculated-summary-piping", + "content": { + "title": "This section is only showing because the grand calculated summary exceeded £500." + } + } + ] + } + ] } ] } diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index baaf937498..4b25ee8a77 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -1012,3 +1012,81 @@ def test_when_rule_dependencies_dont_include_variants(list_collector_variant_sch assert not schema.when_rules_section_dependencies_by_answer assert not schema.when_rules_section_dependencies_by_section assert not schema.get_when_rule_section_dependencies_for_list("people") + + +def test_grand_calculated_summary_dependencies(): + """ + Ensures that both the grand calculated summary, and the breakdown depending on it appear in + the list dependencies of the two lists involved in the grand calculated summary, and each answer id + """ + schema = load_schema_from_name( + "test_grand_calculated_summary_inside_repeating_section" + ) + gcs = Dependent( + section_id="vehicle-details-section", + block_id="grand-calculated-summary-vehicle", + for_list="vehicles", + ) + gcs_dependent = Dependent( + section_id="vehicle-details-section", + block_id="gcs-breakdown-block", + for_list="vehicles", + ) + assert gcs in schema.list_dependencies["costs"] + assert gcs in schema.list_dependencies["vehicles"] + assert gcs_dependent in schema.list_dependencies["costs"] + assert gcs_dependent in schema.list_dependencies["vehicles"] + gcs_answers = [ + "dynamic-answer-cost-extra", + "finance-cost-answer", + "vehicle-maintenance-cost", + "vehicle-fuel-cost", + ] + for answer in gcs_answers: + assert gcs in schema.answer_dependencies[answer] + assert gcs_dependent in schema.answer_dependencies[answer] + + +def test_grand_calculated_summary_path_dependencies(): + """ + Ensures that the calculation block dependencies are correct for the grand calculated summary + These are used to look up the routing path block ids and include only answers on the path in the total value + + For this schema, the grand calculated summary, breakdown depending on it, and page it is piped into, + all need the routing path block ids from both sections to display the correct total + """ + schema = load_schema_from_name( + "test_grand_calculated_summary_inside_repeating_section" + ) + calculation_deps = schema.calculation_summary_section_dependencies_by_block[ + "vehicle-details-section" + ] + for block_id in [ + "grand-calculated-summary-vehicle", + "gcs-breakdown-block", + "gcs-piping", + ]: + assert calculation_deps[block_id] == { + "base-costs-section", + "vehicle-details-section", + } + + +def test_grand_calculated_summary_when_rule_dependencies(): + """ + Tests that for a section enabled only when a grand calculated summary is a specific value, the schema when rules + have the section depending on each answer making up the GCS and depending on the sections containing the answers and GCS + """ + schema = load_schema_from_name("test_grand_calculated_summary_overlapping_answers") + assert schema.when_rules_section_dependencies_by_section["section-4"] == { + "section-1", + "section-3", + } + assert schema.when_rules_section_dependencies_by_answer == ImmutableDict( + { + "q1-a2": {"section-4"}, + "q2-a1": {"section-4"}, + "q1-a1": {"section-4"}, + "q2-a2": {"section-4"}, + } + ) diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index ccd0a024fa..1c891e413e 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -229,7 +229,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { ); }); - it("Given I edit the non-repeating calculated summary, When I return to the Hub, Then I see repeating sections are incomplete", async () => { + it("Given I have edited a static answer whilst completing the repeating section, When I return to the Hub and enter the other repeat, Then I see the breakdown block needs to be revisited", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); await $(GcsBreakdownBlockPage.payDebit()).setValue(100); await $(GcsBreakdownBlockPage.payCredit()).setValue(110); @@ -239,9 +239,16 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(VehicleDetailsSectionPage.submit()); await $(HubPage.summaryRowLink("vehicle-details-section-1")).click(); await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await expect(await $(GcsBreakdownBlockPage.questionText()).getText()).toBe("How do you pay for the monthly fees of £325.00?"); + await $(GcsBreakdownBlockPage.payCredit()).setValue(125); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Completed"); + }); + + it("Given I edit the non-repeating calculated summary, When I return to the Hub, Then I see repeating sections are incomplete", async () => { await $(HubPage.summaryRowLink("base-costs-section")).click(); await $(BaseCostsSectionPage.financeCostAnswerEdit()).click(); await $(FinanceCostPage.answer()).setValue(80); @@ -261,6 +268,9 @@ describe("Grand Calculated Summary inside a repeating section", () => { "The total cost of owning and running your Car is calculated to be £335.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await $(GcsBreakdownBlockPage.payCredit()).setValue(135); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); }); @@ -322,8 +332,17 @@ describe("Grand Calculated Summary inside a repeating section", () => { ); }); - it("Given I complete the Grand Calculated Summary, When I return to the Hub, Then I see the other repeating section is incomplete as the static calculated summary changed", async () => { + it("Given I complete the Grand Calculated Summary, When I press continue, I am taken to the calculation question that depends on it and cant proceed till entering a valid breakdown", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await click(GcsBreakdownBlockPage.submit()); + await expect(await $(GcsBreakdownBlockPage.errorNumber()).getText()).toBe("Enter answers that add up to 260"); + await $(GcsBreakdownBlockPage.payOther()).setValue(50); + await click(GcsBreakdownBlockPage.submit()); + await expect(browser).toHaveUrlContaining(VehicleDetailsSectionPage.pageName); + }); + + it("Given I have changed a static calculated summary during the section, When I return to the Hub, Then I see the other repeating section is incomplete as it also uses this calculated summary", async () => { await click(VehicleDetailsSectionPage.submit()); await expect(await $(HubPage.summaryRowState("vehicle-details-section-1")).getText()).toBe("Partially completed"); await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Completed"); @@ -335,7 +354,13 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £365.00. Is this correct?", ); + }); + + it("Given I the grand calculated summary has changed, When I confirm it, Then I see the breakdown question and need to update the values", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await $(GcsBreakdownBlockPage.payOther()).setValue(130); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); }); @@ -355,18 +380,24 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Partially completed"); }); - it("Given I revisit both repeating sections, When I start each, Then I see the grand calculated summary page with correct values", async () => { + it("Given I revisit both repeating sections, When I start each, Then I see the grand calculated summary page with correct values and must update the breakdown after", async () => { await click(HubPage.submit()); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £355.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await $(GcsBreakdownBlockPage.payOther()).setValue(120); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); await click(HubPage.submit()); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £250.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await $(GcsBreakdownBlockPage.payOther()).setValue(40); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); }); @@ -391,16 +422,20 @@ describe("Grand Calculated Summary inside a repeating section", () => { await expect(await $(HubPage.summaryRowState("vehicle-details-section-2")).getText()).toBe("Partially completed"); }); - it("Given I revisit both repeating sections with new items, When I start each, Then I see the grand calculated summary page with correct values", async () => { + it("Given I revisit both repeating sections with new items, When I start each, Then I see the grand calculated summary page with correct values and the breakdown after", async () => { await click(HubPage.submit()); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £405.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); + await $(GcsBreakdownBlockPage.payOther()).setValue(170); + await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); await click(HubPage.submit()); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £300.00. Is this correct?", ); + await click(GrandCalculatedSummaryVehiclePage.submit()); + await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index 2d4041779f..40eab2b584 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -121,7 +121,7 @@ describe("Feature: Grand Calculated Summary", () => { await $(HubPage.summaryRowLink("section-3")).click(); await $(GrandCalculatedSummaryShoppingPage.calculatedSummary4Edit()).click(); await $(CalculatedSummary4Page.q2A2Edit()).click(); - await $(Block2Page.q2A2()).setValue(100); + await $(Block2Page.q2A2()).setValue(50); await click(Block2Page.submit()); // confirm one of the calculated summaries but return to the hub instead of confirming the other @@ -132,5 +132,30 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); }); + + it("Given I complete the calculated and grand calculated summaries, When I return to the Hub, Then I see a new conditional section has opened up", async () => { + await click(HubPage.submit()); + await click(CalculatedSummary4Page.submit()); + await click(Section1SummaryPage.submit()); + await click(HubPage.submit()); + await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( + "Grand Calculated Summary of purchases this week comes to £520.00. Is this correct?", + ); + await click(GrandCalculatedSummaryShoppingPage.submit()); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowLink("section-4")).isExisting()).toBe(true); + }); + + it("Given I change my answer about purchasing additional items decreasing the gcs, When I return to the Hub, Then I see the conditional section is gone", async () => { + await $(HubPage.summaryRowLink("section-1")).click(); + await $(Section1SummaryPage.radioExtraEdit()).click(); + await $(Block3Page.no()).click(); + await click(Block3Page.submit()); + await click(Section1SummaryPage.submit()); + await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowState("section-3")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowLink("section-4")).isExisting()).toBe(false); + }); }); }); diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 1f5e2bee3e..9c4f1b34ac 100644 --- a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -456,6 +456,8 @@ describe("Feature: Grand Calculated Summary", () => { "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); + await $(InternetBreakdownBlockPage.internetPhone()).setValue(45); + await click(InternetBreakdownBlockPage.submit()); await click(Section6SummaryPage.submit()); }); @@ -493,10 +495,12 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); - await click(GrandCalculatedSummary5Page.submit()); }); it("Given I have a further section depending on the grand calculated summary section, When I return to the Hub, Then I see the new section is available", async () => { + await click(GrandCalculatedSummary5Page.submit()); + await $(InternetBreakdownBlockPage.internetPhone()).setValue(25); + await click(InternetBreakdownBlockPage.submit()); await click(Section6SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-7")).getText()).toBe("Not started"); await click(HubPage.submit()); @@ -518,7 +522,7 @@ describe("Feature: Grand Calculated Summary", () => { await expect(await $("body").getText()).toContain("Total household expenditure: £1,199.00"); await expect(await $("body").getText()).toContain("Personal contribution: £1,100.00"); await expect(await $("body").getText()).toContain("Total internet usage: 85 GB"); - await expect(await $("body").getText()).toContain("Usage by phone: 40 GB"); + await expect(await $("body").getText()).toContain("Usage by phone: 25 GB"); await expect(await $("body").getText()).toContain("Usage by PC: 60 GB"); }); }); From 67ce919e529c1ee31dc0303d3c718fc5466f3c45 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:07:31 +0000 Subject: [PATCH 360/567] Update a schema to demonstrate minimum/maximum supports floats (#1277) --- app/jinja_filters.py | 25 ++++++++++++---------- app/questionnaire/questionnaire_schema.py | 6 ++++-- app/translations/messages.pot | 4 ++-- schemas/test/en/test_metadata_routing.json | 6 +++--- schemas/test/en/test_numbers.json | 4 ++-- tests/app/forms/test_questionnaire_form.py | 2 +- tests/functional/spec/numbers.spec.js | 15 +++++++++++++ 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 8f7920e512..b5cdee9898 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -250,18 +250,21 @@ def get_min_max_value_width( """ This function gets the minimum and maximum value accepted for a question. Which then allows us to use that value to set the width of the textbox to suit that min and max. - """ - - if ( - answer.get(min_max, {}) - and isinstance(answer[min_max]["value"], Mapping) - and answer[min_max]["value"].get("source") == "answers" - ): - schema: QuestionnaireSchema = g.get("schema") - identifier = answer[min_max]["value"].get("identifier") - return schema.min_and_max_map[identifier][min_max] - return len(str(answer.get(min_max, {}).get("value", default_value))) + If the min or max for the answer is a value source but not an "answers" source, such as a calculated or grand calculated summary, + use the length of the default value for the min and max width, as the actual min and max width cannot currently be determined + """ + min_max_value = answer.get(min_max, {}) + if min_max_value and isinstance(answer[min_max]["value"], Mapping): + if answer[min_max]["value"].get("source") == "answers": + schema: QuestionnaireSchema = g.get("schema") + identifier = answer[min_max]["value"]["identifier"] + return schema.min_and_max_map[identifier][min_max] + return len(str(default_value)) + + # Factor out the decimals as it's accounted for in get_width_for_number + result = int(min_max_value.get("value", default_value)) + return len(str(result)) @blueprint.app_template_filter() diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index a4a0671292..1fda8a2778 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -2,6 +2,7 @@ from collections import defaultdict from copy import deepcopy from dataclasses import dataclass +from decimal import Decimal from functools import cached_property from typing import Any, Generator, Iterable, Literal, Mapping, Sequence, TypeAlias @@ -167,8 +168,9 @@ def _create_min_max_map( for answer in answers: value = answer.get(min_max, {}).get("value") - if isinstance(value, float | int): - value_length = len(str(value)) + if isinstance(value, float | int | Decimal): + # Factor out the decimals as it's accounted for in jinja_filters.py + value_length = len(str(int(value))) longest_value_length = max(longest_value_length, value_length) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 991f75061d..ac138a2682 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-01-02 14:50+0000\n" +"POT-Creation-Date: 2024-01-02 11:59+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,7 +41,7 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:731 +#: app/jinja_filters.py:734 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:25 msgid "No answer provided" diff --git a/schemas/test/en/test_metadata_routing.json b/schemas/test/en/test_metadata_routing.json index b97f19e384..1142d57f9e 100644 --- a/schemas/test/en/test_metadata_routing.json +++ b/schemas/test/en/test_metadata_routing.json @@ -17,7 +17,7 @@ "type": "string" }, { - "name": "flag_1", + "name": "boolean_flag", "type": "boolean" }, { @@ -63,10 +63,10 @@ "when": { "==": [ { - "identifier": "flag_1", + "identifier": "boolean_flag", "source": "metadata" }, - "true" + true ] } }, diff --git a/schemas/test/en/test_numbers.json b/schemas/test/en/test_numbers.json index 85353843d1..195f2ea576 100644 --- a/schemas/test/en/test_numbers.json +++ b/schemas/test/en/test_numbers.json @@ -48,7 +48,7 @@ "type": "Number", "decimal_places": 2, "minimum": { - "value": -1000 + "value": -1000.98 }, "maximum": { "value": 1000 @@ -65,7 +65,7 @@ "value": 1001 }, "maximum": { - "value": 10000 + "value": 10000.98 } } ], diff --git a/tests/app/forms/test_questionnaire_form.py b/tests/app/forms/test_questionnaire_form.py index 360af156f4..3885064967 100644 --- a/tests/app/forms/test_questionnaire_form.py +++ b/tests/app/forms/test_questionnaire_form.py @@ -1489,7 +1489,7 @@ def test_answer_errors_are_interpolated(app, data_stores): form.validate() answer_errors = form.answer_errors("set-minimum") assert ( - schema.error_messages["NUMBER_TOO_SMALL"] % {"min": "-1,000"} + schema.error_messages["NUMBER_TOO_SMALL"] % {"min": "-1,000.98"} in answer_errors ) diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index e1e7f0036f..e6c8bb2a49 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -12,7 +12,22 @@ describe("Number validation", () => { before(async () => { await browser.openQuestionnaire("test_numbers.json"); }); + describe("Given I am completing the test numbers questionnaire,", () => { + it("When a minimum value with decimals is used and I enter a value less than the minimum, Then the error message includes the minimum value with the decimals values", async () => { + await $(SetMinMax.setMinimum()).setValue(-1000.99); + await $(SetMinMax.setMaximum()).setValue(1000); + await click(SetMinMax.submit()); + await expect(await $(SetMinMax.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to -1,000.98"); + }); + + it("When a maximum value with decimals is used and I enter a value greater than the maximum, Then the error message includes the minimum value with the decimal values", async () => { + await $(SetMinMax.setMinimum()).setValue(100); + await $(SetMinMax.setMaximum()).setValue(10000.99); + await click(SetMinMax.submit()); + await expect(await $(SetMinMax.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to 10,000.98"); + }); + it("When I am on the set minimum and maximum page, Then each field has a label", async () => { await expect(await $(SetMinMax.setMinimumLabelDescription()).getText()).toBe("This is a description of the minimum value"); await expect(await $(SetMinMax.setMaximumLabelDescription()).getText()).toBe("This is a description of the maximum value"); From bc3d5f634fcc9f85ec04c253defd6edaec3bf31b Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:21:15 +0000 Subject: [PATCH 361/567] Authenticate with CIR using google ID token (#1288) --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Co-authored-by: Mebin Abraham --- README.md | 4 ++++ app/services/supplementary_data.py | 9 ++----- app/settings.py | 1 + app/setup.py | 7 ++++-- app/utilities/credentials.py | 12 ++++++++++ app/utilities/schema.py | 12 ++++++++-- tests/app/utilities/test_schema.py | 35 ++++++++++++++++++++++++++++ tests/integration/test_app_create.py | 14 +++++++++++ 8 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 app/utilities/credentials.py diff --git a/README.md b/README.md index b4aa4585a5..8237cd9fac 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,10 @@ The following env variables can be used | ONS_URL | `https://www.ons.gov.uk` | The URL of the ONS website where static content is sourced, e.g. accessibility info | | SDS_API_BASE_URL | | The base URL of the SDS API used for fetching supplementary data | | CIR_API_BASE_URL | | The base URL of the CIR API used for fetching collection instruments | +| OIDC_TOKEN_BACKEND | gcp | The backend to use when fetching the Open ID Connect token | +| OIDC_TOKEN_LEEWAY_IN_SECONDS | 300 | The leeway to use when validating OIDC tokens | +| SDS_OAUTH2_CLIENT_ID | | The OAuth2 Client ID used when setting up IAP on the SDS | +| CIR_OAUTH2_CLIENT_ID | | The OAuth2 Client ID used when setting up IAP on the CIR | The following env variables can be used when running tests diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index 711f25cc19..fc50267889 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -10,8 +10,8 @@ from structlog import get_logger from app.keys import KEY_PURPOSE_SDS -from app.oidc.oidc import OIDCCredentialsService from app.settings import SDS_OAUTH2_CLIENT_ID +from app.utilities.credentials import fetch_and_apply_oidc_credentials from app.utilities.request_session import get_retryable_session from app.utilities.supplementary_data_parser import validate_supplementary_data_v1 @@ -66,13 +66,8 @@ def get_supplementary_data_v1( backoff_factor=SUPPLEMENTARY_DATA_REQUEST_BACKOFF_FACTOR, ) - # Type ignore: oidc_credentials_service is a singleton of this application - oidc_credentials_service: OIDCCredentialsService = current_app.eq["oidc_credentials_service"] # type: ignore # Type ignore: SDS_OAUTH2_CLIENT_ID is an env var which must exist as it is verified in setup.py - credentials = oidc_credentials_service.get_credentials( - iap_client_id=SDS_OAUTH2_CLIENT_ID # type: ignore - ) - credentials.apply(headers=session.headers) + fetch_and_apply_oidc_credentials(session=session, client_id=SDS_OAUTH2_CLIENT_ID) # type: ignore try: response = session.get( diff --git a/app/settings.py b/app/settings.py index 4f061c8678..8f1f4caaf9 100644 --- a/app/settings.py +++ b/app/settings.py @@ -157,6 +157,7 @@ def utcoffset_or_fail(date_value, key): OIDC_TOKEN_LEEWAY_IN_SECONDS = int(os.getenv("OIDC_TOKEN_LEEWAY_IN_SECONDS", "300")) SDS_OAUTH2_CLIENT_ID = os.getenv("SDS_OAUTH2_CLIENT_ID") +CIR_OAUTH2_CLIENT_ID = os.getenv("CIR_OAUTH2_CLIENT_ID") ACCOUNT_SERVICE_BASE_URL = os.getenv( "ACCOUNT_SERVICE_BASE_URL", "https://surveys.ons.gov.uk" diff --git a/app/setup.py b/app/setup.py index 6bda0591aa..7f4f7d93ca 100644 --- a/app/setup.py +++ b/app/setup.py @@ -387,15 +387,18 @@ def setup_task_client(application): def setup_oidc(application): - def sds_client_id_exists(): + def client_ids_exist(): if not application.config.get("SDS_OAUTH2_CLIENT_ID"): raise MissingEnvironmentVariable("Setting SDS_OAUTH2_CLIENT_ID Missing") + if not application.config.get("CIR_OAUTH2_CLIENT_ID"): + raise MissingEnvironmentVariable("Setting CIR_OAUTH2_CLIENT_ID Missing") + if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): raise MissingEnvironmentVariable("Setting OIDC_TOKEN_BACKEND Missing") if oidc_token_backend == "gcp": - sds_client_id_exists() + client_ids_exist() application.eq["oidc_credentials_service"] = OIDCCredentialsServiceGCP() elif oidc_token_backend == "local": diff --git a/app/utilities/credentials.py b/app/utilities/credentials.py new file mode 100644 index 0000000000..f24b803217 --- /dev/null +++ b/app/utilities/credentials.py @@ -0,0 +1,12 @@ +import requests +from flask import current_app + +from app.oidc.oidc import OIDCCredentialsService + + +def fetch_and_apply_oidc_credentials(session: requests.Session, client_id: str) -> None: + # Type ignore: oidc_credentials_service is a singleton of this application + oidc_credentials_service: OIDCCredentialsService = current_app.eq["oidc_credentials_service"] # type: ignore + + credentials = oidc_credentials_service.get_credentials(iap_client_id=client_id) + credentials.apply(headers=session.headers) diff --git a/app/utilities/schema.py b/app/utilities/schema.py index 57c9e4e7fa..a6bfb30638 100644 --- a/app/utilities/schema.py +++ b/app/utilities/schema.py @@ -14,6 +14,8 @@ DEFAULT_LANGUAGE_CODE, QuestionnaireSchema, ) +from app.settings import CIR_OAUTH2_CLIENT_ID +from app.utilities.credentials import fetch_and_apply_oidc_credentials from app.utilities.json import json_load, json_loads from app.utilities.request_session import get_retryable_session @@ -141,7 +143,7 @@ def load_schema_from_instrument_id( ) -> QuestionnaireSchema: parameters = {"guid": cir_instrument_id} cir_url = f"{current_app.config['CIR_API_BASE_URL']}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}?{urlencode(parameters)}" - return load_schema_from_url(url=cir_url, language_code=language_code) + return load_schema_from_url(url=cir_url, language_code=language_code, is_cir=True) @lru_cache(maxsize=None) @@ -194,7 +196,9 @@ def _load_schema_file(schema_name: str, language_code: str) -> Any: @lru_cache(maxsize=None) -def load_schema_from_url(url: str, *, language_code: str | None) -> QuestionnaireSchema: +def load_schema_from_url( + url: str, *, language_code: str | None, is_cir: bool = False +) -> QuestionnaireSchema: """ Fetches a schema from the provided url. The caller is responsible for including any required query parameters in the url @@ -214,6 +218,10 @@ def load_schema_from_url(url: str, *, language_code: str | None) -> Questionnair backoff_factor=SCHEMA_REQUEST_BACKOFF_FACTOR, ) + if is_cir: + # Type ignore: CIR_OAUTH2_CLIENT_ID is an env var which must exist as it is verified in setup.py + fetch_and_apply_oidc_credentials(session=session, client_id=CIR_OAUTH2_CLIENT_ID) # type: ignore + try: req = session.get(url, timeout=SCHEMA_REQUEST_TIMEOUT) except RequestException as exc: diff --git a/tests/app/utilities/test_schema.py b/tests/app/utilities/test_schema.py index 652b2c15ac..58a0b4babe 100644 --- a/tests/app/utilities/test_schema.py +++ b/tests/app/utilities/test_schema.py @@ -8,6 +8,7 @@ from urllib3.connectionpool import HTTPConnectionPool from urllib3.response import HTTPResponse +from app.oidc.gcp_oidc import OIDCCredentialsServiceGCP from app.questionnaire import QuestionnaireSchema from app.setup import create_app from app.utilities.schema import ( @@ -374,3 +375,37 @@ def test_load_schema_from_url_max_retries(mocker): assert str(exc.value) == "schema request failed" assert mocked_make_request.call_count == 3 + + +@responses.activate +def test_load_schema_from_metadata_cir_with_gcp_authentication( + app, metadata_with_cir_instrument_id, mocker +): + load_schema_from_url.cache_clear() + mock_schema = QuestionnaireSchema({}, language_code="cy") + + mock_oidc_service = Mock(spec=OIDCCredentialsServiceGCP) + mocker.patch.dict( + "app.services.supplementary_data.current_app.eq", + {"oidc_credentials_service": mock_oidc_service}, + ) + + responses.add( + responses.GET, + f"{TEST_CIR_URL}{CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL}", + json=mock_schema.json, + status=200, + ) + + with app.app_context(): + app.config["CIR_API_BASE_URL"] = TEST_CIR_URL + loaded_schema = load_schema_from_metadata( + metadata=metadata_with_cir_instrument_id, language_code="cy" + ) + + mock_oidc_service.get_credentials.assert_called_once_with( + iap_client_id=app.config["CIR_OAUTH2_CLIENT_ID"] + ) + + assert loaded_schema.json == mock_schema.json + assert loaded_schema.language_code == mock_schema.language_code diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index ba046be6f2..823332f7fa 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -395,6 +395,7 @@ def test_setup_oidc_service_gcp(self): # Given self._setting_overrides["OIDC_TOKEN_BACKEND"] = "gcp" self._setting_overrides["SDS_OAUTH2_CLIENT_ID"] = "1234567890" + self._setting_overrides["CIR_OAUTH2_CLIENT_ID"] = "1234567890" # When application = create_app(self._setting_overrides) @@ -449,3 +450,16 @@ def test_sds_oauth_2_client_id_missing_raises_exception(self): # Then assert "Setting SDS_OAUTH2_CLIENT_ID Missing" in str(ex.exception) + + def test_cir_oauth_2_client_id_missing_raises_exception(self): + # Given + self._setting_overrides["OIDC_TOKEN_BACKEND"] = "gcp" + self._setting_overrides["SDS_OAUTH2_CLIENT_ID"] = "123456789" + self._setting_overrides["CIR_OAUTH2_CLIENT_ID"] = "" + + # When + with self.assertRaises(MissingEnvironmentVariable) as ex: + create_app(self._setting_overrides) + + # Then + assert "Setting CIR_OAUTH2_CLIENT_ID Missing" in str(ex.exception) From 82543624f4be0175128e5170d6daa3a2699a9956 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:45:37 +0000 Subject: [PATCH 362/567] Migrate to GA4 (#1283) --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- README.md | 8 ++--- app/helpers/template_helpers.py | 8 ++--- app/settings.py | 3 +- app/setup.py | 4 --- app/translations/messages.pot | 26 +++++++------- ci/deploy_app.sh | 6 ++-- templates/layouts/_base.html | 34 +++++++++++-------- tests/app/helpers/test_template_helpers.py | 8 ++--- tests/integration/test_app_create.py | 6 ++-- .../integration/test_application_variables.py | 11 ++---- 10 files changed, 48 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 8237cd9fac..46975c88cf 100644 --- a/README.md +++ b/README.md @@ -347,15 +347,14 @@ The following environment variables must be set when deploying the app. The following environment variables are optional: | Variable Name | Default | Description | -| ---------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------- | +|------------------------------| ---------------- |----------------------------------------------------------------------------------------------------------------| | REGION | europe-west2 | The region that will be used for your Cloud Run service | | CONCURRENCY | 80 | The maximum number of requests that can be processed simultaneously by a given container instance | | MIN_INSTANCES | 1 | The minimum number of container instances that can be used for your Cloud Run service | | MAX_INSTANCES | 1 | The maximum number of container instances that can be used for your Cloud Run service | | CPU | 4 | The number of CPUs to allocate for each Cloud Run container instance | | MEMORY | 4G | The amount of memory to allocate for each Cloud Run container instance | -| GOOGLE_TAG_MANAGER_ID | | The Google Tag Manger ID - Specifies the GTM account | -| GOOGLE_TAG_MANAGER_AUTH | | The Google Tag Manger Auth - Ties the GTM container with the whole environment | +| GOOGLE_TAG_ID | | The Google Tag ID - Specifies the GTM account | | WEB_SERVER_TYPE | gunicorn-threads | Web server type used to run the application. This also determines the worker class which can be async/threaded | | WEB_SERVER_WORKERS | 7 | The number of worker processes | | WEB_SERVER_THREADS | 10 | The number of worker threads per worker | @@ -394,8 +393,7 @@ The following env variables can be used |-------------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------| | EQ_SESSION_TIMEOUT_SECONDS | 2700 (45 mins) | The duration of the flask session | | EQ_PROFILING | False | Enables or disables profiling (True/False) Default False/Disabled | -| EQ_GOOGLE_TAG_MANAGER_ID | | The Google Tag Manger ID - Specifies the GTM account | -| EQ_GOOGLE_TAG_MANAGER_AUTH | | The Google Tag Manger Auth - Ties the GTM container with the whole environment | +| EQ_GOOGLE_TAG_ID | | The Google Tag Manger ID - Specifies the GTM account | | EQ_ENABLE_HTML_MINIFY | True | Enable minification of html | | EQ_ENABLE_SECURE_SESSION_COOKIE | True | Set secure session cookies | | EQ_MAX_HTTP_POST_CONTENT_LENGTH | 65536 | The maximum http post content length that the system wil accept | diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 04107b645c..36aef1307a 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -51,10 +51,7 @@ def __init__( f'{current_app.config["CDN_URL"]}{current_app.config["CDN_ASSETS_PATH"]}' ) self._address_lookup_api = current_app.config["ADDRESS_LOOKUP_API_URL"] - self._google_tag_manager_id = current_app.config.get("EQ_GOOGLE_TAG_MANAGER_ID") - self._google_tag_manager_auth = current_app.config.get( - "EQ_GOOGLE_TAG_MANAGER_AUTH" - ) + self._google_tag_id = current_app.config.get("EQ_GOOGLE_TAG_ID") self._survey_type = cookie_session.get("theme") self._preview_enabled = ( self._survey_config.schema.preview_enabled @@ -81,8 +78,7 @@ def context(self) -> dict[str, Any]: "address_lookup_api_url": self._address_lookup_api, "data_layer": self.data_layer_context, "include_csrf_token": self._include_csrf_token, - "google_tag_manager_id": self._google_tag_manager_id, - "google_tag_manager_auth": self._google_tag_manager_auth, + "google_tag_id": self._google_tag_id, "survey_type": self._survey_type, "preview_enabled": self._preview_enabled, "masthead_logo": self._survey_config.masthead_logo, diff --git a/app/settings.py b/app/settings.py index 8f1f4caaf9..6e6c11abf4 100644 --- a/app/settings.py +++ b/app/settings.py @@ -90,8 +90,7 @@ def utcoffset_or_fail(date_value, key): EQ_SESSION_TIMEOUT_SECONDS = int(os.getenv("EQ_SESSION_TIMEOUT_SECONDS", str(45 * 60))) -EQ_GOOGLE_TAG_MANAGER_ID = os.getenv("EQ_GOOGLE_TAG_MANAGER_ID") -EQ_GOOGLE_TAG_MANAGER_AUTH = os.getenv("EQ_GOOGLE_TAG_MANAGER_AUTH") +EQ_GOOGLE_TAG_ID = os.getenv("EQ_GOOGLE_TAG_ID") EQ_APPLICATION_VERSION_PATH = ".application-version" EQ_APPLICATION_VERSION = read_file(EQ_APPLICATION_VERSION_PATH) diff --git a/app/setup.py b/app/setup.py index 7f4f7d93ca..273a4ec3ff 100644 --- a/app/setup.py +++ b/app/setup.py @@ -60,15 +60,11 @@ "font-src": ["'self'", "data:", "https://fonts.gstatic.com"], "script-src": [ "'self'", - "https://tagmanager.google.com", "https://*.googletagmanager.com", - "'unsafe-inline'", ], "style-src": [ "'self'", - "https://tagmanager.google.com", "https://fonts.googleapis.com", - "'unsafe-inline'", ], "connect-src": [ "'self'", diff --git a/app/translations/messages.pot b/app/translations/messages.pot index ac138a2682..8281cfe719 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -217,15 +217,15 @@ msgstr "" msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:114 +#: app/helpers/template_helpers.py:110 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:145 +#: app/helpers/template_helpers.py:141 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:170 +#: app/helpers/template_helpers.py:166 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" @@ -907,19 +907,19 @@ msgid "" "your answers for your records." msgstr "" -#: templates/layouts/_base.html:143 templates/thank-you.html:71 +#: templates/layouts/_base.html:133 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/layouts/_base.html:144 templates/thank-you.html:72 +#: templates/layouts/_base.html:134 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:145 templates/thank-you.html:73 +#: templates/layouts/_base.html:135 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/layouts/_base.html:146 templates/thank-you.html:74 +#: templates/layouts/_base.html:136 templates/thank-you.html:74 msgid "seconds" msgstr "" @@ -1213,29 +1213,29 @@ msgstr "" msgid "Hide" msgstr "" -#: templates/layouts/_base.html:113 +#: templates/layouts/_base.html:103 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:138 +#: templates/layouts/_base.html:128 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:139 +#: templates/layouts/_base.html:129 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:140 +#: templates/layouts/_base.html:130 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:141 +#: templates/layouts/_base.html:131 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:142 +#: templates/layouts/_base.html:132 msgid "Continue survey" msgstr "" diff --git a/ci/deploy_app.sh b/ci/deploy_app.sh index 488b524a9f..6adef551e4 100755 --- a/ci/deploy_app.sh +++ b/ci/deploy_app.sh @@ -61,8 +61,7 @@ ADDRESS_LOOKUP_API_AUTH_TOKEN_LEEWAY_IN_SECONDS="${ADDRESS_LOOKUP_API_AUTH_TOKEN CONFIRMATION_EMAIL_LIMIT="${CONFIRMATION_EMAIL_LIMIT:=10}" VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS="${VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS:=2700}" -GOOGLE_TAG_MANAGER_ID="${GOOGLE_TAG_MANAGER_ID:=}" -GOOGLE_TAG_MANAGER_AUTH="${GOOGLE_TAG_MANAGER_AUTH:=}" +GOOGLE_TAG_ID="${GOOGLE_TAG_ID:=}" gcloud beta run deploy eq-questionnaire-runner \ @@ -109,5 +108,4 @@ gcloud beta run deploy eq-questionnaire-runner \ --set-env-vars ADDRESS_LOOKUP_API_AUTH_TOKEN_LEEWAY_IN_SECONDS="${ADDRESS_LOOKUP_API_AUTH_TOKEN_LEEWAY_IN_SECONDS}" \ --set-env-vars CONFIRMATION_EMAIL_LIMIT="${CONFIRMATION_EMAIL_LIMIT}" \ --set-env-vars VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS="${VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS}" \ - --set-env-vars GOOGLE_TAG_MANAGER_ID="${GOOGLE_TAG_MANAGER_ID}" \ - --set-env-vars GOOGLE_TAG_MANAGER_AUTH="${GOOGLE_TAG_MANAGER_AUTH}" + --set-env-vars GOOGLE_TAG_ID="${GOOGLE_TAG_ID}" diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index edb739abce..5970feb77b 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -73,32 +73,36 @@ {% block head %} - {% if google_tag_manager_id and google_tag_manager_auth %} - + {% if google_tag_id %} - + - {% endif %} {% endblock %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 4cf600edde..9006c71333 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -914,10 +914,7 @@ def test_context_set_from_app_config(app): current_app.config["CDN_URL"] = "test-cdn-url" current_app.config["CDN_ASSETS_PATH"] = "/test-assets-path" current_app.config["ADDRESS_LOOKUP_API_URL"] = "test-address-lookup-api-url" - current_app.config["EQ_GOOGLE_TAG_MANAGER_ID"] = "test-google-tag-manager-id" - current_app.config[ - "EQ_GOOGLE_TAG_MANAGER_AUTH" - ] = "test-google-tag-manager-auth" + current_app.config["EQ_GOOGLE_TAG_ID"] = "test-google-tag-manager-id" survey_config = SurveyConfig() context = ContextHelper( @@ -929,8 +926,7 @@ def test_context_set_from_app_config(app): assert context["cdn_url"] == "test-cdn-url/test-assets-path" assert context["address_lookup_api_url"] == "test-address-lookup-api-url" - assert context["google_tag_manager_id"] == "test-google-tag-manager-id" - assert context["google_tag_manager_auth"] == "test-google-tag-manager-auth" + assert context["google_tag_id"] == "test-google-tag-manager-id" @pytest.mark.parametrize( diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index 823332f7fa..913ded0dae 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -139,12 +139,12 @@ def test_csp_policy_headers(self): csp_policy_parts = headers["Content-Security-Policy"].split("; ") self.assertIn(f"default-src 'self' {cdn_url}", csp_policy_parts) self.assertIn( - "script-src 'self' https://tagmanager.google.com https://*.googletagmanager.com " - f"'unsafe-inline' {cdn_url} 'nonce-{request.csp_nonce}'", + "script-src 'self' https://*.googletagmanager.com " + f"{cdn_url} 'nonce-{request.csp_nonce}'", csp_policy_parts, ) self.assertIn( - f"style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com 'unsafe-inline' {cdn_url}", + f"style-src 'self' https://fonts.googleapis.com {cdn_url}", csp_policy_parts, ) self.assertIn( diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 4ac227be57..b1a6b984a4 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -6,15 +6,13 @@ class TestApplicationVariables(IntegrationTestCase): def setUp(self): settings.EQ_ENABLE_LIVE_RELOAD = True - settings.EQ_GOOGLE_TAG_MANAGER_ID = "TestId" - settings.EQ_GOOGLE_TAG_MANAGER_AUTH = "TestAuth" + settings.EQ_GOOGLE_TAG_ID = "TestId" super().setUp() def tearDown(self): super().tearDown() settings.EQ_ENABLE_LIVE_RELOAD = False - settings.EQ_GOOGLE_TAG_MANAGER_ID = None - settings.EQ_GOOGLE_TAG_MANAGER_AUTH = None + settings.EQ_GOOGLE_TAG_ID = None def test_google_analytics_code_and_credentials_are_present(self): self.launchSurvey("test_feedback", roles=["dumper"]) @@ -25,13 +23,11 @@ def test_google_analytics_code_and_credentials_are_present(self): ) self.get("/questionnaire/feedback/") self.assertStatusOK() - self.assertInHead("gtm.start") self.assertInHead( f'dataLayer = [{{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' ) self.assertInHead("https://www.googletagmanager.com") - self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_AUTH) - self.assertInHead(settings.EQ_GOOGLE_TAG_MANAGER_ID) + self.assertInHead(settings.EQ_GOOGLE_TAG_ID) def test_google_analytics_data_layer_has_no_null_fields(self): self.launchSurvey("test_textfield", roles=["dumper"]) @@ -42,7 +38,6 @@ def test_google_analytics_data_layer_has_no_null_fields(self): ) self.get("/questionnaire/name-block/") self.assertStatusOK() - self.assertInHead("gtm.start") # form_type is empty so should not be present self.assertInHead( f'dataLayer = [{{"survey_id": "001", "title": "Other input fields"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' From 6efbe6036e8ad7f98ce6de5251e3748ba18572ba Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 18 Jan 2024 14:12:54 +0000 Subject: [PATCH 363/567] Schemas v3.86.0 (#1299) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 8f8c6ffb94..27a90807cf 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.85.0 +v3.86.0 From b2a50e928fee48b75b0a05ef95f6f61a454748e1 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:53:10 +0000 Subject: [PATCH 364/567] Reintroduce unsafe-inline keyword to style-src of CSP policy (#1300) --- app/setup.py | 1 + tests/integration/test_app_create.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/setup.py b/app/setup.py index 273a4ec3ff..d13a3e31f1 100644 --- a/app/setup.py +++ b/app/setup.py @@ -65,6 +65,7 @@ "style-src": [ "'self'", "https://fonts.googleapis.com", + "'unsafe-inline'", ], "connect-src": [ "'self'", diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index 913ded0dae..b4a2003cc1 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -144,7 +144,7 @@ def test_csp_policy_headers(self): csp_policy_parts, ) self.assertIn( - f"style-src 'self' https://fonts.googleapis.com {cdn_url}", + f"style-src 'self' https://fonts.googleapis.com 'unsafe-inline' {cdn_url}", csp_policy_parts, ) self.assertIn( From 1bb38b665ffa2a2b56bfcdded846fe99abc098a0 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:29:45 +0000 Subject: [PATCH 365/567] Schemas v3.86.1 (#1303) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 27a90807cf..963acd8f65 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.86.0 +v3.86.1 From a9c29e9e6146409aa9437adfc638b127e7b5658d Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:57:58 +0000 Subject: [PATCH 366/567] Bug fix: Backwards routing does not work when dependencies exist (#1306) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> --- app/questionnaire/path_finder.py | 21 +++++++++--------- ...nfirmation_question_backwards_routing.json | 18 ++++++++++++--- tests/app/questionnaire/test_path_finder.py | 22 +++++++++++-------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/questionnaire/path_finder.py b/app/questionnaire/path_finder.py index 4b7e82d099..466c0aaf6c 100644 --- a/app/questionnaire/path_finder.py +++ b/app/questionnaire/path_finder.py @@ -105,7 +105,7 @@ def _build_routing_path_block_ids( when_rules_block_dependencies: list[str], ) -> list[str]: # Keep going unless we've hit the last block - + # routing_path_block_ids can be mutated by _evaluate_routing_rules routing_path_block_ids: list[str] = [] block_index = 0 repeating_list = self.schema.get_repeating_list_for_section( @@ -175,16 +175,16 @@ def _evaluate_routing_rules( routing_path_block_ids: list[str], when_rules_block_dependencies: list[str], ) -> int | None: - if when_rules_block_dependencies: - routing_path_block_ids = ( - when_rules_block_dependencies + routing_path_block_ids - ) + # Use `list` to create a shallow copy since routing_path_block_ids is mutated hence we don't want to update its memory reference + block_ids_for_dependencies = ( + list(routing_path_block_ids) + when_rules_block_dependencies + ) when_rule_evaluator = RuleEvaluator( self.schema, self.data_stores, location=this_location, - routing_path_block_ids=routing_path_block_ids, + routing_path_block_ids=block_ids_for_dependencies, ) for rule in routing_rules: rule_valid = ( @@ -224,16 +224,15 @@ def evaluate_skip_conditions( if not skip_conditions: return False - if when_rules_block_dependencies: - routing_path_block_ids = ( - when_rules_block_dependencies + routing_path_block_ids - ) + block_ids_for_dependencies = ( + list(routing_path_block_ids) + when_rules_block_dependencies + ) when_rule_evaluator = RuleEvaluator( schema=self.schema, data_stores=self.data_stores, location=current_location, - routing_path_block_ids=routing_path_block_ids, + routing_path_block_ids=block_ids_for_dependencies, ) return when_rule_evaluator.evaluate(skip_conditions["when"]) diff --git a/schemas/test/en/test_confirmation_question_backwards_routing.json b/schemas/test/en/test_confirmation_question_backwards_routing.json index 53555fb1cc..b8bc14b8fe 100644 --- a/schemas/test/en/test_confirmation_question_backwards_routing.json +++ b/schemas/test/en/test_confirmation_question_backwards_routing.json @@ -40,11 +40,11 @@ "sections": [ { "id": "default-section", - "title": "Questions", + "title": "Section 1", "groups": [ { "id": "confirmation", - "title": "Confirmation Question Test", + "title": "Confirmation Driver", "blocks": [ { "type": "Question", @@ -71,7 +71,19 @@ } ] } - }, + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Section 2", + "groups": [ + { + "id": "group-2", + "title": "Confirmation Question", + "blocks": [ { "id": "number-of-employees-total-block", "question": { diff --git a/tests/app/questionnaire/test_path_finder.py b/tests/app/questionnaire/test_path_finder.py index 1a84a43d16..f47562f025 100644 --- a/tests/app/questionnaire/test_path_finder.py +++ b/tests/app/questionnaire/test_path_finder.py @@ -414,12 +414,17 @@ def test_remove_answer_and_block_if_routing_backwards(): "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, + "block_ids": ["route-backwards-block"], + }, + { + "section_id": "section-2", + "list_item_id": None, + "status": CompletionStatus.COMPLETED, "block_ids": [ - "route-backwards-block", "number-of-employees-total-block", "confirm-zero-employees-block", ], - } + }, ] ) @@ -443,10 +448,10 @@ def test_remove_answer_and_block_if_routing_backwards(): assert ( len( path_finder.data_stores.progress_store.get_completed_block_ids( - SectionKey("default-section") + SectionKey("section-2") ) ) - == 3 + == 2 ) assert len(path_finder.data_stores.answer_store) == 3 @@ -454,17 +459,16 @@ def test_remove_answer_and_block_if_routing_backwards(): expected_path = RoutingPath( block_ids=[ - "route-backwards-block", "number-of-employees-total-block", "confirm-zero-employees-block", "number-of-employees-total-block", ], - section_id="default-section", + section_id="section-2", ) assert routing_path == expected_path assert path_finder.data_stores.progress_store.get_completed_block_ids( - SectionKey("default-section") - ) == progress_store.get_completed_block_ids(SectionKey("default-section")) + SectionKey("section-2") + ) == progress_store.get_completed_block_ids(SectionKey("section-2")) assert len(path_finder.data_stores.answer_store) == 2 assert not path_finder.data_stores.answer_store.get_answer( @@ -472,7 +476,7 @@ def test_remove_answer_and_block_if_routing_backwards(): ) assert ( path_finder.data_stores.progress_store.get_section_status( - SectionKey("default-section") + SectionKey("section-2") ) == CompletionStatus.IN_PROGRESS ) From 86a81aad807d9296688af1536be969abc830c9cc Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:48:20 +0000 Subject: [PATCH 367/567] Update to DS 67.0.5 (#1305) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index a76dccdfcc..87d32df300 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -67.0.0 +67.0.5 From 74647bb89d2545c0da925f523efea61523eabda5 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:17:47 +0000 Subject: [PATCH 368/567] Update dataflow purge task templates (#1304) --- ci/purge_expired_questionnaire_state.yaml | 2 +- ci/purge_expired_sessions.yaml | 2 +- ci/purge_submitted_questionnaire_state.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/purge_expired_questionnaire_state.yaml b/ci/purge_expired_questionnaire_state.yaml index 09de46a127..5228e3ab38 100644 --- a/ci/purge_expired_questionnaire_state.yaml +++ b/ci/purge_expired_questionnaire_state.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2021-11-12-00_RC00' + DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' run: path: bash args: diff --git a/ci/purge_expired_sessions.yaml b/ci/purge_expired_sessions.yaml index e4d1d66955..c8f4f72026 100644 --- a/ci/purge_expired_sessions.yaml +++ b/ci/purge_expired_sessions.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2021-11-12-00_RC00' + DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' EXPIRES_AT_PURGE_OFFSET_IN_SECONDS: 10800 run: path: bash diff --git a/ci/purge_submitted_questionnaire_state.yaml b/ci/purge_submitted_questionnaire_state.yaml index 8d992aed52..0c9855f1e3 100644 --- a/ci/purge_submitted_questionnaire_state.yaml +++ b/ci/purge_submitted_questionnaire_state.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2021-11-12-00_RC00' + DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' SUBMITTED_AT_PURGE_OFFSET_IN_SECONDS: 3600 run: path: bash From 693f9e9e3e2d3073dcaaa9cae7d3f56829b637a3 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:36:36 +0000 Subject: [PATCH 369/567] Allow the hub to be enabled with repeating sections (#1309) --- app/questionnaire/router.py | 29 +- ...test_hub_section_required_with_repeat.json | 431 ++++++++++++++++++ ...on_required_with_repeat_supplementary.json | 414 +++++++++++++++++ tests/app/questionnaire/test_router.py | 120 +++++ .../hub_and_spoke/hub_and_spoke.spec.js | 121 +++++ .../questionnaire/test_questionnaire_hub.py | 47 ++ 6 files changed, 1157 insertions(+), 5 deletions(-) create mode 100644 schemas/test/en/test_hub_section_required_with_repeat.json create mode 100644 schemas/test/en/test_hub_section_required_with_repeat_supplementary.json diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index fb8faac566..b8ceb78e80 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -3,6 +3,7 @@ from flask import url_for from app.data_models.data_stores import DataStores +from app.data_models.list_store import ListModel from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import Location, SectionKey from app.questionnaire.path_finder import PathFinder @@ -82,11 +83,29 @@ def can_access_location( return location.block_id in self._get_allowable_path(routing_path) def can_access_hub(self) -> bool: - return self._schema.is_flow_hub and all( - self._data_stores.progress_store.is_section_complete(SectionKey(section_id)) - for section_id in self._schema.get_section_ids_required_for_hub() - if section_id in self.enabled_section_ids - ) + if not self._schema.is_flow_hub: + return False + + for section_id in self._schema.get_section_ids_required_for_hub(): + if section_id in self.enabled_section_ids: + repeating_list_for_section = ( + self._schema.get_repeating_list_for_section(section_id) + ) + + items: ListModel | list[None] = ( + self._data_stores.list_store.get(repeating_list_for_section) + if repeating_list_for_section + else [None] + ) + + for list_item_id in items: + section_key = SectionKey(section_id, list_item_id) + if not self._data_stores.progress_store.is_section_complete( + section_key + ): + return False + + return True def can_display_section_summary(self, section_key: SectionKey) -> bool: return bool( diff --git a/schemas/test/en/test_hub_section_required_with_repeat.json b/schemas/test/en/test_hub_section_required_with_repeat.json new file mode 100644 index 0000000000..ab13825938 --- /dev/null +++ b/schemas/test/en/test_hub_section_required_with_repeat.json @@ -0,0 +1,431 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Hub & Spoke Enabled with Required Repeating Sections", + "theme": "default", + "description": "A questionnaire to demo hub and spoke functionality with required repeating sections", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["list-collector-section", "personal-details-section"] + } + }, + "sections": [ + { + "summary": { + "show_on_completion": true, + "items": [ + { + "type": "List", + "for_list": "people", + "title": "Household members", + "add_link_text": "Add someone to this household", + "empty_list_text": "There are no householders" + } + ] + }, + "id": "list-collector-section", + "title": "Household", + "groups": [ + { + "id": "group", + "title": "List", + "blocks": [ + { + "id": "primary-person-list-collector", + "type": "PrimaryPersonListCollector", + "for_list": "people", + "add_or_edit_block": { + "id": "add-or-edit-primary-person", + "type": "PrimaryPersonListAddOrEditQuestion", + "question": { + "id": "primary-person-add-or-edit-question", + "type": "General", + "title": "What is your name?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "question": { + "id": "primary-confirmation-question", + "type": "General", + "title": "Do you live here?", + "answers": [ + { + "id": "you-live-here", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + { + "id": "list-collector", + "type": "ListCollector", + "for_list": "people", + "question": { + "id": "confirmation-question", + "type": "General", + "title": "Does anyone else live here?", + "answers": [ + { + "id": "anyone-else", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-person", + "type": "ListAddQuestion", + "question": { + "id": "add-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "edit_block": { + "id": "edit-person", + "type": "ListEditQuestion", + "question": { + "id": "edit-question", + "type": "General", + "title": "What is the name of the person?", + "answers": [ + { + "id": "first-name", + "label": "First name", + "mandatory": true, + "type": "TextField" + }, + { + "id": "last-name", + "label": "Last name", + "mandatory": true, + "type": "TextField" + } + ] + } + }, + "remove_block": { + "id": "remove-person", + "type": "ListRemoveQuestion", + "question": { + "id": "remove-question", + "type": "General", + "title": "Are you sure you want to remove this person?", + "answers": [ + { + "id": "remove-confirmation", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Household members", + "item_title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "personal-details-section", + "title": "Personal Details", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "people", + "title": { + "text": "{person_name}", + "placeholders": [ + { + "placeholder": "person_name", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ], + "delimiter": " " + } + } + ] + } + ] + } + }, + "groups": [ + { + "id": "personal-details-group", + "title": "Personal Details", + "blocks": [ + { + "id": "proxy", + "question": { + "answers": [ + { + "default": "Yes", + "id": "proxy-answer", + "mandatory": false, + "options": [ + { + "label": "No, I’m answering for myself", + "value": "No, I’m answering for myself" + }, + { + "label": "Yes", + "value": "Yes" + } + ], + "type": "Radio" + } + ], + "id": "proxy-question", + "title": "Are you answering the questions on behalf of someone else?", + "type": "General" + }, + "type": "Question" + }, + { + "id": "date-of-birth", + "question_variants": [ + { + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": true, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": "What is your date of birth?", + "type": "General" + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "No, I’m answering for myself" + ] + } + }, + { + "question": { + "answers": [ + { + "id": "date-of-birth-answer", + "mandatory": true, + "maximum": { + "value": "now" + }, + "minimum": { + "offset_by": { + "years": -115 + }, + "value": "2019-10-13" + }, + "type": "Date" + } + ], + "guidance": { + "contents": [ + { + "description": "For example 31 12 1970" + } + ] + }, + "id": "date-of-birth-question", + "title": { + "placeholders": [ + { + "placeholder": "person_name_possessive", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "first-name" + }, + { + "source": "answers", + "identifier": "last-name" + } + ] + }, + "transform": "concatenate_list" + }, + { + "arguments": { + "string_to_format": { + "source": "previous_transform" + } + }, + "transform": "format_possessive" + } + ] + } + ], + "text": "What is {person_name_possessive} date of birth?" + }, + "type": "General" + }, + "when": { + "==": [ + { + "source": "answers", + "identifier": "proxy-answer" + }, + "Yes" + ] + } + } + ], + "type": "Question" + } + ] + } + ] + } + ] +} diff --git a/schemas/test/en/test_hub_section_required_with_repeat_supplementary.json b/schemas/test/en/test_hub_section_required_with_repeat_supplementary.json new file mode 100644 index 0000000000..a61369a87a --- /dev/null +++ b/schemas/test/en/test_hub_section_required_with_repeat_supplementary.json @@ -0,0 +1,414 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "123", + "title": "Test Hub Enabled with Repeats Using Supplementary Data", + "theme": "default", + "description": "A questionnaire to demo the Hub enabled when repeating sections using Supplementary data are complete.", + "metadata": [ + { + "name": "survey_id", + "type": "string" + }, + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "sds_dataset_id", + "type": "string" + } + ], + "supplementary_data": { + "lists": ["employees"] + }, + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["introduction-section", "section-2", "section-3"] + } + }, + "post_submission": { + "view_response": true + }, + "sections": [ + { + "id": "introduction-section", + "title": "Introduction", + "groups": [ + { + "id": "introduction-group", + "title": "Introduction Group", + "blocks": [ + { + "id": "loaded-successfully-block", + "type": "Interstitial", + "content": { + "title": "Supplementary Data", + "contents": [ + { + "title": "You have successfully loaded Supplementary data", + "description": "Press continue to proceed to the introduction", + "guidance": { + "contents": [ + { + "description": "The purpose of this block, is to test that supplementary data loads successfully, separate to using the supplementary data" + } + ] + } + } + ] + } + }, + { + "id": "introduction-block", + "type": "Introduction", + "primary_content": [ + { + "id": "business-details", + "title": { + "text": "You are completing this survey for {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "contents": [ + { + "description": { + "text": "If the company details or structure have changed contact us on {telephone_number_link}", + "placeholders": [ + { + "placeholder": "telephone_number_link", + "value": { + "source": "supplementary_data", + "identifier": "company_details", + "selectors": ["telephone_number"] + } + } + ] + } + }, + { + "guidance": { + "contents": [ + { + "title": "Guidance for completing this survey", + "list": [ + "The company name, telephone number all come from supplementary data", + "if you picked the supplementary dataset with guidance, there will be a 3rd bullet point below this one, with the supplementary guidance.", + { + "text": "{survey_guidance}", + "placeholders": [ + { + "placeholder": "survey_guidance", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "source": "supplementary_data", + "identifier": "guidance" + } + ] + } + } + ] + } + ] + } + ] + } + ] + } + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "section-2", + "title": "Employees", + "groups": [ + { + "id": "employee-reporting", + "blocks": [ + { + "id": "list-collector-employees", + "type": "ListCollectorContent", + "page_title": "Employees", + "for_list": "employees", + "content": { + "title": "Employees", + "contents": [ + { + "definition": { + "title": "Company employees", + "contents": [ + { + "description": "List of previously reported employees." + } + ] + } + }, + { + "description": "You have previously reported on the above employees. Press continue to proceed to the next section where you can add any additional employees." + } + ] + }, + "summary": { + "title": "employees", + "item_title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "section-3", + "title": "Employee Details", + "summary": { + "show_on_completion": true + }, + "repeat": { + "for_list": "employees", + "title": { + "text": "{employee_name}", + "placeholders": [ + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + }, + "groups": [ + { + "id": "employee-detail-questions", + "blocks": [ + { + "type": "Question", + "id": "length-of-employment", + "question": { + "id": "length-employment-question", + "type": "General", + "title": { + "text": "When did {employee_name} start working for {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "employment-start", + "label": { + "text": "Start date at {company_name}", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + } + ] + }, + "mandatory": true, + "type": "Date", + "maximum": { + "value": "now" + }, + "minimum": { + "value": { + "source": "supplementary_data", + "identifier": "incorporation_date" + } + } + } + ] + } + }, + { + "id": "conditional-employee-block", + "type": "Question", + "skip_conditions": { + "when": { + "!=": [ + { + "count": [ + { + "source": "list", + "identifier": "employees" + } + ] + }, + 3 + ] + } + }, + "question": { + "id": "conditional-employee-question", + "guidance": { + "contents": [ + { + "description": "This block is enabled because there are 3 employees in the supplementary dataset" + } + ] + }, + "type": "General", + "title": { + "text": "Has {employee_name} been promoted since starting at {company_name}?", + "placeholders": [ + { + "placeholder": "company_name", + "value": { + "source": "supplementary_data", + "identifier": "company_name" + } + }, + { + "placeholder": "employee_name", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "forename"] + }, + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + }, + "answers": [ + { + "id": "promoted-yes-no-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + } + ] + } + ] + } + ] +} diff --git a/tests/app/questionnaire/test_router.py b/tests/app/questionnaire/test_router.py index 02ef686ddb..7034e6d272 100644 --- a/tests/app/questionnaire/test_router.py +++ b/tests/app/questionnaire/test_router.py @@ -112,6 +112,126 @@ def test_full_routing_path_with_repeating_sections(self): assert expected_path == routing_path + def test_can_access_hub(self): + self.schema = load_schema_from_name( + "test_repeating_sections_with_hub_and_spoke" + ) + + assert self.router.can_access_hub() + + def test_can_access_hub_with_required_sections_enabled_and_sections_complete(self): + self.schema = load_schema_from_name("test_hub_section_required_and_enabled") + + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="household-section", + block_ids=["household-relationships-block"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="relationships-section", + block_ids=["relationships-count"], + status=CompletionStatus.COMPLETED, + ), + ], + ) + + assert self.router.can_access_hub() + + def test_can_access_hub_with_required_sections_enabled_and_section_incomplete(self): + self.schema = load_schema_from_name("test_hub_section_required_and_enabled") + + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="household-section", + block_ids=["household-relationships-block"], + status=CompletionStatus.IN_PROGRESS, + ), + ], + ) + + assert not self.router.can_access_hub() + + def test_can_access_hub_with_required_sections_enabled_and_repeating_sections_complete( + self, + ): + self.schema = load_schema_from_name("test_hub_section_required_with_repeat") + + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="list-collector-section", + block_ids=["primary-person-list-collector", "list-collector"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="personal-details-section", + block_ids=["proxy", "date-of-birth"], + status=CompletionStatus.COMPLETED, + list_item_id="XLvVvS", + ), + ProgressDict( + section_id="personal-details-section", + block_ids=["proxy", "date-of-birth"], + status=CompletionStatus.COMPLETED, + list_item_id="mJPRpW", + ), + ], + ) + + self.data_stores.list_store = ListStore( + [ + { + "items": ["XLvVvS", "mJPRpW"], + "name": "people", + "primary_person": "XLvVvS", + } + ] + ) + + assert self.router.can_access_hub() + + def test_can_access_hub_with_required_sections_enabled_and_repeating_sections_incomplete( + self, + ): + self.schema = load_schema_from_name("test_hub_section_required_with_repeat") + + self.data_stores.progress_store = ProgressStore( + [ + ProgressDict( + section_id="list-collector-section", + block_ids=["primary-person-list-collector", "list-collector"], + status=CompletionStatus.COMPLETED, + ), + ProgressDict( + section_id="personal-details-section", + block_ids=["proxy", "date-of-birth"], + status=CompletionStatus.COMPLETED, + list_item_id="XLvVvS", + ), + ProgressDict( + section_id="personal-details-section", + block_ids=["proxy"], + status=CompletionStatus.IN_PROGRESS, + list_item_id="mJPRpW", + ), + ], + ) + + self.data_stores.list_store = ListStore( + [ + { + "items": ["XLvVvS", "mJPRpW"], + "name": "people", + "primary_person": "XLvVvS", + } + ] + ) + + assert not self.router.can_access_hub() + class TestRouterPathCompletion(RouterTestCase): def test_is_complete(self): diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index d70363b693..592e06c284 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -8,7 +8,22 @@ import HowManyPeopleLiveHere from "../../../generated_pages/hub_and_spoke/how-ma import HubPage from "../../../base_pages/hub.page.js"; import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; import RelationshipsSummary from "../../../generated_pages/hub_and_spoke/relationships-section-summary.page.js"; +import ListCollectorSectionSummaryPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector-section-summary.page.js"; +import ProxyRepeatPage from "../../../generated_pages/hub_section_required_with_repeat/proxy.page.js"; import { click } from "../../../helpers"; +import DateOfBirthPage from "../../../generated_pages/hub_section_required_with_repeat/date-of-birth.page"; +import PrimaryPersonListCollectorPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector.page"; +import PrimaryPersonListCollectorAddPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector-add.page"; +import ListCollectorPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector.page"; +import ListCollectorAddPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector-add.page"; +import RepeatingSummaryPage from "../../../generated_pages/hub_section_required_with_repeat/personal-details-section-summary.page"; +import { getRandomString } from "../../../jwt_helper"; +import LoadedSuccessfullyBlockPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/loaded-successfully-block.page"; +import IntroductionBlockPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/introduction-block.page"; +import ListCollectorEmployeesPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/list-collector-employees.page.js"; +import LengthOfEmploymentPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/length-of-employment.page.js"; +import Section3Page from "../../../generated_pages/hub_section_required_with_repeat_supplementary/section-3-summary.page.js"; + describe("Feature: Hub and Spoke", () => { const hubAndSpokeSchema = "test_hub_and_spoke.json"; @@ -228,6 +243,112 @@ describe("Feature: Hub and Spoke", () => { }); }); + describe("Given a user opens a schema with hub required sections based on a repeating section", () => { + beforeEach("Load survey", async () => { + await browser.openQuestionnaire("test_hub_section_required_with_repeat.json"); + }); + + it("When all the repeating sections are complete, Then the hub should be displayed", async () => { + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await click(PrimaryPersonListCollectorAddPage.submit()); + await $(ListCollectorPage.yes()).click(); + await $(ListCollectorPage.submit()).click(); + await $(ListCollectorAddPage.firstName()).setValue("John"); + await $(ListCollectorAddPage.lastName()).setValue("Doe"); + await click(ListCollectorAddPage.submit()); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await click(ListCollectorSectionSummaryPage.submit()); + + // Try to access the hub + await browser.url(HubPage.url()); + + // Redirected to the repeating sections to be completed + await $(ProxyRepeatPage.yes()).click(); + await $(ProxyRepeatPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue(12); + await $(DateOfBirthPage.month()).setValue(4); + await $(DateOfBirthPage.year()).setValue(2021); + await click(DateOfBirthPage.submit()); + await $(RepeatingSummaryPage.submit()).click(); + await $(ProxyRepeatPage.yes()).click(); + await $(ProxyRepeatPage.submit()).click(); + await $(DateOfBirthPage.day()).setValue(1); + await $(DateOfBirthPage.month()).setValue(1); + await $(DateOfBirthPage.year()).setValue(2000); + await $(RepeatingSummaryPage.submit()).click(); + await expect(browser).toHaveUrlContaining(HubPage.url()); + }); + + it("When the repeating sections are incomplete, Then the hub should not be displayed", async () => { + await $(PrimaryPersonListCollectorPage.yes()).click(); + await $(PrimaryPersonListCollectorPage.submit()).click(); + await $(PrimaryPersonListCollectorAddPage.firstName()).setValue("Marcus"); + await $(PrimaryPersonListCollectorAddPage.lastName()).setValue("Twin"); + await click(PrimaryPersonListCollectorAddPage.submit()); + await $(ListCollectorPage.no()).click(); + await $(ListCollectorPage.submit()).click(); + await click(ListCollectorSectionSummaryPage.submit()); + + // Don't complete all the repeating questions + await $(ProxyRepeatPage.yes()).click(); + await $(ProxyRepeatPage.submit()).click(); + + await browser.url(HubPage.url()); + await expect(browser).toHaveUrlContaining("date-of-birth"); + }); + }); + + describe("Given a user opens a schema with hub required sections based on a repeating section using supplementary data", () => { + beforeEach("Load survey", async () => { + const responseId = getRandomString(16); + + await browser.openQuestionnaire("test_hub_section_required_with_repeat_supplementary.json.json", { + version: "v2", + sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + responseId, + }); + }); + + it("When all the repeating sections are complete, Then the hub should be displayed", async () => { + await click(LoadedSuccessfullyBlockPage.submit()); + await click(IntroductionBlockPage.submit()); + + // Complete the repeating sections using supplementary data + await click(ListCollectorEmployeesPage.submit()); + await $(LengthOfEmploymentPage.day()).setValue(1); + await $(LengthOfEmploymentPage.month()).setValue(1); + await $(LengthOfEmploymentPage.year()).setValue(1930); + await click(LengthOfEmploymentPage.submit()); + await click(Section3Page.submit()); + await $(LengthOfEmploymentPage.day()).setValue(1); + await $(LengthOfEmploymentPage.month()).setValue(1); + await $(LengthOfEmploymentPage.year()).setValue(1930); + await click(LengthOfEmploymentPage.submit()); + await click(Section3Page.submit()); + await expect(browser).toHaveUrlContaining(HubPage.url()); + }); + + it("When the repeating sections are incomplete. Then the hub should not be displayed", async () => { + await click(LoadedSuccessfullyBlockPage.submit()); + await click(IntroductionBlockPage.submit()); + + // Don't complete the repeating sections that use supplementary data + await click(ListCollectorEmployeesPage.submit()); + await $(LengthOfEmploymentPage.day()).setValue(1); + await $(LengthOfEmploymentPage.month()).setValue(1); + await $(LengthOfEmploymentPage.year()).setValue(1930); + await click(LengthOfEmploymentPage.submit()); + await click(Section3Page.submit()); + + await browser.url(HubPage.url()); + await expect(browser).toHaveUrlContaining("length-of-employment"); + }); + }); + describe("Given a section is complete and the user has been returned to a section summary by clicking the 'View answers' link ", () => { beforeEach("Complete section", async () => { await browser.openQuestionnaire(hubAndSpokeSchema); diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index 685476caa1..270425c3df 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -177,3 +177,50 @@ def test_hub_section_required_but_enabled_true(self): # Then I should be redirected to the second section self.assertEqualUrl("/questionnaire/relationships-count/") + + def test_hub_section_enabled_and_accessible_with_repeating_sections(self): + # Given the hub is enabled and there are two required sections (and one is for a repeat) + self.launchSurvey("test_hub_section_required_with_repeat") + + # When I answer I complete the first section and the repeating section + self.post({"you-live-here": "Yes"}) + self.post({"first-name": "John", "last-name": "Doe"}) + self.post({"anyone-else": "No"}) + self.post() + self.post({"proxy-answer": "Yes"}) + self.post( + { + "date-of-birth-answer-day": 1, + "date-of-birth-answer-month": 2, + "date-of-birth-answer-year": 1999, + } + ) + self.post() + + # Then I should see the hub + self.assertEqualUrl(HUB_URL_PATH) + + def test_hub_section_is_inaccessible_when_all_repeats_not_complete(self): + # Given the hub is enabled and there are two required sections (and one is for a repeat) + self.launchSurvey("test_hub_section_required_with_repeat") + + # When I complete the first section and the first repeat, but not the second repeat + self.post({"you-live-here": "Yes"}) + self.post({"first-name": "John", "last-name": "Doe"}) + self.post({"anyone-else": "Yes"}) + self.post({"first-name": "Jane", "last-name": "Doe"}) + self.post({"anyone-else": "No"}) + self.post() + self.post({"proxy-answer": "Yes"}) + self.post( + { + "date-of-birth-answer-day": 1, + "date-of-birth-answer-month": 2, + "date-of-birth-answer-year": 1999, + } + ) + self.post() + + # Then the hub should not yet be accessible + self.get(HUB_URL_PATH) + self.assertInUrl("/proxy") From 53a08cf16a083f526e51ad8cda6c78180cff069a Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:38:17 +0000 Subject: [PATCH 370/567] Schemas v3.87.0 (#1312) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 963acd8f65..4bccaba0c5 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.86.1 +v3.87.0 From a049f7b73f101bbaf8358824c968fb90956815a7 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:30:32 +0000 Subject: [PATCH 371/567] Schemas v4.0.0 (#1313) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 4bccaba0c5..857572fcd3 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v3.87.0 +v4.0.0 From 6086525df9a87a06018a4522871c53288d3cec55 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 1 Feb 2024 12:05:37 +0000 Subject: [PATCH 372/567] Update dependencies in pipfile and package json locks (#1308) * Update relevant files * Update jinja2 version --------- Co-authored-by: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> --- Pipfile.lock | 5497 +++++++++++++++++++++++---------------------- package-lock.json | 8 +- package.json | 2 +- 3 files changed, 2755 insertions(+), 2752 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 02ad875c5a..3d44cfb4e5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,2750 +1,2753 @@ { - "_meta": { - "hash": { - "sha256": "fc45f58bb88b36d9a6136dce2a43825ddb10145475d5110804526ebe2eb4dc05" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.11" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "babel": { - "hashes": [ - "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900", - "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed" - ], - "markers": "python_version >= '3.7'", - "version": "==2.13.1" - }, - "blinker": { - "hashes": [ - "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", - "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.0" - }, - "boto3": { - "hashes": [ - "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", - "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "botocore": { - "hashes": [ - "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", - "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" - ], - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "brotli": { - "hashes": [ - "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", - "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", - "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", - "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", - "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", - "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", - "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", - "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", - "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", - "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", - "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", - "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", - "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", - "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", - "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", - "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", - "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", - "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", - "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", - "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", - "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", - "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", - "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", - "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", - "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", - "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", - "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", - "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", - "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", - "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", - "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", - "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", - "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", - "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", - "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", - "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", - "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", - "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", - "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", - "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", - "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", - "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", - "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", - "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", - "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", - "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", - "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", - "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", - "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", - "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", - "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", - "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", - "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", - "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", - "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", - "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", - "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", - "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", - "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", - "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", - "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", - "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", - "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", - "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", - "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", - "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", - "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", - "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", - "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", - "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", - "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", - "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", - "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", - "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", - "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", - "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", - "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", - "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", - "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", - "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", - "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", - "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", - "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" - ], - "markers": "platform_python_implementation != 'PyPy'", - "version": "==1.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2", - "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.2" - }, - "certifi": { - "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, - "coloredlogs": { - "hashes": [ - "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", - "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==15.0.1" - }, - "cryptography": { - "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.7" - }, - "deprecated": { - "hashes": [ - "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", - "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.14" - }, - "dnspython": { - "hashes": [ - "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8", - "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984" - ], - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==2.4.2" - }, - "email-validator": { - "hashes": [ - "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44", - "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.1.0.post1" - }, - "flask": { - "hashes": [ - "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", - "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "flask-babel": { - "hashes": [ - "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625", - "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593" - ], - "index": "pypi", - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==4.0.0" - }, - "flask-compress": { - "hashes": [ - "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c", - "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee" - ], - "index": "pypi", - "version": "==1.14" - }, - "flask-login": { - "hashes": [ - "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", - "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.6.3" - }, - "flask-talisman": { - "hashes": [ - "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208", - "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff" - ], - "index": "pypi", - "version": "==1.1.0" - }, - "flask-wtf": { - "hashes": [ - "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695", - "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.2.1" - }, - "gevent": { - "hashes": [ - "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", - "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", - "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", - "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", - "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", - "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", - "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", - "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", - "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", - "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", - "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", - "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", - "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", - "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", - "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", - "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", - "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", - "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", - "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", - "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", - "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", - "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", - "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", - "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", - "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", - "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", - "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", - "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", - "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", - "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", - "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", - "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", - "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", - "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", - "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", - "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", - "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", - "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", - "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", - "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" - ], - "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", - "version": "==23.9.1" - }, - "google-api-core": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41", - "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952" - ], - "markers": "python_version >= '3.7'", - "version": "==2.14.0" - }, - "google-auth": { - "hashes": [ - "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3", - "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2" - ], - "markers": "python_version >= '3.7'", - "version": "==2.23.4" - }, - "google-cloud-core": { - "hashes": [ - "sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb", - "sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.3" - }, - "google-cloud-datastore": { - "hashes": [ - "sha256:369df0653128fe63f0fb1b5a1cd5a05edae49a805f79f9b639ca9d58833bc6b5", - "sha256:63b31b676dcb2786a650d23d324d8f886c4e14586afdd0cfe6e260a73f12448e" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.18.0" - }, - "google-cloud-pubsub": { - "hashes": [ - "sha256:32eb61fd4c1dc6c842f594d69d9afa80544e3b327aa640a164eb6fb0201eaf2d", - "sha256:f32144ad9ed32331a80a2f8379a3ca7526bbc01e7bd76de2e8ab52e492d21f50" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.18.4" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d", - "sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.13.0" - }, - "google-cloud-tasks": { - "hashes": [ - "sha256:3efb280e7a635f57860a06a346131c117042e820ad483afd256800fae1b4d701", - "sha256:801a671c6dbd2269179037e93490f87e2df59ddfcb59551128bab27e7dd09f94" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.14.2" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7", - "sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b" - ], - "markers": "python_version >= '3.7'", - "version": "==2.6.0" - }, - "googleapis-common-protos": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0", - "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b" - ], - "markers": "python_version >= '3.7'", - "version": "==1.61.0" - }, - "greenlet": { - "hashes": [ - "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", - "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", - "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", - "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", - "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", - "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", - "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", - "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", - "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", - "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", - "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", - "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", - "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", - "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", - "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", - "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", - "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", - "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", - "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", - "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", - "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", - "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", - "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", - "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", - "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", - "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", - "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", - "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", - "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", - "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", - "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", - "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", - "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", - "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", - "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", - "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", - "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", - "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", - "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", - "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", - "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", - "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", - "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", - "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", - "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", - "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", - "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", - "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", - "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", - "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", - "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", - "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", - "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", - "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", - "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", - "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", - "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" - ], - "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", - "version": "==3.0.1" - }, - "grpc-google-iam-v1": { - "hashes": [ - "sha256:009197a7f1eaaa22149c96e5e054ac5934ba7241974e92663d8d3528a21203d1", - "sha256:834da89f4c4a2abbe842a793ed20fc6d9a77011ef2626755b1b89116fb9596d7" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.7" - }, - "grpcio": { - "hashes": [ - "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8", - "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea", - "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69", - "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb", - "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07", - "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d", - "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b", - "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354", - "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79", - "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59", - "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86", - "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7", - "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e", - "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281", - "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069", - "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb", - "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d", - "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3", - "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25", - "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c", - "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a", - "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80", - "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf", - "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118", - "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53", - "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e", - "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9", - "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556", - "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b", - "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e", - "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994", - "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc", - "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a", - "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d", - "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e", - "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718", - "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4", - "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab", - "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552", - "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3", - "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391", - "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13", - "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7", - "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3", - "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025", - "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8", - "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6", - "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473", - "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94", - "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0", - "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9", - "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d", - "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881", - "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.59.3" - }, - "grpcio-status": { - "hashes": [ - "sha256:2fd2eb39ca4e9afb3c874c0878ff75b258db0b7dcc25570fc521f16ae0ab942a", - "sha256:65c394ba43380d6bdf8c04c61efc493104b5535552aed35817a1b4dc66598a1f" - ], - "markers": "python_version >= '3.6'", - "version": "==1.59.3" - }, - "gunicorn": { - "hashes": [ - "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", - "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==21.2.0" - }, - "htmlmin": { - "hashes": [ - "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178" - ], - "index": "pypi", - "version": "==0.1.12" - }, - "humanfriendly": { - "hashes": [ - "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", - "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==10.0" - }, - "humanize": { - "hashes": [ - "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", - "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.9.0" - }, - "idna": { - "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" - ], - "markers": "python_version >= '3.5'", - "version": "==3.6" - }, - "itsdangerous": { - "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "jsonpointer": { - "hashes": [ - "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", - "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==2.4" - }, - "jwcrypto": { - "hashes": [ - "sha256:2c1dc51cf8e38ddf324795dfe9426dee9dd46caf47f535ccbc18781fba810b8d" - ], - "markers": "python_version >= '3.6'", - "version": "==1.5.0" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "marshmallow": { - "hashes": [ - "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", - "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.20.1" - }, - "ordered-set": { - "hashes": [ - "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", - "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pdfkit": { - "hashes": [ - "sha256:992f821e1e18fc8a0e701ecae24b51a2d598296a180caee0a24c0af181da02a9", - "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f", - "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e" - ], - "index": "pypi", - "version": "==1.0.0" - }, - "pika": { - "hashes": [ - "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", - "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.3.2" - }, - "proto-plus": { - "hashes": [ - "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df", - "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.3" - }, - "protobuf": { - "hashes": [ - "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd", - "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb", - "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0", - "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7", - "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b", - "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2", - "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510", - "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6", - "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd", - "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10", - "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7" - ], - "markers": "python_version >= '3.8'", - "version": "==4.25.1" - }, - "pyasn1": { - "hashes": [ - "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58", - "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.1" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-snappy": { - "hashes": [ - "sha256:03bb511380fca2a13325b6f16fe8234c8e12da9660f0258cd45d9a02ffc916af", - "sha256:0bdb6942180660bda7f7d01f4c0def3cfc72b1c6d99aad964801775a3e379aba", - "sha256:0d489b50f49433494160c45048fe806de6b3aeab0586e497ebd22a0bab56e427", - "sha256:1a993dc8aadd901915a510fe6af5f20ae4256f527040066c22a154db8946751f", - "sha256:1d029f7051ec1bbeaa3e03030b6d8ed47ceb69cae9016f493c802a08af54e026", - "sha256:277757d5dad4e239dc1417438a0871b65b1b155beb108888e7438c27ffc6a8cc", - "sha256:2a7e528ab6e09c0d67dcb61a1730a292683e5ff9bb088950638d3170cf2a0a54", - "sha256:2aaaf618c68d8c9daebc23a20436bd01b09ee70d7fbf7072b7f38b06d2fab539", - "sha256:2be4f4550acd484912441f5f1209ba611ac399aac9355fee73611b9a0d4f949c", - "sha256:39692bedbe0b717001a99915ac0eb2d9d0bad546440d392a2042b96d813eede1", - "sha256:3fb9a88a4dd6336488f3de67ce75816d0d796dce53c2c6e4d70e0b565633c7fd", - "sha256:4038019b1bcaadde726a57430718394076c5a21545ebc5badad2c045a09546cf", - "sha256:463fd340a499d47b26ca42d2f36a639188738f6e2098c6dbf80aef0e60f461e1", - "sha256:4d3cafdf454354a621c8ab7408e45aa4e9d5c0b943b61ff4815f71ca6bdf0130", - "sha256:4ec533a8c1f8df797bded662ec3e494d225b37855bb63eb0d75464a07947477c", - "sha256:530bfb9efebcc1aab8bb4ebcbd92b54477eed11f6cf499355e882970a6d3aa7d", - "sha256:546c1a7470ecbf6239101e9aff0f709b68ca0f0268b34d9023019a55baa1f7c6", - "sha256:5843feb914796b1f0405ccf31ea0fb51034ceb65a7588edfd5a8250cb369e3b2", - "sha256:586724a0276d7a6083a17259d0b51622e492289a9998848a1b01b6441ca12b2f", - "sha256:59e975be4206cc54d0a112ef72fa3970a57c2b1bcc2c97ed41d6df0ebe518228", - "sha256:5a453c45178d7864c1bdd6bfe0ee3ed2883f63b9ba2c9bb967c6b586bf763f96", - "sha256:5bb05c28298803a74add08ba496879242ef159c75bc86a5406fac0ffc7dd021b", - "sha256:5e973e637112391f05581f427659c05b30b6843bc522a65be35ac7b18ce3dedd", - "sha256:66c80e9b366012dbee262bb1869e4fc5ba8786cda85928481528bc4a72ec2ee8", - "sha256:6a7620404da966f637b9ce8d4d3d543d363223f7a12452a575189c5355fc2d25", - "sha256:6f8bf4708a11b47517baf962f9a02196478bbb10fdb9582add4aa1459fa82380", - "sha256:735cd4528c55dbe4516d6d2b403331a99fc304f8feded8ae887cf97b67d589bb", - "sha256:7778c224efc38a40d274da4eb82a04cac27aae20012372a7db3c4bbd8926c4d4", - "sha256:8277d1f6282463c40761f802b742f833f9f2449fcdbb20a96579aa05c8feb614", - "sha256:88b6ea78b83d2796f330b0af1b70cdd3965dbdab02d8ac293260ec2c8fe340ee", - "sha256:8c07220408d3268e8268c9351c5c08041bc6f8c6172e59d398b71020df108541", - "sha256:8d0c019ee7dcf2c60e240877107cddbd95a5b1081787579bf179938392d66480", - "sha256:90b0186516b7a101c14764b0c25931b741fb0102f21253eff67847b4742dfc72", - "sha256:9837ac1650cc68d22a3cf5f15fb62c6964747d16cecc8b22431f113d6e39555d", - "sha256:9eac51307c6a1a38d5f86ebabc26a889fddf20cbba7a116ccb54ba1446601d5b", - "sha256:9f0c0d88b84259f93c3aa46398680646f2c23e43394779758d9f739c34e15295", - "sha256:a0ad38bc98d0b0497a0b0dbc29409bcabfcecff4511ed7063403c86de16927bc", - "sha256:b265cde49774752aec9ca7f5d272e3f98718164afc85521622a8a5394158a2b5", - "sha256:b6a107ab06206acc5359d4c5632bd9b22d448702a79b3169b0c62e0fb808bb2a", - "sha256:b7f920eaf46ebf41bd26f9df51c160d40f9e00b7b48471c3438cb8d027f7fb9b", - "sha256:c20498bd712b6e31a4402e1d027a1cd64f6a4a0066a3fe3c7344475886d07fdf", - "sha256:cb18d9cd7b3f35a2f5af47bb8ed6a5bdbf4f3ddee37f3daade4ab7864c292f5b", - "sha256:cf5bb9254e1c38aacf253d510d3d9be631bba21f3d068b17672b38b5cbf2fff5", - "sha256:d017775851a778ec9cc32651c4464079d06d927303c2dde9ae9830ccf6fe94e1", - "sha256:dc96668d9c7cc656609764275c5f8da58ef56d89bdd6810f6923d36497468ff7", - "sha256:e066a0586833d610c4bbddba0be5ba0e3e4f8e0bc5bb6d82103d8f8fc47bb59a", - "sha256:e3a013895c64352b49d0d8e107a84f99631b16dbab156ded33ebf0becf56c8b2", - "sha256:eaf905a580f2747c4a474040a5063cd5e0cc3d1d2d6edb65f28196186493ad4a" - ], - "index": "pypi", - "version": "==0.6.1" - }, - "pytz": { - "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" - ], - "version": "==2023.3.post1" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "redis": { - "hashes": [ - "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", - "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.1" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6' and python_version < '4'", - "version": "==4.9" - }, - "s3transfer": { - "hashes": [ - "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", - "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.0" - }, - "sdc-cryptography": { - "hashes": [ - "sha256:0abf35a298e2be51379f9867b701524a255357921651ff35ebfe1c6b2eb81f8d", - "sha256:80e5e9dae84830bbb072f807ca880d186790ece443fddce34dbee7bf2c271a7d" - ], - "index": "pypi", - "version": "==1.1.2" - }, - "setuptools": { - "hashes": [ - "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2", - "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6" - ], - "markers": "python_version >= '3.8'", - "version": "==69.0.2" - }, - "simplejson": { - "hashes": [ - "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137", - "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a", - "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae", - "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a", - "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba", - "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087", - "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b", - "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41", - "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d", - "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd", - "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d", - "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e", - "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a", - "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9", - "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6", - "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb", - "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664", - "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2", - "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b", - "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5", - "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835", - "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17", - "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69", - "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd", - "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6", - "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2", - "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2", - "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428", - "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f", - "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2", - "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f", - "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7", - "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f", - "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637", - "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3", - "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816", - "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565", - "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378", - "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0", - "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b", - "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693", - "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358", - "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9", - "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23", - "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf", - "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a", - "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832", - "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414", - "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f", - "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48", - "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad", - "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290", - "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3", - "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917", - "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402", - "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0", - "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc", - "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867", - "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f", - "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589", - "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb", - "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b", - "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3", - "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb", - "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c", - "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46", - "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672", - "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c", - "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e", - "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0", - "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b", - "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4", - "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4", - "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c", - "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c", - "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8", - "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b", - "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb", - "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80", - "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2", - "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374", - "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc", - "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50", - "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c", - "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f", - "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28", - "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5", - "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734", - "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a", - "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d", - "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4", - "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c", - "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13", - "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973", - "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a", - "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e", - "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff", - "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded" - ], - "index": "pypi", - "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.19.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "structlog": { - "hashes": [ - "sha256:16a167e87b9fa7fae9a972d5d12805ef90e04857a93eba479d4be3801a6a1482", - "sha256:334666b94707f89dbc4c81a22a8ccd34449f0201d5b1ee097a030b577fa8c858" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==23.2.0" - }, - "ua-parser": { - "hashes": [ - "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", - "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" - ], - "index": "pypi", - "version": "==0.18.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "uwsgi": { - "hashes": [ - "sha256:0cafda0c16f921db7fe42cfaf81b167cf884ee17350efbdd87d1ecece2d7de37" - ], - "index": "pypi", - "version": "==2.0.23" - }, - "werkzeug": { - "hashes": [ - "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", - "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.1" - }, - "wrapt": { - "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" - ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" - }, - "wtforms": { - "hashes": [ - "sha256:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620", - "sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0" - ], - "markers": "python_version >= '3.8'", - "version": "==3.1.1" - }, - "zope.event": { - "hashes": [ - "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", - "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" - ], - "markers": "python_version >= '3.7'", - "version": "==5.0" - }, - "zope.interface": { - "hashes": [ - "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", - "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", - "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", - "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", - "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", - "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", - "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", - "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", - "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", - "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", - "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", - "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", - "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", - "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", - "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", - "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", - "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", - "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", - "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", - "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", - "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", - "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", - "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", - "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", - "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", - "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", - "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", - "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", - "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", - "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", - "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", - "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", - "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", - "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", - "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", - "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" - ], - "markers": "python_version >= '3.7'", - "version": "==6.1" - } - }, - "develop": { - "astroid": { - "hashes": [ - "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", - "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a" - ], - "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.8" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", - "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" - ], - "index": "pypi", - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.2" - }, - "black": { - "hashes": [ - "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4", - "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b", - "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f", - "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07", - "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187", - "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6", - "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05", - "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06", - "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e", - "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5", - "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244", - "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f", - "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221", - "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055", - "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479", - "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394", - "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911", - "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==23.11.0" - }, - "blinker": { - "hashes": [ - "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", - "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.0" - }, - "boto3": { - "hashes": [ - "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", - "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "botocore": { - "hashes": [ - "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", - "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" - ], - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "certifi": { - "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "coverage": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", - "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", - "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", - "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", - "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", - "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", - "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", - "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", - "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", - "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", - "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", - "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", - "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", - "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", - "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", - "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", - "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", - "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", - "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", - "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", - "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", - "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", - "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", - "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", - "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", - "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", - "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", - "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", - "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", - "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", - "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", - "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", - "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", - "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", - "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", - "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", - "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", - "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", - "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", - "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", - "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", - "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", - "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", - "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", - "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", - "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", - "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", - "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", - "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", - "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", - "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", - "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" - ], - "markers": "python_version >= '3.8'", - "version": "==7.3.2" - }, - "cryptography": { - "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.7" - }, - "dill": { - "hashes": [ - "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", - "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" - ], - "markers": "python_version >= '3.11'", - "version": "==0.3.7" - }, - "execnet": { - "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" - }, - "fakeredis": { - "hashes": [ - "sha256:69987928d719d1ae1665ae8ebb16199d22a5ebae0b7d0d0d6586fc3a1a67428c", - "sha256:c9baf3c7fd2ebf40db50db4c642c7c76b712b1eed25d91efcc175bba9bc40ca3" - ], - "index": "pypi", - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==2.20.0" - }, - "flake8": { - "hashes": [ - "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", - "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" - ], - "index": "pypi", - "markers": "python_full_version >= '3.8.1'", - "version": "==6.1.0" - }, - "flake8-datetimez": { - "hashes": [ - "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc", - "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==20.10.0" - }, - "flake8-debugger": { - "hashes": [ - "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf", - "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.2" - }, - "flake8-mock": { - "hashes": [ - "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867", - "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6" - ], - "index": "pypi", - "version": "==0.4" - }, - "flake8-print": { - "hashes": [ - "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9", - "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.0" - }, - "flake8-tuple": { - "hashes": [ - "sha256:8a1b42aab134ef4c3fef13c6a8f383363f158b19fbc165bd91aed9c51851a61d", - "sha256:d828cc8e461c50cacca116e9abb0c9e3be565e8451d3f5c00578c63670aae680" - ], - "index": "pypi", - "version": "==0.4.1" - }, - "flask": { - "hashes": [ - "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", - "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "freezegun": { - "hashes": [ - "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446", - "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==1.2.2" - }, - "httmock": { - "hashes": [ - "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9", - "sha256:44eaf4bb59cc64cd6f5d8bf8700b46aa3097cc5651b9bc85c527dfbc71792f41" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "idna": { - "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" - ], - "markers": "python_version >= '3.5'", - "version": "==3.6" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "isort": { - "hashes": [ - "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", - "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.12.0" - }, - "itsdangerous": { - "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "jsonschema": { - "hashes": [ - "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa", - "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.20.0" - }, - "jsonschema-specifications": { - "hashes": [ - "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca", - "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.11.1" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", - "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", - "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", - "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", - "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", - "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", - "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", - "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", - "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", - "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", - "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", - "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", - "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", - "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", - "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", - "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", - "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", - "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", - "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", - "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", - "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", - "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", - "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", - "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", - "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", - "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", - "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", - "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", - "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", - "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", - "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", - "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", - "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", - "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", - "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", - "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.0" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mock": { - "hashes": [ - "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", - "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==5.1.0" - }, - "moto": { - "hashes": [ - "sha256:5cf0736d1f43cb887498d00b00ae522774bfddb7db1f4994fedea65b290b9f0e", - "sha256:92595fe287474a31ac3ef847941ebb097e8ffb0c3d6c106e47cf573db06933b2" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.2.10" - }, - "mypy": { - "hashes": [ - "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", - "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", - "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82", - "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", - "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", - "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", - "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", - "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", - "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", - "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", - "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", - "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", - "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6", - "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", - "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", - "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7", - "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", - "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", - "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", - "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", - "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", - "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", - "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", - "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42", - "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", - "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", - "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.1" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pathspec": { - "hashes": [ - "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", - "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" - ], - "markers": "python_version >= '3.7'", - "version": "==0.11.2" - }, - "pep8": { - "hashes": [ - "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee", - "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374" - ], - "index": "pypi", - "version": "==1.7.1" - }, - "platformdirs": { - "hashes": [ - "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", - "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731" - ], - "markers": "python_version >= '3.7'", - "version": "==4.0.0" - }, - "pluggy": { - "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.0" - }, - "pycodestyle": { - "hashes": [ - "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", - "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" - ], - "markers": "python_version >= '3.8'", - "version": "==2.11.1" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pyflakes": { - "hashes": [ - "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", - "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" - ], - "markers": "python_version >= '3.8'", - "version": "==3.1.0" - }, - "pylint": { - "hashes": [ - "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", - "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad" - ], - "index": "pypi", - "markers": "python_full_version >= '3.7.2'", - "version": "==2.17.7" - }, - "pylint-absolute-imports": { - "hashes": [ - "sha256:e70d96abbad2fe0bdc3dbae05e1a1b7963dea5b7df4cb63e001f7248de46acd3" - ], - "index": "pypi", - "version": "==1.0.1" - }, - "pylint-mccabe": { - "hashes": [ - "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29" - ], - "index": "pypi", - "version": "==0.1.3" - }, - "pylint-quotes": { - "hashes": [ - "sha256:2d6bb3fa8a1a85af3af8a0ca875a719ac5bcdb735c45756284699d809c109c95", - "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.2.3" - }, - "pytest": { - "hashes": [ - "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.3" - }, - "pytest-cov": { - "hashes": [ - "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", - "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "pytest-flask": { - "hashes": [ - "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", - "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.3.0" - }, - "pytest-mock": { - "hashes": [ - "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f", - "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.12.0" - }, - "pytest-sugar": { - "hashes": [ - "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062", - "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46" - ], - "index": "pypi", - "version": "==0.9.7" - }, - "pytest-xdist": { - "hashes": [ - "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a", - "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.5.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "redis": { - "hashes": [ - "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", - "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.1" - }, - "referencing": { - "hashes": [ - "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882", - "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863" - ], - "markers": "python_version >= '3.8'", - "version": "==0.31.0" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "responses": { - "hashes": [ - "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9", - "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.24.1" - }, - "rpds-py": { - "hashes": [ - "sha256:0290712eb5603a725769b5d857f7cf15cf6ca93dda3128065bbafe6fdb709beb", - "sha256:032c242a595629aacace44128f9795110513ad27217b091e834edec2fb09e800", - "sha256:08832078767545c5ee12561ce980714e1e4c6619b5b1e9a10248de60cddfa1fd", - "sha256:08b335fb0c45f0a9e2478a9ece6a1bfb00b6f4c4780f9be3cf36479c5d8dd374", - "sha256:0b70c1f800059c92479dc94dda41288fd6607f741f9b1b8f89a21a86428f6383", - "sha256:0d9f8930092558fd15c9e07198625efb698f7cc00b3dc311c83eeec2540226a8", - "sha256:181ee352691c4434eb1c01802e9daa5edcc1007ff15023a320e2693fed6a661b", - "sha256:19f5aa7f5078d35ed8e344bcba40f35bc95f9176dddb33fc4f2084e04289fa63", - "sha256:1a3b2583c86bbfbf417304eeb13400ce7f8725376dc7d3efbf35dc5d7052ad48", - "sha256:1c9a1dc5e898ce30e2f9c0aa57181cddd4532b22b7780549441d6429d22d3b58", - "sha256:1f36a1e80ef4ed1996445698fd91e0d3e54738bf597c9995118b92da537d7a28", - "sha256:20147996376be452cd82cd6c17701daba69a849dc143270fa10fe067bb34562a", - "sha256:249c8e0055ca597707d71c5ad85fd2a1c8fdb99386a8c6c257e1b47b67a9bec1", - "sha256:2647192facf63be9ed2d7a49ceb07efe01dc6cfb083bd2cc53c418437400cb99", - "sha256:264f3a5906c62b9df3a00ad35f6da1987d321a053895bd85f9d5c708de5c0fbf", - "sha256:2abd669a39be69cdfe145927c7eb53a875b157740bf1e2d49e9619fc6f43362e", - "sha256:2b2415d5a7b7ee96aa3a54d4775c1fec140476a17ee12353806297e900eaeddc", - "sha256:2c173f529666bab8e3f948b74c6d91afa22ea147e6ebae49a48229d9020a47c4", - "sha256:2da81c1492291c1a90987d76a47c7b2d310661bf7c93a9de0511e27b796a8b46", - "sha256:2eca04a365be380ca1f8fa48b334462e19e3382c0bb7386444d8ca43aa01c481", - "sha256:37b08df45f02ff1866043b95096cbe91ac99de05936dd09d6611987a82a3306a", - "sha256:37f79f4f1f06cc96151f4a187528c3fd4a7e1065538a4af9eb68c642365957f7", - "sha256:3dd5fb7737224e1497c886fb3ca681c15d9c00c76171f53b3c3cc8d16ccfa7fb", - "sha256:3e3ac5b602fea378243f993d8b707189f9061e55ebb4e56cb9fdef8166060f28", - "sha256:3f55ae773abd96b1de25fc5c3fb356f491bd19116f8f854ba705beffc1ddc3c5", - "sha256:4011d5c854aa804c833331d38a2b6f6f2fe58a90c9f615afdb7aa7cf9d31f721", - "sha256:4145172ab59b6c27695db6d78d040795f635cba732cead19c78cede74800949a", - "sha256:42b9535aa22ab023704cfc6533e968f7e420affe802d85e956d8a7b4c0b0b5ea", - "sha256:46a07a258bda12270de02b34c4884f200f864bba3dcd6e3a37fef36a168b859d", - "sha256:4f13d3f6585bd07657a603780e99beda96a36c86acaba841f131e81393958336", - "sha256:528e2afaa56d815d2601b857644aeb395afe7e59212ab0659906dc29ae68d9a6", - "sha256:545e94c84575057d3d5c62634611858dac859702b1519b6ffc58eca7fb1adfcf", - "sha256:577d40a72550eac1386b77b43836151cb61ff6700adacda2ad4d883ca5a0b6f2", - "sha256:5967fa631d0ed9f8511dede08bc943a9727c949d05d1efac4ac82b2938024fb7", - "sha256:5b769396eb358d6b55dbf78f3f7ca631ca1b2fe02136faad5af74f0111b4b6b7", - "sha256:63c9e2794329ef070844ff9bfc012004aeddc0468dc26970953709723f76c8a5", - "sha256:6574f619e8734140d96c59bfa8a6a6e7a3336820ccd1bfd95ffa610673b650a2", - "sha256:6bfe72b249264cc1ff2f3629be240d7d2fdc778d9d298087cdec8524c91cd11f", - "sha256:736817dbbbd030a69a1faf5413a319976c9c8ba8cdcfa98c022d3b6b2e01eca6", - "sha256:74a2044b870df7c9360bb3ce7e12f9ddf8e72e49cd3a353a1528cbf166ad2383", - "sha256:74be3b215a5695690a0f1a9f68b1d1c93f8caad52e23242fcb8ba56aaf060281", - "sha256:76a8374b294e4ccb39ccaf11d39a0537ed107534139c00b4393ca3b542cc66e5", - "sha256:7ba239bb37663b2b4cd08e703e79e13321512dccd8e5f0e9451d9e53a6b8509a", - "sha256:7c40851b659d958c5245c1236e34f0d065cc53dca8d978b49a032c8e0adfda6e", - "sha256:7cf241dbb50ea71c2e628ab2a32b5bfcd36e199152fc44e5c1edb0b773f1583e", - "sha256:7cfae77da92a20f56cf89739a557b76e5c6edc094f6ad5c090b9e15fbbfcd1a4", - "sha256:7d152ec7bb431040af2500e01436c9aa0d993f243346f0594a15755016bf0be1", - "sha256:80080972e1d000ad0341c7cc58b6855c80bd887675f92871221451d13a975072", - "sha256:82dbcd6463e580bcfb7561cece35046aaabeac5a9ddb775020160b14e6c58a5d", - "sha256:8308a8d49d1354278d5c068c888a58d7158a419b2e4d87c7839ed3641498790c", - "sha256:839676475ac2ccd1532d36af3d10d290a2ca149b702ed464131e450a767550df", - "sha256:83feb0f682d75a09ddc11aa37ba5c07dd9b824b22915207f6176ea458474ff75", - "sha256:88956c993a20201744282362e3fd30962a9d86dc4f1dcf2bdb31fab27821b61f", - "sha256:8a6ad8429340e0a4de89353447c6441329def3632e7b2293a7d6e873217d3c2b", - "sha256:8ba9fbc5d6e36bfeb5292530321cc56c4ef3f98048647fabd8f57543c34174ec", - "sha256:8c1f6c8df23be165eb0cb78f305483d00c6827a191e3a38394c658d5b9c80bbd", - "sha256:91276caef95556faeb4b8f09fe4439670d3d6206fee78d47ddb6e6de837f0b4d", - "sha256:960e7e460fda2d0af18c75585bbe0c99f90b8f09963844618a621b804f8c3abe", - "sha256:9656a09653b18b80764647d585750df2dff8928e03a706763ab40ec8c4872acc", - "sha256:9cd935c0220d012a27c20135c140f9cdcbc6249d5954345c81bfb714071b985c", - "sha256:a2b3c79586636f1fa69a7bd59c87c15fca80c0d34b5c003d57f2f326e5276575", - "sha256:a4b9d3f5c48bbe8d9e3758e498b3c34863f2c9b1ac57a4e6310183740e59c980", - "sha256:a8c2bf286e5d755a075e5e97ba56b3de08cccdad6b323ab0b21cc98875176b03", - "sha256:a90031658805c63fe488f8e9e7a88b260ea121ba3ee9cdabcece9c9ddb50da39", - "sha256:ad666a904212aa9a6c77da7dce9d5170008cda76b7776e6731928b3f8a0d40fa", - "sha256:af2d1648eb625a460eee07d3e1ea3a4a6e84a1fb3a107f6a8e95ac19f7dcce67", - "sha256:b3d4b390ee70ca9263b331ccfaf9819ee20e90dfd0201a295e23eb64a005dbef", - "sha256:ba4432301ad7eeb1b00848cf46fae0e5fecfd18a8cb5fdcf856c67985f79ecc7", - "sha256:bc3179e0815827cf963e634095ae5715ee73a5af61defbc8d6ca79f1bdae1d1d", - "sha256:c5fd099acaee2325f01281a130a39da08d885e4dedf01b84bf156ec2737d78fe", - "sha256:c797ea56f36c6f248656f0223b11307fdf4a1886f3555eba371f34152b07677f", - "sha256:cd4ea56c9542ad0091dfdef3e8572ae7a746e1e91eb56c9e08b8d0808b40f1d1", - "sha256:cdd6f8738e1f1d9df5b1603bb03cb30e442710e5672262b95d0f9fcb4edb0dab", - "sha256:d0580faeb9def6d0beb7aa666294d5604e569c4e24111ada423cf9936768d95c", - "sha256:d11afdc5992bbd7af60ed5eb519873690d921425299f51d80aa3099ed49f2bcc", - "sha256:d1d388d2f5f5a6065cf83c54dd12112b7389095669ff395e632003ae8999c6b8", - "sha256:d20da6b4c7aa9ee75ad0730beaba15d65157f5beeaca54a038bb968f92bf3ce3", - "sha256:d22e0660de24bd8e9ac82f4230a22a5fe4e397265709289d61d5fb333839ba50", - "sha256:d22f2cb82e0b40e427a74a93c9a4231335bbc548aed79955dde0b64ea7f88146", - "sha256:d4fa1eeb9bea6d9b64ac91ec51ee94cc4fc744955df5be393e1c923c920db2b0", - "sha256:d9793d46d3e6522ae58e9321032827c9c0df1e56cbe5d3de965facb311aed6aa", - "sha256:dab979662da1c9fbb464e310c0b06cb5f1d174d09a462553af78f0bfb3e01920", - "sha256:db8d0f0ad92f74feb61c4e4a71f1d573ef37c22ef4dc19cab93e501bfdad8cbd", - "sha256:df2af1180b8eeececf4f819d22cc0668bfadadfd038b19a90bd2fb2ee419ec6f", - "sha256:dfb5d2ab183c0efe5e7b8917e4eaa2e837aacafad8a69b89aa6bc81550eed857", - "sha256:e04f8c76b8d5c70695b4e8f1d0b391d8ef91df00ef488c6c1ffb910176459bc6", - "sha256:e4a45ba34f904062c63049a760790c6a2fa7a4cc4bd160d8af243b12371aaa05", - "sha256:e9be1f7c5f9673616f875299339984da9447a40e3aea927750c843d6e5e2e029", - "sha256:edc91c50e17f5cd945d821f0f1af830522dba0c10267c3aab186dc3dbaab8def", - "sha256:ee70ee5f4144a45a9e6169000b5b525d82673d5dab9f7587eccc92794814e7ac", - "sha256:f1059ca9a51c936c9a8d46fbc2c9a6b4c15ab3f13a97f1ad32f024b39666ba85", - "sha256:f47eef55297799956464efc00c74ae55c48a7b68236856d56183fe1ddf866205", - "sha256:f4ae6f423cb7d1c6256b7482025ace2825728f53b7ac58bcd574de6ee9d242c2", - "sha256:f4b15a163448ec79241fb2f1bc5a8ae1a4a304f7a48d948d208a2935b26bf8a5", - "sha256:f55601fb58f92e4f4f1d05d80c24cb77505dc42103ddfd63ddfdc51d3da46fa2", - "sha256:fa84bbe22ffa108f91631935c28a623001e335d66e393438258501e618fb0dde", - "sha256:faa12a9f34671a30ea6bb027f04ec4e1fb8fa3fb3ed030893e729d4d0f3a9791", - "sha256:fcfd5f91b882eedf8d9601bd21261d6ce0e61a8c66a7152d1f5df08d3f643ab1", - "sha256:fe30ef31172bdcf946502a945faad110e8fff88c32c4bec9a593df0280e64d8a" - ], - "markers": "python_version >= '3.8'", - "version": "==0.13.1" - }, - "s3transfer": { - "hashes": [ - "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", - "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, - "soupsieve": { - "hashes": [ - "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", - "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" - ], - "markers": "python_version >= '3.8'", - "version": "==2.5" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "tomlkit": { - "hashes": [ - "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4", - "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.3" - }, - "types-cachetools": { - "hashes": [ - "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199", - "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.0.7" - }, - "types-pyopenssl": { - "hashes": [ - "sha256:00171433653265843b7469ddb9f3c86d698668064cc33ef10537822156130ebf", - "sha256:5ffb077fe70b699c88d5caab999ae80e192fe28bf6cda7989b7e79b1e4e2dcd3" - ], - "markers": "python_version >= '3.7'", - "version": "==23.3.0.0" - }, - "types-python-dateutil": { - "hashes": [ - "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b", - "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9" - ], - "index": "pypi", - "version": "==2.8.19.14" - }, - "types-pytz": { - "hashes": [ - "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", - "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" - ], - "index": "pypi", - "version": "==2023.3.1.1" - }, - "types-pyyaml": { - "hashes": [ - "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", - "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" - ], - "version": "==6.0.12.12" - }, - "types-redis": { - "hashes": [ - "sha256:94fc61118601fb4f79206b33b9f4344acff7ca1d7bba67834987fb0efcf6a770", - "sha256:c8cfc84635183deca2db4a528966c5566445fd3713983f0034fb0f5a09e0890d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.6.0.11" - }, - "types-requests": { - "hashes": [ - "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", - "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0.10" - }, - "types-simplejson": { - "hashes": [ - "sha256:8ba093dc7884f59b3e62aed217144085e675a269debc32678fd80e0b43b2b86f", - "sha256:ebc81f886f89d99d6b80c726518aa2228bc77c26438f18fd81455e4f79f8ee1b" - ], - "index": "pypi", - "version": "==3.19.0.2" - }, - "typing-extensions": { - "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" - ], - "markers": "python_version >= '3.8'", - "version": "==4.8.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "werkzeug": { - "hashes": [ - "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", - "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.1" - }, - "wrapt": { - "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" - ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" - }, - "xmltodict": { - "hashes": [ - "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", - "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" - ], - "markers": "python_version >= '3.4'", - "version": "==0.13.0" - } + "_meta": { + "hash": { + "sha256": "fc45f58bb88b36d9a6136dce2a43825ddb10145475d5110804526ebe2eb4dc05" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.11" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "babel": { + "hashes": [ + "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900", + "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed" + ], + "markers": "python_version >= '3.7'", + "version": "==2.13.1" + }, + "blinker": { + "hashes": [ + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.7.0" + }, + "boto3": { + "hashes": [ + "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", + "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.33.1" + }, + "botocore": { + "hashes": [ + "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", + "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" + ], + "markers": "python_version >= '3.7'", + "version": "==1.33.1" + }, + "brotli": { + "hashes": [ + "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", + "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", + "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", + "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", + "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", + "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", + "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", + "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", + "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", + "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", + "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", + "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", + "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", + "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", + "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", + "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", + "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", + "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", + "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", + "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", + "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", + "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", + "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", + "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", + "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", + "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", + "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", + "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", + "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", + "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", + "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", + "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", + "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", + "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", + "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", + "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", + "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", + "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", + "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", + "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", + "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", + "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", + "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", + "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", + "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", + "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", + "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", + "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", + "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", + "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", + "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", + "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", + "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", + "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", + "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", + "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", + "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", + "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", + "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", + "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", + "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", + "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", + "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", + "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", + "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", + "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", + "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", + "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", + "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", + "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", + "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", + "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", + "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", + "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", + "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", + "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", + "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", + "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", + "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", + "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", + "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", + "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", + "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.1.0" + }, + "cachetools": { + "hashes": [ + "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2", + "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.3.2" + }, + "certifi": { + "hashes": [ + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.11.17" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, + "coloredlogs": { + "hashes": [ + "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", + "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==15.0.1" + }, + "cryptography": { + "hashes": [ + "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", + "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", + "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", + "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", + "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", + "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", + "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", + "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", + "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", + "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", + "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", + "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", + "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", + "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", + "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", + "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", + "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", + "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", + "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", + "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", + "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", + "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", + "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.7" + }, + "deprecated": { + "hashes": [ + "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", + "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.2.14" + }, + "dnspython": { + "hashes": [ + "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8", + "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984" + ], + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==2.4.2" + }, + "email-validator": { + "hashes": [ + "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44", + "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.1.0.post1" + }, + "flask": { + "hashes": [ + "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", + "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.0.0" + }, + "flask-babel": { + "hashes": [ + "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625", + "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593" + ], + "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==4.0.0" + }, + "flask-compress": { + "hashes": [ + "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c", + "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee" + ], + "index": "pypi", + "version": "==1.14" + }, + "flask-login": { + "hashes": [ + "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", + "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.6.3" + }, + "flask-talisman": { + "hashes": [ + "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208", + "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "flask-wtf": { + "hashes": [ + "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695", + "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.2.1" + }, + "gevent": { + "hashes": [ + "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", + "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", + "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", + "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", + "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", + "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", + "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", + "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", + "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", + "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", + "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", + "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", + "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", + "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", + "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", + "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", + "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", + "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", + "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", + "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", + "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", + "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", + "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", + "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", + "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", + "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", + "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", + "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", + "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", + "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", + "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", + "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", + "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", + "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", + "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", + "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", + "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", + "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", + "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", + "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" + ], + "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", + "version": "==23.9.1" + }, + "google-api-core": { + "extras": [ + "grpc" + ], + "hashes": [ + "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41", + "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952" + ], + "markers": "python_version >= '3.7'", + "version": "==2.14.0" + }, + "google-auth": { + "hashes": [ + "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3", + "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2" + ], + "markers": "python_version >= '3.7'", + "version": "==2.23.4" + }, + "google-cloud-core": { + "hashes": [ + "sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb", + "sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863" + ], + "markers": "python_version >= '3.7'", + "version": "==2.3.3" + }, + "google-cloud-datastore": { + "hashes": [ + "sha256:369df0653128fe63f0fb1b5a1cd5a05edae49a805f79f9b639ca9d58833bc6b5", + "sha256:63b31b676dcb2786a650d23d324d8f886c4e14586afdd0cfe6e260a73f12448e" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.18.0" + }, + "google-cloud-pubsub": { + "hashes": [ + "sha256:32eb61fd4c1dc6c842f594d69d9afa80544e3b327aa640a164eb6fb0201eaf2d", + "sha256:f32144ad9ed32331a80a2f8379a3ca7526bbc01e7bd76de2e8ab52e492d21f50" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.18.4" + }, + "google-cloud-storage": { + "hashes": [ + "sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d", + "sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.13.0" + }, + "google-cloud-tasks": { + "hashes": [ + "sha256:3efb280e7a635f57860a06a346131c117042e820ad483afd256800fae1b4d701", + "sha256:801a671c6dbd2269179037e93490f87e2df59ddfcb59551128bab27e7dd09f94" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.14.2" + }, + "google-crc32c": { + "hashes": [ + "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", + "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", + "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", + "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", + "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", + "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", + "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", + "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", + "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", + "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", + "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", + "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", + "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", + "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", + "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", + "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", + "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", + "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", + "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", + "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", + "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", + "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", + "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", + "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", + "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", + "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", + "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", + "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", + "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", + "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", + "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", + "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", + "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", + "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", + "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", + "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", + "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", + "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", + "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", + "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", + "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", + "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", + "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", + "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", + "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", + "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", + "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", + "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", + "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", + "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", + "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", + "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", + "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", + "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", + "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", + "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", + "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", + "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", + "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", + "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", + "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", + "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", + "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", + "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", + "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", + "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", + "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", + "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" + ], + "markers": "python_version >= '3.7'", + "version": "==1.5.0" + }, + "google-resumable-media": { + "hashes": [ + "sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7", + "sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b" + ], + "markers": "python_version >= '3.7'", + "version": "==2.6.0" + }, + "googleapis-common-protos": { + "extras": [ + "grpc" + ], + "hashes": [ + "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0", + "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b" + ], + "markers": "python_version >= '3.7'", + "version": "==1.61.0" + }, + "greenlet": { + "hashes": [ + "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", + "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", + "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", + "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", + "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", + "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", + "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", + "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", + "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", + "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", + "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", + "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", + "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", + "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", + "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", + "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", + "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", + "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", + "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", + "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", + "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", + "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", + "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", + "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", + "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", + "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", + "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", + "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", + "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", + "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", + "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", + "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", + "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", + "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", + "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", + "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", + "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", + "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", + "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", + "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", + "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", + "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", + "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", + "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", + "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", + "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", + "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", + "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", + "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", + "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", + "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", + "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", + "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", + "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", + "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", + "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", + "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" + ], + "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", + "version": "==3.0.1" + }, + "grpc-google-iam-v1": { + "hashes": [ + "sha256:009197a7f1eaaa22149c96e5e054ac5934ba7241974e92663d8d3528a21203d1", + "sha256:834da89f4c4a2abbe842a793ed20fc6d9a77011ef2626755b1b89116fb9596d7" + ], + "markers": "python_version >= '3.7'", + "version": "==0.12.7" + }, + "grpcio": { + "hashes": [ + "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8", + "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea", + "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69", + "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb", + "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07", + "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d", + "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b", + "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354", + "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79", + "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59", + "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86", + "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7", + "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e", + "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281", + "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069", + "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb", + "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d", + "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3", + "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25", + "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c", + "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a", + "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80", + "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf", + "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118", + "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53", + "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e", + "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9", + "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556", + "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b", + "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e", + "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994", + "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc", + "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a", + "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d", + "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e", + "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718", + "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4", + "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab", + "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552", + "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3", + "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391", + "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13", + "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7", + "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3", + "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025", + "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8", + "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6", + "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473", + "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94", + "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0", + "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9", + "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d", + "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881", + "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.59.3" + }, + "grpcio-status": { + "hashes": [ + "sha256:2fd2eb39ca4e9afb3c874c0878ff75b258db0b7dcc25570fc521f16ae0ab942a", + "sha256:65c394ba43380d6bdf8c04c61efc493104b5535552aed35817a1b4dc66598a1f" + ], + "markers": "python_version >= '3.6'", + "version": "==1.59.3" + }, + "gunicorn": { + "hashes": [ + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + ], + "index": "pypi", + "markers": "python_version >= '3.5'", + "version": "==21.2.0" + }, + "htmlmin": { + "hashes": [ + "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178" + ], + "index": "pypi", + "version": "==0.1.12" + }, + "humanfriendly": { + "hashes": [ + "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", + "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==10.0" + }, + "humanize": { + "hashes": [ + "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", + "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.9.0" + }, + "idna": { + "hashes": [ + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + ], + "markers": "python_version >= '3.5'", + "version": "==3.6" + }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, + "jinja2": { + "hashes": [ + "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", + "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.1.3" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "jsonpointer": { + "hashes": [ + "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", + "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==2.4" + }, + "jwcrypto": { + "hashes": [ + "sha256:48bb9bf433777136253579e52b75ffe0f9a4a721d133d01f45a0b91ed5f4f1ae" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==1.5.1" + }, + "markupsafe": { + "hashes": [ + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.3" + }, + "marshmallow": { + "hashes": [ + "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", + "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.20.1" + }, + "ordered-set": { + "hashes": [ + "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", + "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pdfkit": { + "hashes": [ + "sha256:992f821e1e18fc8a0e701ecae24b51a2d598296a180caee0a24c0af181da02a9", + "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f", + "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e" + ], + "index": "pypi", + "version": "==1.0.0" + }, + "pika": { + "hashes": [ + "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", + "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.3.2" + }, + "proto-plus": { + "hashes": [ + "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df", + "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b" + ], + "markers": "python_version >= '3.6'", + "version": "==1.22.3" + }, + "protobuf": { + "hashes": [ + "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd", + "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb", + "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0", + "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7", + "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b", + "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2", + "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510", + "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6", + "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd", + "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10", + "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7" + ], + "markers": "python_version >= '3.8'", + "version": "==4.25.1" + }, + "pyasn1": { + "hashes": [ + "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58", + "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.5.1" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", + "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.3.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "python-snappy": { + "hashes": [ + "sha256:03bb511380fca2a13325b6f16fe8234c8e12da9660f0258cd45d9a02ffc916af", + "sha256:0bdb6942180660bda7f7d01f4c0def3cfc72b1c6d99aad964801775a3e379aba", + "sha256:0d489b50f49433494160c45048fe806de6b3aeab0586e497ebd22a0bab56e427", + "sha256:1a993dc8aadd901915a510fe6af5f20ae4256f527040066c22a154db8946751f", + "sha256:1d029f7051ec1bbeaa3e03030b6d8ed47ceb69cae9016f493c802a08af54e026", + "sha256:277757d5dad4e239dc1417438a0871b65b1b155beb108888e7438c27ffc6a8cc", + "sha256:2a7e528ab6e09c0d67dcb61a1730a292683e5ff9bb088950638d3170cf2a0a54", + "sha256:2aaaf618c68d8c9daebc23a20436bd01b09ee70d7fbf7072b7f38b06d2fab539", + "sha256:2be4f4550acd484912441f5f1209ba611ac399aac9355fee73611b9a0d4f949c", + "sha256:39692bedbe0b717001a99915ac0eb2d9d0bad546440d392a2042b96d813eede1", + "sha256:3fb9a88a4dd6336488f3de67ce75816d0d796dce53c2c6e4d70e0b565633c7fd", + "sha256:4038019b1bcaadde726a57430718394076c5a21545ebc5badad2c045a09546cf", + "sha256:463fd340a499d47b26ca42d2f36a639188738f6e2098c6dbf80aef0e60f461e1", + "sha256:4d3cafdf454354a621c8ab7408e45aa4e9d5c0b943b61ff4815f71ca6bdf0130", + "sha256:4ec533a8c1f8df797bded662ec3e494d225b37855bb63eb0d75464a07947477c", + "sha256:530bfb9efebcc1aab8bb4ebcbd92b54477eed11f6cf499355e882970a6d3aa7d", + "sha256:546c1a7470ecbf6239101e9aff0f709b68ca0f0268b34d9023019a55baa1f7c6", + "sha256:5843feb914796b1f0405ccf31ea0fb51034ceb65a7588edfd5a8250cb369e3b2", + "sha256:586724a0276d7a6083a17259d0b51622e492289a9998848a1b01b6441ca12b2f", + "sha256:59e975be4206cc54d0a112ef72fa3970a57c2b1bcc2c97ed41d6df0ebe518228", + "sha256:5a453c45178d7864c1bdd6bfe0ee3ed2883f63b9ba2c9bb967c6b586bf763f96", + "sha256:5bb05c28298803a74add08ba496879242ef159c75bc86a5406fac0ffc7dd021b", + "sha256:5e973e637112391f05581f427659c05b30b6843bc522a65be35ac7b18ce3dedd", + "sha256:66c80e9b366012dbee262bb1869e4fc5ba8786cda85928481528bc4a72ec2ee8", + "sha256:6a7620404da966f637b9ce8d4d3d543d363223f7a12452a575189c5355fc2d25", + "sha256:6f8bf4708a11b47517baf962f9a02196478bbb10fdb9582add4aa1459fa82380", + "sha256:735cd4528c55dbe4516d6d2b403331a99fc304f8feded8ae887cf97b67d589bb", + "sha256:7778c224efc38a40d274da4eb82a04cac27aae20012372a7db3c4bbd8926c4d4", + "sha256:8277d1f6282463c40761f802b742f833f9f2449fcdbb20a96579aa05c8feb614", + "sha256:88b6ea78b83d2796f330b0af1b70cdd3965dbdab02d8ac293260ec2c8fe340ee", + "sha256:8c07220408d3268e8268c9351c5c08041bc6f8c6172e59d398b71020df108541", + "sha256:8d0c019ee7dcf2c60e240877107cddbd95a5b1081787579bf179938392d66480", + "sha256:90b0186516b7a101c14764b0c25931b741fb0102f21253eff67847b4742dfc72", + "sha256:9837ac1650cc68d22a3cf5f15fb62c6964747d16cecc8b22431f113d6e39555d", + "sha256:9eac51307c6a1a38d5f86ebabc26a889fddf20cbba7a116ccb54ba1446601d5b", + "sha256:9f0c0d88b84259f93c3aa46398680646f2c23e43394779758d9f739c34e15295", + "sha256:a0ad38bc98d0b0497a0b0dbc29409bcabfcecff4511ed7063403c86de16927bc", + "sha256:b265cde49774752aec9ca7f5d272e3f98718164afc85521622a8a5394158a2b5", + "sha256:b6a107ab06206acc5359d4c5632bd9b22d448702a79b3169b0c62e0fb808bb2a", + "sha256:b7f920eaf46ebf41bd26f9df51c160d40f9e00b7b48471c3438cb8d027f7fb9b", + "sha256:c20498bd712b6e31a4402e1d027a1cd64f6a4a0066a3fe3c7344475886d07fdf", + "sha256:cb18d9cd7b3f35a2f5af47bb8ed6a5bdbf4f3ddee37f3daade4ab7864c292f5b", + "sha256:cf5bb9254e1c38aacf253d510d3d9be631bba21f3d068b17672b38b5cbf2fff5", + "sha256:d017775851a778ec9cc32651c4464079d06d927303c2dde9ae9830ccf6fe94e1", + "sha256:dc96668d9c7cc656609764275c5f8da58ef56d89bdd6810f6923d36497468ff7", + "sha256:e066a0586833d610c4bbddba0be5ba0e3e4f8e0bc5bb6d82103d8f8fc47bb59a", + "sha256:e3a013895c64352b49d0d8e107a84f99631b16dbab156ded33ebf0becf56c8b2", + "sha256:eaf905a580f2747c4a474040a5063cd5e0cc3d1d2d6edb65f28196186493ad4a" + ], + "index": "pypi", + "version": "==0.6.1" + }, + "pytz": { + "hashes": [ + "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", + "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" + ], + "version": "==2023.3.post1" + }, + "pyyaml": { + "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==6.0.1" + }, + "redis": { + "hashes": [ + "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", + "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.1" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "rsa": { + "hashes": [ + "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", + "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" + ], + "markers": "python_version >= '3.6' and python_version < '4'", + "version": "==4.9" + }, + "s3transfer": { + "hashes": [ + "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", + "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" + ], + "markers": "python_version >= '3.7'", + "version": "==0.8.0" + }, + "sdc-cryptography": { + "hashes": [ + "sha256:0abf35a298e2be51379f9867b701524a255357921651ff35ebfe1c6b2eb81f8d", + "sha256:80e5e9dae84830bbb072f807ca880d186790ece443fddce34dbee7bf2c271a7d" + ], + "index": "pypi", + "version": "==1.1.2" + }, + "setuptools": { + "hashes": [ + "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2", + "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6" + ], + "markers": "python_version >= '3.8'", + "version": "==69.0.2" + }, + "simplejson": { + "hashes": [ + "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137", + "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a", + "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae", + "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a", + "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba", + "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087", + "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b", + "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41", + "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d", + "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd", + "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d", + "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e", + "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a", + "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9", + "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6", + "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb", + "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664", + "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2", + "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b", + "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5", + "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835", + "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17", + "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69", + "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd", + "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6", + "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2", + "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2", + "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428", + "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f", + "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2", + "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f", + "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7", + "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f", + "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637", + "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3", + "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816", + "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565", + "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378", + "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0", + "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b", + "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693", + "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358", + "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9", + "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23", + "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf", + "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a", + "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832", + "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414", + "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f", + "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48", + "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad", + "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290", + "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3", + "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917", + "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402", + "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0", + "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc", + "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867", + "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f", + "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589", + "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb", + "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b", + "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3", + "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb", + "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c", + "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46", + "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672", + "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c", + "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e", + "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0", + "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b", + "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4", + "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4", + "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c", + "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c", + "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8", + "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b", + "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb", + "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80", + "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2", + "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374", + "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc", + "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50", + "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c", + "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f", + "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28", + "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5", + "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734", + "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a", + "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d", + "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4", + "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c", + "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13", + "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973", + "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a", + "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e", + "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff", + "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded" + ], + "index": "pypi", + "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.19.2" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "structlog": { + "hashes": [ + "sha256:16a167e87b9fa7fae9a972d5d12805ef90e04857a93eba479d4be3801a6a1482", + "sha256:334666b94707f89dbc4c81a22a8ccd34449f0201d5b1ee097a030b577fa8c858" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==23.2.0" + }, + "ua-parser": { + "hashes": [ + "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", + "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" + ], + "index": "pypi", + "version": "==0.18.0" + }, + "urllib3": { + "hashes": [ + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.7" + }, + "uwsgi": { + "hashes": [ + "sha256:0cafda0c16f921db7fe42cfaf81b167cf884ee17350efbdd87d1ecece2d7de37" + ], + "index": "pypi", + "version": "==2.0.23" + }, + "werkzeug": { + "hashes": [ + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.1" + }, + "wrapt": { + "hashes": [ + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + ], + "markers": "python_version >= '3.6'", + "version": "==1.16.0" + }, + "wtforms": { + "hashes": [ + "sha256:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620", + "sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0" + ], + "markers": "python_version >= '3.8'", + "version": "==3.1.1" + }, + "zope.event": { + "hashes": [ + "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" + ], + "markers": "python_version >= '3.7'", + "version": "==5.0" + }, + "zope.interface": { + "hashes": [ + "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", + "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", + "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", + "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", + "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", + "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", + "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", + "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", + "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", + "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", + "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", + "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", + "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", + "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", + "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", + "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", + "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", + "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", + "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", + "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", + "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", + "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", + "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", + "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", + "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", + "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", + "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", + "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", + "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", + "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", + "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", + "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", + "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", + "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", + "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", + "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" + ], + "markers": "python_version >= '3.7'", + "version": "==6.1" + } + }, + "develop": { + "astroid": { + "hashes": [ + "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", + "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a" + ], + "markers": "python_full_version >= '3.7.2'", + "version": "==2.15.8" + }, + "attrs": { + "hashes": [ + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" + ], + "markers": "python_version >= '3.7'", + "version": "==23.1.0" + }, + "beautifulsoup4": { + "hashes": [ + "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", + "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" + ], + "index": "pypi", + "markers": "python_full_version >= '3.6.0'", + "version": "==4.12.2" + }, + "black": { + "hashes": [ + "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4", + "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b", + "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f", + "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07", + "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187", + "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6", + "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05", + "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06", + "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e", + "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5", + "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244", + "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f", + "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221", + "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055", + "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479", + "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394", + "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911", + "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==23.11.0" + }, + "blinker": { + "hashes": [ + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.7.0" + }, + "boto3": { + "hashes": [ + "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", + "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.33.1" + }, + "botocore": { + "hashes": [ + "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", + "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" + ], + "markers": "python_version >= '3.7'", + "version": "==1.33.1" + }, + "certifi": { + "hashes": [ + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" + ], + "markers": "python_version >= '3.6'", + "version": "==2023.11.17" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "python_version >= '3.8'", + "version": "==1.16.0" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "coverage": { + "extras": [ + "toml" + ], + "hashes": [ + "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", + "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", + "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", + "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", + "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", + "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", + "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", + "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", + "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", + "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", + "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", + "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", + "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", + "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", + "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", + "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", + "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", + "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", + "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", + "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", + "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", + "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", + "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", + "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", + "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", + "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", + "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", + "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", + "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", + "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", + "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", + "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", + "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", + "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", + "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", + "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", + "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", + "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", + "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", + "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", + "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", + "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", + "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", + "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", + "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", + "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", + "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", + "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", + "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", + "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", + "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", + "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" + ], + "markers": "python_version >= '3.8'", + "version": "==7.3.2" + }, + "cryptography": { + "hashes": [ + "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", + "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", + "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", + "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", + "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", + "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", + "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", + "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", + "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", + "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", + "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", + "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", + "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", + "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", + "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", + "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", + "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", + "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", + "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", + "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", + "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", + "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", + "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" + ], + "markers": "python_version >= '3.7'", + "version": "==41.0.7" + }, + "dill": { + "hashes": [ + "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", + "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" + ], + "markers": "python_version >= '3.11'", + "version": "==0.3.7" + }, + "execnet": { + "hashes": [ + "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", + "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.2" + }, + "fakeredis": { + "hashes": [ + "sha256:69987928d719d1ae1665ae8ebb16199d22a5ebae0b7d0d0d6586fc3a1a67428c", + "sha256:c9baf3c7fd2ebf40db50db4c642c7c76b712b1eed25d91efcc175bba9bc40ca3" + ], + "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.20.0" + }, + "flake8": { + "hashes": [ + "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", + "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.1'", + "version": "==6.1.0" + }, + "flake8-datetimez": { + "hashes": [ + "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc", + "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==20.10.0" + }, + "flake8-debugger": { + "hashes": [ + "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf", + "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.2" + }, + "flake8-mock": { + "hashes": [ + "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867", + "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6" + ], + "index": "pypi", + "version": "==0.4" + }, + "flake8-print": { + "hashes": [ + "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9", + "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.0" + }, + "flake8-tuple": { + "hashes": [ + "sha256:8a1b42aab134ef4c3fef13c6a8f383363f158b19fbc165bd91aed9c51851a61d", + "sha256:d828cc8e461c50cacca116e9abb0c9e3be565e8451d3f5c00578c63670aae680" + ], + "index": "pypi", + "version": "==0.4.1" + }, + "flask": { + "hashes": [ + "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", + "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.0.0" + }, + "freezegun": { + "hashes": [ + "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446", + "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==1.2.2" + }, + "httmock": { + "hashes": [ + "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9", + "sha256:44eaf4bb59cc64cd6f5d8bf8700b46aa3097cc5651b9bc85c527dfbc71792f41" + ], + "index": "pypi", + "version": "==1.4.0" + }, + "idna": { + "hashes": [ + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + ], + "markers": "python_version >= '3.5'", + "version": "==3.6" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "isort": { + "hashes": [ + "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", + "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==5.12.0" + }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, + "jinja2": { + "hashes": [ + "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", + "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.1.3" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "jsonschema": { + "hashes": [ + "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa", + "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.20.0" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca", + "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779" + ], + "markers": "python_version >= '3.8'", + "version": "==2023.11.1" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", + "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", + "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", + "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", + "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", + "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", + "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", + "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", + "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", + "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", + "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", + "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", + "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", + "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", + "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", + "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", + "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", + "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", + "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", + "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", + "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", + "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", + "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", + "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", + "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", + "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", + "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", + "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", + "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", + "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", + "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", + "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", + "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", + "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", + "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", + "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" + ], + "markers": "python_version >= '3.7'", + "version": "==1.9.0" + }, + "markupsafe": { + "hashes": [ + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.3" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mock": { + "hashes": [ + "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", + "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==5.1.0" + }, + "moto": { + "hashes": [ + "sha256:5cf0736d1f43cb887498d00b00ae522774bfddb7db1f4994fedea65b290b9f0e", + "sha256:92595fe287474a31ac3ef847941ebb097e8ffb0c3d6c106e47cf573db06933b2" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.2.10" + }, + "mypy": { + "hashes": [ + "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", + "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", + "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82", + "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", + "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", + "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", + "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", + "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", + "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", + "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", + "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", + "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", + "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6", + "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", + "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", + "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7", + "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", + "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", + "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", + "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", + "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", + "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", + "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", + "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42", + "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", + "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", + "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.7.1" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pathspec": { + "hashes": [ + "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", + "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" + ], + "markers": "python_version >= '3.7'", + "version": "==0.11.2" + }, + "pep8": { + "hashes": [ + "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee", + "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374" + ], + "index": "pypi", + "version": "==1.7.1" + }, + "platformdirs": { + "hashes": [ + "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", + "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731" + ], + "markers": "python_version >= '3.7'", + "version": "==4.0.0" + }, + "pluggy": { + "hashes": [ + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" + ], + "markers": "python_version >= '3.8'", + "version": "==2.11.1" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pyflakes": { + "hashes": [ + "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", + "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" + ], + "markers": "python_version >= '3.8'", + "version": "==3.1.0" + }, + "pylint": { + "hashes": [ + "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", + "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad" + ], + "index": "pypi", + "markers": "python_full_version >= '3.7.2'", + "version": "==2.17.7" + }, + "pylint-absolute-imports": { + "hashes": [ + "sha256:e70d96abbad2fe0bdc3dbae05e1a1b7963dea5b7df4cb63e001f7248de46acd3" + ], + "index": "pypi", + "version": "==1.0.1" + }, + "pylint-mccabe": { + "hashes": [ + "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29" + ], + "index": "pypi", + "version": "==0.1.3" + }, + "pylint-quotes": { + "hashes": [ + "sha256:2d6bb3fa8a1a85af3af8a0ca875a719ac5bcdb735c45756284699d809c109c95", + "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==0.2.3" + }, + "pytest": { + "hashes": [ + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==7.4.3" + }, + "pytest-cov": { + "hashes": [ + "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", + "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.0" + }, + "pytest-flask": { + "hashes": [ + "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", + "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "pytest-mock": { + "hashes": [ + "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f", + "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.12.0" + }, + "pytest-sugar": { + "hashes": [ + "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062", + "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46" + ], + "index": "pypi", + "version": "==0.9.7" + }, + "pytest-xdist": { + "hashes": [ + "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a", + "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.5.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "pyyaml": { + "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==6.0.1" + }, + "redis": { + "hashes": [ + "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", + "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.1" + }, + "referencing": { + "hashes": [ + "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882", + "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863" + ], + "markers": "python_version >= '3.8'", + "version": "==0.31.0" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "responses": { + "hashes": [ + "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9", + "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.24.1" + }, + "rpds-py": { + "hashes": [ + "sha256:0290712eb5603a725769b5d857f7cf15cf6ca93dda3128065bbafe6fdb709beb", + "sha256:032c242a595629aacace44128f9795110513ad27217b091e834edec2fb09e800", + "sha256:08832078767545c5ee12561ce980714e1e4c6619b5b1e9a10248de60cddfa1fd", + "sha256:08b335fb0c45f0a9e2478a9ece6a1bfb00b6f4c4780f9be3cf36479c5d8dd374", + "sha256:0b70c1f800059c92479dc94dda41288fd6607f741f9b1b8f89a21a86428f6383", + "sha256:0d9f8930092558fd15c9e07198625efb698f7cc00b3dc311c83eeec2540226a8", + "sha256:181ee352691c4434eb1c01802e9daa5edcc1007ff15023a320e2693fed6a661b", + "sha256:19f5aa7f5078d35ed8e344bcba40f35bc95f9176dddb33fc4f2084e04289fa63", + "sha256:1a3b2583c86bbfbf417304eeb13400ce7f8725376dc7d3efbf35dc5d7052ad48", + "sha256:1c9a1dc5e898ce30e2f9c0aa57181cddd4532b22b7780549441d6429d22d3b58", + "sha256:1f36a1e80ef4ed1996445698fd91e0d3e54738bf597c9995118b92da537d7a28", + "sha256:20147996376be452cd82cd6c17701daba69a849dc143270fa10fe067bb34562a", + "sha256:249c8e0055ca597707d71c5ad85fd2a1c8fdb99386a8c6c257e1b47b67a9bec1", + "sha256:2647192facf63be9ed2d7a49ceb07efe01dc6cfb083bd2cc53c418437400cb99", + "sha256:264f3a5906c62b9df3a00ad35f6da1987d321a053895bd85f9d5c708de5c0fbf", + "sha256:2abd669a39be69cdfe145927c7eb53a875b157740bf1e2d49e9619fc6f43362e", + "sha256:2b2415d5a7b7ee96aa3a54d4775c1fec140476a17ee12353806297e900eaeddc", + "sha256:2c173f529666bab8e3f948b74c6d91afa22ea147e6ebae49a48229d9020a47c4", + "sha256:2da81c1492291c1a90987d76a47c7b2d310661bf7c93a9de0511e27b796a8b46", + "sha256:2eca04a365be380ca1f8fa48b334462e19e3382c0bb7386444d8ca43aa01c481", + "sha256:37b08df45f02ff1866043b95096cbe91ac99de05936dd09d6611987a82a3306a", + "sha256:37f79f4f1f06cc96151f4a187528c3fd4a7e1065538a4af9eb68c642365957f7", + "sha256:3dd5fb7737224e1497c886fb3ca681c15d9c00c76171f53b3c3cc8d16ccfa7fb", + "sha256:3e3ac5b602fea378243f993d8b707189f9061e55ebb4e56cb9fdef8166060f28", + "sha256:3f55ae773abd96b1de25fc5c3fb356f491bd19116f8f854ba705beffc1ddc3c5", + "sha256:4011d5c854aa804c833331d38a2b6f6f2fe58a90c9f615afdb7aa7cf9d31f721", + "sha256:4145172ab59b6c27695db6d78d040795f635cba732cead19c78cede74800949a", + "sha256:42b9535aa22ab023704cfc6533e968f7e420affe802d85e956d8a7b4c0b0b5ea", + "sha256:46a07a258bda12270de02b34c4884f200f864bba3dcd6e3a37fef36a168b859d", + "sha256:4f13d3f6585bd07657a603780e99beda96a36c86acaba841f131e81393958336", + "sha256:528e2afaa56d815d2601b857644aeb395afe7e59212ab0659906dc29ae68d9a6", + "sha256:545e94c84575057d3d5c62634611858dac859702b1519b6ffc58eca7fb1adfcf", + "sha256:577d40a72550eac1386b77b43836151cb61ff6700adacda2ad4d883ca5a0b6f2", + "sha256:5967fa631d0ed9f8511dede08bc943a9727c949d05d1efac4ac82b2938024fb7", + "sha256:5b769396eb358d6b55dbf78f3f7ca631ca1b2fe02136faad5af74f0111b4b6b7", + "sha256:63c9e2794329ef070844ff9bfc012004aeddc0468dc26970953709723f76c8a5", + "sha256:6574f619e8734140d96c59bfa8a6a6e7a3336820ccd1bfd95ffa610673b650a2", + "sha256:6bfe72b249264cc1ff2f3629be240d7d2fdc778d9d298087cdec8524c91cd11f", + "sha256:736817dbbbd030a69a1faf5413a319976c9c8ba8cdcfa98c022d3b6b2e01eca6", + "sha256:74a2044b870df7c9360bb3ce7e12f9ddf8e72e49cd3a353a1528cbf166ad2383", + "sha256:74be3b215a5695690a0f1a9f68b1d1c93f8caad52e23242fcb8ba56aaf060281", + "sha256:76a8374b294e4ccb39ccaf11d39a0537ed107534139c00b4393ca3b542cc66e5", + "sha256:7ba239bb37663b2b4cd08e703e79e13321512dccd8e5f0e9451d9e53a6b8509a", + "sha256:7c40851b659d958c5245c1236e34f0d065cc53dca8d978b49a032c8e0adfda6e", + "sha256:7cf241dbb50ea71c2e628ab2a32b5bfcd36e199152fc44e5c1edb0b773f1583e", + "sha256:7cfae77da92a20f56cf89739a557b76e5c6edc094f6ad5c090b9e15fbbfcd1a4", + "sha256:7d152ec7bb431040af2500e01436c9aa0d993f243346f0594a15755016bf0be1", + "sha256:80080972e1d000ad0341c7cc58b6855c80bd887675f92871221451d13a975072", + "sha256:82dbcd6463e580bcfb7561cece35046aaabeac5a9ddb775020160b14e6c58a5d", + "sha256:8308a8d49d1354278d5c068c888a58d7158a419b2e4d87c7839ed3641498790c", + "sha256:839676475ac2ccd1532d36af3d10d290a2ca149b702ed464131e450a767550df", + "sha256:83feb0f682d75a09ddc11aa37ba5c07dd9b824b22915207f6176ea458474ff75", + "sha256:88956c993a20201744282362e3fd30962a9d86dc4f1dcf2bdb31fab27821b61f", + "sha256:8a6ad8429340e0a4de89353447c6441329def3632e7b2293a7d6e873217d3c2b", + "sha256:8ba9fbc5d6e36bfeb5292530321cc56c4ef3f98048647fabd8f57543c34174ec", + "sha256:8c1f6c8df23be165eb0cb78f305483d00c6827a191e3a38394c658d5b9c80bbd", + "sha256:91276caef95556faeb4b8f09fe4439670d3d6206fee78d47ddb6e6de837f0b4d", + "sha256:960e7e460fda2d0af18c75585bbe0c99f90b8f09963844618a621b804f8c3abe", + "sha256:9656a09653b18b80764647d585750df2dff8928e03a706763ab40ec8c4872acc", + "sha256:9cd935c0220d012a27c20135c140f9cdcbc6249d5954345c81bfb714071b985c", + "sha256:a2b3c79586636f1fa69a7bd59c87c15fca80c0d34b5c003d57f2f326e5276575", + "sha256:a4b9d3f5c48bbe8d9e3758e498b3c34863f2c9b1ac57a4e6310183740e59c980", + "sha256:a8c2bf286e5d755a075e5e97ba56b3de08cccdad6b323ab0b21cc98875176b03", + "sha256:a90031658805c63fe488f8e9e7a88b260ea121ba3ee9cdabcece9c9ddb50da39", + "sha256:ad666a904212aa9a6c77da7dce9d5170008cda76b7776e6731928b3f8a0d40fa", + "sha256:af2d1648eb625a460eee07d3e1ea3a4a6e84a1fb3a107f6a8e95ac19f7dcce67", + "sha256:b3d4b390ee70ca9263b331ccfaf9819ee20e90dfd0201a295e23eb64a005dbef", + "sha256:ba4432301ad7eeb1b00848cf46fae0e5fecfd18a8cb5fdcf856c67985f79ecc7", + "sha256:bc3179e0815827cf963e634095ae5715ee73a5af61defbc8d6ca79f1bdae1d1d", + "sha256:c5fd099acaee2325f01281a130a39da08d885e4dedf01b84bf156ec2737d78fe", + "sha256:c797ea56f36c6f248656f0223b11307fdf4a1886f3555eba371f34152b07677f", + "sha256:cd4ea56c9542ad0091dfdef3e8572ae7a746e1e91eb56c9e08b8d0808b40f1d1", + "sha256:cdd6f8738e1f1d9df5b1603bb03cb30e442710e5672262b95d0f9fcb4edb0dab", + "sha256:d0580faeb9def6d0beb7aa666294d5604e569c4e24111ada423cf9936768d95c", + "sha256:d11afdc5992bbd7af60ed5eb519873690d921425299f51d80aa3099ed49f2bcc", + "sha256:d1d388d2f5f5a6065cf83c54dd12112b7389095669ff395e632003ae8999c6b8", + "sha256:d20da6b4c7aa9ee75ad0730beaba15d65157f5beeaca54a038bb968f92bf3ce3", + "sha256:d22e0660de24bd8e9ac82f4230a22a5fe4e397265709289d61d5fb333839ba50", + "sha256:d22f2cb82e0b40e427a74a93c9a4231335bbc548aed79955dde0b64ea7f88146", + "sha256:d4fa1eeb9bea6d9b64ac91ec51ee94cc4fc744955df5be393e1c923c920db2b0", + "sha256:d9793d46d3e6522ae58e9321032827c9c0df1e56cbe5d3de965facb311aed6aa", + "sha256:dab979662da1c9fbb464e310c0b06cb5f1d174d09a462553af78f0bfb3e01920", + "sha256:db8d0f0ad92f74feb61c4e4a71f1d573ef37c22ef4dc19cab93e501bfdad8cbd", + "sha256:df2af1180b8eeececf4f819d22cc0668bfadadfd038b19a90bd2fb2ee419ec6f", + "sha256:dfb5d2ab183c0efe5e7b8917e4eaa2e837aacafad8a69b89aa6bc81550eed857", + "sha256:e04f8c76b8d5c70695b4e8f1d0b391d8ef91df00ef488c6c1ffb910176459bc6", + "sha256:e4a45ba34f904062c63049a760790c6a2fa7a4cc4bd160d8af243b12371aaa05", + "sha256:e9be1f7c5f9673616f875299339984da9447a40e3aea927750c843d6e5e2e029", + "sha256:edc91c50e17f5cd945d821f0f1af830522dba0c10267c3aab186dc3dbaab8def", + "sha256:ee70ee5f4144a45a9e6169000b5b525d82673d5dab9f7587eccc92794814e7ac", + "sha256:f1059ca9a51c936c9a8d46fbc2c9a6b4c15ab3f13a97f1ad32f024b39666ba85", + "sha256:f47eef55297799956464efc00c74ae55c48a7b68236856d56183fe1ddf866205", + "sha256:f4ae6f423cb7d1c6256b7482025ace2825728f53b7ac58bcd574de6ee9d242c2", + "sha256:f4b15a163448ec79241fb2f1bc5a8ae1a4a304f7a48d948d208a2935b26bf8a5", + "sha256:f55601fb58f92e4f4f1d05d80c24cb77505dc42103ddfd63ddfdc51d3da46fa2", + "sha256:fa84bbe22ffa108f91631935c28a623001e335d66e393438258501e618fb0dde", + "sha256:faa12a9f34671a30ea6bb027f04ec4e1fb8fa3fb3ed030893e729d4d0f3a9791", + "sha256:fcfd5f91b882eedf8d9601bd21261d6ce0e61a8c66a7152d1f5df08d3f643ab1", + "sha256:fe30ef31172bdcf946502a945faad110e8fff88c32c4bec9a593df0280e64d8a" + ], + "markers": "python_version >= '3.8'", + "version": "==0.13.1" + }, + "s3transfer": { + "hashes": [ + "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", + "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" + ], + "markers": "python_version >= '3.7'", + "version": "==0.8.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "soupsieve": { + "hashes": [ + "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", + "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" + ], + "markers": "python_version >= '3.8'", + "version": "==2.5" + }, + "termcolor": { + "hashes": [ + "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", + "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" + ], + "markers": "python_version >= '3.7'", + "version": "==2.3.0" + }, + "tomlkit": { + "hashes": [ + "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4", + "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba" + ], + "markers": "python_version >= '3.7'", + "version": "==0.12.3" + }, + "types-cachetools": { + "hashes": [ + "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199", + "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.3.0.7" + }, + "types-pyopenssl": { + "hashes": [ + "sha256:00171433653265843b7469ddb9f3c86d698668064cc33ef10537822156130ebf", + "sha256:5ffb077fe70b699c88d5caab999ae80e192fe28bf6cda7989b7e79b1e4e2dcd3" + ], + "markers": "python_version >= '3.7'", + "version": "==23.3.0.0" + }, + "types-python-dateutil": { + "hashes": [ + "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b", + "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9" + ], + "index": "pypi", + "version": "==2.8.19.14" + }, + "types-pytz": { + "hashes": [ + "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", + "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" + ], + "index": "pypi", + "version": "==2023.3.1.1" + }, + "types-pyyaml": { + "hashes": [ + "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", + "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" + ], + "version": "==6.0.12.12" + }, + "types-redis": { + "hashes": [ + "sha256:94fc61118601fb4f79206b33b9f4344acff7ca1d7bba67834987fb0efcf6a770", + "sha256:c8cfc84635183deca2db4a528966c5566445fd3713983f0034fb0f5a09e0890d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.6.0.11" + }, + "types-requests": { + "hashes": [ + "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", + "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0.10" + }, + "types-simplejson": { + "hashes": [ + "sha256:8ba093dc7884f59b3e62aed217144085e675a269debc32678fd80e0b43b2b86f", + "sha256:ebc81f886f89d99d6b80c726518aa2228bc77c26438f18fd81455e4f79f8ee1b" + ], + "index": "pypi", + "version": "==3.19.0.2" + }, + "typing-extensions": { + "hashes": [ + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" + ], + "markers": "python_version >= '3.8'", + "version": "==4.8.0" + }, + "urllib3": { + "hashes": [ + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.7" + }, + "werkzeug": { + "hashes": [ + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.1" + }, + "wrapt": { + "hashes": [ + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + ], + "markers": "python_version >= '3.6'", + "version": "==1.16.0" + }, + "xmltodict": { + "hashes": [ + "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", + "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" + ], + "markers": "python_version >= '3.4'", + "version": "==0.13.0" } + } } diff --git a/package-lock.json b/package-lock.json index 0dbb022dfd..e0973ebcce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.1.1", "json-web-key": "^0.4.0", - "jsrsasign": "^10.8.6", + "jsrsasign": "^11.0.0", "lint-staged": "^13.2.3", "livereload": "^0.9.3", "node-forge": "^1.2.1", @@ -7001,9 +7001,9 @@ } }, "node_modules/jsrsasign": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.9.0.tgz", - "integrity": "sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.0.0.tgz", + "integrity": "sha512-BtRwVKS+5dsgPpAtzJcpo5OoWjSs1/zllSBG0+8o8/aV0Ki76m6iZwHnwnsqoTdhfFZDN1XIdcaZr5ZkP+H2gg==", "dev": true, "funding": { "url": "https://github.com/kjur/jsrsasign#donations" diff --git a/package.json b/package.json index 9b1717f277..2478a9bbc0 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.1.1", "json-web-key": "^0.4.0", - "jsrsasign": "^10.8.6", + "jsrsasign": "^11.0.0", "lint-staged": "^13.2.3", "livereload": "^0.9.3", "node-forge": "^1.2.1", From e054b0e1ff6a04dfa123240cc212542c17b3bdf8 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Thu, 1 Feb 2024 14:37:41 +0000 Subject: [PATCH 373/567] Update template content for Calculated Summary & Grand Calculated Summary (#1310) --- app/translations/messages.pot | 30 ++++++++----------- templates/layouts/_calculatedsummary.html | 8 +---- tests/integration/integration_test_case.py | 3 ++ .../test_questionnaire_calculated_summary.py | 4 +++ ..._questionnaire_grand_calculated_summary.py | 4 +++ 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 8281cfe719..39d97a27b6 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-01-02 11:59+0000\n" +"POT-Creation-Date: 2024-01-30 11:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -907,19 +907,19 @@ msgid "" "your answers for your records." msgstr "" -#: templates/layouts/_base.html:133 templates/thank-you.html:71 +#: templates/layouts/_base.html:147 templates/thank-you.html:71 msgid "minute" msgstr "" -#: templates/layouts/_base.html:134 templates/thank-you.html:72 +#: templates/layouts/_base.html:148 templates/thank-you.html:72 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:135 templates/thank-you.html:73 +#: templates/layouts/_base.html:149 templates/thank-you.html:73 msgid "second" msgstr "" -#: templates/layouts/_base.html:136 templates/thank-you.html:74 +#: templates/layouts/_base.html:150 templates/thank-you.html:74 msgid "seconds" msgstr "" @@ -1213,38 +1213,34 @@ msgstr "" msgid "Hide" msgstr "" -#: templates/layouts/_base.html:103 +#: templates/layouts/_base.html:117 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:128 +#: templates/layouts/_base.html:142 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:129 +#: templates/layouts/_base.html:143 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:130 +#: templates/layouts/_base.html:144 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:131 +#: templates/layouts/_base.html:145 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:132 +#: templates/layouts/_base.html:146 msgid "Continue survey" msgstr "" -#: templates/layouts/_calculatedsummary.html:14 -msgid "Please review your answers and confirm these are correct" -msgstr "" - -#: templates/layouts/_calculatedsummary.html:25 -msgid "Yes, I confirm these are correct" +#: templates/layouts/_calculatedsummary.html:19 +msgid "Yes, I confirm this is correct" msgstr "" #: templates/layouts/_questionnaire.html:39 diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html index b618e817b3..54ac18fda1 100644 --- a/templates/layouts/_calculatedsummary.html +++ b/templates/layouts/_calculatedsummary.html @@ -8,12 +8,6 @@ {% block form_title %} {% endblock %} - {% call onsPanel({ - "classes": "ons-u-mb-l" - }) %} -

      {{ _("Please review your answers and confirm these are correct") }}

      - {% endcall %} -
      {% include 'partials/summary/summary.html' %}
      @@ -22,7 +16,7 @@ {% block submit_button %} {{ onsButton({ - "text": _("Yes, I confirm these are correct"), + "text": _("Yes, I confirm this is correct"), "variants": 'timer', "attributes": { "data-qa": "btn-submit", diff --git a/tests/integration/integration_test_case.py b/tests/integration/integration_test_case.py index 1bbfef0255..551230b5b9 100644 --- a/tests/integration/integration_test_case.py +++ b/tests/integration/integration_test_case.py @@ -273,6 +273,9 @@ def getLinksByAttribute(self, attributes: dict[str, str]): def getSignOutButton(self): return self.getHtmlSoup().find("a", {"data-qa": "btn-save-sign-out"}) + def getSubmitButton(self): + return self.getHtmlSoup().find("button", {"data-qa": "btn-submit"}) + def saveAndSignOut(self): """ Sign out of eQ using the `Save and exit survey` button and do not follow redirects since the redirect is external diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 2cddb74a30..74cf33c39a 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -48,9 +48,13 @@ def _complete_calculated_summary_path_no_skip(self): def test_calculated_summary(self): self.launchSurvey("test_calculated_summary") self._complete_calculated_summary_path_with_skip() + self.assertInBody( "We calculate the total of currency values entered to be £80.00" ) + self.assertEqual( + "Yes, I confirm this is correct", self.getSubmitButton().text.strip() + ) def test_calculated_summary_no_skip(self): self.launchSurvey("test_calculated_summary") diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index e71e366ca8..5301f7a42f 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -24,6 +24,10 @@ def test_grand_calculated_summary(self): self.assertInBody( "We calculate the grand total journeys per week to be 13. Is this correct?" ) + # check the submit button text + self.assertEqual( + "Yes, I confirm this is correct", self.getSubmitButton().text.strip() + ) def test_grand_calculated_summary_multiple_sections(self): """ From f6e4a81366ec5f47339df8c2966d966975b06a7e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 7 Feb 2024 12:40:02 +0000 Subject: [PATCH 374/567] Update error message content for 500 errors (#1311) Co-authored-by: Mebin Abraham --- app/translations/messages.pot | 35 ++++++++---- templates/errors/500.html | 14 +++-- tests/integration/routes/test_errors.py | 74 +++++++++++++++++++++---- 3 files changed, 96 insertions(+), 27 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 39d97a27b6..3ea45f4d5c 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-01-30 11:47+0000\n" +"POT-Creation-Date: 2024-02-06 15:03+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -982,7 +982,7 @@ msgid "" msgstr "" #: templates/errors/401.html:20 templates/errors/403.html:12 -#: templates/errors/404.html:13 templates/errors/500.html:12 +#: templates/errors/404.html:13 templates/errors/500.html:16 #: templates/errors/submission-failed.html:14 msgid "Business surveys" msgstr "" @@ -994,7 +994,7 @@ msgid "" msgstr "" #: templates/errors/401.html:22 templates/errors/403.html:14 -#: templates/errors/404.html:15 templates/errors/500.html:14 +#: templates/errors/404.html:15 templates/errors/500.html:19 #: templates/errors/submission-failed.html:16 msgid "All other surveys" msgstr "" @@ -1081,22 +1081,33 @@ msgstr "" msgid "If you have started a survey, your answers have been saved." msgstr "" -#: templates/errors/500.html:10 +#: templates/errors/500.html:10 templates/errors/500.html:17 msgid "" -"Contact us if you need to speak to someone about your" -" survey." +"If you have attempted to submit your survey, you should check that this " +"was successful. To do this, sign in to your business " +"survey account." msgstr "" -#: templates/errors/500.html:13 +#: templates/errors/500.html:11 templates/errors/500.html:14 +msgid "If you need more help, contact us." +msgstr "" + +#: templates/errors/500.html:13 templates/errors/500.html:20 +msgid "" +"If you have attempted to submit your survey, you should check that this " +"was successful. To do this, re-enter your code." +msgstr "" + +#: templates/errors/500.html:18 msgid "" -"If you are completing a business survey and you need to speak to someone " -"about your survey, please contact us." +"If you need more help, contact us about business " +"surveys." msgstr "" -#: templates/errors/500.html:15 +#: templates/errors/500.html:21 msgid "" -"If you started your survey using an access code and you need to speak to " -"someone about your survey, please contact us." +"If you need more help, contact us about all other " +"surveys." msgstr "" #: templates/errors/previously-submitted.html:3 diff --git a/templates/errors/500.html b/templates/errors/500.html index 14cad74421..d9ad2701f0 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -6,12 +6,18 @@

      {{ _("Sorry, there is a problem with this service") }}

      {{ _("Try again later.") }}

      {{ _("If you have started a survey, your answers have been saved.") }}

      - {% if survey_type and survey_type in SURVEY_TYPES_ALL %} -

      {{ _("Contact us if you need to speak to someone about your survey.").format(url = contact_us_url) }}

      + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} +

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, sign in to your business survey account.").format(url = business_logout_url) }}

      +

      {{ _("If you need more help, contact us.").format(url=contact_us_url) }}

      + {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %} +

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, re-enter your code.").format(url = other_logout_url) }}

      +

      {{ _("If you need more help, contact us.").format(url=contact_us_url) }}

      {% else %}

      {{ _("Business surveys") }}

      -

      {{ _("If you are completing a business survey and you need to speak to someone about your survey, please contact us.").format(url=business_contact_us_url) }}

      +

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, sign in to your business survey account.").format(url = business_logout_url) }}

      +

      {{ _("If you need more help, contact us about business surveys.").format(url=business_contact_us_url) }}

      {{ _("All other surveys") }}

      -

      {{ _("If you started your survey using an access code and you need to speak to someone about your survey, please contact us.").format(url=other_contact_us_url) }}

      +

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, re-enter your code.").format(url = other_logout_url) }}

      +

      {{ _("If you need more help, contact us about all other surveys.").format(url=other_contact_us_url) }}

      {% endif %} {% endblock %} diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index 957520afc5..bdadc81945 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -35,6 +35,34 @@ class TestErrors(IntegrationTestCase): # pylint: disable=too-many-public-method "response_expires_at": get_response_expires_at(), } + def _assert_generic_500_page_content(self): + self.assertInBody( + "

      Sorry, there is a problem with this service

      \n" + "

      Try again later.

      \n" + "

      If you have started a survey, your answers have been saved.

      " + ) + + def _assert_default_theme_500_page_content( + self, *, url=DEFAULT_URL, has_header=False, contact_us_text="contact us" + ): + header_text = "

      Business surveys

      \n" if has_header else "" + self.assertInBody( + f"{header_text}

      If you have attempted to submit your survey, you should check that this was successful. To do this, " + f'sign in to your business survey account.

      \n' + f'

      If you need more help, {contact_us_text}.

      ' + ) + + def _assert_social_theme_500_page_content( + self, has_header=False, contact_us_text="contact us" + ): + header_text = "

      All other surveys

      \n" if has_header else "" + + self.assertInBody( + f"{header_text}

      If you have attempted to submit your survey, you should check that this was successful. To do this, " + f're-enter your code.

      \n' + f'

      If you need more help, {contact_us_text}.

      ' + ) + def test_errors_404(self): self.get("/hfjdskahfjdkashfsa") self.assertStatusNotFound() @@ -92,7 +120,7 @@ def test_errors_500_exception_during_error_handling(self): self.post({"answer": "5000000"}) self.assertStatusCode(500) - self.assertInBody("Sorry, there is a problem with this service") + self._assert_generic_500_page_content() def test_401_theme_default_cookie_exists(self): # Given @@ -291,14 +319,17 @@ def test_500_theme_default_cookie_exists(self): # Then self.assertEqual(cookie.get("theme"), "default") - self.assertException() - self.assertInBody( - f'

      Contact us if you need to speak to someone about your survey.

      ' - ) + self.assertStatusCode(500) + self._assert_generic_500_page_content() + self._assert_default_theme_500_page_content() def test_500_theme_social_cookie_exists(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2( + schema_name="test_theme_social", + theme="social", + account_service_url=SOCIAL_URL, + ) # When with patch( "app.routes.questionnaire.get_block_handler", @@ -308,11 +339,32 @@ def test_500_theme_social_cookie_exists(self): cookie = self.getCookie() # Then - self.assertEqual(cookie.get("theme"), "default") - self.assertException() - self.assertInBody( - f'

      Contact us if you need to speak to someone about your survey.

      ' - ) + self.assertEqual(cookie.get("theme"), "social") + self.assertStatusCode(500) + self._assert_generic_500_page_content() + self._assert_social_theme_500_page_content() + + def test_500_theme_not_set_in_cookie(self): + # Given I launch a survey, When the 'theme' is not set in the cookie + with patch( + "app.routes.session.set_schema_context_in_cookie", + side_effect=Exception("Theme set failed"), + ): + self.launchSurvey("test_introduction") + + # Then I see the generic 500 error page + cookie = self.getCookie() + self.assertEqual(cookie.get("theme"), None) + self.assertStatusCode(500) + self._assert_generic_500_page_content() + self._assert_default_theme_500_page_content( + url=BUSINESS_URL, + has_header=True, + contact_us_text="contact us about business surveys", + ) + self._assert_social_theme_500_page_content( + has_header=True, contact_us_text="contact us about all other surveys" + ) def test_submission_failed_theme_default_cookie_exists(self): # Given From 70706bd4a7cdd938186ff461f19dba8cb24478c3 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:13:28 +0000 Subject: [PATCH 375/567] Ignore node_modules in pylint and fix useless suppression warnings (#1315) --- .pylintrc | 2 +- tests/app/data_model/conftest.py | 1 - tests/app/helpers/test_template_helpers.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.pylintrc b/.pylintrc index 043074ca1e..4308bddeef 100644 --- a/.pylintrc +++ b/.pylintrc @@ -48,7 +48,7 @@ ignore=CVS # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\' represents the directory delimiter on Windows systems, it # can't be used as an escape character. -ignore-paths= +ignore-paths=node_modules # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores diff --git a/tests/app/data_model/conftest.py b/tests/app/data_model/conftest.py index d5d94b5313..4e182b8abb 100644 --- a/tests/app/data_model/conftest.py +++ b/tests/app/data_model/conftest.py @@ -1,4 +1,3 @@ -# pylint: disable=redefined-outer-name from datetime import datetime, timedelta, timezone import pytest diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index 9006c71333..bdf8b3bf6d 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -1,4 +1,3 @@ -# pylint: disable=too-many-lines from typing import Type import pytest From e8df3089132198e2fa9153450137a3e124937ce2 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:25:22 +0000 Subject: [PATCH 376/567] Schemas v4.1.0 (#1317) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 857572fcd3..b913b7c658 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.0.0 +v4.1.0 From 55cc45ba60ada4b5b74bc16719451cf763fb3c0a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 19 Feb 2024 14:16:24 +0000 Subject: [PATCH 377/567] Update Node packages (#1321) --- package-lock.json | 1416 +++++++++++++++++++++++++-------------------- 1 file changed, 788 insertions(+), 628 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0973ebcce..ab00f1289c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,21 +82,21 @@ } }, "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", + "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", + "@babel/helpers": "^7.23.9", + "@babel/parser": "^7.23.9", + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -112,12 +112,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -151,14 +151,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -167,9 +167,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.5.tgz", - "integrity": "sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==", + "version": "7.23.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", + "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -207,9 +207,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", - "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", + "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -432,14 +432,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", + "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" + "@babel/template": "^7.23.9", + "@babel/traverse": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" @@ -460,9 +460,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -504,9 +504,9 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", - "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -782,9 +782,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", - "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", + "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -880,16 +880,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", - "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", + "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", @@ -1013,12 +1012,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", - "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", + "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1140,9 +1140,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", + "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", @@ -1400,16 +1400,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.4.tgz", - "integrity": "sha512-ITwqpb6V4btwUG0YJR82o2QvmWrLgDnx/p2A3CTPYGaRgULkDiC0DRA2C4jlRB9uXGUEfaSS/IGHfVW+ohzYDw==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", + "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "semver": "^6.3.1" }, "engines": { @@ -1559,18 +1559,18 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.5.tgz", - "integrity": "sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", + "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", "dev": true, "dependencies": { "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1591,13 +1591,13 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.4", + "@babel/plugin-transform-async-generator-functions": "^7.23.9", "@babel/plugin-transform-async-to-generator": "^7.23.3", "@babel/plugin-transform-block-scoped-functions": "^7.23.3", "@babel/plugin-transform-block-scoping": "^7.23.4", "@babel/plugin-transform-class-properties": "^7.23.3", "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.5", + "@babel/plugin-transform-classes": "^7.23.8", "@babel/plugin-transform-computed-properties": "^7.23.3", "@babel/plugin-transform-destructuring": "^7.23.3", "@babel/plugin-transform-dotall-regex": "^7.23.3", @@ -1605,7 +1605,7 @@ "@babel/plugin-transform-dynamic-import": "^7.23.4", "@babel/plugin-transform-exponentiation-operator": "^7.23.3", "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-for-of": "^7.23.6", "@babel/plugin-transform-function-name": "^7.23.3", "@babel/plugin-transform-json-strings": "^7.23.4", "@babel/plugin-transform-literals": "^7.23.3", @@ -1613,7 +1613,7 @@ "@babel/plugin-transform-member-expression-literals": "^7.23.3", "@babel/plugin-transform-modules-amd": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.9", "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.23.3", @@ -1639,9 +1639,9 @@ "@babel/plugin-transform-unicode-regex": "^7.23.3", "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.8", + "babel-plugin-polyfill-corejs3": "^0.9.0", + "babel-plugin-polyfill-regenerator": "^0.5.5", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -1667,15 +1667,15 @@ } }, "node_modules/@babel/register": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", - "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.23.7.tgz", + "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", "make-dir": "^2.1.0", - "pirates": "^4.0.5", + "pirates": "^4.0.6", "source-map-support": "^0.5.16" }, "engines": { @@ -1692,9 +1692,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", - "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1704,34 +1704,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -1739,9 +1739,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -1800,9 +1800,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1827,22 +1827,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -1863,9 +1863,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -2063,9 +2063,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2087,9 +2087,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2097,12 +2097,12 @@ } }, "node_modules/@ljharb/through": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", - "integrity": "sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==", + "version": "2.3.12", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", + "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.5" }, "engines": { "node": ">= 0.4" @@ -2154,9 +2154,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", - "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "dependencies": { "debug": "4.3.4", @@ -2253,9 +2253,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", - "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2319,19 +2319,34 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/snapshot": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.0.tgz", + "integrity": "sha512-swmktcviVVPYx9U4SEQXLV6AEY51Y6bZ14jA2yo6TgMxQ3h+ZYiO0YhAHGJNp0ohCFbPAis1R9kK0cvN6lDPQA==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@wdio/cli": { - "version": "8.24.13", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.13.tgz", - "integrity": "sha512-UG4dvnT6KfnKDsNVn/GeUidi21Pso6N6eu1O5oin9+fP612zpPFrx3/TuYrAfjJb+qmy1QkKq3zX99y+xlp7og==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.32.2.tgz", + "integrity": "sha512-CbTALXXnDzvthu+EK0dK5QDTXToU4wNrldtonQcsD8tT726O56BLFGm9tzcGP6PZWh9NxAuvsFlWSwUxcqXq0Q==", "dev": true, "dependencies": { "@types/node": "^20.1.1", - "@wdio/config": "8.24.12", - "@wdio/globals": "8.24.12", - "@wdio/logger": "8.24.12", - "@wdio/protocols": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@vitest/snapshot": "^1.2.1", + "@wdio/config": "8.32.2", + "@wdio/globals": "8.32.2", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.32.0", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^3.5.3", @@ -2344,9 +2359,9 @@ "lodash.flattendeep": "^4.4.0", "lodash.pickby": "^4.6.0", "lodash.union": "^4.6.0", - "read-pkg-up": "^10.0.0", + "read-pkg-up": "10.0.0", "recursive-readdir": "^2.2.3", - "webdriverio": "8.24.12", + "webdriverio": "8.32.2", "yargs": "^17.7.2" }, "bin": { @@ -2369,14 +2384,14 @@ } }, "node_modules/@wdio/config": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.12.tgz", - "integrity": "sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.32.2.tgz", + "integrity": "sha512-ubqe4X+TgcERzXKIpMfisquNxPZNtRU5uPeV7hvas++mD75QyNpmWHCtea2+TjoXKxlZd1MVrtZAwtmqMmyhPw==", "dev": true, "dependencies": { - "@wdio/logger": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -2387,29 +2402,29 @@ } }, "node_modules/@wdio/globals": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.12.tgz", - "integrity": "sha512-uF26a89Q+6DdqzSfK9suXJNdWYJnsazjzPuq4Xtz6nKdjgmBufSeX1JHV4LxErEu5b/IdzVcMCUKKEvsZPc9vA==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.32.2.tgz", + "integrity": "sha512-WEN7Tq0+Ny8OHS+7e1RCu4ss3lYG2Ln8/TpicacTsYWM3jtrf1SkUT+4H8JtG1qywj4WXTH8CxD4H9zFoofiJw==", "dev": true, "engines": { "node": "^16.13 || >=18" }, "optionalDependencies": { - "expect-webdriverio": "^4.6.1", - "webdriverio": "8.24.12" + "expect-webdriverio": "^4.11.2", + "webdriverio": "8.32.2" } }, "node_modules/@wdio/local-runner": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.12.tgz", - "integrity": "sha512-Q1lfdSPDEgKwuE1gNucJrkVfgOJLTjtnYGb7Fe7oYUHGDwjkudjSBJYmyx30qFZKfZ4zRqXtaEdys54/0TxibA==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.32.2.tgz", + "integrity": "sha512-iCVnwBIIwgRqiF2Fz/XVlGf3ejHOzR2+kc3dsdMgKljLkVq4ZDFwPMtw8Hk06sq+BaiNLamdxj+8ElD6OGJ88A==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/logger": "8.24.12", + "@wdio/logger": "8.28.0", "@wdio/repl": "8.24.12", - "@wdio/runner": "8.24.12", - "@wdio/types": "8.24.12", + "@wdio/runner": "8.32.2", + "@wdio/types": "8.32.2", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", "stream-buffers": "^3.0.2" @@ -2419,9 +2434,9 @@ } }, "node_modules/@wdio/logger": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz", - "integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", + "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", "dev": true, "dependencies": { "chalk": "^5.1.2", @@ -2446,16 +2461,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.12.tgz", - "integrity": "sha512-SHN7CYZnDkVUNYxLp8iMV92xcmU/4gq5dqA0pRrK4m5nIU7BoL0flm0kA+ydYUQyNedQh2ru1V63uNyTOyCKAg==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.32.2.tgz", + "integrity": "sha512-76DA95u0Z2AfyZSQglJn9BFJ+XveRR6+oH1K/J8rDOWoIHgMcASGEj+fsXAPSDNcSVDBe0QN5HLnVi3tciuyQw==", "dev": true, "dependencies": { "@types/mocha": "^10.0.0", "@types/node": "^20.1.0", - "@wdio/logger": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "mocha": "^10.0.0" }, "engines": { @@ -2463,9 +2478,9 @@ } }, "node_modules/@wdio/protocols": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", - "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.32.0.tgz", + "integrity": "sha512-inLJRrtIGdTz/YPbcsvpSvPlYQFTVtF3OYBwAXhG2FiP1ZwE1CQNLP/xgRGye1ymdGCypGkexRqIx3KBGm801Q==", "dev": true }, "node_modules/@wdio/repl": { @@ -2481,14 +2496,14 @@ } }, "node_modules/@wdio/reporter": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.12.tgz", - "integrity": "sha512-FtLzDTBXdgxXf4T9HJQ2bNpYYSKEw//jojFm9XzB4fPwzPeFY3HC+dbePucVW1SSLrVzVxqIOyHiwCLqQ/4cQw==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.32.2.tgz", + "integrity": "sha512-BZdReAFfRCtgtYbyhkKgSGqqoIn/yG5/Z4COjvRvon9NJkz+eA4PiHCKdEP7+ekBIjud7H8Gy+6mPBDEu+wllw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/logger": "8.24.12", - "@wdio/types": "8.24.12", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.32.2", "diff": "^5.0.0", "object-inspect": "^1.12.0" }, @@ -2497,35 +2512,35 @@ } }, "node_modules/@wdio/runner": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.12.tgz", - "integrity": "sha512-wiwXZWG12YDe7GCYBnZ1xEg3UKi18Rvh4RNQiumjypDOErJit1hOCppbJ37LqLqQu+tfWGfN73j46yR7fQOCHw==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.32.2.tgz", + "integrity": "sha512-/24wPBg2okkfpA1bl7mCWIvWXO3pa9OhsKNav+gGm7BP+hQ1lxULyYg/o5fCEwEjFPWDLy0jjknJLqXcTWvmiQ==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.24.12", - "@wdio/globals": "8.24.12", - "@wdio/logger": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@wdio/config": "8.32.2", + "@wdio/globals": "8.32.2", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "deepmerge-ts": "^5.0.0", - "expect-webdriverio": "^4.6.1", + "expect-webdriverio": "^4.11.2", "gaze": "^1.1.2", - "webdriver": "8.24.12", - "webdriverio": "8.24.12" + "webdriver": "8.32.2", + "webdriverio": "8.32.2" }, "engines": { "node": "^16.13 || >=18" } }, "node_modules/@wdio/spec-reporter": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.12.tgz", - "integrity": "sha512-Ng3ErWK8eESamCYwIr2Uv49+46RvmT8FnmGaJ6irJoAp101K8zENEs1pyqYHJReucN+ka/wM87blfc2k8NEHCA==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.32.2.tgz", + "integrity": "sha512-3hUXpE+idC4KOXofJnpubdDDCE8X0OTd6ykypiaXMI2hJTA2nIZcHtpRQnAE0E4JT9OzLnPWODcMq54GGBDRkg==", "dev": true, "dependencies": { - "@wdio/reporter": "8.24.12", - "@wdio/types": "8.24.12", + "@wdio/reporter": "8.32.2", + "@wdio/types": "8.32.2", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^7.0.0" @@ -2547,9 +2562,9 @@ } }, "node_modules/@wdio/types": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.12.tgz", - "integrity": "sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.32.2.tgz", + "integrity": "sha512-jq8LcBBQpBP9ZF5kECKEpXv8hN7irCGCjLFAN0Bd5ScRR6qu6MGWvwkDkau2sFPr0b++sKDCEaMzQlwrGFjZXg==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -2559,18 +2574,18 @@ } }, "node_modules/@wdio/utils": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.12.tgz", - "integrity": "sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.32.2.tgz", + "integrity": "sha512-PJcP4d1Fr8Zp+YIfGN93G0fjDj/6J0I6Gf6p0IpJk8qKQpdFDm4gB+lc202iv2YkyC+oT6b4Ik2W9LzvpSKNoQ==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.24.12", - "@wdio/types": "8.24.12", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.32.2", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.3.5", - "geckodriver": "^4.2.0", + "geckodriver": "^4.3.1", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", @@ -2583,9 +2598,9 @@ } }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2780,13 +2795,16 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2811,17 +2829,36 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "node_modules/array.prototype.filter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", + "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", + "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2867,17 +2904,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" }, "engines": { @@ -2927,9 +2965,9 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", "dev": true, "engines": { "node": ">= 0.4" @@ -2939,19 +2977,19 @@ } }, "node_modules/b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", "dev": true }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", - "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", + "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.3", + "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { @@ -2959,25 +2997,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", - "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", + "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3", - "core-js-compat": "^3.33.1" + "@babel/helper-define-polyfill-provider": "^0.5.0", + "core-js-compat": "^3.34.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", - "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", + "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.3" + "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -2989,6 +3027,13 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", + "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", + "dev": true, + "optional": true + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3019,9 +3064,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", + "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", "dev": true, "engines": { "node": ">=10.0.0" @@ -3134,9 +3179,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -3153,8 +3198,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, @@ -3256,9 +3301,9 @@ } }, "node_modules/builtins/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3316,14 +3361,19 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3351,9 +3401,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", + "version": "1.0.30001588", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", + "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", "dev": true, "funding": [ { @@ -3403,16 +3453,10 @@ "dev": true }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3425,6 +3469,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -3691,12 +3738,12 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", + "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", "dev": true, "dependencies": { - "browserslist": "^4.22.2" + "browserslist": "^4.22.3" }, "funding": { "type": "opencollective", @@ -3912,17 +3959,20 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -3966,15 +4016,15 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1233178", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz", - "integrity": "sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg==", + "version": "0.0.1261483", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1261483.tgz", + "integrity": "sha512-7vJvejpzA5DTfZVkr7a8sGpEAzEiAqcgmRTB0LSUrWeOicwL09lMQTzxHtFNVhJ1OOJkgYdH6Txvy9E5j3VOUQ==", "dev": true }, "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -4002,15 +4052,15 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz", + "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==", "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/duplexer2": { @@ -4114,13 +4164,13 @@ } }, "node_modules/edgedriver": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.8.tgz", - "integrity": "sha512-FWLPDuwJDeGGgtmlqTXb4lQi/HV9yylLo1F9O1g9TLqSemA5T6xH28seUIfyleVirLFtDQyKNUxKsMhMT4IfnA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.10.tgz", + "integrity": "sha512-RFSHYMNtcF1PjaGZCA2rdQQ8hSTLPZgcYgeY1V6dC+tR4NhZXwFAku+8hCbRYh7ZlwKKrTbVu9FwknjFddIuuw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@wdio/logger": "^8.16.17", + "@wdio/logger": "^8.28.0", "decamelize": "^6.0.0", "edge-paths": "^3.0.5", "node-fetch": "^3.3.2", @@ -4147,9 +4197,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.605", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.605.tgz", - "integrity": "sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==", + "version": "1.4.673", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz", + "integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==", "dev": true }, "node_modules/emoji-regex": { @@ -4177,50 +4227,52 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", + "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", + "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.8", "string.prototype.trimend": "^1.0.7", "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", + "typed-array-buffer": "^1.0.1", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -4229,6 +4281,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", @@ -4276,9 +4355,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -4315,15 +4394,15 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4526,9 +4605,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", @@ -4547,7 +4626,7 @@ "object.groupby": "^1.0.1", "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -4591,15 +4670,16 @@ } }, "node_modules/eslint-plugin-n": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.3.1.tgz", - "integrity": "sha512-w46eDIkxQ2FaTHcey7G40eD+FhTXOdKudDXPUO2n9WNcslze/i/HT2qJ3GXjHngYSGDISIgPNhwGtgoix4zeOw==", + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.1.0", + "eslint-plugin-es-x": "^7.5.0", "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", "ignore": "^5.2.4", "is-builtin-module": "^3.2.1", "is-core-module": "^2.12.1", @@ -4617,6 +4697,21 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-n/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4630,9 +4725,9 @@ } }, "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4644,6 +4739,18 @@ "node": ">=10" } }, + "node_modules/eslint-plugin-n/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-n/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -4808,9 +4915,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4985,11 +5092,12 @@ } }, "node_modules/expect-webdriverio": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.6.1.tgz", - "integrity": "sha512-w6ee91kN3BoxNGVKQheAqFpRGMehdDg7kDiErEk/oM7tbd/WUT4R4v9KYOUtjiaUFHWWCRW2FtcOOjcd0+1pvQ==", + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.11.8.tgz", + "integrity": "sha512-BRezX8yEWZN7H5eYde/4EeDbVu8HRWq7VKyRrx4s2RnMa23wF+axZC83a4F/eezP6Kek700n3dA3Ph8RSE6GAQ==", "dev": true, "dependencies": { + "@vitest/snapshot": "^1.2.2", "expect": "^29.7.0", "jest-matcher-utils": "^29.7.0", "lodash.isequal": "^4.5.0" @@ -4998,9 +5106,9 @@ "node": ">=16 || >=18 || >=20" }, "optionalDependencies": { - "@wdio/globals": "^8.23.1", - "@wdio/logger": "^8.16.17", - "webdriverio": "^8.23.1" + "@wdio/globals": "^8.29.3", + "@wdio/logger": "^8.28.0", + "webdriverio": "^8.29.3" } }, "node_modules/external-editor": { @@ -5077,9 +5185,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -5305,17 +5413,17 @@ } }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -5434,17 +5542,17 @@ } }, "node_modules/geckodriver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", - "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.2.tgz", + "integrity": "sha512-TNOoy+ULXJWI5XOq7CXD3PAD9TJa4NjMe7nKUXjlIsf+vezuaRsFgPwcgYdEem1K7106wabYsqr7Kqn51g0sJg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@wdio/logger": "^8.11.0", + "@wdio/logger": "^8.28.0", "decamelize": "^6.0.0", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.1", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", "tar-fs": "^3.0.4", "unzipper": "^0.10.14", "which": "^4.0.0" @@ -5475,16 +5583,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5514,13 +5626,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -5542,24 +5655,24 @@ } }, "node_modules/get-uri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", - "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", "dev": true, "dependencies": { "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.0", + "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4", - "fs-extra": "^8.1.0" + "fs-extra": "^11.2.0" }, "engines": { "node": ">= 14" } }, "node_modules/get-uri/node_modules/data-uri-to-buffer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", - "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, "engines": { "node": ">= 14" @@ -5779,12 +5892,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5815,12 +5928,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5830,9 +5943,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -5863,9 +5976,9 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -5878,9 +5991,9 @@ "dev": true }, "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { "agent-base": "^7.1.0", @@ -5904,9 +6017,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -5958,9 +6071,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -6068,12 +6181,12 @@ } }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" }, @@ -6081,21 +6194,30 @@ "node": ">= 0.4" } }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6383,12 +6505,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -6925,6 +7047,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6986,24 +7114,27 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/jsrsasign": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.0.0.tgz", - "integrity": "sha512-BtRwVKS+5dsgPpAtzJcpo5OoWjSs1/zllSBG0+8o8/aV0Ki76m6iZwHnwnsqoTdhfFZDN1XIdcaZr5ZkP+H2gg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.1.0.tgz", + "integrity": "sha512-Ov74K9GihaK9/9WncTe1mPmvrO7Py665TUfUKvraXBpu+xcTWitrtuOwcjf4KMU9maPaYn0OuaWy0HOzy/GBXg==", "dev": true, "funding": { "url": "https://github.com/kjur/jsrsasign#donations" @@ -7315,12 +7446,12 @@ "dev": true }, "node_modules/locate-app": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.1.0.tgz", - "integrity": "sha512-rcVo/iLUxrd9d0lrmregK/Z5Y5NCpSwf9KlMbPpOHmKmdxdQY1Fj8NDQ5QymJTryCsBLqwmniFv2f3JKbk9Bvg==", + "version": "2.2.19", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.2.19.tgz", + "integrity": "sha512-mjhvrYRHnLAVwreShl8NTwq9EUyfRoCqB0UsOlMKXo2KBmtb4dhlHbZH4mcfDsoNoLkHZ1Rq4TsWP/59Ix62Ww==", "dev": true, "dependencies": { - "n12": "0.4.0", + "n12": "1.8.22", "type-fest": "2.13.0", "userhome": "1.0.0" } @@ -7664,9 +7795,9 @@ } }, "node_modules/loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", "dev": true, "engines": { "node": ">= 0.6.0" @@ -7709,6 +7840,18 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -7835,9 +7978,9 @@ "dev": true }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", + "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -7847,13 +7990,12 @@ "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -7868,10 +8010,6 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" } }, "node_modules/mocha/node_modules/ansi-styles": { @@ -7889,6 +8027,42 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/mocha/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -7940,37 +8114,24 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7992,15 +8153,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -8085,23 +8237,11 @@ } }, "node_modules/n12": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/n12/-/n12-0.4.0.tgz", - "integrity": "sha512-p/hj4zQ8d3pbbFLQuN1K9honUxiDDhueOWyFLw/XgBv+wZCE44bcLH4CIcsolOceJQduh4Jf7m/LfaTxyGmGtQ==", + "version": "1.8.22", + "resolved": "https://registry.npmjs.org/n12/-/n12-1.8.22.tgz", + "integrity": "sha512-nzPCOuLOIoUuninAMRXfrbkB7O9XkWS7iv7fzDW1pRUaQhMpatj8iX55evwcNRWnm0UF29uuoHpwubYbsV7OGw==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8214,9 +8354,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -8256,9 +8396,9 @@ } }, "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, "dependencies": { "path-key": "^4.0.0" @@ -8336,15 +8476,16 @@ } }, "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", + "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "array.prototype.filter": "^1.0.3", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0" } }, "node_modules/object.values": { @@ -8605,13 +8746,12 @@ } }, "node_modules/pac-resolver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "dependencies": { "degenerator": "^5.0.0", - "ip": "^1.1.8", "netmask": "^2.0.2" }, "engines": { @@ -8726,14 +8866,20 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", "dev": true, "engines": { "node": "14 || >=16.14" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -8871,9 +9017,9 @@ } }, "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -9217,14 +9363,14 @@ } }, "node_modules/read-pkg-up": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", - "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.0.0.tgz", + "integrity": "sha512-jgmKiS//w2Zs+YbX039CorlkOp8FIVbSAN8r8GJHDsGlmNPXo+VeHkqAwCiQVTTx5/LwLZTcEw59z3DvcLbr0g==", "dev": true, "dependencies": { "find-up": "^6.3.0", - "read-pkg": "^8.1.0", - "type-fest": "^4.2.0" + "read-pkg": "^8.0.0", + "type-fest": "^3.12.0" }, "engines": { "node": ">=16" @@ -9304,12 +9450,12 @@ } }, "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz", - "integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { - "node": ">=16" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9328,9 +9474,9 @@ } }, "node_modules/read-pkg/node_modules/type-fest": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz", - "integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz", + "integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==", "dev": true, "engines": { "node": ">=16" @@ -9426,9 +9572,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true }, "node_modules/regenerator-transform": { @@ -9441,14 +9587,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -9641,9 +9788,9 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, "node_modules/rgb2hex": { @@ -9729,19 +9876,19 @@ } }, "node_modules/safaridriver": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.0.tgz", - "integrity": "sha512-azzzIP3gR1TB9bVPv7QO4Zjw0rR1BWEU/s2aFdUMN48gxDjxEB13grAEuXDmkKPgE74cObymDxmAmZnL3clj4w==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", + "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", "dev": true }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -9773,15 +9920,18 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9838,15 +9988,17 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dev": true, "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -9906,14 +10058,18 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9979,16 +10135,16 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "dev": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, @@ -10006,12 +10162,6 @@ "node": ">= 14" } }, - "node_modules/socks/node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -10042,9 +10192,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -10058,9 +10208,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "node_modules/split2": { @@ -10072,6 +10222,12 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10103,13 +10259,16 @@ } }, "node_modules/streamx": { - "version": "2.15.5", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz", - "integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.0.tgz", + "integrity": "sha512-a7Fi0PoUeusrUcMS4+HxivnZqYsw2MFEP841TIyLxTcEIucHcJsk+0ARcq3tGq1xDn+xK7sKHetvfMzI1/CzMA==", "dev": true, "dependencies": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { @@ -10355,9 +10514,9 @@ } }, "node_modules/tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "dependencies": { "b4a": "^1.6.4", @@ -10426,9 +10585,9 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -10480,14 +10639,14 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", + "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -10512,16 +10671,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.1.tgz", + "integrity": "sha512-tcqKMrTRXjqvHN9S3553NPCaGL0VPgFI92lXszmrE8DMhiDPLBYLlvo8Uu4WZAAX/aGqp/T1sbA4ph8EWjDF9Q==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.7", "for-each": "^0.3.3", + "gopd": "^1.0.1", "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -10545,9 +10705,9 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10653,12 +10813,12 @@ } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/unzipper": { @@ -10935,27 +11095,27 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/webdriver": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.12.tgz", - "integrity": "sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.32.2.tgz", + "integrity": "sha512-uyCT2QzCqoz+EsMLTApG5/+RvHJR9MVbdEnjMoxpJDt+IeZCG2Vy/Gq9oNgNQfpxrvZme/EY+PtBsltZi7BAyg==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.24.12", - "@wdio/logger": "8.24.12", - "@wdio/protocols": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@wdio/config": "8.32.2", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.32.0", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "deepmerge-ts": "^5.1.0", "got": "^12.6.1", "ky": "^0.33.0", @@ -10966,9 +11126,9 @@ } }, "node_modules/webdriver/node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -10987,23 +11147,23 @@ } }, "node_modules/webdriverio": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.12.tgz", - "integrity": "sha512-Ddu0NNRMVkTzRzqvm3m0wt2eLUn+Plz2Cj+1QXDnVpddYJvk9J3elZC2hqNyscEtecQ+h2y3r36OcJqkl9jPag==", + "version": "8.32.2", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.32.2.tgz", + "integrity": "sha512-Z0Wc/dHFfWGWJZpaQ8u910/LG0E9EIVTO7J5yjqWx2XtXz2LzQMxYwNRnvNLhY/1tI4y/cZxI6kFMWr8wD2TtA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.24.12", - "@wdio/logger": "8.24.12", - "@wdio/protocols": "8.24.12", + "@wdio/config": "8.32.2", + "@wdio/logger": "8.28.0", + "@wdio/protocols": "8.32.0", "@wdio/repl": "8.24.12", - "@wdio/types": "8.24.12", - "@wdio/utils": "8.24.12", + "@wdio/types": "8.32.2", + "@wdio/utils": "8.32.2", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1233178", + "devtools-protocol": "^0.0.1261483", "grapheme-splitter": "^1.0.2", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", @@ -11015,7 +11175,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.24.12" + "webdriver": "8.32.2" }, "engines": { "node": "^16.13 || >=18" @@ -11101,16 +11261,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.1" }, "engines": { "node": ">= 0.4" From 352e95211fcc46e2c578b722393def8acda36cac Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Mon, 19 Feb 2024 14:37:41 +0000 Subject: [PATCH 378/567] Schemas v4.2.0 (#1320) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index b913b7c658..51be872eb3 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.1.0 +v4.2.0 From fd8ba6157c0b774b261c651e9ab22e7f613b4e8f Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 28 Feb 2024 08:30:05 +0000 Subject: [PATCH 379/567] Support piping repeating supplementary data outside a repeat (#1318) --- app/data_models/supplementary_data_store.py | 24 +++++++-- schemas/test/en/test_supplementary_data.json | 45 +++++++++++++++- tests/app/conftest.py | 21 ++++++++ .../test_value_source_resolver.py | 54 ++++++++++++------- .../supplementary_data.spec.js | 7 ++- 5 files changed, 125 insertions(+), 26 deletions(-) diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index f8b740ffca..a162e31f33 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -115,10 +115,28 @@ def get_data( it would be get_data(identifier="some_list", selectors=["identifier"], list_item_id=list_item_id-1) """ if self.is_data_repeating(identifier) and not list_item_id: - raise InvalidSupplementaryDataSelector( - f"Cannot reference items from `{identifier}` outside a repeat" - ) + values = [] + for _list_item_id in self.list_lookup.get(identifier, {}).values(): + value = self._resolve_value( + identifier=identifier, + selectors=selectors, + list_item_id=_list_item_id, + ) + if value is not None: + values.append(value) + return values + + return self._resolve_value( + identifier=identifier, selectors=selectors, list_item_id=list_item_id + ) + def _resolve_value( + self, + *, + identifier: str, + selectors: Iterable[str] | None, + list_item_id: str | None, + ) -> dict | str | list | None: value = self._data_map.get((identifier, list_item_id)) # for nested data, index with each selector, or return None if there is no data to index for selector in selectors or []: diff --git a/schemas/test/en/test_supplementary_data.json b/schemas/test/en/test_supplementary_data.json index 03291f04d9..8b541a4e09 100644 --- a/schemas/test/en/test_supplementary_data.json +++ b/schemas/test/en/test_supplementary_data.json @@ -58,11 +58,52 @@ "contents": [ { "title": "You have successfully loaded Supplementary data", - "description": "Press continue to proceed to the introduction", + "description": { + "text": "List of products: {products}", + "placeholders": [ + { + "placeholder": "products", + "transforms": [ + { + "transform": "format_list", + "arguments": { + "list_to_format": { + "source": "supplementary_data", + "identifier": "products", + "selectors": ["name"] + } + } + } + ] + } + ] + }, "guidance": { "contents": [ { - "description": "The purpose of this block, is to test that supplementary data loads successfully, separate to using the supplementary data" + "description": { + "text": "The purpose of this block, is to test that supplementary data loads successfully, separate to using the supplementary data. The surnames of the employees are: {surnames}.", + "placeholders": [ + { + "placeholder": "surnames", + "transforms": [ + { + "transform": "concatenate_list", + "arguments": { + "list_to_concatenate": [ + { + "source": "supplementary_data", + "identifier": "employees", + "selectors": ["personal_details", "surname"] + } + ], + "delimiter": ", " + } + } + ] + } + ] + } } ] } diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 1ddb2999d3..e849d59867 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -387,6 +387,17 @@ def supplementary_data_list_mappings(): } +@pytest.fixture +def supplementary_data_list_mappings_extra_item(): + return { + "products": [ + SupplementaryDataListMapping(identifier=89929001, list_item_id="item-1"), + SupplementaryDataListMapping(identifier="201630601", list_item_id="item-2"), + SupplementaryDataListMapping(identifier="103219277", list_item_id="item-3"), + ], + } + + @pytest.fixture def supplementary_data_employee_list_mappings(): return { @@ -417,6 +428,16 @@ def supplementary_data_store_with_data( ) +@pytest.fixture +def supplementary_data_store_with_data_extra_item( + supplementary_data, supplementary_data_list_mappings_extra_item +): + return SupplementaryDataStore( + supplementary_data=supplementary_data, + list_mappings=supplementary_data_list_mappings_extra_item, + ) + + @pytest.fixture def supplementary_data_store_with_employees( supplementary_data_with_employees, diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index 7bb2975d3c..58e9319db9 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1131,6 +1131,21 @@ def test_supplementary_data_value_source_non_list_items( {"identifier": "products", "selectors": ["value_sales", "answer_code"]}, "201630601", ), + ( + None, + {"identifier": "products", "selectors": ["name"]}, + ["Articles and equipment for sports or outdoor games", "Other Minerals"], + ), + ( + None, + {"identifier": "products", "selectors": ["value_sales", "answer_code"]}, + ["89929001", "201630601"], + ), + ( + None, + {"identifier": "products", "selectors": ["non_existing_optional_key"]}, + [], + ), ], ) def test_supplementary_data_value_source_list_items( @@ -1165,43 +1180,42 @@ def test_supplementary_data_value_source_list_items( ) -def test_supplementary_data_invalid_selector_raises_exception( - supplementary_data_store_with_data, +def test_supplementary_data_value_source_list_items_value_missing_excluded( + supplementary_data_store_with_data_extra_item, ): + list_store = ListStore([{"name": "products", "items": get_list_items(3)}]) location = Location( section_id="section", block_id="block-id", + list_name="products", + list_item_id=None, ) value_source_resolver = get_value_source_resolver( data_stores=DataStores( - supplementary_data_store=supplementary_data_store_with_data + supplementary_data_store=supplementary_data_store_with_data_extra_item, + list_store=list_store, ), location=location, + list_item_id=None, ) - with pytest.raises(InvalidSupplementaryDataSelector) as e: - value_source_resolver.resolve( - { - "source": "supplementary_data", - "identifier": "guidance", - "selectors": ["invalid"], - } - ) - - assert e.value.args[0] == "Cannot use the selector `invalid` on non-nested data" + assert value_source_resolver.resolve( + { + "source": "supplementary_data", + **{"identifier": "products", "selectors": ["name"]}, + } + ) == ["Articles and equipment for sports or outdoor games", "Other Minerals"] -def test_supplementary_data_list_item_outside_repeating_section_raises_exception( +def test_supplementary_data_invalid_selector_raises_exception( supplementary_data_store_with_data, ): - list_store = ListStore([{"name": "products", "items": get_list_items(2)}]) location = Location( section_id="section", block_id="block-id", ) value_source_resolver = get_value_source_resolver( data_stores=DataStores( - supplementary_data_store=supplementary_data_store_with_data, - list_store=list_store, + supplementary_data_store=supplementary_data_store_with_data ), location=location, ) @@ -1209,9 +1223,9 @@ def test_supplementary_data_list_item_outside_repeating_section_raises_exception value_source_resolver.resolve( { "source": "supplementary_data", - "identifier": "products", - "selectors": ["name"], + "identifier": "guidance", + "selectors": ["invalid"], } ) - assert e.value.args[0] == "Cannot reference items from `products` outside a repeat" + assert e.value.args[0] == "Cannot use the selector `invalid` on non-nested data" diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 671207d24d..da0eaae74f 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -46,8 +46,13 @@ describe("Using supplementary data", () => { responseId, }); }); + it("Given I launch a survey using supplementary data, When I am outside a repeating section, Then I am able to see the list of items relating to a given supplementary data list item on the page", async () => { + await expect(await $("#main-content #guidance-1").getText()).toContain("The surnames of the employees are: Potter, Kent."); + await expect(await $$("#main-content li")[0].getText()).toBe("Articles and equipment for sports or outdoor games"); + await expect(await $$("#main-content li")[1].getText()).toBe("Kitchen Equipment"); + }); - it("Given I launch a survey using supplementary data, When I begin the introduction block, Then I see the supplementary data piped in", async () => { + it("Given I progress through the interstitial block, When I begin the introduction block, Then I see the supplementary data piped in", async () => { await click(LoadedSuccessfullyBlockPage.submit()); await $(IntroductionBlockPage.acceptCookies()).click(); await expect(await $(IntroductionBlockPage.businessDetailsContent()).getText()).toContain("You are completing this survey for Tesco"); From 2f539a5dce966f98a1dca709c2372fdc8fe4e99f Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 28 Feb 2024 13:37:26 +0000 Subject: [PATCH 380/567] Schemas v4.3.0 (#1325) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 51be872eb3..9ca2398277 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.2.0 +v4.3.0 From c5fdfe3b103b614eb8c12d7bdf959e15ae4cf845 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:42:23 +0000 Subject: [PATCH 381/567] Runner test script improperly generating survey metadata (#1302) --- tests/integration/create_token.py | 19 ++- tests/integration/session/test_login.py | 44 ++++-- tests/integration/test_create_token.py | 201 ++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 18 deletions(-) create mode 100644 tests/integration/test_create_token.py diff --git a/tests/integration/create_token.py b/tests/integration/create_token.py index 159e9cb133..1d153339fc 100644 --- a/tests/integration/create_token.py +++ b/tests/integration/create_token.py @@ -1,5 +1,5 @@ -import time from copy import deepcopy +from time import time from uuid import uuid4 from sdc.crypto.encrypter import encrypt @@ -94,6 +94,11 @@ } +def populate_with_extra_payload_items(extra_payload, payload): + for key, value in extra_payload.items(): + payload[key] = value + + class TokenGenerator: def __init__(self, key_store, upstream_kid, sr_public_kid): self._key_store = key_store @@ -109,7 +114,7 @@ def _get_payload_with_params( payload=PAYLOAD, **extra_payload, ): # pylint: disable=dangerous-default-value - payload_vars = payload.copy() + payload_vars = deepcopy(payload) payload_vars["tx_id"] = str(uuid4()) if schema_name: payload_vars["schema_name"] = schema_name @@ -118,14 +123,16 @@ def _get_payload_with_params( if cir_instrument_id: payload_vars["cir_instrument_id"] = cir_instrument_id - payload_vars["iat"] = time.time() + payload_vars["iat"] = time() payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now payload_vars["jti"] = str(uuid4()) payload_vars["case_id"] = str(uuid4()) payload_vars["response_expires_at"] = get_response_expires_at() - for key, value in extra_payload.items(): - payload_vars[key] = value + if data := payload_vars.get("survey_metadata"): + populate_with_extra_payload_items(extra_payload, data.get("data")) + else: + populate_with_extra_payload_items(extra_payload, payload_vars) return payload_vars @@ -147,7 +154,7 @@ def create_token_v2(self, schema_name, theme="default", **extra_payload): return self.generate_token(payload) def create_supplementary_data_token(self, schema_name, **extra_payload): - payload = deepcopy(PAYLOAD_V2_SUPPLEMENTARY_DATA) + payload = PAYLOAD_V2_SUPPLEMENTARY_DATA # iterate over a copy so items can be deleted for key, value in list(extra_payload.items()): diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index 8a2d2737d5..469790ed09 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -151,6 +151,16 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() + def test_login_with_invalid_questionnaire_claims_should_be_forbidden_v2_get(self): + # flag_1 should be a boolean + token = self.token_generator.create_token_v2( + "test_metadata_routing", flag_1=123 + ) + + self.get(url=f"/session?token={token}") + + self.assertStatusForbidden() + def test_login_with_invalid_version_should_be_forbidden(self): token = self.token_generator.create_token_invalid_version("test_checkbox") @@ -167,7 +177,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): ) # When - with HTTMock(self.schema_url_mock): + with HTTMock(self._schema_url_mock): self.get(url=f"/session?token={token}") self.assertStatusOK() @@ -182,7 +192,7 @@ def test_login_token_with_incorrect_schema_url_results_in_500(self): ) # When - with HTTMock(self.schema_url_mock_500): + with HTTMock(self._schema_url_mock_500): self.get(url=f"/session?token={token}") # Then @@ -190,7 +200,7 @@ def test_login_token_with_incorrect_schema_url_results_in_500(self): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") - def schema_url_mock(_url, _request): + def _schema_url_mock(_url, _request): schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] with open(schema_path, encoding="utf8") as json_data: @@ -198,7 +208,7 @@ def schema_url_mock(_url, _request): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def schema_url_mock_500(_url, _request): + def _schema_url_mock_500(_url, _request): return response(500) @@ -287,6 +297,16 @@ def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): self.assertStatusForbidden() + def test_login_with_invalid_questionnaire_claims_should_be_forbidden_v2_post(self): + # flag_1 should be a boolean + token = self.token_generator.create_token_v2( + "test_metadata_routing", flag_1=123 + ) + + self.get(url=f"/session?token={token}") + + self.assertStatusForbidden() + def test_v2_business_login_with_invalid_questionnaire_claims_should_be_forbidden( self, ): @@ -330,7 +350,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): ) # When - with HTTMock(self.schema_url_mock): + with HTTMock(self._schema_url_mock): self.post(url=f"/session?token={token}") self.assertStatusOK() @@ -345,7 +365,7 @@ def test_login_token_with_incorrect_schema_url_results_in_404(self): ) # When - with HTTMock(self.schema_url_mock_500): + with HTTMock(self._schema_url_mock_500): self.post(url=f"/session?token={token}") # Then @@ -368,7 +388,7 @@ def test_login_token_with_cir_instrument_id_should_redirect_to_survey(self): ) # When - with HTTMock(self.cir_url_mock): + with HTTMock(self._cir_url_mock): self.post(url=f"/session?token={token}") # Then @@ -384,7 +404,7 @@ def test_login_token_with_invalid_cir_instrument_id_results_in_500(self): ) # When - with HTTMock(self.cir_url_mock_500): + with HTTMock(self._cir_url_mock_500): self.post(url=f"/session?token={token}") # Then @@ -392,7 +412,7 @@ def test_login_token_with_invalid_cir_instrument_id_results_in_500(self): @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema") - def schema_url_mock(_url, _request): + def _schema_url_mock(_url, _request): schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] with open(schema_path, encoding="utf8") as json_data: @@ -403,7 +423,7 @@ def schema_url_mock(_url, _request): path=CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, query="guid=f0519981-426c-8b93-75c0-bfc40c66fe25", ) - def cir_url_mock(_url, _request): + def _cir_url_mock(_url, _request): schema_path = SCHEMA_PATH_MAP["test"]["en"]["test_textarea"] with open(schema_path, encoding="utf8") as json_data: @@ -414,10 +434,10 @@ def cir_url_mock(_url, _request): path=CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, query="guid=a0df1208-dff5-4a3d-b35d-f9620c4a48ef", ) - def cir_url_mock_500(_url, _request): + def _cir_url_mock_500(_url, _request): return response(500) @staticmethod @urlmatch(netloc=r"eq-survey-register", path=r"\/my-test-schema-not-found") - def schema_url_mock_500(_url, _request): + def _schema_url_mock_500(_url, _request): return response(500) diff --git a/tests/integration/test_create_token.py b/tests/integration/test_create_token.py new file mode 100644 index 0000000000..643acbd110 --- /dev/null +++ b/tests/integration/test_create_token.py @@ -0,0 +1,201 @@ +from unittest import mock + +from app.authentication.authenticator import decrypt_token +from tests.integration.app_context_test_case import AppContextTestCase +from tests.integration.create_token import ( + PAYLOAD_V2_BUSINESS, + PAYLOAD_V2_SOCIAL, + PAYLOAD_V2_SUPPLEMENTARY_DATA, +) +from tests.integration.integration_test_case import IntegrationTestCase + +EXPECTED_TOKEN_BUSINESS = { + "account_service_url": "http://upstream.url", + "case_id": "1001", + "collection_exercise_sid": "789", + "exp": 1709058195.091798, + "iat": 1709054595.091798, + "jti": "1001", + "language_code": "en", + "response_expires_at": "2024-02-28T09:59:43.109276+00:00", + "response_id": "1234567890123456", + "roles": [], + "schema_name": "test_metadata_routing.json", + "survey_metadata": { + "data": { + "display_address": "68 Abingdon Road, Goathill", + "employment_date": "1983-06-02", + "flag_1": 123, + "link": "https://example.com", + "period_id": "202402", + "period_str": "April 2016", + "ref_p_end_date": "2016-04-30", + "ref_p_start_date": "2016-04-01", + "ru_name": "Integration Testing", + "ru_ref": "12345678901A", + "trad_as": "Integration Tests", + "user_id": "integration-test", + } + }, + "tx_id": "1001", + "version": "v2", +} +EXPECTED_TOKEN_SOCIAL = { + "account_service_url": "http://upstream.url", + "case_id": "1001", + "collection_exercise_sid": "789", + "exp": 1709058195.091798, + "iat": 1709054595.091798, + "jti": "1001", + "language_code": "en", + "response_expires_at": "2024-02-28T09:59:43.109276+00:00", + "response_id": "1234567890123456", + "roles": [], + "schema_name": "social_demo.json", + "survey_metadata": { + "data": { + "case_ref": "1000000000000001", + "date": "2016-05-12", + "flag_1": True, + "qid": PAYLOAD_V2_SOCIAL["survey_metadata"]["data"]["qid"], + "user_id": "64389274239", + }, + "receipting_keys": ["qid"], + }, + "tx_id": "1001", + "version": "v2", +} + + +class TestCreateToken(IntegrationTestCase, AppContextTestCase): + """ + The purpose of this test class is to test the creation of a token (from create_token.py) to ensure + metadata in a decrypted token is nested/found in the correct level. + """ + + # Patches are used since the values "uuid4", "time" and "get_response_expires_at" return are dynamic + @mock.patch("tests.integration.create_token.uuid4") + @mock.patch( + "tests.integration.create_token.time", + ) + @mock.patch( + "tests.integration.create_token.get_response_expires_at", + ) + def test_payload_content_and_structure_from_token( + self, mock_response_expiry_time, mock_time, mock_uuid + ): + mock_uuid.return_value = 1001 + mock_time.return_value = 1709054595.091798 + mock_response_expiry_time.return_value = "2024-02-28T09:59:43.109276+00:00" + test_parameters = [ + { + "schema": "test_metadata_routing.json", + "theme": "default", + "additional_payload": { + "flag_1": 123, + "period_id": "202402", + "link": "https://example.com", + }, + "payload": PAYLOAD_V2_BUSINESS, + "expected_token": EXPECTED_TOKEN_BUSINESS, + }, + { + "schema": "social_demo.json", + "theme": "social", + "additional_payload": { + "flag_1": True, + "user_id": "64389274239", + "date": "2016-05-12", + }, + "payload": PAYLOAD_V2_SOCIAL, + "expected_token": EXPECTED_TOKEN_SOCIAL, + }, + ] + for value in test_parameters: + with self.subTest(): + additional_payload = value["additional_payload"] + token = self.token_generator.create_token_v2( + schema_name=value["schema"], + theme=value["theme"], + **additional_payload, + ) + + with self.test_app.app_context(): + decrypted_token = decrypt_token(token) + self.assertEqual(value["expected_token"], decrypted_token) + + def test_uuid_consistent_after_decryption(self): + token = self.token_generator.create_token_v2( + "test_checkbox.json", theme="social", value="Dummy Text" + ) + with self.test_app.app_context(): + decrypted_token = decrypt_token(token) + assert decrypted_token["survey_metadata"] == { + "data": { + "case_ref": "1000000000000001", + "qid": PAYLOAD_V2_SOCIAL["survey_metadata"]["data"]["qid"], + "value": "Dummy Text", + }, + "receipting_keys": ["qid"], + } + + def test_sds_metadata_included_in_token(self): + token = self.token_generator.create_supplementary_data_token( + "test_checkbox.json" + ) + with self.test_app.app_context(): + decrypted_token = decrypt_token(token) + self.assertEqual( + decrypted_token, PAYLOAD_V2_SUPPLEMENTARY_DATA | decrypted_token + ) + + def test_additional_payload_added_in_token(self): + token = self.token_generator.create_supplementary_data_token( + "test_checkbox.json", flag_1=True + ) + with self.test_app.app_context(): + decrypted_token = decrypt_token(token) + assert decrypted_token["survey_metadata"] == { + "data": { + "display_address": "68 Abingdon Road, Goathill", + "employment_date": "1983-06-02", + "flag_1": True, + "period_id": "201604", + "period_str": "April 2016", + "ref_p_end_date": "2016-04-30", + "ref_p_start_date": "2016-04-01", + "ru_name": "Integration Testing", + "ru_ref": "12345678901A", + "sds_dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "survey_id": "123", + "trad_as": "Integration Tests", + "user_id": "integration-test", + } + } + + def test_metadata_is_removed_from_token(self): + metadata_tokens = [ + { + "token": self.token_generator.create_token_without_jti( + "test_number.json" + ), + "removed_metadata": "jti", + }, + { + "token": self.token_generator.create_token_without_case_id( + "test_numbers.json" + ), + "removed_metadata": "case_id", + }, + { + "token": self.token_generator.create_token_without_trad_as( + "test_numbers.json" + ), + "removed_metadata": "trad_as", + }, + ] + for values in metadata_tokens: + with self.subTest(): + with self.test_app.app_context(): + decrypted_token = decrypt_token(values["token"]) + self.assertNotIn(values["removed_metadata"], decrypted_token) From e36e52d8e088f67ae0f0710d70629ad2729ec375 Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:11:05 +0000 Subject: [PATCH 382/567] Schemas v4.3.1 (#1326) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 9ca2398277..6062a5ebae 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.3.0 +v4.3.1 From ba98075bdbe1c11740c07b1066ff064ce3b175d2 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:25:04 +0000 Subject: [PATCH 383/567] Schemas v4.4.0 (#1328) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6062a5ebae..37c93beffd 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.3.1 +v4.4.0 From 9b954f50a51f9c18e7fbb1838949001f05a0bd59 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:46:29 +0000 Subject: [PATCH 384/567] Schemas v4.4.1 (#1331) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 37c93beffd..a4022d8ae8 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.4.0 +v4.4.1 From b1e8d85646734e54b5c079ed515b33481a61fe3a Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:43:30 +0000 Subject: [PATCH 385/567] Update Runner Python Packages (#1322) --- Pipfile | 2 +- Pipfile.lock | 5495 ++++++++--------- app/authentication/authenticator.py | 8 +- app/data_models/answer.py | 19 +- app/data_models/list_store.py | 6 +- app/forms/field_handlers/field_handler.py | 6 +- app/forms/validators.py | 8 +- app/helpers/url_safe_serializer.py | 3 +- app/jinja_filters.py | 10 +- app/questionnaire/placeholder_parser.py | 4 +- app/questionnaire/questionnaire_schema.py | 36 +- .../questionnaire_store_updater.py | 28 +- app/questionnaire/rules/operations_helper.py | 1 + app/questionnaire/rules/utils.py | 6 +- app/questionnaire/value_source_resolver.py | 8 +- app/routes/errors.py | 15 +- app/setup.py | 7 +- app/storage/dynamodb.py | 6 +- app/translations/messages.pot | 26 +- .../contexts/calculated_summary_context.py | 20 +- .../grand_calculated_summary_context.py | 8 +- app/views/handlers/content.py | 16 +- app/views/handlers/individual_response.py | 6 +- app/views/handlers/question.py | 6 +- .../relationships/relationship_collector.py | 12 +- setup.cfg | 2 +- tests/app/conftest.py | 6 +- .../test_questionnaire_store_updater.py | 8 +- tests/app/storage/conftest.py | 8 +- tests/app/test_setup.py | 15 + .../test_calculated_summary_context.py | 8 +- tests/integration/test_app_create.py | 12 +- 32 files changed, 2928 insertions(+), 2893 deletions(-) diff --git a/Pipfile b/Pipfile index 0d23dbb97d..1edd8c5a6a 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,7 @@ verify_ssl = true mock = "*" pytest-cov = "*" jsonschema = "*" -pylint = "*" +pylint = "<3.0.0" pylint-mccabe = "*" pylint-quotes = "*" pylint-absolute-imports = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 3d44cfb4e5..ae499cb591 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,2753 +1,2748 @@ { - "_meta": { - "hash": { - "sha256": "fc45f58bb88b36d9a6136dce2a43825ddb10145475d5110804526ebe2eb4dc05" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.11" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "babel": { - "hashes": [ - "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900", - "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed" - ], - "markers": "python_version >= '3.7'", - "version": "==2.13.1" - }, - "blinker": { - "hashes": [ - "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", - "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.0" - }, - "boto3": { - "hashes": [ - "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", - "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "botocore": { - "hashes": [ - "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", - "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" - ], - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "brotli": { - "hashes": [ - "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", - "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", - "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", - "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", - "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", - "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", - "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", - "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", - "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", - "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", - "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", - "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", - "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", - "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", - "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", - "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", - "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", - "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", - "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", - "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", - "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", - "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", - "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", - "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", - "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", - "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", - "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", - "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", - "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", - "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", - "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", - "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", - "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", - "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", - "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", - "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", - "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", - "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", - "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", - "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", - "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", - "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", - "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", - "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", - "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", - "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", - "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", - "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", - "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", - "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", - "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", - "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", - "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", - "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", - "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", - "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", - "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", - "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", - "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", - "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", - "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", - "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", - "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", - "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", - "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", - "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", - "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", - "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", - "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", - "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", - "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", - "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", - "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", - "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", - "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", - "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", - "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", - "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", - "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", - "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", - "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", - "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", - "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" - ], - "markers": "platform_python_implementation != 'PyPy'", - "version": "==1.1.0" - }, - "cachetools": { - "hashes": [ - "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2", - "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.2" - }, - "certifi": { - "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==0.4.6" - }, - "coloredlogs": { - "hashes": [ - "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", - "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==15.0.1" - }, - "cryptography": { - "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.7" - }, - "deprecated": { - "hashes": [ - "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", - "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.14" - }, - "dnspython": { - "hashes": [ - "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8", - "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984" - ], - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==2.4.2" - }, - "email-validator": { - "hashes": [ - "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44", - "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.1.0.post1" - }, - "flask": { - "hashes": [ - "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", - "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "flask-babel": { - "hashes": [ - "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625", - "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593" - ], - "index": "pypi", - "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==4.0.0" - }, - "flask-compress": { - "hashes": [ - "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c", - "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee" - ], - "index": "pypi", - "version": "==1.14" - }, - "flask-login": { - "hashes": [ - "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", - "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==0.6.3" - }, - "flask-talisman": { - "hashes": [ - "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208", - "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff" - ], - "index": "pypi", - "version": "==1.1.0" - }, - "flask-wtf": { - "hashes": [ - "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695", - "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.2.1" - }, - "gevent": { - "hashes": [ - "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", - "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", - "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", - "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", - "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", - "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", - "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", - "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", - "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", - "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", - "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", - "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", - "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", - "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", - "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", - "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", - "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", - "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", - "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", - "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", - "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", - "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", - "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", - "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", - "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", - "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", - "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", - "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", - "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", - "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", - "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", - "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", - "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", - "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", - "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", - "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", - "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", - "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", - "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", - "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" - ], - "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", - "version": "==23.9.1" - }, - "google-api-core": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41", - "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952" - ], - "markers": "python_version >= '3.7'", - "version": "==2.14.0" - }, - "google-auth": { - "hashes": [ - "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3", - "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2" - ], - "markers": "python_version >= '3.7'", - "version": "==2.23.4" - }, - "google-cloud-core": { - "hashes": [ - "sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb", - "sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.3" - }, - "google-cloud-datastore": { - "hashes": [ - "sha256:369df0653128fe63f0fb1b5a1cd5a05edae49a805f79f9b639ca9d58833bc6b5", - "sha256:63b31b676dcb2786a650d23d324d8f886c4e14586afdd0cfe6e260a73f12448e" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.18.0" - }, - "google-cloud-pubsub": { - "hashes": [ - "sha256:32eb61fd4c1dc6c842f594d69d9afa80544e3b327aa640a164eb6fb0201eaf2d", - "sha256:f32144ad9ed32331a80a2f8379a3ca7526bbc01e7bd76de2e8ab52e492d21f50" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.18.4" - }, - "google-cloud-storage": { - "hashes": [ - "sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d", - "sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.13.0" - }, - "google-cloud-tasks": { - "hashes": [ - "sha256:3efb280e7a635f57860a06a346131c117042e820ad483afd256800fae1b4d701", - "sha256:801a671c6dbd2269179037e93490f87e2df59ddfcb59551128bab27e7dd09f94" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.14.2" - }, - "google-crc32c": { - "hashes": [ - "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", - "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", - "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", - "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", - "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", - "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", - "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", - "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", - "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", - "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", - "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", - "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", - "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", - "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", - "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", - "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", - "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", - "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", - "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", - "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", - "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", - "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", - "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", - "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", - "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", - "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", - "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", - "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", - "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", - "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", - "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", - "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", - "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", - "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", - "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", - "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", - "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", - "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", - "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", - "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", - "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", - "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", - "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", - "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", - "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", - "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", - "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", - "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", - "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", - "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", - "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", - "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", - "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", - "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", - "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", - "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", - "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", - "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", - "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", - "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", - "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", - "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", - "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", - "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", - "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", - "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", - "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", - "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" - ], - "markers": "python_version >= '3.7'", - "version": "==1.5.0" - }, - "google-resumable-media": { - "hashes": [ - "sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7", - "sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b" - ], - "markers": "python_version >= '3.7'", - "version": "==2.6.0" - }, - "googleapis-common-protos": { - "extras": [ - "grpc" - ], - "hashes": [ - "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0", - "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b" - ], - "markers": "python_version >= '3.7'", - "version": "==1.61.0" - }, - "greenlet": { - "hashes": [ - "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", - "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", - "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", - "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", - "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", - "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", - "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", - "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", - "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", - "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", - "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", - "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", - "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", - "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", - "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", - "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", - "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", - "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", - "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", - "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", - "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", - "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", - "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", - "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", - "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", - "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", - "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", - "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", - "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", - "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", - "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", - "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", - "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", - "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", - "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", - "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", - "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", - "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", - "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", - "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", - "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", - "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", - "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", - "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", - "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", - "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", - "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", - "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", - "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", - "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", - "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", - "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", - "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", - "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", - "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", - "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", - "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" - ], - "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", - "version": "==3.0.1" - }, - "grpc-google-iam-v1": { - "hashes": [ - "sha256:009197a7f1eaaa22149c96e5e054ac5934ba7241974e92663d8d3528a21203d1", - "sha256:834da89f4c4a2abbe842a793ed20fc6d9a77011ef2626755b1b89116fb9596d7" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.7" - }, - "grpcio": { - "hashes": [ - "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8", - "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea", - "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69", - "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb", - "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07", - "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d", - "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b", - "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354", - "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79", - "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59", - "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86", - "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7", - "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e", - "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281", - "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069", - "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb", - "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d", - "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3", - "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25", - "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c", - "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a", - "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80", - "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf", - "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118", - "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53", - "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e", - "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9", - "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556", - "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b", - "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e", - "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994", - "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc", - "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a", - "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d", - "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e", - "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718", - "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4", - "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab", - "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552", - "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3", - "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391", - "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13", - "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7", - "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3", - "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025", - "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8", - "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6", - "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473", - "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94", - "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0", - "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9", - "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d", - "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881", - "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.59.3" - }, - "grpcio-status": { - "hashes": [ - "sha256:2fd2eb39ca4e9afb3c874c0878ff75b258db0b7dcc25570fc521f16ae0ab942a", - "sha256:65c394ba43380d6bdf8c04c61efc493104b5535552aed35817a1b4dc66598a1f" - ], - "markers": "python_version >= '3.6'", - "version": "==1.59.3" - }, - "gunicorn": { - "hashes": [ - "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", - "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" - ], - "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==21.2.0" - }, - "htmlmin": { - "hashes": [ - "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178" - ], - "index": "pypi", - "version": "==0.1.12" - }, - "humanfriendly": { - "hashes": [ - "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", - "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==10.0" - }, - "humanize": { - "hashes": [ - "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", - "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.9.0" - }, - "idna": { - "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" - ], - "markers": "python_version >= '3.5'", - "version": "==3.6" - }, - "itsdangerous": { - "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "jinja2": { - "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.1.3" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "jsonpointer": { - "hashes": [ - "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", - "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" - ], - "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==2.4" - }, - "jwcrypto": { - "hashes": [ - "sha256:48bb9bf433777136253579e52b75ffe0f9a4a721d133d01f45a0b91ed5f4f1ae" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==1.5.1" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "marshmallow": { - "hashes": [ - "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", - "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.20.1" - }, - "ordered-set": { - "hashes": [ - "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", - "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pdfkit": { - "hashes": [ - "sha256:992f821e1e18fc8a0e701ecae24b51a2d598296a180caee0a24c0af181da02a9", - "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f", - "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e" - ], - "index": "pypi", - "version": "==1.0.0" - }, - "pika": { - "hashes": [ - "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", - "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.3.2" - }, - "proto-plus": { - "hashes": [ - "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df", - "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b" - ], - "markers": "python_version >= '3.6'", - "version": "==1.22.3" - }, - "protobuf": { - "hashes": [ - "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd", - "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb", - "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0", - "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7", - "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b", - "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2", - "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510", - "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6", - "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd", - "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10", - "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7" - ], - "markers": "python_version >= '3.8'", - "version": "==4.25.1" - }, - "pyasn1": { - "hashes": [ - "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58", - "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.5.1" - }, - "pyasn1-modules": { - "hashes": [ - "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", - "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==0.3.0" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-snappy": { - "hashes": [ - "sha256:03bb511380fca2a13325b6f16fe8234c8e12da9660f0258cd45d9a02ffc916af", - "sha256:0bdb6942180660bda7f7d01f4c0def3cfc72b1c6d99aad964801775a3e379aba", - "sha256:0d489b50f49433494160c45048fe806de6b3aeab0586e497ebd22a0bab56e427", - "sha256:1a993dc8aadd901915a510fe6af5f20ae4256f527040066c22a154db8946751f", - "sha256:1d029f7051ec1bbeaa3e03030b6d8ed47ceb69cae9016f493c802a08af54e026", - "sha256:277757d5dad4e239dc1417438a0871b65b1b155beb108888e7438c27ffc6a8cc", - "sha256:2a7e528ab6e09c0d67dcb61a1730a292683e5ff9bb088950638d3170cf2a0a54", - "sha256:2aaaf618c68d8c9daebc23a20436bd01b09ee70d7fbf7072b7f38b06d2fab539", - "sha256:2be4f4550acd484912441f5f1209ba611ac399aac9355fee73611b9a0d4f949c", - "sha256:39692bedbe0b717001a99915ac0eb2d9d0bad546440d392a2042b96d813eede1", - "sha256:3fb9a88a4dd6336488f3de67ce75816d0d796dce53c2c6e4d70e0b565633c7fd", - "sha256:4038019b1bcaadde726a57430718394076c5a21545ebc5badad2c045a09546cf", - "sha256:463fd340a499d47b26ca42d2f36a639188738f6e2098c6dbf80aef0e60f461e1", - "sha256:4d3cafdf454354a621c8ab7408e45aa4e9d5c0b943b61ff4815f71ca6bdf0130", - "sha256:4ec533a8c1f8df797bded662ec3e494d225b37855bb63eb0d75464a07947477c", - "sha256:530bfb9efebcc1aab8bb4ebcbd92b54477eed11f6cf499355e882970a6d3aa7d", - "sha256:546c1a7470ecbf6239101e9aff0f709b68ca0f0268b34d9023019a55baa1f7c6", - "sha256:5843feb914796b1f0405ccf31ea0fb51034ceb65a7588edfd5a8250cb369e3b2", - "sha256:586724a0276d7a6083a17259d0b51622e492289a9998848a1b01b6441ca12b2f", - "sha256:59e975be4206cc54d0a112ef72fa3970a57c2b1bcc2c97ed41d6df0ebe518228", - "sha256:5a453c45178d7864c1bdd6bfe0ee3ed2883f63b9ba2c9bb967c6b586bf763f96", - "sha256:5bb05c28298803a74add08ba496879242ef159c75bc86a5406fac0ffc7dd021b", - "sha256:5e973e637112391f05581f427659c05b30b6843bc522a65be35ac7b18ce3dedd", - "sha256:66c80e9b366012dbee262bb1869e4fc5ba8786cda85928481528bc4a72ec2ee8", - "sha256:6a7620404da966f637b9ce8d4d3d543d363223f7a12452a575189c5355fc2d25", - "sha256:6f8bf4708a11b47517baf962f9a02196478bbb10fdb9582add4aa1459fa82380", - "sha256:735cd4528c55dbe4516d6d2b403331a99fc304f8feded8ae887cf97b67d589bb", - "sha256:7778c224efc38a40d274da4eb82a04cac27aae20012372a7db3c4bbd8926c4d4", - "sha256:8277d1f6282463c40761f802b742f833f9f2449fcdbb20a96579aa05c8feb614", - "sha256:88b6ea78b83d2796f330b0af1b70cdd3965dbdab02d8ac293260ec2c8fe340ee", - "sha256:8c07220408d3268e8268c9351c5c08041bc6f8c6172e59d398b71020df108541", - "sha256:8d0c019ee7dcf2c60e240877107cddbd95a5b1081787579bf179938392d66480", - "sha256:90b0186516b7a101c14764b0c25931b741fb0102f21253eff67847b4742dfc72", - "sha256:9837ac1650cc68d22a3cf5f15fb62c6964747d16cecc8b22431f113d6e39555d", - "sha256:9eac51307c6a1a38d5f86ebabc26a889fddf20cbba7a116ccb54ba1446601d5b", - "sha256:9f0c0d88b84259f93c3aa46398680646f2c23e43394779758d9f739c34e15295", - "sha256:a0ad38bc98d0b0497a0b0dbc29409bcabfcecff4511ed7063403c86de16927bc", - "sha256:b265cde49774752aec9ca7f5d272e3f98718164afc85521622a8a5394158a2b5", - "sha256:b6a107ab06206acc5359d4c5632bd9b22d448702a79b3169b0c62e0fb808bb2a", - "sha256:b7f920eaf46ebf41bd26f9df51c160d40f9e00b7b48471c3438cb8d027f7fb9b", - "sha256:c20498bd712b6e31a4402e1d027a1cd64f6a4a0066a3fe3c7344475886d07fdf", - "sha256:cb18d9cd7b3f35a2f5af47bb8ed6a5bdbf4f3ddee37f3daade4ab7864c292f5b", - "sha256:cf5bb9254e1c38aacf253d510d3d9be631bba21f3d068b17672b38b5cbf2fff5", - "sha256:d017775851a778ec9cc32651c4464079d06d927303c2dde9ae9830ccf6fe94e1", - "sha256:dc96668d9c7cc656609764275c5f8da58ef56d89bdd6810f6923d36497468ff7", - "sha256:e066a0586833d610c4bbddba0be5ba0e3e4f8e0bc5bb6d82103d8f8fc47bb59a", - "sha256:e3a013895c64352b49d0d8e107a84f99631b16dbab156ded33ebf0becf56c8b2", - "sha256:eaf905a580f2747c4a474040a5063cd5e0cc3d1d2d6edb65f28196186493ad4a" - ], - "index": "pypi", - "version": "==0.6.1" - }, - "pytz": { - "hashes": [ - "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b", - "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7" - ], - "version": "==2023.3.post1" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "redis": { - "hashes": [ - "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", - "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.1" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "rsa": { - "hashes": [ - "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", - "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" - ], - "markers": "python_version >= '3.6' and python_version < '4'", - "version": "==4.9" - }, - "s3transfer": { - "hashes": [ - "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", - "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.0" - }, - "sdc-cryptography": { - "hashes": [ - "sha256:0abf35a298e2be51379f9867b701524a255357921651ff35ebfe1c6b2eb81f8d", - "sha256:80e5e9dae84830bbb072f807ca880d186790ece443fddce34dbee7bf2c271a7d" - ], - "index": "pypi", - "version": "==1.1.2" - }, - "setuptools": { - "hashes": [ - "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2", - "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6" - ], - "markers": "python_version >= '3.8'", - "version": "==69.0.2" - }, - "simplejson": { - "hashes": [ - "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137", - "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a", - "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae", - "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a", - "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba", - "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087", - "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b", - "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41", - "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d", - "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd", - "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d", - "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e", - "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a", - "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9", - "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6", - "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb", - "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664", - "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2", - "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b", - "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5", - "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835", - "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17", - "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69", - "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd", - "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6", - "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2", - "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2", - "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428", - "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f", - "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2", - "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f", - "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7", - "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f", - "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637", - "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3", - "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816", - "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565", - "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378", - "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0", - "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b", - "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693", - "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358", - "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9", - "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23", - "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf", - "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a", - "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832", - "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414", - "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f", - "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48", - "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad", - "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290", - "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3", - "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917", - "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402", - "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0", - "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc", - "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867", - "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f", - "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589", - "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb", - "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b", - "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3", - "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb", - "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c", - "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46", - "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672", - "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c", - "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e", - "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0", - "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b", - "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4", - "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4", - "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c", - "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c", - "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8", - "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b", - "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb", - "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80", - "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2", - "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374", - "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc", - "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50", - "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c", - "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f", - "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28", - "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5", - "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734", - "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a", - "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d", - "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4", - "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c", - "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13", - "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973", - "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a", - "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e", - "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff", - "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded" - ], - "index": "pypi", - "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.19.2" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "structlog": { - "hashes": [ - "sha256:16a167e87b9fa7fae9a972d5d12805ef90e04857a93eba479d4be3801a6a1482", - "sha256:334666b94707f89dbc4c81a22a8ccd34449f0201d5b1ee097a030b577fa8c858" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==23.2.0" - }, - "ua-parser": { - "hashes": [ - "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", - "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" - ], - "index": "pypi", - "version": "==0.18.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "uwsgi": { - "hashes": [ - "sha256:0cafda0c16f921db7fe42cfaf81b167cf884ee17350efbdd87d1ecece2d7de37" - ], - "index": "pypi", - "version": "==2.0.23" - }, - "werkzeug": { - "hashes": [ - "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", - "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.1" - }, - "wrapt": { - "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" - ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" - }, - "wtforms": { - "hashes": [ - "sha256:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620", - "sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0" - ], - "markers": "python_version >= '3.8'", - "version": "==3.1.1" - }, - "zope.event": { - "hashes": [ - "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", - "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" - ], - "markers": "python_version >= '3.7'", - "version": "==5.0" - }, - "zope.interface": { - "hashes": [ - "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", - "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", - "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", - "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", - "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", - "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", - "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", - "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", - "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", - "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", - "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", - "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", - "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", - "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", - "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", - "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", - "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", - "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", - "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", - "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", - "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", - "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", - "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", - "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", - "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", - "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", - "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", - "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", - "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", - "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", - "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", - "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", - "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", - "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", - "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", - "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" - ], - "markers": "python_version >= '3.7'", - "version": "==6.1" - } - }, - "develop": { - "astroid": { - "hashes": [ - "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", - "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a" - ], - "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.8" - }, - "attrs": { - "hashes": [ - "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" - ], - "markers": "python_version >= '3.7'", - "version": "==23.1.0" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", - "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" - ], - "index": "pypi", - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.2" - }, - "black": { - "hashes": [ - "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4", - "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b", - "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f", - "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07", - "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187", - "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6", - "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05", - "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06", - "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e", - "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5", - "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244", - "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f", - "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221", - "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055", - "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479", - "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394", - "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911", - "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==23.11.0" - }, - "blinker": { - "hashes": [ - "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", - "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.0" - }, - "boto3": { - "hashes": [ - "sha256:1fe5fa75ff0f0c29a6f55e818d149d33571731e692a7b785ded7a28ac832cae8", - "sha256:fa5aa92d16763cb906fb4a83d6eba887342202a980bea07862af5ba40827aa5a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "botocore": { - "hashes": [ - "sha256:c744b90980786c610dd9ad9c50cf2cdde3f1c4634b954a33613f6f8a1865a1de", - "sha256:d22d29916905e5f0670b91f07688e92b2c4a2075f9a474d6edbe7d22040d8fbf" - ], - "markers": "python_version >= '3.7'", - "version": "==1.33.1" - }, - "certifi": { - "hashes": [ - "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" - ], - "markers": "python_version >= '3.6'", - "version": "==2023.11.17" - }, - "cffi": { - "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" - ], - "markers": "python_version >= '3.8'", - "version": "==1.16.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" - }, - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "coverage": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1", - "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63", - "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9", - "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312", - "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3", - "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb", - "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25", - "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92", - "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda", - "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148", - "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6", - "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216", - "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a", - "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640", - "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836", - "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c", - "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f", - "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2", - "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901", - "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed", - "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a", - "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074", - "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc", - "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84", - "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083", - "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f", - "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c", - "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c", - "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637", - "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2", - "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82", - "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f", - "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce", - "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef", - "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f", - "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611", - "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c", - "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76", - "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9", - "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce", - "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9", - "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf", - "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf", - "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9", - "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6", - "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2", - "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a", - "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a", - "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf", - "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738", - "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a", - "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4" - ], - "markers": "python_version >= '3.8'", - "version": "==7.3.2" - }, - "cryptography": { - "hashes": [ - "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960", - "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a", - "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc", - "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a", - "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf", - "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1", - "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39", - "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406", - "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a", - "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a", - "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c", - "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be", - "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15", - "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2", - "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d", - "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157", - "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003", - "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248", - "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a", - "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec", - "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309", - "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7", - "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d" - ], - "markers": "python_version >= '3.7'", - "version": "==41.0.7" - }, - "dill": { - "hashes": [ - "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e", - "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03" - ], - "markers": "python_version >= '3.11'", - "version": "==0.3.7" - }, - "execnet": { - "hashes": [ - "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", - "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.2" - }, - "fakeredis": { - "hashes": [ - "sha256:69987928d719d1ae1665ae8ebb16199d22a5ebae0b7d0d0d6586fc3a1a67428c", - "sha256:c9baf3c7fd2ebf40db50db4c642c7c76b712b1eed25d91efcc175bba9bc40ca3" - ], - "index": "pypi", - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==2.20.0" - }, - "flake8": { - "hashes": [ - "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", - "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" - ], - "index": "pypi", - "markers": "python_full_version >= '3.8.1'", - "version": "==6.1.0" - }, - "flake8-datetimez": { - "hashes": [ - "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc", - "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==20.10.0" - }, - "flake8-debugger": { - "hashes": [ - "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf", - "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.2" - }, - "flake8-mock": { - "hashes": [ - "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867", - "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6" - ], - "index": "pypi", - "version": "==0.4" - }, - "flake8-print": { - "hashes": [ - "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9", - "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.0" - }, - "flake8-tuple": { - "hashes": [ - "sha256:8a1b42aab134ef4c3fef13c6a8f383363f158b19fbc165bd91aed9c51851a61d", - "sha256:d828cc8e461c50cacca116e9abb0c9e3be565e8451d3f5c00578c63670aae680" - ], - "index": "pypi", - "version": "==0.4.1" - }, - "flask": { - "hashes": [ - "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638", - "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, - "freezegun": { - "hashes": [ - "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446", - "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==1.2.2" - }, - "httmock": { - "hashes": [ - "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9", - "sha256:44eaf4bb59cc64cd6f5d8bf8700b46aa3097cc5651b9bc85c527dfbc71792f41" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "idna": { - "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" - ], - "markers": "python_version >= '3.5'", - "version": "==3.6" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, - "isort": { - "hashes": [ - "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504", - "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.12.0" - }, - "itsdangerous": { - "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "jinja2": { - "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.1.3" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "jsonschema": { - "hashes": [ - "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa", - "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.20.0" - }, - "jsonschema-specifications": { - "hashes": [ - "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca", - "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779" - ], - "markers": "python_version >= '3.8'", - "version": "==2023.11.1" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382", - "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82", - "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9", - "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494", - "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46", - "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30", - "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63", - "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4", - "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae", - "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be", - "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701", - "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd", - "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006", - "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a", - "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586", - "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8", - "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821", - "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07", - "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b", - "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171", - "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b", - "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2", - "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7", - "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4", - "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8", - "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e", - "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f", - "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda", - "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4", - "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e", - "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671", - "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11", - "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455", - "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734", - "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb", - "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59" - ], - "markers": "python_version >= '3.7'", - "version": "==1.9.0" - }, - "markupsafe": { - "hashes": [ - "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", - "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", - "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", - "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", - "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", - "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", - "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", - "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", - "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", - "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", - "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", - "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", - "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", - "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", - "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", - "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", - "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", - "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", - "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", - "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", - "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", - "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", - "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", - "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", - "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", - "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", - "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", - "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", - "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", - "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", - "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", - "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", - "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", - "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", - "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", - "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", - "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", - "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", - "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", - "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", - "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", - "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", - "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", - "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", - "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", - "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", - "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", - "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", - "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", - "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", - "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", - "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", - "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", - "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", - "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", - "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", - "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", - "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", - "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", - "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.1.3" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mock": { - "hashes": [ - "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", - "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==5.1.0" - }, - "moto": { - "hashes": [ - "sha256:5cf0736d1f43cb887498d00b00ae522774bfddb7db1f4994fedea65b290b9f0e", - "sha256:92595fe287474a31ac3ef847941ebb097e8ffb0c3d6c106e47cf573db06933b2" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.2.10" - }, - "mypy": { - "hashes": [ - "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", - "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", - "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82", - "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", - "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", - "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", - "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", - "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", - "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", - "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", - "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", - "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", - "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6", - "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", - "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", - "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7", - "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", - "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", - "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", - "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", - "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", - "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", - "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", - "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42", - "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", - "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", - "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.7.1" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "packaging": { - "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" - ], - "markers": "python_version >= '3.7'", - "version": "==23.2" - }, - "pathspec": { - "hashes": [ - "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", - "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" - ], - "markers": "python_version >= '3.7'", - "version": "==0.11.2" - }, - "pep8": { - "hashes": [ - "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee", - "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374" - ], - "index": "pypi", - "version": "==1.7.1" - }, - "platformdirs": { - "hashes": [ - "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b", - "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731" - ], - "markers": "python_version >= '3.7'", - "version": "==4.0.0" - }, - "pluggy": { - "hashes": [ - "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.0" - }, - "pycodestyle": { - "hashes": [ - "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", - "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" - ], - "markers": "python_version >= '3.8'", - "version": "==2.11.1" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pyflakes": { - "hashes": [ - "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", - "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" - ], - "markers": "python_version >= '3.8'", - "version": "==3.1.0" - }, - "pylint": { - "hashes": [ - "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", - "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad" - ], - "index": "pypi", - "markers": "python_full_version >= '3.7.2'", - "version": "==2.17.7" - }, - "pylint-absolute-imports": { - "hashes": [ - "sha256:e70d96abbad2fe0bdc3dbae05e1a1b7963dea5b7df4cb63e001f7248de46acd3" - ], - "index": "pypi", - "version": "==1.0.1" - }, - "pylint-mccabe": { - "hashes": [ - "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29" - ], - "index": "pypi", - "version": "==0.1.3" - }, - "pylint-quotes": { - "hashes": [ - "sha256:2d6bb3fa8a1a85af3af8a0ca875a719ac5bcdb735c45756284699d809c109c95", - "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==0.2.3" - }, - "pytest": { - "hashes": [ - "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.3" - }, - "pytest-cov": { - "hashes": [ - "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", - "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.1.0" - }, - "pytest-flask": { - "hashes": [ - "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", - "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.3.0" - }, - "pytest-mock": { - "hashes": [ - "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f", - "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==3.12.0" - }, - "pytest-sugar": { - "hashes": [ - "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062", - "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46" - ], - "index": "pypi", - "version": "==0.9.7" - }, - "pytest-xdist": { - "hashes": [ - "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a", - "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==3.5.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" - ], - "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==6.0.1" - }, - "redis": { - "hashes": [ - "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f", - "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.1" - }, - "referencing": { - "hashes": [ - "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882", - "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863" - ], - "markers": "python_version >= '3.8'", - "version": "==0.31.0" - }, - "requests": { - "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0" - }, - "responses": { - "hashes": [ - "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9", - "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.24.1" - }, - "rpds-py": { - "hashes": [ - "sha256:0290712eb5603a725769b5d857f7cf15cf6ca93dda3128065bbafe6fdb709beb", - "sha256:032c242a595629aacace44128f9795110513ad27217b091e834edec2fb09e800", - "sha256:08832078767545c5ee12561ce980714e1e4c6619b5b1e9a10248de60cddfa1fd", - "sha256:08b335fb0c45f0a9e2478a9ece6a1bfb00b6f4c4780f9be3cf36479c5d8dd374", - "sha256:0b70c1f800059c92479dc94dda41288fd6607f741f9b1b8f89a21a86428f6383", - "sha256:0d9f8930092558fd15c9e07198625efb698f7cc00b3dc311c83eeec2540226a8", - "sha256:181ee352691c4434eb1c01802e9daa5edcc1007ff15023a320e2693fed6a661b", - "sha256:19f5aa7f5078d35ed8e344bcba40f35bc95f9176dddb33fc4f2084e04289fa63", - "sha256:1a3b2583c86bbfbf417304eeb13400ce7f8725376dc7d3efbf35dc5d7052ad48", - "sha256:1c9a1dc5e898ce30e2f9c0aa57181cddd4532b22b7780549441d6429d22d3b58", - "sha256:1f36a1e80ef4ed1996445698fd91e0d3e54738bf597c9995118b92da537d7a28", - "sha256:20147996376be452cd82cd6c17701daba69a849dc143270fa10fe067bb34562a", - "sha256:249c8e0055ca597707d71c5ad85fd2a1c8fdb99386a8c6c257e1b47b67a9bec1", - "sha256:2647192facf63be9ed2d7a49ceb07efe01dc6cfb083bd2cc53c418437400cb99", - "sha256:264f3a5906c62b9df3a00ad35f6da1987d321a053895bd85f9d5c708de5c0fbf", - "sha256:2abd669a39be69cdfe145927c7eb53a875b157740bf1e2d49e9619fc6f43362e", - "sha256:2b2415d5a7b7ee96aa3a54d4775c1fec140476a17ee12353806297e900eaeddc", - "sha256:2c173f529666bab8e3f948b74c6d91afa22ea147e6ebae49a48229d9020a47c4", - "sha256:2da81c1492291c1a90987d76a47c7b2d310661bf7c93a9de0511e27b796a8b46", - "sha256:2eca04a365be380ca1f8fa48b334462e19e3382c0bb7386444d8ca43aa01c481", - "sha256:37b08df45f02ff1866043b95096cbe91ac99de05936dd09d6611987a82a3306a", - "sha256:37f79f4f1f06cc96151f4a187528c3fd4a7e1065538a4af9eb68c642365957f7", - "sha256:3dd5fb7737224e1497c886fb3ca681c15d9c00c76171f53b3c3cc8d16ccfa7fb", - "sha256:3e3ac5b602fea378243f993d8b707189f9061e55ebb4e56cb9fdef8166060f28", - "sha256:3f55ae773abd96b1de25fc5c3fb356f491bd19116f8f854ba705beffc1ddc3c5", - "sha256:4011d5c854aa804c833331d38a2b6f6f2fe58a90c9f615afdb7aa7cf9d31f721", - "sha256:4145172ab59b6c27695db6d78d040795f635cba732cead19c78cede74800949a", - "sha256:42b9535aa22ab023704cfc6533e968f7e420affe802d85e956d8a7b4c0b0b5ea", - "sha256:46a07a258bda12270de02b34c4884f200f864bba3dcd6e3a37fef36a168b859d", - "sha256:4f13d3f6585bd07657a603780e99beda96a36c86acaba841f131e81393958336", - "sha256:528e2afaa56d815d2601b857644aeb395afe7e59212ab0659906dc29ae68d9a6", - "sha256:545e94c84575057d3d5c62634611858dac859702b1519b6ffc58eca7fb1adfcf", - "sha256:577d40a72550eac1386b77b43836151cb61ff6700adacda2ad4d883ca5a0b6f2", - "sha256:5967fa631d0ed9f8511dede08bc943a9727c949d05d1efac4ac82b2938024fb7", - "sha256:5b769396eb358d6b55dbf78f3f7ca631ca1b2fe02136faad5af74f0111b4b6b7", - "sha256:63c9e2794329ef070844ff9bfc012004aeddc0468dc26970953709723f76c8a5", - "sha256:6574f619e8734140d96c59bfa8a6a6e7a3336820ccd1bfd95ffa610673b650a2", - "sha256:6bfe72b249264cc1ff2f3629be240d7d2fdc778d9d298087cdec8524c91cd11f", - "sha256:736817dbbbd030a69a1faf5413a319976c9c8ba8cdcfa98c022d3b6b2e01eca6", - "sha256:74a2044b870df7c9360bb3ce7e12f9ddf8e72e49cd3a353a1528cbf166ad2383", - "sha256:74be3b215a5695690a0f1a9f68b1d1c93f8caad52e23242fcb8ba56aaf060281", - "sha256:76a8374b294e4ccb39ccaf11d39a0537ed107534139c00b4393ca3b542cc66e5", - "sha256:7ba239bb37663b2b4cd08e703e79e13321512dccd8e5f0e9451d9e53a6b8509a", - "sha256:7c40851b659d958c5245c1236e34f0d065cc53dca8d978b49a032c8e0adfda6e", - "sha256:7cf241dbb50ea71c2e628ab2a32b5bfcd36e199152fc44e5c1edb0b773f1583e", - "sha256:7cfae77da92a20f56cf89739a557b76e5c6edc094f6ad5c090b9e15fbbfcd1a4", - "sha256:7d152ec7bb431040af2500e01436c9aa0d993f243346f0594a15755016bf0be1", - "sha256:80080972e1d000ad0341c7cc58b6855c80bd887675f92871221451d13a975072", - "sha256:82dbcd6463e580bcfb7561cece35046aaabeac5a9ddb775020160b14e6c58a5d", - "sha256:8308a8d49d1354278d5c068c888a58d7158a419b2e4d87c7839ed3641498790c", - "sha256:839676475ac2ccd1532d36af3d10d290a2ca149b702ed464131e450a767550df", - "sha256:83feb0f682d75a09ddc11aa37ba5c07dd9b824b22915207f6176ea458474ff75", - "sha256:88956c993a20201744282362e3fd30962a9d86dc4f1dcf2bdb31fab27821b61f", - "sha256:8a6ad8429340e0a4de89353447c6441329def3632e7b2293a7d6e873217d3c2b", - "sha256:8ba9fbc5d6e36bfeb5292530321cc56c4ef3f98048647fabd8f57543c34174ec", - "sha256:8c1f6c8df23be165eb0cb78f305483d00c6827a191e3a38394c658d5b9c80bbd", - "sha256:91276caef95556faeb4b8f09fe4439670d3d6206fee78d47ddb6e6de837f0b4d", - "sha256:960e7e460fda2d0af18c75585bbe0c99f90b8f09963844618a621b804f8c3abe", - "sha256:9656a09653b18b80764647d585750df2dff8928e03a706763ab40ec8c4872acc", - "sha256:9cd935c0220d012a27c20135c140f9cdcbc6249d5954345c81bfb714071b985c", - "sha256:a2b3c79586636f1fa69a7bd59c87c15fca80c0d34b5c003d57f2f326e5276575", - "sha256:a4b9d3f5c48bbe8d9e3758e498b3c34863f2c9b1ac57a4e6310183740e59c980", - "sha256:a8c2bf286e5d755a075e5e97ba56b3de08cccdad6b323ab0b21cc98875176b03", - "sha256:a90031658805c63fe488f8e9e7a88b260ea121ba3ee9cdabcece9c9ddb50da39", - "sha256:ad666a904212aa9a6c77da7dce9d5170008cda76b7776e6731928b3f8a0d40fa", - "sha256:af2d1648eb625a460eee07d3e1ea3a4a6e84a1fb3a107f6a8e95ac19f7dcce67", - "sha256:b3d4b390ee70ca9263b331ccfaf9819ee20e90dfd0201a295e23eb64a005dbef", - "sha256:ba4432301ad7eeb1b00848cf46fae0e5fecfd18a8cb5fdcf856c67985f79ecc7", - "sha256:bc3179e0815827cf963e634095ae5715ee73a5af61defbc8d6ca79f1bdae1d1d", - "sha256:c5fd099acaee2325f01281a130a39da08d885e4dedf01b84bf156ec2737d78fe", - "sha256:c797ea56f36c6f248656f0223b11307fdf4a1886f3555eba371f34152b07677f", - "sha256:cd4ea56c9542ad0091dfdef3e8572ae7a746e1e91eb56c9e08b8d0808b40f1d1", - "sha256:cdd6f8738e1f1d9df5b1603bb03cb30e442710e5672262b95d0f9fcb4edb0dab", - "sha256:d0580faeb9def6d0beb7aa666294d5604e569c4e24111ada423cf9936768d95c", - "sha256:d11afdc5992bbd7af60ed5eb519873690d921425299f51d80aa3099ed49f2bcc", - "sha256:d1d388d2f5f5a6065cf83c54dd12112b7389095669ff395e632003ae8999c6b8", - "sha256:d20da6b4c7aa9ee75ad0730beaba15d65157f5beeaca54a038bb968f92bf3ce3", - "sha256:d22e0660de24bd8e9ac82f4230a22a5fe4e397265709289d61d5fb333839ba50", - "sha256:d22f2cb82e0b40e427a74a93c9a4231335bbc548aed79955dde0b64ea7f88146", - "sha256:d4fa1eeb9bea6d9b64ac91ec51ee94cc4fc744955df5be393e1c923c920db2b0", - "sha256:d9793d46d3e6522ae58e9321032827c9c0df1e56cbe5d3de965facb311aed6aa", - "sha256:dab979662da1c9fbb464e310c0b06cb5f1d174d09a462553af78f0bfb3e01920", - "sha256:db8d0f0ad92f74feb61c4e4a71f1d573ef37c22ef4dc19cab93e501bfdad8cbd", - "sha256:df2af1180b8eeececf4f819d22cc0668bfadadfd038b19a90bd2fb2ee419ec6f", - "sha256:dfb5d2ab183c0efe5e7b8917e4eaa2e837aacafad8a69b89aa6bc81550eed857", - "sha256:e04f8c76b8d5c70695b4e8f1d0b391d8ef91df00ef488c6c1ffb910176459bc6", - "sha256:e4a45ba34f904062c63049a760790c6a2fa7a4cc4bd160d8af243b12371aaa05", - "sha256:e9be1f7c5f9673616f875299339984da9447a40e3aea927750c843d6e5e2e029", - "sha256:edc91c50e17f5cd945d821f0f1af830522dba0c10267c3aab186dc3dbaab8def", - "sha256:ee70ee5f4144a45a9e6169000b5b525d82673d5dab9f7587eccc92794814e7ac", - "sha256:f1059ca9a51c936c9a8d46fbc2c9a6b4c15ab3f13a97f1ad32f024b39666ba85", - "sha256:f47eef55297799956464efc00c74ae55c48a7b68236856d56183fe1ddf866205", - "sha256:f4ae6f423cb7d1c6256b7482025ace2825728f53b7ac58bcd574de6ee9d242c2", - "sha256:f4b15a163448ec79241fb2f1bc5a8ae1a4a304f7a48d948d208a2935b26bf8a5", - "sha256:f55601fb58f92e4f4f1d05d80c24cb77505dc42103ddfd63ddfdc51d3da46fa2", - "sha256:fa84bbe22ffa108f91631935c28a623001e335d66e393438258501e618fb0dde", - "sha256:faa12a9f34671a30ea6bb027f04ec4e1fb8fa3fb3ed030893e729d4d0f3a9791", - "sha256:fcfd5f91b882eedf8d9601bd21261d6ce0e61a8c66a7152d1f5df08d3f643ab1", - "sha256:fe30ef31172bdcf946502a945faad110e8fff88c32c4bec9a593df0280e64d8a" - ], - "markers": "python_version >= '3.8'", - "version": "==0.13.1" - }, - "s3transfer": { - "hashes": [ - "sha256:baa479dc2e63e5c2ed51611b4d46cdf0295e2070d8d0b86b22f335ee5b954986", - "sha256:e8d6bd52ffd99841e3a57b34370a54841f12d3aab072af862cdcc50955288002" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "sortedcontainers": { - "hashes": [ - "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", - "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" - ], - "version": "==2.4.0" - }, - "soupsieve": { - "hashes": [ - "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", - "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" - ], - "markers": "python_version >= '3.8'", - "version": "==2.5" - }, - "termcolor": { - "hashes": [ - "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475", - "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.3.0" - }, - "tomlkit": { - "hashes": [ - "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4", - "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba" - ], - "markers": "python_version >= '3.7'", - "version": "==0.12.3" - }, - "types-cachetools": { - "hashes": [ - "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199", - "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.3.0.7" - }, - "types-pyopenssl": { - "hashes": [ - "sha256:00171433653265843b7469ddb9f3c86d698668064cc33ef10537822156130ebf", - "sha256:5ffb077fe70b699c88d5caab999ae80e192fe28bf6cda7989b7e79b1e4e2dcd3" - ], - "markers": "python_version >= '3.7'", - "version": "==23.3.0.0" - }, - "types-python-dateutil": { - "hashes": [ - "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b", - "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9" - ], - "index": "pypi", - "version": "==2.8.19.14" - }, - "types-pytz": { - "hashes": [ - "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", - "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" - ], - "index": "pypi", - "version": "==2023.3.1.1" - }, - "types-pyyaml": { - "hashes": [ - "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", - "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" - ], - "version": "==6.0.12.12" - }, - "types-redis": { - "hashes": [ - "sha256:94fc61118601fb4f79206b33b9f4344acff7ca1d7bba67834987fb0efcf6a770", - "sha256:c8cfc84635183deca2db4a528966c5566445fd3713983f0034fb0f5a09e0890d" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==4.6.0.11" - }, - "types-requests": { - "hashes": [ - "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", - "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.31.0.10" - }, - "types-simplejson": { - "hashes": [ - "sha256:8ba093dc7884f59b3e62aed217144085e675a269debc32678fd80e0b43b2b86f", - "sha256:ebc81f886f89d99d6b80c726518aa2228bc77c26438f18fd81455e4f79f8ee1b" - ], - "index": "pypi", - "version": "==3.19.0.2" - }, - "typing-extensions": { - "hashes": [ - "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" - ], - "markers": "python_version >= '3.8'", - "version": "==4.8.0" - }, - "urllib3": { - "hashes": [ - "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", - "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.7" - }, - "werkzeug": { - "hashes": [ - "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", - "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.1" - }, - "wrapt": { - "hashes": [ - "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", - "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", - "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", - "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", - "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", - "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", - "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", - "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", - "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", - "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", - "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", - "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", - "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", - "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", - "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", - "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", - "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", - "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", - "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", - "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", - "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", - "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", - "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", - "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", - "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", - "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", - "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", - "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", - "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", - "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", - "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", - "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", - "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", - "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", - "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", - "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", - "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", - "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", - "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", - "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", - "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", - "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", - "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", - "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", - "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", - "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", - "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", - "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", - "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", - "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", - "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", - "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", - "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", - "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", - "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", - "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", - "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", - "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", - "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", - "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", - "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", - "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", - "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", - "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", - "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", - "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", - "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", - "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", - "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", - "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" - ], - "markers": "python_version >= '3.6'", - "version": "==1.16.0" - }, - "xmltodict": { - "hashes": [ - "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", - "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" - ], - "markers": "python_version >= '3.4'", - "version": "==0.13.0" + "_meta": { + "hash": { + "sha256": "b857953d9c0ba947a86596e0beb665969ae68afee425c3350a0dd4049a927daa" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.11" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "async-timeout": { + "hashes": [ + "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", + "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" + ], + "markers": "python_version >= '3.7'", + "version": "==4.0.3" + }, + "babel": { + "hashes": [ + "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363", + "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287" + ], + "markers": "python_version >= '3.7'", + "version": "==2.14.0" + }, + "blinker": { + "hashes": [ + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.7.0" + }, + "boto3": { + "hashes": [ + "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714", + "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.34.56" + }, + "botocore": { + "hashes": [ + "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f", + "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec" + ], + "markers": "python_version >= '3.8'", + "version": "==1.34.56" + }, + "brotli": { + "hashes": [ + "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", + "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", + "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", + "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", + "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", + "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", + "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", + "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", + "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", + "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", + "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", + "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", + "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", + "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", + "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", + "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", + "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", + "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", + "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", + "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", + "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", + "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", + "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", + "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", + "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", + "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", + "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", + "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", + "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", + "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", + "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", + "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", + "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", + "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", + "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", + "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", + "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", + "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", + "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", + "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", + "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", + "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", + "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", + "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", + "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", + "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", + "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", + "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", + "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", + "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", + "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", + "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", + "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", + "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", + "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", + "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", + "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", + "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", + "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", + "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", + "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", + "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", + "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", + "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", + "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", + "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", + "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", + "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", + "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", + "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", + "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", + "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", + "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", + "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", + "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", + "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", + "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", + "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", + "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", + "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", + "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", + "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", + "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.1.0" + }, + "cachetools": { + "hashes": [ + "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945", + "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.3.3" + }, + "certifi": { + "hashes": [ + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + ], + "markers": "python_version >= '3.6'", + "version": "==2024.2.2" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.16.0" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==0.4.6" + }, + "coloredlogs": { + "hashes": [ + "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", + "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==15.0.1" + }, + "cramjam": { + "hashes": [ + "sha256:014a8b0d859b688e3c24ea4448957db26f78ac792f4ab3098e0a5a7e4eed1dcc", + "sha256:09d2789da4e397bc5204d796e3e252943ae78c797711be8d5525adefe163c8df", + "sha256:0a25eaba13ff1e27b7d9d7aa47108d002ff3e9999c6114e5aaca031873e6c6e5", + "sha256:0dff57824cbe460c1a458d995292eb6f5ea853cfc668289a29025790fee73736", + "sha256:0f133735f502a63a74a9cfeb2a5225bf33aa2c7a9f30e1e5a85d3a688c8dc938", + "sha256:118a661fd9287d9ed96ccde578a9e271b61b11a1aa02656b3f0da93a0643ce0f", + "sha256:150bb70bfe9b882b706998adea461c877ce205a8476c0e7a039db04e1b35be9d", + "sha256:194bdd4e820fed9e23183197cff6f5c29b4a6a4d2e95c2d3743b83cf55e7f304", + "sha256:2440c975f9a30b232172de1790d99a438b45c9745f4e7f4560554b83030e37ac", + "sha256:2a7ea3364095074ff701b950dcb5e03236002f4b4b17180d6d82796e7c26e8fc", + "sha256:32c8a3ae3d484da10b2083029950b2dda8f53fed4983d051547bc491c87737e6", + "sha256:3905cbb46dce4f46e8ed1a5af53b14ca6cc8fc983158fa22b1058d1bbbdd5554", + "sha256:3c210b56403fe275b8c578121ead2550e5a68e82cefe83da5821d05d64bcde93", + "sha256:3d661f4cee4b72ffb083a59de4ac2a4d332f15fac072e4224083f0dc138bc8d6", + "sha256:419bf9bc693d8db6d2eb502fad04eb5ce48bf38a2d40241d070e61321bb03037", + "sha256:4403d0a05b812c17160a80222ea9ad27086509e0b241d3ffe8dc5902cc0b8fcc", + "sha256:48a47e443bd314a4d03c8bd4eb35d9097664aecc693da0731a5f3be299430855", + "sha256:4b5f6afbbff427fa49bc16bc017f61101c24df01eb06935d501f8659f0b2ce1d", + "sha256:4bcb7a97b1304f12badd4de4d84b90e55c56c274cca41413614dd72c4b55591e", + "sha256:4c4f1f8d3a536fc9bbde951b66c0fae09d2f65523e4d77738950106ac7c064a6", + "sha256:5c509d441ae7940b127c9e34582bba534b367a220fd3e8176ca72638dd209e12", + "sha256:5f9fea90e4063fba7cff9bf8e48a908001bdb22a3bb13b978350385548659a78", + "sha256:631f35e2137fb1f14933aed6ecfcaa955569110946afda168b01a01fd29f8da7", + "sha256:678e0a3ea9fc2f2953f09f7feb8cdc92d2db2c5f024dd50faefc3ef3ed19bd7b", + "sha256:6bec8428e817bb3b67c1e5e29714fc274eefa3a8a20774cb07b736b564f1fb49", + "sha256:6e25567380aca988cc0c335eb01a2c39a533add5491ddc33255b11e0275f855e", + "sha256:7104ca8adeac8e50913537ebafcb7327f4e3093231556e49ecf6d5415942de77", + "sha256:764a27810d8a12320bc9a5664d5f37c355a9966f6f4e170b0a48a9fba8101b82", + "sha256:78b175732ca8dbc61f9b4c8e1f4c3fb3f275819c71b635dd637fa7a22bd7239d", + "sha256:7a5e624ad909172d3c74a66cae25f1ce42bda67863023d7cf79ea40239786f7b", + "sha256:8204c85bf8c049bc2e1a170d5728634f191242188272b03d445bfc2eba1d87c6", + "sha256:85d42d81e46e7752f6a7fd4bcb586d90159e227409f2d10970161f2a636451ee", + "sha256:85f902be87c2dfd06e67fc3d852c1007ec6ce3934b0137131173633df47b0159", + "sha256:86fd732b37d545660354de60a314f27026ebb6b3782f4f3b2cc53b3be43cfbd8", + "sha256:8d02a99a9fc171fc2e6de29d664ecce08a59f95d9d3450277f506154853a3866", + "sha256:95022b98cfa4004e99122dcfd2ead8d07cfb67e44e557376659363a6e056d4dd", + "sha256:95359af45a8945292732ea641bc88c7c34f0e780194bd57939ad440d3229b4c6", + "sha256:9558f18624421c929b0f3723905fc1b48039009f5adae89ed03bf83573e41014", + "sha256:96567733e306464ae65d9a4c00fe099ae9c527614213b9e55a7aec0263cd36f6", + "sha256:9c54c6a761f0f2d3565894c8189cefb9abf5476999e5472a9d508875715a3c1c", + "sha256:9cebfd2fec5470ef786ee9503512514b0f25a022b511fb9bb35f7df5568dc649", + "sha256:9f07ae3b5ce995f407c1f08f32bd829e949551de368a70311bd897cd27c02ee0", + "sha256:a128df3c04d213216a6fec5e9e9a27fe7b6678e234b641b98a2031e293602ed6", + "sha256:a56811e3729421711f4c16061068a0f8a1fe2c64a95274ca1a66fc1709398787", + "sha256:a921ac4b8a26961505eade705f24a09faa642e99aae074104859aa44e3ed40ad", + "sha256:ab0db497263f5319e38cc76fa60a617e04b150c7dfde9804085bdf95a2a4f2f7", + "sha256:ac71e6e14813dac09300b0c0b11fbf415c3813f80cd442a79b5ceb720b6d375a", + "sha256:acfea460a28ee0f047cf3c0fcd5cf8e758d504017579188ccca0f08ee6c0b96b", + "sha256:add2d2b8e949605e836c481d49ec4e5f56fa8a729e2b6d76e5f903712d6b6781", + "sha256:b47f9a0d51f12430bcc7e1fe0b8a475ffecbb25950421ddf653e8e75fdbb4348", + "sha256:b724799de4d1a417b3ba2fbb17b5ed412eea5308d6cc5e59b32cb9c8befd2eae", + "sha256:bbb32fe037c1d207ca07ab3bfd5de285c0c627236449a500f4dae526fbc7472f", + "sha256:c132fafb4c3ae8fde959e6b6e56eb77660dea4f23ce82bb2ca40503254e29bc8", + "sha256:c21dfa6a238fddf85fbd05cd6573b6a66c1614374713506c4837b76fc38632c6", + "sha256:c2c02e4f937f1abb7b25fb8e25635c8e180ed0644f12ce4df798c026ce35d8ce", + "sha256:c6270cfe9a2963116cecf972bf68267364546326b6b1aff8e531c0f65fd1992e", + "sha256:ce77bcae1b90785882c516ad660237473428a168158edaef8fd76c9c60e0a67a", + "sha256:cf61c45d956b36a39b73a7e587081a39b2e91711820cd2bd450946b777ff8cad", + "sha256:d2d689746195477cdbcfed36680d816d7c32af72c62c1b93b05430f72a833a70", + "sha256:d3aeb2626aaacec8455e6ae7154d92459bcba678fce68a1b443eca4f27e2e8f4", + "sha256:def02072f230ceaf399dac03a0be805941519a12502795007f6be586e6ec3573", + "sha256:e32b7861a9fee2e8eef0a56ef8da7ae68d1d00fbc1227253616786f23926695e", + "sha256:ea0e0d54dd7cae6793569c5650b5f245ac4abc74bd35ccaee9f4ab05fb90b2c3", + "sha256:ec9c8997edef2d74d9190be8195eaf983c705066f4c6ea1d4a96a807b54b8a91", + "sha256:edbc6c031544c556af2d46762c62962ff37ffcf0e503fea2535fd080d84a0d2f", + "sha256:f41673f82808661f05c1e115315b7b46a7e7a9c5365ea9056281c73697b2eeba" + ], + "markers": "python_version >= '3.7'", + "version": "==2.8.2" + }, + "cryptography": { + "hashes": [ + "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee", + "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576", + "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d", + "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30", + "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413", + "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb", + "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da", + "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4", + "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd", + "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc", + "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8", + "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1", + "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc", + "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e", + "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8", + "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940", + "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400", + "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7", + "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16", + "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278", + "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74", + "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec", + "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1", + "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2", + "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c", + "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922", + "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a", + "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6", + "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1", + "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e", + "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", + "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7" + ], + "markers": "python_version >= '3.7'", + "version": "==42.0.5" + }, + "dnspython": { + "hashes": [ + "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50", + "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc" + ], + "markers": "python_version >= '3.8'", + "version": "==2.6.1" + }, + "email-validator": { + "hashes": [ + "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84", + "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.1.1" + }, + "flask": { + "hashes": [ + "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e", + "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.0.2" + }, + "flask-babel": { + "hashes": [ + "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625", + "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593" + ], + "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==4.0.0" + }, + "flask-compress": { + "hashes": [ + "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c", + "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee" + ], + "index": "pypi", + "version": "==1.14" + }, + "flask-login": { + "hashes": [ + "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333", + "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.6.3" + }, + "flask-talisman": { + "hashes": [ + "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208", + "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "flask-wtf": { + "hashes": [ + "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695", + "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.2.1" + }, + "gevent": { + "hashes": [ + "sha256:03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5", + "sha256:117e5837bc74a1673605fb53f8bfe22feb6e5afa411f524c835b2ddf768db0de", + "sha256:141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8", + "sha256:14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5", + "sha256:1dffb395e500613e0452b9503153f8f7ba587c67dd4a85fc7cd7aa7430cb02cc", + "sha256:2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800", + "sha256:2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe", + "sha256:2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7", + "sha256:368a277bd9278ddb0fde308e6a43f544222d76ed0c4166e0d9f6b036586819d9", + "sha256:3adfb96637f44010be8abd1b5e73b5070f851b817a0b182e601202f20fa06533", + "sha256:3d5325ccfadfd3dcf72ff88a92fb8fc0b56cacc7225f0f4b6dcf186c1a6eeabc", + "sha256:432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056", + "sha256:44098038d5e2749b0784aabb27f1fcbb3f43edebedf64d0af0d26955611be8d6", + "sha256:5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026", + "sha256:6c47ae7d1174617b3509f5d884935e788f325eb8f1a7efc95d295c68d83cce40", + "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07", + "sha256:782a771424fe74bc7e75c228a1da671578c2ba4ddb2ca09b8f959abdf787331e", + "sha256:7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be", + "sha256:7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8", + "sha256:8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5", + "sha256:90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1", + "sha256:918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789", + "sha256:9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19", + "sha256:94138682e68ec197db42ad7442d3cf9b328069c3ad8e4e5022e6b5cd3e7ffae5", + "sha256:968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7", + "sha256:9d8d0642c63d453179058abc4143e30718b19a85cbf58c2744c9a63f06a1d388", + "sha256:a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8", + "sha256:b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98", + "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3", + "sha256:bf5b9c72b884c6f0c4ed26ef204ee1f768b9437330422492c319470954bc4cc7", + "sha256:ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060", + "sha256:cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d", + "sha256:d4faf846ed132fd7ebfbbf4fde588a62d21faa0faa06e6f468b7faa6f436b661", + "sha256:d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c", + "sha256:dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb", + "sha256:f5de3c676e57177b38857f6e3cdfbe8f38d1cd754b63200c0615eaa31f514b4f", + "sha256:f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91", + "sha256:f7cac622e11b4253ac4536a654fe221249065d9a69feb6cdcd4d9af3503602e0", + "sha256:f8a04cf0c5b7139bc6368b461257d4a757ea2fe89b3773e494d235b7dd51119f", + "sha256:f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836", + "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682" + ], + "markers": "platform_python_implementation == 'CPython' and python_version >= '3.8'", + "version": "==24.2.1" + }, + "google-api-core": { + "extras": [ + "grpc" + ], + "hashes": [ + "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e", + "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95" + ], + "markers": "python_version >= '3.7'", + "version": "==2.17.1" + }, + "google-auth": { + "hashes": [ + "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72", + "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885" + ], + "markers": "python_version >= '3.7'", + "version": "==2.28.1" + }, + "google-cloud-core": { + "hashes": [ + "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073", + "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61" + ], + "markers": "python_version >= '3.7'", + "version": "==2.4.1" + }, + "google-cloud-datastore": { + "hashes": [ + "sha256:07fc5870a0261f25466c557c134df95a96dfd2537abd088b9d537fbabe99b974", + "sha256:c52086670d4c3779ea7bd8f8353b093a9b5e81c6606f36ffcdf46e6ce9fc80c0" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.19.0" + }, + "google-cloud-pubsub": { + "hashes": [ + "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188", + "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.20.0" + }, + "google-cloud-storage": { + "hashes": [ + "sha256:5d9237f88b648e1d724a0f20b5cde65996a37fe51d75d17660b1404097327dd2", + "sha256:7560a3c48a03d66c553dc55215d35883c680fe0ab44c23aa4832800ccc855c74" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.15.0" + }, + "google-cloud-tasks": { + "hashes": [ + "sha256:a1a8473f5c76907525b51ae4c20182a42333b5c3e1dc5782d7af6c2f58241a0c", + "sha256:d891fe7006db4d6122838aa6de4aca6e0077bab224edcae684666ae3e303c45f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.16.3" + }, + "google-crc32c": { + "hashes": [ + "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a", + "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876", + "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c", + "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289", + "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298", + "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02", + "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f", + "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2", + "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a", + "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb", + "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210", + "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5", + "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee", + "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c", + "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a", + "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314", + "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd", + "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65", + "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37", + "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4", + "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13", + "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894", + "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31", + "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e", + "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709", + "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740", + "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc", + "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d", + "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c", + "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c", + "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d", + "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906", + "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61", + "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57", + "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c", + "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a", + "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438", + "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946", + "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7", + "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96", + "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091", + "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae", + "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d", + "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88", + "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2", + "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd", + "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541", + "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728", + "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178", + "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968", + "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346", + "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8", + "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93", + "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7", + "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273", + "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462", + "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94", + "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd", + "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e", + "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57", + "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b", + "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9", + "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a", + "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100", + "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325", + "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183", + "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556", + "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4" + ], + "markers": "python_version >= '3.7'", + "version": "==1.5.0" + }, + "google-resumable-media": { + "hashes": [ + "sha256:5f18f5fa9836f4b083162064a1c2c98c17239bfda9ca50ad970ccf905f3e625b", + "sha256:79543cfe433b63fd81c0844b7803aba1bb8950b47bedf7d980c38fa123937e08" + ], + "markers": "python_version >= '3.7'", + "version": "==2.7.0" + }, + "googleapis-common-protos": { + "extras": [ + "grpc" + ], + "hashes": [ + "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07", + "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277" + ], + "markers": "python_version >= '3.7'", + "version": "==1.62.0" + }, + "greenlet": { + "hashes": [ + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + ], + "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", + "version": "==3.0.3" + }, + "grpc-google-iam-v1": { + "hashes": [ + "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89", + "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e" + ], + "markers": "python_version >= '3.7'", + "version": "==0.13.0" + }, + "grpcio": { + "hashes": [ + "sha256:0b9179478b09ee22f4a36b40ca87ad43376acdccc816ce7c2193a9061bf35701", + "sha256:0d3dee701e48ee76b7d6fbbba18ba8bc142e5b231ef7d3d97065204702224e0e", + "sha256:0d7ae7fc7dbbf2d78d6323641ded767d9ec6d121aaf931ec4a5c50797b886532", + "sha256:0e97f37a3b7c89f9125b92d22e9c8323f4e76e7993ba7049b9f4ccbe8bae958a", + "sha256:136ffd79791b1eddda8d827b607a6285474ff8a1a5735c4947b58c481e5e4271", + "sha256:1bc8449084fe395575ed24809752e1dc4592bb70900a03ca42bf236ed5bf008f", + "sha256:1eda79574aec8ec4d00768dcb07daba60ed08ef32583b62b90bbf274b3c279f7", + "sha256:29cb592c4ce64a023712875368bcae13938c7f03e99f080407e20ffe0a9aa33b", + "sha256:2c1488b31a521fbba50ae86423f5306668d6f3a46d124f7819c603979fc538c4", + "sha256:2e84bfb2a734e4a234b116be208d6f0214e68dcf7804306f97962f93c22a1839", + "sha256:2f3d9a4d0abb57e5f49ed5039d3ed375826c2635751ab89dcc25932ff683bbb6", + "sha256:36df33080cd7897623feff57831eb83c98b84640b016ce443305977fac7566fb", + "sha256:38f69de9c28c1e7a8fd24e4af4264726637b72f27c2099eaea6e513e7142b47e", + "sha256:39cd45bd82a2e510e591ca2ddbe22352e8413378852ae814549c162cf3992a93", + "sha256:3fa15850a6aba230eed06b236287c50d65a98f05054a0f01ccedf8e1cc89d57f", + "sha256:4cd356211579043fce9f52acc861e519316fff93980a212c8109cca8f47366b6", + "sha256:56ca7ba0b51ed0de1646f1735154143dcbdf9ec2dbe8cc6645def299bb527ca1", + "sha256:5e709f7c8028ce0443bddc290fb9c967c1e0e9159ef7a030e8c21cac1feabd35", + "sha256:614c3ed234208e76991992342bab725f379cc81c7dd5035ee1de2f7e3f7a9842", + "sha256:62aa1659d8b6aad7329ede5d5b077e3d71bf488d85795db517118c390358d5f6", + "sha256:62ccb92f594d3d9fcd00064b149a0187c246b11e46ff1b7935191f169227f04c", + "sha256:662d3df5314ecde3184cf87ddd2c3a66095b3acbb2d57a8cada571747af03873", + "sha256:748496af9238ac78dcd98cce65421f1adce28c3979393e3609683fcd7f3880d7", + "sha256:77d48e5b1f8f4204889f1acf30bb57c30378e17c8d20df5acbe8029e985f735c", + "sha256:7a195531828b46ea9c4623c47e1dc45650fc7206f8a71825898dd4c9004b0928", + "sha256:7e1f51e2a460b7394670fdb615e26d31d3260015154ea4f1501a45047abe06c9", + "sha256:7eea57444a354ee217fda23f4b479a4cdfea35fb918ca0d8a0e73c271e52c09c", + "sha256:7f9d6c3223914abb51ac564dc9c3782d23ca445d2864321b9059d62d47144021", + "sha256:81531632f93fece32b2762247c4c169021177e58e725494f9a746ca62c83acaa", + "sha256:81d444e5e182be4c7856cd33a610154fe9ea1726bd071d07e7ba13fafd202e38", + "sha256:821a44bd63d0f04e33cf4ddf33c14cae176346486b0df08b41a6132b976de5fc", + "sha256:88f41f33da3840b4a9bbec68079096d4caf629e2c6ed3a72112159d570d98ebe", + "sha256:8aab8f90b2a41208c0a071ec39a6e5dbba16fd827455aaa070fec241624ccef8", + "sha256:921148f57c2e4b076af59a815467d399b7447f6e0ee10ef6d2601eb1e9c7f402", + "sha256:92cdb616be44c8ac23a57cce0243af0137a10aa82234f23cd46e69e115071388", + "sha256:95370c71b8c9062f9ea033a0867c4c73d6f0ff35113ebd2618171ec1f1e903e0", + "sha256:98d8f4eb91f1ce0735bf0b67c3b2a4fea68b52b2fd13dc4318583181f9219b4b", + "sha256:a33f2bfd8a58a02aab93f94f6c61279be0f48f99fcca20ebaee67576cd57307b", + "sha256:ab140a3542bbcea37162bdfc12ce0d47a3cda3f2d91b752a124cc9fe6776a9e2", + "sha256:b3d3d755cfa331d6090e13aac276d4a3fb828bf935449dc16c3d554bf366136b", + "sha256:b71c65427bf0ec6a8b48c68c17356cb9fbfc96b1130d20a07cb462f4e4dcdcd5", + "sha256:b7a6be562dd18e5d5bec146ae9537f20ae1253beb971c0164f1e8a2f5a27e829", + "sha256:bcff647e7fe25495e7719f779cc219bbb90b9e79fbd1ce5bda6aae2567f469f2", + "sha256:c912688acc05e4ff012c8891803659d6a8a8b5106f0f66e0aed3fb7e77898fa6", + "sha256:ce1aafdf8d3f58cb67664f42a617af0e34555fe955450d42c19e4a6ad41c84bd", + "sha256:d6a56ba703be6b6267bf19423d888600c3f574ac7c2cc5e6220af90662a4d6b0", + "sha256:e803e9b58d8f9b4ff0ea991611a8d51b31c68d2e24572cd1fe85e99e8cc1b4f8", + "sha256:eef1d16ac26c5325e7d39f5452ea98d6988c700c427c52cbc7ce3201e6d93334", + "sha256:f359d635ee9428f0294bea062bb60c478a8ddc44b0b6f8e1f42997e5dc12e2ee", + "sha256:f4c04fe33039b35b97c02d2901a164bbbb2f21fb9c4e2a45a959f0b044c3512c", + "sha256:f897b16190b46bc4d4aaf0a32a4b819d559a37a756d7c6b571e9562c360eed72", + "sha256:fbe0c20ce9a1cff75cfb828b21f08d0a1ca527b67f2443174af6626798a754a4", + "sha256:fc2836cb829895ee190813446dce63df67e6ed7b9bf76060262c55fcd097d270", + "sha256:fcc98cff4084467839d0a20d16abc2a76005f3d1b38062464d088c07f500d170" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.62.0" + }, + "grpcio-status": { + "hashes": [ + "sha256:0d693e9c09880daeaac060d0c3dba1ae470a43c99e5d20dfeafd62cf7e08a85d", + "sha256:3baac03fcd737310e67758c4082a188107f771d32855bce203331cd4c9aa687a" + ], + "markers": "python_version >= '3.6'", + "version": "==1.62.0" + }, + "gunicorn": { + "hashes": [ + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + ], + "index": "pypi", + "markers": "python_version >= '3.5'", + "version": "==21.2.0" + }, + "htmlmin": { + "hashes": [ + "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178" + ], + "index": "pypi", + "version": "==0.1.12" + }, + "humanfriendly": { + "hashes": [ + "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", + "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==10.0" + }, + "humanize": { + "hashes": [ + "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa", + "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.9.0" + }, + "idna": { + "hashes": [ + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + ], + "markers": "python_version >= '3.5'", + "version": "==3.6" + }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, + "jinja2": { + "hashes": [ + "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", + "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.3" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "jsonpointer": { + "hashes": [ + "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a", + "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88" + ], + "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==2.4" + }, + "jwcrypto": { + "hashes": [ + "sha256:59e7d5e4589d1b07170f368e20c32eb32a023911806a9723b1f43a0d8b3028d6", + "sha256:c18b10b2049603bef3ae7b77ad14bded431a9077d113447d62bebd8550b0d5bd" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.5" + }, + "markupsafe": { + "hashes": [ + "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", + "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", + "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", + "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", + "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", + "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", + "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", + "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", + "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", + "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", + "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", + "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", + "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", + "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", + "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", + "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", + "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", + "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", + "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", + "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", + "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", + "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", + "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", + "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", + "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", + "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", + "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", + "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", + "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", + "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", + "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", + "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", + "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", + "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", + "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", + "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", + "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", + "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", + "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", + "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", + "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", + "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", + "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", + "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", + "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", + "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", + "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", + "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", + "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", + "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", + "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", + "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", + "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", + "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", + "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", + "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", + "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", + "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", + "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", + "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.5" + }, + "marshmallow": { + "hashes": [ + "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3", + "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.21.1" + }, + "ordered-set": { + "hashes": [ + "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", + "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pdfkit": { + "hashes": [ + "sha256:992f821e1e18fc8a0e701ecae24b51a2d598296a180caee0a24c0af181da02a9", + "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f", + "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e" + ], + "index": "pypi", + "version": "==1.0.0" + }, + "pika": { + "hashes": [ + "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", + "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.3.2" + }, + "proto-plus": { + "hashes": [ + "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2", + "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c" + ], + "markers": "python_version >= '3.6'", + "version": "==1.23.0" + }, + "protobuf": { + "hashes": [ + "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4", + "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8", + "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c", + "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d", + "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4", + "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa", + "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c", + "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019", + "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9", + "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c", + "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2" + ], + "markers": "python_version >= '3.8'", + "version": "==4.25.3" + }, + "pyasn1": { + "hashes": [ + "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58", + "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.5.1" + }, + "pyasn1-modules": { + "hashes": [ + "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c", + "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.3.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.9.0.post0" + }, + "python-snappy": { + "hashes": [ + "sha256:1bc29d36211d44bb9f04f3d7ccfbaeaebbc2f62b6d40f4fc4edd1fb16bc52c13", + "sha256:7c9111be1ae1dcbf4ce32b752366d4a5d4f07898d517691c4003d41e04b03488" + ], + "index": "pypi", + "version": "==0.7.1" + }, + "pytz": { + "hashes": [ + "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", + "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319" + ], + "version": "==2024.1" + }, + "pyyaml": { + "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==6.0.1" + }, + "redis": { + "hashes": [ + "sha256:3f82cc80d350e93042c8e6e7a5d0596e4dd68715babffba79492733e1f367037", + "sha256:4caa8e1fcb6f3c0ef28dba99535101d80934b7d4cd541bbb47f4a3826ee472d1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.2" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "rsa": { + "hashes": [ + "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", + "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21" + ], + "markers": "python_version >= '3.6' and python_version < '4'", + "version": "==4.9" + }, + "s3transfer": { + "hashes": [ + "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e", + "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b" + ], + "markers": "python_version >= '3.8'", + "version": "==0.10.0" + }, + "sdc-cryptography": { + "hashes": [ + "sha256:9b2cc663d03e43a45919b49e6d4e4818f6a344b971322e6980bf64379e466548", + "sha256:d6ecbe45b42d9449ee4fd47774fc9ba6f5e14a4e8531517468fc391902ad98fd" + ], + "index": "pypi", + "version": "==1.1.4" + }, + "setuptools": { + "hashes": [ + "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56", + "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8" + ], + "markers": "python_version >= '3.8'", + "version": "==69.1.1" + }, + "simplejson": { + "hashes": [ + "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137", + "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a", + "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae", + "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a", + "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba", + "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087", + "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b", + "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41", + "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d", + "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd", + "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d", + "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e", + "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a", + "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9", + "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6", + "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb", + "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664", + "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2", + "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b", + "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5", + "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835", + "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17", + "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69", + "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd", + "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6", + "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2", + "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2", + "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428", + "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f", + "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2", + "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f", + "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7", + "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f", + "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637", + "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3", + "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816", + "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565", + "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378", + "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0", + "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b", + "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693", + "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358", + "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9", + "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23", + "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf", + "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a", + "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832", + "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414", + "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f", + "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48", + "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad", + "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290", + "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3", + "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917", + "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402", + "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0", + "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc", + "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867", + "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f", + "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589", + "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb", + "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b", + "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3", + "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb", + "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c", + "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46", + "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672", + "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c", + "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e", + "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0", + "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b", + "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4", + "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4", + "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c", + "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c", + "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8", + "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b", + "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb", + "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80", + "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2", + "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374", + "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc", + "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50", + "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c", + "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f", + "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28", + "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5", + "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734", + "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a", + "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d", + "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4", + "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c", + "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13", + "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973", + "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a", + "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e", + "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff", + "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded" + ], + "index": "pypi", + "markers": "python_version >= '2.5' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.19.2" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "structlog": { + "hashes": [ + "sha256:3f6efe7d25fab6e86f277713c218044669906537bb717c1807a09d46bca0714d", + "sha256:41a09886e4d55df25bdcb9b5c9674bccfab723ff43e0a86a1b7b236be8e57b16" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.1.0" + }, + "typing-extensions": { + "hashes": [ + "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", + "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" + ], + "markers": "python_version >= '3.8'", + "version": "==4.10.0" + }, + "ua-parser": { + "hashes": [ + "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", + "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" + ], + "index": "pypi", + "version": "==0.18.0" + }, + "urllib3": { + "hashes": [ + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.7" + }, + "uwsgi": { + "hashes": [ + "sha256:77b6dd5cd633f4ae87ee393f7701f617736815499407376e78f3d16467523afe" + ], + "index": "pypi", + "version": "==2.0.24" + }, + "werkzeug": { + "hashes": [ + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.1" + }, + "wtforms": { + "hashes": [ + "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07", + "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9" + ], + "markers": "python_version >= '3.8'", + "version": "==3.1.2" + }, + "zope.event": { + "hashes": [ + "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" + ], + "markers": "python_version >= '3.7'", + "version": "==5.0" + }, + "zope.interface": { + "hashes": [ + "sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe", + "sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac", + "sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad", + "sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b", + "sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000", + "sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328", + "sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565", + "sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f", + "sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70", + "sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037", + "sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b", + "sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab", + "sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85", + "sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099", + "sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5", + "sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef", + "sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c", + "sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd", + "sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48", + "sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd", + "sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550", + "sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797", + "sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe", + "sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d", + "sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e", + "sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1", + "sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0", + "sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532", + "sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f", + "sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f", + "sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3", + "sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a", + "sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000", + "sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e", + "sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce", + "sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440" + ], + "markers": "python_version >= '3.7'", + "version": "==6.2" + } + }, + "develop": { + "astroid": { + "hashes": [ + "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c", + "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a" + ], + "markers": "python_full_version >= '3.7.2'", + "version": "==2.15.8" + }, + "async-timeout": { + "hashes": [ + "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", + "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" + ], + "markers": "python_version >= '3.7'", + "version": "==4.0.3" + }, + "attrs": { + "hashes": [ + "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2.0" + }, + "beautifulsoup4": { + "hashes": [ + "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", + "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" + ], + "index": "pypi", + "markers": "python_full_version >= '3.6.0'", + "version": "==4.12.3" + }, + "black": { + "hashes": [ + "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8", + "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8", + "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd", + "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9", + "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31", + "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92", + "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f", + "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29", + "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4", + "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693", + "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218", + "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a", + "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23", + "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0", + "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982", + "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894", + "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540", + "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430", + "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b", + "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2", + "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6", + "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==24.2.0" + }, + "blinker": { + "hashes": [ + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.7.0" + }, + "boto3": { + "hashes": [ + "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714", + "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.34.56" + }, + "botocore": { + "hashes": [ + "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f", + "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec" + ], + "markers": "python_version >= '3.8'", + "version": "==1.34.56" + }, + "certifi": { + "hashes": [ + "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + ], + "markers": "python_version >= '3.6'", + "version": "==2024.2.2" + }, + "cffi": { + "hashes": [ + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + ], + "markers": "platform_python_implementation != 'PyPy'", + "version": "==1.16.0" + }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "coverage": { + "extras": [ + "toml" + ], + "hashes": [ + "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa", + "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003", + "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f", + "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c", + "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e", + "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0", + "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9", + "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52", + "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e", + "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454", + "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0", + "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079", + "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352", + "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f", + "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30", + "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe", + "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113", + "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765", + "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc", + "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e", + "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501", + "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7", + "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2", + "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f", + "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4", + "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524", + "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c", + "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51", + "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840", + "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6", + "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee", + "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e", + "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45", + "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba", + "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d", + "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3", + "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10", + "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e", + "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb", + "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9", + "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a", + "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47", + "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1", + "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3", + "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914", + "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328", + "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6", + "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d", + "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0", + "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94", + "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc", + "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2" + ], + "markers": "python_version >= '3.8'", + "version": "==7.4.3" + }, + "cryptography": { + "hashes": [ + "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee", + "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576", + "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d", + "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30", + "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413", + "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb", + "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da", + "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4", + "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd", + "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc", + "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8", + "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1", + "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc", + "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e", + "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8", + "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940", + "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400", + "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7", + "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16", + "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278", + "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74", + "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec", + "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1", + "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2", + "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c", + "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922", + "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a", + "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6", + "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1", + "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e", + "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", + "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7" + ], + "markers": "python_version >= '3.7'", + "version": "==42.0.5" + }, + "dill": { + "hashes": [ + "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", + "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7" + ], + "markers": "python_version >= '3.11'", + "version": "==0.3.8" + }, + "execnet": { + "hashes": [ + "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41", + "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.2" + }, + "fakeredis": { + "hashes": [ + "sha256:5d1b113a92c1e5dd6e8055008d9204ace4c125e104f04ac08cca4296bc6c78d4", + "sha256:773bd03c38fe745c0c03c5b4ebb92521a25d3306f903c0ca65706bf65cf19e2a" + ], + "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4.0'", + "version": "==2.21.1" + }, + "flake8": { + "hashes": [ + "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132", + "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.1'", + "version": "==7.0.0" + }, + "flake8-datetimez": { + "hashes": [ + "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc", + "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==20.10.0" + }, + "flake8-debugger": { + "hashes": [ + "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf", + "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.2" + }, + "flake8-mock": { + "hashes": [ + "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867", + "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6" + ], + "index": "pypi", + "version": "==0.4" + }, + "flake8-print": { + "hashes": [ + "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9", + "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.0" + }, + "flake8-tuple": { + "hashes": [ + "sha256:8a1b42aab134ef4c3fef13c6a8f383363f158b19fbc165bd91aed9c51851a61d", + "sha256:d828cc8e461c50cacca116e9abb0c9e3be565e8451d3f5c00578c63670aae680" + ], + "index": "pypi", + "version": "==0.4.1" + }, + "flask": { + "hashes": [ + "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e", + "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.0.2" + }, + "freezegun": { + "hashes": [ + "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b", + "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.4.0" + }, + "httmock": { + "hashes": [ + "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9", + "sha256:44eaf4bb59cc64cd6f5d8bf8700b46aa3097cc5651b9bc85c527dfbc71792f41" + ], + "index": "pypi", + "version": "==1.4.0" + }, + "idna": { + "hashes": [ + "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", + "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + ], + "markers": "python_version >= '3.5'", + "version": "==3.6" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "isort": { + "hashes": [ + "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", + "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==5.13.2" + }, + "itsdangerous": { + "hashes": [ + "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", + "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.2" + }, + "jinja2": { + "hashes": [ + "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", + "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.3" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "jsonschema": { + "hashes": [ + "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f", + "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.21.1" + }, + "jsonschema-specifications": { + "hashes": [ + "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", + "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + ], + "markers": "python_version >= '3.8'", + "version": "==2023.12.1" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56", + "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4", + "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8", + "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282", + "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757", + "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424", + "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b", + "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255", + "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70", + "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94", + "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074", + "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c", + "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee", + "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9", + "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9", + "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69", + "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f", + "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3", + "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9", + "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d", + "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977", + "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b", + "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43", + "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658", + "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a", + "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd", + "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83", + "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4", + "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696", + "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05", + "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3", + "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6", + "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895", + "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4", + "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba", + "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03", + "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c" + ], + "markers": "python_version >= '3.8'", + "version": "==1.10.0" + }, + "markupsafe": { + "hashes": [ + "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", + "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", + "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", + "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", + "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", + "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", + "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", + "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", + "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", + "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", + "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", + "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", + "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", + "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", + "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", + "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", + "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", + "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", + "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", + "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", + "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", + "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", + "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", + "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", + "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", + "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", + "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", + "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", + "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", + "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", + "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", + "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", + "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", + "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", + "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", + "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", + "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", + "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", + "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", + "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", + "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", + "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", + "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", + "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", + "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", + "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", + "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", + "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", + "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", + "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", + "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", + "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", + "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", + "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", + "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", + "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", + "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", + "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", + "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", + "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.1.5" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mock": { + "hashes": [ + "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744", + "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==5.1.0" + }, + "moto": { + "hashes": [ + "sha256:71bb832a18b64f10fc4cec117b9b0e2305e5831d9a17eb74f6b9819ed7613843", + "sha256:7e27395e5c63ff9554ae14b5baa41bfe6d6b1be0e59eb02977c6ce28411246de" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==5.0.2" + }, + "mypy": { + "hashes": [ + "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6", + "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d", + "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02", + "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d", + "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3", + "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3", + "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", + "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66", + "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259", + "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", + "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd", + "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", + "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8", + "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", + "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b", + "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e", + "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6", + "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", + "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9", + "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", + "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a", + "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592", + "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218", + "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", + "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4", + "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410", + "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.8.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packaging": { + "hashes": [ + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + ], + "markers": "python_version >= '3.7'", + "version": "==23.2" + }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "pep8": { + "hashes": [ + "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee", + "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374" + ], + "index": "pypi", + "version": "==1.7.1" + }, + "platformdirs": { + "hashes": [ + "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068", + "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768" + ], + "markers": "python_version >= '3.8'", + "version": "==4.2.0" + }, + "pluggy": { + "hashes": [ + "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", + "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be" + ], + "markers": "python_version >= '3.8'", + "version": "==1.4.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" + ], + "markers": "python_version >= '3.8'", + "version": "==2.11.1" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pyflakes": { + "hashes": [ + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" + ], + "markers": "python_version >= '3.8'", + "version": "==3.2.0" + }, + "pylint": { + "hashes": [ + "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87", + "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad" + ], + "index": "pypi", + "markers": "python_full_version >= '3.7.2'", + "version": "==2.17.7" + }, + "pylint-absolute-imports": { + "hashes": [ + "sha256:3ea2bb868626c4662b76cb84d541af4e4cc4a355872fa28def72ad4439b31717", + "sha256:3fa7b73481c50cb7e7892d5eeaecf8108ad0ed06d9a545b5c620d20250a1f727" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "pylint-mccabe": { + "hashes": [ + "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29" + ], + "index": "pypi", + "version": "==0.1.3" + }, + "pylint-quotes": { + "hashes": [ + "sha256:2d6bb3fa8a1a85af3af8a0ca875a719ac5bcdb735c45756284699d809c109c95", + "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==0.2.3" + }, + "pytest": { + "hashes": [ + "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd", + "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==8.0.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6", + "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==4.1.0" + }, + "pytest-flask": { + "hashes": [ + "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e", + "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==1.3.0" + }, + "pytest-mock": { + "hashes": [ + "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f", + "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.12.0" + }, + "pytest-sugar": { + "hashes": [ + "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a", + "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd" + ], + "index": "pypi", + "version": "==1.0.0" + }, + "pytest-xdist": { + "hashes": [ + "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a", + "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.5.0" + }, + "python-dateutil": { + "hashes": [ + "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", + "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.9.0.post0" + }, + "pyyaml": { + "hashes": [ + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + ], + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==6.0.1" + }, + "redis": { + "hashes": [ + "sha256:3f82cc80d350e93042c8e6e7a5d0596e4dd68715babffba79492733e1f367037", + "sha256:4caa8e1fcb6f3c0ef28dba99535101d80934b7d4cd541bbb47f4a3826ee472d1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.0.2" + }, + "referencing": { + "hashes": [ + "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5", + "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7" + ], + "markers": "python_version >= '3.8'", + "version": "==0.33.0" + }, + "requests": { + "hashes": [ + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.31.0" + }, + "responses": { + "hashes": [ + "sha256:01ae6a02b4f34e39bffceb0fc6786b67a25eae919c6368d05eabc8d9576c2a66", + "sha256:2f0b9c2b6437db4b528619a77e5d565e4ec2a9532162ac1a131a83529db7be1a" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.25.0" + }, + "rpds-py": { + "hashes": [ + "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f", + "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c", + "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76", + "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e", + "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157", + "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f", + "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5", + "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05", + "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24", + "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1", + "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8", + "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b", + "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb", + "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07", + "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1", + "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6", + "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e", + "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e", + "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1", + "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab", + "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4", + "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17", + "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594", + "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d", + "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d", + "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3", + "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c", + "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66", + "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f", + "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80", + "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33", + "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f", + "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c", + "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022", + "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e", + "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f", + "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da", + "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1", + "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688", + "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795", + "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c", + "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98", + "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1", + "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20", + "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307", + "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4", + "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18", + "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294", + "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66", + "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467", + "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948", + "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e", + "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1", + "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0", + "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7", + "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd", + "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641", + "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d", + "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9", + "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1", + "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da", + "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3", + "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa", + "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7", + "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40", + "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496", + "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124", + "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836", + "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434", + "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984", + "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f", + "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6", + "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e", + "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461", + "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c", + "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432", + "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73", + "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58", + "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88", + "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337", + "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7", + "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863", + "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475", + "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3", + "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51", + "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf", + "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024", + "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40", + "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9", + "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec", + "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb", + "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7", + "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861", + "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880", + "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f", + "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd", + "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca", + "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58", + "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e" + ], + "markers": "python_version >= '3.8'", + "version": "==0.18.0" + }, + "s3transfer": { + "hashes": [ + "sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e", + "sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b" + ], + "markers": "python_version >= '3.8'", + "version": "==0.10.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "soupsieve": { + "hashes": [ + "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", + "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" + ], + "markers": "python_version >= '3.8'", + "version": "==2.5" + }, + "termcolor": { + "hashes": [ + "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63", + "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a" + ], + "markers": "python_version >= '3.8'", + "version": "==2.4.0" + }, + "tomlkit": { + "hashes": [ + "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b", + "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3" + ], + "markers": "python_version >= '3.7'", + "version": "==0.12.4" + }, + "types-cachetools": { + "hashes": [ + "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199", + "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.3.0.7" + }, + "types-pyopenssl": { + "hashes": [ + "sha256:a472cf877a873549175e81972f153f44e975302a3cf17381eb5f3d41ccfb75a4", + "sha256:cd990717d8aa3743ef0e73e0f462e64b54d90c304249232d48fece4f0f7c3c6a" + ], + "markers": "python_version >= '3.8'", + "version": "==24.0.0.20240228" + }, + "types-python-dateutil": { + "hashes": [ + "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f", + "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.8.19.20240106" + }, + "types-pytz": { + "hashes": [ + "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3", + "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2024.1.0.20240203" + }, + "types-pyyaml": { + "hashes": [ + "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", + "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" + ], + "version": "==6.0.12.12" + }, + "types-redis": { + "hashes": [ + "sha256:5103d7e690e5c74c974a161317b2d59ac2303cf8bef24175b04c2a4c3486cb39", + "sha256:dc9c45a068240e33a04302aec5655cf41e80f91eecffccbb2df215b2f6fc375d" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.6.0.20240218" + }, + "types-requests": { + "hashes": [ + "sha256:a82807ec6ddce8f00fe0e949da6d6bc1fbf1715420218a9640d695f70a9e5a9b", + "sha256:f1721dba8385958f504a5386240b92de4734e047a08a40751c1654d1ac3349c5" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.31.0.20240218" + }, + "types-simplejson": { + "hashes": [ + "sha256:7d698bd530459f9d7d2e3a52ea61df03060d69d22915b26586fe8e8ac409f049", + "sha256:8b15f2a5cbd24ab78f13d7e045767eeaa674865f246e88f040348ad7e91adde9" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.19.0.20240218" + }, + "typing-extensions": { + "hashes": [ + "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", + "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" + ], + "markers": "python_version >= '3.8'", + "version": "==4.10.0" + }, + "urllib3": { + "hashes": [ + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.7" + }, + "werkzeug": { + "hashes": [ + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.1" + }, + "wrapt": { + "hashes": [ + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + ], + "markers": "python_version >= '3.11'", + "version": "==1.16.0" + }, + "xmltodict": { + "hashes": [ + "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", + "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" + ], + "markers": "python_version >= '3.4'", + "version": "==0.13.0" + } } - } } diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 0659f9f86a..ccf0285b72 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -123,9 +123,11 @@ def load_user(extend_session: bool = True) -> Optional[User]: user_ik_present=USER_IK in cookie_session, eq_session_id_present=EQ_SESSION_ID in cookie_session, session_store_exists=bool(session_store), - session_expiration=session_store.expiration_time.isoformat() - if session_store and session_store.expiration_time - else None, + session_expiration=( + session_store.expiration_time.isoformat() + if session_store and session_store.expiration_time + else None + ), ) cookie_session.pop(USER_IK, None) diff --git a/app/data_models/answer.py b/app/data_models/answer.py index 24295cd950..6e16eaeb38 100644 --- a/app/data_models/answer.py +++ b/app/data_models/answer.py @@ -50,28 +50,27 @@ def to_dict(self) -> dict: @overload -def escape_answer_value(value: ListAnswer) -> ListAnswerEscaped: - ... # pragma: no cover +def escape_answer_value(value: ListAnswer) -> ListAnswerEscaped: ... # pragma: no cover @overload -def escape_answer_value(value: DictAnswer) -> DictAnswerEscaped: - ... # pragma: no cover +def escape_answer_value(value: DictAnswer) -> DictAnswerEscaped: ... # pragma: no cover @overload -def escape_answer_value(value: ListDictAnswer) -> ListDictAnswerEscaped: - ... # pragma: no cover +def escape_answer_value( + value: ListDictAnswer, +) -> ListDictAnswerEscaped: ... # pragma: no cover @overload -def escape_answer_value(value: str) -> Markup: - ... # pragma: no cover +def escape_answer_value(value: str) -> Markup: ... # pragma: no cover @overload -def escape_answer_value(value: Union[None, int, Decimal]) -> Union[None, int, Decimal]: - ... # pragma: no cover +def escape_answer_value( + value: Union[None, int, Decimal] +) -> Union[None, int, Decimal]: ... # pragma: no cover def escape_answer_value( diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index b7ad32bd0d..0ca90df7ca 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -45,12 +45,10 @@ def __iter__(self) -> Iterator[str]: yield from self.items @overload - def __getitem__(self, list_item_index: int) -> str: - ... # pragma: no cover + def __getitem__(self, list_item_index: int) -> str: ... # pragma: no cover @overload - def __getitem__(self, list_item_index: slice) -> list[str]: - ... # pragma: no cover + def __getitem__(self, list_item_index: slice) -> list[str]: ... # pragma: no cover def __getitem__(self, list_item_index: slice | int) -> str | list[str]: return self.items[list_item_index] diff --git a/app/forms/field_handlers/field_handler.py b/app/forms/field_handlers/field_handler.py index b416aa5ee5..38a4ee4c68 100644 --- a/app/forms/field_handlers/field_handler.py +++ b/app/forms/field_handlers/field_handler.py @@ -73,9 +73,9 @@ def get_schema_value( ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes]: if isinstance(schema_element["value"], dict): return self.value_source_resolver.resolve(schema_element["value"]) - schema_element_value: Union[ - ValueSourceEscapedTypes, ValueSourceTypes - ] = schema_element["value"] + schema_element_value: Union[ValueSourceEscapedTypes, ValueSourceTypes] = ( + schema_element["value"] + ) return schema_element_value def get_field(self) -> Field: diff --git a/app/forms/validators.py b/app/forms/validators.py index 4a0b479d95..486ea11680 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -494,9 +494,11 @@ def __call__( raise validators.ValidationError(self.messages["MUTUALLY_EXCLUSIVE"]) if is_mandatory and total_answered < 1: message = format_message_with_title( - self.messages["MANDATORY_CHECKBOX"] - if is_only_checkboxes_or_radios - else self.messages["MANDATORY_QUESTION"], + ( + self.messages["MANDATORY_CHECKBOX"] + if is_only_checkboxes_or_radios + else self.messages["MANDATORY_QUESTION"] + ), self.question_title, ) raise validators.ValidationError(message) diff --git a/app/helpers/url_safe_serializer.py b/app/helpers/url_safe_serializer.py index a8c0634337..7eac6a2deb 100644 --- a/app/helpers/url_safe_serializer.py +++ b/app/helpers/url_safe_serializer.py @@ -6,4 +6,5 @@ def url_safe_serializer() -> URLSafeSerializer: - return URLSafeSerializer(current_app.secret_key, salt=cookie_session[EQ_SESSION_ID]) + # Type Ignore: Secret key is validated on app start up, so it must exist at this point + return URLSafeSerializer(current_app.secret_key, salt=cookie_session[EQ_SESSION_ID]) # type: ignore diff --git a/app/jinja_filters.py b/app/jinja_filters.py index b5cdee9898..aa7f2965ab 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -178,12 +178,10 @@ def get_format_date_range(start_date: Markup, end_date: Markup) -> Markup: @blueprint.app_context_processor -def format_unit_processor() -> ( - dict[ - str, - Callable[[str, int | float | Decimal, UnitLengthType], str], - ] -): +def format_unit_processor() -> dict[ + str, + Callable[[str, int | float | Decimal, UnitLengthType], str], +]: return {"format_unit": format_unit} diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index 19407a9256..b2d6025898 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -143,7 +143,9 @@ def _parse_transforms( transform_args["unresolved_arguments"] = transform["arguments"] for arg_key, arg_value in transform["arguments"].items(): - resolved_value: ValueSourceEscapedTypes | ValueSourceTypes | TransformedValueTypes + resolved_value: ( + ValueSourceEscapedTypes | ValueSourceTypes | TransformedValueTypes + ) if isinstance(arg_value, list): resolved_value = value_source_resolver.resolve_list( diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 1fda8a2778..38541eced8 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -84,33 +84,33 @@ def __init__( self, questionnaire_json: Mapping, language_code: str = DEFAULT_LANGUAGE_CODE ): self._parent_id_map: dict[str, str] = {} - self._list_collector_section_ids_by_list_name: dict[ - str, list[str] - ] = defaultdict(list) + self._list_collector_section_ids_by_list_name: dict[str, list[str]] = ( + defaultdict(list) + ) self._answer_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) self._list_dependencies_map: dict[str, set[Dependent]] = defaultdict(set) - self._when_rules_section_dependencies_by_section: dict[ - str, set[str] - ] = defaultdict(set) - self._when_rules_section_dependencies_by_section_for_progress_value_source: defaultdict[ - str, OrderedSet[str] - ] = defaultdict( + self._when_rules_section_dependencies_by_section: dict[str, set[str]] = ( + defaultdict(set) + ) + self._when_rules_section_dependencies_by_section_for_progress_value_source: ( + defaultdict[str, OrderedSet[str]] + ) = defaultdict( OrderedSet ) - self._when_rules_block_dependencies_by_section_for_progress_value_source: defaultdict[ - str, DependencyDictType - ] = defaultdict( + self._when_rules_block_dependencies_by_section_for_progress_value_source: ( + defaultdict[str, DependencyDictType] + ) = defaultdict( lambda: defaultdict(OrderedSet) ) self.calculation_summary_section_dependencies_by_block: dict[ str, DependencyDictType ] = defaultdict(lambda: defaultdict(OrderedSet)) - self._when_rules_section_dependencies_by_answer: dict[ - str, set[str] - ] = defaultdict(set) - self._when_rules_section_dependencies_by_list: dict[ - str, set[str] - ] = defaultdict(set) + self._when_rules_section_dependencies_by_answer: dict[str, set[str]] = ( + defaultdict(set) + ) + self._when_rules_section_dependencies_by_list: dict[str, set[str]] = ( + defaultdict(set) + ) self._placeholder_transform_section_dependencies_by_block: dict[ str, dict[str, set[str]] ] = defaultdict(lambda: defaultdict(set)) diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index 84dccf0040..b0c18fe887 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -43,9 +43,9 @@ def __init__( self._progress_store = self._questionnaire_store.data_stores.progress_store self._router = router - self.dependent_block_id_by_section_key: Mapping[ - SectionKey, set[str] - ] = defaultdict(set) + self.dependent_block_id_by_section_key: Mapping[SectionKey, set[str]] = ( + defaultdict(set) + ) self.dependent_sections: set[DependentSection] = set() @property @@ -342,8 +342,10 @@ def _capture_section_dependencies_progress_value_source_for_section( """ Captures a unique list of section ids that are dependents of the provided section, for progress value sources. """ - dependent_sections: Iterable = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( - section_id, set() + dependent_sections: Iterable = ( + self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( + section_id, set() + ) ) self._update_section_dependencies(dependent_sections) @@ -353,10 +355,12 @@ def _capture_section_dependencies_progress_value_source_for_block( """ Captures a unique list of section ids that are dependents of the provided block, for progress value sources. """ - dependent_sections: Iterable = self._schema.when_rules_block_dependencies_by_section_for_progress_value_source.get( - section_id, {} - ).get( - block_id, set() + dependent_sections: Iterable = ( + self._schema.when_rules_block_dependencies_by_section_for_progress_value_source.get( + section_id, {} + ).get( + block_id, set() + ) ) self._update_section_dependencies(dependent_sections) @@ -411,8 +415,10 @@ def _evaluate_dependents( if self.update_section_status( is_complete=is_path_complete, section_key=dependent_section.section_key ): - dependents_of_dependent: OrderedSet = self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( - dependent_section.section_id, OrderedSet() + dependents_of_dependent: OrderedSet = ( + self._schema.when_rules_section_dependencies_by_section_for_progress_value_source.get( + dependent_section.section_id, OrderedSet() + ) ) for dependent_section_id in dependents_of_dependent: if repeating_list := self._schema.get_repeating_list_for_section( diff --git a/app/questionnaire/rules/operations_helper.py b/app/questionnaire/rules/operations_helper.py index 40dd7c686a..29d0c9be8e 100644 --- a/app/questionnaire/rules/operations_helper.py +++ b/app/questionnaire/rules/operations_helper.py @@ -3,6 +3,7 @@ The methods here invoke operations methods, so it can be imported in placeholder transformer this is a temporary solution until placeholder transformer is refactored. """ + from datetime import date from typing import TYPE_CHECKING, Optional diff --git a/app/questionnaire/rules/utils.py b/app/questionnaire/rules/utils.py index c23d2d4769..7a8ca7e836 100644 --- a/app/questionnaire/rules/utils.py +++ b/app/questionnaire/rules/utils.py @@ -9,13 +9,11 @@ def parse_iso_8601_datetime(iso_8601_date_string: str) -> datetime: @overload -def parse_datetime(date_string: None) -> None: - ... # pragma: no cover +def parse_datetime(date_string: None) -> None: ... # pragma: no cover @overload -def parse_datetime(date_string: str) -> datetime: - ... # pragma: no cover +def parse_datetime(date_string: str) -> datetime: ... # pragma: no cover def parse_datetime(date_string: Optional[str]) -> Optional[datetime]: diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index c4050e6f16..7b903bcb03 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -223,9 +223,11 @@ def _resolve_progress_value_source( block_id=identifier, section_key=SectionKey( section_id=section_id_for_block, - list_item_id=self.location.list_item_id - if self.location.section_id == section_id_for_block - else None, + list_item_id=( + self.location.list_item_id + if self.location.section_id == section_id_for_block + else None + ), ), ) diff --git a/app/routes/errors.py b/app/routes/errors.py index da309490d9..4b182e6cd5 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -102,10 +102,9 @@ def bad_request( @errors_blueprint.app_errorhandler(NoQuestionnaireStateException) @errors_blueprint.app_errorhandler(Unauthorized) def unauthorized( - exception: CSRFError - | NoTokenException - | NoQuestionnaireStateException - | Unauthorized, + exception: ( + CSRFError | NoTokenException | NoQuestionnaireStateException | Unauthorized + ), ) -> tuple[str, int]: log_exception(exception, 401) return _render_error_page(401, template="401") @@ -198,9 +197,11 @@ def too_many_feedback_requests( @errors_blueprint.app_errorhandler(MissingSupplementaryDataKey) @errors_blueprint.app_errorhandler(InvalidSupplementaryData) def supplementary_data_request_failed( - exception: SupplementaryDataRequestFailed - | MissingSupplementaryDataKey - | InvalidSupplementaryData, + exception: ( + SupplementaryDataRequestFailed + | MissingSupplementaryDataKey + | InvalidSupplementaryData + ), ) -> tuple[str, int]: log_exception(exception, 500) return _render_error_page(500, template=500) diff --git a/app/setup.py b/app/setup.py index d13a3e31f1..9efd794737 100644 --- a/app/setup.py +++ b/app/setup.py @@ -466,9 +466,10 @@ def add_blueprints(application): def setup_secure_cookies(application): - application.secret_key = application.eq["secret_store"].get_secret_by_name( - "EQ_SECRET_KEY" - ) + secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") + if not secret_key: + raise ValueError("Application secret key does not exist") + application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/storage/dynamodb.py b/app/storage/dynamodb.py index 567ab6ed43..382233f4e3 100644 --- a/app/storage/dynamodb.py +++ b/app/storage/dynamodb.py @@ -17,9 +17,9 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: put_kwargs: dict = {"Item": storage_model.serialize(model)} if not overwrite: - put_kwargs[ - "ConditionExpression" - ] = f"attribute_not_exists({storage_model.key_field})" + put_kwargs["ConditionExpression"] = ( + f"attribute_not_exists({storage_model.key_field})" + ) try: response = table.put_item(**put_kwargs)["ResponseMetadata"][ diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 3ea45f4d5c..700a61f344 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-02-06 15:03+0000\n" +"POT-Creation-Date: 2024-02-23 11:00+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.13.1\n" +"Generated-By: Babel 2.14.0\n" #: app/forms/validators.py:389 app/jinja_filters.py:111 #, python-format @@ -249,55 +249,55 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:160 +#: app/routes/errors.py:159 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:163 +#: app/routes/errors.py:162 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:181 +#: app/routes/errors.py:180 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:184 +#: app/routes/errors.py:183 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:232 +#: app/routes/errors.py:233 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:239 +#: app/routes/errors.py:240 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:242 app/routes/errors.py:267 app/routes/errors.py:289 +#: app/routes/errors.py:243 app/routes/errors.py:268 app/routes/errors.py:290 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:263 +#: app/routes/errors.py:264 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:264 +#: app/routes/errors.py:265 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:285 templates/errors/403.html:3 +#: app/routes/errors.py:286 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:286 +#: app/routes/errors.py:287 msgid "You can try to submit your feedback again." msgstr "" diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index a6c0c61b9f..6ab07ca0a4 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -82,9 +82,11 @@ def build_groups_for_section( return_to=return_to, return_to_block_id=return_to_block_id, return_to_list_item_id=self.return_location.return_to_list_item_id, - return_to_answer_id=self.return_location.return_to_answer_id - if self.return_location.return_to == "grand-calculated-summary" - else None, + return_to_answer_id=( + self.return_location.return_to_answer_id + if self.return_location.return_to == "grand-calculated-summary" + else None + ), ), ).serialize() for group in section["groups"] @@ -103,11 +105,13 @@ def build_view_context(self) -> dict[str, dict]: formatted_total = self._get_formatted_total( groups=groups or [], - calculation=ValueSourceResolver.get_calculation_operator( - calculation["calculation_type"] - ) - if calculation.get("answers_to_calculate") - else calculation["operation"], + calculation=( + ValueSourceResolver.get_calculation_operator( + calculation["calculation_type"] + ) + if calculation.get("answers_to_calculate") + else calculation["operation"] + ), ) return self._build_formatted_summary( diff --git a/app/views/contexts/grand_calculated_summary_context.py b/app/views/contexts/grand_calculated_summary_context.py index 0931d969e3..daec4ccd80 100644 --- a/app/views/contexts/grand_calculated_summary_context.py +++ b/app/views/contexts/grand_calculated_summary_context.py @@ -115,10 +115,10 @@ def build_view_context(self) -> dict[str, dict]: answer_format = self._schema.get_answer_format_for_calculated_summary( calculated_summary_ids[0] ) - answer_format[ - "decimal_places" - ] = self._schema.get_decimal_limit_from_calculated_summaries( - calculated_summary_ids + answer_format["decimal_places"] = ( + self._schema.get_decimal_limit_from_calculated_summaries( + calculated_summary_ids + ) ) formatted_total = self._format_total(answer_format=answer_format, total=total) diff --git a/app/views/handlers/content.py b/app/views/handlers/content.py index 317eb92c6f..1c192fbbb5 100644 --- a/app/views/handlers/content.py +++ b/app/views/handlers/content.py @@ -29,13 +29,15 @@ def rendered_block(self) -> dict: def get_context(self) -> dict: return { "block": self.rendered_block, - "individual_response_url": individual_response_url( - self._schema.get_individual_response_list(), - self._current_location.list_item_id, # type: ignore - self._questionnaire_store, - ) - if self._is_block_first_block_in_individual_response() - else None, + "individual_response_url": ( + individual_response_url( + self._schema.get_individual_response_list(), + self._current_location.list_item_id, # type: ignore + self._questionnaire_store, + ) + if self._is_block_first_block_in_individual_response() + else None + ), } def _get_content_title(self, transformed_block: ImmutableDict) -> str | None: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index a49bdcfb25..68124b58b2 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -81,9 +81,9 @@ def _person_name_placeholder(list_name: str) -> list[Mapping]: @staticmethod def _person_name_placeholder_possessive(list_name: str) -> list[Mapping]: - name_transforms: list[ - Mapping - ] = IndividualResponseHandler._person_name_transforms(list_name) + name_transforms: list[Mapping] = ( + IndividualResponseHandler._person_name_transforms(list_name) + ) return [ { "placeholder": "person_name_possessive", diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index ea6de60cd2..ad18cbf844 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -163,9 +163,9 @@ def _get_answer_action(self) -> dict | None: def get_context(self) -> dict[str, dict]: context = build_question_context(self.rendered_block, self.form) context["return_to_hub_url"] = self.get_return_to_hub_url() - context[ - "last_viewed_question_guidance" - ] = self.get_last_viewed_question_guidance_context() + context["last_viewed_question_guidance"] = ( + self.get_last_viewed_question_guidance_context() + ) if "list_summary" in self.rendered_block: context.update(self.get_list_summary_context()) diff --git a/app/views/handlers/relationships/relationship_collector.py b/app/views/handlers/relationships/relationship_collector.py index c5f09cabad..e4e79d54ac 100644 --- a/app/views/handlers/relationships/relationship_collector.py +++ b/app/views/handlers/relationships/relationship_collector.py @@ -57,12 +57,12 @@ def _resolve_custom_page_title_vars(self) -> MutableMapping: page_title_vars = super()._resolve_custom_page_title_vars() if to_list_item_position := self.current_location.to_list_item_id: # type: ignore - page_title_vars[ - "to_list_item_position" - ] = self._questionnaire_store.data_stores.list_store.list_item_position( - # Type ignore: list_name populated at this stage - self.current_location.list_name, # type: ignore - to_list_item_position, + page_title_vars["to_list_item_position"] = ( + self._questionnaire_store.data_stores.list_store.list_item_position( + # Type ignore: list_name populated at this stage + self.current_location.list_name, # type: ignore + to_list_item_position, + ) ) return page_title_vars diff --git a/setup.cfg b/setup.cfg index 2518f8258d..7dcd5b8506 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,4 +10,4 @@ filterwarnings= # Ignore node_modules and cloned repos when not in a virtual environment exclude = node_modules/*,tests/*,src/* max-line-length = 160 -ignore = C815,C816,W503,E203 +ignore = C815,C816,W503,E203,E704 diff --git a/tests/app/conftest.py b/tests/app/conftest.py index e849d59867..310f397a3e 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -183,7 +183,11 @@ def data_stores(): def publisher(mocker): mocker.patch( "app.publisher.publisher.google.auth._default._get_explicit_environ_credentials", - return_value=(mocker.Mock(), "test-project-id"), + return_value=(mocker.Mock(universe_domain="test"), "test-project-id"), + ) + mocker.patch( + "google.pubsub_v1.services.publisher.client.PublisherClient._compare_universes", + return_value=True, ) return PubSubPublisher() diff --git a/tests/app/questionnaire/test_questionnaire_store_updater.py b/tests/app/questionnaire/test_questionnaire_store_updater.py index b4ca4f52d9..cbe1d3e4b3 100644 --- a/tests/app/questionnaire/test_questionnaire_store_updater.py +++ b/tests/app/questionnaire/test_questionnaire_store_updater.py @@ -1551,9 +1551,11 @@ def test_questionnaire_store_updater_dependency_capture( { "section_id": f"section-{idx}", "block_ids": ["block-1", "block-2"], - "status": CompletionStatus.IN_PROGRESS - if f"section-{idx}" in status_unchanged_section_ids - else CompletionStatus.COMPLETED, + "status": ( + CompletionStatus.IN_PROGRESS + if f"section-{idx}" in status_unchanged_section_ids + else CompletionStatus.COMPLETED + ), } for idx in range(1, 8) ], diff --git a/tests/app/storage/conftest.py b/tests/app/storage/conftest.py index a34869df51..dc7c15c253 100644 --- a/tests/app/storage/conftest.py +++ b/tests/app/storage/conftest.py @@ -5,7 +5,7 @@ import fakeredis import pytest from flask import current_app -from moto import mock_dynamodb +from moto import mock_aws from app.data_models.app_models import EQSession, QuestionnaireState from app.storage.dynamodb import Dynamodb @@ -16,8 +16,8 @@ @pytest.fixture def dynamodb(): - with mock_dynamodb() as mock_dynamo: - mock_dynamo.start() + with mock_aws() as mocked_aws_environment: + mocked_aws_environment.start() boto3_client = boto3.resource("dynamodb", endpoint_url=None) for config in StorageModel.TABLE_CONFIG_BY_TYPE.values(): table_name = current_app.config[config["table_name_key"]] @@ -36,7 +36,7 @@ def dynamodb(): }, ) yield Dynamodb(boto3_client) - mock_dynamo.stop() + mocked_aws_environment.stop() @pytest.fixture diff --git a/tests/app/test_setup.py b/tests/app/test_setup.py index ffecdfa1b8..695ee19298 100644 --- a/tests/app/test_setup.py +++ b/tests/app/test_setup.py @@ -1,6 +1,8 @@ import pytest +from mock import MagicMock from app.helpers import get_span_and_trace +from app.setup import setup_secure_cookies @pytest.mark.parametrize( @@ -26,3 +28,16 @@ def test_get_span_and_trace(cloud_trace_header, expected_trace, expected_span): span, trace = get_span_and_trace(cloud_trace_header) assert trace == expected_trace assert span == expected_span + + +def test_setup_secure_cookies_missing_secret_key(): + app = MagicMock() + app.eq = {"secret_store": MagicMock()} + app.secret_key = None + + app.eq["secret_store"].get_secret_by_name.return_value = None + + with pytest.raises(ValueError): + setup_secure_cookies(app) + + assert app.secret_key is None diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index a845121917..c7b67b85bb 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -113,9 +113,11 @@ def test_build_view_context_for_currency_calculated_summary( language=language, schema=test_calculated_summary_schema, data_stores=DataStores( - answer_store=test_calculated_summary_answers_skipped_fourth - if skip_fourth - else test_calculated_summary_answers + answer_store=( + test_calculated_summary_answers_skipped_fourth + if skip_fourth + else test_calculated_summary_answers + ) ), routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), current_location=Location(section_id="default-section", block_id=block_id), diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index b4a2003cc1..714b378515 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -289,9 +289,9 @@ def test_defaults_to_adding_the_log_publisher_to_the_application(self): def test_adds_cloud_task_publisher_to_the_application(self): self._setting_overrides["EQ_SUBMISSION_CONFIRMATION_BACKEND"] = "cloud-tasks" - self._setting_overrides[ - "EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME" - ] = "test" + self._setting_overrides["EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME"] = ( + "test" + ) # When with patch( @@ -306,9 +306,9 @@ def test_adds_cloud_task_publisher_to_the_application(self): def test_submission_backend_not_set_raises_exception(self): # Given self._setting_overrides["EQ_SUBMISSION_CONFIRMATION_BACKEND"] = "" - self._setting_overrides[ - "EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME" - ] = "test" + self._setting_overrides["EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME"] = ( + "test" + ) # When with patch( From aae666a22f841a73f6ca0f8a349abac80b0d322d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 8 Mar 2024 10:06:33 +0000 Subject: [PATCH 386/567] Add automatic integration test generation script (#1316) Co-authored-by: Liam Toozer --- Makefile | 5 + Pipfile | 1 + Pipfile.lock | 124 +++++++++++++++++---- README.md | 5 + app/translations/messages.pot | 22 ++-- mypy.ini | 5 + scripts/README.md | 40 +++++++ scripts/generate_integration_test.py | 146 +++++++++++++++++++++++++ scripts/merge_profiles.py | 3 +- scripts/run_mypy.sh | 2 +- tests/integration/test_header_links.py | 2 +- 11 files changed, 322 insertions(+), 33 deletions(-) create mode 100644 scripts/README.md create mode 100644 scripts/generate_integration_test.py diff --git a/Makefile b/Makefile index 9f01ddaae9..239cf0ac3c 100644 --- a/Makefile +++ b/Makefile @@ -116,3 +116,8 @@ dev-compose-down-linux: profile: pipenv run python profile_application.py + +generate-integration-test: + pipenv run playwright install chromium + pipenv run python -m scripts.generate_integration_test + pipenv run black ./scripts/test_* diff --git a/Pipfile b/Pipfile index 1edd8c5a6a..07b6148af8 100644 --- a/Pipfile +++ b/Pipfile @@ -38,6 +38,7 @@ types-python-dateutil = "*" pytest-mock = "*" types-cachetools = "*" types-pytz = "*" +playwright = "*" [packages] colorama = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ae499cb591..bf4b748975 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b857953d9c0ba947a86596e0beb665969ae68afee425c3350a0dd4049a927daa" + "sha256": "408dbd7bc01df9a590c2f82b3818b9cc68d7e98e5f11b78350f75d87d706ee4c" }, "pipfile-spec": 6, "requires": { @@ -43,20 +43,20 @@ }, "boto3": { "hashes": [ - "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714", - "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba" + "sha256:c26c31ceeeb2bc5d2bb96ba0fdc9a04d7b10e6e0b081c55b9cea9069a0be04dd", + "sha256:f8046e3e2d1186a49b49f7464c4811c265c86001f404dd1a96c4365c773a4245" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.56" + "version": "==1.34.57" }, "botocore": { "hashes": [ - "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f", - "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec" + "sha256:9a5aa2034de9f0c367b4b61a92af0fa827f5c21affa19e0a284838a142e71083", + "sha256:c8dafe0ad378a88bcf4153e6972870b03fb5aab406b694202307500709940baf" ], "markers": "python_version >= '3.8'", - "version": "==1.34.56" + "version": "==1.34.57" }, "brotli": { "hashes": [ @@ -608,12 +608,12 @@ }, "google-cloud-pubsub": { "hashes": [ - "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188", - "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684" + "sha256:06dd62181e2f248f32b9077f4dc07b413191a84fc06d7323b208602d887207bc", + "sha256:b6d06f1827968273c42b57a09f642462649c9504dc0f8756f99770f4e3e755ad" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.20.0" + "version": "==2.20.1" }, "google-cloud-storage": { "hashes": [ @@ -944,11 +944,11 @@ }, "jwcrypto": { "hashes": [ - "sha256:59e7d5e4589d1b07170f368e20c32eb32a023911806a9723b1f43a0d8b3028d6", - "sha256:c18b10b2049603bef3ae7b77ad14bded431a9077d113447d62bebd8550b0d5bd" + "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789", + "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039" ], "markers": "python_version >= '3.8'", - "version": "==1.5.5" + "version": "==1.5.6" }, "markupsafe": { "hashes": [ @@ -1534,20 +1534,20 @@ }, "boto3": { "hashes": [ - "sha256:300888f0c1b6f32f27f85a9aa876f50f46514ec619647af7e4d20db74d339714", - "sha256:b26928f9a21cf3649cea20a59061340f3294c6e7785ceb6e1a953eb8010dc3ba" + "sha256:c26c31ceeeb2bc5d2bb96ba0fdc9a04d7b10e6e0b081c55b9cea9069a0be04dd", + "sha256:f8046e3e2d1186a49b49f7464c4811c265c86001f404dd1a96c4365c773a4245" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.56" + "version": "==1.34.57" }, "botocore": { "hashes": [ - "sha256:bffeb71ab21d47d4ecf947d9bdb2fbd1b0bbd0c27742cea7cf0b77b701c41d9f", - "sha256:fff66e22a5589c2d58fba57d1d95c334ce771895e831f80365f6cff6453285ec" + "sha256:9a5aa2034de9f0c367b4b61a92af0fa827f5c21affa19e0a284838a142e71083", + "sha256:c8dafe0ad378a88bcf4153e6972870b03fb5aab406b694202307500709940baf" ], "markers": "python_version >= '3.8'", - "version": "==1.34.56" + "version": "==1.34.57" }, "certifi": { "hashes": [ @@ -1913,6 +1913,70 @@ "markers": "python_version >= '3.7'", "version": "==1.4.0" }, + "greenlet": { + "hashes": [ + "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", + "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", + "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", + "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", + "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", + "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", + "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", + "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", + "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", + "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", + "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", + "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", + "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", + "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", + "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", + "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", + "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", + "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", + "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", + "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", + "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", + "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", + "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", + "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", + "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", + "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", + "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", + "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", + "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", + "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", + "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", + "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", + "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", + "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", + "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", + "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", + "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", + "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", + "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", + "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", + "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", + "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", + "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", + "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", + "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", + "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", + "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", + "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", + "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", + "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", + "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", + "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", + "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", + "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", + "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", + "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", + "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", + "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + ], + "markers": "python_version >= '3.11' and platform_python_implementation == 'CPython'", + "version": "==3.0.3" + }, "httmock": { "hashes": [ "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9", @@ -2197,6 +2261,20 @@ "markers": "python_version >= '3.8'", "version": "==4.2.0" }, + "playwright": { + "hashes": [ + "sha256:283887f0bdd0039c3d720e32fbc73a045c24fa800599a6ad60fb199c29580534", + "sha256:313f2551a772f57c9ccca017c4dd4661f2277166f9e1d84bbf5a2e316f0f892c", + "sha256:4e1fc1c049a0af64626ddd50814d14a01f316bcbb4d1aa83c3416fe420add558", + "sha256:b2a46a24641e5d468046cde567c98fdb8d85e32df901630b14dfb288cbd1ed4f", + "sha256:dbf473496808d4c2c816902c1dee2aabc029648e56ce8514b643f5a1a6fc8e22", + "sha256:e092c6cfbf797bff03fbdfc53c3e6a9e29fbcf6b82f9e43113d37494aee0561b", + "sha256:e2b293f077efeaa45253fde31cea4bc6b0ae8be6b5e65e8ce8b4aa3b9f0d55b6" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==1.42.0" + }, "pluggy": { "hashes": [ "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", @@ -2220,6 +2298,14 @@ ], "version": "==2.21" }, + "pyee": { + "hashes": [ + "sha256:9bcc9647822234f42c228d88de63d0f9ffa881e87a87f9d36ddf5211f6ac977d", + "sha256:a642c51e3885a33ead087286e35212783a4e9b8d6514a10a5db4e57ac57b2b29" + ], + "markers": "python_version >= '3.8'", + "version": "==11.0.1" + }, "pyflakes": { "hashes": [ "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", diff --git a/README.md b/README.md index 46975c88cf..c700ff6c37 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,11 @@ Or set the `GOOGLE_CLOUD_PROJECT` environment variable to your gcp project id. --- + +## Integration Tests +There is a dev-convenience script that auto generates the lines of code for a user journey. See [README](scripts/README.md) for more information and how to run +the script. + ## Frontend Tests The frontend tests use NodeJS to run. To handle different versions of NodeJS it is recommended to install `Node Version Manager` (`nvm`). It is similar to pyenv but for Node versions. diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 700a61f344..fa30249d65 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -249,55 +249,55 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:159 +#: app/routes/errors.py:160 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:162 +#: app/routes/errors.py:163 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:180 +#: app/routes/errors.py:181 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:183 +#: app/routes/errors.py:184 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:233 +#: app/routes/errors.py:232 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:240 +#: app/routes/errors.py:239 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:243 app/routes/errors.py:268 app/routes/errors.py:290 +#: app/routes/errors.py:242 app/routes/errors.py:267 app/routes/errors.py:289 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:264 +#: app/routes/errors.py:263 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:265 +#: app/routes/errors.py:264 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:286 templates/errors/403.html:3 +#: app/routes/errors.py:285 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:287 +#: app/routes/errors.py:286 msgid "You can try to submit your feedback again." msgstr "" diff --git a/mypy.ini b/mypy.ini index 8918828886..0e98cd508a 100644 --- a/mypy.ini +++ b/mypy.ini @@ -106,3 +106,8 @@ no_implicit_optional = True disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True + +[mypy-scripts.generate_integration_test] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..0d055d48bb --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,40 @@ +# Scripts + +## Script to auto-generate code for integration test + +### Details + +To speed up the process of generating integration tests for Runner, there is a dev-convenience script that records the GET and POST requests of a user journey +and outputs this formatted in the style of an integration test. + +### Overview + +* All POSTs are recorded. To ensure only the necessary GET requests are recorded, additional logic excludes the following GET requests: + * Session tokens + * Initial URL requests for each page load +* Additional logic is in place to ensure that, when navigating backwards in a journey after following links (e.g. 'previous' link), it is recorded correctly. + This is achieved by storing the previous request method at module-level so that it can be used in deciding whether to record or disregard the GET request. +* You will need to manually add your assertions in the generated test file +* When the script is launched, it will create a new file for the schema chosen. If you launch the script again for the same schema, it will overwrite the + previous file output +* The script is intended to be run with schemas with a `test_` prefix, which would suit most scenarios for test generation. If you wish to use a schema without + this prefix, you will need to manually amend the generated names for the file, class, and function to allow pytest to process the test file correctly +* It does **not** handle dynamic answers because these are generated at runtime - you will need to update the output script to handle `list_item_id` separately, + as they will not be known beforehand + +### Usage + +Run the following make command from the project root folder: + +```shell +make generate-integration-test +``` + +This will pause the script and open a browser pointing to the Launcher UI (make sure the application and supporting services are running). Now follow the below +steps: + +1. Choose a schema and launch it - the schema name will be used for the name of the integration test output file +1. Navigate through the survey +1. When you're finished with the journey at any point, return to the command line and hit Enter +1. The output will be shown in the logs, and a formatted file will be created for you in the scripts folder. For example: `scripts/test_checkbox.py` +1. Add your assertions to the test file and move the file into the appropriate `test/integration/` location diff --git a/scripts/generate_integration_test.py b/scripts/generate_integration_test.py new file mode 100644 index 0000000000..449bd4997c --- /dev/null +++ b/scripts/generate_integration_test.py @@ -0,0 +1,146 @@ +from typing import IO, Dict +from urllib.parse import parse_qs, urlparse + +from playwright.sync_api import Playwright, Request, sync_playwright +from structlog import get_logger + +logger = get_logger() + +LAUNCHER_ROOT_URL = "http://localhost:8000" +RUNNER_ROOT_URL = "http://localhost:5000" + +TEST_TEMPLATE = """from tests.integration.integration_test_case import IntegrationTestCase + + +class Test{class_name}(IntegrationTestCase): + def test_{function_name}(self): + self.launchSurvey("{schema_name}") +""" + +survey_journey: Dict[str, str | bool | None] = { + "previous_request_method": None, + "in_progress": False, + "schema_name": None, +} + +output: Dict[str, str] = {"file_name": ""} + + +def process_runner_request(request: Request) -> None: + with open(output["file_name"], "a", encoding="utf-8") as file: + if request.method == "POST": + process_post(request, file) + + elif request.method == "GET": + process_get(request, file) + + +def process_post(request: Request, file: IO) -> None: + survey_journey["previous_request_method"] = "POST" + + # Playwright Request.post_data comes formatted like a URL query string, so can be parsed + post_data = parse_qs(request.post_data) + del post_data["csrf_token"] + + items = { + answer_id: answer_values[0] if len(answer_values) == 1 else answer_values + for answer_id, answer_values in post_data.items() + } + + # Post items, or empty post for no answers/non-question pages + file.write(generate_method_request(method="post", data=items or "")) + + +def is_recordable_survey_navigation(request: Request) -> bool: + return ( + survey_journey["previous_request_method"] == "GET" + and "session?token" not in request.url + and request.url != f"{RUNNER_ROOT_URL}/questionnaire/" + ) + + +def process_get(request: Request, file: IO) -> None: + """ + We only want to record GET requests in Runner for actions like navigating back in a survey journey. Therefore, we exclude the following: + - the very first GET action of a survey journey, after schema is loaded + - tokens/authentication + """ + has_journey_started = ( + not survey_journey["in_progress"] + and request.url == f"{RUNNER_ROOT_URL}/questionnaire/" + ) + if has_journey_started: + survey_journey["in_progress"] = True + return + + if is_recordable_survey_navigation(request): + path = f'"{urlparse(request.url).path}"' + file.write(generate_method_request(method="get", data=path)) + + elif survey_journey["in_progress"]: + # ensure the request method is captured - allows us to record Runner GET navigation actions on the next pass through + survey_journey["previous_request_method"] = request.method + + +def process_launcher_request(request: Request) -> None: + if request.method != "GET": + return + + if survey_journey["in_progress"]: + # capture launcher urls for sign-out, save etc + with open(output["file_name"], "a", encoding="utf-8") as file: + path = f'"{urlparse(request.url).path}"' + file.write(generate_method_request(method="get", data=path)) + else: + # start of journey, so create a skeleton file using the schema name + survey_journey["schema_name"] = parse_qs(request.url)["schema_name"][0] + output["file_name"] = f"./scripts/{survey_journey['schema_name']}.py" + + with open(output["file_name"], "w", encoding="utf-8") as file: + # Type ignore: schema_name is taken as string from query string + class_name = survey_journey["schema_name"].title().replace("_", "") # type: ignore + file.write( + TEST_TEMPLATE.format( + class_name=class_name, + function_name=survey_journey["schema_name"], + schema_name=survey_journey["schema_name"], + ) + ) + logger.info(request) + + +def generate_method_request(*, method: str, data: dict | str | None = None) -> str: + snippet = f"self.{method}({data})" + logger.info(f'Generating Runner code snippet for HTTP request: "{snippet}"') + return f"\n {snippet}" + + +def request_handler(request: Request) -> None: + if LAUNCHER_ROOT_URL in request.url: + process_launcher_request(request) + elif RUNNER_ROOT_URL in request.url: + process_runner_request(request) + + +def run(pw: Playwright) -> None: + chromium = pw.chromium + browser = chromium.launch(headless=False, args=["--start-maximized"]) + page = browser.new_page(no_viewport=True) + page.goto(LAUNCHER_ROOT_URL) + + page.on("request", request_handler) + + input( + "Script is paused. Start navigating through the browser for the journey & press Enter when finished to capture" + " the output and add into a test file\n" + ) + browser.close() + logger.info( + "Integration test generated successfully", + integration_test_file=output["file_name"], + ) + + +if __name__ == "__main__": + with sync_playwright() as playwright: + run(playwright) diff --git a/scripts/merge_profiles.py b/scripts/merge_profiles.py index 4f5c4e6ea3..452bb27c59 100644 --- a/scripts/merge_profiles.py +++ b/scripts/merge_profiles.py @@ -15,4 +15,5 @@ else: stats.add(profiling_dir + p) -stats.dump_stats("combined_profile.prof") +if stats: + stats.dump_stats("combined_profile.prof") diff --git a/scripts/run_mypy.sh b/scripts/run_mypy.sh index d3bdb34e1e..27a4f3ec42 100755 --- a/scripts/run_mypy.sh +++ b/scripts/run_mypy.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -pipenv run mypy app +pipenv run mypy app scripts diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index 92a11ff9fc..72d81f5053 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -165,7 +165,7 @@ def test_links_not_in_header_when_no_session(self): self.assert_sign_out_link_does_not_exist() self.assert_help_link_does_not_exist() - def test_links_not_in_header_when_valid_session_theme_social(self): + def test_links_not_in_header_when_valid_session_theme_social_thank_you_page(self): # Given self.launchSurveyV2(schema_name="test_theme_social", theme="social") self.post() From 3f9eb50d35b57d7b0bf6efb8232b88280556621e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:12:04 +0000 Subject: [PATCH 387/567] Add backup name env var handling in restore CI task (#1324) --- ci/restore_questionnaire_state.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/restore_questionnaire_state.yaml b/ci/restore_questionnaire_state.yaml index 68e9f30fbc..8c03b5959b 100644 --- a/ci/restore_questionnaire_state.yaml +++ b/ci/restore_questionnaire_state.yaml @@ -17,6 +17,12 @@ run: args: - -exc - | + PARAMS=('PROJECT_ID' 'BUCKET_NAME' 'BACKUP_NAME' 'FILE_PREFIX') + for PARAM in "${PARAMS[@]}" + do + : "${!PARAM:?"Error: $PARAM must be set"}" + done + export GOOGLE_APPLICATION_CREDENTIALS=~/gcloud-service-key.json cat >$GOOGLE_APPLICATION_CREDENTIALS < Date: Wed, 13 Mar 2024 13:55:03 +0000 Subject: [PATCH 388/567] Schemas v4.5.0 (#1335) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a4022d8ae8..c2d2cb0bf0 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.4.1 +v4.5.0 From 84c2409ff2ed7c7ee3704502b3d6183114996ab7 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:53:13 +0000 Subject: [PATCH 389/567] Fix placeholder transform resolution issue in calc summary pages (#1330) --- app/questionnaire/questionnaire_schema.py | 77 ++- ...pendencies_with_calculation_summaries.json | 509 ++++++++++++++++++ .../test_questionnaire_schema.py | 21 + .../test_questionnaire_calculated_summary.py | 80 +++ 4 files changed, 669 insertions(+), 18 deletions(-) create mode 100644 schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 38541eced8..2ba5cacd03 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -1318,7 +1318,9 @@ def _populate_calculation_summary_section_dependencies(self) -> None: block["id"] ].update(section_dependencies) - def _get_section_ids_for_answer_ids(self, answer_ids: set[str]) -> OrderedSet[str]: + def _get_section_ids_for_answer_ids( + self, answer_ids: Iterable[str] + ) -> OrderedSet[str]: section_dependencies: OrderedSet[str] = OrderedSet() for answer_id in answer_ids: block = self.get_block_for_answer_id(answer_id) @@ -1385,26 +1387,53 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None: if item["for_list"] == list_name and item.get("item_anchor_answer_id"): return f"#{str(item['item_anchor_answer_id'])}" + def _update_dependencies_for_placeholders( + self, answer_ids: Iterable[str], block: ImmutableDict + ) -> None: + if dependent_sections := self._get_section_ids_for_answer_ids( + answer_ids=answer_ids + ): + # Type Ignore: At this point the section id and block id cannot be None + section_id = self.get_section_id_for_block_id(block["id"]) + self._placeholder_transform_section_dependencies_by_block[section_id][ # type: ignore + block["id"] + ].update( + dependent_sections + ) + def _populate_placeholder_transform_section_dependencies(self) -> None: for block in self.get_blocks(): - transforms = get_mappings_with_key("transform", data=block) - placeholder_answer_ids = { - item["identifier"] - for transform in transforms - if transform["transform"] in TRANSFORMS_REQUIRING_ROUTING_PATH - for item in transform["arguments"]["items"] - if item.get("source") == "answers" - } - placeholder_dependencies = self._get_section_ids_for_answer_ids( - answer_ids=placeholder_answer_ids - ) - if placeholder_dependencies: - # Type Ignore: At this point we section id and block id cannot be None - section_id = self.get_section_id_for_block_id(block["id"]) - self._placeholder_transform_section_dependencies_by_block[section_id][ # type: ignore + # Calculation summary blocks can indirectly reference placeholders from their dependent blocks, so + # logic is needed to identify the relevant dependent blocks that may contain placeholders and assess + # whether there are any dependent sections + if block["type"] == "GrandCalculatedSummary": + answer_ids = self.get_answer_ids_for_grand_calculated_summary_id( block["id"] - ].update( - placeholder_dependencies + ) + elif block["type"] == "CalculatedSummary": + answer_ids = get_calculated_summary_answer_ids(block) + else: + answer_ids = [] + + if answer_ids: + dependent_blocks = [ + self.get_block_for_answer_id(answer_id) + for answer_id in set(answer_ids) + ] + for dependent_block in dependent_blocks: + # Type ignore: Block will exist at this point + if placeholder_answer_ids := get_placeholder_answer_ids_requiring_routing_path( + dependent_block # type: ignore + ): + self._update_dependencies_for_placeholders( + placeholder_answer_ids, block + ) + + elif placeholder_answer_ids := get_placeholder_answer_ids_requiring_routing_path( + block + ): + self._update_dependencies_for_placeholders( + placeholder_answer_ids, block ) def update_dependencies_for_dynamic_answers( @@ -1470,3 +1499,15 @@ def get_calculated_summary_ids_for_grand_calculated_summary( def is_list_collector_block_editable(block: Mapping) -> bool: return bool(block["type"] == "ListCollector") + + +def get_placeholder_answer_ids_requiring_routing_path(block: ImmutableDict) -> set[str]: + transforms = get_mappings_with_key("transform", data=block) + + return { + item["identifier"] + for transform in transforms + if transform["transform"] in TRANSFORMS_REQUIRING_ROUTING_PATH + for item in transform["arguments"]["items"] + if item.get("source") == "answers" + } diff --git a/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json b/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json new file mode 100644 index 0000000000..ae0785897e --- /dev/null +++ b/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json @@ -0,0 +1,509 @@ +{ + "language": "en", + "mime_type": "application/json/ons/eq", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test placeholder dependencies in calculated summary pages", + "questionnaire_flow": { + "type": "Hub", + "options": { + "required_completed_sections": ["reporting-period-section"] + } + }, + "sections": [ + { + "id": "reporting-period-section", + "title": "Reporting period", + "summary": { + "show_on_completion": false, + "page_title": "Reporting period", + "collapsible": false + }, + "show_on_hub": true, + "groups": [ + { + "id": "reporting-period-group", + "blocks": [ + { + "id": "reporting-date", + "type": "Question", + "question": { + "id": "reporting-date-question", + "title": { + "text": "Are you able to report for the calendar year, {ref_p_start_date} to {ref_p_end_date}?", + "placeholders": [ + { + "placeholder": "ref_p_start_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_start_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ref_p_end_date", + "transforms": [ + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "metadata", + "identifier": "ref_p_end_date" + }, + "date_format": "d MMMM yyyy" + } + } + ] + } + ] + }, + "type": "General", + "answers": [ + { + "id": "reporting-date-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes, I can report for this period", + "value": "Yes, I can report for this period" + }, + { + "label": "No, I need to report for a different period", + "value": "No, I need to report for a different period" + } + ] + } + ] + }, + "page_title": "Calendar year reporting period", + "routing_rules": [ + { + "block": "undertake-rnd", + "when": { + "in": [ + { + "identifier": "reporting-date-answer", + "source": "answers" + }, + ["Yes, I can report for this period"] + ] + } + }, + { + "block": "enter-dates" + } + ] + }, + { + "id": "enter-dates", + "type": "Question", + "question": { + "id": "enter-dates-question", + "title": "What dates will you be reporting for?", + "type": "DateRange", + "answers": [ + { + "id": "date-from", + "type": "Date", + "mandatory": true, + "label": "From" + }, + { + "id": "date-to", + "type": "Date", + "mandatory": true, + "label": "To" + } + ], + "period_limits": { + "minimum": { + "months": 3 + }, + "maximum": { + "months": 18 + } + } + }, + "page_title": "Alternative reporting period" + }, + { + "id": "undertake-rnd", + "type": "Question", + "question": { + "id": "undertake-rnd-question", + "title": "For the reporting period, did your business undertake any in-house R&D?", + "type": "General", + "answers": [ + { + "id": "undertake-rnd-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes" + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "page_title": "In-house R&D for reporting period" + } + ] + } + ] + }, + { + "id": "questions-section", + "title": "In-house R&D", + "summary": { + "show_on_completion": false, + "page_title": "In-house R&D", + "collapsible": false + }, + "show_on_hub": true, + "groups": [ + { + "id": "questions-group", + "blocks": [ + { + "id": "how-much-rnd", + "type": "Question", + "question": { + "id": "how-much-rnd-question", + "title": { + "text": "For the period {from} to {to} what was the expenditure on R&D for {ru_name}?", + "placeholders": [ + { + "placeholder": "from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-from", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-to", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "Calculated", + "answers": [ + { + "id": "civil-research", + "mandatory": true, + "type": "Currency", + "label": "Civil Research and Development", + "description": "Enter a value to the nearest thousand (e.g. 56,000).", + "decimal_places": 0, + "currency": "GBP" + }, + { + "id": "defence", + "mandatory": true, + "type": "Currency", + "label": "Defence Research and Development", + "description": "Enter a value to the nearest thousand (e.g. 56,000).", + "decimal_places": 0, + "currency": "GBP" + } + ], + "calculations": [ + { + "calculation_type": "sum", + "answers_to_calculate": ["civil-research", "defence"], + "conditions": ["greater than"], + "value": 0 + } + ] + }, + "page_title": "In-house expenditure on R&D" + }, + { + "id": "calc-summary-1", + "type": "CalculatedSummary", + "page_title": "Total in-house expenditure on R&D", + "title": "We have calculated your total in-house expenditure on R&D to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "identifier": "civil-research", + "source": "answers" + }, + { + "identifier": "defence", + "source": "answers" + } + ] + }, + "title": "Total in-house expenditure on R&D" + } + }, + { + "id": "how-much-rnd-2", + "type": "Question", + "question": { + "id": "how-much-rnd-question-2", + "title": { + "text": "For the period {from} to {to} what was the expenditure on R&D for {ru_name}?", + "placeholders": [ + { + "placeholder": "from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-from", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-to", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, + "type": "Calculated", + "answers": [ + { + "id": "innovation", + "mandatory": true, + "type": "Currency", + "label": "Innovation", + "description": "Enter a value to the nearest thousand (e.g. 56,000).", + "decimal_places": 0, + "currency": "GBP" + }, + { + "id": "software", + "mandatory": true, + "type": "Currency", + "label": "Software Development", + "description": "Enter a value to the nearest thousand (e.g. 56,000).", + "decimal_places": 0, + "currency": "GBP" + } + ], + "calculations": [ + { + "calculation_type": "sum", + "answers_to_calculate": ["innovation", "software"], + "conditions": ["greater than"], + "value": 0 + } + ] + }, + "page_title": "In-house expenditure on R&D" + }, + { + "id": "calc-summary-2", + "type": "CalculatedSummary", + "page_title": "Total in-house expenditure on R&D - Part Two", + "title": "We have calculated your total in-house expenditure on R&D to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "identifier": "innovation", + "source": "answers" + }, + { + "identifier": "software", + "source": "answers" + } + ] + }, + "title": "Total in-house expenditure on R&D - Part Two" + } + }, + { + "type": "GrandCalculatedSummary", + "id": "rnd-grand-calculated-summary", + "title": "We have calculated the grand total of in-house expenditure on R&D to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "source": "calculated_summary", + "identifier": "calc-summary-1" + }, + { + "source": "calculated_summary", + "identifier": "calc-summary-2" + } + ] + }, + "title": "Grand Total in-house expenditure on R&D" + } + } + ] + } + ], + "enabled": { + "when": { + "in": [ + { + "identifier": "undertake-rnd-answer", + "source": "answers" + }, + ["Yes"] + ] + } + } + } + ], + "theme": "business", + "navigation": { + "visible": false + }, + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + }, + { + "name": "ru_ref", + "type": "string" + }, + { + "name": "trad_as", + "type": "string", + "optional": true + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ref_p_start_date", + "type": "date" + }, + { + "name": "ref_p_end_date", + "type": "date" + } + ] +} diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 4b25ee8a77..08719b8189 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -1090,3 +1090,24 @@ def test_grand_calculated_summary_when_rule_dependencies(): "q2-a2": {"section-4"}, } ) + + +def test_placeholder_transform_section_dependencies_by_block_for_calculation_summaries(): + """ + Ensures that dependencies are captured correctly for calculation summary blocks using transforms. + In this schema the calculation summaries use placeholder transforms based on other blocks that have dependencies in the + reporting-period-section + """ + schema = load_schema_from_name( + "test_placeholder_dependencies_with_calculation_summaries" + ) + + assert schema.placeholder_transform_section_dependencies_by_block == { + "questions-section": { + "calc-summary-1": {"reporting-period-section"}, + "calc-summary-2": {"reporting-period-section"}, + "how-much-rnd": {"reporting-period-section"}, + "how-much-rnd-2": {"reporting-period-section"}, + "rnd-grand-calculated-summary": {"reporting-period-section"}, + } + } diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 74cf33c39a..b68dbe87e2 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -350,3 +350,83 @@ def test_calculated_summary_with_varying_decimal_places(self): ) self.post() self.assertInBody("Total currency values: £120.58985") + + def test_placeholder_rendering_in_calculated_summary_label(self): + """ + Tests that a placeholder using the first_non_empty_item is rendered correctly on the calculated summary page + using the answer values that are on the path. In this instance it is the happy path where the user has entered + their own reporting dates which should be reflected on the calcualted summary label. + """ + self.launchSurvey("test_placeholder_dependencies_with_calculation_summaries") + + self.post( + {"reporting-date-answer": "No, I need to report for a different period"} + ) + self.post( + { + "date-from-day": "1", + "date-from-month": "1", + "date-from-year": "2000", + "date-to-day": "1", + "date-to-month": "4", + "date-to-year": "2000", + } + ) + self.post({"undertake-rnd-answer": "Yes"}) + self.post() + self.post({"civil-research": "10", "defence": "10"}) + self.assertInUrl("/questionnaire/calc-summary-1/") + self.assertInBody( + "For the period 1 January 2000 to 1 April 2000 what was the expenditure on R&D for Integration Testing?" + ) + + def test_placeholder_rendering_in_calculated_summary_label_unhappy_path(self): + """ + Tests that a placeholder using the first_non_empty_item is rendered correctly on the calculated summary page + using the answer values that are on the path. In this instance it is the unhappy path where the user has entered + their own reporting dates, but has then gone back to the first section and changed their answer. In this instance + the dates displayed in the label should come from metadata rather than the dates entered by the user (which are no longer on the path) + """ + self.launchSurvey("test_placeholder_dependencies_with_calculation_summaries") + + # Happy path journey + self.post( + {"reporting-date-answer": "No, I need to report for a different period"} + ) + self.post( + { + "date-from-day": "1", + "date-from-month": "1", + "date-from-year": "2000", + "date-to-day": "1", + "date-to-month": "4", + "date-to-year": "2000", + } + ) + self.post({"undertake-rnd-answer": "Yes"}) + self.post() + self.post({"civil-research": "10", "defence": "10"}) + self.assertInUrl("/questionnaire/calc-summary-1/") + self.assertInBody( + "For the period 1 January 2000 to 1 April 2000 what was the expenditure on R&D for Integration Testing?" + ) + + # Complete the rest of the survey + self.post() + self.post({"innovation": "10", "software": "10"}) + self.post() + self.post() + + # Go back and change the answer and get back to the Calculated Summary page + self.get("/questionnaire/sections/reporting-period-section/") + self.get("/questionnaire/reporting-date/") + self.post({"reporting-date-answer": "Yes, I can report for this period"}) + self.get("/questionnaire/sections/questions-section/") + self.get("/questionnaire/how-much-rnd/") + self.post({"civil-research": "10", "defence": "100"}) + + # The placeholder dates should now be taken from metadata + self.assertInUrl("/questionnaire/calc-summary-1/") + self.assertInBody( + "For the period 1 April 2016 to 30 April 2016 what was the expenditure on R&D for Integration Testing?" + ) From a2196fc3cd4801fb50f982018dc37ddd2bb635b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 11:00:04 +0000 Subject: [PATCH 390/567] Bump black from 24.2.0 to 24.3.0 (#1337) --- Pipfile.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index bf4b748975..cef50fec6a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1496,32 +1496,32 @@ }, "black": { "hashes": [ - "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8", - "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8", - "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd", - "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9", - "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31", - "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92", - "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f", - "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29", - "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4", - "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693", - "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218", - "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a", - "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23", - "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0", - "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982", - "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894", - "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540", - "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430", - "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b", - "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2", - "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6", - "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d" + "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f", + "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93", + "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11", + "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0", + "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9", + "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5", + "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213", + "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d", + "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7", + "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837", + "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f", + "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395", + "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995", + "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f", + "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597", + "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959", + "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5", + "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb", + "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4", + "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7", + "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd", + "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==24.2.0" + "version": "==24.3.0" }, "blinker": { "hashes": [ @@ -2231,11 +2231,11 @@ }, "packaging": { "hashes": [ - "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" + "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" ], "markers": "python_version >= '3.7'", - "version": "==23.2" + "version": "==24.0" }, "pathspec": { "hashes": [ From ee2550fe3e9ef8b50c18ccb7182fd6a3fba8d330 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Mon, 25 Mar 2024 14:07:31 +0000 Subject: [PATCH 391/567] Schemas v4.6.0 (#1338) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c2d2cb0bf0..2707569bc4 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.5.0 +v4.6.0 From 98e73265eb1f8893fd4a72ee9bdefa89198b34ce Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:22:06 +0000 Subject: [PATCH 392/567] Update heading level for introduction questions (#1336) --- templates/partials/introduction/preview.html | 1 + tests/functional/base_pages/introduction.page.js | 4 ++++ tests/functional/spec/introduction.spec.js | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index ae1fcc27c8..5e96a6ad5d 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -18,6 +18,7 @@

      {{ intro.title }}

      {% set item = { "title": question.question, + "titleTag": "h3", "content": content, "summaryAttributes": { "data-ga": "click", diff --git a/tests/functional/base_pages/introduction.page.js b/tests/functional/base_pages/introduction.page.js index 381155653c..bde3065313 100644 --- a/tests/functional/base_pages/introduction.page.js +++ b/tests/functional/base_pages/introduction.page.js @@ -36,6 +36,10 @@ class IntroductionBasePage extends BasePage { previewQuestions() { return 'a[href="/questionnaire/preview"]'; } + + introQuestion(number = 1) { + return `#intro-questions-${number}`; + } } export default IntroductionBasePage; diff --git a/tests/functional/spec/introduction.spec.js b/tests/functional/spec/introduction.spec.js index 80b5653493..b00bef77bd 100644 --- a/tests/functional/spec/introduction.spec.js +++ b/tests/functional/spec/introduction.spec.js @@ -18,6 +18,12 @@ describe("Introduction page", () => { "To take part, all you need to do is check that you have the information you need to answer the survey questions.", ); }); + it("Given I start a survey, When preview content is set on the introduction page, Then the content headings should be displayed at the correct level", async () => { + await browser.openQuestionnaire(introductionSchema); + const introQuestionH3Selector = `${IntroductionPage.introQuestion()} h3`; + const h3Exists = await $(introQuestionH3Selector).isExisting(); + await expect(h3Exists).toBe(true); + }); it("Given I start a survey with introduction guidance set, When I view the introduction page, Then I should be able to see introduction guidance", async () => { await browser.openQuestionnaire(introductionSchema); await expect(await $(IntroductionPage.guidancePanel(1)).isDisplayed()).toBe(true); From 2431c387f0acca5d626f0b164b9c477a46846ab0 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:52:09 +0000 Subject: [PATCH 393/567] Update eQ to DS 68.0.2 for GA change (#1339) --- .design-system-version | 2 +- templates/hub.html | 2 +- templates/thank-you.html | 4 ++-- templates/view-submitted-response.html | 4 ++-- tests/functional/base_pages/calculated-summary.page.js | 2 +- tests/functional/base_pages/hub.page.js | 2 +- tests/functional/base_pages/thank-you.page.js | 2 +- .../base_pages/view_submitted_response_base.page.js | 6 +++--- .../integration/routes/test_view_submitted_response_pdf.py | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.design-system-version b/.design-system-version index 87d32df300..6eb4d83d18 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -67.0.5 +68.0.2 diff --git a/templates/hub.html b/templates/hub.html index 7151a244b3..e394d36473 100644 --- a/templates/hub.html +++ b/templates/hub.html @@ -12,7 +12,7 @@ {% block summary %} {% if content.rows %} {{ onsSummary({ - "hub": true, + "variant": "hub", "classes": "ons-u-mt-m", "summaries": [{ "groups": [{ diff --git a/templates/thank-you.html b/templates/thank-you.html index ae7504a6e3..c8c2e98ac3 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -1,6 +1,6 @@ {% extends 'layouts/_base.html' %} {% from 'components/panel/_macro.njk' import onsPanel %} -{% from "components/metadata/_macro.njk" import onsMetadata %} +{% from "components/description-list/_macro.njk" import onsDescriptionList %} {% from "components/timeout-panel/_macro.njk" import onsTimeoutPanel %} {% set page_title = _("We’ve received your answers") %} @@ -35,7 +35,7 @@

      {{ _("Thank you for completing the {survey_title}").format(

      {{ content.submission_text }}

      - {{ onsMetadata(content.metadata) }} + {{ onsDescriptionList(content.metadata) }} {% endcall %} {% if content.guidance %} diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index c227f3e4a2..056cb6bb4b 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -2,7 +2,7 @@ {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/button/_macro.njk" import onsButton %} -{% from "components/metadata/_macro.njk" import onsMetadata %} +{% from "components/description-list/_macro.njk" import onsDescriptionList %} {% set hide_sign_out_button = content.hide_sign_out_button %} {% set sign_out_url = content.sign_out_url %} @@ -27,7 +27,7 @@

      {{ content.submitted_text}}

      - {{ onsMetadata(content.metadata)}} + {{ onsDescriptionList(content.metadata)}} {% if not content.view_submitted_response.expired %} {{ onsButton({ diff --git a/tests/functional/base_pages/calculated-summary.page.js b/tests/functional/base_pages/calculated-summary.page.js index 7ee638b426..49b569d3e9 100644 --- a/tests/functional/base_pages/calculated-summary.page.js +++ b/tests/functional/base_pages/calculated-summary.page.js @@ -14,7 +14,7 @@ class CalculatedSummaryPage extends BasePage { } summaryItems() { - return "div.ons-summary__items"; + return "dl.ons-summary__items"; } } diff --git a/tests/functional/base_pages/hub.page.js b/tests/functional/base_pages/hub.page.js index c56f39926e..6c7c7fe19d 100644 --- a/tests/functional/base_pages/hub.page.js +++ b/tests/functional/base_pages/hub.page.js @@ -6,7 +6,7 @@ class HubPage extends BasePage { } summaryItems() { - return "div.ons-summary__items"; + return "dl.ons-summary__items"; } summaryRowState(sectionId) { diff --git a/tests/functional/base_pages/thank-you.page.js b/tests/functional/base_pages/thank-you.page.js index 5921ec34ae..9b9577ea24 100644 --- a/tests/functional/base_pages/thank-you.page.js +++ b/tests/functional/base_pages/thank-you.page.js @@ -30,7 +30,7 @@ class ThankYouPage extends BasePage { } metadata() { - return ".ons-metadata"; + return ".ons-description-list"; } exitButton() { diff --git a/tests/functional/base_pages/view_submitted_response_base.page.js b/tests/functional/base_pages/view_submitted_response_base.page.js index 735ef3006c..785fc4589c 100644 --- a/tests/functional/base_pages/view_submitted_response_base.page.js +++ b/tests/functional/base_pages/view_submitted_response_base.page.js @@ -2,15 +2,15 @@ import BasePage from "./base.page"; class ViewSubmittedResponseBasePage extends BasePage { metadata() { - return ".ons-metadata"; + return ".ons-description-list"; } metadataTerm(number = 1) { - return `.ons-metadata > dt:nth-of-type(${number})`; + return `.ons-description-list > dt:nth-of-type(${number})`; } metadataValue(number = 1) { - return `.ons-metadata > dd:nth-of-type(${number})`; + return `.ons-description-list > dd:nth-of-type(${number})`; } informationPanel() { diff --git a/tests/integration/routes/test_view_submitted_response_pdf.py b/tests/integration/routes/test_view_submitted_response_pdf.py index 4de03dc3b6..1a4ae0b0b3 100644 --- a/tests/integration/routes/test_view_submitted_response_pdf.py +++ b/tests/integration/routes/test_view_submitted_response_pdf.py @@ -47,7 +47,7 @@ def test_download_when_submitted_response_enabled_but_not_expired(self): # Check content length is reasonable. # This is given some leeway as it can change with DS changes. - self.assertGreater(self.last_response.content_length, 16500) + self.assertGreater(self.last_response.content_length, 16000) def test_download_when_submitted_response_enabled_but_expired(self): settings.VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS = 3 From c9df8479604ed85996379e9dbbbc16b28242abbd Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:13:41 +0000 Subject: [PATCH 394/567] Add missing GA click events to templates (#1342) --- templates/confirm-email.html | 3 ++- templates/feedback-sent.html | 3 ++- templates/feedback.html | 5 +++-- templates/individual_response/confirmation-post.html | 3 ++- .../individual_response/confirmation-text-message.html | 3 ++- templates/individual_response/interstitial.html | 3 ++- templates/interstitial.html | 3 ++- templates/layouts/_calculatedsummary.html | 3 ++- templates/layouts/_questionnaire.html | 3 ++- templates/layouts/_submit.html | 3 ++- templates/layouts/configs/_header.html | 3 ++- templates/partials/confirmation-email-form.html | 3 ++- templates/partials/introduction/start-survey.html | 3 ++- templates/preview.html | 6 ++++-- templates/sectionsummary.html | 6 ++++-- templates/view-submitted-response.html | 6 ++++-- 16 files changed, 39 insertions(+), 20 deletions(-) diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 8c0420be35..1ff9190992 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -29,7 +29,8 @@

      {{ conten "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Continue", - "data-ga-label": "Continue button click" + "data-ga-label": "Continue button click", + "data-ga": "click" } }) }} diff --git a/templates/feedback-sent.html b/templates/feedback-sent.html index 71da0142a4..29f02b4fbe 100644 --- a/templates/feedback-sent.html +++ b/templates/feedback-sent.html @@ -28,7 +28,8 @@

      {{ _("Thank you for your feedback") }}

      {{ content.que "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Send feedback", - "data-ga-label": "Send feedback button click" + "data-ga-label": "Send feedback button click", + "data-ga": "click" } }) }} diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index df05095a2d..3282416256 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -25,7 +25,8 @@

      'data-qa': 'btn-submit', "data-ga-category": "Submit button", "data-ga-action": "Continue", - "data-ga-label": "Continue button click" + "data-ga-label": "Continue button click", + "data-ga": "click" } }) }} diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index da28d39fe7..c268c02819 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -33,7 +33,8 @@

      'data-qa': 'btn-submit', "data-ga-category": "Submit button", "data-ga-action": "Continue", - "data-ga-label": "Continue button click" + "data-ga-label": "Continue button click", + "data-ga": "click" } }) }} diff --git a/templates/individual_response/interstitial.html b/templates/individual_response/interstitial.html index 440cb6be1c..70be633586 100644 --- a/templates/individual_response/interstitial.html +++ b/templates/individual_response/interstitial.html @@ -18,7 +18,8 @@

      {{_("If you can't answer questions for others in your hou 'data-qa': 'btn-submit', "data-ga-category": "Submit button", "data-ga-action": "Request separate census", - "data-ga-label": "Request separate census button click" + "data-ga-label": "Request separate census button click", + "data-ga": "click" } }) }} diff --git a/templates/interstitial.html b/templates/interstitial.html index eb5ff6ae58..125bc85a58 100644 --- a/templates/interstitial.html +++ b/templates/interstitial.html @@ -35,7 +35,8 @@

      {{ block.content.title }}

      "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Save and continue", - "data-ga-label": "Save and continue button click" + "data-ga-label": "Save and continue button click", + "data-ga": "click" } }) }} diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html index 54ac18fda1..286f50b8e3 100644 --- a/templates/layouts/_calculatedsummary.html +++ b/templates/layouts/_calculatedsummary.html @@ -22,7 +22,8 @@ "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Confirm", - "data-ga-label": "Confirm button click" + "data-ga-label": "Confirm button click", + "data-ga": "click" } }) }} diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index 9d61efeb53..50574c7904 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -28,7 +28,8 @@ "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Save and continue", - "data-ga-label": "Save and continue button click" + "data-ga-label": "Save and continue button click", + "data-ga": "click" } }) }} diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index 3a41b32b24..581c286b38 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -42,7 +42,8 @@

      {{ content.title }}

      'data-qa': 'btn-submit', "data-ga-category": "Submit Button", "data-ga-action": "Submit", - "data-ga-label": "Submit Button click" + "data-ga-label": "Submit Button click", + "data-ga": "click" } }) }} diff --git a/templates/layouts/configs/_header.html b/templates/layouts/configs/_header.html index fd9ac1bb21..0a41e6b0fb 100644 --- a/templates/layouts/configs/_header.html +++ b/templates/layouts/configs/_header.html @@ -21,7 +21,8 @@ "data-qa": header_button_data_qa, "data-ga-category": "Navigation", "data-ga-action": header_button_data_ga_action, - "data-ga-label": header_button_data_ga_label + "data-ga-label": header_button_data_ga_label, + "data-ga": "click" }, "iconType": "exit", "iconPosition": "after" diff --git a/templates/partials/confirmation-email-form.html b/templates/partials/confirmation-email-form.html index 87d2476dab..5900890b60 100644 --- a/templates/partials/confirmation-email-form.html +++ b/templates/partials/confirmation-email-form.html @@ -42,7 +42,8 @@ "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Send confirmation", - "data-ga-label": "Send confirmation button click" + "data-ga-label": "Send confirmation button click", + "data-ga": "click" } }) }} diff --git a/templates/partials/introduction/start-survey.html b/templates/partials/introduction/start-survey.html index 2b91d0a686..9860359a9a 100644 --- a/templates/partials/introduction/start-survey.html +++ b/templates/partials/introduction/start-survey.html @@ -10,7 +10,8 @@ "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Start survey", - "data-ga-label": "Start button click" + "data-ga-label": "Start button click", + "data-ga": "click" } }) }} diff --git a/templates/preview.html b/templates/preview.html index 0aaf290f6f..71b012c8b5 100644 --- a/templates/preview.html +++ b/templates/preview.html @@ -51,7 +51,8 @@

      {{ _("Preview of the questions in this survey") }}

      "data-qa": "btn-print", "data-ga-category": "Print button", "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click" + "data-ga-label": "Print button click", + "data-ga": "click" } }) }} @@ -65,7 +66,8 @@

      {{ _("Preview of the questions in this survey") }}

      "data-qa": "btn-pdf", "data-ga-category": "PDF button", "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click" + "data-ga-label": "PDF button click", + "data-ga": "click" } }) }} diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 11745adee0..057e5def51 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -12,7 +12,8 @@ "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Continue", - "data-ga-label": "Continue button click" + "data-ga-label": "Continue button click", + "data-ga": "click" } }) }} @@ -52,7 +53,8 @@

      {{content.summary.title}}

      "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Continue", - "data-ga-label": "Continue button click" + "data-ga-label": "Continue button click", + "data-ga": "click" } }) }} diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index 056cb6bb4b..1a9c40f9f3 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -38,7 +38,8 @@

      "data-qa": "btn-print", "data-ga-category": "Print button", "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click" + "data-ga-label": "Print button click", + "data-ga": "click" } }) }} @@ -52,7 +53,8 @@

      "data-qa": "btn-pdf", "data-ga-category": "PDF button", "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click" + "data-ga-label": "PDF button click", + "data-ga": "click" } }) }} From 3a3e7e161b4708c8b8eef2553765a6e82967c353 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 3 Apr 2024 06:53:25 +0100 Subject: [PATCH 395/567] Replace raw url on thank-you page (#1343) --- app/translations/messages.pot | 74 +++++++++++----------- templates/thank-you.html | 6 +- tests/integration/routes/test_thank_you.py | 4 +- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index fa30249d65..56424cf1c0 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-02-23 11:00+0000\n" +"POT-Creation-Date: 2024-04-02 10:47+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,7 +41,7 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:734 +#: app/jinja_filters.py:732 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:25 msgid "No answer provided" @@ -249,55 +249,55 @@ msgid_plural "{number_of_days} days" msgstr[0] "" msgstr[1] "" -#: app/routes/errors.py:160 +#: app/routes/errors.py:159 msgid "You have reached the maximum number of individual access codes" msgstr "" -#: app/routes/errors.py:163 +#: app/routes/errors.py:162 msgid "" "If you need more individual access codes, please contact us." msgstr "" -#: app/routes/errors.py:181 +#: app/routes/errors.py:180 msgid "You have reached the maximum number of times for submitting feedback" msgstr "" -#: app/routes/errors.py:184 +#: app/routes/errors.py:183 msgid "" "If you need to give more feedback, please contact us." msgstr "" -#: app/routes/errors.py:232 +#: app/routes/errors.py:233 msgid "Sorry, there was a problem sending the access code" msgstr "" -#: app/routes/errors.py:239 +#: app/routes/errors.py:240 msgid "You can try to request a new access code again." msgstr "" -#: app/routes/errors.py:242 app/routes/errors.py:267 app/routes/errors.py:289 +#: app/routes/errors.py:243 app/routes/errors.py:268 app/routes/errors.py:290 msgid "" "If this problem keeps happening, please contact us for help." msgstr "" -#: app/routes/errors.py:263 +#: app/routes/errors.py:264 msgid "Sorry, there was a problem sending the confirmation email" msgstr "" -#: app/routes/errors.py:264 +#: app/routes/errors.py:265 msgid "You can try to send the email again." msgstr "" -#: app/routes/errors.py:285 templates/errors/403.html:3 +#: app/routes/errors.py:286 templates/errors/403.html:3 #: templates/errors/403.html:6 templates/errors/submission-failed.html:5 #: templates/errors/submission-failed.html:8 msgid "Sorry, there is a problem" msgstr "" -#: app/routes/errors.py:286 +#: app/routes/errors.py:287 msgid "You can try to submit your feedback again." msgstr "" @@ -419,7 +419,7 @@ msgstr "" #: templates/individual_response/confirmation-text-message.html:29 #: templates/individual_response/question.html:5 templates/interstitial.html:8 #: templates/listcollectorcontent.html:6 templates/sectionsummary.html:10 -#: templates/sectionsummary.html:48 +#: templates/sectionsummary.html:49 msgid "Continue" msgstr "" @@ -831,19 +831,19 @@ msgstr "" msgid "Print questions" msgstr "" -#: templates/preview.html:60 +#: templates/preview.html:61 msgid "Save questions as PDF" msgstr "" -#: templates/partials/introduction/preview.html:36 +#: templates/partials/introduction/preview.html:37 #: templates/partials/summary/collapsible-summary.html:50 -#: templates/preview.html:92 +#: templates/preview.html:94 msgid "Show all" msgstr "" -#: templates/partials/introduction/preview.html:37 +#: templates/partials/introduction/preview.html:38 #: templates/partials/summary/collapsible-summary.html:51 -#: templates/preview.html:93 +#: templates/preview.html:95 msgid "Hide all" msgstr "" @@ -878,60 +878,58 @@ msgid "Thank you for completing the {survey_title}" msgstr "" #: templates/thank-you.html:45 -msgid "Your answers will be processed in the next few weeks." +msgid "" +"Your response will help inform decision-makers how best to support the UK" +" population and economy." msgstr "" #: templates/thank-you.html:46 -msgid "We may contact you to query your answers via phone or secure message." +msgid "Learn more about how we use this data" msgstr "" -#: templates/thank-you.html:47 -msgid "For more information on how we use this data." -msgstr "" - -#: templates/thank-you.html:51 templates/view-submitted-response.html:69 +#: templates/thank-you.html:49 templates/view-submitted-response.html:71 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" -#: templates/thank-you.html:63 +#: templates/thank-you.html:61 msgid "For security, your answers will only be available to view for another " msgstr "" -#: templates/thank-you.html:64 +#: templates/thank-you.html:62 msgid "Get a copy of your answers" msgstr "" -#: templates/thank-you.html:66 +#: templates/thank-you.html:64 msgid "" "You can save or print " "your answers for your records." msgstr "" -#: templates/layouts/_base.html:147 templates/thank-you.html:71 +#: templates/layouts/_base.html:147 templates/thank-you.html:69 msgid "minute" msgstr "" -#: templates/layouts/_base.html:148 templates/thank-you.html:72 +#: templates/layouts/_base.html:148 templates/thank-you.html:70 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:149 templates/thank-you.html:73 +#: templates/layouts/_base.html:149 templates/thank-you.html:71 msgid "second" msgstr "" -#: templates/layouts/_base.html:150 templates/thank-you.html:74 +#: templates/layouts/_base.html:150 templates/thank-you.html:72 msgid "seconds" msgstr "" -#: templates/thank-you.html:76 +#: templates/thank-you.html:74 msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" -#: templates/thank-you.html:85 +#: templates/thank-you.html:83 msgid "Get confirmation email" msgstr "" -#: templates/thank-you.html:86 +#: templates/thank-you.html:84 msgid "" "If you would like to be sent confirmation that you have completed your " "survey, enter your email address" @@ -941,7 +939,7 @@ msgstr "" msgid "Print answers" msgstr "" -#: templates/view-submitted-response.html:47 +#: templates/view-submitted-response.html:48 msgid "Save answers as PDF" msgstr "" @@ -1254,7 +1252,7 @@ msgstr "" msgid "Yes, I confirm this is correct" msgstr "" -#: templates/layouts/_questionnaire.html:39 +#: templates/layouts/_questionnaire.html:40 msgid "Choose another section and return to this later" msgstr "" diff --git a/templates/thank-you.html b/templates/thank-you.html index c8c2e98ac3..fecad2fd97 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -42,10 +42,8 @@

      {{ _("Thank you for completing the {survey_title}").format( {% set contents = content.guidance.contents %} {% include 'partials/contents.html' %} {% else %} -

      {{ _("Your answers will be processed in the next few weeks.") }} - {{ _("We may contact you to query your answers via phone or secure message.") }}

      -

      {{ _("For more information on how we use this data.") }}
      - https://www.ons.gov.uk/surveys

      +

      {{ _("Your response will help inform decision-makers how best to support the UK population and economy.") }}

      +

      {{ _("Learn more about how we use this data") }}

      {% endif %} {% set countdown_expired_text = _("For security, you can no longer view or get a copy of your answers") %} diff --git a/tests/integration/routes/test_thank_you.py b/tests/integration/routes/test_thank_you.py index 44a48f9800..ee107bf74e 100644 --- a/tests/integration/routes/test_thank_you.py +++ b/tests/integration/routes/test_thank_you.py @@ -85,7 +85,9 @@ def test_default_guidance(self): self.post() self.assertInUrl("thank-you") - self.assertInBody("Your answers will be processed in the next few weeks.") + self.assertInBody( + "Your response will help inform decision-makers how best to support the UK population and economy." + ) def test_custom_guidance(self): self.launchSurvey("test_thank_you") From 1eb96b573f49df6ece4428c12443a254f76643dd Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:24:01 +0100 Subject: [PATCH 396/567] Update to Docker Compose v2 (#1345) --------- Co-authored-by: Mebin Abraham --- .github/workflows/pull_request.yml | 4 ++-- Makefile | 14 +++++++------- README.md | 6 +++--- scripts/run_validator.sh | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 070829d5b0..44834790ad 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -142,11 +142,11 @@ jobs: - name: Install npm deps run: npm install - name: Docker compose - run: docker-compose --version && RUNNER_ENV_FILE=.functional-tests.env docker-compose up --build -d + run: docker compose --version && RUNNER_ENV_FILE=.functional-tests.env docker compose up --build -d - name: Functional tests run: make test-functional-suite SUITE=${{ matrix.suite }} - name: Docker compose shutdown - run: RUNNER_ENV_FILE=.functional-tests.env docker-compose kill + run: RUNNER_ENV_FILE=.functional-tests.env docker compose kill docker-push: runs-on: ubuntu-22.04 steps: diff --git a/Makefile b/Makefile index 239cf0ac3c..ad6aba02e9 100644 --- a/Makefile +++ b/Makefile @@ -100,19 +100,19 @@ run-uwsgi-async: link-development-env WEB_SERVER_TYPE=uwsgi-async pipenv run ./run_app.sh dev-compose-up: - docker-compose -f docker-compose-dev-mac.yml pull eq-questionnaire-launcher - docker-compose -f docker-compose-dev-mac.yml pull sds - docker-compose -f docker-compose-dev-mac.yml pull cir - docker-compose -f docker-compose-dev-mac.yml up -d + docker compose -f docker-compose-dev-mac.yml pull eq-questionnaire-launcher + docker compose -f docker-compose-dev-mac.yml pull sds + docker compose -f docker-compose-dev-mac.yml pull cir + docker compose -f docker-compose-dev-mac.yml up -d dev-compose-up-linux: - docker-compose -f docker-compose-dev-linux.yml up -d + docker compose -f docker-compose-dev-linux.yml up -d dev-compose-down: - docker-compose -f docker-compose-dev-mac.yml down + docker compose -f docker-compose-dev-mac.yml down dev-compose-down-linux: - docker-compose -f docker-compose-dev-linux.yml down + docker compose -f docker-compose-dev-linux.yml down profile: pipenv run python profile_application.py diff --git a/README.md b/README.md index c700ff6c37..c3e751e1ff 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Install Docker for your system: [https://www.docker.com/](https://www.docker.com To get eq-questionnaire-runner running the following command will build and run the containers ``` shell -RUNNER_ENV_FILE=.development.env docker-compose up -d +RUNNER_ENV_FILE=.development.env docker compose up -d ``` To launch a survey, navigate to [http://localhost:8000/](http://localhost:8000/) @@ -22,13 +22,13 @@ However, any new dependencies that are added would require a re-build. To rebuild the eq-questionnaire-runner container, the following command can be used. ``` shell -RUNNER_ENV_FILE=.development.env docker-compose build +RUNNER_ENV_FILE=.development.env docker compose build ``` If you need to rebuild the container from scratch to re-load any dependencies then you can run the following ``` shell -RUNNER_ENV_FILE=.development.env docker-compose build --no-cache +RUNNER_ENV_FILE=.development.env docker compose build --no-cache ``` ## Run locally diff --git a/scripts/run_validator.sh b/scripts/run_validator.sh index 6e554b7704..e97dce745e 100755 --- a/scripts/run_validator.sh +++ b/scripts/run_validator.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash tag=latest -TAG=${tag} docker-compose -f docker-compose-schema-validator.yml up -d +TAG=${tag} docker compose -f docker-compose-schema-validator.yml up -d From 67b6a31c9734099c0c76e69dc4c500ff3aa0aecc Mon Sep 17 00:00:00 2001 From: Mebin Abraham <35296336+MebinAbraham@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:22:43 +0100 Subject: [PATCH 397/567] Update README badges (#1344) --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c3e751e1ff..6b38a1423e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ # eQ Questionnaire Runner -![Build Status](https://github.com/ONSdigital/eq-questionnaire-runner/workflows/main/badge.svg) -[![codecov](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/main/graph/badge.svg)](https://codecov.io/gh/ONSdigital/eq-questionnaire-runner/branch/main) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/4c39ddd3285748f8bfb6b70fd5aaf9cc)](https://www.codacy.com/manual/ONSDigital/eq-questionnaire-runner?utm_source=github.com&utm_medium=referral&utm_content=ONSdigital/eq-questionnaire-runner&utm_campaign=Badge_Grade) +[![Build Status](https://github.com/ONSdigital/eq-questionnaire-runner/actions/workflows/main.yml/badge.svg)](https://github.com/ONSdigital/eq-questionnaire-runner/actions/workflows/main.yml) +[![Build Status](https://github.com/ONSdigital/eq-questionnaire-runner/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/ONSdigital/eq-questionnaire-runner/actions/workflows/codeql-analysis.yml) +![Coverage](https://img.shields.io/badge/Coverage-100%25-2FC050.svg) + +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) +[![poetry-managed](https://img.shields.io/badge/poetry-managed-blue)](https://python-poetry.org/) +[![License - MIT](https://img.shields.io/badge/licence%20-MIT-1ac403.svg)](https://github.com/ONSdigital/eq-questionnaire-runner/blob/main/LICENSE) ## Run with Docker From 618c7174d8690142d31b5c49f20a0867a0030a36 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:47:14 +0100 Subject: [PATCH 398/567] Schemas v4.7.0 (#1351) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 2707569bc4..79214dce16 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.6.0 +v4.7.0 From a2e758e3ee6123252273c92615e4f203c30ed740 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:20:56 +0100 Subject: [PATCH 399/567] Remove unsafe inline in CSP policy and update svg fill (#1349) --- app/setup.py | 6 +----- templates/assets/images/dbt-logo-stacked.svg | 2 +- templates/assets/images/dsit-logo-stacked.svg | 2 +- .../assets/images/finance-ni-logo-stacked.svg | 14 +++++++------- templates/assets/images/ons-logo-stacked.svg | 2 +- templates/assets/images/ukhsa-logo-stacked.svg | 2 +- tests/integration/test_app_create.py | 2 +- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/app/setup.py b/app/setup.py index 9efd794737..c461239c67 100644 --- a/app/setup.py +++ b/app/setup.py @@ -62,11 +62,7 @@ "'self'", "https://*.googletagmanager.com", ], - "style-src": [ - "'self'", - "https://fonts.googleapis.com", - "'unsafe-inline'", - ], + "style-src": ["'self'", "https://fonts.googleapis.com"], "connect-src": [ "'self'", "https://*.google-analytics.com", diff --git a/templates/assets/images/dbt-logo-stacked.svg b/templates/assets/images/dbt-logo-stacked.svg index 96d351601e..de0b99d4a7 100644 --- a/templates/assets/images/dbt-logo-stacked.svg +++ b/templates/assets/images/dbt-logo-stacked.svg @@ -1,6 +1,6 @@ Department for Business and Trade logo - + diff --git a/templates/assets/images/dsit-logo-stacked.svg b/templates/assets/images/dsit-logo-stacked.svg index a5f4c76fb2..b9a805f7d5 100644 --- a/templates/assets/images/dsit-logo-stacked.svg +++ b/templates/assets/images/dsit-logo-stacked.svg @@ -1,6 +1,6 @@ Department for Science, Innovation and Technology logo - + diff --git a/templates/assets/images/finance-ni-logo-stacked.svg b/templates/assets/images/finance-ni-logo-stacked.svg index a52e639453..1164744f4e 100644 --- a/templates/assets/images/finance-ni-logo-stacked.svg +++ b/templates/assets/images/finance-ni-logo-stacked.svg @@ -1,9 +1,9 @@ - + Northern Ireland Department of Finance logo - - - - - - + + + + + + diff --git a/templates/assets/images/ons-logo-stacked.svg b/templates/assets/images/ons-logo-stacked.svg index 898fecb407..01ebeba844 100644 --- a/templates/assets/images/ons-logo-stacked.svg +++ b/templates/assets/images/ons-logo-stacked.svg @@ -1,4 +1,4 @@ -£80.00") def test_new_calculated_summary_no_skip(self): - self.launchSurvey("test_new_calculated_summary") + self.launchSurveyV2(schema_name="test_new_calculated_summary") self._complete_calculated_summary_path_no_skip() self.assertInBody( "We calculate the total of currency values entered to be £180.00" ) def test_new_calculated_summary_repeating_section(self): - self.launchSurvey("test_new_calculated_summary_repeating_section") + self.launchSurveyV2(schema_name="test_new_calculated_summary_repeating_section") self._add_list_items() self.post() @@ -97,7 +97,7 @@ def test_new_calculated_summary_repeating_section(self): ) def test_new_calculated_summary_no_skip_repeating_section(self): - self.launchSurvey("test_new_calculated_summary_repeating_section") + self.launchSurveyV2(schema_name="test_new_calculated_summary_repeating_section") self._add_list_items() self.post() @@ -107,7 +107,9 @@ def test_new_calculated_summary_no_skip_repeating_section(self): ) def test_calculated_summary_value_sources_across_sections(self): - self.launchSurvey("test_calculated_summary_cross_section_dependencies") + self.launchSurveyV2( + schema_name="test_calculated_summary_cross_section_dependencies" + ) # Complete the first section self.post() @@ -152,8 +154,8 @@ def test_calculated_summary_value_sources_across_sections(self): self.assertInBody("Enter an answer more than or equal to £60.00") def test_calculated_summary_value_sources_across_sections_repeating(self): - self.launchSurvey( - "test_new_calculated_summary_cross_section_dependencies_repeating" + self.launchSurveyV2( + schema_name="test_new_calculated_summary_cross_section_dependencies_repeating" ) # Add household members @@ -204,7 +206,9 @@ def test_calculated_summary_repeating_answers_only(self): """ Tests a calculated summary with a dynamic answer source resolving to a list of repeating answers """ - self.launchSurvey("test_new_calculated_summary_repeating_answers_only") + self.launchSurveyV2( + schema_name="test_new_calculated_summary_repeating_answers_only" + ) self.post({"any-transport-answer": "Yes"}) self.post({"transport-name": "Bus"}) @@ -230,7 +234,7 @@ def test_new_calculated_summary_repeating_blocks(self): """ Tests a calculated summary with a repeating block answer id source resolving to a list of answers """ - self.launchSurvey("test_new_calculated_summary_repeating_blocks") + self.launchSurveyV2(schema_name="test_new_calculated_summary_repeating_blocks") self.post({"answer-car": "100"}) self.post({"answer-skip": "No"}) self.post({"list-collector-answer": "Yes"}) @@ -313,7 +317,9 @@ def test_calculated_summary_default_decimal_places(self): are entered then we should default to two decimal places on the calculated summary page and the playback page """ - self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.launchSurveyV2( + schema_name="test_calculated_and_grand_calculated_summary_decimals" + ) self.post({"first-number-answer": "10"}) self.post( { @@ -335,7 +341,9 @@ def test_calculated_summary_with_varying_decimal_places(self): places are entered then we should use the largest number of decimal places that are below the decimal limit on the calculated summary page and the playback page """ - self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.launchSurveyV2( + schema_name="test_calculated_and_grand_calculated_summary_decimals" + ) self.post({"first-number-answer": "10.1"}) self.post( { @@ -357,7 +365,9 @@ def test_placeholder_rendering_in_calculated_summary_label(self): using the answer values that are on the path. In this instance it is the happy path where the user has entered their own reporting dates which should be reflected on the calcualted summary label. """ - self.launchSurvey("test_placeholder_dependencies_with_calculation_summaries") + self.launchSurveyV2( + schema_name="test_placeholder_dependencies_with_calculation_summaries" + ) self.post( {"reporting-date-answer": "No, I need to report for a different period"} @@ -387,7 +397,9 @@ def test_placeholder_rendering_in_calculated_summary_label_unhappy_path(self): their own reporting dates, but has then gone back to the first section and changed their answer. In this instance the dates displayed in the label should come from metadata rather than the dates entered by the user (which are no longer on the path) """ - self.launchSurvey("test_placeholder_dependencies_with_calculation_summaries") + self.launchSurveyV2( + schema_name="test_placeholder_dependencies_with_calculation_summaries" + ) # Happy path journey self.post( diff --git a/tests/integration/questionnaire/test_questionnaire_change_answer.py b/tests/integration/questionnaire/test_questionnaire_change_answer.py index 9ae29b40d8..3d65a4a9eb 100644 --- a/tests/integration/questionnaire/test_questionnaire_change_answer.py +++ b/tests/integration/questionnaire/test_questionnaire_change_answer.py @@ -4,7 +4,7 @@ class TestQuestionnaireChangeAnswer(IntegrationTestCase): def test_change_non_mandatory_date_from_answered_to_not_answered(self): # Given the test_dates questionnaire with a non-mandatory date answered. - self.launchSurvey("test_dates") + self.launchSurveyV2(schema_name="test_dates") post_data = [ { diff --git a/tests/integration/questionnaire/test_questionnaire_csrf.py b/tests/integration/questionnaire/test_questionnaire_csrf.py index 75e9a46f62..835c8e105c 100644 --- a/tests/integration/questionnaire/test_questionnaire_csrf.py +++ b/tests/integration/questionnaire/test_questionnaire_csrf.py @@ -7,7 +7,7 @@ def test_given_on_interstitial_page_when_submit_with_no_csrf_token_then_forbidde self, ): # Given - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") self.last_csrf_token = None # When @@ -21,7 +21,7 @@ def test_given_on_interstitial_page_when_submit_with_invalid_csrf_token_then_for self, ): # Given - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") self.last_csrf_token = "made-up-token" # When @@ -35,7 +35,7 @@ def test_given_on_introduction_page_when_submit_valid_token_then_redirect_to_nex self, ): # Given - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") # When self.post(action="start_questionnaire") @@ -48,7 +48,7 @@ def test_given_answered_question_when_change_answer_with_invalid_csrf_token_then self, ): # Given - self.launchSurvey("test_interstitial_page", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_interstitial_page", roles=["dumper"]) self.post() self.post({"favourite-breakfast": "Muesli"}) @@ -66,7 +66,7 @@ def test_given_valid_answer_when_answer_with_invalid_csrf_token_then_answer_not_ self, ): # Given - self.launchSurvey("test_checkbox", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_checkbox", roles=["dumper"]) self.post( { "mandatory-checkbox-answer": "Other", @@ -86,7 +86,7 @@ def test_given_valid_answer_when_answer_with_invalid_csrf_token_then_answer_not_ def test_given_csrf_attack_when_refresh_then_on_question(self): # Given - self.launchSurvey("test_interstitial_page", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_interstitial_page", roles=["dumper"]) self.post() self.last_csrf_token = "made-up-token" self.post({"favourite-breakfast": "Pancakes"}) @@ -102,7 +102,7 @@ def test_given_csrf_attack_when_refresh_then_on_question(self): def test_given_csrf_attack_when_submit_new_answers_then_answers_saved(self): # Given - self.launchSurvey("test_interstitial_page", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_interstitial_page", roles=["dumper"]) self.post() self.last_csrf_token = "made-up-token" self.post({"favourite-breakfast": "Muesli"}) diff --git a/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py b/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py index 637988aa48..bcaec567b0 100644 --- a/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py +++ b/tests/integration/questionnaire/test_questionnaire_custom_page_titles.py @@ -3,7 +3,7 @@ class TestQuestionnaireCustomPageTitles(QuestionnaireTestCase): def test_custom_page_titles(self): - self.launchSurvey("test_custom_page_titles") + self.launchSurveyV2(schema_name="test_custom_page_titles") self.post() self.assertEqualPageTitle("Custom page title - Test Custom Page Titles") @@ -38,7 +38,7 @@ def test_custom_page_titles(self): ) def test_custom_repeating_page_titles(self): - self.launchSurvey("test_custom_page_titles") + self.launchSurveyV2(schema_name="test_custom_page_titles") self.post() self.post({"anyone-else": "Yes"}) self.post({"first-name": "Marie", "last-name": "Doe"}) diff --git a/tests/integration/questionnaire/test_questionnaire_detail_answer.py b/tests/integration/questionnaire/test_questionnaire_detail_answer.py index 42960ffa57..ac94932a55 100644 --- a/tests/integration/questionnaire/test_questionnaire_detail_answer.py +++ b/tests/integration/questionnaire/test_questionnaire_detail_answer.py @@ -5,7 +5,7 @@ class TestQuestionnaireDetailAnswer(IntegrationTestCase): BASE_URL = "/questionnaire/" def test_detail_answer(self): - self.launchSurvey("test_checkbox_detail_answer_multiple") + self.launchSurveyV2(schema_name="test_checkbox_detail_answer_multiple") self.post( { "mandatory-checkbox-answer": ["Ham", "Pineapple", "Your choice"], diff --git a/tests/integration/questionnaire/test_questionnaire_dynamic_answer_options.py b/tests/integration/questionnaire/test_questionnaire_dynamic_answer_options.py index 61f7e53e3d..f98a145af2 100644 --- a/tests/integration/questionnaire/test_questionnaire_dynamic_answer_options.py +++ b/tests/integration/questionnaire/test_questionnaire_dynamic_answer_options.py @@ -30,7 +30,7 @@ def complete_reference_date_question(self): def assert_dynamic_answer_options(self, schema_name): # Given I launch a schema with dynamic options with additional static option - self.launchSurvey(schema_name) + self.launchSurveyV2(schema_name=schema_name) # When I answer the questions using the dynamic options self.complete_reference_date_question() @@ -65,7 +65,7 @@ def assert_dynamic_answer_options(self, schema_name): def assert_dynamic_answer_options_no_answer_provided(self, schema_name): # Given I launch a schema with dynamic options with additional static option - self.launchSurvey(schema_name, roles=["dumper"]) + self.launchSurveyV2(schema_name=schema_name, roles=["dumper"]) # When I Save and continue without answering any questions self.complete_reference_date_question() @@ -128,8 +128,8 @@ def test_dynamic_answer_options_no_answer_provided(self): def test_static_answer_options(self): # Given I launch a schema with dynamic options with additional static option - self.launchSurvey( - "test_dynamic_answer_options_function_driven_with_static_options" + self.launchSurveyV2( + schema_name="test_dynamic_answer_options_function_driven_with_static_options" ) # When I answer the questions using the static options @@ -165,8 +165,8 @@ def test_static_answer_options(self): def test_dynamic_options_answer_cleared_on_dependency_change(self): # Given I launch a schema and submit an answer for a question which has dynamic options - self.launchSurvey( - "test_dynamic_answer_options_function_driven_with_static_options" + self.launchSurveyV2( + schema_name="test_dynamic_answer_options_function_driven_with_static_options" ) self.complete_reference_date_question() self.answer_checkbox_question(["2020-12-29", "2020-12-30"]) diff --git a/tests/integration/questionnaire/test_questionnaire_endpoints.py b/tests/integration/questionnaire/test_questionnaire_endpoints.py index 8ebd85efd3..e80dbdfefc 100644 --- a/tests/integration/questionnaire/test_questionnaire_endpoints.py +++ b/tests/integration/questionnaire/test_questionnaire_endpoints.py @@ -7,7 +7,7 @@ class TestQuestionnaireEndpoints(IntegrationTestCase): def test_invalid_section_id_raises_404(self): # Given - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # When I navigate to the url for a section that does not exist self.get(f"{self.BASE_URL}/sections/invalid-section/") @@ -17,7 +17,7 @@ def test_invalid_section_id_raises_404(self): def test_get_invalid_questionnaire_location_raises_404(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When self.get(f"{self.BASE_URL}/test") @@ -27,7 +27,7 @@ def test_get_invalid_questionnaire_location_raises_404(self): def test_post_invalid_questionnaire_location_raises_404(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When self.post(url=f"{self.BASE_URL}/test") @@ -39,7 +39,7 @@ def test_post_on_questionnaire_route_without_hub_redirects_to_first_incomplete_l self, ): # Given - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") # When self.post(url="/questionnaire/") @@ -49,7 +49,7 @@ def test_post_on_questionnaire_route_without_hub_redirects_to_first_incomplete_l def test_get_thank_you_data_not_deleted_when_questionnaire_is_not_complete(self): # Given we start a survey - self.launchSurvey("test_percentage", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_percentage", roles=["dumper"]) self.post({"answer": "99"}) # When we request the thank you page (without submitting the survey) @@ -62,7 +62,7 @@ def test_get_thank_you_data_not_deleted_when_questionnaire_is_not_complete(self) def test_get_thank_you_raises_404_when_questionnaire_is_not_complete(self): # Given we start a survey - self.launchSurvey("test_percentage", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_percentage", roles=["dumper"]) # When we request the thank you page (without submitting the survey) self.get("submitted/thank-you") @@ -72,7 +72,7 @@ def test_get_thank_you_raises_404_when_questionnaire_is_not_complete(self): def test_when_on_thank_you_get_thank_you_returns_thank_you(self): # Given we complete the test_percentage survey and are on the thank you page - self.launchSurvey("test_percentage", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_percentage", roles=["dumper"]) self.post({"answer": "99"}) self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index 5301f7a42f..f7af00c001 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -5,7 +5,7 @@ class TestQuestionnaireGrandCalculatedSummary(QuestionnaireTestCase): BASE_URL = "/questionnaire/" def test_grand_calculated_summary(self): - self.launchSurvey("test_grand_calculated_summary") + self.launchSurveyV2(schema_name="test_grand_calculated_summary") # section-1 two types of unit questions self.post({"q1-a1": 20, "q1-a2": 5}) self.post({"q2-a1": 100, "q2-a2": 3}) @@ -33,7 +33,9 @@ def test_grand_calculated_summary_multiple_sections(self): """ Use the repeating answers schema to test the grand calculated summary which uses calculated summaries in multiple different sections """ - self.launchSurvey("test_grand_calculated_summary_repeating_answers") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_repeating_answers" + ) # section 1 self.post() self.post({"q1-a1": 10, "q1-a2": 20}) @@ -80,7 +82,9 @@ def _complete_upto_grand_calculated_summary_cross_section_dependencies(self): self.post({"third-number-answer-part-a": "70"}) def test_grand_calculated_summary_cross_section_dependencies_with_skip(self): - self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_cross_section_dependencies" + ) self._complete_upto_grand_calculated_summary_cross_section_dependencies() # skip the calculated summary @@ -95,7 +99,9 @@ def test_grand_calculated_summary_cross_section_dependencies_with_skip(self): ) def test_grand_calculated_summary_cross_section_dependencies_no_skip(self): - self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_cross_section_dependencies" + ) self._complete_upto_grand_calculated_summary_cross_section_dependencies() # don't skip calculated summary, confirm it, and go to section summary @@ -111,7 +117,9 @@ def test_grand_calculated_summary_cross_section_dependencies_no_skip(self): ) def test_grand_calculated_summary_cross_section_dependencies_extra_question(self): - self.launchSurvey("test_grand_calculated_summary_cross_section_dependencies") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_cross_section_dependencies" + ) self._complete_upto_grand_calculated_summary_cross_section_dependencies() # edit question to unlock the extra one @@ -148,7 +156,9 @@ def _complete_upto_grand_calculated_summary_overlapping_answers( self.post() def test_grand_calculated_summary_overlapping_answers_full_overlap(self): - self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_overlapping_answers" + ) self._complete_upto_grand_calculated_summary_overlapping_answers( "Yes, I am going to buy two of everything" ) @@ -157,7 +167,9 @@ def test_grand_calculated_summary_overlapping_answers_full_overlap(self): ) def test_grand_calculated_summary_overlapping_answers_partial_overlap(self): - self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_overlapping_answers" + ) self._complete_upto_grand_calculated_summary_overlapping_answers( "Yes, extra bread and cheese" ) @@ -166,7 +178,9 @@ def test_grand_calculated_summary_overlapping_answers_partial_overlap(self): ) def test_grand_calculated_summary_overlapping_answers_no_overlap(self): - self.launchSurvey("test_grand_calculated_summary_overlapping_answers") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_overlapping_answers" + ) self._complete_upto_grand_calculated_summary_overlapping_answers("No") self.assertInBody( "Grand Calculated Summary of purchases this week comes to £330.00. Is this correct?" @@ -177,7 +191,9 @@ def test_grand_calculated_summary_default_decimal_places(self): When multiple decimal limits are set in the schema but no decimals are entered then we should default to two decimal places on the grand calculated summary page """ - self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.launchSurveyV2( + schema_name="test_calculated_and_grand_calculated_summary_decimals" + ) self.post({"first-number-answer": "10"}) self.post( { @@ -201,7 +217,9 @@ def test_grand_calculated_summary_with_varying_decimal_places(self): places are entered then we should use the largest number of decimal places that are below the decimal limit on the grand calculated summary page """ - self.launchSurvey("test_calculated_and_grand_calculated_summary_decimals") + self.launchSurveyV2( + schema_name="test_calculated_and_grand_calculated_summary_decimals" + ) self.post({"first-number-answer": "10.1"}) self.post( { @@ -224,7 +242,9 @@ def test_grand_calculated_summary_inside_repeating_section(self): """ Happy path for a grand calculated summary inside a repeating section """ - self.launchSurvey("test_grand_calculated_summary_inside_repeating_section") + self.launchSurveyV2( + schema_name="test_grand_calculated_summary_inside_repeating_section" + ) self.post() self.post({"any-cost-answer": "No"}) self.post({"finance-cost-answer": "150"}) diff --git a/tests/integration/questionnaire/test_questionnaire_html_escaping.py b/tests/integration/questionnaire/test_questionnaire_html_escaping.py index 790bd133a3..ea762cd401 100644 --- a/tests/integration/questionnaire/test_questionnaire_html_escaping.py +++ b/tests/integration/questionnaire/test_questionnaire_html_escaping.py @@ -7,7 +7,7 @@ class TestQuestionnaireHtmlEscaping(IntegrationTestCase): def test_quotes_in_textfield(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post({"name-answer": HTML_CONTENT}) self.get("/questionnaire/name-block") @@ -15,7 +15,7 @@ def test_quotes_in_textfield(self): assert ESCAPED_CONTENT in self.getResponseData() def test_quotes_in_textarea(self): - self.launchSurvey("test_textarea") + self.launchSurveyV2(schema_name="test_textarea") self.post({"answer": HTML_CONTENT}) self.get("/questionnaire/textarea-block") @@ -23,7 +23,9 @@ def test_quotes_in_textarea(self): assert ESCAPED_CONTENT in self.getResponseData() def test_quotes_in_detail_answer(self): - self.launchSurvey("test_radio_mandatory_with_detail_answer_mandatory") + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory" + ) self.post( {"radio-mandatory-answer": "Other", "other-answer-mandatory": HTML_CONTENT} ) @@ -44,18 +46,18 @@ def test_quotes_in_numeric_answers(self): ] for schema, answer_id in testdata: with self.subTest(schema=schema, answer_id=answer_id): - self.launchSurvey(schema) + self.launchSurveyV2(schema_name=schema) self.post({answer_id: HTML_CONTENT}) assert ESCAPED_CONTENT in self.getResponseData() def test_textfield_summary(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post({"name-answer": HTML_CONTENT}) assert ESCAPED_CONTENT in self.getResponseData() def test_relationships(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.post({"anyone-else": "Yes"}) self.post({"first-name": HTML_CONTENT, "last-name": "Jones"}) self.post({"anyone-else": "Yes"}) @@ -80,7 +82,7 @@ def test_relationships(self): ) def test_composite_address(self): - self.launchSurvey("test_address") + self.launchSurveyV2(schema_name="test_address") self.post( { "address-mandatory-line1": "

      7 Evelyn Street

      ", @@ -94,7 +96,7 @@ def test_composite_address(self): ) def test_composite_address_summary(self): - self.launchSurvey("test_address") + self.launchSurveyV2(schema_name="test_address") self.post( { "address-mandatory-line1": "

      7 Evelyn Street

      ", @@ -107,7 +109,7 @@ def test_composite_address_summary(self): self.assertInBody("<p>7 Evelyn Street</p>") def test_list_collector(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post({"anyone-else": "Yes"}) self.post( { @@ -122,7 +124,7 @@ def test_list_collector(self): assert expected_remove_aria_label in self.getResponseData() def test_summary(self): - self.launchSurvey("test_submit_with_summary") + self.launchSurveyV2(schema_name="test_submit_with_summary") self.post({"radio-answer": "Bacon"}) self.post({"dessert-answer": HTML_CONTENT}) self.post({"dessert-confirmation-answer": "Yes"}) @@ -136,7 +138,7 @@ def test_summary(self): assert expected_change_aria_label in self.getResponseData() def test_radio_mandatory_error_with_placeholders(self): - self.launchSurvey("test_submit_with_summary") + self.launchSurveyV2(schema_name="test_submit_with_summary") self.post({"radio-answer": "Bacon"}) self.post({"dessert-answer": HTML_CONTENT}) self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index 270425c3df..b5323aabfe 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -5,7 +5,7 @@ class TestQuestionnaireHub(IntegrationTestCase): def test_navigation_to_hub_route_when_hub_not_enabled(self): # Given the hub is not enabled - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When I navigate to the hub url self.get(HUB_URL_PATH) @@ -16,7 +16,7 @@ def test_navigation_to_hub_route_when_hub_not_enabled(self): def test_redirect_to_hub_when_section_complete(self): # Given the hub is enabled - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # When I complete a section self.post() @@ -27,7 +27,7 @@ def test_redirect_to_hub_when_section_complete(self): def test_hub_section_url_when_hub_not_enabled(self): # Given the hub is not enabled - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When I navigate to the url for a hub's section self.get("/questionnaire/sections/default-section/") @@ -37,7 +37,7 @@ def test_hub_section_url_when_hub_not_enabled(self): def test_section_url_when_hub_enabled_and_section_not_started(self): # Given the hub is enabled - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # When I navigate to a url for a hub's section self.get("questionnaire/sections/employment-section/") @@ -47,7 +47,7 @@ def test_section_url_when_hub_enabled_and_section_not_started(self): def test_hub_section_url_when_hub_enabled_and_section_in_progress(self): # Given the hub is enabled and a section is in-progress - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") self.post() self.post({"employment-status-answer-exclusive": "None of these apply"}) self.get(HUB_URL_PATH) @@ -62,7 +62,7 @@ def test_hub_section_url_when_hub_enabled_and_section_in_progress(self): def test_hub_section_url_when_hub_enabled_and_section_complete(self): # Given the hub is enabled and a section is complete - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") self.get("/questionnaire/sections/accommodation-section/") self.post() self.post() @@ -76,7 +76,7 @@ def test_hub_section_url_when_hub_enabled_and_section_complete(self): self.assertEqualUrl("/questionnaire/sections/accommodation-section/") def test_hub_inaccessible_if_sections_required_and_incomplete(self): - self.launchSurvey("test_hub_complete_sections") + self.launchSurveyV2(schema_name="test_hub_complete_sections") self.get(HUB_URL_PATH) @@ -84,7 +84,7 @@ def test_hub_inaccessible_if_sections_required_and_incomplete(self): self.assertEqualUrl("/questionnaire/employment-status/") def test_hub_accessible_if_sections_required_and_complete(self): - self.launchSurvey("test_hub_complete_sections") + self.launchSurveyV2(schema_name="test_hub_complete_sections") self.post({"employment-status-answer": "Working as an employee"}) self.post() @@ -95,7 +95,7 @@ def test_hub_accessible_if_sections_required_and_complete(self): def test_hub_displays_repeating_sections_with_valid_urls(self): # Given the hub is enabled and a section is complete - self.launchSurvey("test_repeating_sections_with_hub_and_spoke") + self.launchSurveyV2(schema_name="test_repeating_sections_with_hub_and_spoke") # Go to first section self.post() @@ -158,7 +158,7 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): def test_hub_section_required_but_enabled_false(self): # Given the hub is enabled and there are two required sections - self.launchSurvey("test_hub_section_required_and_enabled") + self.launchSurveyV2(schema_name="test_hub_section_required_and_enabled") # When I answer 'No' to the first section, meaning the second section is not enabled self.post({"household-relationships-answer": "No"}) @@ -170,7 +170,7 @@ def test_hub_section_required_but_enabled_false(self): def test_hub_section_required_but_enabled_true(self): # Given the hub is enabled and there are two required sections - self.launchSurvey("test_hub_section_required_and_enabled") + self.launchSurveyV2(schema_name="test_hub_section_required_and_enabled") # When I answer 'Yes' to the first section, meaning the second section is enabled self.post({"household-relationships-answer": "Yes"}) @@ -180,7 +180,7 @@ def test_hub_section_required_but_enabled_true(self): def test_hub_section_enabled_and_accessible_with_repeating_sections(self): # Given the hub is enabled and there are two required sections (and one is for a repeat) - self.launchSurvey("test_hub_section_required_with_repeat") + self.launchSurveyV2(schema_name="test_hub_section_required_with_repeat") # When I answer I complete the first section and the repeating section self.post({"you-live-here": "Yes"}) @@ -202,7 +202,7 @@ def test_hub_section_enabled_and_accessible_with_repeating_sections(self): def test_hub_section_is_inaccessible_when_all_repeats_not_complete(self): # Given the hub is enabled and there are two required sections (and one is for a repeat) - self.launchSurvey("test_hub_section_required_with_repeat") + self.launchSurveyV2(schema_name="test_hub_section_required_with_repeat") # When I complete the first section and the first repeat, but not the second repeat self.post({"you-live-here": "Yes"}) diff --git a/tests/integration/questionnaire/test_questionnaire_instructions.py b/tests/integration/questionnaire/test_questionnaire_instructions.py index 836c668c7e..2086fad032 100644 --- a/tests/integration/questionnaire/test_questionnaire_instructions.py +++ b/tests/integration/questionnaire/test_questionnaire_instructions.py @@ -5,12 +5,12 @@ class TestQuestionnaireInstructions(IntegrationTestCase): BASE_URL = "/questionnaire/" def test_interstitial_instruction(self): - self.launchSurvey("test_instructions") + self.launchSurveyV2(schema_name="test_instructions") self.post(action="start_questionnaire") self.assertInBody("Just pause for a second") def test_question_instruction(self): - self.launchSurvey("test_instructions") + self.launchSurveyV2(schema_name="test_instructions") self.post(action="start_questionnaire") self.post() self.assertInBody("Tell us about what you eat") diff --git a/tests/integration/questionnaire/test_questionnaire_interstitial.py b/tests/integration/questionnaire/test_questionnaire_interstitial.py index 510c3a26e2..d660f0ef1c 100644 --- a/tests/integration/questionnaire/test_questionnaire_interstitial.py +++ b/tests/integration/questionnaire/test_questionnaire_interstitial.py @@ -6,13 +6,13 @@ class TestQuestionnaireInterstitial(IntegrationTestCase): BASE_URL = "/questionnaire/" def test_interstitial_page_button_text_is_continue(self): - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") self.post(action="start_questionnaire") self.post({"favourite-breakfast": "Cereal"}) self.assertInBody("Continue") def test_interstitial_can_continue_and_submit(self): - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") self.post(action="start_questionnaire") self.post({"favourite-breakfast": "Cereal"}) self.post() @@ -23,12 +23,12 @@ def test_interstitial_can_continue_and_submit(self): self.assertInUrl(THANK_YOU_URL_PATH) def test_interstitial_definition(self): - self.launchSurvey("test_interstitial_definition") + self.launchSurveyV2(schema_name="test_interstitial_definition") self.assertInBody("Successfully") self.assertInBody("In a way that accomplishes a desired aim or result") def test_interstitial_content_variant_definition(self): - self.launchSurvey("test_interstitial_definition") + self.launchSurveyV2(schema_name="test_interstitial_definition") self.post() self.post({"content-variant-definition-answer": "Answer"}) diff --git a/tests/integration/questionnaire/test_questionnaire_last_viewed_guidance.py b/tests/integration/questionnaire/test_questionnaire_last_viewed_guidance.py index b73f03ba87..da304b55a1 100644 --- a/tests/integration/questionnaire/test_questionnaire_last_viewed_guidance.py +++ b/tests/integration/questionnaire/test_questionnaire_last_viewed_guidance.py @@ -11,14 +11,14 @@ def __init__(self, *args, **kwargs): def test_not_shown_on_survey_launch(self): # Given - self.launchSurvey("test_last_viewed_question_guidance") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance") # Then last viewed question guidance should not be shown self._assert_last_viewed_question_guidance_not_shown() def test_not_shown_on_linear_journey(self): # Given - self.launchSurvey("test_last_viewed_question_guidance") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance") # When I complete the journey as normal, without resuming self.post() @@ -28,8 +28,9 @@ def test_not_shown_on_linear_journey(self): def test_not_shown_on_section_resume_first_block_in_new_section(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on the first block of a new section @@ -38,8 +39,9 @@ def test_not_shown_on_section_resume_first_block_in_new_section(self): self._post_you_live_here_answer() self._post_list_collector_answers() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed question guidance is not shown @@ -47,14 +49,16 @@ def test_not_shown_on_section_resume_first_block_in_new_section(self): def test_not_shown_on_resume_section_not_started(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out without starting the section and I resume the survey self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed question guidance should not be shown @@ -62,15 +66,17 @@ def test_not_shown_on_resume_section_not_started(self): def test_shown_on_resume_section_in_progress(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out after I have started the section and I resume the survey self.post() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance should be shown @@ -81,16 +87,18 @@ def test_shown_on_resume_section_in_progress(self): def test_shown_on_section_in_progress_resume_primary_person_list_collector(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on a primary person list collector self.post() self._post_address_confirmation_answer() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance is shown @@ -103,8 +111,9 @@ def test_shown_on_section_in_progress_resume_primary_person_list_collector_add_p self, ): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on a primary person list collector add person page @@ -112,8 +121,9 @@ def test_shown_on_section_in_progress_resume_primary_person_list_collector_add_p self._post_address_confirmation_answer() self._post_you_live_here_answer() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance is shown on the parent page @@ -124,8 +134,9 @@ def test_shown_on_section_in_progress_resume_primary_person_list_collector_add_p def test_shown_on_section_in_progress_resume_list_collector(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on a list collector @@ -134,8 +145,9 @@ def test_shown_on_section_in_progress_resume_list_collector(self): self._post_you_live_here_answer() self._post_primary_person_answer() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance is shown @@ -146,8 +158,9 @@ def test_shown_on_section_in_progress_resume_list_collector(self): def test_shown_on_section_in_progress_resume_list_collector_add_person(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on a list collector add person @@ -157,8 +170,9 @@ def test_shown_on_section_in_progress_resume_list_collector_add_person(self): self._post_primary_person_answer() self.post({"anyone-else": "Yes"}) self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance is shown on the parent page @@ -169,8 +183,9 @@ def test_shown_on_section_in_progress_resume_list_collector_add_person(self): def test_not_shown_on_section_in_progress_resume_relationships(self): # Given - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # When I sign out and resume on a relationship question @@ -182,8 +197,9 @@ def test_not_shown_on_section_in_progress_resume_relationships(self): self.post() self.post() self.signOut() - self.launchSurvey( - "test_last_viewed_question_guidance", reponse_id=self.response_id + self.launchSurveyV2( + schema_name="test_last_viewed_question_guidance", + reponse_id=self.response_id, ) # Then the last viewed guidance is not shown @@ -191,14 +207,14 @@ def test_not_shown_on_section_in_progress_resume_relationships(self): def test_not_shown_on_survey_launch_hub_not_available(self): # Given - self.launchSurvey("test_last_viewed_question_guidance_hub") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance_hub") # When the hub is not available, then last viewed guidance should not be shown self._assert_last_viewed_question_guidance_not_shown() def test_not_shown_on_section_not_started_hub(self): # Given - self.launchSurvey("test_last_viewed_question_guidance_hub") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance_hub") # When clicking on a link from the hub to a section not started self._posts_for_hub_required_section_to_complete() @@ -209,7 +225,7 @@ def test_not_shown_on_section_not_started_hub(self): def test_shown_on_section_in_progress_hub_using_link_from_hub(self): # Given - self.launchSurvey("test_last_viewed_question_guidance_hub") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance_hub") # When clicking on a link from the hub to a section that is in progress self._posts_for_hub_required_section_to_complete() @@ -222,7 +238,7 @@ def test_shown_on_section_in_progress_hub_using_link_from_hub(self): def test_shown_on_section_in_progress_hub_using_continue_from_hub(self): # Given - self.launchSurvey("test_last_viewed_question_guidance_hub") + self.launchSurveyV2(schema_name="test_last_viewed_question_guidance_hub") # When clicking on continue from the hub to a section that is in progress self._posts_for_hub_required_section_to_complete() diff --git a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py index b3384d45a6..fbcbb6e1a7 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py +++ b/tests/integration/questionnaire/test_questionnaire_list_change_evaluates_sections.py @@ -11,7 +11,7 @@ def get_link(self, action, position): return filtered[0].get("href") def test_without_primary_person(self): - self.launchSurvey("test_list_change_evaluates_sections") + self.launchSurveyV2(schema_name="test_list_change_evaluates_sections") self.get("/questionnaire/sections/who-lives-here/") self.assertEqualUrl("/questionnaire/primary-person-list-collector/") @@ -45,7 +45,7 @@ def test_without_primary_person(self): self.assertEqualUrl("/questionnaire/own-or-rent/?resume=True") def test_with_primary_person(self): - self.launchSurvey("test_list_change_evaluates_sections") + self.launchSurveyV2(schema_name="test_list_change_evaluates_sections") self.get("/questionnaire/sections/accommodation-section/") self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector.py b/tests/integration/questionnaire/test_questionnaire_list_collector.py index c54717f434..d78c486049 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector.py @@ -10,28 +10,28 @@ def get_add_someone_link(self): return selected[0].get("href") def test_invalid_add_block_url(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.get("/questionnaire/people/123/add-person") self.assertStatusNotFound() def test_invalid_list_name(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.get("/questionnaire/invalid-list-name/add-person/") self.assertStatusNotFound() def test_invalid_list_item_id_for_edit_block(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.get("/questionnaire/people/123/edit-person") self.assertStatusNotFound() def test_happy_path(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -105,7 +105,7 @@ def test_happy_path(self): self.assertEqualUrl("/questionnaire/list-collector/") def test_list_collector_submission(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post(action="start_questionnaire") @@ -152,7 +152,7 @@ def test_list_collector_submission(self): self.assertInUrl("thank-you") def test_optional_list_collector_submission(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post(action="start_questionnaire") @@ -171,7 +171,7 @@ def test_optional_list_collector_submission(self): self.assertInUrl(SUBMIT_URL_PATH) def test_list_summary_on_question(self): - self.launchSurvey("test_list_summary_on_question") + self.launchSurveyV2(schema_name="test_list_summary_on_question") self.post(action="start_questionnaire") @@ -196,7 +196,7 @@ def test_list_summary_on_question(self): self.assertInBody("Marie Claire Doe") def test_questionnaire_summary_with_custom_section_summary(self): - self.launchSurvey("test_list_summary_on_question") + self.launchSurveyV2(schema_name="test_list_summary_on_question") self.post(action="start_questionnaire") @@ -219,7 +219,7 @@ def test_questionnaire_summary_with_custom_section_summary(self): self.assertNotInBody("No, all household members are unrelated") def test_cancel_text_displayed_on_add_block_if_exists(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post(action="start_questionnaire") @@ -228,7 +228,7 @@ def test_cancel_text_displayed_on_add_block_if_exists(self): self.assertInBody("Don’t need to add anyone else?") def test_cancel_text_displayed_on_edit_block_if_exists(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post(action="start_questionnaire") @@ -243,7 +243,7 @@ def test_cancel_text_displayed_on_edit_block_if_exists(self): self.assertInBody("Don’t need to change anything?") def test_warning_text_displayed_on_remove_block_if_exists(self): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.post(action="start_questionnaire") @@ -263,7 +263,7 @@ def test_warning_text_displayed_on_remove_block_if_exists(self): def test_list_collector_return_to_when_section_summary_cant_be_displayed(self): # Given I have completed a section and returned to a list_collector from the section summary - self.launchSurvey("test_relationships", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_relationships", roles=["dumper"]) self.add_person("Marie", "Doe") @@ -285,7 +285,7 @@ def test_list_collector_return_to_when_section_summary_cant_be_displayed(self): def test_adding_person_using_second_list_collector_when_no_people( self, ): - self.launchSurvey("test_list_collector_two_list_collectors") + self.launchSurveyV2(schema_name="test_list_collector_two_list_collectors") self.assertInBody("Does anyone live at your address?") @@ -326,7 +326,7 @@ def test_adding_person_using_second_list_collector_when_no_people( def test_adding_from_the_summary_page_adds_the_return_to_param_to_the_url( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -355,7 +355,7 @@ def test_adding_from_the_summary_page_adds_the_return_to_param_to_the_url( def test_removing_from_the_summary_page_adds_the_return_to_param_to_the_url( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -384,7 +384,7 @@ def test_removing_from_the_summary_page_adds_the_return_to_param_to_the_url( def test_changing_item_from_the_summary_page_adds_the_return_to_param_to_the_url( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -411,7 +411,7 @@ def test_changing_item_from_the_summary_page_adds_the_return_to_param_to_the_url def test_adding_from_the_summary_page_and_then_removing_from_parent_page_keeps_return_to_url_param( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -452,7 +452,7 @@ def test_adding_from_the_summary_page_and_then_removing_from_parent_page_keeps_r def test_adding_from_the_summary_page_and_then_changing_from_parent_page_keeps_return_to_url_param( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -493,7 +493,7 @@ def test_adding_from_the_summary_page_and_then_changing_from_parent_page_keeps_r def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_edit_question_block_persists_return_to_url_param( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -532,7 +532,7 @@ def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_edit_ def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_remove_question_block_persists_return_to_url_param( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") @@ -571,7 +571,7 @@ def test_adding_from_the_summary_page_and_then_clicking_previous_link_from_remov def test_adding_from_the_summary_page_and_then_adding_again_from_list_collector_persists_the_return_to_url_param( self, ): - self.launchSurvey("test_list_collector") + self.launchSurveyV2(schema_name="test_list_collector") self.assertInBody("Does anyone else live here?") diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py index 1d0ce55d64..9d6416e142 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_content.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_content.py @@ -17,7 +17,7 @@ def add_company(self, name: str, number: str, authorised_insurer: str): ) def test_happy_path(self): - self.launchSurvey("test_list_collector_content_page") + self.launchSurveyV2(schema_name="test_list_collector_content_page") self.post({"any-companies-or-branches-answer": "Yes"}) self.add_company("Company A", "123", "No") @@ -93,7 +93,7 @@ def test_happy_path(self): self.assertInUrl("/questionnaire/") def test_hub_section_in_progress(self): - self.launchSurvey("test_list_collector_content_page") + self.launchSurveyV2(schema_name="test_list_collector_content_page") self.post({"any-companies-or-branches-answer": "Yes"}) self.add_company("Company A", "123", "No") @@ -130,7 +130,7 @@ def test_hub_section_in_progress(self): self.assertInBody("Partially completed") def test_hub_section_in_progress_when_one_complete(self): - self.launchSurvey("test_list_collector_content_page") + self.launchSurveyV2(schema_name="test_list_collector_content_page") self.post({"any-companies-or-branches-answer": "Yes"}) self.add_company("Company A", "123", "No") diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py b/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py index f8c6733397..2eb9bcaf87 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_primary_person.py @@ -7,14 +7,14 @@ class TestQuestionnaireListCollector(QuestionnaireTestCase): def test_invalid_list_on_primary_person_collector(self): - self.launchSurvey("test_list_collector_primary_person") + self.launchSurveyV2(schema_name="test_list_collector_primary_person") self.get("/questionnaire/invalid/123423/add-or-edit-person/") self.assertStatusNotFound() def test_invalid_list_item_id_for_primary_person_add_block(self): - self.launchSurvey("test_list_collector_primary_person") + self.launchSurveyV2(schema_name="test_list_collector_primary_person") self.post({"you-live-here": "Yes"}) self.assertInUrl("add-or-edit-primary-person/") @@ -24,7 +24,7 @@ def test_invalid_list_item_id_for_primary_person_add_block(self): self.assertStatusNotFound() def test_non_primary_person_list_item_id_for_primary_person_add_block(self): - self.launchSurvey("test_list_collector_primary_person") + self.launchSurveyV2(schema_name="test_list_collector_primary_person") # Add a non-primary person self.post({"you-live-here": "No"}) @@ -52,7 +52,7 @@ def test_non_primary_person_list_item_id_for_primary_person_add_block(self): self.assertStatusNotFound() def test_adding_then_removing_primary_person(self): - self.launchSurvey("test_list_collector_primary_person") + self.launchSurveyV2(schema_name="test_list_collector_primary_person") self.post({"you-live-here": "Yes"}) @@ -83,7 +83,7 @@ def test_adding_then_removing_primary_person(self): self.assertInBody("James May") def test_cannot_remove_primary_person_from_list_collector(self): - self.launchSurvey("test_list_collector_primary_person") + self.launchSurveyV2(schema_name="test_list_collector_primary_person") self.post({"you-live-here": "Yes"}) @@ -112,7 +112,9 @@ def test_changing_answer_from_no_to_yes_on_primary_person_list_collector_resumes response_id = random.choices(string.digits, k=16) # Given I initially answer 'No' to the primary person list collector - self.launchSurvey("test_list_collector_primary_person", reponse_id=response_id) + self.launchSurveyV2( + schema_name="test_list_collector_primary_person", reponse_id=response_id + ) self.post({"you-live-here": "No"}) # When I change my answer to 'Yes' and sign out @@ -121,14 +123,16 @@ def test_changing_answer_from_no_to_yes_on_primary_person_list_collector_resumes self.signOut() # Then on resuming, I am returned to the primary-person-list-collector - self.launchSurvey("test_list_collector_primary_person", reponse_id=response_id) + self.launchSurveyV2( + schema_name="test_list_collector_primary_person", reponse_id=response_id + ) self.assertInUrl("/questionnaire/primary-person-list-collector/") def test_section_summary_with_primary_no_driving_question_on_path( self, ): - self.launchSurvey( - "test_list_collector_primary_and_collector_with_driving_question" + self.launchSurveyV2( + schema_name="test_list_collector_primary_and_collector_with_driving_question" ) self.assertInBody("Start section") diff --git a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py index c54b543107..198b959f20 100644 --- a/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py +++ b/tests/integration/questionnaire/test_questionnaire_list_collector_repeating_blocks.py @@ -7,7 +7,9 @@ class TestQuestionnaireListCollectorRepeatingBlocks(IntegrationTestCase): def launch_repeating_blocks_test_survey(self): - self.launchSurvey("test_list_collector_repeating_blocks_section_summary") + self.launchSurveyV2( + schema_name="test_list_collector_repeating_blocks_section_summary" + ) self.post({"responsible-party-answer": "Yes"}) def add_company(self, company_name: str): @@ -478,7 +480,7 @@ def test_edit_repeating_block_from_submit_page_returns_to_submit_page( def test_previous_link_in_two_list_collectors_with_repeating_blocks_returns_to_previous_location( self, ): - self.launchSurvey("test_list_collector_repeating_blocks_with_hub") + self.launchSurveyV2(schema_name="test_list_collector_repeating_blocks_with_hub") self.post({"responsible-party-answer": "Yes"}) # Add some items to first list collector in first section diff --git a/tests/integration/questionnaire/test_questionnaire_page_titles.py b/tests/integration/questionnaire/test_questionnaire_page_titles.py index afa5d2f976..8468cfb7cc 100644 --- a/tests/integration/questionnaire/test_questionnaire_page_titles.py +++ b/tests/integration/questionnaire/test_questionnaire_page_titles.py @@ -4,13 +4,13 @@ class TestQuestionnairePageTitles(IntegrationTestCase): def test_introduction_has_introduction_in_page_title(self): # Given, When - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # Then self.assertEqualPageTitle("Introduction - Submit without summary") def test_should_have_question_in_page_title_when_loading_questionnaire(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When self.post(action="start_questionnaire") # Then @@ -20,7 +20,7 @@ def test_should_have_question_in_page_title_when_loading_questionnaire(self): def test_should_have_question_in_page_title_on_submit_page(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When self.post(action="start_questionnaire") self.post({"breakfast-answer": ""}) @@ -29,7 +29,7 @@ def test_should_have_question_in_page_title_on_submit_page(self): def test_should_have_question_in_page_title_on_submit_page_with_summary(self): # Given - self.launchSurvey("test_percentage") + self.launchSurveyV2(schema_name="test_percentage") # When self.post({"answer": ""}) self.post({"answer-decimal": ""}) @@ -40,7 +40,7 @@ def test_should_have_question_in_page_title_on_submit_page_with_summary(self): def test_should_have_survey_in_page_title_on_thank_you(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") self.post(action="start_questionnaire") self.post({"breakfast-answer": ""}) # When submit @@ -52,7 +52,7 @@ def test_should_have_survey_in_page_title_on_thank_you(self): def test_session_timed_out_page_title(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When self.get("/session-expired") # Then @@ -60,7 +60,7 @@ def test_session_timed_out_page_title(self): def test_should_have_content_title_in_page_title_on_interstitial(self): # Given - self.launchSurvey("test_interstitial_page") + self.launchSurveyV2(schema_name="test_interstitial_page") self.post(action="start_questionnaire") # When self.post({"favourite-breakfast": ""}) @@ -69,14 +69,14 @@ def test_should_have_content_title_in_page_title_on_interstitial(self): def test_html_stripped_from_page_titles(self): # Given - self.launchSurvey("test_markup") + self.launchSurveyV2(schema_name="test_markup") # When # Then self.assertEqualPageTitle("This is a title with emphasis - Markup test") def test_should_have_question_title_in_page_title_on_question(self): # Given - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When # Then self.assertEqualPageTitle( @@ -85,8 +85,9 @@ def test_should_have_question_title_in_page_title_on_question(self): def test_should_not_use_names_in_question_page_titles(self): # Given - self.launchSurvey( - "test_placeholder_full", display_address="68 Abingdon Road, Goathill" + self.launchSurveyV2( + schema_name="test_placeholder_full", + display_address="68 Abingdon Road, Goathill", ) # When self.post({"first-name": "Kevin", "last-name": "Bacon"}) @@ -97,14 +98,14 @@ def test_content_page_should_use_nested_content_text_in_page_title_if_it_exists( self, ): # Given - self.launchSurvey("test_interstitial_page_title") + self.launchSurveyV2(schema_name="test_interstitial_page_title") # When # Then self.assertEqualPageTitle("Your RU name: … - Interstitial Page Titles") def test_should_have_error_in_page_title_when_fail_validation(self): # Given - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When self.post() # Then diff --git a/tests/integration/questionnaire/test_questionnaire_piping.py b/tests/integration/questionnaire/test_questionnaire_piping.py index f9a38b351c..a931e7cfbf 100644 --- a/tests/integration/questionnaire/test_questionnaire_piping.py +++ b/tests/integration/questionnaire/test_questionnaire_piping.py @@ -6,7 +6,7 @@ def test_given_quotes_in_answer_when_piped_into_page_then_html_escaped_quotes_on self, ): # Given - self.launchSurvey("test_multiple_piping") + self.launchSurveyV2(schema_name="test_multiple_piping") self.post(action="start_questionnaire") self.post({"address-line-1": "44 hill side"}) self.post() @@ -27,7 +27,7 @@ def test_given_quotes_in_answer_when_piped_into_page_then_html_escaped_quotes_on def test_given_html_in_answer_when_piped_into_page_then_html_escaped_on_page(self): # Given - self.launchSurvey("test_multiple_piping") + self.launchSurveyV2(schema_name="test_multiple_piping") self.post(action="start_questionnaire") self.post({"address-line-1": "44 hill side"}) self.post() @@ -50,7 +50,7 @@ def test_given_backslash_in_answer_when_piped_into_page_then_backslash_on_page( self, ): # Given - self.launchSurvey("test_multiple_piping") + self.launchSurveyV2(schema_name="test_multiple_piping") self.post(action="start_questionnaire") self.post({"address-line-1": "44 hill side"}) self.post() @@ -66,7 +66,7 @@ def test_given_backslash_in_answer_when_piped_into_page_then_backslash_on_page( def test_answer_piped_into_option(self): # Given - self.launchSurvey("test_multiple_piping") + self.launchSurveyV2(schema_name="test_multiple_piping") self.post(action="start_questionnaire") self.post({"address-line-1": "44 hill side", "town-city": "newport"}) self.post() @@ -87,7 +87,7 @@ def test_answer_piped_into_option_on_validation_error(self): the option label on the form it is rendered with a validation error """ # Given - self.launchSurvey("test_multiple_piping") + self.launchSurveyV2(schema_name="test_multiple_piping") self.post(action="start_questionnaire") self.post({"address-line-1": "44 hill side", "town-city": "newport"}) self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_placeholders.py b/tests/integration/questionnaire/test_questionnaire_placeholders.py index b0f4041b43..f248686c2d 100644 --- a/tests/integration/questionnaire/test_questionnaire_placeholders.py +++ b/tests/integration/questionnaire/test_questionnaire_placeholders.py @@ -4,8 +4,9 @@ class TestPlaceholders(IntegrationTestCase): def test_title_placeholders_rendered_in_summary(self): - self.launchSurvey( - "test_placeholder_full", display_address="68 Abingdon Road, Goathill" + self.launchSurveyV2( + schema_name="test_placeholder_full", + display_address="68 Abingdon Road, Goathill", ) self.assertInBody("Please enter a name") self.post({"first-name": "Kevin", "last-name": "Bacon"}) @@ -33,7 +34,7 @@ def test_title_placeholders_rendered_in_summary(self): self.assertInBody("68 Abingdon Road, Goathill") def test_placeholders_rendered_in_pages(self): - self.launchSurvey("test_placeholder_transform") + self.launchSurveyV2(schema_name="test_placeholder_transform") self.assertInBody( "For Integration Testing (Integration Tests), please enter the total retail turnover" ) @@ -98,7 +99,7 @@ def test_conditional_trad_as_without_trad_as_in_token(self): ) def test_placeholder_address_selector_rendered_in_page(self): - self.launchSurvey("test_address") + self.launchSurveyV2(schema_name="test_address") self.post( { diff --git a/tests/integration/questionnaire/test_questionnaire_plurals.py b/tests/integration/questionnaire/test_questionnaire_plurals.py index 1c022c18a1..aa53c5cd71 100644 --- a/tests/integration/questionnaire/test_questionnaire_plurals.py +++ b/tests/integration/questionnaire/test_questionnaire_plurals.py @@ -3,7 +3,7 @@ class TestQuestionnairePlurals(IntegrationTestCase): def test_plural_page(self): - self.launchSurvey("test_plural_forms") + self.launchSurveyV2(schema_name="test_plural_forms") self.post({"number-of-people-answer": 0}) diff --git a/tests/integration/questionnaire/test_questionnaire_previous_link.py b/tests/integration/questionnaire/test_questionnaire_previous_link.py index ce571d70d7..d235438309 100644 --- a/tests/integration/questionnaire/test_questionnaire_previous_link.py +++ b/tests/integration/questionnaire/test_questionnaire_previous_link.py @@ -5,14 +5,14 @@ class TestQuestionnairePreviousLink(IntegrationTestCase): def test_previous_link_doesnt_appear_on_introduction(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When we open the introduction # Then previous link does not appear self.assertNotInBody("Previous") def test_previous_link_appears_on_the_submit_page(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When we proceed through the questionnaire self.post(action="start_questionnaire") @@ -23,7 +23,7 @@ def test_previous_link_appears_on_the_submit_page(self): def test_previous_link_appears_on_the_submit_page_with_summary(self): # Given - self.launchSurvey("test_submit_with_summary") + self.launchSurveyV2(schema_name="test_submit_with_summary") # When we proceed through the questionnaire self.post() @@ -35,7 +35,7 @@ def test_previous_link_appears_on_the_submit_page_with_summary(self): def test_previous_link_doesnt_appear_on_thank_you(self): # Given - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When ee proceed through the questionnaire self.post(action="start_questionnaire") @@ -46,7 +46,7 @@ def test_previous_link_doesnt_appear_on_thank_you(self): def test_previous_link_appears_on_questions_preceded_by_another_question(self): # Given a survey with multiple questions - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When I answer a question self.assertInUrl("mandatory-checkbox") @@ -57,7 +57,7 @@ def test_previous_link_appears_on_questions_preceded_by_another_question(self): def test_previous_link_appears_on_the_first_question_preceded_by_the_hub(self): # Given a survey with a hub enabled - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # When I answer go to the first question in a section self.assertInUrl("/") diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py index b8279026ea..35facdfac3 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_blocks.py @@ -11,7 +11,7 @@ def test_skip_condition_block_not_complete(self): Test that a block is skipped if the progress value source is not complete """ - self.launchSurvey("test_progress_value_source_blocks") + self.launchSurveyV2(schema_name="test_progress_value_source_blocks") self.assertInBody("Section 1 Question 1") self.post({"s1-b1-q1-a1": 0}) @@ -28,7 +28,7 @@ def test_routing_condition_block_not_complete(self): Test that routes to proper block if the progress value source is not complete """ - self.launchSurvey("test_progress_value_source_blocks") + self.launchSurveyV2(schema_name="test_progress_value_source_blocks") self.assertInBody("Section 1 Question 1") self.post({"s1-b1-q1-a1": 0}) @@ -50,7 +50,7 @@ def test_block_value_source_dependencies_updated(self): Test that the block value source dependencies are updated when a dependent block progress changes """ - self.launchSurvey("test_progress_value_source_blocks") + self.launchSurveyV2(schema_name="test_progress_value_source_blocks") self.assertInBody("Section 1 Question 1") self.post({"s1-b1-q1-a1": 0}) @@ -110,7 +110,7 @@ def test_block_value_source_dependencies_removed_from_path(self): Test that the block value source dependencies are updated when a dependent block progress changes and gets removed from path """ - self.launchSurvey("test_progress_value_source_blocks") + self.launchSurveyV2(schema_name="test_progress_value_source_blocks") self.assertInBody("Section 1 Question 1") self.post({"s1-b1-q1-a1": 1}) @@ -155,7 +155,9 @@ def test_block_value_source_cross_section_dependencies_removed_from_path(self): Test that the block value source dependencies are updated when a dependent block progress changes and gets removed from path """ - self.launchSurvey("test_progress_value_source_blocks_cross_section") + self.launchSurveyV2( + schema_name="test_progress_value_source_blocks_cross_section" + ) self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py index 3027aea765..c1ab6823ec 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_calculated_summary.py @@ -44,7 +44,7 @@ def go_to_hub(self): # pylint: disable=locally-disabled, too-many-statements def test_happy_path(self): - self.launchSurvey("test_progress_value_source_calculated_summary") + self.launchSurveyV2(schema_name="test_progress_value_source_calculated_summary") self.assertInBody("Choose another section to complete") self.assertInBody("Calculated Summary") @@ -146,7 +146,7 @@ def test_happy_path(self): # pylint: disable=locally-disabled, too-many-statements def test_calculated_summary_first_incomplete_then_complete(self): - self.launchSurvey("test_progress_value_source_calculated_summary") + self.launchSurveyV2(schema_name="test_progress_value_source_calculated_summary") # 1. Start completing the first section self.go_to_section("section-1") @@ -258,7 +258,7 @@ def test_calculated_summary_first_incomplete_then_complete(self): self.assert_section_status(4, "Completed", ["James Bond"]) def test_happy_path_then_make_calculated_summary_incomplete(self): - self.launchSurvey("test_progress_value_source_calculated_summary") + self.launchSurveyV2(schema_name="test_progress_value_source_calculated_summary") # 1. Complete section 1 self.go_to_section("section-1") @@ -339,7 +339,9 @@ def test_happy_path_then_make_calculated_summary_incomplete(self): self.assertNotInBody("Random question about") def test_progress_value_source_with_backward_chained_dependencies(self): - self.launchSurvey("test_progress_value_source_calculated_summary_extended") + self.launchSurveyV2( + schema_name="test_progress_value_source_calculated_summary_extended" + ) self.post() # 1. Complete section 7 @@ -375,7 +377,9 @@ def test_progress_value_source_with_backward_chained_dependencies(self): self.assert_section_status(6, "Completed") def test_progress_value_source_with_chained_dependencies(self): - self.launchSurvey("test_progress_value_source_calculated_summary_extended") + self.launchSurveyV2( + schema_name="test_progress_value_source_calculated_summary_extended" + ) self.post() # 1. Complete section 8, 9, 10, 11 and 12 diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py index a05257f090..6a31ef4c69 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_in_repeating_sections.py @@ -63,7 +63,9 @@ def test_disable_block_in_repeating_section_if_block_source_progress_not_complet from a block in another section is not completed """ - self.launchSurvey("test_progress_block_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_block_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -119,7 +121,9 @@ def test_disable_block_in_repeating_section_if_section_source_progress_not_compl from a block in another section is not completed """ - self.launchSurvey("test_progress_section_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_section_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -175,7 +179,9 @@ def test_enable_block_in_repeating_section_if_block_source_progress_is_completed from a block in another section is completeted """ - self.launchSurvey("test_progress_block_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_block_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -224,7 +230,9 @@ def test_enable_block_in_repeating_section_if_section_source_progress_is_complet Test that a block inside a repeating section is enabled if the progress value source from a block in another section is completeted """ - self.launchSurvey("test_progress_section_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_section_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -273,7 +281,9 @@ def test_block_progress_dependencies_updated_in_repeating_sections(self): Test that dependency blocks inside repeating sections are updated properly """ - self.launchSurvey("test_progress_block_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_block_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -364,7 +374,9 @@ def test_section_progress_dependencies_updated_in_repeating_sections(self): Test that dependency blocks inside repeating sections are updated properly """ - self.launchSurvey("test_progress_section_value_source_repeating_sections") + self.launchSurveyV2( + schema_name="test_progress_section_value_source_repeating_sections" + ) self.assertInBody("Choose another section to complete") @@ -456,8 +468,8 @@ def test_section_progress_dependencies_updated_in_repeating_sections_with_chaine """ Test that dependency blocks inside repeating sections are updated properly when there are chained dependencies """ - self.launchSurvey( - "test_progress_value_source_repeating_sections_chained_dependencies" + self.launchSurveyV2( + schema_name="test_progress_value_source_repeating_sections_chained_dependencies" ) self.assertInBody("Choose another section to complete") diff --git a/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py b/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py index d528cfc9a7..0e8078ab74 100644 --- a/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py +++ b/tests/integration/questionnaire/test_questionnaire_progress_value_source_section_enabled.py @@ -20,7 +20,9 @@ def test_enable_section_by_progress_linear_flow(self): In a linear flow with no hub """ - self.launchSurvey("test_progress_value_source_section_enabled_no_hub") + self.launchSurveyV2( + schema_name="test_progress_value_source_section_enabled_no_hub" + ) self.assertInBody("Section 1 Question 1") self.post({"s1-b1-q1-a1": 1}) @@ -37,7 +39,9 @@ def test_enable_section_by_progress_hub_flow(self): In a hub flow """ - self.launchSurvey("test_progress_value_source_section_enabled_hub") + self.launchSurveyV2( + schema_name="test_progress_value_source_section_enabled_hub" + ) # 1. Only section 1 shows on the hub self.assertInBody("Choose another section to complete") @@ -79,7 +83,9 @@ def test_value_source_dependency_enable_section_by_progress_hub_flow(self): are updated when the section progress changes """ - self.launchSurvey("test_progress_value_source_section_enabled_hub") + self.launchSurveyV2( + schema_name="test_progress_value_source_section_enabled_hub" + ) self.assertInBody("Choose another section to complete") self.assertInBody("Section 1") @@ -116,7 +122,9 @@ def test_value_source_dependency_enable_section_by_progress_hub_flow(self): self.assert_section_status(2, "Not started", ["Start section"]) def test_enable_section_by_progress_hub_complex_happy_path(self): - self.launchSurvey("test_progress_value_source_section_enabled_hub_complex") + self.launchSurveyV2( + schema_name="test_progress_value_source_section_enabled_hub_complex" + ) self.assertInBody("Choose another section to complete") self.assertInBody("Section 1") diff --git a/tests/integration/questionnaire/test_questionnaire_question_definition.py b/tests/integration/questionnaire/test_questionnaire_question_definition.py index 640e18d602..53c1921545 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_definition.py +++ b/tests/integration/questionnaire/test_questionnaire_question_definition.py @@ -5,7 +5,7 @@ class TestQuestionnaireQuestionDefinition(IntegrationTestCase): def test_question_definition(self): # Given I launch a questionnaire with definitions - self.launchSurvey("test_question_definition") + self.launchSurveyV2(schema_name="test_question_definition") # When I start the survey I am presented with the definitions title and content correctly self.assertInBody( diff --git a/tests/integration/questionnaire/test_questionnaire_question_guidance.py b/tests/integration/questionnaire/test_questionnaire_question_guidance.py index a379bc1afe..5157ea2bc2 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_guidance.py +++ b/tests/integration/questionnaire/test_questionnaire_question_guidance.py @@ -5,7 +5,7 @@ class TestQuestionnaireQuestionGuidance(IntegrationTestCase): def test_question_guidance(self): # Given I launch a questionnaire with various guidance - self.launchSurvey("test_question_guidance") + self.launchSurveyV2(schema_name="test_question_guidance") self.post(action="start_questionnaire") # When I start the survey I am presented with the title guidance correctly diff --git a/tests/integration/questionnaire/test_questionnaire_question_variants.py b/tests/integration/questionnaire/test_questionnaire_question_variants.py index 1c8d2b9e11..18141fef72 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_variants.py +++ b/tests/integration/questionnaire/test_questionnaire_question_variants.py @@ -7,17 +7,17 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def test_non_proxy_answer_shows_non_proxy_title(self): - self.launchSurvey("test_variants_question") + self.launchSurveyV2(schema_name="test_variants_question") self.complete_first_section(proxy=False) def test_proxy_answer_shows_proxy_title(self): - self.launchSurvey("test_variants_question") + self.launchSurveyV2(schema_name="test_variants_question") self.complete_first_section(proxy=True) def test_summaries_proxy(self): - self.launchSurvey("test_variants_question") + self.launchSurveyV2(schema_name="test_variants_question") self.complete_first_section(proxy=True) diff --git a/tests/integration/questionnaire/test_questionnaire_radio_voluntary.py b/tests/integration/questionnaire/test_questionnaire_radio_voluntary.py index fd73f08bf3..e456e10f82 100644 --- a/tests/integration/questionnaire/test_questionnaire_radio_voluntary.py +++ b/tests/integration/questionnaire/test_questionnaire_radio_voluntary.py @@ -5,7 +5,7 @@ class TestQuestionnaireRadioVoluntary(IntegrationTestCase): BASE_URL = "/questionnaire/" def test_radio_voluntary(self): - self.launchSurvey("test_radio_voluntary") + self.launchSurveyV2(schema_name="test_radio_voluntary") self.post({"radio-voluntary-true-answer": "Coffee"}) self.previous() self.post(action="clear_radios") @@ -17,7 +17,7 @@ class TestQuestionnaireRepeatingSectionRadioVoluntary(IntegrationTestCase): BASE_URL = "/questionnaire/" def test_clear_radios(self): - self.launchSurvey("test_radio_voluntary_with_repeating_sections") + self.launchSurveyV2(schema_name="test_radio_voluntary_with_repeating_sections") self.post() self.post({"anyone-lives-here": "Yes"}) self.post({"first-name": "James", "last-name": "May"}) diff --git a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py index 1d84f07220..b6e3e2ff23 100644 --- a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py +++ b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_checkbox.py @@ -6,7 +6,9 @@ def test_add_list_question_displayed_before_list_collector_and_return_to_in_url( self, ): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_checkbox") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_checkbox" + ) # When self.post({"anyone-usually-live-at-answer": ["I think so", "No"]}) @@ -18,7 +20,9 @@ def test_add_list_question_displayed_before_list_collector_and_return_to_in_url( def test_previous_link_when_list_empty_with_return_to_query_string(self): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_checkbox") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_checkbox" + ) self.post({"anyone-usually-live-at-answer": ["I think so", "No"]}) # When @@ -29,7 +33,9 @@ def test_previous_link_when_list_empty_with_return_to_query_string(self): def test_previous_link_when_list_not_empty(self): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_checkbox") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_checkbox" + ) self.post({"anyone-usually-live-at-answer": ["I think so", "No"]}) self.add_person("John", "Doe") self.post({"anyone-else-live-at-answer": "Yes"}) @@ -44,7 +50,9 @@ def test_previous_link_return_to_list_collector_when_invalid_return_to_block_id( self, ): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_checkbox") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_checkbox" + ) self.post({"anyone-usually-live-at-answer": ["I think so"]}) url_with_invalid_return_to = self.last_url + "-invalid" diff --git a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py index bd22511d3f..09030e7f13 100644 --- a/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py +++ b/tests/integration/questionnaire/test_questionnaire_redirect_to_list_add_question_action_radio.py @@ -6,7 +6,9 @@ def test_add_list_question_displayed_before_list_collector_and_return_to_in_url( self, ): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_radio") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_radio" + ) # When self.post({"anyone-usually-live-at-answer": "Yes"}) @@ -18,7 +20,9 @@ def test_add_list_question_displayed_before_list_collector_and_return_to_in_url( def test_previous_link_when_list_empty_with_return_to_query_string(self): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_radio") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_radio" + ) self.post({"anyone-usually-live-at-answer": "Yes"}) # When @@ -29,7 +33,9 @@ def test_previous_link_when_list_empty_with_return_to_query_string(self): def test_previous_link_when_list_not_empty(self): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_radio") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_radio" + ) self.post({"anyone-usually-live-at-answer": "Yes"}) self.add_person("John", "Doe") self.post({"anyone-else-live-at-answer": "Yes"}) @@ -44,7 +50,9 @@ def test_previous_link_return_to_list_collector_when_invalid_return_to_block_id( self, ): # Given - self.launchSurvey("test_answer_action_redirect_to_list_add_block_radio") + self.launchSurveyV2( + schema_name="test_answer_action_redirect_to_list_add_block_radio" + ) self.post({"anyone-usually-live-at-answer": "Yes"}) url_with_invalid_return_to = self.last_url + "-invalid" diff --git a/tests/integration/questionnaire/test_questionnaire_relationships.py b/tests/integration/questionnaire/test_questionnaire_relationships.py index 361c9fff62..da04ddd25c 100644 --- a/tests/integration/questionnaire/test_questionnaire_relationships.py +++ b/tests/integration/questionnaire/test_questionnaire_relationships.py @@ -8,7 +8,7 @@ def remove_list_item(self, position): self.post({"remove-confirmation": "Yes"}) def test_valid_relationship(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -19,7 +19,7 @@ def test_valid_relationship(self): self.assertInUrl("/questionnaire/sections/") def test_resume_should_not_show_last_viewed_guidance(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -28,7 +28,7 @@ def test_resume_should_not_show_last_viewed_guidance(self): self.assertNotInBody("This is the last viewed question in this section") def test_last_relationship(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") first_list_item_id = self.add_person("Marie", "Doe") second_list_item_id = self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -38,17 +38,17 @@ def test_last_relationship(self): ) def test_get_relationships_when_not_on_path_raises_404(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.get("/questionnaire/relationships") self.assertStatusNotFound() def test_invalid_relationship_raises_404(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.get("/questionnaire/relationships/people/fake-id/to/another-fake-id") self.assertStatusNotFound() def test_go_to_invalid_relationship(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -57,7 +57,7 @@ def test_go_to_invalid_relationship(self): self.assertInUrl("/questionnaire/relationships") def test_failed_validation(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -65,7 +65,7 @@ def test_failed_validation(self): self.assertInBody("There is a problem with your answer") def test_multiple_relationships(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.add_person("Susan", "Doe") @@ -78,7 +78,7 @@ def test_multiple_relationships(self): self.assertInUrl("/questionnaire/sections/section/") def test_relationships_removed_when_list_item_removed(self): - self.launchSurvey("test_relationships", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_relationships", roles=["dumper"]) self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.add_person("Susan", "Doe") @@ -104,7 +104,7 @@ def test_relationships_removed_when_list_item_removed(self): self.assertNotIn(list_item_ids[-1], relationship.values()) def test_relationship_not_altered_when_new_list_item_not_submitted(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") list_item_ids_original = self.get_list_item_ids() @@ -120,7 +120,7 @@ def test_relationship_not_altered_when_new_list_item_not_submitted(self): self.assertEqual(list_item_ids_original, list_item_ids_new) def test_post_to_relationships_root(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") self.add_person("Marie", "Doe") self.add_person("John", "Doe") self.post({"anyone-else": "No"}) @@ -128,7 +128,7 @@ def test_post_to_relationships_root(self): self.assertStatusOK() def test_head_request_on_relationships_url(self): - self.launchSurvey("test_relationships") + self.launchSurveyV2(schema_name="test_relationships") first_list_item_id = self.add_person("Marie", "Doe") second_list_item_id = self.add_person("John", "Doe") self.post({"anyone-else": "No"}) diff --git a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py index a0fd44c4b7..de8d3ba10b 100644 --- a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py +++ b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py @@ -3,7 +3,9 @@ class TestQuestionnaireRelationshipsUnrelated(QuestionnaireTestCase): def launch_survey_and_add_people(self): - self.launchSurvey("test_relationships_unrelated", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_relationships_unrelated", roles=["dumper"] + ) self.add_person("Andrew", "Austin") self.add_person("Betty", "Burns") self.add_person("Carla", "Clark") @@ -36,7 +38,7 @@ def test_is_accessible_when_list_name_and_list_item_valid( self.assertInBody("Are any of these people related to you?") def test_is_not_accessible_when_invalid_list_item(self): - self.launchSurvey("test_relationships_unrelated") + self.launchSurveyV2(schema_name="test_relationships_unrelated") self.get( "/questionnaire/relationships/people/invalid-id/related-to-anyone-else" ) diff --git a/tests/integration/questionnaire/test_questionnaire_resume.py b/tests/integration/questionnaire/test_questionnaire_resume.py index 49f2eb652d..9bea235762 100644 --- a/tests/integration/questionnaire/test_questionnaire_resume.py +++ b/tests/integration/questionnaire/test_questionnaire_resume.py @@ -5,39 +5,43 @@ class TestResume(IntegrationTestCase): def test_navigating_backwards(self): # Given I submit the first page - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post({"name-answer": "Joe Bloggs"}) # When I go back to the first page, sign out and then resume self.get("/questionnaire/name-block") self.signOut() - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") # Then I should resume on the first incomplete location self.assertEqual(SUBMIT_URL_PATH, self.last_url) def test_sign_out_on_section_summary(self): # Given I complete the first section - self.launchSurvey("test_section_summary", display_address="test address") + self.launchSurveyV2( + schema_name="test_section_summary", display_address="test address" + ) self.post({"insurance-type-answer": "Both"}) self.post({"insurance-address-answer": "Address"}) self.post({"listed-answer": "No"}) # When I sign out and then resume self.signOut() - self.launchSurvey("test_section_summary", display_address="test address") + self.launchSurveyV2( + schema_name="test_section_summary", display_address="test address" + ) # Then I should resume on the start of the next section self.assertInUrl("/questionnaire/house-type/") def test_after_submission(self): # Given I complete the questionnaire and submit - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post({"name-answer": "Joe Bloggs"}) self.post() # When I resume - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") # Then I should resume on the thank you page self.assertInUrl(THANK_YOU_URL_PATH) diff --git a/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py b/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py index 39e7db797c..0f5257c116 100644 --- a/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py +++ b/tests/integration/questionnaire/test_questionnaire_routing_to_questionnaire_end.py @@ -4,7 +4,7 @@ class TestRoutingToQuestionnaireEndBase(IntegrationTestCase): def _launch_and_complete_questionnaire(self, schema): - self.launchSurvey(schema) + self.launchSurveyV2(schema_name=schema) self.post({"test-answer": "No"}) diff --git a/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py b/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py index 3293567457..325b3eb93b 100644 --- a/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py +++ b/tests/integration/questionnaire/test_questionnaire_routing_to_section_end.py @@ -6,7 +6,7 @@ class TestRoutingToSectionEnd(IntegrationTestCase): def test_section_summary_not_available_if_any_question_in_section_incomplete(self): # Given I launch questionnaire and have not answered questions for a section - self.launchSurvey("test_routing_to_section_end") + self.launchSurveyV2(schema_name="test_routing_to_section_end") # When I try access the section summary self.get(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) @@ -16,14 +16,14 @@ def test_section_summary_not_available_if_any_question_in_section_incomplete(sel def test_section_summary_available_after_completing_section(self): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_routing_to_section_end") + self.launchSurveyV2(schema_name="test_routing_to_section_end") self.post({"test-answer": "No"}) self.assertInBody("Were you forced to complete section 1?") self.assertInUrl(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) def test_section_summary_not_available_after_invalidating_section(self): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_routing_to_section_end") + self.launchSurveyV2(schema_name="test_routing_to_section_end") self.post({"test-answer": "No"}) self.assertInBody("Were you forced to complete section 1?") self.assertInUrl(SECTION_SUMMARY_URL_PATH.format(section_id="test-section")) @@ -42,7 +42,7 @@ def test_section_summary_available_after_completing_section_new_routing_engine( self, ): # Given I launch questionnaire and have completed a section - self.launchSurvey("test_routing_number_equals") + self.launchSurveyV2(schema_name="test_routing_number_equals") self.post({"answer": "123"}) self.post() self.assertInBody("Check your answers and submit") diff --git a/tests/integration/questionnaire/test_questionnaire_same_name_items.py b/tests/integration/questionnaire/test_questionnaire_same_name_items.py index 133fff2adf..78b3beb487 100644 --- a/tests/integration/questionnaire/test_questionnaire_same_name_items.py +++ b/tests/integration/questionnaire/test_questionnaire_same_name_items.py @@ -4,7 +4,9 @@ class TestQuestionnaireSameNameItems(QuestionnaireTestCase): def test_same_name_items(self): - self.launchSurvey("test_list_collector_same_name_items", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_list_collector_same_name_items", roles=["dumper"] + ) self.post({"you-live-here": "Yes"}) self.post({"first-name": "James", "middle-names": "Brian", "last-name": "May"}) @@ -22,7 +24,9 @@ def test_same_name_items(self): assert item_id_b in actual["LISTS"][0]["same_name_items"] def test_same_name_items_edit_primary(self): - self.launchSurvey("test_list_collector_same_name_items", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_list_collector_same_name_items", roles=["dumper"] + ) self.post({"you-live-here": "Yes"}) self.post({"first-name": "James", "last-name": "May"}) @@ -42,7 +46,9 @@ def test_same_name_items_edit_primary(self): assert "same_name_items" not in actual["LISTS"][0] def test_same_name_remove_primary(self): - self.launchSurvey("test_list_collector_same_name_items", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_list_collector_same_name_items", roles=["dumper"] + ) self.post({"you-live-here": "Yes"}) self.post({"first-name": "James", "last-name": "May"}) @@ -60,7 +66,9 @@ def test_same_name_remove_primary(self): assert "same_name_items" not in actual["LISTS"][0] def test_same_name_items_remove_non_primary(self): - self.launchSurvey("test_list_collector_same_name_items", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_list_collector_same_name_items", roles=["dumper"] + ) self.post({"you-live-here": "Yes"}) self.post({"first-name": "James", "last-name": "May"}) @@ -80,7 +88,9 @@ def test_same_name_items_remove_non_primary(self): assert "same_name_items" not in actual["LISTS"][0] def test_same_name_items_edit_non_primary(self): - self.launchSurvey("test_list_collector_same_name_items", roles=["dumper"]) + self.launchSurveyV2( + schema_name="test_list_collector_same_name_items", roles=["dumper"] + ) self.post({"you-live-here": "Yes"}) self.post({"first-name": "Joe", "last-name": "Smith"}) diff --git a/tests/integration/questionnaire/test_questionnaire_submission.py b/tests/integration/questionnaire/test_questionnaire_submission.py index 1ebedc1f68..8ec22fbeae 100644 --- a/tests/integration/questionnaire/test_questionnaire_submission.py +++ b/tests/integration/questionnaire/test_questionnaire_submission.py @@ -26,7 +26,7 @@ def _mock_submission_failure(self): class TestQuestionnaireSubmission(SubmissionTestCase): def _launch_and_submit_questionnaire(self): # Launch questionnaire - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # Answer questions and submit survey self.post(action="start_questionnaire") @@ -63,7 +63,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): # Given token = self.token_generator.create_token_with_schema_url( - "test_textarea", schema_url + schema_name=None, schema_url=schema_url ) # When @@ -88,7 +88,7 @@ def schema_url_mock(_url, _request): class TestQuestionnaireSubmissionHub(SubmissionTestCase): def _launch_and_submit_questionnaire(self): # Launch questionnaire - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # Answer questions and submit questionnaire self.post() @@ -131,7 +131,7 @@ def test_unsuccessful_submission(self): class TestQuestionnaireSubmissionWithSummary(SubmissionTestCase): def _launch_and_submit_questionnaire(self): # Launch questionnaire - self.launchSurvey("test_submit_with_summary") + self.launchSurveyV2(schema_name="test_submit_with_summary") # Answer questions and submit survey self.post() @@ -154,7 +154,7 @@ def test_unsuccessful_submission(self): self._mock_submission_failure() # Given I launch and answer a questionnaire, When I submit but the submissions fails - self.launchSurvey("test_submit_with_summary") + self.launchSurveyV2(schema_name="test_submit_with_summary") self.post() self.post({"dessert-answer": "Cake"}) self.post({"dessert-confirmation-answer": "Yes"}) diff --git a/tests/integration/questionnaire/test_questionnaire_submit.py b/tests/integration/questionnaire/test_questionnaire_submit.py index 4e6d06b53e..bde8cb18f2 100644 --- a/tests/integration/questionnaire/test_questionnaire_submit.py +++ b/tests/integration/questionnaire/test_questionnaire_submit.py @@ -4,12 +4,12 @@ class TestQuestionnaireSubmit(IntegrationTestCase): def _launch_and_complete_questionnaire(self, schema): - self.launchSurvey(schema) + self.launchSurveyV2(schema_name=schema) self.post({"test-answer": "No"}) def test_submit_page_not_accessible_when_hub_enabled(self): # Given I launch a hub questionnaire - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") # When I try access the submit page for method in [self.get, self.post]: @@ -21,7 +21,7 @@ def test_submit_page_not_accessible_when_hub_enabled(self): def test_invalid_block_once_questionnaire_complete_raises_404(self): # Given I launch questionnaire - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When I proceed through the questionnaire self.post(action="start_questionnaire") @@ -41,7 +41,7 @@ def test_submit_page_not_available_after_invalidating_section(self): "test_routing_to_questionnaire_end_multiple_sections", ]: with self.subTest(schema=schema): - self.launchSurvey(schema) + self.launchSurveyV2(schema_name=schema) self.post({"test-answer": "No"}) self.assertInUrl(SUBMIT_URL_PATH) @@ -62,7 +62,7 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest self, ): # Given a partially completed questionnaire - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") self.post(action="start_questionnaire") self.assertInBody("What is your favourite breakfast food") @@ -76,7 +76,7 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest def test_is_displayed(self): # Given I launch a questionnaire - self.launchSurvey("test_submit_with_custom_submission_text") + self.launchSurveyV2(schema_name="test_submit_with_custom_submission_text") # When I complete the questionnaire self.post(action="start_questionnaire") @@ -98,7 +98,9 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest self, ): # Given a partially completed questionnaire - self.launchSurvey("test_routing_to_questionnaire_end_single_section") + self.launchSurveyV2( + schema_name="test_routing_to_questionnaire_end_single_section" + ) self.post({"test-answer": "Yes"}) # When I make a GET or POST request to the submit page @@ -111,7 +113,9 @@ def test_accessing_submit_page_redirects_to_first_incomplete_question_when_quest def test_is_displayed(self): # Given I launch a questionnaire - self.launchSurvey("test_routing_to_questionnaire_end_multiple_sections") + self.launchSurveyV2( + schema_name="test_routing_to_questionnaire_end_multiple_sections" + ) # When I complete the questionnaire self.post({"test-answer": "Yes"}) diff --git a/tests/integration/routes/test_confirmation_email.py b/tests/integration/routes/test_confirmation_email.py index a09ef85e78..5de8c52154 100644 --- a/tests/integration/routes/test_confirmation_email.py +++ b/tests/integration/routes/test_confirmation_email.py @@ -12,7 +12,7 @@ def setUp(self): super().setUp() def _launch_and_complete_questionnaire(self): - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") self.post({"answer_id": "Yes"}) self.post() @@ -70,7 +70,7 @@ def test_missing_email_param_confirm_email(self): def test_confirm_email_with_confirmation_email_not_set(self): # Given I launch the test_thank_you questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post() self.post() @@ -83,7 +83,7 @@ def test_confirm_email_with_confirmation_email_not_set(self): def test_confirmation_email_send_with_confirmation_email_not_set(self): # Given I launch the test_thank_you questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post() self.post() @@ -109,7 +109,7 @@ def test_bad_signature_confirmation_email_send(self): def test_thank_you_page_get_not_allowed(self): # Given I launch the test_confirmation_email questionnaire - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") # When I try to view the thank you page without completing the questionnaire self.get("/submitted/thank-you/") @@ -119,7 +119,7 @@ def test_thank_you_page_get_not_allowed(self): def test_thank_you_page_post_not_allowed(self): # Given I launch the test_confirmation_email questionnaire - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") # When I try to POST to the thank you page without completing the questionnaire self.post(url="/submitted/thank-you/") @@ -150,7 +150,7 @@ def test_default_themed_schema_with_confirmation_email_true(self): def test_default_themed_schema_with_confirmation_email_not_set(self): # Given I launch the test_checkbox questionnaire, which doesn't have email confirmation set in the schema - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When I complete the questionnaire self.post({"mandatory-checkbox-answer": "Tuna"}) diff --git a/tests/integration/routes/test_cookie.py b/tests/integration/routes/test_cookie.py index d22a6af4b3..9897db045e 100644 --- a/tests/integration/routes/test_cookie.py +++ b/tests/integration/routes/test_cookie.py @@ -3,7 +3,7 @@ class TestCookie(IntegrationTestCase): def test_cookie_contents(self): - self.launchSurvey() + self.launchSurveyV2() cookie = self.getCookie() self.assertIsNotNone(cookie.get("_fresh")) diff --git a/tests/integration/routes/test_dump.py b/tests/integration/routes/test_dump.py index 883b90a41a..6e49fac5f9 100644 --- a/tests/integration/routes/test_dump.py +++ b/tests/integration/routes/test_dump.py @@ -14,7 +14,9 @@ def test_dump_debug_not_authenticated(self): def test_dump_debug_authenticated_missing_role(self): # Given I am an authenticated user who has launched a survey # but does not have the 'dumper' role in my metadata - self.launchSurvey("test_radio_mandatory_with_detail_answer_mandatory") + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory" + ) # When I attempt to dump the questionnaire store self.get("/dump/debug") @@ -25,8 +27,9 @@ def test_dump_debug_authenticated_missing_role(self): def test_dump_debug_authenticated_with_role(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey( - "test_radio_mandatory_with_detail_answer_mandatory", roles=["dumper"] + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory", + roles=["dumper"], ) # And I attempt to dump the questionnaire store @@ -48,7 +51,9 @@ def test_dump_submission_not_authenticated(self): def test_dump_submission_authenticated_missing_role(self): # Given I am an authenticated user who has launched a survey # but does not have the 'dumper' role in my metadata - self.launchSurvey("test_radio_mandatory_with_detail_answer_mandatory") + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory" + ) # When I attempt to dump the submission payload self.get("/dump/submission") @@ -59,8 +64,9 @@ def test_dump_submission_authenticated_missing_role(self): def test_dump_submission_authenticated_with_role_no_answers(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey( - "test_radio_mandatory_with_detail_answer_mandatory", roles=["dumper"] + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory", + roles=["dumper"], ) # When I haven't submitted any answers @@ -75,23 +81,32 @@ def test_dump_submission_authenticated_with_role_no_answers(self): # tx_id and submitted_at are dynamic; so copy them over expected = { "submission": { - "version": "0.0.3", - "survey_id": "0", - "flushed": False, - "origin": "uk.gov.ons.edc.eq", - "type": "uk.gov.ons.edc.eq:surveyresponse", - "tx_id": actual["submission"]["tx_id"], - "submitted_at": actual["submission"]["submitted_at"], "case_id": actual["submission"]["case_id"], - "collection": { - "period": "201604", - "exercise_sid": "789", - "schema_name": "test_radio_mandatory_with_detail_answer_mandatory", - }, + "collection_exercise_sid": "789", "data": {"answers": [], "lists": []}, - "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, + "data_version": "0.0.3", + "flushed": False, "launch_language_code": "en", + "origin": "uk.gov.ons.edc.eq", + "schema_name": "test_radio_mandatory_with_detail_answer_mandatory", "submission_language_code": "en", + "submitted_at": actual["submission"]["submitted_at"], + "survey_metadata": { + "display_address": "68 Abingdon Road, " "Goathill", + "employment_date": "1983-06-02", + "period_id": "201604", + "period_str": "April 2016", + "ref_p_end_date": "2016-04-30", + "ref_p_start_date": "2016-04-01", + "ru_name": "Integration Testing", + "ru_ref": "12345678901A", + "survey_id": "0", + "trad_as": "Integration Tests", + "user_id": "integration-test", + }, + "tx_id": actual["submission"]["tx_id"], + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "v2", } } @@ -100,7 +115,7 @@ def test_dump_submission_authenticated_with_role_no_answers(self): def test_dump_submission_authenticated_with_role_with_answers(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey("test_radio_mandatory", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_radio_mandatory", roles=["dumper"]) # When I submit an answer self.post(post_data={"radio-mandatory-answer": "Coffee"}) @@ -117,29 +132,38 @@ def test_dump_submission_authenticated_with_role_with_answers(self): # tx_id and submitted_at are dynamic; so copy them over expected = { "submission": { - "version": "0.0.3", - "survey_id": "0", - "flushed": False, - "origin": "uk.gov.ons.edc.eq", - "type": "uk.gov.ons.edc.eq:surveyresponse", - "tx_id": actual["submission"]["tx_id"], - "started_at": actual["submission"]["started_at"], - "submitted_at": actual["submission"]["submitted_at"], "case_id": actual["submission"]["case_id"], - "collection": { - "period": "201604", - "exercise_sid": "789", - "schema_name": "test_radio_mandatory", - }, + "collection_exercise_sid": "789", "data": { "answers": [ {"answer_id": "radio-mandatory-answer", "value": "Coffee"} ], "lists": [], }, - "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, + "data_version": "0.0.3", + "flushed": False, "launch_language_code": "en", + "origin": "uk.gov.ons.edc.eq", + "schema_name": "test_radio_mandatory", + "started_at": actual["submission"]["started_at"], "submission_language_code": "en", + "submitted_at": actual["submission"]["submitted_at"], + "survey_metadata": { + "display_address": "68 Abingdon Road, " "Goathill", + "employment_date": "1983-06-02", + "period_id": "201604", + "period_str": "April 2016", + "ref_p_end_date": "2016-04-30", + "ref_p_start_date": "2016-04-01", + "ru_name": "Integration Testing", + "ru_ref": "12345678901A", + "survey_id": "0", + "trad_as": "Integration Tests", + "user_id": "integration-test", + }, + "tx_id": actual["submission"]["tx_id"], + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "v2", } } assert actual == expected @@ -147,7 +171,7 @@ def test_dump_submission_authenticated_with_role_with_answers(self): def test_dump_submission_authenticated_with_role_with_lists(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey("test_relationships", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_relationships", roles=["dumper"]) # When I submit my answers self.post({"anyone-else": "Yes"}) @@ -166,48 +190,61 @@ def test_dump_submission_authenticated_with_role_with_lists(self): # tx_id and submitted_at are dynamic; so copy them over expected = { "submission": { - "version": "0.0.3", - "survey_id": "0", - "flushed": False, - "origin": "uk.gov.ons.edc.eq", - "type": "uk.gov.ons.edc.eq:surveyresponse", - "tx_id": actual["submission"]["tx_id"], - "started_at": actual["submission"]["started_at"], - "submitted_at": actual["submission"]["submitted_at"], "case_id": actual["submission"]["case_id"], - "collection": { - "period": "201604", - "exercise_sid": "789", - "schema_name": "test_relationships", - }, + "collection_exercise_sid": "789", "data": { "answers": [ { "answer_id": "first-name", - "value": "John", "list_item_id": actual["submission"]["data"]["answers"][0][ "list_item_id" ], + "value": "John", }, { "answer_id": "last-name", - "value": "Doe", "list_item_id": actual["submission"]["data"]["answers"][0][ "list_item_id" ], + "value": "Doe", }, {"answer_id": "anyone-else", "value": "No"}, ], "lists": [ { + "items": [ + actual["submission"]["data"]["answers"][0][ + "list_item_id" + ] + ], "name": "people", - "items": actual["submission"]["data"]["lists"][0]["items"], } ], }, - "metadata": {"ru_ref": "12345678901A", "user_id": "integration-test"}, + "data_version": "0.0.3", + "flushed": False, "launch_language_code": "en", + "origin": "uk.gov.ons.edc.eq", + "schema_name": "test_relationships", + "started_at": actual["submission"]["started_at"], "submission_language_code": "en", + "submitted_at": actual["submission"]["submitted_at"], + "survey_metadata": { + "display_address": "68 Abingdon Road, " "Goathill", + "employment_date": "1983-06-02", + "period_id": "201604", + "period_str": "April 2016", + "ref_p_end_date": "2016-04-30", + "ref_p_start_date": "2016-04-01", + "ru_name": "Integration Testing", + "ru_ref": "12345678901A", + "survey_id": "0", + "trad_as": "Integration Tests", + "user_id": "integration-test", + }, + "tx_id": actual["submission"]["tx_id"], + "type": "uk.gov.ons.edc.eq:surveyresponse", + "version": "v2", } } assert actual == expected @@ -225,7 +262,9 @@ def test_dump_route_not_authenticated(self): def test_dump_route_authenticated_missing_role(self): # Given I am an authenticated user who has launched a survey # but does not have the 'dumper' role in my metadata - self.launchSurvey("test_radio_mandatory_with_detail_answer_mandatory") + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory" + ) # When I attempt to dump the questionnaire store self.get("/dump/routing-path") @@ -236,8 +275,9 @@ def test_dump_route_authenticated_missing_role(self): def test_dump_route_authenticated_with_role(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey( - "test_radio_mandatory_with_detail_answer_mandatory", roles=["dumper"] + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory", + roles=["dumper"], ) # And I attempt to dump the questionnaire store @@ -249,8 +289,9 @@ def test_dump_route_authenticated_with_role(self): def test_dump_route_authenticated_with_role_no_answers(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey( - "test_radio_mandatory_with_detail_answer_mandatory", roles=["dumper"] + self.launchSurveyV2( + schema_name="test_radio_mandatory_with_detail_answer_mandatory", + roles=["dumper"], ) # When I haven't submitted any answers @@ -276,7 +317,7 @@ def test_dump_route_authenticated_with_role_no_answers(self): def test_dump_submission_authenticated_with_role_with_answers(self): # Given I am an authenticated user who has launched a survey # and does have the 'dumper' role in my metadata - self.launchSurvey("test_radio_mandatory", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_radio_mandatory", roles=["dumper"]) # When I submit an answer self.post(post_data={"radio-mandatory-answer": "Coffee"}) diff --git a/tests/integration/routes/test_errors.py b/tests/integration/routes/test_errors.py index bdadc81945..7871cf4f08 100644 --- a/tests/integration/routes/test_errors.py +++ b/tests/integration/routes/test_errors.py @@ -17,22 +17,27 @@ class TestErrors(IntegrationTestCase): # pylint: disable=too-many-public-methods example_payload = { - "user_id": "integration-test", - "period_str": "April 2016", - "period_id": "201604", + "survey_metadata": { + "data": { + "user_id": "integration-test", + "period_str": "April 2016", + "period_id": "201604", + "ru_ref": "12345678901A", + "ru_name": "Integration Testing", + "ref_p_start_date": "2016-04-01", + "ref_p_end_date": "2016-04-30", + "return_by": "2016-05-06", + "employment_date": "1983-06-02", + "region_code": "GB-ENG", + } + }, "collection_exercise_sid": "789", - "ru_ref": "12345678901A", "response_id": "1234567890123456", - "ru_name": "Integration Testing", - "ref_p_start_date": "2016-04-01", - "ref_p_end_date": "2016-04-30", - "return_by": "2016-05-06", - "employment_date": "1983-06-02", - "region_code": "GB-ENG", "language_code": "en", "account_service_url": "http://correct.place", "roles": [], "response_expires_at": get_response_expires_at(), + "version": "v2", } def _assert_generic_500_page_content(self): @@ -73,8 +78,10 @@ def test_errors_404(self): self.assertNotInBody("Sign out") def test_errors_404_with_payload(self): - with patch("tests.integration.create_token.PAYLOAD", self.example_payload): - self.launchSurvey("test_percentage") + with patch( + "tests.integration.create_token.PAYLOAD_V2_BUSINESS", self.example_payload + ): + self.launchSurveyV2(schema_name="test_percentage") self.get("/hfjdskahfjdkashfsa") self.assertStatusNotFound() @@ -88,8 +95,10 @@ def test_errors_405(self): def test_errors_500_with_payload(self): # Given - with patch("tests.integration.create_token.PAYLOAD", self.example_payload): - self.launchSurvey("test_percentage") + with patch( + "tests.integration.create_token.PAYLOAD_V2_BUSINESS", self.example_payload + ): + self.launchSurveyV2(schema_name="test_percentage") # When / Then # Patch out a class in post to raise an exception so that the application error handler # gets called @@ -102,8 +111,10 @@ def test_errors_500_with_payload(self): def test_errors_500_exception_during_error_handling(self): # Given - with patch("tests.integration.create_token.PAYLOAD", self.example_payload): - self.launchSurvey("test_percentage") + with patch( + "tests.integration.create_token.PAYLOAD_V2_BUSINESS", self.example_payload + ): + self.launchSurveyV2(schema_name="test_percentage") # When # Patch out a class in post to raise an exception so that the application error handler @@ -124,7 +135,7 @@ def test_errors_500_exception_during_error_handling(self): def test_401_theme_default_cookie_exists(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") self.assertInUrl("/questionnaire/introduction/") # When @@ -164,7 +175,7 @@ def test_401_theme_social_cookie_exists(self): def test_401_no_cookie(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") self.assertInUrl("/questionnaire/introduction/") # When @@ -186,7 +197,7 @@ def test_401_no_cookie(self): def test_403_theme_default_cookie_exists(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When cookie = self.getUrlAndCookie("/dump/debug") @@ -217,7 +228,7 @@ def test_403_theme_social_cookie_exists(self): def test_403_no_cookie(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When token = 123 @@ -239,7 +250,7 @@ def test_403_no_cookie(self): def test_404_theme_default_cookie_exists(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When cookie = self.getUrlAndCookie("/abc123") @@ -272,7 +283,7 @@ def test_404_theme_social_cookie_exists(self): def test_404_no_cookie(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When self.deleteCookieAndGetUrl("/abc123") @@ -289,7 +300,7 @@ def test_404_no_cookie(self): def test_404_no_cookie_unauthenticated(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When self.exit() @@ -307,7 +318,7 @@ def test_404_no_cookie_unauthenticated(self): def test_500_theme_default_cookie_exists(self): # Given - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # When with patch( @@ -350,7 +361,7 @@ def test_500_theme_not_set_in_cookie(self): "app.routes.session.set_schema_context_in_cookie", side_effect=Exception("Theme set failed"), ): - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") # Then I see the generic 500 error page cookie = self.getCookie() @@ -403,13 +414,13 @@ def test_submission_failed_theme_social_cookie_exists(self): ) def test_preview_not_enabled_results_in_404(self): - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") self.post(action="start_questionnaire") self.get("/questionnaire/preview/") self.assertStatusCode(404) def launchAndFailSubmission(self, schema): - self.launchSurvey(schema) + self.launchSurveyV2(schema_name=schema) self.post() self.post() self.post() diff --git a/tests/integration/routes/test_feedback.py b/tests/integration/routes/test_feedback.py index 7147e8bd95..e39022f9bf 100644 --- a/tests/integration/routes/test_feedback.py +++ b/tests/integration/routes/test_feedback.py @@ -16,7 +16,7 @@ def setUp(self): def test_questionnaire_not_completed(self): # Given I launch the test_feedback questionnaire - self.launchSurvey("test_feedback") + self.launchSurveyV2(schema_name="test_feedback") # When I try to view the feedback page without completing the questionnaire self.get(self.SEND_FEEDBACK_URL) @@ -26,7 +26,7 @@ def test_questionnaire_not_completed(self): def test_questionnaire_not_completed_post(self): # Given I launch the test_feedback questionnaire - self.launchSurvey("test_feedback") + self.launchSurveyV2(schema_name="test_feedback") # When I try to POST to the feedback page without completing the questionnaire self.post(url=self.SEND_FEEDBACK_URL) @@ -46,7 +46,7 @@ def test_feedback_sent_page_without_feedback_submitted(self): def test_feedback_flag_not_set_in_schema(self): # Given I launch the test_textfield questionnaire - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post() self.post() @@ -239,7 +239,7 @@ def test_feedback_call_to_action_shown(self): def test_feedback_submission(self): # Given I submit the email confirmation form - self.launchSurvey("test_feedback_email_confirmation") + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") self.post({"answer_id": "Yes"}) self.post() self.post({"email": "email@example.com"}) @@ -286,7 +286,7 @@ def test_feedback_submission_v2_social(self): def test_feedback_call_to_action_visible_on_email_confirmation(self): # Given I complete the survey - self.launchSurvey("test_feedback_email_confirmation") + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") self.post({"answer_id": "Yes"}) self.post() @@ -299,7 +299,7 @@ def test_feedback_call_to_action_visible_on_email_confirmation(self): def test_feedback_submission_from_email_confirmation(self): # Given I submit the email confirmation form - self.launchSurvey("test_feedback_email_confirmation") + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") self.post({"answer_id": "Yes"}) self.post() self.post({"email": "email@example.com"}) @@ -315,7 +315,7 @@ def test_feedback_submission_from_email_confirmation(self): def test_feedback_back_breadcrumb_after_email_confirmation(self): # Given I submit the email confirmation form - self.launchSurvey("test_feedback_email_confirmation") + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") self.post({"answer_id": "Yes"}) self.post() self.post({"email": "email@example.com"}) @@ -329,7 +329,7 @@ def test_feedback_back_breadcrumb_after_email_confirmation(self): def test_feedback_submitted_done_button_after_email_confirmation(self): # Given I submit the email confirmation form after submitting feedback - self.launchSurvey("test_feedback_email_confirmation") + self.launchSurveyV2(schema_name="test_feedback_email_confirmation") self.post({"answer_id": "Yes"}) self.post() self.get("/submitted/feedback/send") @@ -387,6 +387,6 @@ def test_head_request_on_feedback_sent(self): self.assertStatusOK() def _launch_and_complete_questionnaire(self): - self.launchSurvey("test_feedback") + self.launchSurveyV2(schema_name="test_feedback") self.post({"answer_id": "Yes"}) self.post() diff --git a/tests/integration/routes/test_jwt_authentication.py b/tests/integration/routes/test_jwt_authentication.py index f0683e3cfc..eb2fa02cb1 100644 --- a/tests/integration/routes/test_jwt_authentication.py +++ b/tests/integration/routes/test_jwt_authentication.py @@ -72,21 +72,26 @@ def create_payload(): "tx_id": str(uuid.uuid4()), "jti": str(uuid.uuid4()), "case_id": str(uuid.uuid4()), - "user_id": "jimmy", "iat": int(iat), "exp": int(exp), - "period_str": "2016-01-01", - "period_id": "12", + "survey_metadata": { + "data": { + "period_str": "2016-01-01", + "period_id": "12", + "ref_p_start_date": "2016-01-01", + "ref_p_end_date": "2016-09-01", + "ru_ref": "12345678901A", + "ru_name": "Test", + "return_by": "2016-09-09", + "user_id": "jimmy", + } + }, "schema_name": "test_default", "collection_exercise_sid": "sid", - "ref_p_start_date": "2016-01-01", - "ref_p_end_date": "2016-09-01", - "ru_ref": "12345678901A", "response_id": response_id, - "ru_name": "Test", - "return_by": "2016-09-09", "account_service_url": "http://upstream.url/", "response_expires_at": get_response_expires_at(), + "version": "v2", } diff --git a/tests/integration/routes/test_questionnaire.py b/tests/integration/routes/test_questionnaire.py index 9bad635b05..5870b0a34c 100644 --- a/tests/integration/routes/test_questionnaire.py +++ b/tests/integration/routes/test_questionnaire.py @@ -3,22 +3,22 @@ class TestQuestionnaire(IntegrationTestCase): def test_head_request_on_root_url(self): - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") self.head("/questionnaire/") self.assertStatusOK() def test_head_request_on_section_url(self): - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") self.head("/questionnaire/sections/employment-section") self.assertStatusCode(302) def test_head_request_on_block_url(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.head("/questionnaire/name-block") self.assertStatusOK() def test_head_request_on_block_with_optional_date_answer(self): - self.launchSurvey("test_dates") + self.launchSurveyV2(schema_name="test_dates") self.post( { "date-range-from-answer-day": "1", @@ -46,7 +46,7 @@ def test_head_request_on_block_with_optional_date_answer(self): self.assertStatusOK() def test_options_request_before_request(self): - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") with self.assertLogs() as logs: self.options("/questionnaire/") self.assertStatusOK() @@ -55,7 +55,7 @@ def test_options_request_before_request(self): self.assertNotIn("questionnaire request", output) def test_get_request_logs_output(self): - self.launchSurvey("test_hub_and_spoke") + self.launchSurveyV2(schema_name="test_hub_and_spoke") with self.assertLogs() as logs: self.get("/questionnaire/") self.assertStatusOK() diff --git a/tests/integration/routes/test_questionnaire_language.py b/tests/integration/routes/test_questionnaire_language.py index 6e570d982b..c893ca7b78 100644 --- a/tests/integration/routes/test_questionnaire_language.py +++ b/tests/integration/routes/test_questionnaire_language.py @@ -6,21 +6,21 @@ class TestQuestionnaireLanguage(IntegrationTestCase): def test_load_cy_survey(self): # When: load a cy survey - self.launchSurvey("test_language", language_code="cy") + self.launchSurveyV2(schema_name="test_language", language_code="cy") # Then: welsh self.post() self.assertInBody("Rhowch enw") def test_load_non_existent_lang_fallback(self): # When: load a hindi survey - self.launchSurvey("test_language", language_code="hi") + self.launchSurveyV2(schema_name="test_language", language_code="hi") # Then: Falls back to english self.post() self.assertInBody("First Name") def test_language_switch_in_flight(self): # load a english survey - self.launchSurvey("test_language", language_code="en") + self.launchSurveyV2(schema_name="test_language", language_code="en") # The language is english self.post() self.assertInBody("First Name") @@ -30,7 +30,7 @@ def test_language_switch_in_flight(self): def test_switch_to_invalid_language(self): # load a english survey - self.launchSurvey("test_language", language_code="en") + self.launchSurveyV2(schema_name="test_language", language_code="en") # The language is english self.post() self.assertInBody("First Name") @@ -132,7 +132,7 @@ def test_plural_forms_rendered_using_correct_language(self): for data in test_data_sets: with self.subTest(data=data): self.setUp() - self.launchSurvey("test_language") + self.launchSurveyV2(schema_name="test_language") self.post() self.post({"first-name": "Kevin", "last-name": "Bacon"}) @@ -155,7 +155,7 @@ def test_plural_forms_rendered_using_correct_language(self): def test_error_messages(self): # load a welsh survey - self.launchSurvey("test_language", language_code="cy") + self.launchSurveyV2(schema_name="test_language", language_code="cy") # Submit and check the error message is in Welsh self.post() self.post() @@ -164,7 +164,7 @@ def test_error_messages(self): def test_language_switch_hub_submission(self): # load an English survey - self.launchSurvey("test_language", language_code="en") + self.launchSurveyV2(schema_name="test_language", language_code="en") # Complete the survey self.post() @@ -196,7 +196,7 @@ def test_language_switch_hub_submission(self): def test_last_viewed_guidance_is_displayed_after_language_switch(self): # load a welsh survey - self.launchSurvey("test_language", language_code="en") + self.launchSurveyV2(schema_name="test_language", language_code="en") self.post() self.post({"first-name": "John", "last-name": "Smith"}) @@ -210,7 +210,7 @@ def test_last_viewed_guidance_is_displayed_after_language_switch(self): def test_sign_out_cy_survey(self): # When: load a cy survey - self.launchSurvey("test_language", language_code="cy") + self.launchSurveyV2(schema_name="test_language", language_code="cy") # Then: sign out self.get(self.getSignOutButton()["href"], follow_redirects=True) # Check the text and logos are in Welsh diff --git a/tests/integration/routes/test_session.py b/tests/integration/routes/test_session.py index 02fdb13082..29f57a634e 100644 --- a/tests/integration/routes/test_session.py +++ b/tests/integration/routes/test_session.py @@ -74,7 +74,7 @@ def test_session_expired(self): ) def test_session_jti_token_expired(self): - self.launchSurvey(exp=time.time() - float(60)) + self.launchSurveyV2(exp=time.time() - float(60)) self.assertStatusUnauthorised() def test_head_request_on_session_expired(self): @@ -82,13 +82,13 @@ def test_head_request_on_session_expired(self): self.assertStatusOK() def test_head_request_on_session_signed_out(self): - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") self.get("/signed-out") self.assertStatusOK() @freeze_time(TIME_TO_FREEZE) def test_get_session_expiry_doesnt_extend_session(self): - self.launchSurvey() + self.launchSurveyV2() # Advance time by 20 mins... with freeze_time(TIME_TO_FREEZE + timedelta(minutes=20)): self.get("/session-expiry") @@ -105,7 +105,7 @@ def test_get_session_expiry_doesnt_extend_session(self): @freeze_time(TIME_TO_FREEZE) def test_patch_session_expiry_extends_session(self): - self.launchSurvey() + self.launchSurveyV2() # Advance time by 20 mins... request_time = TIME_TO_FREEZE + timedelta(minutes=20) with freeze_time(request_time): diff --git a/tests/integration/routes/test_thank_you.py b/tests/integration/routes/test_thank_you.py index ee107bf74e..5f4d26a3ba 100644 --- a/tests/integration/routes/test_thank_you.py +++ b/tests/integration/routes/test_thank_you.py @@ -5,7 +5,7 @@ class TestThankYou(IntegrationTestCase): def test_thank_you_page_no_sign_out(self): - self.launchSurvey("test_currency") + self.launchSurveyV2(schema_name="test_currency") # We fill in our answers form_data = { @@ -27,7 +27,7 @@ def test_thank_you_page_no_sign_out(self): self.assertIsNone(self.getSignOutButton()) def test_can_switch_language_on_thank_you_page(self): - self.launchSurvey("test_language") + self.launchSurveyV2(schema_name="test_language") self.post() # We fill in our answers self.post({"first-name": "Kevin", "last-name": "Bacon"}) @@ -61,14 +61,14 @@ def test_can_switch_language_on_thank_you_page(self): self.assertNotInBody("Cymraeg") def test_head_request_on_thank_you(self): - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") self.post() self.post() self.head("/submitted/thank-you") self.assertStatusOK() def test_options_request_post_submission_before_request(self): - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") self.post() self.post() @@ -80,7 +80,7 @@ def test_options_request_post_submission_before_request(self): self.assertNotIn("questionnaire request", output) def test_default_guidance(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post({"name-answer": "Adam"}) self.post() @@ -90,7 +90,7 @@ def test_default_guidance(self): ) def test_custom_guidance(self): - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post({"answer": "Yes"}) self.post() @@ -99,7 +99,7 @@ def test_custom_guidance(self): self.assertInBody('Important link') def test_back_to_surveys_link_on_thank_you(self): - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post({"answer": "Yes"}) self.post() @@ -117,7 +117,7 @@ def test_back_to_surveys_link_not_on_thank_you_theme_social(self): self.assertNotInBody(ACCOUNT_SERVICE_TODO_PATH) def test_view_answers_after_submission_guidance(self): - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post({"answer": "Yes"}) self.post() diff --git a/tests/integration/routes/test_view_preview_questions.py b/tests/integration/routes/test_view_preview_questions.py index 5d37835b0b..cfca86e921 100644 --- a/tests/integration/routes/test_view_preview_questions.py +++ b/tests/integration/routes/test_view_preview_questions.py @@ -6,7 +6,7 @@ def test_download_pdf(self): super().setUp() # Given I launch a questionnaire and open preview of questions - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") self.get("/questionnaire/preview/") # When I try to download preview of questions from the preview page @@ -25,7 +25,7 @@ def test_download_pdf_no_preview(self): super().setUp() # Given I launch a questionnaire without preview enabled - self.launchSurvey("test_checkbox") + self.launchSurveyV2(schema_name="test_checkbox") # When I try to download preview of questions self.get("/questionnaire/preview/download-pdf") @@ -37,7 +37,7 @@ def test_print_button(self): super().setUp() # Given I launch a questionnaire and open preview of questions - self.launchSurvey("test_introduction") + self.launchSurveyV2(schema_name="test_introduction") self.get("/questionnaire/preview/") # Then the print button is displayed correctly diff --git a/tests/integration/routes/test_view_submitted_response.py b/tests/integration/routes/test_view_submitted_response.py index 2e4f92e41d..4fa9dd0f47 100644 --- a/tests/integration/routes/test_view_submitted_response.py +++ b/tests/integration/routes/test_view_submitted_response.py @@ -6,7 +6,7 @@ class ViewSubmittedResponseBase(IntegrationTestCase): VIEW_RESPONSE_PAGE_URL = "/submitted/view-response" def _launch_and_complete_questionnaire(self): - self.launchSurvey("test_view_submitted_response") + self.launchSurveyV2(schema_name="test_view_submitted_response") self.post({"name-answer": "John Smith"}) self.post({"address-answer": "NP10 8XG"}) self.post() @@ -61,7 +61,7 @@ def test_enabled(self): def test_not_enabled(self): # Given I launch and complete a questionnaire that does not have view-submitted-response enabled - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") self.post() self.post() diff --git a/tests/integration/routes/test_view_submitted_response_pdf.py b/tests/integration/routes/test_view_submitted_response_pdf.py index 1a4ae0b0b3..fb0311b1dd 100644 --- a/tests/integration/routes/test_view_submitted_response_pdf.py +++ b/tests/integration/routes/test_view_submitted_response_pdf.py @@ -11,7 +11,7 @@ class TestViewSubmissionResponsePDF(ViewSubmittedResponseBase): def test_download_when_submitted_response_not_enabled(self): # Given I launch and complete a questionnaire that does not have view-submitted-response enabled - self.launchSurvey("test_confirmation_email") + self.launchSurveyV2(schema_name="test_confirmation_email") self.post() self.post() diff --git a/tests/integration/routing/test_answer_comparison.py b/tests/integration/routing/test_answer_comparison.py index cf4720c42b..ca35ad1a00 100644 --- a/tests/integration/routing/test_answer_comparison.py +++ b/tests/integration/routing/test_answer_comparison.py @@ -8,7 +8,7 @@ class TestAnswerComparisonsSkips(IntegrationTestCase): """ def test_skip_condition_answer_comparison(self): - self.launchSurvey("test_skip_condition_answer_comparison") + self.launchSurveyV2(schema_name="test_skip_condition_answer_comparison") self.post(action="start_questionnaire") @@ -42,7 +42,7 @@ class TestAnswerComparisonsRoutes(IntegrationTestCase): """ def test_routes_over_interstitial(self): - self.launchSurvey("test_routing_answer_comparison") + self.launchSurveyV2(schema_name="test_routing_answer_comparison") self.post(action="start_questionnaire") diff --git a/tests/integration/routing/test_routing_case_insensitive.py b/tests/integration/routing/test_routing_case_insensitive.py index 9b093a510a..a54cc2d178 100644 --- a/tests/integration/routing/test_routing_case_insensitive.py +++ b/tests/integration/routing/test_routing_case_insensitive.py @@ -7,7 +7,7 @@ class TestCaseInsensitiveRouting(IntegrationTestCase): """ def test_routing_equals_any_case_insensitive(self): - self.launchSurvey("test_routing_case_insensitive_text_field") + self.launchSurveyV2(schema_name="test_routing_case_insensitive_text_field") # Given I launch the case insensiitive routing rules schema self.post(action="start_questionnaire") @@ -19,7 +19,7 @@ def test_routing_equals_any_case_insensitive(self): self.assertInBody("You submitted India or Azerbaijan.") def test_routing_equals_case_insensitive(self): - self.launchSurvey("test_routing_case_insensitive_text_field") + self.launchSurveyV2(schema_name="test_routing_case_insensitive_text_field") # Given I launch the case insensiitive routing rules schema self.post(action="start_questionnaire") diff --git a/tests/integration/session/test_login.py b/tests/integration/session/test_login.py index 469790ed09..72a428c0d9 100644 --- a/tests/integration/session/test_login.py +++ b/tests/integration/session/test_login.py @@ -6,7 +6,7 @@ CIR_RETRIEVE_COLLECTION_INSTRUMENT_URL, get_schema_path_map, ) -from tests.integration.create_token import PAYLOAD +from tests.integration.create_token import PAYLOAD_V2_BUSINESS from tests.integration.integration_test_case import IntegrationTestCase SCHEMA_PATH_MAP = get_schema_path_map(include_test_schemas=True) @@ -33,17 +33,6 @@ def test_login_with_invalid_token_should_be_forbidden(self): # Then self.assertStatusForbidden() - def test_login_with_valid_token_should_redirect_to_survey(self): - # Given - token = self.token_generator.create_token("test_checkbox") - - # When - self.get(url=f"/session?token={token}") - - # Then - self.assertStatusOK() - self.assertInUrl("/questionnaire") - def test_login_with_valid_v2_business_token_should_redirect_to_survey(self): # Given token = self.token_generator.create_token_v2(schema_name="test_checkbox") @@ -70,7 +59,7 @@ def test_login_with_valid_v2_social_token_should_redirect_to_survey(self): def test_login_with_token_twice_is_unauthorised_when_same_jti_provided(self): # Given - token = self.token_generator.create_token("test_checkbox") + token = self.token_generator.create_token_v2("test_checkbox") self.get(url=f"/session?token={token}") # When @@ -87,16 +76,6 @@ def test_login_without_jti_in_token_is_unauthorised(self): # Then self.assertStatusForbidden() - def test_login_with_valid_token_no_schema_name(self): - # Given - token = self.token_generator.create_token("") - - # When - self.get(url=f"/session?token={token}") - - # Then - self.assertStatusForbidden() - def test_login_with_valid_v2_business_token_no_schema_name(self): # Given token = self.token_generator.create_token_v2(schema_name="") @@ -119,7 +98,7 @@ def test_login_with_valid_v2_social_token_no_schema_name(self): def test_http_head_request_to_login_returns_successfully_and_get_still_works(self): # Given - token = self.token_generator.create_token("test_checkbox") + token = self.token_generator.create_token_v2("test_checkbox") # When self.head("/session?token=" + token) @@ -131,7 +110,7 @@ def test_http_head_request_to_login_returns_successfully_and_get_still_works(sel def test_login_with_missing_mandatory_claims_should_be_forbidden(self): # Given - payload_vars = PAYLOAD.copy() + payload_vars = PAYLOAD_V2_BUSINESS.copy() payload_vars["iat"] = time.time() payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now @@ -143,14 +122,6 @@ def test_login_with_missing_mandatory_claims_should_be_forbidden(self): # Then self.assertStatusForbidden() - def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): - # flag_1 should be a boolean - token = self.token_generator.create_token("test_metadata_routing", flag_1=123) - - self.get(url=f"/session?token={token}") - - self.assertStatusForbidden() - def test_login_with_invalid_questionnaire_claims_should_be_forbidden_v2_get(self): # flag_1 should be a boolean token = self.token_generator.create_token_v2( @@ -172,9 +143,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): schema_url = "http://eq-survey-register.url/my-test-schema" # Given - token = self.token_generator.create_token_with_schema_url( - "test_textarea", schema_url - ) + token = self.token_generator.create_token_with_schema_url(schema_url) # When with HTTMock(self._schema_url_mock): @@ -187,9 +156,7 @@ def test_login_token_with_incorrect_schema_url_results_in_500(self): schema_url = "http://eq-survey-register.url/my-test-schema-not-found" # Given - token = self.token_generator.create_token_with_schema_url( - "test_textarea", schema_url - ) + token = self.token_generator.create_token_with_schema_url(schema_url) # When with HTTMock(self._schema_url_mock_500): @@ -235,7 +202,7 @@ def test_login_with_invalid_token_should_be_forbidden(self): def test_login_with_valid_token_should_redirect_to_survey(self): # Given - token = self.token_generator.create_token("test_checkbox") + token = self.token_generator.create_token_v2("test_checkbox") # When self.post(url=f"/session?token={token}") @@ -246,7 +213,7 @@ def test_login_with_valid_token_should_redirect_to_survey(self): def test_login_with_token_twice_is_unauthorised_when_same_jti_provided(self): # Given - token = self.token_generator.create_token("test_checkbox") + token = self.token_generator.create_token_v2("test_checkbox") self.post(url=f"/session?token={token}") # When @@ -265,7 +232,7 @@ def test_login_without_jti_in_token_is_unauthorised(self): def test_http_head_request_to_login_returns_successfully_and_post_still_works(self): # Given - token = self.token_generator.create_token("test_checkbox") + token = self.token_generator.create_token_v2("test_checkbox") # When self.head(f"/session?token={token}") @@ -277,7 +244,7 @@ def test_http_head_request_to_login_returns_successfully_and_post_still_works(se def test_login_with_missing_mandatory_claims_should_be_forbidden(self): # Given - payload_vars = PAYLOAD.copy() + payload_vars = PAYLOAD_V2_BUSINESS.copy() payload_vars["iat"] = time.time() payload_vars["exp"] = payload_vars["iat"] + float(3600) # one hour from now @@ -289,14 +256,6 @@ def test_login_with_missing_mandatory_claims_should_be_forbidden(self): # Then self.assertStatusForbidden() - def test_login_with_invalid_questionnaire_claims_should_be_forbidden(self): - # flag_1 should be a boolean - token = self.token_generator.create_token("test_metadata_routing", flag_1=123) - - self.post(url=f"/session?token={token}") - - self.assertStatusForbidden() - def test_login_with_invalid_questionnaire_claims_should_be_forbidden_v2_post(self): # flag_1 should be a boolean token = self.token_generator.create_token_v2( @@ -345,9 +304,7 @@ def test_login_token_with_schema_url_should_redirect_to_survey(self): schema_url = "http://eq-survey-register.url/my-test-schema" # Given - token = self.token_generator.create_token_with_schema_url( - "test_textarea", schema_url - ) + token = self.token_generator.create_token_with_schema_url(schema_url) # When with HTTMock(self._schema_url_mock): @@ -360,9 +317,7 @@ def test_login_token_with_incorrect_schema_url_results_in_404(self): schema_url = "http://eq-survey-register.url/my-test-schema-not-found" # Given - token = self.token_generator.create_token_with_schema_url( - "test_textarea", schema_url - ) + token = self.token_generator.create_token_with_schema_url(schema_url) # When with HTTMock(self._schema_url_mock_500): diff --git a/tests/integration/session/test_multiple_login.py b/tests/integration/session/test_multiple_login.py index 9a7dc603d2..e58504c88b 100644 --- a/tests/integration/session/test_multiple_login.py +++ b/tests/integration/session/test_multiple_login.py @@ -14,7 +14,7 @@ def setUp(self): self.cache = {} def launchSurvey(self, client, schema_name, **payload_kwargs): - token = self.token_generator.create_token(schema_name, **payload_kwargs) + token = self.token_generator.create_token_v2(schema_name, **payload_kwargs) self.get(client, "/session?token=" + token) # pylint: disable=arguments-renamed @@ -81,11 +81,11 @@ def test_multiple_users_same_survey(self): input_data = "foo bar" # user A inputs an answer - self.launchSurvey(self.client_a, "test_textfield") + self.launchSurvey(self.client_a, schema_name="test_textfield") self.post(self.client_a, {"name-answer": input_data}) # user B gets taken straight to summary as survey is complete - self.launchSurvey(self.client_b, "test_textfield") + self.launchSurvey(self.client_b, schema_name="test_textfield") last_url_b = self.cache[self.client_b]["last_url"] self.assertIn(SUBMIT_URL_PATH, last_url_b) @@ -110,7 +110,9 @@ def test_concurrent_users_same_survey_different_languages(self): """ # user A launches the test language questionnaire in English - self.launchSurvey(self.client_a, "test_language", language_code="en") + self.launchSurvey( + self.client_a, schema_name="test_language", language_code="en" + ) self.post(self.client_a) last_response_a = self.cache[self.client_a]["last_response"] self.assertIn("Please enter a name", last_response_a.get_data(True)) @@ -127,7 +129,9 @@ def test_concurrent_users_same_survey_different_languages(self): self.assertIn("Please enter a name", last_response_a.get_data(True)) # user B launches the same questionnaire but in Welsh - self.launchSurvey(self.client_b, "test_language", language_code="cy") + self.launchSurvey( + self.client_b, schema_name="test_language", language_code="cy" + ) self.post(self.client_b) last_response_b = self.cache[self.client_b]["last_response"] self.assertIn("Rhowch enw", last_response_b.get_data(True)) @@ -169,7 +173,9 @@ def test_multiple_logins_have_same_started_at(self): Ensure that started_at is retained between collections """ # User A starts a survey - self.launchSurvey(self.client_a, "test_introduction", roles=["dumper"]) + self.launchSurvey( + self.client_a, schema_name="test_introduction", roles=["dumper"] + ) # And starts the questionnaire self.post(self.client_a, action="start_questionnaire") @@ -177,7 +183,9 @@ def test_multiple_logins_have_same_started_at(self): a_submission = self.dumpSubmission(self.client_a)["submission"] # User B loads the survey - self.launchSurvey(self.client_b, "test_introduction", roles=["dumper"]) + self.launchSurvey( + self.client_b, schema_name="test_introduction", roles=["dumper"] + ) # And we dump their submission b_submission = self.dumpSubmission(self.client_b)["submission"] diff --git a/tests/integration/session/test_sign_out_and_exit.py b/tests/integration/session/test_sign_out_and_exit.py index ffb05b2767..cdf9e332c1 100644 --- a/tests/integration/session/test_sign_out_and_exit.py +++ b/tests/integration/session/test_sign_out_and_exit.py @@ -16,22 +16,22 @@ class TestSaveAndSignOut(IntegrationTestCase): def test_sign_out_button_link(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.assertEqual( "/sign-out?internal_redirect=True", self.getSignOutButton()["href"] ) def test_sign_out_url(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.saveAndSignOut() self.assertInRedirect(SIGNED_OUT_URL_PATH) def test_sign_out_button_text(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.assertEqual("Save and exit survey", self.getSignOutButton().text.strip()) def test_sign_out_button_displayed_pre_submission(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.assertIsNotNone(self.getSignOutButton()) def test_no_session_cookie_redirects_to_default_account_service_log_out_url(self): @@ -48,12 +48,14 @@ def test_no_session_cookie_signed_out_redirects_to_session_expiry(self): def test_redirects_to_account_service_log_out_url_using_base_url_from_claims(self): for schema in ["test_textfield", "test_hub_and_spoke"]: with self.subTest(schema=schema): - self.launchSurvey(schema, account_service_url=ACCOUNT_SERVICE_BASE_URL) + self.launchSurveyV2( + schema_name=schema, account_service_url=ACCOUNT_SERVICE_BASE_URL + ) self.signOut() self.assertInRedirect(ACCOUNT_SERVICE_LOG_OUT_URL) def test_head_request_doesnt_sign_out(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.head(SIGN_OUT_URL_PATH) self.assertStatusCode(302) self.get("/questionnaire/name-block") @@ -62,7 +64,7 @@ def test_head_request_doesnt_sign_out(self): class TestExitPostSubmissionTestCase(IntegrationTestCase): def _launch_and_submit_questionnaire(self, schema, **kwargs): - self.launchSurvey(schema, **kwargs) + self.launchSurveyV2(schema_name=schema, **kwargs) self.post() self.post() self.assertInUrl("/thank-you") @@ -82,7 +84,7 @@ def test_redirects_to_account_service_log_out_url_using_base_url_from_claims(sel class TestExitPostSubmissionWithHubDefaultTheme(IntegrationTestCase): def _launch_and_submit_questionnaire(self, schema, **kwargs): - self.launchSurvey(schema, **kwargs) + self.launchSurveyV2(schema_name=schema, **kwargs) self.post({"household-relationships-answer": "No"}) self.post() self.assertInUrl("/thank-you") diff --git a/tests/integration/session/test_timeout.py b/tests/integration/session/test_timeout.py index d175191810..ec13f3877a 100644 --- a/tests/integration/session/test_timeout.py +++ b/tests/integration/session/test_timeout.py @@ -15,12 +15,12 @@ def tearDown(self): super().tearDown() def test_timeout_continue_valid_session_returns_200(self): - self.launchSurvey("test_timeout") + self.launchSurveyV2(schema_name="test_timeout") self.get(self.last_url) self.assertStatusOK() def test_when_session_times_out_server_side_401_is_returned(self): - self.launchSurvey("test_timeout") + self.launchSurveyV2(schema_name="test_timeout") time.sleep(5) self.get(self.last_url) self.assertStatusUnauthorised() @@ -33,7 +33,7 @@ def test_alternate_401_page_is_displayed_when_no_cookie(self): self.assertEqualPageTitle("Page is not available - ONS Surveys") def test_schema_defined_timeout_cant_be_higher_than_server(self): - self.launchSurvey("test_timeout") + self.launchSurveyV2(schema_name="test_timeout") time.sleep(4) self.get(self.last_url) self.assertStatusUnauthorised() @@ -43,7 +43,7 @@ def test_schema_defined_timeout_cant_be_higher_than_server(self): self.assertEqualPageTitle("Page is not available - Timeout test") def test_submission_complete_timeout(self): - self.launchSurvey("test_timeout") + self.launchSurveyV2(schema_name="test_timeout") self.post() self.post() time.sleep(4) diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index b1a6b984a4..5ba3b54cea 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -15,7 +15,7 @@ def tearDown(self): settings.EQ_GOOGLE_TAG_ID = None def test_google_analytics_code_and_credentials_are_present(self): - self.launchSurvey("test_feedback", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_feedback", roles=["dumper"]) self.get("/dump/debug") actual = json_loads(self.getResponseData()) self._client.set_cookie( @@ -30,7 +30,7 @@ def test_google_analytics_code_and_credentials_are_present(self): self.assertInHead(settings.EQ_GOOGLE_TAG_ID) def test_google_analytics_data_layer_has_no_null_fields(self): - self.launchSurvey("test_textfield", roles=["dumper"]) + self.launchSurveyV2(schema_name="test_textfield", roles=["dumper"]) self.get("/dump/debug") actual = json_loads(self.getResponseData()) self._client.set_cookie( @@ -44,6 +44,6 @@ def test_google_analytics_data_layer_has_no_null_fields(self): ) def test_livereload_script_rendered(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.assertStatusOK() self.assertTrue("__bs_script__" in self.getResponseData()) diff --git a/tests/integration/test_application_variables_negative.py b/tests/integration/test_application_variables_negative.py index a87fa99d9b..8c83a6cebc 100644 --- a/tests/integration/test_application_variables_negative.py +++ b/tests/integration/test_application_variables_negative.py @@ -8,6 +8,6 @@ def setUp(self): super().setUp() def test_livereload_script_not_rendered(self): - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.assertStatusOK() self.assertFalse("__bs_script__" in self.getResponseData()) diff --git a/tests/integration/test_broken_submission.py b/tests/integration/test_broken_submission.py index 7cf23b00e0..eb50e5d0ff 100644 --- a/tests/integration/test_broken_submission.py +++ b/tests/integration/test_broken_submission.py @@ -12,7 +12,7 @@ def setUp(self): self.instance.send_message.return_value = False super().setUp() - self.launchSurvey("test_percentage") + self.launchSurveyV2(schema_name="test_percentage") def tearDown(self): self.patcher.stop() diff --git a/tests/integration/test_flush_data.py b/tests/integration/test_flush_data.py index 1069acff31..7bbd9a16af 100644 --- a/tests/integration/test_flush_data.py +++ b/tests/integration/test_flush_data.py @@ -26,7 +26,7 @@ def setUp(self): self.encrypt_instance = mock_encrypter_class super().setUp() - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") form_data = {"name-answer": "Joe Bloggs"} self.post(form_data) @@ -137,49 +137,12 @@ def get_payload(): "exp": time.time() + 1000, "response_id": "1234567890123456", "roles": ["flusher"], + "version": "v2", + "survey_metadata": {"data": {}}, } @patch("app.routes.flush.convert_answers_v2") - @patch("app.routes.flush.convert_answers") - def test_flush_data_successful_v1( - self, mock_convert_answers, mock_convert_answers_v2 - ): - mock_convert_answer_payload = { - "case_id": "7e0fd167-36af-4506-806d-421e5ba3544b", - "tx_id": "5432e910-c6ef-418a-abcc-a8de8c23b0f9", - "type": "uk.gov.ons.edc.eq:surveyresponse", - "version": "0.0.3", - "origin": "uk.gov.ons.edc.eq", - "survey_id": "001", - "flushed": True, - "submitted_at": "2023-02-07T11:41:12.126783+00:00", - "collection": { - "exercise_sid": "f9fb5e81-9820-44cc-a2c3-16bf382b4d8d", - "schema_name": "test_textfield", - "period": "201605", - }, - "metadata": {"user_id": "UNKNOWN", "ru_ref": "12345678901A"}, - "launch_language_code": "en", - "data": { - "answers": [{"answer_id": "name-answer", "value": "sdfsdaf"}], - "lists": [], - }, - "started_at": "2023-02-07T11:40:46.845149+00:00", - } - mock_convert_answers.return_value = mock_convert_answer_payload - self.post( - url="/flush?token=" - + self.token_generator.generate_token(self.get_payload()) - ) - self.assertStatusOK() - mock_convert_answers.assert_called_once() - mock_convert_answers_v2.assert_not_called() - - @patch("app.routes.flush.convert_answers_v2") - @patch("app.routes.flush.convert_answers") - def test_flush_data_successful_v2( - self, mock_convert_answers, mock_convert_answers_v2 - ): + def test_flush_data_successful_v2(self, mock_convert_answers_v2): mock_convert_answer_payload = { "case_id": "19300487-87e7-42df-9330-718efb08e660", "tx_id": "5d8b97f7-c8bd-42e1-88c9-e7721388463b", @@ -216,15 +179,14 @@ def test_flush_data_successful_v2( ) self.assertStatusOK() mock_convert_answers_v2.assert_called_once() - mock_convert_answers.assert_not_called() def test_flush_logs_output(self): with self.assertLogs() as logs: self.post( - url=f"/flush?token={self.token_generator.create_token(schema_name='test_textfield', payload=self.get_payload())}" + url=f"/flush?token={self.token_generator.create_token_v2(schema_name='test_textfield', **self.get_payload())}" ) - flush_log = logs.output[5] + flush_log = logs.output[6] self.assertIn("successfully flushed answers", flush_log) self.assertIn("tx_id", flush_log) @@ -234,23 +196,20 @@ def test_flush_logs_output(self): def test_flush_logs_output_schema_url(self): schema_url = "http://eq-survey-register.url/my-test-schema" - token = self.token_generator.create_token_with_schema_url( - "test_textfield", schema_url - ) + token = self.token_generator.create_token_with_schema_url(schema_url=schema_url) with HTTMock(self.schema_url_mock): self.get(url=f"/session?token={token}") self.assertStatusOK() with self.assertLogs() as logs: self.post( - url=f"/flush?token={self.token_generator.create_token_with_schema_url('test_textfield', schema_url, payload=self.get_payload())}" + url=f"/flush?token={self.token_generator.create_token_with_schema_url(schema_url=schema_url, **self.get_payload())}" ) - flush_log = logs.output[5] + flush_log = logs.output[6] self.assertIn("successfully flushed answers", flush_log) self.assertIn("tx_id", flush_log) self.assertIn("ce_id", flush_log) - self.assertIn("schema_name", flush_log) self.assertIn("schema_url", flush_log) def test_flush_logs_output_cir_instrument_id(self): diff --git a/tests/integration/test_header_links.py b/tests/integration/test_header_links.py index 72d81f5053..744b7540f4 100644 --- a/tests/integration/test_header_links.py +++ b/tests/integration/test_header_links.py @@ -66,7 +66,7 @@ def assert_help_link_does_not_exist(self): class TestHeaderLinksPreSubmission(TestHeaderLinks): def test_links_in_header_when_valid_session(self): # Given - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") # When self.assertStatusOK() @@ -78,7 +78,7 @@ def test_links_in_header_when_valid_session(self): def test_links_in_header_when_no_session_but_cookie_exists(self): # Given - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.assertInUrl("questionnaire/did-you-know/") self.saveAndSignOut() @@ -140,7 +140,7 @@ def test_links_not_in_header_when_valid_session_theme_social(self): class TestHeaderLinksPostSubmission(TestHeaderLinks): def test_links_in_header_when_valid_session(self): # Given - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.post() self.post() @@ -184,7 +184,7 @@ def test_links_not_in_header_when_valid_session_theme_social_thank_you_page(self class TestHeaderLinksPostSignOut(TestHeaderLinks): def test_links_not_in_header_after_sign_out(self): # Given - self.launchSurvey("test_thank_you") + self.launchSurveyV2(schema_name="test_thank_you") self.assert_my_account_link_exist() self.assert_sign_out_link_exist() self.assert_help_link_exist() @@ -202,7 +202,7 @@ def test_links_not_in_header_after_sign_out(self): def test_links_not_in_header_after_sign_out_theme_social(self): # Given - self.launchSurvey("test_theme_social") + self.launchSurveyV2(schema_name="test_theme_social") self.assert_my_account_link_does_not_exist() self.assert_sign_out_link_does_not_exist() self.assert_help_link_does_not_exist() diff --git a/tests/integration/test_no_questionnaire_state.py b/tests/integration/test_no_questionnaire_state.py index e5f6362a7f..919a939733 100644 --- a/tests/integration/test_no_questionnaire_state.py +++ b/tests/integration/test_no_questionnaire_state.py @@ -6,7 +6,7 @@ class TestNoQuestionnaireState(IntegrationTestCase): def test_questionnaire_route_before_questionnaire_submitted(self): # Given - self.launchSurvey("test_view_submitted_response") + self.launchSurveyV2(schema_name="test_view_submitted_response") # When with patch("app.routes.questionnaire.get_metadata", return_value=None): @@ -17,7 +17,7 @@ def test_questionnaire_route_before_questionnaire_submitted(self): def test_post_submission_route_before_questionnaire_submitted(self): # Given - self.launchSurvey("test_view_submitted_response") + self.launchSurveyV2(schema_name="test_view_submitted_response") # When with patch("app.routes.questionnaire.get_metadata", return_value=None): @@ -28,7 +28,7 @@ def test_post_submission_route_before_questionnaire_submitted(self): def test_post_submission_route_after_questionnaire_submitted(self): # Given - self.launchSurvey("test_view_submitted_response") + self.launchSurveyV2(schema_name="test_view_submitted_response") self.post() self.post() self.post() diff --git a/tests/integration/test_previously_submitted.py b/tests/integration/test_previously_submitted.py index b3c9080aaf..cc5f50bbba 100644 --- a/tests/integration/test_previously_submitted.py +++ b/tests/integration/test_previously_submitted.py @@ -5,7 +5,7 @@ class TestPreviouslySubmitted(IntegrationTestCase): def test_previously_submitted(self): # Given I complete the questionnaire and submit - self.launchSurvey("test_textfield") + self.launchSurveyV2(schema_name="test_textfield") self.post() self.post() self.assertInUrl(THANK_YOU_URL_PATH) diff --git a/tests/integration/test_textarea.py b/tests/integration/test_textarea.py index 8f580c14c7..ddff8af27e 100644 --- a/tests/integration/test_textarea.py +++ b/tests/integration/test_textarea.py @@ -8,7 +8,7 @@ class TestTextArea(IntegrationTestCase): def test_empty_submission(self): - self.launchSurvey("test_textarea") + self.launchSurveyV2(schema_name="test_textarea") self.post() self.assertInBody("No answer provided") @@ -17,7 +17,7 @@ def test_empty_submission(self): self.assertInUrl(THANK_YOU_URL_PATH) def test_too_many_characters(self): - self.launchSurvey("test_textarea") + self.launchSurveyV2(schema_name="test_textarea") self.post({"answer": "This is longer than twenty characters"}) self.assertInBody( @@ -25,7 +25,7 @@ def test_too_many_characters(self): ) def test_acceptable_submission(self): - self.launchSurvey("test_textarea") + self.launchSurveyV2(schema_name="test_textarea") self.post({"answer": "Less than 20 chars"}) self.assertInBody("Less than 20 chars") @@ -34,7 +34,7 @@ def test_acceptable_submission(self): self.assertInUrl(THANK_YOU_URL_PATH) def test_big_list_of_naughty_strings(self): - self.launchSurvey("test_big_list_naughty_strings") + self.launchSurveyV2(schema_name="test_big_list_naughty_strings") answers = {} for counter, value in enumerate(NAUGHTY_STRINGS): From 26a6086355752dc3932e0417a977dfcfdac21309 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:21:34 +0100 Subject: [PATCH 403/567] Fix version obsolete warning (#1358) --- docker-compose-dev-linux.yml | 2 -- docker-compose-dev-mac.yml | 2 -- docker-compose-schema-validator.yml | 3 --- docker-compose.yml | 2 -- 4 files changed, 9 deletions(-) diff --git a/docker-compose-dev-linux.yml b/docker-compose-dev-linux.yml index 5e40871664..eb96380f47 100644 --- a/docker-compose-dev-linux.yml +++ b/docker-compose-dev-linux.yml @@ -1,5 +1,3 @@ -version: "3" - services: datastore: image: knarz/datastore-emulator diff --git a/docker-compose-dev-mac.yml b/docker-compose-dev-mac.yml index ae4d85612d..9312153355 100644 --- a/docker-compose-dev-mac.yml +++ b/docker-compose-dev-mac.yml @@ -1,5 +1,3 @@ -version: "3" - services: datastore: image: knarz/datastore-emulator diff --git a/docker-compose-schema-validator.yml b/docker-compose-schema-validator.yml index 7055c79a43..64f9ed7d22 100644 --- a/docker-compose-schema-validator.yml +++ b/docker-compose-schema-validator.yml @@ -1,6 +1,3 @@ ---- -version: '3' - services: ajv-validator: image: onsdigital/eq-questionnaire-validator:${TAG}-ajv diff --git a/docker-compose.yml b/docker-compose.yml index 9cc816b7ce..fd9839314b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: datastore: From 07b6d7b53d6aade24c26d30fc32ba0ab64bb3317 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 09:56:59 +0100 Subject: [PATCH 404/567] Bump the pip group across 1 directory with 2 updates (#1356) Bumps the pip group with 2 updates in the / directory: [gunicorn](https://github.com/benoitc/gunicorn) and [idna](https://github.com/kjd/idna). Updates `gunicorn` from 21.2.0 to 22.0.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/21.2.0...22.0.0) Updates `idna` from 3.6 to 3.7 - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7) --- updated-dependencies: - dependency-name: gunicorn dependency-type: direct:production dependency-group: pip - dependency-name: idna dependency-type: indirect dependency-group: pip ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: liamtoozer --- poetry.lock | 22 ++++++++++++---------- pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3dcb66098f..31014fae97 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "astroid" @@ -1535,22 +1535,23 @@ protobuf = ">=4.21.6" [[package]] name = "gunicorn" -version = "21.2.0" +version = "22.0.0" description = "WSGI HTTP Server for UNIX" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" files = [ - {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"}, - {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"}, + {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, + {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, ] [package.dependencies] packaging = "*" [package.extras] -eventlet = ["eventlet (>=0.24.1)"] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] [[package]] @@ -1607,13 +1608,13 @@ tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -2495,6 +2496,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -3297,4 +3299,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "a3e91d57bee2c49c0f9793237502b06af68f2cb33968e81e226d1bf4252210a4" +content-hash = "ea96ed50e96557d491f7678f36a890ba5533d4316bb66c1e74c18cc69e5ab237" diff --git a/pyproject.toml b/pyproject.toml index 623ea5c609..b4ec0d5c4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ flask-login = "^0.6.3" flask-wtf = "^1.2.1" google-cloud-datastore = "^2.19.0" grpcio = "^1.62.1" -gunicorn = "^21.2.0" +gunicorn = "^22.0.0" pika = "^1.3.2" pyyaml = "^6.0.1" requests = "^2.31.0" From 5dbd1a5bf7ee407baa32352463cb4d46b73fae53 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:06:16 +0100 Subject: [PATCH 405/567] Format html templates using djlint (#1350) --- .github/workflows/pull_request.yml | 4 +- Makefile | 10 +- app/translations/messages.pot | 471 +++++++++--------- poetry.lock | 222 ++++++++- pyproject.toml | 10 + templates/answersummary.html | 2 +- templates/calculatedsummary.html | 6 +- templates/confirm-email.html | 68 +-- templates/confirmation-email-sent.html | 74 +-- templates/confirmation-email.html | 20 +- templates/confirmationquestion.html | 2 +- templates/errors/401.html | 44 +- templates/errors/403.html | 23 +- templates/errors/404.html | 31 +- templates/errors/500.html | 43 +- templates/errors/_base.html | 2 +- templates/errors/error.html | 14 +- templates/errors/previously-submitted.html | 11 +- templates/errors/submission-failed.html | 31 +- templates/feedback-sent.html | 32 +- templates/feedback.html | 90 ++-- templates/grandcalculatedsummary.html | 6 +- templates/hub.html | 37 +- .../confirmation-post.html | 28 +- .../confirmation-text-message.html | 49 +- .../individual_response/interstitial.html | 21 +- templates/individual_response/question.html | 8 +- templates/interstitial.html | 15 +- templates/introduction.html | 28 +- templates/layouts/_base.html | 256 +++++----- templates/layouts/_calculatedsummary.html | 44 +- templates/layouts/_questionnaire.html | 28 +- templates/layouts/_submit.html | 87 ++-- templates/layouts/configs/_header.html | 12 +- templates/list-action.html | 19 +- templates/listaddquestion.html | 2 +- templates/listcollector.html | 2 +- templates/listcollectorcontent.html | 28 +- templates/listcollectordrivingquestion.html | 2 +- templates/listeditquestion.html | 2 +- templates/listremovequestion.html | 4 +- templates/listrepeatingquestion.html | 2 +- templates/macros/helpers.html | 20 +- templates/multiple_survey.html | 34 +- templates/partials/answer-guidance.html | 42 +- templates/partials/answer.html | 53 +- templates/partials/answers/address.html | 79 ++- templates/partials/answers/checkbox.html | 18 +- templates/partials/answers/currency.html | 51 +- templates/partials/answers/date.html | 90 ++-- templates/partials/answers/dropdown.html | 27 +- templates/partials/answers/duration.html | 53 +- templates/partials/answers/mobilenumber.html | 34 +- templates/partials/answers/monthyeardate.html | 2 +- templates/partials/answers/number.html | 41 +- templates/partials/answers/percentage.html | 53 +- templates/partials/answers/radio.html | 29 +- templates/partials/answers/relationship.html | 20 +- templates/partials/answers/textarea.html | 46 +- templates/partials/answers/textfield.html | 84 ++-- templates/partials/answers/unit.html | 53 +- templates/partials/answers/yeardate.html | 2 +- templates/partials/block.html | 13 +- .../partials/confirmation-email-form.html | 77 +-- templates/partials/content-block.html | 8 +- templates/partials/contents.html | 23 +- templates/partials/definition.html | 38 +- templates/partials/error-panel.html | 53 +- .../partials/feedback-call-to-action.html | 4 +- templates/partials/guidance.html | 20 +- .../individual-response-guidance.html | 36 +- templates/partials/introduction/basic.html | 6 +- templates/partials/introduction/preview.html | 76 ++- .../partials/introduction/start-survey.html | 28 +- .../last_viewed_question_guidance.html | 21 +- templates/partials/preview-question.html | 115 ++--- templates/partials/question.html | 237 +++++---- .../partials/summary/collapsible-summary.html | 111 +++-- templates/partials/summary/list-summary.html | 68 +-- templates/partials/summary/summary.html | 98 ++-- templates/preview.html | 202 ++++---- .../primarypersonlistaddoreditquestion.html | 2 +- templates/primarypersonlistcollector.html | 2 +- templates/question.html | 17 +- templates/relationshipcollector.html | 2 +- templates/sectionsummary.html | 96 ++-- templates/signed-out.html | 43 +- templates/submit-with-summary.html | 8 +- templates/submit.html | 2 +- templates/thank-you.html | 181 +++---- templates/unrelatedquestion.html | 2 +- templates/view-submitted-response.html | 128 ++--- .../test_view_submitted_response_pdf.py | 2 +- 93 files changed, 2383 insertions(+), 2057 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ff779fd3fc..16ddc61a73 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -76,6 +76,8 @@ jobs: run: ./scripts/lint_functional_test_specs.sh - name: Javascript linting run: make lint-js + - name: HTML linting + run: make lint-html test-unit: needs: python-dependencies runs-on: ubuntu-22.04 @@ -109,7 +111,7 @@ jobs: - name: Compile translations run: make translate - name: Link env vars - run: ln -sf .development.env .env + run: ln -sf .development.env .env - name: Running unit tests run: make test-unit validate-schemas: diff --git a/Makefile b/Makefile index a6cbbdb1fb..7b92690e02 100644 --- a/Makefile +++ b/Makefile @@ -20,14 +20,20 @@ build: load-design-system-templates load-schemas translate generate-pages: npm run generate_pages -lint: lint-python lint-js +lint: lint-python lint-js lint-html + +lint-html: + poetry run djlint ./templates --profile=jinja lint-python: poetry run ./scripts/run_lint_python.sh lint-test-python: lint-python test-unit -format: format-python format-js +format: format-python format-js format-html + +format-html: + poetry run djlint ./templates --reformat --profile=jinja format-python: poetry run isort . diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 9fd7a00894..3c3e1b0dd5 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-03-13 09:13+0000\n" +"POT-Creation-Date: 2024-04-24 15:36+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -43,7 +43,7 @@ msgstr "" #: app/jinja_filters.py:732 #: templates/partials/summary/collapsible-summary.html:27 -#: templates/partials/summary/summary.html:25 +#: templates/partials/summary/summary.html:24 msgid "No answer provided" msgstr "" @@ -73,7 +73,7 @@ msgstr "" msgid "Enter a date" msgstr "" -#: app/forms/error_messages.py:23 templates/partials/answers/address.html:56 +#: app/forms/error_messages.py:23 templates/partials/answers/address.html:55 msgid "Enter an address" msgstr "" @@ -292,8 +292,8 @@ msgid "You can try to send the email again." msgstr "" #: app/routes/errors.py:286 templates/errors/403.html:3 -#: templates/errors/403.html:6 templates/errors/submission-failed.html:5 -#: templates/errors/submission-failed.html:8 +#: templates/errors/403.html:9 templates/errors/submission-failed.html:4 +#: templates/errors/submission-failed.html:11 msgid "Sorry, there is a problem" msgstr "" @@ -414,12 +414,12 @@ msgstr "" msgid "Choose another section to complete" msgstr "" -#: app/views/contexts/hub_context.py:65 templates/confirm-email.html:25 -#: templates/individual_response/confirmation-post.html:21 -#: templates/individual_response/confirmation-text-message.html:29 -#: templates/individual_response/question.html:5 templates/interstitial.html:8 -#: templates/listcollectorcontent.html:6 templates/sectionsummary.html:10 -#: templates/sectionsummary.html:49 +#: app/views/contexts/hub_context.py:65 templates/confirm-email.html:28 +#: templates/individual_response/confirmation-post.html:26 +#: templates/individual_response/confirmation-text-message.html:39 +#: templates/individual_response/question.html:5 templates/interstitial.html:7 +#: templates/listcollectorcontent.html:6 templates/sectionsummary.html:11 +#: templates/sectionsummary.html:50 msgid "Continue" msgstr "" @@ -698,7 +698,7 @@ msgstr "" msgid "View Submitted Response" msgstr "" -#: templates/confirm-email.html:12 templates/partials/answers/address.html:55 +#: templates/confirm-email.html:14 templates/partials/answers/address.html:54 #: templates/question.html:10 #, python-format msgid "There is a problem with your answer" @@ -706,32 +706,33 @@ msgid_plural "There are %(num)s problems with your answer" msgstr[0] "" msgstr[1] "" -#: templates/confirmation-email-sent.html:3 +#: templates/confirmation-email-sent.html:5 msgid "Confirmation email sent" msgstr "" -#: templates/confirmation-email-sent.html:16 +#: templates/confirmation-email-sent.html:21 msgid "A confirmation email has been sent to {email}" msgstr "" -#: templates/confirmation-email-sent.html:25 +#: templates/confirmation-email-sent.html:32 msgid "" "The email will be sent from " "census.2021@notifications.service.gov.uk" msgstr "" -#: templates/confirmation-email-sent.html:29 -msgid "Didn't receive an email?" +#: templates/confirmation-email-sent.html:38 +msgid "Didn’t receive an email?" msgstr "" -#: templates/confirmation-email-sent.html:30 +#: templates/confirmation-email-sent.html:41 msgid "" -"It can take a few minutes for the email to arrive. If it doesn't arrive, " -"check your junk mail, or you can send another confirmation email." +"It can take a few minutes for the email to arrive. If it doesn’t arrive, " +"check your junk mail, or you can send " +"another confirmation email." msgstr "" -#: templates/confirmation-email.html:8 +#: templates/confirmation-email.html:9 msgid "There is a problem with this page" msgstr "" @@ -739,37 +740,37 @@ msgstr "" msgid "Send a confirmation email" msgstr "" -#: templates/feedback-sent.html:6 +#: templates/feedback-sent.html:7 msgid "Feedback sent" msgstr "" -#: templates/feedback-sent.html:17 +#: templates/feedback-sent.html:21 msgid "Thank you for your feedback" msgstr "" -#: templates/feedback-sent.html:18 +#: templates/feedback-sent.html:23 msgid "" "Your comments will help us make improvements to our surveys. We are not " "able to reply to comments, but we appreciate your feedback" msgstr "" -#: templates/feedback-sent.html:24 +#: templates/feedback-sent.html:29 msgid "Done" msgstr "" -#: templates/feedback.html:14 templates/preview.html:15 -#: templates/view-submitted-response.html:16 +#: templates/feedback.html:16 templates/preview.html:14 +#: templates/view-submitted-response.html:15 msgid "Back" msgstr "" -#: templates/feedback.html:28 +#: templates/feedback.html:30 #, python-format msgid "There is a problem with your feedback" msgid_plural "There are %(num)s problems with your feedback" msgstr[0] "" msgstr[1] "" -#: templates/feedback.html:39 +#: templates/feedback.html:40 msgid "Send feedback" msgstr "" @@ -777,43 +778,43 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:23 templates/partials/question.html:50 +#: templates/interstitial.html:22 templates/partials/question.html:46 msgid "If you can’t answer questions for this person" msgstr "" -#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:26 +#: templates/interstitial.html:31 templates/layouts/_questionnaire.html:29 msgid "Save and continue" msgstr "" -#: templates/introduction.html:10 +#: templates/introduction.html:8 msgid "Introduction" msgstr "" -#: templates/introduction.html:24 +#: templates/introduction.html:20 msgid "View the questions you will be asked in this survey" msgstr "" -#: templates/introduction.html:29 +#: templates/introduction.html:24 msgid "Your response is legally required" msgstr "" -#: templates/list-action.html:9 +#: templates/list-action.html:10 msgid "Cancel and return to the previous page" msgstr "" -#: templates/multiple_survey.html:4 templates/multiple_survey.html:7 +#: templates/multiple_survey.html:5 templates/multiple_survey.html:8 msgid "Information" msgstr "" -#: templates/multiple_survey.html:16 +#: templates/multiple_survey.html:20 msgid "Unfortunately you can only complete one survey at a time" msgstr "" -#: templates/multiple_survey.html:17 +#: templates/multiple_survey.html:22 msgid "Close this window to continue with your current survey" msgstr "" -#: templates/preview.html:36 +#: templates/preview.html:33 msgid "Preview of the questions in this survey" msgstr "" @@ -821,28 +822,28 @@ msgstr "" msgid "To answer these questions you need to start survey" msgstr "" -#: templates/preview.html:43 +#: templates/preview.html:46 msgid "" "You may not have to answer all of these questions. The questions you see " "will depend on the answers you provide." msgstr "" -#: templates/preview.html:48 +#: templates/preview.html:52 msgid "Print questions" msgstr "" -#: templates/preview.html:61 +#: templates/preview.html:65 msgid "Save questions as PDF" msgstr "" -#: templates/partials/introduction/preview.html:37 -#: templates/partials/summary/collapsible-summary.html:50 +#: templates/partials/introduction/preview.html:32 +#: templates/partials/summary/collapsible-summary.html:51 #: templates/preview.html:94 msgid "Show all" msgstr "" -#: templates/partials/introduction/preview.html:38 -#: templates/partials/summary/collapsible-summary.html:51 +#: templates/partials/introduction/preview.html:33 +#: templates/partials/summary/collapsible-summary.html:52 #: templates/preview.html:95 msgid "Hide all" msgstr "" @@ -855,91 +856,91 @@ msgstr "" msgid "

      Your progress has been saved

      " msgstr "" -#: templates/signed-out.html:21 +#: templates/signed-out.html:22 msgid "" "To find further information or resume the survey, return " "to My Account." msgstr "" -#: templates/signed-out.html:23 +#: templates/signed-out.html:26 msgid "To resume the survey, re-enter your access code." msgstr "" -#: templates/thank-you.html:6 +#: templates/thank-you.html:7 msgid "We’ve received your answers" msgstr "" -#: templates/thank-you.html:15 +#: templates/thank-you.html:17 msgid "Back to surveys" msgstr "" -#: templates/thank-you.html:33 +#: templates/thank-you.html:40 msgid "Thank you for completing the {survey_title}" msgstr "" -#: templates/thank-you.html:45 +#: templates/thank-you.html:52 msgid "" "Your response will help inform decision-makers how best to support the UK" " population and economy." msgstr "" -#: templates/thank-you.html:46 +#: templates/thank-you.html:55 msgid "Learn more about how we use this data" msgstr "" -#: templates/thank-you.html:49 templates/view-submitted-response.html:71 +#: templates/thank-you.html:58 templates/view-submitted-response.html:72 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" -#: templates/thank-you.html:61 +#: templates/thank-you.html:72 msgid "For security, your answers will only be available to view for another " msgstr "" -#: templates/thank-you.html:62 +#: templates/thank-you.html:73 msgid "Get a copy of your answers" msgstr "" -#: templates/thank-you.html:64 +#: templates/thank-you.html:75 msgid "" -"You can save or print " -"your answers for your records." +"You can save or " +"print your answers for your records." msgstr "" -#: templates/layouts/_base.html:147 templates/thank-you.html:69 +#: templates/layouts/_base.html:146 templates/thank-you.html:81 msgid "minute" msgstr "" -#: templates/layouts/_base.html:148 templates/thank-you.html:70 +#: templates/layouts/_base.html:147 templates/thank-you.html:82 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:149 templates/thank-you.html:71 +#: templates/layouts/_base.html:148 templates/thank-you.html:83 msgid "second" msgstr "" -#: templates/layouts/_base.html:150 templates/thank-you.html:72 +#: templates/layouts/_base.html:149 templates/thank-you.html:84 msgid "seconds" msgstr "" -#: templates/thank-you.html:74 +#: templates/thank-you.html:86 msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" -#: templates/thank-you.html:83 +#: templates/thank-you.html:96 msgid "Get confirmation email" msgstr "" -#: templates/thank-you.html:84 +#: templates/thank-you.html:98 msgid "" "If you would like to be sent confirmation that you have completed your " "survey, enter your email address" msgstr "" -#: templates/view-submitted-response.html:35 +#: templates/view-submitted-response.html:33 msgid "Print answers" msgstr "" -#: templates/view-submitted-response.html:48 +#: templates/view-submitted-response.html:46 msgid "Save answers as PDF" msgstr "" @@ -947,316 +948,316 @@ msgstr "" msgid "Page is not available" msgstr "" -#: templates/errors/401.html:6 -msgid "Sorry, you need to sign in again" -msgstr "" - -#: templates/errors/401.html:7 -msgid "This is because you have either:" +#: templates/errors/401.html:4 +msgid "You will need to sign back in to access your account" msgstr "" -#: templates/errors/401.html:9 +#: templates/errors/401.html:5 msgid "" -"been inactive for 45 minutes and your session has timed out to protect " -"your information" -msgstr "" - -#: templates/errors/401.html:10 -msgid "followed a link to a page you are not signed in to" +"To access this page you need to re-enter your access " +"code." msgstr "" -#: templates/errors/401.html:11 -msgid "followed a link to a survey that has already been submitted" +#: templates/errors/401.html:6 +msgid "" +"If you are completing a business survey, you need to sign back in to your account." msgstr "" -#: templates/errors/401.html:14 -msgid "You will need to sign back in to access your account" +#: templates/errors/401.html:7 +msgid "" +"If you started your survey using an access code, you need to re-enter your code." msgstr "" -#: templates/errors/401.html:17 -msgid "" -"To access this page you need to re-enter your access " -"code." +#: templates/errors/401.html:10 +msgid "Sorry, you need to sign in again" msgstr "" -#: templates/errors/401.html:20 templates/errors/403.html:12 -#: templates/errors/404.html:13 templates/errors/500.html:16 -#: templates/errors/submission-failed.html:14 -msgid "Business surveys" +#: templates/errors/401.html:11 +msgid "This is because you have either:" msgstr "" -#: templates/errors/401.html:21 +#: templates/errors/401.html:13 msgid "" -"If you are completing a business survey, you need to sign back in to your account." +"been inactive for 45 minutes and your session has timed out to protect " +"your information" msgstr "" -#: templates/errors/401.html:22 templates/errors/403.html:14 -#: templates/errors/404.html:15 templates/errors/500.html:19 -#: templates/errors/submission-failed.html:16 -msgid "All other surveys" +#: templates/errors/401.html:14 +msgid "followed a link to a page you are not signed in to" msgstr "" -#: templates/errors/401.html:23 -msgid "" -"If you started your survey using an access code, you need to re-enter your code." +#: templates/errors/401.html:15 +msgid "followed a link to a survey that has already been submitted" msgstr "" -#: templates/errors/403.html:7 -msgid "You may need to update your browser to a newer version." +#: templates/errors/401.html:22 templates/errors/403.html:15 +#: templates/errors/404.html:16 templates/errors/500.html:21 +#: templates/errors/submission-failed.html:17 +msgid "Business surveys" msgstr "" -#: templates/errors/403.html:8 -msgid "If the problem still occurs, try using a different browser or device." +#: templates/errors/401.html:24 templates/errors/403.html:17 +#: templates/errors/404.html:18 templates/errors/500.html:24 +#: templates/errors/submission-failed.html:19 +msgid "All other surveys" msgstr "" -#: templates/errors/403.html:10 +#: templates/errors/403.html:4 msgid "For further help, please contact us." msgstr "" -#: templates/errors/403.html:13 +#: templates/errors/403.html:5 msgid "" "If you are completing a business survey and you need further help, please" " contact us." msgstr "" -#: templates/errors/403.html:15 +#: templates/errors/403.html:6 msgid "" "If you started your survey using an access code and you need further " "help, please contact us." msgstr "" -#: templates/errors/404.html:3 templates/errors/404.html:6 -msgid "Page not found" +#: templates/errors/403.html:10 +msgid "You may need to update your browser to a newer version." msgstr "" -#: templates/errors/404.html:7 -msgid "If you entered a web address, check it is correct." +#: templates/errors/403.html:11 +msgid "If the problem still occurs, try using a different browser or device." msgstr "" -#: templates/errors/404.html:8 -msgid "If you pasted the web address, check you copied the whole address." +#: templates/errors/404.html:3 templates/errors/404.html:9 +msgid "Page not found" msgstr "" -#: templates/errors/404.html:10 +#: templates/errors/404.html:4 msgid "" "If the web address is correct or you selected a link or button, contact us for more help." msgstr "" -#: templates/errors/404.html:12 -msgid "" -"If the web address is correct or you selected a link or button, please " -"see the following help links." -msgstr "" - -#: templates/errors/404.html:14 templates/errors/submission-failed.html:15 +#: templates/errors/404.html:5 templates/errors/submission-failed.html:7 msgid "" "If you are completing a business survey, please contact " "us." msgstr "" -#: templates/errors/404.html:16 templates/errors/submission-failed.html:17 +#: templates/errors/404.html:6 templates/errors/submission-failed.html:8 msgid "" "If you started your survey using an access code, please contact us." msgstr "" -#: templates/errors/500.html:3 -msgid "An error has occurred" +#: templates/errors/404.html:10 +msgid "If you entered a web address, check it is correct." msgstr "" -#: templates/errors/500.html:6 -msgid "Sorry, there is a problem with this service" +#: templates/errors/404.html:11 +msgid "If you pasted the web address, check you copied the whole address." msgstr "" -#: templates/errors/500.html:7 -msgid "Try again later." +#: templates/errors/404.html:15 +msgid "" +"If the web address is correct or you selected a link or button, please " +"see the following help links." msgstr "" -#: templates/errors/500.html:8 -msgid "If you have started a survey, your answers have been saved." +#: templates/errors/500.html:3 +msgid "An error has occurred" msgstr "" -#: templates/errors/500.html:10 templates/errors/500.html:17 +#: templates/errors/500.html:4 msgid "" "If you have attempted to submit your survey, you should check that this " "was successful. To do this, sign in to your business " "survey account." msgstr "" -#: templates/errors/500.html:11 templates/errors/500.html:14 +#: templates/errors/500.html:5 msgid "If you need more help, contact us." msgstr "" -#: templates/errors/500.html:13 templates/errors/500.html:20 +#: templates/errors/500.html:6 msgid "" "If you have attempted to submit your survey, you should check that this " "was successful. To do this, re-enter your code." msgstr "" -#: templates/errors/500.html:18 +#: templates/errors/500.html:7 msgid "" "If you need more help, contact us about business " "surveys." msgstr "" -#: templates/errors/500.html:21 +#: templates/errors/500.html:8 msgid "" "If you need more help, contact us about all other " "surveys." msgstr "" +#: templates/errors/500.html:11 +msgid "Sorry, there is a problem with this service" +msgstr "" + +#: templates/errors/500.html:12 +msgid "Try again later." +msgstr "" + +#: templates/errors/500.html:13 +msgid "If you have started a survey, your answers have been saved." +msgstr "" + #: templates/errors/previously-submitted.html:3 msgid "Submission Complete" msgstr "" -#: templates/errors/previously-submitted.html:6 -msgid "This page is no longer available" +#: templates/errors/previously-submitted.html:4 +msgid "Return to previous page" msgstr "" #: templates/errors/previously-submitted.html:7 -msgid "Your survey has been submitted" +msgid "This page is no longer available" msgstr "" #: templates/errors/previously-submitted.html:8 -msgid "Return to previous page" +msgid "Your survey has been submitted" msgstr "" -#: templates/errors/submission-failed.html:9 +#: templates/errors/submission-failed.html:5 msgid "You can try to submit your survey again" msgstr "" -#: templates/errors/submission-failed.html:11 +#: templates/errors/submission-failed.html:6 msgid "" "If this problem keeps happening, please contact us " "for help." msgstr "" -#: templates/errors/submission-failed.html:13 +#: templates/errors/submission-failed.html:16 msgid "If this problem keeps happening, please see the following help links." msgstr "" -#: templates/individual_response/confirmation-post.html:15 +#: templates/individual_response/confirmation-post.html:20 msgid "A letter has been sent to Individual Resident at {display_address}" msgstr "" -#: templates/individual_response/confirmation-post.html:17 +#: templates/individual_response/confirmation-post.html:22 msgid "" "The letter with an individual access code should arrive soon for them to " "complete their own census" msgstr "" -#: templates/individual_response/confirmation-text-message.html:15 +#: templates/individual_response/confirmation-text-message.html:20 msgid "We have sent a text to {mobile_number}" msgstr "" -#: templates/individual_response/confirmation-text-message.html:17 +#: templates/individual_response/confirmation-text-message.html:23 msgid "" "The text message with an individual access code should arrive soon for " "them to complete their own census" msgstr "" -#: templates/individual_response/confirmation-text-message.html:25 +#: templates/individual_response/confirmation-text-message.html:34 msgid "The text will be sent from Census2021" msgstr "" -#: templates/individual_response/interstitial.html:8 +#: templates/individual_response/interstitial.html:9 msgid "If you can't answer questions for others in your household" msgstr "" -#: templates/individual_response/interstitial.html:9 +#: templates/individual_response/interstitial.html:11 msgid "" "You can ask the people you live with to answer their own questions by " "sharing the household access code with them." msgstr "" -#: templates/individual_response/interstitial.html:10 +#: templates/individual_response/interstitial.html:13 msgid "" "If this is not possible, you can request a separate census for them to " "complete." msgstr "" -#: templates/individual_response/interstitial.html:13 +#: templates/individual_response/interstitial.html:17 msgid "Request separate census" msgstr "" -#: templates/individual_response/question.html:9 +#: templates/individual_response/question.html:10 msgid "" "To request a census in a different format or for further help, please contact us" msgstr "" -#: templates/layouts/_base.html:13 +#: templates/layouts/_base.html:15 msgid "Previous" msgstr "" -#: templates/layouts/_base.html:62 +#: templates/layouts/_base.html:58 msgid "Tell us whether you accept cookies" msgstr "" -#: templates/layouts/_base.html:63 +#: templates/layouts/_base.html:59 msgid "" "We use cookies to collect information" " about how you use {cookie_domain}. We use this information to make the " "website work as well as possible and improve our services." msgstr "" -#: templates/layouts/_base.html:64 +#: templates/layouts/_base.html:60 msgid "" "You’ve accepted all cookies. You can change your cookie preferences at any " "time." msgstr "" -#: templates/layouts/_base.html:65 +#: templates/layouts/_base.html:61 msgid "Accept all cookies" msgstr "" -#: templates/layouts/_base.html:66 +#: templates/layouts/_base.html:62 msgid "Set cookie preferences" msgstr "" -#: templates/layouts/_base.html:67 +#: templates/layouts/_base.html:63 msgid "Hide" msgstr "" -#: templates/layouts/_base.html:117 +#: templates/layouts/_base.html:113 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:142 +#: templates/layouts/_base.html:141 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:143 +#: templates/layouts/_base.html:142 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:144 +#: templates/layouts/_base.html:143 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:145 +#: templates/layouts/_base.html:144 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:146 +#: templates/layouts/_base.html:145 msgid "Continue survey" msgstr "" -#: templates/layouts/_calculatedsummary.html:19 +#: templates/layouts/_calculatedsummary.html:18 msgid "Yes, I confirm this is correct" msgstr "" -#: templates/layouts/_questionnaire.html:40 +#: templates/layouts/_questionnaire.html:46 msgid "Choose another section and return to this later" msgstr "" -#: templates/layouts/configs/_header.html:11 +#: templates/layouts/configs/_header.html:10 msgid "Exit" msgstr "" @@ -1264,11 +1265,11 @@ msgstr "" msgid "Interviewer note:" msgstr "" -#: templates/partials/confirmation-email-form.html:25 +#: templates/partials/confirmation-email-form.html:26 msgid "Email address" msgstr "" -#: templates/partials/confirmation-email-form.html:26 +#: templates/partials/confirmation-email-form.html:27 msgid "This will not be stored and only used once to send your confirmation" msgstr "" @@ -1276,252 +1277,252 @@ msgstr "" msgid "Send confirmation" msgstr "" -#: templates/partials/feedback-call-to-action.html:6 +#: templates/partials/feedback-call-to-action.html:7 msgid "What do you think about this service?" msgstr "" -#: templates/partials/feedback-call-to-action.html:7 +#: templates/partials/feedback-call-to-action.html:8 msgid "Your comments will help us make improvements" msgstr "" -#: templates/partials/feedback-call-to-action.html:9 +#: templates/partials/feedback-call-to-action.html:10 msgid "Give feedback" msgstr "" -#: templates/partials/individual-response-guidance.html:14 +#: templates/partials/individual-response-guidance.html:18 msgid "" "You can share your household access code with the people you " "live with so they can complete their own sections." msgstr "" -#: templates/partials/individual-response-guidance.html:15 +#: templates/partials/individual-response-guidance.html:21 msgid "" "If this is not possible, there are other ways each person " "can complete their own census." msgstr "" -#: templates/partials/last_viewed_question_guidance.html:7 +#: templates/partials/last_viewed_question_guidance.html:10 msgid "This is the last viewed question in this section" msgstr "" -#: templates/partials/last_viewed_question_guidance.html:10 +#: templates/partials/last_viewed_question_guidance.html:12 msgid "" "You can also go back to the start" " of the section" msgstr "" -#: templates/partials/preview-question.html:34 -#: templates/partials/question.html:93 +#: templates/partials/preview-question.html:31 +#: templates/partials/question.html:85 msgid "Or" msgstr "" -#: templates/partials/preview-question.html:55 +#: templates/partials/preview-question.html:51 msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:84 +#: templates/partials/question.html:77 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:85 +#: templates/partials/question.html:78 msgid "cleared" msgstr "" -#: templates/partials/question.html:88 +#: templates/partials/question.html:80 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:89 templates/partials/question.html:97 +#: templates/partials/question.html:81 templates/partials/question.html:89 msgid "deselected" msgstr "" -#: templates/partials/answers/address.html:9 +#: templates/partials/answers/address.html:8 msgid "Address line 1" msgstr "" -#: templates/partials/answers/address.html:14 +#: templates/partials/answers/address.html:13 msgid "Address line 2" msgstr "" -#: templates/partials/answers/address.html:18 +#: templates/partials/answers/address.html:17 msgid "Town or city" msgstr "" -#: templates/partials/answers/address.html:22 +#: templates/partials/answers/address.html:21 msgid "Postcode" msgstr "" -#: templates/partials/answers/address.html:33 +#: templates/partials/answers/address.html:32 msgid "Enter address or postcode and select from results" msgstr "" -#: templates/partials/answers/address.html:35 +#: templates/partials/answers/address.html:34 msgid "Search for an address" msgstr "" -#: templates/partials/answers/address.html:36 +#: templates/partials/answers/address.html:35 msgid "Manually enter address" msgstr "" -#: templates/partials/answers/address.html:41 +#: templates/partials/answers/address.html:40 msgid "" "Use up and down keys to navigate suggestions once you’ve typed more than " "two characters. Use the enter key to select a suggestion. Touch device " "users, explore by touch or with swipe gestures." msgstr "" -#: templates/partials/answers/address.html:42 +#: templates/partials/answers/address.html:41 msgid "You have selected" msgstr "" -#: templates/partials/answers/address.html:43 +#: templates/partials/answers/address.html:42 msgid "Enter 3 or more characters for suggestions." msgstr "" -#: templates/partials/answers/address.html:44 +#: templates/partials/answers/address.html:43 msgid "There is one suggestion available." msgstr "" -#: templates/partials/answers/address.html:45 +#: templates/partials/answers/address.html:44 msgid "There are {n} suggestions available." msgstr "" -#: templates/partials/answers/address.html:46 +#: templates/partials/answers/address.html:45 msgid "" "Results have been limited to 10 suggestions. Type more characters to " "improve your search" msgstr "" -#: templates/partials/answers/address.html:47 +#: templates/partials/answers/address.html:46 msgid "There are {n} for {x}" msgstr "" -#: templates/partials/answers/address.html:48 +#: templates/partials/answers/address.html:47 msgid "{n} addresses" msgstr "" -#: templates/partials/answers/address.html:49 +#: templates/partials/answers/address.html:48 msgid "Enter more of the address to improve results" msgstr "" -#: templates/partials/answers/address.html:50 +#: templates/partials/answers/address.html:49 msgid "Select an address" msgstr "" -#: templates/partials/answers/address.html:51 +#: templates/partials/answers/address.html:50 msgid "No results found. Try entering a different part of the address" msgstr "" -#: templates/partials/answers/address.html:52 +#: templates/partials/answers/address.html:51 msgid "{n} results found. Enter more of the address to improve results" msgstr "" -#: templates/partials/answers/address.html:53 +#: templates/partials/answers/address.html:52 msgid "Enter more of the address to get results" msgstr "" -#: templates/partials/answers/address.html:57 +#: templates/partials/answers/address.html:56 msgid "Select or manually enter an address" msgstr "" -#: templates/partials/answers/address.html:58 +#: templates/partials/answers/address.html:57 msgid "Sorry, there was a problem loading addresses" msgstr "" -#: templates/partials/answers/address.html:59 +#: templates/partials/answers/address.html:58 msgid "Enter address manually" msgstr "" -#: templates/partials/answers/checkbox.html:13 +#: templates/partials/answers/checkbox.html:12 msgid "Select all that apply" msgstr "" -#: templates/partials/answers/date.html:23 +#: templates/partials/answers/date.html:21 msgid "Day" msgstr "" -#: templates/partials/answers/date.html:38 +#: templates/partials/answers/date.html:35 msgid "Month" msgstr "" -#: templates/partials/answers/date.html:53 +#: templates/partials/answers/date.html:49 msgid "Year" msgstr "" -#: templates/partials/answers/radio.html:15 +#: templates/partials/answers/radio.html:14 msgid "Clear selection" msgstr "" #: templates/partials/answers/textarea.html:20 -#: templates/partials/answers/textfield.html:45 +#: templates/partials/answers/textfield.html:44 msgid "You have {x} character remaining" msgstr "" #: templates/partials/answers/textarea.html:21 -#: templates/partials/answers/textfield.html:46 +#: templates/partials/answers/textfield.html:45 msgid "You have {x} characters remaining" msgstr "" -#: templates/partials/answers/textfield.html:25 +#: templates/partials/answers/textfield.html:24 msgid "" "Use up and down keys to navigate suggestions once you've typed more than " "two characters. Use the enter key to select a suggestion. Touch device " "users, explore by touch or with swipe gestures." msgstr "" -#: templates/partials/answers/textfield.html:26 +#: templates/partials/answers/textfield.html:25 msgid "Continue entering to improve suggestions" msgstr "" -#: templates/partials/answers/textfield.html:27 +#: templates/partials/answers/textfield.html:26 msgid "Suggestions" msgstr "" -#: templates/partials/answers/textfield.html:28 +#: templates/partials/answers/textfield.html:27 msgid "No results found" msgstr "" -#: templates/partials/answers/textfield.html:29 +#: templates/partials/answers/textfield.html:28 msgid "Continue entering to get suggestions" msgstr "" -#: templates/partials/answers/textfield.html:43 +#: templates/partials/answers/textfield.html:42 msgid "{x} character too many" msgstr "" -#: templates/partials/answers/textfield.html:44 +#: templates/partials/answers/textfield.html:43 msgid "{x} characters too many" msgstr "" -#: templates/partials/introduction/start-survey.html:5 +#: templates/partials/introduction/start-survey.html:6 msgid "Start survey" msgstr "" #: templates/partials/summary/collapsible-summary.html:28 -#: templates/partials/summary/list-summary.html:7 -#: templates/partials/summary/summary.html:28 +#: templates/partials/summary/list-summary.html:8 +#: templates/partials/summary/summary.html:27 msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:29 -#: templates/partials/summary/summary.html:29 +#: templates/partials/summary/summary.html:28 msgid "Change your answer for:" msgstr "" -#: templates/partials/summary/list-summary.html:8 +#: templates/partials/summary/list-summary.html:9 msgid "Change details for {item_name}" msgstr "" -#: templates/partials/summary/list-summary.html:9 -#: templates/partials/summary/summary.html:26 +#: templates/partials/summary/list-summary.html:10 +#: templates/partials/summary/summary.html:25 msgid "Remove" msgstr "" -#: templates/partials/summary/list-summary.html:10 +#: templates/partials/summary/list-summary.html:11 msgid "Remove {item_name}" msgstr "" -#: templates/partials/summary/summary.html:27 +#: templates/partials/summary/summary.html:26 msgid "Remove your answer for:" msgstr "" diff --git a/poetry.lock b/poetry.lock index 31014fae97..a2cd5cea99 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "astroid" @@ -750,6 +750,21 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cssbeautifier" +version = "1.15.1" +description = "CSS unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +jsbeautifier = "*" +six = ">=1.13.0" + [[package]] name = "dill" version = "0.3.8" @@ -765,6 +780,30 @@ files = [ graph = ["objgraph (>=1.7.2)"] profile = ["gprof2dot (>=2022.7.29)"] +[[package]] +name = "djlint" +version = "1.34.1" +description = "HTML Template Linter and Formatter" +optional = false +python-versions = ">=3.8.0,<4.0.0" +files = [ + {file = "djlint-1.34.1-py3-none-any.whl", hash = "sha256:96ff1c464fb6f061130ebc88663a2ea524d7ec51f4b56221a2b3f0320a3cfce8"}, + {file = "djlint-1.34.1.tar.gz", hash = "sha256:db93fa008d19eaadb0454edf1704931d14469d48508daba2df9941111f408346"}, +] + +[package.dependencies] +click = ">=8.0.1,<9.0.0" +colorama = ">=0.4.4,<0.5.0" +cssbeautifier = ">=1.14.4,<2.0.0" +html-tag-names = ">=0.1.2,<0.2.0" +html-void-elements = ">=0.1.0,<0.2.0" +jsbeautifier = ">=1.14.4,<2.0.0" +json5 = ">=0.9.11,<0.10.0" +pathspec = ">=0.12.0,<0.13.0" +PyYAML = ">=6.0,<7.0" +regex = ">=2023.0.0,<2024.0.0" +tqdm = ">=4.62.2,<5.0.0" + [[package]] name = "dnspython" version = "2.6.1" @@ -785,6 +824,16 @@ idna = ["idna (>=3.6)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] +[[package]] +name = "editorconfig" +version = "0.12.4" +description = "EditorConfig File Locator and Interpreter for Python" +optional = false +python-versions = "*" +files = [ + {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"}, +] + [[package]] name = "email-validator" version = "2.1.1" @@ -1554,6 +1603,28 @@ setproctitle = ["setproctitle"] testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] +[[package]] +name = "html-tag-names" +version = "0.1.2" +description = "List of known HTML tag names" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"}, + {file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"}, +] + +[[package]] +name = "html-void-elements" +version = "0.1.0" +description = "List of HTML void tag names." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"}, + {file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"}, +] + [[package]] name = "htmlmin" version = "0.1.12" @@ -1681,6 +1752,31 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] +[[package]] +name = "jsbeautifier" +version = "1.15.1" +description = "JavaScript unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +six = ">=1.13.0" + +[[package]] +name = "json5" +version = "0.9.25" +description = "A Python implementation of the JSON5 data format." +optional = false +python-versions = ">=3.8" +files = [ + {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, + {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, +] + [[package]] name = "jsonpointer" version = "2.4" @@ -2561,6 +2657,108 @@ files = [ attrs = ">=22.2.0" rpds-py = ">=0.7.0" +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + [[package]] name = "requests" version = "2.31.0" @@ -2954,6 +3152,26 @@ files = [ {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] +[[package]] +name = "tqdm" +version = "4.66.2" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "types-cachetools" version = "5.3.0.7" @@ -3299,4 +3517,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "ea96ed50e96557d491f7678f36a890ba5533d4316bb66c1e74c18cc69e5ab237" +content-hash = "3abdfcf4cc6d0e5ea580cc8064546b061bf53db977c72dd5466a0620f9692a28" diff --git a/pyproject.toml b/pyproject.toml index b4ec0d5c4e..9e40e25f2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,12 @@ +[tool.djlint] +blank_line_after_tag="load,extends,import,from" +blank_line_before_tag="load,extends,block" +max_line_length=120 +# _base.html excluded due to the bad nesting after format +exclude=".templates/layouts/_base.html" +# when using double quote escape sequence in strings we get "H025 Tag seems to be an orphan" false positives +ignore="H025" + [tool.poetry] name = "eq-questionnaire-runner" version = "1.0.0" @@ -40,6 +49,7 @@ types-cachetools = "^5.3.0.7" types-pytz = "^2024.1.0.20240203" playwright = "^1.42.0" black = "^24.2.0" +djlint = "^1.34.1" [tool.poetry.dependencies] diff --git a/templates/answersummary.html b/templates/answersummary.html index 31e3bb7e24..0c73b74864 100644 --- a/templates/answersummary.html +++ b/templates/answersummary.html @@ -1 +1 @@ -{% extends 'summary.html' %} +{% extends "summary.html" %} diff --git a/templates/calculatedsummary.html b/templates/calculatedsummary.html index bdb0f96511..f909cfff0d 100644 --- a/templates/calculatedsummary.html +++ b/templates/calculatedsummary.html @@ -1,5 +1,5 @@ -{% extends 'layouts/_calculatedsummary.html' %} +{% extends "layouts/_calculatedsummary.html" %} {% block form_title %} -

      {{ content.summary.title }}

      -{% endblock %} +

      {{ content.summary.title }}

      +{% endblock form_title %} diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 1ff9190992..63e8d6d4e0 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -1,5 +1,7 @@ -{% extends 'layouts/_base.html' %} -{% import 'macros/helpers.html' as helpers %} +{% extends "layouts/_base.html" %} + +{% import "macros/helpers.html" as helpers %} + {% from "components/button/_macro.njk" import onsButton %} {% set hide_sign_out_button = content.hide_sign_out_button %} @@ -7,33 +9,35 @@ {% set sign_out_url = content.sign_out_url %} {% block main %} - {% set form = content.form %} - {% if form.mapped_errors %} - {% set error_title = _("There is a problem with your answer") %} - {% include 'partials/error-panel.html' %} - {% endif %} - -

      {{ content.question.title }}

      -

      {{ content.question.description}}

      - {%- for answer in question.answers -%} - {% set question_title = answer.title %} - {% include 'partials/answer.html' %} - {%- endfor -%} - - {{ - onsButton({ - "text": _("Continue"), - "variants": 'timer', - "classes": "ons-u-mt-xl", - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" - } - }) - }} -

      - -{% endblock %} + {% set form = content.form %} + {% if form.mapped_errors %} + {% set error_title = _("There is a problem with your answer") %} + {% include "partials/error-panel.html" %} + {% endif %} +

      {{ content.question.title }}

      +
      +

      {{ content.question.description }}

      +
      + {%- for answer in question.answers -%} + {% set question_title = answer.title %} + {% include "partials/answer.html" %} + {%- endfor -%} + {# djlint:off #} + {{ + onsButton({ + "text": _("Continue"), + "variants": 'timer', + "classes": "ons-u-mt-xl", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} +
      +
      +{% endblock main %} diff --git a/templates/confirmation-email-sent.html b/templates/confirmation-email-sent.html index 88d00b4b52..87ccd55535 100644 --- a/templates/confirmation-email-sent.html +++ b/templates/confirmation-email-sent.html @@ -1,37 +1,47 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/panel/_macro.njk' import onsPanel %} +{% extends "layouts/_base.html" %} + +{% from "components/panel/_macro.njk" import onsPanel %} + {% set page_title = _("Confirmation email sent") %} {% set hide_sign_out_button = content.hide_sign_out_button %} {% set sign_out_url = content.sign_out_url %} {% block main %} - {% call onsPanel({ - "spacious": true, - "type": "success", - "classes": "ons-u-mb-s", - "iconType": "check", - "iconSize": "xl" - }) - %} -

      {{ _("A confirmation email has been sent to {email}").format(email = content.email) }}

      - {% endcall %} - - {% call onsPanel({ - "type": "bare", - "classes": "ons-u-mb-s", - "iconType": "lock" - }) - %} -

      {{ _("The email will be sent from census.2021@notifications.service.gov.uk") }}

      - {% endcall %} - - {% if content.show_send_another_email_guidance %} -

      {{ _("Didn't receive an email?") }}

      -

      {{ _("It can take a few minutes for the email to arrive. If it doesn't arrive, check your junk mail, or you can send another confirmation email.").format(send_confirmation_email_url = content.send_confirmation_email_url)}}

      - {% endif %} - - {% if content.show_feedback_call_to_action %} - {% include 'partials/feedback-call-to-action.html' %} - {% endif %} - -{% endblock %} + {# djlint:off #} + {% call + onsPanel({ + "spacious": true, + "type": "success", + "classes": "ons-u-mb-s", + "iconType": "check", + "iconSize": "xl" + }) + %} +

      + {{ _("A confirmation email has been sent to {email}").format(email = content.email) }} +

      + {% endcall %} + {% call + onsPanel({ + "type": "bare", + "classes": "ons-u-mb-s", + "iconType": "lock" + }) + %} +

      + {{ _("The email will be sent from census.2021@notifications.service.gov.uk") }} +

      + {% endcall %} + {# djlint:on #} + {% if content.show_send_another_email_guidance %} +

      + {{ _("Didn’t receive an email?") }} +

      +

      + {{ _("It can take a few minutes for the email to arrive. If it doesn’t arrive, check your junk mail, or you can send another confirmation email.").format(send_confirmation_email_url = content.send_confirmation_email_url) }} +

      + {% endif %} + {% if content.show_feedback_call_to_action %} + {% include "partials/feedback-call-to-action.html" %} + {% endif %} +{% endblock main %} diff --git a/templates/confirmation-email.html b/templates/confirmation-email.html index f84e2af11f..7c4f8e41db 100644 --- a/templates/confirmation-email.html +++ b/templates/confirmation-email.html @@ -1,14 +1,14 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} + {% set hide_sign_out_button = content.hide_sign_out_button %} {% set sign_out_url = content.sign_out_url %} {% block main %} - {% set form = content.form %} - {% if form.mapped_errors %} - {% set error_title = _("There is a problem with this page") %} - {% include 'partials/error-panel.html' %} - {% endif %} - -

      {{ _("Send a confirmation email") }}

      - {% include 'partials/confirmation-email-form.html' %} -{% endblock %} + {% set form = content.form %} + {% if form.mapped_errors %} + {% set error_title = _("There is a problem with this page") %} + {% include "partials/error-panel.html" %} + {% endif %} +

      {{ _("Send a confirmation email") }}

      + {% include "partials/confirmation-email-form.html" %} +{% endblock main %} diff --git a/templates/confirmationquestion.html b/templates/confirmationquestion.html index 7a03464a9b..5e03e2e2a8 100644 --- a/templates/confirmationquestion.html +++ b/templates/confirmationquestion.html @@ -1 +1 @@ -{% extends 'question.html' %} +{% extends "question.html" %} diff --git a/templates/errors/401.html b/templates/errors/401.html index a94af63ac4..91a3e7a4c7 100644 --- a/templates/errors/401.html +++ b/templates/errors/401.html @@ -1,25 +1,27 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% set page_title = _("Page is not available") %} +{% set sign_in = _("You will need to sign back in to access your account").format(url = business_logout_url) %} +{% set access_code = _("To access this page you need to re-enter your access code.").format(url = other_logout_url) %} +{% set business_sign_in = _("If you are completing a business survey, you need to sign back in to your account.").format(url = business_logout_url) %} +{% set other_access_code = _("If you started your survey using an access code, you need to re-enter your code.").format(url = other_logout_url) %} {% block main %} -

      {{ _("Sorry, you need to sign in again") }}

      -

      {{ _("This is because you have either:") }}

      -
        -
      • {{ _("been inactive for 45 minutes and your session has timed out to protect your information") }}
      • -
      • {{ _("followed a link to a page you are not signed in to") }}
      • -
      • {{ _("followed a link to a survey that has already been submitted") }}
      • -
      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} -

      {{ _("You will need to sign back in to access your account").format(url = business_logout_url) }}

      - - {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %} -

      {{ _("To access this page you need to re-enter your access code.").format(url = other_logout_url) }}

      - - {% else %} -

      {{ _("Business surveys") }}

      -

      {{ _("If you are completing a business survey, you need to sign back in to your account.").format(url = business_logout_url) }}

      -

      {{ _("All other surveys") }}

      -

      {{ _("If you started your survey using an access code, you need to re-enter your code.").format(url = other_logout_url) }}

      - {% endif %} -{% endblock %} +

      {{ _("Sorry, you need to sign in again") }}

      +

      {{ _("This is because you have either:") }}

      +
        +
      • {{ _("been inactive for 45 minutes and your session has timed out to protect your information") }}
      • +
      • {{ _("followed a link to a page you are not signed in to") }}
      • +
      • {{ _("followed a link to a survey that has already been submitted") }}
      • +
      + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} +

      {{ sign_in }}

      + {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %} +

      {{ access_code }}

      + {% else %} +

      {{ _("Business surveys") }}

      +

      {{ business_sign_in }}

      +

      {{ _("All other surveys") }}

      +

      {{ other_access_code }}

      + {% endif %} +{% endblock main %} diff --git a/templates/errors/403.html b/templates/errors/403.html index 857a3025c3..701b059157 100644 --- a/templates/errors/403.html +++ b/templates/errors/403.html @@ -1,17 +1,20 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% set page_title = _("Sorry, there is a problem") %} +{% set all_contact_us = _("For further help, please contact us.").format(url=contact_us_url) %} +{% set business_contact_us = _("If you are completing a business survey and you need further help, please contact us.").format(url=business_contact_us_url) %} +{% set other_contact_us = _("If you started your survey using an access code and you need further help, please contact us.").format(url=other_contact_us_url) %} {% block main %}

      {{ _("Sorry, there is a problem") }}

      {{ _("You may need to update your browser to a newer version.") }}

      {{ _("If the problem still occurs, try using a different browser or device.") }}

      - {% if survey_type in SURVEY_TYPES_ALL %} -

      {{ _("For further help, please contact us.").format(url=contact_us_url) }}

      - {% else %} -

      {{ _("Business surveys") }}

      -

      {{ _("If you are completing a business survey and you need further help, please contact us.").format(url=business_contact_us_url) }}

      -

      {{ _("All other surveys") }}

      -

      {{ _("If you started your survey using an access code and you need further help, please contact us.").format(url=other_contact_us_url) }}

      - {% endif %} -{% endblock %} + {% if survey_type in SURVEY_TYPES_ALL %} +

      {{ all_contact_us }}

      + {% else %} +

      {{ _("Business surveys") }}

      +

      {{ business_contact_us }}

      +

      {{ _("All other surveys") }}

      +

      {{ other_contact_us }}

      + {% endif %} +{% endblock main %} diff --git a/templates/errors/404.html b/templates/errors/404.html index f7d4a357e1..c6b35b38da 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -1,18 +1,21 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% set page_title = _("Page not found") %} +{% set all_contact_us = _("If the web address is correct or you selected a link or button, contact us for more help.").format(url=contact_us_url) %} +{% set business_contact_us = _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) %} +{% set other_contact_us = _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) %} {% block main %} -

      {{ _("Page not found") }}

      -

      {{ _("If you entered a web address, check it is correct.") }}

      -

      {{ _("If you pasted the web address, check you copied the whole address.") }}

      - {% if survey_type in SURVEY_TYPES_ALL %} -

      {{ _("If the web address is correct or you selected a link or button, contact us for more help.").format(url=contact_us_url) }}

      - {% else %} -

      {{ _("If the web address is correct or you selected a link or button, please see the following help links.") }}

      -

      {{ _("Business surveys") }}

      -

      {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

      -

      {{ _("All other surveys") }}

      -

      {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

      - {% endif %} -{% endblock %} +

      {{ _("Page not found") }}

      +

      {{ _("If you entered a web address, check it is correct.") }}

      +

      {{ _("If you pasted the web address, check you copied the whole address.") }}

      + {% if survey_type in SURVEY_TYPES_ALL %} +

      {{ all_contact_us }}

      + {% else %} +

      {{ _("If the web address is correct or you selected a link or button, please see the following help links.") }}

      +

      {{ _("Business surveys") }}

      +

      {{ business_contact_us }}

      +

      {{ _("All other surveys") }}

      +

      {{ other_contact_us }}

      + {% endif %} +{% endblock main %} diff --git a/templates/errors/500.html b/templates/errors/500.html index d9ad2701f0..0572870fdf 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -1,23 +1,28 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% set page_title = _("An error has occurred") %} +{% set business_sign_in = _("If you have attempted to submit your survey, you should check that this was successful. To do this, sign in to your business survey account.").format(url = business_logout_url) %} +{% set contact_us = _("If you need more help, contact us.").format(url=contact_us_url) %} +{% set access_code = _("If you have attempted to submit your survey, you should check that this was successful. To do this, re-enter your code.").format(url = other_logout_url) %} +{% set business_contact_us = _("If you need more help, contact us about business surveys.").format(url=business_contact_us_url) %} +{% set other_contact_us = _("If you need more help, contact us about all other surveys.").format(url=other_contact_us_url) %} {% block main %} -

      {{ _("Sorry, there is a problem with this service") }}

      -

      {{ _("Try again later.") }}

      -

      {{ _("If you have started a survey, your answers have been saved.") }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} -

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, sign in to your business survey account.").format(url = business_logout_url) }}

      -

      {{ _("If you need more help, contact us.").format(url=contact_us_url) }}

      - {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %} -

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, re-enter your code.").format(url = other_logout_url) }}

      -

      {{ _("If you need more help, contact us.").format(url=contact_us_url) }}

      - {% else %} -

      {{ _("Business surveys") }}

      -

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, sign in to your business survey account.").format(url = business_logout_url) }}

      -

      {{ _("If you need more help, contact us about business surveys.").format(url=business_contact_us_url) }}

      -

      {{ _("All other surveys") }}

      -

      {{ _("If you have attempted to submit your survey, you should check that this was successful. To do this, re-enter your code.").format(url = other_logout_url) }}

      -

      {{ _("If you need more help, contact us about all other surveys.").format(url=other_contact_us_url) }}

      - {% endif %} -{% endblock %} +

      {{ _("Sorry, there is a problem with this service") }}

      +

      {{ _("Try again later.") }}

      +

      {{ _("If you have started a survey, your answers have been saved.") }}

      + {% if survey_type and (survey_type in SURVEY_TYPES_BUSINESS or survey_type in SURVEY_TYPES_DEFAULT) %} +

      {{ business_sign_in }}

      +

      {{ contact_us }}

      + {% elif survey_type and survey_type in SURVEY_TYPES_SOCIAL or survey_type in SURVEY_TYPES_HEALTH %} +

      {{ access_code }}

      +

      {{ contact_us }}

      + {% else %} +

      {{ _("Business surveys") }}

      +

      {{ business_sign_in }}

      +

      {{ business_contact_us }}

      +

      {{ _("All other surveys") }}

      +

      {{ access_code }}

      +

      {{ other_contact_us }}

      + {% endif %} +{% endblock main %} diff --git a/templates/errors/_base.html b/templates/errors/_base.html index b7d8e95fe5..caf4f26152 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -1,4 +1,4 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr", "desnz", "desnz-ni"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} diff --git a/templates/errors/error.html b/templates/errors/error.html index f729a96a25..d460b57132 100644 --- a/templates/errors/error.html +++ b/templates/errors/error.html @@ -1,9 +1,9 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% block main %} -

      {{ heading }}

      -{%- if retry_message and retry_url -%} -

      {{ retry_message.format(retry_url=retry_url) }}

      -{%- endif -%} -

      {{ contact_us_message.format(contact_us_url=contact_us_url) }}

      -{% endblock %} +

      {{ heading }}

      + {%- if retry_message and retry_url -%} +

      {{ retry_message.format(retry_url=retry_url) }}

      + {%- endif -%} +

      {{ contact_us_message.format(contact_us_url=contact_us_url) }}

      +{% endblock main %} diff --git a/templates/errors/previously-submitted.html b/templates/errors/previously-submitted.html index 59080fada2..275bf2e368 100644 --- a/templates/errors/previously-submitted.html +++ b/templates/errors/previously-submitted.html @@ -1,9 +1,10 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% set page_title = _("Submission Complete") %} +{% set return_previous = _("Return to previous page").format(url = thank_you_url) %} {% block main %} -

      {{ _("This page is no longer available") }}

      -

      {{ _("Your survey has been submitted") }}

      -

      {{ _("Return to previous page").format(url = thank_you_url) }}

      -{% endblock %} +

      {{ _("This page is no longer available") }}

      +

      {{ _("Your survey has been submitted") }}

      +

      {{ return_previous }}

      +{% endblock main %} diff --git a/templates/errors/submission-failed.html b/templates/errors/submission-failed.html index 8371ceb4d8..d734596093 100644 --- a/templates/errors/submission-failed.html +++ b/templates/errors/submission-failed.html @@ -1,19 +1,22 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% set hide_sign_out_button = True %} - {% set page_title = _("Sorry, there is a problem") %} +{% set resubmit_survey = _("You can try to submit your survey again").format(url=url_for('questionnaire.get_questionnaire')) %} +{% set contact_us = _("If this problem keeps happening, please contact us for help.").format(url=contact_us_url) %} +{% set business_contact_us = _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) %} +{% set other_contact_us = _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) %} {% block main %} -

      {{ _("Sorry, there is a problem") }}

      -

      {{ _("You can try to submit your survey again").format(url=url_for('questionnaire.get_questionnaire')) }}

      - {% if survey_type and (survey_type in SURVEY_TYPES_ALL) %} -

      {{ _("If this problem keeps happening, please contact us for help.").format(url=contact_us_url) }}

      - {% else %} -

      {{ _("If this problem keeps happening, please see the following help links.") }}

      -

      {{ _("Business surveys") }}

      -

      {{ _("If you are completing a business survey, please contact us.").format(url=business_contact_us_url) }}

      -

      {{ _("All other surveys") }}

      -

      {{ _("If you started your survey using an access code, please contact us.").format(url=other_contact_us_url) }}

      - {% endif %} -{% endblock %} +

      {{ _("Sorry, there is a problem") }}

      +

      {{ resubmit_survey }}

      + {% if survey_type and (survey_type in SURVEY_TYPES_ALL) %} +

      {{ contact_us }}

      + {% else %} +

      {{ _("If this problem keeps happening, please see the following help links.") }}

      +

      {{ _("Business surveys") }}

      +

      {{ business_contact_us }}

      +

      {{ _("All other surveys") }}

      +

      {{ other_contact_us }}

      + {% endif %} +{% endblock main %} diff --git a/templates/feedback-sent.html b/templates/feedback-sent.html index 29f02b4fbe..f4f098a3e6 100644 --- a/templates/feedback-sent.html +++ b/templates/feedback-sent.html @@ -1,23 +1,28 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/panel/_macro.njk' import onsPanel %} -{% from 'components/button/_macro.njk' import onsButton %} +{% extends "layouts/_base.html" %} + +{% from "components/panel/_macro.njk" import onsPanel %} +{% from "components/button/_macro.njk" import onsButton %} {% set hide_sign_out_button = content.hide_sign_out_button %} {% set page_title = _("Feedback sent") %} {% set sign_out_url = content.sign_out_url %} {% block main %} - {% call onsPanel({ - "spacious": true, - "type": "success", - "classes": "ons-u-mb-s", - "iconType": "check", - "iconSize": "xl" - }) %} + {# djlint:off #} + {% call + onsPanel({ + "spacious": true, + "type": "success", + "classes": "ons-u-mb-s", + "iconType": "check", + "iconSize": "xl" + }) + %}

      {{ _("Thank you for your feedback") }}

      -

      {{ _("Your comments will help us make improvements to our surveys. We are not able to reply to comments, but we appreciate your feedback") }}

      +

      + {{ _("Your comments will help us make improvements to our surveys. We are not able to reply to comments, but we appreciate your feedback") }} +

      {% endcall %} - {{ onsButton({ "type": "button", @@ -33,4 +38,5 @@

      {{ _("Thank you for your feedback") }}

      {{ content.question.title }}

      - {%- for answer in question.answers -%} - {% include 'partials/answer.html' %} - {%- endfor -%} - - {{ - onsButton({ - "text": _("Send feedback"), - "variants": 'timer', - "classes": "ons-u-mt-xl", - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Send feedback", - "data-ga-label": "Send feedback button click", - "data-ga": "click" - } - }) - }} -

      - -{% endblock %} + {% set form = content.form %} + {% if form.mapped_errors %} + {% set error_title = ngettext('There is a problem with your feedback', 'There are %(num)s problems with your feedback', form.mapped_errors | length) %} + {% include "partials/error-panel.html" %} + {% endif %} +

      {{ content.question.title }}

      + {%- for answer in question.answers -%} + {% include "partials/answer.html" %} + {%- endfor -%} + {# djlint:off #} + {{ + onsButton({ + "text": _("Send feedback"), + "variants": 'timer', + "classes": "ons-u-mt-xl", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Send feedback", + "data-ga-label": "Send feedback button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} +
      +
      +{% endblock main %} diff --git a/templates/grandcalculatedsummary.html b/templates/grandcalculatedsummary.html index 875870974a..4ecb236c77 100644 --- a/templates/grandcalculatedsummary.html +++ b/templates/grandcalculatedsummary.html @@ -1,5 +1,5 @@ -{% extends 'layouts/_calculatedsummary.html' %} +{% extends "layouts/_calculatedsummary.html" %} {% block form_title %} -

      {{ content.summary.title }}

      -{% endblock %} +

      {{ content.summary.title }}

      +{% endblock form_title %} diff --git a/templates/hub.html b/templates/hub.html index e394d36473..fe1d942d21 100644 --- a/templates/hub.html +++ b/templates/hub.html @@ -1,25 +1,28 @@ -{% extends 'layouts/_submit.html' %} +{% extends "layouts/_submit.html" %} {% from "components/summary/_macro.njk" import onsSummary %} {% block pre_submit_button_content %} - {% if content.individual_response_url %} - {% set title = _("If you can’t answer someone else’s questions") %} - {% include 'partials/individual-response-guidance.html' %} - {% endif %} + {% if content.individual_response_url %} + {% set title = _("If you can’t answer someone else’s questions") %} + {% include "partials/individual-response-guidance.html" %} + {% endif %} {% endblock pre_submit_button_content %} {% block summary %} - {% if content.rows %} - {{ onsSummary({ - "variant": "hub", - "classes": "ons-u-mt-m", - "summaries": [{ - "groups": [{ - "rows": content.rows - }] - }] - }) - }} - {% endif %} + {% if content.rows %} + {# djlint:off #} + {{ + onsSummary({ + "variant": "hub", + "classes": "ons-u-mt-m", + "summaries": [{ + "groups": [{ + "rows": content.rows + }] + }] + }) + }} + {# djlint:on #} + {% endif %} {% endblock summary %} diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index 3282416256..58cfbc426c 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -1,17 +1,22 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/button/_macro.njk' import onsButton %} -{% from 'components/panel/_macro.njk' import onsPanel %} +{% extends "layouts/_base.html" %} + +{% from "components/button/_macro.njk" import onsButton %} +{% from "components/panel/_macro.njk" import onsPanel %} {% set save_on_signout = true %} {% block main %} - {% call onsPanel({ - "type": "success", - "classes": "ons-u-mb-m", - "iconType": "check", - "iconSize": "l" - }) %} -

      + {# djlint:off #} + {% call + onsPanel({ + "type": "success", + "classes": "ons-u-mb-m", + "iconType": "check", + "iconSize": "l" + }) + %} +

      {{ _("A letter has been sent to Individual Resident at {display_address}").format(display_address=display_address) }}

      {{ _("The letter with an individual access code should arrive soon for them to complete their own census") }}

      @@ -30,4 +35,5 @@

      } }) }} -{% endblock %} + {# djlint:on #} +{% endblock main %} diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index c268c02819..3989b5199a 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -1,28 +1,38 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/button/_macro.njk' import onsButton %} -{% from 'components/panel/_macro.njk' import onsPanel %} +{% extends "layouts/_base.html" %} + +{% from "components/button/_macro.njk" import onsButton %} +{% from "components/panel/_macro.njk" import onsPanel %} {% set save_on_signout = true %} {% block main %} - {% call onsPanel({ - "type": "success", - "classes": "ons-u-mb-m", - "iconType": "check", - "iconSize": "l" - }) %} -

      + {# djlint:off #} + {% call + onsPanel({ + "type": "success", + "classes": "ons-u-mb-m", + "iconType": "check", + "iconSize": "l" + }) + %} +

      {{ _("We have sent a text to {mobile_number}").format(mobile_number=mobile_number) }}

      -

      {{ _("The text message with an individual access code should arrive soon for them to complete their own census") }}

      +

      + {{ _("The text message with an individual access code should arrive soon for them to complete their own census") }} +

      {% endcall %} - - {% call onsPanel({ - "type": "bare", - "classes": "ons-u-mb-s", - "iconType": "lock" - }) %} -

      {{ _("The text will be sent from Census2021") }}

      + {% call + onsPanel({ + "type": "bare", + "classes": "ons-u-mb-s", + "iconType": "lock" + }) + %} +

      + {{ _("The text will be sent from Census2021") }} +

      {% endcall %} {{ onsButton({ @@ -38,4 +48,5 @@

      } }) }} -{% endblock %} + {# djlint:on #} +{% endblock main %} diff --git a/templates/individual_response/interstitial.html b/templates/individual_response/interstitial.html index 70be633586..234675485a 100644 --- a/templates/individual_response/interstitial.html +++ b/templates/individual_response/interstitial.html @@ -1,21 +1,25 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} + {% from "components/button/_macro.njk" import onsButton %} {% set save_on_signout = true %} {% block main %} -

      {{_("If you can't answer questions for others in your household") }}

      -

      {{_("You can ask the people you live with to answer their own questions by sharing the household access code with them.") }}

      -

      {{_("If this is not possible, you can request a separate census for them to complete.") }}

      +

      {{ _("If you can't answer questions for others in your household") }}

      +

      + {{ _("You can ask the people you live with to answer their own questions by sharing the household access code with them.") }} +

      +

      {{ _("If this is not possible, you can request a separate census for them to complete.") }}

      + {# djlint:off #} {{ onsButton({ "text": _("Request separate census"), - "variants": 'timer', - "classes": 'ons-u-mb-m ons-u-mt-s', + "variants": "timer", + "classes": "ons-u-mb-m ons-u-mt-s", "url": next_location_url, "attributes": { - 'data-qa': 'btn-submit', + "data-qa": "btn-submit", "data-ga-category": "Submit button", "data-ga-action": "Request separate census", "data-ga-label": "Request separate census button click", @@ -23,4 +27,5 @@

      {{_("If you can't answer questions for others in your hou } }) }} -{% endblock %} + {# djlint:on #} +{% endblock main %} diff --git a/templates/individual_response/question.html b/templates/individual_response/question.html index dd6325e111..16497fad06 100644 --- a/templates/individual_response/question.html +++ b/templates/individual_response/question.html @@ -1,4 +1,4 @@ -{% extends 'question.html' %} +{% extends "question.html" %} {% set save_on_signout = true %} {% set question = content.block.question %} @@ -6,6 +6,8 @@ {% block after_submit_button_content %} {% if show_contact_us_guidance %} -

      {{ _("To request a census in a different format or for further help, please contact us").format(contact_us_url=contact_us_url) }}

      +

      + {{ _("To request a census in a different format or for further help, please contact us").format(contact_us_url=contact_us_url) }} +

      {% endif %} -{% endblock %} +{% endblock after_submit_button_content %} diff --git a/templates/interstitial.html b/templates/interstitial.html index 125bc85a58..1c3103f936 100644 --- a/templates/interstitial.html +++ b/templates/interstitial.html @@ -1,10 +1,9 @@ -{% extends 'layouts/_questionnaire.html' %} -{% from 'macros/helpers.html' import format_paragraphs %} +{% extends "layouts/_questionnaire.html" %} -{% from 'macros/helpers.html' import interviewer_note %} +{% from "macros/helpers.html" import format_paragraphs %} +{% from "macros/helpers.html" import interviewer_note %} {% set save_on_signout = true %} - {% set continue_button_text = _("Continue") %} {% block form_content %} @@ -18,14 +17,15 @@

      {{ block.content.title }}

      {{ interstitial_instruction | safe }}
      {% endif %} {% set contents = block.content.contents %} - {% include 'partials/contents.html' %} + {% include "partials/contents.html" %} {% if content.individual_response_url %} {% set title = _("If you can’t answer questions for this person") %} - {% include 'partials/individual-response-guidance.html' %} + {% include "partials/individual-response-guidance.html" %} {% endif %} {% endblock form_content %} {% block submit_button %} + {# djlint:off #} {{ onsButton({ "text": continue_button_text | default(_("Save and continue")), @@ -40,4 +40,5 @@

      {{ block.content.title }}

      } }) }} -{% endblock %} + {# djlint:on #} +{% endblock submit_button %} diff --git a/templates/introduction.html b/templates/introduction.html index 5c7727fa2e..30d3d638a6 100644 --- a/templates/introduction.html +++ b/templates/introduction.html @@ -1,50 +1,42 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} -{% import 'macros/helpers.html' as helpers %} +{% import "macros/helpers.html" as helpers %} {% set form = content.form %} - {# Test failing because section title is missing #} {# {% set page_title =
      + ' - ' + page_title %} #} - {% set page_title = _("Introduction") %} {% block main %} - {% block intro_content %} {% if content.block.primary_content %} {% for content_block in content.block.primary_content %} - {% set title_tag = 'h1' %} - {% include 'partials/introduction/basic.html' %} + {% set title_tag = "h1" %} + {% include "partials/introduction/basic.html" %} {% endfor %} {% endif %} - {% if preview_enabled %}
      - {{ _("View the questions you will be asked in this survey").format(url=url_for('questionnaire.get_preview')) }} + {{ _("View the questions you will be asked in this survey").format(url=url_for('questionnaire.get_preview')) }}
      {% endif %} - {%- if legal_basis -%}

      {{ _("Your response is legally required") }}

      {{ legal_basis }}

      {%- endif -%} {% block start_survey %} - {% include 'partials/introduction/start-survey.html' %} + {% include "partials/introduction/start-survey.html" %} {% endblock start_survey %} - {% if content.block.preview_content %} {% set intro = content.block.preview_content %} - {% include 'partials/introduction/preview.html' %} + {% include "partials/introduction/preview.html" %} {% endif %} - {% if content.block.secondary_content %} {% for content_block in content.block.secondary_content %} - {% set title_tag = 'h2' %} - {% include 'partials/introduction/basic.html' %} + {% set title_tag = "h2" %} + {% include "partials/introduction/basic.html" %} {% endfor %} {% endif %} {% endblock intro_content %} - -{% endblock %} +{% endblock main %} diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 5970feb77b..51c641d4c1 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -1,155 +1,155 @@ {% extends "layout/_template.njk" %} + {% from "components/cookies-banner/_macro.njk" import onsCookiesBanner %} {% from "components/skip-to-content/_macro.njk" import onsSkipToContent %} {% from "components/timeout-modal/_macro.njk" import onsTimeoutModal %} {% if previous_location_url %} - {% set breadcrumbs = { - "ariaLabel": 'Previous', - "itemsList": [ - { - "url": previous_location_url, - "id": "top-previous", - "text": _("Previous"), - "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' - } - } - ] - } %} + {# djlint:off #} + {% set breadcrumbs = { + "ariaLabel": 'Previous', + "itemsList": [ + { + "url": previous_location_url, + "id": "top-previous", + "text": _("Previous"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Previous link click' + } + } + ] + } %} + {# djlint:on #} {% endif %} - {% if survey_title %} - {% set full_page_title = page_title ~ " - " ~ survey_title %} + {% set full_page_title = page_title ~ " - " ~ survey_title %} {% else %} - {% set full_page_title = page_title %} + {% set full_page_title = page_title %} {% endif %} - {% set pageConfig = { - "title": full_page_title, - "pageColNumber": 8, - "footer": footer, - "cdn": { - "url": cdn_url - }, - "breadcrumbs": breadcrumbs, - "cspNonce": csp_nonce + "title": full_page_title, + "pageColNumber": 8, + "footer": footer, + "cdn": { + "url": cdn_url + }, + "breadcrumbs": breadcrumbs, + "cspNonce": csp_nonce } %} - {% if theme %} - {% do pageConfig.update({"theme":theme}) %} + {% do pageConfig.update({"theme":theme}) %} {% endif %} - -{% include 'layouts/configs/_header.html' %} - - +{% include "layouts/configs/_header.html" %} {# if there is not a previous link add extra margin top to the page #} {% if not previous_location_url %} - {% set pageClasses = pageClasses + " ons-u-mt-m" if pageClasses else "ons-u-mt-m" %} + {% set pageClasses = pageClasses + " ons-u-mt-m" if pageClasses else "ons-u-mt-m" %} {% endif %} {% block preHeader %} - {% if include_csrf_token %} - - {% endif %} - - {% if cookie_settings_url and cookie_domain %} - {{ - onsCookiesBanner({ - "secondaryButtonUrl": cookie_settings_url, - "statementTitle": _('Tell us whether you accept cookies'), - "statementText": _("We use cookies to collect information about how you use {cookie_domain}. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url, cookie_domain=cookie_domain), - "confirmationText": _("You’ve accepted all cookies. You can change your cookie preferences at any time.").format(cookie_settings_url=cookie_settings_url), - "primaryButtonText": _('Accept all cookies'), - "secondaryButtonText": _('Set cookie preferences'), - "confirmationButtonText": _('Hide'), - 'lang': language_code - }) - }} - {% endif %} -{% endblock %} - + {# djlint:off #} + {% if include_csrf_token %} + + {% endif %} + {% if cookie_settings_url and cookie_domain %} + {{ onsCookiesBanner({ + "secondaryButtonUrl": cookie_settings_url, + "statementTitle": _('Tell us whether you accept cookies') , + "statementText": _("We use cookies to collect information about how you use {cookie_domain}. We use this information to make the website work as well as possible and improve our services.").format(cookie_settings_url=cookie_settings_url, cookie_domain=cookie_domain), + "confirmationText": _("You’ve accepted all cookies. You can change your cookie preferences at any time.").format(cookie_settings_url=cookie_settings_url), + "primaryButtonText": _('Accept all cookies'), + "secondaryButtonText": _('Set cookie preferences'), + "confirmationButtonText": _('Hide'), + 'lang': language_code + }) + }} + {% endif %} + {# djlint:on #} +{% endblock preHeader %} {% block head %} - {% if google_tag_id %} - - - - - {% endif %} -{% endblock %} - -{% block bodyStart %} -
      -{% endblock %} - -{% block skipLink %} - {{ - onsSkipToContent({ - "url": "#main-content", - "text": _("Skip to main content") - }) - }} -{% endblock %} - -{% block bodyEnd %} -
      -{% endblock %} + {# djlint:off #} + {% if google_tag_id %} + + + + {% endif %} + {# djlint:on #} +{% endblock head %} + +{% block bodyStart %}
      {% endblock bodyStart %} + + {% block skipLink %} + {# djlint:off #} + {{ + onsSkipToContent({ + "url": "#main-content", + "text": _("Skip to main content") + }) + }} + {# djlint:on #} + {% endblock skipLink %} + + {% block bodyEnd %}
      {% endblock bodyEnd %} {% block scripts %} - {% if config['EQ_ENABLE_LIVE_RELOAD'] %} - - {% endif %} -{% endblock %} + {% if config['EQ_ENABLE_LIVE_RELOAD'] %} + + {% endif %} +{% endblock scripts %} {% block preFooter %} - {% if session_expires_at %} +{# djlint:off #} + {% if session_expires_at %} + {{ - onsTimeoutModal({ - "showModalTimeInSeconds": 60, - "serverSessionExpiryEndpoint": url_for('session.session_expiry'), - "sessionExpiresAt": session_expires_at, - "redirectUrl": url_for('session.get_session_expired'), - "title": _("You will be signed out soon"), - "textFirstLine": _("It appears you have been inactive for a while."), - "countdownText": _("To protect your information, your progress will be saved and you will be signed out in"), - "countdownExpiredText": _("You are being signed out"), - "btnText": _("Continue survey"), - "minutesTextSingular": _("minute"), - "minutesTextPlural": _("minutes"), - "secondsTextSingular": _("second"), - "secondsTextPlural": _("seconds"), - "endWithFullStop": true - }) + onsTimeoutModal({ + "showModalTimeInSeconds": 60, + "serverSessionExpiryEndpoint": url_for('session.session_expiry') , + "sessionExpiresAt": session_expires_at, + "redirectUrl": url_for('session.get_session_expired'), + "title": _("You will be signed out soon"), + "textFirstLine": _("It appears you have been inactive for a while."), + "countdownText": _("To protect your information, your progress will be saved and you will be signed out in"), + "countdownExpiredText": _("You are being signed out"), + "btnText": _("Continue survey"), + "minutesTextSingular": _("minute"), + "minutesTextPlural": _("minutes"), + "secondsTextSingular": _("second"), + "secondsTextPlural": _("seconds"), + "endWithFullStop": true + }) }} - {% endif %} -{% endblock %} + {% endif %} +{# djlint:on #} +{% endblock preFooter %} diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html index 286f50b8e3..721995f1d7 100644 --- a/templates/layouts/_calculatedsummary.html +++ b/templates/layouts/_calculatedsummary.html @@ -1,30 +1,30 @@ -{% extends 'layouts/_questionnaire.html' %} +{% extends "layouts/_questionnaire.html" %} + {% from "components/button/_macro.njk" import onsButton %} {% from "components/panel/_macro.njk" import onsPanel %} {% set save_on_signout = true %} {% block form_content %} - {% block form_title %} - {% endblock %} - -
      - {% include 'partials/summary/summary.html' %} -
      -{% endblock -%} + {% block form_title %} + {% endblock form_title %} +
      {% include "partials/summary/summary.html" %}
      +{% endblock form_content -%} {% block submit_button %} - {{ - onsButton({ - "text": _("Yes, I confirm this is correct"), - "variants": 'timer', - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Confirm", - "data-ga-label": "Confirm button click", - "data-ga": "click" - } - }) - }} -{% endblock %} + {# djlint:off #} + {{ + onsButton({ + "text": _("Yes, I confirm this is correct"), + "variants": 'timer', + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Confirm", + "data-ga-label": "Confirm button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} +{% endblock submit_button %} diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index 50574c7904..ae3d3b561d 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -1,4 +1,5 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} + {% from "components/button/_macro.njk" import onsButton %} {% set block = content.block %} @@ -8,22 +9,24 @@ {% block main %} - {% block form_errors %}{% endblock %} + {% block form_errors %} + {% endblock form_errors %} {% block form_content %}
      {% if last_viewed_question_guidance %} - {% include 'partials/last_viewed_question_guidance.html' %} + {% include "partials/last_viewed_question_guidance.html" %} {% endif %} - {% include 'partials/block.html' %} + {% include "partials/block.html" %}
      - {% endblock %} + {% endblock form_content %} {% block submit_button %} + {# djlint:off #} {{ onsButton({ "variants": 'timer', - "text": continue_button_text | default(_("Save and continue")), + "text": continue_button_text | default(_("Save and continue") ), "attributes": { "data-qa": "btn-submit", "data-ga-category": "Submit button", @@ -33,12 +36,15 @@ } }) }} - {% endblock %} + {# djlint:on #} + {% endblock submit_button %} {% block after_submit_button_content %} {% if content.return_to_hub_url %} -

      {{ _("Choose another section and return to this later") }}

      +

      + {{ _("Choose another section and return to this later") }} +

      {% endif %} - {% endblock %} - -{% endblock %} + {% endblock after_submit_button_content %} +{% endblock main %} diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index 581c286b38..dfd87cfa18 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -1,4 +1,4 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/button/_macro.njk" import onsButton %} @@ -6,46 +6,49 @@ {% set save_on_signout = true %} {% block main %} - {% block page_title %} -

      {{ content.title }}

      - {% endblock page_title %} - - {% block post_title_panel %} - {% if content.warning %} - {% call onsPanel({ "type": "warn"}) %} -

      {{ content.warning }}

      - {% endcall %} + {% block page_title %} +

      {{ content.title }}

      + {% endblock page_title %} + + {% block post_title_panel %} + {% if content.warning %} + {# djlint:off #} + {% call onsPanel({ "type": "warn"}) %} +

      {{ content.warning }}

      + {% endcall %} + {# djlint:on #} + {% endif %} + {% endblock post_title_panel %} + + {% block summary %} + {% endblock summary %} + {% if content.guidance %} +
      +

      + {{ content.guidance }} +

      +
      {% endif %} - {% endblock post_title_panel %} - - {% block summary %} - {% endblock summary %} - - {% if content.guidance %} -
      -

      - {{ content.guidance }} -

      -
      - {% endif %} - - {% block pre_submit_button_content %} - {% endblock pre_submit_button_content %} - {% block submit_button %} - {{ - onsButton({ - "text": content.submit_button, - "variants": 'timer', - "classes": 'ons-u-mb-m ons-u-mt-' ~ ("s" if content.guidance else "xl"), - "attributes": { - 'data-qa': 'btn-submit', - "data-ga-category": "Submit Button", - "data-ga-action": "Submit", - "data-ga-label": "Submit Button click", - "data-ga": "click" - } - }) - }} - {% endblock submit_button %} -{% endblock %} + {% block pre_submit_button_content %} + {% endblock pre_submit_button_content %} + + {% block submit_button %} + {# djlint:off #} + {{ + onsButton({ + "text": content.submit_button, + "variants": 'timer', + "classes": 'ons-u-mb-m ons-u-mt-' ~ ("s" if content.guidance else "xl"), + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit Button", + "data-ga-action": "Submit", + "data-ga-label": "Submit Button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} + {% endblock submit_button %} +{% endblock main %} diff --git a/templates/layouts/configs/_header.html b/templates/layouts/configs/_header.html index 0a41e6b0fb..13fc9039cd 100644 --- a/templates/layouts/configs/_header.html +++ b/templates/layouts/configs/_header.html @@ -1,5 +1,4 @@ {% if current_user.is_authenticated and not hide_sign_out_button %} - {% if save_on_signout %} {% set sign_out_url = url_for('session.get_sign_out', internal_redirect=true) %} {% set header_button_text = sign_out_button_text %} @@ -13,7 +12,7 @@ {% set header_button_data_ga_action = "Exit" %} {% set header_button_data_ga_label = "Exit button click" %} {% endif %} - + {# djlint:off #} {% set signout_button = { "text": header_button_text, "url": sign_out_url, @@ -27,16 +26,17 @@ "iconType": "exit", "iconPosition": "after" } %} - + {# djlint:on #} {% endif %} - +{# djlint:off #} {% do pageConfig | setAttribute("header", { "title": survey_title, "signoutButton": signout_button, "language": languages, "serviceLinks": service_links, "mastheadLogo": { - "large": masthead_logo, - "small": masthead_logo_mobile + "large": masthead_logo, + "small": masthead_logo_mobile } }) %} +{# djlint:on #} diff --git a/templates/list-action.html b/templates/list-action.html index bc8f87adbb..67a7a4f4bb 100644 --- a/templates/list-action.html +++ b/templates/list-action.html @@ -1,12 +1,13 @@ -{% extends 'question.html' %} +{% extends "question.html" %} {% block after_submit_button_content %} - {% if previous_location_url %} -

      - {% if content.block.cancel_text %} - {{ content.block.cancel_text }}
      - {% endif %} - {{ _("Cancel and return to the previous page") }} -

      - {% endif %} + {% if previous_location_url %} +

      + {% if content.block.cancel_text %} + {{ content.block.cancel_text }} +
      + {% endif %} + {{ _("Cancel and return to the previous page") }} +

      + {% endif %} {% endblock after_submit_button_content %} diff --git a/templates/listaddquestion.html b/templates/listaddquestion.html index d8516f06da..475fefb203 100644 --- a/templates/listaddquestion.html +++ b/templates/listaddquestion.html @@ -1 +1 @@ -{% extends 'list-action.html' %} +{% extends "list-action.html" %} diff --git a/templates/listcollector.html b/templates/listcollector.html index 118134653c..2f61a13acd 100644 --- a/templates/listcollector.html +++ b/templates/listcollector.html @@ -1 +1 @@ -{% include 'question.html' %} +{% include "question.html" %} diff --git a/templates/listcollectorcontent.html b/templates/listcollectorcontent.html index 214f6a0bb1..e98068d486 100644 --- a/templates/listcollectorcontent.html +++ b/templates/listcollectorcontent.html @@ -1,25 +1,17 @@ -{% extends 'layouts/_questionnaire.html' %} -{% import 'macros/helpers.html' as helpers %} +{% extends "layouts/_questionnaire.html" %} -{% set save_on_signout = true %} +{% import "macros/helpers.html" as helpers %} +{% set save_on_signout = true %} {% set continue_button_text = _("Continue") %} - {% set title = content.title %} {% set contents = content.contents %} {% block form_content %} - -

      {{ title }}

      - - {%- if content.list and content.list.list_items -%} - {% set list = content.list %} -
      - {% include 'partials/summary/list-summary.html' %} -
      - {%- endif -%} - - {% include 'partials/contents.html' %} - -{% endblock %} - +

      {{ title }}

      + {%- if content.list and content.list.list_items -%} + {% set list = content.list %} +
      {% include "partials/summary/list-summary.html" %}
      + {%- endif -%} + {% include "partials/contents.html" %} +{% endblock form_content %} diff --git a/templates/listcollectordrivingquestion.html b/templates/listcollectordrivingquestion.html index 118134653c..2f61a13acd 100644 --- a/templates/listcollectordrivingquestion.html +++ b/templates/listcollectordrivingquestion.html @@ -1 +1 @@ -{% include 'question.html' %} +{% include "question.html" %} diff --git a/templates/listeditquestion.html b/templates/listeditquestion.html index d8516f06da..475fefb203 100644 --- a/templates/listeditquestion.html +++ b/templates/listeditquestion.html @@ -1 +1 @@ -{% extends 'list-action.html' %} +{% extends "list-action.html" %} diff --git a/templates/listremovequestion.html b/templates/listremovequestion.html index f905bbdde1..b1294137a4 100644 --- a/templates/listremovequestion.html +++ b/templates/listremovequestion.html @@ -1,7 +1,7 @@ -{% extends 'list-action.html' %} +{% extends "list-action.html" %} {% if content.individual_response_enabled %} - {% set show_individual_response_guidance = True %} + {% set show_individual_response_guidance = True %} {% endif %} {% block after_submit_button_content %} diff --git a/templates/listrepeatingquestion.html b/templates/listrepeatingquestion.html index d8516f06da..475fefb203 100644 --- a/templates/listrepeatingquestion.html +++ b/templates/listrepeatingquestion.html @@ -1 +1 @@ -{% extends 'list-action.html' %} +{% extends "list-action.html" %} diff --git a/templates/macros/helpers.html b/templates/macros/helpers.html index 48a2ddfb67..0db5b281f7 100644 --- a/templates/macros/helpers.html +++ b/templates/macros/helpers.html @@ -1,16 +1,16 @@ {% macro include_with(path, data, dataHandle='data') %} - {% with dataHandle = data %}{% include path %}{% endwith %} + {% with dataHandle = data %} + {% include path %} + {% endwith %} {% endmacro %} - {%- macro format_paragraphs(paragraphs) -%} - {%- for paragraph in paragraphs -%} - {%- if paragraph -%} -

      {{paragraph}}

      - {%- endif -%} - {%- else -%} - {%- endfor -%} + {%- for paragraph in paragraphs -%} + {%- if paragraph -%} +

      {{ paragraph }}

      + {%- endif -%} + {%- else -%} + {%- endfor -%} {%- endmacro -%} - {%- macro interviewer_note(title=None) -%} -{{_("Interviewer note:")}}{{ title }} + {{ _("Interviewer note:") }}{{ title }} {%- endmacro -%} diff --git a/templates/multiple_survey.html b/templates/multiple_survey.html index eb0d75ab30..8b053290da 100644 --- a/templates/multiple_survey.html +++ b/templates/multiple_survey.html @@ -1,19 +1,25 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} + {% from "components/panel/_macro.njk" import onsPanel %} {% set page_title = _("Information") %} {% block main %} -

      {{ _("Information") }}

      - - {% call onsPanel({ - "type": "error", - "spacious": true, - "attributes": { - "data-qa": "multiple-survey-error" - } - }) %} -

      {{ _("Unfortunately you can only complete one survey at a time") }}.

      -

      {{ _("Close this window to continue with your current survey") }}.

      - {% endcall %} -{% endblock %} +

      {{ _("Information") }}

      + {# djlint:off #} + {% call + onsPanel({ + "type": "error", + "spacious": true, + "attributes": { + "data-qa": "multiple-survey-error" + } + }) + %} +

      + {{ _("Unfortunately you can only complete one survey at a time") }}. +

      +

      {{ _("Close this window to continue with your current survey") }}.

      + {% endcall %} + {# djlint:on #} +{% endblock main %} diff --git a/templates/partials/answer-guidance.html b/templates/partials/answer-guidance.html index f9286bac10..e006d4f81a 100644 --- a/templates/partials/answer-guidance.html +++ b/templates/partials/answer-guidance.html @@ -1,22 +1,26 @@ {% from "components/details/_macro.njk" import onsDetails %} -{% call onsDetails({ - "id": "answer-guidance-" ~ answer.id, - "classes": "ons-u-mt-s ons-u-mb-m", - "title": _(answer_guidance.schema_item.show_guidance), - "headingAttributes": { - "data-ga": "click", - "data-ga-category": "Answer guidance", - "data-ga-action": "Open panel", - "data-ga-label": _(answer_guidance.schema_item.show_guidance), - "data-qa": "answer-guidance-" ~ answer.id ~ "-title" - }, - "contentAttributes": { - "data-qa": "answer-guidance-" ~ answer.id ~ "-content" - } -}) %} -
      - {% set content_block = answer_guidance.schema_item %} - {% include 'partials/content-block.html' %} -
      +{# djlint:off #} +{% call + onsDetails({ + "id": "answer-guidance-" ~ answer.id, + "classes": "ons-u-mt-s ons-u-mb-m", + "title": _(answer_guidance.schema_item.show_guidance), + "headingAttributes": { + "data-ga": "click", + "data-ga-category": "Answer guidance", + "data-ga-action": "Open panel", + "data-ga-label": _(answer_guidance.schema_item.show_guidance), + "data-qa": "answer-guidance-" ~ answer.id ~ "-title" + }, + "contentAttributes": { + "data-qa": "answer-guidance-" ~ answer.id ~ "-content" + } + }) +%} +
      + {% set content_block = answer_guidance.schema_item %} + {% include "partials/content-block.html" %} +
      {% endcall %} +{# djlint:on #} diff --git a/templates/partials/answer.html b/templates/partials/answer.html index 0de9ee3f49..a0666ffa99 100644 --- a/templates/partials/answer.html +++ b/templates/partials/answer.html @@ -1,34 +1,37 @@ -{% import 'macros/helpers.html' as helpers %} +{% import "macros/helpers.html" as helpers %} + {% set form = content.form %} {% set errors = form.answer_errors[answer.id] %} {% from "components/error/_macro.njk" import onsError %} {% if render_guidance != False %} - {%- set answer_guidance %} - {% if answer.guidance %} - {% with answer_guidance = { - 'id': answer.id, - 'label': answer.label, - 'schema_item': answer.guidance - } %} - {% include 'partials/answer-guidance.html' %} - {% endwith %} - {% endif %} - {% endset -%} + {# djlint:off #} + {%- set answer_guidance %} + {% if answer.guidance %} + {% with answer_guidance = { + "id": answer.id, + "label": answer.label, + "schema_item": answer.guidance + } %} + {% include "partials/answer-guidance.html" %} + {% endwith %} + {% endif %} + {% endset -%} + {# djlint:on #} {% endif %} - {% if errors | length > 0 %} - {% set error = { - "text": errors[0], - "id": answer.id ~ '-error', - "attributes": { - "data-ga": "error", - "data-ga-category": "Error", - "data-ga-action": answer.type, - "data-ga-label": question.id - } - } %} + {# djlint:off #} + {% set error = { + "text": errors[0], + "id": answer.id ~ '-error', + "attributes": { + "data-ga": "error", + "data-ga-category": "Error", + "data-ga-action": answer.type, + "data-ga-label": question.id + } + } %} + {# djlint:on #} {% endif %} - -{% include 'partials/answers/' ~ answer.type|lower ~ '.html' %} +{% include "partials/answers/" ~ answer.type|lower ~ ".html" %} {{ answer_guidance }} diff --git a/templates/partials/answers/address.html b/templates/partials/answers/address.html index d3512efcb1..382880aff5 100644 --- a/templates/partials/answers/address.html +++ b/templates/partials/answers/address.html @@ -1,7 +1,6 @@ {% from "components/address-input/_macro.njk" import onsAddressInput %} {% set address_form = form.fields[answer.id] %} - {% set config = { "id": answer.id, "dontWrap": true, @@ -23,48 +22,48 @@ "value": address_form.postcode._value() | e }, "uprn": { - "value": address_form.uprn._value() | e + "value": address_form.uprn._value() | e } } %} - +{# djlint:off #} {% if answer.lookup_options and address_lookup_api_url %} - {% set config = config | setAttributes({ - "label": { - "text": _("Enter address or postcode and select from results") - }, - "searchButton": _("Search for an address"), - "manualLinkText": _("Manually enter address"), - "isEditable": true, - "mandatory": answer.mandatory, - "APIDomain": address_lookup_api_url, - "APIDomainBearerToken": content.address_lookup_api_auth_token, - "instructions": _("Use up and down keys to navigate suggestions once you’ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), - "ariaYouHaveSelected": _("You have selected"), - "ariaMinChars": _("Enter 3 or more characters for suggestions."), - "ariaOneResult": _("There is one suggestion available."), - "ariaNResults": _("There are {n} suggestions available."), - "ariaLimitedResults": _("Results have been limited to 10 suggestions. Type more characters to improve your search"), - "ariaGroupedResults": _("There are {n} for {x}"), - "groupCount": _("{n} addresses"), - "moreResults": _("Enter more of the address to improve results"), - "resultsTitle": _("Select an address"), - "noResults": _("No results found. Try entering a different part of the address"), - "tooManyResults": _("{n} results found. Enter more of the address to improve results"), - "typeMore": _("Enter more of the address to get results"), - "autocomplete": "new-password", - "errorTitle": ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', 1), - "errorMessageEnter": _("Enter an address"), - "errorMessageSelect": _("Select or manually enter an address"), - "errorMessageAPI": _("Sorry, there was a problem loading addresses"), - "errorMessageAPILinkText": _("Enter address manually"), - "options": { - "regionCode": answer.lookup_options.region_code | lower, - "oneYearAgo": answer.lookup_options.one_year_ago if answer.lookup_options.one_year_ago is defined, - "addressType": answer.lookup_options.address_type | lower - } - }) %} + {% set config = config | setAttributes({ + "label": { + "text": _("Enter address or postcode and select from results") + }, + "searchButton": _("Search for an address"), + "manualLinkText": _("Manually enter address"), + "isEditable": true, + "mandatory": answer.mandatory, + "APIDomain": address_lookup_api_url, + "APIDomainBearerToken": content.address_lookup_api_auth_token, + "instructions": _("Use up and down keys to navigate suggestions once you’ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), + "ariaYouHaveSelected": _("You have selected"), + "ariaMinChars": _("Enter 3 or more characters for suggestions."), + "ariaOneResult": _("There is one suggestion available."), + "ariaNResults": _("There are {n} suggestions available."), + "ariaLimitedResults": _("Results have been limited to 10 suggestions. Type more characters to improve your search"), + "ariaGroupedResults": _("There are {n} for {x}"), + "groupCount": _("{n} addresses"), + "moreResults": _("Enter more of the address to improve results"), + "resultsTitle": _("Select an address"), + "noResults": _("No results found. Try entering a different part of the address"), + "tooManyResults": _("{n} results found. Enter more of the address to improve results"), + "typeMore": _("Enter more of the address to get results"), + "autocomplete": "new-password", + "errorTitle": ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', 1), + "errorMessageEnter": _("Enter an address"), + "errorMessageSelect": _("Select or manually enter an address"), + "errorMessageAPI": _("Sorry, there was a problem loading addresses"), + "errorMessageAPILinkText": _("Enter address manually"), + "options": { + "regionCode": answer.lookup_options.region_code | lower, + "oneYearAgo": answer.lookup_options.one_year_ago if answer.lookup_options.one_year_ago is defined, + "addressType": answer.lookup_options.address_type | lower + } + }) %} {% else %} - {% set config = config | setAttribute("manualEntry", true) %} + {% set config = config | setAttribute("manualEntry", true) %} {% endif %} - {{ onsAddressInput(config) }} +{# djlint:on #} diff --git a/templates/partials/answers/checkbox.html b/templates/partials/answers/checkbox.html index f485c5ea4a..599aa5d2ef 100644 --- a/templates/partials/answers/checkbox.html +++ b/templates/partials/answers/checkbox.html @@ -1,16 +1,16 @@ {% from "components/checkboxes/_macro.njk" import onsCheckboxes %} {% set config = { - "legend": answer.label, - "dontWrap": not answer.label, - "id": answer.id, - "checkboxes": map_select_config(form, answer), - "mutuallyExclusive": mutuallyExclusive, - "error": error + "legend": answer.label, + "dontWrap": not answer.label, + "id": answer.id, + "checkboxes": map_select_config(form, answer), + "mutuallyExclusive": mutuallyExclusive, + "error": error } %} - {% if answer.instruction != None and answer.options|length > 1 %} - {% set config = config | setAttribute("checkboxesLabel", answer.instruction or _("Select all that apply")) %} + {% set config = config | setAttribute("checkboxesLabel", answer.instruction or _("Select all that apply")) %} {% endif %} - +{# djlint:off #} {{ onsCheckboxes(config) }} +{# djlint:on #} diff --git a/templates/partials/answers/currency.html b/templates/partials/answers/currency.html index 7cbd7e1be6..f46c3e1bd6 100644 --- a/templates/partials/answers/currency.html +++ b/templates/partials/answers/currency.html @@ -1,27 +1,30 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer['id']] %} - -{{ onsInput({ - "id": answer.id, - "type": "number", - "name": answer.id, - "value": input._value() | e, - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "prefix": { - "id": answer.id ~ "-type", - "title": answer.currency, - "text": get_currency_symbol(answer.currency) - }, - "attributes": { - "data-qa": "input-text" - }, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "width": get_width_for_number(answer), - "error": error -}) }} +{# djlint:off #} +{{ + onsInput({ + "id": answer.id, + "type": "number", + "name": answer.id, + "value": input._value() | e, + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "prefix": { + "id": answer.id ~ "-type", + "title": answer.currency, + "text": get_currency_symbol(answer.currency) + }, + "attributes": { + "data-qa": "input-text" + }, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "width": get_width_for_number(answer), + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/date.html b/templates/partials/answers/date.html index f2ac2b1c0e..c1e1a80c92 100644 --- a/templates/partials/answers/date.html +++ b/templates/partials/answers/date.html @@ -1,64 +1,60 @@ {%- from "components/date-input/_macro.njk" import onsDateInput -%} - {%- set day_field = form.fields[answer['id']]['day'] -%} {%- set month_field = form.fields[answer['id']]['month'] -%} {%- set year_field = form.fields[answer['id']]['year'] -%} - +{# djlint:off #} {%- set config = { - "id": answer.id, - "legendOrLabel": answer.label, - "dontWrap": not answer.label, - "description": answer.description, - "attributes": { + "id": answer.id, + "legendOrLabel": answer.label, + "dontWrap": not answer.label, + "description": answer.description, + "attributes": { "data-qa": "widget-date" - }, - "mutuallyExclusive": mutuallyExclusive, - "error": error + }, + "mutuallyExclusive": mutuallyExclusive, + "error": error } -%} - {%- if day_field -%} - {%- set config = config | setAttributes({ - "day": { - "label": { - "text": _("Day"), - "attributes": { - "data-qa": "label-day" + {%- set config = config | setAttributes({ + "day": { + "label": { + "text": _("Day"), + "attributes": { + "data-qa": "label-day" + } + }, + "name": day_field.name, + "value": day_field._value() | e } - }, - "name": day_field.name, - "value": day_field._value() | e - } - }) -%} + }) -%} {%- endif -%} - {%- if month_field -%} - {%- set config = config | setAttributes({ - "month": { - "label": { - "text": _("Month"), - "attributes": { - "data-qa": "label-month" + {%- set config = config | setAttributes({ + "month": { + "label": { + "text": _("Month"), + "attributes": { + "data-qa": "label-month" + } + }, + "name": month_field.name, + "value": month_field._value() | e } - }, - "name": month_field.name, - "value": month_field._value() | e - } - }) -%} + }) -%} {%- endif -%} - {%- if year_field -%} - {%- set config = config | setAttributes({ - "year": { - "label": { - "text": _("Year"), - "attributes": { - "data-qa": "label-year" + {%- set config = config | setAttributes({ + "year": { + "label": { + "text": _("Year"), + "attributes": { + "data-qa": "label-year" + } + }, + "name": year_field.name, + "value": year_field._value() | e } - }, - "name": year_field.name, - "value": year_field._value() | e - } - }) -%} + }) -%} {%- endif -%} - {{ onsDateInput(config) }} +{# djlint:on #} diff --git a/templates/partials/answers/dropdown.html b/templates/partials/answers/dropdown.html index 3a41e089fa..de7a4c6dec 100644 --- a/templates/partials/answers/dropdown.html +++ b/templates/partials/answers/dropdown.html @@ -1,15 +1,18 @@ {% from "components/select/_macro.njk" import onsSelect %} {% set select = form.fields[answer['id']] %} - -{{ onsSelect({ - "id": answer.id, - "name": select.name, - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "options": map_dropdown_config(select), - "error": error -}) }} +{# djlint:off #} +{{ + onsSelect({ + "id": answer.id, + "name": select.name, + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "options": map_dropdown_config(select), + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/duration.html b/templates/partials/answers/duration.html index 0259ec477f..289038c629 100644 --- a/templates/partials/answers/duration.html +++ b/templates/partials/answers/duration.html @@ -1,38 +1,37 @@ {% from "components/duration/_macro.njk" import onsDuration %} {% set config = { - "id": answer.id, - "dontWrap": not answer.label, - "mutuallyExclusive": mutuallyExclusive, - "error": error, - "legendOrLabel": answer.label + "id": answer.id, + "dontWrap": not answer.label, + "mutuallyExclusive": mutuallyExclusive, + "error": error, + "legendOrLabel": answer.label } %} - {% set years = form.fields[answer.id].years %} {% set months = form.fields[answer.id].months %} - +{# djlint:off #} {% if years %} - {% set config = config | setAttribute("field1", { - "id": years.id, - "name": years.name, - "value": years.data if years.data is not none else '', - "suffix": { - "id": years.id ~ "-type", - "text": _(years.label.text) - } - }) %} + {% set config = config | setAttribute("field1", { + "id": years.id, + "name": years.name, + "value": years.data if years.data is not none else '', + "suffix": { + "id": years.id ~ "-type", + "text": _(years.label.text) + } + }) %} {% endif %} - {% if months %} - {% set config = config | setAttribute("field2", { - "id": months.id, - "name": months.name, - "value": months.data if months.data is not none else '', - "suffix": { - "id": months.id ~ "-type", - "text": _(months.label.text) - } - }) %} -{% endif %} + {% set config = config | setAttribute("field2", { + "id": months.id, + "name": months.name, + "value": months.data if months.data is not none else '', + "suffix": { + "id": months.id ~ "-type", + "text": _(months.label.text) + } + }) %} +{% endif %} {{ onsDuration(config) }} +{# djlint:on #} diff --git a/templates/partials/answers/mobilenumber.html b/templates/partials/answers/mobilenumber.html index 4d00ea3424..d2c06ceaf4 100644 --- a/templates/partials/answers/mobilenumber.html +++ b/templates/partials/answers/mobilenumber.html @@ -1,23 +1,23 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer.id] if input is undefined else input %} - {% set config = { - "id": answer.id, - "name": input.name, - "type": "tel", - "autocomplete": "tel", - "width": "15", - "value": input._value() | e, - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "attributes": { - "data-qa": "input-text" - }, - "error": error + "id": answer.id, + "name": input.name, + "type": "tel", + "autocomplete": "tel", + "width": "15", + "value": input._value() | e, + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "attributes": { + "data-qa": "input-text" + }, + "error": error } %} - +{# djlint:off #} {{ onsInput(config) }} +{# djlint:on #} diff --git a/templates/partials/answers/monthyeardate.html b/templates/partials/answers/monthyeardate.html index 8cfd76628e..43599a6b96 100644 --- a/templates/partials/answers/monthyeardate.html +++ b/templates/partials/answers/monthyeardate.html @@ -1 +1 @@ -{% include 'partials/answers/date.html' %} +{% include "partials/answers/date.html" %} diff --git a/templates/partials/answers/number.html b/templates/partials/answers/number.html index 899d1533bb..8782cbfc3d 100644 --- a/templates/partials/answers/number.html +++ b/templates/partials/answers/number.html @@ -1,22 +1,25 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer['id']] %} - -{{ onsInput({ - "id": answer.id, - "type": "number", - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "value": input._value() | e, - "name": answer.id, - "attributes": { - "data-qa": "input-text" - }, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "width": get_width_for_number(answer), - "error": error -}) }} +{# djlint:off #} +{{ + onsInput({ + "id": answer.id, + "type": "number", + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "value": input._value() | e, + "name": answer.id, + "attributes": { + "data-qa": "input-text" + }, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "width": get_width_for_number(answer), + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/percentage.html b/templates/partials/answers/percentage.html index bf1683d254..942d9f0bfa 100644 --- a/templates/partials/answers/percentage.html +++ b/templates/partials/answers/percentage.html @@ -1,28 +1,31 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer['id']] %} - -{{ onsInput({ - "id": answer.id, - "classes": "js-totaliser-input-calculated" if answer.calculated, - "type": "number", - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "value": input._value() | e, - "name": input.name, - "attributes": { - "data-qa": "input-text" - }, - "suffix": { - "id": answer.id ~ "-type", - "title": "Percent", - "text": "%" - }, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "width": get_width_for_number(answer), - "error": error -}) }} +{# djlint:off #} +{{ + onsInput({ + "id": answer.id, + "classes": "js-totaliser-input-calculated" if answer.calculated, + "type": "number", + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "value": input._value() | e, + "name": input.name, + "attributes": { + "data-qa": "input-text" + }, + "suffix": { + "id": answer.id ~ "-type", + "title": "Percent", + "text": "%" + }, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "width": get_width_for_number(answer), + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/radio.html b/templates/partials/answers/radio.html index ef7cd5f995..a3e8a44bca 100644 --- a/templates/partials/answers/radio.html +++ b/templates/partials/answers/radio.html @@ -1,22 +1,21 @@ {% from "components/radios/_macro.njk" import onsRadios %} - {% set config = { - "id": answer.id, - "name": answer.id, - "legend": answer.label, - "dontWrap": not answer.label, - "radios": map_select_config(form, answer), - "error": error + "id": answer.id, + "name": answer.id, + "legend": answer.label, + "dontWrap": not answer.label, + "radios": map_select_config(form, answer), + "error": error } %} - +{# djlint:off #} {% if answer.voluntary %} - {% set config = config | setAttribute("clearRadios", { - "text": _("Clear selection"), - "name": "action[clear_radios]", - "ariaClearText": "You can clear your answer using the clear selection button after the radio inputs", - "ariaClearedText": "You have cleared your answer" - }) %} + {% set config = config | setAttribute("clearRadios", { + "text": _("Clear selection"), + "name": "action[clear_radios]", + "ariaClearText": "You can clear your answer using the clear selection button after the radio inputs", + "ariaClearedText": "You have cleared your answer" + }) %} {% endif %} - {{ onsRadios(config) }} +{# djlint:off #} diff --git a/templates/partials/answers/relationship.html b/templates/partials/answers/relationship.html index 268f9c92ea..1aebf82d5b 100644 --- a/templates/partials/answers/relationship.html +++ b/templates/partials/answers/relationship.html @@ -1,10 +1,14 @@ {% from "components/relationships/_macro.njk" import onsRelationships %} -{{ onsRelationships({ - "id": answer.id, - "name": answer.id, - "dontWrap": true, - "playback": answer["playback"], - "radios": map_relationships_config(form, answer), - "error": error -}) }} +{# djlint:off #} +{{ + onsRelationships({ + "id": answer.id, + "name": answer.id, + "dontWrap": true, + "playback": answer["playback"], + "radios": map_relationships_config(form, answer) , + "error": error + }) +}} +{# djlint:oN #} diff --git a/templates/partials/answers/textarea.html b/templates/partials/answers/textarea.html index 0bec0a1864..a101f2f474 100644 --- a/templates/partials/answers/textarea.html +++ b/templates/partials/answers/textarea.html @@ -1,27 +1,29 @@ {% from "components/textarea/_macro.njk" import onsTextarea %} {% set input = form.fields[answer['id']] %} - +{# djlint:off #} {% if answer.label %} - {% set label = { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - } %} + {% set label = { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + } %} {% endif %} - -{{ onsTextarea({ - "id": answer.id, - "name": input.name, - "label": label, - "value": input._value() | e, - "charCheckLimit": { - "limit": answer.max_length | default(input.maxlength, true), - "charCountSingular": _("You have {x} character remaining"), - "charCountPlural": _("You have {x} characters remaining") - }, - "rows": answer.rows, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "error": error -}) }} +{{ + onsTextarea({ + "id": answer.id, + "name": input.name, + "label": label, + "value": input._value() | e, + "charCheckLimit": { + "limit": answer.max_length | default(input.maxlength, true), + "charCountSingular": _("You have {x} character remaining"), + "charCountPlural": _("You have {x} characters remaining") + }, + "rows": answer.rows, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/textfield.html b/templates/partials/answers/textfield.html index 5d44ce1fda..d4ca8163ea 100644 --- a/templates/partials/answers/textfield.html +++ b/templates/partials/answers/textfield.html @@ -2,51 +2,49 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer.id] if input is undefined else input %} - {% set config = { - "id": answer.id, - "name": input.name, - "value": input._value() | e, - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "attributes": { - "data-qa": "input-text" - }, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "error": error + "id": answer.id, + "name": input.name, + "value": input._value() | e, + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "attributes": { + "data-qa": "input-text" + }, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "error": error } %} - {% if answer.suggestions %} - {% set config = config | setAttributes({ - "instructions": _("Use up and down keys to navigate suggestions once you\'ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), - "moreResults": _("Continue entering to improve suggestions"), - "resultsTitle": _("Suggestions"), - "noResults": _("No results found"), - "typeMore": _("Continue entering to get suggestions"), - "ariaYouHaveSelected": "You have selected", - "ariaMinChars": "Enter 3 or more characters for suggestions.", - "ariaOneResult": "There is one suggestion available.", - "ariaNResults": "There are {n} suggestions available.", - "ariaLimitedResults": "Results have been limited to 10 suggestions. Type more characters to improve your search.", - "autosuggestData": answer.suggestions.url, - "allowMultiple": true if answer.suggestions.allow_multiple else false - }) %} - {{ onsAutosuggest(config) }} -{% else %} - {% if answer.max_length %} - {% set config = config | setAttribute("charCheckLimit", { - "limit": answer.max_length, - "charCountOverLimitSingular": _("{x} character too many"), - "charCountOverLimitPlural": _("{x} characters too many"), - "charCountSingular": _("You have {x} character remaining"), - "charCountPlural": _("You have {x} characters remaining") + {# djlint:off #} + {% set config = config | setAttributes({ + "instructions": _("Use up and down keys to navigate suggestions once you\'ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), + "moreResults": _("Continue entering to improve suggestions"), + "resultsTitle": _("Suggestions"), + "noResults": _("No results found"), + "typeMore": _("Continue entering to get suggestions"), + "ariaYouHaveSelected": "You have selected", + "ariaMinChars": "Enter 3 or more characters for suggestions.", + "ariaOneResult": "There is one suggestion available.", + "ariaNResults": "There are {n} suggestions available.", + "ariaLimitedResults": "Results have been limited to 10 suggestions. Type more characters to improve your search.", + "autosuggestData": answer.suggestions.url, + "allowMultiple": true if answer.suggestions.allow_multiple else false }) %} - {% endif %} - {{ onsInput(config) }} + {{ onsAutosuggest(config) }} +{% else %} + {% if answer.max_length %} + {% set config = config | setAttribute("charCheckLimit", { + "limit": answer.max_length, + "charCountOverLimitSingular": _("{x} character too many"), + "charCountOverLimitPlural": _("{x} characters too many"), + "charCountSingular": _("You have {x} character remaining"), + "charCountPlural": _("You have {x} characters remaining") + }) %} + {% endif %} + {{ onsInput(config) }} + {# djlint:on #} {% endif %} - - diff --git a/templates/partials/answers/unit.html b/templates/partials/answers/unit.html index cf21db83f2..2c520adbee 100644 --- a/templates/partials/answers/unit.html +++ b/templates/partials/answers/unit.html @@ -1,28 +1,31 @@ {% from "components/input/_macro.njk" import onsInput %} {% set input = form.fields[answer['id']] %} - -{{ onsInput({ - "id": answer.id, - "type": "number", - "classes": "js-totaliser-input-calculated" if answer.calculated, - "name": input.name, - "value": input._value() | e, - "label": { - "id": answer.id ~ "-label", - "text": answer.label, - "description": answer.description - }, - "suffix": { - "id": answer.id ~ "-type", - "title": format_unit_input_label(answer.unit, unit_length="long"), - "text": format_unit_input_label(answer.unit, unit_length=answer.unit_length) - }, - "attributes": { - "data-qa": "input-text" - }, - "dontWrap": true if mutuallyExclusive else false, - "mutuallyExclusive": mutuallyExclusive, - "width": get_width_for_number(answer), - "error": error -}) }} +{# djlint:off #} +{{ + onsInput({ + "id": answer.id, + "type": "number", + "classes": "js-totaliser-input-calculated" if answer.calculated, + "name": input.name, + "value": input._value() | e, + "label": { + "id": answer.id ~ "-label", + "text": answer.label, + "description": answer.description + }, + "suffix": { + "id": answer.id ~ "-type", + "title": format_unit_input_label(answer.unit, unit_length="long"), + "text": format_unit_input_label(answer.unit, unit_length=answer.unit_length) + }, + "attributes": { + "data-qa": "input-text" + }, + "dontWrap": true if mutuallyExclusive else false, + "mutuallyExclusive": mutuallyExclusive, + "width": get_width_for_number(answer), + "error": error + }) +}} +{# djlint:on #} diff --git a/templates/partials/answers/yeardate.html b/templates/partials/answers/yeardate.html index 8cfd76628e..43599a6b96 100644 --- a/templates/partials/answers/yeardate.html +++ b/templates/partials/answers/yeardate.html @@ -1 +1 @@ -{% include 'partials/answers/date.html' %} +{% include "partials/answers/date.html" %} diff --git a/templates/partials/block.html b/templates/partials/block.html index c937050913..714e2df4c4 100644 --- a/templates/partials/block.html +++ b/templates/partials/block.html @@ -1,10 +1,7 @@ {% set block = content.block %} - -
      - - {% if 'question' in block %} - {% set question = block['question'] %} - {% include 'partials/question.html' %} - {% endif %} - +
      + {% if 'question' in block %} + {% set question = block['question'] %} + {% include "partials/question.html" %} + {% endif %}
      diff --git a/templates/partials/confirmation-email-form.html b/templates/partials/confirmation-email-form.html index 5900890b60..0a600b76fb 100644 --- a/templates/partials/confirmation-email-form.html +++ b/templates/partials/confirmation-email-form.html @@ -1,49 +1,50 @@ {% from "components/button/_macro.njk" import onsButton %} {% from "components/input/_macro.njk" import onsInput %} + {% set errors = form.errors['email'] %} {% set email_field = form.email_field %} - {% if errors %} - {% set error = { - "text": errors[0], - "id": email_field.id ~ '-error', - "attributes": { - "data-ga": "error", - "data-ga-category": "Error", - "data-ga-action": "Confirmation Email", - "data-ga-label": email_field.id - } - } %} + {# djlint:off #} + {% set error = { + "text": errors[0], + "id": email_field.id ~ '-error', + "attributes": { + "data-ga": "error", + "data-ga-category": "Error", + "data-ga-action": "Confirmation Email", + "data-ga-label": email_field.id + } + } %} + {# djlint:on #} {% endif %} - {% set config = { - "id": email_field.id, - "name": email_field.name, - "value": email_field._value() | e, - "label": { - "id": email_field.id ~ "-label", - "text": _("Email address"), - "description": _("This will not be stored and only used once to send your confirmation") - }, - "attributes": { - "data-qa": "input-text", - }, - "error": error + "id": email_field.id, + "name": email_field.name, + "value": email_field._value() | e, + "label": { + "id": email_field.id ~ "-label", + "text": _("Email address"), + "description": _("This will not be stored and only used once to send your confirmation") + }, + "attributes": { + "data-qa": "input-text", + }, + "error": error } %} - +{# djlint:off #} {{ onsInput(config) }} - {{ - onsButton({ - "text": _("Send confirmation"), - "variants": 'timer', - "classes": "ons-u-mt-s", - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Send confirmation", - "data-ga-label": "Send confirmation button click", - "data-ga": "click" - } - }) + onsButton({ + "text": _("Send confirmation"), + "variants": 'timer', + "classes": "ons-u-mt-s", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Send confirmation", + "data-ga-label": "Send confirmation button click", + "data-ga": "click" + } + }) }} +{# djlint:on #} diff --git a/templates/partials/content-block.html b/templates/partials/content-block.html index 54934eaee1..5e43c6581c 100644 --- a/templates/partials/content-block.html +++ b/templates/partials/content-block.html @@ -1,7 +1,5 @@ -{% import 'macros/helpers.html' as helpers %} +{% import "macros/helpers.html" as helpers %} -{%- if content_block.title -%} - {{content_block.title}} -{% endif %} +{%- if content_block.title -%}{{ content_block.title }}{% endif %} {% set contents = content_block.contents %} -{% include 'partials/contents.html' %} +{% include "partials/contents.html" %} diff --git a/templates/partials/contents.html b/templates/partials/contents.html index 6fcb6730e9..e24699425a 100644 --- a/templates/partials/contents.html +++ b/templates/partials/contents.html @@ -1,36 +1,27 @@ {% set definition_count = namespace(value=1) %} {% set guidance_count = namespace(value=1) %} - {% for item in contents %} {%- if item.definition -%} {% set definition = item.definition %} - {% set definition_id = 'definition' %} - {% set category = 'Definition' %} + {% set definition_id = "definition" %} + {% set category = "Definition" %} {%- include 'partials/definition.html' -%} {% set definition_count.value = definition_count.value + 1 %} {%- endif -%} - {%- if item.guidance -%} {% set guidance = item.guidance %} {% set guidance_id = 'guidance-' ~ guidance_count.value %} {%- include 'partials/guidance.html' -%} {% set guidance_count.value = guidance_count.value + 1 %} {%- endif -%} - - {%- if item.title -%} -

      {{ item.title }}

      - {% endif %} - - {%- if item.description -%} -

      {{ item.description }}

      - {% endif %} - + {%- if item.title -%}

      {{ item.title }}

      {% endif %} + {%- if item.description -%}

      {{ item.description }}

      {% endif %} {%- if item.list -%}
        {%- for list_item in item.list -%} - {%- if list_item|length > 0 -%} -
      • {{ list_item }}
      • - {%- endif -%} + {%- if list_item|length > 0 -%} +
      • {{ list_item }}
      • + {%- endif -%} {% endfor %}
      {% endif %} diff --git a/templates/partials/definition.html b/templates/partials/definition.html index 3b00c4fca6..3f29df0d02 100644 --- a/templates/partials/definition.html +++ b/templates/partials/definition.html @@ -1,20 +1,24 @@ {% from "components/details/_macro.njk" import onsDetails %} -{% call onsDetails({ - "id": definition_id, - "title": definition.title, - "classes": "ons-u-mb-s", - "headingAttributes": { - "data-ga": "click", - "data-ga-category": category, - "data-ga-action": "Open panel", - "data-ga-label": definition.title, - "data-qa": definition_id ~ "-title" - }, - "contentAttributes": { - "data-qa": definition_id ~ "-content" - } -}) %} - {% set contents = definition.contents %} - {% include 'partials/contents.html' %} +{# djlint:off #} +{% call + onsDetails({ + "id": definition_id, + "title": definition.title, + "classes": "ons-u-mb-s", + "headingAttributes": { + "data-ga": "click", + "data-ga-category": category, + "data-ga-action": "Open panel", + "data-ga-label": definition.title, + "data-qa": definition_id ~ "-title" + }, + "contentAttributes": { + "data-qa": definition_id ~ "-content" + } + }) + %} + {% set contents = definition.contents %} + {% include "partials/contents.html" %} {% endcall %} +{# djlint:on #} diff --git a/templates/partials/error-panel.html b/templates/partials/error-panel.html index 03136914e7..1fdb8e96c9 100644 --- a/templates/partials/error-panel.html +++ b/templates/partials/error-panel.html @@ -2,34 +2,35 @@ {% from "components/list/_macro.njk" import onsList %} {% set error_list = [] %} +{# djlint:off #} {% for error_id, error in form.mapped_errors %} - {{ error_list.append( { - "text": error, - "url": "#" + error_id, - "variants": "inPageLink", - "attributes": { - "data-qa": "error-link-" + loop.index|string - } - } ) or "" }} + {{ error_list.append({ + "text": error, + "url": "#" + error_id, + "variants": "inPageLink", + "attributes": { + "data-qa": "error-link-" + loop.index|string + } + }) or "" }} {% endfor %} - {% call - onsPanel({ - "type": "error", - "classes": "ons-u-mb-s", - "title": error_title, - "attributes": { - "data-qa": "error-body" - } - }) -%} - {{ - onsList({ - "element": "ol", - "attributes": { - "data-qa": "error-list" - }, - "itemsList": error_list + onsPanel({ + "type": "error", + "classes": "ons-u-mb-s", + "title": error_title, + "attributes": { + "data-qa": "error-body" + } }) - }} +%} + {{ + onsList({ + "element": "ol", + "attributes": { + "data-qa": "error-list" + }, + "itemsList": error_list + }) + }} {% endcall %} +{# djlint:on #} diff --git a/templates/partials/feedback-call-to-action.html b/templates/partials/feedback-call-to-action.html index 8595648c41..4186065279 100644 --- a/templates/partials/feedback-call-to-action.html +++ b/templates/partials/feedback-call-to-action.html @@ -1,5 +1,6 @@ -{% from 'components/feedback/_macro.njk' import onsFeedback %} +{% from "components/feedback/_macro.njk" import onsFeedback %} +{# djlint:off #} {{ onsFeedback({ "id": "feedback", @@ -9,3 +10,4 @@ "linkText": _("Give feedback") }) }} +{# djlint:on #} diff --git a/templates/partials/guidance.html b/templates/partials/guidance.html index 90e0e9a593..e04f4682eb 100644 --- a/templates/partials/guidance.html +++ b/templates/partials/guidance.html @@ -1,12 +1,16 @@ {% from "components/panel/_macro.njk" import onsPanel %} -{% call onsPanel({ - "id": guidance_id, - "classes": "ons-u-mb-m", - "attributes": { - "data-qa": guidance_id - } -}) %} +{# djlint:off #} +{% call + onsPanel({ + "id": guidance_id, + "classes": "ons-u-mb-m", + "attributes": { + "data-qa": guidance_id + } + }) +%} {% set contents = guidance.contents %} - {% include 'partials/contents.html' %} + {% include "partials/contents.html" %} {% endcall %} +{# djlint:on #} diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index 6c2a1a81d6..6bdcb36b74 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -1,17 +1,25 @@ {% from "components/details/_macro.njk" import onsDetails %} -{% call onsDetails({ - "classes": "ons-u-mt-s", - "title": title, - "headingAttributes": { - "data-ga": "click", - "data-ga-category": "definition", - "data-ga-action": "Open panel", - "data-ga-label": title - } -}) %} -
      -

      {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }}

      -

      {{ _("If this is not possible, there are other ways each person can complete their own census.").format(url=content.individual_response_url)}}

      -
      +{# djlint:off #} +{% call + onsDetails({ + "classes": "ons-u-mt-s", + "title": title, + "headingAttributes": { + "data-ga": "click", + "data-ga-category": "definition", + "data-ga-action": "Open panel", + "data-ga-label": title + } + }) +%} +
      +

      + {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }} +

      +

      + {{ _("If this is not possible, there are other ways each person can complete their own census.").format(url=content.individual_response_url) }} +

      +
      {% endcall %} +{# djlint:on #} diff --git a/templates/partials/introduction/basic.html b/templates/partials/introduction/basic.html index 50a9ba5c57..6c0d99afa4 100644 --- a/templates/partials/introduction/basic.html +++ b/templates/partials/introduction/basic.html @@ -1,7 +1,5 @@
      - {% if content_block.title %} - <{{ title_tag }}>{{ content_block.title }} - {% endif %} + {% if content_block.title %}<{{ title_tag }}>{{ content_block.title }}{% endif %} {% set contents = content_block.contents %} - {% include 'partials/contents.html' %} + {% include "partials/contents.html" %}
      diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index 5e96a6ad5d..5c0669fea9 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -1,49 +1,45 @@

      {{ intro.title }}

      -
      - {% set contents = intro.contents %} - {% include "partials/contents.html" %} + {% set contents = intro.contents %} + {% include "partials/contents.html" %}
      - {% if intro.questions %} - {% from "components/accordion/_macro.njk" import onsAccordion %} - - {% set accordionItems = [] %} - - {% for question in intro.questions %} - {% set content %} - {% set contents = question.contents %} - {% include "partials/contents.html" %} - {% endset %} + {% from "components/accordion/_macro.njk" import onsAccordion %} - {% set item = { - "title": question.question, - "titleTag": "h3", - "content": content, - "summaryAttributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Open panel", - } - } %} - - {% do accordionItems.append(item) %} - {% endfor %} - - {{ - onsAccordion({ - "id": "intro-questions", - "allButton": { - "open": _('Show all'), + {% set accordionItems = [] %} + {# djlint:off #} + {% for question in intro.questions %} + {% set content %} + {% set contents = question.contents %} + {% include "partials/contents.html" %} + {% endset %} + {% set item = { + "title": question.question, + "titleTag": "h3", + "content": content, + "summaryAttributes": { + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Open panel", + } + } %} + {% do accordionItems.append(item) %} + {% endfor %} + {{ + onsAccordion({ + "id": "intro-questions", + "allButton": { + "open": _('Show all') , "close": _('Hide all'), "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Show all", + "data-ga-label": "Show all" } - }, - "itemsList": accordionItems - }) - }} + }, + "itemsList": accordionItems + }) + }} + {# djlint:on #} {% endif %} diff --git a/templates/partials/introduction/start-survey.html b/templates/partials/introduction/start-survey.html index 9860359a9a..a74e37fe2f 100644 --- a/templates/partials/introduction/start-survey.html +++ b/templates/partials/introduction/start-survey.html @@ -1,17 +1,19 @@ {% from "components/button/_macro.njk" import onsButton %} +{# djlint:off #} {{ - onsButton({ - "text": _("Start survey"), - "variants": 'timer', - "classes": "qa-btn-get-started", - "name": "action[start_questionnaire]", - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Start survey", - "data-ga-label": "Start button click", - "data-ga": "click" - } - }) + onsButton({ + "text": _("Start survey"), + "variants": 'timer', + "classes": "qa-btn-get-started", + "name": "action[start_questionnaire]", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Start survey", + "data-ga-label": "Start button click", + "data-ga": "click" + } + }) }} +{# djlint:on #} diff --git a/templates/partials/last_viewed_question_guidance.html b/templates/partials/last_viewed_question_guidance.html index c581c45991..3175d38560 100644 --- a/templates/partials/last_viewed_question_guidance.html +++ b/templates/partials/last_viewed_question_guidance.html @@ -1,11 +1,14 @@ {% from "components/panel/_macro.njk" import onsPanel %} -{% call onsPanel({ - "id": "last-viewed-question-guidance", - "classes": "ons-u-mb-m" - }) %} - - {{ _("This is the last viewed question in this section") }} - -
      -{{ _("You can also go back to the start of the section").format(url = last_viewed_question_guidance.first_location_in_section_url)}} + +{# djlint:off #} +{% call + onsPanel({ + "id": "last-viewed-question-guidance", + "classes": "ons-u-mb-m" + }) +%} + {{ _("This is the last viewed question in this section") }} +
      + {{ _("You can also go back to the start of the section").format(url = last_viewed_question_guidance.first_location_in_section_url) }} {% endcall %} +{# djlint:on #} diff --git a/templates/partials/preview-question.html b/templates/partials/preview-question.html index 0d6b2d2a3b..5980e3dc34 100644 --- a/templates/partials/preview-question.html +++ b/templates/partials/preview-question.html @@ -1,72 +1,65 @@ -{% from 'components/panel/_macro.njk' import onsPanel %} -{% from 'macros/helpers.html' import format_paragraphs %} +{% from "components/panel/_macro.njk" import onsPanel %} +{% from "macros/helpers.html" import format_paragraphs %} {% set answers = question.answers %} -
      - -

      {{ question.title }}

      - - {% set answers_length = answers | length %} - - {% if question.descriptions %} - {% set descriptions = question.descriptions %} - {% set descriptions_length = descriptions | length %} - - {% for description in descriptions %} -
      {{- description | safe -}}
      - {% endfor %} - {% endif %} - - {%- if question.guidance -%} - {% set contents = question.guidance.contents %} - {% call onsPanel({ - "id": "question-guidance-" ~ question.id, - "classes": "ons-u-mb-m" - }) %} - {% include 'partials/contents.html' %} - {% endcall %} - {% endif %} - - {% for answer in answers %} - - {% if loop.last and question.type == "MutuallyExclusive" %} -

      {{ _("Or") }}

      - {% endif %} - - {% if not loop.last or (answer.options and answers_length == 1) %} -

      {{ answer.options_text }}

      - {% endif %} - - {% if answer.options %} - {% if answer.label %} -

      {{- answer.label | safe -}}

      - {% endif %} -
        - {% for option in answer.options %} -
      • {{ option }}
      • +

        {{ question.title }}

        + {% set answers_length = answers | length %} + {% if question.descriptions %} + {% set descriptions = question.descriptions %} + {% set descriptions_length = descriptions | length %} + {% for description in descriptions %} +
        {{- description | safe -}}
        {% endfor %} -
      - {% else %} - {% if answer.label %} -

      {{- answer.label | safe -}}

      - {% endif %} - {% if answer.max_length %} -

      {{ _("{max_characters} characters can be added.").format(max_characters = answer.max_length) }}

      - {% endif %} - {% endif %} - - {# answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 #} - {# {% if answer.guidance %} + {% endif %} + {%- if question.guidance -%} + {% set contents = question.guidance.contents %} + {# djlint:off #} + {% call + onsPanel({ + "id": "question-guidance-" ~ question.id, + "classes": "ons-u-mb-m" + }) + %} + {% include "partials/contents.html" %} + {% endcall %} + {# djlint:on #} + {% endif %} + {% for answer in answers %} + {% if loop.last and question.type == "MutuallyExclusive" %} +

      + {{ _("Or") }} +

      + {% endif %} + {% if not loop.last or (answer.options and answers_length == 1) %}

      {{ answer.options_text }}

      {% endif %} + {% if answer.options %} + {% if answer.label %} +

      + {{- answer.label | safe -}} +

      + {% endif %} +
        + {% for option in answer.options %}
      • {{ option }}
      • {% endfor %} +
      + {% else %} + {% if answer.label %} +

      + {{- answer.label | safe -}} +

      + {% endif %} + {% if answer.max_length %} +

      {{ _("{max_characters} characters can be added.").format(max_characters = answer.max_length) }}

      + {% endif %} + {% endif %} + {# answer guidance not implemented yet due to some work that needs to be done in the DS will be implemented in iteration 2 #} + {# {% if answer.guidance %} {% with answer_guidance = { 'id': answer.id, 'label': answer.label, 'schema_item': answer.guidance } %} - {% include 'partials/answer-guidance.html' %} + {% include "partials/answer-guidance.html" %} {% endwith %} - {% endif %} #} - - {% endfor %} - +{% endif %} #} + {% endfor %}
      diff --git a/templates/partials/question.html b/templates/partials/question.html index d0207cfa28..e4d5dee71b 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -1,148 +1,137 @@ {% from "components/question/_macro.njk" import onsQuestion %} {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/error/_macro.njk" import onsError %} - -{% from 'macros/helpers.html' import format_paragraphs %} -{% from 'macros/helpers.html' import interviewer_note %} +{% from "macros/helpers.html" import format_paragraphs %} +{% from "macros/helpers.html" import interviewer_note %} {% set form = content.form %} {% set display = namespace(guidance=False) %} - -{% set title= interviewer_note(question.title) if block.interviewer_only else question.title %} -{% set question_title= question.title %} +{% set title = interviewer_note(question.title) if block.interviewer_only else question.title %} +{% set question_title = question.title %} {% set question_description = format_paragraphs(question.description) %} {% set question_instruction = format_paragraphs(question.instruction) %} {% set question_error = form.question_errors[question.id] %} - {%- if question.definition -%} - {% set definition_id = "question-definition" %} - {% set definition_content %} - {% set contents = question.definition.contents %} - {% include 'partials/contents.html' %} - {% endset %} - {% set question_definition = { - "title": question.definition.title, - "id": definition_id, - "content": definition_content, - "headingAttributes": { - "data-ga": "click", - "data-ga-category": "Question definition", - "data-ga-action": "Open panel", - "data-ga-label": question.definition.title, - "data-qa": definition_id ~ "-title" - }, - "contentAttributes": { - "data-qa": definition_id ~ "-content" - } - } %} + {% set definition_id = "question-definition" %} + {% set definition_content %} + {% set contents = question.definition.contents %} + {% include "partials/contents.html" %} + {% endset %} + {% set question_definition = { + "title": question.definition.title, + "id": definition_id, + "content": definition_content, + "headingAttributes": { + "data-ga": "click", + "data-ga-category": "Question definition", + "data-ga-action": "Open panel", + "data-ga-label": question.definition.title, + "data-qa": definition_id ~ "-title" + }, + "contentAttributes": { + "data-qa": definition_id ~ "-content" + } + } %} {% elif question.definitions %} - {%- set question_definitions -%} - {% for definition in question.definitions %} - {% set definition_id = "question-definition" %} - {% set category = 'Question definition' %} - {%- include 'partials/definition.html' -%} - {% endfor %} - {%- endset -%} + {%- set question_definitions -%} + {% for definition in question.definitions %} + {% set definition_id = "question-definition" %} + {% set category = "Question definition" %} + {%- include 'partials/definition.html' -%} + {% endfor %} + {%- endset -%} {%- endif -%} - {% set individual_response_guidance %} - {%- if show_individual_response_guidance == True -%} - {% set title = _("If you can’t answer questions for this person") %} - {% include 'partials/individual-response-guidance.html' %} - {%- endif -%} + {%- if show_individual_response_guidance == True -%} + {% set title = _("If you can’t answer questions for this person") %} + {% include "partials/individual-response-guidance.html" %} + {%- endif -%} {% endset %} - {%- if question.warning -%} - {% set question_warning = {"body": question.warning} %} + {% set question_warning = {"body": question.warning} %} {% endif %} - {% set question_guidance %} - {%- if question.guidance -%} - {% set contents = question.guidance.contents %} - {% for item in contents %} - {%- if (item['title'], item['description'], item['list'], item['definition'], item['guidance'])|select|first -%} - {% set display.guidance=True %} - {%- endif -%} - {% endfor %} - {%- if display.guidance -%} - {% call onsPanel({ - "id": "question-guidance-" ~ question.id, - "classes": "ons-u-mb-m" - }) %} - {% include 'partials/contents.html' %} - {% endcall %} - {%- endif -%} - {% endif %} + {%- if question.guidance -%} + {% set contents = question.guidance.contents %} + {% for item in contents %} + {%- if (item['title'], item['description'], item['list'], item['definition'], item['guidance'])|select|first -%} + {% set display.guidance = True %} + {%- endif -%} + {% endfor %} + {%- if display.guidance -%} + {# djlint:off #} + {% call onsPanel({ + "id": "question-guidance-" ~ question.id, + "classes": "ons-u-mb-m" + }) %} + {% include "partials/contents.html" %} + {% endcall %} + {# djlint:on #} + {%- endif -%} + {% endif %} {% endset %} - {%- set mutually_exclusive_question = question.type == 'MutuallyExclusive' -%} - {% set question_answers %} - {% if mutually_exclusive_question %} - {%- set answer = question.answers[0] -%} - - {%- set deselectionMessage = _("Selecting this will clear your answer") -%} - {%- set deselectGroupAdjective = _("cleared") -%} - - {%- if answer.type == 'checkbox' -%} - {%- set deselectionMessage = _("Selecting this will deselect any selected options") -%} - {%- set deselectGroupAdjective = _("deselected") -%} + {% if mutually_exclusive_question %} + {%- set answer = question.answers[0] -%} + {%- set deselectionMessage = _("Selecting this will clear your answer") -%} + {%- set deselectGroupAdjective = _("cleared") -%} + {%- if answer.type == 'checkbox' -%} + {%- set deselectionMessage = _("Selecting this will deselect any selected options") -%} + {%- set deselectGroupAdjective = _("deselected") -%} + {%- endif -%} + {# djlint:off #} + {%- set mutuallyExclusive = { + "or": _("Or"), + "exclusiveOptions": map_select_config(form, question.answers[-1]), + "deselectionMessage": deselectionMessage, + "deselectGroupAdjective": deselectGroupAdjective, + "deselectExclusiveOptionAdjective": _("deselected") + } -%} + {# djlint:on #} + {% include "partials/answer.html" %} + {%- else -%} + {%- set answers -%} + {%- for answer in question.answers -%} + {% include "partials/answer.html" %} + {%- endfor -%} + {%- endset -%} + {{ answers }} {%- endif -%} - - {%- set mutuallyExclusive = { - "or": _("Or"), - "exclusiveOptions": map_select_config(form, question.answers[-1]), - "deselectionMessage": deselectionMessage, - "deselectGroupAdjective": deselectGroupAdjective, - "deselectExclusiveOptionAdjective": _("deselected") - } -%} - - {% include 'partials/answer.html' %} - {%- else -%} - {%- set answers -%} - {%- for answer in question.answers -%} - {% include 'partials/answer.html' %} - {%- endfor -%} - {%- endset -%} - {{ answers }} - {%- endif -%} {% endset %} - {% call onsQuestion({ - "id": question.id, - "title": title, - "description": question_description, - "instruction": question_instruction, - "warning": question_warning, - "definition": question_definition, - "legendIsQuestionTitle": should_wrap_with_fieldset(question) -}) %} - {%- if content.list and content.list.list_items -%} - {% set list = content.list %} -
      - {% include 'partials/summary/list-summary.html' %} -
      - {% endif %} - {{ individual_response_guidance }} - {{ question_definitions }} - {{ question_guidance }} - - {% if question_error %} - {% set config = { - "text": question_error, - "id": question.id ~ '-error', - "attributes": { - "data-ga": "question-error", - "data-ga-category": "Question error", - "data-ga-action": question.type, - "data-ga-label": question.id - } - } %} - {% call onsError(config) %} - {{ question_answers }} - {% endcall %} - {% else %} - {{ question_answers }} - {% endif %} - + "id": question.id, + "title": title, + "description": question_description, + "instruction": question_instruction, + "warning": question_warning, + "definition": question_definition, + "legendIsQuestionTitle": should_wrap_with_fieldset(question) + }) %} + {%- if content.list and content.list.list_items -%} + {% set list = content.list %} +
      {% include "partials/summary/list-summary.html" %}
      + {% endif %} + {{ individual_response_guidance }} + {{ question_definitions }} + {{ question_guidance }} + {# djlint:off #} + {% if question_error %} + {% set config = { + "text": question_error, + "id": question.id ~ '-error', + "attributes": { + "data-ga": "question-error", + "data-ga-category": "Question error", + "data-ga-action": question.type, + "data-ga-label": question.id + } + } %} + {% call onsError(config) %} + {{ question_answers }} + {% endcall %} + {% else %} + {{ question_answers }} + {% endif %} + {# djlint:on #} {% endcall %} diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index 2fc1005761..6139cadc9e 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -2,59 +2,62 @@ {% from "components/summary/_macro.njk" import onsSummary %} {%- set itemList = [] -%} - {%- for section in content.summary.sections -%} - {%- for group in section.groups -%} - {%- if group["blocks"] | length -%} - {%- if group.title -%} - {%- set item = { - "title": group.title, - "id": group.id, - "content": "" - } -%} - - {%- set summary %} - {{ onsSummary({ - "classes": "summary--no-bottom-border", - "summaries": [ - { - "groups": [ - { - "rows": map_summary_item_config( - group=group, - summary_type=content.summary.summary_type, - answers_are_editable=content.summary.answers_are_editable, - no_answer_provided=_("No answer provided"), - edit_link_text=_("Change"), - edit_link_aria_label=_("Change your answer for:"), - calculated_question=content.summary.calculated_question - ) - } - ] - } - ] - }) }} - {%- endset -%} - - {%- do item | setAttribute("content", item.content + summary) -%} - - {%- do itemList.append(item) -%} - {%- endif -%} - {%- endif -%} - {%- endfor -%} + {%- for group in section.groups -%} + {%- if group["blocks"] | length -%} + {%- if group.title -%} + {# djlint:off #} + {%- set item = { + "title": group.title, + "id": group.id, + "content": "" + } -%} + {%- set summary %} + {{ + onsSummary({ + "classes": "summary--no-bottom-border", + "summaries": [ + { + "groups": [ + { + "rows": map_summary_item_config( + group=group, + summary_type=content.summary.summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + edit_link_text=_("Change"), + edit_link_aria_label=_("Change your answer for:"), + calculated_question=content.summary.calculated_question + ) + } + ] + } + ] + }) + }} + {%- endset -%} + {# djlint:on #} + {%- do item | setAttribute("content", item.content + summary) -%} + {%- do itemList.append(item) -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} {%- endfor -%} - -{{ onsAccordion({ - "id": "summary-accordion", - "allButton": { - "open": _('Show all'), - "close": _('Hide all'), - "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" - } - }, - "itemsList": itemList -}) }} +{# djlint:off #} +{{ + onsAccordion({ + "id": "summary-accordion", + "allButton": { + "open": _('Show all') , + "close": _('Hide all'), + "attributes": { + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Show all", + "data-ga-label": "Show all" + } + }, + "itemsList": itemList + }) +}} +{# djlint:on #} diff --git a/templates/partials/summary/list-summary.html b/templates/partials/summary/list-summary.html index 50dde7e6d2..588f6cb923 100644 --- a/templates/partials/summary/list-summary.html +++ b/templates/partials/summary/list-summary.html @@ -1,46 +1,46 @@ {% from "components/summary/_macro.njk" import onsSummary %} {% if list.editable %} - {% set rows = map_list_collector_config( - list_items=list.list_items, - render_icon=True, - edit_link_text=_("Change"), - edit_link_aria_label=_("Change details for {item_name}"), - remove_link_text=_("Remove"), - remove_link_aria_label=_("Remove {item_name}") - ) %} + {# djlint:off #} + {% set rows = map_list_collector_config( + list_items=list.list_items, + render_icon=True, + edit_link_text=_("Change"), + edit_link_aria_label=_("Change details for {item_name}"), + remove_link_text=_("Remove"), + remove_link_aria_label=_("Remove {item_name}") + ) %} + {# djlint:on #} {% else %} - {% set rows = map_list_collector_config( - list_items=list.list_items, - render_icon=True - ) %} + {% set rows = map_list_collector_config( + list_items=list.list_items, + render_icon=True + ) %} {% endif %} - {% set group_config = { - "groupTitle": list_title, - "rows": rows, - "placeholderText": empty_list_text, + "groupTitle": list_title, + "rows": rows, + "placeholderText": empty_list_text, } %} - {% if add_link %} - {% set group_config = group_config | setAttribute("summaryLink", { - "url": add_link, - "text": add_link_text, - "attributes": { - "data-qa": "add-item-link" - } - }) %} + {% set group_config = group_config | setAttribute("summaryLink", { + "url": add_link, + "text": add_link_text, + "attributes": { + "data-qa": "add-item-link" + } + }) %} {% endif %} - {% set config = { - "withinQuestion": true, - "summaries": [ - { - "groups": [ - group_config - ] - } - ] + "withinQuestion": true, + "summaries": [ + { + "groups": [ + group_config + ] + } + ] } %} - +{# djlint:off #} {{ onsSummary(config) }} +{# djlint:on #} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 55543a9dbb..5e370158e2 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -1,57 +1,55 @@ {%- if content.summary.collapsible -%} - {%- include 'partials/summary/collapsible-summary.html' -%} + {%- include 'partials/summary/collapsible-summary.html' -%} {%- else -%} - {% from "components/summary/_macro.njk" import onsSummary %} + {% from "components/summary/_macro.njk" import onsSummary %} - {% set summary_sections = [] %} - {% set summary_type = content.summary.summary_type %} - {% set view_submitted_response = content.summary.view_submitted_response %} - - - {%- for section in content.summary.sections -%} - {% set summary_groups = [] %} - {%- if section.groups -%} - {%- for group in section.groups -%} - {%- if group.blocks -%} - {% do summary_groups.append + {% set summary_sections = [] %} + {% set summary_type = content.summary.summary_type %} + {% set view_submitted_response = content.summary.view_submitted_response %} + {# djlint:off #} + {%- for section in content.summary.sections -%} + {% set summary_groups = [] %} + {%- if section.groups -%} + {%- for group in section.groups -%} + {%- if group.blocks -%} + {% do summary_groups.append + ( + { + "groupTitle": group.title if group.title else None, + "id": group.id if group.id else None, + "rows": map_summary_item_config( + group=group, + summary_type=summary_type, + answers_are_editable=content.summary.answers_are_editable, + no_answer_provided=_("No answer provided"), + remove_link_text=_("Remove") if not view_submitted_response else "", + remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", + edit_link_text=_("Change") if not view_submitted_response else "", + edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", + calculated_question=content.summary.calculated_question + ), + "classes": "ons-u-mt-m" if loop.index > 1 else "", + "placeholderText": group.placeholder_text, + "summaryLink": group.links.add_link, + } + ) + %} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + {% do summary_sections.append ( - { - "groupTitle": group.title if group.title else None, - "id": group.id if group.id else None, - "rows": map_summary_item_config( - group=group, - summary_type=summary_type, - answers_are_editable=content.summary.answers_are_editable, - no_answer_provided=_("No answer provided"), - remove_link_text=_("Remove") if not view_submitted_response else "", - remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", - edit_link_text=_("Change") if not view_submitted_response else "", - edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", - calculated_question=content.summary.calculated_question - ), - "classes": "ons-u-mt-m" if loop.index > 1 else "", - "placeholderText": group.placeholder_text, - "summaryLink": group.links.add_link, + { + "summaryTitle": section.title if summary_type == "Summary", + "groups": summary_groups } ) - %} - {%- endif -%} - {%- endfor -%} - {%- endif -%} - {% do summary_sections.append - ( - { - "summaryTitle": section.title if summary_type == "Summary", - "groups": summary_groups - } - ) - %} - {%- endfor -%} - - - {{ onsSummary({ - "summaries": summary_sections - }) }} - + %} + {%- endfor -%} + {{ + onsSummary({ + "summaries": summary_sections + }) + }} + {# djlint:on #} {%- endif -%} - diff --git a/templates/preview.html b/templates/preview.html index 71b012c8b5..16659a98b1 100644 --- a/templates/preview.html +++ b/templates/preview.html @@ -1,118 +1,116 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/button/_macro.njk" import onsButton %} {% from "components/accordion/_macro.njk" import onsAccordion %} {% set save_on_signout = true %} - {% set breadcrumbs = { - "ariaLabel": 'Back', - "itemsList": [ - { - "url": url_for("questionnaire.get_questionnaire"), - "id": "top-previous", - "text": _("Back"), - "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' - } - } - ] + "ariaLabel": 'Back', + "itemsList": [ + { + "url": url_for("questionnaire.get_questionnaire"), + "id": "top-previous", + "text": _("Back"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Previous link click' + } + } + ] } %} - {% macro preview_blocks_for_sections(blocks) -%} - {% for block in blocks %} - {% if 'question' in block %} - {% set question = block['question'] %} - {% include 'partials/preview-question.html' %} - {% endif %} - {% endfor %} + {% for block in blocks %} + {% if 'question' in block %} + {% set question = block['question'] %} + {% include "partials/preview-question.html" %} + {% endif %} + {% endfor %} {%- endmacro %} {% block main %} - -

      {{ _("Preview of the questions in this survey") }}

      - - {% call onsPanel({ - "classes": 'ons-u-mb-m ons-u-ph' - }) %} -

      {{ _("To answer these questions you need to start survey").format(url=url_for('questionnaire.get_questionnaire')) }}

      - {% endcall %} -

      {{ _("You may not have to answer all of these questions. The questions you see will depend on the answers you provide.") }}

      - - {{ - onsButton({ - "type": 'button', - "text": _('Print questions'), - "variants": ['small', 'secondary', 'print'], - "attributes": { - "data-qa": "btn-print", - "data-ga-category": "Print button", - "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click", - "data-ga": "click" - } - }) - }} - {{ - onsButton({ - "text": _('Save questions as PDF'), - "variants": ['small', 'secondary', 'timer', 'download'], - "url": content.pdf_url, - "removeDownloadAttribute": true, - "attributes": { - "data-qa": "btn-pdf", - "data-ga-category": "PDF button", - "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click", - "data-ga": "click" - } - }) - }} -
      - - {%- if content.preview.sections | length > 1 -%} - {%- set itemList = [] -%} - {%- for section in content.preview.sections if section.blocks -%} - {%- set item = { - "title": section.title, - "id": section.id - } -%} - - {%- set block_previews = preview_blocks_for_sections(blocks=section["blocks"]) -%} - {%- do item | setAttribute("content", block_previews) -%} - - {%- do itemList.append(item) -%} - {%- endfor -%} - - {{ - onsAccordion({ - "id": "summary-accordion", - "allButton": { - "open": _('Show all'), - "close": _('Hide all'), +

      {{ _("Preview of the questions in this survey") }}

      + {# djlint:off #} + {% call + onsPanel({ + "classes": 'ons-u-mb-m ons-u-ph' + }) + %} +

      + {{ _("To answer these questions you need to start survey").format(url=url_for('questionnaire.get_questionnaire')) }} +

      + {% endcall %} + {# djlint:on #} +

      + {{ _("You may not have to answer all of these questions. The questions you see will depend on the answers you provide.") }} +

      + {# djlint:off #} + {{ + onsButton({ + "type": 'button', + "text": _('Print questions'), + "variants": ['small', 'secondary', 'print'], "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" + "data-qa": "btn-print", + "data-ga-category": "Print button", + "data-ga-action": "Open print Dialogue", + "data-ga-label": "Print button click", + "data-ga": "click" } - }, - "itemsList": itemList }) - }} - - {%- else %} - {%- for section in content.preview.sections if section.blocks -%} -
      -

      {{ section.title }}

      - {{ preview_blocks_for_sections(blocks=section["blocks"]) }} -
      - {%- endfor -%} - {%- endif -%} - -
      - + }} + {{ + onsButton({ + "text": _('Save questions as PDF') , + "variants": ['small', 'secondary', 'timer', 'download'], + "url": content.pdf_url, + "removeDownloadAttribute": true, + "attributes": { + "data-qa": "btn-pdf", + "data-ga-category": "PDF button", + "data-ga-action": "Download PDF", + "data-ga-label": "PDF button click", + "data-ga": "click" + } + }) + }} +
      + {%- if content.preview.sections | length > 1 -%} + {%- set itemList = [] -%} + {%- for section in content.preview.sections if section.blocks -%} + {%- set item = { + "title": section.title, + "id": section.id + } -%} + {%- set block_previews = preview_blocks_for_sections(blocks=section["blocks"]) -%} + {%- do item | setAttribute("content", block_previews) -%} + {%- do itemList.append(item) -%} + {%- endfor -%} + {{ + onsAccordion({ + "id": "summary-accordion", + "allButton": { + "open": _('Show all') , + "close": _('Hide all'), + "attributes": { + "data-ga": "click", + "data-ga-category": "Preview Survey", + "data-ga-action": "Show all", + "data-ga-label": "Show all" + } + }, + "itemsList": itemList + }) + }} + {%- else %} + {%- for section in content.preview.sections if section.blocks -%} +
      +

      {{ section.title }}

      + {{ preview_blocks_for_sections(blocks=section["blocks"]) }} +
      + {%- endfor -%} + {%- endif -%} +
      + {# djlint:on #} {% endblock main %} diff --git a/templates/primarypersonlistaddoreditquestion.html b/templates/primarypersonlistaddoreditquestion.html index 118134653c..2f61a13acd 100644 --- a/templates/primarypersonlistaddoreditquestion.html +++ b/templates/primarypersonlistaddoreditquestion.html @@ -1 +1 @@ -{% include 'question.html' %} +{% include "question.html" %} diff --git a/templates/primarypersonlistcollector.html b/templates/primarypersonlistcollector.html index 118134653c..2f61a13acd 100644 --- a/templates/primarypersonlistcollector.html +++ b/templates/primarypersonlistcollector.html @@ -1 +1 @@ -{% include 'question.html' %} +{% include "question.html" %} diff --git a/templates/question.html b/templates/question.html index aa21d8965d..9c7aa9b7f3 100644 --- a/templates/question.html +++ b/templates/question.html @@ -1,14 +1,13 @@ -{% extends 'layouts/_questionnaire.html' %} -{% import 'macros/helpers.html' as helpers %} +{% extends "layouts/_questionnaire.html" %} + +{% import "macros/helpers.html" as helpers %} {% set save_on_signout = true %} {% block form_errors %} - {% set form = content.form %} - - {% if form and (form.errors or form.question_errors) %} - {% set error_title = ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', form.mapped_errors | length) %} - {% include 'partials/error-panel.html' %} - {% endif %} - + {% set form = content.form %} + {% if form and (form.errors or form.question_errors) %} + {% set error_title = ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', form.mapped_errors | length) %} + {% include "partials/error-panel.html" %} + {% endif %} {% endblock form_errors %} diff --git a/templates/relationshipcollector.html b/templates/relationshipcollector.html index 9956ae57e6..2f61a13acd 100644 --- a/templates/relationshipcollector.html +++ b/templates/relationshipcollector.html @@ -1 +1 @@ -{% include 'question.html' %} \ No newline at end of file +{% include "question.html" %} diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 057e5def51..466681bb1d 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -1,61 +1,63 @@ -{% extends 'layouts/_questionnaire.html' %} +{% extends "layouts/_questionnaire.html" %} + {% from "components/button/_macro.njk" import onsButton %} -{% import 'macros/helpers.html' as helpers %} +{% import "macros/helpers.html" as helpers %} {% set save_on_signout = true %} - +{# djlint:off #} {{ - onsButton({ - "text": _("Continue"), - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" - } - }) + onsButton({ + "text": _("Continue"), + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click", + "data-ga": "click" + } + }) }} +{# djlint:on #} + {% block form_content %} {% if content.summary.custom_summary %} -

      {{content.summary.title}}

      - {% for summary in content.summary.custom_summary %} - {% if summary.type == 'List' %} - {% set add_link = summary.add_link %} - {% set add_link_text = summary.add_link_text %} - {% set empty_list_text = summary.empty_list_text %} - {% set list_title = summary.title %} - -
      - {% if summary.list %} - {% set list = summary.list %} - {% include 'partials/summary/list-summary.html' %} +

      {{ content.summary.title }}

      + {% for summary in content.summary.custom_summary %} + {% if summary.type == 'List' %} + {% set add_link = summary.add_link %} + {% set add_link_text = summary.add_link_text %} + {% set empty_list_text = summary.empty_list_text %} + {% set list_title = summary.title %} +
      + {% if summary.list %} + {% set list = summary.list %} + {% include "partials/summary/list-summary.html" %} + {% endif %} +
      {% endif %} -
      - {% endif %} {% endfor %} {%- else -%} -

      {{content.summary.title}}

      -
      - {% include 'partials/summary/summary.html' %} -
      +

      {{ content.summary.title }}

      +
      {% include "partials/summary/summary.html" %}
      {% endif %} -{% endblock %} +{% endblock form_content %} {% block submit_button %} - {{ - onsButton({ - "text": continue_button_text | default(_("Continue")), - "variants": 'timer', - "classes": "ons-u-mt-xl", - "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" - } - }) - }} -{% endblock %} + {# djlint:off #} + {{ + onsButton({ + "text": continue_button_text | default(_("Continue")), + "variants": 'timer', + "classes": "ons-u-mt-xl", + "attributes": { + "data-qa": "btn-submit", + "data-ga-category": "Submit button", + "data-ga-action": "Continue", + "data-ga-label": "Continue button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} +{% endblock submit_button %} diff --git a/templates/signed-out.html b/templates/signed-out.html index 7833a751a6..38a50bd356 100644 --- a/templates/signed-out.html +++ b/templates/signed-out.html @@ -1,26 +1,29 @@ -{% extends 'errors/_base.html' %} +{% extends "errors/_base.html" %} {% from "components/panel/_macro.njk" import onsPanel %} {% set page_title = _("Signed out") %} {% block main %} - - {{ - onsPanel({ - "id": '', - "type": 'success', - "iconType": 'check', - "iconSize": 'xl', - "classes": "ons-u-mb-xl", - "body": _("

      Your progress has been saved

      ") - }) - }} - - {% if survey_type in SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT %} -

      {{ _("To find further information or resume the survey, return to My Account.").format(url = redirect_url) }}

      - {% elif survey_type in SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} -

      {{ _("To resume the survey, re-enter your access code.").format(url = redirect_url) }}

      - {% endif %} - -{% endblock %} + {# djlint:off #} + {{ + onsPanel({ + "id": '', + "type": 'success', + "iconType": 'check', + "iconSize": 'xl', + "classes": "ons-u-mb-xl", + "body": _("

      Your progress has been saved

      ") + }) + }} + {# djlint:on #} + {% if survey_type in SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT %} +

      + {{ _("To find further information or resume the survey, return to My Account.").format(url = redirect_url) }} +

      + {% elif survey_type in SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} +

      + {{ _("To resume the survey, re-enter your access code.").format(url = redirect_url) }} +

      + {% endif %} +{% endblock main %} diff --git a/templates/submit-with-summary.html b/templates/submit-with-summary.html index 6a2fac86bb..4e8586d9da 100644 --- a/templates/submit-with-summary.html +++ b/templates/submit-with-summary.html @@ -1,7 +1,5 @@ -{% extends 'layouts/_submit.html' %} +{% extends "layouts/_submit.html" %} {% block summary %} -
      - {% include 'partials/summary/summary.html' %} -
      -{% endblock summary %} +
      {% include "partials/summary/summary.html" %}
      +{% endblock summary %} diff --git a/templates/submit.html b/templates/submit.html index f1465577a6..4ba917cf2f 100644 --- a/templates/submit.html +++ b/templates/submit.html @@ -1 +1 @@ -{% extends 'layouts/_submit.html' %} +{% extends "layouts/_submit.html" %} diff --git a/templates/thank-you.html b/templates/thank-you.html index fecad2fd97..01d17ad85a 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -1,94 +1,107 @@ -{% extends 'layouts/_base.html' %} -{% from 'components/panel/_macro.njk' import onsPanel %} +{% extends "layouts/_base.html" %} + +{% from "components/panel/_macro.njk" import onsPanel %} {% from "components/description-list/_macro.njk" import onsDescriptionList %} {% from "components/timeout-panel/_macro.njk" import onsTimeoutPanel %} {% set page_title = _("We’ve received your answers") %} {% set hide_sign_out_button = content.hide_sign_out_button %} {% if account_service_todo_url %} - {% set breadcrumbs = { - "ariaLabel": 'Back to surveys', - "itemsList": [ - { - "url": account_service_todo_url, - "id": "back-to-surveys", - "text": _("Back to surveys"), - "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Back to surveys link click' - } - } - ] - } %} + {# djlint:off #} + {% set breadcrumbs = { + "ariaLabel": 'Back to surveys', + "itemsList": [ + { + "url": account_service_todo_url, + "id": "back-to-surveys", + "text": _("Back to surveys"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Back to surveys link click' + } + } + ] + } %} + {# djlint:on #} {% endif %} -{% block main %} - - {% call onsPanel({ - "type": "success", - "iconType": "check", - "iconSize": "xl", - "classes": "ons-u-mb-m" - }) %} -

      {{ _("Thank you for completing the {survey_title}").format( - survey_title = survey_title) }}

      -

      - {{ content.submission_text }} -

      - {{ onsDescriptionList(content.metadata) }} - {% endcall %} - - {% if content.guidance %} - {% set contents = content.guidance.contents %} - {% include 'partials/contents.html' %} - {% else %} -

      {{ _("Your response will help inform decision-makers how best to support the UK population and economy.") }}

      -

      {{ _("Learn more about how we use this data") }}

      - {% endif %} - - {% set countdown_expired_text = _("For security, you can no longer view or get a copy of your answers") %} - - {% if content.view_submitted_response.enabled %} - {% if content.view_submitted_response.expired %} - {% call onsPanel({ - "id": "view-submitted-response-guidance", - "classes": "ons-u-mb-m" - }) %} - {{ countdown_expired_text }} - {% endcall %} +{% block main %} + {# djlint:off #} + {% call + onsPanel({ + "type": "success", + "iconType": "check", + "iconSize": "xl", + "classes": "ons-u-mb-m" + }) + %} +

      + {{ _("Thank you for completing the {survey_title}").format( + survey_title = survey_title) }} +

      +

      {{ content.submission_text }}

      + {{ onsDescriptionList(content.metadata) }} + {% endcall %} + {# djlint:on #} + {% if content.guidance %} + {% set contents = content.guidance.contents %} + {% include "partials/contents.html" %} {% else %} - {% set countdown_text = _("For security, your answers will only be available to view for another ") %} -

      {{ _("Get a copy of your answers") }}

      -

      - {{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }} -

      - {{ onsTimeoutPanel ({ - "id": "view-submitted-response-countdown", - "redirectUrl": url_for("post_submission.get_thank_you"), - "minutesTextSingular": _("minute"), - "minutesTextPlural": _("minutes"), - "secondsTextSingular": _("second"), - "secondsTextPlural": _("seconds"), - "countdownText": countdown_text, - "nojsText": _("For security, your answers will only be available to view for 45 minutes"), - "countdownExpiredText": countdown_expired_text, - "sessionExpiresAt": content.view_submitted_response.expires_at - }) }} +

      + {{ _("Your response will help inform decision-makers how best to support the UK population and economy.") }} +

      +

      + {{ _("Learn more about how we use this data") }} +

      {% endif %} - {% endif %} - - {% if content.confirmation_email_form %} -
      -

      {{ _("Get confirmation email") }}

      -

      {{ _("If you would like to be sent confirmation that you have completed your survey, enter your email address") }}

      - {% with form=content.confirmation_email_form %} - {% include 'partials/confirmation-email-form.html' %} - {% endwith %} - {% endif %} - - {% if content.show_feedback_call_to_action %} - {% include 'partials/feedback-call-to-action.html' %} - {% endif %} - -{% endblock %} + {% set countdown_expired_text = _("For security, you can no longer view or get a copy of your answers") %} + {% if content.view_submitted_response.enabled %} + {% if content.view_submitted_response.expired %} + {# djlint:off #} + {% call + onsPanel({ + "id": "view-submitted-response-guidance", + "classes": "ons-u-mb-m" + }) + %} + {{ countdown_expired_text }} + {% endcall %} + {# djlint:on #} + {% else %} + {% set countdown_text = _("For security, your answers will only be available to view for another ") %} +

      {{ _("Get a copy of your answers") }}

      +

      + {{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }} +

      + {# djlint:off #} + {{ onsTimeoutPanel ({ + "id": "view-submitted-response-countdown", + "redirectUrl": url_for("post_submission.get_thank_you"), + "minutesTextSingular": _("minute"), + "minutesTextPlural": _("minutes"), + "secondsTextSingular": _("second"), + "secondsTextPlural": _("seconds"), + "countdownText": countdown_text, + "nojsText": _("For security, your answers will only be available to view for 45 minutes"), + "countdownExpiredText": countdown_expired_text, + "sessionExpiresAt": content.view_submitted_response.expires_at + }) + }} + {# djlint:on #} + {% endif %} + {% endif %} + {% if content.confirmation_email_form %} +
      +

      {{ _("Get confirmation email") }}

      +

      + {{ _("If you would like to be sent confirmation that you have completed your survey, enter your email address") }} +

      + {% with form=content.confirmation_email_form %} + {% include "partials/confirmation-email-form.html" %} + {% endwith %} + {% endif %} + {% if content.show_feedback_call_to_action %} + {% include "partials/feedback-call-to-action.html" %} + {% endif %} +{% endblock main %} diff --git a/templates/unrelatedquestion.html b/templates/unrelatedquestion.html index 9956ae57e6..2f61a13acd 100644 --- a/templates/unrelatedquestion.html +++ b/templates/unrelatedquestion.html @@ -1 +1 @@ -{% include 'question.html' %} \ No newline at end of file +{% include "question.html" %} diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index 1a9c40f9f3..0a6fee2f4c 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -1,4 +1,4 @@ -{% extends 'layouts/_base.html' %} +{% extends "layouts/_base.html" %} {% from "components/panel/_macro.njk" import onsPanel %} {% from "components/button/_macro.njk" import onsButton %} @@ -6,69 +6,71 @@ {% set hide_sign_out_button = content.hide_sign_out_button %} {% set sign_out_url = content.sign_out_url %} - {% set breadcrumbs = { - "ariaLabel": 'Back', - "itemsList": [ - { - "url": url_for("post_submission.get_thank_you"), - "id": "top-previous", - "text": _("Back"), - "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' - } - } - ] + "ariaLabel": 'Back', + "itemsList": [ + { + "url": url_for("post_submission.get_thank_you"), + "id": "top-previous", + "text": _("Back"), + "attributes": { + "data-ga": 'click', + "data-ga-category": 'Navigation', + "data-ga-action": 'Previous link click' + } + } + ] } %} {% block main %} -

      - {{ content.submitted_text}} -

      - {{ onsDescriptionList(content.metadata)}} - {% if not content.view_submitted_response.expired %} - {{ - onsButton({ - "type": 'button', - "text": _('Print answers'), - "variants": ['small', 'secondary', 'print'], - "attributes": { - "data-qa": "btn-print", - "data-ga-category": "Print button", - "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click", - "data-ga": "click" - } - }) - }} - {{ - onsButton({ - "text": _('Save answers as PDF'), - "variants": ['small', 'secondary', 'timer', 'download'], - "url": content.pdf_url, - "removeDownloadAttribute": true, - "attributes": { - "data-qa": "btn-pdf", - "data-ga-category": "PDF button", - "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click", - "data-ga": "click" - } - }) - }} - {% block summary %} -
      - {% include 'partials/summary/summary.html' %} -
      - {% endblock summary %} - {% else %} - {% call onsPanel({ - "id": "view-submitted-guidance", - "classes": "ons-u-mb-m" - }) %} - {{ _("For security, you can no longer view or get a copy of your answers") }} - {% endcall %} - {% endif %} -{% endblock %} +

      {{ content.submitted_text }}

      + {{ onsDescriptionList(content.metadata) }} + {% if not content.view_submitted_response.expired %} + {# djlint:off #} + {{ + onsButton({ + "type": 'button', + "text": _('Print answers'), + "variants": ['small', 'secondary', 'print'], + "attributes": { + "data-qa": "btn-print", + "data-ga-category": "Print button", + "data-ga-action": "Open print Dialogue", + "data-ga-label": "Print button click", + "data-ga": "click" + } + }) + }} + {{ + onsButton({ + "text": _('Save answers as PDF') , + "variants": ['small', 'secondary', 'timer', 'download'], + "url": content.pdf_url, + "removeDownloadAttribute": true, + "attributes": { + "data-qa": "btn-pdf", + "data-ga-category": "PDF button", + "data-ga-action": "Download PDF", + "data-ga-label": "PDF button click", + "data-ga": "click" + } + }) + }} + {# djlint:on #} + + {% block summary %} +
      {% include "partials/summary/summary.html" %}
      + {% endblock summary %} + {% else %} + {# djlint:off #} + {% call + onsPanel({ + "id": "view-submitted-guidance", + "classes": "ons-u-mb-m" + }) + %} + {{ _("For security, you can no longer view or get a copy of your answers") }} + {% endcall %} + {# djlint:on #} + {% endif %} +{% endblock main %} diff --git a/tests/integration/routes/test_view_submitted_response_pdf.py b/tests/integration/routes/test_view_submitted_response_pdf.py index fb0311b1dd..4eb0528dae 100644 --- a/tests/integration/routes/test_view_submitted_response_pdf.py +++ b/tests/integration/routes/test_view_submitted_response_pdf.py @@ -47,7 +47,7 @@ def test_download_when_submitted_response_enabled_but_not_expired(self): # Check content length is reasonable. # This is given some leeway as it can change with DS changes. - self.assertGreater(self.last_response.content_length, 16000) + self.assertGreater(self.last_response.content_length, 10000) def test_download_when_submitted_response_enabled_but_expired(self): settings.VIEW_SUBMITTED_RESPONSE_EXPIRATION_IN_SECONDS = 3 From d6afd951d9b25801c028f26ec2c7f5c9fe16d835 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:44:00 +0100 Subject: [PATCH 406/567] Prevent Generated pages having same name as Base Page (#1357) --- tests/functional/base_pages/calculated-summary.page.js | 4 ++-- tests/functional/base_pages/confirm-email.page.js | 4 ++-- tests/functional/base_pages/confirmation-email-sent.page.js | 4 ++-- tests/functional/base_pages/confirmation-email.page.js | 4 ++-- tests/functional/base_pages/feedback-sent.page.js | 4 ++-- .../functional/base_pages/grand-calculated-summary.page.js | 4 ++-- tests/functional/base_pages/hub.page.js | 4 ++-- tests/functional/base_pages/question.page.js | 4 ++-- tests/functional/base_pages/thank-you.page.js | 4 ++-- tests/functional/generate_pages.py | 6 +++--- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/functional/base_pages/calculated-summary.page.js b/tests/functional/base_pages/calculated-summary.page.js index 49b569d3e9..1ac4b20ff7 100644 --- a/tests/functional/base_pages/calculated-summary.page.js +++ b/tests/functional/base_pages/calculated-summary.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class CalculatedSummaryPage extends BasePage { +class CalculatedSummaryBasePage extends BasePage { calculatedSummaryTitle() { return '[data-qa="calculated-summary-title"]'; } @@ -18,4 +18,4 @@ class CalculatedSummaryPage extends BasePage { } } -export default CalculatedSummaryPage; +export default CalculatedSummaryBasePage; diff --git a/tests/functional/base_pages/confirm-email.page.js b/tests/functional/base_pages/confirm-email.page.js index 75c4b0e2aa..db533e8b0c 100644 --- a/tests/functional/base_pages/confirm-email.page.js +++ b/tests/functional/base_pages/confirm-email.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class ConfirmEmailPage extends BasePage { +class ConfirmEmailBasePage extends BasePage { questionTitle() { return '[data-qa="confirm-email-title"]'; } @@ -17,4 +17,4 @@ class ConfirmEmailPage extends BasePage { return `[data-qa=error-body] div.ons-panel__body > [data-qa=error-list]`; } } -export default new ConfirmEmailPage("confirm-email"); +export default new ConfirmEmailBasePage("confirm-email"); diff --git a/tests/functional/base_pages/confirmation-email-sent.page.js b/tests/functional/base_pages/confirmation-email-sent.page.js index 38501c2dc5..ea8e34bd89 100644 --- a/tests/functional/base_pages/confirmation-email-sent.page.js +++ b/tests/functional/base_pages/confirmation-email-sent.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class ConfirmationEmailSentPage extends BasePage { +class ConfirmationEmailSentBasePage extends BasePage { confirmationText() { return '[data-qa="confirmation-text"]'; } @@ -17,4 +17,4 @@ class ConfirmationEmailSentPage extends BasePage { return ".ons-feedback__link"; } } -export default new ConfirmationEmailSentPage("email-confirmation"); +export default new ConfirmationEmailSentBasePage("email-confirmation"); diff --git a/tests/functional/base_pages/confirmation-email.page.js b/tests/functional/base_pages/confirmation-email.page.js index cf9ea293ac..1c3d44b4fd 100644 --- a/tests/functional/base_pages/confirmation-email.page.js +++ b/tests/functional/base_pages/confirmation-email.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class ConfirmationEmailSentPage extends BasePage { +class ConfirmationEmailBasePage extends BasePage { title() { return '[data-qa="title"]'; } @@ -17,4 +17,4 @@ class ConfirmationEmailSentPage extends BasePage { return ".ons-feedback"; } } -export default new ConfirmationEmailSentPage("email-confirmation"); +export default new ConfirmationEmailBasePage("email-confirmation"); diff --git a/tests/functional/base_pages/feedback-sent.page.js b/tests/functional/base_pages/feedback-sent.page.js index 7c49ca4286..69cb868e85 100644 --- a/tests/functional/base_pages/feedback-sent.page.js +++ b/tests/functional/base_pages/feedback-sent.page.js @@ -1,6 +1,6 @@ import FeedbackBasePage from "./feedback-base.page.js"; -class FeedbackSentPage extends FeedbackBasePage { +class FeedbackSentBasePage extends FeedbackBasePage { feedbackThankYouText() { return '[data-qa="feedback-thank-you-text"]'; } @@ -9,4 +9,4 @@ class FeedbackSentPage extends FeedbackBasePage { return '[data-qa="btn-done"]'; } } -export default new FeedbackSentPage("sent"); +export default new FeedbackSentBasePage("sent"); diff --git a/tests/functional/base_pages/grand-calculated-summary.page.js b/tests/functional/base_pages/grand-calculated-summary.page.js index 4f1c9e3312..89f69bbfe5 100644 --- a/tests/functional/base_pages/grand-calculated-summary.page.js +++ b/tests/functional/base_pages/grand-calculated-summary.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class GrandCalculatedSummaryPage extends BasePage { +class GrandCalculatedSummaryBasePage extends BasePage { grandCalculatedSummaryTitle() { return '[data-qa="grand-calculated-summary-title"]'; } @@ -14,4 +14,4 @@ class GrandCalculatedSummaryPage extends BasePage { } } -export default GrandCalculatedSummaryPage; +export default GrandCalculatedSummaryBasePage; diff --git a/tests/functional/base_pages/hub.page.js b/tests/functional/base_pages/hub.page.js index 6c7c7fe19d..6755160e98 100644 --- a/tests/functional/base_pages/hub.page.js +++ b/tests/functional/base_pages/hub.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class HubPage extends BasePage { +class HubBasePage extends BasePage { url() { return `/${this.pageName}/`; } @@ -22,4 +22,4 @@ class HubPage extends BasePage { } } -export default new HubPage("questionnaire"); +export default new HubBasePage("questionnaire"); diff --git a/tests/functional/base_pages/question.page.js b/tests/functional/base_pages/question.page.js index 8d96ed0ea0..fcd926369f 100644 --- a/tests/functional/base_pages/question.page.js +++ b/tests/functional/base_pages/question.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class QuestionPage extends BasePage { +class QuestionBasePage extends BasePage { constructor(pageName) { super(pageName); this.questions = []; @@ -51,4 +51,4 @@ class QuestionPage extends BasePage { } } -export default QuestionPage; +export default QuestionBasePage; diff --git a/tests/functional/base_pages/thank-you.page.js b/tests/functional/base_pages/thank-you.page.js index 9b9577ea24..4ccef94430 100644 --- a/tests/functional/base_pages/thank-you.page.js +++ b/tests/functional/base_pages/thank-you.page.js @@ -1,6 +1,6 @@ import BasePage from "./base.page"; -class ThankYouPage extends BasePage { +class ThankYouBasePage extends BasePage { url() { return `/submitted/${this.pageName}`; } @@ -57,4 +57,4 @@ class ThankYouPage extends BasePage { return ".ons-feedback__link"; } } -export default new ThankYouPage("thank-you"); +export default new ThankYouBasePage("thank-you"); diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 9a781a3371..156b678297 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -881,15 +881,15 @@ def process_block( with open(page_path, "w", encoding="utf-8") as page_spec: page_name = generate_pascal_case_from_id(block["id"]) - base_page = "QuestionPage" + base_page = "QuestionBasePage" base_page_file = "question.page" if block["type"] == "CalculatedSummary": - base_page = "CalculatedSummaryPage" + base_page = "CalculatedSummaryBasePage" base_page_file = "calculated-summary.page" if block["type"] == "GrandCalculatedSummary": - base_page = "GrandCalculatedSummaryPage" + base_page = "GrandCalculatedSummaryBasePage" base_page_file = "grand-calculated-summary.page" if block["type"] == "Introduction": From e88d61293a616c03c28d700aff62f514fb2b129d Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:29:10 +0100 Subject: [PATCH 407/567] Update text highlighting to current suggested implementation (#1327) --- .design-system-version | 2 +- README.md | 8 ++-- app/translations/cy/LC_MESSAGES/messages.po | 24 +++++------ app/translations/eo/LC_MESSAGES/messages.po | 24 +++++------ app/translations/ga/LC_MESSAGES/messages.po | 24 +++++------ app/translations/messages.pot | 18 +++++---- app/views/handlers/individual_response.py | 6 +-- schemas/test/en/test_benchmark_business.json | 2 +- ...and_grand_calculated_summary_decimals.json | 2 +- schemas/test/en/test_calculated_summary.json | 12 +++--- ...alculated_summary_dependent_questions.json | 2 +- .../test/en/test_confirmation_question.json | 4 +- ...ion_question_within_repeating_section.json | 4 +- schemas/test/en/test_custom_page_titles.json | 22 +++++----- ...ynamic_answer_options_function_driven.json | 8 ++-- ...s_function_driven_with_static_options.json | 8 ++-- ..._driven_with_static_options_mandatory.json | 8 ++-- ...ated_summary_inside_repeating_section.json | 16 ++++---- ..._calculated_summary_repeating_answers.json | 10 ++--- ...test_hub_section_required_with_repeat.json | 2 +- schemas/test/en/test_individual_response.json | 2 +- ...t_individual_response_on_hub_disabled.json | 2 +- .../test/en/test_interstitial_page_title.json | 2 +- schemas/test/en/test_introduction.json | 2 +- .../test_last_viewed_question_guidance.json | 16 ++++---- .../en/test_list_collector_list_summary.json | 6 +-- schemas/test/en/test_markup.json | 6 +-- schemas/test/en/test_multiple_piping.json | 2 +- .../test/en/test_new_calculated_summary.json | 12 +++--- ...alculated_summary_dependent_questions.json | 2 +- ..._summary_repeating_and_static_answers.json | 14 +++---- ..._calculated_summary_repeating_section.json | 8 ++-- .../en/test_placeholder_default_value.json | 4 +- ...t_placeholder_option_label_from_value.json | 2 +- .../test/en/test_placeholder_transform.json | 8 ++-- ...block_value_source_repeating_sections.json | 4 +- ...ction_value_source_repeating_sections.json | 4 +- ...gress_value_source_calculated_summary.json | 6 +-- ...ue_source_calculated_summary_extended.json | 10 ++--- ...peating_sections_chained_dependencies.json | 4 +- schemas/test/en/test_question_definition.json | 2 +- .../test_question_definition_array_type.json | 2 +- schemas/test/en/test_relationships.json | 20 +++++----- .../test/en/test_relationships_primary.json | 40 +++++++++---------- .../test/en/test_relationships_unrelated.json | 14 +++---- .../en/test_repeating_section_summaries.json | 2 +- ...repeating_sections_with_hub_and_spoke.json | 8 ++-- schemas/test/en/test_routing_and.json | 4 +- .../test/en/test_routing_checkbox_count.json | 4 +- .../test/en/test_routing_number_equals.json | 4 +- .../en/test_routing_number_greater_than.json | 4 +- ..._routing_number_greater_than_or_equal.json | 4 +- ...reater_than_or_equal_single_condition.json | 4 +- .../en/test_routing_number_less_than.json | 4 +- ...est_routing_number_less_than_or_equal.json | 4 +- ...r_less_than_or_equal_single_condition.json | 4 +- .../en/test_routing_number_not_equals.json | 4 +- schemas/test/en/test_routing_or.json | 4 +- schemas/test/en/test_submit_with_summary.json | 2 +- .../en/test_titles_radio_and_checkbox.json | 10 ++--- ...inst_total_hub_with_dependent_section.json | 4 +- ...otal_repeating_with_dependent_section.json | 4 +- schemas/test/en/test_variants_question.json | 8 ++-- templates/confirmation-email-sent.html | 6 +-- templates/feedback-sent.html | 2 +- .../confirmation-post.html | 2 +- .../confirmation-text-message.html | 6 +-- templates/layouts/_calculatedsummary.html | 1 - templates/layouts/_submit.html | 2 +- templates/macros/helpers.html | 2 +- templates/multiple_survey.html | 2 +- templates/partials/error-panel.html | 2 +- .../individual-response-guidance.html | 2 +- templates/signed-out.html | 2 +- ...mutually_exclusive_month_year_date.spec.js | 2 +- ...est_questionnaire_array_type_definition.py | 2 +- .../test_questionnaire_calculated_summary.py | 6 +-- ..._questionnaire_grand_calculated_summary.py | 16 ++++---- .../test_questionnaire_html_escaping.py | 6 +-- .../questionnaire/test_questionnaire_hub.py | 6 +-- .../test_questionnaire_piping.py | 4 +- .../test_questionnaire_placeholders.py | 4 +- .../test_questionnaire_question_definition.py | 2 +- .../test_questionnaire_question_variants.py | 10 +++-- ...t_questionnaire_relationships_unrelated.py | 4 +- 85 files changed, 291 insertions(+), 282 deletions(-) diff --git a/.design-system-version b/.design-system-version index 6eb4d83d18..1f5c54aff3 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -68.0.2 +70.0.2 diff --git a/README.md b/README.md index 869ee3f612..d5793b3f1b 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ pip install --upgrade pip setuptools Install poetry, poetry dotenv plugin and install dependencies: -''' shell +``` shell curl -sSL https://install.python-poetry.org | python3 - --version 1.8.2 poetry self add poetry-plugin-dotenv poetry install -''' +``` We use [poetry-plugin-up](https://github.com/MousaZeidBaker/poetry-plugin-up) to update dependencies in the `pyproject.toml` file: -'''shell +``` shell poetry self add poetry-plugin-up -''' +``` #### Design system templates diff --git a/app/translations/cy/LC_MESSAGES/messages.po b/app/translations/cy/LC_MESSAGES/messages.po index 30cf80b23f..3af221ad79 100644 --- a/app/translations/cy/LC_MESSAGES/messages.po +++ b/app/translations/cy/LC_MESSAGES/messages.po @@ -618,8 +618,8 @@ msgid "Cannot answer questions for others in your household" msgstr "Methu ateb cwestiynau ar ran pobl eraill yn eich cartref" #: app/views/handlers/individual_response.py:324 -msgid "How would you like {person_name} to receive a separate census?" -msgstr "Sut hoffech chi i {person_name} gael cyfrifiad ar wahân?" +msgid "How would you like {person_name} to receive a separate census?" +msgstr "Sut hoffech chi i {person_name} gael cyfrifiad ar wahân?" #: app/views/handlers/individual_response.py:347 msgid "Text message" @@ -654,8 +654,8 @@ msgid "Send individual access code" msgstr "Anfon cod mynediad unigol" #: app/views/handlers/individual_response.py:450 -msgid "How would you like to answer {person_name_possessive} questions?" -msgstr "Sut hoffech chi ateb cwestiynau {person_name_possessive}?" +msgid "How would you like to answer {person_name_possessive} questions?" +msgstr "Sut hoffech chi ateb cwestiynau {person_name_possessive}?" #: app/views/handlers/individual_response.py:465 msgid "I would like to request a separate census for them to complete" @@ -711,8 +711,8 @@ msgid "Who do you need to request a separate census for?" msgstr "Ar gyfer pwy y mae angen i chi wneud cais am gyfrifiad ar wahân?" #: app/views/handlers/individual_response.py:772 -msgid "What is {person_name_possessive} mobile number?" -msgstr "Beth yw rhif ffôn symudol {person_name_possessive}?" +msgid "What is {person_name_possessive} mobile number?" +msgstr "Beth yw rhif ffôn symudol {person_name_possessive}?" #: app/views/handlers/individual_response.py:784 msgid "UK mobile number" @@ -815,8 +815,8 @@ msgid "A confirmation email has been sent to {email}" msgstr "Mae e-bost cadarnhau wedi cael ei anfon i {email}" #: templates/confirmation-email-sent.html:25 -msgid "The email will be sent from census.2021@notifications.service.gov.uk" -msgstr "Caiff yr e-bost ei anfon o census.2021@notifications.service.gov.uk" +msgid "The email will be sent from census.2021@notifications.service.gov.uk" +msgstr "Caiff yr e-bost ei anfon o census.2021@notifications.service.gov.uk" #: templates/confirmation-email-sent.html:29 msgid "Didn't receive an email?" @@ -1208,8 +1208,8 @@ msgid "The text message with an individual access code should arrive soon for th msgstr "Dylai’r neges destun â chod mynediad unigol gyrraedd yn fuan er mwyn i’r unigolyn allu cwblhau ei gyfrifiad ei hun" #: templates/individual_response/confirmation-text-message.html:25 -msgid "The text will be sent from Census2021" -msgstr "Caiff y neges destun ei hanfon o Census2021" +msgid "The text will be sent from Census2021" +msgstr "Caiff y neges destun ei hanfon o Census2021" #: templates/individual_response/interstitial.html:8 msgid "If you can't answer questions for others in your household" @@ -1322,8 +1322,8 @@ msgid "Give feedback" msgstr "Rhoi adborth" #: templates/partials/individual-response-guidance.html:24 -msgid "You can share your household access code with the people you live with so they can complete their own sections." -msgstr "Gallwch rannu cod mynediad eich cartref â’r bobl rydych chi’n byw gyda nhw fel y gallant gwblhau eu hadrannau eu hunain." +msgid "You can share your household access code with the people you live with so they can complete their own sections." +msgstr "Gallwch rannu cod mynediad eich cartref â’r bobl rydych chi’n byw gyda nhw fel y gallant gwblhau eu hadrannau eu hunain." #: templates/partials/individual-response-guidance.html:25 msgid "If this is not possible, there are other ways each person can complete their own census." diff --git a/app/translations/eo/LC_MESSAGES/messages.po b/app/translations/eo/LC_MESSAGES/messages.po index 6efb67342d..9ceb4f0fc4 100644 --- a/app/translations/eo/LC_MESSAGES/messages.po +++ b/app/translations/eo/LC_MESSAGES/messages.po @@ -540,8 +540,8 @@ msgid "Cannot answer questions for others in your household" msgstr "Cannae mak repone fur speirins anent ithers in yer hoosehaud" #: app/views/handlers/individual_response.py:311 -msgid "How would you like {person_name} to receive a separate census?" -msgstr "Hoo wud ye lake {person_name} tae get anither heid-coont?" +msgid "How would you like {person_name} to receive a separate census?" +msgstr "Hoo wud ye lake {person_name} tae get anither heid-coont?" #: app/views/handlers/individual_response.py:334 msgid "Text message" @@ -576,8 +576,8 @@ msgid "Send individual access code" msgstr "Sen sing'l ingang code" #: app/views/handlers/individual_response.py:437 -msgid "How would you like to answer {person_name_possessive} questions?" -msgstr "Hoo wud ye lake tae mak repone fur {person_name_possessive} speirins?" +msgid "How would you like to answer {person_name_possessive} questions?" +msgstr "Hoo wud ye lake tae mak repone fur {person_name_possessive} speirins?" #: app/views/handlers/individual_response.py:452 msgid "I would like to request a separate census for them to complete" @@ -633,8 +633,8 @@ msgid "Who do you need to request a separate census for?" msgstr "Wha's tha bodie ye need tae ax fur anither heid-coont fur?" #: app/views/handlers/individual_response.py:759 -msgid "What is {person_name_possessive} mobile number?" -msgstr "Whut bes {person_name_possessive} mobil phone nummer?" +msgid "What is {person_name_possessive} mobile number?" +msgstr "Whut bes {person_name_possessive} mobil phone nummer?" #: app/views/handlers/individual_response.py:771 msgid "UK mobile number" @@ -721,8 +721,8 @@ msgid "A confirmation email has been sent to {email}" msgstr "An homologation email haes bin sent til {email}" #: templates/confirmation-email-sent.html:25 -msgid "The email will be sent from census.2021@notifications.service.gov.uk" -msgstr "Tha email wull be sent frae census.2021@notifications.service.gov.uk" +msgid "The email will be sent from census.2021@notifications.service.gov.uk" +msgstr "Tha email wull be sent frae census.2021@notifications.service.gov.uk" #: templates/confirmation-email-sent.html:29 msgid "Didn't receive an email?" @@ -987,8 +987,8 @@ msgid "The text message with an individual access code should arrive soon for th msgstr "Tha text message wi a sing'l ingang code shud land shane eneuch fur thaim tae fill oot thair ain heid-coont" #: templates/individual_response/confirmation-text-message.html:26 -msgid "The text will be sent from Census2021" -msgstr "Tha text wull be sent frae Heid-coont 20an21" +msgid "The text will be sent from Census2021" +msgstr "Tha text wull be sent frae Heid-coont 20an21" #: templates/individual_response/interstitial.html:8 msgid "If you can't answer questions for others in your household" @@ -1095,8 +1095,8 @@ msgid "Give feedback" msgstr "Gie feedbak" #: templates/partials/individual-response-guidance.html:25 -msgid "You can share your household access code with the people you live with so they can complete their own sections." -msgstr "Ye can gie oot yer hoosehaud ingang code wi tha fowk wha leeve wi ye sae they can fill oot thair ain lay- oots." +msgid "You can share your household access code with the people you live with so they can complete their own sections." +msgstr "Ye can gie oot yer hoosehaud ingang code wi tha fowk wha leeve wi ye sae they can fill oot thair ain lay- oots." #: templates/partials/individual-response-guidance.html:26 msgid "If this is not possible, there are other ways each person can complete their own census." diff --git a/app/translations/ga/LC_MESSAGES/messages.po b/app/translations/ga/LC_MESSAGES/messages.po index 165acc553f..4e72dbfa5b 100644 --- a/app/translations/ga/LC_MESSAGES/messages.po +++ b/app/translations/ga/LC_MESSAGES/messages.po @@ -558,8 +558,8 @@ msgid "Cannot answer questions for others in your household" msgstr "Ní thig liom ceisteanna a fhreagairt do dhaoine eile i do theaghlach" #: app/views/handlers/individual_response.py:311 -msgid "How would you like {person_name} to receive a separate census?" -msgstr "Cén dóigh ar ar mhaith leat go bhfaigheadh {person_name} daonáireamh ar leith?" +msgid "How would you like {person_name} to receive a separate census?" +msgstr "Cén dóigh ar ar mhaith leat go bhfaigheadh {person_name} daonáireamh ar leith?" #: app/views/handlers/individual_response.py:334 msgid "Text message" @@ -594,8 +594,8 @@ msgid "Send individual access code" msgstr "Cuir cód rochtana ar leith" #: app/views/handlers/individual_response.py:437 -msgid "How would you like to answer {person_name_possessive} questions?" -msgstr "Cén dóigh ar mhaith leat ceisteanna {person_name_possessive} a fhreagairt?" +msgid "How would you like to answer {person_name_possessive} questions?" +msgstr "Cén dóigh ar mhaith leat ceisteanna {person_name_possessive} a fhreagairt?" #: app/views/handlers/individual_response.py:452 msgid "I would like to request a separate census for them to complete" @@ -651,8 +651,8 @@ msgid "Who do you need to request a separate census for?" msgstr "Cé dó a bhfuil tú ag iarraidh daonáireamh ar leith>?" #: app/views/handlers/individual_response.py:759 -msgid "What is {person_name_possessive} mobile number?" -msgstr "Cén uimhir ghuthán póca atá ag {person_name_possessive}?" +msgid "What is {person_name_possessive} mobile number?" +msgstr "Cén uimhir ghuthán póca atá ag {person_name_possessive}?" #: app/views/handlers/individual_response.py:771 msgid "UK mobile number" @@ -742,8 +742,8 @@ msgid "A confirmation email has been sent to {email}" msgstr "Seoladh r-phost dearbhaithe chuig {email}" #: templates/confirmation-email-sent.html:25 -msgid "The email will be sent from census.2021@notifications.service.gov.uk" -msgstr "Seolfar an r-phost ó census.2021@notifications.service.gov.uk" +msgid "The email will be sent from census.2021@notifications.service.gov.uk" +msgstr "Seolfar an r-phost ó census.2021@notifications.service.gov.uk" #: templates/confirmation-email-sent.html:29 msgid "Didn't receive an email?" @@ -1011,8 +1011,8 @@ msgid "The text message with an individual access code should arrive soon for th msgstr "Ba chóir go dtiocfadh an téacstheachtaireacht ina bhfuil cód rochtana duine aonair dóibh gan mhoill le go dtig leo a ndaonáireamh féin a chomhlánú" #: templates/individual_response/confirmation-text-message.html:26 -msgid "The text will be sent from Census2021" -msgstr "Seolfar an téacs ó Census2021" +msgid "The text will be sent from Census2021" +msgstr "Seolfar an téacs ó Census2021" #: templates/individual_response/interstitial.html:8 msgid "If you can't answer questions for others in your household" @@ -1119,8 +1119,8 @@ msgid "Give feedback" msgstr "Tabhair aiseolas" #: templates/partials/individual-response-guidance.html:25 -msgid "You can share your household access code with the people you live with so they can complete their own sections." -msgstr "Tig leat do chód rochtana teaghlaigh a roinnt leis na daoine a gcónaíonn tú leo sa dóigh go dtig leo a míreanna féin a chomhlánú." +msgid "You can share your household access code with the people you live with so they can complete their own sections." +msgstr "Tig leat do chód rochtana teaghlaigh a roinnt leis na daoine a gcónaíonn tú leo sa dóigh go dtig leo a míreanna féin a chomhlánú." #: templates/partials/individual-response-guidance.html:26 msgid "If this is not possible, there are other ways each person can complete their own census." diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 3c3e1b0dd5..0310115b70 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -562,7 +562,9 @@ msgid "Cannot answer questions for others in your household" msgstr "" #: app/views/handlers/individual_response.py:329 -msgid "How would you like {person_name} to receive a separate census?" +msgid "" +"How would you like {person_name} to receive a separate " +"census?" msgstr "" #: app/views/handlers/individual_response.py:352 @@ -602,7 +604,9 @@ msgid "Send individual access code" msgstr "" #: app/views/handlers/individual_response.py:455 -msgid "How would you like to answer {person_name_possessive} questions?" +msgid "" +"How would you like to answer {person_name_possessive} " +"questions?" msgstr "" #: app/views/handlers/individual_response.py:470 @@ -663,7 +667,7 @@ msgid "Who do you need to request a separate census for?" msgstr "" #: app/views/handlers/individual_response.py:800 -msgid "What is {person_name_possessive} mobile number?" +msgid "What is {person_name_possessive} mobile number?" msgstr "" #: app/views/handlers/individual_response.py:812 @@ -717,7 +721,7 @@ msgstr "" #: templates/confirmation-email-sent.html:32 msgid "" "The email will be sent from " -"census.2021@notifications.service.gov.uk" +"census.2021@notifications.service.gov.uk" msgstr "" #: templates/confirmation-email-sent.html:38 @@ -1160,7 +1164,7 @@ msgid "" msgstr "" #: templates/individual_response/confirmation-text-message.html:34 -msgid "The text will be sent from Census2021" +msgid "The text will be sent from Census2021" msgstr "" #: templates/individual_response/interstitial.html:9 @@ -1291,8 +1295,8 @@ msgstr "" #: templates/partials/individual-response-guidance.html:18 msgid "" -"You can share your household access code with the people you " -"live with so they can complete their own sections." +"You can share your household access code with the people" +" you live with so they can complete their own sections." msgstr "" #: templates/partials/individual-response-guidance.html:21 diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 68124b58b2..79c36fce56 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -327,7 +327,7 @@ def block_definition(self) -> Mapping[str, Any]: "id": "individual-response-how", "title": { "text": lazy_gettext( - "How would you like {person_name} to receive a separate census?" + "How would you like {person_name} to receive a separate census?" ), "placeholders": IndividualResponseHandler._person_name_placeholder( self._list_name @@ -453,7 +453,7 @@ def block_definition(self) -> Mapping[str, Any]: "id": "individual-response-change-question", "title": { "text": lazy_gettext( - "How would you like to answer {person_name_possessive} questions?" + "How would you like to answer {person_name_possessive} questions?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( self._list_name @@ -798,7 +798,7 @@ def block_definition(self) -> Mapping: "id": "individual-response-enter-number", "title": { "text": lazy_gettext( - "What is {person_name_possessive} mobile number?" + "What is {person_name_possessive} mobile number?" ), "placeholders": IndividualResponseHandler._person_name_placeholder_possessive( self._list_name diff --git a/schemas/test/en/test_benchmark_business.json b/schemas/test/en/test_benchmark_business.json index db844fefaf..d410688f17 100644 --- a/schemas/test/en/test_benchmark_business.json +++ b/schemas/test/en/test_benchmark_business.json @@ -260,7 +260,7 @@ "type": "Question", "question": { "id": "question-381", - "title": "What was the total value of stocks held (net of progress payments on long-term contracts)?", + "title": "What was the total value of stocks held (net of progress payments on long-term contracts)?", "guidance": { "contents": [ { diff --git a/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json b/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json index 304957746e..278f140a47 100644 --- a/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json +++ b/schemas/test/en/test_calculated_and_grand_calculated_summary_decimals.json @@ -162,7 +162,7 @@ { "list": [ { - "text": "Total currency values: {currency_total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { "placeholder": "currency_total", diff --git a/schemas/test/en/test_calculated_summary.json b/schemas/test/en/test_calculated_summary.json index 90a0524635..82d6586c60 100644 --- a/schemas/test/en/test_calculated_summary.json +++ b/schemas/test/en/test_calculated_summary.json @@ -342,7 +342,7 @@ { "list": [ { - "text": "Total currency values: {currency_total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { "placeholder": "currency_total", @@ -361,7 +361,7 @@ ] }, { - "text": "Total unformatted unit values: {unit_total}", + "text": "Total unformatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -380,7 +380,7 @@ ] }, { - "text": "Total formatted unit values: {unit_total}", + "text": "Total formatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -401,7 +401,7 @@ ] }, { - "text": "Total unformatted percentage values: {percentage_total}", + "text": "Total unformatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -420,7 +420,7 @@ ] }, { - "text": "Total formatted percentage values: {percentage_total}", + "text": "Total formatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -439,7 +439,7 @@ ] }, { - "text": "Total number values: {number_total}", + "text": "Total number values: {number_total}", "placeholders": [ { "placeholder": "number_total", diff --git a/schemas/test/en/test_calculated_summary_dependent_questions.json b/schemas/test/en/test_calculated_summary_dependent_questions.json index 85c0964a49..15617ed8c7 100644 --- a/schemas/test/en/test_calculated_summary_dependent_questions.json +++ b/schemas/test/en/test_calculated_summary_dependent_questions.json @@ -137,7 +137,7 @@ { "id": "calculated-summary-block", "type": "CalculatedSummary", - "title": "We have calculated your total spend to be %(total)s. Is this correct?", + "title": "We have calculated your total spend to be %(total)s. Is this correct?", "calculation": { "calculation_type": "sum", "answers_to_calculate": ["answer-1", "answer-3"], diff --git a/schemas/test/en/test_confirmation_question.json b/schemas/test/en/test_confirmation_question.json index 5ac6abcfb7..1f526d4182 100644 --- a/schemas/test/en/test_confirmation_question.json +++ b/schemas/test/en/test_confirmation_question.json @@ -123,7 +123,7 @@ ], "id": "confirm-zero-employees-question", "title": { - "text": "The current number of employees for {company_name} is 0, is this correct?", + "text": "The current number of employees for {company_name} is 0, is this correct?", "placeholders": [ { "placeholder": "company_name", @@ -198,7 +198,7 @@ ], "id": "number-of-employees-split-question", "title": { - "text": "Of the {number_of_employees_total} total employees employed, how many male and female employees worked the following hours?", + "text": "Of the {number_of_employees_total} total employees employed, how many male and female employees worked the following hours?", "placeholders": [ { "placeholder": "number_of_employees_total", diff --git a/schemas/test/en/test_confirmation_question_within_repeating_section.json b/schemas/test/en/test_confirmation_question_within_repeating_section.json index 938a8a5f07..6f754192de 100644 --- a/schemas/test/en/test_confirmation_question_within_repeating_section.json +++ b/schemas/test/en/test_confirmation_question_within_repeating_section.json @@ -283,7 +283,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -459,7 +459,7 @@ ] } ], - "text": "Does {person_name} look after, or give any help or support to, anyone because they have long-term physical or mental health conditions or illnesses, or problems related to old age?" + "text": "Does {person_name} look after, or give any help or support to, anyone because they have long-term physical or mental health conditions or illnesses, or problems related to old age?" }, "answers": [ { diff --git a/schemas/test/en/test_custom_page_titles.json b/schemas/test/en/test_custom_page_titles.json index 8d1c99f1b2..1b9865a4a4 100644 --- a/schemas/test/en/test_custom_page_titles.json +++ b/schemas/test/en/test_custom_page_titles.json @@ -196,7 +196,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their ", + "text": "Thinking of {first_person_name}, {second_person_name} is their ", "placeholders": [ { "placeholder": "first_person_name", @@ -264,7 +264,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is {first_person_name_possessive} ", + "text": "{second_person_name} is {first_person_name_possessive} ", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -339,7 +339,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", + "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", "placeholders": [ { "placeholder": "first_person_name", @@ -402,7 +402,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} husband or wife", + "text": "{second_person_name} is {first_person_name_possessive} husband or wife", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -477,7 +477,7 @@ "label": "Legally registered civil partner", "value": "Legally registered civil partner", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", + "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name", @@ -540,7 +540,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", + "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -615,7 +615,7 @@ "label": "Son or daughter", "value": "Son or daughter", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", + "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", "placeholders": [ { "placeholder": "first_person_name", @@ -678,7 +678,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} son or daughter", + "text": "{second_person_name} is {first_person_name_possessive} son or daughter", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -753,7 +753,7 @@ "label": "Brother or sister", "value": "Brother or sister", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", + "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", "placeholders": [ { "placeholder": "first_person_name", @@ -816,7 +816,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} brother or sister", + "text": "{second_person_name} is {first_person_name_possessive} brother or sister", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -1032,7 +1032,7 @@ ] } ], - "text": "Are you {person_name}?" + "text": "Are you {person_name}?" }, "type": "General" }, diff --git a/schemas/test/en/test_dynamic_answer_options_function_driven.json b/schemas/test/en/test_dynamic_answer_options_function_driven.json index 2c8167dd26..aebd35f764 100644 --- a/schemas/test/en/test_dynamic_answer_options_function_driven.json +++ b/schemas/test/en/test_dynamic_answer_options_function_driven.json @@ -57,7 +57,7 @@ "question": { "id": "dynamic-checkbox-question", "title": { - "text": "In the week of {date}, which days did you work?", + "text": "In the week of {date}, which days did you work?", "placeholders": [ { "placeholder": "date", @@ -125,7 +125,7 @@ "question": { "id": "dynamic-radio-question", "title": { - "text": "In the week of {date}, which day did you work the most?", + "text": "In the week of {date}, which day did you work the most?", "placeholders": [ { "placeholder": "date", @@ -193,7 +193,7 @@ "question": { "id": "dynamic-dropdown-question", "title": { - "text": "In the week of {date}, which day did you work the least?", + "text": "In the week of {date}, which day did you work the least?", "placeholders": [ { "placeholder": "date", @@ -262,7 +262,7 @@ "question": { "id": "dynamic-mutually-exclusive-question", "title": { - "text": "In the week of {date}, which days did you book annual leave?", + "text": "In the week of {date}, which days did you book annual leave?", "placeholders": [ { "placeholder": "date", diff --git a/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options.json b/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options.json index 77a61cbe3a..94117b7672 100644 --- a/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options.json +++ b/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options.json @@ -57,7 +57,7 @@ "question": { "id": "dynamic-checkbox-question", "title": { - "text": "In the week of {date}, which days did you work?", + "text": "In the week of {date}, which days did you work?", "placeholders": [ { "placeholder": "date", @@ -131,7 +131,7 @@ "question": { "id": "dynamic-radio-question", "title": { - "text": "In the week of {date}, which day did you work the most?", + "text": "In the week of {date}, which day did you work the most?", "placeholders": [ { "placeholder": "date", @@ -205,7 +205,7 @@ "question": { "id": "dynamic-dropdown-question", "title": { - "text": "In the week of {date}, which day did you work the least?", + "text": "In the week of {date}, which day did you work the least?", "placeholders": [ { "placeholder": "date", @@ -280,7 +280,7 @@ "question": { "id": "dynamic-mutually-exclusive-question", "title": { - "text": "In the week of {date}, which days did you book annual leave?", + "text": "In the week of {date}, which days did you book annual leave?", "placeholders": [ { "placeholder": "date", diff --git a/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options_mandatory.json b/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options_mandatory.json index fe27b21d53..45962f6437 100644 --- a/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options_mandatory.json +++ b/schemas/test/en/test_dynamic_answer_options_function_driven_with_static_options_mandatory.json @@ -57,7 +57,7 @@ "question": { "id": "dynamic-checkbox-question", "title": { - "text": "In the week of {date}, which days did you work?", + "text": "In the week of {date}, which days did you work?", "placeholders": [ { "placeholder": "date", @@ -131,7 +131,7 @@ "question": { "id": "dynamic-radio-question", "title": { - "text": "In the week of {date}, which day did you work the most?", + "text": "In the week of {date}, which day did you work the most?", "placeholders": [ { "placeholder": "date", @@ -205,7 +205,7 @@ "question": { "id": "dynamic-dropdown-question", "title": { - "text": "In the week of {date}, which day did you work the least?", + "text": "In the week of {date}, which day did you work the least?", "placeholders": [ { "placeholder": "date", @@ -280,7 +280,7 @@ "question": { "id": "dynamic-mutually-exclusive-question", "title": { - "text": "In the week of {date}, which days did you book annual leave?", + "text": "In the week of {date}, which days did you book annual leave?", "placeholders": [ { "placeholder": "date", diff --git a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json index 41722556d2..41930c4a5e 100644 --- a/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json +++ b/schemas/test/en/test_grand_calculated_summary_inside_repeating_section.json @@ -945,7 +945,7 @@ { "list": [ { - "text": "Monthly maintenance cost: {total_maintenance}", + "text": "Monthly maintenance cost: {total_maintenance}", "placeholders": [ { "placeholder": "total_maintenance", @@ -964,7 +964,7 @@ ] }, { - "text": "Monthly fuel cost: {total_fuel}", + "text": "Monthly fuel cost: {total_fuel}", "placeholders": [ { "placeholder": "total_fuel", @@ -983,7 +983,7 @@ ] }, { - "text": "Total base cost: {total_base}", + "text": "Total base cost: {total_base}", "placeholders": [ { "placeholder": "total_base", @@ -1002,7 +1002,7 @@ ] }, { - "text": "Total running cost: {total_running}", + "text": "Total running cost: {total_running}", "placeholders": [ { "placeholder": "total_running", @@ -1021,7 +1021,7 @@ ] }, { - "text": "Total owning and running cost: {total}", + "text": "Total owning and running cost: {total}", "placeholders": [ { "placeholder": "total", @@ -1040,7 +1040,7 @@ ] }, { - "text": "Paid by debit card: {debit}", + "text": "Paid by debit card: {debit}", "placeholders": [ { "placeholder": "debit", @@ -1059,7 +1059,7 @@ ] }, { - "text": "Paid by credit card: {credit}", + "text": "Paid by credit card: {credit}", "placeholders": [ { "placeholder": "credit", @@ -1078,7 +1078,7 @@ ] }, { - "text": "Paid by other means: {other}", + "text": "Paid by other means: {other}", "placeholders": [ { "placeholder": "other", diff --git a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json index 45fbd983b1..1938d8263c 100644 --- a/schemas/test/en/test_grand_calculated_summary_repeating_answers.json +++ b/schemas/test/en/test_grand_calculated_summary_repeating_answers.json @@ -1313,7 +1313,7 @@ { "list": [ { - "text": "Total household expenditure: {total_expenditure}", + "text": "Total household expenditure: {total_expenditure}", "placeholders": [ { "placeholder": "total_expenditure", @@ -1332,7 +1332,7 @@ ] }, { - "text": "Personal contribution: {personal_contribution}", + "text": "Personal contribution: {personal_contribution}", "placeholders": [ { "placeholder": "personal_contribution", @@ -1351,7 +1351,7 @@ ] }, { - "text": "Total internet usage: {internet_usage}", + "text": "Total internet usage: {internet_usage}", "placeholders": [ { "placeholder": "internet_usage", @@ -1372,7 +1372,7 @@ ] }, { - "text": "Usage by phone: {internet_phone}", + "text": "Usage by phone: {internet_phone}", "placeholders": [ { "placeholder": "internet_phone", @@ -1393,7 +1393,7 @@ ] }, { - "text": "Usage by PC: {internet_pc}", + "text": "Usage by PC: {internet_pc}", "placeholders": [ { "placeholder": "internet_pc", diff --git a/schemas/test/en/test_hub_section_required_with_repeat.json b/schemas/test/en/test_hub_section_required_with_repeat.json index ab13825938..aaff2a9ed3 100644 --- a/schemas/test/en/test_hub_section_required_with_repeat.json +++ b/schemas/test/en/test_hub_section_required_with_repeat.json @@ -406,7 +406,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" }, diff --git a/schemas/test/en/test_individual_response.json b/schemas/test/en/test_individual_response.json index d4154dbd4b..447c3b0915 100644 --- a/schemas/test/en/test_individual_response.json +++ b/schemas/test/en/test_individual_response.json @@ -576,7 +576,7 @@ ] } ], - "text": "Are you {person_name}?" + "text": "Are you {person_name}?" }, "type": "General" }, diff --git a/schemas/test/en/test_individual_response_on_hub_disabled.json b/schemas/test/en/test_individual_response_on_hub_disabled.json index 69a8f80537..8e64f1967b 100644 --- a/schemas/test/en/test_individual_response_on_hub_disabled.json +++ b/schemas/test/en/test_individual_response_on_hub_disabled.json @@ -398,7 +398,7 @@ ] } ], - "text": "Are you {person_name}?" + "text": "Are you {person_name}?" }, "type": "General" }, diff --git a/schemas/test/en/test_interstitial_page_title.json b/schemas/test/en/test_interstitial_page_title.json index 1e8df298c4..12f9b9b766 100644 --- a/schemas/test/en/test_interstitial_page_title.json +++ b/schemas/test/en/test_interstitial_page_title.json @@ -44,7 +44,7 @@ } } ], - "text": "Your RU name: {ru_name}" + "text": "Your RU name: {ru_name}" }, "contents": [ { diff --git a/schemas/test/en/test_introduction.json b/schemas/test/en/test_introduction.json index 130b68eab8..c2ab5babf1 100644 --- a/schemas/test/en/test_introduction.json +++ b/schemas/test/en/test_introduction.json @@ -707,7 +707,7 @@ "title": "Section complete", "contents": [ { - "description": "

      You have successfully completed this section

      The next section covers changes in business strategy and practices, for example, implementing changes to marketing concepts or strategies.

      " + "description": "

      You have successfully completed this section

      The next section covers changes in business strategy and practices, for example, implementing changes to marketing concepts or strategies.

      " } ] } diff --git a/schemas/test/en/test_last_viewed_question_guidance.json b/schemas/test/en/test_last_viewed_question_guidance.json index 037a022254..7b6b183db1 100644 --- a/schemas/test/en/test_last_viewed_question_guidance.json +++ b/schemas/test/en/test_last_viewed_question_guidance.json @@ -395,7 +395,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their ", + "text": "Thinking of {first_person_name}, {second_person_name} is their ", "placeholders": [ { "placeholder": "first_person_name", @@ -463,7 +463,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is {first_person_name_possessive} ", + "text": "{second_person_name} is {first_person_name_possessive} ", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -538,7 +538,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", + "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", "placeholders": [ { "placeholder": "first_person_name", @@ -601,7 +601,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} husband or wife", + "text": "{second_person_name} is {first_person_name_possessive} husband or wife", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -676,7 +676,7 @@ "label": "Son or daughter", "value": "Son or daughter", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", + "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", "placeholders": [ { "placeholder": "first_person_name", @@ -739,7 +739,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} son or daughter", + "text": "{second_person_name} is {first_person_name_possessive} son or daughter", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -814,7 +814,7 @@ "label": "Brother or sister", "value": "Brother or sister", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", + "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", "placeholders": [ { "placeholder": "first_person_name", @@ -877,7 +877,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} brother or sister", + "text": "{second_person_name} is {first_person_name_possessive} brother or sister", "placeholders": [ { "placeholder": "first_person_name_possessive", diff --git a/schemas/test/en/test_list_collector_list_summary.json b/schemas/test/en/test_list_collector_list_summary.json index a7f3a51290..b07504fe6e 100644 --- a/schemas/test/en/test_list_collector_list_summary.json +++ b/schemas/test/en/test_list_collector_list_summary.json @@ -156,7 +156,7 @@ } } ], - "text": "Do you live at {household_address}?" + "text": "Do you live at {household_address}?" }, "answers": [ { @@ -197,7 +197,7 @@ } } ], - "text": "Does anyone else live at {household_address}?" + "text": "Does anyone else live at {household_address}?" }, "answers": [ { @@ -367,7 +367,7 @@ } } ], - "text": "Are there any other visitors staying overnight at {household_address}?" + "text": "Are there any other visitors staying overnight at {household_address}?" }, "answers": [ { diff --git a/schemas/test/en/test_markup.json b/schemas/test/en/test_markup.json index d5256089b7..98bf348532 100644 --- a/schemas/test/en/test_markup.json +++ b/schemas/test/en/test_markup.json @@ -48,7 +48,7 @@ "hide_guidance": "hide lorem ipsum guidance", "contents": [ { - "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet." + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue. Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet." } ] }, @@ -59,10 +59,10 @@ } ], "description": [ - "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur blandit tempus porttitor." + "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur blandit tempus porttitor." ], "id": "question", - "title": "This is a title with emphasis", + "title": "This is a title with emphasis", "type": "General" } } diff --git a/schemas/test/en/test_multiple_piping.json b/schemas/test/en/test_multiple_piping.json index d1dce19adb..a7e56dbb94 100644 --- a/schemas/test/en/test_multiple_piping.json +++ b/schemas/test/en/test_multiple_piping.json @@ -135,7 +135,7 @@ "question": { "id": "multiple-piping-question", "title": { - "text": "Does {person} live at {address}", + "text": "Does {person} live at {address}", "placeholders": [ { "placeholder": "person", diff --git a/schemas/test/en/test_new_calculated_summary.json b/schemas/test/en/test_new_calculated_summary.json index cb70fbf9de..9e10f5cd04 100644 --- a/schemas/test/en/test_new_calculated_summary.json +++ b/schemas/test/en/test_new_calculated_summary.json @@ -391,7 +391,7 @@ { "list": [ { - "text": "Total currency values: {currency_total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { "placeholder": "currency_total", @@ -410,7 +410,7 @@ ] }, { - "text": "Total unformatted unit values: {unit_total}", + "text": "Total unformatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -429,7 +429,7 @@ ] }, { - "text": "Total formatted unit values: {unit_total}", + "text": "Total formatted unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -450,7 +450,7 @@ ] }, { - "text": "Total unformatted percentage values: {percentage_total}", + "text": "Total unformatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -469,7 +469,7 @@ ] }, { - "text": "Total formatted percentage values: {percentage_total}", + "text": "Total formatted percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -488,7 +488,7 @@ ] }, { - "text": "Total number values: {number_total}", + "text": "Total number values: {number_total}", "placeholders": [ { "placeholder": "number_total", diff --git a/schemas/test/en/test_new_calculated_summary_dependent_questions.json b/schemas/test/en/test_new_calculated_summary_dependent_questions.json index b55565346d..ada5453af6 100644 --- a/schemas/test/en/test_new_calculated_summary_dependent_questions.json +++ b/schemas/test/en/test_new_calculated_summary_dependent_questions.json @@ -136,7 +136,7 @@ { "id": "calculated-summary-block", "type": "CalculatedSummary", - "title": "We have calculated your total spend to be %(total)s. Is this correct?", + "title": "We have calculated your total spend to be %(total)s. Is this correct?", "calculation": { "operation": { "+": [ diff --git a/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json index b6b9f73f22..0a8a3ae325 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_and_static_answers.json @@ -551,7 +551,7 @@ { "list": [ { - "text": "Total weekly supermarket spending: {currency_total}", + "text": "Total weekly supermarket spending: {currency_total}", "placeholders": [ { "placeholder": "currency_total", @@ -570,7 +570,7 @@ ] }, { - "text": "Total weekly supermarket visits: {number_total}", + "text": "Total weekly supermarket visits: {number_total}", "placeholders": [ { "placeholder": "number_total", @@ -589,7 +589,7 @@ ] }, { - "text": "Total of supermarket visits by car: {number_total}", + "text": "Total of supermarket visits by car: {number_total}", "placeholders": [ { "placeholder": "number_total", @@ -628,7 +628,7 @@ { "list": [ { - "text": "Total weekly supermarket spending: {currency_total}", + "text": "Total weekly supermarket spending: {currency_total}", "placeholders": [ { "placeholder": "currency_total", @@ -647,7 +647,7 @@ ] }, { - "text": "Total weekly supermarket visits: {number_total}", + "text": "Total weekly supermarket visits: {number_total}", "placeholders": [ { "placeholder": "number_total", @@ -666,7 +666,7 @@ ] }, { - "text": "Total of supermarket visits by car: {number_total}", + "text": "Total of supermarket visits by car: {number_total}", "placeholders": [ { "placeholder": "number_total", @@ -685,7 +685,7 @@ ] }, { - "text": "Total spending on parking: {currency_total}", + "text": "Total spending on parking: {currency_total}", "placeholders": [ { "placeholder": "currency_total", diff --git a/schemas/test/en/test_new_calculated_summary_repeating_section.json b/schemas/test/en/test_new_calculated_summary_repeating_section.json index f66c7626c1..a01eeb68cf 100644 --- a/schemas/test/en/test_new_calculated_summary_repeating_section.json +++ b/schemas/test/en/test_new_calculated_summary_repeating_section.json @@ -669,7 +669,7 @@ { "list": [ { - "text": "Total currency values: {currency_total}", + "text": "Total currency values: {currency_total}", "placeholders": [ { "placeholder": "currency_total", @@ -688,7 +688,7 @@ ] }, { - "text": "Total unit values: {unit_total}", + "text": "Total unit values: {unit_total}", "placeholders": [ { "placeholder": "unit_total", @@ -707,7 +707,7 @@ ] }, { - "text": "Total percentage values: {percentage_total}", + "text": "Total percentage values: {percentage_total}", "placeholders": [ { "placeholder": "percentage_total", @@ -726,7 +726,7 @@ ] }, { - "text": "Total number values: {number_total}", + "text": "Total number values: {number_total}", "placeholders": [ { "placeholder": "number_total", diff --git a/schemas/test/en/test_placeholder_default_value.json b/schemas/test/en/test_placeholder_default_value.json index c728152865..227ab461bd 100644 --- a/schemas/test/en/test_placeholder_default_value.json +++ b/schemas/test/en/test_placeholder_default_value.json @@ -79,7 +79,7 @@ "contents": [ { "description": { - "text": "The total number of employees confirmed are {answer_employee} , now next question is about training budget", + "text": "The total number of employees confirmed are {answer_employee} , now next question is about training budget", "placeholders": [ { "placeholder": "answer_employee", @@ -136,7 +136,7 @@ "contents": [ { "description": { - "text": "The average training budget per employee is {answer_emp_training}", + "text": "The average training budget per employee is {answer_emp_training}", "placeholders": [ { "placeholder": "answer_emp_training", diff --git a/schemas/test/en/test_placeholder_option_label_from_value.json b/schemas/test/en/test_placeholder_option_label_from_value.json index 6e2227e41b..79102c41e6 100644 --- a/schemas/test/en/test_placeholder_option_label_from_value.json +++ b/schemas/test/en/test_placeholder_option_label_from_value.json @@ -103,7 +103,7 @@ "question": { "id": "confirmation-question-radio", "title": { - "text": "You chose {business_name} as your business name, is that correct ?", + "text": "You chose {business_name} as your business name, is that correct ?", "placeholders": [ { "placeholder": "business_name", diff --git a/schemas/test/en/test_placeholder_transform.json b/schemas/test/en/test_placeholder_transform.json index 4ad87b158d..78f2bfb63e 100644 --- a/schemas/test/en/test_placeholder_transform.json +++ b/schemas/test/en/test_placeholder_transform.json @@ -108,7 +108,7 @@ "title": "Please enter the value of internet sales", "description": [ { - "text": "Of the {total_turnover} total retail turnover, what was the value of internet sales?", + "text": "Of the {total_turnover} total retail turnover, what was the value of internet sales?", "placeholders": [ { "placeholder": "total_turnover", @@ -221,7 +221,7 @@ ] } ], - "text": "Do you want to add {item} item?" + "text": "Do you want to add {item} item?" }, "type": "General" }, @@ -253,7 +253,7 @@ "contents": [ { "description": { - "text": "The percentage of the company budget you spend on training is {answer_percentage}", + "text": "The percentage of the company budget you spend on training is {answer_percentage}", "placeholders": [ { "placeholder": "answer_percentage", @@ -303,7 +303,7 @@ "contents": [ { "description": { - "text": "The average commuting distance of an employee is {answer_distance}", + "text": "The average commuting distance of an employee is {answer_distance}", "placeholders": [ { "placeholder": "answer_distance", diff --git a/schemas/test/en/test_progress_block_value_source_repeating_sections.json b/schemas/test/en/test_progress_block_value_source_repeating_sections.json index 605565a303..764e05b6ea 100644 --- a/schemas/test/en/test_progress_block_value_source_repeating_sections.json +++ b/schemas/test/en/test_progress_block_value_source_repeating_sections.json @@ -316,7 +316,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -366,7 +366,7 @@ ] } ], - "text": "Random question about {person_name_possessive}" + "text": "Random question about {person_name_possessive}" }, "description": ["Shows because the random question was completed in section 1"], "type": "General" diff --git a/schemas/test/en/test_progress_section_value_source_repeating_sections.json b/schemas/test/en/test_progress_section_value_source_repeating_sections.json index 4f7c41b55c..dcdfad3364 100644 --- a/schemas/test/en/test_progress_section_value_source_repeating_sections.json +++ b/schemas/test/en/test_progress_section_value_source_repeating_sections.json @@ -316,7 +316,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -366,7 +366,7 @@ ] } ], - "text": "Random question about {person_name_possessive}" + "text": "Random question about {person_name_possessive}" }, "description": ["Shows because the random question was completed in section 1"], "type": "General" diff --git a/schemas/test/en/test_progress_value_source_calculated_summary.json b/schemas/test/en/test_progress_value_source_calculated_summary.json index b8e3ff32ba..be36064f2b 100644 --- a/schemas/test/en/test_progress_value_source_calculated_summary.json +++ b/schemas/test/en/test_progress_value_source_calculated_summary.json @@ -378,7 +378,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -428,7 +428,7 @@ ] } ], - "text": "Random question about {person_name_possessive}" + "text": "Random question about {person_name_possessive}" }, "description": ["Shows because the calculated summary was completed in section 1"], "type": "General" @@ -491,7 +491,7 @@ ] } ], - "text": "Another random question about {person_name_possessive}" + "text": "Another random question about {person_name_possessive}" }, "description": ["Shows because block 2 of this repeating section was completed."], "type": "General" diff --git a/schemas/test/en/test_progress_value_source_calculated_summary_extended.json b/schemas/test/en/test_progress_value_source_calculated_summary_extended.json index 9a11b9b5f9..caac2ba203 100644 --- a/schemas/test/en/test_progress_value_source_calculated_summary_extended.json +++ b/schemas/test/en/test_progress_value_source_calculated_summary_extended.json @@ -52,10 +52,10 @@ "description": "This schema was created in order to ensure that dependencies based on a progress value sources captured in order." }, { - "description": "It is also being used to test progress value sources with chained dependents. For example, In this schema, Sections 7, 8, 9 and 10 are all dependent on Section 2, and Sections 11 and 12 are dependent on Section 9 and 10." + "description": "It is also being used to test progress value sources with chained dependents. For example, In this schema, Sections 7, 8, 9 and 10 are all dependent on Section 2, and Sections 11 and 12 are dependent on Section 9 and 10." }, { - "description": "So we can use this schema to test journeys and ensure that all dependent sections are updated. For example if we had not started Section 2 yet, but Sections 8, 9 and 10 are all Complete, and sections 11 and 12 are Partially Completed. Given the dependencies in this schema, completing Section 2 would mean that the status of Sections 8, 9 and 10 would change to Partially Complete and Sections 11 and 12 to Complete." + "description": "So we can use this schema to test journeys and ensure that all dependent sections are updated. For example if we had not started Section 2 yet, but Sections 8, 9 and 10 are all Complete, and sections 11 and 12 are Partially Completed. Given the dependencies in this schema, completing Section 2 would mean that the status of Sections 8, 9 and 10 would change to Partially Complete and Sections 11 and 12 to Complete." } ] }, @@ -410,7 +410,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -460,7 +460,7 @@ ] } ], - "text": "Random question about {person_name_possessive}" + "text": "Random question about {person_name_possessive}" }, "description": ["Shows because the calculated summary was completed in section 1"], "type": "General" @@ -523,7 +523,7 @@ ] } ], - "text": "Another random question about {person_name_possessive}" + "text": "Another random question about {person_name_possessive}" }, "description": ["Shows because block 2 of this repeating section was completed."], "type": "General" diff --git a/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json b/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json index 5e53c28755..390821f170 100644 --- a/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json +++ b/schemas/test/en/test_progress_value_source_repeating_sections_chained_dependencies.json @@ -414,7 +414,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } @@ -464,7 +464,7 @@ ] } ], - "text": "Random question about {person_name_possessive}" + "text": "Random question about {person_name_possessive}" }, "description": ["Shows because section 2 was completed"], "type": "General" diff --git a/schemas/test/en/test_question_definition.json b/schemas/test/en/test_question_definition.json index b7d118cae1..9233ebff3d 100644 --- a/schemas/test/en/test_question_definition.json +++ b/schemas/test/en/test_question_definition.json @@ -41,7 +41,7 @@ "id": "definition-block", "question": { "id": "question", - "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", + "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", "type": "General", "definition": { "title": "What is a photovoltaic system?", diff --git a/schemas/test/en/test_question_definition_array_type.json b/schemas/test/en/test_question_definition_array_type.json index 6c7c7ec1b2..f691d55993 100644 --- a/schemas/test/en/test_question_definition_array_type.json +++ b/schemas/test/en/test_question_definition_array_type.json @@ -41,7 +41,7 @@ "id": "definition-block", "question": { "id": "question", - "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", + "title": "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?", "type": "General", "definitions": [ { diff --git a/schemas/test/en/test_relationships.json b/schemas/test/en/test_relationships.json index f2019bff4d..9054e842a7 100644 --- a/schemas/test/en/test_relationships.json +++ b/schemas/test/en/test_relationships.json @@ -193,7 +193,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their ", + "text": "Thinking of {first_person_name}, {second_person_name} is their ", "placeholders": [ { "placeholder": "first_person_name", @@ -261,7 +261,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is {first_person_name_possessive} ", + "text": "{second_person_name} is {first_person_name_possessive} ", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -336,7 +336,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", + "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", "placeholders": [ { "placeholder": "first_person_name", @@ -399,7 +399,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} husband or wife", + "text": "{second_person_name} is {first_person_name_possessive} husband or wife", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -474,7 +474,7 @@ "label": "Legally registered civil partner", "value": "Legally registered civil partner", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", + "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name", @@ -537,7 +537,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", + "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -612,7 +612,7 @@ "label": "Son or daughter", "value": "Son or daughter", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", + "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", "placeholders": [ { "placeholder": "first_person_name", @@ -675,7 +675,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} son or daughter", + "text": "{second_person_name} is {first_person_name_possessive} son or daughter", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -750,7 +750,7 @@ "label": "Brother or sister", "value": "Brother or sister", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", + "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", "placeholders": [ { "placeholder": "first_person_name", @@ -813,7 +813,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} brother or sister", + "text": "{second_person_name} is {first_person_name_possessive} brother or sister", "placeholders": [ { "placeholder": "first_person_name_possessive", diff --git a/schemas/test/en/test_relationships_primary.json b/schemas/test/en/test_relationships_primary.json index 73ca1d5ed9..2fe0d5149f 100644 --- a/schemas/test/en/test_relationships_primary.json +++ b/schemas/test/en/test_relationships_primary.json @@ -249,7 +249,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "{second_person_name} is your ", + "text": "{second_person_name} is your ", "placeholders": [ { "placeholder": "second_person_name", @@ -288,7 +288,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is your ", + "text": "{second_person_name} is your ", "placeholders": [ { "placeholder": "second_person_name", @@ -326,7 +326,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "{second_person_name} is your husband or wife", + "text": "{second_person_name} is your husband or wife", "placeholders": [ { "placeholder": "second_person_name", @@ -360,7 +360,7 @@ ] }, "playback": { - "text": "{second_person_name} is your husband or wife", + "text": "{second_person_name} is your husband or wife", "placeholders": [ { "placeholder": "second_person_name", @@ -398,7 +398,7 @@ "label": "Legally registered civil partner", "value": "Legally registered civil partner", "title": { - "text": "{second_person_name} is your legally registered civil partner", + "text": "{second_person_name} is your legally registered civil partner", "placeholders": [ { "placeholder": "second_person_name", @@ -432,7 +432,7 @@ ] }, "playback": { - "text": "{second_person_name} is your legally registered civil partner", + "text": "{second_person_name} is your legally registered civil partner", "placeholders": [ { "placeholder": "second_person_name", @@ -470,7 +470,7 @@ "label": "Son or daughter", "value": "Son or daughter", "title": { - "text": "{second_person_name} is your son or daughter", + "text": "{second_person_name} is your son or daughter", "placeholders": [ { "placeholder": "second_person_name", @@ -504,7 +504,7 @@ ] }, "playback": { - "text": "{second_person_name} is your son or daughter", + "text": "{second_person_name} is your son or daughter", "placeholders": [ { "placeholder": "second_person_name", @@ -542,7 +542,7 @@ "label": "Brother or sister", "value": "Brother or sister", "title": { - "text": "{second_person_name} is your brother or sister", + "text": "{second_person_name} is your brother or sister", "placeholders": [ { "placeholder": "second_person_name", @@ -576,7 +576,7 @@ ] }, "playback": { - "text": "{second_person_name} is your brother or sister", + "text": "{second_person_name} is your brother or sister", "placeholders": [ { "placeholder": "second_person_name", @@ -633,7 +633,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their ", + "text": "Thinking of {first_person_name}, {second_person_name} is their ", "placeholders": [ { "placeholder": "first_person_name", @@ -701,7 +701,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is {first_person_name_possessive} ", + "text": "{second_person_name} is {first_person_name_possessive} ", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -776,7 +776,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", + "text": "Thinking of {first_person_name}, {second_person_name} is their husband or wife", "placeholders": [ { "placeholder": "first_person_name", @@ -839,7 +839,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} husband or wife", + "text": "{second_person_name} is {first_person_name_possessive} husband or wife", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -914,7 +914,7 @@ "label": "Legally registered civil partner", "value": "Legally registered civil partner", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", + "text": "Thinking of {first_person_name}, {second_person_name} is their legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name", @@ -977,7 +977,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", + "text": "{second_person_name} is {first_person_name_possessive} legally registered civil partner", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -1052,7 +1052,7 @@ "label": "Son or daughter", "value": "Son or daughter", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", + "text": "Thinking of {first_person_name}, {second_person_name} is their son or daughter", "placeholders": [ { "placeholder": "first_person_name", @@ -1115,7 +1115,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} son or daughter", + "text": "{second_person_name} is {first_person_name_possessive} son or daughter", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -1190,7 +1190,7 @@ "label": "Brother or sister", "value": "Brother or sister", "title": { - "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", + "text": "Thinking of {first_person_name}, {second_person_name} is their brother or sister", "placeholders": [ { "placeholder": "first_person_name", @@ -1253,7 +1253,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} brother or sister", + "text": "{second_person_name} is {first_person_name_possessive} brother or sister", "placeholders": [ { "placeholder": "first_person_name_possessive", diff --git a/schemas/test/en/test_relationships_unrelated.json b/schemas/test/en/test_relationships_unrelated.json index e535e4b349..ea03d61774 100644 --- a/schemas/test/en/test_relationships_unrelated.json +++ b/schemas/test/en/test_relationships_unrelated.json @@ -193,7 +193,7 @@ "id": "relationship-question", "type": "General", "title": { - "text": "Thinking about {first_person_name}, {second_person_name} is their ", + "text": "Thinking about {first_person_name}, {second_person_name} is their ", "placeholders": [ { "placeholder": "first_person_name", @@ -261,7 +261,7 @@ "mandatory": true, "type": "Relationship", "playback": { - "text": "{second_person_name} is {first_person_name_possessive} ", + "text": "{second_person_name} is {first_person_name_possessive} ", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -336,7 +336,7 @@ "label": "Husband or Wife", "value": "Husband or Wife", "title": { - "text": "Thinking about {first_person_name}, {second_person_name} is their husband or wife", + "text": "Thinking about {first_person_name}, {second_person_name} is their husband or wife", "placeholders": [ { "placeholder": "first_person_name", @@ -399,7 +399,7 @@ ] }, "playback": { - "text": "{second_person_name} is {first_person_name_possessive} husband or wife", + "text": "{second_person_name} is {first_person_name_possessive} husband or wife", "placeholders": [ { "placeholder": "first_person_name_possessive", @@ -474,7 +474,7 @@ "label": "Unrelated", "value": "Unrelated", "title": { - "text": "Thinking about {first_person_name}, {second_person_name} is unrelated to {first_person_name}", + "text": "Thinking about {first_person_name}, {second_person_name} is unrelated to {first_person_name}", "placeholders": [ { "placeholder": "first_person_name", @@ -537,7 +537,7 @@ ] }, "playback": { - "text": "{second_person_name} is unrelated to {first_person_name}", + "text": "{second_person_name} is unrelated to {first_person_name}", "placeholders": [ { "placeholder": "first_person_name", @@ -740,7 +740,7 @@ ] } ], - "text": "Are any of these people related to {person_name}?" + "text": "Are any of these people related to {person_name}?" }, "guidance": { "contents": [ diff --git a/schemas/test/en/test_repeating_section_summaries.json b/schemas/test/en/test_repeating_section_summaries.json index c8ec5c3ca7..3ba64c74a9 100644 --- a/schemas/test/en/test_repeating_section_summaries.json +++ b/schemas/test/en/test_repeating_section_summaries.json @@ -386,7 +386,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" }, diff --git a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json index 224a7bb5cc..3d3e74cb38 100644 --- a/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json +++ b/schemas/test/en/test_repeating_sections_with_hub_and_spoke.json @@ -703,7 +703,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" }, @@ -1068,7 +1068,7 @@ ] } ], - "text": "What is {person_name_possessive} sex?" + "text": "What is {person_name_possessive} sex?" }, "type": "General" }, @@ -1181,7 +1181,7 @@ ] } ], - "text": "What is {person_name_possessive} sex?" + "text": "What is {person_name_possessive} sex?" }, "type": "General" }, @@ -1299,7 +1299,7 @@ ] } ], - "text": "What is {person_name_possessive} date of birth?" + "text": "What is {person_name_possessive} date of birth?" }, "type": "General" } diff --git a/schemas/test/en/test_routing_and.json b/schemas/test/en/test_routing_and.json index 6d27ecc22c..89f0ea82b3 100644 --- a/schemas/test/en/test_routing_and.json +++ b/schemas/test/en/test_routing_and.json @@ -114,7 +114,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 and 321 but you actually entered {answer_1} and {answer_2}.", + "text": "You were asked to enter 123 and 321 but you actually entered {answer_1} and {answer_2}.", "placeholders": [ { "placeholder": "answer_1", @@ -149,7 +149,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 and 321 and you entered {answer_1} and {answer_2}.", + "text": "You were asked to enter 123 and 321 and you entered {answer_1} and {answer_2}.", "placeholders": [ { "placeholder": "answer_1", diff --git a/schemas/test/en/test_routing_checkbox_count.json b/schemas/test/en/test_routing_checkbox_count.json index 67bba4897c..cd351370e2 100644 --- a/schemas/test/en/test_routing_checkbox_count.json +++ b/schemas/test/en/test_routing_checkbox_count.json @@ -103,7 +103,7 @@ "contents": [ { "description": { - "text": "You were asked to select 2 or more toppings but you actually selected {answer_count}.", + "text": "You were asked to select 2 or more toppings but you actually selected {answer_count}.", "placeholders": [ { "placeholder": "answer_count", @@ -138,7 +138,7 @@ "contents": [ { "description": { - "text": "You were asked to select 2 or more toppings and you selected {answer_count}.", + "text": "You were asked to select 2 or more toppings and you selected {answer_count}.", "placeholders": [ { "placeholder": "answer_count", diff --git a/schemas/test/en/test_routing_number_equals.json b/schemas/test/en/test_routing_number_equals.json index 6338404ceb..83c33bf109 100644 --- a/schemas/test/en/test_routing_number_equals.json +++ b/schemas/test/en/test_routing_number_equals.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 but you actually entered {answer}.", + "text": "You were asked to enter 123 but you actually entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 and you entered {answer}.", + "text": "You were asked to enter 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_greater_than.json b/schemas/test/en/test_routing_number_greater_than.json index da375ab8f8..1e5f8552b5 100644 --- a/schemas/test/en/test_routing_number_greater_than.json +++ b/schemas/test/en/test_routing_number_greater_than.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than 123 but you actually entered {answer}.", + "text": "You were asked to enter a number greater than 123 but you actually entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than 123 and you entered {answer}.", + "text": "You were asked to enter a number greater than 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_greater_than_or_equal.json b/schemas/test/en/test_routing_number_greater_than_or_equal.json index 39c5df0825..b0096acdf8 100644 --- a/schemas/test/en/test_routing_number_greater_than_or_equal.json +++ b/schemas/test/en/test_routing_number_greater_than_or_equal.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than or equal to 123 but you entered {answer}.", + "text": "You were asked to enter a number greater than or equal to 123 but you entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than or equal to 123 and you entered {answer}.", + "text": "You were asked to enter a number greater than or equal to 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json b/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json index c4896eb621..b16a50963d 100644 --- a/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json +++ b/schemas/test/en/test_routing_number_greater_than_or_equal_single_condition.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than or equal to 123 but you entered {answer}.", + "text": "You were asked to enter a number greater than or equal to 123 but you entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number greater than or equal to 123 and you entered {answer}.", + "text": "You were asked to enter a number greater than or equal to 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_less_than.json b/schemas/test/en/test_routing_number_less_than.json index 5036a036b9..ed5eec019f 100644 --- a/schemas/test/en/test_routing_number_less_than.json +++ b/schemas/test/en/test_routing_number_less_than.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than 123 but you actually entered {answer}.", + "text": "You were asked to enter a number less than 123 but you actually entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than 123 and you entered {answer}.", + "text": "You were asked to enter a number less than 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_less_than_or_equal.json b/schemas/test/en/test_routing_number_less_than_or_equal.json index 6953c80bc6..d45041fd8a 100644 --- a/schemas/test/en/test_routing_number_less_than_or_equal.json +++ b/schemas/test/en/test_routing_number_less_than_or_equal.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than or equal to 123 but you entered {answer}.", + "text": "You were asked to enter a number less than or equal to 123 but you entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than or equal to 123 and you entered {answer}.", + "text": "You were asked to enter a number less than or equal to 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json b/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json index 6948cdd64e..ef8ba8b256 100644 --- a/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json +++ b/schemas/test/en/test_routing_number_less_than_or_equal_single_condition.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than or equal to 123 but you entered {answer}.", + "text": "You were asked to enter a number less than or equal to 123 but you entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked to enter a number less than or equal to 123 and you entered {answer}.", + "text": "You were asked to enter a number less than or equal to 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_number_not_equals.json b/schemas/test/en/test_routing_number_not_equals.json index c400bcf04a..e0c7505133 100644 --- a/schemas/test/en/test_routing_number_not_equals.json +++ b/schemas/test/en/test_routing_number_not_equals.json @@ -77,7 +77,7 @@ "contents": [ { "description": { - "text": "You were asked not to enter 123 but you entered {answer}.", + "text": "You were asked not to enter 123 but you entered {answer}.", "placeholders": [ { "placeholder": "answer", @@ -105,7 +105,7 @@ "contents": [ { "description": { - "text": "You were asked not to enter 123 and you entered {answer}.", + "text": "You were asked not to enter 123 and you entered {answer}.", "placeholders": [ { "placeholder": "answer", diff --git a/schemas/test/en/test_routing_or.json b/schemas/test/en/test_routing_or.json index 8e5984ffc4..0b888a557c 100644 --- a/schemas/test/en/test_routing_or.json +++ b/schemas/test/en/test_routing_or.json @@ -114,7 +114,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 or 321 but you actually entered {answer_1} and {answer_2}.", + "text": "You were asked to enter 123 or 321 but you actually entered {answer_1} and {answer_2}.", "placeholders": [ { "placeholder": "answer_1", @@ -149,7 +149,7 @@ "contents": [ { "description": { - "text": "You were asked to enter 123 or 321 and you entered {answer_1} and {answer_2}.", + "text": "You were asked to enter 123 or 321 and you entered {answer_1} and {answer_2}.", "placeholders": [ { "placeholder": "answer_1", diff --git a/schemas/test/en/test_submit_with_summary.json b/schemas/test/en/test_submit_with_summary.json index f43a9105e8..3a8a3af974 100644 --- a/schemas/test/en/test_submit_with_summary.json +++ b/schemas/test/en/test_submit_with_summary.json @@ -109,7 +109,7 @@ "question": { "id": "test-dessert-confirmation-question", "title": { - "text": "Are you sure {dessert} is your favourite?", + "text": "Are you sure {dessert} is your favourite?", "placeholders": [ { "placeholder": "dessert", diff --git a/schemas/test/en/test_titles_radio_and_checkbox.json b/schemas/test/en/test_titles_radio_and_checkbox.json index 98b10beb8c..e40074c8db 100644 --- a/schemas/test/en/test_titles_radio_and_checkbox.json +++ b/schemas/test/en/test_titles_radio_and_checkbox.json @@ -81,7 +81,7 @@ "question": { "id": "checkbox-question", "type": "General", - "title": "Did Peter make changes to this business?", + "title": "Did Peter make changes to this business?", "answers": [ { "id": "checkbox-answer", @@ -122,7 +122,7 @@ "question": { "id": "checkbox-question", "type": "General", - "title": "Did Mary make changes to this business?", + "title": "Did Mary make changes to this business?", "answers": [ { "id": "checkbox-answer", @@ -210,7 +210,7 @@ "question": { "id": "radio-question", "type": "General", - "title": "Is Peter the boss?", + "title": "Is Peter the boss?", "answers": [ { "id": "radio-answer", @@ -255,7 +255,7 @@ "question": { "id": "radio-question", "type": "General", - "title": "Is Mary the boss?", + "title": "Is Mary the boss?", "answers": [ { "id": "radio-answer", @@ -301,7 +301,7 @@ "id": "radio-question", "type": "General", "title": { - "text": "Is {name} the boss?", + "text": "Is {name} the boss?", "placeholders": [ { "placeholder": "name", diff --git a/schemas/test/en/test_validation_sum_against_total_hub_with_dependent_section.json b/schemas/test/en/test_validation_sum_against_total_hub_with_dependent_section.json index 3beb84110c..7001c9d763 100644 --- a/schemas/test/en/test_validation_sum_against_total_hub_with_dependent_section.json +++ b/schemas/test/en/test_validation_sum_against_total_hub_with_dependent_section.json @@ -138,7 +138,7 @@ "question": { "id": "turnover-breakdown-question", "title": { - "text": "Please breakdown your total turnover of {total_turnover}", + "text": "Please breakdown your total turnover of {total_turnover}", "placeholders": [ { "placeholder": "total_turnover", @@ -200,7 +200,7 @@ "question": { "id": "employees-breakdown-question", "title": { - "text": "Please breakdown your number of employees of {total_employees}", + "text": "Please breakdown your number of employees of {total_employees}", "placeholders": [ { "placeholder": "total_employees", diff --git a/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json b/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json index ce2a486b0c..faa5d77f3f 100644 --- a/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json +++ b/schemas/test/en/test_validation_sum_against_total_repeating_with_dependent_section.json @@ -364,7 +364,7 @@ "question": { "id": "spending-breakdown-question", "title": { - "text": "How do you spending the monthly budget of {total_spending}?", + "text": "How do you spending the monthly budget of {total_spending}?", "placeholders": [ { "placeholder": "total_spending", @@ -426,7 +426,7 @@ "question": { "id": "second-spending-breakdown-question", "title": { - "text": "How do you spend the monthly entertainment budget of {entertainment}?", + "text": "How do you spend the monthly entertainment budget of {entertainment}?", "placeholders": [ { "placeholder": "entertainment", diff --git a/schemas/test/en/test_variants_question.json b/schemas/test/en/test_variants_question.json index 9bedff415e..17303c9df2 100644 --- a/schemas/test/en/test_variants_question.json +++ b/schemas/test/en/test_variants_question.json @@ -66,7 +66,7 @@ "question": { "id": "proxy-question", "title": { - "text": "Are you {person_name}?", + "text": "Are you {person_name}?", "placeholders": [ { "placeholder": "person_name", @@ -157,7 +157,7 @@ "id": "age-question", "type": "General", "title": { - "text": "What age is {person_name}?", + "text": "What age is {person_name}?", "placeholders": [ { "placeholder": "person_name", @@ -304,7 +304,7 @@ "id": "age-confirmation-question", "type": "General", "title": { - "text": "{person_name} is over 16?", + "text": "{person_name} is over 16?", "placeholders": [ { "placeholder": "person_name", @@ -375,7 +375,7 @@ "id": "age-confirmation-question", "type": "General", "title": { - "text": "{person_name} is under 16?", + "text": "{person_name} is under 16?", "placeholders": [ { "placeholder": "person_name", diff --git a/templates/confirmation-email-sent.html b/templates/confirmation-email-sent.html index 87ccd55535..b57cd431f9 100644 --- a/templates/confirmation-email-sent.html +++ b/templates/confirmation-email-sent.html @@ -11,7 +11,7 @@ {% call onsPanel({ "spacious": true, - "type": "success", + "variant": "success", "classes": "ons-u-mb-s", "iconType": "check", "iconSize": "xl" @@ -23,13 +23,13 @@

      {% endcall %} {% call onsPanel({ - "type": "bare", + "variant": "bare", "classes": "ons-u-mb-s", "iconType": "lock" }) %}

      - {{ _("The email will be sent from census.2021@notifications.service.gov.uk") }} + {{ _("The email will be sent from census.2021@notifications.service.gov.uk") }}

      {% endcall %} {# djlint:on #} diff --git a/templates/feedback-sent.html b/templates/feedback-sent.html index f4f098a3e6..7ed1d9cfe0 100644 --- a/templates/feedback-sent.html +++ b/templates/feedback-sent.html @@ -12,7 +12,7 @@ {% call onsPanel({ "spacious": true, - "type": "success", + "variant": "success", "classes": "ons-u-mb-s", "iconType": "check", "iconSize": "xl" diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index 58cfbc426c..bb74687735 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -9,7 +9,7 @@ {# djlint:off #} {% call onsPanel({ - "type": "success", + "variant": "success", "classes": "ons-u-mb-m", "iconType": "check", "iconSize": "l" diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index 3989b5199a..1e62894bd2 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -9,7 +9,7 @@ {# djlint:off #} {% call onsPanel({ - "type": "success", + "variant": "success", "classes": "ons-u-mb-m", "iconType": "check", "iconSize": "l" @@ -25,13 +25,13 @@ {% endcall %} {% call onsPanel({ - "type": "bare", + "variant": "bare", "classes": "ons-u-mb-s", "iconType": "lock" }) %}

      - {{ _("The text will be sent from Census2021") }} + {{ _("The text will be sent from Census2021") }}

      {% endcall %} {{ diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html index 721995f1d7..7df4a3bf19 100644 --- a/templates/layouts/_calculatedsummary.html +++ b/templates/layouts/_calculatedsummary.html @@ -1,7 +1,6 @@ {% extends "layouts/_questionnaire.html" %} {% from "components/button/_macro.njk" import onsButton %} -{% from "components/panel/_macro.njk" import onsPanel %} {% set save_on_signout = true %} diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index dfd87cfa18..6ca899bfdd 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -13,7 +13,7 @@

      {{ content.title }}

      {% block post_title_panel %} {% if content.warning %} {# djlint:off #} - {% call onsPanel({ "type": "warn"}) %} + {% call onsPanel({ "variant": "warn"}) %}

      {{ content.warning }}

      {% endcall %} {# djlint:on #} diff --git a/templates/macros/helpers.html b/templates/macros/helpers.html index 0db5b281f7..6283c21509 100644 --- a/templates/macros/helpers.html +++ b/templates/macros/helpers.html @@ -12,5 +12,5 @@ {%- endfor -%} {%- endmacro -%} {%- macro interviewer_note(title=None) -%} - {{ _("Interviewer note:") }}{{ title }} + {{ _("Interviewer note:") }}{{ " " + title }} {%- endmacro -%} diff --git a/templates/multiple_survey.html b/templates/multiple_survey.html index 8b053290da..40efd3cb98 100644 --- a/templates/multiple_survey.html +++ b/templates/multiple_survey.html @@ -9,7 +9,7 @@

      {{ _("Information") }}

      {# djlint:off #} {% call onsPanel({ - "type": "error", + "variant": "error", "spacious": true, "attributes": { "data-qa": "multiple-survey-error" diff --git a/templates/partials/error-panel.html b/templates/partials/error-panel.html index 1fdb8e96c9..7d4c4dad9e 100644 --- a/templates/partials/error-panel.html +++ b/templates/partials/error-panel.html @@ -15,7 +15,7 @@ {% endfor %} {% call onsPanel({ - "type": "error", + "variant": "error", "classes": "ons-u-mb-s", "title": error_title, "attributes": { diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index 6bdcb36b74..ebec7af9d0 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -15,7 +15,7 @@ %}

      - {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }} + {{ _("You can share your household access code with the people you live with so they can complete their own sections.") }}

      {{ _("If this is not possible, there are other ways each person can complete their own census.").format(url=content.individual_response_url) }} diff --git a/templates/signed-out.html b/templates/signed-out.html index 38a50bd356..2b8542b5bb 100644 --- a/templates/signed-out.html +++ b/templates/signed-out.html @@ -9,7 +9,7 @@ {{ onsPanel({ "id": '', - "type": 'success', + "variant": 'success', "iconType": 'check', "iconSize": 'xl', "classes": "ons-u-mb-xl", diff --git a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js index d1c2befe03..b089da7571 100644 --- a/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js +++ b/tests/functional/spec/components/checkbox/mutually_exclusive/mutually_exclusive_month_year_date.spec.js @@ -10,7 +10,7 @@ describe("Component: Mutually Exclusive Month Year Date With Single Checkbox Ove }); describe("Given the user has entered a value for the non-exclusive month year date answer", () => { - it("When then user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { + it("When the user clicks the mutually exclusive checkbox answer, Then only the mutually exclusive checkbox should be answered.", async () => { // Given await $(MonthYearDatePage.monthYearDateMonth()).setValue("3"); await $(MonthYearDatePage.monthYearDateYear()).setValue("2018"); diff --git a/tests/integration/questionnaire/test_questionnaire_array_type_definition.py b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py index 49f73df4ae..9cb3c9d2d1 100644 --- a/tests/integration/questionnaire/test_questionnaire_array_type_definition.py +++ b/tests/integration/questionnaire/test_questionnaire_array_type_definition.py @@ -9,7 +9,7 @@ def test_question_definition(self): # When I start the survey I am presented with the definitions title and content correctly self.assertInBody( - "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?" + "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?" ) self.assertInBody("What is a photovoltaic system?") diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 2f9e8bf3bc..63112c7413 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -77,7 +77,7 @@ def test_calculated_summary_total_playback(self): self.post() self.post() self.post() - self.assertInBody("Total currency values: £80.00") + self.assertInBody("Total currency values: £80.00") def test_new_calculated_summary_no_skip(self): self.launchSurveyV2(schema_name="test_new_calculated_summary") @@ -333,7 +333,7 @@ def test_calculated_summary_default_decimal_places(self): "We calculate the total of currency values entered to be £120.00" ) self.post() - self.assertInBody("Total currency values: £120.00") + self.assertInBody("Total currency values: £120.00") def test_calculated_summary_with_varying_decimal_places(self): """ @@ -357,7 +357,7 @@ def test_calculated_summary_with_varying_decimal_places(self): "We calculate the total of currency values entered to be £120.58985" ) self.post() - self.assertInBody("Total currency values: £120.58985") + self.assertInBody("Total currency values: £120.58985") def test_placeholder_rendering_in_calculated_summary_label(self): """ diff --git a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py index f7af00c001..979ff3baa6 100644 --- a/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_grand_calculated_summary.py @@ -270,14 +270,14 @@ def test_grand_calculated_summary_inside_repeating_section(self): ) self.post() self.post({"pay-debit": "110", "pay-credit": "120", "pay-other": "100"}) - self.assertInBody("Monthly maintenance cost: £100.00") - self.assertInBody("Monthly fuel cost: £80.00") - self.assertInBody("Total base cost: £150.00") - self.assertInBody("Total running cost: £180.00") - self.assertInBody("Total owning and running cost: £330.00") - self.assertInBody("Paid by debit card: £110.00") - self.assertInBody("Paid by credit card: £120.00") - self.assertInBody("Paid by other means: £100.00") + self.assertInBody("Monthly maintenance cost: £100.00") + self.assertInBody("Monthly fuel cost: £80.00") + self.assertInBody("Total base cost: £150.00") + self.assertInBody("Total running cost: £180.00") + self.assertInBody("Total owning and running cost: £330.00") + self.assertInBody("Paid by debit card: £110.00") + self.assertInBody("Paid by credit card: £120.00") + self.assertInBody("Paid by other means: £100.00") self.post() self.post() self.post() diff --git a/tests/integration/questionnaire/test_questionnaire_html_escaping.py b/tests/integration/questionnaire/test_questionnaire_html_escaping.py index ea762cd401..dc8b241fe1 100644 --- a/tests/integration/questionnaire/test_questionnaire_html_escaping.py +++ b/tests/integration/questionnaire/test_questionnaire_html_escaping.py @@ -73,11 +73,11 @@ def test_relationships(self): # https://stackoverflow.com/questions/11224362/getattributename-unescapes-html # pylint: disable=line-too-long assert ( - 'data-title="Thinking of &#34;&gt;&lt;b&gt;some html&lt;/b&gt; Jones, Dave Jones is their <em>brother or sister</em>"' + 'data-title="Thinking of &#34;&gt;&lt;b&gt;some html&lt;/b&gt; Jones, Dave Jones is their <strong>brother or sister</strong>"' in self.getResponseData() ) assert ( - 'data-playback="Dave Jones is &#34;&gt;&lt;b&gt;some html&lt;/b&gt; Jones’ <em>brother or sister</em>"' + 'data-playback="Dave Jones is &#34;&gt;&lt;b&gt;some html&lt;/b&gt; Jones’ <strong>brother or sister</strong>"' in self.getResponseData() ) @@ -144,7 +144,7 @@ def test_radio_mandatory_error_with_placeholders(self): self.post() expected_question_text = ( - f"Are you sure {ESCAPED_CONTENT} is your favourite?" + f"Are you sure {ESCAPED_CONTENT} is your favourite?" ) expected_error_message = f'Select an answer to ‘Are you sure {ESCAPED_CONTENT} is your favourite?’' assert expected_question_text in self.getResponseData() diff --git a/tests/integration/questionnaire/test_questionnaire_hub.py b/tests/integration/questionnaire/test_questionnaire_hub.py index b5323aabfe..2024246c87 100644 --- a/tests/integration/questionnaire/test_questionnaire_hub.py +++ b/tests/integration/questionnaire/test_questionnaire_hub.py @@ -140,7 +140,7 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): self.get(first_repeating_section_url) self.post({"proxy-answer": "Yes"}) - self.assertInBody("What is John Doe’s date of birth?") + self.assertInBody("What is John Doe’s date of birth?") self.get(HUB_URL_PATH) @@ -149,12 +149,12 @@ def test_hub_displays_repeating_sections_with_valid_urls(self): self.get(second_repeating_section_url) self.post({"proxy-answer": "Yes"}) - self.assertInBody("What is Anna Doe’s date of birth?") + self.assertInBody("What is Anna Doe’s date of birth?") # Go to visitors visitor_repeating_section_url = section_urls[3].attrs["href"] self.get(visitor_repeating_section_url) - self.assertInBody("What is Joe Public’s date of birth?") + self.assertInBody("What is Joe Public’s date of birth?") def test_hub_section_required_but_enabled_false(self): # Given the hub is enabled and there are two required sections diff --git a/tests/integration/questionnaire/test_questionnaire_piping.py b/tests/integration/questionnaire/test_questionnaire_piping.py index a931e7cfbf..a4f54ff23f 100644 --- a/tests/integration/questionnaire/test_questionnaire_piping.py +++ b/tests/integration/questionnaire/test_questionnaire_piping.py @@ -21,7 +21,7 @@ def test_given_quotes_in_answer_when_piped_into_page_then_html_escaped_quotes_on # Using raw response data rather than assertInSelectorCSS as otherwise the # content will be unescaped by BeautifulSoup assert ( - "Does Joe Bloggs "Junior" live at 44 hill side" + "Does Joe Bloggs "Junior" live at 44 hill side" in self.getResponseData() ) @@ -42,7 +42,7 @@ def test_given_html_in_answer_when_piped_into_page_then_html_escaped_on_page(sel # Using raw response data rather than assertInSelectorCSS as otherwise the # content will be unescaped by BeautifulSoup assert ( - "Does Joe Bloggs <b>Junior</b> live at 44 hill side" + "Does Joe Bloggs <b>Junior</b> live at 44 hill side" in self.getResponseData() ) diff --git a/tests/integration/questionnaire/test_questionnaire_placeholders.py b/tests/integration/questionnaire/test_questionnaire_placeholders.py index f248686c2d..f22e227f40 100644 --- a/tests/integration/questionnaire/test_questionnaire_placeholders.py +++ b/tests/integration/questionnaire/test_questionnaire_placeholders.py @@ -41,14 +41,14 @@ def test_placeholders_rendered_in_pages(self): self.post({"total-retail-turnover-answer": 2000}) self.assertInBody( - "Of the £2,000.00 total retail turnover, what was the value of internet sales?" + "Of the £2,000.00 total retail turnover, what was the value of internet sales?" ) self.post({"total-retail-turnover-internet-sales-answer": 3000}) self.post({"total-items-answer": 2}) - self.assertInBody("Do you want to add a 3rd item?") + self.assertInBody("Do you want to add a 3rd item?") self.post({"add-item-question": "Yes"}) diff --git a/tests/integration/questionnaire/test_questionnaire_question_definition.py b/tests/integration/questionnaire/test_questionnaire_question_definition.py index 53c1921545..83c502a22b 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_definition.py +++ b/tests/integration/questionnaire/test_questionnaire_question_definition.py @@ -9,7 +9,7 @@ def test_question_definition(self): # When I start the survey I am presented with the definitions title and content correctly self.assertInBody( - "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?" + "Do you connect a LiFePO4 battery to your photovoltaic system to store surplus energy?" ) self.assertInBody("What is a photovoltaic system?") diff --git a/tests/integration/questionnaire/test_questionnaire_question_variants.py b/tests/integration/questionnaire/test_questionnaire_question_variants.py index 18141fef72..42e4522de1 100644 --- a/tests/integration/questionnaire/test_questionnaire_question_variants.py +++ b/tests/integration/questionnaire/test_questionnaire_question_variants.py @@ -43,7 +43,7 @@ def complete_first_section(self, proxy=False): self.post({"first-name-answer": "Linus", "last-name-answer": "Torvalds"}) - self.assertInBody("Are you Linus Torvalds?") + self.assertInBody("Are you Linus Torvalds?") proxy_answer = "No, I am answering on their behalf" if proxy else "Yes, I am" @@ -52,7 +52,9 @@ def complete_first_section(self, proxy=False): self.post({"proxy-answer": proxy_answer}) expected_question = ( - "What age is Linus Torvalds?" if proxy else "What is your age?" + "What age is Linus Torvalds?" + if proxy + else "What is your age?" ) self.assertInBody(expected_question) @@ -60,7 +62,9 @@ def complete_first_section(self, proxy=False): self.post({"age-answer": "49"}) expected_question = ( - "Linus Torvalds is over 16?" if proxy else "You are over 16?" + "Linus Torvalds is over 16?" + if proxy + else "You are over 16?" ) self.assertInBody(expected_question) diff --git a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py index de8d3ba10b..77e6eef878 100644 --- a/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py +++ b/tests/integration/questionnaire/test_questionnaire_relationships_unrelated.py @@ -137,7 +137,9 @@ def test_variants(self): ) self.post({"relationship-answer": "Unrelated"}) self.post({"relationship-answer": "Unrelated"}) - self.assertInBody("Are any of these people related to Betty Burns?") + self.assertInBody( + "Are any of these people related to Betty Burns?" + ) def test_variant_no_answer_routes_to_next_person(self): self.launch_survey_and_add_people() From 1f04b580e3bdeddb32ac4d8c2e614f05e2187a9e Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 1 May 2024 09:26:43 +0100 Subject: [PATCH 408/567] Schemas v5.0.0 (#1361) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 08cb5402d9..d3845ad3c5 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v4.7.1 +v5.0.0 From 82613489e89e702b9527407442e918f8ba918dfd Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 2 May 2024 11:26:39 +0100 Subject: [PATCH 409/567] Standardise GA events (#1347) --- app/jinja_filters.py | 6 ++- templates/confirm-email.html | 11 +++--- templates/feedback-sent.html | 9 +++-- templates/feedback.html | 16 ++++---- .../confirmation-post.html | 9 +++-- .../confirmation-text-message.html | 9 +++-- .../individual_response/interstitial.html | 8 ++-- templates/interstitial.html | 9 +++-- templates/layouts/_base.html | 8 ++-- templates/layouts/_calculatedsummary.html | 9 +++-- templates/layouts/_questionnaire.html | 9 +++-- templates/layouts/_submit.html | 7 ++-- templates/layouts/configs/_header.html | 11 +++--- templates/partials/answer-guidance.html | 9 +++-- templates/partials/answer.html | 7 +++- .../partials/confirmation-email-form.html | 14 ++++--- templates/partials/definition.html | 9 +++-- .../individual-response-guidance.html | 7 ++-- templates/partials/introduction/preview.html | 13 ++++--- .../partials/introduction/start-survey.html | 9 +++-- templates/partials/question.html | 20 ++++++---- .../partials/summary/collapsible-summary.html | 7 ++-- templates/preview.html | 37 +++++++++++-------- templates/sectionsummary.html | 18 +++++---- templates/thank-you.html | 8 ++-- templates/view-submitted-response.html | 26 +++++++------ tests/app/test_jinja_filters.py | 30 ++++++++++----- tests/functional/spec/preview.spec.js | 2 +- 28 files changed, 196 insertions(+), 141 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index aa7f2965ab..d910df59a0 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -443,8 +443,10 @@ def __init__( self.attributes = { "data-qa": answer["id"] + "-edit", "data-ga": "click", - "data-ga-category": "Summary", - "data-ga-action": "Edit click", + "data-ga-category": "Link", + "data-ga-action": "Edit", + "data-ga-label": "Edit", + "data-ga-page": "Summary", } diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 63e8d6d4e0..6bd0ce6ad8 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -29,11 +29,12 @@

      {{ conten "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" + "data-qa": "btn-submit", + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Confirm", + "data-ga-page": "Confirmation Email", + "data-ga": "click", } }) }} diff --git a/templates/feedback-sent.html b/templates/feedback-sent.html index 7ed1d9cfe0..92424fc9f3 100644 --- a/templates/feedback-sent.html +++ b/templates/feedback-sent.html @@ -31,10 +31,11 @@

      {{ _("Thank you for your feedback") }}

      {{ content.que "classes": "ons-u-mt-xl", "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Send feedback", - "data-ga-label": "Send feedback button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Send", + "data-ga-page": "Feedback", + "data-ga": "click", } }) }} diff --git a/templates/individual_response/confirmation-post.html b/templates/individual_response/confirmation-post.html index bb74687735..99d0bab548 100644 --- a/templates/individual_response/confirmation-post.html +++ b/templates/individual_response/confirmation-post.html @@ -28,10 +28,11 @@ "classes": 'ons-u-mb-m ons-u-mt-l', "attributes": { 'data-qa': 'btn-submit', - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Confirmation Post Continue", + "data-ga-page": "Individual Response", + "data-ga": "click", } }) }} diff --git a/templates/individual_response/confirmation-text-message.html b/templates/individual_response/confirmation-text-message.html index 1e62894bd2..6b35af4b7b 100644 --- a/templates/individual_response/confirmation-text-message.html +++ b/templates/individual_response/confirmation-text-message.html @@ -41,10 +41,11 @@ "classes": 'ons-u-mb-m ons-u-mt-l', "attributes": { 'data-qa': 'btn-submit', - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Confirmation Text Message Continue", + "data-ga-page": "Individual Response", + "data-ga": "click", } }) }} diff --git a/templates/individual_response/interstitial.html b/templates/individual_response/interstitial.html index 234675485a..b069758ec4 100644 --- a/templates/individual_response/interstitial.html +++ b/templates/individual_response/interstitial.html @@ -20,10 +20,10 @@

      {{ _("If you can't answer questions for others in your ho "url": next_location_url, "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Request separate census", - "data-ga-label": "Request separate census button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Request Separate Census", + "data-ga": "click", } }) }} diff --git a/templates/interstitial.html b/templates/interstitial.html index 1c3103f936..c59498b0ba 100644 --- a/templates/interstitial.html +++ b/templates/interstitial.html @@ -33,10 +33,11 @@

      {{ block.content.title }}

      "classes": "ons-u-mt-l", "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Save and continue", - "data-ga-label": "Save and continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Save and Continue", + "data-ga-page": "Interstitial", + "data-ga": "click", } }) }} diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 51c641d4c1..40b21c128f 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -14,9 +14,11 @@ "id": "top-previous", "text": _("Previous"), "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' + "data-ga": "click", + "data-ga-category": "Link", + "data-ga-action": "Navigate", + "data-ga-label": "Previous", + "data-ga-page": "Questionnaire", } } ] diff --git a/templates/layouts/_calculatedsummary.html b/templates/layouts/_calculatedsummary.html index 7df4a3bf19..07e2681d7d 100644 --- a/templates/layouts/_calculatedsummary.html +++ b/templates/layouts/_calculatedsummary.html @@ -18,10 +18,11 @@ "variants": 'timer', "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Confirm", - "data-ga-label": "Confirm button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Confirm", + "data-ga-page": "Calculated Summary", + "data-ga": "click", } }) }} diff --git a/templates/layouts/_questionnaire.html b/templates/layouts/_questionnaire.html index ae3d3b561d..c1f4563e6e 100644 --- a/templates/layouts/_questionnaire.html +++ b/templates/layouts/_questionnaire.html @@ -29,10 +29,11 @@ "text": continue_button_text | default(_("Save and continue") ), "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Save and continue", - "data-ga-label": "Save and continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Save and Continue", + "data-ga-page": "Questionnaire", + "data-ga": "click", } }) }} diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index 6ca899bfdd..19c1eedb67 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -42,10 +42,11 @@

      {{ content.title }}

      "classes": 'ons-u-mb-m ons-u-mt-' ~ ("s" if content.guidance else "xl"), "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit Button", + "data-ga-category": "Button", "data-ga-action": "Submit", - "data-ga-label": "Submit Button click", - "data-ga": "click" + "data-ga-label": "Submit", + "data-ga-page": "Questionnaire", + "data-ga": "click", } }) }} diff --git a/templates/layouts/configs/_header.html b/templates/layouts/configs/_header.html index 13fc9039cd..b38383e5e3 100644 --- a/templates/layouts/configs/_header.html +++ b/templates/layouts/configs/_header.html @@ -3,14 +3,14 @@ {% set sign_out_url = url_for('session.get_sign_out', internal_redirect=true) %} {% set header_button_text = sign_out_button_text %} {% set header_button_data_qa = "btn-save-sign-out" %} - {% set header_button_data_ga_action = "Save and sign out" %} - {% set header_button_data_ga_label = "Save and sign out button click" %} + {% set header_button_data_ga_action = "Save and Exit" %} + {% set header_button_data_ga_label = "Save and Exit Survey" %} {% else %} {% set sign_out_url = url_for('session.get_sign_out', todo=true) %} {% set header_button_text = _("Exit") %} {% set header_button_data_qa = "btn-exit" %} {% set header_button_data_ga_action = "Exit" %} - {% set header_button_data_ga_label = "Exit button click" %} + {% set header_button_data_ga_label = "Exit Survey" %} {% endif %} {# djlint:off #} {% set signout_button = { @@ -18,10 +18,11 @@ "url": sign_out_url, "attributes": { "data-qa": header_button_data_qa, - "data-ga-category": "Navigation", + "data-ga-category": "Button", "data-ga-action": header_button_data_ga_action, "data-ga-label": header_button_data_ga_label, - "data-ga": "click" + "data-ga-page": "Questionnaire", + "data-ga": "click", }, "iconType": "exit", "iconPosition": "after" diff --git a/templates/partials/answer-guidance.html b/templates/partials/answer-guidance.html index e006d4f81a..e1d9941748 100644 --- a/templates/partials/answer-guidance.html +++ b/templates/partials/answer-guidance.html @@ -8,10 +8,11 @@ "title": _(answer_guidance.schema_item.show_guidance), "headingAttributes": { "data-ga": "click", - "data-ga-category": "Answer guidance", - "data-ga-action": "Open panel", - "data-ga-label": _(answer_guidance.schema_item.show_guidance), - "data-qa": "answer-guidance-" ~ answer.id ~ "-title" + "data-ga-category": "Accordion", + "data-ga-action": "View", + "data-ga-label": "Answer Guidance", + "data-ga-page": "Questionnaire", + "data-qa": "answer-guidance-" ~ answer.id ~ "-title", }, "contentAttributes": { "data-qa": "answer-guidance-" ~ answer.id ~ "-content" diff --git a/templates/partials/answer.html b/templates/partials/answer.html index a0666ffa99..d4ca1a78fa 100644 --- a/templates/partials/answer.html +++ b/templates/partials/answer.html @@ -27,8 +27,11 @@ "attributes": { "data-ga": "error", "data-ga-category": "Error", - "data-ga-action": answer.type, - "data-ga-label": question.id + "data-ga-action": "Display", + "data-ga-label": "Answer Error", + "data-ga-type": answer.type, + "data-ga-identifier": question.id, + "data-ga-page": "Questionnaire", } } %} {# djlint:on #} diff --git a/templates/partials/confirmation-email-form.html b/templates/partials/confirmation-email-form.html index 0a600b76fb..c63b642844 100644 --- a/templates/partials/confirmation-email-form.html +++ b/templates/partials/confirmation-email-form.html @@ -11,8 +11,9 @@ "attributes": { "data-ga": "error", "data-ga-category": "Error", - "data-ga-action": "Confirmation Email", - "data-ga-label": email_field.id + "data-ga-action": "Display", + "data-ga-label": "Invalid / Missing Email", + "data-ga-page": "Confirmation Email", } } %} {# djlint:on #} @@ -40,10 +41,11 @@ "classes": "ons-u-mt-s", "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Send confirmation", - "data-ga-label": "Send confirmation button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Send", + "data-ga-page": "Confirmation Email", + "data-ga": "click", } }) }} diff --git a/templates/partials/definition.html b/templates/partials/definition.html index 3f29df0d02..51e3957155 100644 --- a/templates/partials/definition.html +++ b/templates/partials/definition.html @@ -8,10 +8,11 @@ "classes": "ons-u-mb-s", "headingAttributes": { "data-ga": "click", - "data-ga-category": category, - "data-ga-action": "Open panel", - "data-ga-label": definition.title, - "data-qa": definition_id ~ "-title" + "data-ga-category": "Accordion", + "data-ga-action": "View", + "data-ga-label": "Definition", + "data-ga-page": "Questionnaire", + "data-qa": definition_id ~ "-title", }, "contentAttributes": { "data-qa": definition_id ~ "-content" diff --git a/templates/partials/individual-response-guidance.html b/templates/partials/individual-response-guidance.html index ebec7af9d0..7d4c2051c8 100644 --- a/templates/partials/individual-response-guidance.html +++ b/templates/partials/individual-response-guidance.html @@ -7,9 +7,10 @@ "title": title, "headingAttributes": { "data-ga": "click", - "data-ga-category": "definition", - "data-ga-action": "Open panel", - "data-ga-label": title + "data-ga-category": "Accordion", + "data-ga-action": "View", + "data-ga-label": "Open Definition", + "data-ga-page": "Individual Response", } }) %} diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index 5c0669fea9..5b5050ba8d 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -19,8 +19,10 @@

      {{ intro.title }}

      "content": content, "summaryAttributes": { "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Open panel", + "data-ga-category": "Accordion", + "data-ga-action": "Preview", + "data-ga-label": "Preview Question", + "data-ga-page": "Introduction", } } %} {% do accordionItems.append(item) %} @@ -33,9 +35,10 @@

      {{ intro.title }}

      "close": _('Hide all'), "attributes": { "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" + "data-ga-category": "Button", + "data-ga-action": "View", + "data-ga-label": "Show all", + "data-ga-page": "Introduction", } }, "itemsList": accordionItems diff --git a/templates/partials/introduction/start-survey.html b/templates/partials/introduction/start-survey.html index a74e37fe2f..e90b4c6ed3 100644 --- a/templates/partials/introduction/start-survey.html +++ b/templates/partials/introduction/start-survey.html @@ -9,10 +9,11 @@ "name": "action[start_questionnaire]", "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Start survey", - "data-ga-label": "Start button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Start", + "data-ga-label": "Start Survey", + "data-ga-page": "Introduction", + "data-ga": "click", } }) }} diff --git a/templates/partials/question.html b/templates/partials/question.html index e4d5dee71b..2aca679da8 100644 --- a/templates/partials/question.html +++ b/templates/partials/question.html @@ -23,10 +23,11 @@ "content": definition_content, "headingAttributes": { "data-ga": "click", - "data-ga-category": "Question definition", - "data-ga-action": "Open panel", - "data-ga-label": question.definition.title, - "data-qa": definition_id ~ "-title" + "data-ga-category": "Accordion", + "data-ga-action": "View", + "data-ga-label": "Question Definition", + "data-ga-page": "Questionnaire", + "data-qa": definition_id ~ "-title", }, "contentAttributes": { "data-qa": definition_id ~ "-content" @@ -121,10 +122,13 @@ "text": question_error, "id": question.id ~ '-error', "attributes": { - "data-ga": "question-error", - "data-ga-category": "Question error", - "data-ga-action": question.type, - "data-ga-label": question.id + "data-ga": "error", + "data-ga-category": "Error", + "data-ga-action": "Display", + "data-ga-label": "Question Error", + "data-ga-type": question.type, + "data-ga-identifier": question.id, + "data-ga-page": "Questionnaire", } } %} {% call onsError(config) %} diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index 6139cadc9e..f264784c91 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -52,9 +52,10 @@ "close": _('Hide all'), "attributes": { "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" + "data-ga-category": "Button", + "data-ga-action": "View", + "data-ga-label": "Collapsible Answers", + "data-ga-page": "Summary", } }, "itemsList": itemList diff --git a/templates/preview.html b/templates/preview.html index 16659a98b1..2dd4d03a09 100644 --- a/templates/preview.html +++ b/templates/preview.html @@ -13,9 +13,11 @@ "id": "top-previous", "text": _("Back"), "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' + "data-ga": "click", + "data-ga-category": "Link", + "data-ga-action": "Navigate", + "data-ga-label": "Previous", + "data-ga-page": "Preview", } } ] @@ -53,10 +55,11 @@

      {{ _("Preview of the questions in this survey") }}

      "variants": ['small', 'secondary', 'print'], "attributes": { "data-qa": "btn-print", - "data-ga-category": "Print button", - "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "View", + "data-ga-label": "Print Dialogue", + "data-ga-page": "Preview", + "data-ga": "click", } }) }} @@ -68,10 +71,11 @@

      {{ _("Preview of the questions in this survey") }}

      "removeDownloadAttribute": true, "attributes": { "data-qa": "btn-pdf", - "data-ga-category": "PDF button", - "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Download", + "data-ga-label": "Download PDF", + "data-ga-page": "Preview", + "data-ga": "click", } }) }} @@ -93,11 +97,12 @@

      {{ _("Preview of the questions in this survey") }}

      "allButton": { "open": _('Show all') , "close": _('Hide all'), - "attributes": { - "data-ga": "click", - "data-ga-category": "Preview Survey", - "data-ga-action": "Show all", - "data-ga-label": "Show all" + "attributes": { + "data-ga": "click", + "data-ga-category": "Button", + "data-ga-action": "View", + "data-ga-label": "Show all", + "data-ga-page": "Preview", } }, "itemsList": itemList diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 466681bb1d..5def65d85d 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -11,10 +11,11 @@ "text": _("Continue"), "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Continue", + "data-ga-page": "Section Summary", + "data-ga": "click", } }) }} @@ -52,10 +53,11 @@

      {{ content.summary.title }}

      "classes": "ons-u-mt-xl", "attributes": { "data-qa": "btn-submit", - "data-ga-category": "Submit button", - "data-ga-action": "Continue", - "data-ga-label": "Continue button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Continue", + "data-ga-page": "Section Summary", + "data-ga": "click", } }) }} diff --git a/templates/thank-you.html b/templates/thank-you.html index 01d17ad85a..9221e96c66 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -16,9 +16,11 @@ "id": "back-to-surveys", "text": _("Back to surveys"), "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Back to surveys link click' + "data-ga": "click", + "data-ga-category": "Link", + "data-ga-action": "Navigate", + "data-ga-label": "Back to Surveys", + "data-ga-page": "Thank You", } } ] diff --git a/templates/view-submitted-response.html b/templates/view-submitted-response.html index 0a6fee2f4c..b86ed939a3 100644 --- a/templates/view-submitted-response.html +++ b/templates/view-submitted-response.html @@ -14,9 +14,11 @@ "id": "top-previous", "text": _("Back"), "attributes": { - "data-ga": 'click', - "data-ga-category": 'Navigation', - "data-ga-action": 'Previous link click' + "data-ga": "click", + "data-ga-category": "Link", + "data-ga-action": "Navigate", + "data-ga-label": "Previous", + "data-ga-page": "View Submitted Response", } } ] @@ -34,10 +36,11 @@

      {{ content.submitted_text }}

      "variants": ['small', 'secondary', 'print'], "attributes": { "data-qa": "btn-print", - "data-ga-category": "Print button", - "data-ga-action": "Open print Dialogue", - "data-ga-label": "Print button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "View", + "data-ga-label": "Print Dialogue", + "data-ga-page": "View Submitted Response", + "data-ga": "click", } }) }} @@ -49,10 +52,11 @@

      {{ content.submitted_text }}

      "removeDownloadAttribute": true, "attributes": { "data-qa": "btn-pdf", - "data-ga-category": "PDF button", - "data-ga-action": "Download PDF", - "data-ga-label": "PDF button click", - "data-ga": "click" + "data-ga-category": "Button", + "data-ga-action": "Download", + "data-ga-label": "Download PDF", + "data-ga-page": "View Submitted Response", + "data-ga": "click", } }) }} diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 69841c695c..5824a91831 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -742,8 +742,10 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "visuallyHiddenText": "edit_link_aria_label Registration number", "attributes": { "data-ga": "click", - "data-ga-action": "Edit click", - "data-ga-category": "Summary", + "data-ga-action": "Edit", + "data-ga-category": "Link", + "data-ga-label": "Edit", + "data-ga-page": "Summary", "data-qa": "registration-number-edit", }, "text": "edit_link_text", @@ -764,8 +766,10 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "insurer?", "attributes": { "data-ga": "click", - "data-ga-action": "Edit click", - "data-ga-category": "Summary", + "data-ga-action": "Edit", + "data-ga-category": "Link", + "data-ga-label": "Edit", + "data-ga-page": "Summary", "data-qa": "authorised-insurer-radio-edit", }, "text": "edit_link_text", @@ -1033,8 +1037,10 @@ def test_summary_item_config_with_list_collector(): "Registration number", "attributes": { "data-ga": "click", - "data-ga-action": "Edit click", - "data-ga-category": "Summary", + "data-ga-action": "Edit", + "data-ga-category": "Link", + "data-ga-label": "Edit", + "data-ga-page": "Summary", "data-qa": "registration-number-edit", }, "text": "Change", @@ -1055,8 +1061,10 @@ def test_summary_item_config_with_list_collector(): "insurer?", "attributes": { "data-ga": "click", - "data-ga-action": "Edit click", - "data-ga-category": "Summary", + "data-ga-action": "Edit", + "data-ga-category": "Link", + "data-ga-label": "Edit", + "data-ga-page": "Summary", "data-qa": "authorised-insurer-radio-edit", }, "text": "Change", @@ -1188,8 +1196,10 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): "Registration number", "attributes": { "data-ga": "click", - "data-ga-action": "Edit click", - "data-ga-category": "Summary", + "data-ga-action": "Edit", + "data-ga-category": "Link", + "data-ga-label": "Edit", + "data-ga-page": "Summary", "data-qa": "registration-number-edit", }, "text": "Change", diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index 8e44bbcc3f..73a596674f 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -4,7 +4,7 @@ import IntroductionPageLinear from "../generated_pages/introduction/introduction describe("Introduction preview questions", () => { const introductionSchemaHub = "test_introduction_hub.json"; const introductionSchemaLinear = "test_introduction.json"; - const showButton = 'button[data-ga-category="Preview Survey"]'; + const showButton = 'button[data-ga-label="Hide all"]'; const previewSummaryContent = "#summary-accordion-1-content"; const previewSectionTitle = ".ons-summary__group-title"; const previewQuestion = ".ons-summary__item"; From cdd58c77aaace751493a04320241868d37f0be27 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 8 May 2024 09:23:43 +0100 Subject: [PATCH 410/567] Create test cases for rules that compare to a boolean (#1348) * Create Test for Boolean Flag * revert accidental edit on test * format files using prettier * make booleanFlag null instead of false * revert booleanFlag default value to False * update description for functional tests * update Functional test description to be generic * update booleanFlag configuratgion in jwt helper for recent v2 change * fix lint spacing for json --- tests/functional/jwt_helper.js | 6 +++-- .../spec/features/routing/boolean.spec.js | 22 +++++++++++++++++++ tests/functional/wdio.conf.js | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/functional/spec/features/routing/boolean.spec.js diff --git a/tests/functional/jwt_helper.js b/tests/functional/jwt_helper.js index 2862cffd75..ba8d84c65d 100644 --- a/tests/functional/jwt_helper.js +++ b/tests/functional/jwt_helper.js @@ -76,6 +76,7 @@ export function generateToken( includeLogoutUrl = true, displayAddress = "", cirInstrumentId = null, + booleanFlag = false, }, ) { let schemaParams = {}; @@ -113,7 +114,7 @@ export function generateToken( region_code: regionCode, language_code: languageCode, account_service_url: "http://localhost:8000", - survey_metadata: getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId), + survey_metadata: getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId, booleanFlag), version: launchVersion, response_expires_at: isoDate, }; @@ -158,7 +159,7 @@ export function generateToken( }); } -function getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId) { +function getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, periodStr, ruRef, sdsDatasetId, booleanFlag) { let surveyMetadata = {}; if (theme === "social") { @@ -185,6 +186,7 @@ function getSurveyMetadata(theme, userId, displayAddress, surveyId, periodId, pe return_by: "2017-03-01", ru_name: "Apple", trad_as: "Apple", + boolean_flag: booleanFlag, }, }; } diff --git a/tests/functional/spec/features/routing/boolean.spec.js b/tests/functional/spec/features/routing/boolean.spec.js new file mode 100644 index 0000000000..4359c1e996 --- /dev/null +++ b/tests/functional/spec/features/routing/boolean.spec.js @@ -0,0 +1,22 @@ +import Block1Page from "../../../generated_pages/metadata_routing/block1.page"; +import Block2Page from "../../../generated_pages/metadata_routing/block2.page"; +import Block3Page from "../../../generated_pages/metadata_routing/block3.page"; +import { click } from "../../../helpers"; + +describe("Feature: Routing - Boolean Flag", () => { + it("Given I have a routing rule that uses a boolean flag and it is False, When I press continue, Then I should be routed to the correct page", async () => { + await browser.openQuestionnaire("test_metadata_routing.json", { + booleanFlag: false, + }); + await click(Block1Page.submit()); + await expect(browser).toHaveUrlContaining(Block2Page.pageName); + }); + + it("Given I have a routing rule that uses a boolean flag and it is True, When I press continue, Then I should be routed to the correct page ", async () => { + await browser.openQuestionnaire("test_metadata_routing.json", { + booleanFlag: true, + }); + await click(Block1Page.submit()); + await expect(browser).toHaveUrlContaining(Block3Page.pageName); + }); +}); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 3bfd8aad63..74dd7065b9 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -215,6 +215,7 @@ export const config = { language = "en", includeLogoutUrl = false, cirInstrumentId = null, + booleanFlag = false, } = {}, ) { const token = await JwtHelper.generateToken(schema, { @@ -232,6 +233,7 @@ export const config = { languageCode: language, includeLogoutUrl, cirInstrumentId, + booleanFlag, }); this.url(`/session?token=${token}`); }, From 1f851ca8d84f3289cadea67f93b13e5f3d6509a6 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 8 May 2024 11:22:22 +0100 Subject: [PATCH 411/567] Update tests with new supplementary dataset IDs (#1341) --- .../spec/features/hub_and_spoke/hub_and_spoke.spec.js | 2 +- .../supplementary_data/supplementary_data.spec.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 592e06c284..830d98ced4 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -308,7 +308,7 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire("test_hub_section_required_with_repeat_supplementary.json.json", { version: "v2", - sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", responseId, }); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index da0eaae74f..e9539e0788 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -42,7 +42,7 @@ describe("Using supplementary data", () => { before("Starting the survey", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", responseId, }); }); @@ -341,7 +341,7 @@ describe("Using supplementary data", () => { it("Given I relaunch the survey with new supplementary data and new list items for the repeating section, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not any old ones", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", + sdsDatasetId: "1f61750f-51dd-e933-0df9-40261b06d93f", responseId, }); await expect(await $(HubPage.summaryItems("section-4-1")).getText()).toContain("Harry Potter"); @@ -391,7 +391,7 @@ describe("Using supplementary data", () => { await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "c067f6de-6d64-42b1-8b02-431a3486c178", + sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", responseId, }); await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); @@ -401,7 +401,7 @@ describe("Using supplementary data", () => { it("Given I return to the new data resulting in a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "693dc252-2e90-4412-bd9c-c4d953e36fcd", + sdsDatasetId: "1f61750f-51dd-e933-0df9-40261b06d93f", responseId, }); await click(HubPage.submit()); From 222c0a414cfe498a4220485d826724315ecbe577 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 14:05:36 +0100 Subject: [PATCH 412/567] Bump ejs in the npm_and_yarn group across 1 directory (#1362) Bumps the npm_and_yarn group with 1 update in the / directory: [ejs](https://github.com/mde/ejs). Updates `ejs` from 3.1.9 to 3.1.10 - [Release notes](https://github.com/mde/ejs/releases) - [Commits](https://github.com/mde/ejs/compare/v3.1.9...v3.1.10) --- updated-dependencies: - dependency-name: ejs dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: petechd <53475968+petechd@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ab00f1289c..4cf5457ff2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4182,9 +4182,9 @@ } }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" From 7d87d06f8fe68bf53c5b576a2bc94ce1f327e31a Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Wed, 15 May 2024 11:52:43 +0100 Subject: [PATCH 413/567] Schemas v5.1.0 (#1365) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index d3845ad3c5..6b7ace3e91 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.0.0 +v5.1.0 From 8db9577ddae60eba8a6e82ce63955db292a56cb2 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Thu, 23 May 2024 07:52:51 +0100 Subject: [PATCH 414/567] Type hints for Publisher Module (#1364) --- app/publisher/publisher.py | 17 +++++++++++------ mypy.ini | 5 +++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/publisher/publisher.py b/app/publisher/publisher.py index 141857fcad..909a437a3b 100644 --- a/app/publisher/publisher.py +++ b/app/publisher/publisher.py @@ -2,7 +2,6 @@ import google.auth from google.cloud.pubsub import PublisherClient -from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1.futures import Future from structlog import get_logger @@ -13,22 +12,26 @@ class Publisher(ABC): @abstractmethod - def publish(self, topic_id, message, fulfilment_request_transaction_id): + def publish( + self, topic_id: str, message: bytes, fulfilment_request_transaction_id: str + ) -> None: pass # pragma: no cover class PubSubPublisher(Publisher): - def __init__(self): + def __init__(self) -> None: self._client = PublisherClient() _, self._project_id = google.auth.default() - def _publish(self, topic_id, message) -> "publisher.futures.Future": + def _publish(self, topic_id: str, message: bytes) -> Future: logger.info("publishing message", topic_id=topic_id) topic_path = self._client.topic_path(self._project_id, topic_id) response: Future = self._client.publish(topic_path, message) return response - def publish(self, topic_id, message: bytes, fulfilment_request_transaction_id: str): + def publish( + self, topic_id: str, message: bytes, fulfilment_request_transaction_id: str + ) -> None: response = self._publish(topic_id, message) try: # Resolve the future @@ -48,7 +51,9 @@ def publish(self, topic_id, message: bytes, fulfilment_request_transaction_id: s class LogPublisher(Publisher): - def publish(self, topic_id, message: bytes, fulfilment_request_transaction_id: str): + def publish( + self, topic_id: str, message: bytes, fulfilment_request_transaction_id: str + ) -> None: logger.info( "publishing message", topic_id=topic_id, diff --git a/mypy.ini b/mypy.ini index 0e98cd508a..dc4f23c263 100644 --- a/mypy.ini +++ b/mypy.ini @@ -107,6 +107,11 @@ disallow_untyped_defs = True warn_return_any = True no_implicit_optional = True +[mypy-app.publisher.*] +disallow_untyped_defs = True +warn_return_any = True +no_implicit_optional = True + [mypy-scripts.generate_integration_test] disallow_untyped_defs = True warn_return_any = True From 511b8110370fb77f8432c52eceae1d1f2117cd78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 09:47:35 +0100 Subject: [PATCH 415/567] Bump tqdm from 4.66.2 to 4.66.3 in the pip group across 1 directory (#1363) --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index a2cd5cea99..a977f35505 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "astroid" @@ -3154,13 +3154,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.2" +version = "4.66.3" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, - {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, + {file = "tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53"}, + {file = "tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5"}, ] [package.dependencies] From 6981a7156e552b20d7ddb350bbb59adae305c1e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 10:09:25 +0100 Subject: [PATCH 416/567] --- (#1367) --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index a977f35505..0db98cdbeb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2761,13 +2761,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, + {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, ] [package.dependencies] @@ -3517,4 +3517,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "3abdfcf4cc6d0e5ea580cc8064546b061bf53db977c72dd5466a0620f9692a28" +content-hash = "843d4b703dd0ef4fa5176771f9bb940983a71f620e23af3894e8027c91fa27d0" diff --git a/pyproject.toml b/pyproject.toml index 9e40e25f2e..07e4461ec3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ grpcio = "^1.62.1" gunicorn = "^22.0.0" pika = "^1.3.2" pyyaml = "^6.0.1" -requests = "^2.31.0" +requests = "^2.32.0" sdc-cryptography = "^1.2.0" structlog = "^24.1.0" ua-parser = "^0.18.0" From 438f19e7022ad6a5478fe2a3a8f2f33e6042d484 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 28 May 2024 10:13:11 +0100 Subject: [PATCH 417/567] Add more context to list item tick icons (#1366) --- .design-system-version | 2 +- app/jinja_filters.py | 1 + tests/app/test_jinja_filters.py | 61 +++++++++++++++++++ tests/functional/helpers.js | 2 +- .../list_collector_repeating_blocks.spec.js | 3 +- 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/.design-system-version b/.design-system-version index 1f5c54aff3..14a228fe9a 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -70.0.2 +70.0.5 diff --git a/app/jinja_filters.py b/app/jinja_filters.py index d910df59a0..1c65af0dad 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -711,6 +711,7 @@ def map_list_collector_config( row_item = { "iconType": icon, + "iconVisuallyHiddenText": "Completed" if icon else None, "actions": actions, "id": list_item.get("list_item_id"), "rowTitleAttributes": { diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 5824a91831..8d76e96c56 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -534,6 +534,7 @@ def test_map_list_collector_config_no_actions(): "rowItems": [ { "actions": [], + "iconVisuallyHiddenText": None, "iconType": None, "id": "one", "rowTitleAttributes": { @@ -548,6 +549,7 @@ def test_map_list_collector_config_no_actions(): "rowItems": [ { "actions": [], + "iconVisuallyHiddenText": None, "iconType": None, "id": "two", "rowTitleAttributes": { @@ -605,6 +607,7 @@ def test_map_list_collector_config(): "url": "/primary/change", } ], + "iconVisuallyHiddenText": None, "iconType": None, "id": "primary", "rowTitleAttributes": { @@ -633,6 +636,7 @@ def test_map_list_collector_config(): }, ], "iconType": None, + "iconVisuallyHiddenText": None, "id": "nonprimary", "rowTitleAttributes": { "data-list-item-id": "nonprimary", @@ -727,6 +731,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "url": "/nonprimary/remove", }, ], + "iconVisuallyHiddenText": None, "iconType": None, "id": "VHoiow", "rowTitle": "Name of UK company or branch", @@ -1022,6 +1027,7 @@ def test_summary_item_config_with_list_collector(): "url": "remove_link_url", }, ], + "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", "rowTitle": "Company A", @@ -1181,6 +1187,7 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): "url": "remove_link_url", }, ], + "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", "rowTitle": "Company A", @@ -1282,5 +1289,59 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): assert to_dict(expected) == to_dict(result) +def test_map_list_collector_config_render_icon_set(): + list_items = [ + { + "item_title": "Mark Bloggs", + "list_item_id": "one", + "repeating_blocks": True, + "is_complete": True, + }, + { + "item_title": "Joe Bloggs", + "list_item_id": "two", + "repeating_block": True, + "is_complete": False, + }, + ] + + output = map_list_collector_config(list_items, editable=True, render_icon=True) + + expected = [ + { + "rowItems": [ + { + "actions": [], + "iconVisuallyHiddenText": "Completed", + "iconType": "check", + "id": "one", + "rowTitleAttributes": { + "data-list-item-id": "one", + "data-qa": "list-item-1-label", + }, + "rowTitle": "Mark Bloggs", + } + ] + }, + { + "rowItems": [ + { + "actions": [], + "iconVisuallyHiddenText": None, + "iconType": None, + "id": "two", + "rowTitleAttributes": { + "data-list-item-id": "two", + "data-qa": "list-item-2-label", + }, + "rowTitle": "Joe Bloggs", + } + ] + }, + ] + + assert output == expected + + def to_dict(obj): return json.loads(json.dumps(obj, default=lambda o: o.__dict__)) diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 213369f7f8..c0fa90daae 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -2,7 +2,7 @@ export const checkItemsInList = async (itemsExpected, listLabel) => { await $(listLabel(1)).waitForDisplayed(); for (let i = 1; i <= itemsExpected.length; i++) { - await expect(await $(listLabel(i)).getText()).toEqual(itemsExpected[i - 1]); + await expect(await $(listLabel(i)).getText()).toContain(itemsExpected[i - 1]); } }; diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 62340d9f5f..4e2fcd403c 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -96,7 +96,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.listEditLink(2)).click(); await $(EditCompanyPage.companyOrBranchName()).setValue("Government"); await click(EditCompanyPage.submit()); - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("Government"); + await checkItemsInList(["ONS", "Government", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); }); it("When the user clicks the remove link, Then the item selected is removed", async () => { @@ -105,7 +105,6 @@ describe("List Collector Repeating Blocks", () => { await click(RemoveCompanyPage.submit()); await checkItemsInList(["ONS", "MOD"], AnyOtherCompaniesOrBranchesPage.listLabel); await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).not.toContain("Government"); - await expect(await $(AnyOtherCompaniesOrBranchesPage.listLabel(2)).getText()).toBe("MOD"); }); it("When a user has finished editing or removing from the list, Then they are still able to add additional companies", async () => { From b69b05d88ccd70df32fb94546e092c36d925511b Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 28 May 2024 13:33:20 +0100 Subject: [PATCH 418/567] Add Dependabot config file (#1360) --- .github/dependabot.yaml | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000000..609aabeae6 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,94 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + groups: + production-dependencies: + dependency-type: "production" + patterns: + - "flask*" + - "google*" + - "python*" + - "colorama" + - "grpcio" + - "gunicorn" + - "pika" + - "pyyaml" + - "requests" + - "sdc-cryptography" + - "structlog" + - "ua-parser" + - "blinker" + - "boto3" + - "humanize" + - "marshmallow" + - "jsonpointer" + - "redis" + - "htmlmin" + - "coloredlogs" + - "uwsgi" + - "email-validator" + - "itsdangerous" + - "simplejson" + - "markupsafe" + - "pdfkit" + - "ordered-set" + - "cachetools" + - "gevent" + development-dependencies: + dependency-type: "development" + patterns: + - "pytest*" + - "flake8*" + - "pylint*" + - "types*" + - "pep8" + - "mock" + - "jsonschema" + - "beautifulsoup4" + - "httmock" + - "moto" + - "freezegun" + - "fakeredis" + - "mypy" + - "responses" + - "playwright" + - "black" + - "djlint" + ignore: + - dependency-name: "pytest*" + update-types: ["version-update:semver-minor"] + - dependency-name: "flake8*" + update-types: ["version-update:semver-minor"] + - dependency-name: "pylint*" + update-types: ["version-update:semver-minor"] + - dependency-name: "types*" + update-types: ["version-update:semver-minor"] + - dependency-name: "pep8" + update-types: ["version-update:semver-minor"] + - dependency-name: "mock" + update-types: ["version-update:semver-minor"] + - dependency-name: "jsonschema" + update-types: ["version-update:semver-minor"] + - dependency-name: "beautifulsoup4" + update-types: ["version-update:semver-minor"] + - dependency-name: "httmock" + update-types: ["version-update:semver-minor"] + - dependency-name: "moto" + update-types: ["version-update:semver-minor"] + - dependency-name: "freezegun" + update-types: ["version-update:semver-minor"] + - dependency-name: "fakeredis" + update-types: ["version-update:semver-minor"] + - dependency-name: "mypy" + update-types: ["version-update:semver-minor"] + - dependency-name: "responses" + update-types: ["version-update:semver-minor"] + - dependency-name: "playwright" + update-types: ["version-update:semver-minor"] + - dependency-name: "black" + update-types: ["version-update:semver-minor"] + - dependency-name: "djlint" + update-types: ["version-update:semver-minor"] From e764aa6db8b93b88221b68b9f02d66f2aefe0b6e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 29 May 2024 11:39:43 +0100 Subject: [PATCH 419/567] Add NSHE theme (#1369) --- app/helpers/template_helpers.py | 2 + app/survey_config/__init__.py | 2 + app/survey_config/social_survey_config.py | 7 ++ app/survey_config/survey_type.py | 1 + schemas/test/en/test_theme_ons_nhs.json | 75 +++++++++++++++++++ .../assets/images/desnz-logo-stacked.svg | 2 +- templates/assets/images/nhs-logo.svg | 9 +++ .../assets/images/ons-logo-stacked-mb.svg | 12 +++ templates/assets/images/ons-logo-stacked.svg | 4 +- templates/errors/_base.html | 2 +- tests/app/helpers/test_template_helpers.py | 32 ++++++++ tests/functional/spec/theme_nhse.spec.js | 16 ++++ tests/functional/spec/theme_ukhsa_ons.spec.js | 2 +- 13 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 schemas/test/en/test_theme_ons_nhs.json create mode 100644 templates/assets/images/nhs-logo.svg create mode 100644 templates/assets/images/ons-logo-stacked-mb.svg create mode 100644 tests/functional/spec/theme_nhse.spec.js diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 36aef1307a..75c9c68244 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -22,6 +22,7 @@ DESNZBusinessSurveyConfig, DESNZNIBusinessSurveyConfig, NIBusinessSurveyConfig, + ONSNHSSocialSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, @@ -188,6 +189,7 @@ def survey_config_mapping( SurveyType.DESNZ_NI: DESNZNIBusinessSurveyConfig, SurveyType.ORR: ORRBusinessSurveyConfig, SurveyType.UKHSA_ONS: UKHSAONSSocialSurveyConfig, + SurveyType.ONS_NHS: ONSNHSSocialSurveyConfig, } return survey_type_to_config[theme]( diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index e800b7b03a..6cfa6e8f3a 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -11,12 +11,14 @@ ) from app.survey_config.link import Link from app.survey_config.social_survey_config import ( + ONSNHSSocialSurveyConfig, SocialSurveyConfig, UKHSAONSSocialSurveyConfig, ) from app.survey_config.survey_config import SurveyConfig __all__ = [ + "ONSNHSSocialSurveyConfig", "SocialSurveyConfig", "UKHSAONSSocialSurveyConfig", "SurveyConfig", diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index 7ec2abbd96..c8fb8b7fbc 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -73,3 +73,10 @@ class UKHSAONSSocialSurveyConfig(SocialSurveyConfig): masthead_logo_mobile: str = read_file( "./templates/assets/images/ukhsa-logo-stacked.svg" ) + read_file("./templates/assets/images/ons-logo-stacked.svg") + + +@dataclass +class ONSNHSSocialSurveyConfig(SocialSurveyConfig): + masthead_logo: str = read_file( + "./templates/assets/images/ons-logo-stacked.svg" + ) + read_file("./templates/assets/images/nhs-logo.svg") diff --git a/app/survey_config/survey_type.py b/app/survey_config/survey_type.py index fbfbc16062..666b4afb1b 100644 --- a/app/survey_config/survey_type.py +++ b/app/survey_config/survey_type.py @@ -15,3 +15,4 @@ class SurveyType(Enum): DESNZ = "desnz" DESNZ_NI = "desnz-ni" UKHSA_ONS = "ukhsa-ons" + ONS_NHS = "ons-nhs" diff --git a/schemas/test/en/test_theme_ons_nhs.json b/schemas/test/en/test_theme_ons_nhs.json new file mode 100644 index 0000000000..e733508d94 --- /dev/null +++ b/schemas/test/en/test_theme_ons_nhs.json @@ -0,0 +1,75 @@ +{ + "mime_type": "application/json/ons/eq", + "language": "en", + "schema_version": "0.0.1", + "data_version": "0.0.3", + "survey_id": "0", + "title": "Test ONS NHS Survey", + "theme": "ons-nhs", + "description": "A questionnaire to demo the ons-nhs survey theme", + "metadata": [ + { + "name": "user_id", + "type": "string" + }, + { + "name": "period_id", + "type": "string" + }, + { + "name": "ru_name", + "type": "string" + } + ], + "questionnaire_flow": { + "type": "Linear", + "options": { + "summary": { + "collapsible": false + } + } + }, + "sections": [ + { + "id": "section", + "groups": [ + { + "blocks": [ + { + "type": "Question", + "id": "radio", + "question": { + "answers": [ + { + "id": "radio-answer", + "mandatory": false, + "options": [ + { + "label": "Bacon", + "value": "Bacon" + }, + { + "label": "Eggs", + "value": "Eggs" + }, + { + "label": "Sausage", + "value": "Sausage" + } + ], + "type": "Radio" + } + ], + "id": "radio-question", + "title": "What is your favourite breakfast food?", + "type": "General" + } + } + ], + "id": "group", + "title": "ONS NHS Theme Test" + } + ] + } + ] +} diff --git a/templates/assets/images/desnz-logo-stacked.svg b/templates/assets/images/desnz-logo-stacked.svg index ea62005183..3a76910b53 100644 --- a/templates/assets/images/desnz-logo-stacked.svg +++ b/templates/assets/images/desnz-logo-stacked.svg @@ -1,4 +1,4 @@ - + Department for Energy Security and Net Zero diff --git a/templates/assets/images/nhs-logo.svg b/templates/assets/images/nhs-logo.svg new file mode 100644 index 0000000000..c05d83269b --- /dev/null +++ b/templates/assets/images/nhs-logo.svg @@ -0,0 +1,9 @@ + + National Heath Service + + + + + + + diff --git a/templates/assets/images/ons-logo-stacked-mb.svg b/templates/assets/images/ons-logo-stacked-mb.svg new file mode 100644 index 0000000000..b918000216 --- /dev/null +++ b/templates/assets/images/ons-logo-stacked-mb.svg @@ -0,0 +1,12 @@ + + Office for National Statistics + + + + + + + + + + diff --git a/templates/assets/images/ons-logo-stacked.svg b/templates/assets/images/ons-logo-stacked.svg index 01ebeba844..d7056700ab 100644 --- a/templates/assets/images/ons-logo-stacked.svg +++ b/templates/assets/images/ons-logo-stacked.svg @@ -1,5 +1,5 @@ - - Office for National Statistics logo + + Office for National Statistics diff --git a/templates/errors/_base.html b/templates/errors/_base.html index caf4f26152..048b29c2bd 100644 --- a/templates/errors/_base.html +++ b/templates/errors/_base.html @@ -3,5 +3,5 @@ {% set SURVEY_TYPES_BUSINESS = ["northernireland", "business", "dbt", "dbt-ni", "dbt-dsit", "dbt-dsit-ni", "orr", "desnz", "desnz-ni"] %} {% set SURVEY_TYPES_DEFAULT = ["default"] %} {% set SURVEY_TYPES_SOCIAL = ["social"] %} -{% set SURVEY_TYPES_HEALTH = ["health", "ukhsa-ons"] %} +{% set SURVEY_TYPES_HEALTH = ["health", "ukhsa-ons", "ons-nhs"] %} {% set SURVEY_TYPES_ALL = SURVEY_TYPES_BUSINESS + SURVEY_TYPES_DEFAULT + SURVEY_TYPES_SOCIAL + SURVEY_TYPES_HEALTH %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index ce5e492451..f622be9cd5 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -23,6 +23,7 @@ DESNZBusinessSurveyConfig, DESNZNIBusinessSurveyConfig, NIBusinessSurveyConfig, + ONSNHSSocialSurveyConfig, ORRBusinessSurveyConfig, SocialSurveyConfig, SurveyConfig, @@ -298,6 +299,17 @@ def test_footer_context(app: Flask, theme, survey_config, language, expected_foo + read_file("./templates/assets/images/ons-logo-stacked.svg"), ], ), + ( + SurveyType.ONS_NHS, + "Test", + ONSNHSSocialSurveyConfig(), + [ + "Test", + read_file("./templates/assets/images/ons-logo-stacked.svg") + + read_file("./templates/assets/images/nhs-logo.svg"), + None, + ], + ), ), ) def test_header_context(app: Flask, theme, survey_title, survey_config, expected): @@ -484,6 +496,11 @@ def test_service_links_context( "en", f"{ONS_URL}/aboutus/contactus/surveyenquiries/", ), + ( + ONSNHSSocialSurveyConfig(), + "en", + f"{ONS_URL}/aboutus/contactus/surveyenquiries/", + ), ], ) def test_contact_us_url_context( @@ -587,6 +604,11 @@ def test_sign_out_button_text_context( True, f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/cookies/", ), + ( + ONSNHSSocialSurveyConfig(), + True, + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/cookies/", + ), (SurveyConfig(), False, None), ], ) @@ -671,6 +693,11 @@ def test_cookie_settings_url_context( "en", ACCOUNT_SERVICE_BASE_URL_SOCIAL, ), + ( + ONSNHSSocialSurveyConfig(), + "en", + ACCOUNT_SERVICE_BASE_URL_SOCIAL, + ), ], ) def test_cookie_domain_context( @@ -814,6 +841,10 @@ def test_account_service_my_todo_url_context( UKHSAONSSocialSurveyConfig(), f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", ), + ( + ONSNHSSocialSurveyConfig(), + f"{ACCOUNT_SERVICE_BASE_URL_SOCIAL}/en/start/", + ), ], ) def test_account_service_log_out_url_context( @@ -841,6 +872,7 @@ def test_account_service_log_out_url_context( (SurveyType.DBT_DSIT_NI, "en", DBTDSITNIBusinessSurveyConfig), (SurveyType.ORR, "en", ORRBusinessSurveyConfig), (SurveyType.UKHSA_ONS, "en", UKHSAONSSocialSurveyConfig), + (SurveyType.ONS_NHS, "en", ONSNHSSocialSurveyConfig), (None, None, BusinessSurveyConfig), ], ) diff --git a/tests/functional/spec/theme_nhse.spec.js b/tests/functional/spec/theme_nhse.spec.js new file mode 100644 index 0000000000..660613c73f --- /dev/null +++ b/tests/functional/spec/theme_nhse.spec.js @@ -0,0 +1,16 @@ +import RadioPage from "../generated_pages/theme_ons_nhs/radio.page"; +import { expect } from "@wdio/globals"; + +describe("Theme NHSE", () => { + describe("Given I launch a NHSE themed questionnaire", () => { + before(async () => { + await browser.openQuestionnaire("test_theme_ons_nhs.json"); + }); + + it("When I navigate to the radio page, Then I should see NHSE theme content", async () => { + await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics"); + await expect(await $("#nhs-logo-alt").getHTML()).toContain("National Heath Service"); + }); + }); +}); diff --git a/tests/functional/spec/theme_ukhsa_ons.spec.js b/tests/functional/spec/theme_ukhsa_ons.spec.js index fb0b6d6836..e329df91dc 100644 --- a/tests/functional/spec/theme_ukhsa_ons.spec.js +++ b/tests/functional/spec/theme_ukhsa_ons.spec.js @@ -9,7 +9,7 @@ describe("Theme UKHSA-ONS", () => { it("When I navigate to the radio page, Then I should see UKHSA-ONS theme content", async () => { await expect(browser).toHaveUrlContaining(RadioPage.pageName); - await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics logo"); + await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics"); await expect(await $("#ukhsa-logo-alt").getHTML()).toContain("UK Health Security Agency"); }); }); From 5be5afe280b43a65d566b69580153d906fcf7b2c Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 29 May 2024 12:00:33 +0100 Subject: [PATCH 420/567] Schemas v5.2.0 (#1372) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6b7ace3e91..ee80415e10 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.1.0 +v5.2.0 From 204a3570fec8ddeba6ab66625907f3abeb3ea71f Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 30 May 2024 11:30:58 +0100 Subject: [PATCH 421/567] Add correct aria labels to change and remove actions on summaries (#1368) --- app/jinja_filters.py | 34 ++-- app/translations/messages.pot | 145 +++++++++--------- .../partials/summary/collapsible-summary.html | 2 +- templates/partials/summary/summary.html | 4 +- tests/app/test_jinja_filters.py | 30 ++-- 5 files changed, 111 insertions(+), 104 deletions(-) diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 1c65af0dad..29d8af10df 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -434,10 +434,18 @@ def __init__( answer: SelectFieldBase._Option, item_title: str, edit_link_text: str, - edit_link_aria_label: str, + item_name: str | None = None, ) -> None: self.text = edit_link_text - self.visuallyHiddenText = edit_link_aria_label + " " + item_title + if item_name: + self.visuallyHiddenText = flask_babel.lazy_gettext( + "Change answer for {item_name}: {question_title_or_answer_label}" + ).format(item_name=item_name, question_title_or_answer_label=item_title) + else: + self.visuallyHiddenText = flask_babel.lazy_gettext( + "Change your answer for: {question_title_or_answer_label}" + ).format(question_title_or_answer_label=item_title) + self.url = answer["link"] self.attributes = { @@ -466,9 +474,9 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche answers_are_editable: bool, no_answer_provided: str, edit_link_text: str, - edit_link_aria_label: str, summary_type: str, use_answer_label: bool = False, + item_name: str | None = None, ) -> None: answer_type = answer.get("type", "calculated") if ( @@ -540,9 +548,7 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche if answers_are_editable: self.actions = [ - SummaryAction( - answer, self.rowTitle, edit_link_text, edit_link_aria_label - ) + SummaryAction(answer, self.rowTitle, edit_link_text, item_name) ] @@ -554,8 +560,8 @@ def __init__( answers_are_editable: bool, no_answer_provided: str, edit_link_text: str, - edit_link_aria_label: str, use_answer_label: bool = False, + item_name: str | None = None, ) -> None: self.rowTitle = strip_tags(question["title"]) self.id = question["id"] @@ -573,9 +579,9 @@ def __init__( answers_are_editable, no_answer_provided, edit_link_text, - edit_link_aria_label, summary_type, use_answer_label, + item_name, ) ) @@ -603,7 +609,6 @@ def map_summary_item_config( answers_are_editable, no_answer_provided, edit_link_text, - edit_link_aria_label, ) ) elif block.get("calculated_summary"): @@ -614,7 +619,6 @@ def map_summary_item_config( answers_are_editable, no_answer_provided, edit_link_text, - edit_link_aria_label, ) ) else: @@ -628,12 +632,13 @@ def map_summary_item_config( related_answers=block.get("related_answers"), item_label=block.get("item_label"), item_anchor=block.get("item_anchor"), + answers_are_editable=answers_are_editable, ) rows.extend(list_collector_rows) if is_summary_with_calculation(summary_type): - rows.append(SummaryRow(calculated_question, summary_type, False, "", "", "")) + rows.append(SummaryRow(calculated_question, summary_type, False, "", "")) return rows @@ -656,11 +661,12 @@ def map_list_collector_config( related_answers: dict | None = None, item_label: str | None = None, item_anchor: str | None = None, + answers_are_editable: bool = True, ) -> list[dict[str, list] | SummaryRow]: rows: list[dict[str, list] | SummaryRow] = [] for index, list_item in enumerate(list_items, start=1): - item_name = list_item.get("item_title") + item_name = str(list_item.get("item_title")) actions = [] edit_link_hidden_text = None @@ -731,11 +737,11 @@ def map_list_collector_config( summary_row = SummaryRow( block["question"], summary_type="SectionSummary", - answers_are_editable=True, + answers_are_editable=answers_are_editable, no_answer_provided=flask_babel.lazy_gettext("No answer provided"), edit_link_text=edit_link_text, - edit_link_aria_label=edit_link_aria_label, use_answer_label=True, + item_name=item_name, ) row_items.extend(summary_row.rowItems) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 0310115b70..469dc08389 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-04-24 15:36+0100\n" +"POT-Creation-Date: 2024-05-21 12:29+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -41,7 +41,15 @@ msgstr "" msgid "%(from_date)s to %(to_date)s" msgstr "" -#: app/jinja_filters.py:732 +#: app/jinja_filters.py:441 +msgid "Change answer for {item_name}: {question_title_or_answer_label}" +msgstr "" + +#: app/jinja_filters.py:443 +msgid "Change your answer for: {question_title_or_answer_label}" +msgstr "" + +#: app/jinja_filters.py:734 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:24 msgid "No answer provided" @@ -419,7 +427,7 @@ msgstr "" #: templates/individual_response/confirmation-text-message.html:39 #: templates/individual_response/question.html:5 templates/interstitial.html:7 #: templates/listcollectorcontent.html:6 templates/sectionsummary.html:11 -#: templates/sectionsummary.html:50 +#: templates/sectionsummary.html:51 msgid "Continue" msgstr "" @@ -509,7 +517,7 @@ msgstr "" #: app/views/handlers/confirm_email.py:128 #: app/views/handlers/confirmation_email.py:65 -#: app/views/handlers/feedback.py:78 app/views/handlers/question.py:173 +#: app/views/handlers/feedback.py:73 app/views/handlers/question.py:173 msgid "Error: {page_title}" msgstr "" @@ -517,39 +525,39 @@ msgstr "" msgid "Confirmation email" msgstr "" -#: app/views/handlers/feedback.py:44 +#: app/views/handlers/feedback.py:39 msgid "Feedback" msgstr "" -#: app/views/handlers/feedback.py:132 +#: app/views/handlers/feedback.py:122 msgid "Give feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:138 app/views/handlers/feedback.py:162 +#: app/views/handlers/feedback.py:128 app/views/handlers/feedback.py:152 msgid "Select what your feedback is about" msgstr "" -#: app/views/handlers/feedback.py:141 app/views/handlers/feedback.py:142 +#: app/views/handlers/feedback.py:131 app/views/handlers/feedback.py:132 msgid "The survey questions" msgstr "" -#: app/views/handlers/feedback.py:143 +#: app/views/handlers/feedback.py:133 msgid "For example, questions not clear, answer options not relevant" msgstr "" -#: app/views/handlers/feedback.py:148 app/views/handlers/feedback.py:149 +#: app/views/handlers/feedback.py:138 app/views/handlers/feedback.py:139 msgid "Page design and structure" msgstr "" -#: app/views/handlers/feedback.py:152 app/views/handlers/feedback.py:155 +#: app/views/handlers/feedback.py:142 app/views/handlers/feedback.py:145 msgid "General feedback about this service" msgstr "" -#: app/views/handlers/feedback.py:170 app/views/handlers/feedback.py:180 +#: app/views/handlers/feedback.py:160 app/views/handlers/feedback.py:170 msgid "Enter your feedback" msgstr "" -#: app/views/handlers/feedback.py:171 +#: app/views/handlers/feedback.py:161 msgid "Do not include confidential information, such as your contact details" msgstr "" @@ -767,14 +775,14 @@ msgstr "" msgid "Back" msgstr "" -#: templates/feedback.html:30 +#: templates/feedback.html:31 #, python-format msgid "There is a problem with your feedback" msgid_plural "There are %(num)s problems with your feedback" msgstr[0] "" msgstr[1] "" -#: templates/feedback.html:40 +#: templates/feedback.html:41 msgid "Send feedback" msgstr "" @@ -782,7 +790,7 @@ msgstr "" msgid "If you can’t answer someone else’s questions" msgstr "" -#: templates/interstitial.html:22 templates/partials/question.html:46 +#: templates/interstitial.html:22 templates/partials/question.html:47 msgid "If you can’t answer questions for this person" msgstr "" @@ -818,37 +826,37 @@ msgstr "" msgid "Close this window to continue with your current survey" msgstr "" -#: templates/preview.html:33 +#: templates/preview.html:35 msgid "Preview of the questions in this survey" msgstr "" -#: templates/preview.html:41 +#: templates/preview.html:43 msgid "To answer these questions you need to start survey" msgstr "" -#: templates/preview.html:46 +#: templates/preview.html:48 msgid "" "You may not have to answer all of these questions. The questions you see " "will depend on the answers you provide." msgstr "" -#: templates/preview.html:52 +#: templates/preview.html:54 msgid "Print questions" msgstr "" -#: templates/preview.html:65 +#: templates/preview.html:68 msgid "Save questions as PDF" msgstr "" -#: templates/partials/introduction/preview.html:32 +#: templates/partials/introduction/preview.html:34 #: templates/partials/summary/collapsible-summary.html:51 -#: templates/preview.html:94 +#: templates/preview.html:98 msgid "Show all" msgstr "" -#: templates/partials/introduction/preview.html:33 +#: templates/partials/introduction/preview.html:35 #: templates/partials/summary/collapsible-summary.html:52 -#: templates/preview.html:95 +#: templates/preview.html:99 msgid "Hide all" msgstr "" @@ -878,73 +886,73 @@ msgstr "" msgid "Back to surveys" msgstr "" -#: templates/thank-you.html:40 +#: templates/thank-you.html:42 msgid "Thank you for completing the {survey_title}" msgstr "" -#: templates/thank-you.html:52 +#: templates/thank-you.html:54 msgid "" "Your response will help inform decision-makers how best to support the UK" " population and economy." msgstr "" -#: templates/thank-you.html:55 +#: templates/thank-you.html:57 msgid "Learn more about how we use this data" msgstr "" -#: templates/thank-you.html:58 templates/view-submitted-response.html:72 +#: templates/thank-you.html:60 templates/view-submitted-response.html:76 msgid "For security, you can no longer view or get a copy of your answers" msgstr "" -#: templates/thank-you.html:72 +#: templates/thank-you.html:74 msgid "For security, your answers will only be available to view for another " msgstr "" -#: templates/thank-you.html:73 +#: templates/thank-you.html:75 msgid "Get a copy of your answers" msgstr "" -#: templates/thank-you.html:75 +#: templates/thank-you.html:77 msgid "" "You can save or " "print your answers for your records." msgstr "" -#: templates/layouts/_base.html:146 templates/thank-you.html:81 +#: templates/layouts/_base.html:148 templates/thank-you.html:83 msgid "minute" msgstr "" -#: templates/layouts/_base.html:147 templates/thank-you.html:82 +#: templates/layouts/_base.html:149 templates/thank-you.html:84 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:148 templates/thank-you.html:83 +#: templates/layouts/_base.html:150 templates/thank-you.html:85 msgid "second" msgstr "" -#: templates/layouts/_base.html:149 templates/thank-you.html:84 +#: templates/layouts/_base.html:151 templates/thank-you.html:86 msgid "seconds" msgstr "" -#: templates/thank-you.html:86 +#: templates/thank-you.html:88 msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" -#: templates/thank-you.html:96 +#: templates/thank-you.html:98 msgid "Get confirmation email" msgstr "" -#: templates/thank-you.html:98 +#: templates/thank-you.html:100 msgid "" "If you would like to be sent confirmation that you have completed your " "survey, enter your email address" msgstr "" -#: templates/view-submitted-response.html:33 +#: templates/view-submitted-response.html:35 msgid "Print answers" msgstr "" -#: templates/view-submitted-response.html:46 +#: templates/view-submitted-response.html:49 msgid "Save answers as PDF" msgstr "" @@ -1197,67 +1205,67 @@ msgstr "" msgid "Previous" msgstr "" -#: templates/layouts/_base.html:58 +#: templates/layouts/_base.html:60 msgid "Tell us whether you accept cookies" msgstr "" -#: templates/layouts/_base.html:59 +#: templates/layouts/_base.html:61 msgid "" "We use cookies to collect information" " about how you use {cookie_domain}. We use this information to make the " "website work as well as possible and improve our services." msgstr "" -#: templates/layouts/_base.html:60 +#: templates/layouts/_base.html:62 msgid "" "You’ve accepted all cookies. You can change your cookie preferences at any " "time." msgstr "" -#: templates/layouts/_base.html:61 +#: templates/layouts/_base.html:63 msgid "Accept all cookies" msgstr "" -#: templates/layouts/_base.html:62 +#: templates/layouts/_base.html:64 msgid "Set cookie preferences" msgstr "" -#: templates/layouts/_base.html:63 +#: templates/layouts/_base.html:65 msgid "Hide" msgstr "" -#: templates/layouts/_base.html:113 +#: templates/layouts/_base.html:115 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:141 +#: templates/layouts/_base.html:143 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:142 +#: templates/layouts/_base.html:144 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:143 +#: templates/layouts/_base.html:145 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:144 +#: templates/layouts/_base.html:146 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:145 +#: templates/layouts/_base.html:147 msgid "Continue survey" msgstr "" -#: templates/layouts/_calculatedsummary.html:18 +#: templates/layouts/_calculatedsummary.html:17 msgid "Yes, I confirm this is correct" msgstr "" -#: templates/layouts/_questionnaire.html:46 +#: templates/layouts/_questionnaire.html:47 msgid "Choose another section and return to this later" msgstr "" @@ -1269,15 +1277,15 @@ msgstr "" msgid "Interviewer note:" msgstr "" -#: templates/partials/confirmation-email-form.html:26 +#: templates/partials/confirmation-email-form.html:27 msgid "Email address" msgstr "" -#: templates/partials/confirmation-email-form.html:27 +#: templates/partials/confirmation-email-form.html:28 msgid "This will not be stored and only used once to send your confirmation" msgstr "" -#: templates/partials/confirmation-email-form.html:38 +#: templates/partials/confirmation-email-form.html:39 msgid "Send confirmation" msgstr "" @@ -1293,13 +1301,13 @@ msgstr "" msgid "Give feedback" msgstr "" -#: templates/partials/individual-response-guidance.html:18 +#: templates/partials/individual-response-guidance.html:19 msgid "" "You can share your household access code with the people" " you live with so they can complete their own sections." msgstr "" -#: templates/partials/individual-response-guidance.html:21 +#: templates/partials/individual-response-guidance.html:22 msgid "" "If this is not possible, there are other ways each person " "can complete their own census." @@ -1316,7 +1324,7 @@ msgid "" msgstr "" #: templates/partials/preview-question.html:31 -#: templates/partials/question.html:85 +#: templates/partials/question.html:86 msgid "Or" msgstr "" @@ -1324,19 +1332,19 @@ msgstr "" msgid "{max_characters} characters can be added." msgstr "" -#: templates/partials/question.html:77 +#: templates/partials/question.html:78 msgid "Selecting this will clear your answer" msgstr "" -#: templates/partials/question.html:78 +#: templates/partials/question.html:79 msgid "cleared" msgstr "" -#: templates/partials/question.html:80 +#: templates/partials/question.html:81 msgid "Selecting this will deselect any selected options" msgstr "" -#: templates/partials/question.html:81 templates/partials/question.html:89 +#: templates/partials/question.html:82 templates/partials/question.html:90 msgid "deselected" msgstr "" @@ -1509,11 +1517,8 @@ msgid "Change" msgstr "" #: templates/partials/summary/collapsible-summary.html:29 -#: templates/partials/summary/summary.html:28 -msgid "Change your answer for:" -msgstr "" - #: templates/partials/summary/list-summary.html:9 +#: templates/partials/summary/summary.html:28 msgid "Change details for {item_name}" msgstr "" @@ -1527,6 +1532,6 @@ msgid "Remove {item_name}" msgstr "" #: templates/partials/summary/summary.html:26 -msgid "Remove your answer for:" +msgid "Remove details for {item_name}" msgstr "" diff --git a/templates/partials/summary/collapsible-summary.html b/templates/partials/summary/collapsible-summary.html index f264784c91..e443df3e5d 100644 --- a/templates/partials/summary/collapsible-summary.html +++ b/templates/partials/summary/collapsible-summary.html @@ -26,7 +26,7 @@ answers_are_editable=content.summary.answers_are_editable, no_answer_provided=_("No answer provided"), edit_link_text=_("Change"), - edit_link_aria_label=_("Change your answer for:"), + edit_link_aria_label=_("Change details for {item_name}"), calculated_question=content.summary.calculated_question ) } diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 5e370158e2..c0fa459536 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -23,9 +23,9 @@ answers_are_editable=content.summary.answers_are_editable, no_answer_provided=_("No answer provided"), remove_link_text=_("Remove") if not view_submitted_response else "", - remove_link_aria_label=_("Remove your answer for:") if not view_submitted_response else "", + remove_link_aria_label=_("Remove details for {item_name}") if not view_submitted_response else "", edit_link_text=_("Change") if not view_submitted_response else "", - edit_link_aria_label=_("Change your answer for:") if not view_submitted_response else "", + edit_link_aria_label=_("Change details for {item_name}") if not view_submitted_response else "", calculated_question=content.summary.calculated_question ), "classes": "ons-u-mt-m" if loop.index > 1 else "", diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index 8d76e96c56..d8d45a160f 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -744,7 +744,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): { "actions": [ { - "visuallyHiddenText": "edit_link_aria_label Registration number", + "visuallyHiddenText": "Change answer for Name of UK company or branch: Registration number", "attributes": { "data-ga": "click", "data-ga-action": "Edit", @@ -766,7 +766,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): { "actions": [ { - "visuallyHiddenText": "edit_link_aria_label Is this UK " + "visuallyHiddenText": "Change answer for Name of UK company or branch: Is this UK " "company or branch an authorised " "insurer?", "attributes": { @@ -903,7 +903,6 @@ def test_calculated_summary_config(): answers_are_editable=True, no_answer_provided="No answer Provided", edit_link_text="Change", - edit_link_aria_label="Change your answer for", ), SummaryRow( question={ @@ -926,7 +925,6 @@ def test_calculated_summary_config(): answers_are_editable=True, no_answer_provided="No answer Provided", edit_link_text="Change", - edit_link_aria_label="Change your answer for", ), SummaryRow( question={ @@ -938,7 +936,6 @@ def test_calculated_summary_config(): answers_are_editable=False, no_answer_provided=None, edit_link_text=None, - edit_link_aria_label=None, ), ] @@ -1015,13 +1012,13 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "visuallyHiddenText": "Change your answer for:", + "visuallyHiddenText": "Change details for Company A", "attributes": {"data-qa": "list-item-change-1-link"}, "text": "Change", "url": "change_link_url", }, { - "visuallyHiddenText": "Remove Company A", + "visuallyHiddenText": "Remove details for Company A", "attributes": {"data-qa": "list-item-remove-1-link"}, "text": "Remove", "url": "remove_link_url", @@ -1039,8 +1036,7 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "visuallyHiddenText": "Change your answer for: " - "Registration number", + "visuallyHiddenText": "Change answer for Company A: Registration number", "attributes": { "data-ga": "click", "data-ga-action": "Edit", @@ -1062,7 +1058,7 @@ def test_summary_item_config_with_list_collector(): { "actions": [ { - "visuallyHiddenText": "Change your answer for: Is this UK " + "visuallyHiddenText": "Change answer for Company A: Is this UK " "company or branch an authorised " "insurer?", "attributes": { @@ -1157,10 +1153,10 @@ def test_summary_item_config_with_list_collector(): summary_type="SectionSummary", answers_are_editable=True, no_answer_provided="No answer Provided", - remove_link_aria_label="Remove Company A", + remove_link_aria_label="Remove details for {item_name}", remove_link_text="Remove", edit_link_text="Change", - edit_link_aria_label="Change your answer for:", + edit_link_aria_label="Change details for {item_name}", calculated_question={}, ) @@ -1175,13 +1171,13 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): { "actions": [ { - "visuallyHiddenText": "Change your answer for:", + "visuallyHiddenText": "Change details for Company A", "attributes": {"data-qa": "list-item-change-1-link"}, "text": "Change", "url": "change_link_url", }, { - "visuallyHiddenText": "Remove Company A", + "visuallyHiddenText": "Remove details for Company A", "attributes": {"data-qa": "list-item-remove-1-link"}, "text": "Remove", "url": "remove_link_url", @@ -1199,7 +1195,7 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): { "actions": [ { - "visuallyHiddenText": "Change your answer for: " + "visuallyHiddenText": "Change answer for Company A: " "Registration number", "attributes": { "data-ga": "click", @@ -1279,10 +1275,10 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): summary_type="SectionSummary", answers_are_editable=True, no_answer_provided="No answer Provided", - remove_link_aria_label="Remove Company A", + remove_link_aria_label="Remove details for {item_name}", remove_link_text="Remove", edit_link_text="Change", - edit_link_aria_label="Change your answer for:", + edit_link_aria_label="Change details for {item_name}", calculated_question={}, ) From c210f8e6c9a231c1d68fc4af07353b22a874a147 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 30 May 2024 16:10:48 +0100 Subject: [PATCH 422/567] Schemas v5.3.0 (#1374) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ee80415e10..32bc9525eb 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.2.0 +v5.3.0 From 12d0cdb95ffe9578157403b0b77715ffad74c1fd Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:59:29 +0100 Subject: [PATCH 423/567] Update development dependencies (#1375) --- app/translations/messages.pot | 14 +- poetry.lock | 1057 ++++++++++++++++++--------------- pyproject.toml | 13 +- 3 files changed, 584 insertions(+), 500 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 469dc08389..b7e09a2d23 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-05-21 12:29+0100\n" +"POT-Creation-Date: 2024-06-03 08:34+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -45,11 +45,11 @@ msgstr "" msgid "Change answer for {item_name}: {question_title_or_answer_label}" msgstr "" -#: app/jinja_filters.py:443 +#: app/jinja_filters.py:445 msgid "Change your answer for: {question_title_or_answer_label}" msgstr "" -#: app/jinja_filters.py:734 +#: app/jinja_filters.py:741 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:24 msgid "No answer provided" @@ -221,19 +221,19 @@ msgstr[1] "" msgid "Not a valid choice." msgstr "" -#: app/helpers/template_helpers.py:48 +#: app/helpers/template_helpers.py:49 msgid "ONS Surveys" msgstr "" -#: app/helpers/template_helpers.py:110 +#: app/helpers/template_helpers.py:111 msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:141 +#: app/helpers/template_helpers.py:142 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:166 +#: app/helpers/template_helpers.py:167 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" diff --git a/poetry.lock b/poetry.lock index 0db98cdbeb..c77c930868 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "astroid" @@ -71,33 +71,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.3.0" +version = "24.4.2" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, - {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, - {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, - {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, - {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, - {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, - {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, - {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, - {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, - {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, - {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, - {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, - {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, - {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, - {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, - {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, - {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, - {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, - {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, - {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, - {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, - {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, ] [package.dependencies] @@ -115,28 +115,28 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "blinker" -version = "1.7.0" +version = "1.8.2" description = "Fast, simple object-to-object and broadcast signaling" optional = false python-versions = ">=3.8" files = [ - {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, - {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, + {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, + {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] [[package]] name = "boto3" -version = "1.34.76" +version = "1.34.117" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.76-py3-none-any.whl", hash = "sha256:530a4cea3d40a6bd2f15a368ea395beef1ea6dff4491823bc48bd20c7d4da655"}, - {file = "boto3-1.34.76.tar.gz", hash = "sha256:8c598382e8fb61cfa8f75056197e9b509eb52039ebc291af3b1096241ba2542c"}, + {file = "boto3-1.34.117-py3-none-any.whl", hash = "sha256:1506589e30566bbb2f4997b60968ff7d4ef8a998836c31eedd36437ac3b7408a"}, + {file = "boto3-1.34.117.tar.gz", hash = "sha256:c8a383b904d6faaf7eed0c06e31b423db128e4c09ce7bd2afc39d1cd07030a51"}, ] [package.dependencies] -botocore = ">=1.34.76,<1.35.0" +botocore = ">=1.34.117,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -145,13 +145,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.76" +version = "1.34.117" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.76-py3-none-any.whl", hash = "sha256:62e45e7374844ee39e86a96fe7f5e973eb5bf3469da028b4e3a8caba0909fb1f"}, - {file = "botocore-1.34.76.tar.gz", hash = "sha256:68be44487a95132fccbc0b836fded4190dae30324f6bf822e1b6efd385ffdc83"}, + {file = "botocore-1.34.117-py3-none-any.whl", hash = "sha256:26a431997f882bcdd1e835f44c24b2a1752b1c4e5183c2ce62999ce95d518d6c"}, + {file = "botocore-1.34.117.tar.gz", hash = "sha256:4637ca42e6c51aebc4d9a2d92f97bf4bdb042e3f7985ff31a659a11e4c170e73"}, ] [package.dependencies] @@ -160,7 +160,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.19.19)"] +crt = ["awscrt (==0.20.9)"] [[package]] name = "brotli" @@ -306,13 +306,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -522,63 +522,63 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.4.4" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.extras] @@ -698,43 +698,43 @@ dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-xdi [[package]] name = "cryptography" -version = "42.0.5" +version = "42.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, - {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, - {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, - {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, - {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, - {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, - {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, ] [package.dependencies] @@ -851,13 +851,13 @@ idna = ">=2.0.0" [[package]] name = "execnet" -version = "2.0.2" +version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, - {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, + {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, + {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, ] [package.extras] @@ -865,13 +865,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.21.3" +version = "2.23.2" description = "Python implementation of redis API, can be used for testing purposes." optional = false -python-versions = ">=3.7,<4.0" +python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.21.3-py3-none-any.whl", hash = "sha256:033fe5882a20ec308ed0cf67a86c1cd982a1bffa63deb0f52eaa625bd8ce305f"}, - {file = "fakeredis-2.21.3.tar.gz", hash = "sha256:e9e1c309d49d83c4ce1ab6f3ee2e56787f6a5573a305109017bf140334dd396d"}, + {file = "fakeredis-2.23.2-py3-none-any.whl", hash = "sha256:3721946b955930c065231befd24a9cdc68b339746e93848ef01a010d98e4eb4f"}, + {file = "fakeredis-2.23.2.tar.gz", hash = "sha256:d649c409abe46c63690b6c35d3c460e4ce64c69a52cea3f02daff2649378f878"}, ] [package.dependencies] @@ -882,7 +882,7 @@ sortedcontainers = ">=2,<3" bf = ["pyprobables (>=0.6,<0.7)"] cf = ["pyprobables (>=0.6,<0.7)"] json = ["jsonpath-ng (>=1.6,<2.0)"] -lua = ["lupa (>=1.14,<3.0)"] +lua = ["lupa (>=2.1,<3.0)"] probabilistic = ["pyprobables (>=0.6,<0.7)"] [[package]] @@ -973,13 +973,13 @@ six = "*" [[package]] name = "flask" -version = "3.0.2" +version = "3.0.3" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.8" files = [ - {file = "flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e"}, - {file = "flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d"}, + {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, + {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, ] [package.dependencies] @@ -1012,19 +1012,23 @@ pytz = ">=2022.7" [[package]] name = "flask-compress" -version = "1.14" -description = "Compress responses in your Flask app with gzip, deflate or brotli." +version = "1.15" +description = "Compress responses in your Flask app with gzip, deflate, brotli or zstandard." optional = false python-versions = "*" files = [ - {file = "Flask-Compress-1.14.tar.gz", hash = "sha256:e46528f37b91857012be38e24e65db1a248662c3dc32ee7808b5986bf1d123ee"}, - {file = "Flask_Compress-1.14-py3-none-any.whl", hash = "sha256:b86c9808f0f38ea2246c9730972cf978f2cdf6a9a1a69102ba81e07891e6b26c"}, + {file = "Flask_Compress-1.15-py3-none-any.whl", hash = "sha256:5d6efe3584c89516c3ab9d94adabe08c218517b957a9bd5cd0c3955dd3834c51"}, + {file = "flask_compress-1.15.tar.gz", hash = "sha256:b7b66cd5d08fc46bbcc71561e13ca64321590b0ca4c172f8001bf5374f8f5c58"}, ] [package.dependencies] brotli = {version = "*", markers = "platform_python_implementation != \"PyPy\""} brotlicffi = {version = "*", markers = "platform_python_implementation == \"PyPy\""} flask = "*" +zstandard = [ + {version = "*", markers = "platform_python_implementation != \"PyPy\""}, + {version = "*", extras = ["cffi"], markers = "platform_python_implementation == \"PyPy\""}, +] [[package]] name = "flask-login" @@ -1073,13 +1077,13 @@ email = ["email-validator"] [[package]] name = "freezegun" -version = "1.4.0" +version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, + {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, + {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, ] [package.dependencies] @@ -1150,13 +1154,13 @@ test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.18.0" +version = "2.19.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.18.0.tar.gz", hash = "sha256:62d97417bfc674d6cef251e5c4d639a9655e00c45528c4364fbfebb478ce72a9"}, - {file = "google_api_core-2.18.0-py3-none-any.whl", hash = "sha256:5a63aa102e0049abe85b5b88cb9409234c1f70afcda21ce1e40b285b9629c1d6"}, + {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, + {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, ] [package.dependencies] @@ -1236,13 +1240,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.0" +version = "2.21.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.0.tar.gz", hash = "sha256:94017f0bc9a85fa3f4d913f312e930a0fe21775bd68dde5c666e2f1b1addf811"}, - {file = "google_cloud_pubsub-2.21.0-py2.py3-none-any.whl", hash = "sha256:fabd19e08faa1f70081b0e5ea003a3c031a1d2c1b798cf1be5ded2dbf1d1dbef"}, + {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, + {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, ] [package.dependencies] @@ -1502,84 +1506,76 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.62.1" +version = "1.64.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"}, - {file = "grpcio-1.62.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:48611e4fa010e823ba2de8fd3f77c1322dd60cb0d180dc6630a7e157b205f7ea"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b2a0e71b0a2158aa4bce48be9f8f9eb45cbd17c78c7443616d00abbe2a509f6d"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbe80577c7880911d3ad65e5ecc997416c98f354efeba2f8d0f9112a67ed65a5"}, - {file = "grpcio-1.62.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58f6c693d446964e3292425e1d16e21a97a48ba9172f2d0df9d7b640acb99243"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:77c339403db5a20ef4fed02e4d1a9a3d9866bf9c0afc77a42234677313ea22f3"}, - {file = "grpcio-1.62.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b5a4ea906db7dec694098435d84bf2854fe158eb3cd51e1107e571246d4d1d70"}, - {file = "grpcio-1.62.1-cp310-cp310-win32.whl", hash = "sha256:4187201a53f8561c015bc745b81a1b2d278967b8de35f3399b84b0695e281d5f"}, - {file = "grpcio-1.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:844d1f3fb11bd1ed362d3fdc495d0770cfab75761836193af166fee113421d66"}, - {file = "grpcio-1.62.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:833379943d1728a005e44103f17ecd73d058d37d95783eb8f0b28ddc1f54d7b2"}, - {file = "grpcio-1.62.1-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c7fcc6a32e7b7b58f5a7d27530669337a5d587d4066060bcb9dee7a8c833dfb7"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:fa7d28eb4d50b7cbe75bb8b45ed0da9a1dc5b219a0af59449676a29c2eed9698"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48f7135c3de2f298b833be8b4ae20cafe37091634e91f61f5a7eb3d61ec6f660"}, - {file = "grpcio-1.62.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71f11fd63365ade276c9d4a7b7df5c136f9030e3457107e1791b3737a9b9ed6a"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b49fd8fe9f9ac23b78437da94c54aa7e9996fbb220bac024a67469ce5d0825f"}, - {file = "grpcio-1.62.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:482ae2ae78679ba9ed5752099b32e5fe580443b4f798e1b71df412abf43375db"}, - {file = "grpcio-1.62.1-cp311-cp311-win32.whl", hash = "sha256:1faa02530b6c7426404372515fe5ddf66e199c2ee613f88f025c6f3bd816450c"}, - {file = "grpcio-1.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bd90b8c395f39bc82a5fb32a0173e220e3f401ff697840f4003e15b96d1befc"}, - {file = "grpcio-1.62.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:b134d5d71b4e0837fff574c00e49176051a1c532d26c052a1e43231f252d813b"}, - {file = "grpcio-1.62.1-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d1f6c96573dc09d50dbcbd91dbf71d5cf97640c9427c32584010fbbd4c0e0037"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:359f821d4578f80f41909b9ee9b76fb249a21035a061a327f91c953493782c31"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a485f0c2010c696be269184bdb5ae72781344cb4e60db976c59d84dd6354fac9"}, - {file = "grpcio-1.62.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b50b09b4dc01767163d67e1532f948264167cd27f49e9377e3556c3cba1268e1"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3227c667dccbe38f2c4d943238b887bac588d97c104815aecc62d2fd976e014b"}, - {file = "grpcio-1.62.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3952b581eb121324853ce2b191dae08badb75cd493cb4e0243368aa9e61cfd41"}, - {file = "grpcio-1.62.1-cp312-cp312-win32.whl", hash = "sha256:83a17b303425104d6329c10eb34bba186ffa67161e63fa6cdae7776ff76df73f"}, - {file = "grpcio-1.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:6696ffe440333a19d8d128e88d440f91fb92c75a80ce4b44d55800e656a3ef1d"}, - {file = "grpcio-1.62.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:e3393b0823f938253370ebef033c9fd23d27f3eae8eb9a8f6264900c7ea3fb5a"}, - {file = "grpcio-1.62.1-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83e7ccb85a74beaeae2634f10eb858a0ed1a63081172649ff4261f929bacfd22"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:882020c87999d54667a284c7ddf065b359bd00251fcd70279ac486776dbf84ec"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a10383035e864f386fe096fed5c47d27a2bf7173c56a6e26cffaaa5a361addb1"}, - {file = "grpcio-1.62.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:960edebedc6b9ada1ef58e1c71156f28689978188cd8cff3b646b57288a927d9"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:23e2e04b83f347d0aadde0c9b616f4726c3d76db04b438fd3904b289a725267f"}, - {file = "grpcio-1.62.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978121758711916d34fe57c1f75b79cdfc73952f1481bb9583399331682d36f7"}, - {file = "grpcio-1.62.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9084086190cc6d628f282e5615f987288b95457292e969b9205e45b442276407"}, - {file = "grpcio-1.62.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:22bccdd7b23c420a27fd28540fb5dcbc97dc6be105f7698cb0e7d7a420d0e362"}, - {file = "grpcio-1.62.1-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:8999bf1b57172dbc7c3e4bb3c732658e918f5c333b2942243f10d0d653953ba9"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d9e52558b8b8c2f4ac05ac86344a7417ccdd2b460a59616de49eb6933b07a0bd"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1714e7bc935780bc3de1b3fcbc7674209adf5208ff825799d579ffd6cd0bd505"}, - {file = "grpcio-1.62.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8842ccbd8c0e253c1f189088228f9b433f7a93b7196b9e5b6f87dba393f5d5d"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f1e7b36bdff50103af95a80923bf1853f6823dd62f2d2a2524b66ed74103e49"}, - {file = "grpcio-1.62.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bba97b8e8883a8038606480d6b6772289f4c907f6ba780fa1f7b7da7dfd76f06"}, - {file = "grpcio-1.62.1-cp38-cp38-win32.whl", hash = "sha256:a7f615270fe534548112a74e790cd9d4f5509d744dd718cd442bf016626c22e4"}, - {file = "grpcio-1.62.1-cp38-cp38-win_amd64.whl", hash = "sha256:e6c8c8693df718c5ecbc7babb12c69a4e3677fd11de8886f05ab22d4e6b1c43b"}, - {file = "grpcio-1.62.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:73db2dc1b201d20ab7083e7041946910bb991e7e9761a0394bbc3c2632326483"}, - {file = "grpcio-1.62.1-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:407b26b7f7bbd4f4751dbc9767a1f0716f9fe72d3d7e96bb3ccfc4aace07c8de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f8de7c8cef9261a2d0a62edf2ccea3d741a523c6b8a6477a340a1f2e417658de"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd5c8a1af40ec305d001c60236308a67e25419003e9bb3ebfab5695a8d0b369"}, - {file = "grpcio-1.62.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be0477cb31da67846a33b1a75c611f88bfbcd427fe17701b6317aefceee1b96f"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:60dcd824df166ba266ee0cfaf35a31406cd16ef602b49f5d4dfb21f014b0dedd"}, - {file = "grpcio-1.62.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:973c49086cabab773525f6077f95e5a993bfc03ba8fc32e32f2c279497780585"}, - {file = "grpcio-1.62.1-cp39-cp39-win32.whl", hash = "sha256:12859468e8918d3bd243d213cd6fd6ab07208195dc140763c00dfe901ce1e1b4"}, - {file = "grpcio-1.62.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7209117bbeebdfa5d898205cc55153a51285757902dd73c47de498ad4d11332"}, - {file = "grpcio-1.62.1.tar.gz", hash = "sha256:6c455e008fa86d9e9a9d85bb76da4277c0d7d9668a3bfa70dbe86e9f3c759947"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.62.1)"] + {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, + {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, + {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, + {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, + {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, + {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, + {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, + {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, + {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, + {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, + {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, + {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, + {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, + {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, + {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, + {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, + {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, + {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, + {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, + {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, + {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, + {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, + {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, + {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, + {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, + {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, + {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, + {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, + {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, + {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, + {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.0)"] [[package]] name = "grpcio-status" -version = "1.62.1" +version = "1.62.2" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.6" files = [ - {file = "grpcio-status-1.62.1.tar.gz", hash = "sha256:3431c8abbab0054912c41df5c72f03ddf3b7a67be8a287bb3c18a3456f96ff77"}, - {file = "grpcio_status-1.62.1-py3-none-any.whl", hash = "sha256:af0c3ab85da31669f21749e8d53d669c061ebc6ce5637be49a46edcb7aa8ab17"}, + {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, + {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.1" +grpcio = ">=1.62.2" protobuf = ">=4.21.6" [[package]] @@ -1715,24 +1711,24 @@ colors = ["colorama (>=0.4.6)"] [[package]] name = "itsdangerous" -version = "2.1.2" +version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, ] [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -1790,13 +1786,13 @@ files = [ [[package]] name = "jsonschema" -version = "4.21.1" +version = "4.22.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, ] [package.dependencies] @@ -1955,13 +1951,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.1" +version = "3.21.2" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, - {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, + {file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"}, + {file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"}, ] [package.dependencies] @@ -1969,7 +1965,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -2001,13 +1997,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.4" +version = "5.0.9" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.4-py2.py3-none-any.whl", hash = "sha256:4054360b882b6e7bab25d52d057e196b978b8d15f1921333f534c4d8f6510bbb"}, - {file = "moto-5.0.4.tar.gz", hash = "sha256:8d19125d40c919cb40df62f4576904c2647c4e9a0e1ebc42491dd7787d09e107"}, + {file = "moto-5.0.9-py2.py3-none-any.whl", hash = "sha256:21a13e02f83d6a18cfcd99949c96abb2e889f4bd51c4c6a3ecc8b78765cb854e"}, + {file = "moto-5.0.9.tar.gz", hash = "sha256:eb71f1cba01c70fff1f16086acb24d6d9aeb32830d646d8989f98a29aeae24ba"}, ] [package.dependencies] @@ -2022,61 +2018,61 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.2)", "pyparsing (>=3.0.7)", "setuptools"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.2)", "pyparsing (>=3.0.7)", "setuptools"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] cognitoidp = ["joserfc (>=0.9.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.2)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.2)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.2)", "pyparsing (>=3.0.7)", "setuptools"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.2)", "pyparsing (>=3.0.7)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.2)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.2)"] -server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.2)", "pyparsing (>=3.0.7)", "setuptools"] +proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.5)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.5)"] +server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] stepfunctions = ["antlr4-python3-runtime", "jsonpath-ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.9.0" +version = "1.10.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, + {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, + {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, + {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, + {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, + {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, + {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, + {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, + {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, + {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, + {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, + {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, + {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, + {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, + {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, + {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, + {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, + {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, + {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, + {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, + {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, + {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, + {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, ] [package.dependencies] @@ -2177,48 +2173,49 @@ twisted = ["twisted"] [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "playwright" -version = "1.42.0" +version = "1.44.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" files = [ - {file = "playwright-1.42.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:e2b293f077efeaa45253fde31cea4bc6b0ae8be6b5e65e8ce8b4aa3b9f0d55b6"}, - {file = "playwright-1.42.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:283887f0bdd0039c3d720e32fbc73a045c24fa800599a6ad60fb199c29580534"}, - {file = "playwright-1.42.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:4e1fc1c049a0af64626ddd50814d14a01f316bcbb4d1aa83c3416fe420add558"}, - {file = "playwright-1.42.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:313f2551a772f57c9ccca017c4dd4661f2277166f9e1d84bbf5a2e316f0f892c"}, - {file = "playwright-1.42.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2a46a24641e5d468046cde567c98fdb8d85e32df901630b14dfb288cbd1ed4f"}, - {file = "playwright-1.42.0-py3-none-win32.whl", hash = "sha256:dbf473496808d4c2c816902c1dee2aabc029648e56ce8514b643f5a1a6fc8e22"}, - {file = "playwright-1.42.0-py3-none-win_amd64.whl", hash = "sha256:e092c6cfbf797bff03fbdfc53c3e6a9e29fbcf6b82f9e43113d37494aee0561b"}, + {file = "playwright-1.44.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:c2317a80896796fdeb03d60f06cc229e775ff2e19b80c64b1bb9b29c8a59d992"}, + {file = "playwright-1.44.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54d44fb634d870839301c2326e1e12a178a1be0de76d0caaec230ab075c2e077"}, + {file = "playwright-1.44.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:64b67194e73b47ae72acf25f1a9cfacfef38ca2b52e4bb8b0abd385c5deeaadf"}, + {file = "playwright-1.44.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:29161b1fae71f7c402df5b15f0bd3deaeecd8b3d1ecd9ff01271700c66210e7b"}, + {file = "playwright-1.44.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8c8a3bfea17576d3f94a2363eee195cbda8dbba86975588c7eaac7792b25eee"}, + {file = "playwright-1.44.0-py3-none-win32.whl", hash = "sha256:235e37832deaa9af8a629d09955396259ab757533cc1922f9b0308b4ee0d9cdf"}, + {file = "playwright-1.44.0-py3-none-win_amd64.whl", hash = "sha256:5b8a4a1d4d50f4ff99b47965576322a8c4e34631854b862a25c1feb824be22a8"}, ] [package.dependencies] greenlet = "3.0.3" -pyee = "11.0.1" +pyee = "11.1.0" [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -2311,20 +2308,20 @@ files = [ [[package]] name = "pyee" -version = "11.0.1" +version = "11.1.0" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" files = [ - {file = "pyee-11.0.1-py3-none-any.whl", hash = "sha256:9bcc9647822234f42c228d88de63d0f9ffa881e87a87f9d36ddf5211f6ac977d"}, - {file = "pyee-11.0.1.tar.gz", hash = "sha256:a642c51e3885a33ead087286e35212783a4e9b8d6514a10a5db4e57ac57b2b29"}, + {file = "pyee-11.1.0-py3-none-any.whl", hash = "sha256:5d346a7d0f861a4b2e6c47960295bd895f816725b27d656181947346be98d7c1"}, + {file = "pyee-11.1.0.tar.gz", hash = "sha256:b53af98f6990c810edd9b56b87791021a8f54fd13db4edd1142438d44ba2263f"}, ] [package.dependencies] typing-extensions = "*" [package.extras] -dev = ["black", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"] +dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "sphinx", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"] [[package]] name = "pyflakes" @@ -2417,33 +2414,33 @@ files = [ [[package]] name = "pytest" -version = "8.1.1" +version = "8.2.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, + {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2.0" [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "4.1.0" +version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] @@ -2451,7 +2448,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-flask" @@ -2510,18 +2507,18 @@ dev = ["black", "flake8", "pre-commit"] [[package]] name = "pytest-xdist" -version = "3.5.0" +version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, - {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, ] [package.dependencies] -execnet = ">=1.1" -pytest = ">=6.2.0" +execnet = ">=2.1" +pytest = ">=7.0.0" [package.extras] psutil = ["psutil (>=3.0)"] @@ -2629,13 +2626,13 @@ files = [ [[package]] name = "redis" -version = "5.0.3" +version = "5.0.4" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"}, - {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"}, + {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"}, + {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"}, ] [package.extras] @@ -2644,13 +2641,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" [[package]] name = "referencing" -version = "0.34.0" +version = "0.35.1" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, - {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, ] [package.dependencies] @@ -2761,13 +2758,13 @@ files = [ [[package]] name = "requests" -version = "2.32.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, - {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -2801,110 +2798,110 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.18.0" +version = "0.18.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, + {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, + {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, + {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, + {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, + {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, + {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, + {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, + {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, + {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, + {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, + {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, + {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, + {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, + {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, + {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, + {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, + {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, + {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, + {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, + {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, + {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, + {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, + {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, + {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, + {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, + {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, + {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, + {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, + {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, ] [[package]] @@ -2956,19 +2953,18 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "69.2.0" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simplejson" @@ -3112,17 +3108,17 @@ files = [ [[package]] name = "structlog" -version = "24.1.0" +version = "24.2.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" files = [ - {file = "structlog-24.1.0-py3-none-any.whl", hash = "sha256:3f6efe7d25fab6e86f277713c218044669906537bb717c1807a09d46bca0714d"}, - {file = "structlog-24.1.0.tar.gz", hash = "sha256:41a09886e4d55df25bdcb9b5c9674bccfab723ff43e0a86a1b7b236be8e57b16"}, + {file = "structlog-24.2.0-py3-none-any.whl", hash = "sha256:983bd49f70725c5e1e3867096c0c09665918936b3db27341b41d294283d7a48a"}, + {file = "structlog-24.2.0.tar.gz", hash = "sha256:0e3fe74924a6d8857d3f612739efb94c72a7417d7c7c008d12276bca3b5bf13b"}, ] [package.extras] -dev = ["structlog[tests,typing]"] +dev = ["freezegun (>=0.2.8)", "mypy (>=1.4)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "rich", "simplejson", "twisted"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] typing = ["mypy (>=1.4)", "rich", "twisted"] @@ -3143,24 +3139,24 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "tomlkit" -version = "0.12.4" +version = "0.12.5" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, + {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, + {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, ] [[package]] name = "tqdm" -version = "4.66.3" +version = "4.66.4" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.3-py3-none-any.whl", hash = "sha256:4f41d54107ff9a223dca80b53efe4fb654c67efaba7f47bada3ee9d50e05bd53"}, - {file = "tqdm-4.66.3.tar.gz", hash = "sha256:23097a41eba115ba99ecae40d06444c15d1c0c698d527a01c6c8bd1c5d0647e5"}, + {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, + {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, ] [package.dependencies] @@ -3183,19 +3179,34 @@ files = [ {file = "types_cachetools-5.3.0.7-py3-none-any.whl", hash = "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0"}, ] +[[package]] +name = "types-cffi" +version = "1.16.0.20240331" +description = "Typing stubs for cffi" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-cffi-1.16.0.20240331.tar.gz", hash = "sha256:b8b20d23a2b89cfed5f8c5bc53b0cb8677c3aac6d970dbc771e28b9c698f5dee"}, + {file = "types_cffi-1.16.0.20240331-py3-none-any.whl", hash = "sha256:a363e5ea54a4eb6a4a105d800685fde596bc318089b025b27dee09849fe41ff0"}, +] + +[package.dependencies] +types-setuptools = "*" + [[package]] name = "types-pyopenssl" -version = "24.0.0.20240311" +version = "24.1.0.20240425" description = "Typing stubs for pyOpenSSL" optional = false python-versions = ">=3.8" files = [ - {file = "types-pyOpenSSL-24.0.0.20240311.tar.gz", hash = "sha256:7bca00cfc4e7ef9c5d2663c6a1c068c35798e59670595439f6296e7ba3d58083"}, - {file = "types_pyOpenSSL-24.0.0.20240311-py3-none-any.whl", hash = "sha256:6e8e8bfad34924067333232c93f7fc4b369856d8bea0d5c9d1808cb290ab1972"}, + {file = "types-pyOpenSSL-24.1.0.20240425.tar.gz", hash = "sha256:0a7e82626c1983dc8dc59292bf20654a51c3c3881bcbb9b337c1da6e32f0204e"}, + {file = "types_pyOpenSSL-24.1.0.20240425-py3-none-any.whl", hash = "sha256:f51a156835555dd2a1f025621e8c4fbe7493470331afeef96884d1d29bf3a473"}, ] [package.dependencies] cryptography = ">=35.0.0" +types-cffi = "*" [[package]] name = "types-python-dateutil" @@ -3210,13 +3221,13 @@ files = [ [[package]] name = "types-pytz" -version = "2024.1.0.20240203" +version = "2024.1.0.20240417" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.1.0.20240203.tar.gz", hash = "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e"}, - {file = "types_pytz-2024.1.0.20240203-py3-none-any.whl", hash = "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3"}, + {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"}, + {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"}, ] [[package]] @@ -3232,13 +3243,13 @@ files = [ [[package]] name = "types-redis" -version = "4.6.0.20240311" +version = "4.6.0.20240425" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.20240311.tar.gz", hash = "sha256:e049bbdff0e0a1f8e701b64636811291d21bff79bf1e7850850a44055224a85f"}, - {file = "types_redis-4.6.0.20240311-py3-none-any.whl", hash = "sha256:6b9d68a29aba1ee400c823d8e5fe88675282eb69d7211e72fe65dbe54b33daca"}, + {file = "types-redis-4.6.0.20240425.tar.gz", hash = "sha256:9402a10ee931d241fdfcc04592ebf7a661d7bb92a8dea631279f0d8acbcf3a22"}, + {file = "types_redis-4.6.0.20240425-py3-none-any.whl", hash = "sha256:ac5bc19e8f5997b9e76ad5d9cf15d0392d9f28cf5fc7746ea4a64b989c45c6a8"}, ] [package.dependencies] @@ -3247,18 +3258,29 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.31.0.20240403" +version = "2.32.0.20240602" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240403.tar.gz", hash = "sha256:e1e0cd0b655334f39d9f872b68a1310f0e343647688bf2cee932ec4c2b04de59"}, - {file = "types_requests-2.31.0.20240403-py3-none-any.whl", hash = "sha256:06abf6a68f5c4f2a62f6bb006672dfb26ed50ccbfddb281e1ee6f09a65707d5d"}, + {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, + {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, ] [package.dependencies] urllib3 = ">=2" +[[package]] +name = "types-setuptools" +version = "70.0.0.20240524" +description = "Typing stubs for setuptools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-setuptools-70.0.0.20240524.tar.gz", hash = "sha256:e31fee7b9d15ef53980526579ac6089b3ae51a005a281acf97178e90ac71aff6"}, + {file = "types_setuptools-70.0.0.20240524-py3-none-any.whl", hash = "sha256:8f5379b9948682d72a9ab531fbe52932e84c4f38deda570255f9bae3edd766bc"}, +] + [[package]] name = "types-simplejson" version = "3.19.0.20240310" @@ -3272,13 +3294,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.1" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, + {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, ] [[package]] @@ -3311,23 +3333,23 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uwsgi" -version = "2.0.24" +version = "2.0.26" description = "The uWSGI server" optional = false python-versions = "*" files = [ - {file = "uwsgi-2.0.24.tar.gz", hash = "sha256:77b6dd5cd633f4ae87ee393f7701f617736815499407376e78f3d16467523afe"}, + {file = "uwsgi-2.0.26.tar.gz", hash = "sha256:86e6bfcd4dc20529665f5b7777193cdc48622fb2c59f0a7f1e3dc32b3882e7f9"}, ] [[package]] name = "werkzeug" -version = "3.0.2" +version = "3.0.3" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"}, - {file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"}, + {file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"}, + {file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"}, ] [package.dependencies] @@ -3463,58 +3485,119 @@ test = ["zope.testrunner"] [[package]] name = "zope-interface" -version = "6.2" +version = "6.4.post2" description = "Interfaces for Python" optional = false python-versions = ">=3.7" files = [ - {file = "zope.interface-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab"}, - {file = "zope.interface-6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f"}, - {file = "zope.interface-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328"}, - {file = "zope.interface-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd"}, - {file = "zope.interface-6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037"}, - {file = "zope.interface-6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad"}, - {file = "zope.interface-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac"}, - {file = "zope.interface-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe"}, - {file = "zope.interface-6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48"}, - {file = "zope.interface-6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000"}, - {file = "zope.interface-6.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b"}, - {file = "zope.interface-6.2-cp312-cp312-win_amd64.whl", hash = "sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe"}, - {file = "zope.interface-6.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000"}, - {file = "zope.interface-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b"}, - {file = "zope.interface-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550"}, - {file = "zope.interface-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532"}, - {file = "zope.interface-6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd"}, - {file = "zope.interface-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3"}, - {file = "zope.interface-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0"}, - {file = "zope.interface-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099"}, - {file = "zope.interface-6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f"}, - {file = "zope.interface-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a"}, - {file = "zope.interface-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1"}, - {file = "zope.interface-6.2.tar.gz", hash = "sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565"}, + {file = "zope.interface-6.4.post2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2eccd5bef45883802848f821d940367c1d0ad588de71e5cabe3813175444202c"}, + {file = "zope.interface-6.4.post2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:762e616199f6319bb98e7f4f27d254c84c5fb1c25c908c2a9d0f92b92fb27530"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef8356f16b1a83609f7a992a6e33d792bb5eff2370712c9eaae0d02e1924341"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e4fa5d34d7973e6b0efa46fe4405090f3b406f64b6290facbb19dcbf642ad6b"}, + {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d22fce0b0f5715cdac082e35a9e735a1752dc8585f005d045abb1a7c20e197f9"}, + {file = "zope.interface-6.4.post2-cp310-cp310-win_amd64.whl", hash = "sha256:97e615eab34bd8477c3f34197a17ce08c648d38467489359cb9eb7394f1083f7"}, + {file = "zope.interface-6.4.post2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:599f3b07bde2627e163ce484d5497a54a0a8437779362395c6b25e68c6590ede"}, + {file = "zope.interface-6.4.post2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:136cacdde1a2c5e5bc3d0b2a1beed733f97e2dad8c2ad3c2e17116f6590a3827"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47937cf2e7ed4e0e37f7851c76edeb8543ec9b0eae149b36ecd26176ff1ca874"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0a6be264afb094975b5ef55c911379d6989caa87c4e558814ec4f5125cfa2e"}, + {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47654177e675bafdf4e4738ce58cdc5c6d6ee2157ac0a78a3fa460942b9d64a8"}, + {file = "zope.interface-6.4.post2-cp311-cp311-win_amd64.whl", hash = "sha256:e2fb8e8158306567a3a9a41670c1ff99d0567d7fc96fa93b7abf8b519a46b250"}, + {file = "zope.interface-6.4.post2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b912750b13d76af8aac45ddf4679535def304b2a48a07989ec736508d0bbfbde"}, + {file = "zope.interface-6.4.post2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ac46298e0143d91e4644a27a769d1388d5d89e82ee0cf37bf2b0b001b9712a4"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86a94af4a88110ed4bb8961f5ac72edf782958e665d5bfceaab6bf388420a78b"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73f9752cf3596771c7726f7eea5b9e634ad47c6d863043589a1c3bb31325c7eb"}, + {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b5c3e9744dcdc9e84c24ed6646d5cf0cf66551347b310b3ffd70f056535854"}, + {file = "zope.interface-6.4.post2-cp312-cp312-win_amd64.whl", hash = "sha256:551db2fe892fcbefb38f6f81ffa62de11090c8119fd4e66a60f3adff70751ec7"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96ac6b3169940a8cd57b4f2b8edcad8f5213b60efcd197d59fbe52f0accd66e"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cebff2fe5dc82cb22122e4e1225e00a4a506b1a16fafa911142ee124febf2c9e"}, + {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ee982237cffaf946db365c3a6ebaa37855d8e3ca5800f6f48890209c1cfefc"}, + {file = "zope.interface-6.4.post2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:fbf649bc77510ef2521cf797700b96167bb77838c40780da7ea3edd8b78044d1"}, + {file = "zope.interface-6.4.post2-cp37-cp37m-win_amd64.whl", hash = "sha256:4c0b208a5d6c81434bdfa0f06d9b667e5de15af84d8cae5723c3a33ba6611b82"}, + {file = "zope.interface-6.4.post2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3fe667935e9562407c2511570dca14604a654988a13d8725667e95161d92e9b"}, + {file = "zope.interface-6.4.post2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a96e6d4074db29b152222c34d7eec2e2db2f92638d2b2b2c704f9e8db3ae0edc"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:866a0f583be79f0def667a5d2c60b7b4cc68f0c0a470f227e1122691b443c934"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fe919027f29b12f7a2562ba0daf3e045cb388f844e022552a5674fcdf5d21f1"}, + {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0343a6e06d94f6b6ac52fbc75269b41dd3c57066541a6c76517f69fe67cb43"}, + {file = "zope.interface-6.4.post2-cp38-cp38-win_amd64.whl", hash = "sha256:dabb70a6e3d9c22df50e08dc55b14ca2a99da95a2d941954255ac76fd6982bc5"}, + {file = "zope.interface-6.4.post2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:706efc19f9679a1b425d6fa2b4bc770d976d0984335eaea0869bd32f627591d2"}, + {file = "zope.interface-6.4.post2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d136e5b8821073e1a09dde3eb076ea9988e7010c54ffe4d39701adf0c303438"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1730c93a38b5a18d24549bc81613223962a19d457cfda9bdc66e542f475a36f4"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc2676312cc3468a25aac001ec727168994ea3b69b48914944a44c6a0b251e79"}, + {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a62fd6cd518693568e23e02f41816adedfca637f26716837681c90b36af3671"}, + {file = "zope.interface-6.4.post2-cp39-cp39-win_amd64.whl", hash = "sha256:d3f7e001328bd6466b3414215f66dde3c7c13d8025a9c160a75d7b2687090d15"}, + {file = "zope.interface-6.4.post2.tar.gz", hash = "sha256:1c207e6f6dfd5749a26f5a5fd966602d6b824ec00d2df84a7e9a924e8933654e"}, ] [package.dependencies] setuptools = "*" [package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx_rtd_theme"] +docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +[[package]] +name = "zstandard" +version = "0.22.0" +description = "Zstandard bindings for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zstandard-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:275df437ab03f8c033b8a2c181e51716c32d831082d93ce48002a5227ec93019"}, + {file = "zstandard-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ac9957bc6d2403c4772c890916bf181b2653640da98f32e04b96e4d6fb3252a"}, + {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe3390c538f12437b859d815040763abc728955a52ca6ff9c5d4ac707c4ad98e"}, + {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1958100b8a1cc3f27fa21071a55cb2ed32e9e5df4c3c6e661c193437f171cba2"}, + {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e1856c8313bc688d5df069e106a4bc962eef3d13372020cc6e3ebf5e045202"}, + {file = "zstandard-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1a90ba9a4c9c884bb876a14be2b1d216609385efb180393df40e5172e7ecf356"}, + {file = "zstandard-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3db41c5e49ef73641d5111554e1d1d3af106410a6c1fb52cf68912ba7a343a0d"}, + {file = "zstandard-0.22.0-cp310-cp310-win32.whl", hash = "sha256:d8593f8464fb64d58e8cb0b905b272d40184eac9a18d83cf8c10749c3eafcd7e"}, + {file = "zstandard-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a4b358947a65b94e2501ce3e078bbc929b039ede4679ddb0460829b12f7375"}, + {file = "zstandard-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:589402548251056878d2e7c8859286eb91bd841af117dbe4ab000e6450987e08"}, + {file = "zstandard-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a97079b955b00b732c6f280d5023e0eefe359045e8b83b08cf0333af9ec78f26"}, + {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:445b47bc32de69d990ad0f34da0e20f535914623d1e506e74d6bc5c9dc40bb09"}, + {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33591d59f4956c9812f8063eff2e2c0065bc02050837f152574069f5f9f17775"}, + {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:888196c9c8893a1e8ff5e89b8f894e7f4f0e64a5af4d8f3c410f0319128bb2f8"}, + {file = "zstandard-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:53866a9d8ab363271c9e80c7c2e9441814961d47f88c9bc3b248142c32141d94"}, + {file = "zstandard-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ac59d5d6910b220141c1737b79d4a5aa9e57466e7469a012ed42ce2d3995e88"}, + {file = "zstandard-0.22.0-cp311-cp311-win32.whl", hash = "sha256:2b11ea433db22e720758cba584c9d661077121fcf60ab43351950ded20283440"}, + {file = "zstandard-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:11f0d1aab9516a497137b41e3d3ed4bbf7b2ee2abc79e5c8b010ad286d7464bd"}, + {file = "zstandard-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6c25b8eb733d4e741246151d895dd0308137532737f337411160ff69ca24f93a"}, + {file = "zstandard-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9b2cde1cd1b2a10246dbc143ba49d942d14fb3d2b4bccf4618d475c65464912"}, + {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a88b7df61a292603e7cd662d92565d915796b094ffb3d206579aaebac6b85d5f"}, + {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466e6ad8caefb589ed281c076deb6f0cd330e8bc13c5035854ffb9c2014b118c"}, + {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1d67d0d53d2a138f9e29d8acdabe11310c185e36f0a848efa104d4e40b808e4"}, + {file = "zstandard-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:39b2853efc9403927f9065cc48c9980649462acbdf81cd4f0cb773af2fd734bc"}, + {file = "zstandard-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a1b2effa96a5f019e72874969394edd393e2fbd6414a8208fea363a22803b45"}, + {file = "zstandard-0.22.0-cp312-cp312-win32.whl", hash = "sha256:88c5b4b47a8a138338a07fc94e2ba3b1535f69247670abfe422de4e0b344aae2"}, + {file = "zstandard-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:de20a212ef3d00d609d0b22eb7cc798d5a69035e81839f549b538eff4105d01c"}, + {file = "zstandard-0.22.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d75f693bb4e92c335e0645e8845e553cd09dc91616412d1d4650da835b5449df"}, + {file = "zstandard-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:36a47636c3de227cd765e25a21dc5dace00539b82ddd99ee36abae38178eff9e"}, + {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68953dc84b244b053c0d5f137a21ae8287ecf51b20872eccf8eaac0302d3e3b0"}, + {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2612e9bb4977381184bb2463150336d0f7e014d6bb5d4a370f9a372d21916f69"}, + {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23d2b3c2b8e7e5a6cb7922f7c27d73a9a615f0a5ab5d0e03dd533c477de23004"}, + {file = "zstandard-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d43501f5f31e22baf822720d82b5547f8a08f5386a883b32584a185675c8fbf"}, + {file = "zstandard-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a493d470183ee620a3df1e6e55b3e4de8143c0ba1b16f3ded83208ea8ddfd91d"}, + {file = "zstandard-0.22.0-cp38-cp38-win32.whl", hash = "sha256:7034d381789f45576ec3f1fa0e15d741828146439228dc3f7c59856c5bcd3292"}, + {file = "zstandard-0.22.0-cp38-cp38-win_amd64.whl", hash = "sha256:d8fff0f0c1d8bc5d866762ae95bd99d53282337af1be9dc0d88506b340e74b73"}, + {file = "zstandard-0.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2fdd53b806786bd6112d97c1f1e7841e5e4daa06810ab4b284026a1a0e484c0b"}, + {file = "zstandard-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73a1d6bd01961e9fd447162e137ed949c01bdb830dfca487c4a14e9742dccc93"}, + {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9501f36fac6b875c124243a379267d879262480bf85b1dbda61f5ad4d01b75a3"}, + {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48f260e4c7294ef275744210a4010f116048e0c95857befb7462e033f09442fe"}, + {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959665072bd60f45c5b6b5d711f15bdefc9849dd5da9fb6c873e35f5d34d8cfb"}, + {file = "zstandard-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d22fdef58976457c65e2796e6730a3ea4a254f3ba83777ecfc8592ff8d77d303"}, + {file = "zstandard-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7ccf5825fd71d4542c8ab28d4d482aace885f5ebe4b40faaa290eed8e095a4c"}, + {file = "zstandard-0.22.0-cp39-cp39-win32.whl", hash = "sha256:f058a77ef0ece4e210bb0450e68408d4223f728b109764676e1a13537d056bb0"}, + {file = "zstandard-0.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:e9e9d4e2e336c529d4c435baad846a181e39a982f823f7e4495ec0b0ec8538d2"}, + {file = "zstandard-0.22.0.tar.gz", hash = "sha256:8226a33c542bcb54cd6bd0a366067b610b41713b64c9abec1bc4533d69f51e70"}, +] + +[package.dependencies] +cffi = {version = ">=1.11", optional = true, markers = "platform_python_implementation == \"PyPy\" or extra == \"cffi\""} + +[package.extras] +cffi = ["cffi (>=1.11)"] + [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "843d4b703dd0ef4fa5176771f9bb940983a71f620e23af3894e8027c91fa27d0" +content-hash = "f69346ffbee2c5a3ee7fe54d771bbee38be54137ad9fdfaccd99bc38bf795d56" diff --git a/pyproject.toml b/pyproject.toml index 07e4461ec3..b63a4aef4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ authors = ["ONSDigital"] [tool.poetry.group.dev.dependencies] pep8 = "^1.7.1" mock = "^5.1.0" -pytest-cov = "^4.1.0" +pytest-cov = "^5.0.0" jsonschema = "^4.21.1" pylint = "^2.17.7" pylint-mccabe = "^0.1.3" @@ -30,23 +30,23 @@ flake8-mock = "^0.4" flake8-print = "^5.0.0" flake8-tuple = "^0.4.1" flake8-datetimez = "^20.10.0" -moto = "^5.0.3" +moto = "^5.0.8" freezegun = "^1.4.0" pytest-xdist = "^3.5.0" fakeredis = "^2.21.3" mypy = "^1.9.0" pytest-flask = "^1.3.0" -pytest = "^8.1.1" +pytest = "^8.1.2" pytest-sugar = "^1.0.0" responses = "^0.25.0" types-simplejson = "^3.19.0.20240310" -types-requests = "^2.31.0.20240311" -types-redis = "^4.6.0.20240311" +types-requests = "^2.31.0.20240406" +types-redis = "^4.6.0.20240425" types-PyYAML = "^6.0.12.20240311" types-python-dateutil = "^2.8.19.20240311" pytest-mock = "^3.12.0" types-cachetools = "^5.3.0.7" -types-pytz = "^2024.1.0.20240203" +types-pytz = "^2024.1.0.20240417" playwright = "^1.42.0" black = "^24.2.0" djlint = "^1.34.1" @@ -91,6 +91,7 @@ pdfkit = "^1.0.0" ordered-set = "^4.1.0" cachetools = "^5.3.0.7" gevent = "^24.2.1" +babel = "==2.14.0" [build-system] requires = ["poetry-core"] From 641e8c48efce76660db6710419c6f79b05d7ca45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:28:41 +0100 Subject: [PATCH 424/567] Bump the production-dependencies group across 1 directory with 2 updates (#1382) --- poetry.lock | 120 ++++++++++++++++++++++++------------------------- pyproject.toml | 4 +- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/poetry.lock b/poetry.lock index c77c930868..6f740bbd0e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "astroid" @@ -126,17 +126,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.117" +version = "1.34.119" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.117-py3-none-any.whl", hash = "sha256:1506589e30566bbb2f4997b60968ff7d4ef8a998836c31eedd36437ac3b7408a"}, - {file = "boto3-1.34.117.tar.gz", hash = "sha256:c8a383b904d6faaf7eed0c06e31b423db128e4c09ce7bd2afc39d1cd07030a51"}, + {file = "boto3-1.34.119-py3-none-any.whl", hash = "sha256:8f9c43c54b3dfaa36c4a0d7b42c417227a515bc7a2e163e62802780000a5a3e2"}, + {file = "boto3-1.34.119.tar.gz", hash = "sha256:cea2365a25b2b83a97e77f24ac6f922ef62e20636b42f9f6ee9f97188f9c1c03"}, ] [package.dependencies] -botocore = ">=1.34.117,<1.35.0" +botocore = ">=1.34.119,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -145,13 +145,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.117" +version = "1.34.119" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.117-py3-none-any.whl", hash = "sha256:26a431997f882bcdd1e835f44c24b2a1752b1c4e5183c2ce62999ce95d518d6c"}, - {file = "botocore-1.34.117.tar.gz", hash = "sha256:4637ca42e6c51aebc4d9a2d92f97bf4bdb042e3f7985ff31a659a11e4c170e73"}, + {file = "botocore-1.34.119-py3-none-any.whl", hash = "sha256:4bdf7926a1290b2650d62899ceba65073dd2693e61c35f5cdeb3a286a0aaa27b"}, + {file = "botocore-1.34.119.tar.gz", hash = "sha256:b253f15b24b87b070e176af48e8ef146516090429d30a7d8b136a4c079b28008"}, ] [package.dependencies] @@ -1506,61 +1506,61 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4 [[package]] name = "grpcio" -version = "1.64.0" +version = "1.64.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, - {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, - {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, - {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, - {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, - {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, - {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, - {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, - {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, - {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, - {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, - {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, - {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, - {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, - {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, - {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, - {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, - {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, - {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, - {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, - {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.64.0)"] + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.64.1)"] [[package]] name = "grpcio-status" @@ -3600,4 +3600,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "f69346ffbee2c5a3ee7fe54d771bbee38be54137ad9fdfaccd99bc38bf795d56" +content-hash = "57fe7588586070f2228427286b9e93b3b2ff2e4321c5acb571af49dd719588ac" diff --git a/pyproject.toml b/pyproject.toml index b63a4aef4f..2b3cb1b04d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ flask-babel = "^4.0.0" flask-login = "^0.6.3" flask-wtf = "^1.2.1" google-cloud-datastore = "^2.19.0" -grpcio = "^1.62.1" +grpcio = "^1.64.1" gunicorn = "^22.0.0" pika = "^1.3.2" pyyaml = "^6.0.1" @@ -69,7 +69,7 @@ sdc-cryptography = "^1.2.0" structlog = "^24.1.0" ua-parser = "^0.18.0" blinker = "^1.7.0" -boto3 = "^1.34.61" +boto3 = "^1.34.119" humanize = "^4.9.0" flask-talisman = "^1.1.0" marshmallow = "^3.21.1" From fef3d39bed8778b153b537b28cb7eafa873c69ce Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:24:28 +0100 Subject: [PATCH 425/567] Schemas v5.3.1 (#1385) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 32bc9525eb..67617da57e 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.3.0 +v5.3.1 From aa6c6c97c412145101fe26a125cb833a8a2b5e82 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:07:51 +0100 Subject: [PATCH 426/567] Update dependabot config to exclude babel and pylint releases (#1384) --- .github/dependabot.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 609aabeae6..a8823baa3a 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -37,6 +37,7 @@ updates: - "ordered-set" - "cachetools" - "gevent" + - "babel" development-dependencies: dependency-type: "development" patterns: @@ -58,12 +59,14 @@ updates: - "black" - "djlint" ignore: + - dependency-name: "pylint*" + update-types: [ "version-update:semver-major" ] + - dependency-name: "babel" + update-types: [ "version-update:semver-major" ] - dependency-name: "pytest*" update-types: ["version-update:semver-minor"] - dependency-name: "flake8*" update-types: ["version-update:semver-minor"] - - dependency-name: "pylint*" - update-types: ["version-update:semver-minor"] - dependency-name: "types*" update-types: ["version-update:semver-minor"] - dependency-name: "pep8" From 5d642b95cefdb47e969ce4c45f7a8f042aa29e18 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:50:22 +0100 Subject: [PATCH 427/567] Schemas v5.4.0 (#1391) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 67617da57e..6ae910b367 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.3.1 +v5.4.0 From e5a0e7149478204882020925cd7ac76faae5af39 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Mon, 17 Jun 2024 11:23:34 +0100 Subject: [PATCH 428/567] Temporarily ignore babel minor version dependabot bumps (#1396) --- .github/dependabot.yaml | 5 +++-- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index a8823baa3a..bab096f401 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -59,9 +59,10 @@ updates: - "black" - "djlint" ignore: - - dependency-name: "pylint*" - update-types: [ "version-update:semver-major" ] + # Temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 - dependency-name: "babel" + update-types: ["version-update:semver-major", "version-update:semver-minor"] + - dependency-name: "pylint*" update-types: [ "version-update:semver-major" ] - dependency-name: "pytest*" update-types: ["version-update:semver-minor"] diff --git a/pyproject.toml b/pyproject.toml index 2b3cb1b04d..fc01f44325 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ pdfkit = "^1.0.0" ordered-set = "^4.1.0" cachetools = "^5.3.0.7" gevent = "^24.2.1" -babel = "==2.14.0" +babel = "==2.14.0" # Temporarily pinned - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 [build-system] requires = ["poetry-core"] From dbec68d2bd2c93bbda6df21ab82b532164af1ff8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:12:03 +0100 Subject: [PATCH 429/567] Bump the production-dependencies group across 1 directory with 7 updates (#1400) Bumps the production-dependencies group with 7 updates in the / directory: | Package | From | To | | --- | --- | --- | | [boto3](https://github.com/boto/boto3) | `1.34.119` | `1.34.127` | | [marshmallow](https://github.com/marshmallow-code/marshmallow) | `3.21.2` | `3.21.3` | | [google-cloud-storage](https://github.com/googleapis/python-storage) | `2.16.0` | `2.17.0` | | [jsonpointer](https://github.com/stefankoegl/python-json-pointer) | `2.4` | `3.0.0` | | [redis](https://github.com/redis/redis-py) | `5.0.4` | `5.0.6` | | [email-validator](https://github.com/JoshData/python-email-validator) | `2.1.1` | `2.1.2` | | [google-cloud-pubsub](https://github.com/googleapis/python-pubsub) | `2.21.2` | `2.21.3` | Updates `boto3` from 1.34.119 to 1.34.127 - [Release notes](https://github.com/boto/boto3/releases) - [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst) - [Commits](https://github.com/boto/boto3/compare/1.34.119...1.34.127) Updates `marshmallow` from 3.21.2 to 3.21.3 - [Changelog](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst) - [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.21.2...3.21.3) Updates `google-cloud-storage` from 2.16.0 to 2.17.0 - [Release notes](https://github.com/googleapis/python-storage/releases) - [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/python-storage/compare/v2.16.0...v2.17.0) Updates `jsonpointer` from 2.4 to 3.0.0 - [Commits](https://github.com/stefankoegl/python-json-pointer/compare/v2.4...v3.0.0) Updates `redis` from 5.0.4 to 5.0.6 - [Release notes](https://github.com/redis/redis-py/releases) - [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES) - [Commits](https://github.com/redis/redis-py/compare/v5.0.4...v5.0.6) Updates `email-validator` from 2.1.1 to 2.1.2 - [Release notes](https://github.com/JoshData/python-email-validator/releases) - [Changelog](https://github.com/JoshData/python-email-validator/blob/main/CHANGELOG.md) - [Commits](https://github.com/JoshData/python-email-validator/compare/v2.1.1...v2.1.2) Updates `google-cloud-pubsub` from 2.21.2 to 2.21.3 - [Release notes](https://github.com/googleapis/python-pubsub/releases) - [Changelog](https://github.com/googleapis/python-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/python-pubsub/compare/v2.21.2...v2.21.3) --- updated-dependencies: - dependency-name: boto3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: marshmallow dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: google-cloud-storage dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: jsonpointer dependency-type: direct:production update-type: version-update:semver-major dependency-group: production-dependencies - dependency-name: redis dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: email-validator dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 56 +++++++++++++++++++++++++------------------------- pyproject.toml | 14 ++++++------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6f740bbd0e..c128e84d9e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -126,17 +126,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.119" +version = "1.34.127" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.119-py3-none-any.whl", hash = "sha256:8f9c43c54b3dfaa36c4a0d7b42c417227a515bc7a2e163e62802780000a5a3e2"}, - {file = "boto3-1.34.119.tar.gz", hash = "sha256:cea2365a25b2b83a97e77f24ac6f922ef62e20636b42f9f6ee9f97188f9c1c03"}, + {file = "boto3-1.34.127-py3-none-any.whl", hash = "sha256:d370befe4fb7aea5bc383057d7dad18dda5d0cf3cd3295915bcc8c8c4191905c"}, + {file = "boto3-1.34.127.tar.gz", hash = "sha256:58ccdeae3a96811ecc9d5d866d8226faadbd0ee1891756e4a04d5186e9a57a64"}, ] [package.dependencies] -botocore = ">=1.34.119,<1.35.0" +botocore = ">=1.34.127,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -145,13 +145,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.119" +version = "1.34.127" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.119-py3-none-any.whl", hash = "sha256:4bdf7926a1290b2650d62899ceba65073dd2693e61c35f5cdeb3a286a0aaa27b"}, - {file = "botocore-1.34.119.tar.gz", hash = "sha256:b253f15b24b87b070e176af48e8ef146516090429d30a7d8b136a4c079b28008"}, + {file = "botocore-1.34.127-py3-none-any.whl", hash = "sha256:e14fa28c8bb141de965e700f88b196d17c67a703c7f0f5c7e14f7dd1cf636011"}, + {file = "botocore-1.34.127.tar.gz", hash = "sha256:a377871742c40603d559103f19acb7bc93cfaf285e68f21b81637ec396099877"}, ] [package.dependencies] @@ -160,7 +160,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.20.9)"] +crt = ["awscrt (==0.20.11)"] [[package]] name = "brotli" @@ -836,13 +836,13 @@ files = [ [[package]] name = "email-validator" -version = "2.1.1" +version = "2.1.2" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" files = [ - {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, - {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, + {file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"}, + {file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"}, ] [package.dependencies] @@ -1240,13 +1240,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.2" +version = "2.21.3" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.2.tar.gz", hash = "sha256:fc72226b14731db2873f7c4031cc757e274bbcdabcac7523b2cd6e46130d6096"}, - {file = "google_cloud_pubsub-2.21.2-py2.py3-none-any.whl", hash = "sha256:05a6b01e5bda6f4a4858700e3e9a12e3080589718d648b2383e5818131db9ce4"}, + {file = "google-cloud-pubsub-2.21.3.tar.gz", hash = "sha256:df44c79a4d7bfb5adfa675413e3b91bbbe06e91d2715a15f925140b05715bb7d"}, + {file = "google_cloud_pubsub-2.21.3-py2.py3-none-any.whl", hash = "sha256:a417d63d9db5e8b9ff51dee705c69ed02b0a8bc62cac2474a86f22aea288c0ec"}, ] [package.dependencies] @@ -1263,13 +1263,13 @@ libcst = ["libcst (>=0.3.10)"] [[package]] name = "google-cloud-storage" -version = "2.16.0" +version = "2.17.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-storage-2.16.0.tar.gz", hash = "sha256:dda485fa503710a828d01246bd16ce9db0823dc51bbca742ce96a6817d58669f"}, - {file = "google_cloud_storage-2.16.0-py2.py3-none-any.whl", hash = "sha256:91a06b96fb79cf9cdfb4e759f178ce11ea885c79938f89590344d079305f5852"}, + {file = "google-cloud-storage-2.17.0.tar.gz", hash = "sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388"}, + {file = "google_cloud_storage-2.17.0-py2.py3-none-any.whl", hash = "sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1"}, ] [package.dependencies] @@ -1775,13 +1775,13 @@ files = [ [[package]] name = "jsonpointer" -version = "2.4" +version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +python-versions = ">=3.7" files = [ - {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, - {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, ] [[package]] @@ -1951,13 +1951,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.2" +version = "3.21.3" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.2-py3-none-any.whl", hash = "sha256:70b54a6282f4704d12c0a41599682c5c5450e843b9ec406308653b47c59648a1"}, - {file = "marshmallow-3.21.2.tar.gz", hash = "sha256:82408deadd8b33d56338d2182d455db632c6313aa2af61916672146bb32edc56"}, + {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, + {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, ] [package.dependencies] @@ -2626,13 +2626,13 @@ files = [ [[package]] name = "redis" -version = "5.0.4" +version = "5.0.6" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.4-py3-none-any.whl", hash = "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91"}, - {file = "redis-5.0.4.tar.gz", hash = "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61"}, + {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, + {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, ] [package.extras] @@ -3600,4 +3600,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "57fe7588586070f2228427286b9e93b3b2ff2e4321c5acb571af49dd719588ac" +content-hash = "9d57d1d6effa84ee717c88ef05d15077e5716490f2f02fa478d81bbca892f668" diff --git a/pyproject.toml b/pyproject.toml index fc01f44325..dd296ed721 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,21 +69,21 @@ sdc-cryptography = "^1.2.0" structlog = "^24.1.0" ua-parser = "^0.18.0" blinker = "^1.7.0" -boto3 = "^1.34.119" +boto3 = "^1.34.127" humanize = "^4.9.0" flask-talisman = "^1.1.0" -marshmallow = "^3.21.1" +marshmallow = "^3.21.3" python-snappy = "^0.7.1" -google-cloud-storage = "^2.15.0" -jsonpointer = "^2.4" -redis = "^5.0.3" +google-cloud-storage = "^2.17.0" +jsonpointer = "^3.0" +redis = "^5.0.6" flask-compress = "^1.14" htmlmin = "^0.1.12" coloredlogs = "^15.0.1" uwsgi = "^2.0.24" -email-validator = "^2.1.1" +email-validator = "^2.1.2" itsdangerous = "^2.1.2" -google-cloud-pubsub = "^2.20.1" +google-cloud-pubsub = "^2.21.3" google-cloud-tasks = "^2.16.3" simplejson = "^3.19.2" markupsafe = "^2.1.5" From 4f3662629ea25f32368799c2189fa7798c88493d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:36:37 +0100 Subject: [PATCH 430/567] Bump the npm_and_yarn group across 1 directory with 2 updates (#1403) Bumps the npm_and_yarn group with 2 updates in the / directory: [braces](https://github.com/micromatch/braces) and [ws](https://github.com/websockets/ws). Updates `braces` from 3.0.2 to 3.0.3 - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) Updates `ws` from 7.5.9 to 7.5.10 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: braces dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: ws dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4cf5457ff2..ecf0c6fadc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3161,12 +3161,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5296,9 +5296,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -11126,9 +11126,9 @@ } }, "node_modules/webdriver/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -11414,9 +11414,9 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" From 2a1906279c91d3fe61db5c5d3cecd00dead5d633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 14:24:03 +0100 Subject: [PATCH 431/567] Bump urllib3 from 2.2.1 to 2.2.2 in the pip group (#1401) Bumps the pip group with 1 update: [urllib3](https://github.com/urllib3/urllib3). Updates `urllib3` from 2.2.1 to 2.2.2 - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect dependency-group: pip ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: liamtoozer --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index c128e84d9e..4973a56586 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3316,13 +3316,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] From 0b83027b13811c491f44efe98f4e58a3c5e8501a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:18:22 +0100 Subject: [PATCH 432/567] Fix failing token decryption unit test (#1393) --- tests/integration/test_create_token.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_create_token.py b/tests/integration/test_create_token.py index 643acbd110..a172e53ea1 100644 --- a/tests/integration/test_create_token.py +++ b/tests/integration/test_create_token.py @@ -151,7 +151,9 @@ def test_sds_metadata_included_in_token(self): def test_additional_payload_added_in_token(self): token = self.token_generator.create_supplementary_data_token( - "test_checkbox.json", flag_1=True + "test_address.json", + flag_1=True, + sds_dataset_id="54f1b432-9421-49e5-bd26-e63e18a30b69", ) with self.test_app.app_context(): decrypted_token = decrypt_token(token) @@ -166,7 +168,7 @@ def test_additional_payload_added_in_token(self): "ref_p_start_date": "2016-04-01", "ru_name": "Integration Testing", "ru_ref": "12345678901A", - "sds_dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", + "sds_dataset_id": "54f1b432-9421-49e5-bd26-e63e18a30b69", "survey_id": "123", "trad_as": "Integration Tests", "user_id": "integration-test", From 66d5a9d499bc3c674ff92cd5e112c6a8992736c2 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:27:54 +0100 Subject: [PATCH 433/567] Add correct calculated summaries title placeholder rendering (#1402) --- .../contexts/calculated_summary_context.py | 13 +- app/views/handlers/calculation_summary.py | 1 + ...pendencies_with_calculation_summaries.json | 142 ++++++++++++++++- .../test_calculated_summary_context.py | 144 +++++++++++++----- .../test_grand_calculated_summary_context.py | 77 ++++++---- .../test_questionnaire_calculated_summary.py | 10 +- 6 files changed, 305 insertions(+), 82 deletions(-) diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 6ab07ca0a4..6d69df2133 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,4 +1,3 @@ -from functools import cached_property from typing import Callable, Iterable, Mapping, Tuple from werkzeug.datastructures import ImmutableDict @@ -32,6 +31,7 @@ def __init__( routing_path: RoutingPath, current_location: LocationType, return_location: ReturnLocation, + rendered_block: dict, ) -> None: super().__init__( language, @@ -42,16 +42,7 @@ def __init__( self.routing_path_block_ids = routing_path.block_ids self.current_location = current_location self.return_location = return_location - - @cached_property - def rendered_block(self) -> dict: - # Type ignore block is guaranteed to exist at this point - block_id: str = self.current_location.block_id # type: ignore - block: ImmutableDict = self._schema.get_block(block_id) # type: ignore - - return self._placeholder_renderer.render( - data_to_render=block, list_item_id=self.current_location.list_item_id - ) + self.rendered_block = rendered_block def build_groups_for_section( self, diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index 604b5c7257..f82a2ce124 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -16,6 +16,7 @@ def get_context(self) -> dict[str, dict]: current_location=self._current_location, routing_path=self._routing_path, return_location=self._return_location, + rendered_block=self.rendered_block, ) context = summary_context.build_view_context() diff --git a/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json b/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json index ae0785897e..24e4fe25fb 100644 --- a/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json +++ b/schemas/test/en/test_placeholder_dependencies_with_calculation_summaries.json @@ -290,7 +290,76 @@ "id": "calc-summary-1", "type": "CalculatedSummary", "page_title": "Total in-house expenditure on R&D", - "title": "We have calculated your total in-house expenditure on R&D to be %(total)s. Is this correct?", + "title": { + "text": "We have calculated your total in-house expenditure on R&D for {ru_name} for the period {from} to {to} to be %(total)s. Is this correct?", + "placeholders": [ + { + "placeholder": "from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-from", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-to", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, "calculation": { "operation": { "+": [ @@ -418,7 +487,76 @@ "id": "calc-summary-2", "type": "CalculatedSummary", "page_title": "Total in-house expenditure on R&D - Part Two", - "title": "We have calculated your total in-house expenditure on R&D to be %(total)s. Is this correct?", + "title": { + "text": "We have calculated your total in-house expenditure on R&D for {ru_name} for the period {from} to {to} to be %(total)s. Is this correct?", + "placeholders": [ + { + "placeholder": "from", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-from", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_start_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "to", + "transforms": [ + { + "transform": "first_non_empty_item", + "arguments": { + "items": [ + { + "identifier": "date-to", + "source": "answers" + }, + { + "source": "metadata", + "identifier": "ref_p_end_date" + } + ] + } + }, + { + "transform": "format_date", + "arguments": { + "date_to_format": { + "source": "previous_transform" + }, + "date_format": "d MMMM yyyy" + } + } + ] + }, + { + "placeholder": "ru_name", + "value": { + "source": "metadata", + "identifier": "ru_name" + } + } + ] + }, "calculation": { "operation": { "+": [ diff --git a/tests/app/views/contexts/test_calculated_summary_context.py b/tests/app/views/contexts/test_calculated_summary_context.py index c7b67b85bb..7d8e37ca6b 100644 --- a/tests/app/views/contexts/test_calculated_summary_context.py +++ b/tests/app/views/contexts/test_calculated_summary_context.py @@ -3,6 +3,7 @@ from app.data_models import AnswerStore, ListStore from app.data_models.data_stores import DataStores from app.questionnaire import Location +from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.views.contexts.calculated_summary_context import CalculatedSummaryContext @@ -109,19 +110,35 @@ def test_build_view_context_for_currency_calculated_summary( ] ) + current_location = Location(section_id="default-section", block_id=block_id) + data_stores = DataStores( + answer_store=( + test_calculated_summary_answers_skipped_fourth + if skip_fourth + else test_calculated_summary_answers + ) + ) + block = test_calculated_summary_schema.get_block(block_id) + + placeholder_renderer = PlaceholderRenderer( + language=language, + data_stores=data_stores, + schema=test_calculated_summary_schema, + location=current_location, + ) + + rendered_block = placeholder_renderer.render( + data_to_render=block, list_item_id=current_location.list_item_id + ) + calculated_summary_context = CalculatedSummaryContext( language=language, schema=test_calculated_summary_schema, - data_stores=DataStores( - answer_store=( - test_calculated_summary_answers_skipped_fourth - if skip_fourth - else test_calculated_summary_answers - ) - ), + data_stores=data_stores, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), - current_location=Location(section_id="default-section", block_id=block_id), + current_location=current_location, return_location=ReturnLocation(return_to_answer_id=return_to_answer_id), + rendered_block=rendered_block, ) context = calculated_summary_context.build_view_context() @@ -207,15 +224,32 @@ def test_build_view_context_for_return_to_calculated_summary( "number-calculated-summary-2", ] + current_location = Location(section_id="default-section", block_id=block_id) + data_stores = DataStores(answer_store=test_grand_calculated_summary_answers) + block = test_grand_calculated_summary_schema.get_block(block_id) + language = "en" + + placeholder_renderer = PlaceholderRenderer( + language=language, + data_stores=data_stores, + schema=test_grand_calculated_summary_schema, + location=current_location, + ) + + rendered_block = placeholder_renderer.render( + data_to_render=block, list_item_id=current_location.list_item_id + ) + calculated_summary_context = CalculatedSummaryContext( - language="en", + language=language, schema=test_grand_calculated_summary_schema, - data_stores=DataStores(answer_store=test_grand_calculated_summary_answers), + data_stores=data_stores, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), - current_location=Location(section_id="default-section", block_id=block_id), + current_location=current_location, return_location=ReturnLocation( return_to=return_to, return_to_block_id=return_to_block_id ), + rendered_block=rendered_block, ) context = calculated_summary_context.build_view_context() @@ -279,17 +313,34 @@ def test_build_view_context_for_calculated_summary_with_dynamic_answers( "extra-spending-block", ] + current_location = Location(section_id="section-1", block_id=block_id) + data_stores = DataStores( + list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "supermarkets"}]) + ) + block = test_calculated_summary_repeating_and_static_answers_schema.get_block( + block_id + ) + language = "en" + + placeholder_renderer = PlaceholderRenderer( + language=language, + data_stores=data_stores, + schema=test_calculated_summary_repeating_and_static_answers_schema, + location=current_location, + ) + + rendered_block = placeholder_renderer.render( + data_to_render=block, list_item_id=current_location.list_item_id + ) + calculated_summary_context = CalculatedSummaryContext( - language="en", + language=language, schema=test_calculated_summary_repeating_and_static_answers_schema, - data_stores=DataStores( - list_store=ListStore( - [{"items": ["CHKtQS", "laFWcs"], "name": "supermarkets"}] - ) - ), + data_stores=data_stores, routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), - current_location=Location(section_id="section-1", block_id=block_id), + current_location=current_location, return_location=ReturnLocation(), + rendered_block=rendered_block, ) context = calculated_summary_context.build_view_context() @@ -379,31 +430,46 @@ def test_build_view_context_for_calculated_summary_with_answers_from_repeating_b block_ids = ["block-car", "list-collector"] + current_location = Location(section_id="section-1", block_id=block_id) + data_stores = DataStores( + answer_store=AnswerStore( + [ + { + "answer_id": "transport-name", + "value": "Train", + "list_item_id": "CHKtQS", + }, + { + "answer_id": "transport-name", + "value": "Bus", + "list_item_id": "laFWcs", + }, + ] + ), + list_store=ListStore([{"items": ["CHKtQS", "laFWcs"], "name": "transport"}]), + ) + block = test_calculated_summary_repeating_blocks.get_block(block_id) + language = "en" + + placeholder_renderer = PlaceholderRenderer( + language=language, + data_stores=data_stores, + schema=test_calculated_summary_repeating_blocks, + location=current_location, + ) + + rendered_block = placeholder_renderer.render( + data_to_render=block, list_item_id=current_location.list_item_id + ) + calculated_summary_context = CalculatedSummaryContext( - language="en", + language=language, schema=test_calculated_summary_repeating_blocks, - data_stores=DataStores( - answer_store=AnswerStore( - [ - { - "answer_id": "transport-name", - "value": "Train", - "list_item_id": "CHKtQS", - }, - { - "answer_id": "transport-name", - "value": "Bus", - "list_item_id": "laFWcs", - }, - ] - ), - list_store=ListStore( - [{"items": ["CHKtQS", "laFWcs"], "name": "transport"}] - ), - ), + data_stores=data_stores, routing_path=RoutingPath(section_id="section-1", block_ids=block_ids), - current_location=Location(section_id="section-1", block_id=block_id), + current_location=current_location, return_location=ReturnLocation(), + rendered_block=rendered_block, ) context = calculated_summary_context.build_view_context() diff --git a/tests/app/views/contexts/test_grand_calculated_summary_context.py b/tests/app/views/contexts/test_grand_calculated_summary_context.py index f0736e0095..ee9fe856cd 100644 --- a/tests/app/views/contexts/test_grand_calculated_summary_context.py +++ b/tests/app/views/contexts/test_grand_calculated_summary_context.py @@ -4,6 +4,7 @@ from app.data_models.data_stores import DataStores from app.data_models.progress_store import ProgressStore from app.questionnaire import Location +from app.questionnaire.placeholder_renderer import PlaceholderRenderer from app.questionnaire.return_location import ReturnLocation from app.questionnaire.routing_path import RoutingPath from app.views.contexts.grand_calculated_summary_context import ( @@ -30,6 +31,7 @@ ), ), ) +# pylint: disable=too-many-locals def test_build_view_context_for_grand_calculated_summary( block_id, title, @@ -55,39 +57,56 @@ def test_build_view_context_for_grand_calculated_summary( "number-calculated-summary-2", ] + current_location = Location(section_id="default-section", block_id=block_id) + data_stores = DataStores( + answer_store=test_grand_calculated_summary_answers, + progress_store=ProgressStore( + progress=[ + { + "section_id": "section-1", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "first-number-block", + "second-number-block", + "distance-calculated-summary-1", + "number-calculated-summary-1", + ], + }, + { + "section_id": "section-2", + "status": CompletionStatus.COMPLETED, + "block_ids": [ + "third-number-block", + "fourth-number-block", + "distance-calculated-summary-2", + "number-calculated-summary-2", + ], + }, + ] + ), + ) + block = test_grand_calculated_summary_schema.get_block(block_id) + language = "en" + + placeholder_renderer = PlaceholderRenderer( + language=language, + data_stores=data_stores, + schema=test_grand_calculated_summary_schema, + location=current_location, + ) + + rendered_block = placeholder_renderer.render( + data_to_render=block, list_item_id=current_location.list_item_id + ) + grand_calculated_summary_context = GrandCalculatedSummaryContext( - language="en", + language=language, schema=test_grand_calculated_summary_schema, - data_stores=DataStores( - answer_store=test_grand_calculated_summary_answers, - progress_store=ProgressStore( - progress=[ - { - "section_id": "section-1", - "status": CompletionStatus.COMPLETED, - "block_ids": [ - "first-number-block", - "second-number-block", - "distance-calculated-summary-1", - "number-calculated-summary-1", - ], - }, - { - "section_id": "section-2", - "status": CompletionStatus.COMPLETED, - "block_ids": [ - "third-number-block", - "fourth-number-block", - "distance-calculated-summary-2", - "number-calculated-summary-2", - ], - }, - ] - ), - ), + data_stores=data_stores, routing_path=RoutingPath(section_id="default-section", block_ids=block_ids), - current_location=Location(section_id="default-section", block_id=block_id), + current_location=current_location, return_location=ReturnLocation(), + rendered_block=rendered_block, ) context = grand_calculated_summary_context.build_view_context() diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index 63112c7413..c73f5f5b9b 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -390,7 +390,7 @@ def test_placeholder_rendering_in_calculated_summary_label(self): "For the period 1 January 2000 to 1 April 2000 what was the expenditure on R&D for Integration Testing?" ) - def test_placeholder_rendering_in_calculated_summary_label_unhappy_path(self): + def test_placeholders_rendering_in_calculated_summary_unhappy_path(self): """ Tests that a placeholder using the first_non_empty_item is rendered correctly on the calculated summary page using the answer values that are on the path. In this instance it is the unhappy path where the user has entered @@ -422,6 +422,10 @@ def test_placeholder_rendering_in_calculated_summary_label_unhappy_path(self): self.assertInBody( "For the period 1 January 2000 to 1 April 2000 what was the expenditure on R&D for Integration Testing?" ) + self.assertInBody( + "We have calculated your total in-house expenditure on R&D for Integration Testing for the period 1 January 2000 to 1 April 2000 to be £20. " + "Is this correct?" + ) # Complete the rest of the survey self.post() @@ -442,3 +446,7 @@ def test_placeholder_rendering_in_calculated_summary_label_unhappy_path(self): self.assertInBody( "For the period 1 April 2016 to 30 April 2016 what was the expenditure on R&D for Integration Testing?" ) + self.assertInBody( + "We have calculated your total in-house expenditure on R&D for Integration Testing for the period 1 April 2016 to 30 April 2016 to be £110. " + "Is this correct?" + ) From 9f2391d347244f5c8cb17b7056c85882a9ac5c53 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:18:11 +0100 Subject: [PATCH 434/567] Schemas v5.5.0 (#1413) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 6ae910b367..268fccb19a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.4.0 +v5.5.0 From c50df9cd8a3904b32d2b489995789a3fa3fd53c1 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:18:29 +0100 Subject: [PATCH 435/567] Schemas v5.6.0 (#1417) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 268fccb19a..7aefc82460 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.5.0 +v5.6.0 From 7b19e358ab7c7a7abd4980f7d8952e63994934f5 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 9 Jul 2024 08:22:13 +0100 Subject: [PATCH 436/567] Update node and checkout actions versions (#1410) --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/pull_request.yml | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bf962b8af4..04994b0240 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 668e93eecd..7158f7bc44 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: docker-push: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Write app version run: printf "$GITHUB_SHA" > .application-version - name: Build diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 16ddc61a73..7cf3614a9c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,7 +16,7 @@ jobs: python-dependencies: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install Poetry @@ -35,8 +35,8 @@ jobs: node-dependencies: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version-file : ".nvmrc" cache: "npm" @@ -46,10 +46,10 @@ jobs: needs: [python-dependencies, node-dependencies] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" cache: "npm" @@ -82,7 +82,7 @@ jobs: needs: python-dependencies runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install apt dependencies @@ -117,7 +117,7 @@ jobs: validate-schemas: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run validator run: ./scripts/run_validator.sh - name: Running schema tests @@ -133,8 +133,8 @@ jobs: # :TODO: Revisit & update when 2 instances can be used without adverse effects EQ_FUNCTIONAL_TEST_MAX_INSTANCES: 1 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" cache: "npm" @@ -160,7 +160,7 @@ jobs: docker-push: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set Tag and SHA run: | CLEAN_TAG=$(echo "${{ github.event.pull_request.head.ref }}" | tr / -) From 47aedc6e5c0ecdec0c32ef2f8fbd02cfb06eb88e Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 10 Jul 2024 08:38:25 +0100 Subject: [PATCH 437/567] Schemas v5.7.0 (#1427) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 7aefc82460..833e0a4813 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.6.0 +v5.7.0 From d8d09886549e08326891c6e0f77e248b08fc3390 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:58:13 +0100 Subject: [PATCH 438/567] Contain visible labels within accessible names (#1421) --- app/translations/messages.pot | 8 ++--- app/views/contexts/hub_context.py | 6 ++-- tests/app/views/contexts/test_hub_context.py | 36 +++++++++++++++++-- .../hub_and_spoke/hub_and_spoke.spec.js | 14 ++++++++ .../hub_and_spoke_custom_content.spec.js | 2 ++ 5 files changed, 57 insertions(+), 9 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index b7e09a2d23..572c7134bd 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-06-03 08:34+0100\n" +"POT-Creation-Date: 2024-07-02 16:54+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -375,7 +375,7 @@ msgid "View answers" msgstr "" #: app/views/contexts/hub_context.py:19 -msgid "View answers for {section_name}" +msgid "View answers: {section_name}" msgstr "" #: app/views/contexts/hub_context.py:23 @@ -387,7 +387,7 @@ msgid "Continue with section" msgstr "" #: app/views/contexts/hub_context.py:26 -msgid "Continue with {section_name} section" +msgid "Continue with section: {section_name}" msgstr "" #: app/views/contexts/hub_context.py:30 @@ -399,7 +399,7 @@ msgid "Start section" msgstr "" #: app/views/contexts/hub_context.py:33 -msgid "Start {section_name} section" +msgid "Start section: {section_name}" msgstr "" #: app/views/contexts/hub_context.py:37 diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 10cf142795..5fbf484860 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -16,21 +16,21 @@ class HubContext(Context): "text": lazy_gettext("Completed"), "link": { "text": lazy_gettext("View answers"), - "aria_label": lazy_gettext("View answers for {section_name}"), + "aria_label": lazy_gettext("View answers: {section_name}"), }, }, CompletionStatus.IN_PROGRESS: { "text": lazy_gettext("Partially completed"), "link": { "text": lazy_gettext("Continue with section"), - "aria_label": lazy_gettext("Continue with {section_name} section"), + "aria_label": lazy_gettext("Continue with section: {section_name}"), }, }, CompletionStatus.NOT_STARTED: { "text": lazy_gettext("Not started"), "link": { "text": lazy_gettext("Start section"), - "aria_label": lazy_gettext("Start {section_name} section"), + "aria_label": lazy_gettext("Start section: {section_name}"), }, }, CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: { diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 088137f5d4..c92db34d87 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -23,7 +23,7 @@ def test_get_not_started_row_for_section(schema, data_stores): "actions": [ { "text": "Start section", - "visuallyHiddenText": "Start Breakfast section", + "visuallyHiddenText": "Start section: Breakfast", "url": "http://some/url", "attributes": {"data-qa": "hub-row-section-1-link"}, } @@ -44,6 +44,38 @@ def test_get_not_started_row_for_section(schema, data_stores): assert expected == actual +def test_get_in_progress_row_for_section(schema, data_stores): + expected = { + "rowItems": [ + { + "rowTitle": "Breakfast", + "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, + "attributes": {"data-qa": "hub-row-section-1-state"}, + "valueList": [{"text": "Partially completed"}], + "actions": [ + { + "text": "Continue with section", + "visuallyHiddenText": "Continue with section: Breakfast", + "url": "http://some/url", + "attributes": {"data-qa": "hub-row-section-1-link"}, + } + ], + } + ] + } + + hub = HubContext(language=None, schema=schema, data_stores=data_stores) + + actual = hub.get_row_context_for_section( + section_name="Breakfast", + section_status=CompletionStatus.IN_PROGRESS, + section_url="http://some/url", + row_id="section-1", + ) + + assert expected == actual + + def test_get_completed_row_for_section(schema, data_stores): expected = { "rowItems": [ @@ -56,7 +88,7 @@ def test_get_completed_row_for_section(schema, data_stores): "actions": [ { "text": "View answers", - "visuallyHiddenText": "View answers for Breakfast", + "visuallyHiddenText": "View answers: Breakfast", "url": "http://some/url", "attributes": {"data-qa": "hub-row-section-1-link"}, } diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 830d98ced4..534384d40f 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -40,6 +40,12 @@ describe("Feature: Hub and Spoke", () => { await expect(await $(HubPage.summaryRowState("household-section")).getText()).toBe("Not started"); }); + it("When a user utilises a screen reader, The visually hidden text read aloud should be the state and name of each section in the hub", async () => { + await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("Start section: Employment"); + await expect(await $(HubPage.summaryRowLink("accommodation-section")).getHTML()).toContain("Start section: Accommodation"); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("Start section: Household residents"); + }); + it("When a user views the Hub, any section with show_on_hub set to true should appear", async () => { await expect(await $(HubPage.summaryItems()).getText()).toContain("Employment"); await expect(await $(HubPage.summaryItems()).getText()).toContain("Accommodation"); @@ -70,6 +76,9 @@ describe("Feature: Hub and Spoke", () => { await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Not started"); await expect(await $(HubPage.summaryRowState("accommodation-section")).getText()).toBe("Not started"); await expect(await $(HubPage.summaryRowState("household-section")).getText()).toBe("Not started"); + await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("Start section: Employment"); + await expect(await $(HubPage.summaryRowLink("accommodation-section")).getHTML()).toContain("Start section: Accommodation"); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("Start section: Household residents"); }); it("When the user starts a section, Then the first question in the section should be displayed", async () => { @@ -103,6 +112,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user returns to the Hub, Then the section should be marked as 'Partially completed'", async () => { await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("Continue with section: Employment"); }); it("When the user returns to the Hub and restarts the same section, Then they should be redirected to the first incomplete block", async () => { @@ -137,6 +147,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user returns to the Hub, Then the section should be marked as 'Completed'", async () => { await click(EmploymentTypeBlockPage.submit()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Completed"); + await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("View answers: Employment"); }); it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", async () => { @@ -183,6 +194,7 @@ describe("Feature: Hub and Spoke", () => { const expectedUrl = await browser.getUrl(); await expect(expectedUrl).toContain(HubPage.url()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Partially completed"); + await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("Continue with section: Employment"); }); }); @@ -356,6 +368,7 @@ describe("Feature: Hub and Spoke", () => { await $(DoesAnyoneLiveHere.no()).click(); await click(DoesAnyoneLiveHere.submit()); await click(HouseholdSummary.submit()); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("View answers: Household residents"); }); it("When there are no changes, continue returns directly to the hub", async () => { @@ -363,6 +376,7 @@ describe("Feature: Hub and Spoke", () => { await click(HouseholdSummary.submit()); const expectedUrl = await browser.getUrl(); await expect(expectedUrl).toContain(HubPage.url()); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("View answers: Household residents"); }); it("When there are changes, which would set the section to in_progress it routes accordingly", async () => { diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js index b805b39419..ad1509d6ef 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js @@ -10,6 +10,7 @@ describe("Feature: Hub and Spoke with custom content", () => { await browser.openQuestionnaire(hubAndSpokeSchema); await expect(await $(HubPage.heading()).getText()).toBe("Choose another section to complete"); await expect(await $(HubPage.guidance()).isExisting()).toBe(false); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("Start section: Household residents"); await expect(await $(HubPage.submit()).getText()).toBe("Continue"); await expect(await $(HubPage.warning()).isExisting()).toBe(false); }); @@ -22,6 +23,7 @@ describe("Feature: Hub and Spoke with custom content", () => { await $(HowManyPeopleLiveHere.answer1()).click(); await click(HowManyPeopleLiveHere.submit()); await click(HouseholdSummary.submit()); + await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("View answers: Household residents"); await expect(await $(HubPage.heading()).getText()).toBe("Submission title"); await expect(await $(HubPage.guidance()).getText()).toBe("Submission guidance"); await expect(await $(HubPage.submit()).getText()).toBe("Submission button"); From 701ba71151d04bbf751c8cdd0e7c8bc9c0386489 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:19:59 +0100 Subject: [PATCH 439/567] Bump certifi from 2024.6.2 to 2024.7.4 in the pip group (#1424) --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4973a56586..37e305e00c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "astroid" @@ -306,13 +306,13 @@ files = [ [[package]] name = "certifi" -version = "2024.6.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, - {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] From 2a6ac9e4d55eda16af204a6daf061a2468512bba Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:10:04 +0100 Subject: [PATCH 440/567] Schemas v5.8.0 (#1439) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 833e0a4813..4a62af8428 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.7.0 +v5.8.0 From 0cbb427bc13f98329e548fb89615f57af5646c66 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:51:14 +0100 Subject: [PATCH 441/567] Update cryptography to v1.2.1 (#1442) --- poetry.lock | 948 ++++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 499 insertions(+), 451 deletions(-) diff --git a/poetry.lock b/poetry.lock index 37e305e00c..ac2c8d0de3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "astroid" @@ -126,17 +126,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.127" +version = "1.34.147" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.127-py3-none-any.whl", hash = "sha256:d370befe4fb7aea5bc383057d7dad18dda5d0cf3cd3295915bcc8c8c4191905c"}, - {file = "boto3-1.34.127.tar.gz", hash = "sha256:58ccdeae3a96811ecc9d5d866d8226faadbd0ee1891756e4a04d5186e9a57a64"}, + {file = "boto3-1.34.147-py3-none-any.whl", hash = "sha256:e1cef9a1a301866bcdee32ae0c699465eb2345f9a8e613a5835821430165ff6d"}, + {file = "boto3-1.34.147.tar.gz", hash = "sha256:9ec1c6ab22588242a47549f51a63dfc7c21fdf95a94820fc6e629ab060c38bd9"}, ] [package.dependencies] -botocore = ">=1.34.127,<1.35.0" +botocore = ">=1.34.147,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -145,13 +145,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.127" +version = "1.34.147" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.127-py3-none-any.whl", hash = "sha256:e14fa28c8bb141de965e700f88b196d17c67a703c7f0f5c7e14f7dd1cf636011"}, - {file = "botocore-1.34.127.tar.gz", hash = "sha256:a377871742c40603d559103f19acb7bc93cfaf285e68f21b81637ec396099877"}, + {file = "botocore-1.34.147-py3-none-any.whl", hash = "sha256:be94a2f4874b1d1705cae2bd512c475047497379651678593acb6c61c50d91de"}, + {file = "botocore-1.34.147.tar.gz", hash = "sha256:2e8f000b77e4ca345146cb2edab6403769a517b564f627bb084ab335417f3dbe"}, ] [package.dependencies] @@ -295,13 +295,13 @@ cffi = ">=1.0.0" [[package]] name = "cachetools" -version = "5.3.3" +version = "5.4.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, + {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, ] [[package]] @@ -522,63 +522,63 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.5.3" +version = "7.6.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"}, + {file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"}, + {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"}, + {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"}, + {file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"}, + {file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"}, + {file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"}, + {file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"}, + {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"}, + {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"}, + {file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"}, + {file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"}, + {file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"}, + {file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"}, + {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"}, + {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"}, + {file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"}, + {file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"}, + {file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"}, + {file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"}, + {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"}, + {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"}, + {file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"}, + {file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"}, + {file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"}, + {file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"}, + {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"}, + {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"}, + {file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"}, + {file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"}, + {file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"}, + {file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"}, ] [package.extras] @@ -698,43 +698,38 @@ dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-xdi [[package]] name = "cryptography" -version = "42.0.7" +version = "43.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, - {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, - {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, - {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, - {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, - {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, - {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, - {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, - {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, - {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, - {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, - {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, - {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, - {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, + {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"}, + {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"}, + {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"}, + {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"}, + {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"}, + {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"}, + {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"}, + {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"}, + {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"}, + {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"}, + {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"}, + {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"}, + {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"}, ] [package.dependencies] @@ -747,7 +742,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -836,13 +831,13 @@ files = [ [[package]] name = "email-validator" -version = "2.1.2" +version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" files = [ - {file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"}, - {file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"}, + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, ] [package.dependencies] @@ -865,13 +860,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.23.2" +version = "2.23.3" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.23.2-py3-none-any.whl", hash = "sha256:3721946b955930c065231befd24a9cdc68b339746e93848ef01a010d98e4eb4f"}, - {file = "fakeredis-2.23.2.tar.gz", hash = "sha256:d649c409abe46c63690b6c35d3c460e4ce64c69a52cea3f02daff2649378f878"}, + {file = "fakeredis-2.23.3-py3-none-any.whl", hash = "sha256:4779be828f4ebf53e1a286fd11e2ffe0f510d3e5507f143d644e67a07387d759"}, + {file = "fakeredis-2.23.3.tar.gz", hash = "sha256:0c67caa31530114f451f012eca920338c5eb83fa7f1f461dd41b8d2488a99cba"}, ] [package.dependencies] @@ -887,18 +882,18 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"] [[package]] name = "flake8" -version = "7.0.0" +version = "7.1.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, + {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, + {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" +pycodestyle = ">=2.12.0,<2.13.0" pyflakes = ">=3.2.0,<3.3.0" [[package]] @@ -1154,13 +1149,13 @@ test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.19.0" +version = "2.19.1" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.0.tar.gz", hash = "sha256:cf1b7c2694047886d2af1128a03ae99e391108a08804f87cfd35970e49c9cd10"}, - {file = "google_api_core-2.19.0-py3-none-any.whl", hash = "sha256:8661eec4078c35428fd3f69a2c7ee29e342896b70f01d1a1cbcb334372dd6251"}, + {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, + {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, ] [package.dependencies] @@ -1169,7 +1164,7 @@ googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} proto-plus = ">=1.22.3,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" [package.extras] @@ -1179,13 +1174,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.29.0" +version = "2.32.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"}, + {file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"}, ] [package.dependencies] @@ -1240,13 +1235,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.21.3" +version = "2.22.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-pubsub-2.21.3.tar.gz", hash = "sha256:df44c79a4d7bfb5adfa675413e3b91bbbe06e91d2715a15f925140b05715bb7d"}, - {file = "google_cloud_pubsub-2.21.3-py2.py3-none-any.whl", hash = "sha256:a417d63d9db5e8b9ff51dee705c69ed02b0a8bc62cac2474a86f22aea288c0ec"}, + {file = "google_cloud_pubsub-2.22.0-py2.py3-none-any.whl", hash = "sha256:229bf60a3835c1bb21ee36c7d4368b111097678b8ed25d3fbc5e639a1d03388d"}, + {file = "google_cloud_pubsub-2.22.0.tar.gz", hash = "sha256:a4c2b1a5ca2c0b32c8d3776c85f498266c3d79696696ea67010c857b45af17d8"}, ] [package.dependencies] @@ -1256,20 +1251,20 @@ grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" grpcio = ">=1.51.3,<2.0dev" grpcio-status = ">=1.33.2" proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [package.extras] libcst = ["libcst (>=0.3.10)"] [[package]] name = "google-cloud-storage" -version = "2.17.0" +version = "2.18.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-storage-2.17.0.tar.gz", hash = "sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388"}, - {file = "google_cloud_storage-2.17.0-py2.py3-none-any.whl", hash = "sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1"}, + {file = "google_cloud_storage-2.18.0-py2.py3-none-any.whl", hash = "sha256:e8e1a9577952143c3fca8163005ecfadd2d70ec080fa158a8b305000e2c22fbb"}, + {file = "google_cloud_storage-2.18.0.tar.gz", hash = "sha256:0aa3f7c57f3632f81b455d91558d2b27ada96eee2de3aaa17f689db1470d9578"}, ] [package.dependencies] @@ -1281,17 +1276,18 @@ google-resumable-media = ">=2.6.0" requests = ">=2.18.0,<3.0.0dev" [package.extras] -protobuf = ["protobuf (<5.0.0dev)"] +protobuf = ["protobuf (<6.0.0dev)"] +tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.16.3" +version = "2.16.4" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-tasks-2.16.3.tar.gz", hash = "sha256:d891fe7006db4d6122838aa6de4aca6e0077bab224edcae684666ae3e303c45f"}, - {file = "google_cloud_tasks-2.16.3-py2.py3-none-any.whl", hash = "sha256:a1a8473f5c76907525b51ae4c20182a42333b5c3e1dc5782d7af6c2f58241a0c"}, + {file = "google-cloud-tasks-2.16.4.tar.gz", hash = "sha256:61033c1edd7dc5aa3b9fbe5c8deb64be0c50e0baee9d9465f3f8b75b2cda55b9"}, + {file = "google_cloud_tasks-2.16.4-py2.py3-none-any.whl", hash = "sha256:7db2b364ded9c2e0143b2794d1ae53ef7461a99567698e3e4ba9e02b614e1adb"}, ] [package.dependencies] @@ -1299,7 +1295,7 @@ google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extr google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = ">=1.22.3,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [[package]] name = "google-crc32c" @@ -1383,13 +1379,13 @@ testing = ["pytest"] [[package]] name = "google-resumable-media" -version = "2.7.0" +version = "2.7.1" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false -python-versions = ">= 3.7" +python-versions = ">=3.7" files = [ - {file = "google-resumable-media-2.7.0.tar.gz", hash = "sha256:5f18f5fa9836f4b083162064a1c2c98c17239bfda9ca50ad970ccf905f3e625b"}, - {file = "google_resumable_media-2.7.0-py2.py3-none-any.whl", hash = "sha256:79543cfe433b63fd81c0844b7803aba1bb8950b47bedf7d980c38fa123937e08"}, + {file = "google-resumable-media-2.7.1.tar.gz", hash = "sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33"}, + {file = "google_resumable_media-2.7.1-py2.py3-none-any.whl", hash = "sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c"}, ] [package.dependencies] @@ -1401,18 +1397,18 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.63.0" +version = "1.63.2" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"}, - {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"}, + {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, + {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, ] [package.dependencies] grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] @@ -1490,77 +1486,77 @@ test = ["objgraph", "psutil"] [[package]] name = "grpc-google-iam-v1" -version = "0.13.0" +version = "0.13.1" description = "IAM API client library" optional = false python-versions = ">=3.7" files = [ - {file = "grpc-google-iam-v1-0.13.0.tar.gz", hash = "sha256:fad318608b9e093258fbf12529180f400d1c44453698a33509cc6ecf005b294e"}, - {file = "grpc_google_iam_v1-0.13.0-py2.py3-none-any.whl", hash = "sha256:53902e2af7de8df8c1bd91373d9be55b0743ec267a7428ea638db3775becae89"}, + {file = "grpc-google-iam-v1-0.13.1.tar.gz", hash = "sha256:3ff4b2fd9d990965e410965253c0da6f66205d5a8291c4c31c6ebecca18a9001"}, + {file = "grpc_google_iam_v1-0.13.1-py2.py3-none-any.whl", hash = "sha256:c3e86151a981811f30d5e7330f271cee53e73bb87755e88cc3b6f0c7b5fe374e"}, ] [package.dependencies] googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} grpcio = ">=1.44.0,<2.0.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [[package]] name = "grpcio" -version = "1.64.1" +version = "1.65.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, - {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, - {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, - {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, - {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, - {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, - {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, - {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, - {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, - {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, - {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, - {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, - {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, - {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, - {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, - {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, - {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, - {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, - {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, - {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, - {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.64.1)"] + {file = "grpcio-1.65.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062"}, + {file = "grpcio-1.65.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c"}, + {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857"}, + {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364"}, + {file = "grpcio-1.65.1-cp310-cp310-win32.whl", hash = "sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875"}, + {file = "grpcio-1.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad"}, + {file = "grpcio-1.65.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037"}, + {file = "grpcio-1.65.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462"}, + {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8"}, + {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2"}, + {file = "grpcio-1.65.1-cp311-cp311-win32.whl", hash = "sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8"}, + {file = "grpcio-1.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2"}, + {file = "grpcio-1.65.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f"}, + {file = "grpcio-1.65.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11"}, + {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d"}, + {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff"}, + {file = "grpcio-1.65.1-cp312-cp312-win32.whl", hash = "sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177"}, + {file = "grpcio-1.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59"}, + {file = "grpcio-1.65.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83"}, + {file = "grpcio-1.65.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2"}, + {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9"}, + {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f"}, + {file = "grpcio-1.65.1-cp38-cp38-win32.whl", hash = "sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0"}, + {file = "grpcio-1.65.1-cp38-cp38-win_amd64.whl", hash = "sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153"}, + {file = "grpcio-1.65.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3"}, + {file = "grpcio-1.65.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8"}, + {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68"}, + {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e"}, + {file = "grpcio-1.65.1-cp39-cp39-win32.whl", hash = "sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57"}, + {file = "grpcio-1.65.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c"}, + {file = "grpcio-1.65.1.tar.gz", hash = "sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.65.1)"] [[package]] name = "grpcio-status" @@ -1661,13 +1657,13 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve [[package]] name = "humanize" -version = "4.9.0" +version = "4.10.0" description = "Python humanize utilities" optional = false python-versions = ">=3.8" files = [ - {file = "humanize-4.9.0-py3-none-any.whl", hash = "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16"}, - {file = "humanize-4.9.0.tar.gz", hash = "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa"}, + {file = "humanize-4.10.0-py3-none-any.whl", hash = "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6"}, + {file = "humanize-4.10.0.tar.gz", hash = "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978"}, ] [package.extras] @@ -1786,13 +1782,13 @@ files = [ [[package]] name = "jsonschema" -version = "4.22.0" +version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, - {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, ] [package.dependencies] @@ -1803,7 +1799,7 @@ rpds-py = ">=0.7.1" [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] [[package]] name = "jsonschema-specifications" @@ -1997,13 +1993,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.9" +version = "5.0.11" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.9-py2.py3-none-any.whl", hash = "sha256:21a13e02f83d6a18cfcd99949c96abb2e889f4bd51c4c6a3ecc8b78765cb854e"}, - {file = "moto-5.0.9.tar.gz", hash = "sha256:eb71f1cba01c70fff1f16086acb24d6d9aeb32830d646d8989f98a29aeae24ba"}, + {file = "moto-5.0.11-py2.py3-none-any.whl", hash = "sha256:bdba9bec0afcde9f99b58c5271d6458dbfcda0a0a1e9beaecd808d2591db65ea"}, + {file = "moto-5.0.11.tar.gz", hash = "sha256:606b641f4c6ef69f28a84147d6d6806d052011e7ae7b0fe46ae8858e7a27a0a3"}, ] [package.dependencies] @@ -2041,43 +2037,43 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.10.0" +version = "1.11.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229"}, + {file = "mypy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287"}, + {file = "mypy-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6"}, + {file = "mypy-1.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be"}, + {file = "mypy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00"}, + {file = "mypy-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb"}, + {file = "mypy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1"}, + {file = "mypy-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3"}, + {file = "mypy-1.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d"}, + {file = "mypy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20"}, + {file = "mypy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba"}, + {file = "mypy-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd"}, + {file = "mypy-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d"}, + {file = "mypy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2"}, + {file = "mypy-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850"}, + {file = "mypy-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac"}, + {file = "mypy-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9"}, + {file = "mypy-1.11.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7"}, + {file = "mypy-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf"}, + {file = "mypy-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095"}, + {file = "mypy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe"}, + {file = "mypy-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c"}, + {file = "mypy-1.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13"}, + {file = "mypy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac"}, + {file = "mypy-1.11.0-py3-none-any.whl", hash = "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace"}, + {file = "mypy-1.11.0.tar.gz", hash = "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -2112,13 +2108,13 @@ dev = ["black", "mypy", "pytest"] [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -2189,18 +2185,18 @@ type = ["mypy (>=1.8)"] [[package]] name = "playwright" -version = "1.44.0" +version = "1.45.1" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" files = [ - {file = "playwright-1.44.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:c2317a80896796fdeb03d60f06cc229e775ff2e19b80c64b1bb9b29c8a59d992"}, - {file = "playwright-1.44.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54d44fb634d870839301c2326e1e12a178a1be0de76d0caaec230ab075c2e077"}, - {file = "playwright-1.44.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:64b67194e73b47ae72acf25f1a9cfacfef38ca2b52e4bb8b0abd385c5deeaadf"}, - {file = "playwright-1.44.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:29161b1fae71f7c402df5b15f0bd3deaeecd8b3d1ecd9ff01271700c66210e7b"}, - {file = "playwright-1.44.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8c8a3bfea17576d3f94a2363eee195cbda8dbba86975588c7eaac7792b25eee"}, - {file = "playwright-1.44.0-py3-none-win32.whl", hash = "sha256:235e37832deaa9af8a629d09955396259ab757533cc1922f9b0308b4ee0d9cdf"}, - {file = "playwright-1.44.0-py3-none-win_amd64.whl", hash = "sha256:5b8a4a1d4d50f4ff99b47965576322a8c4e34631854b862a25c1feb824be22a8"}, + {file = "playwright-1.45.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:360607e37c00cdf97c74317f010e106ac4671aeaec6a192431dd71a30941da9d"}, + {file = "playwright-1.45.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:20adc2abf164c5e8969f9066011b152e12c210549edec78cd05bd0e9cf4135b7"}, + {file = "playwright-1.45.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:5f047cdc6accf4c7084dfc7587a2a5ef790cddc44cbb111e471293c5a91119db"}, + {file = "playwright-1.45.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:f06f6659abe0abf263e5f6661d379fbf85c112745dd31d82332ceae914f58df7"}, + {file = "playwright-1.45.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87dc3b3d17e12c68830c29b7fdf5e93315221bbb4c6090e83e967e154e2c1828"}, + {file = "playwright-1.45.1-py3-none-win32.whl", hash = "sha256:2b8f517886ef1e2151982f6e7be84be3ef7d8135bdcf8ee705b4e4e99566e866"}, + {file = "playwright-1.45.1-py3-none-win_amd64.whl", hash = "sha256:0d236cf427784e77de352ba1b7d700693c5fe455b8e5f627f6d84ad5b84b5bf5"}, ] [package.dependencies] @@ -2224,20 +2220,20 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "proto-plus" -version = "1.23.0" +version = "1.24.0" description = "Beautiful, Pythonic protocol buffers." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "proto-plus-1.23.0.tar.gz", hash = "sha256:89075171ef11988b3fa157f5dbd8b9cf09d65fffee97e29ce403cd8defba19d2"}, - {file = "proto_plus-1.23.0-py3-none-any.whl", hash = "sha256:a829c79e619e1cf632de091013a4173deed13a55f326ef84f05af6f50ff4c82c"}, + {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, + {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, ] [package.dependencies] -protobuf = ">=3.19.0,<5.0.0dev" +protobuf = ">=3.19.0,<6.0.0dev" [package.extras] -testing = ["google-api-core[grpc] (>=1.31.5)"] +testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" @@ -2286,13 +2282,13 @@ pyasn1 = ">=0.4.6,<0.7.0" [[package]] name = "pycodestyle" -version = "2.11.1" +version = "2.12.0" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, + {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"}, + {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"}, ] [[package]] @@ -2414,20 +2410,20 @@ files = [ [[package]] name = "pytest" -version = "8.2.1" +version = "8.3.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"}, - {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"}, + {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, + {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -2541,13 +2537,13 @@ six = ">=1.5" [[package]] name = "python-snappy" -version = "0.7.1" +version = "0.7.2" description = "Python library for the snappy compression library from Google" optional = false python-versions = "*" files = [ - {file = "python-snappy-0.7.1.tar.gz", hash = "sha256:1bc29d36211d44bb9f04f3d7ccfbaeaebbc2f62b6d40f4fc4edd1fb16bc52c13"}, - {file = "python_snappy-0.7.1-py3-none-any.whl", hash = "sha256:7c9111be1ae1dcbf4ce32b752366d4a5d4f07898d517691c4003d41e04b03488"}, + {file = "python_snappy-0.7.2-py3-none-any.whl", hash = "sha256:b4b2c39142064925d5a554672a09de4188fc4f2b2494a2ecb35042930e129444"}, + {file = "python_snappy-0.7.2.tar.gz", hash = "sha256:04bf182f9d9f67b7a846dae2f1df36180ceeee8d3380e4b6799deff5272c4978"}, ] [package.dependencies] @@ -2626,13 +2622,13 @@ files = [ [[package]] name = "redis" -version = "5.0.6" +version = "5.0.7" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"}, - {file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"}, + {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, + {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, ] [package.extras] @@ -2779,13 +2775,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "responses" -version = "0.25.0" +version = "0.25.3" description = "A utility library for mocking out the `requests` Python library." optional = false python-versions = ">=3.8" files = [ - {file = "responses-0.25.0-py3-none-any.whl", hash = "sha256:2f0b9c2b6437db4b528619a77e5d565e4ec2a9532162ac1a131a83529db7be1a"}, - {file = "responses-0.25.0.tar.gz", hash = "sha256:01ae6a02b4f34e39bffceb0fc6786b67a25eae919c6368d05eabc8d9576c2a66"}, + {file = "responses-0.25.3-py3-none-any.whl", hash = "sha256:521efcbc82081ab8daa588e08f7e8a64ce79b91c39f6e62199b19159bea7dbcb"}, + {file = "responses-0.25.3.tar.gz", hash = "sha256:617b9247abd9ae28313d57a75880422d55ec63c29d33d629697590a034358dba"}, ] [package.dependencies] @@ -2798,110 +2794,110 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.18.1" +version = "0.19.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53"}, - {file = "rpds_py-0.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0"}, - {file = "rpds_py-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da"}, - {file = "rpds_py-0.18.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1"}, - {file = "rpds_py-0.18.1-cp310-none-win32.whl", hash = "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333"}, - {file = "rpds_py-0.18.1-cp310-none-win_amd64.whl", hash = "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8"}, - {file = "rpds_py-0.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100"}, - {file = "rpds_py-0.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e"}, - {file = "rpds_py-0.18.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88"}, - {file = "rpds_py-0.18.1-cp311-none-win32.whl", hash = "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb"}, - {file = "rpds_py-0.18.1-cp311-none-win_amd64.whl", hash = "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3"}, - {file = "rpds_py-0.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8"}, - {file = "rpds_py-0.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac"}, - {file = "rpds_py-0.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a"}, - {file = "rpds_py-0.18.1-cp312-none-win32.whl", hash = "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6"}, - {file = "rpds_py-0.18.1-cp312-none-win_amd64.whl", hash = "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74"}, - {file = "rpds_py-0.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5"}, - {file = "rpds_py-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e"}, - {file = "rpds_py-0.18.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc"}, - {file = "rpds_py-0.18.1-cp38-none-win32.whl", hash = "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9"}, - {file = "rpds_py-0.18.1-cp38-none-win_amd64.whl", hash = "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93"}, - {file = "rpds_py-0.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab"}, - {file = "rpds_py-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b"}, - {file = "rpds_py-0.18.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26"}, - {file = "rpds_py-0.18.1-cp39-none-win32.whl", hash = "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360"}, - {file = "rpds_py-0.18.1-cp39-none-win_amd64.whl", hash = "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c"}, - {file = "rpds_py-0.18.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909"}, - {file = "rpds_py-0.18.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49"}, - {file = "rpds_py-0.18.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e"}, - {file = "rpds_py-0.18.1.tar.gz", hash = "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f"}, + {file = "rpds_py-0.19.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:fb37bd599f031f1a6fb9e58ec62864ccf3ad549cf14bac527dbfa97123edcca4"}, + {file = "rpds_py-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3384d278df99ec2c6acf701d067147320b864ef6727405d6470838476e44d9e8"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54548e0be3ac117595408fd4ca0ac9278fde89829b0b518be92863b17ff67a2"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8eb488ef928cdbc05a27245e52de73c0d7c72a34240ef4d9893fdf65a8c1a955"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5da93debdfe27b2bfc69eefb592e1831d957b9535e0943a0ee8b97996de21b5"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79e205c70afddd41f6ee79a8656aec738492a550247a7af697d5bd1aee14f766"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:959179efb3e4a27610e8d54d667c02a9feaa86bbabaf63efa7faa4dfa780d4f1"}, + {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a6e605bb9edcf010f54f8b6a590dd23a4b40a8cb141255eec2a03db249bc915b"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9133d75dc119a61d1a0ded38fb9ba40a00ef41697cc07adb6ae098c875195a3f"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd36b712d35e757e28bf2f40a71e8f8a2d43c8b026d881aa0c617b450d6865c9"}, + {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354f3a91718489912f2e0fc331c24eaaf6a4565c080e00fbedb6015857c00582"}, + {file = "rpds_py-0.19.0-cp310-none-win32.whl", hash = "sha256:ebcbf356bf5c51afc3290e491d3722b26aaf5b6af3c1c7f6a1b757828a46e336"}, + {file = "rpds_py-0.19.0-cp310-none-win_amd64.whl", hash = "sha256:75a6076289b2df6c8ecb9d13ff79ae0cad1d5fb40af377a5021016d58cd691ec"}, + {file = "rpds_py-0.19.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6d45080095e585f8c5097897313def60caa2046da202cdb17a01f147fb263b81"}, + {file = "rpds_py-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5c9581019c96f865483d031691a5ff1cc455feb4d84fc6920a5ffc48a794d8a"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1540d807364c84516417115c38f0119dfec5ea5c0dd9a25332dea60b1d26fc4d"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e65489222b410f79711dc3d2d5003d2757e30874096b2008d50329ea4d0f88c"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da6f400eeb8c36f72ef6646ea530d6d175a4f77ff2ed8dfd6352842274c1d8b"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37f46bb11858717e0efa7893c0f7055c43b44c103e40e69442db5061cb26ed34"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:071d4adc734de562bd11d43bd134330fb6249769b2f66b9310dab7460f4bf714"}, + {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9625367c8955e4319049113ea4f8fee0c6c1145192d57946c6ffcd8fe8bf48dd"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e19509145275d46bc4d1e16af0b57a12d227c8253655a46bbd5ec317e941279d"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d438e4c020d8c39961deaf58f6913b1bf8832d9b6f62ec35bd93e97807e9cbc"}, + {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90bf55d9d139e5d127193170f38c584ed3c79e16638890d2e36f23aa1630b952"}, + {file = "rpds_py-0.19.0-cp311-none-win32.whl", hash = "sha256:8d6ad132b1bc13d05ffe5b85e7a01a3998bf3a6302ba594b28d61b8c2cf13aaf"}, + {file = "rpds_py-0.19.0-cp311-none-win_amd64.whl", hash = "sha256:7ec72df7354e6b7f6eb2a17fa6901350018c3a9ad78e48d7b2b54d0412539a67"}, + {file = "rpds_py-0.19.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:5095a7c838a8647c32aa37c3a460d2c48debff7fc26e1136aee60100a8cd8f68"}, + {file = "rpds_py-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f2f78ef14077e08856e788fa482107aa602636c16c25bdf59c22ea525a785e9"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7cc6cb44f8636fbf4a934ca72f3e786ba3c9f9ba4f4d74611e7da80684e48d2"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf902878b4af334a09de7a45badbff0389e7cf8dc2e4dcf5f07125d0b7c2656d"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:688aa6b8aa724db1596514751ffb767766e02e5c4a87486ab36b8e1ebc1aedac"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57dbc9167d48e355e2569346b5aa4077f29bf86389c924df25c0a8b9124461fb"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4cf5a9497874822341c2ebe0d5850fed392034caadc0bad134ab6822c0925b"}, + {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a790d235b9d39c70a466200d506bb33a98e2ee374a9b4eec7a8ac64c2c261fa"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d16089dfa58719c98a1c06f2daceba6d8e3fb9b5d7931af4a990a3c486241cb"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bc9128e74fe94650367fe23f37074f121b9f796cabbd2f928f13e9661837296d"}, + {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c8f77e661ffd96ff104bebf7d0f3255b02aa5d5b28326f5408d6284c4a8b3248"}, + {file = "rpds_py-0.19.0-cp312-none-win32.whl", hash = "sha256:5f83689a38e76969327e9b682be5521d87a0c9e5a2e187d2bc6be4765f0d4600"}, + {file = "rpds_py-0.19.0-cp312-none-win_amd64.whl", hash = "sha256:06925c50f86da0596b9c3c64c3837b2481337b83ef3519e5db2701df695453a4"}, + {file = "rpds_py-0.19.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:52e466bea6f8f3a44b1234570244b1cff45150f59a4acae3fcc5fd700c2993ca"}, + {file = "rpds_py-0.19.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e21cc693045fda7f745c790cb687958161ce172ffe3c5719ca1764e752237d16"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b31f059878eb1f5da8b2fd82480cc18bed8dcd7fb8fe68370e2e6285fa86da6"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dd46f309e953927dd018567d6a9e2fb84783963650171f6c5fe7e5c41fd5666"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a01a4490e170376cd79258b7f755fa13b1a6c3667e872c8e35051ae857a92b"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcf426a8c38eb57f7bf28932e68425ba86def6e756a5b8cb4731d8e62e4e0223"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68eea5df6347d3f1378ce992d86b2af16ad7ff4dcb4a19ccdc23dea901b87fb"}, + {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dab8d921b55a28287733263c0e4c7db11b3ee22aee158a4de09f13c93283c62d"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6fe87efd7f47266dfc42fe76dae89060038f1d9cb911f89ae7e5084148d1cc08"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:535d4b52524a961d220875688159277f0e9eeeda0ac45e766092bfb54437543f"}, + {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8b1a94b8afc154fbe36978a511a1f155f9bd97664e4f1f7a374d72e180ceb0ae"}, + {file = "rpds_py-0.19.0-cp38-none-win32.whl", hash = "sha256:7c98298a15d6b90c8f6e3caa6457f4f022423caa5fa1a1ca7a5e9e512bdb77a4"}, + {file = "rpds_py-0.19.0-cp38-none-win_amd64.whl", hash = "sha256:b0da31853ab6e58a11db3205729133ce0df26e6804e93079dee095be3d681dc1"}, + {file = "rpds_py-0.19.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5039e3cef7b3e7a060de468a4a60a60a1f31786da94c6cb054e7a3c75906111c"}, + {file = "rpds_py-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab1932ca6cb8c7499a4d87cb21ccc0d3326f172cfb6a64021a889b591bb3045c"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2afd2164a1e85226fcb6a1da77a5c8896c18bfe08e82e8ceced5181c42d2179"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1c30841f5040de47a0046c243fc1b44ddc87d1b12435a43b8edff7e7cb1e0d0"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f757f359f30ec7dcebca662a6bd46d1098f8b9fb1fcd661a9e13f2e8ce343ba1"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15e65395a59d2e0e96caf8ee5389ffb4604e980479c32742936ddd7ade914b22"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb0f6eb3a320f24b94d177e62f4074ff438f2ad9d27e75a46221904ef21a7b05"}, + {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b228e693a2559888790936e20f5f88b6e9f8162c681830eda303bad7517b4d5a"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2575efaa5d949c9f4e2cdbe7d805d02122c16065bfb8d95c129372d65a291a0b"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5c872814b77a4e84afa293a1bee08c14daed1068b2bb1cc312edbf020bbbca2b"}, + {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850720e1b383df199b8433a20e02b25b72f0fded28bc03c5bd79e2ce7ef050be"}, + {file = "rpds_py-0.19.0-cp39-none-win32.whl", hash = "sha256:ce84a7efa5af9f54c0aa7692c45861c1667080814286cacb9958c07fc50294fb"}, + {file = "rpds_py-0.19.0-cp39-none-win_amd64.whl", hash = "sha256:1c26da90b8d06227d7769f34915913911222d24ce08c0ab2d60b354e2d9c7aff"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:75969cf900d7be665ccb1622a9aba225cf386bbc9c3bcfeeab9f62b5048f4a07"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8445f23f13339da640d1be8e44e5baf4af97e396882ebbf1692aecd67f67c479"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5a7c1062ef8aea3eda149f08120f10795835fc1c8bc6ad948fb9652a113ca55"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:462b0c18fbb48fdbf980914a02ee38c423a25fcc4cf40f66bacc95a2d2d73bc8"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3208f9aea18991ac7f2b39721e947bbd752a1abbe79ad90d9b6a84a74d44409b"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3444fe52b82f122d8a99bf66777aed6b858d392b12f4c317da19f8234db4533"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb4bac7185a9f0168d38c01d7a00addece9822a52870eee26b8d5b61409213"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6b130bd4163c93798a6b9bb96be64a7c43e1cec81126ffa7ffaa106e1fc5cef5"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a707b158b4410aefb6b054715545bbb21aaa5d5d0080217290131c49c2124a6e"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dc9ac4659456bde7c567107556ab065801622396b435a3ff213daef27b495388"}, + {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:81ea573aa46d3b6b3d890cd3c0ad82105985e6058a4baed03cf92518081eec8c"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f148c3f47f7f29a79c38cc5d020edcb5ca780020fab94dbc21f9af95c463581"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0906357f90784a66e89ae3eadc2654f36c580a7d65cf63e6a616e4aec3a81be"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f629ecc2db6a4736b5ba95a8347b0089240d69ad14ac364f557d52ad68cf94b0"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6feacd1d178c30e5bc37184526e56740342fd2aa6371a28367bad7908d454fc"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b6068ee374fdfab63689be0963333aa83b0815ead5d8648389a8ded593378"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78d57546bad81e0da13263e4c9ce30e96dcbe720dbff5ada08d2600a3502e526"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b6683a37338818646af718c9ca2a07f89787551057fae57c4ec0446dc6224b"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8481b946792415adc07410420d6fc65a352b45d347b78fec45d8f8f0d7496f0"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bec35eb20792ea64c3c57891bc3ca0bedb2884fbac2c8249d9b731447ecde4fa"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:aa5476c3e3a402c37779e95f7b4048db2cb5b0ed0b9d006983965e93f40fe05a"}, + {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:19d02c45f2507b489fd4df7b827940f1420480b3e2e471e952af4d44a1ea8e34"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3e2fd14c5d49ee1da322672375963f19f32b3d5953f0615b175ff7b9d38daed"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:93a91c2640645303e874eada51f4f33351b84b351a689d470f8108d0e0694210"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b9fc03bf76a94065299d4a2ecd8dfbae4ae8e2e8098bbfa6ab6413ca267709"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a4b07cdf3f84310c08c1de2c12ddadbb7a77568bcb16e95489f9c81074322ed"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba0ed0dc6763d8bd6e5de5cf0d746d28e706a10b615ea382ac0ab17bb7388633"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:474bc83233abdcf2124ed3f66230a1c8435896046caa4b0b5ab6013c640803cc"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329c719d31362355a96b435f4653e3b4b061fcc9eba9f91dd40804ca637d914e"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef9101f3f7b59043a34f1dccbb385ca760467590951952d6701df0da9893ca0c"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:0121803b0f424ee2109d6e1f27db45b166ebaa4b32ff47d6aa225642636cd834"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8344127403dea42f5970adccf6c5957a71a47f522171fafaf4c6ddb41b61703a"}, + {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:443cec402ddd650bb2b885113e1dcedb22b1175c6be223b14246a714b61cd521"}, + {file = "rpds_py-0.19.0.tar.gz", hash = "sha256:4fdc9afadbeb393b4bbbad75481e0ea78e4469f2e1d713a90811700830b553a9"}, ] [[package]] @@ -2920,13 +2916,13 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.10.1" +version = "0.10.2" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d"}, - {file = "s3transfer-0.10.1.tar.gz", hash = "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19"}, + {file = "s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69"}, + {file = "s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6"}, ] [package.dependencies] @@ -2937,13 +2933,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "sdc-cryptography" -version = "1.2.0" +version = "1.2.1" description = "A shared library for SDC services that use JWT with JWE" optional = false python-versions = "*" files = [ - {file = "sdc-cryptography-1.2.0.tar.gz", hash = "sha256:b39547b0d80c401e9419e44496430c2e498fd2ff59f76b7b89adf7697d5b23db"}, - {file = "sdc_cryptography-1.2.0-py3-none-any.whl", hash = "sha256:1c15e8e412c89c30f129e3b60e2bfc60017a7e9a1e11ef3401e328b154958651"}, + {file = "sdc_cryptography-1.2.1-py3-none-any.whl", hash = "sha256:2b87da178c0d9c6a8add4e2afbe81139bbb056598af0cda61b52f421fb29451f"}, + {file = "sdc_cryptography-1.2.1.tar.gz", hash = "sha256:cea2de62e65940efb72bfd3ed677b1e685678521a52eb4c06769ffce506f5847"}, ] [package.dependencies] @@ -2953,18 +2949,19 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "70.0.0" +version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, - {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, + {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, + {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "simplejson" @@ -3108,18 +3105,18 @@ files = [ [[package]] name = "structlog" -version = "24.2.0" +version = "24.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" files = [ - {file = "structlog-24.2.0-py3-none-any.whl", hash = "sha256:983bd49f70725c5e1e3867096c0c09665918936b3db27341b41d294283d7a48a"}, - {file = "structlog-24.2.0.tar.gz", hash = "sha256:0e3fe74924a6d8857d3f612739efb94c72a7417d7c7c008d12276bca3b5bf13b"}, + {file = "structlog-24.4.0-py3-none-any.whl", hash = "sha256:597f61e80a91cc0749a9fd2a098ed76715a1c8a01f73e336b746504d1aad7610"}, + {file = "structlog-24.4.0.tar.gz", hash = "sha256:b27bfecede327a6d2da5fbc96bd859f114ecc398a6389d664f62085ee7ae6fc4"}, ] [package.extras] dev = ["freezegun (>=0.2.8)", "mypy (>=1.4)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "rich", "simplejson", "twisted"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] typing = ["mypy (>=1.4)", "rich", "twisted"] @@ -3139,13 +3136,13 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "tomlkit" -version = "0.12.5" +version = "0.13.0" description = "Style preserving TOML library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, + {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, + {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, ] [[package]] @@ -3170,13 +3167,13 @@ telegram = ["requests"] [[package]] name = "types-cachetools" -version = "5.3.0.7" +version = "5.4.0.20240717" description = "Typing stubs for cachetools" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "types-cachetools-5.3.0.7.tar.gz", hash = "sha256:27c982cdb9cf3fead8b0089ee6b895715ecc99dac90ec29e2cab56eb1aaf4199"}, - {file = "types_cachetools-5.3.0.7-py3-none-any.whl", hash = "sha256:98c069dc7fc087b1b061703369c80751b0a0fc561f6fb072b554e5eee23773a0"}, + {file = "types-cachetools-5.4.0.20240717.tar.gz", hash = "sha256:1eae90c48760bac44ab89108be938e8ce1d740910f2d4b68446dcdc82763f186"}, + {file = "types_cachetools-5.4.0.20240717-py3-none-any.whl", hash = "sha256:67c84c26df988039be68344b162afd2dd7cd3741dc08e7d67aa1954782fd2d2a"}, ] [[package]] @@ -3195,13 +3192,13 @@ types-setuptools = "*" [[package]] name = "types-pyopenssl" -version = "24.1.0.20240425" +version = "24.1.0.20240722" description = "Typing stubs for pyOpenSSL" optional = false python-versions = ">=3.8" files = [ - {file = "types-pyOpenSSL-24.1.0.20240425.tar.gz", hash = "sha256:0a7e82626c1983dc8dc59292bf20654a51c3c3881bcbb9b337c1da6e32f0204e"}, - {file = "types_pyOpenSSL-24.1.0.20240425-py3-none-any.whl", hash = "sha256:f51a156835555dd2a1f025621e8c4fbe7493470331afeef96884d1d29bf3a473"}, + {file = "types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39"}, + {file = "types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54"}, ] [package.dependencies] @@ -3232,13 +3229,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240311" +version = "6.0.12.20240724" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"}, - {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, + {file = "types-PyYAML-6.0.12.20240724.tar.gz", hash = "sha256:cf7b31ae67e0c5b2919c703d2affc415485099d3fe6666a6912f040fd05cb67f"}, + {file = "types_PyYAML-6.0.12.20240724-py3-none-any.whl", hash = "sha256:e5becec598f3aa3a2ddf671de4a75fa1c6856fbf73b2840286c9d50fae2d5d48"}, ] [[package]] @@ -3258,13 +3255,13 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20240602" +version = "2.32.0.20240712" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, - {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, + {file = "types-requests-2.32.0.20240712.tar.gz", hash = "sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358"}, + {file = "types_requests-2.32.0.20240712-py3-none-any.whl", hash = "sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3"}, ] [package.dependencies] @@ -3272,13 +3269,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "70.0.0.20240524" +version = "71.1.0.20240724" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-70.0.0.20240524.tar.gz", hash = "sha256:e31fee7b9d15ef53980526579ac6089b3ae51a005a281acf97178e90ac71aff6"}, - {file = "types_setuptools-70.0.0.20240524-py3-none-any.whl", hash = "sha256:8f5379b9948682d72a9ab531fbe52932e84c4f38deda570255f9bae3edd766bc"}, + {file = "types-setuptools-71.1.0.20240724.tar.gz", hash = "sha256:ae61c528111a9450e149aedbc1ab0f7874f9bb0dc8f14c3cd200a1f82457050b"}, + {file = "types_setuptools-71.1.0.20240724-py3-none-any.whl", hash = "sha256:90570f0578ed67bb25ada9b3d1432e6727178d2408a7051b27e4a712731df30a"}, ] [[package]] @@ -3294,13 +3291,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.12.1" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, - {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -3538,57 +3535,108 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [[package]] name = "zstandard" -version = "0.22.0" +version = "0.23.0" description = "Zstandard bindings for Python" optional = false python-versions = ">=3.8" files = [ - {file = "zstandard-0.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:275df437ab03f8c033b8a2c181e51716c32d831082d93ce48002a5227ec93019"}, - {file = "zstandard-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ac9957bc6d2403c4772c890916bf181b2653640da98f32e04b96e4d6fb3252a"}, - {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe3390c538f12437b859d815040763abc728955a52ca6ff9c5d4ac707c4ad98e"}, - {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1958100b8a1cc3f27fa21071a55cb2ed32e9e5df4c3c6e661c193437f171cba2"}, - {file = "zstandard-0.22.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93e1856c8313bc688d5df069e106a4bc962eef3d13372020cc6e3ebf5e045202"}, - {file = "zstandard-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1a90ba9a4c9c884bb876a14be2b1d216609385efb180393df40e5172e7ecf356"}, - {file = "zstandard-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3db41c5e49ef73641d5111554e1d1d3af106410a6c1fb52cf68912ba7a343a0d"}, - {file = "zstandard-0.22.0-cp310-cp310-win32.whl", hash = "sha256:d8593f8464fb64d58e8cb0b905b272d40184eac9a18d83cf8c10749c3eafcd7e"}, - {file = "zstandard-0.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:f1a4b358947a65b94e2501ce3e078bbc929b039ede4679ddb0460829b12f7375"}, - {file = "zstandard-0.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:589402548251056878d2e7c8859286eb91bd841af117dbe4ab000e6450987e08"}, - {file = "zstandard-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a97079b955b00b732c6f280d5023e0eefe359045e8b83b08cf0333af9ec78f26"}, - {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:445b47bc32de69d990ad0f34da0e20f535914623d1e506e74d6bc5c9dc40bb09"}, - {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33591d59f4956c9812f8063eff2e2c0065bc02050837f152574069f5f9f17775"}, - {file = "zstandard-0.22.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:888196c9c8893a1e8ff5e89b8f894e7f4f0e64a5af4d8f3c410f0319128bb2f8"}, - {file = "zstandard-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:53866a9d8ab363271c9e80c7c2e9441814961d47f88c9bc3b248142c32141d94"}, - {file = "zstandard-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ac59d5d6910b220141c1737b79d4a5aa9e57466e7469a012ed42ce2d3995e88"}, - {file = "zstandard-0.22.0-cp311-cp311-win32.whl", hash = "sha256:2b11ea433db22e720758cba584c9d661077121fcf60ab43351950ded20283440"}, - {file = "zstandard-0.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:11f0d1aab9516a497137b41e3d3ed4bbf7b2ee2abc79e5c8b010ad286d7464bd"}, - {file = "zstandard-0.22.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6c25b8eb733d4e741246151d895dd0308137532737f337411160ff69ca24f93a"}, - {file = "zstandard-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9b2cde1cd1b2a10246dbc143ba49d942d14fb3d2b4bccf4618d475c65464912"}, - {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a88b7df61a292603e7cd662d92565d915796b094ffb3d206579aaebac6b85d5f"}, - {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466e6ad8caefb589ed281c076deb6f0cd330e8bc13c5035854ffb9c2014b118c"}, - {file = "zstandard-0.22.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1d67d0d53d2a138f9e29d8acdabe11310c185e36f0a848efa104d4e40b808e4"}, - {file = "zstandard-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:39b2853efc9403927f9065cc48c9980649462acbdf81cd4f0cb773af2fd734bc"}, - {file = "zstandard-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8a1b2effa96a5f019e72874969394edd393e2fbd6414a8208fea363a22803b45"}, - {file = "zstandard-0.22.0-cp312-cp312-win32.whl", hash = "sha256:88c5b4b47a8a138338a07fc94e2ba3b1535f69247670abfe422de4e0b344aae2"}, - {file = "zstandard-0.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:de20a212ef3d00d609d0b22eb7cc798d5a69035e81839f549b538eff4105d01c"}, - {file = "zstandard-0.22.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d75f693bb4e92c335e0645e8845e553cd09dc91616412d1d4650da835b5449df"}, - {file = "zstandard-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:36a47636c3de227cd765e25a21dc5dace00539b82ddd99ee36abae38178eff9e"}, - {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68953dc84b244b053c0d5f137a21ae8287ecf51b20872eccf8eaac0302d3e3b0"}, - {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2612e9bb4977381184bb2463150336d0f7e014d6bb5d4a370f9a372d21916f69"}, - {file = "zstandard-0.22.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23d2b3c2b8e7e5a6cb7922f7c27d73a9a615f0a5ab5d0e03dd533c477de23004"}, - {file = "zstandard-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d43501f5f31e22baf822720d82b5547f8a08f5386a883b32584a185675c8fbf"}, - {file = "zstandard-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a493d470183ee620a3df1e6e55b3e4de8143c0ba1b16f3ded83208ea8ddfd91d"}, - {file = "zstandard-0.22.0-cp38-cp38-win32.whl", hash = "sha256:7034d381789f45576ec3f1fa0e15d741828146439228dc3f7c59856c5bcd3292"}, - {file = "zstandard-0.22.0-cp38-cp38-win_amd64.whl", hash = "sha256:d8fff0f0c1d8bc5d866762ae95bd99d53282337af1be9dc0d88506b340e74b73"}, - {file = "zstandard-0.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2fdd53b806786bd6112d97c1f1e7841e5e4daa06810ab4b284026a1a0e484c0b"}, - {file = "zstandard-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73a1d6bd01961e9fd447162e137ed949c01bdb830dfca487c4a14e9742dccc93"}, - {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9501f36fac6b875c124243a379267d879262480bf85b1dbda61f5ad4d01b75a3"}, - {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48f260e4c7294ef275744210a4010f116048e0c95857befb7462e033f09442fe"}, - {file = "zstandard-0.22.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959665072bd60f45c5b6b5d711f15bdefc9849dd5da9fb6c873e35f5d34d8cfb"}, - {file = "zstandard-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d22fdef58976457c65e2796e6730a3ea4a254f3ba83777ecfc8592ff8d77d303"}, - {file = "zstandard-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7ccf5825fd71d4542c8ab28d4d482aace885f5ebe4b40faaa290eed8e095a4c"}, - {file = "zstandard-0.22.0-cp39-cp39-win32.whl", hash = "sha256:f058a77ef0ece4e210bb0450e68408d4223f728b109764676e1a13537d056bb0"}, - {file = "zstandard-0.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:e9e9d4e2e336c529d4c435baad846a181e39a982f823f7e4495ec0b0ec8538d2"}, - {file = "zstandard-0.22.0.tar.gz", hash = "sha256:8226a33c542bcb54cd6bd0a366067b610b41713b64c9abec1bc4533d69f51e70"}, + {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, + {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, + {file = "zstandard-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc"}, + {file = "zstandard-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573"}, + {file = "zstandard-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391"}, + {file = "zstandard-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e"}, + {file = "zstandard-0.23.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0"}, + {file = "zstandard-0.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c"}, + {file = "zstandard-0.23.0-cp310-cp310-win32.whl", hash = "sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813"}, + {file = "zstandard-0.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4"}, + {file = "zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e"}, + {file = "zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23"}, + {file = "zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a"}, + {file = "zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db"}, + {file = "zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2"}, + {file = "zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca"}, + {file = "zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78"}, + {file = "zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473"}, + {file = "zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160"}, + {file = "zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0"}, + {file = "zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094"}, + {file = "zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8"}, + {file = "zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1"}, + {file = "zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072"}, + {file = "zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20"}, + {file = "zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373"}, + {file = "zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90"}, + {file = "zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35"}, + {file = "zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d"}, + {file = "zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b"}, + {file = "zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9"}, + {file = "zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a"}, + {file = "zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2"}, + {file = "zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5"}, + {file = "zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f"}, + {file = "zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed"}, + {file = "zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057"}, + {file = "zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33"}, + {file = "zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd"}, + {file = "zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b"}, + {file = "zstandard-0.23.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2ef3775758346d9ac6214123887d25c7061c92afe1f2b354f9388e9e4d48acfc"}, + {file = "zstandard-0.23.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4051e406288b8cdbb993798b9a45c59a4896b6ecee2f875424ec10276a895740"}, + {file = "zstandard-0.23.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2d1a054f8f0a191004675755448d12be47fa9bebbcffa3cdf01db19f2d30a54"}, + {file = "zstandard-0.23.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f83fa6cae3fff8e98691248c9320356971b59678a17f20656a9e59cd32cee6d8"}, + {file = "zstandard-0.23.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32ba3b5ccde2d581b1e6aa952c836a6291e8435d788f656fe5976445865ae045"}, + {file = "zstandard-0.23.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f146f50723defec2975fb7e388ae3a024eb7151542d1599527ec2aa9cacb152"}, + {file = "zstandard-0.23.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bfe8de1da6d104f15a60d4a8a768288f66aa953bbe00d027398b93fb9680b26"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:29a2bc7c1b09b0af938b7a8343174b987ae021705acabcbae560166567f5a8db"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:61f89436cbfede4bc4e91b4397eaa3e2108ebe96d05e93d6ccc95ab5714be512"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:53ea7cdc96c6eb56e76bb06894bcfb5dfa93b7adcf59d61c6b92674e24e2dd5e"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:a4ae99c57668ca1e78597d8b06d5af837f377f340f4cce993b551b2d7731778d"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:379b378ae694ba78cef921581ebd420c938936a153ded602c4fea612b7eaa90d"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:50a80baba0285386f97ea36239855f6020ce452456605f262b2d33ac35c7770b"}, + {file = "zstandard-0.23.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:61062387ad820c654b6a6b5f0b94484fa19515e0c5116faf29f41a6bc91ded6e"}, + {file = "zstandard-0.23.0-cp38-cp38-win32.whl", hash = "sha256:b8c0bd73aeac689beacd4e7667d48c299f61b959475cdbb91e7d3d88d27c56b9"}, + {file = "zstandard-0.23.0-cp38-cp38-win_amd64.whl", hash = "sha256:a05e6d6218461eb1b4771d973728f0133b2a4613a6779995df557f70794fd60f"}, + {file = "zstandard-0.23.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa014d55c3af933c1315eb4bb06dd0459661cc0b15cd61077afa6489bec63bb"}, + {file = "zstandard-0.23.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7f0804bb3799414af278e9ad51be25edf67f78f916e08afdb983e74161b916"}, + {file = "zstandard-0.23.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb2b1ecfef1e67897d336de3a0e3f52478182d6a47eda86cbd42504c5cbd009a"}, + {file = "zstandard-0.23.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:837bb6764be6919963ef41235fd56a6486b132ea64afe5fafb4cb279ac44f259"}, + {file = "zstandard-0.23.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1516c8c37d3a053b01c1c15b182f3b5f5eef19ced9b930b684a73bad121addf4"}, + {file = "zstandard-0.23.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48ef6a43b1846f6025dde6ed9fee0c24e1149c1c25f7fb0a0585572b2f3adc58"}, + {file = "zstandard-0.23.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11e3bf3c924853a2d5835b24f03eeba7fc9b07d8ca499e247e06ff5676461a15"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2fb4535137de7e244c230e24f9d1ec194f61721c86ebea04e1581d9d06ea1269"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c24f21fa2af4bb9f2c492a86fe0c34e6d2c63812a839590edaf177b7398f700"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a8c86881813a78a6f4508ef9daf9d4995b8ac2d147dcb1a450448941398091c9"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fe3b385d996ee0822fd46528d9f0443b880d4d05528fd26a9119a54ec3f91c69"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:82d17e94d735c99621bf8ebf9995f870a6b3e6d14543b99e201ae046dfe7de70"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c7c517d74bea1a6afd39aa612fa025e6b8011982a0897768a2f7c8ab4ebb78a2"}, + {file = "zstandard-0.23.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fd7e0f1cfb70eb2f95a19b472ee7ad6d9a0a992ec0ae53286870c104ca939e5"}, + {file = "zstandard-0.23.0-cp39-cp39-win32.whl", hash = "sha256:43da0f0092281bf501f9c5f6f3b4c975a8a0ea82de49ba3f7100e64d422a1274"}, + {file = "zstandard-0.23.0-cp39-cp39-win_amd64.whl", hash = "sha256:f8346bfa098532bc1fb6c7ef06783e969d87a99dd1d2a5a18a892c1d7a643c58"}, + {file = "zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09"}, ] [package.dependencies] @@ -3600,4 +3648,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "9d57d1d6effa84ee717c88ef05d15077e5716490f2f02fa478d81bbca892f668" +content-hash = "d6073ff3291a2395139e6bd413ca2e35204818e7095dd6d4dcaca410379fb644" diff --git a/pyproject.toml b/pyproject.toml index dd296ed721..f6e014f023 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ gunicorn = "^22.0.0" pika = "^1.3.2" pyyaml = "^6.0.1" requests = "^2.32.0" -sdc-cryptography = "^1.2.0" +sdc-cryptography = "^1.2.1" structlog = "^24.1.0" ua-parser = "^0.18.0" blinker = "^1.7.0" From 25fe3986045066793cc12e1d0e5b2a6cac5e0273 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:19:34 +0100 Subject: [PATCH 442/567] Add list update fix for repeating sections (#1438) --- app/questionnaire/questionnaire_schema.py | 13 +- ...alculated_summary_dependent_questions.json | 300 +++++++++++++++++- .../test_questionnaire_schema.py | 10 +- .../test_questionnaire_calculated_summary.py | 31 ++ 4 files changed, 340 insertions(+), 14 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 2ba5cacd03..1eccc383e1 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -505,7 +505,9 @@ def _update_dependencies_for_calculated_summary_dependency( ) dependent = self._get_dependent_for_block_id(block_id=dependent_block["id"]) for answer_id in calculated_summary_answer_ids: - if list_name := self.get_list_name_for_answer_id(answer_id): + if list_name := self.get_list_name_for_answer_id( + answer_id, value_source_update=False + ): # dynamic/repeating answers means the calculated summary also depends on the list those answers loop over self._list_dependencies_map[list_name].add(dependent) self._answer_dependencies_map[answer_id].add(dependent) @@ -859,9 +861,12 @@ def is_repeating_answer( or self.is_answer_dynamic(answer_id) ) - def get_list_name_for_answer_id(self, answer_id: str) -> str | None: + def get_list_name_for_answer_id( + self, answer_id: str, value_source_update: bool = True + ) -> str | None: """ - if the answer is dynamic or in a repeating block or section, return the name of the list it repeats over, otherwise None. + if the answer is updated for calculated summary value source, return the name of the list, if updated for calculated summary dependency and the answer + is part of a repeating section, return None. """ # Type ignore: safe to assume block exists, same for section below. block: ImmutableDict = self.get_block_for_answer_id(answer_id) # type: ignore @@ -872,7 +877,7 @@ def get_list_name_for_answer_id(self, answer_id: str) -> str | None: return self.list_names_by_list_repeating_block_id[block_id] if self.is_answer_in_list_collector_block(answer_id): return block["for_list"] # type: ignore - if self.is_answer_in_repeating_section(answer_id): + if self.is_answer_in_repeating_section(answer_id) and value_source_update: section_id: str = self.get_section_id_for_block_id(block_id) # type: ignore return self.get_repeating_list_for_section(section_id) diff --git a/schemas/test/en/test_calculated_summary_dependent_questions.json b/schemas/test/en/test_calculated_summary_dependent_questions.json index 15617ed8c7..2d15fa8d37 100644 --- a/schemas/test/en/test_calculated_summary_dependent_questions.json +++ b/schemas/test/en/test_calculated_summary_dependent_questions.json @@ -7,8 +7,10 @@ "title": "A test schema to demo Calculated Summary", "description": "A schema to showcase Calculated Summary with dependent questions.", "questionnaire_flow": { - "type": "Linear", - "options": {} + "type": "Hub", + "options": { + "required_completed_sections": ["default-section", "list-collector-section"] + } }, "sections": [ { @@ -147,6 +149,300 @@ ] } ] + }, + { + "id": "list-collector-section", + "title": "Additional sites", + "summary": { + "show_on_completion": false, + "collapsible": false, + "items": [ + { + "type": "List", + "for_list": "additional_sites_name", + "title": "What was the business name for this site?", + "item_anchor_answer_id": "business-name", + "item_label": "

      Business name

      ", + "add_link_text": "Add item to this list", + "empty_list_text": "There are no items" + } + ], + "show_non_item_answers": true + }, + "show_on_hub": true, + "groups": [ + { + "id": "list-collector-group", + "blocks": [ + { + "id": "additional-sites-for-your-business", + "type": "ListCollectorDrivingQuestion", + "for_list": "additional_sites_name", + "question": { + "id": "question-driving-further-additional-sites-for-your-business", + "type": "General", + "title": "Did your business have any additional sites that were staffed for a minimum of 20 hours per week or had planned activity for more than a year?", + "answers": [ + { + "id": "additional-sites-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock", + "params": { + "block_id": "add-block-business-name-trading-style-and-address-for-this-additional-site", + "list_name": "additional_sites_name" + } + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "routing_rules": [ + { + "section": "End", + "when": { + "in": [ + { + "source": "answers", + "identifier": "additional-sites-answer" + }, + ["No"] + ] + } + }, + { + "block": "further-additional-sites-for-your-business" + } + ], + "page_title": "Additional sites for your business" + }, + { + "id": "further-additional-sites-for-your-business", + "type": "ListCollector", + "page_title": "Further additional sites for your business", + "for_list": "additional_sites_name", + "question": { + "id": "list-collector-question-further-additional-sites-for-your-business", + "type": "General", + "title": "Did you have any other sites that were staffed for a minimum of 20 hours per week or had planned activity for more than a year?", + "answers": [ + { + "id": "any-other-additional-sites-answer", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RedirectToListAddBlock" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + }, + "add_block": { + "id": "add-block-business-name-trading-style-and-address-for-this-additional-site", + "type": "ListAddQuestion", + "page_title": "Business name, trading style and address for this additional site", + "cancel_text": "Don’t need to add this item", + "question": { + "id": "add-block-question-business-name-trading-style-and-address-for-this-additional-site", + "type": "General", + "title": "What was the business name for this site?", + "answers": [ + { + "id": "business-name", + "mandatory": true, + "type": "TextField", + "label": "Business name" + } + ] + } + }, + "edit_block": { + "id": "edit-block-business-name-trading-style-and-address-for-this-additional-site", + "type": "ListEditQuestion", + "page_title": "Business name, trading style and address for this additional site", + "cancel_text": "Don’t need to edit this item", + "question": { + "id": "edit-block-question-business-name-trading-style-and-address-for-this-additional-site", + "type": "General", + "title": "What was the business name, trading style and address for this site?", + "answers": [ + { + "id": "business-name", + "mandatory": true, + "type": "TextField", + "label": "Business name" + } + ] + } + }, + "remove_block": { + "id": "remove-block-business-name-trading-style-and-address-for-this-additional-site", + "type": "ListRemoveQuestion", + "cancel_text": "Don’t need to remove this item?", + "question": { + "id": "remove-block-question-business-name-trading-style-and-address-for-this-additional-site", + "type": "General", + "title": "Are you sure you want to remove this item?", + "warning": "All of the information about this item will be deleted", + "answers": [ + { + "id": "remove-confirmation-business-name-trading-style-and-address-for-this-additional-site", + "mandatory": true, + "type": "Radio", + "options": [ + { + "label": "Yes", + "value": "Yes", + "action": { + "type": "RemoveListItemAndAnswers" + } + }, + { + "label": "No", + "value": "No" + } + ] + } + ] + } + }, + "summary": { + "title": "Summary", + "item_title": { + "text": "{listcollector_summary_placeholder}", + "placeholders": [ + { + "placeholder": "listcollector_summary_placeholder", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "business-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + } + } + ] + } + ] + }, + { + "id": "repeating-section", + "title": "Additional site details", + "repeat": { + "for_list": "additional_sites_name", + "title": { + "text": "{repeat_title_placeholder}", + "placeholders": [ + { + "placeholder": "repeat_title_placeholder", + "transforms": [ + { + "arguments": { + "delimiter": " ", + "list_to_concatenate": [ + { + "source": "answers", + "identifier": "business-name" + } + ] + }, + "transform": "concatenate_list" + } + ] + } + ] + } + }, + "summary": { + "show_on_completion": false, + "page_title": "Details of Additional Sites in Great Britain", + "collapsible": false + }, + "show_on_hub": true, + "groups": [ + { + "id": "repeating-group", + "blocks": [ + { + "id": "number-of-employees-working-at-this-additional-site", + "type": "Question", + "question": { + "id": "question-number-of-employees-working-at-this-additional-site", + "title": "What was the number of full-time and part-time employees that your business paid from its payroll, for this site?", + "type": "General", + "answers": [ + { + "id": "number-full-time-employees", + "mandatory": true, + "type": "Number", + "label": "Number of full-time employees", + "decimal_places": 0 + }, + { + "id": "number-part-time-employees", + "mandatory": true, + "type": "Number", + "label": "Number of part-time employees", + "decimal_places": 0 + } + ] + }, + "page_title": "Number of employees working at this additional site" + }, + { + "id": "calculated-number-of-employees-for-this-additional-site", + "type": "CalculatedSummary", + "page_title": "Calculated number of employees for this additional site", + "title": "We have calculated the total number of employees that your business paid from its payroll, for this site, to be %(total)s. Is this correct?", + "calculation": { + "operation": { + "+": [ + { + "identifier": "number-full-time-employees", + "source": "answers" + }, + { + "identifier": "number-part-time-employees", + "source": "answers" + } + ] + }, + "title": "Total number of employees paid from your business's payroll" + } + } + ] + } + ] } ], "theme": "default", diff --git a/tests/app/questionnaire/test_questionnaire_schema.py b/tests/app/questionnaire/test_questionnaire_schema.py index 08719b8189..a31804804e 100644 --- a/tests/app/questionnaire/test_questionnaire_schema.py +++ b/tests/app/questionnaire/test_questionnaire_schema.py @@ -1022,18 +1022,13 @@ def test_grand_calculated_summary_dependencies(): schema = load_schema_from_name( "test_grand_calculated_summary_inside_repeating_section" ) - gcs = Dependent( - section_id="vehicle-details-section", - block_id="grand-calculated-summary-vehicle", - for_list="vehicles", - ) + gcs_dependent = Dependent( section_id="vehicle-details-section", block_id="gcs-breakdown-block", for_list="vehicles", ) - assert gcs in schema.list_dependencies["costs"] - assert gcs in schema.list_dependencies["vehicles"] + assert gcs_dependent in schema.list_dependencies["costs"] assert gcs_dependent in schema.list_dependencies["vehicles"] gcs_answers = [ @@ -1043,7 +1038,6 @@ def test_grand_calculated_summary_dependencies(): "vehicle-fuel-cost", ] for answer in gcs_answers: - assert gcs in schema.answer_dependencies[answer] assert gcs_dependent in schema.answer_dependencies[answer] diff --git a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py index c73f5f5b9b..e19f7cb356 100644 --- a/tests/integration/questionnaire/test_questionnaire_calculated_summary.py +++ b/tests/integration/questionnaire/test_questionnaire_calculated_summary.py @@ -450,3 +450,34 @@ def test_placeholders_rendering_in_calculated_summary_unhappy_path(self): "We have calculated your total in-house expenditure on R&D for Integration Testing for the period 1 April 2016 to 30 April 2016 to be £110. " "Is this correct?" ) + + def test_calculated_summary_repeating_sections_complete_after_adding_list_item( + self, + ): + self.launchSurveyV2(schema_name="test_calculated_summary_dependent_questions") + + self.post({"answer-1": "100"}) + self.post({"answer-2": "100"}) + self.post({"answer-3": "100"}) + self.post({"answer-4": "100"}) + self.post() + self.post({"additional-sites-answer": "Yes"}) + self.post({"business-name": "Ebay"}) + self.post({"any-other-additional-sites-answer": "No"}) + self.post() + self.assertInUrl("/number-of-employees-working-at-this-additional-site") + self.post( + {"number-full-time-employees": "1", "number-part-time-employees": "1"} + ) + self.post() + self.get("/questionnaire/sections/list-collector-section/") + self.post({"any-other-additional-sites-answer": "No"}) + self.assertInBody("Completed") + self.get( + "/questionnaire/additional_sites_name/add-block-business-name-trading-style-and-address-for-this-additional-site/" + ) + self.post({"business-name": "Amazon"}) + self.post({"any-other-additional-sites-answer": "No"}) + self.assertInUrl("/questionnaire") + self.assertInBody("Completed") + self.assertNotInBody("Partially completed") From 1a74d2fda2be6974cebc1353ac4f6b99aa5ae639 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:27:45 +0100 Subject: [PATCH 443/567] Update Runner to open prepop schemas with version v2 (#1414) --- app/authentication/auth_payload_versions.py | 1 + app/utilities/supplementary_data_parser.py | 7 ++++++- tests/app/parser/test_supplementary_data_parser.py | 6 ++++-- .../spec/features/hub_and_spoke/hub_and_spoke.spec.js | 2 +- .../supplementary_data/supplementary_data.spec.js | 8 ++++---- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/authentication/auth_payload_versions.py b/app/authentication/auth_payload_versions.py index f467b7e118..df71b6af64 100644 --- a/app/authentication/auth_payload_versions.py +++ b/app/authentication/auth_payload_versions.py @@ -6,4 +6,5 @@ class AuthPayloadVersion(Enum): class SupplementaryDataSchemaVersion(Enum): + V2 = "v2" V1 = "v1" diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 766cdd4faa..a23d59d5b0 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -36,7 +36,12 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): identifier = VALIDATORS["string"](validate=validate.Length(min=1)) schema_version = VALIDATORS["string"]( - validate=validate.OneOf([SupplementaryDataSchemaVersion.V1.value]) + validate=validate.OneOf( + [ + SupplementaryDataSchemaVersion.V1.value, + SupplementaryDataSchemaVersion.V2.value, + ] + ) ) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) diff --git a/tests/app/parser/test_supplementary_data_parser.py b/tests/app/parser/test_supplementary_data_parser.py index cac6f0f75f..7442d79d2f 100644 --- a/tests/app/parser/test_supplementary_data_parser.py +++ b/tests/app/parser/test_supplementary_data_parser.py @@ -177,7 +177,7 @@ def test_validate_supplementary_data_invalid_schema_version(): "survey_id": "123", "some_field": "value", "data": { - "schema_version": "v2", + "schema_version": "v3", "identifier": "12345678901", }, } @@ -190,7 +190,9 @@ def test_validate_supplementary_data_invalid_schema_version(): survey_id="123", ) - assert str(error.value) == "{'data': {'schema_version': ['Must be one of: v1.']}}" + assert ( + str(error.value) == "{'data': {'schema_version': ['Must be one of: v1, v2.']}}" + ) def test_validate_supplementary_data_payload_missing_identifier_in_items(): diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js index 534384d40f..5c350f041d 100644 --- a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js @@ -320,7 +320,7 @@ describe("Feature: Hub and Spoke", () => { await browser.openQuestionnaire("test_hub_section_required_with_repeat_supplementary.json.json", { version: "v2", - sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", + sdsDatasetId: "203b2f9d-c500-8175-98db-86ffcfdccfa3", responseId, }); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index e9539e0788..dbb1344588 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -42,7 +42,7 @@ describe("Using supplementary data", () => { before("Starting the survey", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", + sdsDatasetId: "203b2f9d-c500-8175-98db-86ffcfdccfa3", responseId, }); }); @@ -341,7 +341,7 @@ describe("Using supplementary data", () => { it("Given I relaunch the survey with new supplementary data and new list items for the repeating section, When I open the Hub page, Then I see the new supplementary list items as new incomplete sections and not any old ones", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "1f61750f-51dd-e933-0df9-40261b06d93f", + sdsDatasetId: "3bb41d29-4daa-9520-82f0-cae365f390c6", responseId, }); await expect(await $(HubPage.summaryItems("section-4-1")).getText()).toContain("Harry Potter"); @@ -391,7 +391,7 @@ describe("Using supplementary data", () => { await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "d8afa921-1305-d553-d2c6-955a6db2cc2d", + sdsDatasetId: "203b2f9d-c500-8175-98db-86ffcfdccfa3", responseId, }); await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Partially completed"); @@ -401,7 +401,7 @@ describe("Using supplementary data", () => { it("Given I return to the new data resulting in a new incomplete section, When I start the section, Then I see the new supplementary data piped in accordingly", async () => { await browser.openQuestionnaire("test_supplementary_data.json", { version: "v2", - sdsDatasetId: "1f61750f-51dd-e933-0df9-40261b06d93f", + sdsDatasetId: "3bb41d29-4daa-9520-82f0-cae365f390c6", responseId, }); await click(HubPage.submit()); From b8e1febdff60538dc6b6798194ac12028ab60e92 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:58:07 +0100 Subject: [PATCH 444/567] Schema v5.9.0 (#1451) Signed-off-by: Yuyutsu Rai --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 4a62af8428..7094538d68 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.8.0 +v5.9.0 From cce61af16babc583670e74d35f33730ee245b598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:22:05 +0100 Subject: [PATCH 445/567] Bump the production-dependencies group across 1 directory with 3 updates (#1450) --- poetry.lock | 32 ++++++++++++++++---------------- pyproject.toml | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/poetry.lock b/poetry.lock index ac2c8d0de3..9509b7b7fe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "astroid" @@ -126,17 +126,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.147" +version = "1.34.151" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.147-py3-none-any.whl", hash = "sha256:e1cef9a1a301866bcdee32ae0c699465eb2345f9a8e613a5835821430165ff6d"}, - {file = "boto3-1.34.147.tar.gz", hash = "sha256:9ec1c6ab22588242a47549f51a63dfc7c21fdf95a94820fc6e629ab060c38bd9"}, + {file = "boto3-1.34.151-py3-none-any.whl", hash = "sha256:35bc76faacf1667d3fbb66c1966acf2230ef26206557efc26d9d9d79337bef43"}, + {file = "boto3-1.34.151.tar.gz", hash = "sha256:30498a76b6f651ee2af7ae8edc1704379279ab8b91f1a8dd1f4ddf51259b0bc2"}, ] [package.dependencies] -botocore = ">=1.34.147,<1.35.0" +botocore = ">=1.34.151,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -145,13 +145,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.147" +version = "1.34.151" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.147-py3-none-any.whl", hash = "sha256:be94a2f4874b1d1705cae2bd512c475047497379651678593acb6c61c50d91de"}, - {file = "botocore-1.34.147.tar.gz", hash = "sha256:2e8f000b77e4ca345146cb2edab6403769a517b564f627bb084ab335417f3dbe"}, + {file = "botocore-1.34.151-py3-none-any.whl", hash = "sha256:9018680d7d4a8060c26d127ceec5ab5b270879f423ea39b863d8a46f3e34c404"}, + {file = "botocore-1.34.151.tar.gz", hash = "sha256:0d0968e427a94378f295b49d59170dad539938487ec948de3d030f06092ec6dc"}, ] [package.dependencies] @@ -1235,13 +1235,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.22.0" +version = "2.23.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.22.0-py2.py3-none-any.whl", hash = "sha256:229bf60a3835c1bb21ee36c7d4368b111097678b8ed25d3fbc5e639a1d03388d"}, - {file = "google_cloud_pubsub-2.22.0.tar.gz", hash = "sha256:a4c2b1a5ca2c0b32c8d3776c85f498266c3d79696696ea67010c857b45af17d8"}, + {file = "google_cloud_pubsub-2.23.0-py2.py3-none-any.whl", hash = "sha256:d341b2df8b105d0e3774b4bc9173bc0cf26bced31a4736cd9eefa33453b75dff"}, + {file = "google_cloud_pubsub-2.23.0.tar.gz", hash = "sha256:cf3d6f2ab11b5c8dfc0aa7d4cae5ee1d66b408d9666f1762c9c17e269cd8b658"}, ] [package.dependencies] @@ -2622,17 +2622,17 @@ files = [ [[package]] name = "redis" -version = "5.0.7" +version = "5.0.8" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, - {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, + {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"}, + {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"}, ] [package.extras] -hiredis = ["hiredis (>=1.0.0)"] +hiredis = ["hiredis (>1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] [[package]] @@ -3648,4 +3648,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "d6073ff3291a2395139e6bd413ca2e35204818e7095dd6d4dcaca410379fb644" +content-hash = "4b5fccf206ffde4c54047db91aae7ac33220bb1f157fb768e4938219dcf01269" diff --git a/pyproject.toml b/pyproject.toml index f6e014f023..034883ea79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,21 +69,21 @@ sdc-cryptography = "^1.2.1" structlog = "^24.1.0" ua-parser = "^0.18.0" blinker = "^1.7.0" -boto3 = "^1.34.127" +boto3 = "^1.34.151" humanize = "^4.9.0" flask-talisman = "^1.1.0" marshmallow = "^3.21.3" python-snappy = "^0.7.1" google-cloud-storage = "^2.17.0" jsonpointer = "^3.0" -redis = "^5.0.6" +redis = "^5.0.8" flask-compress = "^1.14" htmlmin = "^0.1.12" coloredlogs = "^15.0.1" uwsgi = "^2.0.24" email-validator = "^2.1.2" itsdangerous = "^2.1.2" -google-cloud-pubsub = "^2.21.3" +google-cloud-pubsub = "^2.23.0" google-cloud-tasks = "^2.16.3" simplejson = "^3.19.2" markupsafe = "^2.1.5" From 7085ca36e61db40ca404b2b089b5505d884ed0a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 08:32:37 +0100 Subject: [PATCH 446/567] Bump the development-dependencies group across 1 directory with 4 updates (#1452) --- poetry.lock | 76 +++++++++++++++++++++++++------------------------- pyproject.toml | 8 +++--- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9509b7b7fe..f5a9beafb9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -860,13 +860,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.23.3" +version = "2.23.4" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.23.3-py3-none-any.whl", hash = "sha256:4779be828f4ebf53e1a286fd11e2ffe0f510d3e5507f143d644e67a07387d759"}, - {file = "fakeredis-2.23.3.tar.gz", hash = "sha256:0c67caa31530114f451f012eca920338c5eb83fa7f1f461dd41b8d2488a99cba"}, + {file = "fakeredis-2.23.4-py3-none-any.whl", hash = "sha256:da592c68c91a14447327bc179f5d124cc2df4f3c864c2de551a4785edde78276"}, + {file = "fakeredis-2.23.4.tar.gz", hash = "sha256:fabfa9ce940f568ee2f1a7cb222c3f45f05035b335fd5979523bbab4c10ff4a0"}, ] [package.dependencies] @@ -2037,38 +2037,38 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.11.0" +version = "1.11.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3824187c99b893f90c845bab405a585d1ced4ff55421fdf5c84cb7710995229"}, - {file = "mypy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96f8dbc2c85046c81bcddc246232d500ad729cb720da4e20fce3b542cab91287"}, - {file = "mypy-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a5d8d8dd8613a3e2be3eae829ee891b6b2de6302f24766ff06cb2875f5be9c6"}, - {file = "mypy-1.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72596a79bbfb195fd41405cffa18210af3811beb91ff946dbcb7368240eed6be"}, - {file = "mypy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:35ce88b8ed3a759634cb4eb646d002c4cef0a38f20565ee82b5023558eb90c00"}, - {file = "mypy-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:98790025861cb2c3db8c2f5ad10fc8c336ed2a55f4daf1b8b3f877826b6ff2eb"}, - {file = "mypy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25bcfa75b9b5a5f8d67147a54ea97ed63a653995a82798221cca2a315c0238c1"}, - {file = "mypy-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bea2a0e71c2a375c9fa0ede3d98324214d67b3cbbfcbd55ac8f750f85a414e3"}, - {file = "mypy-1.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2b3d36baac48e40e3064d2901f2fbd2a2d6880ec6ce6358825c85031d7c0d4d"}, - {file = "mypy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:d8e2e43977f0e09f149ea69fd0556623919f816764e26d74da0c8a7b48f3e18a"}, - {file = "mypy-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d44c1e44a8be986b54b09f15f2c1a66368eb43861b4e82573026e04c48a9e20"}, - {file = "mypy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cea3d0fb69637944dd321f41bc896e11d0fb0b0aa531d887a6da70f6e7473aba"}, - {file = "mypy-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a83ec98ae12d51c252be61521aa5731f5512231d0b738b4cb2498344f0b840cd"}, - {file = "mypy-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7b73a856522417beb78e0fb6d33ef89474e7a622db2653bc1285af36e2e3e3d"}, - {file = "mypy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:f2268d9fcd9686b61ab64f077be7ffbc6fbcdfb4103e5dd0cc5eaab53a8886c2"}, - {file = "mypy-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:940bfff7283c267ae6522ef926a7887305945f716a7704d3344d6d07f02df850"}, - {file = "mypy-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:14f9294528b5f5cf96c721f231c9f5b2733164e02c1c018ed1a0eff8a18005ac"}, - {file = "mypy-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7b54c27783991399046837df5c7c9d325d921394757d09dbcbf96aee4649fe9"}, - {file = "mypy-1.11.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65f190a6349dec29c8d1a1cd4aa71284177aee5949e0502e6379b42873eddbe7"}, - {file = "mypy-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:dbe286303241fea8c2ea5466f6e0e6a046a135a7e7609167b07fd4e7baf151bf"}, - {file = "mypy-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:104e9c1620c2675420abd1f6c44bab7dd33cc85aea751c985006e83dcd001095"}, - {file = "mypy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f006e955718ecd8d159cee9932b64fba8f86ee6f7728ca3ac66c3a54b0062abe"}, - {file = "mypy-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:becc9111ca572b04e7e77131bc708480cc88a911adf3d0239f974c034b78085c"}, - {file = "mypy-1.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6801319fe76c3f3a3833f2b5af7bd2c17bb93c00026a2a1b924e6762f5b19e13"}, - {file = "mypy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1a184c64521dc549324ec6ef7cbaa6b351912be9cb5edb803c2808a0d7e85ac"}, - {file = "mypy-1.11.0-py3-none-any.whl", hash = "sha256:56913ec8c7638b0091ef4da6fcc9136896914a9d60d54670a75880c3e5b99ace"}, - {file = "mypy-1.11.0.tar.gz", hash = "sha256:93743608c7348772fdc717af4aeee1997293a1ad04bc0ea6efa15bf65385c538"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [package.dependencies] @@ -2410,13 +2410,13 @@ files = [ [[package]] name = "pytest" -version = "8.3.1" +version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, - {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] @@ -3240,13 +3240,13 @@ files = [ [[package]] name = "types-redis" -version = "4.6.0.20240425" +version = "4.6.0.20240726" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.20240425.tar.gz", hash = "sha256:9402a10ee931d241fdfcc04592ebf7a661d7bb92a8dea631279f0d8acbcf3a22"}, - {file = "types_redis-4.6.0.20240425-py3-none-any.whl", hash = "sha256:ac5bc19e8f5997b9e76ad5d9cf15d0392d9f28cf5fc7746ea4a64b989c45c6a8"}, + {file = "types-redis-4.6.0.20240726.tar.gz", hash = "sha256:de2aefcf7afe80057debada8c540463d06c8863de50b8016bd369ccdbcb59b5e"}, + {file = "types_redis-4.6.0.20240726-py3-none-any.whl", hash = "sha256:233062b7120a9908532ec9163d17af74b80fa49a89d510444cad4cac42717378"}, ] [package.dependencies] @@ -3648,4 +3648,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "4b5fccf206ffde4c54047db91aae7ac33220bb1f157fb768e4938219dcf01269" +content-hash = "6bfcaa800aaa897b927e819c2c749b50c80d7fe795e743589af7d62f8eac2d8a" diff --git a/pyproject.toml b/pyproject.toml index 034883ea79..98deeb8d6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,15 +33,15 @@ flake8-datetimez = "^20.10.0" moto = "^5.0.8" freezegun = "^1.4.0" pytest-xdist = "^3.5.0" -fakeredis = "^2.21.3" -mypy = "^1.9.0" +fakeredis = "^2.23.4" +mypy = "^1.11.1" pytest-flask = "^1.3.0" -pytest = "^8.1.2" +pytest = "^8.3.2" pytest-sugar = "^1.0.0" responses = "^0.25.0" types-simplejson = "^3.19.0.20240310" types-requests = "^2.31.0.20240406" -types-redis = "^4.6.0.20240425" +types-redis = "^4.6.0.20240726" types-PyYAML = "^6.0.12.20240311" types-python-dateutil = "^2.8.19.20240311" pytest-mock = "^3.12.0" From 86c3b9621a477c15b8157ec62d3c1163b0dbb81d Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:31:42 +0100 Subject: [PATCH 447/567] Investigate flake8-quotes (#1430) --- .github/dependabot.yaml | 2 - .pylintrc | 8 +- app/data_models/progress_store.py | 3 +- app/questionnaire/placeholder_parser.py | 4 +- app/questionnaire/placeholder_renderer.py | 4 +- app/questionnaire/value_source_resolver.py | 2 +- app/views/contexts/hub_context.py | 4 +- poetry.lock | 538 ++++++++------------- pyproject.toml | 4 +- setup.cfg | 6 +- 10 files changed, 227 insertions(+), 348 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index bab096f401..59c374bce8 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -62,8 +62,6 @@ updates: # Temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 - dependency-name: "babel" update-types: ["version-update:semver-major", "version-update:semver-minor"] - - dependency-name: "pylint*" - update-types: [ "version-update:semver-major" ] - dependency-name: "pytest*" update-types: ["version-update:semver-minor"] - dependency-name: "flake8*" diff --git a/.pylintrc b/.pylintrc index 4308bddeef..79934a0cb4 100644 --- a/.pylintrc +++ b/.pylintrc @@ -77,7 +77,7 @@ limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. -load-plugins=pylint.extensions.mccabe,pylint_quotes,pylint.extensions.no_self_use,pylint_absolute_imports +load-plugins=pylint.extensions.mccabe,pylint.extensions.no_self_use,pylint_absolute_imports # Pickle collected data for later comparisons. persistent=yes @@ -335,10 +335,6 @@ single-line-class-stmt=no # else. single-line-if-stmt=no -string-quote=double-avoid-escape -triple-quote=double -docstring-quote=double - [IMPORTS] # List of modules that can be imported at any level, not just the top level @@ -409,7 +405,7 @@ disable= missing-docstring, invalid-name, inconsistent-return-statements, - duplicate-code, + duplicate-code # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where diff --git a/app/data_models/progress_store.py b/app/data_models/progress_store.py index c381d5a98c..7985108b1f 100644 --- a/app/data_models/progress_store.py +++ b/app/data_models/progress_store.py @@ -1,7 +1,6 @@ from typing import Iterable, MutableMapping -from app.data_models import CompletionStatus -from app.data_models.progress import Progress, ProgressDict +from app.data_models.progress import CompletionStatus, Progress, ProgressDict from app.questionnaire.location import Location, SectionKey from app.utilities.types import LocationType diff --git a/app/questionnaire/placeholder_parser.py b/app/questionnaire/placeholder_parser.py index b2d6025898..6e57ea3a03 100644 --- a/app/questionnaire/placeholder_parser.py +++ b/app/questionnaire/placeholder_parser.py @@ -32,8 +32,8 @@ from app.utilities.types import LocationType, SectionKey if TYPE_CHECKING: - from app.questionnaire.placeholder_renderer import ( - PlaceholderRenderer, # pragma: no cover + from app.questionnaire.placeholder_renderer import ( # pragma: no cover + PlaceholderRenderer, ) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index a4506f71c0..afcd146846 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -6,7 +6,9 @@ from app.data_models.answer import AnswerValueTypes from app.data_models.data_stores import DataStores from app.questionnaire import QuestionnaireSchema -from app.questionnaire.placeholder_parser import PlaceholderParser +from app.questionnaire.placeholder_parser import ( # pylint: disable=cyclic-import + PlaceholderParser, +) from app.questionnaire.plural_forms import get_plural_form_key from app.questionnaire.schema_utils import find_pointers_containing from app.utilities.types import LocationType diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 7b903bcb03..defceff9d2 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -15,7 +15,7 @@ from app.data_models.metadata_proxy import NoMetadataException from app.questionnaire import QuestionnaireSchema from app.questionnaire.location import InvalidLocationException, SectionKey -from app.questionnaire.rules import rule_evaluator +from app.questionnaire.rules import rule_evaluator # pylint: disable=cyclic-import from app.utilities.types import LocationType ValueSourceTypes: TypeAlias = None | str | int | Decimal | list | dict diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 5fbf484860..b2e6986f84 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -7,7 +7,9 @@ from app.data_models import CompletionStatus from app.questionnaire.location import SectionKey -from app.views.contexts import Context +from app.views.contexts import Context # pylint: disable=cyclic-import + +# Removing Pylint disable causes linting fail in GHA but not locally this issue has been raised here: https://github.com/pylint-dev/pylint/issues/9168 class HubContext(Context): diff --git a/poetry.lock b/poetry.lock index f5a9beafb9..623ab5ffd6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,19 +2,15 @@ [[package]] name = "astroid" -version = "2.15.8" +version = "3.2.4" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.8.0" files = [ - {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, - {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, + {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, + {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, ] -[package.dependencies] -lazy-object-proxy = ">=1.4.0" -wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""} - [[package]] name = "attrs" version = "23.2.0" @@ -951,6 +947,20 @@ files = [ flake8 = ">=3.0" pycodestyle = "*" +[[package]] +name = "flake8-quotes" +version = "3.4.0" +description = "Flake8 lint for quotes." +optional = false +python-versions = "*" +files = [ + {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, +] + +[package.dependencies] +flake8 = "*" +setuptools = "*" + [[package]] name = "flake8-tuple" version = "0.4.1" @@ -1281,13 +1291,13 @@ tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.16.4" +version = "2.16.5" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-tasks-2.16.4.tar.gz", hash = "sha256:61033c1edd7dc5aa3b9fbe5c8deb64be0c50e0baee9d9465f3f8b75b2cda55b9"}, - {file = "google_cloud_tasks-2.16.4-py2.py3-none-any.whl", hash = "sha256:7db2b364ded9c2e0143b2794d1ae53ef7461a99567698e3e4ba9e02b614e1adb"}, + {file = "google_cloud_tasks-2.16.5-py2.py3-none-any.whl", hash = "sha256:662d3eba82238fd48dee55f780a31f0990e3869b1df04126759a84cd0d83a598"}, + {file = "google_cloud_tasks-2.16.5.tar.gz", hash = "sha256:8bdc294a09e34647e120a2e40dd7ec20873024d5f4e0938b2ff0c39ff374edac"}, ] [package.dependencies] @@ -1502,61 +1512,61 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.65.1" +version = "1.65.2" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.65.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:3dc5f928815b8972fb83b78d8db5039559f39e004ec93ebac316403fe031a062"}, - {file = "grpcio-1.65.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:8333ca46053c35484c9f2f7e8d8ec98c1383a8675a449163cea31a2076d93de8"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7af64838b6e615fff0ec711960ed9b6ee83086edfa8c32670eafb736f169d719"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb64b4166362d9326f7efbf75b1c72106c1aa87f13a8c8b56a1224fac152f5c"}, - {file = "grpcio-1.65.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8422dc13ad93ec8caa2612b5032a2b9cd6421c13ed87f54db4a3a2c93afaf77"}, - {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4effc0562b6c65d4add6a873ca132e46ba5e5a46f07c93502c37a9ae7f043857"}, - {file = "grpcio-1.65.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a6c71575a2fedf259724981fd73a18906513d2f306169c46262a5bae956e6364"}, - {file = "grpcio-1.65.1-cp310-cp310-win32.whl", hash = "sha256:34966cf526ef0ea616e008d40d989463e3db157abb213b2f20c6ce0ae7928875"}, - {file = "grpcio-1.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:ca931de5dd6d9eb94ff19a2c9434b23923bce6f767179fef04dfa991f282eaad"}, - {file = "grpcio-1.65.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:bbb46330cc643ecf10bd9bd4ca8e7419a14b6b9dedd05f671c90fb2c813c6037"}, - {file = "grpcio-1.65.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d827a6fb9215b961eb73459ad7977edb9e748b23e3407d21c845d1d8ef6597e5"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:6e71aed8835f8d9fbcb84babc93a9da95955d1685021cceb7089f4f1e717d719"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1c84560b3b2d34695c9ba53ab0264e2802721c530678a8f0a227951f453462"}, - {file = "grpcio-1.65.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27adee2338d697e71143ed147fe286c05810965d5d30ec14dd09c22479bfe48a"}, - {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f62652ddcadc75d0e7aa629e96bb61658f85a993e748333715b4ab667192e4e8"}, - {file = "grpcio-1.65.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:71a05fd814700dd9cb7d9a507f2f6a1ef85866733ccaf557eedacec32d65e4c2"}, - {file = "grpcio-1.65.1-cp311-cp311-win32.whl", hash = "sha256:b590f1ad056294dfaeac0b7e1b71d3d5ace638d8dd1f1147ce4bd13458783ba8"}, - {file = "grpcio-1.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:12e9bdf3b5fd48e5fbe5b3da382ad8f97c08b47969f3cca81dd9b36b86ed39e2"}, - {file = "grpcio-1.65.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:54cb822e177374b318b233e54b6856c692c24cdbd5a3ba5335f18a47396bac8f"}, - {file = "grpcio-1.65.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aaf3c54419a28d45bd1681372029f40e5bfb58e5265e3882eaf21e4a5f81a119"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:557de35bdfbe8bafea0a003dbd0f4da6d89223ac6c4c7549d78e20f92ead95d9"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8bfd95ef3b097f0cc86ade54eafefa1c8ed623aa01a26fbbdcd1a3650494dd11"}, - {file = "grpcio-1.65.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e6a8f3d6c41e6b642870afe6cafbaf7b61c57317f9ec66d0efdaf19db992b90"}, - {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1faaf7355ceed07ceaef0b9dcefa4c98daf1dd8840ed75c2de128c3f4a4d859d"}, - {file = "grpcio-1.65.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:60f1f38eed830488ad2a1b11579ef0f345ff16fffdad1d24d9fbc97ba31804ff"}, - {file = "grpcio-1.65.1-cp312-cp312-win32.whl", hash = "sha256:e75acfa52daf5ea0712e8aa82f0003bba964de7ae22c26d208cbd7bc08500177"}, - {file = "grpcio-1.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff5a84907e51924973aa05ed8759210d8cdae7ffcf9e44fd17646cf4a902df59"}, - {file = "grpcio-1.65.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:1fbd6331f18c3acd7e09d17fd840c096f56eaf0ef830fbd50af45ae9dc8dfd83"}, - {file = "grpcio-1.65.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de5b6be29116e094c5ef9d9e4252e7eb143e3d5f6bd6d50a78075553ab4930b0"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e4a3cdba62b2d6aeae6027ae65f350de6dc082b72e6215eccf82628e79efe9ba"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941c4869aa229d88706b78187d60d66aca77fe5c32518b79e3c3e03fc26109a2"}, - {file = "grpcio-1.65.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f40cebe5edb518d78b8131e87cb83b3ee688984de38a232024b9b44e74ee53d3"}, - {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2ca684ba331fb249d8a1ce88db5394e70dbcd96e58d8c4b7e0d7b141a453dce9"}, - {file = "grpcio-1.65.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8558f0083ddaf5de64a59c790bffd7568e353914c0c551eae2955f54ee4b857f"}, - {file = "grpcio-1.65.1-cp38-cp38-win32.whl", hash = "sha256:8d8143a3e3966f85dce6c5cc45387ec36552174ba5712c5dc6fcc0898fb324c0"}, - {file = "grpcio-1.65.1-cp38-cp38-win_amd64.whl", hash = "sha256:76e81a86424d6ca1ce7c16b15bdd6a964a42b40544bf796a48da241fdaf61153"}, - {file = "grpcio-1.65.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb5175f45c980ff418998723ea1b3869cce3766d2ab4e4916fbd3cedbc9d0ed3"}, - {file = "grpcio-1.65.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b12c1aa7b95abe73b3e04e052c8b362655b41c7798da69f1eaf8d186c7d204df"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3019fb50128b21a5e018d89569ffaaaa361680e1346c2f261bb84a91082eb3d3"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ae15275ed98ea267f64ee9ddedf8ecd5306a5b5bb87972a48bfe24af24153e8"}, - {file = "grpcio-1.65.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f096ffb881f37e8d4f958b63c74bfc400c7cebd7a944b027357cd2fb8d91a57"}, - {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2f56b5a68fdcf17a0a1d524bf177218c3c69b3947cb239ea222c6f1867c3ab68"}, - {file = "grpcio-1.65.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:941596d419b9736ab548aa0feb5bbba922f98872668847bf0720b42d1d227b9e"}, - {file = "grpcio-1.65.1-cp39-cp39-win32.whl", hash = "sha256:5fd7337a823b890215f07d429f4f193d24b80d62a5485cf88ee06648591a0c57"}, - {file = "grpcio-1.65.1-cp39-cp39-win_amd64.whl", hash = "sha256:1bceeec568372cbebf554eae1b436b06c2ff24cfaf04afade729fb9035408c6c"}, - {file = "grpcio-1.65.1.tar.gz", hash = "sha256:3c492301988cd720cd145d84e17318d45af342e29ef93141228f9cd73222368b"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.65.1)"] + {file = "grpcio-1.65.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:51231a22aea830be1d955de5a15da4391b3ac8e1d7868f362c74c15a0e9f5c89"}, + {file = "grpcio-1.65.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:87da0fb85ba42257e450561b0264e36abe47faae07476621ae65d8f5f60f22cd"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:3a6b36e20b02ca830b15b5eb4abb437de1d42ba93353d1f76b00337108f7ce8e"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03fdd86ff7d9957b822b9bf1fe0ae1e21e258e9c1d5535a5e9c67de0ad45b6a8"}, + {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e5a67bbf8a1b3be5535802f6e9f507d1d8d38fb32de81ec7f03706d95a9126"}, + {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2ce639f2a2951aedbe9a3636f5730288f9b77c2627f116265d7d2789555e5662"}, + {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b10349ceebec745a47e4339ef7c4878c9b53b82ae4b0883e16544625016d6242"}, + {file = "grpcio-1.65.2-cp310-cp310-win32.whl", hash = "sha256:f931fe9b244dc23c7478c513c3ed94ded93da8bd1a95a4d97b21abdef644304a"}, + {file = "grpcio-1.65.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c9c865d2fdf40e7e952038a0b5e0f32b01da84ecf04943b08e8917c8ccc9cf8"}, + {file = "grpcio-1.65.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:f4b7a7d68313e252e09550bd03d9d11e460dae681cf95588a131b6b3e07d1e30"}, + {file = "grpcio-1.65.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ba9d4b3d4fc00b8083bb47a8c40a74ba3ea330713fdd59cf53c926c9a16b002"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b7bfcbee6b32f0e4786b7813692b3907c9e444f529126b8520cac9914479b98c"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aa50787bc8036bd5ea9b7ebbbd2c49c78122eb9ff98d3c217a7c146313c5030"}, + {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd7dc770926cc66050242eb6c63ca8ce12cd69010bf4ff7ea6e721d4f4b11e4d"}, + {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c45977fdc675a8961875adab7f04b785f65d3fd9c737cd60b5e3a9b1392ad444"}, + {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a0cd7297abf0a02a9399edebe8c662058c7f0768bfbe859837707d389ad327f"}, + {file = "grpcio-1.65.2-cp311-cp311-win32.whl", hash = "sha256:60fe2f90875f2bef105158e370fbbefadd179f8cd689bc2cee6844aca4ccb7bb"}, + {file = "grpcio-1.65.2-cp311-cp311-win_amd64.whl", hash = "sha256:e0b2bf34340999c6d938107ec2cc9bce1ea59bf08e4694cfa47e782bdbd361f4"}, + {file = "grpcio-1.65.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:71fa3b7a6cef62a00014205d0e707610cfd50ae54f617d296017f10c6a9fad0d"}, + {file = "grpcio-1.65.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8856187a359a55babfa4d49ad96f2dd7edd8be3a36b813c7a9e41ef3d763400f"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:cb48342de1c3be59e6de79c6bbc01cf05562c571a3ed32f7c2e149e7934824cf"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b193e116e085ad4d7ef1518d79e9fedfa7688f4967f64a6246b5b196a26326a"}, + {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ce7f4c766fecc34455357b31b1e316506ea6ac48abbb9a650843d20337a2036"}, + {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:76125096d2a090d4acdce0f06f9511cebe1bcfbc0bd040e495563d7a8747dda1"}, + {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4fba3ae83ef5acd111c2dd92233ff167411db84e1ff17a00c34b5428355526c5"}, + {file = "grpcio-1.65.2-cp312-cp312-win32.whl", hash = "sha256:7fd639b0988ed5114d4b2a72ea453aafcb1439dd433c61834886b92afed9c6c1"}, + {file = "grpcio-1.65.2-cp312-cp312-win_amd64.whl", hash = "sha256:b6bba0f973ef6fe7434834f1b63d16bab4b50879d5bb0ca6eb0495c87d5cbc78"}, + {file = "grpcio-1.65.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:510bf7ec7f44e9420bb17970fb450522666d8b1c09cdf59b735de0c2dc806b79"}, + {file = "grpcio-1.65.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aacfd499d23130578184057008ea5329732a5ac59a4fcb73c0467d86723d23c8"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:67c5e5aa92b5832ae7a3399bce5b8562fb28686446732bfa17f97d5082e8501d"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7b752471e7ff1472ddbf3035a34fd8e24f2eac4fedbdab311e8f3e0dee889f7"}, + {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3101fa25b93f185e8cc698f8c2abee897891e6bae4f13472f66df21e8ae40d46"}, + {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:01600b1b02fdc9d648630d3de0a4cbf7ebe5f94b40ec1f65e3fd4b94a3b052cf"}, + {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8886d24345bf4b1693e9c09cf6a520f0baedd2af2a876f91bb508b24d0d46041"}, + {file = "grpcio-1.65.2-cp38-cp38-win32.whl", hash = "sha256:0b2ae6868864e4b06bff89cf91730a63141327158bf0677428ef315ea1dbdb0b"}, + {file = "grpcio-1.65.2-cp38-cp38-win_amd64.whl", hash = "sha256:c2900ad06fd8f5ad8832b1ee287caccb4a957e971b2b7983e0cd7a8e7c7098fb"}, + {file = "grpcio-1.65.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:06a7ea12a81e5e2fb17528556c7f828b90bd2aec3a645f5cd5f35f80aa59ac6a"}, + {file = "grpcio-1.65.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5edea0ea18e9fd5326d385a4c92a1fed605454e9a2c57ff131df0a08004b7e69"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d388f093010a014d3b3ddf8185ff45c5279fd825d0b20e21c8076515ae61db31"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5225b8ce980b598187f64436ed95ea149966d538253c28668347d331968e2386"}, + {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892f03939df46d0bfcf89fe1dbcc8818f93ad6f3377587e8db6c2b1f598736c2"}, + {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77fddf42bbca65ee4db679d0608e1ffa8b22b7f516c79665b7620be2f6357c85"}, + {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3a3139414399078560a84203f9fe3592483d902a2af84062c571be6191143a9f"}, + {file = "grpcio-1.65.2-cp39-cp39-win32.whl", hash = "sha256:8d6fd1206433428d0a4ba771eac70579b41a265fe835a4d8a5214c7235e69926"}, + {file = "grpcio-1.65.2-cp39-cp39-win_amd64.whl", hash = "sha256:478725160e2cfc1bfa5ab3e7bb7c896cc182c8f57255d780007cfd6fb46e97b5"}, + {file = "grpcio-1.65.2.tar.gz", hash = "sha256:e2c9bbb84d5517f2bccdb1836b8ee267a1757acb3cb3e575065c103220b577ac"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.65.2)"] [[package]] name = "grpcio-status" @@ -1830,52 +1840,6 @@ files = [ cryptography = ">=3.4" typing-extensions = ">=4.5.0" -[[package]] -name = "lazy-object-proxy" -version = "1.10.0" -description = "A fast and thorough lazy object proxy." -optional = false -python-versions = ">=3.8" -files = [ - {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, - {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, -] - [[package]] name = "markupsafe" version = "2.1.5" @@ -2237,22 +2201,22 @@ testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "4.25.3" +version = "4.25.4" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, - {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, - {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, - {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, - {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, - {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, - {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, - {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, - {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, - {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, + {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, + {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, + {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, + {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, + {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, + {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, + {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, + {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, + {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, ] [[package]] @@ -2332,20 +2296,23 @@ files = [ [[package]] name = "pylint" -version = "2.17.7" +version = "3.2.6" description = "python code static checker" optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.8.0" files = [ - {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, - {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, + {file = "pylint-3.2.6-py3-none-any.whl", hash = "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f"}, + {file = "pylint-3.2.6.tar.gz", hash = "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3"}, ] [package.dependencies] -astroid = ">=2.15.8,<=2.17.0-dev0" +astroid = ">=3.2.4,<=3.3.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""} -isort = ">=4.2.5,<6" +dill = [ + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomlkit = ">=0.10.1" @@ -2383,20 +2350,6 @@ mccabe = ">=0.2" pep8 = ">=1.4.6" pylint = ">=0.28.0" -[[package]] -name = "pylint-quotes" -version = "0.2.3" -description = "Quote consistency checker for PyLint.." -optional = false -python-versions = ">=3.6" -files = [ - {file = "pylint-quotes-0.2.3.tar.gz", hash = "sha256:2d6bb3fa8a1a85af3af8a0ca875a719ac5bcdb735c45756284699d809c109c95"}, - {file = "pylint_quotes-0.2.3-py2.py3-none-any.whl", hash = "sha256:89decd985d3c019314da630f5e3fe0e0df951c2310525fbd6e710bca329c810e"}, -] - -[package.dependencies] -pylint = ">=2.8.0" - [[package]] name = "pyreadline3" version = "3.4.1" @@ -2794,110 +2747,114 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.19.0" +version = "0.19.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:fb37bd599f031f1a6fb9e58ec62864ccf3ad549cf14bac527dbfa97123edcca4"}, - {file = "rpds_py-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3384d278df99ec2c6acf701d067147320b864ef6727405d6470838476e44d9e8"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54548e0be3ac117595408fd4ca0ac9278fde89829b0b518be92863b17ff67a2"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8eb488ef928cdbc05a27245e52de73c0d7c72a34240ef4d9893fdf65a8c1a955"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5da93debdfe27b2bfc69eefb592e1831d957b9535e0943a0ee8b97996de21b5"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79e205c70afddd41f6ee79a8656aec738492a550247a7af697d5bd1aee14f766"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:959179efb3e4a27610e8d54d667c02a9feaa86bbabaf63efa7faa4dfa780d4f1"}, - {file = "rpds_py-0.19.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a6e605bb9edcf010f54f8b6a590dd23a4b40a8cb141255eec2a03db249bc915b"}, - {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9133d75dc119a61d1a0ded38fb9ba40a00ef41697cc07adb6ae098c875195a3f"}, - {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd36b712d35e757e28bf2f40a71e8f8a2d43c8b026d881aa0c617b450d6865c9"}, - {file = "rpds_py-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:354f3a91718489912f2e0fc331c24eaaf6a4565c080e00fbedb6015857c00582"}, - {file = "rpds_py-0.19.0-cp310-none-win32.whl", hash = "sha256:ebcbf356bf5c51afc3290e491d3722b26aaf5b6af3c1c7f6a1b757828a46e336"}, - {file = "rpds_py-0.19.0-cp310-none-win_amd64.whl", hash = "sha256:75a6076289b2df6c8ecb9d13ff79ae0cad1d5fb40af377a5021016d58cd691ec"}, - {file = "rpds_py-0.19.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6d45080095e585f8c5097897313def60caa2046da202cdb17a01f147fb263b81"}, - {file = "rpds_py-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5c9581019c96f865483d031691a5ff1cc455feb4d84fc6920a5ffc48a794d8a"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1540d807364c84516417115c38f0119dfec5ea5c0dd9a25332dea60b1d26fc4d"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e65489222b410f79711dc3d2d5003d2757e30874096b2008d50329ea4d0f88c"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9da6f400eeb8c36f72ef6646ea530d6d175a4f77ff2ed8dfd6352842274c1d8b"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37f46bb11858717e0efa7893c0f7055c43b44c103e40e69442db5061cb26ed34"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:071d4adc734de562bd11d43bd134330fb6249769b2f66b9310dab7460f4bf714"}, - {file = "rpds_py-0.19.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9625367c8955e4319049113ea4f8fee0c6c1145192d57946c6ffcd8fe8bf48dd"}, - {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e19509145275d46bc4d1e16af0b57a12d227c8253655a46bbd5ec317e941279d"}, - {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d438e4c020d8c39961deaf58f6913b1bf8832d9b6f62ec35bd93e97807e9cbc"}, - {file = "rpds_py-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90bf55d9d139e5d127193170f38c584ed3c79e16638890d2e36f23aa1630b952"}, - {file = "rpds_py-0.19.0-cp311-none-win32.whl", hash = "sha256:8d6ad132b1bc13d05ffe5b85e7a01a3998bf3a6302ba594b28d61b8c2cf13aaf"}, - {file = "rpds_py-0.19.0-cp311-none-win_amd64.whl", hash = "sha256:7ec72df7354e6b7f6eb2a17fa6901350018c3a9ad78e48d7b2b54d0412539a67"}, - {file = "rpds_py-0.19.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:5095a7c838a8647c32aa37c3a460d2c48debff7fc26e1136aee60100a8cd8f68"}, - {file = "rpds_py-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f2f78ef14077e08856e788fa482107aa602636c16c25bdf59c22ea525a785e9"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7cc6cb44f8636fbf4a934ca72f3e786ba3c9f9ba4f4d74611e7da80684e48d2"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf902878b4af334a09de7a45badbff0389e7cf8dc2e4dcf5f07125d0b7c2656d"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:688aa6b8aa724db1596514751ffb767766e02e5c4a87486ab36b8e1ebc1aedac"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57dbc9167d48e355e2569346b5aa4077f29bf86389c924df25c0a8b9124461fb"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4cf5a9497874822341c2ebe0d5850fed392034caadc0bad134ab6822c0925b"}, - {file = "rpds_py-0.19.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a790d235b9d39c70a466200d506bb33a98e2ee374a9b4eec7a8ac64c2c261fa"}, - {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d16089dfa58719c98a1c06f2daceba6d8e3fb9b5d7931af4a990a3c486241cb"}, - {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bc9128e74fe94650367fe23f37074f121b9f796cabbd2f928f13e9661837296d"}, - {file = "rpds_py-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c8f77e661ffd96ff104bebf7d0f3255b02aa5d5b28326f5408d6284c4a8b3248"}, - {file = "rpds_py-0.19.0-cp312-none-win32.whl", hash = "sha256:5f83689a38e76969327e9b682be5521d87a0c9e5a2e187d2bc6be4765f0d4600"}, - {file = "rpds_py-0.19.0-cp312-none-win_amd64.whl", hash = "sha256:06925c50f86da0596b9c3c64c3837b2481337b83ef3519e5db2701df695453a4"}, - {file = "rpds_py-0.19.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:52e466bea6f8f3a44b1234570244b1cff45150f59a4acae3fcc5fd700c2993ca"}, - {file = "rpds_py-0.19.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e21cc693045fda7f745c790cb687958161ce172ffe3c5719ca1764e752237d16"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b31f059878eb1f5da8b2fd82480cc18bed8dcd7fb8fe68370e2e6285fa86da6"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dd46f309e953927dd018567d6a9e2fb84783963650171f6c5fe7e5c41fd5666"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a01a4490e170376cd79258b7f755fa13b1a6c3667e872c8e35051ae857a92b"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcf426a8c38eb57f7bf28932e68425ba86def6e756a5b8cb4731d8e62e4e0223"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f68eea5df6347d3f1378ce992d86b2af16ad7ff4dcb4a19ccdc23dea901b87fb"}, - {file = "rpds_py-0.19.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dab8d921b55a28287733263c0e4c7db11b3ee22aee158a4de09f13c93283c62d"}, - {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6fe87efd7f47266dfc42fe76dae89060038f1d9cb911f89ae7e5084148d1cc08"}, - {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:535d4b52524a961d220875688159277f0e9eeeda0ac45e766092bfb54437543f"}, - {file = "rpds_py-0.19.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8b1a94b8afc154fbe36978a511a1f155f9bd97664e4f1f7a374d72e180ceb0ae"}, - {file = "rpds_py-0.19.0-cp38-none-win32.whl", hash = "sha256:7c98298a15d6b90c8f6e3caa6457f4f022423caa5fa1a1ca7a5e9e512bdb77a4"}, - {file = "rpds_py-0.19.0-cp38-none-win_amd64.whl", hash = "sha256:b0da31853ab6e58a11db3205729133ce0df26e6804e93079dee095be3d681dc1"}, - {file = "rpds_py-0.19.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5039e3cef7b3e7a060de468a4a60a60a1f31786da94c6cb054e7a3c75906111c"}, - {file = "rpds_py-0.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab1932ca6cb8c7499a4d87cb21ccc0d3326f172cfb6a64021a889b591bb3045c"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2afd2164a1e85226fcb6a1da77a5c8896c18bfe08e82e8ceced5181c42d2179"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1c30841f5040de47a0046c243fc1b44ddc87d1b12435a43b8edff7e7cb1e0d0"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f757f359f30ec7dcebca662a6bd46d1098f8b9fb1fcd661a9e13f2e8ce343ba1"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15e65395a59d2e0e96caf8ee5389ffb4604e980479c32742936ddd7ade914b22"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb0f6eb3a320f24b94d177e62f4074ff438f2ad9d27e75a46221904ef21a7b05"}, - {file = "rpds_py-0.19.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b228e693a2559888790936e20f5f88b6e9f8162c681830eda303bad7517b4d5a"}, - {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2575efaa5d949c9f4e2cdbe7d805d02122c16065bfb8d95c129372d65a291a0b"}, - {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5c872814b77a4e84afa293a1bee08c14daed1068b2bb1cc312edbf020bbbca2b"}, - {file = "rpds_py-0.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850720e1b383df199b8433a20e02b25b72f0fded28bc03c5bd79e2ce7ef050be"}, - {file = "rpds_py-0.19.0-cp39-none-win32.whl", hash = "sha256:ce84a7efa5af9f54c0aa7692c45861c1667080814286cacb9958c07fc50294fb"}, - {file = "rpds_py-0.19.0-cp39-none-win_amd64.whl", hash = "sha256:1c26da90b8d06227d7769f34915913911222d24ce08c0ab2d60b354e2d9c7aff"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:75969cf900d7be665ccb1622a9aba225cf386bbc9c3bcfeeab9f62b5048f4a07"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8445f23f13339da640d1be8e44e5baf4af97e396882ebbf1692aecd67f67c479"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5a7c1062ef8aea3eda149f08120f10795835fc1c8bc6ad948fb9652a113ca55"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:462b0c18fbb48fdbf980914a02ee38c423a25fcc4cf40f66bacc95a2d2d73bc8"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3208f9aea18991ac7f2b39721e947bbd752a1abbe79ad90d9b6a84a74d44409b"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3444fe52b82f122d8a99bf66777aed6b858d392b12f4c317da19f8234db4533"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb4bac7185a9f0168d38c01d7a00addece9822a52870eee26b8d5b61409213"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6b130bd4163c93798a6b9bb96be64a7c43e1cec81126ffa7ffaa106e1fc5cef5"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a707b158b4410aefb6b054715545bbb21aaa5d5d0080217290131c49c2124a6e"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dc9ac4659456bde7c567107556ab065801622396b435a3ff213daef27b495388"}, - {file = "rpds_py-0.19.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:81ea573aa46d3b6b3d890cd3c0ad82105985e6058a4baed03cf92518081eec8c"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f148c3f47f7f29a79c38cc5d020edcb5ca780020fab94dbc21f9af95c463581"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0906357f90784a66e89ae3eadc2654f36c580a7d65cf63e6a616e4aec3a81be"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f629ecc2db6a4736b5ba95a8347b0089240d69ad14ac364f557d52ad68cf94b0"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6feacd1d178c30e5bc37184526e56740342fd2aa6371a28367bad7908d454fc"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae8b6068ee374fdfab63689be0963333aa83b0815ead5d8648389a8ded593378"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78d57546bad81e0da13263e4c9ce30e96dcbe720dbff5ada08d2600a3502e526"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b6683a37338818646af718c9ca2a07f89787551057fae57c4ec0446dc6224b"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8481b946792415adc07410420d6fc65a352b45d347b78fec45d8f8f0d7496f0"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:bec35eb20792ea64c3c57891bc3ca0bedb2884fbac2c8249d9b731447ecde4fa"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:aa5476c3e3a402c37779e95f7b4048db2cb5b0ed0b9d006983965e93f40fe05a"}, - {file = "rpds_py-0.19.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:19d02c45f2507b489fd4df7b827940f1420480b3e2e471e952af4d44a1ea8e34"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3e2fd14c5d49ee1da322672375963f19f32b3d5953f0615b175ff7b9d38daed"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:93a91c2640645303e874eada51f4f33351b84b351a689d470f8108d0e0694210"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b9fc03bf76a94065299d4a2ecd8dfbae4ae8e2e8098bbfa6ab6413ca267709"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a4b07cdf3f84310c08c1de2c12ddadbb7a77568bcb16e95489f9c81074322ed"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba0ed0dc6763d8bd6e5de5cf0d746d28e706a10b615ea382ac0ab17bb7388633"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:474bc83233abdcf2124ed3f66230a1c8435896046caa4b0b5ab6013c640803cc"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329c719d31362355a96b435f4653e3b4b061fcc9eba9f91dd40804ca637d914e"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef9101f3f7b59043a34f1dccbb385ca760467590951952d6701df0da9893ca0c"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:0121803b0f424ee2109d6e1f27db45b166ebaa4b32ff47d6aa225642636cd834"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:8344127403dea42f5970adccf6c5957a71a47f522171fafaf4c6ddb41b61703a"}, - {file = "rpds_py-0.19.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:443cec402ddd650bb2b885113e1dcedb22b1175c6be223b14246a714b61cd521"}, - {file = "rpds_py-0.19.0.tar.gz", hash = "sha256:4fdc9afadbeb393b4bbbad75481e0ea78e4469f2e1d713a90811700830b553a9"}, + {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, + {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, + {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, + {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, + {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, + {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, + {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, + {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, + {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, + {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, + {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, + {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, + {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, + {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, + {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, + {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, + {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, + {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, + {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, + {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, + {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, + {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, + {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, + {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, + {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, + {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, + {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, + {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, + {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, + {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, + {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, + {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, + {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, + {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, + {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, + {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, + {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, + {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, + {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, ] [[package]] @@ -2949,13 +2906,13 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "71.1.0" +version = "72.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-71.1.0-py3-none-any.whl", hash = "sha256:33874fdc59b3188304b2e7c80d9029097ea31627180896fb549c578ceb8a0855"}, - {file = "setuptools-71.1.0.tar.gz", hash = "sha256:032d42ee9fb536e33087fb66cac5f840eb9391ed05637b3f2a76a7c8fb477936"}, + {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, + {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, ] [package.extras] @@ -3269,24 +3226,24 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "71.1.0.20240724" +version = "71.1.0.20240726" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-71.1.0.20240724.tar.gz", hash = "sha256:ae61c528111a9450e149aedbc1ab0f7874f9bb0dc8f14c3cd200a1f82457050b"}, - {file = "types_setuptools-71.1.0.20240724-py3-none-any.whl", hash = "sha256:90570f0578ed67bb25ada9b3d1432e6727178d2408a7051b27e4a712731df30a"}, + {file = "types-setuptools-71.1.0.20240726.tar.gz", hash = "sha256:85ba28e9461bb1be86ebba4db0f1c2408f2b11115b1966334ea9dc464e29303e"}, + {file = "types_setuptools-71.1.0.20240726-py3-none-any.whl", hash = "sha256:a7775376f36e0ff09bcad236bf265777590a66b11623e48c20bfc30f1444ea36"}, ] [[package]] name = "types-simplejson" -version = "3.19.0.20240310" +version = "3.19.0.20240801" description = "Typing stubs for simplejson" optional = false python-versions = ">=3.8" files = [ - {file = "types-simplejson-3.19.0.20240310.tar.gz", hash = "sha256:2831366f70d5d55832c3d978dff7c989575d55fb38621d086dbfee7a30c2b500"}, - {file = "types_simplejson-3.19.0.20240310-py3-none-any.whl", hash = "sha256:6f06d8e50112bc931863a40e2cf463a99223d55fe07f275826fa969962bb56a4"}, + {file = "types-simplejson-3.19.0.20240801.tar.gz", hash = "sha256:ef90cc81dd915f26c452fa2b5e0cbd3a36af81074ae63878fcf8a477e7594e4d"}, + {file = "types_simplejson-3.19.0.20240801-py3-none-any.whl", hash = "sha256:37f1b33c8626d7f072ea87737629310674845d54d187f12387fe33d31c938288"}, ] [[package]] @@ -3355,85 +3312,6 @@ MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - [[package]] name = "wtforms" version = "3.1.2" @@ -3648,4 +3526,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "6bfcaa800aaa897b927e819c2c749b50c80d7fe795e743589af7d62f8eac2d8a" +content-hash = "17591d5c39e3f41b5ee2807991c02c3766307eed9c2bdd309e995e388fb8299c" diff --git a/pyproject.toml b/pyproject.toml index 98deeb8d6b..fafc08c312 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,9 +18,8 @@ pep8 = "^1.7.1" mock = "^5.1.0" pytest-cov = "^5.0.0" jsonschema = "^4.21.1" -pylint = "^2.17.7" +pylint = "^3.2.5" pylint-mccabe = "^0.1.3" -pylint-quotes = "^0.2.3" pylint-absolute-imports = "^1.1.0" beautifulsoup4 = "^4.12.3" httmock = "^1.4.0" @@ -50,6 +49,7 @@ types-pytz = "^2024.1.0.20240417" playwright = "^1.42.0" black = "^24.2.0" djlint = "^1.34.1" +flake8-quotes = "^3.4.0" [tool.poetry.dependencies] diff --git a/setup.cfg b/setup.cfg index 7dcd5b8506..d6356222f4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,4 +10,8 @@ filterwarnings= # Ignore node_modules and cloned repos when not in a virtual environment exclude = node_modules/*,tests/*,src/* max-line-length = 160 -ignore = C815,C816,W503,E203,E704 +inline-quotes = double +multiline-quotes = double +docstring-quotes = double +avoid-escape = True +ignore = E704,W503,E203 From b24d7ba7ebe75e3b5b61e1493c8a013eab095414 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:57:56 +0100 Subject: [PATCH 448/567] Schemas v5.10.0 (#1461) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 7094538d68..339cbf236a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.9.0 +v5.10.0 From 91d0437341efa575b03282729749bf874501592a Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:29:13 +0100 Subject: [PATCH 449/567] Fix test typo and formatting (#1466) --- templates/confirm-email.html | 12 ++++++------ tests/functional/spec/checkbox.spec.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 6bd0ce6ad8..49c1ad15a5 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -29,12 +29,12 @@

      {{ conten "variants": 'timer', "classes": "ons-u-mt-xl", "attributes": { - "data-qa": "btn-submit", - "data-ga-category": "Button", - "data-ga-action": "Submit", - "data-ga-label": "Confirm", - "data-ga-page": "Confirmation Email", - "data-ga": "click", + "data-qa": "btn-submit", + "data-ga-category": "Button", + "data-ga-action": "Submit", + "data-ga-label": "Confirm", + "data-ga-page": "Confirmation Email", + "data-ga": "click", } }) }} diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index e0892dbc86..e3785dde80 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -49,7 +49,7 @@ describe('Checkbox with "other" option', () => { ); }); - it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.s", async () => { + it("Given a mandatory checkbox answer, when there is an error on the page for other field and I enter valid value and submit page, then the error is cleared and I navigate to next page.", async () => { await $(MandatoryCheckboxPage.other()).click(); await click(MandatoryCheckboxPage.submit()); await expect(await $(MandatoryCheckboxPage.error()).isDisplayed()).toBe(true); From 352c76f304793af7739cd5f0618156d3351e62c9 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:29:07 +0100 Subject: [PATCH 450/567] Schemas v5.11.0 (#1469) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 339cbf236a..ff9c6e1448 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.10.0 +v5.11.0 From 92b588063f6e13b78f97a54f8f09339cb3414dca Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:37:12 +0100 Subject: [PATCH 451/567] Update to CodeQL v3 (#1468) --- .github/workflows/codeql-analysis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 04994b0240..f8dda6a8fd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,19 +38,18 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - setup-python-dependencies: false # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +63,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From f4bf9b805d611d7749836d3c76afb091abe673f3 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:18:44 +0100 Subject: [PATCH 452/567] Schemas v5.12.0 (#1485) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ff9c6e1448..eef4725383 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.11.0 +v5.12.0 From 3450ca27ab229ba60fb5a825f4ae84641deb71f9 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:51:45 +0100 Subject: [PATCH 453/567] Update dataflow default templates (#1484) --- ci/purge_expired_questionnaire_state.yaml | 2 +- ci/purge_expired_sessions.yaml | 2 +- ci/purge_submitted_questionnaire_state.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/purge_expired_questionnaire_state.yaml b/ci/purge_expired_questionnaire_state.yaml index 5228e3ab38..6a095bd47c 100644 --- a/ci/purge_expired_questionnaire_state.yaml +++ b/ci/purge_expired_questionnaire_state.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' + DATAFLOW_TEMPLATE_VERSION: '2024-07-23-00_RC00' run: path: bash args: diff --git a/ci/purge_expired_sessions.yaml b/ci/purge_expired_sessions.yaml index c8f4f72026..0eb10d7d24 100644 --- a/ci/purge_expired_sessions.yaml +++ b/ci/purge_expired_sessions.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' + DATAFLOW_TEMPLATE_VERSION: '2024-07-23-00_RC00' EXPIRES_AT_PURGE_OFFSET_IN_SECONDS: 10800 run: path: bash diff --git a/ci/purge_submitted_questionnaire_state.yaml b/ci/purge_submitted_questionnaire_state.yaml index 0c9855f1e3..029f93be5e 100644 --- a/ci/purge_submitted_questionnaire_state.yaml +++ b/ci/purge_submitted_questionnaire_state.yaml @@ -6,7 +6,7 @@ image_resource: params: SERVICE_ACCOUNT_JSON: ((gcp.service_account_json)) PROJECT_ID: - DATAFLOW_TEMPLATE_VERSION: '2024-01-09-00_RC01' + DATAFLOW_TEMPLATE_VERSION: '2024-07-23-00_RC00' SUBMITTED_AT_PURGE_OFFSET_IN_SECONDS: 3600 run: path: bash From 17b36a9bc72d436c1efdf302b991f6cebb9d2574 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:12:53 +0100 Subject: [PATCH 454/567] Update to Poetry 1.8.3 (#1493) --- .github/workflows/pull_request.yml | 8 ++++---- Dockerfile | 4 ++-- README.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 7cf3614a9c..14972b7ec0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -22,7 +22,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.2 + version: 1.8.3 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -58,7 +58,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.2 + version: 1.8.3 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -98,7 +98,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.2 + version: 1.8.3 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -143,7 +143,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.2 + version: 1.8.3 virtualenvs-create: true - uses: actions/setup-python@v5 with: diff --git a/Dockerfile b/Dockerfile index f6c3f71fb0..99e9965af8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,10 +15,10 @@ ENV HTTP_KEEP_ALIVE 2 ENV GUNICORN_CMD_ARGS -c gunicorn_config.py COPY pyproject.toml pyproject.toml -COPY poetry.lock poetry.lock +COPY poetry.lock poetry.lock RUN groupadd -r appuser && useradd -r -g appuser -u 9000 appuser && chown -R appuser:appuser . -RUN pip install "poetry==1.8.2" && \ +RUN pip install "poetry==1.8.3" && \ poetry config virtualenvs.create false && \ poetry install --only main && \ make build diff --git a/README.md b/README.md index d5793b3f1b..70a73756fd 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ pip install --upgrade pip setuptools Install poetry, poetry dotenv plugin and install dependencies: ``` shell -curl -sSL https://install.python-poetry.org | python3 - --version 1.8.2 +curl -sSL https://install.python-poetry.org | python3 - --version 1.8.3 poetry self add poetry-plugin-dotenv poetry install ``` From ebd6582aa1226f23dd2b6ce5fd7cd35e38ae3ceb Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 4 Sep 2024 14:46:45 +0100 Subject: [PATCH 455/567] Update GitHub Actions for keyless auth (#1464) --- .github/workflows/pull_request.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 14972b7ec0..e645c24626 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -158,9 +158,20 @@ jobs: - name: Docker compose shutdown run: RUNNER_ENV_FILE=.functional-tests.env docker compose kill docker-push: + permissions: + contents: 'read' + id-token: 'write' runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 + - id: auth + name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ secrets.SERVICE_ACCOUNT }} + - name: Set Tag and SHA run: | CLEAN_TAG=$(echo "${{ github.event.pull_request.head.ref }}" | tr / -) @@ -181,10 +192,7 @@ jobs: docker push onsdigital/eq-questionnaire-runner:$TAG - name: Push to GAR - env: - GAR_SERVICE_KEY: ${{ secrets.GAR_SERVICE_KEY }} run: | - echo $GAR_SERVICE_KEY | docker login -u _json_key --password-stdin https://${{ secrets.GAR_LOCATION }} gcloud auth configure-docker ${{ secrets.GAR_LOCATION }} echo "Pushing to GAR with tag $TAG" docker push ${{ secrets.GAR_LOCATION }}/${{ secrets.GAR_PROJECT_ID }}/docker-images/eq-questionnaire-runner:$TAG From c473a23d71f2188270ada75b25df8c73bac84ae3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:13:53 +0100 Subject: [PATCH 456/567] Bump the development-dependencies group across 1 directory with 9 updates (#1492) --- poetry.lock | 131 +++++++++++++++++++++++++------------------------ pyproject.toml | 18 +++---- 2 files changed, 75 insertions(+), 74 deletions(-) diff --git a/poetry.lock b/poetry.lock index 623ab5ffd6..2bd59d6892 100644 --- a/poetry.lock +++ b/poetry.lock @@ -773,27 +773,27 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "djlint" -version = "1.34.1" +version = "1.35.2" description = "HTML Template Linter and Formatter" optional = false -python-versions = ">=3.8.0,<4.0.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "djlint-1.34.1-py3-none-any.whl", hash = "sha256:96ff1c464fb6f061130ebc88663a2ea524d7ec51f4b56221a2b3f0320a3cfce8"}, - {file = "djlint-1.34.1.tar.gz", hash = "sha256:db93fa008d19eaadb0454edf1704931d14469d48508daba2df9941111f408346"}, + {file = "djlint-1.35.2-py3-none-any.whl", hash = "sha256:4ba995bad378f2afa77c8ea56ba1c14429d9ff26a18e8ae23bc71eedb9152243"}, + {file = "djlint-1.35.2.tar.gz", hash = "sha256:318de9d4b9b0061a111f8f5164ecbacd8215f449dd4bd5a76d2a691c815ee103"}, ] [package.dependencies] -click = ">=8.0.1,<9.0.0" -colorama = ">=0.4.4,<0.5.0" -cssbeautifier = ">=1.14.4,<2.0.0" -html-tag-names = ">=0.1.2,<0.2.0" -html-void-elements = ">=0.1.0,<0.2.0" -jsbeautifier = ">=1.14.4,<2.0.0" -json5 = ">=0.9.11,<0.10.0" -pathspec = ">=0.12.0,<0.13.0" -PyYAML = ">=6.0,<7.0" -regex = ">=2023.0.0,<2024.0.0" -tqdm = ">=4.62.2,<5.0.0" +click = ">=8.0.1" +colorama = ">=0.4.4" +cssbeautifier = ">=1.14.4" +html-tag-names = ">=0.1.2" +html-void-elements = ">=0.1.0" +jsbeautifier = ">=1.14.4" +json5 = ">=0.9.11" +pathspec = ">=0.12.0" +PyYAML = ">=6.0" +regex = ">=2023" +tqdm = ">=4.62.2" [[package]] name = "dnspython" @@ -856,13 +856,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.23.4" +version = "2.24.1" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.23.4-py3-none-any.whl", hash = "sha256:da592c68c91a14447327bc179f5d124cc2df4f3c864c2de551a4785edde78276"}, - {file = "fakeredis-2.23.4.tar.gz", hash = "sha256:fabfa9ce940f568ee2f1a7cb222c3f45f05035b335fd5979523bbab4c10ff4a0"}, + {file = "fakeredis-2.24.1-py3-none-any.whl", hash = "sha256:09d3049a29910f80c0ef5789c31bef3dbb9727bd43a67ee8598217f4efd12f35"}, + {file = "fakeredis-2.24.1.tar.gz", hash = "sha256:4a52ab0edad53543ac5e3a41d761f91012613ed583344da54ae6473e05b0f6d0"}, ] [package.dependencies] @@ -878,13 +878,13 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"] [[package]] name = "flake8" -version = "7.1.0" +version = "7.1.1" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a"}, - {file = "flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5"}, + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, ] [package.dependencies] @@ -1957,13 +1957,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.11" +version = "5.0.13" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.11-py2.py3-none-any.whl", hash = "sha256:bdba9bec0afcde9f99b58c5271d6458dbfcda0a0a1e9beaecd808d2591db65ea"}, - {file = "moto-5.0.11.tar.gz", hash = "sha256:606b641f4c6ef69f28a84147d6d6806d052011e7ae7b0fe46ae8858e7a27a0a3"}, + {file = "moto-5.0.13-py2.py3-none-any.whl", hash = "sha256:984377a9c4536543fc09f49a1d5210c61c4a4f55c79719f7d9f8dcdd9bf55ea5"}, + {file = "moto-5.0.13.tar.gz", hash = "sha256:ddf8864f0d61af88fd07a4e5eac428c6bebf4fcd10023f8e756e65e9e7b7e4a5"}, ] [package.dependencies] @@ -1988,6 +1988,7 @@ cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (> cognitoidp = ["joserfc (>=0.9.0)"] dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] +events = ["jsonpath-ng"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] @@ -2001,38 +2002,38 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] @@ -2296,13 +2297,13 @@ files = [ [[package]] name = "pylint" -version = "3.2.6" +version = "3.2.7" description = "python code static checker" optional = false python-versions = ">=3.8.0" files = [ - {file = "pylint-3.2.6-py3-none-any.whl", hash = "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f"}, - {file = "pylint-3.2.6.tar.gz", hash = "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3"}, + {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, + {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, ] [package.dependencies] @@ -3164,13 +3165,13 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20240316" +version = "2.9.0.20240821" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, + {file = "types-python-dateutil-2.9.0.20240821.tar.gz", hash = "sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98"}, + {file = "types_python_dateutil-2.9.0.20240821-py3-none-any.whl", hash = "sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57"}, ] [[package]] @@ -3186,24 +3187,24 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240724" +version = "6.0.12.20240808" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240724.tar.gz", hash = "sha256:cf7b31ae67e0c5b2919c703d2affc415485099d3fe6666a6912f040fd05cb67f"}, - {file = "types_PyYAML-6.0.12.20240724-py3-none-any.whl", hash = "sha256:e5becec598f3aa3a2ddf671de4a75fa1c6856fbf73b2840286c9d50fae2d5d48"}, + {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, + {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, ] [[package]] name = "types-redis" -version = "4.6.0.20240726" +version = "4.6.0.20240819" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.20240726.tar.gz", hash = "sha256:de2aefcf7afe80057debada8c540463d06c8863de50b8016bd369ccdbcb59b5e"}, - {file = "types_redis-4.6.0.20240726-py3-none-any.whl", hash = "sha256:233062b7120a9908532ec9163d17af74b80fa49a89d510444cad4cac42717378"}, + {file = "types-redis-4.6.0.20240819.tar.gz", hash = "sha256:08f51f550ad41d0152bd98d77ac9d6d8f761369121710a213642f6036b9a7183"}, + {file = "types_redis-4.6.0.20240819-py3-none-any.whl", hash = "sha256:86db9af6f0033154e12bc22c77236cef0907b995fda8c9f0f0eacd59943ed2fc"}, ] [package.dependencies] @@ -3526,4 +3527,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "17591d5c39e3f41b5ee2807991c02c3766307eed9c2bdd309e995e388fb8299c" +content-hash = "542b9a0e39f2f4f3697cdb31e53d8a13d16b88d56133a4694aa5602fae627425" diff --git a/pyproject.toml b/pyproject.toml index fafc08c312..4626c74241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,37 +18,37 @@ pep8 = "^1.7.1" mock = "^5.1.0" pytest-cov = "^5.0.0" jsonschema = "^4.21.1" -pylint = "^3.2.5" +pylint = "^3.2.7" pylint-mccabe = "^0.1.3" pylint-absolute-imports = "^1.1.0" beautifulsoup4 = "^4.12.3" httmock = "^1.4.0" -flake8 = "^7.0.0" +flake8 = "^7.1.1" flake8-debugger = "^4.1.2" flake8-mock = "^0.4" flake8-print = "^5.0.0" flake8-tuple = "^0.4.1" flake8-datetimez = "^20.10.0" -moto = "^5.0.8" +moto = "^5.0.13" freezegun = "^1.4.0" pytest-xdist = "^3.5.0" -fakeredis = "^2.23.4" -mypy = "^1.11.1" +fakeredis = "^2.23.5" +mypy = "^1.11.2" pytest-flask = "^1.3.0" pytest = "^8.3.2" pytest-sugar = "^1.0.0" responses = "^0.25.0" types-simplejson = "^3.19.0.20240310" types-requests = "^2.31.0.20240406" -types-redis = "^4.6.0.20240726" -types-PyYAML = "^6.0.12.20240311" -types-python-dateutil = "^2.8.19.20240311" +types-redis = "^4.6.0.20240819" +types-PyYAML = "^6.0.12.20240808" +types-python-dateutil = "^2.9.0.20240821" pytest-mock = "^3.12.0" types-cachetools = "^5.3.0.7" types-pytz = "^2024.1.0.20240417" playwright = "^1.42.0" black = "^24.2.0" -djlint = "^1.34.1" +djlint = "^1.34.2" flake8-quotes = "^3.4.0" From c4ff8aea027ee38e3a030782b979dd46a1246a29 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Tue, 10 Sep 2024 07:28:42 +0100 Subject: [PATCH 457/567] Temporarily remove failing unit test (#1504) --- app/publisher/publisher.py | 3 ++- tests/app/publisher/test_publisher.py | 17 ----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/app/publisher/publisher.py b/app/publisher/publisher.py index 909a437a3b..e4378dda24 100644 --- a/app/publisher/publisher.py +++ b/app/publisher/publisher.py @@ -29,9 +29,10 @@ def _publish(self, topic_id: str, message: bytes) -> Future: response: Future = self._client.publish(topic_path, message) return response + # pragma: no cover def publish( self, topic_id: str, message: bytes, fulfilment_request_transaction_id: str - ) -> None: + ) -> None: # pragma: no cover response = self._publish(topic_id, message) try: # Resolve the future diff --git a/tests/app/publisher/test_publisher.py b/tests/app/publisher/test_publisher.py index d6f9901c05..f6e3c2c431 100644 --- a/tests/app/publisher/test_publisher.py +++ b/tests/app/publisher/test_publisher.py @@ -1,10 +1,5 @@ -from uuid import uuid4 - -import pytest from google.pubsub_v1.types.pubsub import PubsubMessage -from app.publisher.exceptions import PublicationFailed - def test_publish(publisher, mocker): topic_id = "test-topic-id" @@ -29,15 +24,3 @@ def test_publish(publisher, mocker): # Check mock. batch.publish.assert_has_calls([mocker.call(PubsubMessage(data=b"test-message"))]) - - -def test_resolving_message_raises_exception_on_error(publisher): - with pytest.raises(PublicationFailed) as ex: - # Try resolve the future with an invalid credentials - publisher.publish( - "test-topic-id", - b"test-message", - fulfilment_request_transaction_id=str(uuid4()), - ) - - assert "403 The request is missing a valid API key." in str(ex.value) From 9eca78caebb2774c17c2c7f93018075d649ad028 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:41:43 +0100 Subject: [PATCH 458/567] Schema v5.13.0 (#1510) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index eef4725383..fa75732317 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.12.0 +v5.13.0 From 90803b161cf53156e392b72705204b0d283e144a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:43:26 +0100 Subject: [PATCH 459/567] Add improvements to dependabot config (#1487) --- .github/dependabot.yaml | 103 ++++++++++++++++++++++++++-------------- pyproject.toml | 2 + 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 59c374bce8..02ffa2f084 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,9 +1,59 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + day: "friday" + time: "08:00" + timezone: "Europe/London" + labels: + - "dependencies" + - "github-actions" + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "monthly" + day: "friday" + time: "08:00" + timezone: "Europe/London" + versioning-strategy: increase-if-necessary + labels: + - "dependencies" + - "node" + groups: + development-dependencies: + dependency-type: "development" + patterns: + - "@wdio*" + - "@babel*" + - "eslint*" + - "json*" + - "jsrsasign*" + - "lint-staged*" + - "livereload*" + - "node-forge*" + - "prettier*" + - "typescript*" + - "uuid*" + - "webdriverio*" + - package-ecosystem: "pip" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "friday" + time: "08:00" + timezone: "Europe/London" + # Workaround to have two "pip" ecosystems: actively setting "target-branch: main" for one config, and leaving it unset for the other config + target-branch: main + versioning-strategy: increase-if-necessary + labels: + - "dependencies" + - "python" + allow: + - dependency-type: "production" groups: production-dependencies: dependency-type: "production" @@ -38,6 +88,21 @@ updates: - "cachetools" - "gevent" - "babel" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "monthly" + day: "friday" + time: "08:00" + timezone: "Europe/London" + versioning-strategy: increase-if-necessary + labels: + - "dependencies" + - "python-dev" + allow: + - dependency-type: "development" + groups: development-dependencies: dependency-type: "development" patterns: @@ -59,38 +124,6 @@ updates: - "black" - "djlint" ignore: - # Temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 + # "babel" temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 - dependency-name: "babel" - update-types: ["version-update:semver-major", "version-update:semver-minor"] - - dependency-name: "pytest*" - update-types: ["version-update:semver-minor"] - - dependency-name: "flake8*" - update-types: ["version-update:semver-minor"] - - dependency-name: "types*" - update-types: ["version-update:semver-minor"] - - dependency-name: "pep8" - update-types: ["version-update:semver-minor"] - - dependency-name: "mock" - update-types: ["version-update:semver-minor"] - - dependency-name: "jsonschema" - update-types: ["version-update:semver-minor"] - - dependency-name: "beautifulsoup4" - update-types: ["version-update:semver-minor"] - - dependency-name: "httmock" - update-types: ["version-update:semver-minor"] - - dependency-name: "moto" - update-types: ["version-update:semver-minor"] - - dependency-name: "freezegun" - update-types: ["version-update:semver-minor"] - - dependency-name: "fakeredis" - update-types: ["version-update:semver-minor"] - - dependency-name: "mypy" - update-types: ["version-update:semver-minor"] - - dependency-name: "responses" - update-types: ["version-update:semver-minor"] - - dependency-name: "playwright" - update-types: ["version-update:semver-minor"] - - dependency-name: "black" - update-types: ["version-update:semver-minor"] - - dependency-name: "djlint" - update-types: ["version-update:semver-minor"] + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] diff --git a/pyproject.toml b/pyproject.toml index 4626c74241..78b08d89a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ description = "ONS Digital eQ Questionnaire Runner App" authors = ["ONSDigital"] [tool.poetry.group.dev.dependencies] +# update dependabot.yaml when adding new dependencies pep8 = "^1.7.1" mock = "^5.1.0" pytest-cov = "^5.0.0" @@ -53,6 +54,7 @@ flake8-quotes = "^3.4.0" [tool.poetry.dependencies] +# update dependabot.yaml when adding new dependencies python = "^3.11.4" colorama = "^0.4.6" flask = "^3.0.2" From fc589cb638a09cf06381e8bad06d86d3bae0b201 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:36:02 +0100 Subject: [PATCH 460/567] Update DS version (#1480) --- .design-system-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.design-system-version b/.design-system-version index 14a228fe9a..3b74fa7060 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -70.0.5 +70.0.15 From a75dee2f83ce2aaf25e0b44fa91fa22984d52160 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:28:32 +0100 Subject: [PATCH 461/567] Move babel ignore to the prod pip ecosystem (#1517) --- .github/dependabot.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 02ffa2f084..4fe6befed1 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -88,6 +88,10 @@ updates: - "cachetools" - "gevent" - "babel" + ignore: + # "babel" temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 + - dependency-name: "babel" + update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - package-ecosystem: "pip" directory: "/" @@ -123,7 +127,3 @@ updates: - "playwright" - "black" - "djlint" - ignore: - # "babel" temporarily pinned to v2.14.0 - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 - - dependency-name: "babel" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] From 91761b54511037a456e4f7163ea9be521c7d9867 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Mon, 16 Sep 2024 09:14:35 +0100 Subject: [PATCH 462/567] Fix pub/sub unit test (#1507) --- app/publisher/publisher.py | 3 +-- tests/app/publisher/test_publisher.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/publisher/publisher.py b/app/publisher/publisher.py index e4378dda24..909a437a3b 100644 --- a/app/publisher/publisher.py +++ b/app/publisher/publisher.py @@ -29,10 +29,9 @@ def _publish(self, topic_id: str, message: bytes) -> Future: response: Future = self._client.publish(topic_path, message) return response - # pragma: no cover def publish( self, topic_id: str, message: bytes, fulfilment_request_transaction_id: str - ) -> None: # pragma: no cover + ) -> None: response = self._publish(topic_id, message) try: # Resolve the future diff --git a/tests/app/publisher/test_publisher.py b/tests/app/publisher/test_publisher.py index f6e3c2c431..b29a2a2f3a 100644 --- a/tests/app/publisher/test_publisher.py +++ b/tests/app/publisher/test_publisher.py @@ -1,5 +1,11 @@ +from unittest.mock import Mock, patch +from uuid import uuid4 + +import pytest from google.pubsub_v1.types.pubsub import PubsubMessage +from app.publisher.exceptions import PublicationFailed + def test_publish(publisher, mocker): topic_id = "test-topic-id" @@ -24,3 +30,18 @@ def test_publish(publisher, mocker): # Check mock. batch.publish.assert_has_calls([mocker.call(PubsubMessage(data=b"test-message"))]) + + +def test_resolving_message_raises_exception_on_error(publisher): + mock_future = Mock() + mock_future.result.side_effect = Exception() + + with patch( + "app.publisher.publisher.PubSubPublisher._publish", return_value=mock_future + ): + with pytest.raises(PublicationFailed): + publisher.publish( + "test-topic-id", + b"test-message", + fulfilment_request_transaction_id=str(uuid4()), + ) From 8006bab79148d2d6aac236006fb2da3ebdf94a00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:56:45 +0100 Subject: [PATCH 463/567] Bump the development-dependencies group across 1 directory with 9 updates (#1518) Bumps the development-dependencies group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [moto](https://github.com/getmoto/moto) | `5.0.13` | `5.0.14` | | [pytest](https://github.com/pytest-dev/pytest) | `8.3.2` | `8.3.3` | | [types-requests](https://github.com/python/typeshed) | `2.32.0.20240712` | `2.32.0.20240907` | | [types-redis](https://github.com/python/typeshed) | `4.6.0.20240819` | `4.6.0.20240903` | | [types-python-dateutil](https://github.com/python/typeshed) | `2.9.0.20240821` | `2.9.0.20240906` | | [types-cachetools](https://github.com/python/typeshed) | `5.4.0.20240717` | `5.5.0.20240820` | | [types-pytz](https://github.com/python/typeshed) | `2024.1.0.20240417` | `2024.2.0.20240913` | | [playwright](https://github.com/Microsoft/playwright-python) | `1.45.1` | `1.47.0` | | [black](https://github.com/psf/black) | `24.4.2` | `24.8.0` | Updates `moto` from 5.0.13 to 5.0.14 - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/5.0.13...5.0.14) Updates `pytest` from 8.3.2 to 8.3.3 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.3) Updates `types-requests` from 2.32.0.20240712 to 2.32.0.20240907 - [Commits](https://github.com/python/typeshed/commits) Updates `types-redis` from 4.6.0.20240819 to 4.6.0.20240903 - [Commits](https://github.com/python/typeshed/commits) Updates `types-python-dateutil` from 2.9.0.20240821 to 2.9.0.20240906 - [Commits](https://github.com/python/typeshed/commits) Updates `types-cachetools` from 5.4.0.20240717 to 5.5.0.20240820 - [Commits](https://github.com/python/typeshed/commits) Updates `types-pytz` from 2024.1.0.20240417 to 2024.2.0.20240913 - [Commits](https://github.com/python/typeshed/commits) Updates `playwright` from 1.45.1 to 1.47.0 - [Release notes](https://github.com/Microsoft/playwright-python/releases) - [Commits](https://github.com/Microsoft/playwright-python/compare/v1.45.1...v1.47.0) Updates `black` from 24.4.2 to 24.8.0 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.4.2...24.8.0) --- updated-dependencies: - dependency-name: moto dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-redis dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-python-dateutil dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: types-pytz dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: playwright dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: black dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- poetry.lock | 130 ++++++++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2bd59d6892..bb5d22533f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -67,33 +67,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.4.2" +version = "24.8.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, - {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, - {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, - {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, - {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, - {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, - {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, - {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, - {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, - {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, - {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, - {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, - {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, - {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, - {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, - {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, - {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, - {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, - {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, - {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, - {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, - {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, + {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, + {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, + {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, + {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, + {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, + {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, + {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, + {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, + {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, + {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, + {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, + {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, + {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, + {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, + {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, + {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, + {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, + {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, + {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, + {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, + {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, + {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, ] [package.dependencies] @@ -1957,13 +1957,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.13" +version = "5.0.14" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.13-py2.py3-none-any.whl", hash = "sha256:984377a9c4536543fc09f49a1d5210c61c4a4f55c79719f7d9f8dcdd9bf55ea5"}, - {file = "moto-5.0.13.tar.gz", hash = "sha256:ddf8864f0d61af88fd07a4e5eac428c6bebf4fcd10023f8e756e65e9e7b7e4a5"}, + {file = "moto-5.0.14-py2.py3-none-any.whl", hash = "sha256:c738ffe85d3844ef37b865951736c4faf2e0f3e4f05db87bdad97a6c01b88174"}, + {file = "moto-5.0.14.tar.gz", hash = "sha256:0f849243269fd03372426c302b18cb605302da32620d7f0266be6a40735b2acd"}, ] [package.dependencies] @@ -1978,24 +1978,24 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] cognitoidp = ["joserfc (>=0.9.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.5)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.6)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.6)"] events = ["jsonpath-ng"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.5)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.5)"] -server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.5)", "pyparsing (>=3.0.7)", "setuptools"] +proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.6)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.6)"] +server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] stepfunctions = ["antlr4-python3-runtime", "jsonpath-ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] @@ -2150,23 +2150,23 @@ type = ["mypy (>=1.8)"] [[package]] name = "playwright" -version = "1.45.1" +version = "1.47.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" files = [ - {file = "playwright-1.45.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:360607e37c00cdf97c74317f010e106ac4671aeaec6a192431dd71a30941da9d"}, - {file = "playwright-1.45.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:20adc2abf164c5e8969f9066011b152e12c210549edec78cd05bd0e9cf4135b7"}, - {file = "playwright-1.45.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:5f047cdc6accf4c7084dfc7587a2a5ef790cddc44cbb111e471293c5a91119db"}, - {file = "playwright-1.45.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:f06f6659abe0abf263e5f6661d379fbf85c112745dd31d82332ceae914f58df7"}, - {file = "playwright-1.45.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87dc3b3d17e12c68830c29b7fdf5e93315221bbb4c6090e83e967e154e2c1828"}, - {file = "playwright-1.45.1-py3-none-win32.whl", hash = "sha256:2b8f517886ef1e2151982f6e7be84be3ef7d8135bdcf8ee705b4e4e99566e866"}, - {file = "playwright-1.45.1-py3-none-win_amd64.whl", hash = "sha256:0d236cf427784e77de352ba1b7d700693c5fe455b8e5f627f6d84ad5b84b5bf5"}, + {file = "playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd"}, + {file = "playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab"}, + {file = "playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341"}, + {file = "playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955"}, + {file = "playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249"}, + {file = "playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974"}, + {file = "playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280"}, ] [package.dependencies] greenlet = "3.0.3" -pyee = "11.1.0" +pyee = "12.0.0" [[package]] name = "pluggy" @@ -2269,13 +2269,13 @@ files = [ [[package]] name = "pyee" -version = "11.1.0" +version = "12.0.0" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" files = [ - {file = "pyee-11.1.0-py3-none-any.whl", hash = "sha256:5d346a7d0f861a4b2e6c47960295bd895f816725b27d656181947346be98d7c1"}, - {file = "pyee-11.1.0.tar.gz", hash = "sha256:b53af98f6990c810edd9b56b87791021a8f54fd13db4edd1142438d44ba2263f"}, + {file = "pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990"}, + {file = "pyee-12.0.0.tar.gz", hash = "sha256:c480603f4aa2927d4766eb41fa82793fe60a82cbfdb8d688e0d08c55a534e145"}, ] [package.dependencies] @@ -2364,13 +2364,13 @@ files = [ [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -3125,13 +3125,13 @@ telegram = ["requests"] [[package]] name = "types-cachetools" -version = "5.4.0.20240717" +version = "5.5.0.20240820" description = "Typing stubs for cachetools" optional = false python-versions = ">=3.8" files = [ - {file = "types-cachetools-5.4.0.20240717.tar.gz", hash = "sha256:1eae90c48760bac44ab89108be938e8ce1d740910f2d4b68446dcdc82763f186"}, - {file = "types_cachetools-5.4.0.20240717-py3-none-any.whl", hash = "sha256:67c84c26df988039be68344b162afd2dd7cd3741dc08e7d67aa1954782fd2d2a"}, + {file = "types-cachetools-5.5.0.20240820.tar.gz", hash = "sha256:b888ab5c1a48116f7799cd5004b18474cd82b5463acb5ffb2db2fc9c7b053bc0"}, + {file = "types_cachetools-5.5.0.20240820-py3-none-any.whl", hash = "sha256:efb2ed8bf27a4b9d3ed70d33849f536362603a90b8090a328acf0cd42fda82e2"}, ] [[package]] @@ -3165,24 +3165,24 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20240821" +version = "2.9.0.20240906" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240821.tar.gz", hash = "sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98"}, - {file = "types_python_dateutil-2.9.0.20240821-py3-none-any.whl", hash = "sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57"}, + {file = "types-python-dateutil-2.9.0.20240906.tar.gz", hash = "sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e"}, + {file = "types_python_dateutil-2.9.0.20240906-py3-none-any.whl", hash = "sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6"}, ] [[package]] name = "types-pytz" -version = "2024.1.0.20240417" +version = "2024.2.0.20240913" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.1.0.20240417.tar.gz", hash = "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981"}, - {file = "types_pytz-2024.1.0.20240417-py3-none-any.whl", hash = "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"}, + {file = "types-pytz-2024.2.0.20240913.tar.gz", hash = "sha256:4433b5df4a6fc587bbed41716d86a5ba5d832b4378e506f40d34bc9c81df2c24"}, + {file = "types_pytz-2024.2.0.20240913-py3-none-any.whl", hash = "sha256:a1eebf57ebc6e127a99d2fa2ba0a88d2b173784ef9b3defcc2004ab6855a44df"}, ] [[package]] @@ -3198,13 +3198,13 @@ files = [ [[package]] name = "types-redis" -version = "4.6.0.20240819" +version = "4.6.0.20240903" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.20240819.tar.gz", hash = "sha256:08f51f550ad41d0152bd98d77ac9d6d8f761369121710a213642f6036b9a7183"}, - {file = "types_redis-4.6.0.20240819-py3-none-any.whl", hash = "sha256:86db9af6f0033154e12bc22c77236cef0907b995fda8c9f0f0eacd59943ed2fc"}, + {file = "types-redis-4.6.0.20240903.tar.gz", hash = "sha256:4bab1a378dbf23c2c95c370dfdb89a8f033957c4fd1a53fee71b529c182fe008"}, + {file = "types_redis-4.6.0.20240903-py3-none-any.whl", hash = "sha256:0e7537e5c085fe96b7d468d5edae0cf667b4ba4b62c6e4a5dfc340bd3b868c23"}, ] [package.dependencies] @@ -3213,13 +3213,13 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20240712" +version = "2.32.0.20240907" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240712.tar.gz", hash = "sha256:90c079ff05e549f6bf50e02e910210b98b8ff1ebdd18e19c873cd237737c1358"}, - {file = "types_requests-2.32.0.20240712-py3-none-any.whl", hash = "sha256:f754283e152c752e46e70942fa2a146b5bc70393522257bb85bd1ef7e019dcc3"}, + {file = "types-requests-2.32.0.20240907.tar.gz", hash = "sha256:ff33935f061b5e81ec87997e91050f7b4af4f82027a7a7a9d9aaea04a963fdf8"}, + {file = "types_requests-2.32.0.20240907-py3-none-any.whl", hash = "sha256:1d1e79faeaf9d42def77f3c304893dea17a97cae98168ac69f3cb465516ee8da"}, ] [package.dependencies] From a554f46b1b8ac70f110bb8b63427102efa87fb0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:59:30 +0100 Subject: [PATCH 464/567] Bump the production-dependencies group across 1 directory with 11 updates (#1519) Bumps the production-dependencies group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [google-cloud-datastore](https://github.com/googleapis/python-datastore) | `2.19.0` | `2.20.1` | | [grpcio](https://github.com/grpc/grpc) | `1.65.2` | `1.66.1` | | [gunicorn](https://github.com/benoitc/gunicorn) | `22.0.0` | `23.0.0` | | [pyyaml](https://github.com/yaml/pyyaml) | `6.0.1` | `6.0.2` | | [boto3](https://github.com/boto/boto3) | `1.34.151` | `1.35.18` | | [marshmallow](https://github.com/marshmallow-code/marshmallow) | `3.21.3` | `3.22.0` | | [python-snappy](https://github.com/intake/python-snappy) | `0.7.2` | `0.7.3` | | [google-cloud-storage](https://github.com/googleapis/python-storage) | `2.18.0` | `2.18.2` | | [google-cloud-pubsub](https://github.com/googleapis/python-pubsub) | `2.23.0` | `2.23.1` | | [simplejson](https://github.com/simplejson/simplejson) | `3.19.2` | `3.19.3` | | [cachetools](https://github.com/tkem/cachetools) | `5.4.0` | `5.5.0` | Updates `google-cloud-datastore` from 2.19.0 to 2.20.1 - [Release notes](https://github.com/googleapis/python-datastore/releases) - [Changelog](https://github.com/googleapis/python-datastore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/python-datastore/compare/v2.19.0...v2.20.1) Updates `grpcio` from 1.65.2 to 1.66.1 - [Release notes](https://github.com/grpc/grpc/releases) - [Changelog](https://github.com/grpc/grpc/blob/master/doc/grpc_release_schedule.md) - [Commits](https://github.com/grpc/grpc/compare/v1.65.2...v1.66.1) Updates `gunicorn` from 22.0.0 to 23.0.0 - [Release notes](https://github.com/benoitc/gunicorn/releases) - [Commits](https://github.com/benoitc/gunicorn/compare/22.0.0...23.0.0) Updates `pyyaml` from 6.0.1 to 6.0.2 - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/main/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/6.0.1...6.0.2) Updates `boto3` from 1.34.151 to 1.35.18 - [Release notes](https://github.com/boto/boto3/releases) - [Commits](https://github.com/boto/boto3/compare/1.34.151...1.35.18) Updates `marshmallow` from 3.21.3 to 3.22.0 - [Changelog](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst) - [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.21.3...3.22.0) Updates `python-snappy` from 0.7.2 to 0.7.3 - [Commits](https://github.com/intake/python-snappy/commits) Updates `google-cloud-storage` from 2.18.0 to 2.18.2 - [Release notes](https://github.com/googleapis/python-storage/releases) - [Changelog](https://github.com/googleapis/python-storage/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/python-storage/compare/v2.18.0...v2.18.2) Updates `google-cloud-pubsub` from 2.23.0 to 2.23.1 - [Release notes](https://github.com/googleapis/python-pubsub/releases) - [Changelog](https://github.com/googleapis/python-pubsub/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/python-pubsub/compare/v2.23.0...v2.23.1) Updates `simplejson` from 3.19.2 to 3.19.3 - [Release notes](https://github.com/simplejson/simplejson/releases) - [Changelog](https://github.com/simplejson/simplejson/blob/master/CHANGES.txt) - [Commits](https://github.com/simplejson/simplejson/compare/v3.19.2...v3.19.3) Updates `cachetools` from 5.4.0 to 5.5.0 - [Changelog](https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst) - [Commits](https://github.com/tkem/cachetools/compare/v5.4.0...v5.5.0) --- updated-dependencies: - dependency-name: google-cloud-datastore dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: grpcio dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: gunicorn dependency-type: direct:production update-type: version-update:semver-major dependency-group: production-dependencies - dependency-name: pyyaml dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: boto3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: marshmallow dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: python-snappy dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: google-cloud-storage dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: google-cloud-pubsub dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: simplejson dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies - dependency-name: cachetools dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- poetry.lock | 497 +++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 257 insertions(+), 242 deletions(-) diff --git a/poetry.lock b/poetry.lock index bb5d22533f..68e3151f38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.151" +version = "1.35.18" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.151-py3-none-any.whl", hash = "sha256:35bc76faacf1667d3fbb66c1966acf2230ef26206557efc26d9d9d79337bef43"}, - {file = "boto3-1.34.151.tar.gz", hash = "sha256:30498a76b6f651ee2af7ae8edc1704379279ab8b91f1a8dd1f4ddf51259b0bc2"}, + {file = "boto3-1.35.18-py3-none-any.whl", hash = "sha256:71e237d3997cf93425947854d7b121c577944f391ba633afb0659e1015364704"}, + {file = "boto3-1.35.18.tar.gz", hash = "sha256:fd130308f1f49d748a5fc63de92de79a995b51c79af3947ddde8815fcf0684fe"}, ] [package.dependencies] -botocore = ">=1.34.151,<1.35.0" +botocore = ">=1.35.18,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.151" +version = "1.35.18" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.151-py3-none-any.whl", hash = "sha256:9018680d7d4a8060c26d127ceec5ab5b270879f423ea39b863d8a46f3e34c404"}, - {file = "botocore-1.34.151.tar.gz", hash = "sha256:0d0968e427a94378f295b49d59170dad539938487ec948de3d030f06092ec6dc"}, + {file = "botocore-1.35.18-py3-none-any.whl", hash = "sha256:1027083aeb1fe74057273410fd768e018e22f85adfbd717b5a69f578f7812b80"}, + {file = "botocore-1.35.18.tar.gz", hash = "sha256:e59da8b91ab06683d2725b6cbbb0383b30c68a241c3c63363f4c5bff59b3c0c0"}, ] [package.dependencies] @@ -156,7 +156,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.20.11)"] +crt = ["awscrt (==0.21.5)"] [[package]] name = "brotli" @@ -291,13 +291,13 @@ cffi = ">=1.0.0" [[package]] name = "cachetools" -version = "5.4.0" +version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, - {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] @@ -1225,33 +1225,34 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-datastore" -version = "2.19.0" +version = "2.20.1" description = "Google Cloud Datastore API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-datastore-2.19.0.tar.gz", hash = "sha256:07fc5870a0261f25466c557c134df95a96dfd2537abd088b9d537fbabe99b974"}, - {file = "google_cloud_datastore-2.19.0-py2.py3-none-any.whl", hash = "sha256:c52086670d4c3779ea7bd8f8353b093a9b5e81c6606f36ffcdf46e6ce9fc80c0"}, + {file = "google_cloud_datastore-2.20.1-py2.py3-none-any.whl", hash = "sha256:b9383af24d8e90ed6c5d161d72411d82efd9b21c051fa6f4bbd743a49d37ffb3"}, + {file = "google_cloud_datastore-2.20.1.tar.gz", hash = "sha256:07950b9c8865087c565f45fa3fdd7a05d4c3d99adf79e10c3f596ff08a7d9bba"}, ] [package.dependencies] google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" google-cloud-core = ">=1.4.0,<3.0.0dev" proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [package.extras] libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.23.0" +version = "2.23.1" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.23.0-py2.py3-none-any.whl", hash = "sha256:d341b2df8b105d0e3774b4bc9173bc0cf26bced31a4736cd9eefa33453b75dff"}, - {file = "google_cloud_pubsub-2.23.0.tar.gz", hash = "sha256:cf3d6f2ab11b5c8dfc0aa7d4cae5ee1d66b408d9666f1762c9c17e269cd8b658"}, + {file = "google_cloud_pubsub-2.23.1-py2.py3-none-any.whl", hash = "sha256:a173292a699851eb622016d3f2796ecf2d69692e708ea0e7382f338fc1679f8a"}, + {file = "google_cloud_pubsub-2.23.1.tar.gz", hash = "sha256:e1fde79b5b64b721290af4c022907afcbb83512d92f4e5c334c391cfbb022acb"}, ] [package.dependencies] @@ -1268,13 +1269,13 @@ libcst = ["libcst (>=0.3.10)"] [[package]] name = "google-cloud-storage" -version = "2.18.0" +version = "2.18.2" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_storage-2.18.0-py2.py3-none-any.whl", hash = "sha256:e8e1a9577952143c3fca8163005ecfadd2d70ec080fa158a8b305000e2c22fbb"}, - {file = "google_cloud_storage-2.18.0.tar.gz", hash = "sha256:0aa3f7c57f3632f81b455d91558d2b27ada96eee2de3aaa17f689db1470d9578"}, + {file = "google_cloud_storage-2.18.2-py2.py3-none-any.whl", hash = "sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166"}, + {file = "google_cloud_storage-2.18.2.tar.gz", hash = "sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99"}, ] [package.dependencies] @@ -1282,7 +1283,7 @@ google-api-core = ">=2.15.0,<3.0.0dev" google-auth = ">=2.26.1,<3.0dev" google-cloud-core = ">=2.3.0,<3.0dev" google-crc32c = ">=1.0,<2.0dev" -google-resumable-media = ">=2.6.0" +google-resumable-media = ">=2.7.2" requests = ">=2.18.0,<3.0.0dev" [package.extras] @@ -1389,13 +1390,13 @@ testing = ["pytest"] [[package]] name = "google-resumable-media" -version = "2.7.1" +version = "2.7.2" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false python-versions = ">=3.7" files = [ - {file = "google-resumable-media-2.7.1.tar.gz", hash = "sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33"}, - {file = "google_resumable_media-2.7.1-py2.py3-none-any.whl", hash = "sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c"}, + {file = "google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa"}, + {file = "google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0"}, ] [package.dependencies] @@ -1512,61 +1513,61 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.65.2" +version = "1.66.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.65.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:51231a22aea830be1d955de5a15da4391b3ac8e1d7868f362c74c15a0e9f5c89"}, - {file = "grpcio-1.65.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:87da0fb85ba42257e450561b0264e36abe47faae07476621ae65d8f5f60f22cd"}, - {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:3a6b36e20b02ca830b15b5eb4abb437de1d42ba93353d1f76b00337108f7ce8e"}, - {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03fdd86ff7d9957b822b9bf1fe0ae1e21e258e9c1d5535a5e9c67de0ad45b6a8"}, - {file = "grpcio-1.65.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e5a67bbf8a1b3be5535802f6e9f507d1d8d38fb32de81ec7f03706d95a9126"}, - {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2ce639f2a2951aedbe9a3636f5730288f9b77c2627f116265d7d2789555e5662"}, - {file = "grpcio-1.65.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b10349ceebec745a47e4339ef7c4878c9b53b82ae4b0883e16544625016d6242"}, - {file = "grpcio-1.65.2-cp310-cp310-win32.whl", hash = "sha256:f931fe9b244dc23c7478c513c3ed94ded93da8bd1a95a4d97b21abdef644304a"}, - {file = "grpcio-1.65.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c9c865d2fdf40e7e952038a0b5e0f32b01da84ecf04943b08e8917c8ccc9cf8"}, - {file = "grpcio-1.65.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:f4b7a7d68313e252e09550bd03d9d11e460dae681cf95588a131b6b3e07d1e30"}, - {file = "grpcio-1.65.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ba9d4b3d4fc00b8083bb47a8c40a74ba3ea330713fdd59cf53c926c9a16b002"}, - {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b7bfcbee6b32f0e4786b7813692b3907c9e444f529126b8520cac9914479b98c"}, - {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aa50787bc8036bd5ea9b7ebbbd2c49c78122eb9ff98d3c217a7c146313c5030"}, - {file = "grpcio-1.65.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd7dc770926cc66050242eb6c63ca8ce12cd69010bf4ff7ea6e721d4f4b11e4d"}, - {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c45977fdc675a8961875adab7f04b785f65d3fd9c737cd60b5e3a9b1392ad444"}, - {file = "grpcio-1.65.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a0cd7297abf0a02a9399edebe8c662058c7f0768bfbe859837707d389ad327f"}, - {file = "grpcio-1.65.2-cp311-cp311-win32.whl", hash = "sha256:60fe2f90875f2bef105158e370fbbefadd179f8cd689bc2cee6844aca4ccb7bb"}, - {file = "grpcio-1.65.2-cp311-cp311-win_amd64.whl", hash = "sha256:e0b2bf34340999c6d938107ec2cc9bce1ea59bf08e4694cfa47e782bdbd361f4"}, - {file = "grpcio-1.65.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:71fa3b7a6cef62a00014205d0e707610cfd50ae54f617d296017f10c6a9fad0d"}, - {file = "grpcio-1.65.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8856187a359a55babfa4d49ad96f2dd7edd8be3a36b813c7a9e41ef3d763400f"}, - {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:cb48342de1c3be59e6de79c6bbc01cf05562c571a3ed32f7c2e149e7934824cf"}, - {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b193e116e085ad4d7ef1518d79e9fedfa7688f4967f64a6246b5b196a26326a"}, - {file = "grpcio-1.65.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ce7f4c766fecc34455357b31b1e316506ea6ac48abbb9a650843d20337a2036"}, - {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:76125096d2a090d4acdce0f06f9511cebe1bcfbc0bd040e495563d7a8747dda1"}, - {file = "grpcio-1.65.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4fba3ae83ef5acd111c2dd92233ff167411db84e1ff17a00c34b5428355526c5"}, - {file = "grpcio-1.65.2-cp312-cp312-win32.whl", hash = "sha256:7fd639b0988ed5114d4b2a72ea453aafcb1439dd433c61834886b92afed9c6c1"}, - {file = "grpcio-1.65.2-cp312-cp312-win_amd64.whl", hash = "sha256:b6bba0f973ef6fe7434834f1b63d16bab4b50879d5bb0ca6eb0495c87d5cbc78"}, - {file = "grpcio-1.65.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:510bf7ec7f44e9420bb17970fb450522666d8b1c09cdf59b735de0c2dc806b79"}, - {file = "grpcio-1.65.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aacfd499d23130578184057008ea5329732a5ac59a4fcb73c0467d86723d23c8"}, - {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:67c5e5aa92b5832ae7a3399bce5b8562fb28686446732bfa17f97d5082e8501d"}, - {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7b752471e7ff1472ddbf3035a34fd8e24f2eac4fedbdab311e8f3e0dee889f7"}, - {file = "grpcio-1.65.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3101fa25b93f185e8cc698f8c2abee897891e6bae4f13472f66df21e8ae40d46"}, - {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:01600b1b02fdc9d648630d3de0a4cbf7ebe5f94b40ec1f65e3fd4b94a3b052cf"}, - {file = "grpcio-1.65.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8886d24345bf4b1693e9c09cf6a520f0baedd2af2a876f91bb508b24d0d46041"}, - {file = "grpcio-1.65.2-cp38-cp38-win32.whl", hash = "sha256:0b2ae6868864e4b06bff89cf91730a63141327158bf0677428ef315ea1dbdb0b"}, - {file = "grpcio-1.65.2-cp38-cp38-win_amd64.whl", hash = "sha256:c2900ad06fd8f5ad8832b1ee287caccb4a957e971b2b7983e0cd7a8e7c7098fb"}, - {file = "grpcio-1.65.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:06a7ea12a81e5e2fb17528556c7f828b90bd2aec3a645f5cd5f35f80aa59ac6a"}, - {file = "grpcio-1.65.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5edea0ea18e9fd5326d385a4c92a1fed605454e9a2c57ff131df0a08004b7e69"}, - {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d388f093010a014d3b3ddf8185ff45c5279fd825d0b20e21c8076515ae61db31"}, - {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5225b8ce980b598187f64436ed95ea149966d538253c28668347d331968e2386"}, - {file = "grpcio-1.65.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892f03939df46d0bfcf89fe1dbcc8818f93ad6f3377587e8db6c2b1f598736c2"}, - {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77fddf42bbca65ee4db679d0608e1ffa8b22b7f516c79665b7620be2f6357c85"}, - {file = "grpcio-1.65.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3a3139414399078560a84203f9fe3592483d902a2af84062c571be6191143a9f"}, - {file = "grpcio-1.65.2-cp39-cp39-win32.whl", hash = "sha256:8d6fd1206433428d0a4ba771eac70579b41a265fe835a4d8a5214c7235e69926"}, - {file = "grpcio-1.65.2-cp39-cp39-win_amd64.whl", hash = "sha256:478725160e2cfc1bfa5ab3e7bb7c896cc182c8f57255d780007cfd6fb46e97b5"}, - {file = "grpcio-1.65.2.tar.gz", hash = "sha256:e2c9bbb84d5517f2bccdb1836b8ee267a1757acb3cb3e575065c103220b577ac"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.65.2)"] + {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, + {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, + {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, + {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, + {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, + {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, + {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, + {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, + {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, + {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, + {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, + {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, + {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, + {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, + {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, + {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, + {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, + {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, + {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, + {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, + {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.66.1)"] [[package]] name = "grpcio-status" @@ -1586,13 +1587,13 @@ protobuf = ">=4.21.6" [[package]] name = "gunicorn" -version = "22.0.0" +version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" files = [ - {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, - {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, ] [package.dependencies] @@ -1911,13 +1912,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.3" +version = "3.22.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, - {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, + {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, + {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, ] [package.dependencies] @@ -1925,7 +1926,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -2491,13 +2492,13 @@ six = ">=1.5" [[package]] name = "python-snappy" -version = "0.7.2" +version = "0.7.3" description = "Python library for the snappy compression library from Google" optional = false python-versions = "*" files = [ - {file = "python_snappy-0.7.2-py3-none-any.whl", hash = "sha256:b4b2c39142064925d5a554672a09de4188fc4f2b2494a2ecb35042930e129444"}, - {file = "python_snappy-0.7.2.tar.gz", hash = "sha256:04bf182f9d9f67b7a846dae2f1df36180ceeee8d3380e4b6799deff5272c4978"}, + {file = "python_snappy-0.7.3-py3-none-any.whl", hash = "sha256:074c0636cfcd97e7251330f428064050ac81a52c62ed884fc2ddebbb60ed7f50"}, + {file = "python_snappy-0.7.3.tar.gz", hash = "sha256:40216c1badfb2d38ac781ecb162a1d0ec40f8ee9747e610bcfefdfa79486cee3"}, ] [package.dependencies] @@ -2516,62 +2517,64 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -2923,109 +2926,121 @@ test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata [[package]] name = "simplejson" -version = "3.19.2" +version = "3.19.3" description = "Simple, fast, extensible JSON encoder/decoder for Python" optional = false -python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "simplejson-3.19.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2"}, - {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2"}, - {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867"}, - {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a"}, - {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0"}, - {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69"}, - {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973"}, - {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835"}, - {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad"}, - {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6"}, - {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402"}, - {file = "simplejson-3.19.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0"}, - {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48"}, - {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428"}, - {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5"}, - {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb"}, - {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e"}, - {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c"}, - {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3"}, - {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672"}, - {file = "simplejson-3.19.2-cp310-cp310-win32.whl", hash = "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7"}, - {file = "simplejson-3.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a"}, - {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c"}, - {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4"}, - {file = "simplejson-3.19.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba"}, - {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13"}, - {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b"}, - {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf"}, - {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9"}, - {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e"}, - {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414"}, - {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3"}, - {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f"}, - {file = "simplejson-3.19.2-cp311-cp311-win32.whl", hash = "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b"}, - {file = "simplejson-3.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589"}, - {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8"}, - {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378"}, - {file = "simplejson-3.19.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374"}, - {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a"}, - {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6"}, - {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a"}, - {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f"}, - {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734"}, - {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2"}, - {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b"}, - {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb"}, - {file = "simplejson-3.19.2-cp312-cp312-win32.whl", hash = "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917"}, - {file = "simplejson-3.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f"}, - {file = "simplejson-3.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae"}, - {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816"}, - {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d"}, - {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b"}, - {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664"}, - {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5"}, - {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c"}, - {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28"}, - {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc"}, - {file = "simplejson-3.19.2-cp36-cp36m-win32.whl", hash = "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50"}, - {file = "simplejson-3.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f"}, - {file = "simplejson-3.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d"}, - {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290"}, - {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3"}, - {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80"}, - {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4"}, - {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f"}, - {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e"}, - {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2"}, - {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b"}, - {file = "simplejson-3.19.2-cp37-cp37m-win32.whl", hash = "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693"}, - {file = "simplejson-3.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc"}, - {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46"}, - {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087"}, - {file = "simplejson-3.19.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2"}, - {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41"}, - {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17"}, - {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0"}, - {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832"}, - {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a"}, - {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b"}, - {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded"}, - {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f"}, - {file = "simplejson-3.19.2-cp38-cp38-win32.whl", hash = "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637"}, - {file = "simplejson-3.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137"}, - {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565"}, - {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358"}, - {file = "simplejson-3.19.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c"}, - {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a"}, - {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb"}, - {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c"}, - {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9"}, - {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd"}, - {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d"}, - {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd"}, - {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff"}, - {file = "simplejson-3.19.2-cp39-cp39-win32.whl", hash = "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23"}, - {file = "simplejson-3.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4"}, - {file = "simplejson-3.19.2-py3-none-any.whl", hash = "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb"}, - {file = "simplejson-3.19.2.tar.gz", hash = "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c"}, +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5" +files = [ + {file = "simplejson-3.19.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f39caec26007a2d0efab6b8b1d74873ede9351962707afab622cc2285dd26ed0"}, + {file = "simplejson-3.19.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:83c87706265ae3028e8460d08b05f30254c569772e859e5ba61fe8af2c883468"}, + {file = "simplejson-3.19.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0b5ddd2c7d1d3f4d23224bc8a04bbf1430ae9a8149c05b90f8fc610f7f857a23"}, + {file = "simplejson-3.19.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ad0e0b1ce9bd3edb5cf64b5b5b76eacbfdac8c5367153aeeec8a8b1407f68342"}, + {file = "simplejson-3.19.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:93be280fc69a952c76e261036312c20b910e7fa9e234f1d89bdfe3fa34f8a023"}, + {file = "simplejson-3.19.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6d43e24b88c80f997081503f693be832fc90854f278df277dd54f8a4c847ab61"}, + {file = "simplejson-3.19.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2876027ebdd599d730d36464debe84619b0368e9a642ca6e7c601be55aed439e"}, + {file = "simplejson-3.19.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:0766ca6222b410e08e0053a0dda3606cafb3973d5d00538307f631bb59743396"}, + {file = "simplejson-3.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50d8b742d74c449c4dcac570d08ce0f21f6a149d2d9cf7652dbf2ba9a1bc729a"}, + {file = "simplejson-3.19.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd011fc3c1d88b779645495fdb8189fb318a26981eebcce14109460e062f209b"}, + {file = "simplejson-3.19.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:637c4d4b81825c1f4d651e56210bd35b5604034b192b02d2d8f17f7ce8c18f42"}, + {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f56eb03bc9e432bb81adc8ecff2486d39feb371abb442964ffb44f6db23b332"}, + {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef59a53be400c1fad2c914b8d74c9d42384fed5174f9321dd021b7017fd40270"}, + {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72e8abbc86fcac83629a030888b45fed3a404d54161118be52cb491cd6975d3e"}, + {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8efb03ca77bd7725dfacc9254df00d73e6f43013cf39bd37ef1a8ed0ebb5165"}, + {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:add8850db04b98507a8b62d248a326ecc8561e6d24336d1ca5c605bbfaab4cad"}, + {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fc3dc9fb413fc34c396f52f4c87de18d0bd5023804afa8ab5cc224deeb6a9900"}, + {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dfa420bb9225dd33b6efdabde7c6a671b51150b9b1d9c4e5cd74d3b420b3fe1"}, + {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7b5c472099b39b274dcde27f1113db8d818c9aa3ba8f78cbb8ad04a4c1ac2118"}, + {file = "simplejson-3.19.3-cp310-cp310-win32.whl", hash = "sha256:817abad79241ed4a507b3caf4d3f2be5079f39d35d4c550a061988986bffd2ec"}, + {file = "simplejson-3.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:dd5b9b1783e14803e362a558680d88939e830db2466f3fa22df5c9319f8eea94"}, + {file = "simplejson-3.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e88abff510dcff903a18d11c2a75f9964e768d99c8d147839913886144b2065e"}, + {file = "simplejson-3.19.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934a50a614fb831614db5dbfba35127ee277624dda4d15895c957d2f5d48610c"}, + {file = "simplejson-3.19.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:212fce86a22188b0c7f53533b0f693ea9605c1a0f02c84c475a30616f55a744d"}, + {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d9e8f836688a8fabe6a6b41b334aa550a6823f7b4ac3d3712fc0ad8655be9a8"}, + {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23228037dc5d41c36666384062904d74409a62f52283d9858fa12f4c22cffad1"}, + {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0791f64fed7d4abad639491f8a6b1ba56d3c604eb94b50f8697359b92d983f36"}, + {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f614581b61a26fbbba232a1391f6cee82bc26f2abbb6a0b44a9bba25c56a1c"}, + {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1df0aaf1cb787fdf34484ed4a1f0c545efd8811f6028623290fef1a53694e597"}, + {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:951095be8d4451a7182403354c22ec2de3e513e0cc40408b689af08d02611588"}, + {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a954b30810988feeabde843e3263bf187697e0eb5037396276db3612434049b"}, + {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c40df31a75de98db2cdfead6074d4449cd009e79f54c1ebe5e5f1f153c68ad20"}, + {file = "simplejson-3.19.3-cp311-cp311-win32.whl", hash = "sha256:7e2a098c21ad8924076a12b6c178965d88a0ad75d1de67e1afa0a66878f277a5"}, + {file = "simplejson-3.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:c9bedebdc5fdad48af8783022bae307746d54006b783007d1d3c38e10872a2c6"}, + {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99"}, + {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333"}, + {file = "simplejson-3.19.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42e5acf80d4d971238d4df97811286a044d720693092b20a56d5e56b7dcc5d09"}, + {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b0efc7279d768db7c74d3d07f0b5c81280d16ae3fb14e9081dc903e8360771"}, + {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0552eb06e7234da892e1d02365cd2b7b2b1f8233aa5aabdb2981587b7cc92ea0"}, + {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf6a3b9a7d7191471b464fe38f684df10eb491ec9ea454003edb45a011ab187"}, + {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7017329ca8d4dca94ad5e59f496e5fc77630aecfc39df381ffc1d37fb6b25832"}, + {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67a20641afebf4cfbcff50061f07daad1eace6e7b31d7622b6fa2c40d43900ba"}, + {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd6a7dabcc4c32daf601bc45e01b79175dde4b52548becea4f9545b0a4428169"}, + {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08f9b443a94e72dd02c87098c96886d35790e79e46b24e67accafbf13b73d43b"}, + {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa97278ae6614346b5ca41a45a911f37a3261b57dbe4a00602048652c862c28b"}, + {file = "simplejson-3.19.3-cp312-cp312-win32.whl", hash = "sha256:ef28c3b328d29b5e2756903aed888960bc5df39b4c2eab157ae212f70ed5bf74"}, + {file = "simplejson-3.19.3-cp312-cp312-win_amd64.whl", hash = "sha256:1e662336db50ad665777e6548b5076329a94a0c3d4a0472971c588b3ef27de3a"}, + {file = "simplejson-3.19.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0959e6cb62e3994b5a40e31047ff97ef5c4138875fae31659bead691bed55896"}, + {file = "simplejson-3.19.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a7bfad839c624e139a4863007233a3f194e7c51551081f9789cba52e4da5167"}, + {file = "simplejson-3.19.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afab2f7f2486a866ff04d6d905e9386ca6a231379181a3838abce1f32fbdcc37"}, + {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00313681015ac498e1736b304446ee6d1c72c5b287cd196996dad84369998f7"}, + {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d936ae682d5b878af9d9eb4d8bb1fdd5e41275c8eb59ceddb0aeed857bb264a2"}, + {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c6657485393f2e9b8177c77a7634f13ebe70d5e6de150aae1677d91516ce6b"}, + {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a6a750d3c7461b1c47cfc6bba8d9e57a455e7c5f80057d2a82f738040dd1129"}, + {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea7a4a998c87c5674a27089e022110a1a08a7753f21af3baf09efe9915c23c3c"}, + {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6300680d83a399be2b8f3b0ef7ef90b35d2a29fe6e9c21438097e0938bbc1564"}, + {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ab69f811a660c362651ae395eba8ce84f84c944cea0df5718ea0ba9d1e4e7252"}, + {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:256e09d0f94d9c3d177d9e95fd27a68c875a4baa2046633df387b86b652f5747"}, + {file = "simplejson-3.19.3-cp313-cp313-win32.whl", hash = "sha256:2c78293470313aefa9cfc5e3f75ca0635721fb016fb1121c1c5b0cb8cc74712a"}, + {file = "simplejson-3.19.3-cp313-cp313-win_amd64.whl", hash = "sha256:3bbcdc438dc1683b35f7a8dc100960c721f922f9ede8127f63bed7dfded4c64c"}, + {file = "simplejson-3.19.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:89b35433186e977fa86ff1fd179c1fadff39cfa3afa1648dab0b6ca53153acd9"}, + {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d43c2d7504eda566c50203cdc9dc043aff6f55f1b7dae0dcd79dfefef9159d1c"}, + {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6890ff9cf0bd2e1d487e2a8869ebd620a44684c0a9667fa5ee751d099d5d84c8"}, + {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1069143a8fb3905e1bc0696c62be7e3adf812e9f1976ac9ae15b05112ff57cc9"}, + {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb324bb903330cbb35d87cce367a12631cd5720afa06e5b9c906483970946da6"}, + {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:0a32859d45d7b85fb803bb68f6bee14526991a1190269116c33399fa0daf9bbf"}, + {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:23833ee7e791ec968b744dfee2a2d39df7152050051096caf4296506d75608d8"}, + {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:d73efb03c5b39249c82488a994f0998f9e4399e3d085209d2120503305ba77a8"}, + {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7923878b7a0142d39763ec2dbecff3053c1bedd3653585a8474666e420fe83f5"}, + {file = "simplejson-3.19.3-cp36-cp36m-win32.whl", hash = "sha256:7355c7203353c36d46c4e7b6055293b3d2be097bbc5e2874a2b8a7259f0325dd"}, + {file = "simplejson-3.19.3-cp36-cp36m-win_amd64.whl", hash = "sha256:d1b8b4d6379fe55f471914345fe6171d81a18649dacf3248abfc9c349b4442eb"}, + {file = "simplejson-3.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d36608557b4dcd7a62c29ad4cd7c5a1720bbf7dc942eff9dc42d2c542a5f042d"}, + {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7137e69c6781ecf23afab064be94a277236c9cba31aa48ff1a0ec3995c69171e"}, + {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76f8c28fe2d426182405b18ddf3001fce47835a557dc15c3d8bdea01c03361da"}, + {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff7bc1bbdaa3e487c9469128bf39408e91f5573901cb852e03af378d3582c52d"}, + {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0782cb9bf827f0c488b6aa0f2819f618308a3caf2973cfd792e45d631bec4db"}, + {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:6fea0716c593dabb4392c4996d4e902a83b2428e6da82938cf28a523a11eb277"}, + {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:8f41bb5370b34f63171e65fdb00e12be1d83675cecb23e627df26f4c88dfc021"}, + {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:37105d1d708365b91165e1a6e505bdecc88637091348cf4b6adcdcb4f5a5fb8b"}, + {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b9198c1f1f8910a3b86b60f4fe2556d9d28d3fefe35bffe6be509a27402e694d"}, + {file = "simplejson-3.19.3-cp37-cp37m-win32.whl", hash = "sha256:bc164f32dd9691e7082ce5df24b4cf8c6c394bbf9bdeeb5d843127cd07ab8ad2"}, + {file = "simplejson-3.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1bd41f2cb1a2c57656ceff67b12d005cb255c728265e222027ad73193a04005a"}, + {file = "simplejson-3.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0733ecd95ae03ae718ec74aad818f5af5f3155d596f7b242acbc1621e765e5fb"}, + {file = "simplejson-3.19.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a0710d1a5e41c4f829caa1572793dd3130c8d65c2b194c24ff29c4c305c26e0"}, + {file = "simplejson-3.19.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1a53a07320c5ff574d8b1a89c937ce33608832f166f39dff0581ac43dc979abd"}, + {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1773cabfba66a6337b547e45dafbd471b09487370bcab75bd28f626520410d29"}, + {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c0104b4b7d2c75ccedbf1d9d5a3bd2daa75e51053935a44ba012e2fd4c43752"}, + {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c49eeb94b8f09dc8a5843c156a22b8bde6aa1ddc65ca8ddc62dddcc001e6a2d"}, + {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dc5c1a85ff388e98ea877042daec3d157b6db0d85bac6ba5498034689793e7e"}, + {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49549e3d81ab4a58424405aa545602674d8c35c20e986b42bb8668e782a94bac"}, + {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e1a1452ad5723ff129b081e3c8aa4ba56b8734fee4223355ed7b815a7ece69bc"}, + {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d0d5a63f1768fed7e78cf55712dee81f5a345e34d34224f3507ebf71df2b754d"}, + {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7e062767ac165df9a46963f5735aa4eee0089ec1e48b3f2ec46182754b96f55e"}, + {file = "simplejson-3.19.3-cp38-cp38-win32.whl", hash = "sha256:56134bbafe458a7b21f6fddbf889d36bec6d903718f4430768e3af822f8e27c2"}, + {file = "simplejson-3.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:bcde83a553a96dc7533736c547bddaa35414a2566ab0ecf7d3964fc4bdb84c11"}, + {file = "simplejson-3.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b5587feda2b65a79da985ae6d116daf6428bf7489992badc29fc96d16cd27b05"}, + {file = "simplejson-3.19.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0d2b00ecbcd1a3c5ea1abc8bb99a26508f758c1759fd01c3be482a3655a176f"}, + {file = "simplejson-3.19.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:32a3ada8f3ea41db35e6d37b86dade03760f804628ec22e4fe775b703d567426"}, + {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f455672f4738b0f47183c5896e3606cd65c9ddee3805a4d18e8c96aa3f47c84"}, + {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b737a5fefedb8333fa50b8db3dcc9b1d18fd6c598f89fa7debff8b46bf4e511"}, + {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb47ee773ce67476a960e2db4a0a906680c54f662521550828c0cc57d0099426"}, + {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eed8cd98a7b24861da9d3d937f5fbfb6657350c547528a117297fe49e3960667"}, + {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:619756f1dd634b5bdf57d9a3914300526c3b348188a765e45b8b08eabef0c94e"}, + {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dd7230d061e755d60a4d5445bae854afe33444cdb182f3815cff26ac9fb29a15"}, + {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:101a3c8392028cd704a93c7cba8926594e775ca3c91e0bee82144e34190903f1"}, + {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e557712fc79f251673aeb3fad3501d7d4da3a27eff0857af2e1d1afbbcf6685"}, + {file = "simplejson-3.19.3-cp39-cp39-win32.whl", hash = "sha256:0bc5544e3128891bf613b9f71813ee2ec9c11574806f74dd8bb84e5e95bf64a2"}, + {file = "simplejson-3.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:06662392e4913dc8846d6a71a6d5de86db5fba244831abe1dd741d62a4136764"}, + {file = "simplejson-3.19.3-py3-none-any.whl", hash = "sha256:49cc4c7b940d43bd12bf87ec63f28cbc4964fc4e12c031cc8cd01650f43eb94e"}, + {file = "simplejson-3.19.3.tar.gz", hash = "sha256:8e086896c36210ab6050f2f9f095a5f1e03c83fa0e7f296d6cba425411364680"}, ] [[package]] @@ -3527,4 +3542,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "542b9a0e39f2f4f3697cdb31e53d8a13d16b88d56133a4694aa5602fae627425" +content-hash = "4133ab93f768c81d508cd979aebf949c95b7ef1684d0b80d057a5070d345cd5b" diff --git a/pyproject.toml b/pyproject.toml index 78b08d89a5..fb5baa0f22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ flask-login = "^0.6.3" flask-wtf = "^1.2.1" google-cloud-datastore = "^2.19.0" grpcio = "^1.64.1" -gunicorn = "^22.0.0" +gunicorn = "^23.0.0" pika = "^1.3.2" pyyaml = "^6.0.1" requests = "^2.32.0" From c4a22d73cddc30b555e15af707f4aba867206141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:48:55 +0100 Subject: [PATCH 465/567] Bump cryptography from 43.0.0 to 43.0.1 in the pip group (#1496) Bumps the pip group with 1 update: [cryptography](https://github.com/pyca/cryptography). Updates `cryptography` from 43.0.0 to 43.0.1 - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/43.0.0...43.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-type: indirect dependency-group: pip ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rmccar <42928680+rmccar@users.noreply.github.com> --- poetry.lock | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/poetry.lock b/poetry.lock index 68e3151f38..c88e5eb30c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -694,38 +694,38 @@ dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-xdi [[package]] name = "cryptography" -version = "43.0.0" +version = "43.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"}, - {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"}, - {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"}, - {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"}, - {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"}, - {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"}, - {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"}, - {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"}, - {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"}, - {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"}, - {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"}, - {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"}, - {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"}, - {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"}, - {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"}, - {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"}, - {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"}, - {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"}, - {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"}, - {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"}, - {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"}, - {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"}, - {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"}, - {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"}, - {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"}, - {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"}, - {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"}, + {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, + {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, + {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, + {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, + {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, + {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, + {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, ] [package.dependencies] @@ -738,7 +738,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] From 5adfe30d5500e129af889ed6c7d854223e1c6d79 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:47:33 +0100 Subject: [PATCH 466/567] Add thank-you template content changes (#1515) --- app/translations/messages.pot | 56 +++++++++++++++++++---------------- templates/thank-you.html | 3 +- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 572c7134bd..40f50f1b39 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-02 16:54+0100\n" +"POT-Creation-Date: 2024-09-12 12:26+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -366,63 +366,63 @@ msgstr "" msgid "Save and exit survey" msgstr "" -#: app/views/contexts/hub_context.py:16 +#: app/views/contexts/hub_context.py:18 msgid "Completed" msgstr "" -#: app/views/contexts/hub_context.py:18 +#: app/views/contexts/hub_context.py:20 msgid "View answers" msgstr "" -#: app/views/contexts/hub_context.py:19 +#: app/views/contexts/hub_context.py:21 msgid "View answers: {section_name}" msgstr "" -#: app/views/contexts/hub_context.py:23 +#: app/views/contexts/hub_context.py:25 msgid "Partially completed" msgstr "" -#: app/views/contexts/hub_context.py:25 +#: app/views/contexts/hub_context.py:27 msgid "Continue with section" msgstr "" -#: app/views/contexts/hub_context.py:26 +#: app/views/contexts/hub_context.py:28 msgid "Continue with section: {section_name}" msgstr "" -#: app/views/contexts/hub_context.py:30 +#: app/views/contexts/hub_context.py:32 msgid "Not started" msgstr "" -#: app/views/contexts/hub_context.py:32 +#: app/views/contexts/hub_context.py:34 msgid "Start section" msgstr "" -#: app/views/contexts/hub_context.py:33 +#: app/views/contexts/hub_context.py:35 msgid "Start section: {section_name}" msgstr "" -#: app/views/contexts/hub_context.py:37 +#: app/views/contexts/hub_context.py:39 msgid "Separate census requested" msgstr "" -#: app/views/contexts/hub_context.py:39 app/views/contexts/hub_context.py:40 +#: app/views/contexts/hub_context.py:41 app/views/contexts/hub_context.py:42 msgid "Change or resend" msgstr "" -#: app/views/contexts/hub_context.py:52 app/views/contexts/hub_context.py:53 +#: app/views/contexts/hub_context.py:54 app/views/contexts/hub_context.py:55 msgid "Submit survey" msgstr "" -#: app/views/contexts/hub_context.py:57 +#: app/views/contexts/hub_context.py:59 msgid "You must submit this survey to complete it" msgstr "" -#: app/views/contexts/hub_context.py:64 +#: app/views/contexts/hub_context.py:66 msgid "Choose another section to complete" msgstr "" -#: app/views/contexts/hub_context.py:65 templates/confirm-email.html:28 +#: app/views/contexts/hub_context.py:67 templates/confirm-email.html:28 #: templates/individual_response/confirmation-post.html:26 #: templates/individual_response/confirmation-text-message.html:39 #: templates/individual_response/question.html:5 templates/interstitial.html:7 @@ -912,37 +912,41 @@ msgstr "" msgid "Get a copy of your answers" msgstr "" -#: templates/thank-you.html:77 +#: templates/thank-you.html:76 +msgid "We may contact you to query your answers." +msgstr "" + +#: templates/thank-you.html:78 msgid "" -"You can save or " -"print your answers for your records." +"If you need a copy for your records, save or print your answers." msgstr "" -#: templates/layouts/_base.html:148 templates/thank-you.html:83 +#: templates/layouts/_base.html:148 templates/thank-you.html:84 msgid "minute" msgstr "" -#: templates/layouts/_base.html:149 templates/thank-you.html:84 +#: templates/layouts/_base.html:149 templates/thank-you.html:85 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:150 templates/thank-you.html:85 +#: templates/layouts/_base.html:150 templates/thank-you.html:86 msgid "second" msgstr "" -#: templates/layouts/_base.html:151 templates/thank-you.html:86 +#: templates/layouts/_base.html:151 templates/thank-you.html:87 msgid "seconds" msgstr "" -#: templates/thank-you.html:88 +#: templates/thank-you.html:89 msgid "For security, your answers will only be available to view for 45 minutes" msgstr "" -#: templates/thank-you.html:98 +#: templates/thank-you.html:99 msgid "Get confirmation email" msgstr "" -#: templates/thank-you.html:100 +#: templates/thank-you.html:101 msgid "" "If you would like to be sent confirmation that you have completed your " "survey, enter your email address" diff --git a/templates/thank-you.html b/templates/thank-you.html index 9221e96c66..6c952d5d50 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -73,8 +73,9 @@

      {% else %} {% set countdown_text = _("For security, your answers will only be available to view for another ") %}

      {{ _("Get a copy of your answers") }}

      +

      {{ _("We may contact you to query your answers.") }}

      - {{ _("You can save or print your answers for your records.").format(url = content.view_submitted_response.url) }} + {{ _("If you need a copy for your records, save or print your answers.").format(url = content.view_submitted_response.url) }}

      {# djlint:off #} {{ onsTimeoutPanel ({ From 81bdf26db937020e83624be897d144b93d4d7984 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:41:50 +0100 Subject: [PATCH 467/567] Fix integration test generator script and makefile command (#1441) * Fix integration test generator script and makefile command * Remove test duplication and no schema name error --- Makefile | 1 - scripts/generate_integration_test.py | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 7b92690e02..7e721c8086 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,5 @@ profile: poetry run python profile_application.py generate-integration-test: - poetry run playwright install chromium poetry run python -m scripts.generate_integration_test poetry run black ./scripts/test_* diff --git a/scripts/generate_integration_test.py b/scripts/generate_integration_test.py index c5f9e1efa3..551e129960 100644 --- a/scripts/generate_integration_test.py +++ b/scripts/generate_integration_test.py @@ -91,7 +91,7 @@ def process_launcher_request(request: Request) -> None: with open(output["file_name"], "a", encoding="utf-8") as file: path = f'"{urlparse(request.url).path}"' file.write(generate_method_request(method="get", data=path)) - else: + elif "schema_name" in parse_qs(request.url): # start of journey, so create a skeleton file using the schema name survey_journey["schema_name"] = parse_qs(request.url)["schema_name"][0] output["file_name"] = f"./scripts/{survey_journey['schema_name']}.py" @@ -99,10 +99,14 @@ def process_launcher_request(request: Request) -> None: with open(output["file_name"], "w", encoding="utf-8") as file: # Type ignore: schema_name is taken as string from query string class_name = survey_journey["schema_name"].title().replace("_", "") # type: ignore + function_name = survey_journey["schema_name"] + if not class_name.lower().startswith("test"): + class_name = f"Test{class_name}" + function_name = f"test_{survey_journey['schema_name']}" file.write( TEST_TEMPLATE.format( class_name=class_name, - function_name=survey_journey["schema_name"], + function_name=function_name, schema_name=survey_journey["schema_name"], ) ) @@ -124,7 +128,9 @@ def request_handler(request: Request) -> None: def run(pw: Playwright) -> None: chromium = pw.chromium - browser = chromium.launch(headless=False, args=["--start-maximized"]) + browser = chromium.launch( + headless=False, args=["--start-maximized"], channel="chrome" + ) page = browser.new_page(no_viewport=True) page.goto(LAUNCHER_ROOT_URL) From ee84c7f133f558f00427bc3eee5f17a11bcdcdf2 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:47:51 +0100 Subject: [PATCH 468/567] Add custom gtag event (#1460) --- app/helpers/template_helpers.py | 10 +++---- app/survey_config/survey_config.py | 5 ---- templates/layouts/_base.html | 5 ++-- tests/app/helpers/test_template_helpers.py | 26 +++++++++---------- .../integration/test_application_variables.py | 4 +-- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index 75c9c68244..b5bc4d3751 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -119,20 +119,16 @@ def service_links_context( @property def data_layer_context( self, - ) -> list[dict]: + ) -> dict[str, str]: tx_id_context = ( {"tx_id": metadata.tx_id} if (metadata := get_metadata(current_user)) - else None + else {} ) - additional_context = self._survey_config.get_additional_data_layer_context() schema_context = { key: value for key in DATA_LAYER_KEYS if (value := cookie_session.get(key)) } - context = [*additional_context, schema_context] - if tx_id_context: - context.append(tx_id_context) - return context + return tx_id_context | schema_context @property def footer_context(self) -> dict[str, Any]: diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index c9cd640d84..36aeb5ae58 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -70,8 +70,3 @@ def get_footer_legal_links( # pylint: disable=unused-argument, no-self-use self, cookie_has_theme: bool ) -> Optional[list[dict]]: return None - - def get_additional_data_layer_context( # pylint: disable=no-self-use - self, - ) -> list: - return [] diff --git a/templates/layouts/_base.html b/templates/layouts/_base.html index 40b21c128f..fa7c601f73 100644 --- a/templates/layouts/_base.html +++ b/templates/layouts/_base.html @@ -73,9 +73,6 @@ {% block head %} {# djlint:off #} {% if google_tag_id %} - {% endif %} diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index f622be9cd5..d9e277cdbe 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -1029,13 +1029,13 @@ def test_use_default_survey_title_in_context_when_no_cookie( SurveyType.DEFAULT, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DEFAULT, "en", QuestionnaireSchema({"survey_id": "001", "form_type": "test"}), - [{"form_type": "test", "survey_id": "001"}], + {"form_type": "test", "survey_id": "001"}, ), ( SurveyType.BUSINESS, @@ -1043,7 +1043,7 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema( {"survey_id": "001", "form_type": "test", "title": "test_title"} ), - [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + {"form_type": "test", "survey_id": "001", "title": "test_title"}, ), ( SurveyType.HEALTH, @@ -1051,7 +1051,7 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema( {"survey_id": "001", "form_type": "test", "title": "test_title"} ), - [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + {"form_type": "test", "survey_id": "001", "title": "test_title"}, ), ( SurveyType.SOCIAL, @@ -1059,55 +1059,55 @@ def test_use_default_survey_title_in_context_when_no_cookie( QuestionnaireSchema( {"survey_id": "001", "form_type": "test", "title": "test_title"} ), - [{"form_type": "test", "survey_id": "001", "title": "test_title"}], + {"form_type": "test", "survey_id": "001", "title": "test_title"}, ), ( SurveyType.NORTHERN_IRELAND, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DBT_DSIT, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DBT_DSIT_NI, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DBT, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DBT_NI, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.ORR, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DESNZ, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ( SurveyType.DESNZ_NI, "en", QuestionnaireSchema({"survey_id": "001"}), - [{"survey_id": "001"}], + {"survey_id": "001"}, ), ], ) diff --git a/tests/integration/test_application_variables.py b/tests/integration/test_application_variables.py index 5ba3b54cea..62a5398f33 100644 --- a/tests/integration/test_application_variables.py +++ b/tests/integration/test_application_variables.py @@ -24,7 +24,7 @@ def test_google_analytics_code_and_credentials_are_present(self): self.get("/questionnaire/feedback/") self.assertStatusOK() self.assertInHead( - f'dataLayer = [{{"form_type": "H", "survey_id": "0", "title": "Feedback test schema"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' + f'"form_type": "H", "survey_id": "0", "title": "Feedback test schema", "tx_id": "{actual["METADATA"]["tx_id"]}"' ) self.assertInHead("https://www.googletagmanager.com") self.assertInHead(settings.EQ_GOOGLE_TAG_ID) @@ -40,7 +40,7 @@ def test_google_analytics_data_layer_has_no_null_fields(self): self.assertStatusOK() # form_type is empty so should not be present self.assertInHead( - f'dataLayer = [{{"survey_id": "001", "title": "Other input fields"}}, {{"tx_id": "{actual["METADATA"]["tx_id"]}"}}]' + f'"survey_id": "001", "title": "Other input fields", "tx_id": "{actual["METADATA"]["tx_id"]}"' ) def test_livereload_script_rendered(self): From 8cc58d9dc3cd60527915590573fcd913a4121b48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 07:50:34 +0100 Subject: [PATCH 469/567] Bump boto3 from 1.35.18 to 1.35.23 in the production-dependencies group (#1521) Bumps the production-dependencies group with 1 update: [boto3](https://github.com/boto/boto3). Updates `boto3` from 1.35.18 to 1.35.23 - [Release notes](https://github.com/boto/boto3/releases) - [Commits](https://github.com/boto/boto3/compare/1.35.18...1.35.23) --- updated-dependencies: - dependency-name: boto3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: production-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index c88e5eb30c..6ec8047857 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.18" +version = "1.35.23" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.18-py3-none-any.whl", hash = "sha256:71e237d3997cf93425947854d7b121c577944f391ba633afb0659e1015364704"}, - {file = "boto3-1.35.18.tar.gz", hash = "sha256:fd130308f1f49d748a5fc63de92de79a995b51c79af3947ddde8815fcf0684fe"}, + {file = "boto3-1.35.23-py3-none-any.whl", hash = "sha256:ecba4362f82e23ef775c72b3e6fdef3ef68443629b79e88886d5088302ffc050"}, + {file = "boto3-1.35.23.tar.gz", hash = "sha256:3fbf1d5b749c92ed43aa190650979dff9f83790a42522e1e9eefa54c8e44bc4b"}, ] [package.dependencies] -botocore = ">=1.35.18,<1.36.0" +botocore = ">=1.35.23,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.18" +version = "1.35.23" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.18-py3-none-any.whl", hash = "sha256:1027083aeb1fe74057273410fd768e018e22f85adfbd717b5a69f578f7812b80"}, - {file = "botocore-1.35.18.tar.gz", hash = "sha256:e59da8b91ab06683d2725b6cbbb0383b30c68a241c3c63363f4c5bff59b3c0c0"}, + {file = "botocore-1.35.23-py3-none-any.whl", hash = "sha256:cab9ec4e0367b9f33f0bc02c5a29f587b0119ecffd6d125bacee085dcbc8817d"}, + {file = "botocore-1.35.23.tar.gz", hash = "sha256:25b17a9ccba6ad32bb5bf7ba4f52656aa03c1cb29f6b4e438050ee4ad1967a3b"}, ] [package.dependencies] From bf5821f65a05bc0d1389d39f725b836224807cda Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 23 Sep 2024 09:49:35 +0100 Subject: [PATCH 470/567] Update list collector content page to use list component (#1490) --- .design-system-version | 2 +- app/jinja_filters.py | 26 +++++++++++- templates/listcollectorcontent.html | 17 +++++++- tests/app/test_jinja_filters.py | 40 +++++++++++++++++++ tests/functional/helpers.js | 9 +++-- .../list_collector_repeating_blocks.spec.js | 22 +++++----- .../supplementary_data.spec.js | 8 ++-- .../spec/list_collector_content.spec.js | 12 +++--- 8 files changed, 107 insertions(+), 29 deletions(-) diff --git a/.design-system-version b/.design-system-version index 3b74fa7060..10f98a1349 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -70.0.15 +70.0.16 diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 29d8af10df..c3dfd776e3 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -648,6 +648,30 @@ def map_summary_item_config_processor() -> dict[str, Callable]: return {"map_summary_item_config": map_summary_item_config} +@blueprint.app_context_processor +def map_list_config_processor() -> dict[str, Callable]: + return {"map_list_config": map_list_config} + + +@blueprint.app_template_filter() +def map_list_config(list_values: list[dict]) -> list[dict]: + items_list: list[dict] = [] + + for index, value in enumerate(list_values, 1): + item: dict = {"text": value["item_title"]} + + if value["is_complete"]: + item["iconType"] = "check" + + item["attributes"] = { + "data-qa": f"list-item-{index}-label", + } + + items_list.append(item) + + return items_list + + # pylint: disable=too-many-locals @blueprint.app_template_filter() def map_list_collector_config( @@ -665,7 +689,7 @@ def map_list_collector_config( ) -> list[dict[str, list] | SummaryRow]: rows: list[dict[str, list] | SummaryRow] = [] - for index, list_item in enumerate(list_items, start=1): + for index, list_item in enumerate(list_items, 1): item_name = str(list_item.get("item_title")) actions = [] diff --git a/templates/listcollectorcontent.html b/templates/listcollectorcontent.html index e98068d486..e7a23f646e 100644 --- a/templates/listcollectorcontent.html +++ b/templates/listcollectorcontent.html @@ -2,6 +2,8 @@ {% import "macros/helpers.html" as helpers %} +{% from "components/list/_macro.njk" import onsList %} + {% set save_on_signout = true %} {% set continue_button_text = _("Continue") %} {% set title = content.title %} @@ -10,8 +12,19 @@ {% block form_content %}

      {{ title }}

      {%- if content.list and content.list.list_items -%} - {% set list = content.list %} -
      {% include "partials/summary/list-summary.html" %}
      + {% set list = content.list.list_items %} +
      + {# djlint:off #} + {% set itemsList = map_list_config(list) %} + {{ + onsList({ + "variants": "summary", + "iconPosition": "before", + "itemsList": itemsList, + }) + }} + {# djlint:on #} +
      {%- endif -%} {% include "partials/contents.html" %} {% endblock form_content %} diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index d8d45a160f..c2c2b86d29 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -22,6 +22,7 @@ get_formatted_address, get_width_for_number, map_list_collector_config, + map_list_config, map_summary_item_config, should_wrap_with_fieldset, strip_tags, @@ -1339,5 +1340,44 @@ def test_map_list_collector_config_render_icon_set(): assert output == expected +def test_map_list_config(): + list_values = [ + { + "item_title": "Harry Potter", + "primary_person": False, + "list_item_id": "1", + "is_complete": True, + "repeating_blocks": False, + }, + { + "item_title": "Clark Kent", + "primary_person": False, + "list_item_id": "2", + "is_complete": False, + "repeating_blocks": False, + }, + ] + + output = map_list_config(list_values) + + expected = [ + { + "text": "Harry Potter", + "iconType": "check", + "attributes": { + "data-qa": "list-item-1-label", + }, + }, + { + "text": "Clark Kent", + "attributes": { + "data-qa": "list-item-2-label", + }, + }, + ] + + assert output == expected + + def to_dict(obj): return json.loads(json.dumps(obj, default=lambda o: o.__dict__)) diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index c0fa90daae..895b1c6feb 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -6,11 +6,12 @@ export const checkItemsInList = async (itemsExpected, listLabel) => { } }; -export const checkListItemComplete = async (listItemLabel) => { - await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).toBe(true); +export const summaryItemComplete = async (summaryItemLabel, status) => { + await expect(await $(summaryItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).toBe(status); }; -export const checkListItemIncomplete = async (listItemLabel) => { - await expect(await $(listItemLabel).$(`.ons-summary__item-title-icon.ons-summary__item-title-icon--check`).isExisting()).toBe(false); + +export const listItemComplete = async (listItemLabel, status) => { + await expect(await $(listItemLabel).$(`.ons-list__prefix.ons-list__prefix--icon-check`).isExisting()).toBe(status); }; const assertSummaryFunction = (selector) => { diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js index 4e2fcd403c..b2578e6111 100644 --- a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -9,7 +9,7 @@ import AnyOtherCompaniesOrBranchesPage from "../../../generated_pages/list_colle import SectionCompaniesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/section-companies-summary.page"; import AnyOtherTradingDetailsPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-trading-details.page"; import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/submit.page"; -import { repeatingAnswerChangeLink, checkItemsInList, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; +import { repeatingAnswerChangeLink, checkItemsInList, summaryItemComplete, click } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; import { expect } from "@wdio/globals"; @@ -159,10 +159,10 @@ describe("List Collector Repeating Blocks", () => { // Only the ONS and NAV items should be complete await checkItemsInList(["ONS", "GOV", "MOD", "NAV"], AnyOtherCompaniesOrBranchesPage.listLabel); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - await checkListItemIncomplete(`dt[data-qa="list-item-2-label"]`); - await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await summaryItemComplete(`dt[data-qa="list-item-1-label"]`, true); + await summaryItemComplete(`dt[data-qa="list-item-2-label"]`, false); + await summaryItemComplete(`dt[data-qa="list-item-3-label"]`, false); + await summaryItemComplete(`dt[data-qa="list-item-1-label"]`, true); }); it("When an item has incomplete repeating blocks, Then using submit on the list collector page will navigate the user to the first incomplete repeating block.", async () => { @@ -192,10 +192,10 @@ describe("List Collector Repeating Blocks", () => { it("When the last remaining incomplete repeating block is completed, Then all items are marked as completed with the checkmark icon.", async () => { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-3-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-4-label"]`); + await summaryItemComplete(`dt[data-qa="list-item-1-label"]`, true); + await summaryItemComplete(`dt[data-qa="list-item-2-label"]`, true); + await summaryItemComplete(`dt[data-qa="list-item-3-label"]`, true); + await summaryItemComplete(`dt[data-qa="list-item-4-label"]`, true); }); it("When the user clicks a change link from the section summary and submits without changing an answer, Then the user is returned to the section summary anchored to the answer they clicked on", async () => { @@ -304,8 +304,8 @@ describe("List Collector Repeating Blocks", () => { await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); + await summaryItemComplete(`dt[data-qa="list-item-1-label"]`, true); + await summaryItemComplete(`dt[data-qa="list-item-2-label"]`, true); }); it("When another incomplete item is added via the section summary, Then navigating to the submit page of the section will redirect to the list collector page.", async () => { diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index dbb1344588..996b5f8227 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -1,4 +1,4 @@ -import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, checkListItemComplete, checkListItemIncomplete, click } from "../../../helpers"; +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, listItemComplete, click } from "../../../helpers"; import { expect } from "@wdio/globals"; import { getRandomString } from "../../../jwt_helper"; import AddAdditionalEmployeePage from "../../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; @@ -370,9 +370,9 @@ describe("Using supplementary data", () => { it("Given there is now an additional product, When I resume the Product Details Section, Then I start from the list collector content block and see the new product is incomplete", async () => { await $(HubPage.summaryRowLink("section-6")).click(); await expect(browser).toHaveUrlContaining(ListCollectorProductsPage.pageName); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); - await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + await listItemComplete(`li[data-qa="list-item-1-label"]`, true); + await listItemComplete(`li[data-qa="list-item-2-label"]`, true); + await listItemComplete(`li[data-qa="list-item-3-label"]`, false); await click(ListCollectorProductsPage.submit()); await expect(browser).toHaveUrlContaining(ProductRepeatingBlock1Page.pageName); }); diff --git a/tests/functional/spec/list_collector_content.spec.js b/tests/functional/spec/list_collector_content.spec.js index 5c211c89fb..b4a6977c1a 100644 --- a/tests/functional/spec/list_collector_content.spec.js +++ b/tests/functional/spec/list_collector_content.spec.js @@ -11,7 +11,7 @@ import ListCollectorSecondRepeatingBlockPage from "../generated_pages/list_colle import ListCollectorContentPage from "../generated_pages/list_collector_content_page/list-collector-content.page"; import ListCollectorContentSectionSummaryPage from "../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; import ConfirmationCheckboxPage from "../generated_pages/list_collector_content_page/confirmation-checkbox.page"; -import { checkListItemComplete, checkListItemIncomplete, click } from "../helpers"; +import { listItemComplete, click } from "../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -92,13 +92,13 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); await click(HubPage.submit()); await expect(browser).toHaveUrlContaining(ListCollectorContentPage.pageName); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); - await checkListItemComplete(`dt[data-qa="list-item-2-label"]`); - await checkListItemIncomplete(`dt[data-qa="list-item-3-label"]`); + await listItemComplete(`li[data-qa="list-item-1-label"]`, true); + await listItemComplete(`li[data-qa="list-item-2-label"]`, true); + await listItemComplete(`li[data-qa="list-item-3-label"]`, false); await click(ListCollectorContentPage.submit()); await expect(browser).toHaveUrlContaining(ListCollectorFirstRepeatingBlockPage.pageName); await completeRepeatingBlocks(666, 2, 5, 1995, true, true); - await checkListItemComplete(`dt[data-qa="list-item-3-label"]`); + await listItemComplete(`li[data-qa="list-item-3-label"]`, true); await click(ListCollectorContentPage.submit()); await click(ListCollectorContentSectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); @@ -114,7 +114,7 @@ describe("List Collector Section Summary and Summary Items", () => { await click(CompaniesSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); await click(HubPage.submit()); - await checkListItemComplete(`dt[data-qa="list-item-1-label"]`); + await listItemComplete(`li[data-qa="list-item-1-label"]`, true); await click(ListCollectorContentPage.submit()); await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.pageName); await click(ListCollectorContentSectionSummaryPage.submit()); From a89af4bdb64ec6bc7196a14ce95f1260ed2822b4 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:15:53 +0100 Subject: [PATCH 471/567] Schemas v5.14.0 (#1527) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index fa75732317..ec7fd1aab5 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.13.0 +v5.14.0 From 376c83be5bdbf9df2b309a2f57da5a42ad2eca37 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:02:37 +0100 Subject: [PATCH 472/567] Schemas v5.15.0 (#1528) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ec7fd1aab5..858aaccccb 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.14.0 +v5.15.0 From 3eb3929890f3bd3667666a58d048999b5e527ac9 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:25:50 +0100 Subject: [PATCH 473/567] Remove hub submit and npm caches (#1532) --- .github/workflows/pull_request.yml | 3 --- ...new_calculated_summary_repeating_and_static_answers.spec.js | 1 - 2 files changed, 4 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index e645c24626..9fb18a3eca 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -39,7 +39,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version-file : ".nvmrc" - cache: "npm" - name: Install npm deps run: npm install lint: @@ -52,7 +51,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - cache: "npm" - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install Poetry @@ -137,7 +135,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - cache: "npm" - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install Poetry diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index 7602805482..d4dae08024 100644 --- a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -22,7 +22,6 @@ describe("Calculated summary with repeating answers", () => { before("Completing the list collector and dynamic answer", async () => { await browser.openQuestionnaire("test_new_calculated_summary_repeating_and_static_answers.json"); await $(HubPage.acceptCookies()).click(); - await click(HubPage.submit()); await $(AnySupermarketPage.yes()).click(); await click(AnySupermarketPage.submit()); await $(ListCollectorAddPage.supermarketName()).setValue("Tesco"); From 2eb1fac27047b4a655021c9646ac51d584590482 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 15 Oct 2024 09:58:03 +0100 Subject: [PATCH 474/567] Add parallel runs for schemas validator (#1501) --- scripts/validate_test_schemas.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/validate_test_schemas.sh b/scripts/validate_test_schemas.sh index fc3a0b4f2f..e585590bc6 100755 --- a/scripts/validate_test_schemas.sh +++ b/scripts/validate_test_schemas.sh @@ -5,6 +5,9 @@ red="$(tput setaf 1)" default="$(tput sgr0)" checks=4 + + + until [ "$checks" == 0 ]; do response="$(curl -so /dev/null -w '%{http_code}' http://localhost:5002/status)" @@ -39,8 +42,8 @@ passed=0 file_path_name=$(find "$file_path" -name '*.json') -for schema in ${file_path_name}; do - +validate() { + schema=$1 result="$(curl -s -w 'HTTPSTATUS:%{http_code}' -X POST -H "Content-Type: application/json" -d @"$schema" http://localhost:5001/validate | tr -d '\n')" # shellcheck disable=SC2001 HTTP_BODY=$(echo "${result}" | sed -e 's/HTTPSTATUS\:.*//g') @@ -57,9 +60,17 @@ for schema in ${file_path_name}; do (( failed++ )) exit=1 fi +} + +N_TIMES_IN_PARALLEL=20 + +for schema in ${file_path_name}; do + ((i=i%N_TIMES_IN_PARALLEL)); ((i++==0)) && wait +# Spawn multiple (N_TIMES_IN_PARALLEL) processes in subshells and send to background, but keep printing outputs. + validate "$schema" & done -echo -e "\\n${green}$passed Passed${default} - ${red}$failed Failed${default}" -exit "$exit" \ No newline at end of file + +exit "$exit" From 080d87e5d3146ab24b552c3b7b7018ae5f575392 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:56:28 +0100 Subject: [PATCH 475/567] Schemas v5.16.0 (#1536) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 858aaccccb..8837950118 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.15.0 +v5.16.0 From 705ba93f966a2e4a0864e5f581eb2c0fcddad6e6 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:18:58 +0100 Subject: [PATCH 476/567] Add timeout and create more functional test suites (#1537) --- .github/workflows/pull_request.yml | 6 +-- .../enabled_section_checkbox.spec.js | 0 .../enabled_section_hub.spec.js | 0 .../enabled_section_radio.spec.js | 0 .../choose_another_section.spec.js | 0 .../hub_and_spoke/hub_and_spoke.spec.js | 0 .../hub_and_spoke_custom_content.spec.js | 0 .../hub_and_spoke_required_enable.spec.js | 0 .../hub_and_spoke/previous.spec.js | 0 .../progress/progress_value_source_blocks.js | 0 .../progress_value_source_repeating.js | 0 .../list_collector_repeating_blocks.spec.js | 0 ...eating_sections_with_hub_and_spoke.spec.js | 0 .../routing/all_in.spec.js | 0 .../routing/and.spec.js | 0 ...edirect_to_list_add_block_checkbox.spec.js | 10 ++--- ...n_redirect_to_list_add_block_radio.spec.js | 10 ++--- .../routing/answer_comparison_routing.spec.js | 0 .../routing/answer_not_on_path.spec.js | 0 .../routing/answered_unanswered.spec.js | 0 .../routing/any_in.spec.js | 0 .../routing/boolean.spec.js | 0 .../routing/checkbox_count.spec.js | 0 .../conditional_combined_routing.spec.js | 10 ++--- .../routing/date.spec.js | 0 .../{features => journeys}/routing/in.spec.js | 0 .../routing/not.spec.js | 0 .../routing/number.spec.js | 0 .../{features => journeys}/routing/or.spec.js | 0 .../routing/removes_completed_block.spec.js | 0 .../answer_comparison_skip_conditions.spec.js | 0 ..._and_skipping_section_dependencies.spec.js | 44 +++++++++---------- ...on_dependencies_calculated_summary.spec.js | 24 +++++----- .../routing_checkbox_contains.spec.js | 10 ++--- .../skipping}/skip_condition_block.spec.js | 8 ++-- .../skipping}/skip_condition_group.spec.js | 8 ++-- .../skipping}/skip_condition_list.spec.js | 12 ++--- .../skipping}/skip_conditions_not_set.spec.js | 8 ++-- .../skipping}/skip_conditions_set.spec.js | 8 ++-- .../list_collector.spec.js | 44 +++++++++---------- .../list_collector_content.spec.js | 26 +++++------ .../list_collector_driving_question.spec.js | 14 +++--- ...ollector_driving_question_checkbox.spec.js | 20 ++++----- .../list_collector_primary_person.spec.js | 20 ++++----- .../list_collector_section_summary.spec.js | 26 +++++------ .../list_collector_variants.spec.js | 16 +++---- ..._collector_variants_primary_person.spec.js | 16 +++---- .../relationships-unrelated.spec.js | 12 ++--- .../relationships.spec.js | 14 +++--- .../relationships_primary.spec.js | 12 ++--- .../calculated_summary.spec.js | 0 .../calculated_summary_test_case.js | 0 ...mmary_repeating_and_static_answers.spec.js | 0 ...alculated_summary_repeating_blocks.spec.js | 0 ...lculated_summary_repeating_section.spec.js | 0 ...summary_cross_section_dependencies.spec.js | 0 ...d_summary_inside_repeating_section.spec.js | 0 ...ulated_summary_overlapping_answers.spec.js | 0 ...lculated_summary_repeating_answers.spec.js | 0 .../custom_question_summary.spec.js | 0 .../section_summary/section_summary.spec.js | 0 ...section_summary_repeating_sections.spec.js | 0 ...show_section_summary_on_completion.spec.js | 0 tests/functional/wdio.conf.js | 3 ++ 64 files changed, 192 insertions(+), 189 deletions(-) rename tests/functional/spec/{features => journeys}/enabled-sections/enabled_section_checkbox.spec.js (100%) rename tests/functional/spec/{features => journeys}/enabled-sections/enabled_section_hub.spec.js (100%) rename tests/functional/spec/{features => journeys}/enabled-sections/enabled_section_radio.spec.js (100%) rename tests/functional/spec/{features => journeys}/hub_and_spoke/choose_another_section.spec.js (100%) rename tests/functional/spec/{features => journeys}/hub_and_spoke/hub_and_spoke.spec.js (100%) rename tests/functional/spec/{features => journeys}/hub_and_spoke/hub_and_spoke_custom_content.spec.js (100%) rename tests/functional/spec/{features => journeys}/hub_and_spoke/hub_and_spoke_required_enable.spec.js (100%) rename tests/functional/spec/{features => journeys}/hub_and_spoke/previous.spec.js (100%) rename tests/functional/spec/{features => journeys}/progress/progress_value_source_blocks.js (100%) rename tests/functional/spec/{features => journeys}/progress/progress_value_source_repeating.js (100%) rename tests/functional/spec/{features => journeys}/repeating_blocks/list_collector_repeating_blocks.spec.js (100%) rename tests/functional/spec/{features => journeys}/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/all_in.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/and.spec.js (100%) rename tests/functional/spec/{ => journeys/routing}/answer_action_redirect_to_list_add_block_checkbox.spec.js (84%) rename tests/functional/spec/{ => journeys/routing}/answer_action_redirect_to_list_add_block_radio.spec.js (85%) rename tests/functional/spec/{features => journeys}/routing/answer_comparison_routing.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/answer_not_on_path.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/answered_unanswered.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/any_in.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/boolean.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/checkbox_count.spec.js (100%) rename tests/functional/spec/{ => journeys/routing}/conditional_combined_routing.spec.js (77%) rename tests/functional/spec/{features => journeys}/routing/date.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/in.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/not.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/number.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/or.spec.js (100%) rename tests/functional/spec/{features => journeys}/routing/removes_completed_block.spec.js (100%) rename tests/functional/spec/{features => journeys}/skipping/answer_comparison_skip_conditions.spec.js (100%) rename tests/functional/spec/{ => journeys/skipping}/routing_and_skipping_section_dependencies.spec.js (90%) rename tests/functional/spec/{ => journeys/skipping}/routing_and_skipping_section_dependencies_calculated_summary.spec.js (88%) rename tests/functional/spec/{ => journeys/skipping}/routing_checkbox_contains.spec.js (85%) rename tests/functional/spec/{ => journeys/skipping}/skip_condition_block.spec.js (72%) rename tests/functional/spec/{ => journeys/skipping}/skip_condition_group.spec.js (72%) rename tests/functional/spec/{ => journeys/skipping}/skip_condition_list.spec.js (83%) rename tests/functional/spec/{ => journeys/skipping}/skip_conditions_not_set.spec.js (66%) rename tests/functional/spec/{ => journeys/skipping}/skip_conditions_set.spec.js (67%) rename tests/functional/spec/{ => list_collector}/list_collector.spec.js (83%) rename tests/functional/spec/{ => list_collector}/list_collector_content.spec.js (86%) rename tests/functional/spec/{ => list_collector}/list_collector_driving_question.spec.js (78%) rename tests/functional/spec/{ => list_collector}/list_collector_driving_question_checkbox.spec.js (82%) rename tests/functional/spec/{ => list_collector}/list_collector_primary_person.spec.js (86%) rename tests/functional/spec/{ => list_collector}/list_collector_section_summary.spec.js (93%) rename tests/functional/spec/{ => list_collector}/list_collector_variants.spec.js (86%) rename tests/functional/spec/{ => list_collector}/list_collector_variants_primary_person.spec.js (88%) rename tests/functional/spec/{ => list_collector}/relationships-unrelated.spec.js (89%) rename tests/functional/spec/{ => list_collector}/relationships.spec.js (94%) rename tests/functional/spec/{ => list_collector}/relationships_primary.spec.js (86%) rename tests/functional/spec/{features => summaries}/calculated_summary/calculated_summary.spec.js (100%) rename tests/functional/spec/{features => summaries}/calculated_summary/calculated_summary_test_case.js (100%) rename tests/functional/spec/{features => summaries}/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js (100%) rename tests/functional/spec/{features => summaries}/calculated_summary/new_calculated_summary_repeating_blocks.spec.js (100%) rename tests/functional/spec/{features => summaries}/calculated_summary/new_calculated_summary_repeating_section.spec.js (100%) rename tests/functional/spec/{features => summaries}/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js (100%) rename tests/functional/spec/{features => summaries}/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js (100%) rename tests/functional/spec/{features => summaries}/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js (100%) rename tests/functional/spec/{features => summaries}/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js (100%) rename tests/functional/spec/{features => summaries}/question_summary/custom_question_summary.spec.js (100%) rename tests/functional/spec/{features => summaries}/section_summary/section_summary.spec.js (100%) rename tests/functional/spec/{features => summaries}/section_summary/section_summary_repeating_sections.spec.js (100%) rename tests/functional/spec/{features => summaries}/show_section_summary_on_completion/show_section_summary_on_completion.spec.js (100%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 9fb18a3eca..86ca206116 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -86,7 +86,6 @@ jobs: - name: Install apt dependencies run: | sudo apt-get install libsnappy-dev libgconf-2-4 jq - # Install wkthtmltopdf with patched Qt sudo apt-get install -y xfonts-base xfonts-75dpi wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb @@ -124,12 +123,13 @@ jobs: needs: [python-dependencies, node-dependencies] strategy: matrix: - suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, general, components ] + suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, summaries, general, journeys, components, list_collector] runs-on: ubuntu-22.04 + timeout-minutes: 30 env: EQ_RUN_FUNCTIONAL_TESTS_HEADLESS: True # :TODO: Revisit & update when 2 instances can be used without adverse effects - EQ_FUNCTIONAL_TEST_MAX_INSTANCES: 1 + EQ_FUNCTIONAL_TEST_MAX_INSTANCES: 2 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js similarity index 100% rename from tests/functional/spec/features/enabled-sections/enabled_section_checkbox.spec.js rename to tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js b/tests/functional/spec/journeys/enabled-sections/enabled_section_hub.spec.js similarity index 100% rename from tests/functional/spec/features/enabled-sections/enabled_section_hub.spec.js rename to tests/functional/spec/journeys/enabled-sections/enabled_section_hub.spec.js diff --git a/tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js similarity index 100% rename from tests/functional/spec/features/enabled-sections/enabled_section_radio.spec.js rename to tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js diff --git a/tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js b/tests/functional/spec/journeys/hub_and_spoke/choose_another_section.spec.js similarity index 100% rename from tests/functional/spec/features/hub_and_spoke/choose_another_section.spec.js rename to tests/functional/spec/journeys/hub_and_spoke/choose_another_section.spec.js diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js similarity index 100% rename from tests/functional/spec/features/hub_and_spoke/hub_and_spoke.spec.js rename to tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_custom_content.spec.js similarity index 100% rename from tests/functional/spec/features/hub_and_spoke/hub_and_spoke_custom_content.spec.js rename to tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_custom_content.spec.js diff --git a/tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js similarity index 100% rename from tests/functional/spec/features/hub_and_spoke/hub_and_spoke_required_enable.spec.js rename to tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js diff --git a/tests/functional/spec/features/hub_and_spoke/previous.spec.js b/tests/functional/spec/journeys/hub_and_spoke/previous.spec.js similarity index 100% rename from tests/functional/spec/features/hub_and_spoke/previous.spec.js rename to tests/functional/spec/journeys/hub_and_spoke/previous.spec.js diff --git a/tests/functional/spec/features/progress/progress_value_source_blocks.js b/tests/functional/spec/journeys/progress/progress_value_source_blocks.js similarity index 100% rename from tests/functional/spec/features/progress/progress_value_source_blocks.js rename to tests/functional/spec/journeys/progress/progress_value_source_blocks.js diff --git a/tests/functional/spec/features/progress/progress_value_source_repeating.js b/tests/functional/spec/journeys/progress/progress_value_source_repeating.js similarity index 100% rename from tests/functional/spec/features/progress/progress_value_source_repeating.js rename to tests/functional/spec/journeys/progress/progress_value_source_repeating.js diff --git a/tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js similarity index 100% rename from tests/functional/spec/features/repeating_blocks/list_collector_repeating_blocks.spec.js rename to tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js diff --git a/tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js similarity index 100% rename from tests/functional/spec/features/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js rename to tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js diff --git a/tests/functional/spec/features/routing/all_in.spec.js b/tests/functional/spec/journeys/routing/all_in.spec.js similarity index 100% rename from tests/functional/spec/features/routing/all_in.spec.js rename to tests/functional/spec/journeys/routing/all_in.spec.js diff --git a/tests/functional/spec/features/routing/and.spec.js b/tests/functional/spec/journeys/routing/and.spec.js similarity index 100% rename from tests/functional/spec/features/routing/and.spec.js rename to tests/functional/spec/journeys/routing/and.spec.js diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js similarity index 84% rename from tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js rename to tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js index 05d1d5bf5a..0ca4e74ab3 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -1,8 +1,8 @@ -import { checkItemsInList, click } from "../helpers"; -import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at.page"; -import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-add.page"; -import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-remove.page"; -import AnyoneUsuallyLiveAt from "../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-usually-live-at.page"; +import { checkItemsInList, click } from "../../../helpers"; +import AnyoneLiveAtListCollector from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at.page"; +import AnyoneLiveAtListCollectorAddPage from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-add.page"; +import AnyoneLiveAtListCollectorRemovePage from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-remove.page"; +import AnyoneUsuallyLiveAt from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-usually-live-at.page"; describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { describe('Given the user is on a question with a "RedirectToListAddBlock" action enabled', () => { diff --git a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js similarity index 85% rename from tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js rename to tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js index 46871e6162..74354b1ad7 100644 --- a/tests/functional/spec/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js @@ -1,8 +1,8 @@ -import { checkItemsInList, click } from "../helpers"; -import AnyoneLiveAtListCollector from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at.page"; -import AnyoneLiveAtListCollectorAddPage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-add.page"; -import AnyoneLiveAtListCollectorRemovePage from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-remove.page"; -import AnyoneUsuallyLiveAt from "../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-usually-live-at.page"; +import { checkItemsInList, click } from "../../../helpers"; +import AnyoneLiveAtListCollector from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at.page"; +import AnyoneLiveAtListCollectorAddPage from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-add.page"; +import AnyoneLiveAtListCollectorRemovePage from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-remove.page"; +import AnyoneUsuallyLiveAt from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-usually-live-at.page"; describe("Answer Action: Redirect To List Add Question (Radio)", () => { describe('Given the user is on a question with a "RedirectToListAddBlock" action enabled', () => { diff --git a/tests/functional/spec/features/routing/answer_comparison_routing.spec.js b/tests/functional/spec/journeys/routing/answer_comparison_routing.spec.js similarity index 100% rename from tests/functional/spec/features/routing/answer_comparison_routing.spec.js rename to tests/functional/spec/journeys/routing/answer_comparison_routing.spec.js diff --git a/tests/functional/spec/features/routing/answer_not_on_path.spec.js b/tests/functional/spec/journeys/routing/answer_not_on_path.spec.js similarity index 100% rename from tests/functional/spec/features/routing/answer_not_on_path.spec.js rename to tests/functional/spec/journeys/routing/answer_not_on_path.spec.js diff --git a/tests/functional/spec/features/routing/answered_unanswered.spec.js b/tests/functional/spec/journeys/routing/answered_unanswered.spec.js similarity index 100% rename from tests/functional/spec/features/routing/answered_unanswered.spec.js rename to tests/functional/spec/journeys/routing/answered_unanswered.spec.js diff --git a/tests/functional/spec/features/routing/any_in.spec.js b/tests/functional/spec/journeys/routing/any_in.spec.js similarity index 100% rename from tests/functional/spec/features/routing/any_in.spec.js rename to tests/functional/spec/journeys/routing/any_in.spec.js diff --git a/tests/functional/spec/features/routing/boolean.spec.js b/tests/functional/spec/journeys/routing/boolean.spec.js similarity index 100% rename from tests/functional/spec/features/routing/boolean.spec.js rename to tests/functional/spec/journeys/routing/boolean.spec.js diff --git a/tests/functional/spec/features/routing/checkbox_count.spec.js b/tests/functional/spec/journeys/routing/checkbox_count.spec.js similarity index 100% rename from tests/functional/spec/features/routing/checkbox_count.spec.js rename to tests/functional/spec/journeys/routing/checkbox_count.spec.js diff --git a/tests/functional/spec/conditional_combined_routing.spec.js b/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js similarity index 77% rename from tests/functional/spec/conditional_combined_routing.spec.js rename to tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js index 9a82287153..c375ea447d 100644 --- a/tests/functional/spec/conditional_combined_routing.spec.js +++ b/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js @@ -1,8 +1,8 @@ -import ConditionalCombinedRoutingPage from "../generated_pages/conditional_combined_routing/conditional-routing-block.page"; -import ResponseAny from "../generated_pages/conditional_combined_routing/response-any.page"; -import ResponseNotAny from "../generated_pages/conditional_combined_routing/response-not-any.page"; -import SubmitPage from "../generated_pages/conditional_combined_routing/submit.page"; -import { click } from "../helpers"; +import ConditionalCombinedRoutingPage from "../../../generated_pages/conditional_combined_routing/conditional-routing-block.page"; +import ResponseAny from "../../../generated_pages/conditional_combined_routing/response-any.page"; +import ResponseNotAny from "../../../generated_pages/conditional_combined_routing/response-not-any.page"; +import SubmitPage from "../../../generated_pages/conditional_combined_routing/submit.page"; +import { click } from "../../../helpers"; describe("Conditional combined routing.", () => { beforeEach(async () => { diff --git a/tests/functional/spec/features/routing/date.spec.js b/tests/functional/spec/journeys/routing/date.spec.js similarity index 100% rename from tests/functional/spec/features/routing/date.spec.js rename to tests/functional/spec/journeys/routing/date.spec.js diff --git a/tests/functional/spec/features/routing/in.spec.js b/tests/functional/spec/journeys/routing/in.spec.js similarity index 100% rename from tests/functional/spec/features/routing/in.spec.js rename to tests/functional/spec/journeys/routing/in.spec.js diff --git a/tests/functional/spec/features/routing/not.spec.js b/tests/functional/spec/journeys/routing/not.spec.js similarity index 100% rename from tests/functional/spec/features/routing/not.spec.js rename to tests/functional/spec/journeys/routing/not.spec.js diff --git a/tests/functional/spec/features/routing/number.spec.js b/tests/functional/spec/journeys/routing/number.spec.js similarity index 100% rename from tests/functional/spec/features/routing/number.spec.js rename to tests/functional/spec/journeys/routing/number.spec.js diff --git a/tests/functional/spec/features/routing/or.spec.js b/tests/functional/spec/journeys/routing/or.spec.js similarity index 100% rename from tests/functional/spec/features/routing/or.spec.js rename to tests/functional/spec/journeys/routing/or.spec.js diff --git a/tests/functional/spec/features/routing/removes_completed_block.spec.js b/tests/functional/spec/journeys/routing/removes_completed_block.spec.js similarity index 100% rename from tests/functional/spec/features/routing/removes_completed_block.spec.js rename to tests/functional/spec/journeys/routing/removes_completed_block.spec.js diff --git a/tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js b/tests/functional/spec/journeys/skipping/answer_comparison_skip_conditions.spec.js similarity index 100% rename from tests/functional/spec/features/skipping/answer_comparison_skip_conditions.spec.js rename to tests/functional/spec/journeys/skipping/answer_comparison_skip_conditions.spec.js diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies.spec.js similarity index 90% rename from tests/functional/spec/routing_and_skipping_section_dependencies.spec.js rename to tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies.spec.js index f70896c05e..773637e2a5 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies.spec.js +++ b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies.spec.js @@ -1,25 +1,25 @@ -import AgePage from "../generated_pages/routing_and_skipping_section_dependencies/age.page"; -import HouseHoldPersonalDetailsSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/household-personal-details-section-summary.page"; -import HouseholdSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/household-section-summary.page"; -import ListCollectorAddPage from "../generated_pages/routing_and_skipping_section_dependencies/list-collector-add.page"; -import ListCollectorPage from "../generated_pages/routing_and_skipping_section_dependencies/list-collector.page"; -import NamePage from "../generated_pages/routing_and_skipping_section_dependencies/name-block.page"; -import PrimaryPersonSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/primary-person-summary.page"; -import ReasonNoConfirmationPage from "../generated_pages/routing_and_skipping_section_dependencies/reason-no-confirmation.page"; -import RepeatingAgePage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-age.page"; -import RepeatingSexPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-sex.page"; -import SecurityPage from "../generated_pages/routing_and_skipping_section_dependencies/security.page"; -import SkipAgePage from "../generated_pages/routing_and_skipping_section_dependencies/skip-age.page"; -import SkipEnableSectionPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-household-section.page"; -import EnableSectionPage from "../generated_pages/routing_and_skipping_section_dependencies/enable-section.page"; -import SkipConfirmationPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation.page"; -import SkipConfirmationSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; -import SkipSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies/skip-section-summary.page"; -import RepeatingIsDependentPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-is-dependent.page"; -import RepeatingIsSmokerPage from "../generated_pages/routing_and_skipping_section_dependencies/repeating-is-smoker.page"; - -import HubPage from "../base_pages/hub.page"; -import { click } from "../helpers"; +import AgePage from "../../../generated_pages/routing_and_skipping_section_dependencies/age.page"; +import HouseHoldPersonalDetailsSectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies/household-personal-details-section-summary.page"; +import HouseholdSectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies/household-section-summary.page"; +import ListCollectorAddPage from "../../../generated_pages/routing_and_skipping_section_dependencies/list-collector-add.page"; +import ListCollectorPage from "../../../generated_pages/routing_and_skipping_section_dependencies/list-collector.page"; +import NamePage from "../../../generated_pages/routing_and_skipping_section_dependencies/name-block.page"; +import PrimaryPersonSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies/primary-person-summary.page"; +import ReasonNoConfirmationPage from "../../../generated_pages/routing_and_skipping_section_dependencies/reason-no-confirmation.page"; +import RepeatingAgePage from "../../../generated_pages/routing_and_skipping_section_dependencies/repeating-age.page"; +import RepeatingSexPage from "../../../generated_pages/routing_and_skipping_section_dependencies/repeating-sex.page"; +import SecurityPage from "../../../generated_pages/routing_and_skipping_section_dependencies/security.page"; +import SkipAgePage from "../../../generated_pages/routing_and_skipping_section_dependencies/skip-age.page"; +import SkipEnableSectionPage from "../../../generated_pages/routing_and_skipping_section_dependencies/skip-household-section.page"; +import EnableSectionPage from "../../../generated_pages/routing_and_skipping_section_dependencies/enable-section.page"; +import SkipConfirmationPage from "../../../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation.page"; +import SkipConfirmationSectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies/skip-confirmation-section-summary.page"; +import SkipSectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies/skip-section-summary.page"; +import RepeatingIsDependentPage from "../../../generated_pages/routing_and_skipping_section_dependencies/repeating-is-dependent.page"; +import RepeatingIsSmokerPage from "../../../generated_pages/routing_and_skipping_section_dependencies/repeating-is-smoker.page"; + +import HubPage from "../../../base_pages/hub.page"; +import { click } from "../../../helpers"; describe("Routing and skipping section dependencies", () => { describe("Given the routing and skipping section dependencies questionnaire", () => { diff --git a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js similarity index 88% rename from tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js rename to tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 59e5bf2aef..258e92987b 100644 --- a/tests/functional/spec/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -1,15 +1,15 @@ -import CalculatedSummarySectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/calculated-summary-section-summary.page"; -import CurrencyTotalPlaybackPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/currency-total-playback.page"; -import DependentQuestionSectionSummaryPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/dependent-question-section-summary.page"; -import FirstQuestionBlockPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/first-question-block.page"; -import FruitPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; -import SecondQuestionBlockPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; -import VegetablesPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; -import SkipQuestionPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/skip-butter-block.page"; -import ButterPage from "../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/butter-block.page"; - -import HubPage from "../base_pages/hub.page"; -import { click } from "../helpers"; +import CalculatedSummarySectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/calculated-summary-section-summary.page"; +import CurrencyTotalPlaybackPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/currency-total-playback.page"; +import DependentQuestionSectionSummaryPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/dependent-question-section-summary.page"; +import FirstQuestionBlockPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/first-question-block.page"; +import FruitPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/fruit.page"; +import SecondQuestionBlockPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/second-question-block.page"; +import VegetablesPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/vegetables.page"; +import SkipQuestionPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/skip-butter-block.page"; +import ButterPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/butter-block.page"; + +import HubPage from "../../../base_pages/hub.page"; +import { click } from "../../../helpers"; describe("Routing and skipping section dependencies based on calculated summaries", () => { describe("Given the section dependencies based on a calculated summary questionnaire", () => { diff --git a/tests/functional/spec/routing_checkbox_contains.spec.js b/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js similarity index 85% rename from tests/functional/spec/routing_checkbox_contains.spec.js rename to tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js index be317ee262..a5a58e02a0 100644 --- a/tests/functional/spec/routing_checkbox_contains.spec.js +++ b/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js @@ -1,8 +1,8 @@ -import RoutingCheckboxContains from "../generated_pages/routing_checkbox_contains/country-checkbox.page"; -import ContainsAllPage from "../generated_pages/routing_checkbox_contains/country-interstitial-all.page"; -import ContainsAnyPage from "../generated_pages/routing_checkbox_contains/country-interstitial-any.page"; -import SubmitPage from "../generated_pages/routing_checkbox_contains/submit.page"; -import { click } from "../helpers"; +import RoutingCheckboxContains from "../../../generated_pages/routing_checkbox_contains/country-checkbox.page"; +import ContainsAllPage from "../../../generated_pages/routing_checkbox_contains/country-interstitial-all.page"; +import ContainsAnyPage from "../../../generated_pages/routing_checkbox_contains/country-interstitial-any.page"; +import SubmitPage from "../../../generated_pages/routing_checkbox_contains/submit.page"; +import { click } from "../../../helpers"; describe("Routing Checkbox Contains Condition.", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_checkbox_contains.json"); diff --git a/tests/functional/spec/skip_condition_block.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js similarity index 72% rename from tests/functional/spec/skip_condition_block.spec.js rename to tests/functional/spec/journeys/skipping/skip_condition_block.spec.js index 2eb93ab759..c9c444fd2a 100644 --- a/tests/functional/spec/skip_condition_block.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js @@ -1,7 +1,7 @@ -import QuestionPage from "../generated_pages/skip_condition_block/do-you-want-to-skip.page"; -import SkipPage from "../generated_pages/skip_condition_block/should-skip.page"; -import SubmitPage from "../generated_pages/skip_condition_block/submit.page"; -import { click } from "../helpers"; +import QuestionPage from "../../../generated_pages/skip_condition_block/do-you-want-to-skip.page"; +import SkipPage from "../../../generated_pages/skip_condition_block/should-skip.page"; +import SubmitPage from "../../../generated_pages/skip_condition_block/submit.page"; +import { click } from "../../../helpers"; describe("Skip Conditions - Block", () => { const schema = "test_skip_condition_block.json"; diff --git a/tests/functional/spec/skip_condition_group.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js similarity index 72% rename from tests/functional/spec/skip_condition_group.spec.js rename to tests/functional/spec/journeys/skipping/skip_condition_group.spec.js index 8473ced58f..fd6e7c0842 100644 --- a/tests/functional/spec/skip_condition_group.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js @@ -1,7 +1,7 @@ -import QuestionPage from "../generated_pages/skip_condition_group/do-you-want-to-skip.page"; -import SkipPage from "../generated_pages/skip_condition_group/should-skip.page"; -import SubmitPage from "../generated_pages/skip_condition_group/submit.page"; -import { click } from "../helpers"; +import QuestionPage from "../../../generated_pages/skip_condition_group/do-you-want-to-skip.page"; +import SkipPage from "../../../generated_pages/skip_condition_group/should-skip.page"; +import SubmitPage from "../../../generated_pages/skip_condition_group/submit.page"; +import { click } from "../../../helpers"; describe("Skip Conditions - Group", () => { const schema = "test_skip_condition_group.json"; diff --git a/tests/functional/spec/skip_condition_list.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js similarity index 83% rename from tests/functional/spec/skip_condition_list.spec.js rename to tests/functional/spec/journeys/skipping/skip_condition_list.spec.js index 2ec01c2ece..4ee08e3ee7 100644 --- a/tests/functional/spec/skip_condition_list.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js @@ -1,9 +1,9 @@ -import ListCollectorPage from "../generated_pages/skip_condition_list/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/skip_condition_list/list-collector-add.page.js"; -import LessThanTwoInterstitialPage from "../generated_pages/skip_condition_list/less-than-two-interstitial.page.js"; -import TwoInterstitialPage from "../generated_pages/skip_condition_list/two-interstitial.page.js"; -import MoreThanTwoInterstitialPage from "../generated_pages/skip_condition_list/more-than-two-interstitial.page.js"; -import { click } from "../helpers"; +import ListCollectorPage from "../../../generated_pages/skip_condition_list/list-collector.page.js"; +import ListCollectorAddPage from "../../../generated_pages/skip_condition_list/list-collector-add.page.js"; +import LessThanTwoInterstitialPage from "../../../generated_pages/skip_condition_list/less-than-two-interstitial.page.js"; +import TwoInterstitialPage from "../../../generated_pages/skip_condition_list/two-interstitial.page.js"; +import MoreThanTwoInterstitialPage from "../../../generated_pages/skip_condition_list/more-than-two-interstitial.page.js"; +import { click } from "../../../helpers"; describe("Feature: Routing on lists", () => { describe("Given I start skip condition list survey", () => { beforeEach(async () => { diff --git a/tests/functional/spec/skip_conditions_not_set.spec.js b/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js similarity index 66% rename from tests/functional/spec/skip_conditions_not_set.spec.js rename to tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js index 5bd576b804..536eae89fc 100644 --- a/tests/functional/spec/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js @@ -1,7 +1,7 @@ -import FoodPage from "../generated_pages/skip_condition_not_set/food-block.page"; -import DrinkPage from "../generated_pages/skip_condition_not_set/drink-block.page"; -import SubmitPage from "../generated_pages/skip_condition_not_set/submit.page"; -import { click } from "../helpers"; +import FoodPage from "../../../generated_pages/skip_condition_not_set/food-block.page"; +import DrinkPage from "../../../generated_pages/skip_condition_not_set/drink-block.page"; +import SubmitPage from "../../../generated_pages/skip_condition_not_set/submit.page"; +import { click } from "../../../helpers"; describe("Skip Conditions - Not Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_not_set.json"); diff --git a/tests/functional/spec/skip_conditions_set.spec.js b/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js similarity index 67% rename from tests/functional/spec/skip_conditions_set.spec.js rename to tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js index 491c6e344f..3cb75c39fd 100644 --- a/tests/functional/spec/skip_conditions_set.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js @@ -1,7 +1,7 @@ -import FoodPage from "../generated_pages/skip_condition_set/food-block.page"; -import DrinkPage from "../generated_pages/skip_condition_set/drink-block.page"; -import SubmitPage from "../generated_pages/skip_condition_set/submit.page"; -import { click } from "../helpers"; +import FoodPage from "../../../generated_pages/skip_condition_set/food-block.page"; +import DrinkPage from "../../../generated_pages/skip_condition_set/drink-block.page"; +import SubmitPage from "../../../generated_pages/skip_condition_set/submit.page"; +import { click } from "../../../helpers"; describe("Skip Conditions - Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_set.json"); diff --git a/tests/functional/spec/list_collector.spec.js b/tests/functional/spec/list_collector/list_collector.spec.js similarity index 83% rename from tests/functional/spec/list_collector.spec.js rename to tests/functional/spec/list_collector/list_collector.spec.js index 2e53ef29bb..e970a27af0 100644 --- a/tests/functional/spec/list_collector.spec.js +++ b/tests/functional/spec/list_collector/list_collector.spec.js @@ -1,25 +1,25 @@ -import { checkItemsInList, click } from "../helpers"; -import AnotherListCollectorPage from "../generated_pages/list_collector/another-list-collector-block.page.js"; -import AnotherListCollectorAddPage from "../generated_pages/list_collector/another-list-collector-block-add.page.js"; -import AnotherListCollectorEditPage from "../generated_pages/list_collector/another-list-collector-block-edit.page.js"; -import AnotherListCollectorRemovePage from "../generated_pages/list_collector/another-list-collector-block-remove.page.js"; -import ListCollectorPage from "../generated_pages/list_collector/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/list_collector/list-collector-add.page.js"; -import ListCollectorEditPage from "../generated_pages/list_collector/list-collector-edit.page.js"; -import ListCollectorRemovePage from "../generated_pages/list_collector/list-collector-remove.page.js"; -import NextInterstitialPage from "../generated_pages/list_collector/next-interstitial.page.js"; -import SummaryPage from "../generated_pages/list_collector/section-summary.page.js"; -import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_list_summary/primary-person-list-collector.page.js"; -import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_list_summary/primary-person-list-collector-add.page.js"; -import SectionSummaryListCollectorPage from "../generated_pages/list_collector_list_summary/list-collector.page.js"; -import SectionSummaryListCollectorAddPage from "../generated_pages/list_collector_list_summary/list-collector-add.page.js"; -import SectionSummaryListCollectorEditPage from "../generated_pages/list_collector_list_summary/list-collector-edit.page.js"; -import SectionSummaryListCollectorRemovePage from "../generated_pages/list_collector_list_summary/list-collector-remove.page.js"; -import VisitorListCollectorPage from "../generated_pages/list_collector_list_summary/visitor-list-collector.page.js"; -import VisitorListCollectorAddPage from "../generated_pages/list_collector_list_summary/visitor-list-collector-add.page.js"; -import PeopleListSectionSummaryPage from "../generated_pages/list_collector_list_summary/section-summary.page.js"; -import { SubmitPage } from "../base_pages/submit.page.js"; -import IntroductionPage from "../generated_pages/list_collector_list_summary/introduction.page.js"; +import { checkItemsInList, click } from "../../helpers"; +import AnotherListCollectorPage from "../../generated_pages/list_collector/another-list-collector-block.page.js"; +import AnotherListCollectorAddPage from "../../generated_pages/list_collector/another-list-collector-block-add.page.js"; +import AnotherListCollectorEditPage from "../../generated_pages/list_collector/another-list-collector-block-edit.page.js"; +import AnotherListCollectorRemovePage from "../../generated_pages/list_collector/another-list-collector-block-remove.page.js"; +import ListCollectorPage from "../../generated_pages/list_collector/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/list_collector/list-collector-add.page.js"; +import ListCollectorEditPage from "../../generated_pages/list_collector/list-collector-edit.page.js"; +import ListCollectorRemovePage from "../../generated_pages/list_collector/list-collector-remove.page.js"; +import NextInterstitialPage from "../../generated_pages/list_collector/next-interstitial.page.js"; +import SummaryPage from "../../generated_pages/list_collector/section-summary.page.js"; +import PrimaryPersonListCollectorPage from "../../generated_pages/list_collector_list_summary/primary-person-list-collector.page.js"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/list_collector_list_summary/primary-person-list-collector-add.page.js"; +import SectionSummaryListCollectorPage from "../../generated_pages/list_collector_list_summary/list-collector.page.js"; +import SectionSummaryListCollectorAddPage from "../../generated_pages/list_collector_list_summary/list-collector-add.page.js"; +import SectionSummaryListCollectorEditPage from "../../generated_pages/list_collector_list_summary/list-collector-edit.page.js"; +import SectionSummaryListCollectorRemovePage from "../../generated_pages/list_collector_list_summary/list-collector-remove.page.js"; +import VisitorListCollectorPage from "../../generated_pages/list_collector_list_summary/visitor-list-collector.page.js"; +import VisitorListCollectorAddPage from "../../generated_pages/list_collector_list_summary/visitor-list-collector-add.page.js"; +import PeopleListSectionSummaryPage from "../../generated_pages/list_collector_list_summary/section-summary.page.js"; +import { SubmitPage } from "../../base_pages/submit.page.js"; +import IntroductionPage from "../../generated_pages/list_collector_list_summary/introduction.page.js"; describe("List Collector", () => { describe("Given a normal journey through the list collector without variants", () => { diff --git a/tests/functional/spec/list_collector_content.spec.js b/tests/functional/spec/list_collector/list_collector_content.spec.js similarity index 86% rename from tests/functional/spec/list_collector_content.spec.js rename to tests/functional/spec/list_collector/list_collector_content.spec.js index b4a6977c1a..c1c8e1ac44 100644 --- a/tests/functional/spec/list_collector_content.spec.js +++ b/tests/functional/spec/list_collector/list_collector_content.spec.js @@ -1,17 +1,17 @@ -import AnyOtherCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches.page.js"; -import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches-add.page.js"; -import AnyCompaniesOrBranchesRemovePage from "../generated_pages/list_collector_content_page/any-other-companies-or-branches-remove.page.js"; +import AnyOtherCompaniesOrBranchesPage from "../../generated_pages/list_collector_content_page/any-other-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesAddPage from "../../generated_pages/list_collector_content_page/any-other-companies-or-branches-add.page.js"; +import AnyCompaniesOrBranchesRemovePage from "../../generated_pages/list_collector_content_page/any-other-companies-or-branches-remove.page.js"; -import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_content_page/any-companies-or-branches.page"; -import CompaniesSummaryPage from "../generated_pages/list_collector_content_page/section-companies-summary.page"; -import HubPage from "../base_pages/hub.page"; -import ResponsiblePartyQuestionPage from "../generated_pages/list_collector_content_page/responsible-party.page"; -import ListCollectorFirstRepeatingBlockPage from "../generated_pages/list_collector_content_page/companies-repeating-block-1-repeating-block.page"; -import ListCollectorSecondRepeatingBlockPage from "../generated_pages/list_collector_content_page/companies-repeating-block-2-repeating-block.page"; -import ListCollectorContentPage from "../generated_pages/list_collector_content_page/list-collector-content.page"; -import ListCollectorContentSectionSummaryPage from "../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; -import ConfirmationCheckboxPage from "../generated_pages/list_collector_content_page/confirmation-checkbox.page"; -import { listItemComplete, click } from "../helpers"; +import AnyCompaniesOrBranchesPage from "../../generated_pages/list_collector_content_page/any-companies-or-branches.page"; +import CompaniesSummaryPage from "../../generated_pages/list_collector_content_page/section-companies-summary.page"; +import HubPage from "../../base_pages/hub.page"; +import ResponsiblePartyQuestionPage from "../../generated_pages/list_collector_content_page/responsible-party.page"; +import ListCollectorFirstRepeatingBlockPage from "../../generated_pages/list_collector_content_page/companies-repeating-block-1-repeating-block.page"; +import ListCollectorSecondRepeatingBlockPage from "../../generated_pages/list_collector_content_page/companies-repeating-block-2-repeating-block.page"; +import ListCollectorContentPage from "../../generated_pages/list_collector_content_page/list-collector-content.page"; +import ListCollectorContentSectionSummaryPage from "../../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; +import ConfirmationCheckboxPage from "../../generated_pages/list_collector_content_page/confirmation-checkbox.page"; +import { listItemComplete, click } from "../../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { diff --git a/tests/functional/spec/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector/list_collector_driving_question.spec.js similarity index 78% rename from tests/functional/spec/list_collector_driving_question.spec.js rename to tests/functional/spec/list_collector/list_collector_driving_question.spec.js index 6bcccff737..8af6566133 100644 --- a/tests/functional/spec/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector/list_collector_driving_question.spec.js @@ -1,10 +1,10 @@ -import { checkItemsInList, click } from "../helpers"; -import HubPage from "../base_pages/hub.page.js"; -import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_driving_question/anyone-usually-live-at.page.js"; -import AnyoneElseLiveAtListCollectorPage from "../generated_pages/list_collector_driving_question/anyone-else-live-at.page.js"; -import AnyoneElseLiveAtListCollectorAddPage from "../generated_pages/list_collector_driving_question/anyone-else-live-at-add.page.js"; -import AnyoneElseLiveAtListCollectorRemovePage from "../generated_pages/list_collector_driving_question/anyone-else-live-at-remove.page.js"; -import SectionSummaryPage from "../generated_pages/list_collector_driving_question/section-summary.page.js"; +import { checkItemsInList, click } from "../../helpers"; +import HubPage from "../../base_pages/hub.page.js"; +import AnyoneUsuallyLiveAtPage from "../../generated_pages/list_collector_driving_question/anyone-usually-live-at.page.js"; +import AnyoneElseLiveAtListCollectorPage from "../../generated_pages/list_collector_driving_question/anyone-else-live-at.page.js"; +import AnyoneElseLiveAtListCollectorAddPage from "../../generated_pages/list_collector_driving_question/anyone-else-live-at-add.page.js"; +import AnyoneElseLiveAtListCollectorRemovePage from "../../generated_pages/list_collector_driving_question/anyone-else-live-at-remove.page.js"; +import SectionSummaryPage from "../../generated_pages/list_collector_driving_question/section-summary.page.js"; describe("List Collector Driving Question", () => { beforeEach("Load the survey", async () => { diff --git a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js b/tests/functional/spec/list_collector/list_collector_driving_question_checkbox.spec.js similarity index 82% rename from tests/functional/spec/list_collector_driving_question_checkbox.spec.js rename to tests/functional/spec/list_collector/list_collector_driving_question_checkbox.spec.js index 8d0daba5b6..647ad20daa 100644 --- a/tests/functional/spec/list_collector_driving_question_checkbox.spec.js +++ b/tests/functional/spec/list_collector/list_collector_driving_question_checkbox.spec.js @@ -1,13 +1,13 @@ -import { checkItemsInList, click } from "../helpers"; -import HubPage from "../base_pages/hub.page.js"; -import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector.page.js"; -import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_driving_checkbox/primary-person-list-collector-add.page.js"; -import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_driving_checkbox/anyone-usually-live-at.page.js"; -import ListCollectorAddPage from "../generated_pages/list_collector_driving_checkbox/list-collector-add.page.js"; -import ListCollectorPage from "../generated_pages/list_collector_driving_checkbox/list-collector.page.js"; -import ListCollectorTemporaryAwayPage from "../generated_pages/list_collector_driving_checkbox/list-collector-temporary-away-stay.page"; -import ListCollectorTemporaryAwayAddPage from "../generated_pages/list_collector_driving_checkbox/list-collector-temporary-away-stay-add.page"; -import SummaryPage from "../generated_pages/list_collector_driving_checkbox/section-summary.page"; +import { checkItemsInList, click } from "../../helpers"; +import HubPage from "../../base_pages/hub.page.js"; +import PrimaryPersonListCollectorPage from "../../generated_pages/list_collector_driving_checkbox/primary-person-list-collector.page.js"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/list_collector_driving_checkbox/primary-person-list-collector-add.page.js"; +import AnyoneUsuallyLiveAtPage from "../../generated_pages/list_collector_driving_checkbox/anyone-usually-live-at.page.js"; +import ListCollectorAddPage from "../../generated_pages/list_collector_driving_checkbox/list-collector-add.page.js"; +import ListCollectorPage from "../../generated_pages/list_collector_driving_checkbox/list-collector.page.js"; +import ListCollectorTemporaryAwayPage from "../../generated_pages/list_collector_driving_checkbox/list-collector-temporary-away-stay.page"; +import ListCollectorTemporaryAwayAddPage from "../../generated_pages/list_collector_driving_checkbox/list-collector-temporary-away-stay-add.page"; +import SummaryPage from "../../generated_pages/list_collector_driving_checkbox/section-summary.page"; const beforeSetup = async () => { await browser.openQuestionnaire("test_list_collector_driving_checkbox.json"); diff --git a/tests/functional/spec/list_collector_primary_person.spec.js b/tests/functional/spec/list_collector/list_collector_primary_person.spec.js similarity index 86% rename from tests/functional/spec/list_collector_primary_person.spec.js rename to tests/functional/spec/list_collector/list_collector_primary_person.spec.js index 5e49bd5a98..b38c74eaa6 100644 --- a/tests/functional/spec/list_collector_primary_person.spec.js +++ b/tests/functional/spec/list_collector/list_collector_primary_person.spec.js @@ -1,13 +1,13 @@ -import ListCollectorPage from "../generated_pages/list_collector_primary_person/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/list_collector_primary_person/list-collector-add.page.js"; -import ListCollectorEditPage from "../generated_pages/list_collector_primary_person/list-collector-edit.page.js"; -import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_primary_person/primary-person-list-collector.page.js"; -import PrimaryPersonListCollectorAddPage from "../generated_pages/list_collector_primary_person/primary-person-list-collector-add.page.js"; -import SectionSummaryPage from "../generated_pages/list_collector/section-summary.page.js"; -import { SubmitPage } from "../base_pages/submit.page.js"; -import ThankYouPage from "../base_pages/thank-you.page.js"; -import AnyoneUsuallyLiveAtPage from "../generated_pages/list_collector_primary_person/anyone-usually-live-at.page.js"; -import { click } from "../helpers"; +import ListCollectorPage from "../../generated_pages/list_collector_primary_person/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/list_collector_primary_person/list-collector-add.page.js"; +import ListCollectorEditPage from "../../generated_pages/list_collector_primary_person/list-collector-edit.page.js"; +import PrimaryPersonListCollectorPage from "../../generated_pages/list_collector_primary_person/primary-person-list-collector.page.js"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/list_collector_primary_person/primary-person-list-collector-add.page.js"; +import SectionSummaryPage from "../../generated_pages/list_collector/section-summary.page.js"; +import { SubmitPage } from "../../base_pages/submit.page.js"; +import ThankYouPage from "../../base_pages/thank-you.page.js"; +import AnyoneUsuallyLiveAtPage from "../../generated_pages/list_collector_primary_person/anyone-usually-live-at.page.js"; +import { click } from "../../helpers"; describe("Primary Person List Collector Survey", () => { describe("Given the user starts on the 'do you live here' question", () => { diff --git a/tests/functional/spec/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js similarity index 93% rename from tests/functional/spec/list_collector_section_summary.spec.js rename to tests/functional/spec/list_collector/list_collector_section_summary.spec.js index 1adb0ad1a8..48e17933e1 100644 --- a/tests/functional/spec/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js @@ -1,16 +1,16 @@ -import AnyCompaniesOrBranchesDrivingQuestionPage from "../generated_pages/list_collector_section_summary/any-companies-or-branches.page.js"; -import AnyCompaniesOrBranchesPage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches.page.js"; -import AnyCompaniesOrBranchesAddPage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-add.page.js"; -import AnyCompaniesOrBranchesRemovePage from "../generated_pages/list_collector_section_summary/any-other-companies-or-branches-remove.page.js"; -import SectionSummaryPage from "../generated_pages/list_collector_section_summary/section-companies-summary.page"; -import SectionSummaryTwoPage from "../generated_pages/list_collector_section_summary/section-household-summary.page"; -import UkBasedPage from "../generated_pages/list_collector_section_summary/confirmation-checkbox.page"; -import ListCollectorPage from "../generated_pages/list_collector_section_summary/list-collector.page"; -import HouseholderCheckboxPage from "../generated_pages/list_collector_section_summary/householder-checkbox.page"; -import SubmitPage from "../generated_pages/list_collector_section_summary/submit.page"; -import ThankYouPage from "../base_pages/thank-you.page"; -import ViewSubmittedResponsePage from "../generated_pages/list_collector_section_summary/view-submitted-response.page"; -import { click, listItemIds } from "../helpers"; +import AnyCompaniesOrBranchesDrivingQuestionPage from "../../generated_pages/list_collector_section_summary/any-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesPage from "../../generated_pages/list_collector_section_summary/any-other-companies-or-branches.page.js"; +import AnyCompaniesOrBranchesAddPage from "../../generated_pages/list_collector_section_summary/any-other-companies-or-branches-add.page.js"; +import AnyCompaniesOrBranchesRemovePage from "../../generated_pages/list_collector_section_summary/any-other-companies-or-branches-remove.page.js"; +import SectionSummaryPage from "../../generated_pages/list_collector_section_summary/section-companies-summary.page"; +import SectionSummaryTwoPage from "../../generated_pages/list_collector_section_summary/section-household-summary.page"; +import UkBasedPage from "../../generated_pages/list_collector_section_summary/confirmation-checkbox.page"; +import ListCollectorPage from "../../generated_pages/list_collector_section_summary/list-collector.page"; +import HouseholderCheckboxPage from "../../generated_pages/list_collector_section_summary/householder-checkbox.page"; +import SubmitPage from "../../generated_pages/list_collector_section_summary/submit.page"; +import ThankYouPage from "../../base_pages/thank-you.page"; +import ViewSubmittedResponsePage from "../../generated_pages/list_collector_section_summary/view-submitted-response.page"; +import { click, listItemIds } from "../../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { diff --git a/tests/functional/spec/list_collector_variants.spec.js b/tests/functional/spec/list_collector/list_collector_variants.spec.js similarity index 86% rename from tests/functional/spec/list_collector_variants.spec.js rename to tests/functional/spec/list_collector/list_collector_variants.spec.js index 6bb3610d2a..43fb7a4f85 100644 --- a/tests/functional/spec/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector/list_collector_variants.spec.js @@ -1,11 +1,11 @@ -import { checkItemsInList, click } from "../helpers"; -import YouLiveHerePage from "../generated_pages/list_collector_variants/you-live-here-block.page.js"; -import ListCollectorPage from "../generated_pages/list_collector_variants/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/list_collector_variants/list-collector-add.page.js"; -import ListCollectorEditPage from "../generated_pages/list_collector_variants/list-collector-edit.page.js"; -import ListCollectorRemovePage from "../generated_pages/list_collector_variants/list-collector-remove.page.js"; -import { SubmitPage } from "../base_pages/submit.page.js"; -import ThankYouPage from "../base_pages/thank-you.page.js"; +import { checkItemsInList, click } from "../../helpers"; +import YouLiveHerePage from "../../generated_pages/list_collector_variants/you-live-here-block.page.js"; +import ListCollectorPage from "../../generated_pages/list_collector_variants/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/list_collector_variants/list-collector-add.page.js"; +import ListCollectorEditPage from "../../generated_pages/list_collector_variants/list-collector-edit.page.js"; +import ListCollectorRemovePage from "../../generated_pages/list_collector_variants/list-collector-remove.page.js"; +import { SubmitPage } from "../../base_pages/submit.page.js"; +import ThankYouPage from "../../base_pages/thank-you.page.js"; describe("List Collector With Variants", () => { describe("Given that a person lives in house", () => { diff --git a/tests/functional/spec/list_collector_variants_primary_person.spec.js b/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js similarity index 88% rename from tests/functional/spec/list_collector_variants_primary_person.spec.js rename to tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js index 1d01495bf0..1e1d6b3ae6 100644 --- a/tests/functional/spec/list_collector_variants_primary_person.spec.js +++ b/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js @@ -1,11 +1,11 @@ -import VariantBlockPage from "../generated_pages/list_collector_variants_primary_person/variant-block.page"; -import PrimaryPersonListCollectorPage from "../generated_pages/list_collector_variants_primary_person/primary-person-list-collector.page"; -import ListCollectorAddPage from "../generated_pages/list_collector_variants_primary_person/list-collector-add.page"; -import ListCollectorPage from "../generated_pages/list_collector_variants_primary_person/list-collector.page"; -import EditPersonPage from "../generated_pages/list_collector_variants_primary_person/list-collector-edit.page"; -import SubmitPage from "../generated_pages/list_collector_variants_primary_person/submit.page"; -import ThankYouPage from "../base_pages/thank-you.page.js"; -import { click } from "../helpers"; +import VariantBlockPage from "../../generated_pages/list_collector_variants_primary_person/variant-block.page"; +import PrimaryPersonListCollectorPage from "../../generated_pages/list_collector_variants_primary_person/primary-person-list-collector.page"; +import ListCollectorAddPage from "../../generated_pages/list_collector_variants_primary_person/list-collector-add.page"; +import ListCollectorPage from "../../generated_pages/list_collector_variants_primary_person/list-collector.page"; +import EditPersonPage from "../../generated_pages/list_collector_variants_primary_person/list-collector-edit.page"; +import SubmitPage from "../../generated_pages/list_collector_variants_primary_person/submit.page"; +import ThankYouPage from "../../base_pages/thank-you.page.js"; +import { click } from "../../helpers"; describe("List collector with variants primary person", () => { describe("Given that person lives in house", () => { diff --git a/tests/functional/spec/relationships-unrelated.spec.js b/tests/functional/spec/list_collector/relationships-unrelated.spec.js similarity index 89% rename from tests/functional/spec/relationships-unrelated.spec.js rename to tests/functional/spec/list_collector/relationships-unrelated.spec.js index 466d253b45..5f027bc70c 100644 --- a/tests/functional/spec/relationships-unrelated.spec.js +++ b/tests/functional/spec/list_collector/relationships-unrelated.spec.js @@ -1,9 +1,9 @@ -import ListCollectorPage from "../generated_pages/relationships_unrelated/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/relationships_unrelated/list-collector-add.page.js"; -import RelationshipsPage from "../generated_pages/relationships_unrelated/relationships.page.js"; -import RelatedToAnyoneElsePage from "../generated_pages/relationships_unrelated/related-to-anyone-else.page.js"; -import RelationshipsInterstitialPage from "../generated_pages/relationships_unrelated/relationship-interstitial.page.js"; -import { click } from "../helpers"; +import ListCollectorPage from "../../generated_pages/relationships_unrelated/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/relationships_unrelated/list-collector-add.page.js"; +import RelationshipsPage from "../../generated_pages/relationships_unrelated/relationships.page.js"; +import RelatedToAnyoneElsePage from "../../generated_pages/relationships_unrelated/related-to-anyone-else.page.js"; +import RelationshipsInterstitialPage from "../../generated_pages/relationships_unrelated/relationship-interstitial.page.js"; +import { click } from "../../helpers"; describe("Unrelated Relationships", () => { const schema = "test_relationships_unrelated.json"; diff --git a/tests/functional/spec/relationships.spec.js b/tests/functional/spec/list_collector/relationships.spec.js similarity index 94% rename from tests/functional/spec/relationships.spec.js rename to tests/functional/spec/list_collector/relationships.spec.js index eed441f469..8efd56015c 100644 --- a/tests/functional/spec/relationships.spec.js +++ b/tests/functional/spec/list_collector/relationships.spec.js @@ -1,10 +1,10 @@ -import ListCollectorPage from "../generated_pages/relationships/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/relationships/list-collector-add.page.js"; -import ListCollectorRemovePage from "../generated_pages/relationships/list-collector-remove.page.js"; -import RelationshipsPage from "../generated_pages/relationships/relationships.page.js"; -import RelationshipsInterstitialPage from "../generated_pages/relationships/relationship-interstitial.page.js"; -import SectionSummaryPage from "../generated_pages/relationships/section-summary.page.js"; -import { click } from "../helpers"; +import ListCollectorPage from "../../generated_pages/relationships/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/relationships/list-collector-add.page.js"; +import ListCollectorRemovePage from "../../generated_pages/relationships/list-collector-remove.page.js"; +import RelationshipsPage from "../../generated_pages/relationships/relationships.page.js"; +import RelationshipsInterstitialPage from "../../generated_pages/relationships/relationship-interstitial.page.js"; +import SectionSummaryPage from "../../generated_pages/relationships/section-summary.page.js"; +import { click } from "../../helpers"; describe("Relationships", () => { const schema = "test_relationships.json"; diff --git a/tests/functional/spec/relationships_primary.spec.js b/tests/functional/spec/list_collector/relationships_primary.spec.js similarity index 86% rename from tests/functional/spec/relationships_primary.spec.js rename to tests/functional/spec/list_collector/relationships_primary.spec.js index 44b758c19d..ef696073f7 100644 --- a/tests/functional/spec/relationships_primary.spec.js +++ b/tests/functional/spec/list_collector/relationships_primary.spec.js @@ -1,9 +1,9 @@ -import PrimaryPersonListCollectorPage from "../generated_pages/relationships_primary/primary-person-list-collector.page.js"; -import PrimaryPersonListCollectorAddPage from "../generated_pages/relationships_primary/primary-person-list-collector-add.page.js"; -import ListCollectorPage from "../generated_pages/relationships_primary/list-collector.page.js"; -import ListCollectorAddPage from "../generated_pages/relationships_primary/list-collector-add.page.js"; -import RelationshipsPage from "../generated_pages/relationships_primary/relationships.page.js"; -import { click } from "../helpers"; +import PrimaryPersonListCollectorPage from "../../generated_pages/relationships_primary/primary-person-list-collector.page.js"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/relationships_primary/primary-person-list-collector-add.page.js"; +import ListCollectorPage from "../../generated_pages/relationships_primary/list-collector.page.js"; +import ListCollectorAddPage from "../../generated_pages/relationships_primary/list-collector-add.page.js"; +import RelationshipsPage from "../../generated_pages/relationships_primary/relationships.page.js"; +import { click } from "../../helpers"; describe("Relationships - Primary Person", () => { const schema = "test_relationships_primary.json"; diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary.spec.js b/tests/functional/spec/summaries/calculated_summary/calculated_summary.spec.js similarity index 100% rename from tests/functional/spec/features/calculated_summary/calculated_summary.spec.js rename to tests/functional/spec/summaries/calculated_summary/calculated_summary.spec.js diff --git a/tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js similarity index 100% rename from tests/functional/spec/features/calculated_summary/calculated_summary_test_case.js rename to tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js similarity index 100% rename from tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js rename to tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js similarity index 100% rename from tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_blocks.spec.js rename to tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js diff --git a/tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js similarity index 100% rename from tests/functional/spec/features/calculated_summary/new_calculated_summary_repeating_section.spec.js rename to tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js similarity index 100% rename from tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js rename to tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js similarity index 100% rename from tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js rename to tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js similarity index 100% rename from tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js rename to tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js diff --git a/tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js similarity index 100% rename from tests/functional/spec/features/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js rename to tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js diff --git a/tests/functional/spec/features/question_summary/custom_question_summary.spec.js b/tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js similarity index 100% rename from tests/functional/spec/features/question_summary/custom_question_summary.spec.js rename to tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js diff --git a/tests/functional/spec/features/section_summary/section_summary.spec.js b/tests/functional/spec/summaries/section_summary/section_summary.spec.js similarity index 100% rename from tests/functional/spec/features/section_summary/section_summary.spec.js rename to tests/functional/spec/summaries/section_summary/section_summary.spec.js diff --git a/tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js b/tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js similarity index 100% rename from tests/functional/spec/features/section_summary/section_summary_repeating_sections.spec.js rename to tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js diff --git a/tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js b/tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js similarity index 100% rename from tests/functional/spec/features/show_section_summary_on_completion/show_section_summary_on_completion.spec.js rename to tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 74dd7065b9..be5a2971fb 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -23,6 +23,9 @@ export const config = { timeout_modal_extended_new_window: ["./spec/timeout/timeout_modal_extended_new_window/*.js"], components: ["./spec/components/**/*.js"], features: ["./spec/features/**/*.js"], + summaries: ["./spec/summaries/**/*.js"], + journeys: ["./spec/journeys/**/*.js"], + list_collector: ["./spec/list_collector/**/*.js"], general: ["./spec/*.spec.js"], }, // Patterns to exclude. From 79a7ef0a2c800653e4369bea21c3b210428466fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:31:43 +0100 Subject: [PATCH 477/567] Bump the production-dependencies group across 1 directory with 9 updates (#1539) --- poetry.lock | 490 ++++++++++++++++++-------- pyproject.toml | 2 +- tests/app/publisher/test_publisher.py | 7 +- 3 files changed, 348 insertions(+), 151 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6ec8047857..c98b0acaae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.23" +version = "1.35.43" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.23-py3-none-any.whl", hash = "sha256:ecba4362f82e23ef775c72b3e6fdef3ef68443629b79e88886d5088302ffc050"}, - {file = "boto3-1.35.23.tar.gz", hash = "sha256:3fbf1d5b749c92ed43aa190650979dff9f83790a42522e1e9eefa54c8e44bc4b"}, + {file = "boto3-1.35.43-py3-none-any.whl", hash = "sha256:e6a50a0599f75b21de0de1a551a0564793d25b304fa623e4052e527b268de734"}, + {file = "boto3-1.35.43.tar.gz", hash = "sha256:0197f460632804577aa78b2f6daf7b823bffa9d4d67a5cebb179efff0fe9631b"}, ] [package.dependencies] -botocore = ">=1.35.23,<1.36.0" +botocore = ">=1.35.43,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.23" +version = "1.35.43" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.23-py3-none-any.whl", hash = "sha256:cab9ec4e0367b9f33f0bc02c5a29f587b0119ecffd6d125bacee085dcbc8817d"}, - {file = "botocore-1.35.23.tar.gz", hash = "sha256:25b17a9ccba6ad32bb5bf7ba4f52656aa03c1cb29f6b4e438050ee4ad1967a3b"}, + {file = "botocore-1.35.43-py3-none-any.whl", hash = "sha256:7cfdee9117617da97daaf259dd8484bcdc259c59eb7d1ce7db9ecf8506b7d36c"}, + {file = "botocore-1.35.43.tar.gz", hash = "sha256:04539b85ade060601a3023cacb538fc17aad8c059a5a2e18fe4bc5d0d91fbd72"}, ] [package.dependencies] @@ -156,7 +156,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.21.5)"] +crt = ["awscrt (==0.22.0)"] [[package]] name = "brotli" @@ -756,6 +756,23 @@ editorconfig = ">=0.12.2" jsbeautifier = "*" six = ">=1.13.0" +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "dill" version = "0.3.8" @@ -1017,13 +1034,13 @@ pytz = ">=2022.7" [[package]] name = "flask-compress" -version = "1.15" +version = "1.17" description = "Compress responses in your Flask app with gzip, deflate, brotli or zstandard." optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "Flask_Compress-1.15-py3-none-any.whl", hash = "sha256:5d6efe3584c89516c3ab9d94adabe08c218517b957a9bd5cd0c3955dd3834c51"}, - {file = "flask_compress-1.15.tar.gz", hash = "sha256:b7b66cd5d08fc46bbcc71561e13ca64321590b0ca4c172f8001bf5374f8f5c58"}, + {file = "Flask_Compress-1.17-py3-none-any.whl", hash = "sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20"}, + {file = "flask_compress-1.17.tar.gz", hash = "sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8"}, ] [package.dependencies] @@ -1246,13 +1263,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.23.1" +version = "2.26.1" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.23.1-py2.py3-none-any.whl", hash = "sha256:a173292a699851eb622016d3f2796ecf2d69692e708ea0e7382f338fc1679f8a"}, - {file = "google_cloud_pubsub-2.23.1.tar.gz", hash = "sha256:e1fde79b5b64b721290af4c022907afcbb83512d92f4e5c334c391cfbb022acb"}, + {file = "google_cloud_pubsub-2.26.1-py2.py3-none-any.whl", hash = "sha256:932d4434d86af25673082b48d54b318a448d1a7cd718404c33bf008ae9a8bb22"}, + {file = "google_cloud_pubsub-2.26.1.tar.gz", hash = "sha256:d46a302c2c7a008e399f4c04b4be6341d8aa7a537a25810ec8d38a5c125f816d"}, ] [package.dependencies] @@ -1261,6 +1278,8 @@ google-auth = ">=2.14.1,<3.0.0dev" grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" grpcio = ">=1.51.3,<2.0dev" grpcio-status = ">=1.33.2" +opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} +opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" @@ -1513,61 +1532,70 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.66.1" +version = "1.67.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, - {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, - {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, - {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, - {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, - {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, - {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, - {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, - {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, - {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, - {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, - {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, - {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, - {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, - {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, - {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, - {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, - {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, - {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, - {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, - {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.66.1)"] + {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"}, + {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"}, + {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"}, + {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"}, + {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"}, + {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"}, + {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"}, + {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"}, + {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"}, + {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"}, + {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"}, + {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"}, + {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"}, + {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"}, + {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"}, + {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"}, + {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"}, + {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"}, + {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"}, + {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"}, + {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"}, + {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"}, + {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"}, + {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"}, + {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"}, + {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"}, + {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"}, + {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"}, + {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"}, + {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"}, + {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"}, + {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"}, + {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"}, + {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"}, + {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"}, + {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"}, + {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.67.0)"] [[package]] name = "grpcio-status" @@ -1668,13 +1696,13 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve [[package]] name = "humanize" -version = "4.10.0" +version = "4.11.0" description = "Python humanize utilities" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "humanize-4.10.0-py3-none-any.whl", hash = "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6"}, - {file = "humanize-4.10.0.tar.gz", hash = "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978"}, + {file = "humanize-4.11.0-py3-none-any.whl", hash = "sha256:b53caaec8532bcb2fff70c8826f904c35943f8cecaca29d272d9df38092736c0"}, + {file = "humanize-4.11.0.tar.gz", hash = "sha256:e66f36020a2d5a974c504bd2555cf770621dbdbb6d82f94a6857c0b1ea2608be"}, ] [package.extras] @@ -1691,6 +1719,25 @@ files = [ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] +[[package]] +name = "importlib-metadata" +version = "8.4.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1843,91 +1890,92 @@ typing-extensions = ">=4.5.0" [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.1" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +python-versions = ">=3.9" +files = [ + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, + {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, ] [[package]] name = "marshmallow" -version = "3.22.0" +version = "3.23.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, - {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, + {file = "marshmallow-3.23.0-py3-none-any.whl", hash = "sha256:82f20a2397834fe6d9611b241f2f7e7b680ed89c49f84728a1ad937be6b4bdf4"}, + {file = "marshmallow-3.23.0.tar.gz", hash = "sha256:98d8827a9f10c03d44ead298d2e99c6aea8197df18ccfad360dae7f89a50da2e"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "simplejson"] [[package]] name = "mccabe" @@ -2058,6 +2106,52 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "opentelemetry-api" +version = "1.27.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, + {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +importlib-metadata = ">=6.0,<=8.4.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.27.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, + {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, +] + +[package.dependencies] +opentelemetry-api = "1.27.0" +opentelemetry-semantic-conventions = "0.48b0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.48b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, + {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +opentelemetry-api = "1.27.0" + [[package]] name = "ordered-set" version = "4.1.0" @@ -2579,18 +2673,18 @@ files = [ [[package]] name = "redis" -version = "5.0.8" +version = "5.1.1" description = "Python client for Redis database and key-value store" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"}, - {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"}, + {file = "redis-5.1.1-py3-none-any.whl", hash = "sha256:f8ea06b7482a668c6475ae202ed8d9bcaa409f6e87fb77ed1043d912afd62e24"}, + {file = "redis-5.1.1.tar.gz", hash = "sha256:f6c997521fedbae53387307c5d0bf784d9acc28d9f1d058abeac566ec4dbed72"}, ] [package.extras] -hiredis = ["hiredis (>1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] +hiredis = ["hiredis (>=3.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] [[package]] name = "referencing" @@ -3303,12 +3397,12 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uwsgi" -version = "2.0.26" +version = "2.0.27" description = "The uWSGI server" optional = false python-versions = "*" files = [ - {file = "uwsgi-2.0.26.tar.gz", hash = "sha256:86e6bfcd4dc20529665f5b7777193cdc48622fb2c59f0a7f1e3dc32b3882e7f9"}, + {file = "uwsgi-2.0.27.tar.gz", hash = "sha256:3ee5bfb7e6e9c93478c22aa8183eef35b95a2d5b14cca16172e67f135565c458"}, ] [[package]] @@ -3328,6 +3422,85 @@ MarkupSafe = ">=2.1.1" [package.extras] watchdog = ["watchdog (>=2.3)"] +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + [[package]] name = "wtforms" version = "3.1.2" @@ -3356,6 +3529,25 @@ files = [ {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, ] +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [[package]] name = "zope-event" version = "5.0" @@ -3542,4 +3734,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.11.4" -content-hash = "4133ab93f768c81d508cd979aebf949c95b7ef1684d0b80d057a5070d345cd5b" +content-hash = "25d565224777a7e95a41bb5d211384b3db9abea084aacb2882cd58076a57ffc0" diff --git a/pyproject.toml b/pyproject.toml index fb5baa0f22..bf1bbec86e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ itsdangerous = "^2.1.2" google-cloud-pubsub = "^2.23.0" google-cloud-tasks = "^2.16.3" simplejson = "^3.19.2" -markupsafe = "^2.1.5" +markupsafe = "^3.0.1" pdfkit = "^1.0.0" ordered-set = "^4.1.0" cachetools = "^5.3.0.7" diff --git a/tests/app/publisher/test_publisher.py b/tests/app/publisher/test_publisher.py index b29a2a2f3a..569fc18191 100644 --- a/tests/app/publisher/test_publisher.py +++ b/tests/app/publisher/test_publisher.py @@ -2,6 +2,9 @@ from uuid import uuid4 import pytest +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) from google.pubsub_v1.types.pubsub import PubsubMessage from app.publisher.exceptions import PublicationFailed @@ -29,7 +32,9 @@ def test_publish(publisher, mocker): assert future is mocker.sentinel.future # Check mock. - batch.publish.assert_has_calls([mocker.call(PubsubMessage(data=b"test-message"))]) + batch.publish.assert_has_calls( + [mocker.call(PublishMessageWrapper(PubsubMessage({"data": b"test-message"})))] + ) def test_resolving_message_raises_exception_on_error(publisher): From fbac4bb227675dabd64aa5728b22589b1d608b50 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:05:22 +0100 Subject: [PATCH 478/567] Bump the development-dependencies group across 1 directory with 10 updates (#1540) * Bump the development-dependencies group across 1 directory with 10 updates Bumps the development-dependencies group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [pylint](https://github.com/pylint-dev/pylint) | `3.2.7` | `3.3.1` | | [moto](https://github.com/getmoto/moto) | `5.0.14` | `5.0.17` | | [fakeredis](https://github.com/cunla/fakeredis-py) | `2.24.1` | `2.25.1` | | [mypy](https://github.com/python/mypy) | `1.11.2` | `1.12.0` | | [types-requests](https://github.com/python/typeshed) | `2.32.0.20240907` | `2.32.0.20241016` | | [types-redis](https://github.com/python/typeshed) | `4.6.0.20240903` | `4.6.0.20241004` | | [types-pyyaml](https://github.com/python/typeshed) | `6.0.12.20240808` | `6.0.12.20240917` | | [types-python-dateutil](https://github.com/python/typeshed) | `2.9.0.20240906` | `2.9.0.20241003` | | [types-pytz](https://github.com/python/typeshed) | `2024.2.0.20240913` | `2024.2.0.20241003` | | [black](https://github.com/psf/black) | `24.8.0` | `24.10.0` | Updates `pylint` from 3.2.7 to 3.3.1 - [Release notes](https://github.com/pylint-dev/pylint/releases) - [Commits](https://github.com/pylint-dev/pylint/compare/v3.2.7...v3.3.1) Updates `moto` from 5.0.14 to 5.0.17 - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/5.0.14...5.0.17) Updates `fakeredis` from 2.24.1 to 2.25.1 - [Release notes](https://github.com/cunla/fakeredis-py/releases) - [Commits](https://github.com/cunla/fakeredis-py/compare/v2.24.1...v2.25.1) Updates `mypy` from 1.11.2 to 1.12.0 - [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md) - [Commits](https://github.com/python/mypy/compare/v1.11.2...v1.12.0) Updates `types-requests` from 2.32.0.20240907 to 2.32.0.20241016 - [Commits](https://github.com/python/typeshed/commits) Updates `types-redis` from 4.6.0.20240903 to 4.6.0.20241004 - [Commits](https://github.com/python/typeshed/commits) Updates `types-pyyaml` from 6.0.12.20240808 to 6.0.12.20240917 - [Commits](https://github.com/python/typeshed/commits) Updates `types-python-dateutil` from 2.9.0.20240906 to 2.9.0.20241003 - [Commits](https://github.com/python/typeshed/commits) Updates `types-pytz` from 2024.2.0.20240913 to 2024.2.0.20241003 - [Commits](https://github.com/python/typeshed/commits) Updates `black` from 24.8.0 to 24.10.0 - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/24.8.0...24.10.0) --- updated-dependencies: - dependency-name: pylint dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: moto dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: fakeredis dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-redis dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-pyyaml dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-python-dateutil dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: types-pytz dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: black dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] * assign max positional arguments in pylint to 12 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yuyutsu Rai Co-authored-by: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> --- .pylintrc | 3 + poetry.lock | 176 +++++++++++++++++++++++++++------------------------- 2 files changed, 94 insertions(+), 85 deletions(-) diff --git a/.pylintrc b/.pylintrc index 79934a0cb4..c1c4743900 100644 --- a/.pylintrc +++ b/.pylintrc @@ -299,6 +299,9 @@ min-public-methods=2 # Maximum cyclomatic complexity max-complexity=10 +# Maximum positional arguments +max-positional-arguments=12 + [EXCEPTIONS] # Exceptions that will emit a warning when caught. diff --git a/poetry.lock b/poetry.lock index c98b0acaae..494942cf76 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "astroid" -version = "3.2.4" +version = "3.3.5" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, - {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, + {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, + {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] [[package]] @@ -67,33 +67,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.8.0" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -105,7 +105,7 @@ platformdirs = ">=2" [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -873,17 +873,17 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.24.1" +version = "2.25.1" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.24.1-py3-none-any.whl", hash = "sha256:09d3049a29910f80c0ef5789c31bef3dbb9727bd43a67ee8598217f4efd12f35"}, - {file = "fakeredis-2.24.1.tar.gz", hash = "sha256:4a52ab0edad53543ac5e3a41d761f91012613ed583344da54ae6473e05b0f6d0"}, + {file = "fakeredis-2.25.1-py3-none-any.whl", hash = "sha256:d08dcbaceae0804db4644fa634106e3c42d76fe4d11aea2949eda768df0c6450"}, + {file = "fakeredis-2.25.1.tar.gz", hash = "sha256:e9e73bacf412d1d942ee7f80525dc188182158e82d41be57eb9c4e71f7474ac8"}, ] [package.dependencies] -redis = ">=4" +redis = {version = ">=4.3", markers = "python_full_version > \"3.8.0\""} sortedcontainers = ">=2,<3" [package.extras] @@ -2006,13 +2006,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.14" +version = "5.0.17" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.14-py2.py3-none-any.whl", hash = "sha256:c738ffe85d3844ef37b865951736c4faf2e0f3e4f05db87bdad97a6c01b88174"}, - {file = "moto-5.0.14.tar.gz", hash = "sha256:0f849243269fd03372426c302b18cb605302da32620d7f0266be6a40735b2acd"}, + {file = "moto-5.0.17-py2.py3-none-any.whl", hash = "sha256:6b56b7bb5675d60e79890c9867e446f29d7a5be02051238b8df079f649270130"}, + {file = "moto-5.0.17.tar.gz", hash = "sha256:165a291ac0b983f53a09f67f9841f72214c5a1b0c56392d88f7035a6a8718fca"}, ] [package.dependencies] @@ -2027,7 +2027,7 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "jsonschema", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] @@ -2041,6 +2041,7 @@ events = ["jsonpath-ng"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +quicksight = ["jsonschema"] resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)"] s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.6)"] s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.6)"] @@ -2051,38 +2052,43 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.11.2" +version = "1.12.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4397081e620dc4dc18e2f124d5e1d2c288194c2c08df6bdb1db31c38cd1fe1ed"}, + {file = "mypy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:684a9c508a283f324804fea3f0effeb7858eb03f85c4402a967d187f64562469"}, + {file = "mypy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cabe4cda2fa5eca7ac94854c6c37039324baaa428ecbf4de4567279e9810f9e"}, + {file = "mypy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:060a07b10e999ac9e7fa249ce2bdcfa9183ca2b70756f3bce9df7a92f78a3c0a"}, + {file = "mypy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0eff042d7257f39ba4ca06641d110ca7d2ad98c9c1fb52200fe6b1c865d360ff"}, + {file = "mypy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b86de37a0da945f6d48cf110d5206c5ed514b1ca2614d7ad652d4bf099c7de7"}, + {file = "mypy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20c7c5ce0c1be0b0aea628374e6cf68b420bcc772d85c3c974f675b88e3e6e57"}, + {file = "mypy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a64ee25f05fc2d3d8474985c58042b6759100a475f8237da1f4faf7fcd7e6309"}, + {file = "mypy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faca7ab947c9f457a08dcb8d9a8664fd438080e002b0fa3e41b0535335edcf7f"}, + {file = "mypy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:5bc81701d52cc8767005fdd2a08c19980de9ec61a25dbd2a937dfb1338a826f9"}, + {file = "mypy-1.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8462655b6694feb1c99e433ea905d46c478041a8b8f0c33f1dab00ae881b2164"}, + {file = "mypy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:923ea66d282d8af9e0f9c21ffc6653643abb95b658c3a8a32dca1eff09c06475"}, + {file = "mypy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ebf9e796521f99d61864ed89d1fb2926d9ab6a5fab421e457cd9c7e4dd65aa9"}, + {file = "mypy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e478601cc3e3fa9d6734d255a59c7a2e5c2934da4378f3dd1e3411ea8a248642"}, + {file = "mypy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:c72861b7139a4f738344faa0e150834467521a3fba42dc98264e5aa9507dd601"}, + {file = "mypy-1.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52b9e1492e47e1790360a43755fa04101a7ac72287b1a53ce817f35899ba0521"}, + {file = "mypy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48d3e37dd7d9403e38fa86c46191de72705166d40b8c9f91a3de77350daa0893"}, + {file = "mypy-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f106db5ccb60681b622ac768455743ee0e6a857724d648c9629a9bd2ac3f721"}, + {file = "mypy-1.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:233e11b3f73ee1f10efada2e6da0f555b2f3a5316e9d8a4a1224acc10e7181d3"}, + {file = "mypy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ae8959c21abcf9d73aa6c74a313c45c0b5a188752bf37dace564e29f06e9c1b"}, + {file = "mypy-1.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eafc1b7319b40ddabdc3db8d7d48e76cfc65bbeeafaa525a4e0fa6b76175467f"}, + {file = "mypy-1.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9b9ce1ad8daeb049c0b55fdb753d7414260bad8952645367e70ac91aec90e07e"}, + {file = "mypy-1.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfe012b50e1491d439172c43ccb50db66d23fab714d500b57ed52526a1020bb7"}, + {file = "mypy-1.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c40658d4fa1ab27cb53d9e2f1066345596af2f8fe4827defc398a09c7c9519b"}, + {file = "mypy-1.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:dee78a8b9746c30c1e617ccb1307b351ded57f0de0d287ca6276378d770006c0"}, + {file = "mypy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b5df6c8a8224f6b86746bda716bbe4dbe0ce89fd67b1fa4661e11bfe38e8ec8"}, + {file = "mypy-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5feee5c74eb9749e91b77f60b30771563327329e29218d95bedbe1257e2fe4b0"}, + {file = "mypy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77278e8c6ffe2abfba6db4125de55f1024de9a323be13d20e4f73b8ed3402bd1"}, + {file = "mypy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dcfb754dea911039ac12434d1950d69a2f05acd4d56f7935ed402be09fad145e"}, + {file = "mypy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:06de0498798527451ffb60f68db0d368bd2bae2bbfb5237eae616d4330cc87aa"}, + {file = "mypy-1.12.0-py3-none-any.whl", hash = "sha256:fd313226af375d52e1e36c383f39bf3836e1f192801116b31b090dfcd3ec5266"}, + {file = "mypy-1.12.0.tar.gz", hash = "sha256:65a22d87e757ccd95cbbf6f7e181e6caa87128255eb2b6be901bb71b26d8a99d"}, ] [package.dependencies] @@ -2392,17 +2398,17 @@ files = [ [[package]] name = "pylint" -version = "3.2.7" +version = "3.3.1" description = "python code static checker" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, - {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, + {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, + {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, ] [package.dependencies] -astroid = ">=3.2.4,<=3.3.0-dev0" +astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, @@ -3274,46 +3280,46 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20240906" +version = "2.9.0.20241003" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240906.tar.gz", hash = "sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e"}, - {file = "types_python_dateutil-2.9.0.20240906-py3-none-any.whl", hash = "sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6"}, + {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, + {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, ] [[package]] name = "types-pytz" -version = "2024.2.0.20240913" +version = "2024.2.0.20241003" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.2.0.20240913.tar.gz", hash = "sha256:4433b5df4a6fc587bbed41716d86a5ba5d832b4378e506f40d34bc9c81df2c24"}, - {file = "types_pytz-2024.2.0.20240913-py3-none-any.whl", hash = "sha256:a1eebf57ebc6e127a99d2fa2ba0a88d2b173784ef9b3defcc2004ab6855a44df"}, + {file = "types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44"}, + {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20240808" +version = "6.0.12.20240917" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, - {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, ] [[package]] name = "types-redis" -version = "4.6.0.20240903" +version = "4.6.0.20241004" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" files = [ - {file = "types-redis-4.6.0.20240903.tar.gz", hash = "sha256:4bab1a378dbf23c2c95c370dfdb89a8f033957c4fd1a53fee71b529c182fe008"}, - {file = "types_redis-4.6.0.20240903-py3-none-any.whl", hash = "sha256:0e7537e5c085fe96b7d468d5edae0cf667b4ba4b62c6e4a5dfc340bd3b868c23"}, + {file = "types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e"}, + {file = "types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed"}, ] [package.dependencies] @@ -3322,13 +3328,13 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20240907" +version = "2.32.0.20241016" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240907.tar.gz", hash = "sha256:ff33935f061b5e81ec87997e91050f7b4af4f82027a7a7a9d9aaea04a963fdf8"}, - {file = "types_requests-2.32.0.20240907-py3-none-any.whl", hash = "sha256:1d1e79faeaf9d42def77f3c304893dea17a97cae98168ac69f3cb465516ee8da"}, + {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, + {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, ] [package.dependencies] From 2f23ff42170ca1c8349d6bc072987089fa9761af Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:26:45 +0000 Subject: [PATCH 479/567] Schemas v5.17.0 (#1547) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 8837950118..11640d2daf 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.16.0 +v5.17.0 From 190e3cf3f5c959c9bb099ea7cfbaecb985cbc862 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:23:05 +0000 Subject: [PATCH 480/567] Update packages and version to Python 3.12 (#1530) --- .pylintrc | 2 +- .python-version | 2 +- Dockerfile | 2 +- README.md | 2 +- app/questionnaire/placeholder_transforms.py | 3 +- app/questionnaire/rules/operations.py | 2 +- poetry.lock | 2002 ++++++++++--------- pyproject.toml | 3 +- setup.cfg | 2 +- 9 files changed, 1017 insertions(+), 1003 deletions(-) diff --git a/.pylintrc b/.pylintrc index c1c4743900..a30a8bcc96 100644 --- a/.pylintrc +++ b/.pylintrc @@ -84,7 +84,7 @@ persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. -py-version=3.11 +py-version=3.12 # Discover python modules and packages in the file system subtree. recursive=no diff --git a/.python-version b/.python-version index 0c7d5f5f5d..35f236d6e5 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11.4 +3.12.6 diff --git a/Dockerfile b/Dockerfile index 99e9965af8..5cf036ef81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim-bullseye +FROM python:3.12-slim-bullseye EXPOSE 5000 diff --git a/README.md b/README.md index 70a73756fd..d848352cd5 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" ``` -Python versions can be changed with the `pyenv local` or `pyenv global` commands suffixed with the desired version (e.g. 3.11.4). Different versions of Python can be installed first with the `pyenv install` command. Refer to the pyenv project Readme [here](https://github.com/pyenv/pyenv). To avoid confusion, check the current Python version at any given time using `python --version` or `python3 --version`. +Python versions can be changed with the `pyenv local` or `pyenv global` commands suffixed with the desired version (e.g. 3.12.6). Different versions of Python can be installed first with the `pyenv install` command. Refer to the pyenv project Readme [here](https://github.com/pyenv/pyenv). To avoid confusion, check the current Python version at any given time using `python --version` or `python3 --version`. #### Python & dependencies diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 26abcfd17a..2876b42dfd 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -1,6 +1,7 @@ +from collections.abc import Sized from datetime import date, datetime, timezone from decimal import Decimal -from typing import TYPE_CHECKING, Literal, Mapping, Sequence, Sized +from typing import TYPE_CHECKING, Literal, Mapping, Sequence from urllib.parse import quote from babel.dates import format_datetime diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 9ac1f48e38..a02fcd5019 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -1,3 +1,4 @@ +from collections.abc import Sized from copy import deepcopy from datetime import date from decimal import Decimal @@ -6,7 +7,6 @@ Iterable, Mapping, Sequence, - Sized, TypeAlias, TypedDict, TypeVar, diff --git a/poetry.lock b/poetry.lock index 494942cf76..f88e032f38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,22 +13,22 @@ files = [ [[package]] name = "attrs" -version = "23.2.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.43" +version = "1.35.47" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.43-py3-none-any.whl", hash = "sha256:e6a50a0599f75b21de0de1a551a0564793d25b304fa623e4052e527b268de734"}, - {file = "boto3-1.35.43.tar.gz", hash = "sha256:0197f460632804577aa78b2f6daf7b823bffa9d4d67a5cebb179efff0fe9631b"}, + {file = "boto3-1.35.47-py3-none-any.whl", hash = "sha256:0b307f685875e9c7857ce21c0d3050d8d4f3778455a6852d5f98ac75194b400e"}, + {file = "boto3-1.35.47.tar.gz", hash = "sha256:65b808e4cf1af8c2f405382d53656a0d92eee8f85c7388c43d64c7a5571b065f"}, ] [package.dependencies] -botocore = ">=1.35.43,<1.36.0" +botocore = ">=1.35.47,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.43" +version = "1.35.47" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.43-py3-none-any.whl", hash = "sha256:7cfdee9117617da97daaf259dd8484bcdc259c59eb7d1ce7db9ecf8506b7d36c"}, - {file = "botocore-1.35.43.tar.gz", hash = "sha256:04539b85ade060601a3023cacb538fc17aad8c059a5a2e18fe4bc5d0d91fbd72"}, + {file = "botocore-1.35.47-py3-none-any.whl", hash = "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a"}, + {file = "botocore-1.35.47.tar.gz", hash = "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547"}, ] [package.dependencies] @@ -302,74 +302,89 @@ files = [ [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "cffi" -version = "1.16.0" +version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] @@ -377,101 +392,116 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -518,63 +548,73 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.6.0" +version = "7.6.4" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"}, - {file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"}, - {file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"}, - {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"}, - {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"}, - {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"}, - {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"}, - {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"}, - {file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"}, - {file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"}, - {file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"}, - {file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"}, - {file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"}, - {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"}, - {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"}, - {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"}, - {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"}, - {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"}, - {file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"}, - {file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"}, - {file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"}, - {file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"}, - {file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"}, - {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"}, - {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"}, - {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"}, - {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"}, - {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"}, - {file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"}, - {file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"}, - {file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"}, - {file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"}, - {file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"}, - {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"}, - {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"}, - {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"}, - {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"}, - {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"}, - {file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"}, - {file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"}, - {file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"}, - {file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"}, - {file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"}, - {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"}, - {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"}, - {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"}, - {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"}, - {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"}, - {file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"}, - {file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"}, - {file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"}, - {file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, + {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, + {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, + {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, + {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, + {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, + {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, + {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, + {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, + {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, + {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, + {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, + {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, + {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, + {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, + {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, + {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, + {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, + {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, + {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, + {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, + {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, + {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, + {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, + {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, + {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, + {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, + {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, + {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, + {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, + {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, + {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, + {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, ] [package.extras] @@ -582,150 +622,140 @@ toml = ["tomli"] [[package]] name = "cramjam" -version = "2.8.3" +version = "2.9.0" description = "Thin Python bindings to de/compression algorithms in Rust" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "cramjam-2.8.3-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8c8aa6d08c135ae7f0da01e6559a332c5d8fe4989a594db401040e385d04dffd"}, - {file = "cramjam-2.8.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bd8c601fe8717e52517a2f2eef78217086acf449627bfdda97e3f53fd79c92af"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dac42b2b4c3950e7eda9b5551e0e904784ed0c0428accc29171c230fb919ec72"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab8146faa5d8c52edf23724843c36469fc32ff2c4a174eba72f4da6de5016688"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb5f4d061e9abdc6663551446c332a58c101efb31fd1746229872600274c2b20"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d1ac94e00c64258330105473c641441db02b4dc3e9e9f2963d204e53ed93025"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ed658f36a2bf667d5b8c7c6690103ad99f81cc62a1b64891b69298447329d4b"}, - {file = "cramjam-2.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f6303c8cc583dfe5054cf84717674f75b18bca4ae8e576dc863958d5494dc4b"}, - {file = "cramjam-2.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04b31d427a8902e5c2eec4b8f29873de7a3ade202e3d68e7f2354b9f0aa00bc7"}, - {file = "cramjam-2.8.3-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:9728861bc0390681824961778b36f7f0b95039e8b90d46f1b67f51232f1ee159"}, - {file = "cramjam-2.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:87e26e3e1d5fed1cac5b41be648d0daf0793f94cf4a7aebefce1f4f6656e2d21"}, - {file = "cramjam-2.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c1d2d39c2193a77c5e5b327944f90e6ecf2caa1b55e7176cc83d80706ea15de"}, - {file = "cramjam-2.8.3-cp310-none-win32.whl", hash = "sha256:6721edd8f911ad84db83ee4902b7579fc01c55849062f3f1f4171b58fccf98eb"}, - {file = "cramjam-2.8.3-cp310-none-win_amd64.whl", hash = "sha256:4f7c16d358df366e308137411125a2bb50d1b19924fced3a390898fa8c9a074d"}, - {file = "cramjam-2.8.3-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:24c2b426dd8fafb894f93a88f42e2827e14199d66836cb100582037e5371c724"}, - {file = "cramjam-2.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:007aa9444cb27b8691baae73ca907133cd939987438f874774011b4c740732dd"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:29987b54e31efed66738e8f236c597c4c9a91ec9d57bcb74307712e07505b4bb"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65bfd41aa92c0025f32ba09214b48e9367a81122586b2617439b4327c4bd179c"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7337bd8218bd8508f35904274a38cce843a237fe6e23104238bbeb2f337107ed"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:269f94d2efe6b6a97624782cd3b541e60535dd5874f4a8d5d0ba66ef59424ae3"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bec9ca5431c32ba94996b7c1c56695b37d48713b97ee1d2a456f4046f009e82f"}, - {file = "cramjam-2.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cb64a97e625ca029b55e37769b8c354e64cbea042c75471915dc385935d30ed"}, - {file = "cramjam-2.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c28830ecf76501356d678dac4f37563554ec1c651a53a990cdf595f7ed75c651"}, - {file = "cramjam-2.8.3-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35647a0e37a4dfec85a44c7966ae476b7db0e6cd65d91c08f1fb3007ed774d92"}, - {file = "cramjam-2.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e954599c6369f429a868852eff453b894d88866acba439b65131ea93f5400b47"}, - {file = "cramjam-2.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:86e238b6de79e045f5197df2c9dfaf8d10b37a6517ff4ffc4775fe5a3cf4d4a4"}, - {file = "cramjam-2.8.3-cp311-none-win32.whl", hash = "sha256:fe6434d3ee0899bc9396801d1abbc5d1fe77662bd3d1f1c1573fac6708459138"}, - {file = "cramjam-2.8.3-cp311-none-win_amd64.whl", hash = "sha256:e8ec1d4f27eb9d0412f0c567e7ffd14fbeb2b318a1ac394d5de4047c431fe94c"}, - {file = "cramjam-2.8.3-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:24990be4010b2185dcecc67133cd727657036e7b132d7de598148f5b1eb8e452"}, - {file = "cramjam-2.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:572cb9a8dc5a189691d6e03a9bf9b4305fd9a9f36bb0f9fde55fc36837c2e6b3"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9efe6915aa7ef176f3a7f42a4e46504573215953331b139abefd20d07d8aba82"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe84440100e7045190da7f80219be9989b0b6db6acadb3ae9cfe0935d93ebf8c"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00524bb23f4abb3a3bfff08aa32b9274843170c5b43855807e0f59670e2ac98c"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab67f29094165f0771acad8dd16e840259cfedcc94067af229530496dbf1a24c"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be6fb5dd5bf1c89c717a73a1057505959f35c08e0e97a76d4cc6391b90d2263b"}, - {file = "cramjam-2.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93b42d22bf3e17290c5e4cf58e715a419330bb5255c35933c14db82ecf3872c"}, - {file = "cramjam-2.8.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:afa065bab70e27565695441f69f493af3d379b8723030f2c3d2547d2e312a4be"}, - {file = "cramjam-2.8.3-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:832224f52fa1e601e0ab678dba9bdfde3686fc4cd1a9f2ed4748f29eaf1cb553"}, - {file = "cramjam-2.8.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:962b7106287bcc463150766b5b8c69f32dcc69713a8dbce00e0ca6936f95c55b"}, - {file = "cramjam-2.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2be92c6f0bcffaf8ea6a8164fe0388a188fec2fa9eff1828e8b64dc3a83740f9"}, - {file = "cramjam-2.8.3-cp312-none-win32.whl", hash = "sha256:080f3eb7b648f5ba9d35084d8dddc68246a8f365df239792f6712908f0aa568e"}, - {file = "cramjam-2.8.3-cp312-none-win_amd64.whl", hash = "sha256:c14728e3360cd212d5b606ca703c3bd1c8912efcdbc1aa032c81c2882509ebd5"}, - {file = "cramjam-2.8.3-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:c7e8329cde48740df8d332dade2f52b74612b8ea86005341c99bb192c82a5ce7"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77346ac669f5445d14b74476a4e8f3a259fd22681bd73790e92b8956d7e225fc"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274878883e7fadf95a6b5bc58f9c1dd39fef2c31d68e18a0fb8594226457fba7"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7871e1fd3ee8ca16799ba22d49fc1e52e78976fa8c659be41630eeb2914475a7"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:345a952c5d4b922830efaa67dc0b42d21e18c182c1a1bda6d20bb78235f31d6f"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb5d7739e2bc573ade12327ef7717b1ac5876c62938fab20eb54d762da23cae2"}, - {file = "cramjam-2.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440a18fd4ae42e06dbbd7aee91d8248b61da9fef7610ffbd553d1ba93931394b"}, - {file = "cramjam-2.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:476890974229713fc7b4c16fb050b756ba926c67e4d1200b3e03c5c051e9b552"}, - {file = "cramjam-2.8.3-cp37-cp37m-musllinux_1_1_armv7l.whl", hash = "sha256:771b44e549f90b5532508782e25d1c40b8054dd83d52253d05945fc05836b252"}, - {file = "cramjam-2.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d824fd98364bc946c38ed324a3ec7befba055285aaf2c1ca61894bb7616226e8"}, - {file = "cramjam-2.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2476828dea4089aa3cb9160391f8b36f793ca651afdcba80de1e341373928397"}, - {file = "cramjam-2.8.3-cp37-none-win32.whl", hash = "sha256:4a554bcfd068e831affd64a4f067c7c9b00b359742597c4fdadd18ff673baf30"}, - {file = "cramjam-2.8.3-cp37-none-win_amd64.whl", hash = "sha256:246f1f7d32cac2b64617d2dddba11a82851e73cdcf9d1abb799b08dcd9d2ea49"}, - {file = "cramjam-2.8.3-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bc8f24c32124bb47536882c6b941cdb88cc16e4fa64d5bf347cb8dd72a193fc3"}, - {file = "cramjam-2.8.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:28c30078effc100739d3f9b227276a8360c1b32aac65efb4f641630552213548"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef0173fb457f73cf9c2553092419db0eba4d582890db95e542a4d93e11340421"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a1943f2cc0deee037ddcf92beff6049e12d4e6d557f568ddf59fb3b848f2152"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5023a737d8d9cf5d123e6d87d088929c3cfb2aae90e0f584204427f74882150a"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eec7e985f35708c234542721863d82781d0f7f6a71b45e14ce6d2625d4b131d"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b188e750b95172c01defcfcfbba629cad797718b34402ec61b3bc9ff99403599"}, - {file = "cramjam-2.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e2d745cd4d244b7973d15aaebeedb537b980f9d3da80e6dea75ee1a872f9fa"}, - {file = "cramjam-2.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c9d54a4aa475d5e902f2ee518bdaa02f26c089e9f72950d00d1643c090f0deb3"}, - {file = "cramjam-2.8.3-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:19b8c97350c8d65daea26267dd1becb59073569aac2ae5743952d7f48da5d37a"}, - {file = "cramjam-2.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3277fd42399755d6d3730edec4a192174ee64d219e0ffbc90613f15cbabf711f"}, - {file = "cramjam-2.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1fd25201f1278dc6faa2ae35e67b7a5bb352b7fc6ed1ee939637414ca8115863"}, - {file = "cramjam-2.8.3-cp38-none-win32.whl", hash = "sha256:594477faff7f4380fa123cfbcf10ab8ee5af1a28b95750b66931ffafcb11ab5c"}, - {file = "cramjam-2.8.3-cp38-none-win_amd64.whl", hash = "sha256:8ea1dc11538842ff20d9872a17214994f5913cbf3be5594b54aad2422becdf19"}, - {file = "cramjam-2.8.3-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6379b92912f7569e126bd48d10e7087ddd20ea88a939532e3c4a85c2fa05d600"}, - {file = "cramjam-2.8.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:11d2e9eebc7d202eda0ae09fb56a2cdbeb5a1563e89d2118bf18cf0030f35f77"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d5a0a2fe240c97587df07f3d5e1027673d599b3a6a7a0ab540aea69f09e9ff7a"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba542f07fe3f41475d78626973533539e6cf2d5b6af37923fe6c7e7f0f74b9b2"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1374fe9a4431e546bb4501a16b84875d0bf80fc4e6c8942f0d5608ae48474267"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dcf7791e1cedb982ccc873ec9392c6cfb9c714a64ebf1ed4e8310b9cb44655f2"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:990e65c2bf1c155a9ddec5ecabf431cf77596432f697d3c6e0831b5174c51c40"}, - {file = "cramjam-2.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9b244d04cef82872d12c227a2f202f080a454d664c05db351626e6ad4aaa307"}, - {file = "cramjam-2.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:80b088d15866b37851fd53e2b471becc9ec487257dceca1878621072a18e833e"}, - {file = "cramjam-2.8.3-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f667843e7a8fca208eecfe44e04088242f8ca60d74d4950fac3722043538d700"}, - {file = "cramjam-2.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6f838d06d06709b9ce8b1ceae36aea4e1c7e613365185a91edcbeb5884f5e606"}, - {file = "cramjam-2.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4822eb5fe6839cd3d0439e5431e766ad010b2a388ca9617aa6372b6030897782"}, - {file = "cramjam-2.8.3-cp39-none-win32.whl", hash = "sha256:67e09b42e744efd08b93ac56f6100a859a31617d7146725516f3f2c744149d97"}, - {file = "cramjam-2.8.3-cp39-none-win_amd64.whl", hash = "sha256:11c9d30bc53892c57a3b296756c23659323ab1419a2b4bf22bbafc07b247bb67"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:51e847dcfe74fba379fed2bc2b45f5c2f11c3ece5e9eebcf63f39a9594184588"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07af94191f6a245226dc8a8bc6c94808e382ce9dfcca4bab0e8015fbc7fc3322"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9c45469914099897c47bfc501616fb377f28a865adebf90ea6f3c8ae6dd4e6"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ef29fb916fe74be65d0ab8871ab8d964b0f5eb8028bb84b325be43675a59d6e7"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3850dac9a2f6dcb3249d23f9d505117643b967bdc1c572ed0cc492a48fd69daf"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:e23e323ad28ed3e4e3a24ceffdab0ff235954109a88b536ea7b3b7886bd0a536"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1ba1a8ff855b30b4069a9b45ea9e7f2b5d882c7953bdfccda8d4b275fa7057ce"}, - {file = "cramjam-2.8.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eea606b01b43b91626e3aafd463bd19b6ed739bdb8b2b309e5d7ff72afc0e89d"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:97c706c520c3f8b0184278cc86187528458350216c6e4fa85d3f16bcad0d365d"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d08f1bab949ffd6dd6f25a89e4f7062d147aeea9c067e4dd155bdb190e5a519"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba1e45074757ab0482ac544e60613b6b8658100ac9985c91868a4598cdfb63ba"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a2fededed05a042f093dbf1b11d69afb1874a2c9197fcf1d58c142ba9111db5a"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:fc0c6eb8185c68f79a25bb298825e345cc09b826f5828bd8146e3600ca6e9981"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-musllinux_1_1_i686.whl", hash = "sha256:6653c262ad71e6c0ae08eeca3af2ee89ad47483b6312f2c6094518cb77872406"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6c04f363cb4b316719421724521432b6e7f6490e5baaaf7692af961c28d0279b"}, - {file = "cramjam-2.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e30f1f00de913b440baa36647817b9b7120a69b04eca05f3354aaf5b40f95ee5"}, - {file = "cramjam-2.8.3.tar.gz", hash = "sha256:6b1fa0a6ea8183831d04572597c182bd6cece62d583a36cde1e6a86e72ce2389"}, -] - -[package.extras] -dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-xdist"] + {file = "cramjam-2.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eb16d995e454b0155b166f6e6da7df4ac812d44e0f3b6dc0f344a934609fd5bc"}, + {file = "cramjam-2.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb1e86bfea656b51f2e75f2cedb17fc08b552d105b814d19b595294ecbe94d8d"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4bd76b654275736fd4f55521981b73751c34dacf70a1dbce96e454a39d43201f"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21569f19d5848606b85ac0dde0dc3639319d26fed8522c7103515df875bcb300"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f8b1117b4e697d39950ecab01700ce0aef66541e4478eb4d7b3ade8703347b"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3464d0042a03e8ef38a2b774ef23163cf3c0cdc41b8dfbf7c4aadf93e40b459"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0711c776750e243ae347d6609c975f0ff4be9ae65b2764d29e4bbdad8e574c3a"}, + {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00d96f798bc980b29f8e1c3ed7d554050e05d4cde23d1633ffed4cd63110024a"}, + {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc49b6575e3cb15da3180c5a3926ec81db33b109e48530708da76614b306904b"}, + {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:c4fa6c23e56d48df18f534af921ec936c812743a8972ecdd5e5ff47b464fea00"}, + {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b4b8d8160685c11ffb4e8e6daaab79cb351a1c54ceec41cc18a0a62c89309fe0"}, + {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0ed6362cb6c964f8d0c6e7f790e8961b9242cd3acd87c56169ca14d642653707"}, + {file = "cramjam-2.9.0-cp310-none-win32.whl", hash = "sha256:fe9af350dfbdc7ed4c93a8016a8ad7b5492fc116e7197cad7cbce99b434d3fe1"}, + {file = "cramjam-2.9.0-cp310-none-win_amd64.whl", hash = "sha256:37054c73704a3183b60869e7fec1614648752c31d89f44de1ffe1f01ad4d20d5"}, + {file = "cramjam-2.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:170a50407f9400073621cc1d5f3200ca3ad9de3000831e3e86f5561ca8048a08"}, + {file = "cramjam-2.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:912c94781c8ff318a4d3f3306f8d94d41ae5aa7b9760c4bb0476b01142084845"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df089639983a03070be6eabc60317aa1ffbf2c5409023b57a5fc2e4975163bc4"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ca28a8f6ab5fca35f163fd7d7a970880ce4fc1a0bead1249ecdaa96ec9ac1f4"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abd8bf9a94e3866215ac181a7dbcfa1ddbedca4f8048494a79934febe88537df"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7de19a382bcab93cd4d028d51f6f581920a3b79659a384775188135b7fc64f15"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4156fcefa1dfaa65d35ff82c252d1e32be12820f26d04748be6cd3b461cf85f"}, + {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4a3104022129d7463100dfaf12efd398ebfa4b7e4e50832ccc596754f7c26df"}, + {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ebee5f5d7e2b9277895ea4fd94646b72075fe9cfc0e8f4770b65c9e72b1fec1"}, + {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:8e33ebe4d709b21bc15e7ddf485ac6b30d7fdc7ed7c3c65130654c007f50c183"}, + {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d5a39118008bb9f2fba36a0ceea6c41fbd0b55d2647b043ba51a868e5f6de92"}, + {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f6ef35eba883927af2678b561cc4407e0b3b0d58a251c863bec4b3d8258cc2f"}, + {file = "cramjam-2.9.0-cp311-none-win32.whl", hash = "sha256:b21e55b5cfdaff96eae1f323ae9a0d36e86852cdf62fe23b60a2481d2fed5571"}, + {file = "cramjam-2.9.0-cp311-none-win_amd64.whl", hash = "sha256:9f685fe4e49b2f3e233548e3397b3f9189d71a265718ec631d13eca3d5718ddb"}, + {file = "cramjam-2.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:34578e4c1518b10dad5e0ba40c721e529ef13e7742a528843b40e1f20dd6078c"}, + {file = "cramjam-2.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d5b5512dc61ea78f32e021e88a5fd5b46a821409479e6657d33614fc9e45677"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b4f1b5e33915ed591c0c19b8c3bbdd7aa0f6a9bfe2b7246b475d497bda15f18"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad301801afa0eecdacabf353a2802df5e6770f9bfb0a559d6c069813d83cfd42"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:399baf80fea574e3870f233e12e6a12f02c53b054e13d792348b272b0614370a"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3121e2fbec58907fa70636adaeaf30c27614c867e08a7a5bd2887b33786ff790"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd04205b2a87087ffc2257c3ad33f11daabc053956f64ac1ec7bae299cac3f2f"}, + {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb9c4db36188a8f08c2303100a83100f26a8572803ae35eadff359bebd3d204"}, + {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ef553d4080368006817c1a935ed619c71987cf10417a32386acc00c5418a2934"}, + {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:9862ca8ead80857ecfb9b07f02f577733261e981346f31585fe118975eabb738"}, + {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4714e1ea0c3329368b83fe5ad6e831d5ca11fb794ca7cf491622eb6b2d420d2f"}, + {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4ca30c9f27e3b88bc082d4637e7648f93da5cb69a2dbe0c0300bc51353c820"}, + {file = "cramjam-2.9.0-cp312-none-win32.whl", hash = "sha256:0ed2fef010d1caca9ea63814e9cb5b1d47d907b80302b8cc0b3a1e116ea241e2"}, + {file = "cramjam-2.9.0-cp312-none-win_amd64.whl", hash = "sha256:bd26d71939de5dcf169d479fbc7fcfed21e6675bab33e7f7e9f8405f19711c71"}, + {file = "cramjam-2.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:dd70ea5d7b2c5e479e04ac3a00d8bc3deca146d2b5dbfbe3d7b42ed136e19de4"}, + {file = "cramjam-2.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1410e68c464666473a89cade17483b94bb4639d9161c440ee54ee1e0eca583"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b0078727fe8c28ef1695e5d04aae5c41ac697eb087cba387c6a02b825f9071c0"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a63c4e63319bf7dfc3ab46c06afb76d3d9cc1c94369b609dde480e5cc78e4de"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47d7253b5a10c201cc65aecfb517dfa1c0b5831b2524ac32dd2964fceafc0dc4"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05970fb640f236767003e62c256a085754536169bac863f4a3502ecb59cbf197"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0b062d261fa3fac00146cf801896c8cfafe1e41332eb047aa0a36558299daa6"}, + {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017b7066f18b7b676068f51b1dbdecc02d76d9af10092252b22dcbd03a78ed33"}, + {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9de33ef3bc006c11fbad1dc8b15341dcc78430df2c5ce1e790dfb729b11ab593"}, + {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:b99efaf81be8e381de1cde6574e2c89030ed53994e73b0e75b62d6e232f491c5"}, + {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:36426e3f1920f6aa4c644d007bf9cfad06dd9f1a30cd0a921d72b010492d8447"}, + {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea9bcaff298f5d35ef67346d474fca388c5cf6d4edab1d06b84868800f88bd36"}, + {file = "cramjam-2.9.0-cp313-none-win32.whl", hash = "sha256:c48da60a5eb481b412e5e462b81ad307fb2203178a2840a743f0a7c5fc1718c9"}, + {file = "cramjam-2.9.0-cp313-none-win_amd64.whl", hash = "sha256:97a6311bd32f301ff1b922bc9de62ace3d9fd845e20efc0f71b4d0239a45b8d2"}, + {file = "cramjam-2.9.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:78e7349f945a83bc48855fb042873092a69b155a088b8c11942eb76418b32705"}, + {file = "cramjam-2.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:65a097ea765dd4ef2fb868b5b0959d7c93a64c250b2c52f462898c823ae4b950"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:35cad507eb02c775e6c5444312f98b28dd8bf122425677ae199484996e838673"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8982925d179b940efa860513a31b839bb06343501077cca3e67f7a2f7360d355"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba7e2d33e1d092dffd0a3ff4bd1b86177594aa3c2901fd478e78e1fb2aee8ed3"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:904be92e3bc25e78343ee52aa0fd5fba3a31d11d474e8af4623a9d00baa84bc2"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9221297c547d702e1431e96705fce26c6a87df34a681a6b97fe63b536d09c1d8"}, + {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98a18c22a85f321091cc8db6694af1d713a369c2d60ec611c10ccfe24ab103a"}, + {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e248510f8e2dbc71fa99f86238c9023365dbe1a4520eb40e33d73416527349f2"}, + {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:dc07376aa33b6004ea372ac9b0ba0ed3455aa2fc4e18727414142ecb46b176b8"}, + {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e94021c541eb2a199b5a2ffae0ea84fb8b99863dab99a5b154b00bc7a44b5c48"}, + {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4adbf4366f8dc29b7c5c731c800cf633be76c9911e928daeb606827d6ae7c599"}, + {file = "cramjam-2.9.0-cp38-none-win32.whl", hash = "sha256:ca880f555c8db40942acc8a50722c33e229b6be90e598acc1a201f36487b917d"}, + {file = "cramjam-2.9.0-cp38-none-win_amd64.whl", hash = "sha256:ab17a429a92db90bf40115efb97d10e71b94b0dcacf30cf724552df2794a58fb"}, + {file = "cramjam-2.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ed7fd7bc2b86ec3161fe0cc49f5f392e6efa55c91a95397d5047820c38117660"}, + {file = "cramjam-2.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a0f654c739a6bc4a69a2aaf31463328a208757ed780ff886234532f78e06a864"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cd4d4ab9deb5846af0ac6cf1fa139cfa40291ad14d073efa8b8e20c8d1aa90bd"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bafc32f01d4ab64f83fdbc29bc5bd25a920b59c751c12e06e6f4b1e379be7600"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0fb5ea631dbf998f667766a9e485e757817d66ed559916ba553a0ec2f902d788"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c902e56e60c48f5f15e55257aaa1c2678323df5f18a1b839e8d05cac1107576c"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:441d3875cdffe5df9294b93ef570058837732dd727cd9d18efa0f089f1c2687a"}, + {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed486e57a79ccc7aebaa2ec12517d891fdc5d2fde16915e3db705b8a47570981"}, + {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:013cb872205641c6e5269f530ed40aaaa5640d84e0d8f33b89f5a1bf7f655527"}, + {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:a41b4b10a381be1d42a1a7dd07b8c3faccd3d12c7e98e973a6ec558fd040a607"}, + {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598eac1713ddbe69c3b30dcc890d69b206ce08903fc3aed58149aae87c61973a"}, + {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72e9ebc27c557706a3c9964c1d1b4522857760dbd60c105a4f5421f3b66e31a2"}, + {file = "cramjam-2.9.0-cp39-none-win32.whl", hash = "sha256:dbbd6fba677e1cbc9d6bd4ebbe3e8b3667d0295f1731489db2a971c95f0ceca0"}, + {file = "cramjam-2.9.0-cp39-none-win_amd64.whl", hash = "sha256:7f33a83969fa94ee8e0c1f0aef8eb303ead3e9142338dc543abeb7e1a28734ab"}, + {file = "cramjam-2.9.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:132db7d3346ea21ba44e7ee23ec73bd6fa9eb1e77133ca6dfe1f7449a69999af"}, + {file = "cramjam-2.9.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2addf801c88bead21256ccd87dc97cffead03758c4a4947fad8e454f4abfda0a"}, + {file = "cramjam-2.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24afad3ba62774abbb150dc25aab21b047ab999c4143c7a8d96577848baf7af6"}, + {file = "cramjam-2.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:604c16052cf29d0c796927ed7e107f65429d2036c82c9a8009bd453c94e5e4f0"}, + {file = "cramjam-2.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65bded20fd2cef17b22246c336ddd67fac842341ee311042b4a70e65dc745aa7"}, + {file = "cramjam-2.9.0.tar.gz", hash = "sha256:f103e648aa3ebe9b8e2c1a3a92719288d8f3f41007c319ad298cdce2d0c28641"}, +] + +[package.extras] +dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-benchmark", "pytest-xdist"] [[package]] name = "cryptography" -version = "43.0.1" +version = "43.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, - {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, - {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, - {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, - {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, - {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, - {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, - {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, - {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, - {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, - {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, - {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, - {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, ] [package.dependencies] @@ -738,7 +768,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -775,13 +805,13 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "dill" -version = "0.3.8" +version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, + {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, + {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, ] [package.extras] @@ -814,21 +844,21 @@ tqdm = ">=4.62.2" [[package]] name = "dnspython" -version = "2.6.1" +version = "2.7.0" description = "DNS toolkit" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, + {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, + {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=43)"] doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] +doq = ["aioquic (>=1.0.0)"] +idna = ["idna (>=3.7)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] @@ -1080,13 +1110,13 @@ files = [ [[package]] name = "flask-wtf" -version = "1.2.1" +version = "1.2.2" description = "Form rendering, validation, and CSRF protection for Flask with WTForms." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "flask_wtf-1.2.1-py3-none-any.whl", hash = "sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287"}, - {file = "flask_wtf-1.2.1.tar.gz", hash = "sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695"}, + {file = "flask_wtf-1.2.2-py3-none-any.whl", hash = "sha256:e93160c5c5b6b571cf99300b6e01b72f9a101027cab1579901f8b10c5daf0b70"}, + {file = "flask_wtf-1.2.2.tar.gz", hash = "sha256:79d2ee1e436cf570bccb7d916533fa18757a2f18c290accffab1b9a0b684666b"}, ] [package.dependencies] @@ -1113,57 +1143,54 @@ python-dateutil = ">=2.7" [[package]] name = "gevent" -version = "24.2.1" +version = "24.10.3" description = "Coroutine-based network library" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789"}, - {file = "gevent-24.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d5325ccfadfd3dcf72ff88a92fb8fc0b56cacc7225f0f4b6dcf186c1a6eeabc"}, - {file = "gevent-24.2.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d"}, - {file = "gevent-24.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1dffb395e500613e0452b9503153f8f7ba587c67dd4a85fc7cd7aa7430cb02cc"}, - {file = "gevent-24.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:6c47ae7d1174617b3509f5d884935e788f325eb8f1a7efc95d295c68d83cce40"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7cac622e11b4253ac4536a654fe221249065d9a69feb6cdcd4d9af3503602e0"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf5b9c72b884c6f0c4ed26ef204ee1f768b9437330422492c319470954bc4cc7"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5de3c676e57177b38857f6e3cdfbe8f38d1cd754b63200c0615eaa31f514b4f"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4faf846ed132fd7ebfbbf4fde588a62d21faa0faa06e6f468b7faa6f436b661"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:368a277bd9278ddb0fde308e6a43f544222d76ed0c4166e0d9f6b036586819d9"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f8a04cf0c5b7139bc6368b461257d4a757ea2fe89b3773e494d235b7dd51119f"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9d8d0642c63d453179058abc4143e30718b19a85cbf58c2744c9a63f06a1d388"}, - {file = "gevent-24.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:94138682e68ec197db42ad7442d3cf9b328069c3ad8e4e5022e6b5cd3e7ffae5"}, - {file = "gevent-24.2.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19"}, - {file = "gevent-24.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800"}, - {file = "gevent-24.2.1-cp38-cp38-win32.whl", hash = "sha256:44098038d5e2749b0784aabb27f1fcbb3f43edebedf64d0af0d26955611be8d6"}, - {file = "gevent-24.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:117e5837bc74a1673605fb53f8bfe22feb6e5afa411f524c835b2ddf768db0de"}, - {file = "gevent-24.2.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7"}, - {file = "gevent-24.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1"}, - {file = "gevent-24.2.1-cp39-cp39-win32.whl", hash = "sha256:782a771424fe74bc7e75c228a1da671578c2ba4ddb2ca09b8f959abdf787331e"}, - {file = "gevent-24.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:3adfb96637f44010be8abd1b5e73b5070f851b817a0b182e601202f20fa06533"}, - {file = "gevent-24.2.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8"}, - {file = "gevent-24.2.1.tar.gz", hash = "sha256:432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056"}, -] - -[package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""} + {file = "gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9"}, + {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18"}, + {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13"}, + {file = "gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba"}, + {file = "gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db"}, + {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7"}, + {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26"}, + {file = "gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290"}, + {file = "gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c"}, + {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e"}, + {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4"}, + {file = "gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99"}, + {file = "gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3"}, + {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861"}, + {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec"}, + {file = "gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015"}, + {file = "gevent-24.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70e9ed7ecb70e0df7dc97c3bc420de9a45a7c76bd5861c6cfec8c549700e681e"}, + {file = "gevent-24.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ac83b74304487afa211a01909c7dd257e574db0cd429d866c298e21df7aeedf"}, + {file = "gevent-24.10.3-cp39-cp39-win32.whl", hash = "sha256:a9a89d6e396ef6f1e3968521bf56e8c4bee25b193bbf5d428b7782d582410822"}, + {file = "gevent-24.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:40ea3e40e8bb4fdb143c2a8edf2ccfdebd56016c7317c341ce8094c7bee08818"}, + {file = "gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18"}, + {file = "gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1"}, +] + +[package.dependencies] +cffi = {version = ">=1.17.1", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} +greenlet = {version = ">=3.1.1", markers = "platform_python_implementation == \"CPython\""} "zope.event" = "*" "zope.interface" = "*" @@ -1171,18 +1198,18 @@ greenlet = {version = ">=3.0rc3", markers = "platform_python_implementation == \ dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "google-api-core" -version = "2.19.1" +version = "2.21.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, - {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, + {file = "google_api_core-2.21.0-py3-none-any.whl", hash = "sha256:6869eacb2a37720380ba5898312af79a4d30b8bca1548fb4093e0697dc4bdf5d"}, + {file = "google_api_core-2.21.0.tar.gz", hash = "sha256:4a152fd11a9f774ea606388d423b68aa7e6d6a0ffe4c8266f74979613ec09f81"}, ] [package.dependencies] @@ -1195,19 +1222,20 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 requests = ">=2.18.0,<3.0.0.dev0" [package.extras] +async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"] grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.32.0" +version = "2.35.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"}, - {file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"}, + {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, + {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, ] [package.dependencies] @@ -1217,7 +1245,7 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +enterprise-cert = ["cryptography", "pyopenssl"] pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] @@ -1329,79 +1357,38 @@ protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4 [[package]] name = "google-crc32c" -version = "1.5.0" +version = "1.6.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, + {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa"}, + {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9"}, + {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7"}, + {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e"}, + {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc"}, + {file = "google_crc32c-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42"}, + {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4"}, + {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8"}, + {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d"}, + {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f"}, + {file = "google_crc32c-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3"}, + {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d"}, + {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b"}, + {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00"}, + {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3"}, + {file = "google_crc32c-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760"}, + {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205"}, + {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0"}, + {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2"}, + {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871"}, + {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57"}, + {file = "google_crc32c-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c"}, + {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc"}, + {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d"}, + {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24"}, + {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d"}, + {file = "google_crc32c-1.6.0.tar.gz", hash = "sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc"}, ] [package.extras] @@ -1427,13 +1414,13 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.63.2" +version = "1.65.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, - {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, + {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, + {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, ] [package.dependencies] @@ -1445,69 +1432,84 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] @@ -1599,19 +1601,19 @@ protobuf = ["grpcio-tools (>=1.67.0)"] [[package]] name = "grpcio-status" -version = "1.62.2" +version = "1.67.0" description = "Status proto mapping for gRPC" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, - {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, + {file = "grpcio_status-1.67.0-py3-none-any.whl", hash = "sha256:0e79e2e01ba41a6ca6ed9d7a825323c511fe1653a646f8014c7e3c8132527acc"}, + {file = "grpcio_status-1.67.0.tar.gz", hash = "sha256:c3e5a86fa007e9e263cd5f988a8a907484da4caab582874ea2a4a6092734046b"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.2" -protobuf = ">=4.21.6" +grpcio = ">=1.67.0" +protobuf = ">=5.26.1,<6.0dev" [[package]] name = "gunicorn" @@ -1710,15 +1712,18 @@ tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" version = "8.4.0" @@ -1861,13 +1866,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, ] [package.dependencies] @@ -1890,72 +1895,72 @@ typing-extensions = ">=4.5.0" [[package]] name = "markupsafe" -version = "3.0.1" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" files = [ - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, - {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -2006,13 +2011,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.17" +version = "5.0.18" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.17-py2.py3-none-any.whl", hash = "sha256:6b56b7bb5675d60e79890c9867e446f29d7a5be02051238b8df079f649270130"}, - {file = "moto-5.0.17.tar.gz", hash = "sha256:165a291ac0b983f53a09f67f9841f72214c5a1b0c56392d88f7035a6a8718fca"}, + {file = "moto-5.0.18-py2.py3-none-any.whl", hash = "sha256:8e25401f7d7910e19a732b417e0d503ef86cf4de9114a273dd62679a42f3be1c"}, + {file = "moto-5.0.18.tar.gz", hash = "sha256:8a7ad2f53a2e6cc9db2ff65c0e0d4b5d7e78bc00b825c9e1ff6cc394371e76e9"}, ] [package.dependencies] @@ -2052,43 +2057,43 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.12.0" +version = "1.13.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4397081e620dc4dc18e2f124d5e1d2c288194c2c08df6bdb1db31c38cd1fe1ed"}, - {file = "mypy-1.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:684a9c508a283f324804fea3f0effeb7858eb03f85c4402a967d187f64562469"}, - {file = "mypy-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cabe4cda2fa5eca7ac94854c6c37039324baaa428ecbf4de4567279e9810f9e"}, - {file = "mypy-1.12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:060a07b10e999ac9e7fa249ce2bdcfa9183ca2b70756f3bce9df7a92f78a3c0a"}, - {file = "mypy-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:0eff042d7257f39ba4ca06641d110ca7d2ad98c9c1fb52200fe6b1c865d360ff"}, - {file = "mypy-1.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b86de37a0da945f6d48cf110d5206c5ed514b1ca2614d7ad652d4bf099c7de7"}, - {file = "mypy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20c7c5ce0c1be0b0aea628374e6cf68b420bcc772d85c3c974f675b88e3e6e57"}, - {file = "mypy-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a64ee25f05fc2d3d8474985c58042b6759100a475f8237da1f4faf7fcd7e6309"}, - {file = "mypy-1.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:faca7ab947c9f457a08dcb8d9a8664fd438080e002b0fa3e41b0535335edcf7f"}, - {file = "mypy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:5bc81701d52cc8767005fdd2a08c19980de9ec61a25dbd2a937dfb1338a826f9"}, - {file = "mypy-1.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8462655b6694feb1c99e433ea905d46c478041a8b8f0c33f1dab00ae881b2164"}, - {file = "mypy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:923ea66d282d8af9e0f9c21ffc6653643abb95b658c3a8a32dca1eff09c06475"}, - {file = "mypy-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ebf9e796521f99d61864ed89d1fb2926d9ab6a5fab421e457cd9c7e4dd65aa9"}, - {file = "mypy-1.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e478601cc3e3fa9d6734d255a59c7a2e5c2934da4378f3dd1e3411ea8a248642"}, - {file = "mypy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:c72861b7139a4f738344faa0e150834467521a3fba42dc98264e5aa9507dd601"}, - {file = "mypy-1.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52b9e1492e47e1790360a43755fa04101a7ac72287b1a53ce817f35899ba0521"}, - {file = "mypy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:48d3e37dd7d9403e38fa86c46191de72705166d40b8c9f91a3de77350daa0893"}, - {file = "mypy-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f106db5ccb60681b622ac768455743ee0e6a857724d648c9629a9bd2ac3f721"}, - {file = "mypy-1.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:233e11b3f73ee1f10efada2e6da0f555b2f3a5316e9d8a4a1224acc10e7181d3"}, - {file = "mypy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:4ae8959c21abcf9d73aa6c74a313c45c0b5a188752bf37dace564e29f06e9c1b"}, - {file = "mypy-1.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eafc1b7319b40ddabdc3db8d7d48e76cfc65bbeeafaa525a4e0fa6b76175467f"}, - {file = "mypy-1.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9b9ce1ad8daeb049c0b55fdb753d7414260bad8952645367e70ac91aec90e07e"}, - {file = "mypy-1.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfe012b50e1491d439172c43ccb50db66d23fab714d500b57ed52526a1020bb7"}, - {file = "mypy-1.12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c40658d4fa1ab27cb53d9e2f1066345596af2f8fe4827defc398a09c7c9519b"}, - {file = "mypy-1.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:dee78a8b9746c30c1e617ccb1307b351ded57f0de0d287ca6276378d770006c0"}, - {file = "mypy-1.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b5df6c8a8224f6b86746bda716bbe4dbe0ce89fd67b1fa4661e11bfe38e8ec8"}, - {file = "mypy-1.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5feee5c74eb9749e91b77f60b30771563327329e29218d95bedbe1257e2fe4b0"}, - {file = "mypy-1.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:77278e8c6ffe2abfba6db4125de55f1024de9a323be13d20e4f73b8ed3402bd1"}, - {file = "mypy-1.12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dcfb754dea911039ac12434d1950d69a2f05acd4d56f7935ed402be09fad145e"}, - {file = "mypy-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:06de0498798527451ffb60f68db0d368bd2bae2bbfb5237eae616d4330cc87aa"}, - {file = "mypy-1.12.0-py3-none-any.whl", hash = "sha256:fd313226af375d52e1e36c383f39bf3836e1f192801116b31b090dfcd3ec5266"}, - {file = "mypy-1.12.0.tar.gz", hash = "sha256:65a22d87e757ccd95cbbf6f7e181e6caa87128255eb2b6be901bb71b26d8a99d"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [package.dependencies] @@ -2097,6 +2102,7 @@ typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] @@ -2235,38 +2241,38 @@ twisted = ["twisted"] [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.47.0" +version = "1.48.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.8" files = [ - {file = "playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab"}, - {file = "playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341"}, - {file = "playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955"}, - {file = "playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249"}, - {file = "playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974"}, - {file = "playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280"}, + {file = "playwright-1.48.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:082bce2739f1078acc7d0734da8cc0e23eb91b7fae553f3316d733276f09a6b1"}, + {file = "playwright-1.48.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7da2eb51a19c7f3b523e9faa9d98e7af92e52eb983a099979ea79c9668e3cbf7"}, + {file = "playwright-1.48.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:115b988d1da322358b77bc3bf2d3cc90f8c881e691461538e7df91614c4833c9"}, + {file = "playwright-1.48.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8dabb80e62f667fe2640a8b694e26a7b884c0b4803f7514a3954fc849126227b"}, + {file = "playwright-1.48.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ff8303409ebed76bed4c3d655340320b768817d900ba208b394fdd7d7939a5c"}, + {file = "playwright-1.48.0-py3-none-win32.whl", hash = "sha256:85598c360c590076d4f435525be991246d74a905b654ac19d26eab7ed9b98b2d"}, + {file = "playwright-1.48.0-py3-none-win_amd64.whl", hash = "sha256:e0e87b0c4dc8fce83c725dd851aec37bc4e882bb225ec8a96bd83cf32d4f1623"}, ] [package.dependencies] -greenlet = "3.0.3" +greenlet = "3.1.1" pyee = "12.0.0" [[package]] @@ -2286,13 +2292,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "proto-plus" -version = "1.24.0" +version = "1.25.0" description = "Beautiful, Pythonic protocol buffers." optional = false python-versions = ">=3.7" files = [ - {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, - {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, + {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"}, + {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"}, ] [package.dependencies] @@ -2303,44 +2309,44 @@ testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "4.25.4" +version = "5.28.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24"}, + {file = "protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868"}, + {file = "protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687"}, + {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584"}, + {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135"}, + {file = "protobuf-5.28.3-cp38-cp38-win32.whl", hash = "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548"}, + {file = "protobuf-5.28.3-cp38-cp38-win_amd64.whl", hash = "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b"}, + {file = "protobuf-5.28.3-cp39-cp39-win32.whl", hash = "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535"}, + {file = "protobuf-5.28.3-cp39-cp39-win_amd64.whl", hash = "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36"}, + {file = "protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed"}, + {file = "protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b"}, ] [[package]] name = "pyasn1" -version = "0.6.0" +version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, - {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.0" +version = "0.4.1" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, - {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, + {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, + {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] [package.dependencies] @@ -2348,13 +2354,13 @@ pyasn1 = ">=0.4.6,<0.7.0" [[package]] name = "pycodestyle" -version = "2.12.0" +version = "2.12.1" description = "Python style guide checker" optional = false python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4"}, - {file = "pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c"}, + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] [[package]] @@ -2410,10 +2416,7 @@ files = [ [package.dependencies] astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] +dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" @@ -2454,15 +2457,18 @@ pylint = ">=0.28.0" [[package]] name = "pyreadline3" -version = "3.4.1" +version = "3.5.4" description = "A python implementation of GNU readline." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, - {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, + {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, + {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, ] +[package.extras] +dev = ["build", "flake8", "mypy", "pytest", "twine"] + [[package]] name = "pytest" version = "8.3.3" @@ -2606,13 +2612,13 @@ cramjam = "*" [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -2709,104 +2715,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2023.12.25" +version = "2024.9.11" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, - {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, - {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, - {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, - {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, ] [[package]] @@ -2851,114 +2858,114 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.19.1" +version = "0.20.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, - {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, - {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, - {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, - {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, - {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, - {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, - {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, - {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, - {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, - {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, - {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, - {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, - {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, - {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] [[package]] @@ -2977,13 +2984,13 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.10.2" +version = "0.10.3" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69"}, - {file = "s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6"}, + {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, + {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, ] [package.dependencies] @@ -3010,19 +3017,23 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "72.1.0" +version = "75.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] [[package]] name = "simplejson" @@ -3167,13 +3178,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] @@ -3195,13 +3206,13 @@ typing = ["mypy (>=1.4)", "rich", "twisted"] [[package]] name = "termcolor" -version = "2.4.0" +version = "2.5.0" description = "ANSI color formatting for output in terminal" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, - {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, + {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, + {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, ] [package.extras] @@ -3209,24 +3220,24 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "tomlkit" -version = "0.13.0" +version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" files = [ - {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, - {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, + {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, + {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] @@ -3342,13 +3353,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "71.1.0.20240726" +version = "75.2.0.20241019" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-71.1.0.20240726.tar.gz", hash = "sha256:85ba28e9461bb1be86ebba4db0f1c2408f2b11115b1966334ea9dc464e29303e"}, - {file = "types_setuptools-71.1.0.20240726-py3-none-any.whl", hash = "sha256:a7775376f36e0ff09bcad236bf265777590a66b11623e48c20bfc30f1444ea36"}, + {file = "types-setuptools-75.2.0.20241019.tar.gz", hash = "sha256:86ea31b5f6df2c6b8f2dc8ae3f72b213607f62549b6fa2ed5866e5299f968694"}, + {file = "types_setuptools-75.2.0.20241019-py3-none-any.whl", hash = "sha256:2e48ff3acd4919471e80d5e3f049cce5c177e108d5d36d2d4cee3fa4d4104258"}, ] [[package]] @@ -3386,13 +3397,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -3413,13 +3424,13 @@ files = [ [[package]] name = "werkzeug" -version = "3.0.3" +version = "3.0.4" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"}, - {file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"}, + {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, + {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, ] [package.dependencies] @@ -3526,13 +3537,13 @@ email = ["email-validator"] [[package]] name = "xmltodict" -version = "0.13.0" +version = "0.14.2" description = "Makes working with XML feel like you are working with JSON" optional = false -python-versions = ">=3.4" +python-versions = ">=3.6" files = [ - {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"}, - {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, + {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"}, + {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"}, ] [[package]] @@ -3574,56 +3585,57 @@ test = ["zope.testrunner"] [[package]] name = "zope-interface" -version = "6.4.post2" +version = "7.1.1" description = "Interfaces for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zope.interface-6.4.post2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2eccd5bef45883802848f821d940367c1d0ad588de71e5cabe3813175444202c"}, - {file = "zope.interface-6.4.post2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:762e616199f6319bb98e7f4f27d254c84c5fb1c25c908c2a9d0f92b92fb27530"}, - {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef8356f16b1a83609f7a992a6e33d792bb5eff2370712c9eaae0d02e1924341"}, - {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e4fa5d34d7973e6b0efa46fe4405090f3b406f64b6290facbb19dcbf642ad6b"}, - {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d22fce0b0f5715cdac082e35a9e735a1752dc8585f005d045abb1a7c20e197f9"}, - {file = "zope.interface-6.4.post2-cp310-cp310-win_amd64.whl", hash = "sha256:97e615eab34bd8477c3f34197a17ce08c648d38467489359cb9eb7394f1083f7"}, - {file = "zope.interface-6.4.post2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:599f3b07bde2627e163ce484d5497a54a0a8437779362395c6b25e68c6590ede"}, - {file = "zope.interface-6.4.post2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:136cacdde1a2c5e5bc3d0b2a1beed733f97e2dad8c2ad3c2e17116f6590a3827"}, - {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47937cf2e7ed4e0e37f7851c76edeb8543ec9b0eae149b36ecd26176ff1ca874"}, - {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0a6be264afb094975b5ef55c911379d6989caa87c4e558814ec4f5125cfa2e"}, - {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47654177e675bafdf4e4738ce58cdc5c6d6ee2157ac0a78a3fa460942b9d64a8"}, - {file = "zope.interface-6.4.post2-cp311-cp311-win_amd64.whl", hash = "sha256:e2fb8e8158306567a3a9a41670c1ff99d0567d7fc96fa93b7abf8b519a46b250"}, - {file = "zope.interface-6.4.post2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b912750b13d76af8aac45ddf4679535def304b2a48a07989ec736508d0bbfbde"}, - {file = "zope.interface-6.4.post2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ac46298e0143d91e4644a27a769d1388d5d89e82ee0cf37bf2b0b001b9712a4"}, - {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86a94af4a88110ed4bb8961f5ac72edf782958e665d5bfceaab6bf388420a78b"}, - {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73f9752cf3596771c7726f7eea5b9e634ad47c6d863043589a1c3bb31325c7eb"}, - {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b5c3e9744dcdc9e84c24ed6646d5cf0cf66551347b310b3ffd70f056535854"}, - {file = "zope.interface-6.4.post2-cp312-cp312-win_amd64.whl", hash = "sha256:551db2fe892fcbefb38f6f81ffa62de11090c8119fd4e66a60f3adff70751ec7"}, - {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96ac6b3169940a8cd57b4f2b8edcad8f5213b60efcd197d59fbe52f0accd66e"}, - {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cebff2fe5dc82cb22122e4e1225e00a4a506b1a16fafa911142ee124febf2c9e"}, - {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ee982237cffaf946db365c3a6ebaa37855d8e3ca5800f6f48890209c1cfefc"}, - {file = "zope.interface-6.4.post2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:fbf649bc77510ef2521cf797700b96167bb77838c40780da7ea3edd8b78044d1"}, - {file = "zope.interface-6.4.post2-cp37-cp37m-win_amd64.whl", hash = "sha256:4c0b208a5d6c81434bdfa0f06d9b667e5de15af84d8cae5723c3a33ba6611b82"}, - {file = "zope.interface-6.4.post2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3fe667935e9562407c2511570dca14604a654988a13d8725667e95161d92e9b"}, - {file = "zope.interface-6.4.post2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a96e6d4074db29b152222c34d7eec2e2db2f92638d2b2b2c704f9e8db3ae0edc"}, - {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:866a0f583be79f0def667a5d2c60b7b4cc68f0c0a470f227e1122691b443c934"}, - {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fe919027f29b12f7a2562ba0daf3e045cb388f844e022552a5674fcdf5d21f1"}, - {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0343a6e06d94f6b6ac52fbc75269b41dd3c57066541a6c76517f69fe67cb43"}, - {file = "zope.interface-6.4.post2-cp38-cp38-win_amd64.whl", hash = "sha256:dabb70a6e3d9c22df50e08dc55b14ca2a99da95a2d941954255ac76fd6982bc5"}, - {file = "zope.interface-6.4.post2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:706efc19f9679a1b425d6fa2b4bc770d976d0984335eaea0869bd32f627591d2"}, - {file = "zope.interface-6.4.post2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d136e5b8821073e1a09dde3eb076ea9988e7010c54ffe4d39701adf0c303438"}, - {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1730c93a38b5a18d24549bc81613223962a19d457cfda9bdc66e542f475a36f4"}, - {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc2676312cc3468a25aac001ec727168994ea3b69b48914944a44c6a0b251e79"}, - {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a62fd6cd518693568e23e02f41816adedfca637f26716837681c90b36af3671"}, - {file = "zope.interface-6.4.post2-cp39-cp39-win_amd64.whl", hash = "sha256:d3f7e001328bd6466b3414215f66dde3c7c13d8025a9c160a75d7b2687090d15"}, - {file = "zope.interface-6.4.post2.tar.gz", hash = "sha256:1c207e6f6dfd5749a26f5a5fd966602d6b824ec00d2df84a7e9a924e8933654e"}, + {file = "zope.interface-7.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6650bd56ef350d37c8baccfd3ee8a0483ed6f8666e641e4b9ae1a1827b79f9e5"}, + {file = "zope.interface-7.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84e87eba6b77a3af187bae82d8de1a7c208c2a04ec9f6bd444fd091b811ad92e"}, + {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c4e1b4c06d9abd1037c088dae1566c85f344a3e6ae4350744c3f7f7259d9c67"}, + {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cd5e3d910ac87652a09f6e5db8e41bc3b49cf08ddd2d73d30afc644801492cd"}, + {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca95594d936ee349620900be5b46c0122a1ff6ce42d7d5cb2cf09dc84071ef16"}, + {file = "zope.interface-7.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:ad339509dcfbbc99bf8e147db6686249c4032f26586699ec4c82f6e5909c9fe2"}, + {file = "zope.interface-7.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e59f175e868f856a77c0a77ba001385c377df2104fdbda6b9f99456a01e102a"}, + {file = "zope.interface-7.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0de23bcb93401994ea00bc5c677ef06d420340ac0a4e9c10d80e047b9ce5af3f"}, + {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdb7e7e5524b76d3ec037c1d81a9e2c7457b240fd4cb0a2476b65c3a5a6c81f"}, + {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3603ef82a9920bd0bfb505423cb7e937498ad971ad5a6141841e8f76d2fd5446"}, + {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d52d052355e0c5c89e0630dd2ff7c0b823fd5f56286a663e92444761b35e25"}, + {file = "zope.interface-7.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:179ad46ece518c9084cb272e4a69d266b659f7f8f48e51706746c2d8a426433e"}, + {file = "zope.interface-7.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6503534b52bb1720ace9366ee30838a58a3413d3e197512f3338c8f34b5d89d"}, + {file = "zope.interface-7.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f85b290e5b8b11814efb0d004d8ce6c9a483c35c462e8d9bf84abb93e79fa770"}, + {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d029fac6a80edae80f79c37e5e3abfa92968fe921886139b3ee470a1b177321a"}, + {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5836b8fb044c6e75ba34dfaabc602493019eadfa0faf6ff25f4c4c356a71a853"}, + {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7395f13533318f150ee72adb55b29284b16e73b6d5f02ab21f173b3e83f242b8"}, + {file = "zope.interface-7.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:1d0e23c6b746eb8ce04573cc47bcac60961ac138885d207bd6f57e27a1431ae8"}, + {file = "zope.interface-7.1.1-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9fad9bd5502221ab179f13ea251cb30eef7cf65023156967f86673aff54b53a0"}, + {file = "zope.interface-7.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:55c373becbd36a44d0c9be1d5271422fdaa8562d158fb44b4192297b3c67096c"}, + {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed1df8cc01dd1e3970666a7370b8bfc7457371c58ba88c57bd5bca17ab198053"}, + {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99c14f0727c978639139e6cad7a60e82b7720922678d75aacb90cf4ef74a068c"}, + {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b1eed7670d564f1025d7cda89f99f216c30210e42e95de466135be0b4a499d9"}, + {file = "zope.interface-7.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:3defc925c4b22ac1272d544a49c6ba04c3eefcce3200319ee1be03d9270306dd"}, + {file = "zope.interface-7.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8d0fe45be57b5219aa4b96e846631c04615d5ef068146de5a02ccd15c185321f"}, + {file = "zope.interface-7.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bcbeb44fc16e0078b3b68a95e43f821ae34dcbf976dde6985141838a5f23dd3d"}, + {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8e7b05dc6315a193cceaec071cc3cf1c180cea28808ccded0b1283f1c38ba73"}, + {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d553e02b68c0ea5a226855f02edbc9eefd99f6a8886fa9f9bdf999d77f46585"}, + {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81744a7e61b598ebcf4722ac56a7a4f50502432b5b4dc7eb29075a89cf82d029"}, + {file = "zope.interface-7.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7720322763aceb5e0a7cadcc38c67b839efe599f0887cbf6c003c55b1458c501"}, + {file = "zope.interface-7.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ed0852c25950cf430067f058f8d98df6288502ac313861d9803fe7691a9b3"}, + {file = "zope.interface-7.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9595e478047ce752b35cfa221d7601a5283ccdaab40422e0dc1d4a334c70f580"}, + {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2317e1d4dba68203a5227ea3057f9078ec9376275f9700086b8f0ffc0b358e1b"}, + {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6821ef9870f32154da873fcde439274f99814ea452dd16b99fa0b66345c4b6b"}, + {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:190eeec67e023d5aac54d183fa145db0b898664234234ac54643a441da434616"}, + {file = "zope.interface-7.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:d17e7fc814eaab93409b80819fd6d30342844345c27f3bc3c4b43c2425a8d267"}, + {file = "zope.interface-7.1.1.tar.gz", hash = "sha256:4284d664ef0ff7b709836d4de7b13d80873dc5faeffc073abdb280058bfac5e3"}, ] [package.dependencies] setuptools = "*" [package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] -test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] -testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +docs = ["Sphinx", "furo", "repoze.sphinx.autointerface"] +test = ["coverage[toml]", "zope.event", "zope.testing"] +testing = ["coverage[toml]", "zope.event", "zope.testing"] [[package]] name = "zstandard" @@ -3739,5 +3751,5 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" -python-versions = "^3.11.4" -content-hash = "25d565224777a7e95a41bb5d211384b3db9abea084aacb2882cd58076a57ffc0" +python-versions = "^3.12.6" +content-hash = "38bd902453f56b7ef492dbaae0ce65080a283da0dbf8314644ddbcd38a298f95" diff --git a/pyproject.toml b/pyproject.toml index bf1bbec86e..6cacf2e56e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ flake8-quotes = "^3.4.0" [tool.poetry.dependencies] # update dependabot.yaml when adding new dependencies -python = "^3.11.4" +python = "^3.12.6" colorama = "^0.4.6" flask = "^3.0.2" flask-babel = "^4.0.0" @@ -94,6 +94,7 @@ ordered-set = "^4.1.0" cachetools = "^5.3.0.7" gevent = "^24.2.1" babel = "==2.14.0" # Temporarily pinned - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 +wtforms = "==3.1.2" # Temporarily pinned - problem for breaking changes in v3.2.0, see: https://github.com/pallets-eco/wtforms/releases/tag/3.2.0 [build-system] requires = ["poetry-core"] diff --git a/setup.cfg b/setup.cfg index d6356222f4..7624317db1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,4 +14,4 @@ inline-quotes = double multiline-quotes = double docstring-quotes = double avoid-escape = True -ignore = E704,W503,E203 +ignore = E704,W503,E203,E902 From 198db69dc2f3f47d3a87911e033f062216396230 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:25:57 +0000 Subject: [PATCH 481/567] Add Ruff linting to Runner (#1543) --- .github/dependabot.yaml | 2 +- poetry.lock | 375 ++++++++++++++----------------------- pyproject.toml | 113 ++++++++++- scripts/run_lint_python.sh | 7 +- 4 files changed, 252 insertions(+), 245 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 4fe6befed1..6652a49593 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -111,7 +111,6 @@ updates: dependency-type: "development" patterns: - "pytest*" - - "flake8*" - "pylint*" - "types*" - "pep8" @@ -127,3 +126,4 @@ updates: - "playwright" - "black" - "djlint" + - "ruff" diff --git a/poetry.lock b/poetry.lock index f88e032f38..173dd1c068 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.47" +version = "1.35.51" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.47-py3-none-any.whl", hash = "sha256:0b307f685875e9c7857ce21c0d3050d8d4f3778455a6852d5f98ac75194b400e"}, - {file = "boto3-1.35.47.tar.gz", hash = "sha256:65b808e4cf1af8c2f405382d53656a0d92eee8f85c7388c43d64c7a5571b065f"}, + {file = "boto3-1.35.51-py3-none-any.whl", hash = "sha256:c922f6a18958af9d8af0489d6d8503b517029d8159b26aa4859a8294561c72e9"}, + {file = "boto3-1.35.51.tar.gz", hash = "sha256:a57c6c7012ecb40c43e565a6f7a891f39efa990ff933eab63cd456f7501c2731"}, ] [package.dependencies] -botocore = ">=1.35.47,<1.36.0" +botocore = ">=1.35.51,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.47" +version = "1.35.51" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.47-py3-none-any.whl", hash = "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a"}, - {file = "botocore-1.35.47.tar.gz", hash = "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547"}, + {file = "botocore-1.35.51-py3-none-any.whl", hash = "sha256:4d65b00111bd12b98e9f920ecab602cf619cc6a6d0be6e5dd53f517e4b92901c"}, + {file = "botocore-1.35.51.tar.gz", hash = "sha256:a9b3d1da76b3e896ad74605c01d88f596324a3337393d4bfbfa0d6c35822ca9c"}, ] [package.dependencies] @@ -820,13 +820,13 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "djlint" -version = "1.35.2" +version = "1.35.3" description = "HTML Template Linter and Formatter" optional = false -python-versions = "<4.0,>=3.8" +python-versions = ">=3.9" files = [ - {file = "djlint-1.35.2-py3-none-any.whl", hash = "sha256:4ba995bad378f2afa77c8ea56ba1c14429d9ff26a18e8ae23bc71eedb9152243"}, - {file = "djlint-1.35.2.tar.gz", hash = "sha256:318de9d4b9b0061a111f8f5164ecbacd8215f449dd4bd5a76d2a691c815ee103"}, + {file = "djlint-1.35.3-py3-none-any.whl", hash = "sha256:bf2f23798909f9c5a110925c369538383de0141f9a2be37ee0d26422d41b7543"}, + {file = "djlint-1.35.3.tar.gz", hash = "sha256:780ea3e25662fca89033fa96ecf656099954d6f81dce039eac90f4bba3cbe850"}, ] [package.dependencies] @@ -834,11 +834,11 @@ click = ">=8.0.1" colorama = ">=0.4.4" cssbeautifier = ">=1.14.4" html-tag-names = ">=0.1.2" -html-void-elements = ">=0.1.0" +html-void-elements = ">=0.1" jsbeautifier = ">=1.14.4" json5 = ">=0.9.11" -pathspec = ">=0.12.0" -PyYAML = ">=6.0" +pathspec = ">=0.12" +pyyaml = ">=6" regex = ">=2023" tqdm = ">=4.62.2" @@ -903,13 +903,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.25.1" +version = "2.26.1" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.25.1-py3-none-any.whl", hash = "sha256:d08dcbaceae0804db4644fa634106e3c42d76fe4d11aea2949eda768df0c6450"}, - {file = "fakeredis-2.25.1.tar.gz", hash = "sha256:e9e73bacf412d1d942ee7f80525dc188182158e82d41be57eb9c4e71f7474ac8"}, + {file = "fakeredis-2.26.1-py3-none-any.whl", hash = "sha256:68a5615d7ef2529094d6958677e30a6d30d544e203a5ab852985c19d7ad57e32"}, + {file = "fakeredis-2.26.1.tar.gz", hash = "sha256:69f4daafe763c8014a6dbf44a17559c46643c95447b3594b3975251a171b806d"}, ] [package.dependencies] @@ -923,106 +923,6 @@ json = ["jsonpath-ng (>=1.6,<2.0)"] lua = ["lupa (>=2.1,<3.0)"] probabilistic = ["pyprobables (>=0.6,<0.7)"] -[[package]] -name = "flake8" -version = "7.1.1" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, - {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.12.0,<2.13.0" -pyflakes = ">=3.2.0,<3.3.0" - -[[package]] -name = "flake8-datetimez" -version = "20.10.0" -description = "A plugin for flake8 to ban the usage of unsafe naive datetime class." -optional = false -python-versions = ">=3.6" -files = [ - {file = "flake8-datetimez-20.10.0.tar.gz", hash = "sha256:78939f3bcbe2b7fe48235998545c869c27cdfac3f45685099a3f7366c1ffebc6"}, - {file = "flake8_datetimez-20.10.0-py3-none-any.whl", hash = "sha256:57aa2f55eb88797e2d8c06bd536ff8049b9f1ba877d81dc06ff8d9bdc195c1fc"}, -] - -[package.dependencies] -flake8 = ">=3.0.0" - -[[package]] -name = "flake8-debugger" -version = "4.1.2" -description = "ipdb/pdb statement checker plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-debugger-4.1.2.tar.gz", hash = "sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840"}, - {file = "flake8_debugger-4.1.2-py3-none-any.whl", hash = "sha256:0a5e55aeddcc81da631ad9c8c366e7318998f83ff00985a49e6b3ecf61e571bf"}, -] - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" - -[[package]] -name = "flake8-mock" -version = "0.4" -description = "Provides checking for non-existent mock methods" -optional = false -python-versions = "*" -files = [ - {file = "flake8-mock-0.4.tar.gz", hash = "sha256:4a05bac5f66e77661994880dd050705132d19000f17d928a894dfd92d55d4867"}, - {file = "flake8_mock-0.4-py3-none-any.whl", hash = "sha256:a67c3d22b2e7873c72d3f01d3eb5d06405cd09dc1abea74a0bf6fcf29095e8e6"}, -] - -[[package]] -name = "flake8-print" -version = "5.0.0" -description = "print statement checker plugin for flake8" -optional = false -python-versions = ">=3.7" -files = [ - {file = "flake8-print-5.0.0.tar.gz", hash = "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9"}, - {file = "flake8_print-5.0.0-py3-none-any.whl", hash = "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8"}, -] - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" - -[[package]] -name = "flake8-quotes" -version = "3.4.0" -description = "Flake8 lint for quotes." -optional = false -python-versions = "*" -files = [ - {file = "flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c"}, -] - -[package.dependencies] -flake8 = "*" -setuptools = "*" - -[[package]] -name = "flake8-tuple" -version = "0.4.1" -description = "Check code for 1 element tuple." -optional = false -python-versions = "*" -files = [ - {file = "flake8_tuple-0.4.1-py2.py3-none-any.whl", hash = "sha256:d828cc8e461c50cacca116e9abb0c9e3be565e8451d3f5c00578c63670aae680"}, - {file = "flake8_tuple-0.4.1.tar.gz", hash = "sha256:8a1b42aab134ef4c3fef13c6a8f383363f158b19fbc165bd91aed9c51851a61d"}, -] - -[package.dependencies] -flake8 = "*" -six = "*" - [[package]] name = "flask" version = "3.0.3" @@ -1203,13 +1103,13 @@ test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.21.0" +version = "2.22.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_core-2.21.0-py3-none-any.whl", hash = "sha256:6869eacb2a37720380ba5898312af79a4d30b8bca1548fb4093e0697dc4bdf5d"}, - {file = "google_api_core-2.21.0.tar.gz", hash = "sha256:4a152fd11a9f774ea606388d423b68aa7e6d6a0ffe4c8266f74979613ec09f81"}, + {file = "google_api_core-2.22.0-py3-none-any.whl", hash = "sha256:a6652b6bd51303902494998626653671703c420f6f4c88cfd3f50ed723e9d021"}, + {file = "google_api_core-2.22.0.tar.gz", hash = "sha256:26f8d76b96477db42b55fd02a33aae4a42ec8b86b98b94969b7333a2c828bf35"}, ] [package.dependencies] @@ -1217,7 +1117,10 @@ google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -proto-plus = ">=1.22.3,<2.0.0dev" +proto-plus = [ + {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, +] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -1339,20 +1242,23 @@ tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.16.5" +version = "2.17.0" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_tasks-2.16.5-py2.py3-none-any.whl", hash = "sha256:662d3eba82238fd48dee55f780a31f0990e3869b1df04126759a84cd0d83a598"}, - {file = "google_cloud_tasks-2.16.5.tar.gz", hash = "sha256:8bdc294a09e34647e120a2e40dd7ec20873024d5f4e0938b2ff0c39ff374edac"}, + {file = "google_cloud_tasks-2.17.0-py2.py3-none-any.whl", hash = "sha256:a49dcad5360237aa7203f10d2814a8d0de26a8d555aa3624b5d35a1c575cce1d"}, + {file = "google_cloud_tasks-2.17.0.tar.gz", hash = "sha256:e6d709b08af3b173b6c3fb80ff02b66e2d5954f92e94d5474d80b303847a38c1"}, ] [package.dependencies] google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.3,<2.0.0dev" +proto-plus = [ + {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, +] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [[package]] @@ -1534,85 +1440,85 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.67.0" +version = "1.67.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"}, - {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"}, - {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"}, - {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"}, - {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"}, - {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"}, - {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"}, - {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"}, - {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"}, - {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"}, - {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"}, - {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"}, - {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"}, - {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"}, - {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"}, - {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"}, - {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"}, - {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"}, - {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"}, - {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"}, - {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"}, - {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"}, - {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"}, - {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"}, - {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"}, - {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"}, - {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"}, - {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"}, - {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"}, - {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"}, - {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"}, - {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"}, - {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"}, - {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"}, - {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"}, - {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"}, - {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.67.0)"] + {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"}, + {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0"}, + {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa"}, + {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292"}, + {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311"}, + {file = "grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed"}, + {file = "grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e"}, + {file = "grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb"}, + {file = "grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc"}, + {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96"}, + {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f"}, + {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970"}, + {file = "grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744"}, + {file = "grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5"}, + {file = "grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953"}, + {file = "grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af"}, + {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e"}, + {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75"}, + {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38"}, + {file = "grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78"}, + {file = "grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc"}, + {file = "grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b"}, + {file = "grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955"}, + {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8"}, + {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62"}, + {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb"}, + {file = "grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121"}, + {file = "grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba"}, + {file = "grpcio-1.67.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:178f5db771c4f9a9facb2ab37a434c46cb9be1a75e820f187ee3d1e7805c4f65"}, + {file = "grpcio-1.67.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f3e49c738396e93b7ba9016e153eb09e0778e776df6090c1b8c91877cc1c426"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:24e8a26dbfc5274d7474c27759b54486b8de23c709d76695237515bc8b5baeab"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b6c16489326d79ead41689c4b84bc40d522c9a7617219f4ad94bc7f448c5085"}, + {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e6a4dcf5af7bbc36fd9f81c9f372e8ae580870a9e4b6eafe948cd334b81cf3"}, + {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:95b5f2b857856ed78d72da93cd7d09b6db8ef30102e5e7fe0961fe4d9f7d48e8"}, + {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b49359977c6ec9f5d0573ea4e0071ad278ef905aa74e420acc73fd28ce39e9ce"}, + {file = "grpcio-1.67.1-cp38-cp38-win32.whl", hash = "sha256:f5b76ff64aaac53fede0cc93abf57894ab2a7362986ba22243d06218b93efe46"}, + {file = "grpcio-1.67.1-cp38-cp38-win_amd64.whl", hash = "sha256:804c6457c3cd3ec04fe6006c739579b8d35c86ae3298ffca8de57b493524b771"}, + {file = "grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335"}, + {file = "grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d"}, + {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04"}, + {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8"}, + {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f"}, + {file = "grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e"}, + {file = "grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98"}, + {file = "grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.67.1)"] [[package]] name = "grpcio-status" -version = "1.67.0" +version = "1.67.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_status-1.67.0-py3-none-any.whl", hash = "sha256:0e79e2e01ba41a6ca6ed9d7a825323c511fe1653a646f8014c7e3c8132527acc"}, - {file = "grpcio_status-1.67.0.tar.gz", hash = "sha256:c3e5a86fa007e9e263cd5f988a8a907484da4caab582874ea2a4a6092734046b"}, + {file = "grpcio_status-1.67.1-py3-none-any.whl", hash = "sha256:16e6c085950bdacac97c779e6a502ea671232385e6e37f258884d6883392c2bd"}, + {file = "grpcio_status-1.67.1.tar.gz", hash = "sha256:2bf38395e028ceeecfd8866b081f61628114b384da7d51ae064ddc8d766a5d11"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.67.0" +grpcio = ">=1.67.1" protobuf = ">=5.26.1,<6.0dev" [[package]] @@ -2352,17 +2258,6 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.7.0" -[[package]] -name = "pycodestyle" -version = "2.12.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, - {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, -] - [[package]] name = "pycparser" version = "2.22" @@ -2391,17 +2286,6 @@ typing-extensions = "*" [package.extras] dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "sphinx", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"] -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - [[package]] name = "pylint" version = "3.3.1" @@ -2685,13 +2569,13 @@ files = [ [[package]] name = "redis" -version = "5.1.1" +version = "5.2.0" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" files = [ - {file = "redis-5.1.1-py3-none-any.whl", hash = "sha256:f8ea06b7482a668c6475ae202ed8d9bcaa409f6e87fb77ed1043d912afd62e24"}, - {file = "redis-5.1.1.tar.gz", hash = "sha256:f6c997521fedbae53387307c5d0bf784d9acc28d9f1d058abeac566ec4dbed72"}, + {file = "redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897"}, + {file = "redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0"}, ] [package.extras] @@ -2982,6 +2866,33 @@ files = [ [package.dependencies] pyasn1 = ">=0.1.3" +[[package]] +name = "ruff" +version = "0.7.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, + {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, + {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, + {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, + {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, + {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, + {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, +] + [[package]] name = "s3transfer" version = "0.10.3" @@ -3017,23 +2928,23 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "75.2.0" +version = "75.3.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, - {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, + {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, + {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] [[package]] name = "simplejson" @@ -3231,13 +3142,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.5" +version = "4.66.6" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, + {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, ] [package.dependencies] @@ -3353,13 +3264,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.2.0.20241019" +version = "75.2.0.20241025" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-75.2.0.20241019.tar.gz", hash = "sha256:86ea31b5f6df2c6b8f2dc8ae3f72b213607f62549b6fa2ed5866e5299f968694"}, - {file = "types_setuptools-75.2.0.20241019-py3-none-any.whl", hash = "sha256:2e48ff3acd4919471e80d5e3f049cce5c177e108d5d36d2d4cee3fa4d4104258"}, + {file = "types-setuptools-75.2.0.20241025.tar.gz", hash = "sha256:2949913a518d5285ce00a3b7d88961c80a6e72ffb8f3da0a3f5650ea533bd45e"}, + {file = "types_setuptools-75.2.0.20241025-py3-none-any.whl", hash = "sha256:6721ac0f1a620321e2ccd87a9a747c4a383dc381f78d894ce37f2455b45fcf1c"}, ] [[package]] @@ -3414,23 +3325,23 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uwsgi" -version = "2.0.27" +version = "2.0.28" description = "The uWSGI server" optional = false python-versions = "*" files = [ - {file = "uwsgi-2.0.27.tar.gz", hash = "sha256:3ee5bfb7e6e9c93478c22aa8183eef35b95a2d5b14cca16172e67f135565c458"}, + {file = "uwsgi-2.0.28.tar.gz", hash = "sha256:79ca1891ef2df14508ab0471ee8c0eb94bd2d51d03f32f90c4bbe557ab1e99d0"}, ] [[package]] name = "werkzeug" -version = "3.0.4" +version = "3.0.6" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, - {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, + {file = "werkzeug-3.0.6-py3-none-any.whl", hash = "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17"}, + {file = "werkzeug-3.0.6.tar.gz", hash = "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d"}, ] [package.dependencies] @@ -3752,4 +3663,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12.6" -content-hash = "38bd902453f56b7ef492dbaae0ce65080a283da0dbf8314644ddbcd38a298f95" +content-hash = "5143ceb091adda9bad1b5a94c2540fb27d9406ecfb3577a9b7d629e5859aaca7" diff --git a/pyproject.toml b/pyproject.toml index 6cacf2e56e..986a5f54e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,6 @@ pylint-mccabe = "^0.1.3" pylint-absolute-imports = "^1.1.0" beautifulsoup4 = "^4.12.3" httmock = "^1.4.0" -flake8 = "^7.1.1" -flake8-debugger = "^4.1.2" -flake8-mock = "^0.4" -flake8-print = "^5.0.0" -flake8-tuple = "^0.4.1" -flake8-datetimez = "^20.10.0" moto = "^5.0.13" freezegun = "^1.4.0" pytest-xdist = "^3.5.0" @@ -50,7 +44,7 @@ types-pytz = "^2024.1.0.20240417" playwright = "^1.42.0" black = "^24.2.0" djlint = "^1.34.2" -flake8-quotes = "^3.4.0" +ruff = "^0.7.0" [tool.poetry.dependencies] @@ -99,3 +93,108 @@ wtforms = "==3.1.2" # Temporarily pinned - problem for breaking changes in v3.2 [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.ruff] +target-version = "py312" +include = ["*.py"] +line-length = 160 +indent-width = 4 +cache-dir = "~/.cache/ruff" +exclude = ["tests/*", "scripts/*"] + + +[tool.ruff.lint] +extend-ignore = [ + "B024", # No abstract methods in abstract base class + "B027", # Non-abstract empty methods in abstract base classes + "B028",# No explicit keyword argument found + "B905", # zip() without an explicit strict= parameter + "RUF100", # Allow Unused blanket `noqa` directive + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "RUF005", # Consider {expression} instead of concatenation + "RUF009", # Do not perform function call `lazy_gettext` in dataclass defaults + "RUF015", # Prefer `next(...)` over single element slice + "RUF010", # Use explicit conversion flag + "RUF001", # String contains ambiguous character + "ARG001", # Allow Unused function argument: `method_name` + "ARG002", # Unused method argument + "ARG004", # Unused static method argument: `kwargs + "N818", # Exception name should be named with an Error suffix + "EM101", # Exception must not use a string literal, assign to variable first + "EM102", # EM102 Exception must not use an f-string literal, assign to variable first + "UP032", # Use f-string instead of `format` call + "UP018", # Unnecessary {literal_type} call (rewrite as a literal) + "UP034", # Avoid extraneous parentheses + "UP015", # Unnecessary open mode parameters + "UP007", # Use `X | Y` for type annotations + "UP006", # Use `type` instead of `Type` for type annotation + "UP009", # UTF-8 encoding declaration is unnecessary + "UP017", # Use `datetime.UTC` alias + "UP033", # Use @functools.cache instead of @functools.lru_cache(maxsize=None) + "UP037", # Remove quotes from type annotation + "UP038", # Use X | Y in {} call instead of (X, Y) + "UP035", # Import from {target} instead: {names} + "UP040", # Type alias {name} uses {type_alias_method} instead of the type keyword + "S105", # Possible hardcoded password assigned to: "{}" + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes + "C408", # Unnecessary `tuple` call (rewrite as a literal) + "C400", # Unnecessary generator (rewrite as a `list` comprehension) + "N802", # Function name {name} should be lowercase + "I001", # Import block is un-sorted or un-formatted + "FBT001", # Boolean-typed keyword argument in function definition + "FBT002", # Boolean-typed positional argument in function definition + "FBT003", # Boolean positional value in function call + "COM812", # Trailing comma missing + "G004", # Logging statement uses f-string + "PIE810", # Call {attr} once with a tuple + "PIE800", # Unnecessary spread ** + "SLF001", # Private member accessed: {access} + "TRY003", # Avoid specifying long messages outside the exception class + "TRY400", # Use logging.exception instead of logging.error + "TRY201", # Avoid using `raise Exception` without specifying an exception class + "TRY300", # Consider moving this statement to an `else` block + "PERF401", # Use a list comprehension to create a transformed list + "RET501", # Do not explicitly `return None` in function if it is the only possible return value + "RET503", # Missing explicit `return` at the end of function able to return non-`None` value + "RET504", # Unnecessary assignment to `context` before `return` statement +] +extend-select = [ + "E4", "E7", "E9", "E5", # On top of the defaults (`E4`, E7`, `E9`, and `F`), enable E5 (Adds line length check - error "E501") + "Q", # flake8-quotes (Q) + "B", # flake8-bugbear (B) + "A", # flake8-builtins (A) + "C4", # flake8-comprehensions (C4) + "PIE", # flake8-pie (PIE) + "SLF", # flake8-self (SLF) + "ARG", # flake8-unused-arguments (ARG) + "YTT", # flake8-2020 (YTT) + "C", # flake8-comprehensions (C) + "DTZ", # flake8-datetimez (DTZ) + "S", # flake8-bandit (S) + "TID", # flake8-tidy-imports (TID) + "ICN", # flake8-import-conventions (ICN) + "ISC", # flake8-implicit-str-concat (ISC) + "COM", # flake8-commas (COM) + "LOG", # flake8-logging (LOG) + "G", # flake8-logging-format (G) + "EM", # flake8-errmsg (EM) + "FBT", # flake8-boolean-trap (FBT) + "TD", # flake8-todo (TD) + "FA", # flake8-future-annotations (FA) + "T20", # flake8-print (T20) + "RET", # flake8-return (RET) + "E", # pycodestyle Error (E) + "W", # pycodestyle Warning (W) + "F", # pyflakes (F) + "I", # isort (I) + "N", # pep8-naming (N) + "RUF", # Ruff-specific rules (RUF) + "UP", # pyupgrade (UP) + "ERA", # eradicate (ERA) + "FURB", # refurb (FURB) + "TRY", # tryceratops (TRY) + "FLY", # flynt (FLY) + "PERF", # Perflint (PERF) +] +[tool.ruff.lint.isort] +case-sensitive = true diff --git a/scripts/run_lint_python.sh b/scripts/run_lint_python.sh index f11af5b553..44dd02c096 100755 --- a/scripts/run_lint_python.sh +++ b/scripts/run_lint_python.sh @@ -18,8 +18,8 @@ function display_result { fi } -flake8 --max-complexity 10 --count -display_result $? 1 "Flake 8 code style check" +ruff check . +display_result $? 1 "Ruff code style check (including isort)" # pylint bit encodes the exit code to allow you to figure out which category has failed. # https://docs.pylint.org/en/1.6.0/run.html#exit-codes @@ -28,9 +28,6 @@ display_result $? 1 "Flake 8 code style check" find . -type f -name "*.py" | xargs pylint --reports=n --output-format=colorized --rcfile=.pylintrc -j 0 display_result $? 1 "Pylint linting check" -isort --check . -display_result $? 1 "isort linting check" - ./scripts/run_mypy.sh display_result $? 1 "Mypy type check" From dbe149d5ab887ccc1734cb03689cd0357ae8b2f4 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Thu, 31 Oct 2024 13:24:34 +0000 Subject: [PATCH 482/567] Update npm dependencies, deprecated test functions, and add major version dependabot ignore (#1535) --- .github/dependabot.yaml | 9 + package-lock.json | 7661 ++++++++++------- package.json | 38 +- tests/functional/helpers.js | 4 + tests/functional/spec/checkbox.spec.js | 4 +- .../spec/components/address/address.spec.js | 8 +- .../checkbox_detail_answer_multiple.spec.js | 4 +- .../functional/spec/components/radio/radio.js | 14 +- .../radio_detail_answer_multiple.spec.js | 4 +- .../spec/confirmation_email.spec.js | 22 +- tests/functional/spec/dates.spec.js | 6 +- tests/functional/spec/durations.spec.js | 8 +- .../features/confirmation_question.spec.js | 6 +- ..._question_within_repeating_section.spec.js | 10 +- .../default_value/default_value.spec.js | 16 +- .../dynamic_answer_options/function_driven.js | 16 +- .../radio_options_from_checkbox_answers.js | 8 +- .../dynamic_answers_list_value_source.spec.js | 10 +- .../last_viewed_guidance.spec.js | 10 +- .../last_viewed_guidance_hub.spec.js | 20 +- .../features/submit_page/submit_page.spec.js | 6 +- .../submit_page/submit_page_summary.spec.js | 8 +- ...bmit_with_summary_return_to_answer.spec.js | 10 +- .../supplementary_data.spec.js | 12 +- .../date_validation/date-range.spec.js | 6 +- .../date_validation/date-single.spec.js | 4 +- .../features/validation/sum/dynamic.spec.js | 26 +- .../features/validation/sum/equal.spec.js | 10 +- .../validation/sum/equal_multiple.spec.js | 8 +- .../validation/sum/equal_or_less_than.spec.js | 8 +- ...qual_total_in_separate_section_hub.spec.js | 8 +- ...otal_in_separate_section_repeating.spec.js | 10 +- .../features/validation/sum/less_than.spec.js | 6 +- .../validation/sum/value_source.spec.js | 14 +- .../view_submitted_response.spec.js | 10 +- tests/functional/spec/feedback.spec.js | 12 +- .../enabled_section_checkbox.spec.js | 12 +- .../enabled_section_radio.spec.js | 12 +- .../hub_and_spoke/hub_and_spoke.spec.js | 67 +- .../hub_and_spoke_required_enable.spec.js | 4 +- .../progress/progress_value_source_blocks.js | 38 +- .../progress_value_source_repeating.js | 16 +- .../list_collector_repeating_blocks.spec.js | 36 +- ...eating_sections_with_hub_and_spoke.spec.js | 18 +- .../spec/journeys/routing/all_in.spec.js | 8 +- .../spec/journeys/routing/and.spec.js | 10 +- ...edirect_to_list_add_block_checkbox.spec.js | 24 +- ...n_redirect_to_list_add_block_radio.spec.js | 24 +- .../routing/answer_not_on_path.spec.js | 8 +- .../routing/answered_unanswered.spec.js | 20 +- .../spec/journeys/routing/any_in.spec.js | 8 +- .../spec/journeys/routing/boolean.spec.js | 6 +- .../journeys/routing/checkbox_count.spec.js | 10 +- .../conditional_combined_routing.spec.js | 10 +- .../spec/journeys/routing/date.spec.js | 87 +- .../spec/journeys/routing/in.spec.js | 6 +- .../spec/journeys/routing/not.spec.js | 6 +- .../spec/journeys/routing/number.spec.js | 34 +- .../spec/journeys/routing/or.spec.js | 10 +- .../routing/removes_completed_block.spec.js | 4 +- ...on_dependencies_calculated_summary.spec.js | 12 +- .../routing_checkbox_contains.spec.js | 16 +- .../skipping/skip_condition_block.spec.js | 6 +- .../skipping/skip_condition_group.spec.js | 6 +- .../skipping/skip_condition_list.spec.js | 10 +- .../skipping/skip_conditions_not_set.spec.js | 6 +- .../skipping/skip_conditions_set.spec.js | 6 +- tests/functional/spec/language_code.spec.js | 8 +- tests/functional/spec/launch_with_cir.spec.js | 7 +- .../list_collector/list_collector.spec.js | 22 +- .../list_collector_content.spec.js | 16 +- .../list_collector_driving_question.spec.js | 6 +- .../list_collector_primary_person.spec.js | 4 +- .../list_collector_section_summary.spec.js | 66 +- .../list_collector_variants.spec.js | 10 +- ..._collector_variants_primary_person.spec.js | 6 +- .../relationships-unrelated.spec.js | 4 +- .../spec/list_collector/relationships.spec.js | 24 +- .../functional/spec/multiple_answers.spec.js | 6 +- .../spec/my_account_header_link.spec.js | 3 +- tests/functional/spec/numbers.spec.js | 8 +- .../spec/percentage_decimal.spec.js | 6 +- .../placeholder_answers_on_the_path.spec.js | 10 +- tests/functional/spec/preview.spec.js | 7 +- .../spec/question_description.spec.js | 4 +- tests/functional/spec/save_sign_out.spec.js | 12 +- .../calculated_summary_test_case.js | 54 +- ...mmary_repeating_and_static_answers.spec.js | 30 +- ...alculated_summary_repeating_blocks.spec.js | 24 +- ...lculated_summary_repeating_section.spec.js | 62 +- ...summary_cross_section_dependencies.spec.js | 16 +- ...d_summary_inside_repeating_section.spec.js | 108 +- ...ulated_summary_overlapping_answers.spec.js | 22 +- ...lculated_summary_repeating_answers.spec.js | 70 +- .../custom_question_summary.spec.js | 4 +- .../section_summary/section_summary.spec.js | 36 +- ...section_summary_repeating_sections.spec.js | 4 +- ...show_section_summary_on_completion.spec.js | 10 +- tests/functional/spec/textfield.spec.js | 4 +- .../spec/textfield_suggestions.spec.js | 4 +- tests/functional/spec/thank_you.spec.js | 8 +- tests/functional/spec/theme_dbt.spec.js | 3 +- tests/functional/spec/theme_dbt_dsit.spec.js | 3 +- .../functional/spec/theme_dbt_dsit_ni.spec.js | 3 +- tests/functional/spec/theme_dbt_ni.spec.js | 3 +- tests/functional/spec/theme_desnz.spec.js | 3 +- tests/functional/spec/theme_desnz_ni.spec.js | 3 +- tests/functional/spec/theme_nhse.spec.js | 3 +- .../spec/theme_northernireland.spec.js | 3 +- tests/functional/spec/theme_orr.spec.js | 3 +- tests/functional/spec/theme_ukhsa_ons.spec.js | 3 +- .../functional/spec/timeout/timeout_modal.js | 8 +- tests/functional/wdio.conf.js | 2 +- 113 files changed, 5413 insertions(+), 3888 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 6652a49593..884f15a9ce 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -38,6 +38,15 @@ updates: - "typescript*" - "uuid*" - "webdriverio*" + ignore: + # temporarily pinned to minor/patch only - eslint v9 not supported in eslint-config-standard v17.1.0: https://github.com/standard/eslint-config-standard/issues/410 + # This had a knock-on effect with `eslint-plugin-n` and `eslint-plugin-promise` + - dependency-name: "eslint*" + update-types: [ "version-update:semver-major" ] + + # temporarily pinned to minor/patch only - wdio v9 causes getHTML() to return strings with indentation & newlines, causing assertion errors - needs investigation + - dependency-name: "@wdio/local-runner" + update-types: [ "version-update:semver-major" ] - package-ecosystem: "pip" directory: "/" diff --git a/package-lock.json b/package-lock.json index ecf0c6fadc..b9a8037df2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,95 +8,86 @@ "name": "eq-questionnaire-runner", "version": "1.0.0", "devDependencies": { - "@babel/core": "^7.23.2", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/register": "^7.22.5", - "@babel/runtime": "^7.23.2", - "@wdio/cli": "^8.14.4", + "@babel/core": "^7.25.8", + "@babel/plugin-transform-runtime": "^7.25.7", + "@babel/preset-env": "^7.25.8", + "@babel/register": "^7.25.7", + "@babel/runtime": "^7.25.7", + "@wdio/cli": "^9.2.1", "@wdio/local-runner": "^8.14.3", - "@wdio/mocha-framework": "^8.14.0", - "@wdio/spec-reporter": "^8.14.0", - "eslint": "^8.46.0", + "@wdio/mocha-framework": "^9.1.3", + "@wdio/spec-reporter": "^9.1.3", + "eslint": "^v8.57.1", "eslint-cli": "^1.1.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.28.0", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-n": "^16.0.1", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-json": "^4.0.1", + "eslint-plugin-n": "^16.6.2", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-promise": "^6.6.0", "json-web-key": "^0.4.0", "jsrsasign": "^11.0.0", - "lint-staged": "^13.2.3", + "lint-staged": "^15.2.10", "livereload": "^0.9.3", - "node-forge": "^1.2.1", + "node-forge": "^1.3.1", "node-jose": "^2.2.0", - "prettier": "^3.0.1", - "typescript": "^5.1.6", - "uuid": "^9.0.0", - "webdriverio": "^8.15.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "prettier": "^3.3.3", + "typescript": "^5.6.3", + "uuid": "^10.0.0", + "webdriverio": "^9.2.1" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", "dev": true, "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -112,53 +103,54 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", + "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", + "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -167,19 +159,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz", - "integrity": "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", + "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/traverse": "^7.25.7", "semver": "^6.3.1" }, "engines": { @@ -190,13 +180,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", + "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.7", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, "engines": { @@ -207,9 +197,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -222,75 +212,42 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", + "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -300,35 +257,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", + "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", + "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-wrap-function": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -338,14 +295,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", + "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -355,115 +312,108 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", + "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", + "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", "dev": true, "dependencies": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.25.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.8" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -471,13 +421,14 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", + "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -486,31 +437,28 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", + "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.13.0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", + "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -519,104 +467,44 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", + "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-transform-optional-chaining": "^7.25.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", + "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, "engines": { "node": ">=6.9.0" }, @@ -624,109 +512,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", + "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -735,13 +527,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", + "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -767,12 +559,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", + "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -782,15 +574,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", + "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -800,14 +591,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", + "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -817,12 +608,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", + "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -832,12 +623,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", + "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -847,13 +638,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", + "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -863,14 +654,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", + "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -880,18 +670,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", + "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/traverse": "^7.25.7", "globals": "^11.1.0" }, "engines": { @@ -902,13 +690,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", + "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/template": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -918,12 +706,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", + "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -933,13 +721,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", + "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -949,12 +737,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", + "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -963,14 +751,29 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", + "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -980,13 +783,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", + "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -996,13 +799,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", + "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1012,13 +814,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", + "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1028,14 +830,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", + "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1045,13 +847,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", + "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1061,12 +862,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", + "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1076,13 +877,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", + "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1092,12 +892,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", + "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1107,13 +907,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", + "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1123,14 +923,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", + "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1140,15 +940,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", + "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1158,13 +958,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", + "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1174,13 +974,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1190,12 +990,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", + "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1205,13 +1005,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", + "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1221,13 +1020,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", + "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1237,16 +1035,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", + "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-transform-parameters": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1256,13 +1052,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", + "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1272,13 +1068,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", + "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1288,14 +1083,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", + "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1305,12 +1099,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", + "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1320,13 +1114,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", + "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1336,15 +1130,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", + "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1354,12 +1147,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", + "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1369,12 +1162,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", + "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.25.7", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1385,12 +1178,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", + "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1400,16 +1193,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", - "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.7.tgz", + "integrity": "sha512-Y9p487tyTzB0yDYQOtWnC+9HGOuogtP3/wNpun1xJXEEvI6vip59BSBTsHnekZLqxmPcgsrAKt46HAAb//xGhg==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -1420,12 +1213,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", + "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1435,13 +1228,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", + "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1451,12 +1244,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", + "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1466,12 +1259,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", + "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1481,12 +1274,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", + "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1496,12 +1289,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", + "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1511,13 +1304,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", + "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1527,13 +1320,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", + "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1543,13 +1336,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", + "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1559,90 +1352,78 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", + "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.8", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.25.7", + "@babel/plugin-syntax-import-attributes": "^7.25.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/plugin-transform-arrow-functions": "^7.25.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.8", + "@babel/plugin-transform-async-to-generator": "^7.25.7", + "@babel/plugin-transform-block-scoped-functions": "^7.25.7", + "@babel/plugin-transform-block-scoping": "^7.25.7", + "@babel/plugin-transform-class-properties": "^7.25.7", + "@babel/plugin-transform-class-static-block": "^7.25.8", + "@babel/plugin-transform-classes": "^7.25.7", + "@babel/plugin-transform-computed-properties": "^7.25.7", + "@babel/plugin-transform-destructuring": "^7.25.7", + "@babel/plugin-transform-dotall-regex": "^7.25.7", + "@babel/plugin-transform-duplicate-keys": "^7.25.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-dynamic-import": "^7.25.8", + "@babel/plugin-transform-exponentiation-operator": "^7.25.7", + "@babel/plugin-transform-export-namespace-from": "^7.25.8", + "@babel/plugin-transform-for-of": "^7.25.7", + "@babel/plugin-transform-function-name": "^7.25.7", + "@babel/plugin-transform-json-strings": "^7.25.8", + "@babel/plugin-transform-literals": "^7.25.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", + "@babel/plugin-transform-member-expression-literals": "^7.25.7", + "@babel/plugin-transform-modules-amd": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.7", + "@babel/plugin-transform-modules-umd": "^7.25.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-new-target": "^7.25.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", + "@babel/plugin-transform-numeric-separator": "^7.25.8", + "@babel/plugin-transform-object-rest-spread": "^7.25.8", + "@babel/plugin-transform-object-super": "^7.25.7", + "@babel/plugin-transform-optional-catch-binding": "^7.25.8", + "@babel/plugin-transform-optional-chaining": "^7.25.8", + "@babel/plugin-transform-parameters": "^7.25.7", + "@babel/plugin-transform-private-methods": "^7.25.7", + "@babel/plugin-transform-private-property-in-object": "^7.25.8", + "@babel/plugin-transform-property-literals": "^7.25.7", + "@babel/plugin-transform-regenerator": "^7.25.7", + "@babel/plugin-transform-reserved-words": "^7.25.7", + "@babel/plugin-transform-shorthand-properties": "^7.25.7", + "@babel/plugin-transform-spread": "^7.25.7", + "@babel/plugin-transform-sticky-regex": "^7.25.7", + "@babel/plugin-transform-template-literals": "^7.25.7", + "@babel/plugin-transform-typeof-symbol": "^7.25.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.7", + "@babel/plugin-transform-unicode-property-regex": "^7.25.7", + "@babel/plugin-transform-unicode-regex": "^7.25.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -1667,9 +1448,9 @@ } }, "node_modules/@babel/register": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.23.7.tgz", - "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.7.tgz", + "integrity": "sha512-qHTd2Rhn/rKhSUwdY6+n98FmwXN+N+zxSVx3zWqRe9INyvTpv+aQ5gDV2+43ACd3VtMBzPPljbb0gZb8u5ma6Q==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", @@ -1685,16 +1466,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1704,33 +1479,30 @@ } }, "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1739,77 +1511,461 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" - }, + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -1827,21 +1983,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -1863,297 +2020,1018 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@inquirer/checkbox": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", + "integrity": "sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-4.0.1.tgz", + "integrity": "sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-3.0.1.tgz", + "integrity": "sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-3.0.1.tgz", + "integrity": "sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", + "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-3.0.1.tgz", + "integrity": "sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-2.0.1.tgz", + "integrity": "sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-3.0.1.tgz", + "integrity": "sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-6.0.1.tgz", + "integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^3.0.1", + "@inquirer/confirm": "^4.0.1", + "@inquirer/editor": "^3.0.1", + "@inquirer/expand": "^3.0.1", + "@inquirer/input": "^3.0.1", + "@inquirer/number": "^2.0.1", + "@inquirer/password": "^3.0.1", + "@inquirer/rawlist": "^3.0.1", + "@inquirer/search": "^2.0.1", + "@inquirer/select": "^3.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-3.0.1.tgz", + "integrity": "sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-2.0.1.tgz", + "integrity": "sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-3.0.1.tgz", + "integrity": "sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.2.1", + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@promptbook/utils": { + "version": "0.70.0-1", + "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.70.0-1.tgz", + "integrity": "sha512-qd2lLRRN+sE6UuNMi2tEeUUeb4zmXnxY5EMdfHVXNE+bqBDpUC7/aEfXgA3jnUXEr+xFjQ8PTFQgWvBMaKvw0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/webgptorg/promptbook/blob/main/README.md#%EF%B8%8F-contributing" + } + ], + "dependencies": { + "spacetrim": "0.11.39" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", + "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.6", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "dev": true + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz", + "integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", + "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "dev": true + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", "dev": true }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" + "@types/node": "*" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "dependencies": { + "@types/yargs-parser": "*" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, + "optional": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/node": "*" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "tinyrainbow": "^1.2.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "node_modules/@vitest/snapshot": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", + "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", "dev": true, "dependencies": { - "jest-get-type": "^29.6.3" + "@vitest/pretty-format": "2.1.3", + "magic-string": "^0.30.11", + "pathe": "^1.1.2" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@wdio/cli": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.2.1.tgz", + "integrity": "sha512-lGz/JBkSQh4qH9sDFbNrPHfQNxrDtbm2tb3NgzMNXk88G2dq9AzoYriRgfXO7rKETc+nFreuEdULV+MMhRHk/A==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.27.8" + "@types/node": "^20.1.1", + "@vitest/snapshot": "^2.1.1", + "@wdio/config": "9.1.3", + "@wdio/globals": "9.2.1", + "@wdio/logger": "9.1.3", + "@wdio/protocols": "9.2.0", + "@wdio/types": "9.1.3", + "@wdio/utils": "9.1.3", + "async-exit-hook": "^2.0.1", + "chalk": "^5.2.0", + "chokidar": "^4.0.0", + "cli-spinners": "^3.0.0", + "dotenv": "^16.3.1", + "ejs": "^3.1.9", + "execa": "^9.2.0", + "import-meta-resolve": "^4.0.0", + "inquirer": "^11.0.1", + "lodash.flattendeep": "^4.4.0", + "lodash.pickby": "^4.6.0", + "lodash.union": "^4.6.0", + "read-pkg-up": "^10.0.0", + "recursive-readdir": "^2.2.3", + "tsx": "^4.7.2", + "webdriverio": "9.2.1", + "yargs": "^17.7.2" + }, + "bin": { + "wdio": "bin/wdio.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18.20.0" } }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "node_modules/@wdio/cli/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@wdio/config": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.1.3.tgz", + "integrity": "sha512-fozjb5Jl26QqQoZ2lJc8uZwzK2iKKmIfNIdNvx5JmQt78ybShiPuWWgu/EcHYDvAiZwH76K59R1Gp4lNmmEDew==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@wdio/logger": "9.1.3", + "@wdio/types": "9.1.3", + "@wdio/utils": "9.1.3", + "decamelize": "^6.0.0", + "deepmerge-ts": "^7.0.3", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18.20.0" } }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@wdio/globals": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.2.1.tgz", + "integrity": "sha512-svPSPbV9ZxunmkJVmcCw5A7vzGBYpO1kPmBK9LLZFfVhXiwps0EOl+j6KtqwbQ0cTvC6PEHzm/bwmX4DEzBAzA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=18.20.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "optionalDependencies": { + "expect-webdriverio": "^5.0.1", + "webdriverio": "9.2.1" } }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@wdio/local-runner": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.40.6.tgz", + "integrity": "sha512-h2OvQ6kODNiEaM6JzYzTzPl4n9TiqM2KsQ+8x3QoMIHY7Z9JSc+cH9QcnNsm3Sqxk/LVYFGetza6/WTsHx1/zA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@types/node": "^22.2.0", + "@wdio/logger": "8.38.0", + "@wdio/repl": "8.40.3", + "@wdio/runner": "8.40.6", + "@wdio/types": "8.40.6", + "async-exit-hook": "^2.0.1", + "split2": "^4.1.0", + "stream-buffers": "^3.0.2" }, "engines": { - "node": ">=7.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@wdio/local-runner/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "undici-types": "~6.19.2" } }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": "^16.13 || >=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/@wdio/local-runner/node_modules/@wdio/types": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", + "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@types/node": "^22.2.0" }, "engines": { - "node": ">=6.0.0" + "node": "^16.13 || >=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@wdio/local-runner/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@wdio/logger": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.1.3.tgz", + "integrity": "sha512-cumRMK/gE1uedBUw3WmWXOQ7HtB6DR8EyKQioUz2P0IJtRRpglMBdZV7Svr3b++WWawOuzZHMfbTkJQmaVt8Gw==", "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18.20.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "node_modules/@wdio/logger/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@ljharb/through": { - "version": "2.3.12", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", - "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", + "node_modules/@wdio/mocha-framework": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.1.3.tgz", + "integrity": "sha512-MhYTwqZdpqu28vUFnU0swbv9Y/cKRGFdaJtBImpT0HlnbBHG3NouEcQnInSiGst5JMdDBRrkxHYZyTz6y3Uxpw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5" + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.28", + "@wdio/logger": "9.1.3", + "@wdio/types": "9.1.3", + "@wdio/utils": "9.1.3", + "mocha": "^10.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18.20.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@wdio/protocols": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.2.0.tgz", + "integrity": "sha512-lSdKCwLtqMxSIW+cl8au21GlNkvmLNGgyuGYdV/lFdWflmMYH1zusruM6Km6Kpv2VUlWySjjGknYhe7XVTOeMw==", + "dev": true + }, + "node_modules/@wdio/repl": { + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", + "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@types/node": "^22.2.0" }, "engines": { - "node": ">= 8" + "node": "^16.13 || >=18" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@wdio/repl/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@wdio/reporter": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.1.3.tgz", + "integrity": "sha512-j8i2Rs2JkcLdvdP6eysMNKgUnApi/ESwRYtscQvQIOYvzy2xOEJRe6VOeoUjLgKNN4VGo165H04bbxMR0oacUw==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@types/node": "^20.1.0", + "@wdio/logger": "9.1.3", + "@wdio/types": "9.1.3", + "diff": "^7.0.0", + "object-inspect": "^1.12.0" }, "engines": { - "node": ">= 8" + "node": ">=18.20.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@wdio/runner": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.40.6.tgz", + "integrity": "sha512-dScN5mLia3YYctEgrbO4JG7pEttICfNENFd08t1T/hNJAWyzSbG/wvAL5k0HUGqp6Ukly6NUmhVsP5PVBz4ooA==", "dev": true, - "optional": true, + "dependencies": { + "@types/node": "^22.2.0", + "@wdio/config": "8.40.6", + "@wdio/globals": "8.40.6", + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "deepmerge-ts": "^5.1.0", + "expect-webdriverio": "^4.12.0", + "gaze": "^1.1.3", + "webdriver": "8.40.6", + "webdriverio": "8.40.6" + }, "engines": { - "node": ">=14" + "node": "^16.13 || >=18" } }, - "node_modules/@puppeteer/browsers": { + "node_modules/@wdio/runner/node_modules/@puppeteer/browsers": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", @@ -2174,379 +3052,345 @@ "node": ">=16.3.0" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "node_modules/@wdio/runner/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "dependencies": { + "undici-types": "~6.19.2" } }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "node_modules/@wdio/runner/node_modules/@wdio/config": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.40.6.tgz", + "integrity": "sha512-rHCSmrhdJf7FlidcQPDvRKRPLYjklbrdxQa6J20BxHifTO4h2v23Wrq4OqqYIcq23gf9LpZvCA/PAMiET/QdVg==", "dev": true, "dependencies": { - "defer-to-connect": "^2.0.1" + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=14.16" - } - }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" + "node": "^16.13 || >=18" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "node_modules/@wdio/runner/node_modules/@wdio/globals": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.40.6.tgz", + "integrity": "sha512-37llSQk4ngM6wzn8diBQ1Xik2ZZtUCU29Hr7NbfyfXahQdzIApRxNtw0B2J5XrjGX9yuO6gdyg5Kj+UuaKIo7A==", "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" + "engines": { + "node": "^16.13 || >=18" + }, + "optionalDependencies": { + "expect-webdriverio": "^4.11.2", + "webdriverio": "8.40.6" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", - "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", - "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "node_modules/@wdio/runner/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", - "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", + "node_modules/@wdio/runner/node_modules/@wdio/protocols": { + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", + "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", "dev": true }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "node_modules/@wdio/runner/node_modules/@wdio/types": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", + "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", "dev": true, "dependencies": { - "@types/node": "*" + "@types/node": "^22.2.0" + }, + "engines": { + "node": "^16.13 || >=18" } }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "node_modules/@wdio/runner/node_modules/@wdio/utils": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.40.6.tgz", + "integrity": "sha512-+TWfV6h+4f8gs7QiYUAWbWEylpZudQ+xkJPN34tRzPJK6dOBYEnIT/j6+1m3j39m1WPDehyYxIf1wCsrGKBxNQ==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.5.0", + "geckodriver": "^4.3.1", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" + }, + "engines": { + "node": "^16.13 || >=18" } }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "node_modules/@wdio/runner/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "optional": true, "dependencies": { - "@types/node": "*" + "balanced-match": "^1.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@vitest/snapshot": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.0.tgz", - "integrity": "sha512-swmktcviVVPYx9U4SEQXLV6AEY51Y6bZ14jA2yo6TgMxQ3h+ZYiO0YhAHGJNp0ohCFbPAis1R9kK0cvN6lDPQA==", + "node_modules/@wdio/runner/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@wdio/cli": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.32.2.tgz", - "integrity": "sha512-CbTALXXnDzvthu+EK0dK5QDTXToU4wNrldtonQcsD8tT726O56BLFGm9tzcGP6PZWh9NxAuvsFlWSwUxcqXq0Q==", + "node_modules/@wdio/runner/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "@types/node": "^20.1.1", - "@vitest/snapshot": "^1.2.1", - "@wdio/config": "8.32.2", - "@wdio/globals": "8.32.2", - "@wdio/logger": "8.28.0", - "@wdio/protocols": "8.32.0", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "async-exit-hook": "^2.0.1", - "chalk": "^5.2.0", - "chokidar": "^3.5.3", - "cli-spinners": "^2.9.0", - "dotenv": "^16.3.1", - "ejs": "^3.1.9", - "execa": "^8.0.1", - "import-meta-resolve": "^4.0.0", - "inquirer": "9.2.12", - "lodash.flattendeep": "^4.4.0", - "lodash.pickby": "^4.6.0", - "lodash.union": "^4.6.0", - "read-pkg-up": "10.0.0", - "recursive-readdir": "^2.2.3", - "webdriverio": "8.32.2", - "yargs": "^17.7.2" - }, - "bin": { - "wdio": "bin/wdio.js" + "ms": "2.1.2" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@wdio/cli/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/@wdio/runner/node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=16.0.0" } }, - "node_modules/@wdio/config": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.32.2.tgz", - "integrity": "sha512-ubqe4X+TgcERzXKIpMfisquNxPZNtRU5uPeV7hvas++mD75QyNpmWHCtea2+TjoXKxlZd1MVrtZAwtmqMmyhPw==", + "node_modules/@wdio/runner/node_modules/expect-webdriverio": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.15.4.tgz", + "integrity": "sha512-Op1xZoevlv1pohCq7g2Og5Gr3xP2NhY7MQueOApmopVxgweoJ/BqJxyvMNP0A//QsMg8v0WsN/1j81Sx2er9Wg==", "dev": true, "dependencies": { - "@wdio/logger": "8.28.0", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "decamelize": "^6.0.0", - "deepmerge-ts": "^5.0.0", - "glob": "^10.2.2", - "import-meta-resolve": "^4.0.0" + "@vitest/snapshot": "^2.0.3", + "expect": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "lodash.isequal": "^4.5.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=16 || >=18 || >=20" + }, + "optionalDependencies": { + "@wdio/globals": "^8.29.3", + "@wdio/logger": "^8.28.0", + "webdriverio": "^8.29.3" } }, - "node_modules/@wdio/globals": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.32.2.tgz", - "integrity": "sha512-WEN7Tq0+Ny8OHS+7e1RCu4ss3lYG2Ln8/TpicacTsYWM3jtrf1SkUT+4H8JtG1qywj4WXTH8CxD4H9zFoofiJw==", + "node_modules/@wdio/runner/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { - "node": "^16.13 || >=18" - }, - "optionalDependencies": { - "expect-webdriverio": "^4.11.2", - "webdriverio": "8.32.2" + "node": ">=12" } }, - "node_modules/@wdio/local-runner": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.32.2.tgz", - "integrity": "sha512-iCVnwBIIwgRqiF2Fz/XVlGf3ejHOzR2+kc3dsdMgKljLkVq4ZDFwPMtw8Hk06sq+BaiNLamdxj+8ElD6OGJ88A==", + "node_modules/@wdio/runner/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "@types/node": "^20.1.0", - "@wdio/logger": "8.28.0", - "@wdio/repl": "8.24.12", - "@wdio/runner": "8.32.2", - "@wdio/types": "8.32.2", - "async-exit-hook": "^2.0.1", - "split2": "^4.1.0", - "stream-buffers": "^3.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@wdio/logger": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", - "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", + "node_modules/@wdio/runner/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@wdio/runner/node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, "dependencies": { - "chalk": "^5.1.2", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^7.1.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" }, "engines": { - "node": "^16.13 || >=18" + "node": ">= 14" } }, - "node_modules/@wdio/logger/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/@wdio/runner/node_modules/puppeteer-core": { + "version": "21.11.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", + "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "dependencies": { + "@puppeteer/browsers": "1.9.1", + "chromium-bidi": "0.5.8", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1232444", + "ws": "8.16.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=16.13.2" } }, - "node_modules/@wdio/mocha-framework": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.32.2.tgz", - "integrity": "sha512-76DA95u0Z2AfyZSQglJn9BFJ+XveRR6+oH1K/J8rDOWoIHgMcASGEj+fsXAPSDNcSVDBe0QN5HLnVi3tciuyQw==", + "node_modules/@wdio/runner/node_modules/puppeteer-core/node_modules/chromium-bidi": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", + "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", "dev": true, "dependencies": { - "@types/mocha": "^10.0.0", - "@types/node": "^20.1.0", - "@wdio/logger": "8.28.0", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "mocha": "^10.0.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0" }, - "engines": { - "node": "^16.13 || >=18" + "peerDependencies": { + "devtools-protocol": "*" } }, - "node_modules/@wdio/protocols": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.32.0.tgz", - "integrity": "sha512-inLJRrtIGdTz/YPbcsvpSvPlYQFTVtF3OYBwAXhG2FiP1ZwE1CQNLP/xgRGye1ymdGCypGkexRqIx3KBGm801Q==", + "node_modules/@wdio/runner/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1232444", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", + "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", "dev": true }, - "node_modules/@wdio/repl": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", - "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", + "node_modules/@wdio/runner/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, "dependencies": { - "@types/node": "^20.1.0" - }, - "engines": { - "node": "^16.13 || >=18" + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" } }, - "node_modules/@wdio/reporter": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.32.2.tgz", - "integrity": "sha512-BZdReAFfRCtgtYbyhkKgSGqqoIn/yG5/Z4COjvRvon9NJkz+eA4PiHCKdEP7+ekBIjud7H8Gy+6mPBDEu+wllw==", + "node_modules/@wdio/runner/node_modules/webdriverio": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.40.6.tgz", + "integrity": "sha512-hMFYRjVU5Nnk2e9Mi8kDx/IVFMWGaVyDCDpv/SeXXCP17DT9jAZtOWlwGhRaLVikN5JYYuHavHyatVa7gj6QTg==", "dev": true, "dependencies": { - "@types/node": "^20.1.0", - "@wdio/logger": "8.28.0", - "@wdio/types": "8.32.2", - "diff": "^5.0.0", - "object-inspect": "^1.12.0" + "@types/node": "^22.2.0", + "@wdio/config": "8.40.6", + "@wdio/logger": "8.38.0", + "@wdio/protocols": "8.40.3", + "@wdio/repl": "8.40.3", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "archiver": "^7.0.0", + "aria-query": "^5.0.0", + "css-shorthand-properties": "^1.1.1", + "css-value": "^0.0.1", + "devtools-protocol": "^0.0.1359167", + "grapheme-splitter": "^1.0.2", + "import-meta-resolve": "^4.0.0", + "is-plain-obj": "^4.1.0", + "jszip": "^3.10.1", + "lodash.clonedeep": "^4.5.0", + "lodash.zip": "^4.2.0", + "minimatch": "^9.0.0", + "puppeteer-core": "^21.11.0", + "query-selector-shadow-dom": "^1.0.0", + "resq": "^1.9.1", + "rgb2hex": "0.2.5", + "serialize-error": "^11.0.1", + "webdriver": "8.40.6" }, "engines": { "node": "^16.13 || >=18" + }, + "peerDependencies": { + "devtools": "^8.14.0" + }, + "peerDependenciesMeta": { + "devtools": { + "optional": true + } } }, - "node_modules/@wdio/runner": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.32.2.tgz", - "integrity": "sha512-/24wPBg2okkfpA1bl7mCWIvWXO3pa9OhsKNav+gGm7BP+hQ1lxULyYg/o5fCEwEjFPWDLy0jjknJLqXcTWvmiQ==", + "node_modules/@wdio/runner/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, - "dependencies": { - "@types/node": "^20.1.0", - "@wdio/config": "8.32.2", - "@wdio/globals": "8.32.2", - "@wdio/logger": "8.28.0", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "deepmerge-ts": "^5.0.0", - "expect-webdriverio": "^4.11.2", - "gaze": "^1.1.2", - "webdriver": "8.32.2", - "webdriverio": "8.32.2" - }, "engines": { - "node": "^16.13 || >=18" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/@wdio/spec-reporter": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.32.2.tgz", - "integrity": "sha512-3hUXpE+idC4KOXofJnpubdDDCE8X0OTd6ykypiaXMI2hJTA2nIZcHtpRQnAE0E4JT9OzLnPWODcMq54GGBDRkg==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.1.3.tgz", + "integrity": "sha512-N5GsZpDcjfJ9otmxD8q1Kc7PK5/P4Y3B+Aj51FyvYseMPbsOzUuwsKUQJSQu/IhgrDU3UjZQydr8UBU/Gg6a9w==", "dev": true, "dependencies": { - "@wdio/reporter": "8.32.2", - "@wdio/types": "8.32.2", + "@wdio/reporter": "9.1.3", + "@wdio/types": "9.1.3", "chalk": "^5.1.2", "easy-table": "^1.2.0", - "pretty-ms": "^7.0.0" + "pretty-ms": "^9.0.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" } }, "node_modules/@wdio/spec-reporter/node_modules/chalk": { @@ -2562,45 +3406,68 @@ } }, "node_modules/@wdio/types": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.32.2.tgz", - "integrity": "sha512-jq8LcBBQpBP9ZF5kECKEpXv8hN7irCGCjLFAN0Bd5ScRR6qu6MGWvwkDkau2sFPr0b++sKDCEaMzQlwrGFjZXg==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.1.3.tgz", + "integrity": "sha512-oQrzLQBqn/+HXSJJo01NEfeKhzwuDdic7L8PDNxv5ySKezvmLDYVboQfoSDRtpAdfAZCcxuU9L4Jw7iTf6WV3g==", "dev": true, "dependencies": { "@types/node": "^20.1.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" } }, "node_modules/@wdio/utils": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.32.2.tgz", - "integrity": "sha512-PJcP4d1Fr8Zp+YIfGN93G0fjDj/6J0I6Gf6p0IpJk8qKQpdFDm4gB+lc202iv2YkyC+oT6b4Ik2W9LzvpSKNoQ==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.1.3.tgz", + "integrity": "sha512-dYeOzq9MTh8jYRZhzo/DYyn+cKrhw7h0/5hgyXkbyk/wHwF/uLjhATPmfaCr9+MARSEdiF7wwU8iRy/V0jfsLg==", "dev": true, "dependencies": { - "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.28.0", - "@wdio/types": "8.32.2", + "@puppeteer/browsers": "^2.2.0", + "@wdio/logger": "9.1.3", + "@wdio/types": "9.1.3", "decamelize": "^6.0.0", - "deepmerge-ts": "^5.1.0", - "edgedriver": "^5.3.5", - "geckodriver": "^4.3.1", + "deepmerge-ts": "^7.0.3", + "edgedriver": "^5.6.1", + "geckodriver": "^4.3.3", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", - "locate-app": "^2.1.0", - "safaridriver": "^0.1.0", + "locate-app": "^2.2.24", + "safaridriver": "^0.1.2", "split2": "^4.2.0", - "wait-port": "^1.0.4" + "wait-port": "^1.1.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" + } + }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.52", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz", + "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==", + "dev": true, + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2619,9 +3486,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -2647,9 +3514,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -2705,78 +3572,51 @@ } }, "node_modules/archiver": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", - "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "dev": true, "dependencies": { - "archiver-utils": "^4.0.1", + "archiver-utils": "^5.0.2", "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", - "zip-stream": "^5.0.1" + "zip-stream": "^6.0.1" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14" } }, "node_modules/archiver-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", - "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "dev": true, "dependencies": { - "glob": "^8.0.0", + "glob": "^10.0.0", "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14" } }, - "node_modules/archiver-utils/node_modules/brace-expansion": { + "node_modules/archiver-utils/node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/argparse": { @@ -2786,12 +3626,12 @@ "dev": true }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "dependencies": { - "dequal": "^2.0.3" + "engines": { + "node": ">= 0.4" } }, "node_modules/array-buffer-byte-length": { @@ -2811,34 +3651,16 @@ } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -2849,15 +3671,16 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" }, "engines": { @@ -2950,9 +3773,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, "node_modules/async-exit-hook": { @@ -2965,10 +3788,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -2977,19 +3803,19 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", "dev": true }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", - "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.5.0", + "@babel/helper-define-polyfill-provider": "^0.6.2", "semver": "^6.3.1" }, "peerDependencies": { @@ -2997,25 +3823,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -3028,12 +3854,51 @@ "dev": true }, "node_modules/bare-events": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", - "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, "optional": true }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.1.tgz", + "integrity": "sha512-Vm8kAeOcfzHPTH8sq0tHBnUqYrkXdroaBVVylqFT4cF5wnMfKEIxxy2jIGu2zKVNl9P8MAP9XBWwXJ9N2+jfEw==", + "dev": true, + "optional": true, + "dependencies": { + "streamx": "^2.20.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3064,85 +3929,25 @@ } }, "node_modules/basic-ftp": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.4.tgz", - "integrity": "sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "dev": true, "engines": { "node": ">=10.0.0" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dev": true, - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "dev": true + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/bn.js": { "version": "4.12.0", @@ -3150,6 +3955,12 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3179,9 +3990,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -3198,10 +4009,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3235,12 +4046,12 @@ } }, "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "dev": true, "engines": { - "node": "*" + "node": ">=8.0.0" } }, "node_modules/buffer-from": { @@ -3249,24 +4060,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "dev": true, - "engines": { - "node": ">=0.2.0" - } - }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -3280,34 +4073,19 @@ } }, "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "dependencies": { "semver": "^7.0.0" } }, - "node_modules/builtins/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/builtins/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -3315,12 +4093,6 @@ "node": ">=10" } }, - "node_modules/builtins/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -3401,9 +4173,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001588", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", - "integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==", + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", "dev": true, "funding": [ { @@ -3420,18 +4192,6 @@ } ] }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dev": true, - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3452,37 +4212,74 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", + "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "mitt": "3.0.0" + "mitt": "3.0.1", + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" @@ -3504,63 +4301,66 @@ } }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.2.0.tgz", + "integrity": "sha512-pXftdQloMZzjCr3pCTIRniDcys6dDzgpgVhAHHk6TKBDbRuP1MkuetTF5KSv4YUutbOPa7+7ZrAJ2kVtbMqyXA==", "dev": true, "engines": { - "node": ">=6" + "node": ">=18.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "string-width": "^7.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3622,6 +4422,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3656,6 +4485,7 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "optional": true, "engines": { "node": ">=0.8" } @@ -3696,12 +4526,12 @@ "dev": true }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/commondir": { @@ -3711,18 +4541,31 @@ "dev": true }, "node_modules/compress-commons": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", - "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "dev": true, "dependencies": { "crc-32": "^1.2.0", - "crc32-stream": "^5.0.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/concat-map": { @@ -3738,12 +4581,12 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "dev": true, "dependencies": { - "browserslist": "^4.22.3" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -3769,16 +4612,16 @@ } }, "node_modules/crc32-stream": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", - "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "dev": true, "dependencies": { "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14" } }, "node_modules/cross-fetch": { @@ -3845,10 +4688,26 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/css-shorthand-properties": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz", - "integrity": "sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-shorthand-properties/-/css-shorthand-properties-1.1.2.tgz", + "integrity": "sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==", "dev": true }, "node_modules/css-value": { @@ -3857,6 +4716,18 @@ "integrity": "sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==", "dev": true }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -3866,13 +4737,64 @@ "node": ">= 12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3929,9 +4851,9 @@ "dev": true }, "node_modules/deepmerge-ts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", - "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.3.tgz", + "integrity": "sha512-qCSH6I0INPxd9Y1VtAiLpnYvz5O//6rCfJXKk0z66Up9/VOSr+1yS8XSKA5IWRxjocFGlzPyaZYe+jxq7OOLtQ==", "dev": true, "engines": { "node": ">=16.0.0" @@ -3942,6 +4864,7 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "optional": true, "dependencies": { "clone": "^1.0.2" }, @@ -4006,25 +4929,16 @@ "node": ">= 14" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/devtools-protocol": { - "version": "0.0.1261483", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1261483.tgz", - "integrity": "sha512-7vJvejpzA5DTfZVkr7a8sGpEAzEiAqcgmRTB0LSUrWeOicwL09lMQTzxHtFNVhJ1OOJkgYdH6Txvy9E5j3VOUQ==", + "version": "0.0.1359167", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1359167.tgz", + "integrity": "sha512-f/9PeTaSH3weS/WAwrQb5/s9R3KMOeTGe+Jkhg5952yInub7iDPjdlzRdrDgpLZfxHbTrBuG9aUkAMM+ocVkXQ==", "dev": true }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "engines": { "node": ">=0.3.1" @@ -4051,61 +4965,71 @@ "node": ">=6.0.0" } }, - "node_modules/dotenv": { - "version": "16.4.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz", - "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, "funding": { - "url": "https://dotenvx.com" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexer2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/eastasianwidth": { @@ -4164,23 +5088,51 @@ } }, "node_modules/edgedriver": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.10.tgz", - "integrity": "sha512-RFSHYMNtcF1PjaGZCA2rdQQ8hSTLPZgcYgeY1V6dC+tR4NhZXwFAku+8hCbRYh7ZlwKKrTbVu9FwknjFddIuuw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.6.1.tgz", + "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@wdio/logger": "^8.28.0", + "@wdio/logger": "^8.38.0", + "@zip.js/zip.js": "^2.7.48", "decamelize": "^6.0.0", "edge-paths": "^3.0.5", + "fast-xml-parser": "^4.4.1", "node-fetch": "^3.3.2", - "unzipper": "^0.10.14", "which": "^4.0.0" }, "bin": { "edgedriver": "bin/edgedriver.js" } }, + "node_modules/edgedriver/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", + "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/edgedriver/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -4197,17 +5149,42 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.673", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz", - "integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==", + "version": "1.5.41", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", + "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", "dev": true }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4217,6 +5194,30 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4227,18 +5228,22 @@ } }, "node_modules/es-abstract": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", - "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.6", + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", @@ -4246,15 +5251,16 @@ "globalthis": "^1.0.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", @@ -4262,17 +5268,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.1", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -4281,12 +5287,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -4308,15 +5308,27 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -4354,10 +5366,49 @@ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -4394,16 +5445,17 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4479,10 +5531,13 @@ "dev": true }, "node_modules/eslint-compat-utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", - "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { "node": ">=12" }, @@ -4490,6 +5545,18 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-config-standard": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", @@ -4540,9 +5607,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -4585,54 +5652,57 @@ } }, "node_modules/eslint-plugin-es-x": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", - "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.1.2" + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, "peerDependencies": { "eslint": ">=8" } }, "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "node_modules/eslint-plugin-import/node_modules/debug": { @@ -4657,16 +5727,16 @@ } }, "node_modules/eslint-plugin-json": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz", - "integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-4.0.1.tgz", + "integrity": "sha512-3An5ISV5dq/kHfXdNyY5TUe2ONC3yXFSkLX2gu+W8xAhKhfvrRvkSAeKXCxZqZ0KJLX15ojBuLPyj+UikQMkOA==", "dev": true, "dependencies": { "lodash": "^4.17.21", "vscode-json-languageservice": "^4.1.6" }, "engines": { - "node": ">=12.0" + "node": ">=18.0" } }, "node_modules/eslint-plugin-n": { @@ -4712,26 +5782,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-n/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4751,12 +5806,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-n/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -4778,15 +5827,18 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/eslint-scope": { @@ -4902,18 +5954,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/eslint/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -5005,9 +6045,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -5046,30 +6086,51 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.4.1.tgz", + "integrity": "sha512-5eo/BRqZm3GYce+1jqX/tJ7duA2AnE39i88fuedNFUV8XxGxUpF3aWkBRfbUcjV49gCkvS/pzc0YrCPhaIewdg==", "dev": true, "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" }, "engines": { - "node": ">=16.17" + "node": "^18.19.0 || >=20.5.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" @@ -5092,23 +6153,35 @@ } }, "node_modules/expect-webdriverio": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.11.8.tgz", - "integrity": "sha512-BRezX8yEWZN7H5eYde/4EeDbVu8HRWq7VKyRrx4s2RnMa23wF+axZC83a4F/eezP6Kek700n3dA3Ph8RSE6GAQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.0.3.tgz", + "integrity": "sha512-0RHsFZX1856qCWZsXcvacFZpdZc7UAVD9wAglzf3KMWO1AoXt5EorjsNp1H9StGysxhJuVXJxRWKeXnD4LKtjQ==", "dev": true, + "optional": true, "dependencies": { - "@vitest/snapshot": "^1.2.2", + "@vitest/snapshot": "^2.0.5", "expect": "^29.7.0", "jest-matcher-utils": "^29.7.0", "lodash.isequal": "^4.5.0" }, "engines": { - "node": ">=16 || >=18 || >=20" + "node": ">=18 || >=20 || >=22" }, - "optionalDependencies": { - "@wdio/globals": "^8.29.3", - "@wdio/logger": "^8.28.0", - "webdriverio": "^8.29.3" + "peerDependencies": { + "@wdio/globals": "^9.0.0", + "@wdio/logger": "^9.0.0", + "webdriverio": "^9.0.0" + }, + "peerDependenciesMeta": { + "@wdio/globals": { + "optional": false + }, + "@wdio/logger": { + "optional": false + }, + "webdriverio": { + "optional": false + } } }, "node_modules/external-editor": { @@ -5184,6 +6257,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -5226,28 +6321,15 @@ } }, "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" + "is-unicode-supported": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5361,9 +6443,9 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/for-each": { @@ -5376,9 +6458,9 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.0", @@ -5446,53 +6528,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5542,19 +6577,19 @@ } }, "node_modules/geckodriver": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.2.tgz", - "integrity": "sha512-TNOoy+ULXJWI5XOq7CXD3PAD9TJa4NjMe7nKUXjlIsf+vezuaRsFgPwcgYdEem1K7106wabYsqr7Kqn51g0sJg==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.5.1.tgz", + "integrity": "sha512-lGCRqPMuzbRNDWJOQcUqhNqPvNsIFu6yzXF8J/6K3WCYFd2r5ckbeF7h1cxsnjA7YLSEiWzERCt6/gjZ3tW0ug==", "dev": true, "hasInstallScript": true, "dependencies": { - "@wdio/logger": "^8.28.0", + "@wdio/logger": "^9.0.0", + "@zip.js/zip.js": "^2.7.48", "decamelize": "^6.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", "node-fetch": "^3.3.2", - "tar-fs": "^3.0.4", - "unzipper": "^0.10.14", + "tar-fs": "^3.0.6", "which": "^4.0.0" }, "bin": { @@ -5579,7 +6614,19 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-intrinsic": { @@ -5602,9 +6649,9 @@ } }, "node_modules/get-port": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.0.0.tgz", - "integrity": "sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", + "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true, "engines": { "node": ">=16" @@ -5614,12 +6661,16 @@ } }, "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5643,9 +6694,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -5679,37 +6730,35 @@ } }, "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/glob/node_modules/brace-expansion": { @@ -5722,9 +6771,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5746,12 +6795,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5778,6 +6828,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -5904,9 +6955,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -5943,9 +6994,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -5964,9 +7015,9 @@ } }, "node_modules/hosted-git-info": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, "dependencies": { "lru-cache": "^10.0.1" @@ -5976,12 +7027,34 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/htmlfy": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.3.2.tgz", + "integrity": "sha512-FsxzfpeDYRqn1emox9VpxMPfGjADoUmmup8D604q497R0VNxiXs4ZZTN2QzkaMA5C9aHGUoe1iQRVSm+HK9xuA==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, - "engines": { - "node": "14 || >=16.14" + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, "node_modules/http-cache-semantics": { @@ -6017,9 +7090,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -6030,12 +7103,12 @@ } }, "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, "engines": { - "node": ">=16.17.0" + "node": ">=18.18.0" } }, "node_modules/iconv-lite": { @@ -6071,14 +7144,20 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6096,9 +7175,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, "funding": { "type": "github", @@ -6118,6 +7197,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -6131,53 +7211,22 @@ "dev": true }, "node_modules/inquirer": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.12.tgz", - "integrity": "sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-11.1.0.tgz", + "integrity": "sha512-CmLAZT65GG/v30c+D2Fk8+ceP6pxD6RL+hIUOWAltCmeyEqWYwqu9v76q03OvjyZ3AB0C1Ala2stn1z/rMqGEw==", "dev": true, "dependencies": { - "@ljharb/through": "^2.3.11", + "@inquirer/core": "^9.2.1", + "@inquirer/prompts": "^6.0.1", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^5.0.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", + "mute-stream": "^1.0.0", "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" + "rxjs": "^7.8.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/internal-slot": { @@ -6297,12 +7346,30 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6356,19 +7423,10 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -6451,24 +7509,27 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6520,12 +7581,12 @@ } }, "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6568,16 +7629,13 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -6586,9 +7644,9 @@ } }, "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "dependencies": { "async": "^3.2.3", @@ -7054,15 +8112,15 @@ "dev": true }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -7072,9 +8130,9 @@ "dev": true }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", - "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -7114,9 +8172,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true }, "node_modules/jsonfile": { @@ -7140,6 +8198,60 @@ "url": "https://github.com/kjur/jsrsasign#donations" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7231,13 +8343,25 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -7250,123 +8374,169 @@ } }, "node_modules/lint-staged": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz", - "integrity": "sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==", - "dev": true, - "dependencies": { - "chalk": "5.3.0", - "commander": "11.0.0", - "debug": "4.3.4", - "execa": "7.2.0", - "lilconfig": "2.1.0", - "listr2": "6.6.1", - "micromatch": "4.0.5", - "pidtree": "0.6.0", - "string-argv": "0.3.2", - "yaml": "2.3.1" + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "dev": true, + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/chalk": { + "node_modules/lint-staged/node_modules/npm-run-path": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=12" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "dev": true - }, "node_modules/listr2": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", - "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "dependencies": { - "cli-truncate": "^3.1.0", + "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", - "log-update": "^5.0.1", - "rfdc": "^1.3.0", - "wrap-ansi": "^8.1.0" + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "node": ">=18.0.0" } }, "node_modules/listr2/node_modules/ansi-styles": { @@ -7382,40 +8552,40 @@ } }, "node_modules/listr2/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/listr2/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/listr2/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -7445,13 +8615,71 @@ "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", "dev": true }, + "node_modules/livereload/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/livereload/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/livereload/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/locate-app": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.2.19.tgz", - "integrity": "sha512-mjhvrYRHnLAVwreShl8NTwq9EUyfRoCqB0UsOlMKXo2KBmtb4dhlHbZH4mcfDsoNoLkHZ1Rq4TsWP/59Ix62Ww==", + "version": "2.4.43", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.4.43.tgz", + "integrity": "sha512-BX6NEdECUGcDQw8aqqg02qLyF9rF8V+dAfyAnBzL2AofIlIvf4Q6EGXnzVWpWot9uBE+x/o8CjXHo7Zlegu91Q==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/locate-app/blob/main/README.md#%EF%B8%8F-contributing" + } + ], "dependencies": { - "n12": "1.8.22", + "@promptbook/utils": "0.70.0-1", "type-fest": "2.13.0", "userhome": "1.0.0" } @@ -7636,34 +8864,34 @@ } }, "node_modules/log-update": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", - "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "dependencies": { - "ansi-escapes": "^5.0.0", - "cli-cursor": "^4.0.0", - "slice-ansi": "^5.0.0", - "strip-ansi": "^7.0.1", - "wrap-ansi": "^8.0.1" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update/node_modules/ansi-escapes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, "dependencies": { - "type-fest": "^1.0.2" + "environment": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -7681,123 +8909,81 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/log-update/node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/log-update/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, - "node_modules/log-update/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/log-update/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/log-update/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/log-update/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-update/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, "engines": { "node": ">= 0.6.0" @@ -7841,15 +9027,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-dir": { @@ -7881,12 +9064,12 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -7905,6 +9088,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", @@ -7945,32 +9140,20 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -7978,31 +9161,31 @@ "dev": true }, "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", "dev": true, "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -8037,16 +9220,10 @@ } }, "node_modules/mocha/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8059,6 +9236,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -8093,14 +9273,20 @@ "dev": true }, "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -8117,6 +9303,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -8126,10 +9313,22 @@ "once": "^1.3.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/mocha/node_modules/has-flag": { @@ -8141,10 +9340,19 @@ "node": ">=8" } }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -8153,11 +9361,31 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/mocha/node_modules/strip-ansi": { "version": "6.0.1", @@ -8222,9 +9450,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/mute-stream": { @@ -8236,12 +9464,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/n12": { - "version": "1.8.22", - "resolved": "https://registry.npmjs.org/n12/-/n12-1.8.22.tgz", - "integrity": "sha512-nzPCOuLOIoUuninAMRXfrbkB7O9XkWS7iv7fzDW1pRUaQhMpatj8iX55evwcNRWnm0UF29uuoHpwubYbsV7OGw==", - "dev": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8320,20 +9542,32 @@ "uuid": "^9.0.0" } }, + "node_modules/node-jose/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "dependencies": { "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, @@ -8341,26 +9575,11 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -8368,12 +9587,6 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -8384,9 +9597,9 @@ } }, "node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, "engines": { "node": ">=14.16" @@ -8396,15 +9609,16 @@ } }, "node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "dependencies": { - "path-key": "^4.0.0" + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8422,11 +9636,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8459,14 +9688,15 @@ } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8476,27 +9706,28 @@ } }, "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8515,32 +9746,32 @@ } }, "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -8552,123 +9783,6 @@ "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", "dev": true }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -8727,9 +9841,9 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", "dev": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", @@ -8737,9 +9851,9 @@ "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" }, "engines": { "node": ">= 14" @@ -8758,6 +9872,12 @@ "node": ">= 14" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -8800,20 +9920,60 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, - "engines": { - "node": ">=14.16" + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", + "dev": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/path-exists": { @@ -8850,29 +10010,26 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/pathe": { "version": "1.1.2", @@ -8887,9 +10044,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { @@ -9007,6 +10164,15 @@ "node": ">=4" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9017,9 +10183,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -9058,15 +10224,15 @@ } }, "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.1.0.tgz", + "integrity": "sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==", "dev": true, "dependencies": { - "parse-ms": "^2.1.0" + "parse-ms": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9097,15 +10263,15 @@ } }, "node_modules/proxy-agent": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", - "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", "dev": true, "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", @@ -9131,9 +10297,9 @@ "dev": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "dependencies": { "end-of-stream": "^1.1.0", @@ -9150,98 +10316,76 @@ } }, "node_modules/puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "version": "22.15.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", + "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" + "@puppeteer/browsers": "2.3.0", + "chromium-bidi": "0.6.3", + "debug": "^4.3.6", + "devtools-protocol": "0.0.1312386", + "ws": "^8.18.0" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", + "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", "dev": true, - "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.3.5", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", - "dev": true - }, - "node_modules/puppeteer-core/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "0.0.1312386", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", + "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", "dev": true, - "engines": { - "node": ">=12" - } + "optional": true, + "peer": true }, - "node_modules/puppeteer-core/node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "node_modules/puppeteer-core/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 14" + "node": ">=10" } }, "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -9258,33 +10402,6 @@ } } }, - "node_modules/puppeteer-core/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/puppeteer-core/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/query-selector-shadow-dom": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", @@ -9339,9 +10456,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/read-pkg": { @@ -9363,14 +10480,14 @@ } }, "node_modules/read-pkg-up": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.0.0.tgz", - "integrity": "sha512-jgmKiS//w2Zs+YbX039CorlkOp8FIVbSAN8r8GJHDsGlmNPXo+VeHkqAwCiQVTTx5/LwLZTcEw59z3DvcLbr0g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", + "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", "dev": true, "dependencies": { "find-up": "^6.3.0", - "read-pkg": "^8.0.0", - "type-fest": "^3.12.0" + "read-pkg": "^8.1.0", + "type-fest": "^4.2.0" }, "engines": { "node": ">=16" @@ -9450,21 +10567,21 @@ } }, "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", "dev": true, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/read-pkg-up/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "engines": { "node": ">=12.20" @@ -9474,9 +10591,9 @@ } }, "node_modules/read-pkg/node_modules/type-fest": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz", - "integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==", + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", "dev": true, "engines": { "node": ">=16" @@ -9486,17 +10603,19 @@ } }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/readdir-glob": { @@ -9530,15 +10649,16 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/recursive-readdir": { @@ -9560,9 +10680,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -9587,15 +10707,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9617,15 +10737,15 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", "dev": true, "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -9633,27 +10753,24 @@ "node": ">=4" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", + "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", "dev": true, "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9735,48 +10852,21 @@ "dev": true }, "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -9788,9 +10878,9 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rgb2hex": { @@ -9803,6 +10893,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "dependencies": { "glob": "^7.1.3" @@ -9818,6 +10909,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -9882,13 +10974,13 @@ "dev": true }, "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -9979,40 +11071,41 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -10058,12 +11151,12 @@ } }, "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -10135,9 +11228,9 @@ } }, "node_modules/socks": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", - "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "dependencies": { "ip-address": "^9.0.5", @@ -10149,14 +11242,14 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", - "socks": "^2.7.1" + "socks": "^2.8.3" }, "engines": { "node": ">= 14" @@ -10181,6 +11274,22 @@ "source-map": "^0.6.0" } }, + "node_modules/spacetrim": { + "version": "0.11.39", + "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.39.tgz", + "integrity": "sha512-S/baW29azJ7py5ausQRE2S6uEDQnlxgMHOEEq4V770ooBDD1/9kZnxRcco/tjZYuDuqYXblCk/r3N13ZmvHZ2g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://buymeacoffee.com/hejny" + }, + { + "type": "github", + "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing" + } + ] + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -10208,9 +11317,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/split2": { @@ -10250,22 +11359,23 @@ } }, "node_modules/stream-buffers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", - "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", + "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", "dev": true, "engines": { "node": ">= 0.10.0" } }, "node_modules/streamx": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.0.tgz", - "integrity": "sha512-a7Fi0PoUeusrUcMS4+HxivnZqYsw2MFEP841TIyLxTcEIucHcJsk+0ARcq3tGq1xDn+xK7sKHetvfMzI1/CzMA==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "dev": true, "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" @@ -10290,17 +11400,20 @@ } }, "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { @@ -10318,6 +11431,12 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -10339,36 +11458,16 @@ "node": ">=8" } }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -10378,28 +11477,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10434,9 +11536,9 @@ } }, "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -10455,12 +11557,12 @@ } }, "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10478,6 +11580,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -10503,14 +11611,17 @@ } }, "node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", "dev": true, "dependencies": { - "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" } }, "node_modules/tar-stream": { @@ -10524,6 +11635,12 @@ "streamx": "^2.15.0" } }, + "node_modules/text-decoder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "dev": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -10536,6 +11653,15 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -10575,15 +11701,6 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -10609,11 +11726,30 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", "dev": true }, + "node_modules/tsx": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", + "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10639,12 +11775,12 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", - "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "is-typed-array": "^1.1.13" }, @@ -10653,15 +11789,16 @@ } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -10671,16 +11808,16 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.1.tgz", - "integrity": "sha512-tcqKMrTRXjqvHN9S3553NPCaGL0VPgFI92lXszmrE8DMhiDPLBYLlvo8Uu4WZAAX/aGqp/T1sbA4ph8EWjDF9Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "is-typed-array": "^1.1.13" }, "engines": { @@ -10691,23 +11828,29 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10766,16 +11909,25 @@ "ieee754": "^1.1.13" } }, + "node_modules/undici": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz", + "integrity": "sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA==", + "dev": true, + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "engines": { "node": ">=4" @@ -10795,9 +11947,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, "engines": { "node": ">=4" @@ -10812,6 +11964,18 @@ "node": ">=4" } }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -10821,64 +11985,10 @@ "node": ">= 10.0.0" } }, - "node_modules/unzipper": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", - "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, - "node_modules/unzipper/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/unzipper/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/unzipper/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/unzipper/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -10895,8 +12005,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -10914,6 +12024,12 @@ "punycode": "^2.1.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true + }, "node_modules/userhome": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz", @@ -10930,9 +12046,9 @@ "dev": true }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -10966,9 +12082,9 @@ } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", "dev": true }, "node_modules/vscode-languageserver-types": { @@ -11085,50 +12201,239 @@ "node": ">=8" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/webdriver": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.40.6.tgz", + "integrity": "sha512-jkslwUvOmqhFfc1E21Tz48NgYD8ykiR+09iWZlVLtx3P43k4jOfS+CfasvQ+6hJiVck+N5dXjYfg6zDjpkIFRw==", + "dev": true, + "dependencies": { + "@types/node": "^22.2.0", + "@types/ws": "^8.5.3", + "@wdio/config": "8.40.6", + "@wdio/logger": "8.38.0", + "@wdio/protocols": "8.40.3", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "deepmerge-ts": "^5.1.0", + "got": "^12.6.1", + "ky": "^0.33.0", + "ws": "^8.8.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/@puppeteer/browsers": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", + "dev": true, + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.1", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/webdriver/node_modules/@types/node": { + "version": "22.7.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", + "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/webdriver/node_modules/@wdio/config": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.40.6.tgz", + "integrity": "sha512-rHCSmrhdJf7FlidcQPDvRKRPLYjklbrdxQa6J20BxHifTO4h2v23Wrq4OqqYIcq23gf9LpZvCA/PAMiET/QdVg==", + "dev": true, + "dependencies": { + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "@wdio/utils": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/@wdio/logger": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", + "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", + "dev": true, + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/@wdio/protocols": { + "version": "8.40.3", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", + "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", + "dev": true + }, + "node_modules/webdriver/node_modules/@wdio/types": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", + "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", + "dev": true, + "dependencies": { + "@types/node": "^22.2.0" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/@wdio/utils": { + "version": "8.40.6", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.40.6.tgz", + "integrity": "sha512-+TWfV6h+4f8gs7QiYUAWbWEylpZudQ+xkJPN34tRzPJK6dOBYEnIT/j6+1m3j39m1WPDehyYxIf1wCsrGKBxNQ==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "^1.6.0", + "@wdio/logger": "8.38.0", + "@wdio/types": "8.40.6", + "decamelize": "^6.0.0", + "deepmerge-ts": "^5.1.0", + "edgedriver": "^5.5.0", + "geckodriver": "^4.3.1", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.1.0", + "safaridriver": "^0.1.0", + "split2": "^4.2.0", + "wait-port": "^1.0.4" + }, + "engines": { + "node": "^16.13 || >=18" + } + }, + "node_modules/webdriver/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/webdriver/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/webdriver/node_modules/deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true, - "dependencies": { - "defaults": "^1.0.3" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "node_modules/webdriver/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/webdriver": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.32.2.tgz", - "integrity": "sha512-uyCT2QzCqoz+EsMLTApG5/+RvHJR9MVbdEnjMoxpJDt+IeZCG2Vy/Gq9oNgNQfpxrvZme/EY+PtBsltZi7BAyg==", + "node_modules/webdriver/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/webdriver/node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, "dependencies": { - "@types/node": "^20.1.0", - "@types/ws": "^8.5.3", - "@wdio/config": "8.32.2", - "@wdio/logger": "8.28.0", - "@wdio/protocols": "8.32.0", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "deepmerge-ts": "^5.1.0", - "got": "^12.6.1", - "ky": "^0.33.0", - "ws": "^8.8.0" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" }, "engines": { - "node": "^16.13 || >=18" + "node": ">= 14" + } + }, + "node_modules/webdriver/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" } }, "node_modules/webdriver/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -11147,48 +12452,63 @@ } }, "node_modules/webdriverio": { - "version": "8.32.2", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.32.2.tgz", - "integrity": "sha512-Z0Wc/dHFfWGWJZpaQ8u910/LG0E9EIVTO7J5yjqWx2XtXz2LzQMxYwNRnvNLhY/1tI4y/cZxI6kFMWr8wD2TtA==", - "dev": true, - "dependencies": { - "@types/node": "^20.1.0", - "@wdio/config": "8.32.2", - "@wdio/logger": "8.28.0", - "@wdio/protocols": "8.32.0", - "@wdio/repl": "8.24.12", - "@wdio/types": "8.32.2", - "@wdio/utils": "8.32.2", - "archiver": "^6.0.0", - "aria-query": "^5.0.0", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.2.1.tgz", + "integrity": "sha512-AI7xzqTmFiU7oAx4fpEF1U1MA7smhCPVDeM0gxPqG5qWepzib3WDX2SsRtcmhdVW+vLJ3m4bf8rAXxZ2M1msWA==", + "dev": true, + "dependencies": { + "@types/node": "^20.11.30", + "@types/sinonjs__fake-timers": "^8.1.5", + "@wdio/config": "9.1.3", + "@wdio/logger": "9.1.3", + "@wdio/protocols": "9.2.0", + "@wdio/repl": "9.0.8", + "@wdio/types": "9.1.3", + "@wdio/utils": "9.1.3", + "archiver": "^7.0.1", + "aria-query": "^5.3.0", + "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1261483", - "grapheme-splitter": "^1.0.2", + "grapheme-splitter": "^1.0.4", + "htmlfy": "^0.3.0", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", + "jszip": "^3.10.1", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", - "minimatch": "^9.0.0", - "puppeteer-core": "^20.9.0", - "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", + "minimatch": "^9.0.3", + "query-selector-shadow-dom": "^1.0.1", + "resq": "^1.11.0", "rgb2hex": "0.2.5", - "serialize-error": "^11.0.1", - "webdriver": "8.32.2" + "serialize-error": "^11.0.3", + "urlpattern-polyfill": "^10.0.0", + "webdriver": "9.2.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" }, "peerDependencies": { - "devtools": "^8.14.0" + "puppeteer-core": "^22.3.0" }, "peerDependenciesMeta": { - "devtools": { + "puppeteer-core": { "optional": true } } }, + "node_modules/webdriverio/node_modules/@wdio/repl": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-9.0.8.tgz", + "integrity": "sha512-3iubjl4JX5zD21aFxZwQghqC3lgu+mSs8c3NaiYYNCC+IT5cI/8QuKlgh9s59bu+N3gG988jqMJeCYlKuUv/iw==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, "node_modules/webdriverio/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -11199,9 +12519,9 @@ } }, "node_modules/webdriverio/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -11213,12 +12533,86 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/webdriverio/node_modules/webdriver": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.2.0.tgz", + "integrity": "sha512-UrhuHSLq4m3OgncvX75vShfl5w3gmjAy8LvLb6/L6V+a+xcqMRelFx/DQ72Mr84F4m8Li6wjtebrOH1t9V/uOQ==", + "dev": true, + "dependencies": { + "@types/node": "^20.1.0", + "@types/ws": "^8.5.3", + "@wdio/config": "9.1.3", + "@wdio/logger": "9.1.3", + "@wdio/protocols": "9.2.0", + "@wdio/types": "9.1.3", + "@wdio/utils": "9.1.3", + "deepmerge-ts": "^7.0.3", + "ws": "^8.8.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/webdriverio/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -11261,16 +12655,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11279,10 +12673,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -11350,6 +12753,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11395,6 +12827,35 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -11450,10 +12911,13 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } @@ -11477,9 +12941,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -11521,6 +12985,47 @@ "node": ">=8" } }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", @@ -11540,6 +13045,15 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yauzl/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -11552,18 +13066,53 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zip-stream": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", - "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "dev": true, "dependencies": { - "archiver-utils": "^4.0.1", - "compress-commons": "^5.0.1", - "readable-stream": "^3.6.0" + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 12.0.0" + "node": ">= 14" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" } } } diff --git a/package.json b/package.json index 9d772a80eb..5a14f201af 100644 --- a/package.json +++ b/package.json @@ -23,33 +23,33 @@ "wdio": "wdio run ./tests/functional/wdio.conf.js" }, "devDependencies": { - "@babel/core": "^7.23.2", - "@babel/plugin-transform-runtime": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/register": "^7.22.5", - "@babel/runtime": "^7.23.2", - "@wdio/cli": "^8.14.4", + "@babel/core": "^7.25.8", + "@babel/plugin-transform-runtime": "^7.25.7", + "@babel/preset-env": "^7.25.8", + "@babel/register": "^7.25.7", + "@babel/runtime": "^7.25.7", + "@wdio/cli": "^9.2.1", "@wdio/local-runner": "^8.14.3", - "@wdio/mocha-framework": "^8.14.0", - "@wdio/spec-reporter": "^8.14.0", - "eslint": "^8.46.0", + "@wdio/mocha-framework": "^9.1.3", + "@wdio/spec-reporter": "^9.1.3", + "eslint": "^v8.57.1", "eslint-cli": "^1.1.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.28.0", - "eslint-plugin-json": "^3.1.0", - "eslint-plugin-n": "^16.0.1", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-json": "^4.0.1", + "eslint-plugin-n": "^16.6.2", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-promise": "^6.6.0", "json-web-key": "^0.4.0", "jsrsasign": "^11.0.0", - "lint-staged": "^13.2.3", + "lint-staged": "^15.2.10", "livereload": "^0.9.3", - "node-forge": "^1.2.1", + "node-forge": "^1.3.1", "node-jose": "^2.2.0", - "prettier": "^3.0.1", - "typescript": "^5.1.6", - "uuid": "^9.0.0", - "webdriverio": "^8.15.0" + "prettier": "^3.3.3", + "typescript": "^5.6.3", + "uuid": "^10.0.0", + "webdriverio": "^9.2.1" }, "prettier": {} } diff --git a/tests/functional/helpers.js b/tests/functional/helpers.js index 895b1c6feb..2bd7be5287 100644 --- a/tests/functional/helpers.js +++ b/tests/functional/helpers.js @@ -51,3 +51,7 @@ export const click = async (selector) => { // Allow time in case the click loads a new page. await browser.pause(100); }; + +export const verifyUrlContains = async (expectedUrlString) => { + await expect(browser).toHaveUrl(expect.stringContaining(expectedUrlString)); +}; diff --git a/tests/functional/spec/checkbox.spec.js b/tests/functional/spec/checkbox.spec.js index e3785dde80..141ba83afd 100644 --- a/tests/functional/spec/checkbox.spec.js +++ b/tests/functional/spec/checkbox.spec.js @@ -2,7 +2,7 @@ import MandatoryCheckboxPage from "../generated_pages/checkbox/mandatory-checkbo import NonMandatoryCheckboxPage from "../generated_pages/checkbox/non-mandatory-checkbox.page"; import singleCheckboxPage from "../generated_pages/checkbox/single-checkbox.page"; import SubmitPage from "../generated_pages/checkbox/submit.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe('Checkbox with "other" option', () => { beforeEach("Load the survey", async () => { @@ -57,7 +57,7 @@ describe('Checkbox with "other" option', () => { // When await $(MandatoryCheckboxPage.otherDetail()).setValue("Other Text"); await click(MandatoryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(NonMandatoryCheckboxPage.pageName); + await verifyUrlContains(NonMandatoryCheckboxPage.pageName); }); it('Given a non-mandatory checkbox answer, when the user does not select an option, then "No answer provided" should be displayed on the summary screen', async () => { diff --git a/tests/functional/spec/components/address/address.spec.js b/tests/functional/spec/components/address/address.spec.js index 272fe0a97b..db27e65147 100644 --- a/tests/functional/spec/components/address/address.spec.js +++ b/tests/functional/spec/components/address/address.spec.js @@ -2,7 +2,7 @@ import AddressConfirmation from "../../../generated_pages/address/address-confir import AddressMandatory from "../../../generated_pages/address/address-block-mandatory.page"; import AddressOptional from "../../../generated_pages/address/address-block-optional.page"; import SubmitPage from "../../../generated_pages/address/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Address Answer Type", () => { beforeEach("Launch survey", async () => { @@ -19,7 +19,7 @@ describe("Address Answer Type", () => { await click(AddressMandatory.submit()); await click(AddressOptional.submit()); await click(AddressConfirmation.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.addressMandatory()).getText()).toBe("Evelyn Street\nApt 7\nBarry\nCF63 4JG"); await expect(await $(SubmitPage.addressMandatory()).getHTML()).toContain("Evelyn Street
      Apt 7
      Barry
      CF63 4JG"); }); @@ -32,7 +32,7 @@ describe("Address Answer Type", () => { await click(AddressMandatory.submit()); await click(AddressOptional.submit()); await click(AddressConfirmation.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.addressMandatory()).getText()).toBe("Evelyn Street"); }); }); @@ -64,7 +64,7 @@ describe("Address Answer Type", () => { await $(AddressMandatory.Postcode()).setValue("CF63 4JG"); await click(AddressMandatory.submit()); - await expect(browser).toHaveUrlContaining(AddressOptional.pageName); + await verifyUrlContains(AddressOptional.pageName); await browser.url(AddressMandatory.url()); diff --git a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js index 81c20d5b2a..ac8cf20f34 100644 --- a/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/checkbox/checkbox_detail_answer_multiple.spec.js @@ -1,6 +1,6 @@ import MandatoryCheckboxPage from "../../../generated_pages/checkbox_detail_answer_multiple/mandatory-checkbox.page"; import SubmitPage from "../../../generated_pages/checkbox_detail_answer_multiple/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe('Checkbox with multiple "detail_answer" options', () => { const checkboxSchema = "test_checkbox_detail_answer_multiple.json"; @@ -37,7 +37,7 @@ describe('Checkbox with multiple "detail_answer" options', () => { // When await $(MandatoryCheckboxPage.yourChoiceDetail()).setValue("Bacon"); await click(MandatoryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { diff --git a/tests/functional/spec/components/radio/radio.js b/tests/functional/spec/components/radio/radio.js index a077ed88e1..01b1c08b4c 100644 --- a/tests/functional/spec/components/radio/radio.js +++ b/tests/functional/spec/components/radio/radio.js @@ -15,7 +15,7 @@ import RadioNonMandatoryDetailAnswerOverriddenPage from "../../../generated_page import RadioNonMandatoryDetailAnswerPage from "../../../generated_pages/radio_optional_with_detail_answer_mandatory/radio-non-mandatory.page"; import RadioNonMandatoryDetailAnswerSummary from "../../../generated_pages/radio_optional_with_detail_answer_mandatory/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Component: Radio", () => { describe("Given I start a Mandatory Radio survey", () => { before(async () => { @@ -25,7 +25,7 @@ describe("Component: Radio", () => { it("When I have selected a radio option that contains an escaped character, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.teaCoffee()).click(); await click(RadioMandatoryPage.submit()); - await expect(browser).toHaveUrlContaining(RadioMandatorySummary.pageName); + await verifyUrlContains(RadioMandatorySummary.pageName); await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).toBe("Tea & Coffee"); }); }); @@ -38,7 +38,7 @@ describe("Component: Radio", () => { it("When I have selected a radio option, Then the selected option should be displayed in the summary", async () => { await $(RadioMandatoryPage.coffee()).click(); await click(RadioMandatoryPage.submit()); - await expect(browser).toHaveUrlContaining(RadioMandatorySummary.pageName); + await verifyUrlContains(RadioMandatorySummary.pageName); await expect(await $(RadioMandatorySummary.radioMandatoryAnswer()).getText()).toBe("Coffee"); }); }); @@ -65,7 +65,7 @@ describe("Component: Radio", () => { await $(RadioMandatoryOptionalDetailAnswerPage.other()).click(); await $(RadioMandatoryOptionalDetailAnswerPage.otherDetail()).setValue("Hello World"); await click(RadioMandatoryOptionalDetailAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(RadioMandatoryOptionDetailAnswerSummary.pageName); + await verifyUrlContains(RadioMandatoryOptionDetailAnswerSummary.pageName); await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).toContain("Hello World"); }); }); @@ -89,7 +89,7 @@ describe("Component: Radio", () => { it("When I submit without any data in the other text field is selected, Then the selected option should be displayed in the summary", async () => { await click(RadioMandatoryOptionalDetailAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(RadioMandatoryOptionDetailAnswerSummary.pageName); + await verifyUrlContains(RadioMandatoryOptionDetailAnswerSummary.pageName); await expect(await $(RadioMandatoryOptionDetailAnswerSummary.radioMandatoryAnswer()).getText()).toContain("No answer provided"); }); }); @@ -112,7 +112,7 @@ describe("Component: Radio", () => { it("When I have selected no option, Then the selected option should be displayed in the summary", async () => { await click(RadioNonMandatoryPage.submit()); - await expect(browser).toHaveUrlContaining(RadioNonMandatorySummary.pageName); + await verifyUrlContains(RadioNonMandatorySummary.pageName); await expect(await $(RadioNonMandatorySummary.radioNonMandatoryAnswer()).getText()).toBe("No answer provided"); }); }); @@ -138,7 +138,7 @@ describe("Component: Radio", () => { await $(RadioNonMandatoryDetailAnswerPage.other()).click(); await $(RadioNonMandatoryDetailAnswerPage.otherDetail()).setValue("Hello World"); await click(RadioNonMandatoryDetailAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(RadioNonMandatoryDetailAnswerSummary.pageName); + await verifyUrlContains(RadioNonMandatoryDetailAnswerSummary.pageName); await expect(await $(RadioNonMandatoryDetailAnswerSummary.radioNonMandatoryAnswer()).getText()).toContain("Hello World"); }); }); diff --git a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js index d52cad891b..4079fcde7b 100644 --- a/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js +++ b/tests/functional/spec/components/radio/radio_detail_answer_multiple.spec.js @@ -1,6 +1,6 @@ import MandatoryRadioPage from "../../../generated_pages/radio_detail_answer_multiple/radio-mandatory.page"; import SubmitPage from "../../../generated_pages/radio_detail_answer_multiple/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe('Radio with multiple "detail_answer" options', () => { const radioSchema = "test_radio_detail_answer_multiple.json"; @@ -33,7 +33,7 @@ describe('Radio with multiple "detail_answer" options', () => { // When await $(MandatoryRadioPage.favouriteNotListedDetail()).setValue("Bacon"); await click(MandatoryRadioPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given a non-mandatory detail answer, When the user does not provide any text, Then just the option value should be displayed on the summary screen", async () => { diff --git a/tests/functional/spec/confirmation_email.spec.js b/tests/functional/spec/confirmation_email.spec.js index d3947b9a8a..d46b767db3 100644 --- a/tests/functional/spec/confirmation_email.spec.js +++ b/tests/functional/spec/confirmation_email.spec.js @@ -4,7 +4,7 @@ import ThankYouPage from "../base_pages/thank-you.page"; import ConfirmationEmailPage from "../base_pages/confirmation-email.page"; import ConfirmationEmailSentPage from "../base_pages/confirmation-email-sent.page"; import ConfirmEmailPage from "../base_pages/confirm-email.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; const errorPanel = '[data-ga="error"]'; @@ -17,13 +17,13 @@ describe("Email confirmation", () => { it("When I complete the survey and am on the thank you page, Then there is option to enter an email address", async () => { await click(SubmitPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.email()).isExisting()).toBe(true); }); it("When I submit the form without providing an email address, Then I get an error message", async () => { await click(ThankYouPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(errorPanel).isExisting()).toBe(true); await expect(await $(errorPanel).getText()).toBe("Enter an email address"); }); @@ -31,7 +31,7 @@ describe("Email confirmation", () => { it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ThankYouPage.email()).setValue("incorrect-format"); await click(ThankYouPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(errorPanel).isExisting()).toBe(true); await expect(await $(errorPanel).getText()).toBe("Enter an email address in a valid format, for example name@example.com"); }); @@ -39,13 +39,13 @@ describe("Email confirmation", () => { it("When I submit the form with a valid email address, Then I go to the confirm email page", async () => { await $(ThankYouPage.email()).setValue("name@example.com"); await click(ThankYouPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/confirm"); + await verifyUrlContains("confirmation-email/confirm"); await expect(await $(ConfirmEmailPage.questionTitle()).getText()).toBe("Is this email address correct?"); }); it("When I submit the confirm email page without providing an answer, Then I get an error message", async () => { await click(ConfirmEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/confirm"); + await verifyUrlContains("confirmation-email/confirm"); await expect(await $(ConfirmEmailPage.errorPanel()).isExisting()).toBe(true); await expect(await $(ConfirmEmailPage.errorPanel()).getText()).toContain("Select an answer"); }); @@ -53,14 +53,14 @@ describe("Email confirmation", () => { it("When I answer 'Yes' and submit the confirm email page, Then I go to email sent page", async () => { await $(ConfirmEmailPage.yes()).click(); await click(ConfirmEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/sent"); + await verifyUrlContains("confirmation-email/sent"); await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).toBe("A confirmation email has been sent to name@example.com"); }); it("When I go to the confirmation email page and submit without providing an email address, Then I get an error message", async () => { await $(ConfirmationEmailSentPage.sendAnotherEmail()).click(); await click(ConfirmationEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await verifyUrlContains("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).toBe(true); await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).toBe("Enter an email address"); }); @@ -68,7 +68,7 @@ describe("Email confirmation", () => { it("When I submit the form without providing a correctly formatted email address, Then I get an error message", async () => { await $(ConfirmationEmailPage.email()).setValue("incorrect-format"); await click(ConfirmationEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await verifyUrlContains("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.errorPanel()).isExisting()).toBe(true); await expect(await $(ConfirmationEmailPage.errorPanel()).getText()).toBe("Enter an email address in a valid format, for example name@example.com"); }); @@ -78,7 +78,7 @@ describe("Email confirmation", () => { await click(ConfirmationEmailPage.submit()); await $(ConfirmEmailPage.yes()).click(); await click(ConfirmEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/sent"); + await verifyUrlContains("confirmation-email/sent"); await expect(await $(ConfirmationEmailSentPage.confirmationText()).getText()).toBe("A confirmation email has been sent to name@example.com"); }); }); @@ -93,7 +93,7 @@ describe("Email confirmation", () => { await click(ThankYouPage.submit()); await $(ConfirmEmailPage.no()).click(); await click(ConfirmEmailPage.submit()); - await expect(browser).toHaveUrlContaining("confirmation-email/send"); + await verifyUrlContains("confirmation-email/send"); await expect(await $(ConfirmationEmailPage.email()).getValue()).toBe("name@example.com"); }); }); diff --git a/tests/functional/spec/dates.spec.js b/tests/functional/spec/dates.spec.js index 8785af9e2c..72751edcc6 100644 --- a/tests/functional/spec/dates.spec.js +++ b/tests/functional/spec/dates.spec.js @@ -4,7 +4,7 @@ import DateSinglePage from "../generated_pages/dates/date-single-block.page"; import DateNonMandatoryPage from "../generated_pages/dates/date-non-mandatory-block.page"; import DateYearDatePage from "../generated_pages/dates/date-year-date-block.page"; import SubmitPage from "../generated_pages/dates/submit.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Date checks", () => { beforeEach("Load the survey", async () => { @@ -58,7 +58,7 @@ describe("Date checks", () => { await click(DateYearDatePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); // Then the summary screen shows the dates entered formatted await expect(await $(SubmitPage.dateRangeFromAnswer()).getText()).toBe("1 January 1901 to 3 May 2017"); @@ -161,7 +161,7 @@ describe("Date checks", () => { await $(DateMonthYearPage.Year()).setValue(2018); await click(DateMonthYearPage.submit()); - await expect(browser).toHaveUrlContaining(DateSinglePage.url()); + await verifyUrlContains(DateSinglePage.url()); }); it("Given the test_dates survey is selected when an error message is shown then when it is corrected, it goes to the summary page and the information is correct", async () => { diff --git a/tests/functional/spec/durations.spec.js b/tests/functional/spec/durations.spec.js index 48fb87b6e9..59dc2c22b3 100644 --- a/tests/functional/spec/durations.spec.js +++ b/tests/functional/spec/durations.spec.js @@ -1,6 +1,6 @@ import DurationPage from "../generated_pages/durations/duration-block.page.js"; import SubmitPage from "../generated_pages/durations/submit.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Durations", () => { beforeEach("Load the survey", async () => { @@ -23,7 +23,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("1 year 2 months"); await click(SubmitPage.submit()); }); @@ -37,7 +37,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("2 months"); await click(SubmitPage.submit()); }); @@ -49,7 +49,7 @@ describe("Durations", () => { await $(DurationPage.mandatoryMonthMonths()).setValue(1); await click(DurationPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.yearMonthAnswer()).getText()).toBe("No answer provided"); await click(SubmitPage.submit()); }); diff --git a/tests/functional/spec/features/confirmation_question.spec.js b/tests/functional/spec/features/confirmation_question.spec.js index 9e00a59da5..418038019e 100644 --- a/tests/functional/spec/features/confirmation_question.spec.js +++ b/tests/functional/spec/features/confirmation_question.spec.js @@ -1,7 +1,7 @@ import NumberOfEmployeesTotalBlockPage from "../../generated_pages/confirmation_question/number-of-employees-total-block.page.js"; import ConfirmZeroEmployeesBlockPage from "../../generated_pages/confirmation_question/confirm-zero-employees-block.page.js"; import SubmitPage from "../../generated_pages/confirmation_question/submit.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Feature: Confirmation Question", () => { describe("Given I have a completed the confirmation question", () => { @@ -14,7 +14,7 @@ describe("Feature: Confirmation Question", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yesThisIsCorrect()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.numberOfEmployeesTotal()).getText()).toBe("0"); await expect(await $$(SubmitPage.confirmZeroEmployeesAnswer())).toHaveLength(0); }); @@ -25,7 +25,7 @@ describe("Feature: Confirmation Question", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.noINeedToCorrectThis()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(browser).toHaveUrlContaining(NumberOfEmployeesTotalBlockPage.pageName); + await verifyUrlContains(NumberOfEmployeesTotalBlockPage.pageName); }); }); describe("Given a number of employees Question", () => { diff --git a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js index cda3562830..e9d713d467 100644 --- a/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js +++ b/tests/functional/spec/features/confirmation_question_within_repeating_section.spec.js @@ -4,7 +4,7 @@ import CarerPage from "../../generated_pages/confirmation_question_within_repeat import DateOfBirthPage from "../../generated_pages/confirmation_question_within_repeating_section/dob-block.page"; import ConfirmDateOfBirthPage from "../../generated_pages/confirmation_question_within_repeating_section/confirm-dob-block.page"; import DefaultSectionSummary from "../../generated_pages/confirmation_question_within_repeating_section/default-section-summary.page"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Feature: Confirmation Question Within A Repeating Section", () => { describe("Given I am in a repeating section", () => { beforeEach("Add a person", async () => { @@ -16,7 +16,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await click(AddPersonPage.submit()); await $(DoesAnyoneLiveHerePage.no()).click(); await click(DoesAnyoneLiveHerePage.submit()); - await expect(browser).toHaveUrlContaining(DateOfBirthPage.url().split("/").slice(-1)[0]); + await verifyUrlContains(DateOfBirthPage.url().split("/").slice(-1)[0]); }); describe("Given a confirmation question", () => { @@ -30,7 +30,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { // Answer 'No' to confirmation question await $(ConfirmDateOfBirthPage.noINeedToChangeTheirDateOfBirth()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(browser).toHaveUrlContaining(DateOfBirthPage.pageName); + await verifyUrlContains(DateOfBirthPage.pageName); }); }); @@ -44,7 +44,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(ConfirmDateOfBirthPage.yesPersonNameIsAgeOld()).click(); await click(ConfirmDateOfBirthPage.submit()); - await expect(browser).toHaveUrlContaining("sections/default-section/"); + await verifyUrlContains("sections/default-section/"); await expect(await $(DefaultSectionSummary.confirmDateOfBirth()).isExisting()).toBe(false); }); }); @@ -56,7 +56,7 @@ describe("Feature: Confirmation Question Within A Repeating Section", () => { await $(DateOfBirthPage.year()).setValue("2000"); await click(DateOfBirthPage.submit()); - await expect(browser).toHaveUrlContaining(CarerPage.pageName); + await verifyUrlContains(CarerPage.pageName); await expect(await $(CarerPage.questionText()).getText()).toContain("Does John Doe look"); }); }); diff --git a/tests/functional/spec/features/default_value/default_value.spec.js b/tests/functional/spec/features/default_value/default_value.spec.js index fa4ad123ba..8b5497ecde 100644 --- a/tests/functional/spec/features/default_value/default_value.spec.js +++ b/tests/functional/spec/features/default_value/default_value.spec.js @@ -3,36 +3,36 @@ import QuestionPageTwo from "../../../generated_pages/default/number-question-tw import SubmitPage from "../../../generated_pages/default/submit.page.js"; import QuestionPageOneSkip from "../../../generated_pages/default_with_skip/number-question-one.page.js"; import QuestionPageThreeSkip from "../../../generated_pages/default_with_skip/number-question-three.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Default Value", () => { it('Given I start default schema, When I do not answer a question, Then "no answer provided" is displayed on the Summary page', async () => { await browser.openQuestionnaire("test_default.json"); await click(QuestionPageOne.submit()); - await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); + await verifyUrlContains(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); await click(QuestionPageTwo.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.answerOne()).getText()).toBe("0"); }); it("Given I have not answered a question containing a default value, When I return to the question, Then no value should be displayed", async () => { await browser.openQuestionnaire("test_default.json"); await click(QuestionPageOne.submit()); - await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); + await verifyUrlContains(QuestionPageTwo.pageName); await $(QuestionPageTwo.two()).setValue(123); await click(QuestionPageTwo.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.previous()).click(); - await expect(browser).toHaveUrlContaining(QuestionPageTwo.pageName); + await verifyUrlContains(QuestionPageTwo.pageName); await $(QuestionPageTwo.previous()).click(); - await expect(browser).toHaveUrlContaining(QuestionPageOne.pageName); + await verifyUrlContains(QuestionPageOne.pageName); await expect(await $(QuestionPageOne.one()).getValue()).toBe(""); }); it("Given I have not answered a question containing a default value, When a skip condition checks for the default value, Then I should skip the next question", async () => { await browser.openQuestionnaire("test_default_with_skip.json"); await click(QuestionPageOneSkip.submit()); - await expect(browser).toHaveUrlContaining(QuestionPageThreeSkip.pageName); + await verifyUrlContains(QuestionPageThreeSkip.pageName); await expect(await $(QuestionPageThreeSkip.questionText()).getText()).toBe("Question Three"); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/function_driven.js b/tests/functional/spec/features/dynamic_answer_options/function_driven.js index 6c5605edcc..12ce602565 100644 --- a/tests/functional/spec/features/dynamic_answer_options/function_driven.js +++ b/tests/functional/spec/features/dynamic_answer_options/function_driven.js @@ -4,7 +4,7 @@ import DynamicRadioPage from "../../../generated_pages/dynamic_answer_options_fu import DynamicDropdownPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/dynamic-dropdown.page"; import DynamicMutuallyExclusivePage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/dynamic-mutually-exclusive.page"; import SubmitPage from "../../../generated_pages/dynamic_answer_options_function_driven_with_static_options/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; const dropdownOptionValues = ["2020-12-28", "2020-12-29", "2020-12-30", "2020-12-31", "2021-01-01", "2021-01-02", "2021-01-03"]; const dropdownOptionValuesWithStaticOption = [...dropdownOptionValues, "I did not work"]; @@ -54,7 +54,7 @@ testCases.forEach((testCase) => { it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(DynamicRadioPage.pageName); + await verifyUrlContains(DynamicRadioPage.pageName); }); }); @@ -68,7 +68,7 @@ testCases.forEach((testCase) => { it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicRadioPage.submit()); - await expect(browser).toHaveUrlContaining(DynamicDropdownPage.pageName); + await verifyUrlContains(DynamicDropdownPage.pageName); }); }); @@ -82,7 +82,7 @@ testCases.forEach((testCase) => { it("When I submit the page, then I should be taken to the next page", async () => { await click(DynamicDropdownPage.submit()); - await expect(browser).toHaveUrlContaining(DynamicMutuallyExclusivePage.pageName); + await verifyUrlContains(DynamicMutuallyExclusivePage.pageName); }); }); @@ -127,7 +127,7 @@ testCases.forEach((testCase) => { await click(DynamicRadioPage.submit()); await click(DynamicMutuallyExclusivePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("No answer provided"); await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("No answer provided"); await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("No answer provided"); @@ -153,7 +153,7 @@ testCases.forEach((testCase) => { await $(DynamicMutuallyExclusivePage.answerByIndex(6)).click(); // Sunday 3 January 2021 await click(DynamicMutuallyExclusivePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("Wednesday 30 December 2020\nThursday 31 December 2020"); await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("Tuesday 29 December 2020"); await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("Saturday 2 January 2021"); @@ -195,7 +195,7 @@ describe(`Feature: Dynamically generated answer options driven by a function wit await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).click(); await click(DynamicMutuallyExclusivePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.dynamicCheckboxAnswer()).getText()).toBe("I did not work"); await expect(await $(SubmitPage.dynamicRadioAnswer()).getText()).toBe("I did not work"); await expect(await $(SubmitPage.dynamicDropdownAnswer()).getText()).toBe("I did not work"); @@ -226,7 +226,7 @@ describe(`Feature: Dynamically generated answer options driven by a function wit await expect(await $(DynamicMutuallyExclusivePage.staticIDidNotWork()).isSelected()).toBe(true); await click(DynamicMutuallyExclusivePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js index e795ac7a99..f5992d173f 100644 --- a/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js +++ b/tests/functional/spec/features/dynamic_answer_options/radio_options_from_checkbox_answers.js @@ -2,7 +2,7 @@ import HealedTheQuickestPage from "../../../generated_pages/dynamic_radio_option import InjurySustainedPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/injury-sustained.page"; import MostSeriousInjuryPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/most-serious-injury.page"; import SubmitPage from "../../../generated_pages/dynamic_radio_options_from_checkbox/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Dynamic radio options from checkbox answers", () => { describe("Given the dynamic radio options from checkbox questionnaire and I am on the checkbox answer page", () => { it("When the respondent answers the checkbox question and submits, Then the radio question should show the answers from that checkbox as options, as well as a static option", async () => { @@ -11,7 +11,7 @@ describe("Dynamic radio options from checkbox answers", () => { await $(InjurySustainedPage.body()).click(); await click(InjurySustainedPage.submit()); - await expect(browser).toHaveUrlContaining(MostSeriousInjuryPage.pageName); + await verifyUrlContains(MostSeriousInjuryPage.pageName); await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(0)).getText()).toBe("Head"); await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(1)).getText()).toBe("Body"); await expect(await $(MostSeriousInjuryPage.answerLabelByIndex(2)).getText()).toBe("They were of equal severity (static option)"); @@ -22,7 +22,7 @@ describe("Dynamic radio options from checkbox answers", () => { await $(MostSeriousInjuryPage.answerLabelByIndex(0)).click(); await click(MostSeriousInjuryPage.submit()); - await expect(browser).toHaveUrlContaining(HealedTheQuickestPage.pageName); + await verifyUrlContains(HealedTheQuickestPage.pageName); await expect(await $(HealedTheQuickestPage.answerLabelByIndex(0)).getText()).toBe("Head"); await expect(await $(HealedTheQuickestPage.answerLabelByIndex(1)).getText()).toBe("Body"); await expect(await $(HealedTheQuickestPage.answerLabelByIndex(2)).isExisting()).toBe(false); @@ -32,7 +32,7 @@ describe("Dynamic radio options from checkbox answers", () => { await $(HealedTheQuickestPage.answerLabelByIndex(1)).click(); await click(HealedTheQuickestPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.injurySustainedAnswer()).getText()).toBe("Head\nBody"); await expect(await $(SubmitPage.mostSeriousInjuryAnswer()).getText()).toBe("Head"); await expect(await $(SubmitPage.healedTheQuickestAnswer()).getText()).toBe("Body"); diff --git a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js index b2511bddfb..41f64feb97 100644 --- a/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js +++ b/tests/functional/spec/features/dynamic_answers_list_value_source.spec.js @@ -8,7 +8,7 @@ import SetMinimumPage from "../../generated_pages/dynamic_answers_list_source/mi import SectionSummaryPage from "../../generated_pages/dynamic_answers_list_source/list-collector-section-summary.page"; import HubPage from "../../base_pages/hub.page"; import OnlineShoppingPage from "../../generated_pages/dynamic_answers_list_source/dynamic-answer-separate-section.page"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; import { expect } from "@wdio/globals"; describe("Dynamic answers list value source", () => { @@ -70,7 +70,7 @@ describe("Dynamic answers list value source", () => { await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(SetMinimumPage.previous()).click(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].getValue()).toBe("12"); await expect(await $$(DynamicAnswerPage.inputs())[1].getValue()).toBe("21"); await expect(await $$(DynamicAnswerPage.labels())[0].getText()).toBe("Percentage of shopping at Tesco"); @@ -96,11 +96,11 @@ describe("Dynamic answers list value source", () => { await $$(DynamicAnswerPage.inputs())[1].setValue(21); await setMinimumAndGetSectionSummary(); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[0].$("a").click(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[0].isFocused()).toBe(true); await click(DynamicAnswerPage.submit()); await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryActions)[1].$("a").click(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.inputs())[1].isFocused()).toBe(true); }); it("Given list items have been added and the dynamic answers are submitted, When the dynamic answers are resubmitted with answers updated, Then they should be displayed correctly on summary", async () => { @@ -124,7 +124,7 @@ describe("Dynamic answers list value source", () => { await click(ListCollectorRemovePage.submit()); await click(DynamicAnswerPage.submit()); await click(DynamicAnswerOnlyPage.submit()); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); + await verifyUrlContains(SectionSummaryPage.pageName); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryValues)[0].getText()).toBe("21%"); await expect(await $(SectionSummaryPage.listCollectorGroupContent(2)).$$(summaryTitles).length).toBe(5); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js index eae51ad5f0..a8d2f523f7 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance.spec.js @@ -2,7 +2,7 @@ import { getRandomString } from "../../../jwt_helper"; import AddressConfirmationPage from "../../../generated_pages/last_viewed_question_guidance/address-confirmation.page"; import HouseholdInterstitialPage from "../../../generated_pages/last_viewed_question_guidance/household-interstitial.page.js"; import PrimaryPersonListCollectorPage from "../../../generated_pages/last_viewed_question_guidance/primary-person-list-collector.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Last viewed question guidance", () => { const resumableLaunchParams = { responseId: getRandomString(16), @@ -15,7 +15,7 @@ describe("Last viewed question guidance", () => { }); it("When the respondent first launches the survey, then last question guidance is not shown", async () => { - await expect(browser).toHaveUrlContaining(HouseholdInterstitialPage.url()); + await verifyUrlContains(HouseholdInterstitialPage.url()); await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -23,7 +23,7 @@ describe("Last viewed question guidance", () => { await $(HouseholdInterstitialPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); await browser.pause(100); - await expect(browser).toHaveUrlContaining(HouseholdInterstitialPage.url()); + await verifyUrlContains(HouseholdInterstitialPage.url()); await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -32,7 +32,7 @@ describe("Last viewed question guidance", () => { await $(AddressConfirmationPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance.json", resumableLaunchParams); await browser.pause(100); - await expect(browser).toHaveUrlContaining(AddressConfirmationPage.url()); + await verifyUrlContains(AddressConfirmationPage.url()); await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(HouseholdInterstitialPage.url()); await expect(await $(AddressConfirmationPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); @@ -40,7 +40,7 @@ describe("Last viewed question guidance", () => { it("When the respondent answers the question and saves and continues, then last question guidance is not shown on the next question", async () => { await $(AddressConfirmationPage.yes()).click(); await click(AddressConfirmationPage.submit()); - await expect(browser).toHaveUrlContaining(PrimaryPersonListCollectorPage.url()); + await verifyUrlContains(PrimaryPersonListCollectorPage.url()); await expect(await $(HouseholdInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); diff --git a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js index b4e0a699ad..1d53bf1aa1 100644 --- a/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js +++ b/tests/functional/spec/features/last_viewed_guidance/last_viewed_guidance_hub.spec.js @@ -8,7 +8,7 @@ import SportsPage from "../../../generated_pages/last_viewed_question_guidance_h import UnPaidWorkPage from "../../../generated_pages/last_viewed_question_guidance_hub/unpaid-work.page.js"; import WorkInterstitialPage from "../../../generated_pages/last_viewed_question_guidance_hub/work-interstitial.page.js"; import HubPage from "../../../base_pages/hub.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Last viewed question guidance", () => { const resumableLaunchParams = { responseId: getRandomString(16), @@ -21,7 +21,7 @@ describe("Last viewed question guidance", () => { }); it("When the respondent launches the survey, then last question guidance is not shown", async () => { - await expect(browser).toHaveUrlContaining(WorkInterstitialPage.url()); + await verifyUrlContains(WorkInterstitialPage.url()); await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -29,7 +29,7 @@ describe("Last viewed question guidance", () => { await $(WorkInterstitialPage.saveSignOut()).click(); await browser.openQuestionnaire("test_last_viewed_question_guidance_hub.json", resumableLaunchParams); await browser.pause(100); - await expect(browser).toHaveUrlContaining(WorkInterstitialPage.url()); + await verifyUrlContains(WorkInterstitialPage.url()); await expect(await $(WorkInterstitialPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -54,7 +54,7 @@ describe("Last viewed question guidance", () => { it("When the respondent selects a section which is not started, then last question guidance is not shown", async () => { await $(HubPage.summaryRowLink("education-section")).click(); - await expect(browser).toHaveUrlContaining(GcsesPage.url()); + await verifyUrlContains(GcsesPage.url()); await expect(await $(GcsesPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -64,7 +64,7 @@ describe("Last viewed question guidance", () => { await click(GcsesPage.submit()); await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("education-section")).click(); - await expect(browser).toHaveUrlContaining(ALevelsPage.url()); + await verifyUrlContains(ALevelsPage.url()); await expect(await $(ALevelsPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(GcsesPage.url()); await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); @@ -72,11 +72,11 @@ describe("Last viewed question guidance", () => { it("When the respondent selects a section which is complete , then last question guidance is not shown on the summary or any link clicked from the summary", async () => { await $(ALevelsPage.yes()).click(); await click(ALevelsPage.submit()); - await expect(browser).toHaveUrlContaining(EducationSectionSummaryPage.url()); + await verifyUrlContains(EducationSectionSummaryPage.url()); await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); await click(EducationSectionSummaryPage.submit()); await $(HubPage.summaryRowLink("education-section")).click(); - await expect(browser).toHaveUrlContaining(EducationSectionSummaryPage.url()); + await verifyUrlContains(EducationSectionSummaryPage.url()); await $(EducationSectionSummaryPage.alevelsAnswerEdit()).click(); await expect(await $(ALevelsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -84,7 +84,7 @@ describe("Last viewed question guidance", () => { it("When the user clicks continue on the hub and it takes you to a section which is not started, then last question guidance is not shown", async () => { await browser.url(HubPage.url()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(SportsPage.url()); + await verifyUrlContains(SportsPage.url()); await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); @@ -94,7 +94,7 @@ describe("Last viewed question guidance", () => { await click(SportsPage.submit()); await browser.url(HubPage.url()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(HobbiesPage.url()); + await verifyUrlContains(HobbiesPage.url()); await expect(await $(HobbiesPage.lastViewedQuestionGuidanceLink()).getAttribute("href")).toContain(SportsPage.url()); await expect(await $(HobbiesPage.lastViewedQuestionGuidance()).isExisting()).toBe(true); }); @@ -103,7 +103,7 @@ describe("Last viewed question guidance", () => { await $(HobbiesPage.yes()).click(); await click(HobbiesPage.submit()); await $(HubPage.summaryRowLink("interests-section")).click(); - await expect(browser).toHaveUrlContaining(SportsPage.url()); + await verifyUrlContains(SportsPage.url()); await expect(await $(SportsPage.lastViewedQuestionGuidance()).isExisting()).toBe(false); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page.spec.js b/tests/functional/spec/features/submit_page/submit_page.spec.js index 629d0dc3dd..ef7d682cd9 100644 --- a/tests/functional/spec/features/submit_page/submit_page.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page.spec.js @@ -1,7 +1,7 @@ import BreakfastPage from "../../../generated_pages/submit_with_custom_submission_text/breakfast.page.js"; import { SubmitPage } from "../../../base_pages/submit.page"; import { IntroductionPage } from "../../../base_pages/introduction.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Given I launch a linear flow questionnaire without summary", () => { beforeEach("Load the questionnaire", async () => { @@ -12,13 +12,13 @@ describe("Given I launch a linear flow questionnaire without summary", () => { it("When I complete the questionnaire, then I should be taken to the submit page without a summary", async () => { await $(BreakfastPage.answer()).setValue("Bacon"); await click(BreakfastPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); await expect(await $(SubmitPage.summary()).isExisting()).toBe(false); }); it("When I complete the questionnaire and submit the questionnaire, then the submission is successful", async () => { await click(BreakfastPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); await click(SubmitPage.submit()); }); }); diff --git a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js index 5f07fd8d84..53d5cb5084 100644 --- a/tests/functional/spec/features/submit_page/submit_page_summary.spec.js +++ b/tests/functional/spec/features/submit_page/submit_page_summary.spec.js @@ -3,7 +3,7 @@ import DessertConfirmationPage from "../../../generated_pages/submit_with_summar import NumbersPage from "../../../generated_pages/submit_with_summary/numbers.page.js"; import RadioPage from "../../../generated_pages/submit_with_summary/radio.page.js"; import SubmitPage from "../../../generated_pages/submit_with_summary/submit.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Submit Page with Summary", () => { beforeEach("Load the questionnaire", async () => { await browser.openQuestionnaire("test_submit_with_summary.json"); @@ -25,7 +25,7 @@ describe("Submit Page with Summary", () => { await completeAllQuestions(); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question", async () => { @@ -42,7 +42,7 @@ describe("Submit Page with Summary", () => { await $(SubmitPage.radioAnswerEdit()).click(); await $(RadioPage.previous()).click(); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given a questionnaire with a summary has been completed when a summary page edit link is clicked then it should return to that question then back to summary", async () => { @@ -104,6 +104,6 @@ describe("Submit Page with Summary", () => { await $(NumbersPage.decimal()).setValue("123456.78"); await click(NumbersPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); } }); diff --git a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js index 0178aeaf7d..07de09d41f 100644 --- a/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js +++ b/tests/functional/spec/features/submit_with_summary_return_to_answer.spec.js @@ -6,7 +6,7 @@ import HouseholdDetailsSummaryPage from "../../generated_pages/submit_with_summa import SubmitPage from "../../generated_pages/submit_with_summary_return_to_answer/submit.page.js"; import AddressDurationPage from "../../generated_pages/submit_with_summary_return_to_answer/address-duration.page.js"; import NamePage from "../../generated_pages/submit_with_summary_return_to_answer/name.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Summary Anchor Scrolling", () => { describe("Given I start a Test Section Summary survey", () => { beforeEach(async () => { @@ -50,7 +50,7 @@ describe("Summary Anchor Scrolling", () => { await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await $(InsuranceAddressPage.previous()).click(); - await expect(browser).toHaveUrlContaining("property-details-section/#insurance-address-answer2"); + await verifyUrlContains("property-details-section/#insurance-address-answer2"); }); it("When I edit an answer from the section summary page and click the Submit button, Then I am taken to the summary page and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { @@ -58,7 +58,7 @@ describe("Summary Anchor Scrolling", () => { await click(AddressDurationPage.submit()); await $(PropertyDetailsSummaryPage.insuranceAddressAnswer2Edit()).click(); await click(InsuranceAddressPage.submit()); - await expect(browser).toHaveUrlContaining("property-details-section/#insurance-address-answer2"); + await verifyUrlContains("property-details-section/#insurance-address-answer2"); }); it("When I am on the final summary page, Then the Change link url should contain return_to, return_to_answer_id query params", async () => { @@ -81,7 +81,9 @@ describe("Summary Anchor Scrolling", () => { await click(HouseholdDetailsSummaryPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswer2Edit()).click(); - await expect(browser).toHaveUrlContaining("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"); + await expect(browser).toHaveUrl( + expect.stringContaining("?return_to=final-summary&return_to_answer_id=insurance-address-answer2#insurance-address-answer2"), + ); }); }); }); diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js index 996b5f8227..2bc821d5cd 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js @@ -1,4 +1,4 @@ -import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, listItemComplete, click } from "../../../helpers"; +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, listItemComplete, click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; import { getRandomString } from "../../../jwt_helper"; import AddAdditionalEmployeePage from "../../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; @@ -263,7 +263,7 @@ describe("Using supplementary data", () => { it("Given I have a calculated summary using the repeating blocks, When I reach the Calculated Summary, Then I see the correct total and supplementary data labels", async () => { await click(ListCollectorProductsPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryVolumeSalesPage.pageName); + await verifyUrlContains(CalculatedSummaryVolumeSalesPage.pageName); await expect(await $(CalculatedSummaryVolumeSalesPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total volume of sales over the previous quarter to be 150 kg. Is this correct?", ); @@ -333,7 +333,7 @@ describe("Using supplementary data", () => { it("Given I am using a supplementary dataset where the size of one of the lists skips a question in a section, When I enter the section, Then I only see an interstitial block as the other block is skipped", async () => { await $(HubPage.summaryRowLink("section-8")).click(); - await expect(browser).toHaveUrlContaining(ProductVolumeInterstitialPage.pageName); + await verifyUrlContains(ProductVolumeInterstitialPage.pageName); await click(ProductVolumeInterstitialPage.submit()); await expect(await $(HubPage.summaryRowState("section-8")).getText()).toBe("Completed"); }); @@ -362,19 +362,19 @@ describe("Using supplementary data", () => { it("Given I am using a supplementary dataset with a product list size that skips a question in the sales target section, When I enter the section, Then I only see an interstitial block", async () => { await $(HubPage.summaryRowLink("section-7")).click(); - await expect(browser).toHaveUrlContaining(ProductSalesInterstitialPage.pageName); + await verifyUrlContains(ProductSalesInterstitialPage.pageName); await click(ProductSalesInterstitialPage.submit()); await expect(await $(HubPage.summaryRowState("section-7")).getText()).toBe("Completed"); }); it("Given there is now an additional product, When I resume the Product Details Section, Then I start from the list collector content block and see the new product is incomplete", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(browser).toHaveUrlContaining(ListCollectorProductsPage.pageName); + await verifyUrlContains(ListCollectorProductsPage.pageName); await listItemComplete(`li[data-qa="list-item-1-label"]`, true); await listItemComplete(`li[data-qa="list-item-2-label"]`, true); await listItemComplete(`li[data-qa="list-item-3-label"]`, false); await click(ListCollectorProductsPage.submit()); - await expect(browser).toHaveUrlContaining(ProductRepeatingBlock1Page.pageName); + await verifyUrlContains(ProductRepeatingBlock1Page.pageName); }); it("Given I complete the section and relaunch with the old data that has fewer items in the products list, When I am on the Hub, Then I see the products section and sales targets sections are now in progress", async () => { diff --git a/tests/functional/spec/features/validation/date_validation/date-range.spec.js b/tests/functional/spec/features/validation/date_validation/date-range.spec.js index 2b7a2d8531..1f790e6f3a 100644 --- a/tests/functional/spec/features/validation/date_validation/date-range.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-range.spec.js @@ -1,6 +1,6 @@ import DateRangePage from "../../../../generated_pages/date_validation_range/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_range/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Question level validation for date ranges", () => { beforeEach(async () => { await browser.openQuestionnaire("test_date_validation_range.json"); @@ -45,7 +45,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); }); @@ -89,7 +89,7 @@ describe("Feature: Question level validation for date ranges", () => { await $(DateRangePage.dateRangeTomonth()).setValue(2); await $(DateRangePage.dateRangeToyear()).setValue(2018); await click(DateRangePage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/date_validation/date-single.spec.js b/tests/functional/spec/features/validation/date_validation/date-single.spec.js index 6656f19f76..43e7c06a67 100644 --- a/tests/functional/spec/features/validation/date_validation/date-single.spec.js +++ b/tests/functional/spec/features/validation/date_validation/date-single.spec.js @@ -1,7 +1,7 @@ import DatePage from "../../../../generated_pages/date_validation_single/date-block.page"; import DatePeriodPage from "../../../../generated_pages/date_validation_single/date-range-block.page"; import SubmitPage from "../../../../generated_pages/date_validation_single/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Validation for single date periods", () => { beforeEach(async () => { await browser.openQuestionnaire("test_date_validation_single.json"); @@ -64,7 +64,7 @@ describe("Feature: Validation for single date periods", () => { await $(DatePeriodPage.dateRangeTomonth()).setValue(2); await $(DatePeriodPage.dateRangeToyear()).setValue(2018); await click(DatePeriodPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/dynamic.spec.js b/tests/functional/spec/features/validation/sum/dynamic.spec.js index 4e103b60e5..71c218eb53 100644 --- a/tests/functional/spec/features/validation/sum/dynamic.spec.js +++ b/tests/functional/spec/features/validation/sum/dynamic.spec.js @@ -9,7 +9,7 @@ import ListCollectorRemovePage from "../../../../generated_pages/validation_sum_ import ListCollectorEditPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/list-collector-edit.page"; import HubPage from "../../../../base_pages/hub.page"; import TotalBlockOtherPage from "../../../../generated_pages/validation_sum_against_total_dynamic_answers/total-block-other.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Sum of dynamic answers based on list and optional static answers equal to validation against total ", () => { const summaryTitles = 'dt[class="ons-summary__item-title"]'; @@ -21,7 +21,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ it("When I continue and enter numbers on dynamic and static answers page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { await $(TotalBlockPage.acceptCookies()).click(); await addTwoSupermarkets(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(33); await $$(DynamicAnswerPage.inputs())[1].setValue(33); @@ -33,19 +33,19 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ describe("Given I add list items with hardcoded total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic and static answers page that add up to that total, Then I should be able to get to the subsequent question", async () => { await addTwoSupermarkets(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); await $(DynamicAnswerPage.percentageOfShoppingElsewhere()).setValue(33); await click(DynamicAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(TotalBlockOtherPage.pageName); + await verifyUrlContains(TotalBlockOtherPage.pageName); }); }); describe("Given I add list items with custom total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic answers only page that don't add up to that total, Then validation error should be displayed with appropriate message", async () => { await addTwoSupermarkets(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(3); await $$(DynamicAnswerPage.inputs())[0].setValue(34); await $$(DynamicAnswerPage.inputs())[1].setValue(33); @@ -53,7 +53,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await click(DynamicAnswerPage.submit()); await $(TotalBlockOtherPage.totalOther()).setValue(100); await click(TotalBlockOtherPage.submit()); - await expect(browser).toHaveUrlContaining(DynamicAnswerOnlyPage.pageName); + await verifyUrlContains(DynamicAnswerOnlyPage.pageName); await $$(DynamicAnswerOnlyPage.inputs())[0].setValue(50); await $$(DynamicAnswerOnlyPage.inputs())[1].setValue(0); await click(DynamicAnswerOnlyPage.submit()); @@ -63,9 +63,9 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ describe("Given I add list items with custom total used for validation of dynamic answers", () => { it("When I continue and enter numbers on dynamic answers only page that add up to that total, Then I should be able to get to the summary", async () => { await addTwoSupermarkets(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await fillDynamicAnswers(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); + await verifyUrlContains(SectionSummaryPage.pageName); }); }); describe("Given I add list items and fill all the dynamic answers", () => { @@ -78,7 +78,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(4); }); }); @@ -89,7 +89,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $(SectionSummaryPage.supermarketsListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await expect(await $$(DynamicAnswerPage.labels()).length).toBe(2); }); }); @@ -100,7 +100,7 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ await $(SectionSummaryPage.supermarketsListEditLink(1)).click(); await $(ListCollectorEditPage.supermarketName()).setValue("Aldi"); await click(ListCollectorEditPage.submit()); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); + await verifyUrlContains(SectionSummaryPage.pageName); await expect(await $(SectionSummaryPage.groupContent(2)).$$(summaryTitles)[0].getText()).toBe("Percentage of shopping at Aldi"); }); }); @@ -108,13 +108,13 @@ describe("Feature: Sum of dynamic answers based on list and optional static answ it("When I journey backwards, Then I should be revisiting all the previous blocks", async () => { await addTwoSupermarkets(); await fillDynamicAnswers(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.pageName); + await verifyUrlContains(SectionSummaryPage.pageName); await $(SectionSummaryPage.previous()).click(); await $(DynamicAnswerOnlyPage.previous()).click(); await $(TotalBlockOtherPage.previous()).click(); await $(DynamicAnswerPage.previous()).click(); await $(ListCollectorPage.previous()).click(); - await expect(browser).toHaveUrlContaining(DriverPage.pageName); + await verifyUrlContains(DriverPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal.spec.js b/tests/functional/spec/features/validation/sum/equal.spec.js index a0067437a9..fc3694525f 100644 --- a/tests/functional/spec/features/validation/sum/equal.spec.js +++ b/tests/functional/spec/features/validation/sum/equal.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); @@ -22,7 +22,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); @@ -37,14 +37,14 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await click(TotalAnswerPage.submit()); await browser.url(SubmitPage.url()); - await expect(browser).toHaveUrlContaining(BreakdownAnswerPage.pageName); + await verifyUrlContains(BreakdownAnswerPage.pageName); await click(BreakdownAnswerPage.submit()); await expect(await $(BreakdownAnswerPage.errorNumber(1)).getText()).toBe("Enter answers that add up to 15"); await answerAndSubmitBreakdownQuestion("6", "3", "3", "3"); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); @@ -54,7 +54,7 @@ describe("Feature: Sum of grouped answers equal to validation against total ", ( await click(TotalAnswerPage.submit()); await answerAndSubmitBreakdownQuestion("5", "", "", ""); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js index cb4d045c4e..185358053f 100644 --- a/tests/functional/spec/features/validation/sum/equal_multiple.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_multiple.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_multiple/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_multiple/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_multiple/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Sum validation (Multi Rule Equals)", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_multiple.json"); @@ -12,7 +12,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(TotalAnswerPage.total()).setValue("10"); await click(TotalAnswerPage.submit()); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.previous()).click(); await $(BreakdownAnswerPage.breakdown1()).setValue("0"); @@ -20,7 +20,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown3()).setValue("0"); await $(BreakdownAnswerPage.breakdown4()).setValue("0"); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.previous()).click(); await $(BreakdownAnswerPage.breakdown1()).setValue("1"); @@ -28,7 +28,7 @@ describe("Feature: Sum validation (Multi Rule Equals)", () => { await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("4"); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js index f261fe04b5..e8f774150e 100644 --- a/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_or_less_than.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal_or_less_than/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Sum of grouped answers validation (equal or less than) against total", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_equal_or_less_than.json"); @@ -16,7 +16,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); @@ -29,7 +29,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue("3"); await $(BreakdownAnswerPage.breakdown4()).setValue("3"); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); @@ -42,7 +42,7 @@ describe("Feature: Sum of grouped answers validation (equal or less than) agains await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js index 7928433900..d9ca116e3b 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_hub.spec.js @@ -8,7 +8,7 @@ import BreakdownSectionSummary from "../../../../generated_pages/validation_sum_ import HubPage from "../../../../base_pages/hub.page"; import ThankYouPage from "../../../../base_pages/thank-you.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; const companyOverviewSectionID = "company-overview-section"; const breakdownSectionId = "breakdown-section"; @@ -61,7 +61,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in await answerAndSubmitTurnoverBreakdownQuestion(500, 250, 250); await answerAndSubmitEmployeeBreakdownQuestion(5, 5); - await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); + await verifyUrlContains(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Completed"); @@ -112,7 +112,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in it("When I update my answers to equal the new total turnover, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { await answerAndSubmitTurnoverBreakdownQuestion(500, 500, 500); - await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); + await verifyUrlContains(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(breakdownSectionId)).getText()).toBe("Completed"); }); @@ -120,7 +120,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Total in it("When I submit the questionnaire, Then I should see the thank you page", async () => { await $(HubPage.submit()).scrollIntoView(); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js index 1420021d3a..24e868d9f1 100644 --- a/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js +++ b/tests/functional/spec/features/validation/sum/equal_total_in_separate_section_repeating.spec.js @@ -13,7 +13,7 @@ import BreakdownSectionSummary from "../../../../generated_pages/validation_sum_ import HubPage from "../../../../base_pages/hub.page"; import ThankYouPage from "../../../../base_pages/thank-you.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; const householderSectionId = "householders-section"; const householdOverviewSectionId = "household-overview-section"; const repeatingSectionId = (repeatIndex) => { @@ -74,7 +74,7 @@ const assertRepeatingSectionOnChange = (repeatIndex, currentBreakdown1, currentB it("When I update my answers to equal the new total spending, Then I should be able to get to the section summary and the breakdown section should be marked as 'Completed'", async () => { await answerAndSubmitSpendingBreakdownQuestion(newTotal, 0, 0); - await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); + await verifyUrlContains(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(repeatIndex))).getText()).toBe("Completed"); }); @@ -121,7 +121,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await answerAndSubmitSpendingBreakdownQuestion(500, 250, 250); await answerAndSubmitEntertainmentBreakdownQuestion(250, 150, 100); - await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); + await verifyUrlContains(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(1))).getText()).toBe("Completed"); @@ -132,7 +132,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating await $(BreakdownDrivingPage.no()).click(); await click(BreakdownDrivingPage.submit()); - await expect(browser).toHaveUrlContaining(BreakdownSectionSummary.pageName); + await verifyUrlContains(BreakdownSectionSummary.pageName); await click(BreakdownSectionSummary.submit()); await expect(await $(HubPage.summaryRowState(repeatingSectionId(2))).getText()).toBe("Completed"); @@ -195,7 +195,7 @@ describe("Feature: Validation - Sum of grouped answers to equal total (Repeating it("When I submit the questionnaire, Then I should see the thank you page", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/validation/sum/less_than.spec.js b/tests/functional/spec/features/validation/sum/less_than.spec.js index be821d34d9..4a9a0d4269 100644 --- a/tests/functional/spec/features/validation/sum/less_than.spec.js +++ b/tests/functional/spec/features/validation/sum/less_than.spec.js @@ -1,7 +1,7 @@ import TotalAnswerPage from "../../../../generated_pages/validation_sum_against_total_less_than/total-block.page"; import BreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_total_less_than/breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_less_than/submit.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; describe("Feature: Sum of grouped answers validation (less than) against total", () => { beforeEach(async () => { await browser.openQuestionnaire("test_validation_sum_against_total_less_than.json"); @@ -16,7 +16,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue("2"); await $(BreakdownAnswerPage.breakdown4()).setValue("2"); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); @@ -29,7 +29,7 @@ describe("Feature: Sum of grouped answers validation (less than) against total", await $(BreakdownAnswerPage.breakdown3()).setValue(""); await $(BreakdownAnswerPage.breakdown4()).setValue(""); await click(BreakdownAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/features/validation/sum/value_source.spec.js b/tests/functional/spec/features/validation/sum/value_source.spec.js index f6acefef1b..c6b1ab6884 100644 --- a/tests/functional/spec/features/validation/sum/value_source.spec.js +++ b/tests/functional/spec/features/validation/sum/value_source.spec.js @@ -4,7 +4,7 @@ import TotalPlaybackPage from "../../../../generated_pages/validation_sum_agains import SecondBreakdownAnswerPage from "../../../../generated_pages/validation_sum_against_value_source/second-breakdown-block.page"; import SubmitPage from "../../../../generated_pages/validation_sum_against_total_equal/submit.page"; import AnotherTotalPlaybackPage from "../../../../generated_pages/validation_sum_against_value_source/another-number-total-playback.page"; -import { click } from "../../../../helpers"; +import { click, verifyUrlContains } from "../../../../helpers"; const answerAndSubmitBreakdownQuestion = async (breakdown1, breakdown2, breakdown3, breakdown4) => { await $(BreakdownAnswerPage.breakdown1()).setValue(breakdown1); await $(BreakdownAnswerPage.breakdown2()).setValue(breakdown2); @@ -41,7 +41,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerAndSubmitBreakdownQuestion("3", "3", "3", "3"); - await expect(browser).toHaveUrlContaining(TotalPlaybackPage.pageName); + await verifyUrlContains(TotalPlaybackPage.pageName); }); }); @@ -52,7 +52,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["3", "3", "3", "3"], ["2", "2", "1", "1"]); - await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); + await verifyUrlContains(AnotherTotalPlaybackPage.pageName); }); }); @@ -77,7 +77,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["6", "3", "3", "3"], ["3", "3", "2", "1"]); - await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); + await verifyUrlContains(AnotherTotalPlaybackPage.pageName); }); }); @@ -102,7 +102,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await answerBothBreakdownQuestions(["5", "4", "4", "2"], ["3", "3", "2", "1"]); - await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); + await verifyUrlContains(AnotherTotalPlaybackPage.pageName); }); }); @@ -131,7 +131,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await expect(await $(SecondBreakdownAnswerPage.singleErrorLink()).isDisplayed()).toBe(false); - await expect(browser).toHaveUrlContaining(AnotherTotalPlaybackPage.pageName); + await verifyUrlContains(AnotherTotalPlaybackPage.pageName); }); }); @@ -180,7 +180,7 @@ describe("Feature: Sum of grouped answers equal to validation against value sour await click(AnotherTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js index 110019f7de..ec66ee4280 100644 --- a/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js +++ b/tests/functional/spec/features/view_submitted_response/view_submitted_response.spec.js @@ -20,7 +20,7 @@ import DependencyQuestionSectionTwo from "../../../generated_pages/view_submitte import SkippableBlockSectionTwo from "../../../generated_pages/view_submitted_response_repeating_sections/skippable-block.page"; import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; import ListCollectorAddPage from "../../../generated_pages/view_submitted_response_repeating_sections//list-collector-add.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("View Submitted Response", () => { beforeEach("Load the questionnaire", async () => { @@ -30,10 +30,10 @@ describe("View Submitted Response", () => { await $(AddressBlockPage.answer()).setValue("NP10 8XG"); await click(AddressBlockPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); + await verifyUrlContains(ViewSubmittedResponsePage.pageName); }); it("Given I have completed a questionnaire with view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { @@ -123,10 +123,10 @@ describe("View Submitted Response Summary Page With Repeating Sections", () => { await click(SectionSummarySectionTwo.submit()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); + await verifyUrlContains(ViewSubmittedResponsePage.pageName); }); it("Given I have completed a questionnaire with a repeating section and view submitted response enabled, When I am on the view submitted response page within 45 minutes of submission, Then the summary is displayed correctly", async () => { diff --git a/tests/functional/spec/feedback.spec.js b/tests/functional/spec/feedback.spec.js index 02fcb667f0..3d0310de0e 100644 --- a/tests/functional/spec/feedback.spec.js +++ b/tests/functional/spec/feedback.spec.js @@ -4,7 +4,7 @@ import SubmitPage from "../generated_pages/feedback/submit.page"; import FeedbackPage from "../base_pages/feedback.page"; import FeedbackSentPage from "../base_pages/feedback-sent.page"; import ThankYouPage from "../base_pages/thank-you.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Feedback", () => { describe("Given I launch and complete the test feedback survey", () => { @@ -15,7 +15,7 @@ describe("Feedback", () => { }); it("When I view the thank you page, Then I can see the feedback call to action", async () => { - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.feedback()).getText()).toContain("What do you think about this service?"); await expect(await $(ThankYouPage.feedbackLink()).getText()).toBe("Give feedback"); await expect(await $(ThankYouPage.feedbackLink()).getAttribute("href")).toContain("/submitted/feedback/send"); @@ -23,10 +23,10 @@ describe("Feedback", () => { it("When I try to submit without providing feedback, then I stay on the feedback page and get an error message", async () => { await browser.url(FeedbackPage.url()); - await expect(browser).toHaveUrlContaining(FeedbackPage.pageName); + await verifyUrlContains(FeedbackPage.pageName); await expect(await $(FeedbackPage.feedbackTitle()).getText()).toBe("Give feedback about this service"); await click(FeedbackPage.submit()); - await expect(browser).toHaveUrlContaining(FeedbackPage.pageName); + await verifyUrlContains(FeedbackPage.pageName); await expect(await $(FeedbackPage.errorPanel()).isExisting()).toBe(true); await expect(await $(FeedbackPage.errorPanel()).getText()).toBe( "There are 2 problems with your feedback\nSelect what your feedback is about\nEnter your feedback", @@ -38,7 +38,7 @@ describe("Feedback", () => { await $(FeedbackPage.feedbackTypeGeneralFeedback()).click(); await $(FeedbackPage.feedbackText()).setValue("Well done!"); await click(FeedbackPage.submit()); - await expect(browser).toHaveUrlContaining(FeedbackSentPage.pageName); + await verifyUrlContains(FeedbackSentPage.pageName); await expect(await $(FeedbackSentPage.feedbackThankYouText()).getText()).toBe("Thank you for your feedback"); }); @@ -48,7 +48,7 @@ describe("Feedback", () => { await $(FeedbackPage.feedbackText()).setValue("Well done!"); await click(FeedbackPage.submit()); await $(FeedbackSentPage.doneButton()).click(); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); await expect(await $(ThankYouPage.title()).getText()).toBe("Thank you for completing the Feedback test schema"); }); }); diff --git a/tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js b/tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js index 89a692e914..aaddfb2588 100644 --- a/tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js +++ b/tests/functional/spec/journeys/enabled-sections/enabled_section_checkbox.spec.js @@ -1,7 +1,7 @@ import sectionOne from "../../../generated_pages/section_enabled_checkbox/section-1-block.page"; import sectionTwo from "../../../generated_pages/section_enabled_checkbox/section-2-block.page"; import SubmitPage from "../../../generated_pages/section_enabled_checkbox/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Section Enabled Based On Checkbox Answers", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire("test_section_enabled_checkbox.json"); @@ -11,14 +11,14 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { await $(sectionOne.section1Section2()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining("section-2-block"); + await verifyUrlContains("section-2-block"); }); it("When the user selects `Section 3` and submits, Then section 2 should not be displayed and section 3 should be displayed", async () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining("section-3-block"); + await verifyUrlContains("section-3-block"); }); it("When the user selects `Section 2` and `Section 3` and submits, Then section 2 and section 3 should be displayed", async () => { @@ -26,16 +26,16 @@ describe("Feature: Section Enabled Based On Checkbox Answers", () => { await $(sectionOne.section1Section3()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining("section-2-block"); + await verifyUrlContains("section-2-block"); await click(sectionTwo.submit()); - await expect(browser).toHaveUrlContaining("section-3-block"); + await verifyUrlContains("section-3-block"); }); it("When the user selects `Neither` and submits, Then they should be taken straight to the summary", async () => { await $(sectionOne.section1ExclusiveNeither()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); await expect(await $(SubmitPage.section2Question()).isExisting()).toBe(false); await expect(await $(SubmitPage.section3Question()).isExisting()).toBe(false); }); diff --git a/tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js b/tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js index b69f0720d6..cd18bcaf63 100644 --- a/tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js +++ b/tests/functional/spec/journeys/enabled-sections/enabled_section_radio.spec.js @@ -1,6 +1,6 @@ import sectionOne from "../../../generated_pages/section_enabled_radio/section-1-block.page"; import SubmitPage from "../../../generated_pages/section_enabled_radio/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Section Enabled Based On Radio Answers", () => { beforeEach("Open survey", async () => { await browser.openQuestionnaire("test_section_enabled_radio.json"); @@ -10,14 +10,14 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { await $(sectionOne.yesEnableSection2()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining("section-2-block"); + await verifyUrlContains("section-2-block"); }); it("When the user answers `No, disable section 2` and submits, Then they should be taking straight to the summary", async () => { await $(sectionOne.noDisableSection2()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); await expect(await $(SubmitPage.section2Question()).isExisting()).toBe(false); }); @@ -26,16 +26,16 @@ describe("Feature: Section Enabled Based On Radio Answers", () => { await $(sectionOne.yesEnableSection2()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining("section-2-block"); + await verifyUrlContains("section-2-block"); }); it("When the user changes the answers and disables section 2, Then they should be taken straight to the summary", async () => { await browser.back(); - await expect(browser).toHaveUrlContaining("section-1-block"); + await verifyUrlContains("section-1-block"); await $(sectionOne.noDisableSection2()).click(); await click(sectionOne.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); }); }); diff --git a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js index 5c350f041d..9b5d81f165 100644 --- a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js @@ -10,7 +10,7 @@ import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; import RelationshipsSummary from "../../../generated_pages/hub_and_spoke/relationships-section-summary.page.js"; import ListCollectorSectionSummaryPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector-section-summary.page.js"; import ProxyRepeatPage from "../../../generated_pages/hub_section_required_with_repeat/proxy.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; import DateOfBirthPage from "../../../generated_pages/hub_section_required_with_repeat/date-of-birth.page"; import PrimaryPersonListCollectorPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector.page"; import PrimaryPersonListCollectorAddPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector-add.page"; @@ -58,10 +58,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user click the 'Save and sign out' button then they should be redirected to the correct log out url", async () => { await $(HubPage.saveSignOut()).click(); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain("/signed-out"); + await verifyUrlContains("/signed-out"); }); it("When a user views the Hub, Then the page title should be Choose another section to complete", async () => { @@ -83,15 +80,13 @@ describe("Feature: Hub and Spoke", () => { it("When the user starts a section, Then the first question in the section should be displayed", async () => { await click(HubPage.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(EmploymentStatusBlockPage.url()); + await verifyUrlContains(EmploymentStatusBlockPage.url()); }); it("When the user starts a section and clicks the Previous link on the first question, Then they should be taken back to the Hub", async () => { await click(HubPage.submit()); await $(EmploymentStatusBlockPage.previous()).click(); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HubPage.url()); + await verifyUrlPathIs(HubPage.url()); }); }); @@ -118,8 +113,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user returns to the Hub and restarts the same section, Then they should be redirected to the first incomplete block", async () => { await browser.url(HubPage.url()); await $(HubPage.summaryRowLink("employment-section")).click(); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(EmploymentTypeBlockPage.url()); + await verifyUrlContains(EmploymentTypeBlockPage.url()); }); }); @@ -134,8 +128,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user clicks the 'Continue' button, it should return them to the hub", async () => { await click(EmploymentTypeBlockPage.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HubPage.url()); + await verifyUrlPathIs(HubPage.url()); }); it("When the user returns to the Hub, Then the Hub should be in a continue state", async () => { @@ -153,16 +146,14 @@ describe("Feature: Hub and Spoke", () => { it("When the user returns to the Hub and clicks the 'View answers' link on the Hub, if this no summary they are returned to the first block", async () => { await click(EmploymentTypeBlockPage.submit()); await $(HubPage.summaryRowLink("employment-section")).click(); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(EmploymentStatusBlockPage.url()); + await verifyUrlContains(EmploymentStatusBlockPage.url()); }); it("When the user returns to the Hub and continues, Then they should progress to the next section", async () => { await click(EmploymentTypeBlockPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); await click(HubPage.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(ProxyPage.url()); + await verifyUrlContains(ProxyPage.url()); }); }); @@ -178,21 +169,19 @@ describe("Feature: Hub and Spoke", () => { it("When the user clicks the 'View answers' link and incompletes the section, Then they the should be taken to the next incomplete question on 'Continue", async () => { await $(HubPage.summaryRowLink("employment-section")).click(); - await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); + await verifyUrlContains(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); await click(EmploymentStatusBlockPage.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(EmploymentTypeBlockPage.url()); + await verifyUrlContains(EmploymentTypeBlockPage.url()); }); it("When the user clicks the 'View answers' link and incompletes the section and returns to the hub, Then the section should be marked as 'Partially completed'", async () => { await $(HubPage.summaryRowLink("employment-section")).click(); - await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); + await verifyUrlContains(EmploymentStatusBlockPage.url()); await $(EmploymentStatusBlockPage.exclusiveNoneOfTheseApply()).click(); await click(EmploymentStatusBlockPage.submit()); await browser.url(HubPage.url()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HubPage.url()); + await verifyUrlPathIs(HubPage.url()); await expect(await $(HubPage.summaryRowState("employment-section")).getText()).toBe("Partially completed"); await expect(await $(HubPage.summaryRowLink("employment-section")).getHTML()).toContain("Continue with section: Employment"); }); @@ -221,8 +210,7 @@ describe("Feature: Hub and Spoke", () => { }); it("It should return them to the hub", async () => { - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HubPage.url()); + await verifyUrlPathIs(HubPage.url()); }); it("When the user returns to the Hub, Then the Hub should be in a completed state", async () => { @@ -232,8 +220,7 @@ describe("Feature: Hub and Spoke", () => { it("When the user submits, it should show the thankyou page", async () => { await click(HubPage.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain("thank-you"); + await verifyUrlContains("thank-you"); }); }); @@ -243,7 +230,7 @@ describe("Feature: Hub and Spoke", () => { }); it("The hub should not show first of all", async () => { - await expect(browser).toHaveUrlContaining(EmploymentStatusBlockPage.url()); + await verifyUrlContains(EmploymentStatusBlockPage.url()); }); it("The hub should only display when required sections are complete", async () => { @@ -251,7 +238,7 @@ describe("Feature: Hub and Spoke", () => { await click(EmploymentStatusBlockPage.submit()); await $(EmploymentTypeBlockPage.studying()).click(); await click(EmploymentTypeBlockPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); }); }); @@ -292,7 +279,7 @@ describe("Feature: Hub and Spoke", () => { await $(DateOfBirthPage.month()).setValue(1); await $(DateOfBirthPage.year()).setValue(2000); await $(RepeatingSummaryPage.submit()).click(); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); }); it("When the repeating sections are incomplete, Then the hub should not be displayed", async () => { @@ -310,7 +297,7 @@ describe("Feature: Hub and Spoke", () => { await $(ProxyRepeatPage.submit()).click(); await browser.url(HubPage.url()); - await expect(browser).toHaveUrlContaining("date-of-birth"); + await verifyUrlContains("date-of-birth"); }); }); @@ -341,7 +328,7 @@ describe("Feature: Hub and Spoke", () => { await $(LengthOfEmploymentPage.year()).setValue(1930); await click(LengthOfEmploymentPage.submit()); await click(Section3Page.submit()); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); }); it("When the repeating sections are incomplete. Then the hub should not be displayed", async () => { @@ -357,7 +344,7 @@ describe("Feature: Hub and Spoke", () => { await click(Section3Page.submit()); await browser.url(HubPage.url()); - await expect(browser).toHaveUrlContaining("length-of-employment"); + await verifyUrlContains("length-of-employment"); }); }); @@ -374,8 +361,7 @@ describe("Feature: Hub and Spoke", () => { it("When there are no changes, continue returns directly to the hub", async () => { await $(HubPage.summaryRowLink("household-section")).click(); await click(HouseholdSummary.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HubPage.url()); + await verifyUrlPathIs(HubPage.url()); await expect(await $(HubPage.summaryRowLink("household-section")).getHTML()).toContain("View answers: Household residents"); }); @@ -385,8 +371,13 @@ describe("Feature: Hub and Spoke", () => { await $(DoesAnyoneLiveHere.yes()).click(); await click(DoesAnyoneLiveHere.submit()); await click(HouseholdSummary.submit()); - const expectedUrl = await browser.getUrl(); - await expect(expectedUrl).toContain(HowManyPeopleLiveHere.url()); + await verifyUrlContains(HowManyPeopleLiveHere.url()); }); }); }); + +async function verifyUrlPathIs(expectedUrlPath) { + // Hub and Spoke URLs are "/questionnaire/", so we need strict checking of the URL path + const actualUrlPath = new URL(await browser.getUrl()).pathname; + await expect(actualUrlPath).toBe(expectedUrlPath); +} diff --git a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js index b15893849d..253ea4edb5 100644 --- a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js +++ b/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke_required_enable.spec.js @@ -1,7 +1,7 @@ import HouseholdRelationshipsBlockPage from "../../../generated_pages/hub_section_required_and_enabled/household-relationships-block.page"; import RelationshipsCountPage from "../../../generated_pages/hub_section_required_and_enabled/relationships-count.page"; import { SubmitPage } from "../../../base_pages/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Hub and spoke section required and enabled", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_hub_section_required_and_enabled.json"); @@ -16,6 +16,6 @@ describe("Hub and spoke section required and enabled", () => { await click(HouseholdRelationshipsBlockPage.submit()); await expect(await $("body").getText()).toContain("Submit survey"); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); }); diff --git a/tests/functional/spec/journeys/progress/progress_value_source_blocks.js b/tests/functional/spec/journeys/progress/progress_value_source_blocks.js index 7bb57f6b21..a810d5e34b 100644 --- a/tests/functional/spec/journeys/progress/progress_value_source_blocks.js +++ b/tests/functional/spec/journeys/progress/progress_value_source_blocks.js @@ -8,7 +8,7 @@ import SixthQuestionPage from "../../../generated_pages/progress_value_source_bl import SeventhQuestionPage from "../../../generated_pages/progress_value_source_blocks/s1-b7.page"; import SubmitPage from "../../../generated_pages/progress_value_source_blocks/submit.page"; import HubPage from "../../../base_pages/hub.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing based on progress value sources using block identifiers", () => { beforeEach(async () => { await browser.openQuestionnaire("test_progress_value_source_blocks.json"); @@ -19,7 +19,7 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(ThirdQuestionPage.pageName); + await verifyUrlContains(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); await click(ThirdQuestionPage.submit()); @@ -29,7 +29,7 @@ describe("Feature: Routing based on progress value sources using block identifi await $(SeventhQuestionPage.q1A1()).setValue("3"); await click(SeventhQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $("body").getText()).not.toBe("Section 1 Question 2"); await expect(await $("body").getText()).not.toBe("Section 1 Question 4"); }); @@ -40,28 +40,28 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); + await verifyUrlContains(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); await click(ThirdQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); + await verifyUrlContains(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); await click(FifthQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); + await verifyUrlContains(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); await click(SeventhQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $("body").getText()).toContain("Section 1 Question 4"); await expect(await $("body").getText()).toContain("Section 1 Question 6"); }); @@ -72,7 +72,7 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(ThirdQuestionPage.pageName); + await verifyUrlContains(ThirdQuestionPage.pageName); await $(ThirdQuestionPage.q1A1()).setValue("1"); await click(ThirdQuestionPage.submit()); @@ -83,27 +83,27 @@ describe("Feature: Routing based on progress value sources using block identifi await click(SeventhQuestionPage.submit()); await $(SubmitPage.s1B1Q1A1Edit()).click(); - await expect(browser).toHaveUrlContaining(FirstQuestionPage.pageName); + await verifyUrlContains(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); + await verifyUrlContains(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await click(ThirdQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); + await verifyUrlContains(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await click(FifthQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); + await verifyUrlContains(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("3"); await click(SixthQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $("body").getText()).toContain("Section 1 Question 4"); await expect(await $("body").getText()).toContain("Section 1 Question 6"); }); @@ -114,30 +114,30 @@ describe("Feature: Routing based on progress value sources using block identifi await $(FirstQuestionPage.q1A1()).setValue("1"); await click(FirstQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SecondQuestionPage.pageName); + await verifyUrlContains(SecondQuestionPage.pageName); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); await $(ThirdQuestionPage.q1A1()).setValue("2"); await click(ThirdQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(FourthQuestionPage.pageName); + await verifyUrlContains(FourthQuestionPage.pageName); await $(FourthQuestionPage.q1A1()).setValue("3"); await click(FourthQuestionPage.submit()); await $(FifthQuestionPage.q1A1()).setValue("4"); await click(FifthQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SixthQuestionPage.pageName); + await verifyUrlContains(SixthQuestionPage.pageName); await $(SixthQuestionPage.q1A1()).setValue("5"); await click(SixthQuestionPage.submit()); await $(SeventhQuestionPage.q1A1()).setValue("6"); await click(SeventhQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.s1B1Q1A1Edit()).click(); - await expect(browser).toHaveUrlContaining(FirstQuestionPage.pageName); + await verifyUrlContains(FirstQuestionPage.pageName); await $(FirstQuestionPage.q1A1()).setValue("0"); await click(FirstQuestionPage.submit()); @@ -158,7 +158,7 @@ describe("Feature: Section enabled based on progress value sources using block i await click(FirstQuestionPage.submit()); await $(SecondQuestionPage.q1A1()).setValue("1"); await click(SecondQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(ThirdQuestionSectionTwoPage.pageName); + await verifyUrlContains(ThirdQuestionSectionTwoPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/progress/progress_value_source_repeating.js b/tests/functional/spec/journeys/progress/progress_value_source_repeating.js index 94f95adb0c..d46ecf8596 100644 --- a/tests/functional/spec/journeys/progress/progress_value_source_repeating.js +++ b/tests/functional/spec/journeys/progress/progress_value_source_repeating.js @@ -11,7 +11,7 @@ import FirstNumberBlockPage from "../../../generated_pages/progress_value_source import SecondNumberBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/second-number-block.page"; import SectionTwoQuestionBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/s2-b1.page"; import CalculatedSummaryBlockPage from "../../../generated_pages/progress_value_source_calculated_summary/calculated-summary-block.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing rules based on progress value sources in repeating sections", () => { beforeEach(async () => { await browser.openQuestionnaire("test_progress_block_value_source_repeating_sections.json"); @@ -27,14 +27,14 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(QuestionBlockPage.pageName); + await verifyUrlContains(QuestionBlockPage.pageName); await browser.url(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("section-2-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SectionTwoSummaryPage.pageName); + await verifyUrlContains(SectionTwoSummaryPage.pageName); }); }); @@ -57,7 +57,7 @@ describe("Feature: Routing rules based on progress value sources in repeating se await $(HubPage.summaryRowLink("section-2-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(browser).toHaveUrlContaining(OtherQuestionBlockPage.pageName); + await verifyUrlContains(OtherQuestionBlockPage.pageName); }); }); @@ -118,7 +118,7 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); @@ -146,7 +146,7 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); @@ -189,11 +189,11 @@ describe("Feature: Routing rules based on progress value sources in repeating se await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await $(HubPage.summaryRowLink("section-3-1")).click(); await click(DOBQuestionBlockPage.submit()); - await expect(browser).toHaveUrlContaining(OtherQuestionBlockPage.pageName); + await verifyUrlContains(OtherQuestionBlockPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js b/tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js index b2578e6111..733fc607df 100644 --- a/tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js +++ b/tests/functional/spec/journeys/repeating_blocks/list_collector_repeating_blocks.spec.js @@ -9,7 +9,7 @@ import AnyOtherCompaniesOrBranchesPage from "../../../generated_pages/list_colle import SectionCompaniesPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/section-companies-summary.page"; import AnyOtherTradingDetailsPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/any-other-trading-details.page"; import SubmitPage from "../../../generated_pages/list_collector_repeating_blocks_section_summary/submit.page"; -import { repeatingAnswerChangeLink, checkItemsInList, summaryItemComplete, click } from "../../../helpers"; +import { repeatingAnswerChangeLink, checkItemsInList, summaryItemComplete, click, verifyUrlContains } from "../../../helpers"; import HubPage from "../../../base_pages/hub.page"; import ResponsiblePartyHubPage from "../../../generated_pages/list_collector_repeating_blocks_with_hub/responsible-party-business.page"; import { expect } from "@wdio/globals"; @@ -72,7 +72,7 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherTradingDetailsPage.submit()); await click(SectionCompaniesPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); @@ -121,7 +121,7 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherTradingDetailsPage.submit()); await click(SectionCompaniesPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); @@ -168,7 +168,7 @@ describe("List Collector Repeating Blocks", () => { it("When an item has incomplete repeating blocks, Then using submit on the list collector page will navigate the user to the first incomplete repeating block.", async () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock1Page.pageName); + await verifyUrlContains(CompaniesRepeatingBlock1Page.pageName); }); it("When there are multiple incomplete items and only the first incomplete item is completed, Then attempting using Submit on the list collector page will navigate the user to the next incomplete item.", async () => { @@ -186,7 +186,7 @@ describe("List Collector Repeating Blocks", () => { await click(AnyOtherCompaniesOrBranchesPage.submit()); // The user is taken to the next incomplete repeating block - await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock2Page.pageName); + await verifyUrlContains(CompaniesRepeatingBlock2Page.pageName); }); it("When the last remaining incomplete repeating block is completed, Then all items are marked as completed with the checkmark icon.", async () => { @@ -205,18 +205,18 @@ describe("List Collector Repeating Blocks", () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); + await verifyUrlContains("section-companies/#any-other-trading-details-answer"); await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); + await verifyUrlContains("section-companies/#any-other-trading-details-answer"); }); it("When an answer is edited from the section summary which does not affect progress, Then pressing continue returns the user to the section summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("No"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(browser).toHaveUrlContaining("section-companies/#any-other-trading-details-answer"); + await verifyUrlContains("section-companies/#any-other-trading-details-answer"); }); it("When a user clicks a change link from the final summary and submits without changing an answer, Then the user is returned to the final summary anchored to the answer they clicked on", async () => { @@ -224,23 +224,23 @@ describe("List Collector Repeating Blocks", () => { await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await click(AnyOtherTradingDetailsPage.submit()); - await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); + await verifyUrlContains("submit/#any-other-trading-details-answer"); await $(SubmitPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.previous()).click(); - await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); + await verifyUrlContains("submit/#any-other-trading-details-answer"); }); it("When an an answer is edited from the final summary which does not affect progress, Then pressing continue returns the user to the final summary anchored to the answer they edited", async () => { await $(SectionCompaniesPage.anyOtherTradingDetailsAnswerEdit()).click(); await $(AnyOtherTradingDetailsPage.answer()).setValue("Yes"); await click(AnyOtherTradingDetailsPage.submit()); - await expect(browser).toHaveUrlContaining("submit/#any-other-trading-details-answer"); + await verifyUrlContains("submit/#any-other-trading-details-answer"); }); it("When all items are completed by the user, Then the questionnaire is able to be submitted.", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); @@ -292,7 +292,7 @@ describe("List Collector Repeating Blocks", () => { await click(HubPage.submit()); await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); - await expect(browser).toHaveUrlContaining(CompaniesRepeatingBlock1Page.pageName); + await verifyUrlContains(CompaniesRepeatingBlock1Page.pageName); }); it("When the user completes the incomplete blocks and returns to the list collector Page, Then the completed items should display the checkmark icon", async () => { @@ -303,7 +303,7 @@ describe("List Collector Repeating Blocks", () => { await click(CompaniesRepeatingBlock1Page.submit()); await $(CompaniesRepeatingBlock2Page.authorisedTraderUkRadioNo()).click(); await click(CompaniesRepeatingBlock2Page.submit()); - await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); + await verifyUrlContains(AnyOtherCompaniesOrBranchesPage.pageName); await summaryItemComplete(`dt[data-qa="list-item-1-label"]`, true); await summaryItemComplete(`dt[data-qa="list-item-2-label"]`, true); }); @@ -320,7 +320,7 @@ describe("List Collector Repeating Blocks", () => { // Navigating to the section summary will redirect to the list collector page await browser.url("questionnaire/sections/section-companies/"); - await expect(browser).toHaveUrlContaining(AnyOtherCompaniesOrBranchesPage.pageName); + await verifyUrlContains(AnyOtherCompaniesOrBranchesPage.pageName); }); it("When the incomplete repeating blocks are completed, Then the user is able to complete the section and is taken to the hub page.", async () => { @@ -336,7 +336,7 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(SectionCompaniesPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); }); it("When the user is on the Hub page and has completed the section, Then they are able to add additional companies using the Add link", async () => { @@ -354,12 +354,12 @@ describe("List Collector Repeating Blocks", () => { await $(AnyOtherCompaniesOrBranchesPage.no()).click(); await click(AnyOtherCompaniesOrBranchesPage.submit()); await click(SectionCompaniesPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); }); it("When the user has completed the list collector section and uses Submit on the hub page, Then the user will be redirected to the next section.", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ResponsiblePartyHubPage.pageName); + await verifyUrlContains(ResponsiblePartyHubPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js b/tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js index 1ade7a4872..5046b0b235 100644 --- a/tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js +++ b/tests/functional/spec/journeys/repeating_sections/repeating_sections_with_hub_and_spoke.spec.js @@ -16,7 +16,7 @@ import VisitorsDateOfBirthPage from "../../../generated_pages/repeating_sections import VisitorsListCollectorAddPage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block-add.page"; import VisitorsListCollectorPage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block.page"; import VisitorsListCollectorRemovePage from "../../../generated_pages/repeating_sections_with_hub_and_spoke/visitors-block-remove.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Repeating Sections with Hub and Spoke", () => { describe("Given the user has added some members to the household and is on the Hub", () => { before("Open survey and add household members", async () => { @@ -25,7 +25,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { // is pushing the submit button outside window await $(HubPage.acceptCookies()).click(); // Ensure we are on the Hub - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); // Ensure the first section is not started await expect(await $(HubPage.summaryRowState("section")).getText()).toBe("Not started"); // Start first section to add household members @@ -73,7 +73,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { beforeEach("Navigate to the Hub", async () => await browser.url(HubPage.url())); it("Then a section for each household member should be displayed", async () => { - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); await expect(await $(HubPage.summaryRowState("section")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowTitle("personal-details-section-1")).getText()).toBe("Marcus Twin"); @@ -92,7 +92,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); await $(ProxyPage.previous()).click(); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); }); it("When the user partially completes a repeating section, Then that section should be marked as 'Partially completed' on the Hub", async () => { @@ -110,7 +110,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await browser.url(HubPage.url()); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Partially completed"); }); @@ -138,13 +138,13 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await click(PersonalDetailsSummaryPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); it("When the user clicks 'View answers' for a completed repeating section, Then they are taken to the summary", async () => { await $(HubPage.summaryRowLink("personal-details-section-2")).click(); - await expect(browser).toHaveUrlContaining("/sections/personal-details-section"); + await verifyUrlContains("/sections/personal-details-section"); }); it("When the user views the summary for a repeating section, Then the page title is shown", async () => { @@ -165,7 +165,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { await $(VisitorsListCollectorAddPage.visitorFirstName()).setValue("Joe"); await $(VisitorsListCollectorAddPage.visitorLastName()).setValue("Public"); await click(VisitorsListCollectorAddPage.submit()); - await expect(browser).toHaveUrlContaining("/questionnaire/visitors-block"); + await verifyUrlContains("/questionnaire/visitors-block"); // Add second visitor await $(VisitorsListCollectorPage.yes()).click(); @@ -300,7 +300,7 @@ describe("Feature: Repeating Sections with Hub and Spoke", () => { it("When the user submits, it should show the thank you page", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); }); }); diff --git a/tests/functional/spec/journeys/routing/all_in.spec.js b/tests/functional/spec/journeys/routing/all_in.spec.js index 124f90549d..c29c644094 100644 --- a/tests/functional/spec/journeys/routing/all_in.spec.js +++ b/tests/functional/spec/journeys/routing/all_in.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_all/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-india-and-malta.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_all/country-interstitial-not-india-and-malta.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - ALL-IN Operator", () => { describe("Equals", () => { @@ -14,18 +14,18 @@ describe("Feature: Routing - ALL-IN Operator", () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); + await verifyUrlContains(CountryInterstitialPage.pageName); }); it("When I do select India only, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); + await verifyUrlContains(CountryInterstitialOtherPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); + await verifyUrlContains(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/and.spec.js b/tests/functional/spec/journeys/routing/and.spec.js index 2bcf19c927..680ae1878c 100644 --- a/tests/functional/spec/journeys/routing/and.spec.js +++ b/tests/functional/spec/journeys/routing/and.spec.js @@ -2,7 +2,7 @@ import FirstNumberQuestionPage from "../../../generated_pages/routing_and/number import SecondNumberQuestionPage from "../../../generated_pages/routing_and/number-question-2.page"; import CorrectAnswerPage from "../../../generated_pages/routing_and/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_and/incorrect-answer.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - And Operator", () => { describe("Equals", () => { @@ -16,7 +16,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the incorrect page", async () => { @@ -24,7 +24,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the incorrect page", async () => { @@ -32,7 +32,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { @@ -40,7 +40,7 @@ describe("Feature: Routing - And Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js index 0ca4e74ab3..bc703450a7 100644 --- a/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js +++ b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_checkbox.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList, click } from "../../../helpers"; +import { checkItemsInList, click, verifyUrlContains } from "../../../helpers"; import AnyoneLiveAtListCollector from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../../../generated_pages/answer_action_redirect_to_list_add_block_checkbox/anyone-else-live-at-remove.page"; @@ -13,20 +13,20 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { it('When the user selects "No", Then, they should be taken to the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); }); it('When the user selects "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.iThinkSo()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); - await expect(browser).toHaveUrlContaining("?previous=anyone-usually-live-at"); + await verifyUrlContains(AnyoneLiveAtListCollectorAddPage.pageName); + await verifyUrlContains("?previous=anyone-usually-live-at"); }); it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { @@ -34,7 +34,7 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); await click(AnyoneLiveAtListCollectorAddPage.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; await checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); @@ -42,30 +42,30 @@ describe("Answer Action: Redirect To List Add Question (Checkbox)", () => { it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneLiveAtListCollectorRemovePage.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).toBe(false); }); it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); + await verifyUrlContains(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js index 74354b1ad7..b6c4f26d6d 100644 --- a/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js +++ b/tests/functional/spec/journeys/routing/answer_action_redirect_to_list_add_block_radio.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList, click } from "../../../helpers"; +import { checkItemsInList, click, verifyUrlContains } from "../../../helpers"; import AnyoneLiveAtListCollector from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at.page"; import AnyoneLiveAtListCollectorAddPage from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-add.page"; import AnyoneLiveAtListCollectorRemovePage from "../../../generated_pages/answer_action_redirect_to_list_add_block_radio/anyone-else-live-at-remove.page"; @@ -13,20 +13,20 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { it('When the user answers "No", Then, they should be taken to straight the list collector.', async () => { await $(AnyoneUsuallyLiveAt.no()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); }); it('When the user answers "Yes" then they should be taken to the list collector add question.', async () => { await browser.url(AnyoneUsuallyLiveAt.url()); await $(AnyoneUsuallyLiveAt.yes()).click(); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); - await expect(browser).toHaveUrlContaining("?previous=anyone-usually-live-at"); + await verifyUrlContains(AnyoneLiveAtListCollectorAddPage.pageName); + await verifyUrlContains("?previous=anyone-usually-live-at"); }); it('When the user clicks the "Previous" link from the add question then they should be taken to the block they came from, not the list collector', async () => { await $(AnyoneLiveAtListCollectorAddPage.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); }); it("When the user adds a household member, Then, they are taken to the list collector and the household members are displayed", async () => { @@ -34,7 +34,7 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { await $(AnyoneLiveAtListCollectorAddPage.firstName()).setValue("Marcus"); await $(AnyoneLiveAtListCollectorAddPage.lastName()).setValue("Twin"); await click(AnyoneLiveAtListCollectorAddPage.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); const peopleExpected = ["Marcus Twin"]; await checkItemsInList(peopleExpected, AnyoneLiveAtListCollector.listLabel); @@ -42,30 +42,30 @@ describe("Answer Action: Redirect To List Add Question (Radio)", () => { it('When the user click the "Previous" link from the list collector, Then, they are taken to the last complete block', async () => { await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); }); it("When the user resubmits the first block and then list is not empty, Then they are taken to the list collector", async () => { await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); }); it("When the users removes the only person (Marcus Twain), Then, they are shown an empty list collector", async () => { await $(AnyoneLiveAtListCollector.listRemoveLink(1)).click(); await $(AnyoneLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneLiveAtListCollectorRemovePage.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); await expect(await $(AnyoneLiveAtListCollector.listLabel(1)).isExisting()).toBe(false); }); it("When the user resubmits the first block and then list is empty, Then they are taken to the add question", async () => { - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollector.pageName); + await verifyUrlContains(AnyoneLiveAtListCollector.pageName); await $(AnyoneLiveAtListCollector.previous()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAt.pageName); + await verifyUrlContains(AnyoneUsuallyLiveAt.pageName); await click(AnyoneUsuallyLiveAt.submit()); - await expect(browser).toHaveUrlContaining(AnyoneLiveAtListCollectorAddPage.pageName); + await verifyUrlContains(AnyoneLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/answer_not_on_path.spec.js b/tests/functional/spec/journeys/routing/answer_not_on_path.spec.js index 36f95e9f4f..45c137c5e2 100644 --- a/tests/functional/spec/journeys/routing/answer_not_on_path.spec.js +++ b/tests/functional/spec/journeys/routing/answer_not_on_path.spec.js @@ -3,7 +3,7 @@ import InvalidPathPage from "../../../generated_pages/routing_not_affected_by_an import InvalidPathInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/invalid-path-interstitial.page.js"; import ValidPathPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-path.page.js"; import ValidFinalInterstitialPage from "../../../generated_pages/routing_not_affected_by_answers_not_on_path/valid-final-interstitial.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Answers not on path are not considered when routing", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_not_affected_by_answers_not_on_path.json"); @@ -13,13 +13,13 @@ describe("Answers not on path are not considered when routing", () => { await $(InitialChoicePage.goHereFirst()).click(); await click(InitialChoicePage.submit()); - await expect(browser).toHaveUrlContaining(InvalidPathPage.pageName); + await verifyUrlContains(InvalidPathPage.pageName); await $(InvalidPathPage.answer()).setValue(123); await click(InvalidPathPage.submit()); // We now have an answer in the store on the 'invalid' path - await expect(browser).toHaveUrlContaining(InvalidPathInterstitialPage.pageName); + await verifyUrlContains(InvalidPathInterstitialPage.pageName); await $(InvalidPathInterstitialPage.previous()).click(); await $(InvalidPathPage.previous()).click(); @@ -32,6 +32,6 @@ describe("Answers not on path are not considered when routing", () => { await click(ValidPathPage.submit()); // We should be routed to the valid interstitial page since the invalid path answer should not be considered whilst routing. - await expect(browser).toHaveUrlContaining(ValidFinalInterstitialPage.pageName); + await verifyUrlContains(ValidFinalInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/journeys/routing/answered_unanswered.spec.js b/tests/functional/spec/journeys/routing/answered_unanswered.spec.js index 7387072cd4..e54f06b62c 100644 --- a/tests/functional/spec/journeys/routing/answered_unanswered.spec.js +++ b/tests/functional/spec/journeys/routing/answered_unanswered.spec.js @@ -9,7 +9,7 @@ import QuestionTwoUnanswered from "../../../generated_pages/routing_answered_una import QuestionThree from "../../../generated_pages/routing_answered_unanswered/block-3.page"; import QuestionThreeAnsweredOrNotZero from "../../../generated_pages/routing_answered_unanswered/answered-question-3.page"; import QuestionThreeUnansweredOrAnswerZero from "../../../generated_pages/routing_answered_unanswered/unanswered-or-zero-question-3.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Test routing question answered/unanswered", () => { describe("Given I am on the first question", () => { beforeEach("Load the questionnaire", async () => { @@ -20,19 +20,19 @@ describe("Test routing question answered/unanswered", () => { await $(QuestionOne.ham()).click(); await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You answered the first question!"); - await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); + await verifyUrlContains(QuestionOneAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionOne.cheese()).click(); await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You answered the first question!"); - await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); + await verifyUrlContains(QuestionOneAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the first question", async () => { await click(QuestionOne.submit()); await expect(await $(QuestionOneAnswered.heading()).getText()).toBe("You did not answer the first question!"); - await expect(browser).toHaveUrlContaining(QuestionOneAnswered.pageName); + await verifyUrlContains(QuestionOneAnswered.pageName); }); }); @@ -47,19 +47,19 @@ describe("Test routing question answered/unanswered", () => { await $(QuestionTwo.pizzaHut()).click(); await click(QuestionTwo.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You answered the second question!"); - await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); + await verifyUrlContains(QuestionTwoAnswered.pageName); await $(QuestionOneAnswered.previous()).click(); await $(QuestionTwo.dominoS()).click(); await click(QuestionTwo.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You answered the second question!"); - await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); + await verifyUrlContains(QuestionTwoAnswered.pageName); }); it("When I don't select an answer and submit, Then I should see a page saying I have not answered the second question", async () => { await click(QuestionTwo.submit()); await expect(await $(QuestionTwoUnanswered.heading()).getText()).toBe("You did not answer the second question!"); - await expect(browser).toHaveUrlContaining(QuestionTwoAnswered.pageName); + await verifyUrlContains(QuestionTwoAnswered.pageName); }); }); @@ -75,20 +75,20 @@ describe("Test routing question answered/unanswered", () => { it("When I do not answer the question or answer `0` and submit, Then I should see a page saying I did not answer the question or that I chose `0`", async () => { await click(QuestionThree.submit()); await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).toBe("You did not answer the question or chose 0 slices"); - await expect(browser).toHaveUrlContaining(QuestionThreeUnansweredOrAnswerZero.pageName); + await verifyUrlContains(QuestionThreeUnansweredOrAnswerZero.pageName); await $(QuestionThreeUnansweredOrAnswerZero.previous()).click(); await $(QuestionThree.answer3()).setValue("0"); await click(QuestionThree.submit()); await expect(await $(QuestionThreeUnansweredOrAnswerZero.heading()).getText()).toBe("You did not answer the question or chose 0 slices"); - await expect(browser).toHaveUrlContaining(QuestionThreeUnansweredOrAnswerZero.pageName); + await verifyUrlContains(QuestionThreeUnansweredOrAnswerZero.pageName); }); it("When I enter an answer greater than 0 and submit, Then I should see a page saying I chose at least one", async () => { await $(QuestionThree.answer3()).setValue("2"); await click(QuestionThree.submit()); await expect(await $(QuestionTwoAnswered.heading()).getText()).toBe("You chose at least 1 slice"); - await expect(browser).toHaveUrlContaining(QuestionThreeAnsweredOrNotZero.pageName); + await verifyUrlContains(QuestionThreeAnsweredOrNotZero.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/any_in.spec.js b/tests/functional/spec/journeys/routing/any_in.spec.js index 5a910993c6..129a5fa4d3 100644 --- a/tests/functional/spec/journeys/routing/any_in.spec.js +++ b/tests/functional/spec/journeys/routing/any_in.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_any/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-india-or-malta-or-both.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_any/country-interstitial-not-india-or-malta-or-both.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - ANY-IN Operator", () => { describe("Equals", () => { describe("Given I start the ANY-IN operator routing survey", () => { @@ -13,18 +13,18 @@ describe("Feature: Routing - ANY-IN Operator", () => { await $(CountryCheckboxPage.india()).click(); await $(CountryCheckboxPage.malta()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); + await verifyUrlContains(CountryInterstitialPage.pageName); }); it("When I do select India or Malta, Then I should be routed to the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); + await verifyUrlContains(CountryInterstitialPage.pageName); }); it("When I do not select India or Malta, Then I should be routed to the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); + await verifyUrlContains(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/boolean.spec.js b/tests/functional/spec/journeys/routing/boolean.spec.js index 4359c1e996..1b2a4ca021 100644 --- a/tests/functional/spec/journeys/routing/boolean.spec.js +++ b/tests/functional/spec/journeys/routing/boolean.spec.js @@ -1,7 +1,7 @@ import Block1Page from "../../../generated_pages/metadata_routing/block1.page"; import Block2Page from "../../../generated_pages/metadata_routing/block2.page"; import Block3Page from "../../../generated_pages/metadata_routing/block3.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - Boolean Flag", () => { it("Given I have a routing rule that uses a boolean flag and it is False, When I press continue, Then I should be routed to the correct page", async () => { @@ -9,7 +9,7 @@ describe("Feature: Routing - Boolean Flag", () => { booleanFlag: false, }); await click(Block1Page.submit()); - await expect(browser).toHaveUrlContaining(Block2Page.pageName); + await verifyUrlContains(Block2Page.pageName); }); it("Given I have a routing rule that uses a boolean flag and it is True, When I press continue, Then I should be routed to the correct page ", async () => { @@ -17,6 +17,6 @@ describe("Feature: Routing - Boolean Flag", () => { booleanFlag: true, }); await click(Block1Page.submit()); - await expect(browser).toHaveUrlContaining(Block3Page.pageName); + await verifyUrlContains(Block3Page.pageName); }); }); diff --git a/tests/functional/spec/journeys/routing/checkbox_count.spec.js b/tests/functional/spec/journeys/routing/checkbox_count.spec.js index 49e20925a6..7e4f5c75b8 100644 --- a/tests/functional/spec/journeys/routing/checkbox_count.spec.js +++ b/tests/functional/spec/journeys/routing/checkbox_count.spec.js @@ -1,7 +1,7 @@ import ToppingCheckboxPage from "../../../generated_pages/routing_checkbox_count/topping-checkbox.page.js"; import CorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_checkbox_count/incorrect-answer.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Test routing using count of checkboxes checked", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_checkbox_count.json"); @@ -12,14 +12,14 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.ham()).click(); await click(ToppingCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); await expect(await $(CorrectAnswerPage.questionText()).getText()).toBe("You selected 2 or more toppings"); }); it("Given a user selects no checkboxes, When they submit, Then they should be routed to the incorrect page", async () => { await click(ToppingCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); await expect(await $(IncorrectAnswerPage.questionText()).getText()).toBe("You did not select 2 or more toppings"); }); @@ -27,7 +27,7 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.cheese()).click(); await click(ToppingCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); await expect(await $(IncorrectAnswerPage.questionText()).getText()).toBe("You did not select 2 or more toppings"); }); @@ -37,7 +37,7 @@ describe("Test routing using count of checkboxes checked", () => { await $(ToppingCheckboxPage.pineapple()).click(); await click(ToppingCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); await expect(await $(CorrectAnswerPage.questionText()).getText()).toBe("You selected 2 or more toppings"); }); }); diff --git a/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js b/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js index c375ea447d..bd32466634 100644 --- a/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js +++ b/tests/functional/spec/journeys/routing/conditional_combined_routing.spec.js @@ -2,7 +2,7 @@ import ConditionalCombinedRoutingPage from "../../../generated_pages/conditional import ResponseAny from "../../../generated_pages/conditional_combined_routing/response-any.page"; import ResponseNotAny from "../../../generated_pages/conditional_combined_routing/response-not-any.page"; import SubmitPage from "../../../generated_pages/conditional_combined_routing/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Conditional combined routing.", () => { beforeEach(async () => { @@ -14,7 +14,7 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.yes()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(browser).toHaveUrlContaining(ResponseAny.pageName); + await verifyUrlContains(ResponseAny.pageName); // Or await $(ResponseAny.previous()).click(); @@ -24,7 +24,7 @@ describe("Conditional combined routing.", () => { await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(browser).toHaveUrlContaining(ResponseAny.pageName); + await verifyUrlContains(ResponseAny.pageName); }); it('Given a list of radio options, when I choose the option "No, I prefer tea" then I should be routed to the relevant page', async () => { @@ -32,7 +32,7 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.noIPreferTea()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(browser).toHaveUrlContaining(ResponseNotAny.pageName); + await verifyUrlContains(ResponseNotAny.pageName); }); it('Given a list of radio options, when I choose the option "No, I don\'t drink any hot drinks" then I should be routed to the submit page', async () => { @@ -40,6 +40,6 @@ describe("Conditional combined routing.", () => { await $(ConditionalCombinedRoutingPage.noIDonTDrinkAnyHotDrinks()).click(); await click(ConditionalCombinedRoutingPage.submit()); // Then - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); diff --git a/tests/functional/spec/journeys/routing/date.spec.js b/tests/functional/spec/journeys/routing/date.spec.js index 6202b5e91b..d9418b57da 100644 --- a/tests/functional/spec/journeys/routing/date.spec.js +++ b/tests/functional/spec/journeys/routing/date.spec.js @@ -8,7 +8,7 @@ import DateGreaterThanQuestionPage from "../../../generated_pages/routing_date_g import DateGreaterThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_greater_than_or_equals/date-question.page"; import DateLessThanQuestionPage from "../../../generated_pages/routing_date_less_than/date-question.page"; import DateLessThanOrEqualsQuestionPage from "../../../generated_pages/routing_date_less_than_or_equals/date-question.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; const today = new Date(); const dayToday = today.getDate(); const monthToday = today.getMonth() + 1; // January is 0! @@ -42,7 +42,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the yesterday date, Then I should be routed to the correct page", async () => { @@ -50,7 +50,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the tomorrow date, Then I should be routed to the correct page", async () => { @@ -58,7 +58,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the last month date, Then I should be routed to the correct page", async () => { @@ -66,7 +66,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(2); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the next month date, Then I should be routed to the correct page", async () => { @@ -74,7 +74,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(4); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the last year date, Then I should be routed to the correct page", async () => { @@ -82,7 +82,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2019); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the next year date, Then I should be routed to the correct page", async () => { @@ -90,7 +90,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2021); await click(DateEqualsQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter an incorrect date, Then I should be routed to the incorrect page", async () => { @@ -98,7 +98,7 @@ describe("Feature: Routing on a Date", () => { await $(DateEqualsQuestionPage.month()).setValue(3); await $(DateEqualsQuestionPage.year()).setValue(2020); await click(DateEqualsComparisonQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); }); }); @@ -114,19 +114,14 @@ describe("Feature: Routing on a Date", () => { await $(DateNotEqualsQuestionPage.Year()).setValue(2018); await click(DateNotEqualsQuestionPage.submit()); - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter February 2018, Then I should be routed to the incorrect page", async () => { await $(DateNotEqualsQuestionPage.Month()).setValue(2); await $(DateNotEqualsQuestionPage.Year()).setValue(2018); await click(DateNotEqualsQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -142,10 +137,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.month()).setValue(3); await $(DateGreaterThanQuestionPage.year()).setValue(2017); await click(DateGreaterThanQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter the 1st March 2017, Then I should be routed to the incorrect page", async () => { @@ -153,10 +145,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.month()).setValue(3); await $(DateGreaterThanQuestionPage.year()).setValue(2017); await click(DateGreaterThanQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a date less than the 1st March 2017, Then I should be routed to the incorrect page", async () => { @@ -164,10 +153,7 @@ describe("Feature: Routing on a Date", () => { await $(DateGreaterThanQuestionPage.month()).setValue(2); await $(DateGreaterThanQuestionPage.year()).setValue(2017); await click(DateGreaterThanQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -181,28 +167,19 @@ describe("Feature: Routing on a Date", () => { it("When I enter a date greater than 2017, Then I should be routed to the correct page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2018); await click(DateGreaterThanOrEqualsQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 2017, Then I should be routed to the correct page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2017); await click(DateGreaterThanOrEqualsQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a date less than March 2017, Then I should be routed to the incorrect page", async () => { await $(DateGreaterThanOrEqualsQuestionPage.Year()).setValue(2016); await click(DateGreaterThanOrEqualsQuestionPage.submit()); - - const expectedUrl = await browser.getUrl(); - - await expect(expectedUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -218,10 +195,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.month()).setValue(monthYesterday); await $(DateLessThanQuestionPage.year()).setValue(yearYesterday); await click(DateLessThanQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a date equal to today, Then I should be routed to the incorrect page", async () => { @@ -229,10 +203,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.month()).setValue(monthToday); await $(DateLessThanQuestionPage.year()).setValue(yearToday); await click(DateLessThanQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -240,10 +211,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanQuestionPage.month()).setValue(monthTomorrow); await $(DateLessThanQuestionPage.year()).setValue(yearTomorrow); await click(DateLessThanQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -259,10 +227,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthYesterday); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearYesterday); await click(DateLessThanOrEqualsQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a date equal to today, Then I should be routed to the correct page", async () => { @@ -270,10 +235,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthToday); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearToday); await click(DateLessThanOrEqualsQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a date greater than today, Then I should be routed to the incorrect page", async () => { @@ -281,10 +243,7 @@ describe("Feature: Routing on a Date", () => { await $(DateLessThanOrEqualsQuestionPage.month()).setValue(monthTomorrow); await $(DateLessThanOrEqualsQuestionPage.year()).setValue(yearTomorrow); await click(DateLessThanOrEqualsQuestionPage.submit()); - - const browserUrl = await browser.getUrl(); - - await expect(browserUrl).toContain(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/in.spec.js b/tests/functional/spec/journeys/routing/in.spec.js index c5e0fad396..ee913c98ab 100644 --- a/tests/functional/spec/journeys/routing/in.spec.js +++ b/tests/functional/spec/journeys/routing/in.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_checkbox_contains_in/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-india.page"; import CountryInterstitialOtherPage from "../../../generated_pages/routing_checkbox_contains_in/country-interstitial-not-india.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - IN Operator", () => { describe("Equals", () => { describe("Given I start the IN operator routing survey", () => { @@ -12,13 +12,13 @@ describe("Feature: Routing - IN Operator", () => { it("When I do select India, Then I should be routed to the the correct answer interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); + await verifyUrlContains(CountryInterstitialPage.pageName); }); it("When I do not select India, Then I should be routed to the the incorrect answer interstitial page", async () => { await $(CountryCheckboxPage.liechtenstein()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialOtherPage.pageName); + await verifyUrlContains(CountryInterstitialOtherPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/not.spec.js b/tests/functional/spec/journeys/routing/not.spec.js index 85465d18e5..d5e4c2898b 100644 --- a/tests/functional/spec/journeys/routing/not.spec.js +++ b/tests/functional/spec/journeys/routing/not.spec.js @@ -1,7 +1,7 @@ import CountryCheckboxPage from "../../../generated_pages/routing_not/country-checkbox.page"; import CountryInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-not-india.page"; import IndiaInterstitialPage from "../../../generated_pages/routing_not/country-interstitial-india.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - Not Operator", () => { describe("Equals", () => { describe("Given I start the not operator routing survey", () => { @@ -12,13 +12,13 @@ describe("Feature: Routing - Not Operator", () => { it("When I do not select India, Then I should be routed to the not India interstitial page", async () => { await $(CountryCheckboxPage.azerbaijan()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(CountryInterstitialPage.pageName); + await verifyUrlContains(CountryInterstitialPage.pageName); }); it("When I select India, Then I should be routed to the India interstitial page", async () => { await $(CountryCheckboxPage.india()).click(); await click(CountryCheckboxPage.submit()); - await expect(browser).toHaveUrlContaining(IndiaInterstitialPage.pageName); + await verifyUrlContains(IndiaInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/number.spec.js b/tests/functional/spec/journeys/routing/number.spec.js index ba16e485d0..d4b60a1bf3 100644 --- a/tests/functional/spec/journeys/routing/number.spec.js +++ b/tests/functional/spec/journeys/routing/number.spec.js @@ -1,7 +1,7 @@ import NumberQuestionPage from "../../../generated_pages/routing_number_equals/number-question.page"; import CorrectAnswerPage from "../../../generated_pages/routing_number_equals/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_number_equals/incorrect-answer.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing on a Number", () => { describe("Equals", () => { describe("Given I start number routing equals survey", () => { @@ -12,14 +12,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a number that isn't 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -33,14 +33,14 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number that isn't 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(987); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -54,21 +54,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -82,21 +82,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(77); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); it("When I enter a number greater than 123, Then I should be routed to the incorrect page", async () => { await $(IncorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(765); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -110,21 +110,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number greater than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(555); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a number less than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(2); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); @@ -138,21 +138,21 @@ describe("Feature: Routing on a Number", () => { it("When I enter a number less than 123, Then I should be routed to the correct page", async () => { await $(NumberQuestionPage.answer()).setValue(23); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter 123, Then I should be routed to the correct page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(123); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I enter a number larger than 123, Then I should be routed to the incorrect page", async () => { await $(CorrectAnswerPage.previous()).click(); await $(NumberQuestionPage.answer()).setValue(546); await click(NumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/or.spec.js b/tests/functional/spec/journeys/routing/or.spec.js index c2ba961847..1529ff9996 100644 --- a/tests/functional/spec/journeys/routing/or.spec.js +++ b/tests/functional/spec/journeys/routing/or.spec.js @@ -2,7 +2,7 @@ import FirstNumberQuestionPage from "../../../generated_pages/routing_or/number- import SecondNumberQuestionPage from "../../../generated_pages/routing_or/number-question-2.page"; import CorrectAnswerPage from "../../../generated_pages/routing_or/correct-answer.page"; import IncorrectAnswerPage from "../../../generated_pages/routing_or/incorrect-answer.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing - OR Operator", () => { describe("Equals", () => { describe("Given I start the or operator routing survey", () => { @@ -15,7 +15,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I only enter the second answer correctly with 555 and 321, Then I should be routed to the correct page", async () => { @@ -23,7 +23,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(321); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I only enter the first answer correctly with 123 and 555, Then I should be routed to the correct page", async () => { @@ -31,7 +31,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(555); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(CorrectAnswerPage.pageName); + await verifyUrlContains(CorrectAnswerPage.pageName); }); it("When I answer both questions incorrectly with 555 and 444, Then I should be routed to the incorrect page", async () => { @@ -39,7 +39,7 @@ describe("Feature: Routing - OR Operator", () => { await click(FirstNumberQuestionPage.submit()); await $(SecondNumberQuestionPage.answer2()).setValue(444); await click(SecondNumberQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(IncorrectAnswerPage.pageName); + await verifyUrlContains(IncorrectAnswerPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/routing/removes_completed_block.spec.js b/tests/functional/spec/journeys/routing/removes_completed_block.spec.js index b2aef75bdc..d93963fa04 100644 --- a/tests/functional/spec/journeys/routing/removes_completed_block.spec.js +++ b/tests/functional/spec/journeys/routing/removes_completed_block.spec.js @@ -1,7 +1,7 @@ import NumberOfEmployeesTotalBlockPage from "../../../generated_pages/confirmation_question/number-of-employees-total-block.page.js"; import ConfirmZeroEmployeesBlockPage from "../../../generated_pages/confirmation_question/confirm-zero-employees-block.page.js"; import SubmitPage from "../../../generated_pages/confirmation_question/submit.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing incompletes block if routing backwards", () => { describe("Given I have a confirmation Question", () => { before("Get to summary", async () => { @@ -10,7 +10,7 @@ describe("Feature: Routing incompletes block if routing backwards", () => { await click(NumberOfEmployeesTotalBlockPage.submit()); await $(ConfirmZeroEmployeesBlockPage.yes()).click(); await click(ConfirmZeroEmployeesBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js index 258e92987b..7c897bb497 100644 --- a/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js +++ b/tests/functional/spec/journeys/skipping/routing_and_skipping_section_dependencies_calculated_summary.spec.js @@ -9,7 +9,7 @@ import SkipQuestionPage from "../../../generated_pages/routing_and_skipping_sect import ButterPage from "../../../generated_pages/routing_and_skipping_section_dependencies_calculated_summary/butter-block.page"; import HubPage from "../../../base_pages/hub.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Routing and skipping section dependencies based on calculated summaries", () => { describe("Given the section dependencies based on a calculated summary questionnaire", () => { @@ -53,7 +53,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); - await expect(browser).toHaveUrlContaining(FruitPage.pageName); + await verifyUrlContains(FruitPage.pageName); }); it("When a question in another section has a skip condition dependency on a calculated summary total, and the skip condition is met (total greater than £10), then the dependent question should not be displayed", async () => { @@ -69,7 +69,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await click(CalculatedSummarySectionSummaryPage.submit()); await $(HubPage.summaryRowLink("dependent-question-section")).click(); - await expect(browser).toHaveUrlContaining(VegetablesPage.pageName); + await verifyUrlContains(VegetablesPage.pageName); }); it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is greater than £100, then we should be routed to the second question block", async () => { @@ -87,7 +87,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); await click(VegetablesPage.submit()); - await expect(browser).toHaveUrlContaining(SecondQuestionBlockPage.pageName); + await verifyUrlContains(SecondQuestionBlockPage.pageName); }); it("When a question in another section has a routing rule dependency on a calculated summary total, and the calculated summary total is less than £100, then we should be routed to the section summary", async () => { @@ -105,7 +105,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(HubPage.summaryRowLink("dependent-question-section")).click(); await $(VegetablesPage.yes()).click(); await click(VegetablesPage.submit()); - await expect(browser).toHaveUrlContaining(DependentQuestionSectionSummaryPage.pageName); + await verifyUrlContains(DependentQuestionSectionSummaryPage.pageName); }); it("When a question in another section has a dependency on a calculated summary total, and both sections are complete, and I go back and edit the calculated summary total, then the dependent section status should be in progress", async () => { @@ -130,7 +130,7 @@ describe("Routing and skipping section dependencies based on calculated summarie await $(CurrencyTotalPlaybackPage.milkAnswerEdit()).click(); await $(FirstQuestionBlockPage.milk()).setValue(100); await click(FirstQuestionBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await click(CurrencyTotalPlaybackPage.submit()); await click(CalculatedSummarySectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("dependent-question-section")).getText()).toBe("Partially completed"); diff --git a/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js b/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js index a5a58e02a0..27a8ffefa9 100644 --- a/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js +++ b/tests/functional/spec/journeys/skipping/routing_checkbox_contains.spec.js @@ -2,7 +2,7 @@ import RoutingCheckboxContains from "../../../generated_pages/routing_checkbox_c import ContainsAllPage from "../../../generated_pages/routing_checkbox_contains/country-interstitial-all.page"; import ContainsAnyPage from "../../../generated_pages/routing_checkbox_contains/country-interstitial-any.page"; import SubmitPage from "../../../generated_pages/routing_checkbox_contains/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Routing Checkbox Contains Condition.", () => { beforeEach(async () => { await browser.openQuestionnaire("test_routing_checkbox_contains.json"); @@ -15,7 +15,7 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.india()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); + await verifyUrlContains(ContainsAnyPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -26,7 +26,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); + await verifyUrlContains(ContainsAnyPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -36,7 +36,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(ContainsAnyPage.pageName); + await verifyUrlContains(ContainsAnyPage.pageName); }); it('Given a list of checkbox options, when I select the option "Malta" or the option "Liechtenstein" or both then I should be routed to the summary condition page', async () => { @@ -44,7 +44,7 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.liechtenstein()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -55,7 +55,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); // Or await $(ContainsAnyPage.previous()).click(); @@ -65,7 +65,7 @@ describe("Routing Checkbox Contains Condition.", () => { await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it('Given a list of checkbox options, when I select the options "India", "Azerbaijan" and "Liechtenstein" then I should be routed to the "contains all" condition page', async () => { @@ -75,6 +75,6 @@ describe("Routing Checkbox Contains Condition.", () => { await $(RoutingCheckboxContains.liechtenstein()).click(); await click(RoutingCheckboxContains.submit()); // Then - await expect(browser).toHaveUrlContaining(ContainsAllPage.pageName); + await verifyUrlContains(ContainsAllPage.pageName); }); }); diff --git a/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js index c9c444fd2a..256c6412b3 100644 --- a/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_block.spec.js @@ -1,7 +1,7 @@ import QuestionPage from "../../../generated_pages/skip_condition_block/do-you-want-to-skip.page"; import SkipPage from "../../../generated_pages/skip_condition_block/should-skip.page"; import SubmitPage from "../../../generated_pages/skip_condition_block/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Skip Conditions - Block", () => { const schema = "test_skip_condition_block.json"; @@ -13,13 +13,13 @@ describe("Skip Conditions - Block", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); await click(QuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); await click(QuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SkipPage.pageName); + await verifyUrlContains(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js index fd6e7c0842..29567430cc 100644 --- a/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_group.spec.js @@ -1,7 +1,7 @@ import QuestionPage from "../../../generated_pages/skip_condition_group/do-you-want-to-skip.page"; import SkipPage from "../../../generated_pages/skip_condition_group/should-skip.page"; import SubmitPage from "../../../generated_pages/skip_condition_group/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Skip Conditions - Group", () => { const schema = "test_skip_condition_group.json"; @@ -13,13 +13,13 @@ describe("Skip Conditions - Group", () => { it("When I choose to skip on the first page, Then I should see the summary page", async () => { await $(QuestionPage.yes()).click(); await click(QuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I choose not to skip on the first page, Then I should see the should-skip page", async () => { await $(QuestionPage.no()).click(); await click(QuestionPage.submit()); - await expect(browser).toHaveUrlContaining(SkipPage.pageName); + await verifyUrlContains(SkipPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js b/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js index 4ee08e3ee7..14a48ae88e 100644 --- a/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_condition_list.spec.js @@ -3,7 +3,7 @@ import ListCollectorAddPage from "../../../generated_pages/skip_condition_list/l import LessThanTwoInterstitialPage from "../../../generated_pages/skip_condition_list/less-than-two-interstitial.page.js"; import TwoInterstitialPage from "../../../generated_pages/skip_condition_list/two-interstitial.page.js"; import MoreThanTwoInterstitialPage from "../../../generated_pages/skip_condition_list/more-than-two-interstitial.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Routing on lists", () => { describe("Given I start skip condition list survey", () => { beforeEach(async () => { @@ -13,7 +13,7 @@ describe("Feature: Routing on lists", () => { it("When I don't add a person to the list, Then the less than two people skippable page should be shown", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(LessThanTwoInterstitialPage.pageName); + await verifyUrlContains(LessThanTwoInterstitialPage.pageName); }); it("When I add one person to the list, Then the less than two people skippable page should be shown", async () => { @@ -24,7 +24,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(LessThanTwoInterstitialPage.pageName); + await verifyUrlContains(LessThanTwoInterstitialPage.pageName); }); it("When I add two people to the list, Then the two people skippable page should be shown", async () => { @@ -40,7 +40,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(TwoInterstitialPage.pageName); + await verifyUrlContains(TwoInterstitialPage.pageName); }); it("When I add three people to the list, Then the more than two people skippable page should be shown", async () => { @@ -61,7 +61,7 @@ describe("Feature: Routing on lists", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(MoreThanTwoInterstitialPage.pageName); + await verifyUrlContains(MoreThanTwoInterstitialPage.pageName); }); }); }); diff --git a/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js b/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js index 536eae89fc..42f175774e 100644 --- a/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_conditions_not_set.spec.js @@ -1,7 +1,7 @@ import FoodPage from "../../../generated_pages/skip_condition_not_set/food-block.page"; import DrinkPage from "../../../generated_pages/skip_condition_not_set/drink-block.page"; import SubmitPage from "../../../generated_pages/skip_condition_not_set/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Skip Conditions - Not Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_not_set.json"); @@ -9,12 +9,12 @@ describe("Skip Conditions - Not Set", () => { it("Given I do not complete the first page, Then I should see the summary page", async () => { await click(FoodPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I complete the first page, Then I should see the drink page", async () => { await $(FoodPage.bacon()).click(); await click(FoodPage.submit()); - await expect(browser).toHaveUrlContaining(DrinkPage.pageName); + await verifyUrlContains(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js b/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js index 3cb75c39fd..3b9fe3ee4a 100644 --- a/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js +++ b/tests/functional/spec/journeys/skipping/skip_conditions_set.spec.js @@ -1,7 +1,7 @@ import FoodPage from "../../../generated_pages/skip_condition_set/food-block.page"; import DrinkPage from "../../../generated_pages/skip_condition_set/drink-block.page"; import SubmitPage from "../../../generated_pages/skip_condition_set/submit.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Skip Conditions - Set", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_skip_condition_set.json"); @@ -10,11 +10,11 @@ describe("Skip Conditions - Set", () => { it("Given I complete the first page, Then I should see the summary page", async () => { await $(FoodPage.bacon()).click(); await click(FoodPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I do not complete the first page, Then I should see the drink page", async () => { await click(FoodPage.submit()); - await expect(browser).toHaveUrlContaining(DrinkPage.pageName); + await verifyUrlContains(DrinkPage.pageName); }); }); diff --git a/tests/functional/spec/language_code.spec.js b/tests/functional/spec/language_code.spec.js index 8b91be4da7..b47a0961ef 100644 --- a/tests/functional/spec/language_code.spec.js +++ b/tests/functional/spec/language_code.spec.js @@ -3,7 +3,7 @@ import DobPage from "../generated_pages/language/dob-block.page"; import NumberOfPeoplePage from "../generated_pages/language/number-of-people-block.page"; import ConfirmNumberOfPeoplePage from "../generated_pages/language/confirm-number-of-people.page"; import HubPage from "../base_pages/hub.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; const PLURAL_TEST_DATA_SETS = [ { @@ -124,7 +124,7 @@ describe("Language Code", () => { await expect(await $(HubPage.submit()).getText()).toBe("Botwm cyflwyno"); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); it("Given a launch language of English, I should see English text", async () => { @@ -154,7 +154,7 @@ describe("Language Code", () => { await expect(await $(HubPage.submit()).getText()).toBe("Submission button"); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); it("Given a launch language of English, When I select Cymraeg, Then the language should be switched to Welsh", async () => { @@ -195,7 +195,7 @@ describe("Language Code", () => { await expect(await $(HubPage.submit()).getText()).toBe("Botwm cyflwyno"); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); it("Given a launch language of Welsh, When I select English, Then the language should be switched to English", async () => { diff --git a/tests/functional/spec/launch_with_cir.spec.js b/tests/functional/spec/launch_with_cir.spec.js index 166efcb73e..dfc5105055 100644 --- a/tests/functional/spec/launch_with_cir.spec.js +++ b/tests/functional/spec/launch_with_cir.spec.js @@ -1,5 +1,4 @@ -import { expect } from "@wdio/globals"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; import NameBlockPage from "../generated_pages/textfield/name-block.page.js"; import HubPage from "../base_pages/hub.page"; import ThankYouPage from "../base_pages/thank-you.page"; @@ -10,10 +9,10 @@ describe("Launch a survey from the collection instrument registry", () => { version: "v2", cirInstrumentId: "fd4a527f-c126-da2d-8ee6-51663a43e416", }); - await expect(browser).toHaveUrlContaining(NameBlockPage.pageName); + await verifyUrlContains(NameBlockPage.pageName); await $(NameBlockPage.name()).setValue("Joe"); await click(NameBlockPage.submit()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); diff --git a/tests/functional/spec/list_collector/list_collector.spec.js b/tests/functional/spec/list_collector/list_collector.spec.js index e970a27af0..838733c9e8 100644 --- a/tests/functional/spec/list_collector/list_collector.spec.js +++ b/tests/functional/spec/list_collector/list_collector.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList, click } from "../../helpers"; +import { checkItemsInList, click, verifyUrlContains } from "../../helpers"; import AnotherListCollectorPage from "../../generated_pages/list_collector/another-list-collector-block.page.js"; import AnotherListCollectorAddPage from "../../generated_pages/list_collector/another-list-collector-block-add.page.js"; import AnotherListCollectorEditPage from "../../generated_pages/list_collector/another-list-collector-block-edit.page.js"; @@ -95,7 +95,7 @@ describe("List Collector", () => { await $(ListCollectorAddPage.firstName()).setValue("Someone"); await $(ListCollectorAddPage.lastName()).setValue("Else"); await $(ListCollectorAddPage.cancelAndReturn()).click(); - await expect(browser).toHaveUrlContaining(ListCollectorPage.pageName); + await verifyUrlContains(ListCollectorPage.pageName); }); it("The user is returned to the list collector when the cancel link is clicked on the edit page.", async () => { @@ -106,7 +106,7 @@ describe("List Collector", () => { await click(ListCollectorAddPage.submit()); await $(ListCollectorPage.listEditLink(1)).click(); await $(ListCollectorEditPage.cancelAndReturn()).click(); - await expect(browser).toHaveUrlContaining(ListCollectorPage.pageName); + await verifyUrlContains(ListCollectorPage.pageName); }); it("The collector shows everyone on the summary", async () => { @@ -117,12 +117,12 @@ describe("List Collector", () => { it("When No is answered on the list collector the user sees an interstitial", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(NextInterstitialPage.pageName); + await verifyUrlContains(NextInterstitialPage.pageName); await click(NextInterstitialPage.submit()); }); it("After the interstitial, the user should be on the second list collector page", async () => { - await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); + await verifyUrlContains(AnotherListCollectorPage.pageName); }); it("The collector still shows the same list of people on the summary", async () => { @@ -148,26 +148,26 @@ describe("List Collector", () => { it("The user is returned to the list collector when the previous link is clicked.", async () => { await $(AnotherListCollectorPage.listRemoveLink(1)).click(); await $(AnotherListCollectorRemovePage.previous()).click(); - await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); + await verifyUrlContains(AnotherListCollectorPage.pageName); await $(AnotherListCollectorPage.listEditLink(1)).click(); await $(AnotherListCollectorEditPage.previous()).click(); - await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); + await verifyUrlContains(AnotherListCollectorPage.pageName); await $(AnotherListCollectorPage.yes()).click(); await click(AnotherListCollectorPage.submit()); await $(AnotherListCollectorEditPage.previous()).click(); - await expect(browser).toHaveUrlContaining(AnotherListCollectorPage.pageName); + await verifyUrlContains(AnotherListCollectorPage.pageName); }); it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(AnotherListCollectorPage.no()).click(); await click(AnotherListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); }); it("The questionnaire allows submission", async () => { await click(SummaryPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); }); @@ -235,7 +235,7 @@ describe("List Collector", () => { it("When the user reaches the submit page and navigates back, They should see the Section Summary", async () => { await click(PeopleListSectionSummaryPage.submit()); await click(SubmitPage.previous()); - await expect(browser).toHaveUrlContaining(PeopleListSectionSummaryPage.pageName); + await verifyUrlContains(PeopleListSectionSummaryPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector/list_collector_content.spec.js b/tests/functional/spec/list_collector/list_collector_content.spec.js index c1c8e1ac44..43c02d3f6d 100644 --- a/tests/functional/spec/list_collector/list_collector_content.spec.js +++ b/tests/functional/spec/list_collector/list_collector_content.spec.js @@ -11,7 +11,7 @@ import ListCollectorSecondRepeatingBlockPage from "../../generated_pages/list_co import ListCollectorContentPage from "../../generated_pages/list_collector_content_page/list-collector-content.page"; import ListCollectorContentSectionSummaryPage from "../../generated_pages/list_collector_content_page/section-list-collector-contents-summary.page"; import ConfirmationCheckboxPage from "../../generated_pages/list_collector_content_page/confirmation-checkbox.page"; -import { listItemComplete, click } from "../../helpers"; +import { listItemComplete, click, verifyUrlContains } from "../../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -20,12 +20,12 @@ describe("List Collector Section Summary and Summary Items", () => { }); it("When I get to the Hub, Then from there the next block in list collector content section should be list collector content page.", async () => { await fillInListCollectorSection(); - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Not started"); await click(HubPage.submit()); await $(ResponsiblePartyQuestionPage.yes()).click(); await click(ResponsiblePartyQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(ListCollectorContentPage.url()); + await verifyUrlContains(ListCollectorContentPage.url()); }); it("When I get to the list collector content page, Then the relevant content and button is displayed.", async () => { await fillInListCollectorSection(); @@ -50,7 +50,7 @@ describe("List Collector Section Summary and Summary Items", () => { await click(ListCollectorContentPage.submit()); await completeRepeatingBlocks(456, 1, 1, 1990, true, true); await click(ListCollectorContentPage.submit()); - await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.url()); + await verifyUrlContains(ListCollectorContentSectionSummaryPage.url()); }); it("When I fill in first item repeating blocks, Then after going back to the hub the section should be in progress.", async () => { await fillInListCollectorSection(); @@ -82,7 +82,7 @@ describe("List Collector Section Summary and Summary Items", () => { await completeBothSections(); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); await $(HubPage.summaryRowLink("section-companies")).click(); - await expect(browser).toHaveUrlContaining(CompaniesSummaryPage.pageName); + await verifyUrlContains(CompaniesSummaryPage.pageName); await $(CompaniesSummaryPage.companiesListAddLink()).click(); await addCompany("Company C", "789", false); await anyMoreCompaniesNo(); @@ -91,12 +91,12 @@ describe("List Collector Section Summary and Summary Items", () => { await click(CompaniesSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Partially completed"); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ListCollectorContentPage.pageName); + await verifyUrlContains(ListCollectorContentPage.pageName); await listItemComplete(`li[data-qa="list-item-1-label"]`, true); await listItemComplete(`li[data-qa="list-item-2-label"]`, true); await listItemComplete(`li[data-qa="list-item-3-label"]`, false); await click(ListCollectorContentPage.submit()); - await expect(browser).toHaveUrlContaining(ListCollectorFirstRepeatingBlockPage.pageName); + await verifyUrlContains(ListCollectorFirstRepeatingBlockPage.pageName); await completeRepeatingBlocks(666, 2, 5, 1995, true, true); await listItemComplete(`li[data-qa="list-item-3-label"]`, true); await click(ListCollectorContentPage.submit()); @@ -116,7 +116,7 @@ describe("List Collector Section Summary and Summary Items", () => { await click(HubPage.submit()); await listItemComplete(`li[data-qa="list-item-1-label"]`, true); await click(ListCollectorContentPage.submit()); - await expect(browser).toHaveUrlContaining(ListCollectorContentSectionSummaryPage.pageName); + await verifyUrlContains(ListCollectorContentSectionSummaryPage.pageName); await click(ListCollectorContentSectionSummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-list-collector-contents")).getText()).toBe("Completed"); }); diff --git a/tests/functional/spec/list_collector/list_collector_driving_question.spec.js b/tests/functional/spec/list_collector/list_collector_driving_question.spec.js index 8af6566133..4d7d76a758 100644 --- a/tests/functional/spec/list_collector/list_collector_driving_question.spec.js +++ b/tests/functional/spec/list_collector/list_collector_driving_question.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList, click } from "../../helpers"; +import { checkItemsInList, click, verifyUrlContains } from "../../helpers"; import HubPage from "../../base_pages/hub.page.js"; import AnyoneUsuallyLiveAtPage from "../../generated_pages/list_collector_driving_question/anyone-usually-live-at.page.js"; import AnyoneElseLiveAtListCollectorPage from "../../generated_pages/list_collector_driving_question/anyone-else-live-at.page.js"; @@ -38,7 +38,7 @@ describe("List Collector Driving Question", () => { await $(AnyoneUsuallyLiveAtPage.no()).click(); await click(AnyoneUsuallyLiveAtPage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); - await expect(browser).toHaveUrlContaining(AnyoneUsuallyLiveAtPage.url()); + await verifyUrlContains(AnyoneUsuallyLiveAtPage.url()); }); }); @@ -55,7 +55,7 @@ describe("List Collector Driving Question", () => { await $(AnyoneElseLiveAtListCollectorRemovePage.yes()).click(); await click(AnyoneElseLiveAtListCollectorRemovePage.submit()); await $(SectionSummaryPage.peopleListAddLink()).click(); - await expect(browser).toHaveUrlContaining(AnyoneElseLiveAtListCollectorAddPage.pageName); + await verifyUrlContains(AnyoneElseLiveAtListCollectorAddPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector/list_collector_primary_person.spec.js b/tests/functional/spec/list_collector/list_collector_primary_person.spec.js index b38c74eaa6..c71a79ccf7 100644 --- a/tests/functional/spec/list_collector/list_collector_primary_person.spec.js +++ b/tests/functional/spec/list_collector/list_collector_primary_person.spec.js @@ -7,7 +7,7 @@ import SectionSummaryPage from "../../generated_pages/list_collector/section-sum import { SubmitPage } from "../../base_pages/submit.page.js"; import ThankYouPage from "../../base_pages/thank-you.page.js"; import AnyoneUsuallyLiveAtPage from "../../generated_pages/list_collector_primary_person/anyone-usually-live-at.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Primary Person List Collector Survey", () => { describe("Given the user starts on the 'do you live here' question", () => { @@ -96,7 +96,7 @@ describe("Primary Person List Collector Survey", () => { it("When the user submits, then they are allowed to submit the survey", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); diff --git a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js index 48e17933e1..ab018ca044 100644 --- a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js @@ -10,7 +10,7 @@ import HouseholderCheckboxPage from "../../generated_pages/list_collector_sectio import SubmitPage from "../../generated_pages/list_collector_section_summary/submit.page"; import ThankYouPage from "../../base_pages/thank-you.page"; import ViewSubmittedResponsePage from "../../generated_pages/list_collector_section_summary/view-submitted-response.page"; -import { click, listItemIds } from "../../helpers"; +import { click, listItemIds, verifyUrlContains } from "../../helpers"; describe("List Collector Section Summary and Summary Items", () => { describe("Given I launch the test list collector section summary items survey", () => { @@ -21,7 +21,7 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesQuestion()).isExisting()).toBe(true); await expect(await $(SectionSummaryPage.anyCompaniesOrBranchesAnswer()).getText()).toBe("Yes"); }); @@ -66,7 +66,7 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $("body").getText()).not.toBe("Company A"); await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); @@ -76,7 +76,7 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $("body").getText()).toContain("No UK company or branch added"); }); it("When I have multiple items in the list and I remove the first item, Then only the item that was not deleted should be visible on the section summary.", async () => { @@ -86,7 +86,7 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company B", "234", true); await anyMoreCompaniesNo(); await removeFirstCompany(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $("body").getText()).not.toBe("Company A"); await expect(await $("body").getText()).toContain("Company B"); }); @@ -94,7 +94,7 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(true); }); it("When I add an item and relevant data and answer Yes on the additional items page, Then I should be able to and add a new item and relevant data.", async () => { @@ -114,7 +114,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesNo(); await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); - await expect(browser).toHaveUrlContaining("edit-company/?return_to=section-summary"); + await verifyUrlContains("edit-company/?return_to=section-summary"); await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).toBe("Company A"); }); it("When I edit an item after adding it, Then I should be redirected to the summary page", async () => { @@ -125,12 +125,12 @@ describe("List Collector Section Summary and Summary Items", () => { await $(SectionSummaryPage.companiesListEditLink(1)).click(); await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).setValue("Changed Company"); await click(AnyCompaniesOrBranchesAddPage.submit()); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Changed Company"); }); it("When no item is added but I change my answer to the driving question to Yes, Then I should be able to add a new item.", async () => { await drivingQuestionNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(false); @@ -138,7 +138,7 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(true); await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(true); await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(true); @@ -150,14 +150,14 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); await drivingQuestionNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(false); await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(false); await expect(await $("body").getText()).not.toBe("No UK company or branch added"); await expect(await $(SectionSummaryPage.companiesListAddLink()).isExisting()).toBe(false); await $(SectionSummaryPage.anyCompaniesOrBranchesAnswerEdit()).click(); await drivingQuestionYes(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await expect(await $(SectionSummaryPage.companiesListEditLink(1)).isExisting()).toBe(true); await expect(await $(SectionSummaryPage.companiesListRemoveLink(1)).isExisting()).toBe(true); @@ -168,14 +168,14 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await $(SectionSummaryPage.companiesListAddLink()).click(); - await expect(browser).toHaveUrlContaining("/questionnaire/companies/add-company"); - await expect(browser).toHaveUrlContaining("?return_to=section-summary"); + await verifyUrlContains("/questionnaire/companies/add-company"); + await verifyUrlContains("?return_to=section-summary"); await addCompany("Company B", "456", true); - await expect(browser).toHaveUrlContaining(AnyCompaniesOrBranchesPage.url()); + await verifyUrlContains(AnyCompaniesOrBranchesPage.url()); await expect(await $("body").getText()).toContain("Company A"); await expect(await $("body").getText()).toContain("Company B"); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); }); it("When I add three companies, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -185,7 +185,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "789", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(UkBasedPage.url()); + await verifyUrlContains(UkBasedPage.url()); }); it("When I add less than 3 companies, Then I am not prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -193,7 +193,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company B", "456", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); }); it("When I add more than 3 companies, Then I am not prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -205,7 +205,7 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company D", "135", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); }); it("When I add another company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -213,15 +213,15 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company B", "456", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await $(SectionSummaryPage.companiesListAddLink()).click(); - await expect(browser).toHaveUrlContaining("/questionnaire/companies/add-company"); - await expect(browser).toHaveUrlContaining("?return_to=section-summary"); + await verifyUrlContains("/questionnaire/companies/add-company"); + await verifyUrlContains("?return_to=section-summary"); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(UkBasedPage.url()); + await verifyUrlContains(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); }); it("When I remove a company from the summary page, and the amount then totals to 3, and the confirmation question hasn't been previously answered, Then I am prompted with the confirmation question", async () => { await drivingQuestionYes(); @@ -233,11 +233,11 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company D", "345", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await removeFirstCompany(); - await expect(browser).toHaveUrlContaining(UkBasedPage.url()); + await verifyUrlContains(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); }); it("When I get to the summary page, Then the summary should be displayed as expected with change links", async () => { @@ -248,9 +248,9 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(UkBasedPage.url()); + await verifyUrlContains(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); @@ -258,7 +258,7 @@ describe("List Collector Section Summary and Summary Items", () => { await click(HouseholderCheckboxPage.submit()); await click(SectionSummaryTwoPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Change"); @@ -281,9 +281,9 @@ describe("List Collector Section Summary and Summary Items", () => { await anyMoreCompaniesYes(); await addCompany("Company C", "234", true); await anyMoreCompaniesNo(); - await expect(browser).toHaveUrlContaining(UkBasedPage.url()); + await verifyUrlContains(UkBasedPage.url()); await answerUkBasedQuestion(); - await expect(browser).toHaveUrlContaining(SectionSummaryPage.url()); + await verifyUrlContains(SectionSummaryPage.url()); await click(SectionSummaryPage.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); @@ -294,7 +294,7 @@ describe("List Collector Section Summary and Summary Items", () => { await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test"); await $(ThankYouPage.savePrintAnswersLink()).click(); - await expect(browser).toHaveUrlContaining(ViewSubmittedResponsePage.pageName); + await verifyUrlContains(ViewSubmittedResponsePage.pageName); await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); diff --git a/tests/functional/spec/list_collector/list_collector_variants.spec.js b/tests/functional/spec/list_collector/list_collector_variants.spec.js index 43fb7a4f85..f11fabed05 100644 --- a/tests/functional/spec/list_collector/list_collector_variants.spec.js +++ b/tests/functional/spec/list_collector/list_collector_variants.spec.js @@ -1,4 +1,4 @@ -import { checkItemsInList, click } from "../../helpers"; +import { checkItemsInList, click, verifyUrlContains } from "../../helpers"; import YouLiveHerePage from "../../generated_pages/list_collector_variants/you-live-here-block.page.js"; import ListCollectorPage from "../../generated_pages/list_collector_variants/list-collector.page.js"; import ListCollectorAddPage from "../../generated_pages/list_collector_variants/list-collector-add.page.js"; @@ -45,12 +45,12 @@ describe("List Collector With Variants", () => { it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); }); @@ -91,12 +91,12 @@ describe("List Collector With Variants", () => { it("The questionnaire shows the confirmation page when no more people to add", async () => { await $(ListCollectorPage.anyoneElseNo()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("The questionnaire allows submission", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.url()); + await verifyUrlContains(ThankYouPage.url()); }); }); }); diff --git a/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js b/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js index 1e1d6b3ae6..957f54592b 100644 --- a/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js +++ b/tests/functional/spec/list_collector/list_collector_variants_primary_person.spec.js @@ -5,7 +5,7 @@ import ListCollectorPage from "../../generated_pages/list_collector_variants_pri import EditPersonPage from "../../generated_pages/list_collector_variants_primary_person/list-collector-edit.page"; import SubmitPage from "../../generated_pages/list_collector_variants_primary_person/submit.page"; import ThankYouPage from "../../base_pages/thank-you.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("List collector with variants primary person", () => { describe("Given that person lives in house", () => { @@ -80,7 +80,7 @@ describe("List collector with variants primary person", () => { it("When the user answers 'no' to add any person, Then the questionnaire shows the confirmation page", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("When the user attempts to submit, Then they are shown the confirmation page", async () => { @@ -96,7 +96,7 @@ describe("List collector with variants primary person", () => { it("When the user submits, Then they are allowed to submit the survey", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); }); diff --git a/tests/functional/spec/list_collector/relationships-unrelated.spec.js b/tests/functional/spec/list_collector/relationships-unrelated.spec.js index 5f027bc70c..ea61569815 100644 --- a/tests/functional/spec/list_collector/relationships-unrelated.spec.js +++ b/tests/functional/spec/list_collector/relationships-unrelated.spec.js @@ -3,7 +3,7 @@ import ListCollectorAddPage from "../../generated_pages/relationships_unrelated/ import RelationshipsPage from "../../generated_pages/relationships_unrelated/relationships.page.js"; import RelatedToAnyoneElsePage from "../../generated_pages/relationships_unrelated/related-to-anyone-else.page.js"; import RelationshipsInterstitialPage from "../../generated_pages/relationships_unrelated/relationship-interstitial.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Unrelated Relationships", () => { const schema = "test_relationships_unrelated.json"; @@ -80,7 +80,7 @@ describe("Unrelated Relationships", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.unrelated()).click(); await click(RelationshipsPage.submit()); - await expect(browser).toHaveUrlContaining(RelationshipsInterstitialPage.pageName); + await verifyUrlContains(RelationshipsInterstitialPage.pageName); }); }); diff --git a/tests/functional/spec/list_collector/relationships.spec.js b/tests/functional/spec/list_collector/relationships.spec.js index 8efd56015c..8dac968fe9 100644 --- a/tests/functional/spec/list_collector/relationships.spec.js +++ b/tests/functional/spec/list_collector/relationships.spec.js @@ -4,7 +4,7 @@ import ListCollectorRemovePage from "../../generated_pages/relationships/list-co import RelationshipsPage from "../../generated_pages/relationships/relationships.page.js"; import RelationshipsInterstitialPage from "../../generated_pages/relationships/relationship-interstitial.page.js"; import SectionSummaryPage from "../../generated_pages/relationships/section-summary.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; describe("Relationships", () => { const schema = "test_relationships.json"; @@ -23,7 +23,7 @@ describe("Relationships", () => { await $(ListCollectorPage.no()).click(); // eslint-disable-next-line no-undef await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); }); it("When I add two household members, Then I will be asked about one relationship", async () => { @@ -40,11 +40,11 @@ describe("Relationships", () => { await $(ListCollectorPage.no()).click(); await $(ListCollectorPage.submit()).scrollIntoView(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); + await verifyUrlContains(RelationshipsPage.pageName); await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); }); describe("When I add three household members,", () => { @@ -62,14 +62,14 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); }); it("And go to the first relationship, Then the previous link should return to the list collector", async () => { await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); await $(RelationshipsPage.previous()).click(); - await expect(browser).toHaveUrlContaining("/questionnaire/list-collector/"); + await verifyUrlContains("/questionnaire/list-collector/"); }); it("And go to the first relationship, Then the 'Brother or Sister' option should have the text 'Including half brother or half sister'", async () => { @@ -85,7 +85,7 @@ describe("Relationships", () => { await click(RelationshipsPage.submit()); await $(RelationshipsPage.previous()).click(); await click(RelationshipsInterstitialPage.submit()); - await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); + await verifyUrlContains(RelationshipsPage.pageName); await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Marcus"); }); @@ -99,10 +99,10 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); - await expect(browser).toHaveUrlContaining(RelationshipsPage.pageName); + await verifyUrlContains(RelationshipsPage.pageName); await expect(await $(RelationshipsPage.questionText()).getText()).toContain("Olivia"); }); @@ -116,7 +116,7 @@ describe("Relationships", () => { await $(RelationshipsPage.husbandOrWife()).click(); await click(RelationshipsPage.submit()); await click(RelationshipsInterstitialPage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); await $(SectionSummaryPage.previous()).click(); await $(RelationshipsInterstitialPage.previous()).click(); await expect(await $(RelationshipsPage.husbandOrWife()).isSelected()).toBe(true); @@ -163,7 +163,7 @@ describe("Relationships", () => { await $(SectionSummaryPage.peopleListRemoveLink(1)).click(); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); - await expect(browser).toHaveUrlContaining("/sections/section/"); + await verifyUrlContains("/sections/section/"); }); it("Then I add another household member I will be redirected to parent list collector", async () => { @@ -171,7 +171,7 @@ describe("Relationships", () => { await $(ListCollectorAddPage.firstName()).setValue("Tom"); await $(ListCollectorAddPage.lastName()).setValue("Bowden"); await click(ListCollectorAddPage.submit()); - await expect(browser).toHaveUrlContaining("/questionnaire/list-collector/"); + await verifyUrlContains("/questionnaire/list-collector/"); }); }); diff --git a/tests/functional/spec/multiple_answers.spec.js b/tests/functional/spec/multiple_answers.spec.js index 4a9f702e17..20d4fb455e 100644 --- a/tests/functional/spec/multiple_answers.spec.js +++ b/tests/functional/spec/multiple_answers.spec.js @@ -1,7 +1,7 @@ import AboutYou from "../generated_pages/multiple_answers/about-you-block.page"; import AgeBlock from "../generated_pages/multiple_answers/age-block.page"; import SubmitPage from "../generated_pages/multiple_answers/submit.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; async function answerAllQuestions() { await $(AboutYou.textfield()).setValue("John Doe"); @@ -58,8 +58,8 @@ describe("Multiple Answers", () => { it("When I click 'Change' an answer, Then I should be taken to the correct page and the answer input should be focused", async () => { await $(SubmitPage.currencyAnswerEdit()).click(); - await expect(browser).toHaveUrlContaining(AboutYou.url()); - await expect(browser).toHaveUrlContaining(AboutYou.currency()); + await verifyUrlContains(AboutYou.url()); + await verifyUrlContains(AboutYou.currency()); await expect(await $(AboutYou.currency()).isFocused()).toBe(true); }); }); diff --git a/tests/functional/spec/my_account_header_link.spec.js b/tests/functional/spec/my_account_header_link.spec.js index a721b3b301..97f318a2e8 100644 --- a/tests/functional/spec/my_account_header_link.spec.js +++ b/tests/functional/spec/my_account_header_link.spec.js @@ -1,10 +1,11 @@ import IntroductionPage from "../generated_pages/introduction/introduction.page"; +import { verifyUrlContains } from "../helpers"; describe("My Account header link", () => { it("Given I start a survey, When I visit a page then I should not see the My account button", async () => { await browser.openQuestionnaire("test_introduction.json"); await browser.pause(100); - await expect(browser).toHaveUrlContaining("introduction"); + await verifyUrlContains("introduction"); await expect(await $(IntroductionPage.myAccountLink()).isExisting()).toBe(false); }); }); diff --git a/tests/functional/spec/numbers.spec.js b/tests/functional/spec/numbers.spec.js index e6c8bb2a49..00025eef14 100644 --- a/tests/functional/spec/numbers.spec.js +++ b/tests/functional/spec/numbers.spec.js @@ -6,7 +6,7 @@ import currencyBlock from "../generated_pages/variants_question/currency-block.p import firstNumberBlock from "../generated_pages/variants_question/first-number-block.page.js"; import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; import currencySectionSummary from "../generated_pages/variants_question/currency-section-summary.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Number validation", () => { before(async () => { @@ -90,7 +90,7 @@ describe("Number validation", () => { await click(secondNumberBlock.submit()); await click(currencySectionSummary.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I edit and change the maximum value, Then I must re-validate and submit any dependent answers before I can return to the summary", async () => { @@ -108,7 +108,7 @@ describe("Number validation", () => { await click(secondNumberBlock.submit()); await click(currencySectionSummary.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I edit and change the minimum value, Then I must re-validate and submit any dependent answers again before I can return to the summary", async () => { @@ -122,7 +122,7 @@ describe("Number validation", () => { await $(TestMinMax.testRangeExclusive()).setValue("12"); await click(TestMinMax.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When a number with more than 3 decimal places has been entered, Then it should be displayed correctly on the summary", async () => { diff --git a/tests/functional/spec/percentage_decimal.spec.js b/tests/functional/spec/percentage_decimal.spec.js index e6d0807bad..63b582668b 100644 --- a/tests/functional/spec/percentage_decimal.spec.js +++ b/tests/functional/spec/percentage_decimal.spec.js @@ -1,7 +1,7 @@ import PercentagePage from "../generated_pages/percentage/block.page.js"; import PercentageDecimalPage from "../generated_pages/percentage/block-decimal.page.js"; import SubmitPage from "../generated_pages/percentage/submit.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Decimal places", () => { it("Given an answer allows 3 decimal places, When I enter a value to 3 decimal places and return to edit the value, Then the answer should be displayed with 3 decimal places", async () => { @@ -10,7 +10,7 @@ describe("Decimal places", () => { await $(PercentageDecimalPage.decimal()).setValue("3.333"); await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); - await expect(browser).toHaveUrlContaining(PercentageDecimalPage.pageName); + await verifyUrlContains(PercentageDecimalPage.pageName); await expect(await $(PercentageDecimalPage.decimal()).getValue()).toBe("3.333"); }); @@ -20,7 +20,7 @@ describe("Decimal places", () => { await $(PercentageDecimalPage.decimal()).setValue("3.3"); await click(PercentageDecimalPage.submit()); await $(SubmitPage.previous()).click(); - await expect(browser).toHaveUrlContaining(PercentageDecimalPage.pageName); + await verifyUrlContains(PercentageDecimalPage.pageName); await expect(await $(PercentageDecimalPage.decimal()).getValue()).toBe("3.300"); }); }); diff --git a/tests/functional/spec/placeholder_answers_on_the_path.spec.js b/tests/functional/spec/placeholder_answers_on_the_path.spec.js index 1dc9dbff1c..66ec230d3d 100644 --- a/tests/functional/spec/placeholder_answers_on_the_path.spec.js +++ b/tests/functional/spec/placeholder_answers_on_the_path.spec.js @@ -7,7 +7,7 @@ import AddPersonPage from "../generated_pages/placeholder_first_non_empty_item_r import ListCollectorPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/list-collector.page"; import PersonalDetailsBlockPage from "../generated_pages/placeholder_first_non_empty_item_repeating_sections/personal-details-block.page"; import HubPage from "../base_pages/hub.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("First Non Empty Item Transform", () => { before("Launch survey", async () => { @@ -30,7 +30,7 @@ describe("First Non Empty Item Transform", () => { await $(DateEntryBlockPage.previous()).click(); await $(DateQuestionBlockPage.yesICanReportForThisPeriod()).click(); await click(DateQuestionBlockPage.submit()); - expect(await browser.getUrl()).toContain(TotalTurnoverBlockPage.pageName); + await verifyUrlContains(TotalTurnoverBlockPage.pageName); expect(await $(TotalTurnoverBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); @@ -62,7 +62,7 @@ describe("First Non Empty Item Transform Cross Section", () => { await click(DateQuestionBlockPage.submit()); // Check the next section if the metadata date is shown await click(HubPage.submit()); - expect(await browser.getUrl()).toContain(FoodQuestionBlockPage.pageName); + await verifyUrlContains(FoodQuestionBlockPage.pageName); expect(await $(FoodQuestionBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); @@ -96,7 +96,7 @@ describe("First Non Empty Item Transform Repeating Sections", () => { await click(ListCollectorPage.submit()); // Check Repeating Section has the set dates await click(HubPage.submit()); - expect(await browser.getUrl()).toContain(PersonalDetailsBlockPage.pageName); + await verifyUrlContains(PersonalDetailsBlockPage.pageName); expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).toContain("5 January 2017 to 25 January 2017"); await $(PersonalDetailsBlockPage.previous()).click(); // Change to original dates @@ -105,7 +105,7 @@ describe("First Non Empty Item Transform Repeating Sections", () => { await click(DateQuestionBlockPage.submit()); await click(HubPage.submit()); // Check the list collector has metadata dates in the title - expect(await browser.getUrl()).toContain(PersonalDetailsBlockPage.pageName); + await verifyUrlContains(PersonalDetailsBlockPage.pageName); expect(await $(PersonalDetailsBlockPage.questionTitle()).getText()).toContain("1 January 2017 to 1 February 2017"); }); }); diff --git a/tests/functional/spec/preview.spec.js b/tests/functional/spec/preview.spec.js index 73a596674f..8700553462 100644 --- a/tests/functional/spec/preview.spec.js +++ b/tests/functional/spec/preview.spec.js @@ -1,5 +1,6 @@ import IntroductionPageHub from "../generated_pages/introduction_hub/introduction.page"; import IntroductionPageLinear from "../generated_pages/introduction/introduction.page"; +import { verifyUrlContains } from "../helpers"; describe("Introduction preview questions", () => { const introductionSchemaHub = "test_introduction_hub.json"; @@ -24,7 +25,7 @@ describe("Introduction preview questions", () => { async function testPreview(schema, page) { await browser.openQuestionnaire(schema); await $(page.previewQuestions()).click(); - expect(await browser.getUrl()).toContain("questionnaire/preview"); + await verifyUrlContains("questionnaire/preview"); if (schema === "test_introduction.json") { expect(await $(previewSectionTitle).getText()).toBe("Main section"); } else { @@ -64,7 +65,7 @@ describe("Introduction preview questions", () => { expect(await $("h1").getText()).toBe("Are you sure you are able to report for the calendar month 5 December 2016 to 20 December 2016?"); await browser.url("questionnaire/introduction/"); await $(IntroductionPageLinear.previewQuestions()).click(); - expect(await browser.getUrl()).toContain("questionnaire/preview"); + await verifyUrlContains("questionnaire/preview"); expect(await $(previewSectionTitle).getText()).toBe("Main section"); expect(await $$(previewQuestion)[2].$("h3").getText()).toBe( "Are you sure you are able to report for the calendar month {calendar_start_date} to {calendar_end_date}?", @@ -74,7 +75,7 @@ describe("Introduction preview questions", () => { it("Given I start a survey, When I view the preview page of hub flow schema, Then the twisty button should read 'Show all' and answers should be invisible", async () => { await browser.openQuestionnaire(introductionSchemaHub); await $(IntroductionPageHub.previewQuestions()).click(); - expect(await browser.getUrl()).toContain("questionnaire/preview"); + await verifyUrlContains("questionnaire/preview"); expect(await $(printButton).isClickable()).toBe(true); expect(await $(pdfButton).isClickable()).toBe(true); expect(await $(showButton).getText()).toBe("Show all"); diff --git a/tests/functional/spec/question_description.spec.js b/tests/functional/spec/question_description.spec.js index ebc4fdf3a0..dc901cf3f7 100644 --- a/tests/functional/spec/question_description.spec.js +++ b/tests/functional/spec/question_description.spec.js @@ -4,7 +4,7 @@ import RadioPage from "../generated_pages/optional_guidance_and_description/mand import RadioPageTwo from "../generated_pages/optional_guidance_and_description/mandatory-radio-two.page"; import IntroductionPage from "../generated_pages/question_guidance/introduction.page"; import GuidancePage from "../generated_pages/question_guidance/block-test-guidance-title.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Question description", () => { it("Given a question description has been set in the schema as an array, When it is rendered, Then it is displayed correctly as multiple paragraph attributes", async () => { @@ -30,7 +30,7 @@ describe("Question guidance", () => { it("Given a question guidance with multiple content items, When it is rendered, Then there should only be one guidance box", async () => { await browser.openQuestionnaire("test_question_guidance.json"); await click(IntroductionPage.submit()); - await expect(browser).toHaveUrlContaining(GuidancePage.pageName); + await verifyUrlContains(GuidancePage.pageName); await expect(await $$("#question-guidance-question-test-guidance-title").length).toBe(1); }); }); diff --git a/tests/functional/spec/save_sign_out.spec.js b/tests/functional/spec/save_sign_out.spec.js index ce4e309e87..67765c1afb 100644 --- a/tests/functional/spec/save_sign_out.spec.js +++ b/tests/functional/spec/save_sign_out.spec.js @@ -10,7 +10,7 @@ import firstNumberBlock from "../generated_pages/variants_question/first-number- import secondNumberBlock from "../generated_pages/variants_question/second-number-block.page.js"; import currencySectionSummary from "../generated_pages/variants_question/currency-section-summary.page.js"; import { getRandomString } from "../jwt_helper"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Save sign out / Exit", () => { const responseId = getRandomString(16); @@ -18,7 +18,7 @@ describe("Save sign out / Exit", () => { await browser.openQuestionnaire("test_introduction.json"); await $(IntroductionPage.exitButton()).click(); - await expect(browser).toHaveUrlContaining("/surveys/todo"); + await verifyUrlContains("/surveys/todo"); await browser.back(); await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); @@ -31,7 +31,7 @@ describe("Save sign out / Exit", () => { await click(SetMinMax.submit()); await $(TestMinMax.saveSignOut()).click(); - await expect(browser).toHaveUrlContaining("/signed-out"); + await verifyUrlContains("/signed-out"); await browser.back(); await expect(await $("body").getHTML()).toContain("Sorry, you need to sign in again"); @@ -56,13 +56,13 @@ describe("Save sign out / Exit", () => { await click(currencySectionSummary.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining("thank-you"); + await verifyUrlContains("thank-you"); }); it("Given a I have started a social questionnaire, when I select save and sign out, then I am redirected to the signed out page and the correct access code link is shown", async () => { await browser.openQuestionnaire("test_theme_social.json", { theme: "social" }); await $(SubmitPage.saveSignOut()).click(); - await expect(browser).toHaveUrlContaining("/signed-out"); + await verifyUrlContains("/signed-out"); await expect(await $("body").getHTML()).toContain("Your progress has been saved"); await expect(await $("body").getHTML()).toContain("To resume the survey,"); await expect(await $("body").getHTML()).toContain("/en/start"); @@ -72,7 +72,7 @@ describe("Save sign out / Exit", () => { await browser.openQuestionnaire("test_introduction.json"); await $(IntroductionPage.getStarted()).click(); await $(IntroInterstitialPage.saveSignOut()).click(); - await expect(browser).toHaveUrlContaining("/signed-out"); + await verifyUrlContains("/signed-out"); await expect(await $("body").getHTML()).toContain("Your progress has been saved"); await expect(await $("body").getHTML()).toContain("To find further information or resume the survey,"); await expect(await $("body").getHTML()).toContain("/surveys/todo"); diff --git a/tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js b/tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js index 540711cb4d..789b7925de 100644 --- a/tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js +++ b/tests/functional/spec/summaries/calculated_summary/calculated_summary_test_case.js @@ -27,7 +27,7 @@ import SectionSummarySectionOne from "../../../generated_pages/calculated_summar import SectionSummarySectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/calculated-summary-section-summary.page"; import DependencyQuestionSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/calculated_summary_cross_section_dependencies/set-min-max-block.page"; -import { assertSummaryValues, click } from "../../../helpers"; +import { assertSummaryValues, click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; class TestCase { @@ -64,7 +64,7 @@ class TestCase { await $(SixthNumberBlockPage.sixthNumber()).setValue(45.67); await click(SixthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary, Then the page title should use the calculation's title", async () => { @@ -113,13 +113,13 @@ class TestCase { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); + await verifyUrlContains("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); + await verifyUrlContains("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -127,7 +127,7 @@ class TestCase { await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await click(FourthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); @@ -139,7 +139,7 @@ class TestCase { await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await click(FourthAndAHalfNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); @@ -160,7 +160,7 @@ class TestCase { await click(FifthNumberBlockPage.submit()); await click(SixthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( @@ -241,7 +241,7 @@ class TestCase { it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(CalculatedSummaryTotalConfirmation.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £9.36"); @@ -251,7 +251,7 @@ class TestCase { it("Given I have an answer maximum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMaximum()).setValue(10.0); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £9.36"); @@ -260,62 +260,62 @@ class TestCase { }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I must re-confirm the dependant calculated summary page and min max question page before I can return to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); // first incomplete block - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); // second incomplete block - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); // back to summary - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I am on a page with a placeholder containing a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the placeholder should display the updated value", async () => { @@ -324,7 +324,7 @@ class TestCase { await click(SkipFourthBlockPage.submit()); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await browser.url(CalculatedSummaryTotalConfirmation.url()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryTotalConfirmation.pageName); + await verifyUrlContains(CalculatedSummaryTotalConfirmation.pageName); const content = await $("h1 + ul").getText(); const textsToAssert = [ "Total currency values: £25.92", @@ -343,7 +343,7 @@ class TestCase { it("Given I am on a page with a dependent question based on a calculated summary value, When I have updated the calculated summary so that additional answers are on the path, Then the question should display the updated value", async () => { await $(SubmitPage.setMinimumAnswerEdit()).click(); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await expect(await $(SetMinMaxBlockPage.questionTitle()).getText()).toContain( "Set minimum and maximum values based on your calculated summary total of £25.92", ); @@ -356,7 +356,7 @@ class TestCase { it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); } @@ -382,7 +382,7 @@ class TestCase { }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { - await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); + await verifyUrlContains(DependencyQuestionSectionTwo.pageName); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("60 - calculated summary answer (previous section)"); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); @@ -390,7 +390,7 @@ class TestCase { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); await click(DependencyQuestionSectionTwo.submit()); - await expect(browser).toHaveUrlContaining(MinMaxSectionTwo.pageName); + await verifyUrlContains(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); @@ -448,7 +448,7 @@ class TestCase { await $(SixthNumberBlockPage.sixthNumber()).setValue(45); await click(SixthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); }); it("Given I have entered a range of positive and negative values, When I reach the calculated summary, Then the total is correct", async () => { await assertSummaryValues(expectedAnswerValues); diff --git a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js index d4dae08024..badc21d14a 100644 --- a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js +++ b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_and_static_answers.spec.js @@ -12,7 +12,7 @@ import ListCollectorRemovePage from "../../../generated_pages/new_calculated_sum import SupermarketTransportPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport.page"; import SupermarketTransportCostPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/supermarket-transport-cost.page"; import CalculatedSummaryPipingPage from "../../../generated_pages/new_calculated_summary_repeating_and_static_answers/calculated-summary-piping.page"; -import { assertSummaryValues, click } from "../../../helpers"; +import { assertSummaryValues, click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; describe("Calculated summary with repeating answers", () => { @@ -59,16 +59,16 @@ describe("Calculated summary with repeating answers", () => { it("Given I click on a change link, when I use the previous button, I return to the calculated summary", async () => { await dynamicAnswerChangeLink(1).click(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await $(DynamicAnswerPage.previous()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); + await verifyUrlContains(CalculatedSummaryVisitsPage.pageName); }); it("Given I click on a change link, edit an answer and continue, I return to the calculated summary to reconfirm it", async () => { await dynamicAnswerChangeLink(0).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(3); await click(DynamicAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); + await verifyUrlContains(CalculatedSummaryVisitsPage.pageName); await expect(await $(CalculatedSummaryVisitsPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total visits to the shop to be 7. Is this correct?", ); @@ -82,19 +82,19 @@ describe("Calculated summary with repeating answers", () => { await click(ExtraSpendingBlockPage.submit()); // new question - await expect(browser).toHaveUrlContaining(ExtraSpendingMethodBlockPage.pageName); + await verifyUrlContains(ExtraSpendingMethodBlockPage.pageName); await $(ExtraSpendingMethodBlockPage.yes()).click(); await click(ExtraSpendingMethodBlockPage.submit()); // then calculated summary - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £600.00. Is this correct?", ); // then jump straight back to section summary (as other calculated summary is unchanged await click(CalculatedSummarySpendingPage.submit()); - await expect(browser).toHaveUrlContaining(SummaryPage.pageName); + await verifyUrlContains(SummaryPage.pageName); }); it("Given I add a new item to the list, I return to the list collector block, then the dynamic answers, then both calculated summaries to confirm newly added answers", async () => { @@ -105,14 +105,14 @@ describe("Calculated summary with repeating answers", () => { await click(ListCollectorPage.submit()); // return to dynamic answer - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await $$(DynamicAnswerPage.inputs())[2].setValue(100); await $$(DynamicAnswerPage.inputs())[5].setValue(10); await $$(DynamicAnswerPage.inputs())[8].setValue(7); await click(DynamicAnswerPage.submit()); // first calc summary - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total cost of your weekly shopping to be £710.00. Is this correct?", ); @@ -125,7 +125,7 @@ describe("Calculated summary with repeating answers", () => { ); await assertSummaryValues(["4", "3", "2", "14"]); await click(CalculatedSummaryVisitsPage.submit()); - await expect(browser).toHaveUrlContaining(SummaryPage.pageName); + await verifyUrlContains(SummaryPage.pageName); }); it("Given I remove an item from the list which changes the calculated summaries, I return to each incomplete block only to confirm new dynamic answers and totals with answers removed", async () => { @@ -135,12 +135,12 @@ describe("Calculated summary with repeating answers", () => { await expect(await $(SummaryPage.supermarketsListLabel(4)).isExisting()).toBe(false); await $(SummaryPage.supermarketsListRemoveLink(1)).click(); - await expect(browser).toHaveUrlContaining(ListCollectorRemovePage.pageName); + await verifyUrlContains(ListCollectorRemovePage.pageName); await $(ListCollectorRemovePage.yes()).click(); await click(ListCollectorRemovePage.submit()); // section is now incomplete as dynamic answers and calculated summary depend on the removed item - step through each incomplete block only - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await click(DynamicAnswerPage.submit()); // Tesco is now gone @@ -171,13 +171,13 @@ describe("Calculated summary with repeating answers", () => { it("Given I change my answer to a value less than the calculated summary from the previous section, I am able to proceed", async () => { await $(SupermarketTransportPage.weeklyCarTrips()).setValue(9); await click(SupermarketTransportPage.submit()); - await expect(browser).toHaveUrlContaining(SupermarketTransportCostPage.pageName); + await verifyUrlContains(SupermarketTransportCostPage.pageName); }); it("Given I reach the final block, the calculated summary of dynamic answers is piped in correctly", async () => { await $(SupermarketTransportCostPage.weeklyTripsCost()).setValue(30); await click(SupermarketTransportCostPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryPipingPage.pageName); + await verifyUrlContains(CalculatedSummaryPipingPage.pageName); await expect(await $("body").getText()).toContain("Total weekly supermarket spending: £380.00"); await expect(await $("body").getText()).toContain("Total weekly supermarket visits: 10"); await expect(await $("body").getText()).toContain("Total of supermarket visits by car: 9"); @@ -192,7 +192,7 @@ describe("Calculated summary with repeating answers", () => { await dynamicAnswerChangeLink(8).click(); await $$(DynamicAnswerPage.inputs())[5].setValue(1); await click(DynamicAnswerPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryVisitsPage.pageName); + await verifyUrlContains(CalculatedSummaryVisitsPage.pageName); await click(CalculatedSummaryVisitsPage.submit()); await click(SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-1")).getText()).toBe("Completed"); diff --git a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js index 8f1cc6c465..7311aae531 100644 --- a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js +++ b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_blocks.spec.js @@ -11,7 +11,7 @@ import CalculatedSummaryCountPage from "../../../generated_pages/new_calculated_ import HubPage from "../../../base_pages/hub.page"; import FamilyJourneysPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/family-journeys.page"; import BlockSkipPage from "../../../generated_pages/new_calculated_summary_repeating_blocks/block-skip.page"; -import { assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; +import { assertSummaryValues, repeatingAnswerChangeLink, click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; describe("Feature: Calculated Summary using Repeating Blocks", () => { @@ -80,7 +80,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await click(TransportRepeatingBlock2Page.submit()); await $(ListCollectorPage.no()).click(); await click(ListCollectorPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £550.00. Is this correct?", ); @@ -89,7 +89,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I am on the first calculated summary, When I confirm the total, Then I see the second calculated summary with an updated total", async () => { await click(CalculatedSummarySpendingPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await verifyUrlContains(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 18. Is this correct?", ); @@ -99,25 +99,25 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I am on the first calculated summary, When I use one of the change links, Then I see the correct repeating block", async () => { await repeatingAnswerChangeLink(1).click(); - await expect(browser).toHaveUrlContaining(TransportRepeatingBlock1Page.pageName); + await verifyUrlContains(TransportRepeatingBlock1Page.pageName); }); it("Given I have used a change link on a calculated summary to go back to the first repeating block, When I press continue, Then I see the calculated summary I came from", async () => { await click(TransportRepeatingBlock1Page.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); }); it("Given I am on a calculated summary with change links for repeating blocks, When I use a change link and click previous, Then I see the calculated summary I came from", async () => { await repeatingAnswerChangeLink(1).click(); await $(TransportRepeatingBlock1Page.previous()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); }); it("Given I use a repeating block change link on the first calculated summary, When I edit my answer and press continue, Then I see the first calculated summary with a new correct total", async () => { await repeatingAnswerChangeLink(1).click(); await $(TransportRepeatingBlock1Page.transportCost()).setValue(60); await click(TransportRepeatingBlock1Page.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £580.00. Is this correct?", ); @@ -129,7 +129,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await repeatingAnswerChangeLink(2).click(); await $(TransportRepeatingBlock2Page.transportCount()).setValue(12); await click(TransportRepeatingBlock2Page.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await verifyUrlContains(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 24. Is this correct?", ); @@ -141,7 +141,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(SectionOnePage.transportListRemoveLink(1)).click(); await $(RemoveTransportPage.yes()).click(); await click(RemoveTransportPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total monthly expenditure on transport to be £515.00. Is this correct?", ); @@ -150,7 +150,7 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { it("Given I have confirmed the first updated total, When I press continue, Then I see the next calculated summary to confirm that total too", async () => { await click(CalculatedSummarySpendingPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryCountPage.pageName); + await verifyUrlContains(CalculatedSummaryCountPage.pageName); await expect(await $(CalculatedSummaryCountPage.calculatedSummaryTitle()).getText()).toContain( "We calculate the total journeys made per month to be 14. Is this correct?", ); @@ -218,10 +218,10 @@ describe("Feature: Calculated Summary using Repeating Blocks", () => { await $(BlockSkipPage.yes()).click(); await click(BlockSkipPage.submit()); // calculated summary progress is not altered by removing the list collector from the path so next location is summary page - await expect(browser).toHaveUrlContaining(SectionOnePage.pageName); + await verifyUrlContains(SectionOnePage.pageName); await $(SectionOnePage.previous()).click(); // other calculated summary should not be on the path, so go straight back to the spending one which now has none of the list items - await expect(browser).toHaveUrlContaining(CalculatedSummarySpendingPage.pageName); + await verifyUrlContains(CalculatedSummarySpendingPage.pageName); await expect(await $(CalculatedSummarySpendingPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total monthly expenditure on transport to be £100.00. Is this correct?", ); diff --git a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js index 7c43562bd9..348df818c6 100644 --- a/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js +++ b/tests/functional/spec/summaries/calculated_summary/new_calculated_summary_repeating_section.spec.js @@ -33,7 +33,7 @@ import SectionSummarySectionOne from "../../../generated_pages/new_calculated_su import SectionSummarySectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/calculated-summary-section-summary.page"; import DependencyQuestionSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/mutually-exclusive-checkbox.page"; import MinMaxSectionTwo from "../../../generated_pages/new_calculated_summary_cross_section_dependencies_repeating/set-min-max-block.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; describe("Feature: Calculated Summary Repeating Section", () => { @@ -52,7 +52,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await getToFirstCalculatedSummary(); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); }); it("Given I have completed all questions, When I am on the calculated summary and there is no custom page title, Then the page title should use the calculation's title", async () => { @@ -101,13 +101,13 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I edit an answer from the calculated summary page and click the Previous button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.previous()).click(); - await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); + await verifyUrlContains("currency-total-playback/#third-number-answer"); }); it("Given I edit an answer from the calculated summary page and click the Submit button, Then I am taken to the calculated summary page that I clicked the change link from and the browser url should contain an anchor referencing the answer id of the answer I am changing", async () => { await $(CurrencyTotalPlaybackPage.thirdNumberAnswerEdit()).click(); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining("currency-total-playback/#third-number-answer"); + await verifyUrlContains("currency-total-playback/#third-number-answer"); }); it("Given I change an answer, When I get to the currency summary, Then I should see the new total", async () => { @@ -115,7 +115,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FourthNumberBlockPage.fourthNumber()).setValue(19.01); await click(FourthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £30.71. Is this correct?", ); @@ -127,7 +127,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await $(FourthAndAHalfNumberBlockPage.fourthAndAHalfNumberAlsoInTotal()).setValue(""); await click(FourthAndAHalfNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £28.37. Is this correct?", ); @@ -148,7 +148,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { await click(FifthNumberBlockPage.submit()); await click(SixthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $$(CurrencyTotalPlaybackPage.fourthNumberAnswer())).toHaveLength(0); await expect(await $$(CurrencyTotalPlaybackPage.fourthAndAHalfNumberAnswerAlsoInTotal())).toHaveLength(0); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( @@ -215,11 +215,11 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have a calculated summary total that is used as a placeholder in another calculated summary, When I get to the calculated summary page displaying the placeholder, Then I should see the correct total", async () => { await click(NumberTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(BreakdownPage.pageName); + await verifyUrlContains(BreakdownPage.pageName); await $(BreakdownPage.answer1()).setValue(100.0); await $(BreakdownPage.answer2()).setValue(24.58); await click(BreakdownPage.submit()); - await expect(browser).toHaveUrlContaining(SecondCurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(SecondCurrencyTotalPlaybackPage.pageName); await expect(await $(SecondCurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of number values entered to be £124.58. Is this correct?", ); @@ -238,7 +238,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have an answer minimum based on a calculated summary total, When I enter an invalid answer, Then I should see an error message on the page", async () => { await click(CalculatedSummaryTotalConfirmation.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(8.0); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £9.36"); @@ -254,68 +254,68 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I confirm the totals and am on the summary, When I edit and change an answer, Then I go to each incomplete page in turn before I return to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(3.5); await click(ThirdNumberBlockPage.submit()); // first incomplete block - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £9.41. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); // second incomplete block - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(9.0); await click(SetMinMaxBlockPage.submit()); // back to summary - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent minimum value from a calculated summary total, And the minimum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(10.0); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £15.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer more than or equal to £15.91"); await $(SetMinMaxBlockPage.setMinimum()).setValue(16.0); await click(SetMinMaxBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I confirm the totals and am on the summary, When I edit and change an answer that has a dependent maximum value from a calculated summary total, And the maximum value has been changed, Then I must re-validate before I get to the summary", async () => { - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.thirdNumberAnswerEdit()).click(); await $(ThirdNumberBlockPage.thirdNumber()).setValue(1.0); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyTotalPlaybackPage.pageName); + await verifyUrlContains(CurrencyTotalPlaybackPage.pageName); await expect(await $(CurrencyTotalPlaybackPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total of currency values entered to be £6.91. Is this correct?", ); await click(CurrencyTotalPlaybackPage.submit()); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await click(SetMinMaxBlockPage.submit()); await expect(await $(SetMinMaxBlockPage.errorNumber(1)).getText()).toBe("Enter an answer less than or equal to £6.91"); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("Given I am on the summary, When I submit the questionnaire, Then I should see the thank you page", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); }); @@ -348,15 +348,15 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I am on the submit page, When I have completed two repeating sections containing a calculated summary, Then the section status for both repeating sections should be complete", async () => { - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); it("Given I change an answer with a dependent calculated summary question, When I return to the hub, Then only the section status for the repeating section I updated should be incomplete", async () => { - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await $(SubmitPage.skipFourthBlockAnswerEdit()).click(); await $(SkipFourthBlockPage.yes()).click(); await click(SkipFourthBlockPage.submit()); @@ -366,15 +366,15 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I return to a partially completed section with a calculated summary, When I answer the dependent questions and return to the hub, Then the section status for the repeating section I updated should be complete", async () => { - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Partially completed"); await $(HubPage.summaryRowLink("personal-details-section-1")).click(); - await expect(browser).toHaveUrlContaining(SetMinMaxBlockPage.pageName); + await verifyUrlContains(SetMinMaxBlockPage.pageName); await $(SetMinMaxBlockPage.setMinimum()).setValue(10.0); await $(SetMinMaxBlockPage.setMaximum()).setValue(6.0); await click(SetMinMaxBlockPage.submit()); await click(SubmitPage.submit()); - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await expect(await $(HubPage.summaryRowState("personal-details-section-1")).getText()).toBe("Completed"); await expect(await $(HubPage.summaryRowState("personal-details-section-2")).getText()).toBe("Completed"); }); @@ -411,7 +411,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { }); it("Given I have a placeholder displaying a calculated summary value source, When the calculated summary value is from a previous section, Then the value displayed should be correct", async () => { - await expect(browser).toHaveUrlContaining(DependencyQuestionSectionTwo.pageName); + await verifyUrlContains(DependencyQuestionSectionTwo.pageName); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1Label()).getText()).toBe("60 - calculated summary answer (previous section)"); await expect(await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue2Label()).getText()).toBe("40 - calculated summary answer (current section)"); }); @@ -419,7 +419,7 @@ describe("Feature: Calculated Summary Repeating Section", () => { it("Given I have validation using a calculated summary value source, When the calculated summary value is from a previous section, Then the value used to validate should be correct", async () => { await $(DependencyQuestionSectionTwo.checkboxAnswerCalcValue1()).click(); await click(DependencyQuestionSectionTwo.submit()); - await expect(browser).toHaveUrlContaining(MinMaxSectionTwo.pageName); + await verifyUrlContains(MinMaxSectionTwo.pageName); await $(MinMaxSectionTwo.setMinimum()).setValue(59.0); await $(MinMaxSectionTwo.setMaximum()).setValue(1.0); await click(MinMaxSectionTwo.submit()); diff --git a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js index e31770decf..c7db1f1bfa 100644 --- a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js +++ b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_cross_section_dependencies.spec.js @@ -11,7 +11,7 @@ import CurrencyAllPage from "../../../generated_pages/grand_calculated_summary_c import FirstNumberBlockPartAPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/first-number-block-part-a.page"; import FourthNumberBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/fourth-number-block.page"; import tvChoiceBlockPage from "../../../generated_pages/grand_calculated_summary_cross_section_dependencies/tv-choice-block.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; describe("Feature: Grand Calculated Summary", () => { @@ -84,19 +84,19 @@ describe("Feature: Grand Calculated Summary", () => { await $(CalculatedSummarySectionSummaryPage.skipAnswer2Edit()).click(); await $(SkipCalculatedSummaryPage.no()).click(); await click(SkipCalculatedSummaryPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); + await verifyUrlContains(CurrencyQuestion3Page.pageName); }); it("Given I confirm the calculated summary and the blocks following it are already complete, When I press submit, Then I am returned to the section summary anchored to the answer I edited initially", async () => { await click(CurrencyQuestion3Page.submit()); - await expect(browser).toHaveUrlContaining("calculated-summary-section/#skip-answer-2"); + await verifyUrlContains("calculated-summary-section/#skip-answer-2"); }); it("Given I change an answer, When I press previous from the now incomplete calculated summary, Then I am routed to the block before the calculated summary", async () => { await $(CalculatedSummarySectionSummaryPage.thirdNumberAnswerPartAEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartA()).setValue(120); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); + await verifyUrlContains(CurrencyQuestion3Page.pageName); await $(CurrencyQuestion3Page.previous()).click(); - await expect(browser).toHaveUrlContaining(SkipCalculatedSummaryPage.pageName); + await verifyUrlContains(SkipCalculatedSummaryPage.pageName); }); it("Given I complete the section, When I go back to the grand calculated summary, Then I see the new calculated summary included", async () => { await click(SkipCalculatedSummaryPage.submit()); @@ -113,12 +113,12 @@ describe("Feature: Grand Calculated Summary", () => { await $(CurrencyQuestion3Page.thirdNumberAnswerPartBEdit()).click(); await $(ThirdNumberBlockPage.thirdNumberPartB()).setValue(10); await click(ThirdNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(FourthNumberBlockPage.pageName); + await verifyUrlContains(FourthNumberBlockPage.pageName); await $(FourthNumberBlockPage.fourthNumber()).setValue(1); await click(FourthNumberBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CurrencyQuestion3Page.pageName); + await verifyUrlContains(CurrencyQuestion3Page.pageName); await click(CurrencyQuestion3Page.submit()); - await expect(browser).toHaveUrlContaining(CurrencyAllPage.pageName); + await verifyUrlContains(CurrencyAllPage.pageName); await expect(await $(CurrencyAllPage.grandCalculatedSummaryTitle()).getText()).toBe( "The grand calculated summary is calculated to be £461.00. Is this correct?", ); diff --git a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js index 1c891e413e..da64efe138 100644 --- a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js +++ b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_inside_repeating_section.spec.js @@ -1,4 +1,4 @@ -import { assertSummaryValues, click, listItemIds } from "../../../helpers"; +import { assertSummaryValues, click, listItemIds, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; import AddVehiclePage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/list-collector-add.page.js"; import AnyCostPage from "../../../generated_pages/grand_calculated_summary_inside_repeating_section/any-cost.page.js"; @@ -85,7 +85,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { "We calculate the monthly running costs of your Car to be £225.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £315.00. Is this correct?", ); @@ -107,7 +107,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I enter a valid value for the Grand Calculated Summary breakdown, When I press continue, Then I see an Interstitial page with my values correctly piped in", async () => { await $(GcsBreakdownBlockPage.payOther()).setValue(100); await click(GcsBreakdownBlockPage.submit()); - await expect(browser).toHaveUrlContaining(GcsPipingPage.pageName); + await verifyUrlContains(GcsPipingPage.pageName); await expect(await $("body").getText()).toContain("Monthly maintenance cost: £100.00"); await expect(await $("body").getText()).toContain("Monthly fuel cost: £125.00"); await expect(await $("body").getText()).toContain("Total base cost: £90.00"); @@ -130,7 +130,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { "We calculate the monthly running costs of your Van to be £95.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £185.00. Is this correct?", ); @@ -141,53 +141,53 @@ describe("Grand Calculated Summary inside a repeating section", () => { }); it("Given I am at a Grand Summary inside a repeating section, When I click the change link for a repeating calculated summary, Then I am taken to the correct page", async () => { - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostEdit()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(CalculatedSummaryRunningCostPage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I have used a change link for a repeating calculated summary, When I click the continue button, Then I am taken to the Grand Calculated Summary", async () => { await click(CalculatedSummaryRunningCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I am at a Grand Summary inside a repeating section, When I click the change link for a non repeating calculated summary, Then I am taken to the correct page", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); + await verifyUrlContains(CalculatedSummaryBaseCostPage.pageName); }); it("Given I have used a change link for a non repeating calculated summary from a repeating section, When I click the continue button, Then I am taken to the Grand Calculated Summary for the correct list item", async () => { await click(CalculatedSummaryBaseCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I use a change link for a repeating calculated summary, When I use a change link there, Then pressing continue twice takes me back to the correct grand calculated summary", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryRunningCostEdit()).click(); await $(CalculatedSummaryRunningCostPage.vehicleMaintenanceCostEdit()).click(); - await expect(browser).toHaveUrlContaining(VehicleMaintenanceBlockPage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(VehicleMaintenanceBlockPage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); await click(VehicleMaintenanceBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(CalculatedSummaryRunningCostPage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); await click(CalculatedSummaryRunningCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I use a change link for a non repeating calculated summary, When I use a change link there, Then pressing continue twice takes me back to the correct grand calculated summary", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); await $(CalculatedSummaryBaseCostPage.financeCostAnswerEdit()).click(); - await expect(browser).toHaveUrlContaining(FinanceCostPage.pageName); - await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + await verifyUrlContains(FinanceCostPage.pageName); + await verifyUrlContains(`return_to_list_item_id=${vehicleListItemIds[1]}`); await click(FinanceCostPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); - await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + await verifyUrlContains(CalculatedSummaryBaseCostPage.pageName); + await verifyUrlContains(`return_to_list_item_id=${vehicleListItemIds[1]}`); await click(CalculatedSummaryBaseCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I change a non repeating answer which results in the section being incomplete, When I press continue, Then I go to the next incomplete location with the list item id preserved", async () => { @@ -195,19 +195,19 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(CalculatedSummaryBaseCostPage.financeCostAnswerEdit()).click(); await $(FinanceCostPage.answer()).setValue(70); await click(FinanceCostPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryBaseCostPage.pageName); + await verifyUrlContains(CalculatedSummaryBaseCostPage.pageName); await expect(await $(CalculatedSummaryBaseCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the total base cost for any owned vehicle to be £100.00. Is this correct?", ); await click(CalculatedSummaryBaseCostPage.submit()); - await expect(browser).toHaveUrlContaining(BaseCostPaymentBreakdownPage.pageName); - await expect(browser).toHaveUrlContaining(`return_to_list_item_id=${vehicleListItemIds[1]}`); + await verifyUrlContains(BaseCostPaymentBreakdownPage.pageName); + await verifyUrlContains(`return_to_list_item_id=${vehicleListItemIds[1]}`); }); it("Given I have changed a non repeating answer, When I return to the Grand Calculated Summary, Then I see the correctly updated values", async () => { await click(BaseCostPaymentBreakdownPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £195.00. Is this correct?", ); @@ -218,12 +218,12 @@ describe("Grand Calculated Summary inside a repeating section", () => { await $(CalculatedSummaryRunningCostPage.vehicleMaintenanceCostEdit()).click(); await $(VehicleMaintenanceBlockPage.vehicleMaintenanceCost()).setValue(75); await click(VehicleMaintenanceBlockPage.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummaryRunningCostPage.pageName); + await verifyUrlContains(CalculatedSummaryRunningCostPage.pageName); await expect(await $(CalculatedSummaryRunningCostPage.calculatedSummaryTitle()).getText()).toBe( "We calculate the monthly running costs of your Van to be £120.00. Is this correct?", ); await click(CalculatedSummaryRunningCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £220.00. Is this correct?", ); @@ -239,7 +239,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(VehicleDetailsSectionPage.submit()); await $(HubPage.summaryRowLink("vehicle-details-section-1")).click(); await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await expect(await $(GcsBreakdownBlockPage.questionText()).getText()).toBe("How do you pay for the monthly fees of £325.00?"); await $(GcsBreakdownBlockPage.payCredit()).setValue(125); await click(GcsBreakdownBlockPage.submit()); @@ -262,13 +262,13 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I have two partially complete repeating sections, When I press continue, Then I am taken straight to the grand calculated summary as it is the first incomplete block", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[0]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[0]}/`); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £335.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await $(GcsBreakdownBlockPage.payCredit()).setValue(135); await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); @@ -276,8 +276,8 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I've completed the first repeating section, When I press continue, I am taken straight to the grand calculated summary of the second repeat", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £230.00. Is this correct?", ); @@ -286,22 +286,22 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I go to the non-repeating calculated summary, When I click a change link for a dynamic answer and press continue twice, Then I go back to the Grand Calculated Summary for the correct list item", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); await dynamicAnswerChangeLink(2).click(); - await expect(browser).toHaveUrlContaining(DynamicCostBlockPage.pageName); + await verifyUrlContains(DynamicCostBlockPage.pageName); await click(DynamicCostBlockPage.submit()); await click(CalculatedSummaryBaseCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I go to the non-repeating calculated summary, When I click a change link for a repeating block answer and press continue twice, Then I go back to the Grand Calculated Summary for the correct list item", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); await dynamicAnswerChangeLink(0).click(); - await expect(browser).toHaveUrlContaining(CostRepeatingBlock1RepeatingBlockPage.pageName); - await expect(browser).toHaveUrlContaining(`costs/${costListItemIds[0]}/`); + await verifyUrlContains(CostRepeatingBlock1RepeatingBlockPage.pageName); + await verifyUrlContains(`costs/${costListItemIds[0]}/`); await click(CostRepeatingBlock1RepeatingBlockPage.submit()); await click(CalculatedSummaryBaseCostPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); - await expect(browser).toHaveUrlContaining(`vehicles/${vehicleListItemIds[1]}/`); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(`vehicles/${vehicleListItemIds[1]}/`); }); it("Given I edit a dynamic answer from the non-repeating calculated summary, When I return to the Grand Calculated Summary, Then I see the correct total", async () => { @@ -311,7 +311,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { await click(DynamicCostBlockPage.submit()); await click(CalculatedSummaryBaseCostPage.submit()); await click(BaseCostPaymentBreakdownPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £250.00. Is this correct?", ); @@ -320,13 +320,13 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I edit a repeating block answer from the non-repeating calculated summary, When I return to the Grand Calculated Summary, Then I see the correct total", async () => { await $(GrandCalculatedSummaryVehiclePage.calculatedSummaryBaseCostEdit()).click(); await dynamicAnswerChangeLink(1).click(); - await expect(browser).toHaveUrlContaining(CostRepeatingBlock1RepeatingBlockPage.pageName); - await expect(browser).toHaveUrlContaining(`costs/${costListItemIds[1]}/`); + await verifyUrlContains(CostRepeatingBlock1RepeatingBlockPage.pageName); + await verifyUrlContains(`costs/${costListItemIds[1]}/`); await $(CostRepeatingBlock1RepeatingBlockPage.repeatingBlock1CostBase()).setValue(22); await click(CostRepeatingBlock1RepeatingBlockPage.submit()); await click(CalculatedSummaryBaseCostPage.submit()); await click(BaseCostPaymentBreakdownPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Van is calculated to be £260.00. Is this correct?", ); @@ -334,12 +334,12 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I complete the Grand Calculated Summary, When I press continue, I am taken to the calculation question that depends on it and cant proceed till entering a valid breakdown", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await click(GcsBreakdownBlockPage.submit()); await expect(await $(GcsBreakdownBlockPage.errorNumber()).getText()).toBe("Enter answers that add up to 260"); await $(GcsBreakdownBlockPage.payOther()).setValue(50); await click(GcsBreakdownBlockPage.submit()); - await expect(browser).toHaveUrlContaining(VehicleDetailsSectionPage.pageName); + await verifyUrlContains(VehicleDetailsSectionPage.pageName); }); it("Given I have changed a static calculated summary during the section, When I return to the Hub, Then I see the other repeating section is incomplete as it also uses this calculated summary", async () => { @@ -350,7 +350,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I go to the other repeating section, When I enter the section, Then I see the grand calculated summary with correctly updated totals", async () => { await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryVehiclePage.pageName); + await verifyUrlContains(GrandCalculatedSummaryVehiclePage.pageName); await expect(await $(GrandCalculatedSummaryVehiclePage.grandCalculatedSummaryTitle()).getText()).toBe( "The total cost of owning and running your Car is calculated to be £365.00. Is this correct?", ); @@ -358,7 +358,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { it("Given I the grand calculated summary has changed, When I confirm it, Then I see the breakdown question and need to update the values", async () => { await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await $(GcsBreakdownBlockPage.payOther()).setValue(130); await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); @@ -386,7 +386,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { "The total cost of owning and running your Car is calculated to be £355.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await $(GcsBreakdownBlockPage.payOther()).setValue(120); await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); @@ -395,7 +395,7 @@ describe("Grand Calculated Summary inside a repeating section", () => { "The total cost of owning and running your Van is calculated to be £250.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); await $(GcsBreakdownBlockPage.payOther()).setValue(40); await click(GcsBreakdownBlockPage.submit()); await click(VehicleDetailsSectionPage.submit()); @@ -436,6 +436,6 @@ describe("Grand Calculated Summary inside a repeating section", () => { "The total cost of owning and running your Van is calculated to be £300.00. Is this correct?", ); await click(GrandCalculatedSummaryVehiclePage.submit()); - await expect(browser).toHaveUrlContaining(GcsBreakdownBlockPage.pageName); + await verifyUrlContains(GcsBreakdownBlockPage.pageName); }); }); diff --git a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js index 40eab2b584..9e176b898e 100644 --- a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js +++ b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_overlapping_answers.spec.js @@ -8,7 +8,7 @@ import Block3Page from "../../../generated_pages/grand_calculated_summary_overla import CalculatedSummary4Page from "../../../generated_pages/grand_calculated_summary_overlapping_answers/calculated-summary-4.page"; import GrandCalculatedSummaryShoppingPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/grand-calculated-summary-shopping.page"; import Section1SummaryPage from "../../../generated_pages/grand_calculated_summary_overlapping_answers/section-1-summary.page"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; describe("Feature: Grand Calculated Summary", () => { @@ -48,18 +48,18 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block1Page.submit()); // taken back to calculated summary - await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); - await expect(await browser.getUrl()).toContain( + await verifyUrlContains(CalculatedSummary2Page.pageName); + await verifyUrlContains( "/questionnaire/calculated-summary-2/?return_to=grand-calculated-summary&return_to_block_id=grand-calculated-summary-shopping&return_to_answer_id=calculated-summary-2#q1-a2", ); await click(CalculatedSummary2Page.submit()); // then grand calculated summary - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await verifyUrlContains(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary of purchases this week comes to £460.00. Is this correct?.", ); - await expect(await browser.getUrl()).toContain("/questionnaire/grand-calculated-summary-shopping/#calculated-summary-2"); + await verifyUrlContains("/questionnaire/grand-calculated-summary-shopping/#calculated-summary-2"); }); it("Given I edit an answer that is used in two calculated summaries, if I edit it from the first calculated summary change link, I taken through each block between the question and the second calculated summary before returning to the grand calculated summary", async () => { @@ -69,21 +69,21 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it - await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); + await verifyUrlContains(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toBe( "Total of eggs and cheese is calculated to be £700.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it - await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await verifyUrlContains(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Total extra items cost is calculated to be £410.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then grand calculated summary - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await verifyUrlContains(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £1,220.00. Is this correct?", ); @@ -96,21 +96,21 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block2Page.submit()); // taken back to the FIRST calculated summary which uses it - await expect(browser).toHaveUrlContaining(CalculatedSummary2Page.pageName); + await verifyUrlContains(CalculatedSummary2Page.pageName); await expect(await $(CalculatedSummary2Page.calculatedSummaryTitle()).getText()).toBe( "Total of eggs and cheese is calculated to be £800.00. Is this correct?", ); await click(CalculatedSummary2Page.submit()); // taken back to the SECOND calculated summary which uses it - await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await verifyUrlContains(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toContain( "Total extra items cost is calculated to be £510.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then grand calculated summary - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryShoppingPage.pageName); + await verifyUrlContains(GrandCalculatedSummaryShoppingPage.pageName); await expect(await $(GrandCalculatedSummaryShoppingPage.grandCalculatedSummaryTitle()).getText()).toContain( "Grand Calculated Summary of purchases this week comes to £1,420.00. Is this correct?", ); diff --git a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js index 9c4f1b34ac..90a1028c4f 100644 --- a/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js +++ b/tests/functional/spec/summaries/grand_calculated_summary/grand_calculated_summary_repeating_answers.spec.js @@ -30,7 +30,7 @@ import GrandCalculatedSummary5Page from "../../../generated_pages/grand_calculat import AnyUtilityBillsPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/any-utility-bills.page"; import Section4SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-4-summary.page"; import Section5SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-5-summary.page"; -import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink, click } from "../../../helpers"; +import { assertSummaryItems, assertSummaryValues, repeatingAnswerChangeLink, click, verifyUrlContains } from "../../../helpers"; import { expect } from "@wdio/globals"; import InternetBreakdownBlockPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/internet-breakdown-block.page"; import Section6SummaryPage from "../../../generated_pages/grand_calculated_summary_repeating_answers/section-6-summary.page"; @@ -75,22 +75,22 @@ describe("Feature: Grand Calculated Summary", () => { "Grand Calculated Summary for shopping and entertainment is calculated to be £415.00. Is this correct?", ); await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); + await verifyUrlContains(CalculatedSummary1Page.pageName); await click(CalculatedSummary1Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await verifyUrlContains(GrandCalculatedSummary2Page.pageName); }); it("Given I click on the change link for a calculated summary then one for an answer, When I press previous twice, I am return to the calculated summary then grand calculated summary", async () => { await $(GrandCalculatedSummary2Page.calculatedSummary1Edit()).click(); await $(CalculatedSummary1Page.q1A1Edit()).click(); - await expect(browser).toHaveUrlContaining(Block1Page.pageName); + await verifyUrlContains(Block1Page.pageName); await $(Block1Page.previous()).click(); - await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); + await verifyUrlContains(CalculatedSummary1Page.pageName); await $(CalculatedSummary1Page.previous()).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await verifyUrlContains(GrandCalculatedSummary2Page.pageName); }); it("Given I go back to the calculated summary and then to a question and edit the answer. I am first taken back to the each calculated summary that uses the answer, the grand calculated summary in section 1, and then the updated grand calculated summary in section 3.", async () => { @@ -99,20 +99,20 @@ describe("Feature: Grand Calculated Summary", () => { "Calculated Summary for games expenditure is calculated to be £15.00. Is this correct?", ); await $(CalculatedSummary4Page.q4A1Edit()).click(); - await expect(browser).toHaveUrlContaining(Block4Page.pageName); + await verifyUrlContains(Block4Page.pageName); await $(Block4Page.q4A1()).setValue(50); await click(Block4Page.submit()); // first taken back to the calculated summary which has updated - await expect(browser).toHaveUrlContaining(CalculatedSummary4Page.pageName); + await verifyUrlContains(CalculatedSummary4Page.pageName); await expect(await $(CalculatedSummary4Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for games expenditure is calculated to be £60.00. Is this correct?", ); await click(CalculatedSummary4Page.submit()); // then taken back to the grand calculated summary which has also been updated correctly - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await verifyUrlContains(GrandCalculatedSummary2Page.pageName); await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for shopping and entertainment is calculated to be £460.00. Is this correct?", ); @@ -126,19 +126,19 @@ describe("Feature: Grand Calculated Summary", () => { // change first answer await $(CalculatedSummary1Page.q1A1Edit()).click(); - await expect(browser).toHaveUrlContaining(Block1Page.pageName); + await verifyUrlContains(Block1Page.pageName); await $(Block1Page.q1A1()).setValue(100); await click(Block1Page.submit()); // go to each calculated summary that uses the answer in turn, then each grand calculated summary up to the one we were editing - await expect(browser).toHaveUrlContaining(CalculatedSummary1Page.pageName); + await verifyUrlContains(CalculatedSummary1Page.pageName); await expect(await $(CalculatedSummary1Page.calculatedSummaryTitle()).getText()).toBe( "Calculated Summary for food expenditure is calculated to be £190.00. Is this correct?", ); // change another answer await $(CalculatedSummary1Page.q2A2Edit()).click(); - await expect(browser).toHaveUrlContaining(Block2Page.pageName); + await verifyUrlContains(Block2Page.pageName); await $(Block2Page.q2A2()).setValue(400); await click(Block2Page.submit()); @@ -149,11 +149,11 @@ describe("Feature: Grand Calculated Summary", () => { // Go to each calculated/grand calculated summary including this answer and reconfirm before being taken back to grand calculated summary await click(CalculatedSummary1Page.submit()); - await expect(browser).toHaveUrlContaining(CalculatedSummary3Page.pageName); + await verifyUrlContains(CalculatedSummary3Page.pageName); await click(CalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary1Page.pageName); + await verifyUrlContains(GrandCalculatedSummary1Page.pageName); await click(GrandCalculatedSummary1Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary2Page.pageName); + await verifyUrlContains(GrandCalculatedSummary2Page.pageName); await expect(await $(GrandCalculatedSummary2Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for shopping and entertainment is calculated to be £910.00. Is this correct?", ); @@ -192,7 +192,7 @@ describe("Feature: Grand Calculated Summary", () => { await click(Block4Page.submit()); await click(CalculatedSummary4Page.submit()); // should be back at Hub, and grand calculated summary section not present - await expect(browser).toHaveUrlContaining(HubPage.pageName); + await verifyUrlContains(HubPage.pageName); await expect(await $(HubPage.summaryRowLink("section-3")).isExisting()).toBe(false); }); @@ -298,10 +298,10 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I a grand calculated summary featuring repeating answers, When I click edit links to return to a dynamic answer then previous twice, Then I return to the grand calculated summary where I started", async () => { await $(GrandCalculatedSummary5Page.calculatedSummary5Edit()).click(); await repeatingAnswerChangeLink(0).click(); - await expect(browser).toHaveUrlContaining(DynamicAnswerPage.pageName); + await verifyUrlContains(DynamicAnswerPage.pageName); await $(DynamicAnswerPage.previous()).click(); await $(CalculatedSummary5Page.previous()).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); }); it("Given I have a grand calculated summary featuring repeating answers, When I edit a dynamic answer, Then I return to the calculated summary to confirm, and then each affected grand calculated summary in turn", async () => { @@ -310,12 +310,12 @@ describe("Feature: Grand Calculated Summary", () => { await $$(DynamicAnswerPage.inputs())[0].setValue("175"); await click(DynamicAnswerPage.submit()); await click(CalculatedSummary5Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await verifyUrlContains(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £305.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,155.00. Is this correct?", ); @@ -326,7 +326,7 @@ describe("Feature: Grand Calculated Summary", () => { await repeatingAnswerChangeLink(2).click(); await $(StreamingServiceRepeatingBlock1Page.previous()).click(); await $(CalculatedSummary5Page.previous()).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); }); it("Given I have a grand calculated summary featuring repeating answers, When I edit a list repeating block answer, Then I return to the calculated summary to confirm, and then the grand calculated summary to confirm", async () => { @@ -335,12 +335,12 @@ describe("Feature: Grand Calculated Summary", () => { await $(StreamingServiceRepeatingBlock1Page.streamingServiceMonthlyCost()).setValue(12); await click(StreamingServiceRepeatingBlock1Page.submit()); await click(CalculatedSummary5Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await verifyUrlContains(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £309.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,159.00. Is this correct?", ); @@ -361,7 +361,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I use the grand calculated summary for validation, When I enter values with the correct sum, Then I progress to the summary page", async () => { await $(InternetBreakdownBlockPage.internetPhone()).setValue(40); await click(InternetBreakdownBlockPage.submit()); - await expect(browser).toHaveUrlContaining(Section6SummaryPage.pageName); + await verifyUrlContains(Section6SummaryPage.pageName); await click(Section6SummaryPage.submit()); }); @@ -391,22 +391,22 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I return to the grand calculated summary section, When I go to each grand calculated summary, Then I see the correct new values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await verifyUrlContains(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await verifyUrlContains(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 100 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); await click(GrandCalculatedSummary5Page.submit()); - await expect(browser).toHaveUrlContaining(Section6SummaryPage.pageName); + await verifyUrlContains(Section6SummaryPage.pageName); await expect(await $$(summaryRowTitles)[0].getText()).toBe("How did you use the 100 GB across your devices?"); await click(Section6SummaryPage.submit()); await expect(await $(HubPage.summaryRowState("section-6")).getText()).toBe("Completed"); @@ -441,17 +441,17 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I the grand calculated summary section is now incomplete, When I return to the section, Then I am taken to each updated grand calculated summary to confirm the new total", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await verifyUrlContains(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £359.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await verifyUrlContains(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 105 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,209.00. Is this correct?", ); @@ -481,17 +481,17 @@ describe("Feature: Grand Calculated Summary", () => { it("Given the section has reverted to partially complete, When I go back to the section, Then I am taken to each grand calculated summary to reconfirm with correct values", async () => { await $(HubPage.summaryRowLink("section-6")).click(); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary3Page.pageName); + await verifyUrlContains(GrandCalculatedSummary3Page.pageName); await expect(await $(GrandCalculatedSummary3Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for monthly spending on bills and services is calculated to be £349.00. Is this correct?", ); await click(GrandCalculatedSummary3Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary4Page.pageName); + await verifyUrlContains(GrandCalculatedSummary4Page.pageName); await expect(await $(GrandCalculatedSummary4Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for internet usage is calculated to be 85 GB. Is this correct?", ); await click(GrandCalculatedSummary4Page.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummary5Page.pageName); + await verifyUrlContains(GrandCalculatedSummary5Page.pageName); await expect(await $(GrandCalculatedSummary5Page.grandCalculatedSummaryTitle()).getText()).toBe( "Grand Calculated Summary for total monthly household expenditure is calculated to be £1,199.00. Is this correct?", ); @@ -518,7 +518,7 @@ describe("Feature: Grand Calculated Summary", () => { it("Given I display multiple grand calculated summaries on an Interstitial page, When I reach the page, Then I see the correct values piped in", async () => { await $(PersonalExpenditureBlockPage.personalExpenditure()).setValue(1100); await click(PersonalExpenditureBlockPage.submit()); - await expect(browser).toHaveUrlContaining(GrandCalculatedSummaryPipingPage.pageName); + await verifyUrlContains(GrandCalculatedSummaryPipingPage.pageName); await expect(await $("body").getText()).toContain("Total household expenditure: £1,199.00"); await expect(await $("body").getText()).toContain("Personal contribution: £1,100.00"); await expect(await $("body").getText()).toContain("Total internet usage: 85 GB"); diff --git a/tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js b/tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js index caad8184a8..a78a46c300 100644 --- a/tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js +++ b/tests/functional/spec/summaries/question_summary/custom_question_summary.spec.js @@ -2,7 +2,7 @@ import AddressBlockPage from "../../../generated_pages/custom_question_summary/a import AgeBlock from "../../../generated_pages/custom_question_summary/age.page.js"; import NameBlockPage from "../../../generated_pages/custom_question_summary/name.page.js"; import SubmitPage from "../../../generated_pages/custom_question_summary/submit.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Summary Screen", () => { beforeEach("Load the survey", async () => { await browser.openQuestionnaire("test_custom_question_summary.json"); @@ -19,7 +19,7 @@ describe("Summary Screen", () => { await click(NameBlockPage.submit()); await click(AddressBlockPage.submit()); await click(AgeBlock.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.summaryRowState("name-question-concatenated-answer")).getText()).toBe("No answer provided"); }); diff --git a/tests/functional/spec/summaries/section_summary/section_summary.spec.js b/tests/functional/spec/summaries/section_summary/section_summary.spec.js index e13a484581..c21261604b 100644 --- a/tests/functional/spec/summaries/section_summary/section_summary.spec.js +++ b/tests/functional/spec/summaries/section_summary/section_summary.spec.js @@ -8,7 +8,7 @@ import ListedPage from "../../../generated_pages/section_summary/listed.page.js" import NumberOfPeoplePage from "../../../generated_pages/section_summary/number-of-people.page.js"; import PropertyDetailsSummaryPage from "../../../generated_pages/section_summary/property-details-section-summary.page.js"; import SubmitPage from "../../../generated_pages/section_summary/submit.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Section Summary", () => { describe("Given I start a Test Section Summary survey and complete to Section Summary", () => { @@ -35,33 +35,33 @@ describe("Section Summary", () => { it("When I select edit from the section summary and click previous on the question page, Then I should be taken back to the section summary", async () => { await $(PropertyDetailsSummaryPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.previous()).click(); - await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.url()); + await verifyUrlContains(PropertyDetailsSummaryPage.url()); }); it("When I continue on the section summary page, Then I should be taken to the next section", async () => { await click(PropertyDetailsSummaryPage.submit()); - await expect(browser).toHaveUrlContaining(HouseType.pageName); + await verifyUrlContains(HouseType.pageName); }); it("When I select edit from Section Summary but change routing, Then I should step through the section and be returned to the Section Summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); + await verifyUrlContains(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); - await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.pageName); + await verifyUrlContains(PropertyDetailsSummaryPage.pageName); }); it("When I select edit from Section Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { await $(PropertyDetailsSummaryPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); + await verifyUrlContains(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); - await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); + await verifyUrlContains(InsuranceAddressPage.pageName); await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(browser).toHaveUrlContaining(PropertyDetailsSummaryPage.pageName); + await verifyUrlContains(PropertyDetailsSummaryPage.pageName); }); }); @@ -78,14 +78,14 @@ describe("Section Summary", () => { await $(NumberOfPeoplePage.answer()).setValue(3); await click(NumberOfPeoplePage.submit()); await click(HouseholdCountSectionSummaryPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("When I select edit from Final Summary and don't change an answer, Then I should be taken to the Final Summary", async () => { await $(SubmitPage.summaryShowAllButton()).click(); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await click(InsuranceAddressPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("When I select edit from Final Summary and change an answer that doesn't affect completeness, Then I should be taken to the Final Summary", async () => { @@ -93,7 +93,7 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceAddressAnswerEdit()).click(); await $(InsuranceAddressPage.answer()).setValue("Test Address"); await click(InsuranceAddressPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); it("When I select edit from Final Summary but change routing, Then I should step through the section and be returned to the Final Summary once all new questions have been answered", async () => { @@ -101,9 +101,9 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); + await verifyUrlContains(AddressDurationPage.pageName); await click(AddressDurationPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I select edit from Final Summary but change routing, Then using previous should not prevent me returning to the section summary once all new questions have been answered", async () => { @@ -111,12 +111,12 @@ describe("Section Summary", () => { await $(SubmitPage.insuranceTypeAnswerEdit()).click(); await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); - await expect(browser).toHaveUrlContaining(AddressDurationPage.pageName); + await verifyUrlContains(AddressDurationPage.pageName); await $(AddressDurationPage.previous()).click(); - await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); + await verifyUrlContains(InsuranceAddressPage.pageName); await click(InsuranceAddressPage.submit()); await click(AddressDurationPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); }); it("When I select edit from Final Summary and change an answer and then go to the next question and click previous, Since I cannot return to the section summary yet I return to the previous block in the section", async () => { await $(SubmitPage.summaryShowAllButton()).click(); @@ -124,14 +124,14 @@ describe("Section Summary", () => { await $(InsuranceTypePage.contents()).click(); await click(InsuranceTypePage.submit()); await $(AddressDurationPage.previous()).click(); - await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); + await verifyUrlContains(InsuranceAddressPage.pageName); }); it("When I change an answer, Then the final summary should display the updated value", async () => { await $(SubmitPage.summaryShowAllButton()).click(); await expect(await $(SubmitPage.insuranceAddressAnswer()).getText()).toBe("No answer provided"); await $(SubmitPage.insuranceAddressAnswerEdit()).click(); - await expect(browser).toHaveUrlContaining(InsuranceAddressPage.pageName); + await verifyUrlContains(InsuranceAddressPage.pageName); await $(InsuranceAddressPage.answer()).setValue("Test Address"); await click(InsuranceAddressPage.submit()); await $(SubmitPage.summaryShowAllButton()).click(); diff --git a/tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js b/tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js index f06f255b86..81d1e9d8c2 100644 --- a/tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js +++ b/tests/functional/spec/summaries/section_summary/section_summary_repeating_sections.spec.js @@ -6,7 +6,7 @@ import PersonalSummaryPage from "../../../generated_pages/repeating_section_summ import ProxyPage from "../../../generated_pages/repeating_section_summaries/proxy.page"; import DateOfBirthPage from "../../../generated_pages/repeating_section_summaries/date-of-birth.page"; import HubPage from "../../../base_pages/hub.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Repeating Section Summaries", () => { describe("Given the user has added some members to the household and is on the Hub", () => { @@ -15,7 +15,7 @@ describe("Feature: Repeating Section Summaries", () => { // Ensure the questionnaire fully loads await browser.pause(100); // Ensure we are on the Hub - await expect(browser).toHaveUrlContaining(HubPage.url()); + await verifyUrlContains(HubPage.url()); // Start first section to add household members await $(HubPage.summaryRowLink("section")).click(); diff --git a/tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js b/tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js index fe70621269..bd6804880e 100644 --- a/tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js +++ b/tests/functional/spec/summaries/show_section_summary_on_completion/show_section_summary_on_completion.spec.js @@ -5,7 +5,7 @@ import proxyQuestionPage from "../../../generated_pages/show_section_summary_on_ import accommodationSectionSummary from "../../../generated_pages/show_section_summary_on_completion/accommodation-section-summary.page"; import hubPage from "../../../base_pages/hub.page.js"; -import { click } from "../../../helpers"; +import { click, verifyUrlContains } from "../../../helpers"; describe("Feature: Show section summary on completion", () => { before("Launch survey", async () => { @@ -17,7 +17,7 @@ describe("Feature: Show section summary on completion", () => { await $(employmentStatusBlockPage.workingAsAnEmployee()).click(); await click(employmentStatusBlockPage.submit()); - await expect(browser).toHaveUrlContaining(hubPage.url()); + await verifyUrlContains(hubPage.url()); }); }); @@ -25,7 +25,7 @@ describe("Feature: Show section summary on completion", () => { it("When I return to a completed section from the hub, Then I am returned to that section summary", async () => { await $(hubPage.summaryRowLink("employment-section")).click(); - await expect(browser).toHaveUrlContaining(employmentSectionSummary.url()); + await verifyUrlContains(employmentSectionSummary.url()); }); }); @@ -39,7 +39,7 @@ describe("Feature: Show section summary on completion", () => { await $(proxyQuestionPage.noIMAnsweringForMyself()).click(); await click(proxyQuestionPage.submit()); - await expect(browser).toHaveUrlContaining(accommodationSectionSummary.url()); + await verifyUrlContains(accommodationSectionSummary.url()); }); }); @@ -51,7 +51,7 @@ describe("Feature: Show section summary on completion", () => { it("When I return to a completed section from the hub, Then I am returned to the correct section summary", async () => { await $(hubPage.summaryRowLink("accommodation-section")).click(); - await expect(browser).toHaveUrlContaining(accommodationSectionSummary.url()); + await verifyUrlContains(accommodationSectionSummary.url()); }); }); }); diff --git a/tests/functional/spec/textfield.spec.js b/tests/functional/spec/textfield.spec.js index cb53365171..c5e0d1e061 100644 --- a/tests/functional/spec/textfield.spec.js +++ b/tests/functional/spec/textfield.spec.js @@ -1,6 +1,6 @@ import TextFieldPage from "../generated_pages/textfield/name-block.page.js"; import SubmitPage from "../generated_pages/textfield/submit.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Textfield", () => { it("Given a textfield option, a user should be able to click the label of the textfield to focus", async () => { await browser.openQuestionnaire("test_textfield.json"); @@ -12,7 +12,7 @@ describe("Textfield", () => { await browser.openQuestionnaire("test_textfield.json"); await $(TextFieldPage.name()).setValue("'Twenty><&Five'"); await click(TextFieldPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.pageName); + await verifyUrlContains(SubmitPage.pageName); await expect(await $(SubmitPage.nameAnswer()).getText()).toBe("'Twenty><&Five'"); await $(SubmitPage.nameAnswerEdit()).click(); await $(TextFieldPage.name()).getValue(); diff --git a/tests/functional/spec/textfield_suggestions.spec.js b/tests/functional/spec/textfield_suggestions.spec.js index d587a6f4dd..96455563e7 100644 --- a/tests/functional/spec/textfield_suggestions.spec.js +++ b/tests/functional/spec/textfield_suggestions.spec.js @@ -1,7 +1,7 @@ import SuggestionsPage from "../generated_pages/textfield_suggestions/country-block.page.js"; import MultipleSuggestionsPage from "../generated_pages/textfield_suggestions/multiple-country-block.page.js"; import SubmitPage from "../generated_pages/textfield_suggestions/submit.page.js"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Suggestions", () => { it("Given I open a textfield with a suggestions url, when I have entered text, then it will show suggestions", async () => { await browser.openQuestionnaire("test_textfield_suggestions.json"); @@ -30,6 +30,6 @@ describe("Suggestions", () => { await expect(await $$(".ons-js-autosuggest-listbox li").length).not.toBe(0); await suggestionsOption.click(); await click(MultipleSuggestionsPage.submit()); - await expect(browser).toHaveUrlContaining(SubmitPage.url()); + await verifyUrlContains(SubmitPage.url()); }); }); diff --git a/tests/functional/spec/thank_you.spec.js b/tests/functional/spec/thank_you.spec.js index e825db64c8..1747cd5a87 100644 --- a/tests/functional/spec/thank_you.spec.js +++ b/tests/functional/spec/thank_you.spec.js @@ -4,7 +4,7 @@ import CheckboxPage from "../generated_pages/title/single-title-block.page"; import ThankYouPage from "../base_pages/thank-you.page"; import DidYouKnowPage from "../generated_pages/thank_you/did-you-know.page"; import ThankYouSubmitPage from "../generated_pages/thank_you/submit.page"; -import { click } from "../helpers"; +import { click, verifyUrlContains } from "../helpers"; describe("Thank You Social", () => { describe("Given I launch a social themed questionnaire", () => { beforeEach(async () => { @@ -14,7 +14,7 @@ describe("Thank You Social", () => { it("When I navigate to the thank you page, Then I should see social theme content", async () => { await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Test Social Survey"); await expect(await $(ThankYouPage.guidance()).getHTML()).toContain("Your answers have been submitted"); await expect(await $(ThankYouPage.metadata()).getHTML()).toContain("Submitted on:"); @@ -33,7 +33,7 @@ describe("Thank You Default", () => { await $(CheckboxPage.good()).click(); await click(SubmitPage.submit()); await click(HubPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); await expect(await $(ThankYouPage.title()).getHTML()).toContain("Thank you for completing the Question Title Test"); await expect(await $(ThankYouPage.guidance()).getHTML()).toContain("Your answers have been submitted for"); await expect(await $(ThankYouPage.metadata()).getHTML()).toContain("Submitted on:"); @@ -49,7 +49,7 @@ describe("Thank You Default View Response Enabled", () => { await $(DidYouKnowPage.yes()).click(); await click(DidYouKnowPage.submit()); await click(ThankYouSubmitPage.submit()); - await expect(browser).toHaveUrlContaining(ThankYouPage.pageName); + await verifyUrlContains(ThankYouPage.pageName); }); it("When I navigate to the thank you page, and I have submitted less than 40 seconds ago, Then I should see the countdown timer and option to view my answers", async () => { diff --git a/tests/functional/spec/theme_dbt.spec.js b/tests/functional/spec/theme_dbt.spec.js index 5ad48e9826..3dea14156a 100644 --- a/tests/functional/spec/theme_dbt.spec.js +++ b/tests/functional/spec/theme_dbt.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_dbt/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DBT", () => { describe("Given I launch a DBT themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DBT", () => { }); it("When I navigate to the radio page, Then I should see DBT theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade"); }); }); diff --git a/tests/functional/spec/theme_dbt_dsit.spec.js b/tests/functional/spec/theme_dbt_dsit.spec.js index 1b3740ec80..b52afe2d35 100644 --- a/tests/functional/spec/theme_dbt_dsit.spec.js +++ b/tests/functional/spec/theme_dbt_dsit.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_dbt_dsit/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DBT-DSIT", () => { describe("Given I launch a DBT-DSIT themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DBT-DSIT", () => { }); it("When I navigate to the radio page, Then I should see DBT-DSIT theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade logo"); await expect(await $("#dsit-logo-alt").getHTML()).toContain("Department for Science, Innovation and Technology logo"); }); diff --git a/tests/functional/spec/theme_dbt_dsit_ni.spec.js b/tests/functional/spec/theme_dbt_dsit_ni.spec.js index 3f47a86ebe..23e3e801f1 100644 --- a/tests/functional/spec/theme_dbt_dsit_ni.spec.js +++ b/tests/functional/spec/theme_dbt_dsit_ni.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_dbt_dsit_ni/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DBT-DSIT-NI", () => { describe("Given I launch a DBT-DSIT-NI themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DBT-DSIT-NI", () => { }); it("When I navigate to the radio page, Then I should see DBT-DSIT-NI theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade logo"); await expect(await $("#dsit-logo-alt").getHTML()).toContain("Department for Science, Innovation and Technology logo"); await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); diff --git a/tests/functional/spec/theme_dbt_ni.spec.js b/tests/functional/spec/theme_dbt_ni.spec.js index 1dbe188732..83915b7cd8 100644 --- a/tests/functional/spec/theme_dbt_ni.spec.js +++ b/tests/functional/spec/theme_dbt_ni.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_dbt_ni/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DBT-NI", () => { describe("Given I launch a DBT-NI themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DBT-NI", () => { }); it("When I navigate to the radio page, Then I should see DBT-NI theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#dbt-logo-alt").getHTML()).toContain("Department for Business and Trade"); await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); diff --git a/tests/functional/spec/theme_desnz.spec.js b/tests/functional/spec/theme_desnz.spec.js index 953d7a9c68..744283562f 100644 --- a/tests/functional/spec/theme_desnz.spec.js +++ b/tests/functional/spec/theme_desnz.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_desnz/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DESNZ", () => { describe("Given I launch a DESNZ themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DESNZ", () => { }); it("When I navigate to the radio page, Then I should see DESNZ theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#desnz-logo-alt").getHTML()).toContain("Department for Energy Security and Net Zero"); }); }); diff --git a/tests/functional/spec/theme_desnz_ni.spec.js b/tests/functional/spec/theme_desnz_ni.spec.js index 4e92750325..0f9bf748a8 100644 --- a/tests/functional/spec/theme_desnz_ni.spec.js +++ b/tests/functional/spec/theme_desnz_ni.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_desnz_ni/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme DESNZ-NI", () => { describe("Given I launch a DESNZ-NI themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme DESNZ-NI", () => { }); it("When I navigate to the radio page, Then I should see DESNZ-NI theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#desnz-logo-alt").getHTML()).toContain("Department for Energy Security and Net Zero"); await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); diff --git a/tests/functional/spec/theme_nhse.spec.js b/tests/functional/spec/theme_nhse.spec.js index 660613c73f..760b0940c5 100644 --- a/tests/functional/spec/theme_nhse.spec.js +++ b/tests/functional/spec/theme_nhse.spec.js @@ -1,5 +1,6 @@ import RadioPage from "../generated_pages/theme_ons_nhs/radio.page"; import { expect } from "@wdio/globals"; +import { verifyUrlContains } from "../helpers"; describe("Theme NHSE", () => { describe("Given I launch a NHSE themed questionnaire", () => { @@ -8,7 +9,7 @@ describe("Theme NHSE", () => { }); it("When I navigate to the radio page, Then I should see NHSE theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics"); await expect(await $("#nhs-logo-alt").getHTML()).toContain("National Heath Service"); }); diff --git a/tests/functional/spec/theme_northernireland.spec.js b/tests/functional/spec/theme_northernireland.spec.js index e6d7b0e9cc..f3eef0e3f4 100644 --- a/tests/functional/spec/theme_northernireland.spec.js +++ b/tests/functional/spec/theme_northernireland.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_northernireland/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme Northern Ireland", () => { describe("Given I launch a Northern Ireland themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme Northern Ireland", () => { }); it("When I navigate to the radio page, Then I should see Northern Ireland theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#finance-ni-logo-alt").getHTML()).toContain("Northern Ireland Department of Finance logo"); }); }); diff --git a/tests/functional/spec/theme_orr.spec.js b/tests/functional/spec/theme_orr.spec.js index e4a035246a..ae42be571a 100644 --- a/tests/functional/spec/theme_orr.spec.js +++ b/tests/functional/spec/theme_orr.spec.js @@ -1,4 +1,5 @@ import RadioPage from "../generated_pages/theme_orr/radio.page"; +import { verifyUrlContains } from "../helpers"; describe("Theme Rail and Road", () => { describe("Given I launch a Rail and Road themed questionnaire", () => { @@ -7,7 +8,7 @@ describe("Theme Rail and Road", () => { }); it("When I navigate to the radio page, Then I should see Rail and Road theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#orr-logo-mobile-alt").getHTML()).toContain("Office of Rail and Road logo"); }); }); diff --git a/tests/functional/spec/theme_ukhsa_ons.spec.js b/tests/functional/spec/theme_ukhsa_ons.spec.js index e329df91dc..116b7ea828 100644 --- a/tests/functional/spec/theme_ukhsa_ons.spec.js +++ b/tests/functional/spec/theme_ukhsa_ons.spec.js @@ -1,5 +1,6 @@ import RadioPage from "../generated_pages/theme_dbt_ni/radio.page"; import { expect } from "@wdio/globals"; +import { verifyUrlContains } from "../helpers"; describe("Theme UKHSA-ONS", () => { describe("Given I launch a UKHSA-ONS themed questionnaire", () => { @@ -8,7 +9,7 @@ describe("Theme UKHSA-ONS", () => { }); it("When I navigate to the radio page, Then I should see UKHSA-ONS theme content", async () => { - await expect(browser).toHaveUrlContaining(RadioPage.pageName); + await verifyUrlContains(RadioPage.pageName); await expect(await $("#ons-logo-stacked-en-alt").getHTML()).toContain("Office for National Statistics"); await expect(await $("#ukhsa-logo-alt").getHTML()).toContain("UK Health Security Agency"); }); diff --git a/tests/functional/spec/timeout/timeout_modal.js b/tests/functional/spec/timeout/timeout_modal.js index a6102847ba..56275134b1 100644 --- a/tests/functional/spec/timeout/timeout_modal.js +++ b/tests/functional/spec/timeout/timeout_modal.js @@ -1,12 +1,12 @@ import { TimeoutModalPage } from "../../base_pages/timeout-modal.page.js"; -import { click } from "../../helpers"; +import { click, verifyUrlContains } from "../../helpers"; class TestCase { testCaseExpired(page) { it("When the timeout modal is displayed, and I do not extend my session, Then I will be redirected to the session expired page", async () => { await this.checkTimeoutModal(); await browser.pause(65000); // We are waiting for the session to expire - await expect(browser).toHaveUrlContaining("/session-expired"); + await verifyUrlContains("/session-expired"); await expect(await $("body").getHTML()).toContain( "Sorry, you need to sign in again", "This is because you have either:", @@ -25,7 +25,7 @@ class TestCase { await expect(await $(TimeoutModalPage.timer()).getText()).toBe(""); await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired await browser.refresh(); - await expect(browser).toHaveUrlContaining(await page.pageName); + await verifyUrlContains(await page.pageName); await expect(await $("body").getHTML()).not.toContain("Sorry, you need to sign in again"); }).timeout(140000); } @@ -37,7 +37,7 @@ class TestCase { await browser.switchWindow(await page.pageName); await browser.refresh(); await browser.pause(65000); // Waiting 65 seconds to sanity check that it hasn’t expired - await expect(browser).toHaveUrlContaining(await page.pageName); + await verifyUrlContains(await page.pageName); }).timeout(140000); } diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index be5a2971fb..0544160040 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -1,4 +1,4 @@ -export const config = { +exports.config = { // // ==================== // Runner Configuration From f4194d3db2f36947a7c82535a01adb925819c070 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:28:50 +0000 Subject: [PATCH 483/567] Bump the development-dependencies group across 1 directory with 10 updates (#1549) --- package-lock.json | 1125 ++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 560 insertions(+), 567 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9a8037df2..502cdda4e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "node-jose": "^2.2.0", "prettier": "^3.3.3", "typescript": "^5.6.3", - "uuid": "^10.0.0", + "uuid": "^11.0.2", "webdriverio": "^9.2.1" } }, @@ -51,12 +51,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -64,30 +65,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -103,12 +104,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -118,38 +120,38 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", - "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", - "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dev": true, "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -159,17 +161,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", - "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/traverse": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -180,12 +182,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", - "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, @@ -213,41 +215,40 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", - "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -257,35 +258,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", - "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", - "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-wrap-function": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -295,14 +296,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", - "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.7", - "@babel/helper-optimise-call-expression": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -312,107 +313,92 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", "dev": true, "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", - "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", - "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, "dependencies": { - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -422,13 +408,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", - "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -438,12 +424,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", - "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -453,12 +439,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", - "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -468,14 +454,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", - "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", - "@babel/plugin-transform-optional-chaining": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -485,13 +471,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", - "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -513,12 +499,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", - "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -528,12 +514,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -559,12 +545,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", - "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -574,14 +560,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", - "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -591,14 +577,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", - "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-remap-async-to-generator": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -608,12 +594,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", - "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -623,12 +609,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", - "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -638,13 +624,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", - "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -654,13 +640,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", - "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -670,16 +656,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", - "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7", - "@babel/traverse": "^7.25.7", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -690,13 +676,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", - "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/template": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -706,12 +692,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", - "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -721,13 +707,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", - "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -737,12 +723,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", - "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -752,13 +738,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -768,12 +754,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", - "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -783,13 +769,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", - "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -799,12 +785,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", - "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -814,13 +800,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", - "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -830,14 +816,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", - "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -847,12 +833,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", - "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -862,12 +848,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", - "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -877,12 +863,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", - "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -892,12 +878,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", - "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -907,13 +893,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", - "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -923,14 +909,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", - "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -940,15 +926,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", - "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -958,13 +944,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", - "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -974,13 +960,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", - "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -990,12 +976,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", - "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1005,12 +991,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", - "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1020,12 +1006,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", - "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1035,14 +1021,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", - "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/plugin-transform-parameters": "^7.25.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1052,13 +1038,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", - "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-replace-supers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1068,12 +1054,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", - "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1083,13 +1069,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", - "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1099,12 +1085,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", - "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1114,13 +1100,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", - "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1130,14 +1116,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", - "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.7", - "@babel/helper-create-class-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1147,12 +1133,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", - "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1162,12 +1148,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", - "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1177,13 +1163,29 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", - "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1193,13 +1195,13 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.7.tgz", - "integrity": "sha512-Y9p487tyTzB0yDYQOtWnC+9HGOuogtP3/wNpun1xJXEEvI6vip59BSBTsHnekZLqxmPcgsrAKt46HAAb//xGhg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -1213,12 +1215,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", - "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1228,13 +1230,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", - "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1244,12 +1246,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", - "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1259,12 +1261,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", - "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1274,12 +1276,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", - "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1289,12 +1291,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", - "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1304,13 +1306,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", - "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1320,13 +1322,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", - "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1336,13 +1338,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", - "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1352,73 +1354,74 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", - "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.8", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-plugin-utils": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.25.7", - "@babel/plugin-syntax-import-attributes": "^7.25.7", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.8", - "@babel/plugin-transform-async-to-generator": "^7.25.7", - "@babel/plugin-transform-block-scoped-functions": "^7.25.7", - "@babel/plugin-transform-block-scoping": "^7.25.7", - "@babel/plugin-transform-class-properties": "^7.25.7", - "@babel/plugin-transform-class-static-block": "^7.25.8", - "@babel/plugin-transform-classes": "^7.25.7", - "@babel/plugin-transform-computed-properties": "^7.25.7", - "@babel/plugin-transform-destructuring": "^7.25.7", - "@babel/plugin-transform-dotall-regex": "^7.25.7", - "@babel/plugin-transform-duplicate-keys": "^7.25.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-dynamic-import": "^7.25.8", - "@babel/plugin-transform-exponentiation-operator": "^7.25.7", - "@babel/plugin-transform-export-namespace-from": "^7.25.8", - "@babel/plugin-transform-for-of": "^7.25.7", - "@babel/plugin-transform-function-name": "^7.25.7", - "@babel/plugin-transform-json-strings": "^7.25.8", - "@babel/plugin-transform-literals": "^7.25.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", - "@babel/plugin-transform-member-expression-literals": "^7.25.7", - "@babel/plugin-transform-modules-amd": "^7.25.7", - "@babel/plugin-transform-modules-commonjs": "^7.25.7", - "@babel/plugin-transform-modules-systemjs": "^7.25.7", - "@babel/plugin-transform-modules-umd": "^7.25.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", - "@babel/plugin-transform-new-target": "^7.25.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", - "@babel/plugin-transform-numeric-separator": "^7.25.8", - "@babel/plugin-transform-object-rest-spread": "^7.25.8", - "@babel/plugin-transform-object-super": "^7.25.7", - "@babel/plugin-transform-optional-catch-binding": "^7.25.8", - "@babel/plugin-transform-optional-chaining": "^7.25.8", - "@babel/plugin-transform-parameters": "^7.25.7", - "@babel/plugin-transform-private-methods": "^7.25.7", - "@babel/plugin-transform-private-property-in-object": "^7.25.8", - "@babel/plugin-transform-property-literals": "^7.25.7", - "@babel/plugin-transform-regenerator": "^7.25.7", - "@babel/plugin-transform-reserved-words": "^7.25.7", - "@babel/plugin-transform-shorthand-properties": "^7.25.7", - "@babel/plugin-transform-spread": "^7.25.7", - "@babel/plugin-transform-sticky-regex": "^7.25.7", - "@babel/plugin-transform-template-literals": "^7.25.7", - "@babel/plugin-transform-typeof-symbol": "^7.25.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.7", - "@babel/plugin-transform-unicode-property-regex": "^7.25.7", - "@babel/plugin-transform-unicode-regex": "^7.25.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", @@ -1448,9 +1451,9 @@ } }, "node_modules/@babel/register": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.7.tgz", - "integrity": "sha512-qHTd2Rhn/rKhSUwdY6+n98FmwXN+N+zxSVx3zWqRe9INyvTpv+aQ5gDV2+43ACd3VtMBzPPljbb0gZb8u5ma6Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.9.tgz", + "integrity": "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", @@ -1467,9 +1470,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1479,30 +1482,30 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1511,14 +1514,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2774,19 +2776,19 @@ } }, "node_modules/@wdio/cli": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.2.1.tgz", - "integrity": "sha512-lGz/JBkSQh4qH9sDFbNrPHfQNxrDtbm2tb3NgzMNXk88G2dq9AzoYriRgfXO7rKETc+nFreuEdULV+MMhRHk/A==", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.2.6.tgz", + "integrity": "sha512-+Vn2aLwx4M/LZ521yhkDgEXhTOrf8Gg1byZ+0pckWYaB3OmkZ+BGMFEn3a7n781Zcxhq7P/K2BY1lql50axW5g==", "dev": true, "dependencies": { "@types/node": "^20.1.1", "@vitest/snapshot": "^2.1.1", - "@wdio/config": "9.1.3", - "@wdio/globals": "9.2.1", + "@wdio/config": "9.2.5", + "@wdio/globals": "9.2.6", "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.0", - "@wdio/types": "9.1.3", - "@wdio/utils": "9.1.3", + "@wdio/protocols": "9.2.2", + "@wdio/types": "9.2.2", + "@wdio/utils": "9.2.5", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^4.0.0", @@ -2802,7 +2804,7 @@ "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", "tsx": "^4.7.2", - "webdriverio": "9.2.1", + "webdriverio": "9.2.6", "yargs": "^17.7.2" }, "bin": { @@ -2825,14 +2827,14 @@ } }, "node_modules/@wdio/config": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.1.3.tgz", - "integrity": "sha512-fozjb5Jl26QqQoZ2lJc8uZwzK2iKKmIfNIdNvx5JmQt78ybShiPuWWgu/EcHYDvAiZwH76K59R1Gp4lNmmEDew==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.2.5.tgz", + "integrity": "sha512-gqblHShjriGH3va2nSnQ2wktwargHK2oEDepNPG1LMPB5uWd997f+zyaR4s4vtqqUkdxwciIA1H4rDC3fIlesw==", "dev": true, "dependencies": { "@wdio/logger": "9.1.3", - "@wdio/types": "9.1.3", - "@wdio/utils": "9.1.3", + "@wdio/types": "9.2.2", + "@wdio/utils": "9.2.5", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", "glob": "^10.2.2", @@ -2843,16 +2845,16 @@ } }, "node_modules/@wdio/globals": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.2.1.tgz", - "integrity": "sha512-svPSPbV9ZxunmkJVmcCw5A7vzGBYpO1kPmBK9LLZFfVhXiwps0EOl+j6KtqwbQ0cTvC6PEHzm/bwmX4DEzBAzA==", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.2.6.tgz", + "integrity": "sha512-aQqunfSZYqY2oI2YHf9qy9mab0C5Mk9Y904WIZAcEcMA1oojkutRp39btCEMY+twRJjswswWYFoBncTQY8vFDA==", "dev": true, "engines": { "node": ">=18.20.0" }, "optionalDependencies": { "expect-webdriverio": "^5.0.1", - "webdriverio": "9.2.1" + "webdriverio": "9.2.6" } }, "node_modules/@wdio/local-runner": { @@ -2950,16 +2952,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.1.3.tgz", - "integrity": "sha512-MhYTwqZdpqu28vUFnU0swbv9Y/cKRGFdaJtBImpT0HlnbBHG3NouEcQnInSiGst5JMdDBRrkxHYZyTz6y3Uxpw==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.2.5.tgz", + "integrity": "sha512-KmX9TpTrT/9sq/R1seTdV/rRhjCiql7Eqryga4f+a5Sb9PmtHHr8vR2sfyLRXui1Go821TeTw6bqRld7xhAZlA==", "dev": true, "dependencies": { "@types/mocha": "^10.0.6", "@types/node": "^20.11.28", "@wdio/logger": "9.1.3", - "@wdio/types": "9.1.3", - "@wdio/utils": "9.1.3", + "@wdio/types": "9.2.2", + "@wdio/utils": "9.2.5", "mocha": "^10.3.0" }, "engines": { @@ -2967,9 +2969,9 @@ } }, "node_modules/@wdio/protocols": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.2.0.tgz", - "integrity": "sha512-lSdKCwLtqMxSIW+cl8au21GlNkvmLNGgyuGYdV/lFdWflmMYH1zusruM6Km6Kpv2VUlWySjjGknYhe7XVTOeMw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.2.2.tgz", + "integrity": "sha512-0GMUSHCbYm+J+rnRU6XPtaUgVCRICsiH6W5zCXpePm3wLlbmg/mvZ+4OnNErssbpIOulZuAmC2jNmut2AEfWSw==", "dev": true }, "node_modules/@wdio/repl": { @@ -2994,14 +2996,14 @@ } }, "node_modules/@wdio/reporter": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.1.3.tgz", - "integrity": "sha512-j8i2Rs2JkcLdvdP6eysMNKgUnApi/ESwRYtscQvQIOYvzy2xOEJRe6VOeoUjLgKNN4VGo165H04bbxMR0oacUw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.2.2.tgz", + "integrity": "sha512-3FxIMol0lTQ8S0gSlgvBpjTg9w/GRCFH+juHVIwjmdeqgKwID0IoKGxLo2enLjR7T3wsWUzN6JaqL/fa4nAK4A==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@wdio/logger": "9.1.3", - "@wdio/types": "9.1.3", + "@wdio/types": "9.2.2", "diff": "^7.0.0", "object-inspect": "^1.12.0" }, @@ -3378,13 +3380,13 @@ } }, "node_modules/@wdio/spec-reporter": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.1.3.tgz", - "integrity": "sha512-N5GsZpDcjfJ9otmxD8q1Kc7PK5/P4Y3B+Aj51FyvYseMPbsOzUuwsKUQJSQu/IhgrDU3UjZQydr8UBU/Gg6a9w==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.2.2.tgz", + "integrity": "sha512-J/4tt09SJb1mPsy5CQNi5hZyT2ZJ9qhpPS+yBAR/74P/E1bsfHHBMEb2n0CNPp9IhIdyGZzMxueaJAWEiGGWQQ==", "dev": true, "dependencies": { - "@wdio/reporter": "9.1.3", - "@wdio/types": "9.1.3", + "@wdio/reporter": "9.2.2", + "@wdio/types": "9.2.2", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^9.0.0" @@ -3406,9 +3408,9 @@ } }, "node_modules/@wdio/types": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.1.3.tgz", - "integrity": "sha512-oQrzLQBqn/+HXSJJo01NEfeKhzwuDdic7L8PDNxv5ySKezvmLDYVboQfoSDRtpAdfAZCcxuU9L4Jw7iTf6WV3g==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.2.2.tgz", + "integrity": "sha512-nHZ9Ne9iRQFJ1TOYKUn4Fza69IshTTzk6RYmSZ51ImGs9uMZu0+S0Jm9REdly+VLN3FzxG6g2QSe0/F3uNVPdw==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -3418,14 +3420,14 @@ } }, "node_modules/@wdio/utils": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.1.3.tgz", - "integrity": "sha512-dYeOzq9MTh8jYRZhzo/DYyn+cKrhw7h0/5hgyXkbyk/wHwF/uLjhATPmfaCr9+MARSEdiF7wwU8iRy/V0jfsLg==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.2.5.tgz", + "integrity": "sha512-QgBxscPVuqC/fP62ggssBfWLnonVs2/r1xaj5MgH+tLNez/OVS98pWx0/FYsFHVeTVO5vPnJIhoTo2CXz8tQzA==", "dev": true, "dependencies": { "@puppeteer/browsers": "^2.2.0", "@wdio/logger": "9.1.3", - "@wdio/types": "9.1.3", + "@wdio/types": "9.2.2", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", "edgedriver": "^5.6.1", @@ -11674,15 +11676,6 @@ "node": ">=0.6.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -12046,16 +12039,16 @@ "dev": true }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", + "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/validate-npm-package-license": { @@ -12452,19 +12445,19 @@ } }, "node_modules/webdriverio": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.2.1.tgz", - "integrity": "sha512-AI7xzqTmFiU7oAx4fpEF1U1MA7smhCPVDeM0gxPqG5qWepzib3WDX2SsRtcmhdVW+vLJ3m4bf8rAXxZ2M1msWA==", + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.2.6.tgz", + "integrity": "sha512-GjQykwwYBXwqWAs0CUCU0Hg9nR5DonOwFkWQUcUpCfBVSTLdeH0fBEDikKVf7ohuDOUSOGo0wM9LN8ZTMroMXA==", "dev": true, "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.1.3", + "@wdio/config": "9.2.5", "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.0", + "@wdio/protocols": "9.2.2", "@wdio/repl": "9.0.8", - "@wdio/types": "9.1.3", - "@wdio/utils": "9.1.3", + "@wdio/types": "9.2.2", + "@wdio/utils": "9.2.5", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", @@ -12483,7 +12476,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.2.0" + "webdriver": "9.2.5" }, "engines": { "node": ">=18.20.0" @@ -12534,18 +12527,18 @@ } }, "node_modules/webdriverio/node_modules/webdriver": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.2.0.tgz", - "integrity": "sha512-UrhuHSLq4m3OgncvX75vShfl5w3gmjAy8LvLb6/L6V+a+xcqMRelFx/DQ72Mr84F4m8Li6wjtebrOH1t9V/uOQ==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.2.5.tgz", + "integrity": "sha512-7rmK6oD3oYq+6E0qa9FQ1/67Ajf2APTOghmqlcDnSBTnTGF51UPOWnzgy0x3yGozmCTVRe13O75JXRRWpvxOvA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "9.1.3", + "@wdio/config": "9.2.5", "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.0", - "@wdio/types": "9.1.3", - "@wdio/utils": "9.1.3", + "@wdio/protocols": "9.2.2", + "@wdio/types": "9.2.2", + "@wdio/utils": "9.2.5", "deepmerge-ts": "^7.0.3", "ws": "^8.8.0" }, diff --git a/package.json b/package.json index 5a14f201af..4d96f56862 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "node-jose": "^2.2.0", "prettier": "^3.3.3", "typescript": "^5.6.3", - "uuid": "^10.0.0", + "uuid": "^11.0.2", "webdriverio": "^9.2.1" }, "prettier": {} From ed9626a0adc9169db8b5e1059ec4d86217aa05c2 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:49:23 +0000 Subject: [PATCH 484/567] Fix panel implementation on thank you page (#1550) --- templates/layouts/_submit.html | 2 +- templates/thank-you.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/layouts/_submit.html b/templates/layouts/_submit.html index 19c1eedb67..6737b944b1 100644 --- a/templates/layouts/_submit.html +++ b/templates/layouts/_submit.html @@ -13,7 +13,7 @@

      {{ content.title }}

      {% block post_title_panel %} {% if content.warning %} {# djlint:off #} - {% call onsPanel({ "variant": "warn"}) %} + {% call onsPanel({ "variant": "warn" }) %}

      {{ content.warning }}

      {% endcall %} {# djlint:on #} diff --git a/templates/thank-you.html b/templates/thank-you.html index 6c952d5d50..12328fe945 100644 --- a/templates/thank-you.html +++ b/templates/thank-you.html @@ -32,7 +32,7 @@ {# djlint:off #} {% call onsPanel({ - "type": "success", + "variant": "success", "iconType": "check", "iconSize": "xl", "classes": "ons-u-mb-m" From f37859c50a9dc81064dc2e6ad99dc1fb62795eb0 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:24:54 +0000 Subject: [PATCH 485/567] Schemas v5.18.0 (#1551) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 11640d2daf..408dfb526e 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.17.0 +v5.18.0 From b413fcd188c327928039ec65483946fb01ff891d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 25 Nov 2024 08:22:07 +0000 Subject: [PATCH 486/567] Remove Ruff ignore rule UP034 (pyupgrade) (#1553) --- app/questionnaire/questionnaire_schema.py | 2 +- app/questionnaire/questionnaire_store_updater.py | 8 +++----- app/utilities/make_immutable.py | 2 +- pyproject.toml | 1 - 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 1eccc383e1..281bb3f33d 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -301,7 +301,7 @@ def serialize(cls, data: Any) -> Any: @classmethod def get_mutable_deepcopy(cls, data: Any) -> Any: if isinstance(data, tuple): - return list((cls.get_mutable_deepcopy(item) for item in data)) + return [cls.get_mutable_deepcopy(item) for item in data] if isinstance(data, ImmutableDict): key_value_tuples = {k: cls.get_mutable_deepcopy(v) for k, v in data.items()} return dict(key_value_tuples) diff --git a/app/questionnaire/questionnaire_store_updater.py b/app/questionnaire/questionnaire_store_updater.py index b0c18fe887..8ee6591c31 100644 --- a/app/questionnaire/questionnaire_store_updater.py +++ b/app/questionnaire/questionnaire_store_updater.py @@ -62,11 +62,9 @@ def save(self) -> None: def is_dirty(self) -> bool: return bool( - ( - self._answer_store.is_dirty - or self._list_store.is_dirty - or self._progress_store.is_dirty - ) + self._answer_store.is_dirty + or self._list_store.is_dirty + or self._progress_store.is_dirty ) def update_relationships_answer( diff --git a/app/utilities/make_immutable.py b/app/utilities/make_immutable.py index b48cb20b83..522f6ec324 100644 --- a/app/utilities/make_immutable.py +++ b/app/utilities/make_immutable.py @@ -8,7 +8,7 @@ def make_immutable(data: Any) -> Any: if isinstance(data, abc.Hashable): return data if isinstance(data, list): - return tuple((make_immutable(item) for item in data)) + return tuple(make_immutable(item) for item in data) if isinstance(data, dict): key_value_tuples = {k: make_immutable(v) for k, v in data.items()} return ImmutableDict(key_value_tuples) diff --git a/pyproject.toml b/pyproject.toml index 986a5f54e8..be9a61f862 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,7 +124,6 @@ extend-ignore = [ "EM102", # EM102 Exception must not use an f-string literal, assign to variable first "UP032", # Use f-string instead of `format` call "UP018", # Unnecessary {literal_type} call (rewrite as a literal) - "UP034", # Avoid extraneous parentheses "UP015", # Unnecessary open mode parameters "UP007", # Use `X | Y` for type annotations "UP006", # Use `type` instead of `Type` for type annotation From f13cee78412e33e1c94021787cd4280dd22b6ec9 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:44:08 +0000 Subject: [PATCH 487/567] Fix poetry-dotenv issue & update poetry libraries (#1556) --- .github/workflows/pull_request.yml | 8 +- Dockerfile | 2 +- README.md | 2 +- poetry.lock | 1173 ++++++++++++++-------------- 4 files changed, 587 insertions(+), 598 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 86ca206116..20f0254cc0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -22,7 +22,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.3 + version: 1.8.4 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -56,7 +56,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.3 + version: 1.8.4 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -95,7 +95,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.3 + version: 1.8.4 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -140,7 +140,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.3 + version: 1.8.4 virtualenvs-create: true - uses: actions/setup-python@v5 with: diff --git a/Dockerfile b/Dockerfile index 5cf036ef81..bc9d6bfaf7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ COPY pyproject.toml pyproject.toml COPY poetry.lock poetry.lock RUN groupadd -r appuser && useradd -r -g appuser -u 9000 appuser && chown -R appuser:appuser . -RUN pip install "poetry==1.8.3" && \ +RUN pip install "poetry==1.8.4" && \ poetry config virtualenvs.create false && \ poetry install --only main && \ make build diff --git a/README.md b/README.md index d848352cd5..73dd4050a2 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ pip install --upgrade pip setuptools Install poetry, poetry dotenv plugin and install dependencies: ``` shell -curl -sSL https://install.python-poetry.org | python3 - --version 1.8.3 +curl -sSL https://install.python-poetry.org | python3 - --version 1.8.4 poetry self add poetry-plugin-dotenv poetry install ``` diff --git a/poetry.lock b/poetry.lock index 173dd1c068..7c10b43a11 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astroid" @@ -111,28 +111,28 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "blinker" -version = "1.8.2" +version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, - {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, ] [[package]] name = "boto3" -version = "1.35.51" +version = "1.35.68" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.51-py3-none-any.whl", hash = "sha256:c922f6a18958af9d8af0489d6d8503b517029d8159b26aa4859a8294561c72e9"}, - {file = "boto3-1.35.51.tar.gz", hash = "sha256:a57c6c7012ecb40c43e565a6f7a891f39efa990ff933eab63cd456f7501c2731"}, + {file = "boto3-1.35.68-py3-none-any.whl", hash = "sha256:9b26fa31901da7793c1dcd65eee9bab7e897d8aa1ffed0b5e1c3bce93d2aefe4"}, + {file = "boto3-1.35.68.tar.gz", hash = "sha256:091d6bed1422370987a839bff3f8755df7404fc15e9fac2a48e8505356f07433"}, ] [package.dependencies] -botocore = ">=1.35.51,<1.36.0" +botocore = ">=1.35.68,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.51" +version = "1.35.68" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.51-py3-none-any.whl", hash = "sha256:4d65b00111bd12b98e9f920ecab602cf619cc6a6d0be6e5dd53f517e4b92901c"}, - {file = "botocore-1.35.51.tar.gz", hash = "sha256:a9b3d1da76b3e896ad74605c01d88f596324a3337393d4bfbfa0d6c35822ca9c"}, + {file = "botocore-1.35.68-py3-none-any.whl", hash = "sha256:599139d5564291f5be873800711f9e4e14a823395ae9ce7b142be775e9849b94"}, + {file = "botocore-1.35.68.tar.gz", hash = "sha256:42c3700583a82f2b5316281a073d644a521d6358837e2b446dc458ba5d990fb4"}, ] [package.dependencies] @@ -548,73 +548,73 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.8" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, + {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, + {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, + {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, + {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, + {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, + {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, + {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, + {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, + {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, + {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, + {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, + {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, + {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, + {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, + {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, + {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, + {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, + {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, + {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, + {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, + {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, + {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, + {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, + {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, + {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, + {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, + {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, + {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, + {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, + {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, + {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, + {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, + {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, + {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, ] [package.extras] @@ -788,20 +788,20 @@ six = ">=1.13.0" [[package]] name = "deprecated" -version = "1.2.14" +version = "1.2.15" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, + {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, + {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] [[package]] name = "dill" @@ -820,21 +820,39 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "djlint" -version = "1.35.3" +version = "1.36.1" description = "HTML Template Linter and Formatter" optional = false python-versions = ">=3.9" files = [ - {file = "djlint-1.35.3-py3-none-any.whl", hash = "sha256:bf2f23798909f9c5a110925c369538383de0141f9a2be37ee0d26422d41b7543"}, - {file = "djlint-1.35.3.tar.gz", hash = "sha256:780ea3e25662fca89033fa96ecf656099954d6f81dce039eac90f4bba3cbe850"}, + {file = "djlint-1.36.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef40527fd6cd82cdd18f65a6bf5b486b767d2386f6c21f2ebd60e5d88f487fe8"}, + {file = "djlint-1.36.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4712de3dea172000a098da6a0cd709d158909b4964ba0f68bee584cef18b4878"}, + {file = "djlint-1.36.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d01c1425170b7059d68a3b01709e1c31d2cd6520a1eb0166e6670fd250518a"}, + {file = "djlint-1.36.1-cp310-cp310-win_amd64.whl", hash = "sha256:65585a97d3a37760b4c1fbd089a3573506ad0ab2885119322a66231f911d113f"}, + {file = "djlint-1.36.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:607437a0a230462916858c269bc5dfd15ff71b27d15dfd1ad6e96b3da9cbd8f6"}, + {file = "djlint-1.36.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ddc9ae6b83b288465f6685b24797adbde79952d6e1a5276026e5ef479bac76f"}, + {file = "djlint-1.36.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:001e5124b0ebab60a2044134abd11ff11dee772e7c3caaa2c8d12eb5d3b1f1dc"}, + {file = "djlint-1.36.1-cp311-cp311-win_amd64.whl", hash = "sha256:095d62f3cabbac08683c51c1d9dacab522b54437a2a317df9e134599360f7b89"}, + {file = "djlint-1.36.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:210f319c2d22489aebc0e9c1acd5015ca3892b66fa35647344511b3c03fcbe82"}, + {file = "djlint-1.36.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7aa3db13d7702c35f4e408325061d9d4e84d006c99bb3e55fddf2b2543736923"}, + {file = "djlint-1.36.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f907e97f4d67f4423dc71671592891cfd9cd311aeef14db25633f292dbf7048"}, + {file = "djlint-1.36.1-cp312-cp312-win_amd64.whl", hash = "sha256:abadf6b61dc53d81710f230542f57f2d470b7503cd3108ad8a0113271c0514dd"}, + {file = "djlint-1.36.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7f31646435385eec1d4b03dad7bebb5e4078d9893c60d490a685535bd6303c83"}, + {file = "djlint-1.36.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4399477ac51f9c8147eedbef70aa8465eccba6759d875d1feec6782744aa168a"}, + {file = "djlint-1.36.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c217b17d3ae3c0e3b5fff57fb708029cceda6e232f5a54ff1b3aeb43a7540"}, + {file = "djlint-1.36.1-cp313-cp313-win_amd64.whl", hash = "sha256:1577490802ca4697af3488ed13066c9214ef0f625a96aa20d4f297e37aa19303"}, + {file = "djlint-1.36.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ae356faf8180c7629ca705b7b9d8c9269b2c53273a1887a438a21b8fa263588"}, + {file = "djlint-1.36.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2237ac5cecd2524960e1684f64ce358624b0d769b7404e5aad415750ad00edc9"}, + {file = "djlint-1.36.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02c22352a49c053ff6260428ed571afb783011d20afc98b44bbe1dd2fa2d5418"}, + {file = "djlint-1.36.1-cp39-cp39-win_amd64.whl", hash = "sha256:99a2debeea2e931b68360306fdbf13861e3d6f96037a9d882f3d4d5e44fdc319"}, + {file = "djlint-1.36.1-py3-none-any.whl", hash = "sha256:950782b396dd82b74622c09d7e4c52328e56a3b03c8ac790c319708e5caa0686"}, + {file = "djlint-1.36.1.tar.gz", hash = "sha256:f7260637ed72c270fa6dd4a87628e1a21c49b24a46df52e4e26f44d4934fb97c"}, ] [package.dependencies] click = ">=8.0.1" colorama = ">=0.4.4" cssbeautifier = ">=1.14.4" -html-tag-names = ">=0.1.2" -html-void-elements = ">=0.1" jsbeautifier = ">=1.14.4" json5 = ">=0.9.11" pathspec = ">=0.12" @@ -925,21 +943,21 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"] [[package]] name = "flask" -version = "3.0.3" +version = "3.1.0" description = "A simple framework for building complex web applications." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, - {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, + {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, + {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, ] [package.dependencies] -blinker = ">=1.6.2" +blinker = ">=1.9" click = ">=8.1.3" -itsdangerous = ">=2.1.2" +itsdangerous = ">=2.2" Jinja2 = ">=3.1.2" -Werkzeug = ">=3.0.0" +Werkzeug = ">=3.1" [package.extras] async = ["asgiref (>=3.2)"] @@ -1043,49 +1061,49 @@ python-dateutil = ">=2.7" [[package]] name = "gevent" -version = "24.10.3" +version = "24.11.1" description = "Coroutine-based network library" optional = false python-versions = ">=3.9" files = [ - {file = "gevent-24.10.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d7a1ad0f2da582f5bd238bca067e1c6c482c30c15a6e4d14aaa3215cbb2232f3"}, - {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4e526fdc279c655c1e809b0c34b45844182c2a6b219802da5e411bd2cf5a8ad"}, - {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57a5c4e0bdac482c5f02f240d0354e61362df73501ef6ebafce8ef635cad7527"}, - {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d67daed8383326dc8b5e58d88e148d29b6b52274a489e383530b0969ae7b9cb9"}, - {file = "gevent-24.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e24ffea72e27987979c009536fd0868e52239b44afe6cf7135ce8aafd0f108e"}, - {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c1d80090485da1ea3d99205fe97908b31188c1f4857f08b333ffaf2de2e89d18"}, - {file = "gevent-24.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0c129f81d60cda614acb4b0c5731997ca05b031fb406fcb58ad53a7ade53b13"}, - {file = "gevent-24.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:26ca7a6b42d35129617025ac801135118333cad75856ffc3217b38e707383eba"}, - {file = "gevent-24.10.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68c3a0d8402755eba7f69022e42e8021192a721ca8341908acc222ea597029b6"}, - {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d850a453d66336272be4f1d3a8126777f3efdaea62d053b4829857f91e09755"}, - {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e58ee3723f1fbe07d66892f1caa7481c306f653a6829b6fd16cb23d618a5915"}, - {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b52382124eca13135a3abe4f65c6bd428656975980a48e51b17aeab68bdb14db"}, - {file = "gevent-24.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ca2266e08f43c0e22c028801dff7d92a0b102ef20e4caeb6a46abfb95f6a328"}, - {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d758f0d4dbf32502ec87bb9b536ca8055090a16f8305f0ada3ce6f34e70f2fd7"}, - {file = "gevent-24.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0de6eb3d55c03138fda567d9bfed28487ce5d0928c5107549767a93efdf2be26"}, - {file = "gevent-24.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:385710355eadecdb70428a5ae3e7e5a45dcf888baa1426884588be9d25ac4290"}, - {file = "gevent-24.10.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ad8fb70aa0ebc935729c9699ac31b210a49b689a7b27b7ac9f91676475f3f53"}, - {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f18689f7a70d2ed0e75bad5036ec3c89690a493d4cfac8d7cdb258ac04b132bd"}, - {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f4f171d4d2018170454d84c934842e1b5f6ce7468ba298f6e7f7cff15000a3"}, - {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7021e26d70189b33c27173d4173f27bf4685d6b6f1c0ea50e5335f8491cb110c"}, - {file = "gevent-24.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34aea15f9c79f27a8faeaa361bc1e72c773a9b54a1996a2ec4eefc8bcd59a824"}, - {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8af65a4d4feaec6042c666d22c322a310fba3b47e841ad52f724b9c3ce5da48e"}, - {file = "gevent-24.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:89c4115e3f5ada55f92b61701a46043fe42f702b5af863b029e4c1a76f6cc2d4"}, - {file = "gevent-24.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:1ce6dab94c0b0d24425ba55712de2f8c9cb21267150ca63f5bb3a0e1f165da99"}, - {file = "gevent-24.10.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:f147e38423fbe96e8731f60a63475b3d2cab2f3d10578d8ee9d10c507c58a2ff"}, - {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18e6984ec96fc95fd67488555c38ece3015be1f38b1bcceb27b7d6c36b343008"}, - {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:051b22e2758accfddb0457728bfc9abf8c3f2ce6bca43f1ff6e07b5ed9e49bf4"}, - {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb5edb6433764119a664bbb148d2aea9990950aa89cc3498f475c2408d523ea3"}, - {file = "gevent-24.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce417bcaaab496bc9c77f75566531e9d93816262037b8b2dbb88b0fdcd66587c"}, - {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:1c3a828b033fb02b7c31da4d75014a1f82e6c072fc0523456569a57f8b025861"}, - {file = "gevent-24.10.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f2ae3efbbd120cdf4a68b7abc27a37e61e6f443c5a06ec2c6ad94c37cd8471ec"}, - {file = "gevent-24.10.3-cp313-cp313-win_amd64.whl", hash = "sha256:9e1210334a9bc9f76c3d008e0785ca62214f8a54e1325f6c2ecab3b6a572a015"}, - {file = "gevent-24.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70e9ed7ecb70e0df7dc97c3bc420de9a45a7c76bd5861c6cfec8c549700e681e"}, - {file = "gevent-24.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ac83b74304487afa211a01909c7dd257e574db0cd429d866c298e21df7aeedf"}, - {file = "gevent-24.10.3-cp39-cp39-win32.whl", hash = "sha256:a9a89d6e396ef6f1e3968521bf56e8c4bee25b193bbf5d428b7782d582410822"}, - {file = "gevent-24.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:40ea3e40e8bb4fdb143c2a8edf2ccfdebd56016c7317c341ce8094c7bee08818"}, - {file = "gevent-24.10.3-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:e534e6a968d74463b11de6c9c67f4b4bf61775fb00f2e6e0f7fcdd412ceade18"}, - {file = "gevent-24.10.3.tar.gz", hash = "sha256:aa7ee1bd5cabb2b7ef35105f863b386c8d5e332f754b60cfc354148bd70d35d1"}, + {file = "gevent-24.11.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:92fe5dfee4e671c74ffaa431fd7ffd0ebb4b339363d24d0d944de532409b935e"}, + {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7bfcfe08d038e1fa6de458891bca65c1ada6d145474274285822896a858c870"}, + {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7398c629d43b1b6fd785db8ebd46c0a353880a6fab03d1cf9b6788e7240ee32e"}, + {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7886b63ebfb865178ab28784accd32f287d5349b3ed71094c86e4d3ca738af5"}, + {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9ca80711e6553880974898d99357fb649e062f9058418a92120ca06c18c3c59"}, + {file = "gevent-24.11.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e24181d172f50097ac8fc272c8c5b030149b630df02d1c639ee9f878a470ba2b"}, + {file = "gevent-24.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1d4fadc319b13ef0a3c44d2792f7918cf1bca27cacd4d41431c22e6b46668026"}, + {file = "gevent-24.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d882faa24f347f761f934786dde6c73aa6c9187ee710189f12dcc3a63ed4a50"}, + {file = "gevent-24.11.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:351d1c0e4ef2b618ace74c91b9b28b3eaa0dd45141878a964e03c7873af09f62"}, + {file = "gevent-24.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5efe72e99b7243e222ba0c2c2ce9618d7d36644c166d63373af239da1036bab"}, + {file = "gevent-24.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d3b249e4e1f40c598ab8393fc01ae6a3b4d51fc1adae56d9ba5b315f6b2d758"}, + {file = "gevent-24.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81d918e952954675f93fb39001da02113ec4d5f4921bf5a0cc29719af6824e5d"}, + {file = "gevent-24.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9c935b83d40c748b6421625465b7308d87c7b3717275acd587eef2bd1c39546"}, + {file = "gevent-24.11.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff96c5739834c9a594db0e12bf59cb3fa0e5102fc7b893972118a3166733d61c"}, + {file = "gevent-24.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d6c0a065e31ef04658f799215dddae8752d636de2bed61365c358f9c91e7af61"}, + {file = "gevent-24.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:97e2f3999a5c0656f42065d02939d64fffaf55861f7d62b0107a08f52c984897"}, + {file = "gevent-24.11.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:a3d75fa387b69c751a3d7c5c3ce7092a171555126e136c1d21ecd8b50c7a6e46"}, + {file = "gevent-24.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:beede1d1cff0c6fafae3ab58a0c470d7526196ef4cd6cc18e7769f207f2ea4eb"}, + {file = "gevent-24.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85329d556aaedced90a993226d7d1186a539c843100d393f2349b28c55131c85"}, + {file = "gevent-24.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:816b3883fa6842c1cf9d2786722014a0fd31b6312cca1f749890b9803000bad6"}, + {file = "gevent-24.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b24d800328c39456534e3bc3e1684a28747729082684634789c2f5a8febe7671"}, + {file = "gevent-24.11.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5f1701ce0f7832f333dd2faf624484cbac99e60656bfbb72504decd42970f0f"}, + {file = "gevent-24.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d740206e69dfdfdcd34510c20adcb9777ce2cc18973b3441ab9767cd8948ca8a"}, + {file = "gevent-24.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:68bee86b6e1c041a187347ef84cf03a792f0b6c7238378bf6ba4118af11feaae"}, + {file = "gevent-24.11.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d618e118fdb7af1d6c1a96597a5cd6ac84a9f3732b5be8515c6a66e098d498b6"}, + {file = "gevent-24.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2142704c2adce9cd92f6600f371afb2860a446bfd0be5bd86cca5b3e12130766"}, + {file = "gevent-24.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92e0d7759de2450a501effd99374256b26359e801b2d8bf3eedd3751973e87f5"}, + {file = "gevent-24.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca845138965c8c56d1550499d6b923eb1a2331acfa9e13b817ad8305dde83d11"}, + {file = "gevent-24.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:356b73d52a227d3313f8f828025b665deada57a43d02b1cf54e5d39028dbcf8d"}, + {file = "gevent-24.11.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:58851f23c4bdb70390f10fc020c973ffcf409eb1664086792c8b1e20f25eef43"}, + {file = "gevent-24.11.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1ea50009ecb7f1327347c37e9eb6561bdbc7de290769ee1404107b9a9cba7cf1"}, + {file = "gevent-24.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:ec68e270543ecd532c4c1d70fca020f90aa5486ad49c4f3b8b2e64a66f5c9274"}, + {file = "gevent-24.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9347690f4e53de2c4af74e62d6fabc940b6d4a6cad555b5a379f61e7d3f2a8e"}, + {file = "gevent-24.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8619d5c888cb7aebf9aec6703e410620ef5ad48cdc2d813dd606f8aa7ace675f"}, + {file = "gevent-24.11.1-cp39-cp39-win32.whl", hash = "sha256:c6b775381f805ff5faf250e3a07c0819529571d19bb2a9d474bee8c3f90d66af"}, + {file = "gevent-24.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c3443b0ed23dcb7c36a748d42587168672953d368f2956b17fad36d43b58836"}, + {file = "gevent-24.11.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:f43f47e702d0c8e1b8b997c00f1601486f9f976f84ab704f8f11536e3fa144c9"}, + {file = "gevent-24.11.1.tar.gz", hash = "sha256:8bd1419114e9e4a3ed33a5bad766afff9a3cf765cb440a582a1b3a9bc80c1aca"}, ] [package.dependencies] @@ -1103,13 +1121,13 @@ test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.22.0" +version = "2.23.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_core-2.22.0-py3-none-any.whl", hash = "sha256:a6652b6bd51303902494998626653671703c420f6f4c88cfd3f50ed723e9d021"}, - {file = "google_api_core-2.22.0.tar.gz", hash = "sha256:26f8d76b96477db42b55fd02a33aae4a42ec8b86b98b94969b7333a2c828bf35"}, + {file = "google_api_core-2.23.0-py3-none-any.whl", hash = "sha256:c20100d4c4c41070cf365f1d8ddf5365915291b5eb11b83829fbd1c999b5122f"}, + {file = "google_api_core-2.23.0.tar.gz", hash = "sha256:2ceb087315e6af43f256704b871d99326b1f12a9d6ce99beaedec99ba26a0ace"}, ] [package.dependencies] @@ -1132,13 +1150,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.35.0" +version = "2.36.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, - {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, ] [package.dependencies] @@ -1194,13 +1212,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.26.1" +version = "2.27.1" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.26.1-py2.py3-none-any.whl", hash = "sha256:932d4434d86af25673082b48d54b318a448d1a7cd718404c33bf008ae9a8bb22"}, - {file = "google_cloud_pubsub-2.26.1.tar.gz", hash = "sha256:d46a302c2c7a008e399f4c04b4be6341d8aa7a537a25810ec8d38a5c125f816d"}, + {file = "google_cloud_pubsub-2.27.1-py2.py3-none-any.whl", hash = "sha256:3ca8980c198a847ee464845ab60f05478d4819cf693c9950ee89da96f0b80a41"}, + {file = "google_cloud_pubsub-2.27.1.tar.gz", hash = "sha256:7119dbc5af4b915ecdfa1289919f791a432927eaaa7bbfbeb740e6d7020c181e"}, ] [package.dependencies] @@ -1211,7 +1229,10 @@ grpcio = ">=1.51.3,<2.0dev" grpcio-status = ">=1.33.2" opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} -proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} +proto-plus = [ + {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\" and python_version < \"3.13\""}, +] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" [package.extras] @@ -1242,13 +1263,13 @@ tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.17.0" +version = "2.17.1" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_tasks-2.17.0-py2.py3-none-any.whl", hash = "sha256:a49dcad5360237aa7203f10d2814a8d0de26a8d555aa3624b5d35a1c575cce1d"}, - {file = "google_cloud_tasks-2.17.0.tar.gz", hash = "sha256:e6d709b08af3b173b6c3fb80ff02b66e2d5954f92e94d5474d80b303847a38c1"}, + {file = "google_cloud_tasks-2.17.1-py2.py3-none-any.whl", hash = "sha256:cee64243623f5b3bae0f57e94eecaa93b61cbe71cf0f1c135554dc51998ab3b8"}, + {file = "google_cloud_tasks-2.17.1.tar.gz", hash = "sha256:164415b256f2db6a123ab42227b164ed8e25917859824a088f6f2560f47aabe1"}, ] [package.dependencies] @@ -1320,13 +1341,13 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.65.0" +version = "1.66.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, - {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, + {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, + {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, ] [package.dependencies] @@ -1440,85 +1461,85 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.67.1" +version = "1.68.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.67.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:8b0341d66a57f8a3119b77ab32207072be60c9bf79760fa609c5609f2deb1f3f"}, - {file = "grpcio-1.67.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:f5a27dddefe0e2357d3e617b9079b4bfdc91341a91565111a21ed6ebbc51b22d"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:43112046864317498a33bdc4797ae6a268c36345a910de9b9c17159d8346602f"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9b929f13677b10f63124c1a410994a401cdd85214ad83ab67cc077fc7e480f0"}, - {file = "grpcio-1.67.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7d1797a8a3845437d327145959a2c0c47c05947c9eef5ff1a4c80e499dcc6fa"}, - {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0489063974d1452436139501bf6b180f63d4977223ee87488fe36858c5725292"}, - {file = "grpcio-1.67.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9fd042de4a82e3e7aca44008ee2fb5da01b3e5adb316348c21980f7f58adc311"}, - {file = "grpcio-1.67.1-cp310-cp310-win32.whl", hash = "sha256:638354e698fd0c6c76b04540a850bf1db27b4d2515a19fcd5cf645c48d3eb1ed"}, - {file = "grpcio-1.67.1-cp310-cp310-win_amd64.whl", hash = "sha256:608d87d1bdabf9e2868b12338cd38a79969eaf920c89d698ead08f48de9c0f9e"}, - {file = "grpcio-1.67.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:7818c0454027ae3384235a65210bbf5464bd715450e30a3d40385453a85a70cb"}, - {file = "grpcio-1.67.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ea33986b70f83844cd00814cee4451055cd8cab36f00ac64a31f5bb09b31919e"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7a01337407dd89005527623a4a72c5c8e2894d22bead0895306b23c6695698f"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b866f73224b0634f4312a4674c1be21b2b4afa73cb20953cbbb73a6b36c3cc"}, - {file = "grpcio-1.67.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fff78ba10d4250bfc07a01bd6254a6d87dc67f9627adece85c0b2ed754fa96"}, - {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8a23cbcc5bb11ea7dc6163078be36c065db68d915c24f5faa4f872c573bb400f"}, - {file = "grpcio-1.67.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a65b503d008f066e994f34f456e0647e5ceb34cfcec5ad180b1b44020ad4970"}, - {file = "grpcio-1.67.1-cp311-cp311-win32.whl", hash = "sha256:e29ca27bec8e163dca0c98084040edec3bc49afd10f18b412f483cc68c712744"}, - {file = "grpcio-1.67.1-cp311-cp311-win_amd64.whl", hash = "sha256:786a5b18544622bfb1e25cc08402bd44ea83edfb04b93798d85dca4d1a0b5be5"}, - {file = "grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953"}, - {file = "grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af"}, - {file = "grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e"}, - {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75"}, - {file = "grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38"}, - {file = "grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78"}, - {file = "grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc"}, - {file = "grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b"}, - {file = "grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955"}, - {file = "grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8"}, - {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62"}, - {file = "grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb"}, - {file = "grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121"}, - {file = "grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba"}, - {file = "grpcio-1.67.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:178f5db771c4f9a9facb2ab37a434c46cb9be1a75e820f187ee3d1e7805c4f65"}, - {file = "grpcio-1.67.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f3e49c738396e93b7ba9016e153eb09e0778e776df6090c1b8c91877cc1c426"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:24e8a26dbfc5274d7474c27759b54486b8de23c709d76695237515bc8b5baeab"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b6c16489326d79ead41689c4b84bc40d522c9a7617219f4ad94bc7f448c5085"}, - {file = "grpcio-1.67.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e6a4dcf5af7bbc36fd9f81c9f372e8ae580870a9e4b6eafe948cd334b81cf3"}, - {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:95b5f2b857856ed78d72da93cd7d09b6db8ef30102e5e7fe0961fe4d9f7d48e8"}, - {file = "grpcio-1.67.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b49359977c6ec9f5d0573ea4e0071ad278ef905aa74e420acc73fd28ce39e9ce"}, - {file = "grpcio-1.67.1-cp38-cp38-win32.whl", hash = "sha256:f5b76ff64aaac53fede0cc93abf57894ab2a7362986ba22243d06218b93efe46"}, - {file = "grpcio-1.67.1-cp38-cp38-win_amd64.whl", hash = "sha256:804c6457c3cd3ec04fe6006c739579b8d35c86ae3298ffca8de57b493524b771"}, - {file = "grpcio-1.67.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:a25bdea92b13ff4d7790962190bf6bf5c4639876e01c0f3dda70fc2769616335"}, - {file = "grpcio-1.67.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc491ae35a13535fd9196acb5afe1af37c8237df2e54427be3eecda3653127e"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:85f862069b86a305497e74d0dc43c02de3d1d184fc2c180993aa8aa86fbd19b8"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec74ef02010186185de82cc594058a3ccd8d86821842bbac9873fd4a2cf8be8d"}, - {file = "grpcio-1.67.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01f616a964e540638af5130469451cf580ba8c7329f45ca998ab66e0c7dcdb04"}, - {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:299b3d8c4f790c6bcca485f9963b4846dd92cf6f1b65d3697145d005c80f9fe8"}, - {file = "grpcio-1.67.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:60336bff760fbb47d7e86165408126f1dded184448e9a4c892189eb7c9d3f90f"}, - {file = "grpcio-1.67.1-cp39-cp39-win32.whl", hash = "sha256:5ed601c4c6008429e3d247ddb367fe8c7259c355757448d7c1ef7bd4a6739e8e"}, - {file = "grpcio-1.67.1-cp39-cp39-win_amd64.whl", hash = "sha256:5db70d32d6703b89912af16d6d45d78406374a8b8ef0d28140351dd0ec610e98"}, - {file = "grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.67.1)"] + {file = "grpcio-1.68.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:619b5d0f29f4f5351440e9343224c3e19912c21aeda44e0c49d0d147a8d01544"}, + {file = "grpcio-1.68.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a59f5822f9459bed098ffbceb2713abbf7c6fd13f2b9243461da5c338d0cd6c3"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c03d89df516128febc5a7e760d675b478ba25802447624edf7aa13b1e7b11e2a"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44bcbebb24363d587472089b89e2ea0ab2e2b4df0e4856ba4c0b087c82412121"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79f81b7fbfb136247b70465bd836fa1733043fdee539cd6031cb499e9608a110"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88fb2925789cfe6daa20900260ef0a1d0a61283dfb2d2fffe6194396a354c618"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99f06232b5c9138593ae6f2e355054318717d32a9c09cdc5a2885540835067a1"}, + {file = "grpcio-1.68.0-cp310-cp310-win32.whl", hash = "sha256:a6213d2f7a22c3c30a479fb5e249b6b7e648e17f364598ff64d08a5136fe488b"}, + {file = "grpcio-1.68.0-cp310-cp310-win_amd64.whl", hash = "sha256:15327ab81131ef9b94cb9f45b5bd98803a179c7c61205c8c0ac9aff9d6c4e82a"}, + {file = "grpcio-1.68.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3b2b559beb2d433129441783e5f42e3be40a9e1a89ec906efabf26591c5cd415"}, + {file = "grpcio-1.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46541de8425a4d6829ac6c5d9b16c03c292105fe9ebf78cb1c31e8d242f9155"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c1245651f3c9ea92a2db4f95d37b7597db6b246d5892bca6ee8c0e90d76fb73c"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1931c7aa85be0fa6cea6af388e576f3bf6baee9e5d481c586980c774debcb4"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ff09c81e3aded7a183bc6473639b46b6caa9c1901d6f5e2cba24b95e59e30"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8c73f9fbbaee1a132487e31585aa83987ddf626426d703ebcb9a528cf231c9b1"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b2f98165ea2790ea159393a2246b56f580d24d7da0d0342c18a085299c40a75"}, + {file = "grpcio-1.68.0-cp311-cp311-win32.whl", hash = "sha256:e1e7ed311afb351ff0d0e583a66fcb39675be112d61e7cfd6c8269884a98afbc"}, + {file = "grpcio-1.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:e0d2f68eaa0a755edd9a47d40e50dba6df2bceda66960dee1218da81a2834d27"}, + {file = "grpcio-1.68.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8af6137cc4ae8e421690d276e7627cfc726d4293f6607acf9ea7260bd8fc3d7d"}, + {file = "grpcio-1.68.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4028b8e9a3bff6f377698587d642e24bd221810c06579a18420a17688e421af7"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f60fa2adf281fd73ae3a50677572521edca34ba373a45b457b5ebe87c2d01e1d"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18589e747c1e70b60fab6767ff99b2d0c359ea1db8a2cb524477f93cdbedf5b"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d30f3fee9372796f54d3100b31ee70972eaadcc87314be369360248a3dcffe"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7e0a3e72c0e9a1acab77bef14a73a416630b7fd2cbd893c0a873edc47c42c8cd"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a831dcc343440969aaa812004685ed322cdb526cd197112d0db303b0da1e8659"}, + {file = "grpcio-1.68.0-cp312-cp312-win32.whl", hash = "sha256:5a180328e92b9a0050958ced34dddcb86fec5a8b332f5a229e353dafc16cd332"}, + {file = "grpcio-1.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bddd04a790b69f7a7385f6a112f46ea0b34c4746f361ebafe9ca0be567c78e9"}, + {file = "grpcio-1.68.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:fc05759ffbd7875e0ff2bd877be1438dfe97c9312bbc558c8284a9afa1d0f40e"}, + {file = "grpcio-1.68.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15fa1fe25d365a13bc6d52fcac0e3ee1f9baebdde2c9b3b2425f8a4979fccea1"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:32a9cb4686eb2e89d97022ecb9e1606d132f85c444354c17a7dbde4a455e4a3b"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dba037ff8d284c8e7ea9a510c8ae0f5b016004f13c3648f72411c464b67ff2fb"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0efbbd849867e0e569af09e165363ade75cf84f5229b2698d53cf22c7a4f9e21"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:4e300e6978df0b65cc2d100c54e097c10dfc7018b9bd890bbbf08022d47f766d"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:6f9c7ad1a23e1047f827385f4713b5b8c6c7d325705be1dd3e31fb00dcb2f665"}, + {file = "grpcio-1.68.0-cp313-cp313-win32.whl", hash = "sha256:3ac7f10850fd0487fcce169c3c55509101c3bde2a3b454869639df2176b60a03"}, + {file = "grpcio-1.68.0-cp313-cp313-win_amd64.whl", hash = "sha256:afbf45a62ba85a720491bfe9b2642f8761ff348006f5ef67e4622621f116b04a"}, + {file = "grpcio-1.68.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:f8f695d9576ce836eab27ba7401c60acaf9ef6cf2f70dfe5462055ba3df02cc3"}, + {file = "grpcio-1.68.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9fe1b141cda52f2ca73e17d2d3c6a9f3f3a0c255c216b50ce616e9dca7e3441d"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:4df81d78fd1646bf94ced4fb4cd0a7fe2e91608089c522ef17bc7db26e64effd"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46a2d74d4dd8993151c6cd585594c082abe74112c8e4175ddda4106f2ceb022f"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17278d977746472698460c63abf333e1d806bd41f2224f90dbe9460101c9796"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15377bce516b1c861c35e18eaa1c280692bf563264836cece693c0f169b48829"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc5f0a4f5904b8c25729a0498886b797feb817d1fd3812554ffa39551112c161"}, + {file = "grpcio-1.68.0-cp38-cp38-win32.whl", hash = "sha256:def1a60a111d24376e4b753db39705adbe9483ef4ca4761f825639d884d5da78"}, + {file = "grpcio-1.68.0-cp38-cp38-win_amd64.whl", hash = "sha256:55d3b52fd41ec5772a953612db4e70ae741a6d6ed640c4c89a64f017a1ac02b5"}, + {file = "grpcio-1.68.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0d230852ba97654453d290e98d6aa61cb48fa5fafb474fb4c4298d8721809354"}, + {file = "grpcio-1.68.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:50992f214264e207e07222703c17d9cfdcc2c46ed5a1ea86843d440148ebbe10"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:14331e5c27ed3545360464a139ed279aa09db088f6e9502e95ad4bfa852bb116"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f84890b205692ea813653ece4ac9afa2139eae136e419231b0eec7c39fdbe4c2"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cf343c6f4f6aa44863e13ec9ddfe299e0be68f87d68e777328bff785897b05"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd2c2d47969daa0e27eadaf15c13b5e92605c5e5953d23c06d0b5239a2f176d3"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:18668e36e7f4045820f069997834e94e8275910b1f03e078a6020bd464cb2363"}, + {file = "grpcio-1.68.0-cp39-cp39-win32.whl", hash = "sha256:2af76ab7c427aaa26aa9187c3e3c42f38d3771f91a20f99657d992afada2294a"}, + {file = "grpcio-1.68.0-cp39-cp39-win_amd64.whl", hash = "sha256:e694b5928b7b33ca2d3b4d5f9bf8b5888906f181daff6b406f4938f3a997a490"}, + {file = "grpcio-1.68.0.tar.gz", hash = "sha256:7e7483d39b4a4fddb9906671e9ea21aaad4f031cdfc349fec76bdfa1e404543a"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.68.0)"] [[package]] name = "grpcio-status" -version = "1.67.1" +version = "1.68.0" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_status-1.67.1-py3-none-any.whl", hash = "sha256:16e6c085950bdacac97c779e6a502ea671232385e6e37f258884d6883392c2bd"}, - {file = "grpcio_status-1.67.1.tar.gz", hash = "sha256:2bf38395e028ceeecfd8866b081f61628114b384da7d51ae064ddc8d766a5d11"}, + {file = "grpcio_status-1.68.0-py3-none-any.whl", hash = "sha256:0a71b15d989f02df803b4ba85c5bf1f43aeaa58ac021e5f9974b8cadc41f784d"}, + {file = "grpcio_status-1.68.0.tar.gz", hash = "sha256:8369823de22ab6a2cddb3804669c149ae7a71819e127c2dca7c2322028d52bea"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.67.1" +grpcio = ">=1.68.0" protobuf = ">=5.26.1,<6.0dev" [[package]] @@ -1542,28 +1563,6 @@ setproctitle = ["setproctitle"] testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] -[[package]] -name = "html-tag-names" -version = "0.1.2" -description = "List of known HTML tag names" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"}, - {file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"}, -] - -[[package]] -name = "html-void-elements" -version = "0.1.0" -description = "List of HTML void tag names." -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"}, - {file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"}, -] - [[package]] name = "htmlmin" version = "0.1.12" @@ -1632,22 +1631,26 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" @@ -1729,15 +1732,18 @@ six = ">=1.13.0" [[package]] name = "json5" -version = "0.9.25" +version = "0.9.28" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, + {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, ] +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + [[package]] name = "jsonpointer" version = "3.0.0" @@ -1871,13 +1877,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.23.0" +version = "3.23.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" files = [ - {file = "marshmallow-3.23.0-py3-none-any.whl", hash = "sha256:82f20a2397834fe6d9611b241f2f7e7b680ed89c49f84728a1ad937be6b4bdf4"}, - {file = "marshmallow-3.23.0.tar.gz", hash = "sha256:98d8827a9f10c03d44ead298d2e99c6aea8197df18ccfad360dae7f89a50da2e"}, + {file = "marshmallow-3.23.1-py3-none-any.whl", hash = "sha256:fece2eb2c941180ea1b7fcbd4a83c51bfdd50093fdd3ad2585ee5e1df2508491"}, + {file = "marshmallow-3.23.1.tar.gz", hash = "sha256:3a8dfda6edd8dcdbf216c0ede1d1e78d230a6dc9c5a088f58c4083b974a0d468"}, ] [package.dependencies] @@ -1885,7 +1891,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "simplejson"] [[package]] @@ -1917,18 +1923,18 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.18" +version = "5.0.21" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.18-py2.py3-none-any.whl", hash = "sha256:8e25401f7d7910e19a732b417e0d503ef86cf4de9114a273dd62679a42f3be1c"}, - {file = "moto-5.0.18.tar.gz", hash = "sha256:8a7ad2f53a2e6cc9db2ff65c0e0d4b5d7e78bc00b825c9e1ff6cc394371e76e9"}, + {file = "moto-5.0.21-py3-none-any.whl", hash = "sha256:1235b2ae3666459c9cc44504a5e73d35f4959b45e5876b2f6df2e5f4889dfb4f"}, + {file = "moto-5.0.21.tar.gz", hash = "sha256:52f63291daeff9444ef5eb14fbf69b24264567b79f184ae6aee4945d09845f06"}, ] [package.dependencies] boto3 = ">=1.9.201" -botocore = ">=1.14.0" +botocore = ">=1.14.0,<1.35.45 || >1.35.45,<1.35.46 || >1.35.46" cryptography = ">=3.3.1" Jinja2 = ">=2.10.1" python-dateutil = ">=2.1,<3.0.0" @@ -2026,49 +2032,49 @@ files = [ [[package]] name = "opentelemetry-api" -version = "1.27.0" +version = "1.28.2" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, - {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, + {file = "opentelemetry_api-1.28.2-py3-none-any.whl", hash = "sha256:6fcec89e265beb258fe6b1acaaa3c8c705a934bd977b9f534a2b7c0d2d4275a6"}, + {file = "opentelemetry_api-1.28.2.tar.gz", hash = "sha256:ecdc70c7139f17f9b0cf3742d57d7020e3e8315d6cffcdf1a12a905d45b19cc0"}, ] [package.dependencies] deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=8.4.0" +importlib-metadata = ">=6.0,<=8.5.0" [[package]] name = "opentelemetry-sdk" -version = "1.27.0" +version = "1.28.2" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, - {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, + {file = "opentelemetry_sdk-1.28.2-py3-none-any.whl", hash = "sha256:93336c129556f1e3ccd21442b94d3521759541521861b2214c499571b85cb71b"}, + {file = "opentelemetry_sdk-1.28.2.tar.gz", hash = "sha256:5fed24c5497e10df30282456fe2910f83377797511de07d14cec0d3e0a1a3110"}, ] [package.dependencies] -opentelemetry-api = "1.27.0" -opentelemetry-semantic-conventions = "0.48b0" +opentelemetry-api = "1.28.2" +opentelemetry-semantic-conventions = "0.49b2" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.48b0" +version = "0.49b2" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, - {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, + {file = "opentelemetry_semantic_conventions-0.49b2-py3-none-any.whl", hash = "sha256:51e7e1d0daa958782b6c2a8ed05e5f0e7dd0716fc327ac058777b8659649ee54"}, + {file = "opentelemetry_semantic_conventions-0.49b2.tar.gz", hash = "sha256:44e32ce6a5bb8d7c0c617f84b9dc1c8deda1045a07dc16a688cc7cbeab679997"}, ] [package.dependencies] deprecated = ">=1.2.6" -opentelemetry-api = "1.27.0" +opentelemetry-api = "1.28.2" [[package]] name = "ordered-set" @@ -2086,13 +2092,13 @@ dev = ["black", "mypy", "pytest"] [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -2163,18 +2169,18 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.48.0" +version = "1.49.0" description = "A high-level API to automate web browsers" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "playwright-1.48.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:082bce2739f1078acc7d0734da8cc0e23eb91b7fae553f3316d733276f09a6b1"}, - {file = "playwright-1.48.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7da2eb51a19c7f3b523e9faa9d98e7af92e52eb983a099979ea79c9668e3cbf7"}, - {file = "playwright-1.48.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:115b988d1da322358b77bc3bf2d3cc90f8c881e691461538e7df91614c4833c9"}, - {file = "playwright-1.48.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8dabb80e62f667fe2640a8b694e26a7b884c0b4803f7514a3954fc849126227b"}, - {file = "playwright-1.48.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ff8303409ebed76bed4c3d655340320b768817d900ba208b394fdd7d7939a5c"}, - {file = "playwright-1.48.0-py3-none-win32.whl", hash = "sha256:85598c360c590076d4f435525be991246d74a905b654ac19d26eab7ed9b98b2d"}, - {file = "playwright-1.48.0-py3-none-win_amd64.whl", hash = "sha256:e0e87b0c4dc8fce83c725dd851aec37bc4e882bb225ec8a96bd83cf32d4f1623"}, + {file = "playwright-1.49.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:704532a2d8ba580ec9e1895bfeafddce2e3d52320d4eb8aa38e80376acc5cbb0"}, + {file = "playwright-1.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e453f02c4e5cc2db7e9759c47e7425f32e50ac76c76b7eb17c69eed72f01c4d8"}, + {file = "playwright-1.49.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:37ae985309184472946a6eb1a237e5d93c9e58a781fa73b75c8751325002a5d4"}, + {file = "playwright-1.49.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:68d94beffb3c9213e3ceaafa66171affd9a5d9162e0c8a3eed1b1132c2e57598"}, + {file = "playwright-1.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f12d2aecdb41fc25a624cb15f3e8391c252ebd81985e3d5c1c261fe93779345"}, + {file = "playwright-1.49.0-py3-none-win32.whl", hash = "sha256:91103de52d470594ad375b512d7143fa95d6039111ae11a93eb4fe2f2b4a4858"}, + {file = "playwright-1.49.0-py3-none-win_amd64.whl", hash = "sha256:34d28a2c2d46403368610be4339898dc9c34eb9f7c578207b4715c49743a072a"}, ] [package.dependencies] @@ -2599,105 +2605,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -2742,114 +2748,101 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.21.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, + {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, + {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, + {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, + {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, + {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, + {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, + {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, + {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, + {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, + {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, ] [[package]] @@ -2868,40 +2861,40 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.7.1" +version = "0.7.4" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, - {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, - {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, - {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, - {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, - {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, - {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, - {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, - {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, - {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, - {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, - {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, + {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, + {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, + {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, + {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, + {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, + {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, + {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, + {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, + {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, ] [[package]] name = "s3transfer" -version = "0.10.3" +version = "0.10.4" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, - {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, + {file = "s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e"}, + {file = "s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"}, ] [package.dependencies] @@ -2928,23 +2921,23 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "75.3.0" +version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, + {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "simplejson" @@ -3142,20 +3135,21 @@ files = [ [[package]] name = "tqdm" -version = "4.66.6" +version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.6-py3-none-any.whl", hash = "sha256:223e8b5359c2efc4b30555531f09e9f2f3589bcd7fdd389271191031b49b7a63"}, - {file = "tqdm-4.66.6.tar.gz", hash = "sha256:4bdd694238bef1485ce839d67967ab50af8f9272aab687c0d7702a01da0be090"}, + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -3264,13 +3258,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.2.0.20241025" +version = "75.5.0.20241122" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-75.2.0.20241025.tar.gz", hash = "sha256:2949913a518d5285ce00a3b7d88961c80a6e72ffb8f3da0a3f5650ea533bd45e"}, - {file = "types_setuptools-75.2.0.20241025-py3-none-any.whl", hash = "sha256:6721ac0f1a620321e2ccd87a9a747c4a383dc381f78d894ce37f2455b45fcf1c"}, + {file = "types_setuptools-75.5.0.20241122-py3-none-any.whl", hash = "sha256:d69c445f7bdd5e49d1b2441aadcee1388febcc9ad9d9d5fd33648b555e0b1c31"}, + {file = "types_setuptools-75.5.0.20241122.tar.gz", hash = "sha256:196aaf1811cbc1c77ac1d4c4879d5308b6fdf426e56b73baadbca2a1827dadef"}, ] [[package]] @@ -3335,13 +3329,13 @@ files = [ [[package]] name = "werkzeug" -version = "3.0.6" +version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "werkzeug-3.0.6-py3-none-any.whl", hash = "sha256:1bc0c2310d2fbb07b1dd1105eba2f7af72f322e1e455f2f93c993bee8c8a5f17"}, - {file = "werkzeug-3.0.6.tar.gz", hash = "sha256:a8dd59d4de28ca70471a34cba79bed5f7ef2e036a76b3ab0835474246eb41f8d"}, + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, ] [package.dependencies] @@ -3352,81 +3346,76 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "wrapt" -version = "1.16.0" +version = "1.17.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, ] [[package]] @@ -3459,13 +3448,13 @@ files = [ [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] From a0b6a31022edb75ae3e83269b0baf1501d65ade5 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:27:05 +0000 Subject: [PATCH 488/567] Schemas v5.19.0 (#1557) Signed-off-by: Yuyutsu Rai --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 408dfb526e..e38ba33c72 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.18.0 +v5.19.0 From 1fffecd57d5d54822ac5c5d0389f31c709d52d7e Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:01:56 +0000 Subject: [PATCH 489/567] Add Colima support to Runner (#1552) --- Makefile | 16 ++----- README.md | 20 ++++++-- docker-compose-dev-mac.yml | 47 ------------------- ...se-dev-linux.yml => docker-compose-dev.yml | 0 4 files changed, 22 insertions(+), 61 deletions(-) delete mode 100644 docker-compose-dev-mac.yml rename docker-compose-dev-linux.yml => docker-compose-dev.yml (100%) diff --git a/Makefile b/Makefile index 7e721c8086..907e145505 100644 --- a/Makefile +++ b/Makefile @@ -106,19 +106,13 @@ run-uwsgi-async: link-development-env WEB_SERVER_TYPE=uwsgi-async poetry run ./run_app.sh dev-compose-up: - docker compose -f docker-compose-dev-mac.yml pull eq-questionnaire-launcher - docker compose -f docker-compose-dev-mac.yml pull sds - docker compose -f docker-compose-dev-mac.yml pull cir - docker compose -f docker-compose-dev-mac.yml up -d - -dev-compose-up-linux: - docker compose -f docker-compose-dev-linux.yml up -d + docker compose -f docker-compose-dev.yml pull eq-questionnaire-launcher + docker compose -f docker-compose-dev.yml pull sds + docker compose -f docker-compose-dev.yml pull cir + docker compose -f docker-compose-dev.yml up -d dev-compose-down: - docker compose -f docker-compose-dev-mac.yml down - -dev-compose-down-linux: - docker compose -f docker-compose-dev-linux.yml down + docker compose -f docker-compose-dev.yml down profile: poetry run python profile_application.py diff --git a/README.md b/README.md index 73dd4050a2..fcf50cc66c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,21 @@ ## Run with Docker -Install Docker for your system: [https://www.docker.com/](https://www.docker.com/) +Install [Docker](https://www.docker.com/) for your system. Make sure that you've installed both docker and docker-compose packages, preferably using Homebrew: + +``` shell +brew install docker +brew install docker-compose +``` +On MacOS install container runtimes, eg. [Colima](https://github.com/abiosoft/colima): +```shell +brew install colima +``` + +Make sure Colima is started every time you want to use Docker images: +```shell +colima start +``` To get eq-questionnaire-runner running the following command will build and run the containers @@ -141,7 +155,7 @@ Runner requires five supporting services - a questionnaire launcher, a storage b #### Run supporting services with Docker -To run the app locally, but the supporting services in Docker, run: +To run the app locally, but the supporting services in Docker, make sure you have Docker and Colima installed [from this step](#run-with-docker), then run: ``` shell make dev-compose-up @@ -158,7 +172,7 @@ make dev-compose-up-linux ##### [Questionnaire launcher](https://github.com/ONSDigital/eq-questionnaire-launcher) ``` shell -docker run -e SURVEY_RUNNER_SCHEMA_URL=http://docker.for.mac.host.internal:5000 -e SDS_API_BASE_URL=http://docker.for.mac.host.internal:5003 -e CIR_API_BASE_URL=http://docker.for.mac.host.internal:5004 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest +docker run -e SURVEY_RUNNER_SCHEMA_URL=http://host.docker.internal:5000 -e SDS_API_BASE_URL=http://host.docker.internal:5003 -e CIR_API_BASE_URL=http://host.docker.internal:5004 -it -p 8000:8000 onsdigital/eq-questionnaire-launcher:latest ``` ##### [Mock Supplementary data service](https://github.com/ONSDigital/eq-runner-mock-sds) diff --git a/docker-compose-dev-mac.yml b/docker-compose-dev-mac.yml deleted file mode 100644 index 9312153355..0000000000 --- a/docker-compose-dev-mac.yml +++ /dev/null @@ -1,47 +0,0 @@ -services: - datastore: - image: knarz/datastore-emulator - networks: - - eq-env - ports: - - "8432:8432" - - redis: - image: redis:4 - networks: - - eq-env - ports: - - "6379:6379" - - eq-questionnaire-launcher: - image: "onsdigital/eq-questionnaire-launcher:latest" - environment: - SURVEY_RUNNER_URL: http://localhost:5000 - SURVEY_RUNNER_SCHEMA_URL: http://docker.for.mac.host.internal:5000 - SDS_API_BASE_URL: http://docker.for.mac.host.internal:5003 - CIR_API_BASE_URL: http://docker.for.mac.host.internal:5004 - networks: - - eq-env - restart: always - ports: - - "8000:8000" - - sds: - image: "onsdigital/eq-runner-mock-sds:latest" - networks: - - eq-env - restart: always - ports: - - "5003:5003" - - cir: - image: "onsdigital/eq-runner-mock-cir:latest" - networks: - - eq-env - restart: always - ports: - - "5004:5004" - -networks: - eq-env: - driver: bridge diff --git a/docker-compose-dev-linux.yml b/docker-compose-dev.yml similarity index 100% rename from docker-compose-dev-linux.yml rename to docker-compose-dev.yml From eff824a5778450720f0ffcb7cbf4eacbaa68d7c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:24:58 +0000 Subject: [PATCH 490/567] Bump the development-dependencies group with 7 updates (#1558) Bumps the development-dependencies group with 7 updates: | Package | From | To | | --- | --- | --- | | [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli) | `9.2.6` | `9.4.1` | | [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) | `9.2.5` | `9.2.8` | | [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter) | `9.2.2` | `9.2.14` | | [prettier](https://github.com/prettier/prettier) | `3.3.3` | `3.4.1` | | [typescript](https://github.com/microsoft/TypeScript) | `5.6.3` | `5.7.2` | | [uuid](https://github.com/uuidjs/uuid) | `11.0.2` | `11.0.3` | | [webdriverio](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio) | `9.2.6` | `9.4.1` | Updates `@wdio/cli` from 9.2.6 to 9.4.1 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.4.1/packages/wdio-cli) Updates `@wdio/mocha-framework` from 9.2.5 to 9.2.8 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.2.8/packages/wdio-mocha-framework) Updates `@wdio/spec-reporter` from 9.2.2 to 9.2.14 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.2.14/packages/wdio-spec-reporter) Updates `prettier` from 3.3.3 to 3.4.1 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.3.3...3.4.1) Updates `typescript` from 5.6.3 to 5.7.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.6.3...v5.7.2) Updates `uuid` from 11.0.2 to 11.0.3 - [Release notes](https://github.com/uuidjs/uuid/releases) - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v11.0.2...v11.0.3) Updates `webdriverio` from 9.2.6 to 9.4.1 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.4.1/packages/webdriverio) --- updated-dependencies: - dependency-name: "@wdio/cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: "@wdio/mocha-framework" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: "@wdio/spec-reporter" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: uuid dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 105 +++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 502cdda4e4..1b6399fc60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2776,19 +2776,19 @@ } }, "node_modules/@wdio/cli": { - "version": "9.2.6", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.2.6.tgz", - "integrity": "sha512-+Vn2aLwx4M/LZ521yhkDgEXhTOrf8Gg1byZ+0pckWYaB3OmkZ+BGMFEn3a7n781Zcxhq7P/K2BY1lql50axW5g==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.4.1.tgz", + "integrity": "sha512-GDyAer63WDsr2ckXmrpUyAcIZFd3pCRIpi85rL1ZjnWthRy/UtwY0EHPMDuSeUEJ28iYwW3esKgq2ZKlsdbMeA==", "dev": true, "dependencies": { "@types/node": "^20.1.1", "@vitest/snapshot": "^2.1.1", - "@wdio/config": "9.2.5", - "@wdio/globals": "9.2.6", + "@wdio/config": "9.2.8", + "@wdio/globals": "9.4.1", "@wdio/logger": "9.1.3", "@wdio/protocols": "9.2.2", "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.5", + "@wdio/utils": "9.2.8", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^4.0.0", @@ -2804,7 +2804,7 @@ "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", "tsx": "^4.7.2", - "webdriverio": "9.2.6", + "webdriverio": "9.4.1", "yargs": "^17.7.2" }, "bin": { @@ -2827,14 +2827,14 @@ } }, "node_modules/@wdio/config": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.2.5.tgz", - "integrity": "sha512-gqblHShjriGH3va2nSnQ2wktwargHK2oEDepNPG1LMPB5uWd997f+zyaR4s4vtqqUkdxwciIA1H4rDC3fIlesw==", + "version": "9.2.8", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.2.8.tgz", + "integrity": "sha512-EGMmBPGJbz6RmgMjebRWkWu3fGyeTIRcusF4UA4f2tiUEKY8nbzUO/ZyDjVQNR+YVB40q0jcqAqpszYRrIzzeg==", "dev": true, "dependencies": { "@wdio/logger": "9.1.3", "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.5", + "@wdio/utils": "9.2.8", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", "glob": "^10.2.2", @@ -2845,16 +2845,16 @@ } }, "node_modules/@wdio/globals": { - "version": "9.2.6", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.2.6.tgz", - "integrity": "sha512-aQqunfSZYqY2oI2YHf9qy9mab0C5Mk9Y904WIZAcEcMA1oojkutRp39btCEMY+twRJjswswWYFoBncTQY8vFDA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.4.1.tgz", + "integrity": "sha512-CTVAVJ7iFyT54XF9iRmNvsDB+WSHoztJPG9XPL/mHzQ2LYfSyUR8E/j+3iHbTx3v/qRNucgPcGwhxiuY2RcaDg==", "dev": true, "engines": { "node": ">=18.20.0" }, "optionalDependencies": { "expect-webdriverio": "^5.0.1", - "webdriverio": "9.2.6" + "webdriverio": "9.4.1" } }, "node_modules/@wdio/local-runner": { @@ -2952,16 +2952,16 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.2.5.tgz", - "integrity": "sha512-KmX9TpTrT/9sq/R1seTdV/rRhjCiql7Eqryga4f+a5Sb9PmtHHr8vR2sfyLRXui1Go821TeTw6bqRld7xhAZlA==", + "version": "9.2.8", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.2.8.tgz", + "integrity": "sha512-lCek1D+Cdnb8sF/E/PkdKX1PLrq677YVKHV6hFlZeRHWKcJyopCTvhwnJerZNFgmcTuZ6zeiGLcNNlPdQzF4oQ==", "dev": true, "dependencies": { "@types/mocha": "^10.0.6", "@types/node": "^20.11.28", "@wdio/logger": "9.1.3", "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.5", + "@wdio/utils": "9.2.8", "mocha": "^10.3.0" }, "engines": { @@ -2996,9 +2996,9 @@ } }, "node_modules/@wdio/reporter": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.2.2.tgz", - "integrity": "sha512-3FxIMol0lTQ8S0gSlgvBpjTg9w/GRCFH+juHVIwjmdeqgKwID0IoKGxLo2enLjR7T3wsWUzN6JaqL/fa4nAK4A==", + "version": "9.2.14", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.2.14.tgz", + "integrity": "sha512-njOqa9+w5zc9AY4fsUmW46EhZ2nzMFRXZPCmwnGZuYk81L3slVfAFhKk1wSTRhSbghjYAZndt9Yo3c4wFLh6Lg==", "dev": true, "dependencies": { "@types/node": "^20.1.0", @@ -3380,12 +3380,12 @@ } }, "node_modules/@wdio/spec-reporter": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.2.2.tgz", - "integrity": "sha512-J/4tt09SJb1mPsy5CQNi5hZyT2ZJ9qhpPS+yBAR/74P/E1bsfHHBMEb2n0CNPp9IhIdyGZzMxueaJAWEiGGWQQ==", + "version": "9.2.14", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.2.14.tgz", + "integrity": "sha512-/0QI/Lfld2gKscVAOTxgy1KyshxreDEu2F0FsiNQUUMQXuV8bN85lGgH6K3F+xZ0p17J/tg8UnWQCv2IaXTycw==", "dev": true, "dependencies": { - "@wdio/reporter": "9.2.2", + "@wdio/reporter": "9.2.14", "@wdio/types": "9.2.2", "chalk": "^5.1.2", "easy-table": "^1.2.0", @@ -3420,9 +3420,9 @@ } }, "node_modules/@wdio/utils": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.2.5.tgz", - "integrity": "sha512-QgBxscPVuqC/fP62ggssBfWLnonVs2/r1xaj5MgH+tLNez/OVS98pWx0/FYsFHVeTVO5vPnJIhoTo2CXz8tQzA==", + "version": "9.2.8", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.2.8.tgz", + "integrity": "sha512-rKm5FXkpsCyeqh8tdirtRUHvgNytWNMiaVKdctsvKOJvqnDVPAAQcz9Wmgo7bSwoLwtSHcDaRoxY7olV7J4QnA==", "dev": true, "dependencies": { "@puppeteer/browsers": "^2.2.0", @@ -6155,9 +6155,9 @@ } }, "node_modules/expect-webdriverio": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.0.3.tgz", - "integrity": "sha512-0RHsFZX1856qCWZsXcvacFZpdZc7UAVD9wAglzf3KMWO1AoXt5EorjsNp1H9StGysxhJuVXJxRWKeXnD4LKtjQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.0.5.tgz", + "integrity": "sha512-h04OGd7ZksVj8bgv3bYdjFpmJuKeCnyRrBmpMxYpMDmYSspxg9vsSr0kD5p9oOM16bX0ZXEVXr42RbI2hoLpTw==", "dev": true, "optional": true, "dependencies": { @@ -10185,9 +10185,9 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -11841,9 +11841,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -12039,9 +12039,9 @@ "dev": true }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -12445,19 +12445,19 @@ } }, "node_modules/webdriverio": { - "version": "9.2.6", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.2.6.tgz", - "integrity": "sha512-GjQykwwYBXwqWAs0CUCU0Hg9nR5DonOwFkWQUcUpCfBVSTLdeH0fBEDikKVf7ohuDOUSOGo0wM9LN8ZTMroMXA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.4.1.tgz", + "integrity": "sha512-XIPtRnxSES4CoxH2BfUY/6QzNgEgJEUjMYu7t7SJR8bVfbLRVXAA1ie9kM0MtdLs4oZ2Pr8rR8fqytsA1CjEWw==", "dev": true, "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.2.5", + "@wdio/config": "9.2.8", "@wdio/logger": "9.1.3", "@wdio/protocols": "9.2.2", "@wdio/repl": "9.0.8", "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.5", + "@wdio/utils": "9.2.8", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", @@ -12476,7 +12476,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.2.5" + "webdriver": "9.4.1" }, "engines": { "node": ">=18.20.0" @@ -12527,19 +12527,20 @@ } }, "node_modules/webdriverio/node_modules/webdriver": { - "version": "9.2.5", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.2.5.tgz", - "integrity": "sha512-7rmK6oD3oYq+6E0qa9FQ1/67Ajf2APTOghmqlcDnSBTnTGF51UPOWnzgy0x3yGozmCTVRe13O75JXRRWpvxOvA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.4.1.tgz", + "integrity": "sha512-vFDdxMj/9W1+6jhpHSiRYfO8dix23HjAUtLx7aOv9ejEsntC0EzCIAftJ59YsF3Ppu184+FkdDVhnivpkZPTFw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "9.2.5", + "@wdio/config": "9.2.8", "@wdio/logger": "9.1.3", "@wdio/protocols": "9.2.2", "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.5", + "@wdio/utils": "9.2.8", "deepmerge-ts": "^7.0.3", + "undici": "^6.20.1", "ws": "^8.8.0" }, "engines": { From 86fb527c9c465b81a81b9e1cdfc85bbddd57796c Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 11 Dec 2024 12:48:24 +0000 Subject: [PATCH 491/567] Schemas v5.20.0 --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e38ba33c72..bd5492636a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.19.0 +v5.20.0 From a57545c3387c36cc080aca5caa99e4c4714f61c6 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 18 Dec 2024 12:07:38 +0000 Subject: [PATCH 492/567] Schemas v5.21.0 --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index bd5492636a..5e866c06a7 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.20.0 +v5.21.0 From e06542ab31bc122a03b6ee376dd0ea7d5c9ae96b Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 3 Jan 2025 08:10:48 +0000 Subject: [PATCH 493/567] Add CODEOWNERS file (#1562) --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..c01362a6a4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @ONSdigital/eq-runner From f45972b10a202712ffbbfd5463abc63abedc35c6 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:50:39 +0000 Subject: [PATCH 494/567] Update translations file for 2025 (#1572) --- app/translations/messages.pot | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/app/translations/messages.pot b/app/translations/messages.pot index 40f50f1b39..1f8a3639cd 100644 --- a/app/translations/messages.pot +++ b/app/translations/messages.pot @@ -1,14 +1,14 @@ # Translations template for PROJECT. -# Copyright (C) 2024 ORGANIZATION +# Copyright (C) 2025 ORGANIZATION # This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , 2024. +# FIRST AUTHOR , 2025. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-09-12 12:26+0100\n" +"POT-Creation-Date: 2025-01-10 12:58+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -49,7 +49,7 @@ msgstr "" msgid "Change your answer for: {question_title_or_answer_label}" msgstr "" -#: app/jinja_filters.py:741 +#: app/jinja_filters.py:765 #: templates/partials/summary/collapsible-summary.html:27 #: templates/partials/summary/summary.html:24 msgid "No answer provided" @@ -229,29 +229,29 @@ msgstr "" msgid "Menu" msgstr "" -#: app/helpers/template_helpers.py:142 +#: app/helpers/template_helpers.py:138 msgid "The following links open in a new tab" msgstr "" -#: app/helpers/template_helpers.py:167 +#: app/helpers/template_helpers.py:163 msgid "" "Make sure you leave this page or close your " "browser if using a shared device" msgstr "" -#: app/questionnaire/placeholder_transforms.py:202 +#: app/questionnaire/placeholder_transforms.py:203 msgid "{number_of_years} year" msgid_plural "{number_of_years} years" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:208 +#: app/questionnaire/placeholder_transforms.py:209 msgid "{number_of_months} month" msgid_plural "{number_of_months} months" msgstr[0] "" msgstr[1] "" -#: app/questionnaire/placeholder_transforms.py:213 +#: app/questionnaire/placeholder_transforms.py:214 msgid "{number_of_days} day" msgid_plural "{number_of_days} days" msgstr[0] "" @@ -426,7 +426,7 @@ msgstr "" #: templates/individual_response/confirmation-post.html:26 #: templates/individual_response/confirmation-text-message.html:39 #: templates/individual_response/question.html:5 templates/interstitial.html:7 -#: templates/listcollectorcontent.html:6 templates/sectionsummary.html:11 +#: templates/listcollectorcontent.html:8 templates/sectionsummary.html:11 #: templates/sectionsummary.html:51 msgid "Continue" msgstr "" @@ -922,19 +922,19 @@ msgid "" "submitted-response-link\">save or print your answers." msgstr "" -#: templates/layouts/_base.html:148 templates/thank-you.html:84 +#: templates/layouts/_base.html:147 templates/thank-you.html:84 msgid "minute" msgstr "" -#: templates/layouts/_base.html:149 templates/thank-you.html:85 +#: templates/layouts/_base.html:148 templates/thank-you.html:85 msgid "minutes" msgstr "" -#: templates/layouts/_base.html:150 templates/thank-you.html:86 +#: templates/layouts/_base.html:149 templates/thank-you.html:86 msgid "second" msgstr "" -#: templates/layouts/_base.html:151 templates/thank-you.html:87 +#: templates/layouts/_base.html:150 templates/thank-you.html:87 msgid "seconds" msgstr "" @@ -1239,29 +1239,29 @@ msgstr "" msgid "Hide" msgstr "" -#: templates/layouts/_base.html:115 +#: templates/layouts/_base.html:114 msgid "Skip to main content" msgstr "" -#: templates/layouts/_base.html:143 +#: templates/layouts/_base.html:142 msgid "You will be signed out soon" msgstr "" -#: templates/layouts/_base.html:144 +#: templates/layouts/_base.html:143 msgid "It appears you have been inactive for a while." msgstr "" -#: templates/layouts/_base.html:145 +#: templates/layouts/_base.html:144 msgid "" "To protect your information, your progress will be saved and you will be " "signed out in" msgstr "" -#: templates/layouts/_base.html:146 +#: templates/layouts/_base.html:145 msgid "You are being signed out" msgstr "" -#: templates/layouts/_base.html:147 +#: templates/layouts/_base.html:146 msgid "Continue survey" msgstr "" From 565a843d4f3efd608357537f4987e0cc8d203343 Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:15:09 +0000 Subject: [PATCH 495/567] Apply fixes for UP006 (#1555) --- app/forms/duration_form.py | 4 ++-- app/forms/field_handlers/number_handler.py | 4 ++-- app/forms/fields/date_field.py | 4 ++-- app/forms/fields/month_year_date_field.py | 4 ++-- app/forms/fields/year_date_field.py | 4 ++-- app/forms/validators.py | 4 ++-- app/helpers/template_helpers.py | 4 ++-- app/setup.py | 3 +-- app/storage/datastore.py | 4 ++-- app/storage/dynamodb.py | 4 ++-- app/storage/redis.py | 4 ++-- app/storage/storage.py | 10 +++++----- app/views/contexts/calculated_summary_context.py | 4 ++-- app/views/contexts/summary/group.py | 4 ++-- app/views/handlers/calculation_summary.py | 4 +--- pyproject.toml | 1 - tests/app/helpers/test_template_helpers.py | 4 +--- 17 files changed, 32 insertions(+), 38 deletions(-) diff --git a/app/forms/duration_form.py b/app/forms/duration_form.py index eda527ac70..221256a3c9 100644 --- a/app/forms/duration_form.py +++ b/app/forms/duration_form.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Callable, Mapping, Optional, Type +from typing import Callable, Mapping, Optional from wtforms import Form @@ -52,7 +52,7 @@ def data(self) -> Optional[dict[str, Optional[str]]]: def get_duration_form( answer: Mapping, error_messages: ErrorMessageType -) -> Type[DurationForm]: +) -> type[DurationForm]: class CustomDurationForm(DurationForm): mandatory = answer["mandatory"] units = answer["units"] diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index 4d71ccb7af..d5d712e89c 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Any, Type, Union +from typing import Any, Union from wtforms import DecimalField, IntegerField from wtforms.fields.core import UnboundField @@ -58,7 +58,7 @@ def max_decimals(self) -> int: @property def _field_type( self, - ) -> Type[Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator]]: + ) -> type[Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator]]: return ( DecimalFieldWithSeparator if self.max_decimals > 0 diff --git a/app/forms/fields/date_field.py b/app/forms/fields/date_field.py index 2b9affdca7..85754fe9ee 100644 --- a/app/forms/fields/date_field.py +++ b/app/forms/fields/date_field.py @@ -1,6 +1,6 @@ import logging from functools import cached_property -from typing import Any, Callable, Sequence, Type +from typing import Any, Callable, Sequence from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField @@ -11,7 +11,7 @@ logger = logging.getLogger(__name__) -def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: +def get_form_class(validators: Sequence[DateValidatorType]) -> type[Form]: class DateForm(Form): # Validation is only ever added to the 1 field that shows in all 3 variants # This is to prevent an error message for each input box diff --git a/app/forms/fields/month_year_date_field.py b/app/forms/fields/month_year_date_field.py index 75192c45b7..d9afb6adc4 100644 --- a/app/forms/fields/month_year_date_field.py +++ b/app/forms/fields/month_year_date_field.py @@ -1,6 +1,6 @@ import logging from functools import cached_property -from typing import Any, Callable, Sequence, Type +from typing import Any, Callable, Sequence from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField @@ -11,7 +11,7 @@ logger = logging.getLogger(__name__) -def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: +def get_form_class(validators: Sequence[DateValidatorType]) -> type[Form]: class YearMonthDateForm(Form): year = StringField(validators=validators) month = StringField() diff --git a/app/forms/fields/year_date_field.py b/app/forms/fields/year_date_field.py index c9e29607de..7c67405518 100644 --- a/app/forms/fields/year_date_field.py +++ b/app/forms/fields/year_date_field.py @@ -1,6 +1,6 @@ import logging from functools import cached_property -from typing import Any, Callable, Sequence, Type +from typing import Any, Callable, Sequence from werkzeug.datastructures import MultiDict from wtforms import Form, FormField, StringField @@ -11,7 +11,7 @@ logger = logging.getLogger(__name__) -def get_form_class(validators: Sequence[DateValidatorType]) -> Type[Form]: +def get_form_class(validators: Sequence[DateValidatorType]) -> type[Form]: class YearDateForm(Form): year = StringField(validators=validators) diff --git a/app/forms/validators.py b/app/forms/validators.py index 486ea11680..9426038a8d 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -4,7 +4,7 @@ import re from datetime import datetime, timezone from decimal import Decimal, InvalidOperation -from typing import TYPE_CHECKING, Iterable, List, Mapping, Optional, Sequence, Union +from typing import TYPE_CHECKING, Iterable, Mapping, Optional, Sequence, Union import flask_babel from babel import numbers @@ -417,7 +417,7 @@ def __init__( def __call__( self, form: QuestionnaireForm, - conditions: List[str], + conditions: list[str], total: Decimal | int, target_total: Decimal | float | int, decimal_limit: int | None = None, diff --git a/app/helpers/template_helpers.py b/app/helpers/template_helpers.py index b5bc4d3751..4488cf8b46 100644 --- a/app/helpers/template_helpers.py +++ b/app/helpers/template_helpers.py @@ -1,5 +1,5 @@ from functools import cached_property, lru_cache -from typing import Any, Type +from typing import Any from flask import current_app from flask import render_template as flask_render_template @@ -171,7 +171,7 @@ def _footer_warning(self) -> str | None: def survey_config_mapping( *, theme: SurveyType, language: str, base_url: str, schema: QuestionnaireSchema ) -> SurveyConfig: - survey_type_to_config: dict[SurveyType, Type[SurveyConfig]] = { + survey_type_to_config: dict[SurveyType, type[SurveyConfig]] = { SurveyType.DEFAULT: BusinessSurveyConfig, SurveyType.BUSINESS: BusinessSurveyConfig, SurveyType.HEALTH: SocialSurveyConfig, diff --git a/app/setup.py b/app/setup.py index c461239c67..6acbe1adc0 100644 --- a/app/setup.py +++ b/app/setup.py @@ -1,5 +1,4 @@ from copy import deepcopy -from typing import Dict from uuid import uuid4 import boto3 @@ -256,7 +255,7 @@ def setup_jinja_env(application): application.jinja_env.add_extension("jinja2.ext.do") -def _add_cdn_url_to_csp_policy(cdn_url) -> Dict: +def _add_cdn_url_to_csp_policy(cdn_url) -> dict: csp_policy = deepcopy(CSP_POLICY) for directive in csp_policy: if directive not in ["frame-src", "object-src", "base-uri"]: diff --git a/app/storage/datastore.py b/app/storage/datastore.py index b2254d82fd..790ff2ca40 100644 --- a/app/storage/datastore.py +++ b/app/storage/datastore.py @@ -1,4 +1,4 @@ -from typing import Optional, Type +from typing import Optional from google.api_core.retry import Retry from google.cloud import datastore @@ -36,7 +36,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: return True @Retry() - def get(self, model_type: Type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: storage_model = StorageModel(model_type=model_type) key = self.client.key(storage_model.table_name, key_value) diff --git a/app/storage/dynamodb.py b/app/storage/dynamodb.py index 382233f4e3..d578999009 100644 --- a/app/storage/dynamodb.py +++ b/app/storage/dynamodb.py @@ -1,4 +1,4 @@ -from typing import Optional, Type +from typing import Optional import boto3 from botocore.exceptions import ClientError @@ -33,7 +33,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: raise # pragma: no cover - def get(self, model_type: Type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: storage_model = StorageModel(model_type=model_type) table = self.client.Table(storage_model.table_name) key = {storage_model.key_field: key_value} diff --git a/app/storage/redis.py b/app/storage/redis.py index 74e0dd1f83..9a3f5c4b27 100644 --- a/app/storage/redis.py +++ b/app/storage/redis.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import Optional, Type +from typing import Optional import redis from redis.exceptions import ConnectionError as RedisConnectionError @@ -56,7 +56,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: return True - def get(self, model_type: Type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: storage_model = StorageModel(model_type=model_type) try: item = self.client.get(key_value) diff --git a/app/storage/storage.py b/app/storage/storage.py index 323ceca8f8..49a925204d 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from functools import cached_property -from typing import Any, Optional, Type, TypedDict, Union +from typing import Any, Optional, TypedDict, Union from flask import current_app from google.cloud import datastore @@ -23,13 +23,13 @@ class TableConfig(TypedDict, total=False): key_field: str table_name_key: str - schema: Type[ModelSchemaTypes] + schema: type[ModelSchemaTypes] expiry_field: str index_fields: list[str] class StorageModel: - TABLE_CONFIG_BY_TYPE: dict[Type[ModelTypes], TableConfig] = { + TABLE_CONFIG_BY_TYPE: dict[type[ModelTypes], TableConfig] = { app_models.QuestionnaireState: { "key_field": "user_id", "table_name_key": "EQ_QUESTIONNAIRE_STATE_TABLE_NAME", @@ -51,7 +51,7 @@ class StorageModel: }, } - def __init__(self, model_type: Type[ModelTypes]) -> None: + def __init__(self, model_type: type[ModelTypes]) -> None: self._model_type = model_type if self._model_type not in self.TABLE_CONFIG_BY_TYPE: @@ -95,7 +95,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: pass # pragma: no cover @abstractmethod - def get(self, model_type: Type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: pass # pragma: no cover @abstractmethod diff --git a/app/views/contexts/calculated_summary_context.py b/app/views/contexts/calculated_summary_context.py index 6d69df2133..b5f13f543a 100644 --- a/app/views/contexts/calculated_summary_context.py +++ b/app/views/contexts/calculated_summary_context.py @@ -1,4 +1,4 @@ -from typing import Callable, Iterable, Mapping, Tuple +from typing import Callable, Iterable, Mapping from werkzeug.datastructures import ImmutableDict @@ -246,7 +246,7 @@ def _get_formatted_total( return self._format_total(answer_format=answer_format, total=calculated_total) - def _get_answer_format(self, groups: Iterable[Mapping]) -> Tuple[dict, list]: + def _get_answer_format(self, groups: Iterable[Mapping]) -> tuple[dict, list]: values_to_calculate: list = [] answer_format: dict = {"type": None} decimal_limits: list[int] = [] diff --git a/app/views/contexts/summary/group.py b/app/views/contexts/summary/group.py index 290bbe8f61..e906e4825a 100644 --- a/app/views/contexts/summary/group.py +++ b/app/views/contexts/summary/group.py @@ -1,4 +1,4 @@ -from typing import Iterable, Mapping, Type +from typing import Iterable, Mapping from werkzeug.datastructures import ImmutableDict @@ -86,7 +86,7 @@ def _build_blocks_and_links( if parent_list_collector_block_id not in routing_path_block_ids: continue - list_collector_block_class: Type[ + list_collector_block_class: type[ ListCollectorBlock | ListCollectorContentBlock ] = ( ListCollectorBlock diff --git a/app/views/handlers/calculation_summary.py b/app/views/handlers/calculation_summary.py index f82a2ce124..f40fd3b788 100644 --- a/app/views/handlers/calculation_summary.py +++ b/app/views/handlers/calculation_summary.py @@ -1,12 +1,10 @@ -from typing import Type - from app.views.contexts import GrandCalculatedSummaryContext from app.views.contexts.calculated_summary_context import CalculatedSummaryContext from app.views.handlers.content import Content class _SummaryWithCalculation(Content): - summary_class: Type[CalculatedSummaryContext] | Type[GrandCalculatedSummaryContext] + summary_class: type[CalculatedSummaryContext] | type[GrandCalculatedSummaryContext] def get_context(self) -> dict[str, dict]: summary_context = self.summary_class( diff --git a/pyproject.toml b/pyproject.toml index be9a61f862..0e316e72d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,7 +126,6 @@ extend-ignore = [ "UP018", # Unnecessary {literal_type} call (rewrite as a literal) "UP015", # Unnecessary open mode parameters "UP007", # Use `X | Y` for type annotations - "UP006", # Use `type` instead of `Type` for type annotation "UP009", # UTF-8 encoding declaration is unnecessary "UP017", # Use `datetime.UTC` alias "UP033", # Use @functools.cache instead of @functools.lru_cache(maxsize=None) diff --git a/tests/app/helpers/test_template_helpers.py b/tests/app/helpers/test_template_helpers.py index d9e277cdbe..b7bce8136d 100644 --- a/tests/app/helpers/test_template_helpers.py +++ b/tests/app/helpers/test_template_helpers.py @@ -1,5 +1,3 @@ -from typing import Type - import pytest from flask import Flask, current_app from flask import session as cookie_session @@ -893,7 +891,7 @@ def test_get_survey_config( ], ) def test_survey_config_base_url_provided_used_in_links( - app: Flask, survey_config_type: Type[SurveyConfig], base_url: str + app: Flask, survey_config_type: type[SurveyConfig], base_url: str ): with app.app_context(): result = survey_config_type(base_url=base_url) From c329c8f137b21aef71016135bcd74a7a323c315d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 12:03:12 +0000 Subject: [PATCH 496/567] Bump the production-dependencies group across 1 directory with 9 updates (#1575) --- poetry.lock | 244 ++++++++++++++++++++++++++++++------------------- pyproject.toml | 2 +- 2 files changed, 153 insertions(+), 93 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7c10b43a11..d82bf2697a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "astroid" @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.68" +version = "1.35.97" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.68-py3-none-any.whl", hash = "sha256:9b26fa31901da7793c1dcd65eee9bab7e897d8aa1ffed0b5e1c3bce93d2aefe4"}, - {file = "boto3-1.35.68.tar.gz", hash = "sha256:091d6bed1422370987a839bff3f8755df7404fc15e9fac2a48e8505356f07433"}, + {file = "boto3-1.35.97-py3-none-any.whl", hash = "sha256:8e49416216a6e3a62c2a0c44fba4dd2852c85472e7b702516605b1363867d220"}, + {file = "boto3-1.35.97.tar.gz", hash = "sha256:7d398f66a11e67777c189d1f58c0a75d9d60f98d0ee51b8817e828930bf19e4e"}, ] [package.dependencies] -botocore = ">=1.35.68,<1.36.0" +botocore = ">=1.35.97,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.68" +version = "1.35.97" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.68-py3-none-any.whl", hash = "sha256:599139d5564291f5be873800711f9e4e14a823395ae9ce7b142be775e9849b94"}, - {file = "botocore-1.35.68.tar.gz", hash = "sha256:42c3700583a82f2b5316281a073d644a521d6358837e2b446dc458ba5d990fb4"}, + {file = "botocore-1.35.97-py3-none-any.whl", hash = "sha256:fed4f156b1a9b8ece53738f702ba5851b8c6216b4952de326547f349cc494f14"}, + {file = "botocore-1.35.97.tar.gz", hash = "sha256:88f2fab29192ffe2f2115d5bafbbd823ff4b6eb2774296e03ec8b5b0fe074f61"}, ] [package.dependencies] @@ -175,6 +175,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -187,8 +191,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -199,8 +209,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -210,6 +236,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -221,6 +251,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -233,6 +267,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -245,6 +283,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1191,13 +1233,13 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-datastore" -version = "2.20.1" +version = "2.20.2" description = "Google Cloud Datastore API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_datastore-2.20.1-py2.py3-none-any.whl", hash = "sha256:b9383af24d8e90ed6c5d161d72411d82efd9b21c051fa6f4bbd743a49d37ffb3"}, - {file = "google_cloud_datastore-2.20.1.tar.gz", hash = "sha256:07950b9c8865087c565f45fa3fdd7a05d4c3d99adf79e10c3f596ff08a7d9bba"}, + {file = "google_cloud_datastore-2.20.2-py2.py3-none-any.whl", hash = "sha256:d2190180343b807d4aa3b0b3bb837606349b71e5e74e29aa9009c0ae38c0b6a0"}, + {file = "google_cloud_datastore-2.20.2.tar.gz", hash = "sha256:9665d009729d9551329d9476f4d5bda9c11d3469243ea8a2c0d9490b65aa899f"}, ] [package.dependencies] @@ -1212,13 +1254,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.27.1" +version = "2.27.2" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.27.1-py2.py3-none-any.whl", hash = "sha256:3ca8980c198a847ee464845ab60f05478d4819cf693c9950ee89da96f0b80a41"}, - {file = "google_cloud_pubsub-2.27.1.tar.gz", hash = "sha256:7119dbc5af4b915ecdfa1289919f791a432927eaaa7bbfbeb740e6d7020c181e"}, + {file = "google_cloud_pubsub-2.27.2-py2.py3-none-any.whl", hash = "sha256:a919f84fdea683b0a02464e38dd32332edbcbc8e85da82070079a57791119fd6"}, + {file = "google_cloud_pubsub-2.27.2.tar.gz", hash = "sha256:d92c156c7ddd0e5125008f977898198d7b1ae766026056497271bec4909647fe"}, ] [package.dependencies] @@ -1240,13 +1282,13 @@ libcst = ["libcst (>=0.3.10)"] [[package]] name = "google-cloud-storage" -version = "2.18.2" +version = "2.19.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_storage-2.18.2-py2.py3-none-any.whl", hash = "sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166"}, - {file = "google_cloud_storage-2.18.2.tar.gz", hash = "sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99"}, + {file = "google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba"}, + {file = "google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2"}, ] [package.dependencies] @@ -1263,13 +1305,13 @@ tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.17.1" +version = "2.18.0" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_tasks-2.17.1-py2.py3-none-any.whl", hash = "sha256:cee64243623f5b3bae0f57e94eecaa93b61cbe71cf0f1c135554dc51998ab3b8"}, - {file = "google_cloud_tasks-2.17.1.tar.gz", hash = "sha256:164415b256f2db6a123ab42227b164ed8e25917859824a088f6f2560f47aabe1"}, + {file = "google_cloud_tasks-2.18.0-py2.py3-none-any.whl", hash = "sha256:5d994ba60a136c942e322b76bc178e1089fbb9bc08ad58b5178e035cc26d2fe9"}, + {file = "google_cloud_tasks-2.18.0.tar.gz", hash = "sha256:11a26b5b48733405cc4ea066b4f81ac353e5fb8566419b9b4d35d4bd1ceec58c"}, ] [package.dependencies] @@ -1461,70 +1503,70 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [[package]] name = "grpcio" -version = "1.68.0" +version = "1.69.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.68.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:619b5d0f29f4f5351440e9343224c3e19912c21aeda44e0c49d0d147a8d01544"}, - {file = "grpcio-1.68.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a59f5822f9459bed098ffbceb2713abbf7c6fd13f2b9243461da5c338d0cd6c3"}, - {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c03d89df516128febc5a7e760d675b478ba25802447624edf7aa13b1e7b11e2a"}, - {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44bcbebb24363d587472089b89e2ea0ab2e2b4df0e4856ba4c0b087c82412121"}, - {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79f81b7fbfb136247b70465bd836fa1733043fdee539cd6031cb499e9608a110"}, - {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88fb2925789cfe6daa20900260ef0a1d0a61283dfb2d2fffe6194396a354c618"}, - {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99f06232b5c9138593ae6f2e355054318717d32a9c09cdc5a2885540835067a1"}, - {file = "grpcio-1.68.0-cp310-cp310-win32.whl", hash = "sha256:a6213d2f7a22c3c30a479fb5e249b6b7e648e17f364598ff64d08a5136fe488b"}, - {file = "grpcio-1.68.0-cp310-cp310-win_amd64.whl", hash = "sha256:15327ab81131ef9b94cb9f45b5bd98803a179c7c61205c8c0ac9aff9d6c4e82a"}, - {file = "grpcio-1.68.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3b2b559beb2d433129441783e5f42e3be40a9e1a89ec906efabf26591c5cd415"}, - {file = "grpcio-1.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46541de8425a4d6829ac6c5d9b16c03c292105fe9ebf78cb1c31e8d242f9155"}, - {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c1245651f3c9ea92a2db4f95d37b7597db6b246d5892bca6ee8c0e90d76fb73c"}, - {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1931c7aa85be0fa6cea6af388e576f3bf6baee9e5d481c586980c774debcb4"}, - {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ff09c81e3aded7a183bc6473639b46b6caa9c1901d6f5e2cba24b95e59e30"}, - {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8c73f9fbbaee1a132487e31585aa83987ddf626426d703ebcb9a528cf231c9b1"}, - {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b2f98165ea2790ea159393a2246b56f580d24d7da0d0342c18a085299c40a75"}, - {file = "grpcio-1.68.0-cp311-cp311-win32.whl", hash = "sha256:e1e7ed311afb351ff0d0e583a66fcb39675be112d61e7cfd6c8269884a98afbc"}, - {file = "grpcio-1.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:e0d2f68eaa0a755edd9a47d40e50dba6df2bceda66960dee1218da81a2834d27"}, - {file = "grpcio-1.68.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8af6137cc4ae8e421690d276e7627cfc726d4293f6607acf9ea7260bd8fc3d7d"}, - {file = "grpcio-1.68.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4028b8e9a3bff6f377698587d642e24bd221810c06579a18420a17688e421af7"}, - {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f60fa2adf281fd73ae3a50677572521edca34ba373a45b457b5ebe87c2d01e1d"}, - {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18589e747c1e70b60fab6767ff99b2d0c359ea1db8a2cb524477f93cdbedf5b"}, - {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d30f3fee9372796f54d3100b31ee70972eaadcc87314be369360248a3dcffe"}, - {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7e0a3e72c0e9a1acab77bef14a73a416630b7fd2cbd893c0a873edc47c42c8cd"}, - {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a831dcc343440969aaa812004685ed322cdb526cd197112d0db303b0da1e8659"}, - {file = "grpcio-1.68.0-cp312-cp312-win32.whl", hash = "sha256:5a180328e92b9a0050958ced34dddcb86fec5a8b332f5a229e353dafc16cd332"}, - {file = "grpcio-1.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bddd04a790b69f7a7385f6a112f46ea0b34c4746f361ebafe9ca0be567c78e9"}, - {file = "grpcio-1.68.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:fc05759ffbd7875e0ff2bd877be1438dfe97c9312bbc558c8284a9afa1d0f40e"}, - {file = "grpcio-1.68.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15fa1fe25d365a13bc6d52fcac0e3ee1f9baebdde2c9b3b2425f8a4979fccea1"}, - {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:32a9cb4686eb2e89d97022ecb9e1606d132f85c444354c17a7dbde4a455e4a3b"}, - {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dba037ff8d284c8e7ea9a510c8ae0f5b016004f13c3648f72411c464b67ff2fb"}, - {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0efbbd849867e0e569af09e165363ade75cf84f5229b2698d53cf22c7a4f9e21"}, - {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:4e300e6978df0b65cc2d100c54e097c10dfc7018b9bd890bbbf08022d47f766d"}, - {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:6f9c7ad1a23e1047f827385f4713b5b8c6c7d325705be1dd3e31fb00dcb2f665"}, - {file = "grpcio-1.68.0-cp313-cp313-win32.whl", hash = "sha256:3ac7f10850fd0487fcce169c3c55509101c3bde2a3b454869639df2176b60a03"}, - {file = "grpcio-1.68.0-cp313-cp313-win_amd64.whl", hash = "sha256:afbf45a62ba85a720491bfe9b2642f8761ff348006f5ef67e4622621f116b04a"}, - {file = "grpcio-1.68.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:f8f695d9576ce836eab27ba7401c60acaf9ef6cf2f70dfe5462055ba3df02cc3"}, - {file = "grpcio-1.68.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9fe1b141cda52f2ca73e17d2d3c6a9f3f3a0c255c216b50ce616e9dca7e3441d"}, - {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:4df81d78fd1646bf94ced4fb4cd0a7fe2e91608089c522ef17bc7db26e64effd"}, - {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46a2d74d4dd8993151c6cd585594c082abe74112c8e4175ddda4106f2ceb022f"}, - {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17278d977746472698460c63abf333e1d806bd41f2224f90dbe9460101c9796"}, - {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15377bce516b1c861c35e18eaa1c280692bf563264836cece693c0f169b48829"}, - {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc5f0a4f5904b8c25729a0498886b797feb817d1fd3812554ffa39551112c161"}, - {file = "grpcio-1.68.0-cp38-cp38-win32.whl", hash = "sha256:def1a60a111d24376e4b753db39705adbe9483ef4ca4761f825639d884d5da78"}, - {file = "grpcio-1.68.0-cp38-cp38-win_amd64.whl", hash = "sha256:55d3b52fd41ec5772a953612db4e70ae741a6d6ed640c4c89a64f017a1ac02b5"}, - {file = "grpcio-1.68.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0d230852ba97654453d290e98d6aa61cb48fa5fafb474fb4c4298d8721809354"}, - {file = "grpcio-1.68.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:50992f214264e207e07222703c17d9cfdcc2c46ed5a1ea86843d440148ebbe10"}, - {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:14331e5c27ed3545360464a139ed279aa09db088f6e9502e95ad4bfa852bb116"}, - {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f84890b205692ea813653ece4ac9afa2139eae136e419231b0eec7c39fdbe4c2"}, - {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cf343c6f4f6aa44863e13ec9ddfe299e0be68f87d68e777328bff785897b05"}, - {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd2c2d47969daa0e27eadaf15c13b5e92605c5e5953d23c06d0b5239a2f176d3"}, - {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:18668e36e7f4045820f069997834e94e8275910b1f03e078a6020bd464cb2363"}, - {file = "grpcio-1.68.0-cp39-cp39-win32.whl", hash = "sha256:2af76ab7c427aaa26aa9187c3e3c42f38d3771f91a20f99657d992afada2294a"}, - {file = "grpcio-1.68.0-cp39-cp39-win_amd64.whl", hash = "sha256:e694b5928b7b33ca2d3b4d5f9bf8b5888906f181daff6b406f4938f3a997a490"}, - {file = "grpcio-1.68.0.tar.gz", hash = "sha256:7e7483d39b4a4fddb9906671e9ea21aaad4f031cdfc349fec76bdfa1e404543a"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.68.0)"] + {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, + {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"}, + {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"}, + {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"}, + {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"}, + {file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"}, + {file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"}, + {file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"}, + {file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"}, + {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"}, + {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"}, + {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"}, + {file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"}, + {file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"}, + {file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"}, + {file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"}, + {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"}, + {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"}, + {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"}, + {file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"}, + {file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"}, + {file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"}, + {file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"}, + {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"}, + {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"}, + {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"}, + {file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"}, + {file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"}, + {file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"}, + {file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"}, + {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"}, + {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"}, + {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"}, + {file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"}, + {file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"}, + {file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"}, + {file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"}, + {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"}, + {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"}, + {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"}, + {file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"}, + {file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"}, + {file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.69.0)"] [[package]] name = "grpcio-status" @@ -1877,13 +1919,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.23.1" +version = "3.25.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" files = [ - {file = "marshmallow-3.23.1-py3-none-any.whl", hash = "sha256:fece2eb2c941180ea1b7fcbd4a83c51bfdd50093fdd3ad2585ee5e1df2508491"}, - {file = "marshmallow-3.23.1.tar.gz", hash = "sha256:3a8dfda6edd8dcdbf216c0ede1d1e78d230a6dc9c5a088f58c4083b974a0d468"}, + {file = "marshmallow-3.25.1-py3-none-any.whl", hash = "sha256:ec5d00d873ce473b7f2ffcb7104286a376c354cab0c2fa12f5573dab03e87210"}, + {file = "marshmallow-3.25.1.tar.gz", hash = "sha256:f4debda3bb11153d81ac34b0d582bf23053055ee11e791b54b4b35493468040a"}, ] [package.dependencies] @@ -1891,7 +1933,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["autodocsumm (==0.2.14)", "furo (==2024.8.6)", "sphinx (==8.1.3)", "sphinx-copybutton (==0.5.2)", "sphinx-issues (==5.0.0)", "sphinxext-opengraph (==0.9.1)"] tests = ["pytest", "simplejson"] [[package]] @@ -2575,13 +2617,13 @@ files = [ [[package]] name = "redis" -version = "5.2.0" +version = "5.2.1" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" files = [ - {file = "redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897"}, - {file = "redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0"}, + {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, + {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, ] [package.extras] @@ -3291,13 +3333,31 @@ files = [ [[package]] name = "ua-parser" -version = "0.18.0" +version = "1.0.0" description = "Python port of Browserscope's user agent parser" optional = false -python-versions = "*" +python-versions = ">=3.9" +files = [ + {file = "ua_parser-1.0.0-py3-none-any.whl", hash = "sha256:5b31133606a781f56692caa11a9671a9f330c22604b3c4957a7ba18c152212d0"}, + {file = "ua_parser-1.0.0.tar.gz", hash = "sha256:a9740f53f4fbb72b7a03d304cae32a2785cafc55e8207efb74877bba17c35324"}, +] + +[package.dependencies] +ua-parser-builtins = "*" + +[package.extras] +re2 = ["google-re2"] +regex = ["ua-parser-rs"] +yaml = ["PyYaml"] + +[[package]] +name = "ua-parser-builtins" +version = "0.18.0.post1" +description = "Precompiled rules for User Agent Parser" +optional = false +python-versions = ">=3.9" files = [ - {file = "ua-parser-0.18.0.tar.gz", hash = "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e"}, - {file = "ua_parser-0.18.0-py2.py3-none-any.whl", hash = "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950"}, + {file = "ua_parser_builtins-0.18.0.post1-py3-none-any.whl", hash = "sha256:eb4f93504040c3a990a6b0742a2afd540d87d7f9f05fd66e94c101db1564674d"}, ] [[package]] @@ -3652,4 +3712,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12.6" -content-hash = "5143ceb091adda9bad1b5a94c2540fb27d9406ecfb3577a9b7d629e5859aaca7" +content-hash = "b25039e64c33fd57071ca27fbe03ae97e6e53bcda5c8dc57b62c67fb0eaab21b" diff --git a/pyproject.toml b/pyproject.toml index 0e316e72d9..1bdbe5e225 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ pyyaml = "^6.0.1" requests = "^2.32.0" sdc-cryptography = "^1.2.1" structlog = "^24.1.0" -ua-parser = "^0.18.0" +ua-parser = "^1.0.0" blinker = "^1.7.0" boto3 = "^1.34.151" humanize = "^4.9.0" From ae32a003845def373359cc7c859c787f74a16d85 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 16 Jan 2025 07:14:50 +0000 Subject: [PATCH 497/567] Schemas v5.22.0 (#1581) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5e866c06a7..849b11e163 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.21.0 +v5.22.0 From 76f3c9d96a48ab89d6f1b3308d64ff140042a744 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:15:55 +0000 Subject: [PATCH 498/567] Remove the X-XSS code and fix test (#1578) --- app/setup.py | 1 - tests/integration/test_app_create.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/setup.py b/app/setup.py index 6acbe1adc0..bfe130b40d 100644 --- a/app/setup.py +++ b/app/setup.py @@ -284,7 +284,6 @@ def setup_secure_headers(application): strict_transport_security=True, strict_transport_security_max_age=31536000, frame_options="DENY", - x_xss_protection=True, ) diff --git a/tests/integration/test_app_create.py b/tests/integration/test_app_create.py index 02aa110bdc..4fd52d4f52 100644 --- a/tests/integration/test_app_create.py +++ b/tests/integration/test_app_create.py @@ -116,8 +116,8 @@ def test_enforces_secure_headers(self): headers["Strict-Transport-Security"], ) self.assertEqual("DENY", headers["X-Frame-Options"]) - self.assertEqual("1; mode=block", headers["X-Xss-Protection"]) self.assertEqual("nosniff", headers["X-Content-Type-Options"]) + self.assertNotIn("X-XSS-Protection", headers) def test_csp_policy_headers(self): cdn_url = "https://cdn.test.domain" From 25d5b3d90430e6e63b939293ee128d156609ab20 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:01:58 +0000 Subject: [PATCH 499/567] Upgrade to ruff v0.9.1 (#1580) --- app/data_models/progress.py | 8 +- app/helpers/__init__.py | 2 +- app/publisher/__init__.py | 2 +- app/questionnaire/__init__.py | 2 +- app/submitter/__init__.py | 4 +- app/survey_config/__init__.py | 14 +- app/views/contexts/__init__.py | 4 +- poetry.lock | 1481 ++++++++++++++++---------------- pyproject.toml | 3 +- 9 files changed, 750 insertions(+), 770 deletions(-) diff --git a/app/data_models/progress.py b/app/data_models/progress.py index 7983ea57cb..21e641da20 100644 --- a/app/data_models/progress.py +++ b/app/data_models/progress.py @@ -6,10 +6,10 @@ class CompletionStatus(StrEnum): - COMPLETED: str = "COMPLETED" - IN_PROGRESS: str = "IN_PROGRESS" - NOT_STARTED: str = "NOT_STARTED" - INDIVIDUAL_RESPONSE_REQUESTED: str = "INDIVIDUAL_RESPONSE_REQUESTED" + COMPLETED = "COMPLETED" + IN_PROGRESS = "IN_PROGRESS" + NOT_STARTED = "NOT_STARTED" + INDIVIDUAL_RESPONSE_REQUESTED = "INDIVIDUAL_RESPONSE_REQUESTED" class ProgressDict(TypedDict, total=False): diff --git a/app/helpers/__init__.py b/app/helpers/__init__.py index 560a62b61c..e5b58295ec 100644 --- a/app/helpers/__init__.py +++ b/app/helpers/__init__.py @@ -3,7 +3,7 @@ from app.helpers.url_safe_serializer import url_safe_serializer __all__ = [ + "get_address_lookup_api_auth_token", "get_span_and_trace", "url_safe_serializer", - "get_address_lookup_api_auth_token", ] diff --git a/app/publisher/__init__.py b/app/publisher/__init__.py index 78e63544d1..bdd69ec62f 100644 --- a/app/publisher/__init__.py +++ b/app/publisher/__init__.py @@ -1,3 +1,3 @@ from app.publisher.publisher import LogPublisher, PubSubPublisher -__all__ = ["PubSubPublisher", "LogPublisher"] +__all__ = ["LogPublisher", "PubSubPublisher"] diff --git a/app/questionnaire/__init__.py b/app/questionnaire/__init__.py index 40eeb20605..53980d0a57 100644 --- a/app/questionnaire/__init__.py +++ b/app/questionnaire/__init__.py @@ -4,4 +4,4 @@ QuestionSchemaType, ) -__all__ = ["QuestionnaireSchema", "Location", "QuestionSchemaType"] +__all__ = ["Location", "QuestionSchemaType", "QuestionnaireSchema"] diff --git a/app/submitter/__init__.py b/app/submitter/__init__.py index ea3b9d76a8..faa6996e15 100644 --- a/app/submitter/__init__.py +++ b/app/submitter/__init__.py @@ -7,9 +7,9 @@ ) __all__ = [ + "GCSFeedbackSubmitter", "GCSSubmitter", + "LogFeedbackSubmitter", "LogSubmitter", "RabbitMQSubmitter", - "GCSFeedbackSubmitter", - "LogFeedbackSubmitter", ] diff --git a/app/survey_config/__init__.py b/app/survey_config/__init__.py index 6cfa6e8f3a..5246a21eb7 100644 --- a/app/survey_config/__init__.py +++ b/app/survey_config/__init__.py @@ -18,18 +18,18 @@ from app.survey_config.survey_config import SurveyConfig __all__ = [ - "ONSNHSSocialSurveyConfig", - "SocialSurveyConfig", - "UKHSAONSSocialSurveyConfig", - "SurveyConfig", "BusinessSurveyConfig", - "NIBusinessSurveyConfig", "DBTBusinessSurveyConfig", - "DBTNIBusinessSurveyConfig", "DBTDSITBusinessSurveyConfig", "DBTDSITNIBusinessSurveyConfig", - "ORRBusinessSurveyConfig", + "DBTNIBusinessSurveyConfig", "DESNZBusinessSurveyConfig", "DESNZNIBusinessSurveyConfig", "Link", + "NIBusinessSurveyConfig", + "ONSNHSSocialSurveyConfig", + "ORRBusinessSurveyConfig", + "SocialSurveyConfig", + "SurveyConfig", + "UKHSAONSSocialSurveyConfig", ] diff --git a/app/views/contexts/__init__.py b/app/views/contexts/__init__.py index e6fbda5740..3233bec3e7 100644 --- a/app/views/contexts/__init__.py +++ b/app/views/contexts/__init__.py @@ -10,10 +10,10 @@ __all__ = [ "CalculatedSummaryContext", - "GrandCalculatedSummaryContext", "Context", - "SubmitQuestionnaireContext", + "GrandCalculatedSummaryContext", "HubContext", "ListContext", "SectionSummaryContext", + "SubmitQuestionnaireContext", ] diff --git a/poetry.lock b/poetry.lock index d82bf2697a..79f15924ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,31 +1,31 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astroid" -version = "3.3.5" +version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" files = [ - {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, - {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, + {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, + {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, ] [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, + {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, + {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] @@ -122,17 +122,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.97" +version = "1.35.98" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.97-py3-none-any.whl", hash = "sha256:8e49416216a6e3a62c2a0c44fba4dd2852c85472e7b702516605b1363867d220"}, - {file = "boto3-1.35.97.tar.gz", hash = "sha256:7d398f66a11e67777c189d1f58c0a75d9d60f98d0ee51b8817e828930bf19e4e"}, + {file = "boto3-1.35.98-py3-none-any.whl", hash = "sha256:d0224e1499d7189b47aa7f469d96522d98df6f5702fccb20a95a436582ebcd9d"}, + {file = "boto3-1.35.98.tar.gz", hash = "sha256:4b6274b4fe9d7113f978abea66a1f20c8a397c268c9d1b2a6c96b14a256da4a5"}, ] [package.dependencies] -botocore = ">=1.35.97,<1.36.0" +botocore = ">=1.35.98,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -141,13 +141,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.97" +version = "1.35.98" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.97-py3-none-any.whl", hash = "sha256:fed4f156b1a9b8ece53738f702ba5851b8c6216b4952de326547f349cc494f14"}, - {file = "botocore-1.35.97.tar.gz", hash = "sha256:88f2fab29192ffe2f2115d5bafbbd823ff4b6eb2774296e03ec8b5b0fe074f61"}, + {file = "botocore-1.35.98-py3-none-any.whl", hash = "sha256:4f1c0b687488663a774ad3a5e81a5f94fae1bcada2364cfdc48482c4dbf794d5"}, + {file = "botocore-1.35.98.tar.gz", hash = "sha256:d11742b3824bdeac3c89eeeaf5132351af41823bbcef8fc15e95c8250b1de09c"}, ] [package.dependencies] @@ -175,10 +175,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -191,14 +187,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -209,24 +199,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -236,10 +210,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -251,10 +221,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -267,10 +233,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -283,10 +245,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -344,13 +302,13 @@ files = [ [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -434,127 +392,114 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, - {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, - {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, +python-versions = ">=3.7" +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, + {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, ] [package.dependencies] @@ -590,73 +535,73 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.6.8" +version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, - {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, - {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, - {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, - {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, - {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, - {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, - {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, - {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, - {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, - {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, - {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, - {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, - {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, - {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, - {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, + {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, + {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, + {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, + {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, + {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, + {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, + {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, + {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, + {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, + {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, + {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, + {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, + {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, + {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, ] [package.extras] @@ -664,101 +609,101 @@ toml = ["tomli"] [[package]] name = "cramjam" -version = "2.9.0" +version = "2.9.1" description = "Thin Python bindings to de/compression algorithms in Rust" optional = false python-versions = ">=3.8" files = [ - {file = "cramjam-2.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eb16d995e454b0155b166f6e6da7df4ac812d44e0f3b6dc0f344a934609fd5bc"}, - {file = "cramjam-2.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb1e86bfea656b51f2e75f2cedb17fc08b552d105b814d19b595294ecbe94d8d"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4bd76b654275736fd4f55521981b73751c34dacf70a1dbce96e454a39d43201f"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21569f19d5848606b85ac0dde0dc3639319d26fed8522c7103515df875bcb300"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f8b1117b4e697d39950ecab01700ce0aef66541e4478eb4d7b3ade8703347b"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3464d0042a03e8ef38a2b774ef23163cf3c0cdc41b8dfbf7c4aadf93e40b459"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0711c776750e243ae347d6609c975f0ff4be9ae65b2764d29e4bbdad8e574c3a"}, - {file = "cramjam-2.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00d96f798bc980b29f8e1c3ed7d554050e05d4cde23d1633ffed4cd63110024a"}, - {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fc49b6575e3cb15da3180c5a3926ec81db33b109e48530708da76614b306904b"}, - {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:c4fa6c23e56d48df18f534af921ec936c812743a8972ecdd5e5ff47b464fea00"}, - {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b4b8d8160685c11ffb4e8e6daaab79cb351a1c54ceec41cc18a0a62c89309fe0"}, - {file = "cramjam-2.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0ed6362cb6c964f8d0c6e7f790e8961b9242cd3acd87c56169ca14d642653707"}, - {file = "cramjam-2.9.0-cp310-none-win32.whl", hash = "sha256:fe9af350dfbdc7ed4c93a8016a8ad7b5492fc116e7197cad7cbce99b434d3fe1"}, - {file = "cramjam-2.9.0-cp310-none-win_amd64.whl", hash = "sha256:37054c73704a3183b60869e7fec1614648752c31d89f44de1ffe1f01ad4d20d5"}, - {file = "cramjam-2.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:170a50407f9400073621cc1d5f3200ca3ad9de3000831e3e86f5561ca8048a08"}, - {file = "cramjam-2.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:912c94781c8ff318a4d3f3306f8d94d41ae5aa7b9760c4bb0476b01142084845"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df089639983a03070be6eabc60317aa1ffbf2c5409023b57a5fc2e4975163bc4"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ca28a8f6ab5fca35f163fd7d7a970880ce4fc1a0bead1249ecdaa96ec9ac1f4"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abd8bf9a94e3866215ac181a7dbcfa1ddbedca4f8048494a79934febe88537df"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7de19a382bcab93cd4d028d51f6f581920a3b79659a384775188135b7fc64f15"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4156fcefa1dfaa65d35ff82c252d1e32be12820f26d04748be6cd3b461cf85f"}, - {file = "cramjam-2.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4a3104022129d7463100dfaf12efd398ebfa4b7e4e50832ccc596754f7c26df"}, - {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ebee5f5d7e2b9277895ea4fd94646b72075fe9cfc0e8f4770b65c9e72b1fec1"}, - {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:8e33ebe4d709b21bc15e7ddf485ac6b30d7fdc7ed7c3c65130654c007f50c183"}, - {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d5a39118008bb9f2fba36a0ceea6c41fbd0b55d2647b043ba51a868e5f6de92"}, - {file = "cramjam-2.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f6ef35eba883927af2678b561cc4407e0b3b0d58a251c863bec4b3d8258cc2f"}, - {file = "cramjam-2.9.0-cp311-none-win32.whl", hash = "sha256:b21e55b5cfdaff96eae1f323ae9a0d36e86852cdf62fe23b60a2481d2fed5571"}, - {file = "cramjam-2.9.0-cp311-none-win_amd64.whl", hash = "sha256:9f685fe4e49b2f3e233548e3397b3f9189d71a265718ec631d13eca3d5718ddb"}, - {file = "cramjam-2.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:34578e4c1518b10dad5e0ba40c721e529ef13e7742a528843b40e1f20dd6078c"}, - {file = "cramjam-2.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d5b5512dc61ea78f32e021e88a5fd5b46a821409479e6657d33614fc9e45677"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b4f1b5e33915ed591c0c19b8c3bbdd7aa0f6a9bfe2b7246b475d497bda15f18"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad301801afa0eecdacabf353a2802df5e6770f9bfb0a559d6c069813d83cfd42"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:399baf80fea574e3870f233e12e6a12f02c53b054e13d792348b272b0614370a"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3121e2fbec58907fa70636adaeaf30c27614c867e08a7a5bd2887b33786ff790"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd04205b2a87087ffc2257c3ad33f11daabc053956f64ac1ec7bae299cac3f2f"}, - {file = "cramjam-2.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb9c4db36188a8f08c2303100a83100f26a8572803ae35eadff359bebd3d204"}, - {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ef553d4080368006817c1a935ed619c71987cf10417a32386acc00c5418a2934"}, - {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:9862ca8ead80857ecfb9b07f02f577733261e981346f31585fe118975eabb738"}, - {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4714e1ea0c3329368b83fe5ad6e831d5ca11fb794ca7cf491622eb6b2d420d2f"}, - {file = "cramjam-2.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4ca30c9f27e3b88bc082d4637e7648f93da5cb69a2dbe0c0300bc51353c820"}, - {file = "cramjam-2.9.0-cp312-none-win32.whl", hash = "sha256:0ed2fef010d1caca9ea63814e9cb5b1d47d907b80302b8cc0b3a1e116ea241e2"}, - {file = "cramjam-2.9.0-cp312-none-win_amd64.whl", hash = "sha256:bd26d71939de5dcf169d479fbc7fcfed21e6675bab33e7f7e9f8405f19711c71"}, - {file = "cramjam-2.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:dd70ea5d7b2c5e479e04ac3a00d8bc3deca146d2b5dbfbe3d7b42ed136e19de4"}, - {file = "cramjam-2.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1410e68c464666473a89cade17483b94bb4639d9161c440ee54ee1e0eca583"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b0078727fe8c28ef1695e5d04aae5c41ac697eb087cba387c6a02b825f9071c0"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a63c4e63319bf7dfc3ab46c06afb76d3d9cc1c94369b609dde480e5cc78e4de"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47d7253b5a10c201cc65aecfb517dfa1c0b5831b2524ac32dd2964fceafc0dc4"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05970fb640f236767003e62c256a085754536169bac863f4a3502ecb59cbf197"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0b062d261fa3fac00146cf801896c8cfafe1e41332eb047aa0a36558299daa6"}, - {file = "cramjam-2.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017b7066f18b7b676068f51b1dbdecc02d76d9af10092252b22dcbd03a78ed33"}, - {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9de33ef3bc006c11fbad1dc8b15341dcc78430df2c5ce1e790dfb729b11ab593"}, - {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:b99efaf81be8e381de1cde6574e2c89030ed53994e73b0e75b62d6e232f491c5"}, - {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:36426e3f1920f6aa4c644d007bf9cfad06dd9f1a30cd0a921d72b010492d8447"}, - {file = "cramjam-2.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea9bcaff298f5d35ef67346d474fca388c5cf6d4edab1d06b84868800f88bd36"}, - {file = "cramjam-2.9.0-cp313-none-win32.whl", hash = "sha256:c48da60a5eb481b412e5e462b81ad307fb2203178a2840a743f0a7c5fc1718c9"}, - {file = "cramjam-2.9.0-cp313-none-win_amd64.whl", hash = "sha256:97a6311bd32f301ff1b922bc9de62ace3d9fd845e20efc0f71b4d0239a45b8d2"}, - {file = "cramjam-2.9.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:78e7349f945a83bc48855fb042873092a69b155a088b8c11942eb76418b32705"}, - {file = "cramjam-2.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:65a097ea765dd4ef2fb868b5b0959d7c93a64c250b2c52f462898c823ae4b950"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:35cad507eb02c775e6c5444312f98b28dd8bf122425677ae199484996e838673"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8982925d179b940efa860513a31b839bb06343501077cca3e67f7a2f7360d355"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba7e2d33e1d092dffd0a3ff4bd1b86177594aa3c2901fd478e78e1fb2aee8ed3"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:904be92e3bc25e78343ee52aa0fd5fba3a31d11d474e8af4623a9d00baa84bc2"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9221297c547d702e1431e96705fce26c6a87df34a681a6b97fe63b536d09c1d8"}, - {file = "cramjam-2.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98a18c22a85f321091cc8db6694af1d713a369c2d60ec611c10ccfe24ab103a"}, - {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e248510f8e2dbc71fa99f86238c9023365dbe1a4520eb40e33d73416527349f2"}, - {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:dc07376aa33b6004ea372ac9b0ba0ed3455aa2fc4e18727414142ecb46b176b8"}, - {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e94021c541eb2a199b5a2ffae0ea84fb8b99863dab99a5b154b00bc7a44b5c48"}, - {file = "cramjam-2.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4adbf4366f8dc29b7c5c731c800cf633be76c9911e928daeb606827d6ae7c599"}, - {file = "cramjam-2.9.0-cp38-none-win32.whl", hash = "sha256:ca880f555c8db40942acc8a50722c33e229b6be90e598acc1a201f36487b917d"}, - {file = "cramjam-2.9.0-cp38-none-win_amd64.whl", hash = "sha256:ab17a429a92db90bf40115efb97d10e71b94b0dcacf30cf724552df2794a58fb"}, - {file = "cramjam-2.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ed7fd7bc2b86ec3161fe0cc49f5f392e6efa55c91a95397d5047820c38117660"}, - {file = "cramjam-2.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a0f654c739a6bc4a69a2aaf31463328a208757ed780ff886234532f78e06a864"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cd4d4ab9deb5846af0ac6cf1fa139cfa40291ad14d073efa8b8e20c8d1aa90bd"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bafc32f01d4ab64f83fdbc29bc5bd25a920b59c751c12e06e6f4b1e379be7600"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0fb5ea631dbf998f667766a9e485e757817d66ed559916ba553a0ec2f902d788"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c902e56e60c48f5f15e55257aaa1c2678323df5f18a1b839e8d05cac1107576c"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:441d3875cdffe5df9294b93ef570058837732dd727cd9d18efa0f089f1c2687a"}, - {file = "cramjam-2.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed486e57a79ccc7aebaa2ec12517d891fdc5d2fde16915e3db705b8a47570981"}, - {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:013cb872205641c6e5269f530ed40aaaa5640d84e0d8f33b89f5a1bf7f655527"}, - {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:a41b4b10a381be1d42a1a7dd07b8c3faccd3d12c7e98e973a6ec558fd040a607"}, - {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598eac1713ddbe69c3b30dcc890d69b206ce08903fc3aed58149aae87c61973a"}, - {file = "cramjam-2.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72e9ebc27c557706a3c9964c1d1b4522857760dbd60c105a4f5421f3b66e31a2"}, - {file = "cramjam-2.9.0-cp39-none-win32.whl", hash = "sha256:dbbd6fba677e1cbc9d6bd4ebbe3e8b3667d0295f1731489db2a971c95f0ceca0"}, - {file = "cramjam-2.9.0-cp39-none-win_amd64.whl", hash = "sha256:7f33a83969fa94ee8e0c1f0aef8eb303ead3e9142338dc543abeb7e1a28734ab"}, - {file = "cramjam-2.9.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:132db7d3346ea21ba44e7ee23ec73bd6fa9eb1e77133ca6dfe1f7449a69999af"}, - {file = "cramjam-2.9.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2addf801c88bead21256ccd87dc97cffead03758c4a4947fad8e454f4abfda0a"}, - {file = "cramjam-2.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24afad3ba62774abbb150dc25aab21b047ab999c4143c7a8d96577848baf7af6"}, - {file = "cramjam-2.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:604c16052cf29d0c796927ed7e107f65429d2036c82c9a8009bd453c94e5e4f0"}, - {file = "cramjam-2.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:65bded20fd2cef17b22246c336ddd67fac842341ee311042b4a70e65dc745aa7"}, - {file = "cramjam-2.9.0.tar.gz", hash = "sha256:f103e648aa3ebe9b8e2c1a3a92719288d8f3f41007c319ad298cdce2d0c28641"}, + {file = "cramjam-2.9.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8e82464d1e00fbbb12958999b8471ba5e9f3d9711954505a0a7b378762332e6f"}, + {file = "cramjam-2.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d2df8a6511cc08ef1fccd2e0c65e2ebc9f57574ec8376052a76851af5398810"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:21ea784e6c3f1843d3523ae0f03651dd06058b39eeb64beb82ee3b100fa83662"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0c5d98a4e791f0bbd0ffcb7dae879baeb2dcc357348a8dc2be0a8c10403a2a"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e076fd87089197cb61117c63dbe7712ad5eccb93968860eb3bae09b767bac813"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d86b44933aea0151e4a2e1e6935448499849045c38167d288ca4c59d5b8cd4e"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb032549dec897b942ddcf80c1cdccbcb40629f15fc902731dbe6362da49326"}, + {file = "cramjam-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf29b4def86ec503e329fe138842a9b79a997e3beb6c7809b05665a0d291edff"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a36adf7d13b7accfa206e1c917f08924eb905b45aa8e62176509afa7b14db71e"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:cf4ea758d98b6fad1b4b2d808d0de690d3162ac56c26968aea0af6524e3eb736"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4826d6d81ea490fa7a3ae7a4b9729866a945ffac1f77fe57b71e49d6e1b21efd"}, + {file = "cramjam-2.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:335103317475bf992953c58838152a4761fc3c87354000edbfc4d7e57cf05909"}, + {file = "cramjam-2.9.1-cp310-cp310-win32.whl", hash = "sha256:258120cb1e3afc3443f756f9de161ed63eed56a2c31f6093e81c571c0f2dc9f6"}, + {file = "cramjam-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c60e5996aa02547d12bc2740d44e90e006b0f93100f53206f7abe6732ad56e69"}, + {file = "cramjam-2.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9db1debe48060e41a5b91af9193c524e473c57f6105462c5524a41f5aabdb88"}, + {file = "cramjam-2.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f6f18f0242212d3409d26ce3874937b5b979cebd61f08b633a6ea893c32fc7b6"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b5b1cd7d39242b2b903cf09cd4696b3a6e04dc537ffa9f3ac8668edae76eecb6"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47de0a68f5f4d9951250ef5af31f2a7228132caa9ed60994234f7eb98090d33"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13c9a697881e5e38148958612dc6856967f5ff8cd7bba5ff751f2d6ac020aa4"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba560244bc1335b420b74e91e35f9d4e7f307a3be3a4603ce0f0d7e15a0acdf0"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d47fd41ce260cf4f0ff0e788de961fab9e9c6844a05ce55d06ce31e06107bdc"}, + {file = "cramjam-2.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84d154fbadece82935396eb6bcb502085d944d2fd13b07a94348364344370c2c"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:038df668ffb94d64d67b6ecc59cbd206745a425ffc0402897dde12d89fa6a870"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:4125d8cd86fa08495d310e80926c2f0563f157b76862e7479f9b2cf94823ea0c"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4206ebdd1d1ef0f3f86c8c2f7c426aa4af6094f4f41e274601fd4c4569f37454"}, + {file = "cramjam-2.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab687bef5c493732b9a4ab870542ee43f5eae0025f9c684c7cb399c3a85cb380"}, + {file = "cramjam-2.9.1-cp311-cp311-win32.whl", hash = "sha256:dda7698b6d7caeae1047adafebc4b43b2a82478234f6c2b45bc3edad854e0600"}, + {file = "cramjam-2.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:872b00ff83e84bcbdc7e951af291ebe65eed20b09c47e7c4af21c312f90b796f"}, + {file = "cramjam-2.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:79417957972553502b217a0093532e48893c8b4ca30ccc941cefe9c72379df7c"}, + {file = "cramjam-2.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce2b94117f373defc876f88e74e44049a9969223dbca3240415b71752d0422fb"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:67040e0fd84404885ec716a806bee6110f9960c3647e0ef1670aab3b7375a70a"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bedb84e068b53c944bd08dcb501fd00d67daa8a917922356dd559b484ce7eab"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:06e3f97a379386d97debf08638a78b3d3850fdf6124755eb270b54905a169930"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11118675e9c7952ececabc62f023290ee4f8ecf0bee0d2c7eb8d1c402ee9769d"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b7de6b61b11545570e4d6033713f3599525efc615ee353a822be8f6b0c65b77"}, + {file = "cramjam-2.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57ca8f3775324a9de3ee6f05ca172687ba258c0dea79f7e3a6b4112834982f2a"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9847dd6f288f1c56359f52acb48ff2df848ff3e3bff34d23855bbcf7016427cc"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d1248dfa7f151e893ce819670f00879e4b7650b8d4c01279ce4f12140d68dd2"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9da6d970281083bae91b914362de325414aa03c01fc806f6bb2cc006322ec834"}, + {file = "cramjam-2.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c33bc095db5733c841a102b8693062be5db8cdac17b9782ebc00577c6a94480"}, + {file = "cramjam-2.9.1-cp312-cp312-win32.whl", hash = "sha256:9e9193cd4bb57e7acd3af24891526299244bfed88168945efdaa09af4e50720f"}, + {file = "cramjam-2.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:15955dd75e80f66c1ea271167a5347661d9bdc365f894a57698c383c9b7d465c"}, + {file = "cramjam-2.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5a7797a2fff994fc5e323f7a967a35a3e37e3006ed21d64dcded086502f482af"}, + {file = "cramjam-2.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d51b9b140b1df39a44bff7896d98a10da345b7d5f5ce92368d328c1c2c829167"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:07ac76b7f992556e7aa910244be11ece578cdf84f4d5d5297461f9a895e18312"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d90a72608c7550cd7eba914668f6277bfb0b24f074d1f1bd9d061fcb6f2adbd6"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56495975401b1821dbe1f29cf222e23556232209a2fdb809fe8156d120ca9c7f"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b695259e71fde6d5be66b77a4474523ced9ffe9fe8a34cb9b520ec1241a14d3"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab1e69dc4831bbb79b6d547077aae89074c83e8ad94eba1a3d80e94d2424fd02"}, + {file = "cramjam-2.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440b489902bfb7a26d3fec1ca888007615336ff763d2a32a2fc40586548a0dbf"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:217fe22b41f8c3dce03852f828b059abfad11d1344a1df2f43d3eb8634b18d75"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:95f3646ddc98af25af25d5692ae65966488a283813336ea9cf41b22e542e7c0d"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:6b19fc60ead1cae9795a5b359599da3a1c95d38f869bdfb51c441fd76b04e926"}, + {file = "cramjam-2.9.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8dc5207567459d049696f62a1fdfb220f3fe6aa0d722285d44753e12504dac6c"}, + {file = "cramjam-2.9.1-cp313-cp313-win32.whl", hash = "sha256:fbfe35929a61b914de9e5dbacde0cfbba86cbf5122f9285a24c14ed0b645490b"}, + {file = "cramjam-2.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:06068bd191a82ad4fc1ac23d6f8627fb5e37ec4be0431711b9a2dbacaccfeddb"}, + {file = "cramjam-2.9.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a2ca4d3c683d28d3217821029eb08d3487d5043d7eb455df11ff3cacfd4c916"}, + {file = "cramjam-2.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:008b49b455b396acc5459dfb06fb9d56049c4097ee8e590892a4d3da9a711da3"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45c18cc13156e8697a8d3f9e57e49a69b00e14a103196efab0893fae1a5257f8"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d14a0efb21e0fec0631bcd66040b06e6a0fe10825f3aacffded38c1c978bdff9"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f815fb0eba625af45139af4f90f5fc2ddda61b171c2cc3ab63d44b40c5c7768"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04828cbfad7384f06a4a7d0d927c3e85ef11dc5a40b9cf5f3e29ac4e23ecd678"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0944a7c3a78f940c06d1b29bdce91a17798d80593dd01ebfeb842761e48a8b5"}, + {file = "cramjam-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec769e5b16251704502277a1163dcf2611551452d7590ff4cc422b7b0367fc96"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ba79c7d2cc5adb897b690c05dd9b67c4d401736d207314b99315f7be3cd94fd"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d35923fb5411bde30b53c0696dff8e24c8a38b010b89544834c53f4462fd71df"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:da0cc0efdbfb8ee2361f89f38ded03d11678f37e392afff7a97b09c55dadfc83"}, + {file = "cramjam-2.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f89924858712b8b936f04f3d690e72825a3e5127a140b434c79030c1c5a887ce"}, + {file = "cramjam-2.9.1-cp38-cp38-win32.whl", hash = "sha256:5925a738b8478f223ab9756fc794e3cabd5917fd7846f66adcf1d5fc2bf9864c"}, + {file = "cramjam-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:b7ac273498a2c6772d67707e101b74014c0d9413bb4711c51d8ec311de59b4b1"}, + {file = "cramjam-2.9.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:af39006faddfc6253beb93ca821d544931cfee7f0177b99ff106dfd8fd6a2cd8"}, + {file = "cramjam-2.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3291be0d3f73d5774d69013be4ab33978c777363b5312d14f62f77817c2f75a"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1539fd758f0e57fad7913cebff8baaee871bb561ddf6fa710a427b74da6b6778"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff362f68bd68ac0eccb445209238d589bba728fb6d7f2e9dc199e0ec3a61d6e0"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23b9786d1d17686fb8d600ade2a19374c7188d4b8867efa9af0d8274a220aec7"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bc9c2c748aaf91863d89c4583f529c1c709485c94f8dfeb3ee48662d88e3258"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd0fa9a0e7f18224b6d2d1d69dbdc3aecec80ef1393c59244159b131604a4395"}, + {file = "cramjam-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ceef6e09ee22457997370882aa3c69de01e6dd0aaa2f953e1e87ad11641d042"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1376f6fdbf0b30712413a0b4e51663a4938ae2f6b449f8e4635dbb3694db83cf"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:342fb946f8d3e9e35b837288b03ab23cfbe0bb5a30e582ed805ef79706823a96"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a237064a6e2c2256c9a1cf2beb7c971382190c0f1eb2e810e02e971881756132"}, + {file = "cramjam-2.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53145fc9f2319c1245d4329e1da8cfacd6e35e27090c07c0b9d453ae2bbdac3e"}, + {file = "cramjam-2.9.1-cp39-cp39-win32.whl", hash = "sha256:8a9f52c27292c21457f43c4ce124939302a9acfb62295e7cda8667310563a5a3"}, + {file = "cramjam-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:8097ee39b61c86848a443c0b25b2df1de6b331fd512b20836a4f5cfde51ab255"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:86824c695688fcd06c5ac9bbd3fea9bdfb4cca194b1e706fbf11a629df48d2b4"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:27571bfa5a5d618604696747d0dc1d2a99b5906c967c8dee53c13a7107edfde6"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb01f6e38719818778144d3165a89ea1ad9dc58c6342b7f20aa194c70f34cbd1"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b5cef5cf40725fe64592af9ec163e7389855077700678a1d94bec549403a74d"}, + {file = "cramjam-2.9.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ac48b978aa0675f62b642750e798c394a64d25ce852e4e541f69bef9a564c2f0"}, + {file = "cramjam-2.9.1.tar.gz", hash = "sha256:336cc591d86cbd225d256813779f46624f857bc9c779db126271eff9ddc524ae"}, ] [package.extras] @@ -766,51 +711,51 @@ dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-ben [[package]] name = "cryptography" -version = "43.0.3" +version = "44.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, - {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, - {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, - {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, - {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, - {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, - {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, - {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, - {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, - {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, - {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, - {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, - {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, - {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, - {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, +python-versions = "!=3.9.0,!=3.9.1,>=3.7" +files = [ + {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, + {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, + {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, + {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, + {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, + {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"}, + {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, + {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, + {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, + {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, + {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, + {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"}, + {file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"}, + {file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"}, ] [package.dependencies] cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -862,33 +807,33 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "djlint" -version = "1.36.1" +version = "1.36.4" description = "HTML Template Linter and Formatter" optional = false python-versions = ">=3.9" files = [ - {file = "djlint-1.36.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef40527fd6cd82cdd18f65a6bf5b486b767d2386f6c21f2ebd60e5d88f487fe8"}, - {file = "djlint-1.36.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4712de3dea172000a098da6a0cd709d158909b4964ba0f68bee584cef18b4878"}, - {file = "djlint-1.36.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d01c1425170b7059d68a3b01709e1c31d2cd6520a1eb0166e6670fd250518a"}, - {file = "djlint-1.36.1-cp310-cp310-win_amd64.whl", hash = "sha256:65585a97d3a37760b4c1fbd089a3573506ad0ab2885119322a66231f911d113f"}, - {file = "djlint-1.36.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:607437a0a230462916858c269bc5dfd15ff71b27d15dfd1ad6e96b3da9cbd8f6"}, - {file = "djlint-1.36.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ddc9ae6b83b288465f6685b24797adbde79952d6e1a5276026e5ef479bac76f"}, - {file = "djlint-1.36.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:001e5124b0ebab60a2044134abd11ff11dee772e7c3caaa2c8d12eb5d3b1f1dc"}, - {file = "djlint-1.36.1-cp311-cp311-win_amd64.whl", hash = "sha256:095d62f3cabbac08683c51c1d9dacab522b54437a2a317df9e134599360f7b89"}, - {file = "djlint-1.36.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:210f319c2d22489aebc0e9c1acd5015ca3892b66fa35647344511b3c03fcbe82"}, - {file = "djlint-1.36.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7aa3db13d7702c35f4e408325061d9d4e84d006c99bb3e55fddf2b2543736923"}, - {file = "djlint-1.36.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f907e97f4d67f4423dc71671592891cfd9cd311aeef14db25633f292dbf7048"}, - {file = "djlint-1.36.1-cp312-cp312-win_amd64.whl", hash = "sha256:abadf6b61dc53d81710f230542f57f2d470b7503cd3108ad8a0113271c0514dd"}, - {file = "djlint-1.36.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7f31646435385eec1d4b03dad7bebb5e4078d9893c60d490a685535bd6303c83"}, - {file = "djlint-1.36.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4399477ac51f9c8147eedbef70aa8465eccba6759d875d1feec6782744aa168a"}, - {file = "djlint-1.36.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c217b17d3ae3c0e3b5fff57fb708029cceda6e232f5a54ff1b3aeb43a7540"}, - {file = "djlint-1.36.1-cp313-cp313-win_amd64.whl", hash = "sha256:1577490802ca4697af3488ed13066c9214ef0f625a96aa20d4f297e37aa19303"}, - {file = "djlint-1.36.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ae356faf8180c7629ca705b7b9d8c9269b2c53273a1887a438a21b8fa263588"}, - {file = "djlint-1.36.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2237ac5cecd2524960e1684f64ce358624b0d769b7404e5aad415750ad00edc9"}, - {file = "djlint-1.36.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02c22352a49c053ff6260428ed571afb783011d20afc98b44bbe1dd2fa2d5418"}, - {file = "djlint-1.36.1-cp39-cp39-win_amd64.whl", hash = "sha256:99a2debeea2e931b68360306fdbf13861e3d6f96037a9d882f3d4d5e44fdc319"}, - {file = "djlint-1.36.1-py3-none-any.whl", hash = "sha256:950782b396dd82b74622c09d7e4c52328e56a3b03c8ac790c319708e5caa0686"}, - {file = "djlint-1.36.1.tar.gz", hash = "sha256:f7260637ed72c270fa6dd4a87628e1a21c49b24a46df52e4e26f44d4934fb97c"}, + {file = "djlint-1.36.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2dfb60883ceb92465201bfd392291a7597c6752baede6fbb6f1980cac8d6c5c"}, + {file = "djlint-1.36.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bc6a1320c0030244b530ac200642f883d3daa451a115920ef3d56d08b644292"}, + {file = "djlint-1.36.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3164a048c7bb0baf042387b1e33f9bbbf99d90d1337bb4c3d66eb0f96f5400a1"}, + {file = "djlint-1.36.4-cp310-cp310-win_amd64.whl", hash = "sha256:3196d5277da5934962d67ad6c33a948ba77a7b6eadf064648bef6ee5f216b03c"}, + {file = "djlint-1.36.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d68da0ed10ee9ca1e32e225cbb8e9b98bf7e6f8b48a8e4836117b6605b88cc7"}, + {file = "djlint-1.36.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c0478d5392247f1e6ee29220bbdbf7fb4e1bc0e7e83d291fda6fb926c1787ba7"}, + {file = "djlint-1.36.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:962f7b83aee166e499eff916d631c6dde7f1447d7610785a60ed2a75a5763483"}, + {file = "djlint-1.36.4-cp311-cp311-win_amd64.whl", hash = "sha256:53cbc450aa425c832f09bc453b8a94a039d147b096740df54a3547fada77ed08"}, + {file = "djlint-1.36.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b"}, + {file = "djlint-1.36.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e"}, + {file = "djlint-1.36.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675"}, + {file = "djlint-1.36.4-cp312-cp312-win_amd64.whl", hash = "sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08"}, + {file = "djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2"}, + {file = "djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835"}, + {file = "djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f"}, + {file = "djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4"}, + {file = "djlint-1.36.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:89678661888c03d7bc6cadd75af69db29962b5ecbf93a81518262f5c48329f04"}, + {file = "djlint-1.36.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b01a98df3e1ab89a552793590875bc6e954cad661a9304057db75363d519fa0"}, + {file = "djlint-1.36.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbb4f7b93223d471d09ae34ed515fef98b2233cbca2449ad117416c44b1351"}, + {file = "djlint-1.36.4-cp39-cp39-win_amd64.whl", hash = "sha256:7a483390d17e44df5bc23dcea29bdf6b63f3ed8b4731d844773a4829af4f5e0b"}, + {file = "djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd"}, + {file = "djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1"}, ] [package.dependencies] @@ -924,12 +869,13 @@ wmi = ["wmi (>=1.5.1)"] [[package]] name = "editorconfig" -version = "0.12.4" +version = "0.17.0" description = "EditorConfig File Locator and Interpreter for Python" optional = false python-versions = "*" files = [ - {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"}, + {file = "EditorConfig-0.17.0-py3-none-any.whl", hash = "sha256:fe491719c5f65959ec00b167d07740e7ffec9a3f362038c72b289330b9991dfc"}, + {file = "editorconfig-0.17.0.tar.gz", hash = "sha256:8739052279699840065d3a9f5c125d7d5a98daeefe53b0e5274261d77cb49aa2"}, ] [[package]] @@ -963,13 +909,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.26.1" +version = "2.26.2" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.26.1-py3-none-any.whl", hash = "sha256:68a5615d7ef2529094d6958677e30a6d30d544e203a5ab852985c19d7ad57e32"}, - {file = "fakeredis-2.26.1.tar.gz", hash = "sha256:69f4daafe763c8014a6dbf44a17559c46643c95447b3594b3975251a171b806d"}, + {file = "fakeredis-2.26.2-py3-none-any.whl", hash = "sha256:86d4129df001efc25793cb334008160fccc98425d9f94de47884a92b63988c14"}, + {file = "fakeredis-2.26.2.tar.gz", hash = "sha256:3ee5003a314954032b96b1365290541346c9cc24aab071b52cc983bb99ecafbf"}, ] [package.dependencies] @@ -1163,13 +1109,13 @@ test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.23.0" +version = "2.24.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_core-2.23.0-py3-none-any.whl", hash = "sha256:c20100d4c4c41070cf365f1d8ddf5365915291b5eb11b83829fbd1c999b5122f"}, - {file = "google_api_core-2.23.0.tar.gz", hash = "sha256:2ceb087315e6af43f256704b871d99326b1f12a9d6ce99beaedec99ba26a0ace"}, + {file = "google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9"}, + {file = "google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf"}, ] [package.dependencies] @@ -1192,13 +1138,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.36.0" +version = "2.37.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, - {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, + {file = "google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0"}, + {file = "google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00"}, ] [package.dependencies] @@ -1209,6 +1155,7 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] enterprise-cert = ["cryptography", "pyopenssl"] +pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"] pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] @@ -1487,13 +1434,13 @@ test = ["objgraph", "psutil"] [[package]] name = "grpc-google-iam-v1" -version = "0.13.1" +version = "0.14.0" description = "IAM API client library" optional = false python-versions = ">=3.7" files = [ - {file = "grpc-google-iam-v1-0.13.1.tar.gz", hash = "sha256:3ff4b2fd9d990965e410965253c0da6f66205d5a8291c4c31c6ebecca18a9001"}, - {file = "grpc_google_iam_v1-0.13.1-py2.py3-none-any.whl", hash = "sha256:c3e86151a981811f30d5e7330f271cee53e73bb87755e88cc3b6f0c7b5fe374e"}, + {file = "grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4"}, + {file = "grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99"}, ] [package.dependencies] @@ -1570,18 +1517,18 @@ protobuf = ["grpcio-tools (>=1.69.0)"] [[package]] name = "grpcio-status" -version = "1.68.0" +version = "1.69.0" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_status-1.68.0-py3-none-any.whl", hash = "sha256:0a71b15d989f02df803b4ba85c5bf1f43aeaa58ac021e5f9974b8cadc41f784d"}, - {file = "grpcio_status-1.68.0.tar.gz", hash = "sha256:8369823de22ab6a2cddb3804669c149ae7a71819e127c2dca7c2322028d52bea"}, + {file = "grpcio_status-1.69.0-py3-none-any.whl", hash = "sha256:d6b2a3c9562c03a817c628d7ba9a925e209c228762d6d7677ae5c9401a542853"}, + {file = "grpcio_status-1.69.0.tar.gz", hash = "sha256:595ef84e5178d6281caa732ccf68ff83259241608d26b0e9c40a5e66eee2a2d2"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.68.0" +grpcio = ">=1.69.0" protobuf = ">=5.26.1,<6.0dev" [[package]] @@ -1732,13 +1679,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.4" +version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, + {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, + {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, ] [package.dependencies] @@ -1774,13 +1721,13 @@ six = ">=1.13.0" [[package]] name = "json5" -version = "0.9.28" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, - {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] [package.extras] @@ -1965,19 +1912,19 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.21" -description = "" +version = "5.0.26" +description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.21-py3-none-any.whl", hash = "sha256:1235b2ae3666459c9cc44504a5e73d35f4959b45e5876b2f6df2e5f4889dfb4f"}, - {file = "moto-5.0.21.tar.gz", hash = "sha256:52f63291daeff9444ef5eb14fbf69b24264567b79f184ae6aee4945d09845f06"}, + {file = "moto-5.0.26-py3-none-any.whl", hash = "sha256:803831f427ca6c0452ae4fb898d731cfc19906466a33a88cbc1076abcbfcbba7"}, + {file = "moto-5.0.26.tar.gz", hash = "sha256:6829f58a670a087e7c5b63f8183c6b72d64a1444e420c212250b7326b69a9183"}, ] [package.dependencies] boto3 = ">=1.9.201" botocore = ">=1.14.0,<1.35.45 || >1.35.45,<1.35.46 || >1.35.46" -cryptography = ">=3.3.1" +cryptography = ">=35.0.0" Jinja2 = ">=2.10.1" python-dateutil = ">=2.1,<3.0.0" requests = ">=2.5" @@ -1986,73 +1933,78 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "jsonschema", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "jsonschema", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] cognitoidp = ["joserfc (>=0.9.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.6)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.6)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.6.1)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.6.1)"] events = ["jsonpath-ng"] glue = ["pyparsing (>=3.0.7)"] -iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] quicksight = ["jsonschema"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.6)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.6)"] -server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.6)", "pyparsing (>=3.0.7)", "setuptools"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.6.1)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.6.1)"] +server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] stepfunctions = ["antlr4-python3-runtime", "jsonpath-ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.13.0" +version = "1.14.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, - {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, - {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, - {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, - {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, - {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, - {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, - {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, - {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, - {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, - {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, - {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, - {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, - {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, - {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, - {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, - {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, - {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, - {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, - {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, - {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, - {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, - {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, - {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, - {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, - {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, - {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, - {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, - {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, - {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, - {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, - {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -typing-extensions = ">=4.6.0" + {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, + {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, + {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, + {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, + {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, + {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, + {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, + {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, + {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, + {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, + {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, + {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, + {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, + {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, + {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" +typing_extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -2074,13 +2026,13 @@ files = [ [[package]] name = "opentelemetry-api" -version = "1.28.2" +version = "1.29.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_api-1.28.2-py3-none-any.whl", hash = "sha256:6fcec89e265beb258fe6b1acaaa3c8c705a934bd977b9f534a2b7c0d2d4275a6"}, - {file = "opentelemetry_api-1.28.2.tar.gz", hash = "sha256:ecdc70c7139f17f9b0cf3742d57d7020e3e8315d6cffcdf1a12a905d45b19cc0"}, + {file = "opentelemetry_api-1.29.0-py3-none-any.whl", hash = "sha256:5fcd94c4141cc49c736271f3e1efb777bebe9cc535759c54c936cca4f1b312b8"}, + {file = "opentelemetry_api-1.29.0.tar.gz", hash = "sha256:d04a6cf78aad09614f52964ecb38021e248f5714dc32c2e0d8fd99517b4d69cf"}, ] [package.dependencies] @@ -2089,34 +2041,34 @@ importlib-metadata = ">=6.0,<=8.5.0" [[package]] name = "opentelemetry-sdk" -version = "1.28.2" +version = "1.29.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_sdk-1.28.2-py3-none-any.whl", hash = "sha256:93336c129556f1e3ccd21442b94d3521759541521861b2214c499571b85cb71b"}, - {file = "opentelemetry_sdk-1.28.2.tar.gz", hash = "sha256:5fed24c5497e10df30282456fe2910f83377797511de07d14cec0d3e0a1a3110"}, + {file = "opentelemetry_sdk-1.29.0-py3-none-any.whl", hash = "sha256:173be3b5d3f8f7d671f20ea37056710217959e774e2749d984355d1f9391a30a"}, + {file = "opentelemetry_sdk-1.29.0.tar.gz", hash = "sha256:b0787ce6aade6ab84315302e72bd7a7f2f014b0fb1b7c3295b88afe014ed0643"}, ] [package.dependencies] -opentelemetry-api = "1.28.2" -opentelemetry-semantic-conventions = "0.49b2" +opentelemetry-api = "1.29.0" +opentelemetry-semantic-conventions = "0.50b0" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.49b2" +version = "0.50b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_semantic_conventions-0.49b2-py3-none-any.whl", hash = "sha256:51e7e1d0daa958782b6c2a8ed05e5f0e7dd0716fc327ac058777b8659649ee54"}, - {file = "opentelemetry_semantic_conventions-0.49b2.tar.gz", hash = "sha256:44e32ce6a5bb8d7c0c617f84b9dc1c8deda1045a07dc16a688cc7cbeab679997"}, + {file = "opentelemetry_semantic_conventions-0.50b0-py3-none-any.whl", hash = "sha256:e87efba8fdb67fb38113efea6a349531e75ed7ffc01562f65b802fcecb5e115e"}, + {file = "opentelemetry_semantic_conventions-0.50b0.tar.gz", hash = "sha256:02dc6dbcb62f082de9b877ff19a3f1ffaa3c306300fa53bfac761c4567c83d38"}, ] [package.dependencies] deprecated = ">=1.2.6" -opentelemetry-api = "1.28.2" +opentelemetry-api = "1.29.0" [[package]] name = "ordered-set" @@ -2211,18 +2163,18 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.49.0" +version = "1.49.1" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" files = [ - {file = "playwright-1.49.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:704532a2d8ba580ec9e1895bfeafddce2e3d52320d4eb8aa38e80376acc5cbb0"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e453f02c4e5cc2db7e9759c47e7425f32e50ac76c76b7eb17c69eed72f01c4d8"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:37ae985309184472946a6eb1a237e5d93c9e58a781fa73b75c8751325002a5d4"}, - {file = "playwright-1.49.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:68d94beffb3c9213e3ceaafa66171affd9a5d9162e0c8a3eed1b1132c2e57598"}, - {file = "playwright-1.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f12d2aecdb41fc25a624cb15f3e8391c252ebd81985e3d5c1c261fe93779345"}, - {file = "playwright-1.49.0-py3-none-win32.whl", hash = "sha256:91103de52d470594ad375b512d7143fa95d6039111ae11a93eb4fe2f2b4a4858"}, - {file = "playwright-1.49.0-py3-none-win_amd64.whl", hash = "sha256:34d28a2c2d46403368610be4339898dc9c34eb9f7c578207b4715c49743a072a"}, + {file = "playwright-1.49.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1041ffb45a0d0bc44d698d3a5aa3ac4b67c9bd03540da43a0b70616ad52592b8"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f38ed3d0c1f4e0a6d1c92e73dd9a61f8855133249d6f0cec28648d38a7137be"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:3be48c6d26dc819ca0a26567c1ae36a980a0303dcd4249feb6f59e115aaddfb8"}, + {file = "playwright-1.49.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:753ca90ee31b4b03d165cfd36e477309ebf2b4381953f2a982ff612d85b147d2"}, + {file = "playwright-1.49.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd9bc8dab37aa25198a01f555f0a2e2c3813fe200fef018ac34dfe86b34994b9"}, + {file = "playwright-1.49.1-py3-none-win32.whl", hash = "sha256:43b304be67f096058e587dac453ece550eff87b8fbed28de30f4f022cc1745bb"}, + {file = "playwright-1.49.1-py3-none-win_amd64.whl", hash = "sha256:47b23cb346283278f5b4d1e1990bcb6d6302f80c0aa0ca93dd0601a1400191df"}, ] [package.dependencies] @@ -2263,22 +2215,22 @@ testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "5.28.3" +version = "5.29.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24"}, - {file = "protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868"}, - {file = "protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135"}, - {file = "protobuf-5.28.3-cp38-cp38-win32.whl", hash = "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548"}, - {file = "protobuf-5.28.3-cp38-cp38-win_amd64.whl", hash = "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b"}, - {file = "protobuf-5.28.3-cp39-cp39-win32.whl", hash = "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535"}, - {file = "protobuf-5.28.3-cp39-cp39-win_amd64.whl", hash = "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36"}, - {file = "protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed"}, - {file = "protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b"}, + {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, + {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, + {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"}, + {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"}, + {file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"}, + {file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"}, + {file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"}, + {file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"}, + {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"}, + {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"}, ] [[package]] @@ -2336,17 +2288,17 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " [[package]] name = "pylint" -version = "3.3.1" +version = "3.3.3" description = "python code static checker" optional = false python-versions = ">=3.9.0" files = [ - {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, - {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, + {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, + {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, ] [package.dependencies] -astroid = ">=3.3.4,<=3.4.0-dev0" +astroid = ">=3.3.8,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" @@ -2403,13 +2355,13 @@ dev = ["build", "flake8", "mypy", "pytest", "twine"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -2771,13 +2723,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "responses" -version = "0.25.3" +version = "0.25.6" description = "A utility library for mocking out the `requests` Python library." optional = false python-versions = ">=3.8" files = [ - {file = "responses-0.25.3-py3-none-any.whl", hash = "sha256:521efcbc82081ab8daa588e08f7e8a64ce79b91c39f6e62199b19159bea7dbcb"}, - {file = "responses-0.25.3.tar.gz", hash = "sha256:617b9247abd9ae28313d57a75880422d55ec63c29d33d629697590a034358dba"}, + {file = "responses-0.25.6-py3-none-any.whl", hash = "sha256:9cac8f21e1193bb150ec557875377e41ed56248aed94e4567ed644db564bacf1"}, + {file = "responses-0.25.6.tar.gz", hash = "sha256:eae7ce61a9603004e76c05691e7c389e59652d91e94b419623c12bbfb8e331d8"}, ] [package.dependencies] @@ -2790,101 +2742,114 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, - {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, - {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, - {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, - {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, - {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, - {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, - {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, - {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, - {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, - {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, - {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -2903,29 +2868,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.7.4" +version = "0.9.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, + {file = "ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743"}, + {file = "ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f"}, + {file = "ruff-0.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50c647ff96f4ba288db0ad87048257753733763b409b2faf2ea78b45c8bb7fcb"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0c8b149e9c7353cace7d698e1656ffcf1e36e50f8ea3b5d5f7f87ff9986a7ca"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:beb3298604540c884d8b282fe7625651378e1986c25df51dec5b2f60cafc31ce"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0174ccc45c439093971cc06ed3ac4dc545f5e8bdacf9f067adf879544d969"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69572926c0f0c9912288915214ca9b2809525ea263603370b9e00bed2ba56dbd"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:937267afce0c9170d6d29f01fcd1f4378172dec6760a9f4dface48cdabf9610a"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:186c2313de946f2c22bdf5954b8dd083e124bcfb685732cfb0beae0c47233d9b"}, + {file = "ruff-0.9.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f94942a3bb767675d9a051867c036655fe9f6c8a491539156a6f7e6b5f31831"}, + {file = "ruff-0.9.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:728d791b769cc28c05f12c280f99e8896932e9833fef1dd8756a6af2261fd1ab"}, + {file = "ruff-0.9.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f312c86fb40c5c02b44a29a750ee3b21002bd813b5233facdaf63a51d9a85e1"}, + {file = "ruff-0.9.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae017c3a29bee341ba584f3823f805abbe5fe9cd97f87ed07ecbf533c4c88366"}, + {file = "ruff-0.9.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5dc40a378a0e21b4cfe2b8a0f1812a6572fc7b230ef12cd9fac9161aa91d807f"}, + {file = "ruff-0.9.1-py3-none-win32.whl", hash = "sha256:46ebf5cc106cf7e7378ca3c28ce4293b61b449cd121b98699be727d40b79ba72"}, + {file = "ruff-0.9.1-py3-none-win_amd64.whl", hash = "sha256:342a824b46ddbcdddd3abfbb332fa7fcaac5488bf18073e841236aadf4ad5c19"}, + {file = "ruff-0.9.1-py3-none-win_arm64.whl", hash = "sha256:1cd76c7f9c679e6e8f2af8f778367dca82b95009bc7b1a85a47f1521ae524fa7"}, + {file = "ruff-0.9.1.tar.gz", hash = "sha256:fd2b25ecaf907d6458fa842675382c8597b3c746a2dde6717fe3415425df0c17"}, ] [[package]] @@ -2963,23 +2928,23 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "75.6.0" +version = "75.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" files = [ - {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, - {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, + {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, + {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "simplejson" @@ -3102,13 +3067,13 @@ files = [ [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -3209,13 +3174,13 @@ files = [ [[package]] name = "types-cffi" -version = "1.16.0.20240331" +version = "1.16.0.20241221" description = "Typing stubs for cffi" optional = false python-versions = ">=3.8" files = [ - {file = "types-cffi-1.16.0.20240331.tar.gz", hash = "sha256:b8b20d23a2b89cfed5f8c5bc53b0cb8677c3aac6d970dbc771e28b9c698f5dee"}, - {file = "types_cffi-1.16.0.20240331-py3-none-any.whl", hash = "sha256:a363e5ea54a4eb6a4a105d800685fde596bc318089b025b27dee09849fe41ff0"}, + {file = "types_cffi-1.16.0.20241221-py3-none-any.whl", hash = "sha256:e5b76b4211d7a9185f6ab8d06a106d56c7eb80af7cdb8bfcb4186ade10fb112f"}, + {file = "types_cffi-1.16.0.20241221.tar.gz", hash = "sha256:1c96649618f4b6145f58231acb976e0b448be6b847f7ab733dabe62dfbff6591"}, ] [package.dependencies] @@ -3238,35 +3203,35 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] name = "types-pytz" -version = "2024.2.0.20241003" +version = "2024.2.0.20241221" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44"}, - {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, + {file = "types_pytz-2024.2.0.20241221-py3-none-any.whl", hash = "sha256:8fc03195329c43637ed4f593663df721fef919b60a969066e22606edf0b53ad5"}, + {file = "types_pytz-2024.2.0.20241221.tar.gz", hash = "sha256:06d7cde9613e9f7504766a0554a270c369434b50e00975b3a4a0f6eed0f2c1a9"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20240917" +version = "6.0.12.20241230" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, - {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, + {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, + {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, ] [[package]] @@ -3300,24 +3265,24 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.5.0.20241122" +version = "75.8.0.20250110" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types_setuptools-75.5.0.20241122-py3-none-any.whl", hash = "sha256:d69c445f7bdd5e49d1b2441aadcee1388febcc9ad9d9d5fd33648b555e0b1c31"}, - {file = "types_setuptools-75.5.0.20241122.tar.gz", hash = "sha256:196aaf1811cbc1c77ac1d4c4879d5308b6fdf426e56b73baadbca2a1827dadef"}, + {file = "types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480"}, + {file = "types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271"}, ] [[package]] name = "types-simplejson" -version = "3.19.0.20240801" +version = "3.19.0.20241221" description = "Typing stubs for simplejson" optional = false python-versions = ">=3.8" files = [ - {file = "types-simplejson-3.19.0.20240801.tar.gz", hash = "sha256:ef90cc81dd915f26c452fa2b5e0cbd3a36af81074ae63878fcf8a477e7594e4d"}, - {file = "types_simplejson-3.19.0.20240801-py3-none-any.whl", hash = "sha256:37f1b33c8626d7f072ea87737629310674845d54d187f12387fe33d31c938288"}, + {file = "types_simplejson-3.19.0.20241221-py3-none-any.whl", hash = "sha256:179dfaef8c357156c781fa47cfdfcd953a7953fc375dfe9ab19a20054a828980"}, + {file = "types_simplejson-3.19.0.20241221.tar.gz", hash = "sha256:114af9db0f49ad15755d2b6ad8e6fd04b5a493815e2fc1e011729d4650defc70"}, ] [[package]] @@ -3362,13 +3327,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.3" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] @@ -3406,76 +3371,90 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "wrapt" -version = "1.17.0" +version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" files = [ - {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, - {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, - {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, - {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, - {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, - {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, - {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, - {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, - {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, - {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, - {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, - {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, - {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, - {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, - {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, - {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, - {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, - {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, - {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, - {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, - {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, - {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, - {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, - {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, - {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, - {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, - {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, - {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, - {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, - {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, - {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, - {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, - {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, - {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, - {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, - {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, - {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, + {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"}, + {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"}, + {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"}, + {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"}, + {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, + {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, + {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, + {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, + {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, + {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, + {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, + {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, + {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, + {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, + {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, + {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, + {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, + {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, + {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, + {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, + {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, + {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, + {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, + {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, + {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"}, + {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"}, + {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"}, + {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"}, + {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"}, + {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"}, + {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"}, + {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"}, + {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"}, + {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"}, + {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"}, + {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, + {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, ] [[package]] @@ -3545,48 +3524,48 @@ test = ["zope.testrunner"] [[package]] name = "zope-interface" -version = "7.1.1" +version = "7.2" description = "Interfaces for Python" optional = false python-versions = ">=3.8" files = [ - {file = "zope.interface-7.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6650bd56ef350d37c8baccfd3ee8a0483ed6f8666e641e4b9ae1a1827b79f9e5"}, - {file = "zope.interface-7.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84e87eba6b77a3af187bae82d8de1a7c208c2a04ec9f6bd444fd091b811ad92e"}, - {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c4e1b4c06d9abd1037c088dae1566c85f344a3e6ae4350744c3f7f7259d9c67"}, - {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cd5e3d910ac87652a09f6e5db8e41bc3b49cf08ddd2d73d30afc644801492cd"}, - {file = "zope.interface-7.1.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca95594d936ee349620900be5b46c0122a1ff6ce42d7d5cb2cf09dc84071ef16"}, - {file = "zope.interface-7.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:ad339509dcfbbc99bf8e147db6686249c4032f26586699ec4c82f6e5909c9fe2"}, - {file = "zope.interface-7.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e59f175e868f856a77c0a77ba001385c377df2104fdbda6b9f99456a01e102a"}, - {file = "zope.interface-7.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0de23bcb93401994ea00bc5c677ef06d420340ac0a4e9c10d80e047b9ce5af3f"}, - {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdb7e7e5524b76d3ec037c1d81a9e2c7457b240fd4cb0a2476b65c3a5a6c81f"}, - {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3603ef82a9920bd0bfb505423cb7e937498ad971ad5a6141841e8f76d2fd5446"}, - {file = "zope.interface-7.1.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d52d052355e0c5c89e0630dd2ff7c0b823fd5f56286a663e92444761b35e25"}, - {file = "zope.interface-7.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:179ad46ece518c9084cb272e4a69d266b659f7f8f48e51706746c2d8a426433e"}, - {file = "zope.interface-7.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6503534b52bb1720ace9366ee30838a58a3413d3e197512f3338c8f34b5d89d"}, - {file = "zope.interface-7.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f85b290e5b8b11814efb0d004d8ce6c9a483c35c462e8d9bf84abb93e79fa770"}, - {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d029fac6a80edae80f79c37e5e3abfa92968fe921886139b3ee470a1b177321a"}, - {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5836b8fb044c6e75ba34dfaabc602493019eadfa0faf6ff25f4c4c356a71a853"}, - {file = "zope.interface-7.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7395f13533318f150ee72adb55b29284b16e73b6d5f02ab21f173b3e83f242b8"}, - {file = "zope.interface-7.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:1d0e23c6b746eb8ce04573cc47bcac60961ac138885d207bd6f57e27a1431ae8"}, - {file = "zope.interface-7.1.1-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9fad9bd5502221ab179f13ea251cb30eef7cf65023156967f86673aff54b53a0"}, - {file = "zope.interface-7.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:55c373becbd36a44d0c9be1d5271422fdaa8562d158fb44b4192297b3c67096c"}, - {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed1df8cc01dd1e3970666a7370b8bfc7457371c58ba88c57bd5bca17ab198053"}, - {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99c14f0727c978639139e6cad7a60e82b7720922678d75aacb90cf4ef74a068c"}, - {file = "zope.interface-7.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b1eed7670d564f1025d7cda89f99f216c30210e42e95de466135be0b4a499d9"}, - {file = "zope.interface-7.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:3defc925c4b22ac1272d544a49c6ba04c3eefcce3200319ee1be03d9270306dd"}, - {file = "zope.interface-7.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8d0fe45be57b5219aa4b96e846631c04615d5ef068146de5a02ccd15c185321f"}, - {file = "zope.interface-7.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bcbeb44fc16e0078b3b68a95e43f821ae34dcbf976dde6985141838a5f23dd3d"}, - {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8e7b05dc6315a193cceaec071cc3cf1c180cea28808ccded0b1283f1c38ba73"}, - {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d553e02b68c0ea5a226855f02edbc9eefd99f6a8886fa9f9bdf999d77f46585"}, - {file = "zope.interface-7.1.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81744a7e61b598ebcf4722ac56a7a4f50502432b5b4dc7eb29075a89cf82d029"}, - {file = "zope.interface-7.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7720322763aceb5e0a7cadcc38c67b839efe599f0887cbf6c003c55b1458c501"}, - {file = "zope.interface-7.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ed0852c25950cf430067f058f8d98df6288502ac313861d9803fe7691a9b3"}, - {file = "zope.interface-7.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9595e478047ce752b35cfa221d7601a5283ccdaab40422e0dc1d4a334c70f580"}, - {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2317e1d4dba68203a5227ea3057f9078ec9376275f9700086b8f0ffc0b358e1b"}, - {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6821ef9870f32154da873fcde439274f99814ea452dd16b99fa0b66345c4b6b"}, - {file = "zope.interface-7.1.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:190eeec67e023d5aac54d183fa145db0b898664234234ac54643a441da434616"}, - {file = "zope.interface-7.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:d17e7fc814eaab93409b80819fd6d30342844345c27f3bc3c4b43c2425a8d267"}, - {file = "zope.interface-7.1.1.tar.gz", hash = "sha256:4284d664ef0ff7b709836d4de7b13d80873dc5faeffc073abdb280058bfac5e3"}, + {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, + {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d"}, + {file = "zope.interface-7.2-cp310-cp310-win_amd64.whl", hash = "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b"}, + {file = "zope.interface-7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2"}, + {file = "zope.interface-7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a"}, + {file = "zope.interface-7.2-cp311-cp311-win_amd64.whl", hash = "sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1"}, + {file = "zope.interface-7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7"}, + {file = "zope.interface-7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d"}, + {file = "zope.interface-7.2-cp312-cp312-win_amd64.whl", hash = "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5"}, + {file = "zope.interface-7.2-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98"}, + {file = "zope.interface-7.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b"}, + {file = "zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd"}, + {file = "zope.interface-7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a"}, + {file = "zope.interface-7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021"}, + {file = "zope.interface-7.2-cp38-cp38-win_amd64.whl", hash = "sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7"}, + {file = "zope.interface-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb"}, + {file = "zope.interface-7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75"}, + {file = "zope.interface-7.2-cp39-cp39-win_amd64.whl", hash = "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d"}, + {file = "zope.interface-7.2.tar.gz", hash = "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe"}, ] [package.dependencies] @@ -3712,4 +3691,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12.6" -content-hash = "b25039e64c33fd57071ca27fbe03ae97e6e53bcda5c8dc57b62c67fb0eaab21b" +content-hash = "ace42d0a5499a2c26038f09f723d83a8e5b02a943db5bd5dcfc6a094f783bef9" diff --git a/pyproject.toml b/pyproject.toml index 1bdbe5e225..689d031b23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ types-pytz = "^2024.1.0.20240417" playwright = "^1.42.0" black = "^24.2.0" djlint = "^1.34.2" -ruff = "^0.7.0" +ruff = "^0.9.1" [tool.poetry.dependencies] @@ -105,6 +105,7 @@ exclude = ["tests/*", "scripts/*"] [tool.ruff.lint] extend-ignore = [ + "A005", # Reusing a builtin module name for the name of a module "B024", # No abstract methods in abstract base class "B027", # Non-abstract empty methods in abstract base classes "B028",# No explicit keyword argument found From 0bb1ad795a184bc6bcc20a7c3cc41f0e86050316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 09:01:14 +0000 Subject: [PATCH 500/567] Bump the development-dependencies group across 1 directory with 3 updates (#1584) --- poetry.lock | 102 ++++++++++++++++++++++++++++++++++--------------- pyproject.toml | 2 +- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/poetry.lock b/poetry.lock index 79f15924ee..8a96fa2666 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "astroid" @@ -175,6 +175,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -187,8 +191,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -199,8 +209,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -210,6 +236,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -221,6 +251,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -233,6 +267,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -245,6 +283,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1912,13 +1954,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.26" +version = "5.0.27" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.8" files = [ - {file = "moto-5.0.26-py3-none-any.whl", hash = "sha256:803831f427ca6c0452ae4fb898d731cfc19906466a33a88cbc1076abcbfcbba7"}, - {file = "moto-5.0.26.tar.gz", hash = "sha256:6829f58a670a087e7c5b63f8183c6b72d64a1444e420c212250b7326b69a9183"}, + {file = "moto-5.0.27-py3-none-any.whl", hash = "sha256:27042fd94c8def0166d9f2ae8d39d9488d4b3115542b5fca88566c0424549013"}, + {file = "moto-5.0.27.tar.gz", hash = "sha256:6c123de7e0e5e6508a10c399ba3ecf2d5143f263f8e804fd4a7091941c3f5207"}, ] [package.dependencies] @@ -1928,7 +1970,7 @@ cryptography = ">=35.0.0" Jinja2 = ">=2.10.1" python-dateutil = ">=2.1,<3.0.0" requests = ">=2.5" -responses = ">=0.15.0" +responses = ">=0.15.0,<0.25.5 || >0.25.5" werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" @@ -2375,17 +2417,17 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-cov" -version = "5.0.0" +version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, ] [package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} +coverage = {version = ">=7.5", extras = ["toml"]} pytest = ">=4.6" [package.extras] @@ -2868,29 +2910,29 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.9.1" +version = "0.9.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.9.1-py3-none-linux_armv6l.whl", hash = "sha256:84330dda7abcc270e6055551aca93fdde1b0685fc4fd358f26410f9349cf1743"}, - {file = "ruff-0.9.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3cae39ba5d137054b0e5b472aee3b78a7c884e61591b100aeb544bcd1fc38d4f"}, - {file = "ruff-0.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50c647ff96f4ba288db0ad87048257753733763b409b2faf2ea78b45c8bb7fcb"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0c8b149e9c7353cace7d698e1656ffcf1e36e50f8ea3b5d5f7f87ff9986a7ca"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:beb3298604540c884d8b282fe7625651378e1986c25df51dec5b2f60cafc31ce"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39d0174ccc45c439093971cc06ed3ac4dc545f5e8bdacf9f067adf879544d969"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69572926c0f0c9912288915214ca9b2809525ea263603370b9e00bed2ba56dbd"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:937267afce0c9170d6d29f01fcd1f4378172dec6760a9f4dface48cdabf9610a"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:186c2313de946f2c22bdf5954b8dd083e124bcfb685732cfb0beae0c47233d9b"}, - {file = "ruff-0.9.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f94942a3bb767675d9a051867c036655fe9f6c8a491539156a6f7e6b5f31831"}, - {file = "ruff-0.9.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:728d791b769cc28c05f12c280f99e8896932e9833fef1dd8756a6af2261fd1ab"}, - {file = "ruff-0.9.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f312c86fb40c5c02b44a29a750ee3b21002bd813b5233facdaf63a51d9a85e1"}, - {file = "ruff-0.9.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ae017c3a29bee341ba584f3823f805abbe5fe9cd97f87ed07ecbf533c4c88366"}, - {file = "ruff-0.9.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5dc40a378a0e21b4cfe2b8a0f1812a6572fc7b230ef12cd9fac9161aa91d807f"}, - {file = "ruff-0.9.1-py3-none-win32.whl", hash = "sha256:46ebf5cc106cf7e7378ca3c28ce4293b61b449cd121b98699be727d40b79ba72"}, - {file = "ruff-0.9.1-py3-none-win_amd64.whl", hash = "sha256:342a824b46ddbcdddd3abfbb332fa7fcaac5488bf18073e841236aadf4ad5c19"}, - {file = "ruff-0.9.1-py3-none-win_arm64.whl", hash = "sha256:1cd76c7f9c679e6e8f2af8f778367dca82b95009bc7b1a85a47f1521ae524fa7"}, - {file = "ruff-0.9.1.tar.gz", hash = "sha256:fd2b25ecaf907d6458fa842675382c8597b3c746a2dde6717fe3415425df0c17"}, + {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"}, + {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"}, + {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"}, + {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"}, + {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"}, + {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"}, + {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"}, + {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"}, + {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"}, + {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"}, + {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"}, + {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"}, ] [[package]] @@ -3691,4 +3733,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12.6" -content-hash = "ace42d0a5499a2c26038f09f723d83a8e5b02a943db5bd5dcfc6a094f783bef9" +content-hash = "4a7eaa375c51b3cd071caae55ef888ac23ab8088e9a55efa1bc932ee651be2f8" diff --git a/pyproject.toml b/pyproject.toml index 689d031b23..4a1f78e9bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ authors = ["ONSDigital"] # update dependabot.yaml when adding new dependencies pep8 = "^1.7.1" mock = "^5.1.0" -pytest-cov = "^5.0.0" +pytest-cov = "^6.0.0" jsonschema = "^4.21.1" pylint = "^3.2.7" pylint-mccabe = "^0.1.3" From dbc589bd4e485761dc0d8d9b417645be0d99defd Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Tue, 21 Jan 2025 09:38:08 +0000 Subject: [PATCH 501/567] Fix ruff linting issue UP007: Using | for type annotations (#1554) --- app/authentication/authenticator.py | 8 ++-- .../no_questionnaire_state_exception.py | 5 +-- app/authentication/no_token_exception.py | 5 +-- app/data_models/answer.py | 29 +++++++----- app/data_models/answer_store.py | 16 +++---- app/data_models/app_models.py | 22 ++++----- app/data_models/list_store.py | 10 ++--- app/data_models/progress.py | 4 +- app/data_models/questionnaire_store.py | 8 ++-- app/data_models/relationship_store.py | 8 ++-- app/data_models/session_data.py | 4 +- app/data_models/session_store.py | 13 +++--- app/forms/duration_form.py | 8 ++-- app/forms/field_handlers/__init__.py | 4 +- app/forms/field_handlers/date_handlers.py | 16 +++---- app/forms/field_handlers/field_handler.py | 12 ++--- .../field_handlers/mobile_number_handler.py | 3 +- app/forms/field_handlers/number_handler.py | 9 ++-- app/forms/field_handlers/select_handlers.py | 4 +- app/forms/field_handlers/string_handler.py | 3 +- app/forms/field_handlers/text_area_handler.py | 3 +- app/forms/questionnaire_form.py | 24 +++++----- app/forms/validators.py | 45 +++++++++---------- app/helpers/address_lookup_api_helper.py | 3 +- app/helpers/header_helpers.py | 4 +- app/jinja_filters.py | 28 ++++++------ app/questionnaire/dynamic_answer_options.py | 6 +-- app/questionnaire/rules/helpers.py | 6 +-- app/questionnaire/rules/operations_helper.py | 8 ++-- app/questionnaire/rules/operator.py | 10 ++--- app/questionnaire/rules/utils.py | 4 +- app/secrets.py | 6 +-- app/storage/datastore.py | 4 +- app/storage/dynamodb.py | 4 +- .../encrypted_questionnaire_storage.py | 9 ++-- app/storage/redis.py | 3 +- app/storage/storage.py | 24 +++++----- app/storage/storage_encryption.py | 5 +-- .../previously_submitted_exception.py | 5 +-- app/submitter/submitter.py | 10 ++--- app/survey_config/business_config.py | 10 ++--- app/survey_config/link.py | 5 +-- app/survey_config/social_survey_config.py | 4 +- app/survey_config/survey_config.py | 36 +++++++-------- app/utilities/strings.py | 5 +-- app/utilities/types.py | 6 +-- app/views/contexts/email_form_context.py | 4 +- app/views/contexts/feedback_form_context.py | 4 +- app/views/contexts/hub_context.py | 24 +++++----- app/views/contexts/preview/preview_block.py | 6 +-- .../contexts/preview/preview_question.py | 4 +- app/views/contexts/preview_context.py | 4 +- app/views/contexts/section_summary_context.py | 8 ++-- .../contexts/submit_questionnaire_context.py | 4 +- app/views/contexts/summary/question.py | 20 ++++----- app/views/contexts/thank_you_context.py | 6 +-- .../view_submitted_response_context.py | 3 +- app/views/handlers/block.py | 6 +-- app/views/handlers/feedback.py | 16 +++---- app/views/handlers/individual_response.py | 6 +-- app/views/handlers/view_submitted_response.py | 3 +- pyproject.toml | 1 - .../rules/test_rule_evaluator.py | 5 +-- .../test_value_source_resolver.py | 7 ++- 64 files changed, 280 insertions(+), 319 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index ccf0285b72..5e1dcc1475 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -1,6 +1,6 @@ from contextlib import contextmanager from datetime import datetime, timedelta, timezone -from typing import Any, Generator, Mapping, MutableMapping, Optional +from typing import Any, Generator, Mapping, MutableMapping from uuid import uuid4 from blinker import ANY @@ -30,7 +30,7 @@ @login_manager.user_loader -def user_loader(user_id: str) -> Optional[str]: +def user_loader(user_id: str) -> str | None: logger.debug("loading user", user_id=user_id) return load_user() @@ -38,7 +38,7 @@ def user_loader(user_id: str) -> Optional[str]: @login_manager.request_loader def request_load_user( request: Request, -) -> Optional[User]: +) -> User | None: logger.debug("load user") extend_session = not ( @@ -94,7 +94,7 @@ def _is_session_valid(session_store: SessionStore) -> bool: ) -def load_user(extend_session: bool = True) -> Optional[User]: +def load_user(extend_session: bool = True) -> User | None: """ Checks for the present of the JWT in the users sessions :return: A user object if a JWT token is available in the session diff --git a/app/authentication/no_questionnaire_state_exception.py b/app/authentication/no_questionnaire_state_exception.py index 2affbdb90e..aadba31c59 100644 --- a/app/authentication/no_questionnaire_state_exception.py +++ b/app/authentication/no_questionnaire_state_exception.py @@ -1,8 +1,5 @@ -from typing import Union - - class NoQuestionnaireStateException(Exception): - def __init__(self, value: Union[str, int]) -> None: + def __init__(self, value: str | int) -> None: super().__init__() self.value = value diff --git a/app/authentication/no_token_exception.py b/app/authentication/no_token_exception.py index 0790d3a9df..d79e2e82ef 100644 --- a/app/authentication/no_token_exception.py +++ b/app/authentication/no_token_exception.py @@ -1,8 +1,5 @@ -from typing import Union - - class NoTokenException(Exception): - def __init__(self, value: Union[str, int]) -> None: + def __init__(self, value: str | int) -> None: super().__init__() self.value = value diff --git a/app/data_models/answer.py b/app/data_models/answer.py index 6e16eaeb38..d199021276 100644 --- a/app/data_models/answer.py +++ b/app/data_models/answer.py @@ -2,21 +2,26 @@ from dataclasses import asdict, dataclass, field from decimal import Decimal -from typing import Optional, TypedDict, Union, overload +from typing import TypedDict, overload from markupsafe import Markup, escape -DictAnswer = dict[str, Union[int, str]] +DictAnswer = dict[str, int | str] ListAnswer = list[str] ListDictAnswer = list[DictAnswer] -DictAnswerEscaped = dict[str, Union[int, Markup]] +DictAnswerEscaped = dict[str, int | Markup] ListAnswerEscaped = list[Markup] ListDictAnswerEscaped = list[DictAnswerEscaped] -AnswerValueTypes = Union[str, int, Decimal, DictAnswer, ListAnswer, ListDictAnswer] -AnswerValueEscapedTypes = Union[ - Markup, int, Decimal, DictAnswerEscaped, ListAnswerEscaped, ListDictAnswerEscaped -] +AnswerValueTypes = str | int | Decimal | DictAnswer | ListAnswer | ListDictAnswer +AnswerValueEscapedTypes = ( + Markup + | int + | Decimal + | DictAnswerEscaped + | ListAnswerEscaped + | ListDictAnswerEscaped +) class AnswerDict(TypedDict, total=False): @@ -29,7 +34,7 @@ class AnswerDict(TypedDict, total=False): class Answer: answer_id: str value: AnswerValueTypes - list_item_id: Optional[str] = field(default=None) + list_item_id: str | None = field(default=None) @classmethod def from_dict(cls, answer_dict: AnswerDict) -> Answer: @@ -69,13 +74,13 @@ def escape_answer_value(value: str) -> Markup: ... # pragma: no cover @overload def escape_answer_value( - value: Union[None, int, Decimal] -) -> Union[None, int, Decimal]: ... # pragma: no cover + value: None | int | Decimal, +) -> None | int | Decimal: ... # pragma: no cover def escape_answer_value( - value: Optional[AnswerValueTypes], -) -> Optional[AnswerValueEscapedTypes]: + value: AnswerValueTypes | None, +) -> AnswerValueEscapedTypes | None: if isinstance(value, list): return [escape(item) for item in value] diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index afa291b543..d79796a92e 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -1,10 +1,10 @@ from __future__ import annotations -from typing import Iterable, Iterator, Optional +from typing import Iterable, Iterator from app.data_models.answer import Answer, AnswerDict -AnswerKeyType = tuple[str, Optional[str]] +AnswerKeyType = tuple[str, str | None] class AnswerStore: @@ -21,7 +21,7 @@ class AnswerStore: } """ - def __init__(self, answers: Optional[Iterable[AnswerDict]] = None): + def __init__(self, answers: Iterable[AnswerDict] | None = None): """Instantiate an answer_store. Args: @@ -81,8 +81,8 @@ def add_or_update(self, answer: Answer) -> bool: return False def get_answer( - self, answer_id: str, list_item_id: Optional[str] = None - ) -> Optional[Answer]: + self, answer_id: str, list_item_id: str | None = None + ) -> Answer | None: """Get a single answer from the store Args: @@ -95,7 +95,7 @@ def get_answer( return self.answer_map.get((answer_id, list_item_id)) def get_answers_by_answer_id( - self, answer_ids: Iterable[str], list_item_id: Optional[str] = None + self, answer_ids: Iterable[str], list_item_id: str | None = None ) -> list[Answer]: """Get multiple answers from the store using the answer_id @@ -121,9 +121,7 @@ def clear(self) -> None: """ self.answer_map.clear() - def remove_answer( - self, answer_id: str, *, list_item_id: Optional[str] = None - ) -> bool: + def remove_answer(self, answer_id: str, *, list_item_id: str | None = None) -> bool: """ Removes answer *in place* from the answer store. :return: True if answer removed else False diff --git a/app/data_models/app_models.py b/app/data_models/app_models.py index 68aeffc656..d9f0b5d7f6 100644 --- a/app/data_models/app_models.py +++ b/app/data_models/app_models.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import Any, Optional, Union +from typing import Any from marshmallow import Schema, fields, post_load, pre_dump @@ -11,8 +11,8 @@ def __init__( state_data: str, collection_exercise_sid: str, version: int, - submitted_at: Optional[datetime] = None, - expires_at: Optional[datetime] = None, + submitted_at: datetime | None = None, + expires_at: datetime | None = None, ): self.user_id = user_id self.state_data = state_data @@ -28,9 +28,9 @@ class EQSession: def __init__( self, eq_session_id: str, - user_id: Optional[str], + user_id: str | None, expires_at: datetime, - session_data: Optional[str], + session_data: str | None, ): self.eq_session_id = eq_session_id self.user_id = user_id @@ -52,9 +52,9 @@ class Timestamp(fields.Field): def _serialize( self, value: datetime, - *args: Optional[list], + *args: list | None, **kwargs: Any, - ) -> Optional[int]: + ) -> int | None: if value: # Timezone aware datetime to timestamp return int(value.replace(tzinfo=timezone.utc).timestamp()) @@ -63,9 +63,9 @@ def _serialize( def _deserialize( self, value: float, - *args: Optional[list], + *args: list | None, **kwargs: Any, - ) -> Optional[datetime]: + ) -> datetime | None: if value: # Timestamp to timezone aware datetime return datetime.fromtimestamp(value, tz=timezone.utc) @@ -79,9 +79,9 @@ class DateTimeSchemaMixin: @staticmethod @pre_dump def set_date( - data: Union[EQSession, QuestionnaireState], + data: EQSession | QuestionnaireState, **kwargs: Any, - ) -> Union[EQSession, QuestionnaireState]: + ) -> EQSession | QuestionnaireState: data.updated_at = datetime.now(tz=timezone.utc) return data diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 0ca90df7ca..e6c3dc487a 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -3,7 +3,7 @@ import random from functools import cached_property from string import ascii_letters -from typing import Iterable, Iterator, Optional, TypedDict, overload +from typing import Iterable, Iterator, TypedDict, overload from structlog import get_logger @@ -27,9 +27,9 @@ class ListModel: def __init__( self, name: str, - items: Optional[list[str]] = None, - primary_person: Optional[str] = None, - same_name_items: Optional[list[str]] = None, + items: list[str] | None = None, + primary_person: str | None = None, + same_name_items: list[str] | None = None, ): self.name = name self.items = items or [] @@ -127,7 +127,7 @@ class ListStore: ``` """ - def __init__(self, items: Optional[Iterable[ListModelDictType]] = None): + def __init__(self, items: Iterable[ListModelDictType] | None = None): items = items or [] self._lists = self._build_map(items) diff --git a/app/data_models/progress.py b/app/data_models/progress.py index 21e641da20..99d8fdbf84 100644 --- a/app/data_models/progress.py +++ b/app/data_models/progress.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from enum import StrEnum -from typing import Mapping, Optional, TypedDict +from typing import Mapping, TypedDict class CompletionStatus(StrEnum): @@ -24,7 +24,7 @@ class Progress: section_id: str block_ids: list[str] status: CompletionStatus - list_item_id: Optional[str] = None + list_item_id: str | None = None @classmethod def from_dict(cls, progress_dict: ProgressDict) -> Progress: diff --git a/app/data_models/questionnaire_store.py b/app/data_models/questionnaire_store.py index 18a5f457b3..3e5625eeb0 100644 --- a/app/data_models/questionnaire_store.py +++ b/app/data_models/questionnaire_store.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import TYPE_CHECKING, MutableMapping, Optional +from typing import TYPE_CHECKING, MutableMapping from app.data_models.answer_store import AnswerStore from app.data_models.data_stores import DataStores @@ -22,7 +22,7 @@ class QuestionnaireStore: LATEST_VERSION = 1 def __init__( - self, storage: EncryptedQuestionnaireStorage, version: Optional[int] = None + self, storage: EncryptedQuestionnaireStorage, version: int | None = None ): self._storage = storage if version is None: @@ -31,8 +31,8 @@ def __init__( self._metadata: MutableMapping = {} self._stores = DataStores() self.data_stores = self._stores - self.submitted_at: Optional[datetime] - self.collection_exercise_sid: Optional[str] + self.submitted_at: datetime | None + self.collection_exercise_sid: str | None ( raw_data, diff --git a/app/data_models/relationship_store.py b/app/data_models/relationship_store.py index 8beae8cd8a..1fd0d8ed59 100644 --- a/app/data_models/relationship_store.py +++ b/app/data_models/relationship_store.py @@ -1,5 +1,5 @@ from dataclasses import asdict, dataclass -from typing import Iterable, Iterator, Optional, TypedDict, cast +from typing import Iterable, Iterator, TypedDict, cast class RelationshipDict(TypedDict, total=False): @@ -27,9 +27,7 @@ class RelationshipStore: Stores and updates relationships. """ - def __init__( - self, relationships: Optional[Iterable[RelationshipDict]] = None - ) -> None: + def __init__(self, relationships: Iterable[RelationshipDict] | None = None) -> None: self._is_dirty = False self._relationships = self._build_map(relationships or []) @@ -60,7 +58,7 @@ def serialize(self) -> list[RelationshipDict]: def get_relationship( self, list_item_id: str, to_list_item_id: str - ) -> Optional[Relationship]: + ) -> Relationship | None: key = (list_item_id, to_list_item_id) return self._relationships.get(key) diff --git a/app/data_models/session_data.py b/app/data_models/session_data.py index 723eda8a85..31341c96f0 100644 --- a/app/data_models/session_data.py +++ b/app/data_models/session_data.py @@ -1,10 +1,10 @@ -from typing import Any, Optional +from typing import Any class SessionData: def __init__( self, - language_code: Optional[str] = None, + language_code: str | None = None, confirmation_email_count: int = 0, feedback_count: int = 0, **_: Any, diff --git a/app/data_models/session_store.py b/app/data_models/session_store.py index 04271b7fd2..ee73c90416 100644 --- a/app/data_models/session_store.py +++ b/app/data_models/session_store.py @@ -1,7 +1,6 @@ from __future__ import annotations from datetime import datetime -from typing import Optional from flask import current_app from jwcrypto.common import base64url_decode @@ -17,19 +16,19 @@ class SessionStore: def __init__( - self, user_ik: str, pepper: str, eq_session_id: Optional[str] = None + self, user_ik: str, pepper: str, eq_session_id: str | None = None ) -> None: self.eq_session_id = eq_session_id - self.user_id: Optional[str] = None + self.user_id: str | None = None self.user_ik = user_ik - self.session_data: Optional[SessionData] = None - self._eq_session: Optional[EQSession] = None + self.session_data: SessionData | None = None + self._eq_session: EQSession | None = None self.pepper = pepper if eq_session_id: self._load() @property - def expiration_time(self) -> Optional[datetime]: + def expiration_time(self) -> datetime | None: """ Checking if expires_at is available can be removed soon after deployment, it is only needed to cater for in-flight sessions. @@ -97,7 +96,7 @@ def _load(self) -> None: logger.debug( "finding eq_session_id in database", eq_session_id=self.eq_session_id ) - self._eq_session: Optional[EQSession] = current_app.eq["storage"].get(EQSession, self.eq_session_id) # type: ignore + self._eq_session: EQSession | None = current_app.eq["storage"].get(EQSession, self.eq_session_id) # type: ignore if self._eq_session and self._eq_session.session_data: self.user_id = self._eq_session.user_id diff --git a/app/forms/duration_form.py b/app/forms/duration_form.py index 221256a3c9..e10038fa93 100644 --- a/app/forms/duration_form.py +++ b/app/forms/duration_form.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Callable, Mapping, Optional +from typing import Callable, Mapping from wtforms import Form @@ -12,7 +12,7 @@ # pylint: disable=no-member class DurationForm(Form): def validate( - self, extra_validators: Optional[dict[str, list[Callable]]] = None + self, extra_validators: dict[str, list[Callable]] | None = None ) -> bool: super().validate(extra_validators) @@ -43,8 +43,8 @@ def _set_error(self, key: str) -> None: list(self._fields.values())[0].errors = [self.answer_errors[key]] @property - def data(self) -> Optional[dict[str, Optional[str]]]: - data: dict[str, Optional[str]] = super().data + def data(self) -> dict[str, str | None] | None: + data: dict[str, str | None] = super().data if all(value is None for value in data.values()): return None return data diff --git a/app/forms/field_handlers/__init__.py b/app/forms/field_handlers/__init__.py index 6f32ee4d20..53d5d96413 100644 --- a/app/forms/field_handlers/__init__.py +++ b/app/forms/field_handlers/__init__.py @@ -1,5 +1,3 @@ -from typing import Optional - from werkzeug.datastructures import ImmutableDict from app.forms.field_handlers.address_handler import AddressHandler @@ -49,7 +47,7 @@ def get_field_handler( rule_evaluator: RuleEvaluator, error_messages: ImmutableDict, disable_validation: bool = False, - question_title: Optional[str] = None, + question_title: str | None = None, ) -> FieldHandler: return FIELD_HANDLER_MAPPINGS[answer_schema["type"]]( answer_schema=answer_schema, diff --git a/app/forms/field_handlers/date_handlers.py b/app/forms/field_handlers/date_handlers.py index 6330329aa4..e5882c5e53 100644 --- a/app/forms/field_handlers/date_handlers.py +++ b/app/forms/field_handlers/date_handlers.py @@ -1,6 +1,6 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Optional +from typing import Any from dateutil.relativedelta import relativedelta from wtforms.fields.core import UnboundField @@ -56,7 +56,7 @@ def get_field(self) -> UnboundField | DateField: ) def get_min_max_validator( - self, minimum_date: Optional[datetime], maximum_date: Optional[datetime] + self, minimum_date: datetime | None, maximum_date: datetime | None ) -> SingleDatePeriodCheck: messages = self.answer_schema.get("validation", {}).get("messages") @@ -67,7 +67,7 @@ def get_min_max_validator( maximum_date=maximum_date, ) - def get_referenced_date(self, key: str) -> Optional[datetime]: + def get_referenced_date(self, key: str) -> datetime | None: """ Gets value of the referenced date type, whether it is a value, id of an answer or a meta date. @@ -83,8 +83,8 @@ def get_referenced_date(self, key: str) -> Optional[datetime]: @staticmethod def transform_date_by_offset( - date_to_offset: Optional[datetime], offset: dict[str, int] - ) -> Optional[datetime]: + date_to_offset: datetime | None, offset: dict[str, int] + ) -> datetime | None: """ Adds/subtracts offset from a date and returns the new offset value @@ -102,7 +102,7 @@ def transform_date_by_offset( return date_to_offset - def get_date_value(self, key: str) -> Optional[datetime]: + def get_date_value(self, key: str) -> datetime | None: """ Gets attributes within a minimum or maximum of a date field and validates that the entered date is valid. @@ -129,7 +129,7 @@ def get_field(self) -> UnboundField | MonthYearDateField: ) def get_min_max_validator( - self, minimum_date: Optional[datetime], maximum_date: Optional[datetime] + self, minimum_date: datetime | None, maximum_date: datetime | None ) -> SingleDatePeriodCheck: messages = self.answer_schema.get("validation", {}).get("messages") @@ -158,7 +158,7 @@ def get_field(self) -> UnboundField | YearDateField: ) def get_min_max_validator( - self, minimum_date: Optional[datetime], maximum_date: Optional[datetime] + self, minimum_date: datetime | None, maximum_date: datetime | None ) -> SingleDatePeriodCheck: messages = self.answer_schema.get("validation", {}).get("messages") diff --git a/app/forms/field_handlers/field_handler.py b/app/forms/field_handlers/field_handler.py index 38a4ee4c68..4cb0d736ce 100644 --- a/app/forms/field_handlers/field_handler.py +++ b/app/forms/field_handlers/field_handler.py @@ -1,6 +1,6 @@ from abc import ABC from functools import cached_property -from typing import Any, Mapping, Optional, Union +from typing import Any, Mapping from wtforms import Field, validators from wtforms.validators import Optional as OptionalValidator @@ -24,7 +24,7 @@ def __init__( rule_evaluator: RuleEvaluator, error_messages: Mapping[str, str], disable_validation: bool = False, - question_title: Optional[str] = None, + question_title: str | None = None, ): self.answer_schema = answer_schema self.value_source_resolver = value_source_resolver @@ -44,7 +44,7 @@ def validators(self) -> list[validators.Optional]: return [] @cached_property - def label(self) -> Optional[str]: + def label(self) -> str | None: return self.answer_schema.get("label") @cached_property @@ -57,7 +57,7 @@ def get_validation_message(self, message_key: str) -> str: or self.error_messages[message_key] ) - def get_mandatory_validator(self) -> Union[ResponseRequired, OptionalValidator]: + def get_mandatory_validator(self) -> ResponseRequired | OptionalValidator: if self.answer_schema["mandatory"] is True: mandatory_message = self.get_validation_message(self.MANDATORY_MESSAGE_KEY) @@ -70,10 +70,10 @@ def get_mandatory_validator(self) -> Union[ResponseRequired, OptionalValidator]: def get_schema_value( self, schema_element: dict - ) -> Union[ValueSourceEscapedTypes, ValueSourceTypes]: + ) -> ValueSourceEscapedTypes | ValueSourceTypes: if isinstance(schema_element["value"], dict): return self.value_source_resolver.resolve(schema_element["value"]) - schema_element_value: Union[ValueSourceEscapedTypes, ValueSourceTypes] = ( + schema_element_value: ValueSourceEscapedTypes | ValueSourceTypes = ( schema_element["value"] ) return schema_element_value diff --git a/app/forms/field_handlers/mobile_number_handler.py b/app/forms/field_handlers/mobile_number_handler.py index 2344ad5c69..fc78691a52 100644 --- a/app/forms/field_handlers/mobile_number_handler.py +++ b/app/forms/field_handlers/mobile_number_handler.py @@ -1,5 +1,4 @@ from functools import cached_property -from typing import Union from wtforms import StringField from wtforms.fields.core import UnboundField @@ -7,7 +6,7 @@ from app.forms.field_handlers.field_handler import FieldHandler from app.forms.validators import MobileNumberCheck, ResponseRequired -MobileNumberValidatorTypes = list[Union[ResponseRequired, MobileNumberCheck]] +MobileNumberValidatorTypes = list[ResponseRequired | MobileNumberCheck] class MobileNumberHandler(FieldHandler): diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index d5d712e89c..bc721d389a 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -1,5 +1,6 @@ from functools import cached_property -from typing import Any, Union + +from typing import Any from wtforms import DecimalField, IntegerField from wtforms.fields.core import UnboundField @@ -15,7 +16,7 @@ from app.settings import MAX_NUMBER NumberValidatorTypes = list[ - Union[ResponseRequired, NumberCheck, NumberRange, DecimalPlaces] + ResponseRequired | NumberCheck | NumberRange | DecimalPlaces ] @@ -58,7 +59,7 @@ def max_decimals(self) -> int: @property def _field_type( self, - ) -> type[Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator]]: + ) -> type[DecimalFieldWithSeparator | IntegerFieldWithSeparator]: return ( DecimalFieldWithSeparator if self.max_decimals > 0 @@ -80,7 +81,7 @@ def get_field(self) -> UnboundField | DecimalField | IntegerField: def _get_number_field_validators( self, - ) -> list[Union[NumberCheck, NumberRange, DecimalPlaces]]: + ) -> list[NumberCheck | NumberRange | DecimalPlaces]: answer_errors = dict(self.error_messages) for error_key in self.validation_messages.keys(): diff --git a/app/forms/field_handlers/select_handlers.py b/app/forms/field_handlers/select_handlers.py index e76e7d65cf..fb56a3b172 100644 --- a/app/forms/field_handlers/select_handlers.py +++ b/app/forms/field_handlers/select_handlers.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Sequence +from typing import Any, Sequence from wtforms.fields.core import UnboundField @@ -56,7 +56,7 @@ class SelectHandler(SelectHandlerBase): MANDATORY_MESSAGE_KEY = "MANDATORY_RADIO" @staticmethod - def coerce_str_unless_none(value: Optional[str]) -> Optional[str]: + def coerce_str_unless_none(value: str | None) -> str | None: """ Coerces a value using str() unless that value is None :param value: Any value that can be coerced using str() or None diff --git a/app/forms/field_handlers/string_handler.py b/app/forms/field_handlers/string_handler.py index 161cb9e1ff..2f1d6d45cd 100644 --- a/app/forms/field_handlers/string_handler.py +++ b/app/forms/field_handlers/string_handler.py @@ -1,5 +1,4 @@ from functools import cached_property -from typing import Union from wtforms import StringField, validators from wtforms.fields.core import UnboundField @@ -7,7 +6,7 @@ from app.forms.field_handlers.field_handler import FieldHandler -StringValidatorTypes = list[Union[validators.Optional, validators.Length]] +StringValidatorTypes = list[validators.Optional | validators.Length] class StringHandler(FieldHandler): diff --git a/app/forms/field_handlers/text_area_handler.py b/app/forms/field_handlers/text_area_handler.py index 5bc484f10a..d0885b5abe 100644 --- a/app/forms/field_handlers/text_area_handler.py +++ b/app/forms/field_handlers/text_area_handler.py @@ -1,5 +1,4 @@ from functools import cached_property -from typing import Union from wtforms import validators from wtforms.fields.core import UnboundField @@ -8,7 +7,7 @@ from app.forms.field_handlers.field_handler import FieldHandler from app.forms.fields import MaxTextAreaField -TextAreaValidatorTypes = list[Union[validators.Optional, validators.Length]] +TextAreaValidatorTypes = list[validators.Optional | validators.Length] class TextAreaHandler(FieldHandler): diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index bea1885e14..e11aca2700 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -5,7 +5,7 @@ from collections.abc import Callable from datetime import datetime, timedelta, timezone from decimal import Decimal -from typing import Any, Mapping, Optional, Sequence, Union +from typing import Any, Mapping, Sequence from dateutil.relativedelta import relativedelta from flask_wtf import FlaskForm @@ -33,7 +33,7 @@ QuestionnaireExtraValidators = Mapping[str, Sequence[Callable]] Period = Mapping[str, int] PeriodLimits = Mapping[str, Any] -Error = Union[Mapping, Sequence] +Error = Mapping | Sequence Errors = Mapping[str, Error] ErrorList = Sequence[tuple[str, str]] @@ -45,8 +45,8 @@ def __init__( schema: QuestionnaireSchema, question_schema: QuestionSchemaType, data_stores: DataStores, - location: Union[None, Location, RelationshipLocation], - **kwargs: Union[MultiDict, Mapping, None], + location: None | Location | RelationshipLocation, + **kwargs: MultiDict | Mapping | None, ): self.schema = schema self.question = question_schema @@ -66,7 +66,7 @@ def __init__( super().__init__(**kwargs) def validate( - self, extra_validators: Optional[QuestionnaireExtraValidators] = None + self, extra_validators: QuestionnaireExtraValidators | None = None ) -> bool: """ Validate this form as usual and check for any form-level validation errors based on question type @@ -213,7 +213,7 @@ def _get_target_total_and_currency( def validate_date_range_with_period_limits_and_single_date_limits( self, - question_id: Union[str, Sequence[Mapping]], + question_id: str | Sequence[Mapping], period_limits: PeriodLimits, period_range: timedelta, ) -> None: @@ -244,7 +244,7 @@ def _validate_date_range_question( period_from_id: str, period_to_id: str, messages: Mapping[str, str], - period_limits: Optional[PeriodLimits], + period_limits: PeriodLimits | None, ) -> bool: period_from = getattr(self, period_from_id) period_to = getattr(self, period_to_id) @@ -268,7 +268,7 @@ def _validate_calculated_question( calculation: Calculation, question: QuestionSchemaType, target_total: Any, - currency: Optional[str], + currency: str | None, decimal_places: int | None, ) -> bool: messages = None @@ -370,8 +370,8 @@ def _get_offset_value(period_object: Mapping[str, int]) -> timedelta: @staticmethod def _get_period_limits( - limits: Optional[PeriodLimits], - ) -> tuple[Optional[dict[str, Any]], Optional[dict[str, Any]]]: + limits: PeriodLimits | None, + ) -> tuple[dict[str, Any] | None, dict[str, Any] | None]: minimum, maximum = None, None if limits: if "minimum" in limits: @@ -405,7 +405,7 @@ def _get_formatted_calculation_values( @staticmethod def _get_calculation_total( - calculation_type: Callable, values: Sequence[Union[float, int, Decimal, str]] + calculation_type: Callable, values: Sequence[float | int | Decimal | str] ) -> Decimal: result: Decimal = calculation_type(Decimal(value or 0) for value in values) return result @@ -447,7 +447,7 @@ def get_data(self, answer_id: str) -> str: def _option_value_in_data( answer: Mapping[str, str], option: Mapping[str, Any], - data: Union[MultiDict[str, Any], Mapping[str, Any]], + data: MultiDict[str, Any] | Mapping[str, Any], ) -> bool: data_to_inspect = data.to_dict(flat=False) if isinstance(data, MultiDict) else data diff --git a/app/forms/validators.py b/app/forms/validators.py index 9426038a8d..2d76c8f4a9 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -4,7 +4,8 @@ import re from datetime import datetime, timezone from decimal import Decimal, InvalidOperation -from typing import TYPE_CHECKING, Iterable, Mapping, Optional, Sequence, Union +from typing import TYPE_CHECKING, Iterable, Mapping, Sequence + import flask_babel from babel import numbers @@ -39,19 +40,19 @@ ) email_regex = re.compile(r"^.+@([^.@][^@\s]+)$") -OptionalMessage = Optional[Mapping[str, str]] -NumType = Union[int, Decimal] +OptionalMessage = Mapping[str, str] | None +NumType = int | Decimal PeriodType = Mapping[str, int] class NumberCheck: - def __init__(self, message: Optional[str] = None): + def __init__(self, message: str | None = None): self.message = message or error_messages["INVALID_NUMBER"] def __call__( self, form: FlaskForm, - field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], + field: DecimalFieldWithSeparator | IntegerFieldWithSeparator, ) -> None: try: # number is sanitised to guard against inputs like `,NaN_` etc @@ -108,12 +109,12 @@ class NumberRange: def __init__( self, - minimum: Optional[NumType] = None, + minimum: NumType | None = None, minimum_exclusive: bool = False, - maximum: Optional[NumType] = None, + maximum: NumType | None = None, maximum_exclusive: bool = False, messages: OptionalMessage = None, - currency: Optional[str] = None, + currency: str | None = None, ): self.minimum = minimum self.maximum = maximum @@ -125,7 +126,7 @@ def __init__( def __call__( self, form: "QuestionnaireForm", - field: Union[DecimalFieldWithSeparator, IntegerFieldWithSeparator], + field: DecimalFieldWithSeparator | IntegerFieldWithSeparator, ) -> None: value: int | Decimal | None = field.data @@ -141,7 +142,7 @@ def __call__( def validate_minimum( self, *, value: NumType, decimal_limit: int | None - ) -> Optional[str]: + ) -> str | None: if self.minimum is None: return None @@ -161,7 +162,7 @@ def validate_minimum( def validate_maximum( self, *, value: NumType, decimal_limit: int | None - ) -> Optional[str]: + ) -> str | None: if self.maximum is None: return None @@ -240,7 +241,7 @@ def __call__(self, form: Sequence["QuestionnaireForm"], field: Field) -> None: class DateRequired: field_flags = ("required",) - def __init__(self, message: Optional[str] = None): + def __init__(self, message: str | None = None): self.message = message or error_messages["MANDATORY_DATE"] def __call__(self, form: "QuestionnaireForm", field: DateField) -> None: @@ -259,7 +260,7 @@ def __call__(self, form: "QuestionnaireForm", field: DateField) -> None: class DateCheck: - def __init__(self, message: Optional[str] = None): + def __init__(self, message: str | None = None): self.message = message or error_messages["INVALID_DATE"] def __call__(self, form: "QuestionnaireForm", field: StringField) -> None: @@ -285,8 +286,8 @@ def __init__( self, messages: OptionalMessage = None, date_format: str = "d MMMM yyyy", - minimum_date: Optional[datetime] = None, - maximum_date: Optional[datetime] = None, + minimum_date: datetime | None = None, + maximum_date: datetime | None = None, ): self.messages = {**error_messages, **(messages or {})} self.minimum_date = minimum_date @@ -326,8 +327,8 @@ class DateRangeCheck: def __init__( self, messages: OptionalMessage = None, - period_min: Optional[dict[str, int]] = None, - period_max: Optional[dict[str, int]] = None, + period_min: dict[str, int] | None = None, + period_max: dict[str, int] | None = None, ): self.messages = {**error_messages, **(messages or {})} self.period_min = period_min @@ -408,9 +409,7 @@ def _build_range_length_error(period_object: PeriodType) -> str: class SumCheck: - def __init__( - self, messages: OptionalMessage = None, currency: Optional[str] = None - ): + def __init__(self, messages: OptionalMessage = None, currency: str | None = None): self.messages = {**error_messages, **(messages or {})} self.currency = currency @@ -457,8 +456,8 @@ def __call__( @staticmethod def _is_valid( condition: str, - total: Union[Decimal, float], - target_total: Union[Decimal, float], + total: Decimal | float, + target_total: Decimal | float, ) -> tuple[bool, str]: if condition == "equals": return total == target_total, "TOTAL_SUM_NOT_EQUALS" @@ -516,7 +515,7 @@ def __call__(self, form: "QuestionnaireForm", field: StringField) -> None: class EmailTLDCheck: - def __init__(self, message: Optional[str] = None): + def __init__(self, message: str | None = None): self.message = message or error_messages["INVALID_EMAIL_FORMAT"] def __call__(self, form: "QuestionnaireForm", field: StringField) -> None: diff --git a/app/helpers/address_lookup_api_helper.py b/app/helpers/address_lookup_api_helper.py index d1f5e7991a..98f72a488d 100644 --- a/app/helpers/address_lookup_api_helper.py +++ b/app/helpers/address_lookup_api_helper.py @@ -1,5 +1,4 @@ from datetime import datetime, timezone -from typing import Optional from uuid import uuid4 from flask import current_app @@ -11,7 +10,7 @@ def get_jwk_from_secret(secret: str) -> jwk.JWK: return jwk.JWK(kty="oct", k=base64url_encode(secret.encode("utf-8"))) -def get_address_lookup_api_auth_token() -> Optional[str]: +def get_address_lookup_api_auth_token() -> str | None: if current_app.config["ADDRESS_LOOKUP_API_AUTH_ENABLED"]: secret = current_app.eq["secret_store"].get_secret_by_name( # type: ignore "ADDRESS_LOOKUP_API_AUTH_TOKEN_SECRET" diff --git a/app/helpers/header_helpers.py b/app/helpers/header_helpers.py index a4aeb8654e..1525a6b901 100644 --- a/app/helpers/header_helpers.py +++ b/app/helpers/header_helpers.py @@ -1,11 +1,9 @@ -from typing import Union - from werkzeug.datastructures import EnvironHeaders def get_span_and_trace( headers: EnvironHeaders, -) -> Union[tuple[None, None], tuple[str, str]]: +) -> tuple[None, None] | tuple[str, str]: try: trace, span = headers.get("X-Cloud-Trace-Context").split("/") # type: ignore except (ValueError, AttributeError): diff --git a/app/jinja_filters.py b/app/jinja_filters.py index c3dfd776e3..9c8754a8ee 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -3,7 +3,7 @@ import re from datetime import datetime from decimal import Decimal -from typing import Any, Callable, Literal, Mapping, Optional, TypeAlias, Union +from typing import Any, Callable, Literal, Mapping, TypeAlias import flask import flask_babel @@ -31,7 +31,7 @@ UnitLengthType: TypeAlias = Literal["short", "long", "narrow"] -def mark_safe(context: nodes.EvalContext, value: str) -> Union[Markup, str]: +def mark_safe(context: nodes.EvalContext, value: str) -> Markup | str: return Markup(value) if context.autoescape else value @@ -61,7 +61,7 @@ def get_currency_symbol(currency: str = "GBP") -> str: @blueprint.app_template_filter() -def format_percentage(value: Union[int, float, Decimal]) -> str: +def format_percentage(value: int | float | Decimal) -> str: return f"{value}%" @@ -151,9 +151,7 @@ def get_format_date(value: Markup) -> str: @pass_eval_context @blueprint.app_template_filter() -def format_datetime( - context: nodes.EvalContext, date_time: datetime -) -> Union[str, Markup]: +def format_datetime(context: nodes.EvalContext, date_time: datetime) -> str | Markup: # flask babel on formatting will automatically convert based on the time zone specified in setup.py formatted_date = flask_babel.format_date(date_time, format="d MMMM yyyy") formatted_time = flask_babel.format_time(date_time, format="HH:mm") @@ -266,7 +264,7 @@ def get_min_max_value_width( @blueprint.app_template_filter() -def get_width_for_number(answer: AnswerType) -> Optional[int]: +def get_width_for_number(answer: AnswerType) -> int | None: allowable_widths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30, 40, 50] min_value_width = get_min_max_value_width("minimum", answer, 0) @@ -287,7 +285,7 @@ def get_width_for_number_processor() -> dict[str, Callable]: class LabelConfig: - def __init__(self, _for: str, text: str, description: Optional[str] = None) -> None: + def __init__(self, _for: str, text: str, description: str | None = None) -> None: self._for = _for self.text = text self.description = description @@ -299,7 +297,7 @@ def __init__( option: SelectFieldBase._Option, index: int, answer: AnswerType, - form: Optional[FormType] = None, + form: FormType | None = None, ) -> None: self.id = option.id self.name = option.name @@ -395,7 +393,7 @@ def map_select_config_processor() -> dict[str, Callable]: @blueprint.app_template_filter() def map_relationships_config( - form: Mapping[str, str], answer: Mapping[str, Union[int, slice]] + form: Mapping[str, str], answer: Mapping[str, int | slice] ) -> list[RelationshipRadioConfig]: options = form["fields"][answer["id"]] @@ -459,7 +457,7 @@ def __init__( class SummaryRowItemValue: - def __init__(self, text: str, other: Optional[str] = None) -> None: + def __init__(self, text: str, other: str | None = None) -> None: self.text = text if other or other == 0: @@ -588,17 +586,17 @@ def __init__( @blueprint.app_template_filter() def map_summary_item_config( - group: dict[str, Union[list, dict]], + group: dict[str, list | dict], summary_type: str, answers_are_editable: bool, no_answer_provided: str, edit_link_text: str, edit_link_aria_label: str, - calculated_question: Optional[dict[str, list]], + calculated_question: dict[str, list] | None, remove_link_text: str | None = None, remove_link_aria_label: str | None = None, -) -> list[Union[dict[str, list], SummaryRow]]: - rows: list[Union[dict[str, list], SummaryRow]] = [] +) -> list[dict[str, list] | SummaryRow]: + rows: list[dict[str, list] | SummaryRow] = [] for block in group["blocks"]: if block.get("question"): diff --git a/app/questionnaire/dynamic_answer_options.py b/app/questionnaire/dynamic_answer_options.py index 0ee21e60b5..f267f57208 100644 --- a/app/questionnaire/dynamic_answer_options.py +++ b/app/questionnaire/dynamic_answer_options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Mapping, Union +from typing import Mapping from app.questionnaire.rules.operator import Operator from app.questionnaire.rules.rule_evaluator import RuleEvaluator, RuleEvaluatorTypes @@ -18,9 +18,7 @@ class DynamicAnswerOptions: def evaluate(self) -> tuple[dict[str, str], ...]: values = self.dynamic_options_schema["values"] - resolved_values: Union[ - ValueSourceEscapedTypes, ValueSourceTypes, RuleEvaluatorTypes - ] + resolved_values: ValueSourceEscapedTypes | ValueSourceTypes | RuleEvaluatorTypes if "source" in values: if values["source"] != "answers": diff --git a/app/questionnaire/rules/helpers.py b/app/questionnaire/rules/helpers.py index 5db0abb155..3c6bf24ac9 100644 --- a/app/questionnaire/rules/helpers.py +++ b/app/questionnaire/rules/helpers.py @@ -1,12 +1,12 @@ from datetime import datetime from decimal import Decimal from functools import wraps -from typing import Any, Callable, Sequence, Union +from typing import Any, Callable, Sequence -ValueTypes = Union[bool, str, int, float, Decimal, None, datetime] +ValueTypes = bool | str | int | float | Decimal | None | datetime -def _casefold(value: Union[list, ValueTypes]) -> Union[list, ValueTypes]: +def _casefold(value: list | ValueTypes) -> list | ValueTypes: if isinstance(value, str): return value.casefold() diff --git a/app/questionnaire/rules/operations_helper.py b/app/questionnaire/rules/operations_helper.py index 29d0c9be8e..9bce3a555c 100644 --- a/app/questionnaire/rules/operations_helper.py +++ b/app/questionnaire/rules/operations_helper.py @@ -5,7 +5,7 @@ """ from datetime import date -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from app.questionnaire.questionnaire_schema import QuestionnaireSchema from app.questionnaire.rules.operations import DateOffset, Operations @@ -30,10 +30,10 @@ def __init__( def string_to_datetime( self, - date_string: Optional[str], - offset: Optional[DateOffset] = None, + date_string: str | None, + offset: DateOffset | None = None, offset_by_full_weeks: bool = False, - ) -> Optional[date]: + ) -> date | None: return self.ops.resolve_date_from_string( date_string, offset, offset_by_full_weeks ) diff --git a/app/questionnaire/rules/operator.py b/app/questionnaire/rules/operator.py index 0b7ec073b4..b387db11f3 100644 --- a/app/questionnaire/rules/operator.py +++ b/app/questionnaire/rules/operator.py @@ -1,5 +1,5 @@ from datetime import date -from typing import TYPE_CHECKING, Generator, Iterable, Optional, Sequence, Union +from typing import TYPE_CHECKING, Generator, Iterable, Sequence from app.questionnaire.rules.helpers import ValueTypes @@ -40,15 +40,13 @@ def __init__(self, name: str, operations: "Operations") -> None: Operator.ANY_IN, } - def evaluate( - self, operands: Union[Generator, Iterable] - ) -> Union[bool, Optional[date]]: + def evaluate(self, operands: Generator | Iterable) -> bool | date | None: if self._ensure_operands_not_none: operands = list(operands) if self._any_operands_none(*operands): return False - value: Union[bool, Optional[date]] = ( + value: bool | date | None = ( self._operation(operands) if self.name in {Operator.AND, Operator.OR} else self._operation(*operands) @@ -56,7 +54,7 @@ def evaluate( return value @staticmethod - def _any_operands_none(*operands: Union[Sequence, ValueTypes]) -> bool: + def _any_operands_none(*operands: Sequence | ValueTypes) -> bool: return any(operand is None for operand in operands) diff --git a/app/questionnaire/rules/utils.py b/app/questionnaire/rules/utils.py index 7a8ca7e836..ea7d268f3f 100644 --- a/app/questionnaire/rules/utils.py +++ b/app/questionnaire/rules/utils.py @@ -1,5 +1,5 @@ from datetime import datetime, timezone -from typing import Optional, overload +from typing import overload from dateutil import parser @@ -16,7 +16,7 @@ def parse_datetime(date_string: None) -> None: ... # pragma: no cover def parse_datetime(date_string: str) -> datetime: ... # pragma: no cover -def parse_datetime(date_string: Optional[str]) -> Optional[datetime]: +def parse_datetime(date_string: str | None) -> datetime | None: """ :param date_string: string representing a date :return: datetime of that date string diff --git a/app/secrets.py b/app/secrets.py index 840f6047cb..073f0d0b93 100644 --- a/app/secrets.py +++ b/app/secrets.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional +from typing import Mapping SecretsType = Mapping[str, Mapping[str, str]] @@ -13,7 +13,7 @@ def validate_required_secrets( - secrets: SecretsType, additional_required_secrets: Optional[list[str]] = None + secrets: SecretsType, additional_required_secrets: list[str] | None = None ) -> None: all_required_secrets = ( REQUIRED_SECRETS + additional_required_secrets @@ -29,5 +29,5 @@ class SecretStore: def __init__(self, secrets: SecretsType) -> None: self.secrets = secrets.get("secrets", {}) - def get_secret_by_name(self, secret_name: str) -> Optional[str]: + def get_secret_by_name(self, secret_name: str) -> str | None: return self.secrets.get(secret_name) diff --git a/app/storage/datastore.py b/app/storage/datastore.py index 790ff2ca40..4eac362543 100644 --- a/app/storage/datastore.py +++ b/app/storage/datastore.py @@ -1,5 +1,3 @@ -from typing import Optional - from google.api_core.retry import Retry from google.cloud import datastore from google.cloud.datastore import Entity @@ -36,7 +34,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: return True @Retry() - def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> ModelTypes | None: storage_model = StorageModel(model_type=model_type) key = self.client.key(storage_model.table_name, key_value) diff --git a/app/storage/dynamodb.py b/app/storage/dynamodb.py index d578999009..f3f9335d31 100644 --- a/app/storage/dynamodb.py +++ b/app/storage/dynamodb.py @@ -1,5 +1,3 @@ -from typing import Optional - import boto3 from botocore.exceptions import ClientError @@ -33,7 +31,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: raise # pragma: no cover - def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> ModelTypes | None: storage_model = StorageModel(model_type=model_type) table = self.client.Table(storage_model.table_name) key = {storage_model.key_field: key_value} diff --git a/app/storage/encrypted_questionnaire_storage.py b/app/storage/encrypted_questionnaire_storage.py index 49c66b8c46..c83abddcce 100644 --- a/app/storage/encrypted_questionnaire_storage.py +++ b/app/storage/encrypted_questionnaire_storage.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Optional, Union import snappy from flask import current_app @@ -21,8 +20,8 @@ def save( self, data: str, collection_exercise_sid: str, - submitted_at: Optional[datetime] = None, - expires_at: Optional[datetime] = None, + submitted_at: datetime | None = None, + expires_at: datetime | None = None, ) -> None: compressed_data = snappy.compress(data) encrypted_data = self.encrypter.encrypt_data(compressed_data) @@ -39,7 +38,7 @@ def save( def get_user_data( self, - ) -> Union[tuple[None, None, None, None], tuple[str, str, int, Optional[datetime]]]: + ) -> tuple[None, None, None, None] | tuple[str, str, int, datetime | None]: questionnaire_state = self._find_questionnaire_state() if questionnaire_state and questionnaire_state.state_data: version = questionnaire_state.version @@ -58,7 +57,7 @@ def delete(self) -> None: if questionnaire_state: current_app.eq["storage"].delete(questionnaire_state) # type: ignore - def _find_questionnaire_state(self) -> Optional[QuestionnaireState]: + def _find_questionnaire_state(self) -> QuestionnaireState | None: logger.debug("getting questionnaire data", user_id=self._user_id) state: QuestionnaireState = current_app.eq["storage"].get(QuestionnaireState, self._user_id) # type: ignore return state diff --git a/app/storage/redis.py b/app/storage/redis.py index 9a3f5c4b27..b9aa1e2d7b 100644 --- a/app/storage/redis.py +++ b/app/storage/redis.py @@ -1,5 +1,4 @@ from datetime import datetime, timezone -from typing import Optional import redis from redis.exceptions import ConnectionError as RedisConnectionError @@ -56,7 +55,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: return True - def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> ModelTypes | None: storage_model = StorageModel(model_type=model_type) try: item = self.client.get(key_value) diff --git a/app/storage/storage.py b/app/storage/storage.py index 49a925204d..da0704831c 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -2,22 +2,22 @@ from abc import ABC, abstractmethod from functools import cached_property -from typing import Any, Optional, TypedDict, Union +from typing import Any, TypedDict from flask import current_app from google.cloud import datastore from app.data_models import app_models -ModelSchemaTypes = Union[ - app_models.QuestionnaireStateSchema, - app_models.EQSessionSchema, - app_models.UsedJtiClaimSchema, -] +ModelSchemaTypes = ( + app_models.QuestionnaireStateSchema + | app_models.EQSessionSchema + | app_models.UsedJtiClaimSchema +) -ModelTypes = Union[ - app_models.QuestionnaireState, app_models.EQSession, app_models.UsedJtiClaim -] +ModelTypes = ( + app_models.QuestionnaireState | app_models.EQSession | app_models.UsedJtiClaim +) class TableConfig(TypedDict, total=False): @@ -65,7 +65,7 @@ def key_field(self) -> str: return self._config["key_field"] @cached_property - def expiry_field(self) -> Optional[str]: + def expiry_field(self) -> str | None: return self._config.get("expiry_field") @cached_property @@ -81,7 +81,7 @@ def serialize(self, model_to_serialize: ModelTypes) -> dict: serialized_data: dict = self._schema.dump(model_to_serialize) return serialized_data - def deserialize(self, serialized_item: Union[datastore.Entity]) -> ModelTypes: + def deserialize(self, serialized_item: datastore.Entity) -> ModelTypes: deserialized_data: ModelTypes = self._schema.load(serialized_item) return deserialized_data @@ -95,7 +95,7 @@ def put(self, model: ModelTypes, overwrite: bool = True) -> bool: pass # pragma: no cover @abstractmethod - def get(self, model_type: type[ModelTypes], key_value: str) -> Optional[ModelTypes]: + def get(self, model_type: type[ModelTypes], key_value: str) -> ModelTypes | None: pass # pragma: no cover @abstractmethod diff --git a/app/storage/storage_encryption.py b/app/storage/storage_encryption.py index 9b9311a0e1..cd86b74c12 100644 --- a/app/storage/storage_encryption.py +++ b/app/storage/storage_encryption.py @@ -1,5 +1,4 @@ import hashlib -from typing import Optional, Union from jwcrypto import jwe, jwk from jwcrypto.common import base64url_encode @@ -13,7 +12,7 @@ class StorageEncryption: def __init__( - self, user_id: Optional[str], user_ik: Optional[str], pepper: Optional[str] + self, user_id: str | None, user_ik: str | None, pepper: str | None ) -> None: if not user_id: raise ValueError("user_id not provided") @@ -38,7 +37,7 @@ def _generate_key(user_id: str, user_ik: str, pepper: str) -> jwk.JWK: return jwk.JWK(**password) - def encrypt_data(self, data: Union[str, dict]) -> str: + def encrypt_data(self, data: str | dict) -> str: if isinstance(data, dict): data = json_dumps(data) diff --git a/app/submitter/previously_submitted_exception.py b/app/submitter/previously_submitted_exception.py index f467943703..70249a63d7 100644 --- a/app/submitter/previously_submitted_exception.py +++ b/app/submitter/previously_submitted_exception.py @@ -1,8 +1,5 @@ -from typing import Union - - class PreviouslySubmittedException(Exception): - def __init__(self, value: Union[str, int]) -> None: + def __init__(self, value: str | int) -> None: super().__init__() self.value = value diff --git a/app/submitter/submitter.py b/app/submitter/submitter.py index 958d17b812..21d6bd7698 100644 --- a/app/submitter/submitter.py +++ b/app/submitter/submitter.py @@ -1,4 +1,4 @@ -from typing import Mapping, Optional, Union +from typing import Mapping from uuid import uuid4 from google.api_core.exceptions import Forbidden @@ -19,7 +19,7 @@ def send_message( message: str, tx_id: str, case_id: str, - **kwargs: Mapping[str, Union[str, int]], + **kwargs: Mapping[str, str | int], ) -> bool: logger.info("sending message") logger.info( @@ -77,8 +77,8 @@ def __init__( secondary_host: str, port: int, queue: str, - username: Optional[str] = None, - password: Optional[str] = None, + username: str | None = None, + password: str | None = None, ) -> None: self.queue = queue if username and password: @@ -120,7 +120,7 @@ def _connect(self) -> BlockingConnection: raise err @staticmethod - def _disconnect(connection: Optional[BlockingConnection]) -> None: + def _disconnect(connection: BlockingConnection | None) -> None: try: if connection: logger.info("attempt to close connection", category="rabbitmq") diff --git a/app/survey_config/business_config.py b/app/survey_config/business_config.py index a4c3362ed1..bcd3dcb748 100644 --- a/app/survey_config/business_config.py +++ b/app/survey_config/business_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping, Optional +from typing import Iterable, Mapping, MutableMapping from urllib.parse import urlencode from warnings import warn @@ -31,7 +31,7 @@ def __post_init__(self) -> None: self.account_service_todo_url: str = f"{self.base_url}/surveys/todo" def _get_account_service_help_url( - self, *, is_authenticated: bool, ru_ref: Optional[str] + self, *, is_authenticated: bool, ru_ref: str | None ) -> str: if self.schema and is_authenticated and ru_ref: request_data = { @@ -52,8 +52,8 @@ def get_service_links( *, is_authenticated: bool, cookie_has_theme: bool, - ru_ref: Optional[str], - ) -> Optional[list[dict]]: + ru_ref: str | None, + ) -> list[dict] | None: links = ( [ HeaderLink( @@ -102,7 +102,7 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: return links - def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + def get_footer_legal_links(self, cookie_has_theme: bool) -> list[dict] | None: if cookie_has_theme: return [ Link(lazy_gettext("Cookies"), self.cookie_settings_url).as_dict(), diff --git a/app/survey_config/link.py b/app/survey_config/link.py index 155117c344..c6460db774 100644 --- a/app/survey_config/link.py +++ b/app/survey_config/link.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from typing import Optional from flask_babel.speaklater import LazyString @@ -8,8 +7,8 @@ class Link: text: LazyString url: str - target: Optional[str] = "_blank" - attributes: Optional[dict] = field(default_factory=dict) + target: str | None = "_blank" + attributes: dict | None = field(default_factory=dict) def as_dict(self): return {k: v for k, v in self.__dict__.items() if v} diff --git a/app/survey_config/social_survey_config.py b/app/survey_config/social_survey_config.py index c8fb8b7fbc..e3103231b0 100644 --- a/app/survey_config/social_survey_config.py +++ b/app/survey_config/social_survey_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping, Optional +from typing import Iterable, Mapping, MutableMapping from flask_babel import lazy_gettext @@ -52,7 +52,7 @@ def get_footer_links(self, cookie_has_theme: bool) -> list[dict]: return links - def get_footer_legal_links(self, cookie_has_theme: bool) -> Optional[list[dict]]: + def get_footer_legal_links(self, cookie_has_theme: bool) -> list[dict] | None: if cookie_has_theme: return [ Link(lazy_gettext("Cookies"), self.cookie_settings_url).as_dict(), diff --git a/app/survey_config/survey_config.py b/app/survey_config/survey_config.py index 36aeb5ae58..50d7876248 100644 --- a/app/survey_config/survey_config.py +++ b/app/survey_config/survey_config.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import Iterable, Mapping, MutableMapping, Optional +from typing import Iterable, Mapping, MutableMapping from flask_babel import lazy_gettext from flask_babel.speaklater import LazyString @@ -13,32 +13,32 @@ class SurveyConfig: """Valid options for defining survey-based configuration.""" - schema: Optional[QuestionnaireSchema] = None - copyright_declaration: Optional[LazyString] = lazy_gettext( + schema: QuestionnaireSchema | None = None + copyright_declaration: LazyString | None = lazy_gettext( "Crown copyright and database rights 2020 OS 100019153." ) - copyright_text: Optional[LazyString] = lazy_gettext( + copyright_text: LazyString | None = lazy_gettext( "Use of address data is subject to the terms and conditions." ) base_url: str = ACCOUNT_SERVICE_BASE_URL - account_service_my_account_url: Optional[str] = None - account_service_todo_url: Optional[str] = None - account_service_log_out_url: Optional[str] = None + account_service_my_account_url: str | None = None + account_service_todo_url: str | None = None + account_service_log_out_url: str | None = None accessibility_url: str = f"{ONS_URL}/help/accessibility/" what_we_do_url: str = f"{ONS_URL}/aboutus/whatwedo/" - masthead_logo: Optional[str] = None - masthead_logo_mobile: Optional[str] = None + masthead_logo: str | None = None + masthead_logo_mobile: str | None = None crest: bool = True - footer_links: Optional[Iterable[MutableMapping]] = None - footer_legal_links: Optional[Iterable[Mapping]] = None - survey_title: Optional[LazyString] = None - design_system_theme: Optional[str] = None + footer_links: Iterable[MutableMapping] | None = None + footer_legal_links: Iterable[Mapping] | None = None + survey_title: LazyString | None = None + design_system_theme: str | None = None sign_out_button_text: str = lazy_gettext("Save and exit survey") contact_us_url: str = field(init=False) cookie_settings_url: str = field(init=False) cookie_domain: str = field(init=False) privacy_and_data_protection_url: str = field(init=False) - language_code: Optional[str] = None + language_code: str | None = None def __post_init__(self) -> None: self.contact_us_url: str = f"{self.base_url}/contact-us/" @@ -57,16 +57,16 @@ def get_service_links( # pylint: disable=unused-argument, no-self-use *, is_authenticated: bool, cookie_has_theme: bool, - ru_ref: Optional[str], - ) -> Optional[list[dict]]: + ru_ref: str | None, + ) -> list[dict] | None: return None def get_footer_links( # pylint: disable=unused-argument, no-self-use self, cookie_has_theme: bool - ) -> Optional[list[dict]]: + ) -> list[dict] | None: return None def get_footer_legal_links( # pylint: disable=unused-argument, no-self-use self, cookie_has_theme: bool - ) -> Optional[list[dict]]: + ) -> list[dict] | None: return None diff --git a/app/utilities/strings.py b/app/utilities/strings.py index 1bfdf0fb37..f58ce64e10 100644 --- a/app/utilities/strings.py +++ b/app/utilities/strings.py @@ -1,8 +1,7 @@ import re -from typing import Union -def to_bytes(bytes_or_str: Union[bytes, str]) -> bytes: +def to_bytes(bytes_or_str: bytes | str) -> bytes: """ Converts supplied data into bytes if the data is of type str. :param bytes_or_str: Data to be converted. @@ -13,7 +12,7 @@ def to_bytes(bytes_or_str: Union[bytes, str]) -> bytes: return bytes_or_str -def to_str(bytes_or_str: Union[bytes, str]) -> str: +def to_str(bytes_or_str: bytes | str) -> str: """ Converts supplied data into a UTF-8 encoded string if the data is of type bytes. :param bytes_or_str: Data to be converted. diff --git a/app/utilities/types.py b/app/utilities/types.py index 9a0c7d8d2c..2d159b736b 100644 --- a/app/utilities/types.py +++ b/app/utilities/types.py @@ -12,15 +12,15 @@ RelationshipLocation, # pragma: no cover ) -LocationType: TypeAlias = Union["Location", "RelationshipLocation"] +LocationType: TypeAlias = Union["Location", "RelationshipLocation"] # noqa: UP007 SupplementaryDataKeyType: TypeAlias = tuple[str, str | None] SupplementaryDataValueType: TypeAlias = dict | str | list | None DateValidatorType: TypeAlias = Union[ "OptionalForm", "DateRequired", "DateCheck", "SingleDatePeriodCheck" -] +] # noqa: UP007 -ChoiceType: TypeAlias = Union["Choice", "ChoiceWithDetailAnswer"] +ChoiceType: TypeAlias = Union["Choice", "ChoiceWithDetailAnswer"] # noqa: UP007 ChoiceWidgetRenderType: TypeAlias = tuple[str, str, bool, str | None] diff --git a/app/views/contexts/email_form_context.py b/app/views/contexts/email_form_context.py index 1b248baec1..4c57f1eb49 100644 --- a/app/views/contexts/email_form_context.py +++ b/app/views/contexts/email_form_context.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from flask import url_for @@ -7,7 +7,7 @@ def build_confirmation_email_form_context( email_confirmation_form: EmailForm, -) -> dict[str, Union[bool, str, Any]]: +) -> dict[str, bool | str | Any]: return { "hide_sign_out_button": False, "sign_out_url": url_for("session.get_sign_out"), diff --git a/app/views/contexts/feedback_form_context.py b/app/views/contexts/feedback_form_context.py index 478dc6a62a..2757d3d831 100644 --- a/app/views/contexts/feedback_form_context.py +++ b/app/views/contexts/feedback_form_context.py @@ -1,5 +1,3 @@ -from typing import Union - from flask import url_for from app.forms.questionnaire_form import QuestionnaireForm @@ -9,7 +7,7 @@ def build_feedback_context( question_schema: QuestionSchemaType, form: QuestionnaireForm -) -> dict[str, Union[str, bool, dict]]: +) -> dict[str, str | bool | dict]: block = {"question": question_schema} context = build_question_context(block, form) context["hide_sign_out_button"] = False diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index b2e6986f84..937f2cfb8e 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Any, Iterable, Mapping, Optional, Union +from typing import Any, Iterable, Mapping from flask import url_for from flask_babel import lazy_gettext @@ -82,11 +82,11 @@ def __call__( def get_row_context_for_section( self, - section_name: Optional[str], + section_name: str | None, section_status: CompletionStatus, section_url: str, row_id: str, - ) -> dict[str, Union[str, list]]: + ) -> dict[str, str | list]: section_content = self.SECTION_CONTENT_STATES[section_status] context: dict = { "rowItems": [ @@ -119,7 +119,7 @@ def get_row_context_for_section( @staticmethod def get_section_url( - section_id: str, list_item_id: Optional[str], section_status: CompletionStatus + section_id: str, list_item_id: str | None, section_status: CompletionStatus ) -> str: if section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED: return url_for( @@ -137,8 +137,8 @@ def get_section_url( return url_for("questionnaire.get_section", section_id=section_id) def _get_row_for_repeating_section( - self, section_id: str, list_item_id: str, list_item_index: Optional[int] - ) -> dict[str, Union[str, list]]: + self, section_id: str, list_item_id: str, list_item_index: int | None + ) -> dict[str, str | list]: # Type ignore: section id will be valid and repeat will be present at this stage repeating_title: ImmutableDict = self._schema.get_repeating_title_for_section(section_id) # type: ignore @@ -153,11 +153,11 @@ def _get_row_for_repeating_section( def _get_row_for_section( self, - section_title: Optional[str], + section_title: str | None, section_id: str, - list_item_id: Optional[str] = None, - list_item_index: Optional[int] = None, - ) -> dict[str, Union[str, list]]: + list_item_id: str | None = None, + list_item_index: int | None = None, + ) -> dict[str, str | list]: row_id = f"{section_id}-{list_item_index}" if list_item_index else section_id section_status = self._data_stores.progress_store.get_section_status( @@ -173,7 +173,7 @@ def _get_row_for_section( def _get_rows( self, enabled_section_ids: Iterable[str] - ) -> list[dict[str, Union[str, list]]]: + ) -> list[dict[str, str | list]]: rows: list[dict] = [] for section_id in enabled_section_ids: @@ -209,7 +209,7 @@ def _individual_response_enabled(self) -> bool: return True @cached_property - def _individual_response_url(self) -> Union[str, None]: + def _individual_response_url(self) -> str | None: if ( self._individual_response_enabled and self._schema.get_individual_response_show_on_hub() diff --git a/app/views/contexts/preview/preview_block.py b/app/views/contexts/preview/preview_block.py index 187a0677cd..9181879d36 100644 --- a/app/views/contexts/preview/preview_block.py +++ b/app/views/contexts/preview/preview_block.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from werkzeug.datastructures import ImmutableDict @@ -19,12 +19,12 @@ def __init__( @staticmethod def _get_question( block: ImmutableDict, - ) -> dict[str, Union[str, dict]]: + ) -> dict[str, str | dict]: return PreviewQuestion( block=block, ).serialize() - def serialize(self) -> dict[str, Union[str, dict, Any]]: + def serialize(self) -> dict[str, str | dict | Any]: return { "question": self._question, } diff --git a/app/views/contexts/preview/preview_question.py b/app/views/contexts/preview/preview_question.py index 29364aa80d..30630f890a 100644 --- a/app/views/contexts/preview/preview_question.py +++ b/app/views/contexts/preview/preview_question.py @@ -1,4 +1,4 @@ -from typing import Any, Union +from typing import Any from flask_babel import lazy_gettext from werkzeug.datastructures import ImmutableDict @@ -50,7 +50,7 @@ def _build_answers(self) -> list[dict]: answers_list.append(answer_dict) return answers_list - def serialize(self) -> dict[str, Union[str, dict, Any]]: + def serialize(self) -> dict[str, str | dict | Any]: return { "id": self._block_id, "title": self._title, diff --git a/app/views/contexts/preview_context.py b/app/views/contexts/preview_context.py index f3c58b9e00..2abc9a9005 100644 --- a/app/views/contexts/preview_context.py +++ b/app/views/contexts/preview_context.py @@ -1,4 +1,4 @@ -from typing import Generator, Union +from typing import Generator from flask_babel import lazy_gettext @@ -26,7 +26,7 @@ def __init__( placeholder_preview_mode=True, ) - def __call__(self) -> dict[str, Union[str, list, bool]]: + def __call__(self) -> dict[str, str | list | bool]: sections = list(self.build_all_sections()) return { "sections": sections, diff --git a/app/views/contexts/section_summary_context.py b/app/views/contexts/section_summary_context.py index 335cce9b59..bd83394339 100644 --- a/app/views/contexts/section_summary_context.py +++ b/app/views/contexts/section_summary_context.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Any, Generator, Iterable, Mapping, Union +from typing import Any, Generator, Iterable, Mapping from werkzeug.datastructures import ImmutableDict @@ -77,7 +77,7 @@ def section(self) -> ImmutableDict: section: ImmutableDict = self._schema.get_section(self.current_location.section_id) # type: ignore return section - def get_page_title(self, title_for_location: Union[Mapping, str]) -> str: + def get_page_title(self, title_for_location: Mapping | str) -> str: section_repeating_page_title = ( self._schema.get_repeating_page_title_for_section( self.current_location.section_id @@ -146,7 +146,7 @@ def build_summary( return groups - def title_for_location(self) -> Union[str, dict]: + def title_for_location(self) -> str | dict: section_id = self.current_location.section_id return ( # Type ignore: section id should exist at this point @@ -170,7 +170,7 @@ def _custom_summary_elements( ) yield list_collector_block.list_summary_element(summary_element) - def _get_safe_page_title(self, title: Union[Mapping, str]) -> str: + def _get_safe_page_title(self, title: Mapping | str) -> str: return ( safe_content(self._schema.get_single_string_value(title)) if title else "" ) diff --git a/app/views/contexts/submit_questionnaire_context.py b/app/views/contexts/submit_questionnaire_context.py index 2325f64acd..8f3aed9977 100644 --- a/app/views/contexts/submit_questionnaire_context.py +++ b/app/views/contexts/submit_questionnaire_context.py @@ -1,4 +1,4 @@ -from typing import Mapping, Union +from typing import Mapping from flask_babel import lazy_gettext @@ -7,7 +7,7 @@ class SubmitQuestionnaireContext(Context): - def __call__(self) -> dict[str, Union[str, dict]]: + def __call__(self) -> dict[str, str | dict]: submission_schema: Mapping = self._schema.get_submission() title = submission_schema.get("title") or lazy_gettext( diff --git a/app/views/contexts/summary/question.py b/app/views/contexts/summary/question.py index cbb35a69df..da54ae3790 100644 --- a/app/views/contexts/summary/question.py +++ b/app/views/contexts/summary/question.py @@ -1,4 +1,4 @@ -from typing import Any, Mapping, Optional +from typing import Any, Mapping from flask import url_for from markupsafe import Markup, escape @@ -77,7 +77,7 @@ def __init__( def get_answer( self, answer_store: AnswerStore, answer_id: str, list_item_id: str | None = None - ) -> Optional[AnswerValueEscapedTypes]: + ) -> AnswerValueEscapedTypes | None: answer = answer_store.get_answer( answer_id, list_item_id or self.list_item_id ) or self.schema.get_default_answer(answer_id) @@ -178,7 +178,7 @@ def _build_answer( answer_store: AnswerStore, question_schema: QuestionSchemaType, answer_schema: Mapping[str, Any], - answer_value: Optional[AnswerValueEscapedTypes] = None, + answer_value: AnswerValueEscapedTypes | None = None, ) -> InferredAnswerValueTypes: if answer_value is None: return None @@ -200,8 +200,8 @@ def _build_answer( return answer_value def _build_date_range_answer( - self, answer_store: AnswerStore, answer: Optional[AnswerValueEscapedTypes] - ) -> dict[str, Optional[AnswerValueEscapedTypes]]: + self, answer_store: AnswerStore, answer: AnswerValueEscapedTypes | None + ) -> dict[str, AnswerValueEscapedTypes | None]: next_answer = next(self.answer_schemas) to_date = self.get_answer(answer_store, next_answer["id"]) return {"from": answer, "to": to_date} @@ -233,7 +233,7 @@ def _build_checkbox_answers( answer: Markup, answer_schema: Mapping[str, Any], answer_store: AnswerStore, - ) -> Optional[list[RadioCheckboxTypes]]: + ) -> list[RadioCheckboxTypes] | None: multiple_answers = [] for option in self.get_answer_options(answer_schema): if escape(option["value"]) in answer: @@ -255,7 +255,7 @@ def _build_radio_answer( answer: Markup, answer_schema: Mapping[str, Any], answer_store: AnswerStore, - ) -> Optional[RadioCheckboxTypes]: + ) -> RadioCheckboxTypes | None: for option in self.get_answer_options(answer_schema): if answer == escape(option["value"]): detail_answer_value = self._get_detail_answer_value( @@ -268,15 +268,15 @@ def _build_radio_answer( def _get_detail_answer_value( self, option: dict, answer_store: AnswerStore - ) -> Optional[AnswerValueEscapedTypes]: + ) -> AnswerValueEscapedTypes | None: if "detail_answer" in option: return self.get_answer(answer_store, option["detail_answer"]["id"]) def _build_dropdown_answer( self, - answer: Optional[AnswerValueEscapedTypes], + answer: AnswerValueEscapedTypes | None, answer_schema: Mapping[str, Any], - ) -> Optional[str]: + ) -> str | None: for option in self.get_answer_options(answer_schema): if answer == option["value"]: return option["label"] diff --git a/app/views/contexts/thank_you_context.py b/app/views/contexts/thank_you_context.py index fcad74e705..925208f1e4 100644 --- a/app/views/contexts/thank_you_context.py +++ b/app/views/contexts/thank_you_context.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Any, Optional +from typing import Any from flask import url_for from flask_babel import lazy_gettext @@ -23,8 +23,8 @@ def build_thank_you_context( metadata: MetadataProxy, submitted_at: datetime, survey_type: SurveyType, - guidance_content: Optional[dict] = None, - confirmation_email_form: Optional[EmailForm] = None, + guidance_content: dict | None = None, + confirmation_email_form: EmailForm | None = None, ) -> dict[str, Any]: if (ru_name := metadata["ru_name"]) and (trad_as := metadata["trad_as"]): submission_text = lazy_gettext( diff --git a/app/views/contexts/view_submitted_response_context.py b/app/views/contexts/view_submitted_response_context.py index a4f8d0cf40..fc2a348d37 100644 --- a/app/views/contexts/view_submitted_response_context.py +++ b/app/views/contexts/view_submitted_response_context.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Union from flask import url_for from flask_babel import lazy_gettext @@ -20,7 +19,7 @@ def build_view_submitted_response_context( schema: QuestionnaireSchema, questionnaire_store: QuestionnaireStore, survey_type: SurveyType, -) -> dict[str, Union[str, datetime, dict]]: +) -> dict[str, str | datetime | dict]: view_submitted_response_expired = has_view_submitted_response_expired( questionnaire_store.submitted_at # type: ignore ) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 04ccee7c72..16bab685d4 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -1,6 +1,6 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Mapping, MutableMapping, Optional, Union +from typing import Mapping, MutableMapping from structlog import get_logger from werkzeug.datastructures import ImmutableDict, ImmutableMultiDict @@ -41,7 +41,7 @@ def __init__( # Type ignore: Block has to exist at this point. Block existence is checked beforehand in block_factory.py self.block: ImmutableDict = self._schema.get_block(self._current_location.block_id) # type: ignore self._routing_path = self._get_routing_path() - self.page_title: Optional[str] = None + self.page_title: str | None = None self._return_location = ReturnLocation( return_to=request_args.get("return_to"), @@ -122,7 +122,7 @@ def _get_routing_path(self) -> RoutingPath: return self.router.routing_path(self._current_location.section_key) def _update_section_completeness( - self, location: Optional[Union[Location, RelationshipLocation]] = None + self, location: Location | RelationshipLocation | None = None ) -> None: location = location or self._current_location diff --git a/app/views/handlers/feedback.py b/app/views/handlers/feedback.py index 1fe9d35d19..72a63f32fc 100644 --- a/app/views/handlers/feedback.py +++ b/app/views/handlers/feedback.py @@ -1,6 +1,6 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Mapping, MutableMapping, Optional, Union +from typing import Any, Mapping, MutableMapping from flask import current_app from flask_babel import gettext, lazy_gettext @@ -43,7 +43,7 @@ def __init__( questionnaire_store: QuestionnaireStore, schema: QuestionnaireSchema, session_store: SessionStore, - form_data: Optional[MultiDict[str, Any]], + form_data: MultiDict[str, Any] | None, ): if not self.is_enabled(schema): raise FeedbackNotEnabled @@ -65,7 +65,7 @@ def form(self) -> QuestionnaireForm: form_data=self._form_data, ) - def get_context(self) -> Mapping[str, Union[str, bool, dict]]: + def get_context(self) -> Mapping[str, str | bool | dict]: return build_feedback_context(self.question_schema, self.form) def get_page_title(self) -> str: @@ -108,14 +108,14 @@ def handle_post(self) -> None: tx_id=tx_id, case_id=case_id, **additional_metadata ) - submitter: Union[GCSFeedbackSubmitter, LogFeedbackSubmitter] = current_app.eq["feedback_submitter"] # type: ignore + submitter: GCSFeedbackSubmitter | LogFeedbackSubmitter = current_app.eq["feedback_submitter"] # type: ignore if not submitter.upload(feedback_metadata(), encrypted_message): raise FeedbackUploadFailed() self._session_store.save() @cached_property - def question_schema(self) -> Mapping[str, Union[str, list]]: + def question_schema(self) -> Mapping[str, str | list]: return { "type": "General", "id": "feedback", @@ -220,10 +220,10 @@ class FeedbackPayloadV2: def __init__( self, metadata: MetadataProxy, - response_metadata: MutableMapping[str, Union[str, int, list]], + response_metadata: MutableMapping[str, str | int | list], schema: QuestionnaireSchema, - case_id: Optional[str], - submission_language_code: Optional[str], + case_id: str | None, + submission_language_code: str | None, feedback_count: int, feedback_text: str, feedback_type: str, diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 79c36fce56..f16741a436 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -1,6 +1,6 @@ from datetime import datetime, timezone from functools import cached_property -from typing import Any, Mapping, Optional +from typing import Any, Mapping from uuid import uuid4 from flask import current_app, redirect @@ -972,7 +972,7 @@ def handle_post(self) -> Response: class IndividualResponseFulfilmentRequest(FulfilmentRequest): - def __init__(self, metadata: MetadataProxy, mobile_number: Optional[str] = None): + def __init__(self, metadata: MetadataProxy, mobile_number: str | None = None): self._metadata = metadata self._mobile_number = mobile_number self._fulfilment_type = "sms" if self._mobile_number else "postal" @@ -991,7 +991,7 @@ def _get_contact_mapping(self) -> Mapping: else {} ) - def _get_fulfilment_code(self) -> Optional[str]: + def _get_fulfilment_code(self) -> str | None: fulfilment_codes = { "sms": { GB_ENG_REGION_CODE: "UACITA1", diff --git a/app/views/handlers/view_submitted_response.py b/app/views/handlers/view_submitted_response.py index d898db1422..40ee9c059f 100644 --- a/app/views/handlers/view_submitted_response.py +++ b/app/views/handlers/view_submitted_response.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Union from flask_babel import lazy_gettext @@ -42,7 +41,7 @@ def has_expired(self) -> bool: ) return False - def get_context(self) -> dict[str, Union[str, datetime, dict]]: + def get_context(self) -> dict[str, str | datetime | dict]: return build_view_submitted_response_context( self._language, self._schema, self._questionnaire_store, get_survey_type() ) diff --git a/pyproject.toml b/pyproject.toml index 4a1f78e9bc..574a84c484 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,7 +126,6 @@ extend-ignore = [ "UP032", # Use f-string instead of `format` call "UP018", # Unnecessary {literal_type} call (rewrite as a literal) "UP015", # Unnecessary open mode parameters - "UP007", # Use `X | Y` for type annotations "UP009", # UTF-8 encoding declaration is unnecessary "UP017", # Use `datetime.UTC` alias "UP033", # Use @functools.cache instead of @functools.lru_cache(maxsize=None) diff --git a/tests/app/questionnaire/rules/test_rule_evaluator.py b/tests/app/questionnaire/rules/test_rule_evaluator.py index b76e0d496e..65283a1b5f 100644 --- a/tests/app/questionnaire/rules/test_rule_evaluator.py +++ b/tests/app/questionnaire/rules/test_rule_evaluator.py @@ -1,5 +1,4 @@ from datetime import datetime, timezone -from typing import Optional, Union import pytest from freezegun import freeze_time @@ -47,10 +46,10 @@ def get_rule_evaluator( language="en", schema: QuestionnaireSchema = None, data_stores: DataStores = None, - location: Union[Location, RelationshipLocation] = Location( + location: Location | RelationshipLocation = Location( section_id="test-section", block_id="test-block" ), - routing_path_block_ids: Optional[list] = None, + routing_path_block_ids: list | None = None, ): if not schema: schema = get_mock_schema() diff --git a/tests/app/questionnaire/test_value_source_resolver.py b/tests/app/questionnaire/test_value_source_resolver.py index c6220782fa..0e4e43fcd0 100644 --- a/tests/app/questionnaire/test_value_source_resolver.py +++ b/tests/app/questionnaire/test_value_source_resolver.py @@ -1,5 +1,4 @@ # pylint: disable=too-many-lines -from typing import Optional, Union import pytest from mock import MagicMock, Mock @@ -60,11 +59,11 @@ def get_calculation_block( def get_value_source_resolver( schema: QuestionnaireSchema = None, data_stores: DataStores = None, - location: Union[Location, RelationshipLocation] = Location( + location: Location | RelationshipLocation = Location( section_id="test-section", block_id="test-block" ), - list_item_id: Optional[str] = None, - routing_path_block_ids: Optional[list] = None, + list_item_id: str | None = None, + routing_path_block_ids: list | None = None, use_default_answer=False, escape_answer_values=False, ): From 12f17ae0c2c5949fd0324709db552e17f12187a1 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:57:33 +0000 Subject: [PATCH 502/567] Schemas v5.23.0 (#1588) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 849b11e163..ce38047ad6 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.22.0 +v5.23.0 From 85f3e52841ff957954a93e52bbaac1f4cf3c7e88 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:51:33 +0000 Subject: [PATCH 503/567] Add Disable Public IP params to Dataflow tasks (#1590) --- ci/purge_expired_questionnaire_state.yaml | 1 + ci/purge_expired_sessions.yaml | 1 + ci/purge_submitted_questionnaire_state.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/ci/purge_expired_questionnaire_state.yaml b/ci/purge_expired_questionnaire_state.yaml index 6a095bd47c..5bdf05401d 100644 --- a/ci/purge_expired_questionnaire_state.yaml +++ b/ci/purge_expired_questionnaire_state.yaml @@ -28,6 +28,7 @@ run: firestoreReadProjectId="${PROJECT_ID}",\ firestoreDeleteProjectId="${PROJECT_ID}" \ --subnetwork regions/europe-west2/subnetworks/eq \ + --disable-public-ips \ --format="value(id.scope())") DATAFLOW_JOB_STATE=$(gcloud dataflow jobs describe "${DATAFLOW_JOB}" --region europe-west2 --format="value(currentState)") diff --git a/ci/purge_expired_sessions.yaml b/ci/purge_expired_sessions.yaml index 0eb10d7d24..a0ea3834f6 100644 --- a/ci/purge_expired_sessions.yaml +++ b/ci/purge_expired_sessions.yaml @@ -29,6 +29,7 @@ run: firestoreReadProjectId="${PROJECT_ID}",\ firestoreDeleteProjectId="${PROJECT_ID}" \ --subnetwork regions/europe-west2/subnetworks/eq \ + --disable-public-ips \ --format="value(id.scope())") DATAFLOW_JOB_STATE=$(gcloud dataflow jobs describe "${DATAFLOW_JOB}" --region europe-west2 --format="value(currentState)") diff --git a/ci/purge_submitted_questionnaire_state.yaml b/ci/purge_submitted_questionnaire_state.yaml index 029f93be5e..ef86d49ec3 100644 --- a/ci/purge_submitted_questionnaire_state.yaml +++ b/ci/purge_submitted_questionnaire_state.yaml @@ -29,6 +29,7 @@ run: firestoreReadProjectId="${PROJECT_ID}",\ firestoreDeleteProjectId="${PROJECT_ID}" \ --subnetwork regions/europe-west2/subnetworks/eq \ + --disable-public-ips \ --format="value(id.scope())") DATAFLOW_JOB_STATE=$(gcloud dataflow jobs describe "${DATAFLOW_JOB}" --region europe-west2 --format="value(currentState)") From 7832273d5791660114264e332a064024707f4c2c Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:10:34 +0000 Subject: [PATCH 504/567] Update sds schema validation (#1538) --- app/authentication/auth_payload_versions.py | 5 -- app/routes/session.py | 1 + app/services/supplementary_data.py | 16 ++++- app/utilities/supplementary_data_parser.py | 30 ++++----- .../parser/test_supplementary_data_parser.py | 63 ++++++++++++------- 5 files changed, 68 insertions(+), 47 deletions(-) diff --git a/app/authentication/auth_payload_versions.py b/app/authentication/auth_payload_versions.py index df71b6af64..fe4d338d37 100644 --- a/app/authentication/auth_payload_versions.py +++ b/app/authentication/auth_payload_versions.py @@ -3,8 +3,3 @@ class AuthPayloadVersion(Enum): V2 = "v2" - - -class SupplementaryDataSchemaVersion(Enum): - V2 = "v2" - V1 = "v1" diff --git a/app/routes/session.py b/app/routes/session.py index 5d63e82e69..5aa5c89483 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -177,6 +177,7 @@ def _set_questionnaire_supplementary_data( dataset_id=new_sds_dataset_id, identifier=identifier, # type: ignore survey_id=metadata["survey_id"], # type: ignore + sds_schema_version=schema.json.get("sds_schema_version"), ) logger.info( "fetched supplementary data", diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index fc50267889..6e2aa28cd3 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -44,7 +44,11 @@ class InvalidSupplementaryData(Exception): def get_supplementary_data_v1( - *, dataset_id: str, identifier: str, survey_id: str + *, + dataset_id: str, + identifier: str, + survey_id: str, + sds_schema_version: str | None = None, ) -> dict: # Type ignore: current_app is a singleton in this application and has the key_store key in its eq attribute. key_store = current_app.eq["key_store"] # type: ignore @@ -93,6 +97,7 @@ def get_supplementary_data_v1( dataset_id=dataset_id, identifier=identifier, survey_id=survey_id, + sds_schema_version=sds_schema_version, ) logger.error( @@ -121,14 +126,19 @@ def decrypt_supplementary_data( def validate_supplementary_data( - supplementary_data: Mapping, dataset_id: str, identifier: str, survey_id: str -) -> dict: + supplementary_data: Mapping, + dataset_id: str, + identifier: str, + survey_id: str, + sds_schema_version: str | None = None, +) -> dict[str, str | dict | int | list]: try: return validate_supplementary_data_v1( supplementary_data=supplementary_data, dataset_id=dataset_id, identifier=identifier, survey_id=survey_id, + sds_schema_version=sds_schema_version, ) except ValidationError as e: raise ValidationError("Invalid supplementary data") from e diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index a23d59d5b0..870acc92d3 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -10,7 +10,6 @@ validates_schema, ) -from app.authentication.auth_payload_versions import SupplementaryDataSchemaVersion from app.utilities.metadata_parser_v2 import VALIDATORS, StripWhitespaceMixin @@ -35,14 +34,6 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): identifier = VALIDATORS["string"](validate=validate.Length(min=1)) - schema_version = VALIDATORS["string"]( - validate=validate.OneOf( - [ - SupplementaryDataSchemaVersion.V1.value, - SupplementaryDataSchemaVersion.V2.value, - ] - ) - ) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @validates_schema() @@ -66,27 +57,35 @@ class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): ) @validates_schema() - def validate_dataset_and_survey_id( # pylint: disable=unused-argument - self, data: Mapping, **kwargs: Any + def validate_payload( # pylint: disable=unused-argument + self, payload: Mapping, **kwargs: Any ) -> None: - if data: - if data["dataset_id"] != self.context["dataset_id"]: + if payload: + if payload["dataset_id"] != self.context["dataset_id"]: raise ValidationError( "Supplementary data did not return the specified Dataset ID" ) - if data["survey_id"] != self.context["survey_id"]: + if payload["survey_id"] != self.context["survey_id"]: raise ValidationError( "Supplementary data did not return the specified Survey ID" ) + if self.context["sds_schema_version"] and ( + payload["data"]["schema_version"] != self.context["sds_schema_version"] + ): + raise ValidationError( + "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" + ) + def validate_supplementary_data_v1( supplementary_data: Mapping, dataset_id: str, identifier: str, survey_id: str, -) -> dict: + sds_schema_version: str | None = None, +) -> dict[str, str | dict | int | list]: """Validate claims required for supplementary data""" supplementary_data_metadata_schema = SupplementaryDataMetadataSchema( unknown=INCLUDE @@ -95,6 +94,7 @@ def validate_supplementary_data_v1( "dataset_id": dataset_id, "identifier": identifier, "survey_id": survey_id, + "sds_schema_version": sds_schema_version, } validated_supplementary_data = supplementary_data_metadata_schema.load( supplementary_data diff --git a/tests/app/parser/test_supplementary_data_parser.py b/tests/app/parser/test_supplementary_data_parser.py index 7442d79d2f..c5243b1c87 100644 --- a/tests/app/parser/test_supplementary_data_parser.py +++ b/tests/app/parser/test_supplementary_data_parser.py @@ -1,3 +1,4 @@ +from contextlib import contextmanager from copy import deepcopy import pytest @@ -6,6 +7,15 @@ from app.services.supplementary_data import validate_supplementary_data from app.utilities.supplementary_data_parser import validate_supplementary_data_v1 + +@contextmanager +def not_raises(exception): + try: + yield + except exception as validation_error: + raise pytest.fail(f"{validation_error} RAISED") + + SUPPLEMENTARY_DATA_PAYLOAD = { "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", "survey_id": "123", @@ -54,6 +64,35 @@ def test_invalid_supplementary_data_payload_raises_error(): assert str(error.value) == "Invalid supplementary data" +def test_invalid_supplementary_dataset_version_raises_error(): + with pytest.raises(ValidationError) as error: + validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12345678901", + survey_id="123", + sds_schema_version="v6", + ) + + assert ( + str(error.value) + == "{'_schema': ['The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema']}" + ) + + +def test_valid_supplementary_dataset_version_does_not_raise_error(): + with not_raises(ValidationError): + validated_payload = validate_supplementary_data_v1( + supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, + dataset_id="44f1b432-9421-49e5-bd26-e63e18a30b69", + identifier="12345678901", + survey_id="123", + sds_schema_version="v1", + ) + + assert validated_payload == SUPPLEMENTARY_DATA_PAYLOAD + + def test_validate_supplementary_data_payload(): validated_payload = validate_supplementary_data_v1( supplementary_data=SUPPLEMENTARY_DATA_PAYLOAD, @@ -171,30 +210,6 @@ def test_validate_supplementary_data_payload_with_unknown_field(): assert validated_payload == payload -def test_validate_supplementary_data_invalid_schema_version(): - payload = { - "dataset_id": "44f1b432-9421-49e5-bd26-e63e18a30b69", - "survey_id": "123", - "some_field": "value", - "data": { - "schema_version": "v3", - "identifier": "12345678901", - }, - } - - with pytest.raises(ValidationError) as error: - validate_supplementary_data_v1( - supplementary_data=payload, - dataset_id="001", - identifier="12345678901", - survey_id="123", - ) - - assert ( - str(error.value) == "{'data': {'schema_version': ['Must be one of: v1, v2.']}}" - ) - - def test_validate_supplementary_data_payload_missing_identifier_in_items(): payload = deepcopy(SUPPLEMENTARY_DATA_PAYLOAD) payload["data"]["items"]["local_units"][0].pop("identifier") From c786441336aee5c412337b6318be4aeb7538631f Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 29 Jan 2025 12:39:51 +0000 Subject: [PATCH 505/567] Schemas v5.24.0 (#1594) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ce38047ad6..640efa5d83 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.23.0 +v5.24.0 From 364f24fddc0839358be3c838d5501e2675fb16b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 08:03:07 +0000 Subject: [PATCH 506/567] Bump the development-dependencies group across 1 directory with 12 updates (#1593) Bumps the development-dependencies group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.26.0` | `7.26.7` | | [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.26.0` | `7.26.7` | | [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) | `7.26.0` | `7.26.7` | | [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli) | `9.4.1` | `9.7.1` | | [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner) | `8.40.6` | `8.41.0` | | [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) | `9.2.8` | `9.6.4` | | [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter) | `9.2.14` | `9.6.3` | | [lint-staged](https://github.com/lint-staged/lint-staged) | `15.2.10` | `15.4.3` | | [prettier](https://github.com/prettier/prettier) | `3.4.1` | `3.4.2` | | [typescript](https://github.com/microsoft/TypeScript) | `5.7.2` | `5.7.3` | | [uuid](https://github.com/uuidjs/uuid) | `11.0.3` | `11.0.5` | Updates `@babel/core` from 7.26.0 to 7.26.7 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-core) Updates `@babel/preset-env` from 7.26.0 to 7.26.7 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-preset-env) Updates `@babel/runtime` from 7.26.0 to 7.26.7 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-runtime) Updates `@wdio/cli` from 9.4.1 to 9.7.1 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.7.1/packages/wdio-cli) Updates `@wdio/local-runner` from 8.40.6 to 8.41.0 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.41.0/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.41.0/packages/wdio-local-runner) Updates `@wdio/mocha-framework` from 9.2.8 to 9.6.4 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.6.4/packages/wdio-mocha-framework) Updates `@wdio/spec-reporter` from 9.2.14 to 9.6.3 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.6.3/packages/wdio-spec-reporter) Updates `lint-staged` from 15.2.10 to 15.4.3 - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.10...v15.4.3) Updates `prettier` from 3.4.1 to 3.4.2 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.4.1...3.4.2) Updates `typescript` from 5.7.2 to 5.7.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.7.2...v5.7.3) Updates `uuid` from 11.0.3 to 11.0.5 - [Release notes](https://github.com/uuidjs/uuid/releases) - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v11.0.3...v11.0.5) Updates `webdriverio` from 9.4.1 to 9.7.1 - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v9.7.1/packages/webdriverio) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: "@babel/runtime" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: "@wdio/cli" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: "@wdio/local-runner" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: "@wdio/mocha-framework" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: "@wdio/spec-reporter" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: uuid dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> --- package-lock.json | 1159 ++++++++++++++++++++++++++++++++------------- 1 file changed, 818 insertions(+), 341 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b6399fc60..e647514293 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,30 +65,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -104,13 +106,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -131,26 +134,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -270,10 +261,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -312,19 +304,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", @@ -380,25 +359,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -594,12 +575,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -769,12 +751,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -909,14 +891,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -991,12 +973,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1276,12 +1259,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1354,14 +1338,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1375,7 +1360,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1386,7 +1371,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -1395,12 +1380,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1417,7 +1402,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -1470,10 +1455,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1496,16 +1482,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1514,10 +1501,11 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2582,6 +2570,7 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -2606,6 +2595,7 @@ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.1" }, @@ -2623,7 +2613,8 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", @@ -2776,23 +2767,23 @@ } }, "node_modules/@wdio/cli": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.4.1.tgz", - "integrity": "sha512-GDyAer63WDsr2ckXmrpUyAcIZFd3pCRIpi85rL1ZjnWthRy/UtwY0EHPMDuSeUEJ28iYwW3esKgq2ZKlsdbMeA==", + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.7.1.tgz", + "integrity": "sha512-oPCmjle4+UEFs2WAql3HQNfRgTu3orwb139jt79cS9XtNtGku4iiAgD/lND+c555TnVJin2BWdu1D4kZdCTVBw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.1.1", "@vitest/snapshot": "^2.1.1", - "@wdio/config": "9.2.8", - "@wdio/globals": "9.4.1", - "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.2", - "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.8", + "@wdio/config": "9.6.4", + "@wdio/globals": "9.7.1", + "@wdio/logger": "9.4.4", + "@wdio/protocols": "9.7.0", + "@wdio/types": "9.6.3", + "@wdio/utils": "9.6.4", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^4.0.0", - "cli-spinners": "^3.0.0", "dotenv": "^16.3.1", "ejs": "^3.1.9", "execa": "^9.2.0", @@ -2804,7 +2795,7 @@ "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", "tsx": "^4.7.2", - "webdriverio": "9.4.1", + "webdriverio": "9.7.1", "yargs": "^17.7.2" }, "bin": { @@ -2827,15 +2818,15 @@ } }, "node_modules/@wdio/config": { - "version": "9.2.8", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.2.8.tgz", - "integrity": "sha512-EGMmBPGJbz6RmgMjebRWkWu3fGyeTIRcusF4UA4f2tiUEKY8nbzUO/ZyDjVQNR+YVB40q0jcqAqpszYRrIzzeg==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.6.4.tgz", + "integrity": "sha512-oTNXVVzaZ0qaM7oX8tyS3YBr4A3ij2py3Umew3ez0IS2vHpRs1LvLfVWoHRSqrhJIVnfjV3+zqcl9BWALNVD/g==", "dev": true, + "license": "MIT", "dependencies": { - "@wdio/logger": "9.1.3", - "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.8", - "decamelize": "^6.0.0", + "@wdio/logger": "9.4.4", + "@wdio/types": "9.6.3", + "@wdio/utils": "9.6.4", "deepmerge-ts": "^7.0.3", "glob": "^10.2.2", "import-meta-resolve": "^4.0.0" @@ -2845,29 +2836,31 @@ } }, "node_modules/@wdio/globals": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.4.1.tgz", - "integrity": "sha512-CTVAVJ7iFyT54XF9iRmNvsDB+WSHoztJPG9XPL/mHzQ2LYfSyUR8E/j+3iHbTx3v/qRNucgPcGwhxiuY2RcaDg==", + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.7.1.tgz", + "integrity": "sha512-o3Zaiyi/amH3VF+D2+lB+UXcaksWx44SIIVZjkgYgHsKAnmrv04JfkneQo+x05J/vNc+BM6jwTgTAtmkfF/t5w==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.20.0" }, "optionalDependencies": { "expect-webdriverio": "^5.0.1", - "webdriverio": "9.4.1" + "webdriverio": "9.7.1" } }, "node_modules/@wdio/local-runner": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.40.6.tgz", - "integrity": "sha512-h2OvQ6kODNiEaM6JzYzTzPl4n9TiqM2KsQ+8x3QoMIHY7Z9JSc+cH9QcnNsm3Sqxk/LVYFGetza6/WTsHx1/zA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.41.0.tgz", + "integrity": "sha512-A5msAjAC8gqiWvtFl+VNm9BBlVb5q3a2o7i+L+Cw7idV3aFY5etigB2wLYMtyBWgB8cXvbZaxXizHhGvZ+iB8Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0", "@wdio/logger": "8.38.0", "@wdio/repl": "8.40.3", - "@wdio/runner": "8.40.6", - "@wdio/types": "8.40.6", + "@wdio/runner": "8.41.0", + "@wdio/types": "8.41.0", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", "stream-buffers": "^3.0.2" @@ -2877,12 +2870,13 @@ } }, "node_modules/@wdio/local-runner/node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@wdio/local-runner/node_modules/@wdio/logger": { @@ -2901,10 +2895,11 @@ } }, "node_modules/@wdio/local-runner/node_modules/@wdio/types": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", - "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.41.0.tgz", + "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0" }, @@ -2924,11 +2919,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@wdio/local-runner/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/@wdio/logger": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.1.3.tgz", - "integrity": "sha512-cumRMK/gE1uedBUw3WmWXOQ7HtB6DR8EyKQioUz2P0IJtRRpglMBdZV7Svr3b++WWawOuzZHMfbTkJQmaVt8Gw==", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.4.4.tgz", + "integrity": "sha512-BXx8RXFUW2M4dcO6t5Le95Hi2ZkTQBRsvBQqLekT2rZ6Xmw8ZKZBPf0FptnoftFGg6dYmwnDidYv/0+4PiHjpQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -2952,16 +2955,17 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "9.2.8", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.2.8.tgz", - "integrity": "sha512-lCek1D+Cdnb8sF/E/PkdKX1PLrq677YVKHV6hFlZeRHWKcJyopCTvhwnJerZNFgmcTuZ6zeiGLcNNlPdQzF4oQ==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.6.4.tgz", + "integrity": "sha512-8TqCIKGz5CkA2uSJSLXLLjINCeZ/UGvegZiB82Fr9hO3RvirPXbYVV+P6vqB9jqSACZ5kxX22cTUhqXgFxUjyQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/mocha": "^10.0.6", "@types/node": "^20.11.28", - "@wdio/logger": "9.1.3", - "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.8", + "@wdio/logger": "9.4.4", + "@wdio/types": "9.6.3", + "@wdio/utils": "9.6.4", "mocha": "^10.3.0" }, "engines": { @@ -2969,16 +2973,18 @@ } }, "node_modules/@wdio/protocols": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.2.2.tgz", - "integrity": "sha512-0GMUSHCbYm+J+rnRU6XPtaUgVCRICsiH6W5zCXpePm3wLlbmg/mvZ+4OnNErssbpIOulZuAmC2jNmut2AEfWSw==", - "dev": true + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.7.0.tgz", + "integrity": "sha512-5DI8cqJqT9K6oQn8UpaSTmcGAl4ufkUWC5FoPT3oXdLjILfxvweZDf/2XNBCbGMk4+VOMKqB2ofOqKhDIB2nAg==", + "dev": true, + "license": "MIT" }, "node_modules/@wdio/repl": { "version": "8.40.3", "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0" }, @@ -2987,23 +2993,32 @@ } }, "node_modules/@wdio/repl/node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, + "node_modules/@wdio/repl/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/@wdio/reporter": { - "version": "9.2.14", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.2.14.tgz", - "integrity": "sha512-njOqa9+w5zc9AY4fsUmW46EhZ2nzMFRXZPCmwnGZuYk81L3slVfAFhKk1wSTRhSbghjYAZndt9Yo3c4wFLh6Lg==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.6.3.tgz", + "integrity": "sha512-OwuwhRtqDZDPU8/6TBSMg+bvw9DKgUVfGu160zamBt8jPg8LZ5VU2VoZlRBNZMAHl3OnSwj+ih2/meEoLf3DUg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.1.0", - "@wdio/logger": "9.1.3", - "@wdio/types": "9.2.2", + "@wdio/logger": "9.4.4", + "@wdio/types": "9.6.3", "diff": "^7.0.0", "object-inspect": "^1.12.0" }, @@ -3012,22 +3027,23 @@ } }, "node_modules/@wdio/runner": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.40.6.tgz", - "integrity": "sha512-dScN5mLia3YYctEgrbO4JG7pEttICfNENFd08t1T/hNJAWyzSbG/wvAL5k0HUGqp6Ukly6NUmhVsP5PVBz4ooA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.41.0.tgz", + "integrity": "sha512-eQ9vZaHIXBLw7XqiKsasiUGjC8PgJawnHFMPKS0i/4ds+5arHo6ciX0s2uhJ3j/EHw3PYvFPCREp/sXetRuNlQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0", - "@wdio/config": "8.40.6", - "@wdio/globals": "8.40.6", + "@wdio/config": "8.41.0", + "@wdio/globals": "8.41.0", "@wdio/logger": "8.38.0", - "@wdio/types": "8.40.6", - "@wdio/utils": "8.40.6", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "deepmerge-ts": "^5.1.0", "expect-webdriverio": "^4.12.0", "gaze": "^1.1.3", - "webdriver": "8.40.6", - "webdriverio": "8.40.6" + "webdriver": "8.41.0", + "webdriverio": "8.41.0" }, "engines": { "node": "^16.13 || >=18" @@ -3038,6 +3054,7 @@ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", @@ -3055,23 +3072,25 @@ } }, "node_modules/@wdio/runner/node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@wdio/runner/node_modules/@wdio/config": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.40.6.tgz", - "integrity": "sha512-rHCSmrhdJf7FlidcQPDvRKRPLYjklbrdxQa6J20BxHifTO4h2v23Wrq4OqqYIcq23gf9LpZvCA/PAMiET/QdVg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.41.0.tgz", + "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", "dev": true, + "license": "MIT", "dependencies": { "@wdio/logger": "8.38.0", - "@wdio/types": "8.40.6", - "@wdio/utils": "8.40.6", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -3082,16 +3101,17 @@ } }, "node_modules/@wdio/runner/node_modules/@wdio/globals": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.40.6.tgz", - "integrity": "sha512-37llSQk4ngM6wzn8diBQ1Xik2ZZtUCU29Hr7NbfyfXahQdzIApRxNtw0B2J5XrjGX9yuO6gdyg5Kj+UuaKIo7A==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.41.0.tgz", + "integrity": "sha512-xfUpEppdKzMHy4qoSoQN1cXoBPPh7oMeX+U/jtdvOtla+dd/YZ8pu47zLhQ/GM3gDVrBGnO4w3u4L6Zf/P3KEw==", "dev": true, + "license": "MIT", "engines": { "node": "^16.13 || >=18" }, "optionalDependencies": { "expect-webdriverio": "^4.11.2", - "webdriverio": "8.40.6" + "webdriverio": "8.41.0" } }, "node_modules/@wdio/runner/node_modules/@wdio/logger": { @@ -3099,6 +3119,7 @@ "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -3113,13 +3134,15 @@ "version": "8.40.3", "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@wdio/runner/node_modules/@wdio/types": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", - "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.41.0.tgz", + "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0" }, @@ -3128,18 +3151,19 @@ } }, "node_modules/@wdio/runner/node_modules/@wdio/utils": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.40.6.tgz", - "integrity": "sha512-+TWfV6h+4f8gs7QiYUAWbWEylpZudQ+xkJPN34tRzPJK6dOBYEnIT/j6+1m3j39m1WPDehyYxIf1wCsrGKBxNQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-0TcTjBiax1VxtJQ/iQA0ZyYOSHjjX2ARVmEI0AMo9+AuIq+xBfnY561+v8k9GqOMPKsiH/HrK3xwjx8xCVS03g==", "dev": true, + "license": "MIT", "dependencies": { "@puppeteer/browsers": "^1.6.0", "@wdio/logger": "8.38.0", - "@wdio/types": "8.40.6", + "@wdio/types": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.5.0", - "geckodriver": "^4.3.1", + "geckodriver": "~4.2.0", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", @@ -3156,15 +3180,17 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@wdio/runner/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -3177,6 +3203,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -3194,6 +3221,7 @@ "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" } @@ -3203,6 +3231,7 @@ "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-4.15.4.tgz", "integrity": "sha512-Op1xZoevlv1pohCq7g2Og5Gr3xP2NhY7MQueOApmopVxgweoJ/BqJxyvMNP0A//QsMg8v0WsN/1j81Sx2er9Wg==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/snapshot": "^2.0.3", "expect": "^29.7.0", @@ -3218,11 +3247,36 @@ "webdriverio": "^8.29.3" } }, + "node_modules/@wdio/runner/node_modules/geckodriver": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", + "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "dev": true, + "hasInstallScript": true, + "license": "MPL-2.0", + "dependencies": { + "@wdio/logger": "^8.11.0", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.1", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": "^16.13 || >=18 || >=20" + } + }, "node_modules/@wdio/runner/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -3232,6 +3286,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3246,13 +3301,15 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@wdio/runner/node_modules/proxy-agent": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -3272,6 +3329,7 @@ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "1.9.1", "chromium-bidi": "0.5.8", @@ -3289,6 +3347,7 @@ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0" @@ -3301,32 +3360,42 @@ "version": "0.0.1232444", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@wdio/runner/node_modules/tar-fs": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, + "license": "MIT", "dependencies": { "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" } }, + "node_modules/@wdio/runner/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/@wdio/runner/node_modules/webdriverio": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.40.6.tgz", - "integrity": "sha512-hMFYRjVU5Nnk2e9Mi8kDx/IVFMWGaVyDCDpv/SeXXCP17DT9jAZtOWlwGhRaLVikN5JYYuHavHyatVa7gj6QTg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.41.0.tgz", + "integrity": "sha512-WlQfw0mUEhTS8DPr+TBSYMhEnqXkFr2dcUwPb5XkffTB+i0wftf+BLXJPSVD9M1PTLyYcFdCIu68pqR54dq5BA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0", - "@wdio/config": "8.40.6", + "@wdio/config": "8.41.0", "@wdio/logger": "8.38.0", "@wdio/protocols": "8.40.3", "@wdio/repl": "8.40.3", - "@wdio/types": "8.40.6", - "@wdio/utils": "8.40.6", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "archiver": "^7.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", @@ -3344,7 +3413,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.40.6" + "webdriver": "8.41.0" }, "engines": { "node": "^16.13 || >=18" @@ -3363,6 +3432,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -3380,13 +3450,14 @@ } }, "node_modules/@wdio/spec-reporter": { - "version": "9.2.14", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.2.14.tgz", - "integrity": "sha512-/0QI/Lfld2gKscVAOTxgy1KyshxreDEu2F0FsiNQUUMQXuV8bN85lGgH6K3F+xZ0p17J/tg8UnWQCv2IaXTycw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.6.3.tgz", + "integrity": "sha512-P9zi8PfdhUWGnwb1tcl7EyLGKHFiCOkS04I7nBnVRAkTTfeQ+jpuDIlefW8DkW91yHHOpoAOOVeMqVBGRc8OoQ==", "dev": true, + "license": "MIT", "dependencies": { - "@wdio/reporter": "9.2.14", - "@wdio/types": "9.2.2", + "@wdio/reporter": "9.6.3", + "@wdio/types": "9.6.3", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^9.0.0" @@ -3408,10 +3479,11 @@ } }, "node_modules/@wdio/types": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.2.2.tgz", - "integrity": "sha512-nHZ9Ne9iRQFJ1TOYKUn4Fza69IshTTzk6RYmSZ51ImGs9uMZu0+S0Jm9REdly+VLN3FzxG6g2QSe0/F3uNVPdw==", + "version": "9.6.3", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.6.3.tgz", + "integrity": "sha512-K3Lu7K5g5bsUcQV6/95XaS3jMwcGUn2pDdryYibKZafklhHjVt3o/xnw6Vgd/JzoSneCKHdwj941n+yDpTJHAw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.1.0" }, @@ -3420,22 +3492,23 @@ } }, "node_modules/@wdio/utils": { - "version": "9.2.8", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.2.8.tgz", - "integrity": "sha512-rKm5FXkpsCyeqh8tdirtRUHvgNytWNMiaVKdctsvKOJvqnDVPAAQcz9Wmgo7bSwoLwtSHcDaRoxY7olV7J4QnA==", + "version": "9.6.4", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.6.4.tgz", + "integrity": "sha512-FMI/F5ju0h0HKC4RRQKW/H9So2cgtK6dd0JCmVdBzQ+/LMluEzlZmQva14HYmNd2t2ZmejYRqAJPV3aAsMAMZA==", "dev": true, + "license": "MIT", "dependencies": { "@puppeteer/browsers": "^2.2.0", - "@wdio/logger": "9.1.3", - "@wdio/types": "9.2.2", + "@wdio/logger": "9.4.4", + "@wdio/types": "9.6.3", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", - "edgedriver": "^5.6.1", - "geckodriver": "^4.3.3", + "edgedriver": "^6.1.1", + "geckodriver": "^5.0.0", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", "locate-app": "^2.2.24", - "safaridriver": "^0.1.2", + "safaridriver": "^1.0.0", "split2": "^4.2.0", "wait-port": "^1.1.0" }, @@ -3443,11 +3516,63 @@ "node": ">=18.20.0" } }, + "node_modules/@wdio/utils/node_modules/edgedriver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.1.1.tgz", + "integrity": "sha512-/dM/PoBf22Xg3yypMWkmRQrBKEnSyNaZ7wHGCT9+qqT14izwtFT+QvdR89rjNkMfXwW+bSFoqOfbcvM+2Cyc7w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@wdio/logger": "^9.1.3", + "@zip.js/zip.js": "^2.7.53", + "decamelize": "^6.0.0", + "edge-paths": "^3.0.5", + "fast-xml-parser": "^4.5.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^3.3.2", + "which": "^5.0.0" + }, + "bin": { + "edgedriver": "bin/edgedriver.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@wdio/utils/node_modules/safaridriver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.0.tgz", + "integrity": "sha512-J92IFbskyo7OYB3Dt4aTdyhag1GlInrfbPCmMteb7aBK7PwlnGz1HI0+oyNN97j7pV9DqUAVoVgkNRMrfY47mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@wdio/utils/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@zip.js/zip.js": { - "version": "2.7.52", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz", - "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==", + "version": "2.7.54", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.54.tgz", + "integrity": "sha512-qMrJVg2hoEsZJjMJez9yI2+nZlBUxgYzGV3mqcb2B/6T1ihXp0fWBDYlVHlHquuorgNUQP5a8qSmX6HF5rFJNg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "bun": ">=0.7.0", "deno": ">=1.0.0", @@ -3939,6 +4064,30 @@ "node": ">=10.0.0" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3951,6 +4100,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true, + "license": "MIT" + }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -4062,6 +4218,25 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -4100,6 +4275,7 @@ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" } @@ -4109,6 +4285,7 @@ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", @@ -4127,6 +4304,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4194,6 +4372,19 @@ } ] }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4317,18 +4508,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.2.0.tgz", - "integrity": "sha512-pXftdQloMZzjCr3pCTIRniDcys6dDzgpgVhAHHk6TKBDbRuP1MkuetTF5KSv4YUutbOPa7+7ZrAJ2kVtbMqyXA==", - "dev": true, - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -4528,10 +4707,11 @@ "dev": true }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" } @@ -4631,6 +4811,7 @@ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", "dev": true, + "license": "MIT", "dependencies": { "node-fetch": "^2.6.12" } @@ -4640,6 +4821,7 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -4791,10 +4973,11 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -4824,6 +5007,7 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -4839,6 +5023,7 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4879,6 +5064,7 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -4942,6 +5128,7 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -5034,6 +5221,56 @@ "url": "https://dotenvx.com" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6159,6 +6396,7 @@ "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.0.5.tgz", "integrity": "sha512-h04OGd7ZksVj8bgv3bYdjFpmJuKeCnyRrBmpMxYpMDmYSspxg9vsSr0kD5p9oOM16bX0ZXEVXr42RbI2hoLpTw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@vitest/snapshot": "^2.0.5", @@ -6480,6 +6718,7 @@ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.17" } @@ -6530,6 +6769,59 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6571,6 +6863,7 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, + "license": "MIT", "dependencies": { "globule": "^1.0.0" }, @@ -6579,26 +6872,43 @@ } }, "node_modules/geckodriver": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.5.1.tgz", - "integrity": "sha512-lGCRqPMuzbRNDWJOQcUqhNqPvNsIFu6yzXF8J/6K3WCYFd2r5ckbeF7h1cxsnjA7YLSEiWzERCt6/gjZ3tW0ug==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-5.0.0.tgz", + "integrity": "sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@wdio/logger": "^9.0.0", - "@zip.js/zip.js": "^2.7.48", + "@wdio/logger": "^9.1.3", + "@zip.js/zip.js": "^2.7.53", "decamelize": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.5", "node-fetch": "^3.3.2", "tar-fs": "^3.0.6", - "which": "^4.0.0" + "which": "^5.0.0" }, "bin": { "geckodriver": "bin/geckodriver.js" }, "engines": { - "node": "^16.13 || >=18 || >=20" + "node": ">=18.0.0" + } + }, + "node_modules/geckodriver/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/gensync": { @@ -6817,6 +7127,7 @@ "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", "dev": true, + "license": "MIT", "dependencies": { "glob": "~7.1.1", "lodash": "^4.17.21", @@ -6832,6 +7143,7 @@ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6852,6 +7164,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6876,6 +7189,7 @@ "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", @@ -6901,6 +7215,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7035,10 +7350,11 @@ "dev": true }, "node_modules/htmlfy": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.3.2.tgz", - "integrity": "sha512-FsxzfpeDYRqn1emox9VpxMPfGjADoUmmup8D604q497R0VNxiXs4ZZTN2QzkaMA5C9aHGUoe1iQRVSm+HK9xuA==", - "dev": true + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.5.1.tgz", + "integrity": "sha512-nb66M9g0zKrvmR3kk/WOM+5tOT3DzO1yJ4yEJXsz2zfZ3gXiCTrlGvbc4lQzTZyylJj7at+XSVDxFvAVH6J6tQ==", + "dev": true, + "license": "MIT" }, "node_modules/htmlparser2": { "version": "9.1.0", @@ -7063,7 +7379,8 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "7.0.2", @@ -7083,6 +7400,7 @@ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" @@ -8277,6 +8595,7 @@ "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -8355,10 +8674,11 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -8376,21 +8696,22 @@ } }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", - "dev": true, - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "version": "15.4.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz", + "integrity": "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -8403,10 +8724,11 @@ } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -8524,6 +8846,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true, + "license": "ISC" + }, "node_modules/listr2": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", @@ -9012,6 +9341,7 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -9107,6 +9437,7 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -9156,11 +9487,25 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mocha": { "version": "10.7.3", @@ -9603,6 +9948,7 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -9799,6 +10145,7 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" } @@ -10185,10 +10532,11 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -10441,6 +10789,7 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -10803,7 +11152,8 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve-from": { "version": "4.0.0", @@ -10828,6 +11178,7 @@ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", "dev": true, + "license": "MIT", "dependencies": { "lowercase-keys": "^3.0.0" }, @@ -11692,7 +12043,18 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "license": "MIT/X11", + "engines": { + "node": "*" + } }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -11841,10 +12203,11 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11978,6 +12341,65 @@ "node": ">= 10.0.0" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -12039,14 +12461,15 @@ "dev": true }, "node_modules/uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } @@ -12214,18 +12637,19 @@ } }, "node_modules/webdriver": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.40.6.tgz", - "integrity": "sha512-jkslwUvOmqhFfc1E21Tz48NgYD8ykiR+09iWZlVLtx3P43k4jOfS+CfasvQ+6hJiVck+N5dXjYfg6zDjpkIFRw==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.41.0.tgz", + "integrity": "sha512-n8OrFnVT4hAaGa0Advr3T8ObJdeKNTRklHIEzM2CYVx/5DZt+2KwaKSxWsURNd4zU7FbsfaJUU4rQWCmvozQLg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.40.6", + "@wdio/config": "8.41.0", "@wdio/logger": "8.38.0", "@wdio/protocols": "8.40.3", - "@wdio/types": "8.40.6", - "@wdio/utils": "8.40.6", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "deepmerge-ts": "^5.1.0", "got": "^12.6.1", "ky": "^0.33.0", @@ -12240,6 +12664,7 @@ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", @@ -12257,23 +12682,25 @@ } }, "node_modules/webdriver/node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.10.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", + "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/webdriver/node_modules/@wdio/config": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.40.6.tgz", - "integrity": "sha512-rHCSmrhdJf7FlidcQPDvRKRPLYjklbrdxQa6J20BxHifTO4h2v23Wrq4OqqYIcq23gf9LpZvCA/PAMiET/QdVg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.41.0.tgz", + "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", "dev": true, + "license": "MIT", "dependencies": { "@wdio/logger": "8.38.0", - "@wdio/types": "8.40.6", - "@wdio/utils": "8.40.6", + "@wdio/types": "8.41.0", + "@wdio/utils": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -12288,6 +12715,7 @@ "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -12302,13 +12730,15 @@ "version": "8.40.3", "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.40.3.tgz", "integrity": "sha512-wK7+eyrB3TAei8RwbdkcyoNk2dPu+mduMBOdPJjp8jf/mavd15nIUXLID1zA+w5m1Qt1DsT1NbvaeO9+aJQ33A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webdriver/node_modules/@wdio/types": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.40.6.tgz", - "integrity": "sha512-ALftLri1BdsRuPrQkuW3evBNdOA5n4IkuoegOw6UE2z+R0f1YI5fHGSHNRWLnhtbOECbGyHXXqzbSxCEb+o+MA==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.41.0.tgz", + "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^22.2.0" }, @@ -12317,18 +12747,19 @@ } }, "node_modules/webdriver/node_modules/@wdio/utils": { - "version": "8.40.6", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.40.6.tgz", - "integrity": "sha512-+TWfV6h+4f8gs7QiYUAWbWEylpZudQ+xkJPN34tRzPJK6dOBYEnIT/j6+1m3j39m1WPDehyYxIf1wCsrGKBxNQ==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-0TcTjBiax1VxtJQ/iQA0ZyYOSHjjX2ARVmEI0AMo9+AuIq+xBfnY561+v8k9GqOMPKsiH/HrK3xwjx8xCVS03g==", "dev": true, + "license": "MIT", "dependencies": { "@puppeteer/browsers": "^1.6.0", "@wdio/logger": "8.38.0", - "@wdio/types": "8.40.6", + "@wdio/types": "8.41.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.5.0", - "geckodriver": "^4.3.1", + "geckodriver": "~4.2.0", "get-port": "^7.0.0", "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", @@ -12341,10 +12772,11 @@ } }, "node_modules/webdriver/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -12357,6 +12789,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -12374,15 +12807,41 @@ "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" } }, + "node_modules/webdriver/node_modules/geckodriver": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", + "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "dev": true, + "hasInstallScript": true, + "license": "MPL-2.0", + "dependencies": { + "@wdio/logger": "^8.11.0", + "decamelize": "^6.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.1", + "tar-fs": "^3.0.4", + "unzipper": "^0.10.14", + "which": "^4.0.0" + }, + "bin": { + "geckodriver": "bin/geckodriver.js" + }, + "engines": { + "node": "^16.13 || >=18 || >=20" + } + }, "node_modules/webdriver/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -12391,13 +12850,15 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webdriver/node_modules/proxy-agent": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -12417,17 +12878,26 @@ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", "dev": true, + "license": "MIT", "dependencies": { "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^3.1.5" } }, + "node_modules/webdriver/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/webdriver/node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -12445,26 +12915,27 @@ } }, "node_modules/webdriverio": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.4.1.tgz", - "integrity": "sha512-XIPtRnxSES4CoxH2BfUY/6QzNgEgJEUjMYu7t7SJR8bVfbLRVXAA1ie9kM0MtdLs4oZ2Pr8rR8fqytsA1CjEWw==", + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.7.1.tgz", + "integrity": "sha512-P1roVTpXwtzSgNKl9j92LF4+5i2eGd0n9EMvMRdLNnI0v1ws7dNJOHNgsEAepZqMikYPgh2m+5DyXR1Ygoa+nQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.2.8", - "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.2", - "@wdio/repl": "9.0.8", - "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.8", + "@wdio/config": "9.6.4", + "@wdio/logger": "9.4.4", + "@wdio/protocols": "9.7.0", + "@wdio/repl": "9.4.4", + "@wdio/types": "9.6.3", + "@wdio/utils": "9.6.4", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", "grapheme-splitter": "^1.0.4", - "htmlfy": "^0.3.0", + "htmlfy": "^0.5.0", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", "jszip": "^3.10.1", @@ -12476,7 +12947,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.4.1" + "webdriver": "9.7.0" }, "engines": { "node": ">=18.20.0" @@ -12491,10 +12962,11 @@ } }, "node_modules/webdriverio/node_modules/@wdio/repl": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-9.0.8.tgz", - "integrity": "sha512-3iubjl4JX5zD21aFxZwQghqC3lgu+mSs8c3NaiYYNCC+IT5cI/8QuKlgh9s59bu+N3gG988jqMJeCYlKuUv/iw==", + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-9.4.4.tgz", + "integrity": "sha512-kchPRhoG/pCn4KhHGiL/ocNhdpR8OkD2e6sANlSUZ4TGBVi86YSIEjc2yXUwLacHknC/EnQk/SFnqd4MsNjGGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.1.0" }, @@ -12527,18 +12999,19 @@ } }, "node_modules/webdriverio/node_modules/webdriver": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.4.1.tgz", - "integrity": "sha512-vFDdxMj/9W1+6jhpHSiRYfO8dix23HjAUtLx7aOv9ejEsntC0EzCIAftJ59YsF3Ppu184+FkdDVhnivpkZPTFw==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.7.0.tgz", + "integrity": "sha512-O/Ce4I7HcsqlP3kx9L0F14olOsarKkXUz+hSunOTC9YxsiVoOu5yIcRrHyWUQziYgA4K5gobZSKrTuAr+edA4Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "9.2.8", - "@wdio/logger": "9.1.3", - "@wdio/protocols": "9.2.2", - "@wdio/types": "9.2.2", - "@wdio/utils": "9.2.8", + "@wdio/config": "9.6.4", + "@wdio/logger": "9.4.4", + "@wdio/protocols": "9.7.0", + "@wdio/types": "9.6.3", + "@wdio/utils": "9.6.4", "deepmerge-ts": "^7.0.3", "undici": "^6.20.1", "ws": "^8.8.0" @@ -12552,6 +13025,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -12572,7 +13046,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/whatwg-encoding": { "version": "3.1.1", @@ -12612,6 +13087,7 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, + "license": "MIT", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -12905,10 +13381,11 @@ "dev": true }, "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, From 67bb682c362fd689852e9634d5b895980cb39380 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 08:14:09 +0000 Subject: [PATCH 507/567] Bump undici from 6.20.1 to 6.21.1 in the npm_and_yarn group (#1589) Bumps the npm_and_yarn group with 1 update: [undici](https://github.com/nodejs/undici). Updates `undici` from 6.20.1 to 6.21.1 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v6.20.1...v6.21.1) --- updated-dependencies: - dependency-name: undici dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e647514293..f57a5498d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12266,10 +12266,11 @@ } }, "node_modules/undici": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.1.tgz", - "integrity": "sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA==", + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.17" } From 098bb680587c3abd5c238a2a2e06c4e15173e1fa Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:38:37 +0000 Subject: [PATCH 508/567] Validate schemas script fix (#1582) Co-authored-by: Rhys Berrow <47635349+berroar@users.noreply.github.com> --- .github/workflows/pull_request.yml | 13 ++- Makefile | 4 +- scripts/validate_test_schemas.py | 136 +++++++++++++++++++++++++++++ scripts/validate_test_schemas.sh | 76 ---------------- 4 files changed, 150 insertions(+), 79 deletions(-) create mode 100644 scripts/validate_test_schemas.py delete mode 100755 scripts/validate_test_schemas.sh diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 20f0254cc0..0a606602a1 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -115,9 +115,20 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 + - run: | + echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Run validator run: ./scripts/run_validator.sh - - name: Running schema tests + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: 1.8.4 + virtualenvs-create: true + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + cache: "poetry" + - name: Running schema validation run: make validate-test-schemas test-functional: needs: [python-dependencies, node-dependencies] diff --git a/Makefile b/Makefile index 907e145505..bb9da1f6f6 100644 --- a/Makefile +++ b/Makefile @@ -67,10 +67,10 @@ generate-spec: poetry run python -m tests.functional.generate_pages schemas/test/en/$(SCHEMA).json ./tests/functional/generated_pages/$(patsubst test_%,%,$(SCHEMA)) -r '../../base_pages' -s tests/functional/spec/$(SCHEMA).spec.js validate-test-schemas: - ./scripts/validate_test_schemas.sh + poetry run python -m scripts.validate_test_schemas validate-test-schema: - ./scripts/validate_test_schemas.sh $(SCHEMA_PATH)$(SCHEMA).json + poetry run python -m scripts.validate_test_schemas $(SCHEMA_PATH)$(SCHEMA).json translation-templates: poetry run python -m scripts.extract_translation_templates diff --git a/scripts/validate_test_schemas.py b/scripts/validate_test_schemas.py new file mode 100644 index 0000000000..12b7ff2fbb --- /dev/null +++ b/scripts/validate_test_schemas.py @@ -0,0 +1,136 @@ +import json +import logging +import os +import re +import subprocess +import sys +import time +from concurrent.futures import ThreadPoolExecutor, as_completed + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) + + +def check_connection(): + for connection_attempts in range(4, 0, -1): + response = subprocess.run( + [ + "curl", + "-so", + "/dev/null", + "-w", + "%{http_code}", + "http://localhost:5002/status", + ], + capture_output=True, + text=True, + check=False, + ).stdout.strip() + + if response == "200": + return + + logging.error("\033[31m---Error: Schema Validator Not Reachable---\033[0m") + logging.error("\033[31mHTTP Status: %s\033[0m", response) + + if connection_attempts == 1: + logging.info("Exiting...\n") + sys.exit(1) + + logging.info("Retrying...\n") + time.sleep(5) + + +def get_schemas() -> list[str]: + if len(sys.argv) == 1 or sys.argv[1] == "--local": + file_path = "./schemas/test/en" + schemas = [ + os.path.join(file_path, f) + for f in os.listdir(file_path) + if f.endswith(".json") + ] + logging.info("--- Testing Schemas in %s ---", file_path) + else: + schema = sys.argv[1] + schemas = [schema] + logging.info("--- Testing %s Schema ---", schema) + return schemas + + +def validate_schema(schema_path): + try: + result = subprocess.run( + [ + "curl", + "-s", + "-w", + "HTTPSTATUS:%{http_code}", + "-X", + "POST", + "-H", + "Content-Type: application/json", + "-d", + f"@{schema_path}", + "http://localhost:5001/validate", + ], + capture_output=True, + text=True, + check=True, + ) + return schema_path, result.stdout + except subprocess.CalledProcessError as e: + logging.info("Error validating schema %s: %s", schema_path, e) + return schema_path, None + + +def main(): + # pylint: disable=broad-exception-caught + error = False + passed = 0 + failed = 0 + + check_connection() + schemas = get_schemas() + + with ThreadPoolExecutor(max_workers=20) as executor: + future_to_schema = { + executor.submit(validate_schema, schema): schema for schema in schemas + } + for future in as_completed(future_to_schema): + schema = future_to_schema[future] + try: + schema_path, result = future.result() + # Extract HTTP body + http_body = re.sub(r"HTTPSTATUS:.*", "", result) + + # Convert HTTP body to JSON + http_body_json = json.loads(http_body) + + # Format JSON + formatted_json = json.dumps(http_body_json, indent=4) + + # Extract HTTP status code + result_response = re.search(r"HTTPSTATUS:(\d+)", result)[1] + + if result_response == "200" and http_body_json == {}: + logging.info("\033[32m%s: PASSED\033[0m", schema_path) + passed += 1 + else: + logging.error("\033[31m%s: FAILED\033[0m", schema_path) + logging.error( + "\033[31mHTTP Status @ /validate: %s\033[0m", result_response + ) + logging.error("\033[31mHTTP Status: %s\033[0m", formatted_json) + error = True + failed += 1 + except Exception as e: + logging.error("\033[31mError processing %s: %s\033[0m", schema, e) + + logging.info("\033[32m%s passed\033[0m - \033[31m%s failed\033[0m", passed, failed) + if error: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/validate_test_schemas.sh b/scripts/validate_test_schemas.sh deleted file mode 100755 index e585590bc6..0000000000 --- a/scripts/validate_test_schemas.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -green="$(tput setaf 2)" -red="$(tput setaf 1)" -default="$(tput sgr0)" -checks=4 - - - - -until [ "$checks" == 0 ]; do - response="$(curl -so /dev/null -w '%{http_code}' http://localhost:5002/status)" - - if [ "$response" != "200" ]; then - echo "${red}---Error: Schema Validator Not Reachable---" - echo "HTTP Status: $response" - if [ "$checks" != 1 ]; then - echo -e "Retrying...${default}\\n" - sleep 5 - else - echo -e "Exiting...${default}\\n" - exit 1 - fi - (( checks-- )) - else - (( checks=0 )) - fi - -done - -exit=0 - -if [ $# -eq 0 ] || [ "$1" == "--local" ]; then - file_path="./schemas/test/en" -else - file_path="$1" -fi - -echo "--- Testing Schemas in $file_path ---" -failed=0 -passed=0 - -file_path_name=$(find "$file_path" -name '*.json') - -validate() { - schema=$1 - result="$(curl -s -w 'HTTPSTATUS:%{http_code}' -X POST -H "Content-Type: application/json" -d @"$schema" http://localhost:5001/validate | tr -d '\n')" - # shellcheck disable=SC2001 - HTTP_BODY=$(echo "${result}" | sed -e 's/HTTPSTATUS\:.*//g') - result_response="${result//*HTTPSTATUS:/}" - result_body=$(echo "$HTTP_BODY" | python -m json.tool) - - if [ "$result_response" == "200" ] && [ "$result_body" == "{}" ]; then - echo -e "${green}$schema - PASSED${default}" - (( passed++ )) - else - echo -e "\\n${red}$schema - FAILED" - echo "HTTP Status @ /validate: [$result_response]" - echo -e "Error: [$result_body]${default}\\n" - (( failed++ )) - exit=1 - fi -} - - -N_TIMES_IN_PARALLEL=20 - -for schema in ${file_path_name}; do - ((i=i%N_TIMES_IN_PARALLEL)); ((i++==0)) && wait -# Spawn multiple (N_TIMES_IN_PARALLEL) processes in subshells and send to background, but keep printing outputs. - validate "$schema" & -done - - - -exit "$exit" From 9d45237bd51fd58df2d391eaff4bdfdce9938811 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:27:30 +0000 Subject: [PATCH 509/567] Pin Poetry dotenv plugin version in our actions workflow (#1605) --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 0a606602a1..42468fe534 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -102,7 +102,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" - name: Install dotenv plugin - run: poetry self add poetry-plugin-dotenv + run: poetry self add poetry-plugin-dotenv@2.6.1 - name: Load templates run: make load-design-system-templates - name: Compile translations From a65a727c5dd9159e327ed83051c00afa4c2e9bbf Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 12 Feb 2025 16:11:24 +0000 Subject: [PATCH 510/567] Schemas v5.25.1 --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 640efa5d83..cf92f57264 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.24.0 +v5.25.1 From 7561d309810a5f1cc715f20ab3a72b6a9c66ae52 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Wed, 19 Feb 2025 07:32:58 +0000 Subject: [PATCH 511/567] Schemas v5.26.0 (#1609) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index cf92f57264..99afc2b7b8 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.25.1 +v5.26.0 From d344af02e4388f19a17ae1ccd2391224e471a6bf Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 26 Feb 2025 09:35:13 +0000 Subject: [PATCH 512/567] Schemas v5.27.0 --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 99afc2b7b8..0cc951a571 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.26.0 +v5.27.0 From c4875fda843b32a7e21f6fb6c46441ef84824e33 Mon Sep 17 00:00:00 2001 From: Rhys Berrow <47635349+berroar@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:18:37 +0000 Subject: [PATCH 513/567] Move supplementary data and hub and spoke functional tests to their own folders (#1614) --- .github/workflows/pull_request.yml | 2 +- .../hub_and_spoke/hub_and_spoke.spec.js | 50 +++++++------- .../supplementary_data.spec.js | 66 +++++++++---------- tests/functional/wdio.conf.js | 2 + 4 files changed, 61 insertions(+), 59 deletions(-) rename tests/functional/spec/{journeys => }/hub_and_spoke/hub_and_spoke.spec.js (87%) rename tests/functional/spec/{features => }/supplementary_data/supplementary_data.spec.js (90%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 42468fe534..7e4f42e8ca 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -134,7 +134,7 @@ jobs: needs: [python-dependencies, node-dependencies] strategy: matrix: - suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, summaries, general, journeys, components, list_collector] + suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, summaries, general, journeys, components, list_collector, hub_and_spoke, supplementary_data] runs-on: ubuntu-22.04 timeout-minutes: 30 env: diff --git a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js b/tests/functional/spec/hub_and_spoke/hub_and_spoke.spec.js similarity index 87% rename from tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js rename to tests/functional/spec/hub_and_spoke/hub_and_spoke.spec.js index 9b5d81f165..d4ca64bd64 100644 --- a/tests/functional/spec/journeys/hub_and_spoke/hub_and_spoke.spec.js +++ b/tests/functional/spec/hub_and_spoke/hub_and_spoke.spec.js @@ -1,28 +1,28 @@ -import AccomodationDetailsSummaryBlockPage from "../../../generated_pages/hub_and_spoke/accommodation-section-summary.page.js"; -import AnyoneRelated from "../../../generated_pages/hub_and_spoke/anyone-related.page.js"; -import DoesAnyoneLiveHere from "../../../generated_pages/hub_and_spoke/does-anyone-live-here.page.js"; -import EmploymentStatusBlockPage from "../../../generated_pages/hub_and_spoke/employment-status.page.js"; -import EmploymentTypeBlockPage from "../../../generated_pages/hub_and_spoke/employment-type.page.js"; -import HouseholdSummary from "../../../generated_pages/hub_and_spoke/household-section-summary.page.js"; -import HowManyPeopleLiveHere from "../../../generated_pages/hub_and_spoke/how-many-people-live-here.page.js"; -import HubPage from "../../../base_pages/hub.page.js"; -import ProxyPage from "../../../generated_pages/hub_and_spoke/proxy.page.js"; -import RelationshipsSummary from "../../../generated_pages/hub_and_spoke/relationships-section-summary.page.js"; -import ListCollectorSectionSummaryPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector-section-summary.page.js"; -import ProxyRepeatPage from "../../../generated_pages/hub_section_required_with_repeat/proxy.page.js"; -import { click, verifyUrlContains } from "../../../helpers"; -import DateOfBirthPage from "../../../generated_pages/hub_section_required_with_repeat/date-of-birth.page"; -import PrimaryPersonListCollectorPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector.page"; -import PrimaryPersonListCollectorAddPage from "../../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector-add.page"; -import ListCollectorPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector.page"; -import ListCollectorAddPage from "../../../generated_pages/hub_section_required_with_repeat/list-collector-add.page"; -import RepeatingSummaryPage from "../../../generated_pages/hub_section_required_with_repeat/personal-details-section-summary.page"; -import { getRandomString } from "../../../jwt_helper"; -import LoadedSuccessfullyBlockPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/loaded-successfully-block.page"; -import IntroductionBlockPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/introduction-block.page"; -import ListCollectorEmployeesPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/list-collector-employees.page.js"; -import LengthOfEmploymentPage from "../../../generated_pages/hub_section_required_with_repeat_supplementary/length-of-employment.page.js"; -import Section3Page from "../../../generated_pages/hub_section_required_with_repeat_supplementary/section-3-summary.page.js"; +import AccomodationDetailsSummaryBlockPage from "../../generated_pages/hub_and_spoke/accommodation-section-summary.page.js"; +import AnyoneRelated from "../../generated_pages/hub_and_spoke/anyone-related.page.js"; +import DoesAnyoneLiveHere from "../../generated_pages/hub_and_spoke/does-anyone-live-here.page.js"; +import EmploymentStatusBlockPage from "../../generated_pages/hub_and_spoke/employment-status.page.js"; +import EmploymentTypeBlockPage from "../../generated_pages/hub_and_spoke/employment-type.page.js"; +import HouseholdSummary from "../../generated_pages/hub_and_spoke/household-section-summary.page.js"; +import HowManyPeopleLiveHere from "../../generated_pages/hub_and_spoke/how-many-people-live-here.page.js"; +import HubPage from "../../base_pages/hub.page.js"; +import ProxyPage from "../../generated_pages/hub_and_spoke/proxy.page.js"; +import RelationshipsSummary from "../../generated_pages/hub_and_spoke/relationships-section-summary.page.js"; +import ListCollectorSectionSummaryPage from "../../generated_pages/hub_section_required_with_repeat/list-collector-section-summary.page.js"; +import ProxyRepeatPage from "../../generated_pages/hub_section_required_with_repeat/proxy.page.js"; +import { click, verifyUrlContains } from "../../helpers"; +import DateOfBirthPage from "../../generated_pages/hub_section_required_with_repeat/date-of-birth.page"; +import PrimaryPersonListCollectorPage from "../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector.page"; +import PrimaryPersonListCollectorAddPage from "../../generated_pages/hub_section_required_with_repeat/primary-person-list-collector-add.page"; +import ListCollectorPage from "../../generated_pages/hub_section_required_with_repeat/list-collector.page"; +import ListCollectorAddPage from "../../generated_pages/hub_section_required_with_repeat/list-collector-add.page"; +import RepeatingSummaryPage from "../../generated_pages/hub_section_required_with_repeat/personal-details-section-summary.page"; +import { getRandomString } from "../../jwt_helper"; +import LoadedSuccessfullyBlockPage from "../../generated_pages/hub_section_required_with_repeat_supplementary/loaded-successfully-block.page"; +import IntroductionBlockPage from "../../generated_pages/hub_section_required_with_repeat_supplementary/introduction-block.page"; +import ListCollectorEmployeesPage from "../../generated_pages/hub_section_required_with_repeat_supplementary/list-collector-employees.page.js"; +import LengthOfEmploymentPage from "../../generated_pages/hub_section_required_with_repeat_supplementary/length-of-employment.page.js"; +import Section3Page from "../../generated_pages/hub_section_required_with_repeat_supplementary/section-3-summary.page.js"; describe("Feature: Hub and Spoke", () => { const hubAndSpokeSchema = "test_hub_and_spoke.json"; diff --git a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/supplementary_data/supplementary_data.spec.js similarity index 90% rename from tests/functional/spec/features/supplementary_data/supplementary_data.spec.js rename to tests/functional/spec/supplementary_data/supplementary_data.spec.js index 2bc821d5cd..171f3ae1a8 100644 --- a/tests/functional/spec/features/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/supplementary_data/supplementary_data.spec.js @@ -1,37 +1,37 @@ -import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, listItemComplete, click, verifyUrlContains } from "../../../helpers"; +import { assertSummaryItems, assertSummaryTitles, assertSummaryValues, listItemComplete, click, verifyUrlContains } from "../../helpers"; import { expect } from "@wdio/globals"; -import { getRandomString } from "../../../jwt_helper"; -import AddAdditionalEmployeePage from "../../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; -import AdditionalLengthOfEmploymentPage from "../../../generated_pages/supplementary_data/additional-length-of-employment.page.js"; -import AnyAdditionalEmployeesPage from "../../../generated_pages/supplementary_data/any-additional-employees.page.js"; -import CalculatedSummarySalesPage from "../../../generated_pages/supplementary_data/calculated-summary-sales.page.js"; -import CalculatedSummaryValueSalesPage from "../../../generated_pages/supplementary_data/calculated-summary-value-sales.page.js"; -import CalculatedSummaryVolumeSalesPage from "../../../generated_pages/supplementary_data/calculated-summary-volume-sales.page.js"; -import CalculatedSummaryVolumeTotalPage from "../../../generated_pages/supplementary_data/calculated-summary-volume-total.page.js"; -import DynamicProductsPage from "../../../generated_pages/supplementary_data/dynamic-products.page.js"; -import EmailBlockPage from "../../../generated_pages/supplementary_data/email-block.page.js"; -import HubPage from "../../../base_pages/hub.page"; -import IntroductionBlockPage from "../../../generated_pages/supplementary_data/introduction-block.page.js"; -import LengthOfEmploymentPage from "../../../generated_pages/supplementary_data/length-of-employment.page.js"; -import ListCollectorAdditionalPage from "../../../generated_pages/supplementary_data/list-collector-additional.page.js"; -import ListCollectorEmployeesPage from "../../../generated_pages/supplementary_data/list-collector-employees.page.js"; -import ListCollectorProductsPage from "../../../generated_pages/supplementary_data/list-collector-products.page.js"; -import LoadedSuccessfullyBlockPage from "../../../generated_pages/supplementary_data/loaded-successfully-block.page.js"; -import NewEmailPage from "../../../generated_pages/supplementary_data/new-email.page.js"; -import ProductQuestion3EnabledPage from "../../../generated_pages/supplementary_data/product-question-3-enabled.page"; -import ProductRepeatingBlock1Page from "../../../generated_pages/supplementary_data/product-repeating-block-1-repeating-block.page.js"; -import ProductSalesInterstitialPage from "../../../generated_pages/supplementary_data/product-sales-interstitial.page"; -import ProductVolumeInterstitialPage from "../../../generated_pages/supplementary_data/product-volume-interstitial.page"; -import SalesBreakdownBlockPage from "../../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; -import Section1InterstitialPage from "../../../generated_pages/supplementary_data/section-1-interstitial.page.js"; -import Section1Page from "../../../generated_pages/supplementary_data/section-1-summary.page.js"; -import Section3Page from "../../../generated_pages/supplementary_data/section-3-summary.page.js"; -import Section4Page from "../../../generated_pages/supplementary_data/section-4-summary.page.js"; -import Section5Page from "../../../generated_pages/supplementary_data/section-5-summary.page.js"; -import Section6Page from "../../../generated_pages/supplementary_data/section-6-summary.page.js"; -import ThankYouPage from "../../../base_pages/thank-you.page"; -import TradingPage from "../../../generated_pages/supplementary_data/trading.page.js"; -import ViewSubmittedResponsePage from "../../../generated_pages/supplementary_data/view-submitted-response.page.js"; +import { getRandomString } from "../../jwt_helper"; +import AddAdditionalEmployeePage from "../../generated_pages/supplementary_data/list-collector-additional-add.page.js"; +import AdditionalLengthOfEmploymentPage from "../../generated_pages/supplementary_data/additional-length-of-employment.page.js"; +import AnyAdditionalEmployeesPage from "../../generated_pages/supplementary_data/any-additional-employees.page.js"; +import CalculatedSummarySalesPage from "../../generated_pages/supplementary_data/calculated-summary-sales.page.js"; +import CalculatedSummaryValueSalesPage from "../../generated_pages/supplementary_data/calculated-summary-value-sales.page.js"; +import CalculatedSummaryVolumeSalesPage from "../../generated_pages/supplementary_data/calculated-summary-volume-sales.page.js"; +import CalculatedSummaryVolumeTotalPage from "../../generated_pages/supplementary_data/calculated-summary-volume-total.page.js"; +import DynamicProductsPage from "../../generated_pages/supplementary_data/dynamic-products.page.js"; +import EmailBlockPage from "../../generated_pages/supplementary_data/email-block.page.js"; +import HubPage from "../../base_pages/hub.page"; +import IntroductionBlockPage from "../../generated_pages/supplementary_data/introduction-block.page.js"; +import LengthOfEmploymentPage from "../../generated_pages/supplementary_data/length-of-employment.page.js"; +import ListCollectorAdditionalPage from "../../generated_pages/supplementary_data/list-collector-additional.page.js"; +import ListCollectorEmployeesPage from "../../generated_pages/supplementary_data/list-collector-employees.page.js"; +import ListCollectorProductsPage from "../../generated_pages/supplementary_data/list-collector-products.page.js"; +import LoadedSuccessfullyBlockPage from "../../generated_pages/supplementary_data/loaded-successfully-block.page.js"; +import NewEmailPage from "../../generated_pages/supplementary_data/new-email.page.js"; +import ProductQuestion3EnabledPage from "../../generated_pages/supplementary_data/product-question-3-enabled.page"; +import ProductRepeatingBlock1Page from "../../generated_pages/supplementary_data/product-repeating-block-1-repeating-block.page.js"; +import ProductSalesInterstitialPage from "../../generated_pages/supplementary_data/product-sales-interstitial.page"; +import ProductVolumeInterstitialPage from "../../generated_pages/supplementary_data/product-volume-interstitial.page"; +import SalesBreakdownBlockPage from "../../generated_pages/supplementary_data/sales-breakdown-block.page.js"; +import Section1InterstitialPage from "../../generated_pages/supplementary_data/section-1-interstitial.page.js"; +import Section1Page from "../../generated_pages/supplementary_data/section-1-summary.page.js"; +import Section3Page from "../../generated_pages/supplementary_data/section-3-summary.page.js"; +import Section4Page from "../../generated_pages/supplementary_data/section-4-summary.page.js"; +import Section5Page from "../../generated_pages/supplementary_data/section-5-summary.page.js"; +import Section6Page from "../../generated_pages/supplementary_data/section-6-summary.page.js"; +import ThankYouPage from "../../base_pages/thank-you.page"; +import TradingPage from "../../generated_pages/supplementary_data/trading.page.js"; +import ViewSubmittedResponsePage from "../../generated_pages/supplementary_data/view-submitted-response.page.js"; describe("Using supplementary data", () => { const responseId = getRandomString(16); diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.js index 0544160040..24ddfbe834 100644 --- a/tests/functional/wdio.conf.js +++ b/tests/functional/wdio.conf.js @@ -27,6 +27,8 @@ exports.config = { journeys: ["./spec/journeys/**/*.js"], list_collector: ["./spec/list_collector/**/*.js"], general: ["./spec/*.spec.js"], + hub_and_spoke: ["./spec/hub_and_spoke/**/*.js"], + supplementary_data: ["./spec/supplementary_data/**/*.js"], }, // Patterns to exclude. exclude: [ From ffb91c175fa5daf1b06137e20ad562dd46c71440 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 3 Mar 2025 11:06:07 +0000 Subject: [PATCH 514/567] Migrate DS to version 72.2.0 (#1579) --- .design-system-version | 2 +- app/helpers/language_helper.py | 2 +- app/jinja_filters.py | 48 +++++++----- app/views/contexts/hub_context.py | 8 +- templates/confirm-email.html | 2 +- templates/feedback.html | 2 +- templates/partials/answers/address.html | 8 +- templates/partials/answers/textarea.html | 2 +- .../partials/feedback-call-to-action.html | 2 +- templates/partials/introduction/preview.html | 2 +- templates/partials/summary/list-summary.html | 2 +- templates/partials/summary/summary.html | 4 +- tests/app/test_jinja_filters.py | 76 +++++++++---------- tests/app/views/contexts/test_hub_context.py | 18 ++--- tests/functional/base_pages/question.page.js | 2 +- .../view_submitted_response_base.page.js | 4 +- tests/functional/generate_pages.py | 2 +- .../list_collector_section_summary.spec.js | 75 +++++++++--------- .../supplementary_data.spec.js | 20 ++--- 19 files changed, 141 insertions(+), 140 deletions(-) diff --git a/.design-system-version b/.design-system-version index 10f98a1349..420c080fbd 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -70.0.16 +72.2.0 diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 87f9afc70f..f077ec3a5e 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -59,7 +59,7 @@ def _get_language_context( language_code: str, current_language: str ) -> dict[str, str | bool]: return { - "ISOCode": language_code, + "isoCode": language_code, "url": _get_query_string_with_language(language_code), "text": LANGUAGE_TEXT[language_code], "current": language_code == current_language, diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 9c8754a8ee..2ba38a206e 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -482,11 +482,11 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche or is_summary_with_calculation(summary_type) or use_answer_label ) and answer.get("label"): - self.rowTitle = answer["label"] - self.rowTitleAttributes = {"data-qa": answer["id"] + "-label"} + self.title = answer["label"] + self.titleAttributes = {"data-qa": answer["id"] + "-label"} else: - self.rowTitle = strip_tags(question["title"]) - self.rowTitleAttributes = {"data-qa": question["id"]} + self.title = strip_tags(question["title"]) + self.titleAttributes = {"data-qa": question["id"]} if edit_link_text: self.id = answer["id"] @@ -546,7 +546,7 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche if answers_are_editable: self.actions = [ - SummaryAction(answer, self.rowTitle, edit_link_text, item_name) + SummaryAction(answer, self.title, edit_link_text, item_name) ] @@ -561,16 +561,16 @@ def __init__( use_answer_label: bool = False, item_name: str | None = None, ) -> None: - self.rowTitle = strip_tags(question["title"]) + self.title = strip_tags(question["title"]) self.id = question["id"] - self.rowItems = [] + self.itemsList = [] use_answer_label = use_answer_label or len(question["answers"]) > 1 if is_summary_with_calculation(summary_type) and not answers_are_editable: self.total = True for answer in question["answers"]: - self.rowItems.append( + self.itemsList.append( SummaryRowItem( question, answer, @@ -595,8 +595,8 @@ def map_summary_item_config( calculated_question: dict[str, list] | None, remove_link_text: str | None = None, remove_link_aria_label: str | None = None, -) -> list[dict[str, list] | SummaryRow]: - rows: list[dict[str, list] | SummaryRow] = [] +) -> list[dict[str, list | str] | SummaryRow]: + rows: list[dict[str, list | str] | SummaryRow] = [] for block in group["blocks"]: if block.get("question"): @@ -632,7 +632,6 @@ def map_summary_item_config( item_anchor=block.get("item_anchor"), answers_are_editable=answers_are_editable, ) - rows.extend(list_collector_rows) if is_summary_with_calculation(summary_type): @@ -684,8 +683,8 @@ def map_list_collector_config( item_label: str | None = None, item_anchor: str | None = None, answers_are_editable: bool = True, -) -> list[dict[str, list] | SummaryRow]: - rows: list[dict[str, list] | SummaryRow] = [] +) -> list[dict[str, list | str] | SummaryRow]: + rows: list[dict[str, list | str] | SummaryRow] = [] for index, list_item in enumerate(list_items, 1): item_name = str(list_item.get("item_title")) @@ -742,7 +741,7 @@ def map_list_collector_config( "iconVisuallyHiddenText": "Completed" if icon else None, "actions": actions, "id": list_item.get("list_item_id"), - "rowTitleAttributes": { + "titleAttributes": { "data-qa": f"list-item-{index}-label", "data-list-item-id": list_item.get("list_item_id"), }, @@ -751,10 +750,12 @@ def map_list_collector_config( if item_label: row_item["valueList"] = [{"text": item_name}] - row_item["rowTitle"] = item_label or item_name - row_items: list = [row_item] + title = item_label or item_name if related_answers: + # List summaries driven by List Collector Content pages don't use the row_item created above, as they are not editable + row_items_related: list = [row_item] if editable else [] + for block in related_answers[list_item["list_item_id"]]: summary_row = SummaryRow( block["question"], @@ -765,9 +766,18 @@ def map_list_collector_config( use_answer_label=True, item_name=item_name, ) - row_items.extend(summary_row.rowItems) - - rows.append({"rowItems": row_items}) + row_items_related.extend(summary_row.itemsList) + # Again, List Collector Content list summaries don't use the row_item with "title" and are structured differently + if editable: + row_item["title"] = title + rows.append({"itemsList": row_items_related}) + else: + rows.append({"title": title, "itemsList": row_items_related}) + # Logic for list summaries without dynamic answers (related to List Collector) to display + else: + row_item["title"] = title + row_items: list = [row_item] + rows.append({"itemsList": row_items}) return rows diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index 937f2cfb8e..ff0fa89c38 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -89,10 +89,10 @@ def get_row_context_for_section( ) -> dict[str, str | list]: section_content = self.SECTION_CONTENT_STATES[section_status] context: dict = { - "rowItems": [ + "itemsList": [ { - "rowTitle": section_name, - "rowTitleAttributes": {"data-qa": f"hub-row-{row_id}-title"}, + "title": section_name, + "titleAttributes": {"data-qa": f"hub-row-{row_id}-title"}, "attributes": {"data-qa": f"hub-row-{row_id}-state"}, "valueList": [{"text": section_content["text"]}], "actions": [ @@ -113,7 +113,7 @@ def get_row_context_for_section( CompletionStatus.COMPLETED, CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, ): - context["rowItems"][0]["iconType"] = "check" + context["itemsList"][0]["iconType"] = "check" return context diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 49c1ad15a5..6b87db58fb 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -14,7 +14,7 @@ {% set error_title = _("There is a problem with your answer") %} {% include "partials/error-panel.html" %} {% endif %} -

      {{ content.question.title }}

      +

      {{ content.question.title }}

      {{ content.question.description }}

      diff --git a/templates/feedback.html b/templates/feedback.html index 8d2b4abfa7..9055c3aabb 100644 --- a/templates/feedback.html +++ b/templates/feedback.html @@ -31,7 +31,7 @@ {% set error_title = ngettext('There is a problem with your feedback', 'There are %(num)s problems with your feedback', form.mapped_errors | length) %} {% include "partials/error-panel.html" %} {% endif %} -

      {{ content.question.title }}

      +

      {{ content.question.title }}

      {%- for answer in question.answers -%} {% include "partials/answer.html" %} {%- endfor -%} diff --git a/templates/partials/answers/address.html b/templates/partials/answers/address.html index 382880aff5..5999a573f9 100644 --- a/templates/partials/answers/address.html +++ b/templates/partials/answers/address.html @@ -35,8 +35,8 @@ "manualLinkText": _("Manually enter address"), "isEditable": true, "mandatory": answer.mandatory, - "APIDomain": address_lookup_api_url, - "APIDomainBearerToken": content.address_lookup_api_auth_token, + "apiDomain": address_lookup_api_url, + "apiDomainBearerToken": content.address_lookup_api_auth_token, "instructions": _("Use up and down keys to navigate suggestions once you’ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), "ariaYouHaveSelected": _("You have selected"), "ariaMinChars": _("Enter 3 or more characters for suggestions."), @@ -54,8 +54,8 @@ "errorTitle": ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', 1), "errorMessageEnter": _("Enter an address"), "errorMessageSelect": _("Select or manually enter an address"), - "errorMessageAPI": _("Sorry, there was a problem loading addresses"), - "errorMessageAPILinkText": _("Enter address manually"), + "errorMessageApi": _("Sorry, there was a problem loading addresses"), + "errorMessageApiLinkText": _("Enter address manually"), "options": { "regionCode": answer.lookup_options.region_code | lower, "oneYearAgo": answer.lookup_options.one_year_ago if answer.lookup_options.one_year_ago is defined, diff --git a/templates/partials/answers/textarea.html b/templates/partials/answers/textarea.html index a101f2f474..d2782dbdfa 100644 --- a/templates/partials/answers/textarea.html +++ b/templates/partials/answers/textarea.html @@ -16,7 +16,7 @@ "label": label, "value": input._value() | e, "charCheckLimit": { - "limit": answer.max_length | default(input.maxlength, true), + "limit": answer.max_length | default(input.maxLength, true), "charCountSingular": _("You have {x} character remaining"), "charCountPlural": _("You have {x} characters remaining") }, diff --git a/templates/partials/feedback-call-to-action.html b/templates/partials/feedback-call-to-action.html index 4186065279..701df56038 100644 --- a/templates/partials/feedback-call-to-action.html +++ b/templates/partials/feedback-call-to-action.html @@ -6,7 +6,7 @@ "id": "feedback", "heading": _("What do you think about this service?"), "content": _("Your comments will help us make improvements"), - "url": url_for('post_submission.send_feedback'), + "linkUrl": url_for('post_submission.send_feedback'), "linkText": _("Give feedback") }) }} diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index 5b5050ba8d..ba4f6c833d 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -15,7 +15,7 @@

      {{ intro.title }}

      {% endset %} {% set item = { "title": question.question, - "titleTag": "h3", + "headingLevel": 3, "content": content, "summaryAttributes": { "data-ga": "click", diff --git a/templates/partials/summary/list-summary.html b/templates/partials/summary/list-summary.html index 588f6cb923..f7bf6498f8 100644 --- a/templates/partials/summary/list-summary.html +++ b/templates/partials/summary/list-summary.html @@ -18,7 +18,7 @@ ) %} {% endif %} {% set group_config = { - "groupTitle": list_title, + "title": list_title, "rows": rows, "placeholderText": empty_list_text, } %} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index c0fa459536..98031dc831 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -15,7 +15,7 @@ {% do summary_groups.append ( { - "groupTitle": group.title if group.title else None, + "title": group.title if group.title else None, "id": group.id if group.id else None, "rows": map_summary_item_config( group=group, @@ -40,7 +40,7 @@ {% do summary_sections.append ( { - "summaryTitle": section.title if summary_type == "Summary", + "title": section.title if summary_type == "Summary", "groups": summary_groups } ) diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index c2c2b86d29..ab65e927f6 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -532,32 +532,32 @@ def test_map_list_collector_config_no_actions(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "one", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "one", "data-qa": "list-item-1-label", }, - "rowTitle": "Mark Bloggs", + "title": "Mark Bloggs", } ] }, { - "rowItems": [ + "itemsList": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "two", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "two", "data-qa": "list-item-2-label", }, - "rowTitle": "Joe Bloggs", + "title": "Joe Bloggs", } ] }, @@ -598,7 +598,7 @@ def test_map_list_collector_config(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [ { @@ -611,16 +611,16 @@ def test_map_list_collector_config(): "iconVisuallyHiddenText": None, "iconType": None, "id": "primary", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "primary", "data-qa": "list-item-1-label", }, - "rowTitle": "Mark Bloggs (You)", + "title": "Mark Bloggs (You)", } ] }, { - "rowItems": [ + "itemsList": [ { "actions": [ { @@ -639,11 +639,11 @@ def test_map_list_collector_config(): "iconType": None, "iconVisuallyHiddenText": None, "id": "nonprimary", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "nonprimary", "data-qa": "list-item-2-label", }, - "rowTitle": "Joe Bloggs", + "title": "Joe Bloggs", } ] }, @@ -716,7 +716,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [ { @@ -735,8 +735,8 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "iconVisuallyHiddenText": None, "iconType": None, "id": "VHoiow", - "rowTitle": "Name of UK company or branch", - "rowTitleAttributes": { + "title": "Name of UK company or branch", + "titleAttributes": { "data-list-item-id": "VHoiow", "data-qa": "list-item-1-label", }, @@ -760,8 +760,8 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "rowTitle": "Registration number", - "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "title": "Registration number", + "titleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, { @@ -784,9 +784,8 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): ], "attributes": {"data-qa": "authorised-insurer-radio"}, "id": "authorised-insurer-radio", - "rowTitle": "Is this UK company or branch an authorised " - "insurer?", - "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "title": "Is this UK company or branch an authorised " "insurer?", + "titleAttributes": {"data-qa": "authorised-insurer-radio-label"}, "valueList": [{"text": "Yes"}], }, ] @@ -1009,7 +1008,7 @@ def test_calculated_summary_config(): def test_summary_item_config_with_list_collector(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [ { @@ -1028,8 +1027,8 @@ def test_summary_item_config_with_list_collector(): "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", - "rowTitle": "Company A", - "rowTitleAttributes": { + "title": "Company A", + "titleAttributes": { "data-list-item-id": "vmmPmD", "data-qa": "list-item-1-label", }, @@ -1052,8 +1051,8 @@ def test_summary_item_config_with_list_collector(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "rowTitle": "Registration number", - "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "title": "Registration number", + "titleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, { @@ -1076,9 +1075,8 @@ def test_summary_item_config_with_list_collector(): ], "attributes": {"data-qa": "authorised-insurer-radio"}, "id": "authorised-insurer-radio", - "rowTitle": "Is this UK company or branch an authorised " - "insurer?", - "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "title": "Is this UK company or branch an authorised " "insurer?", + "titleAttributes": {"data-qa": "authorised-insurer-radio-label"}, "valueList": [{"text": "Yes"}], }, ] @@ -1168,7 +1166,7 @@ def test_summary_item_config_with_list_collector(): def test_summary_item_config_with_list_collector_and_one_related_answer(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [ { @@ -1187,8 +1185,8 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", - "rowTitle": "Company A", - "rowTitleAttributes": { + "title": "Company A", + "titleAttributes": { "data-list-item-id": "vmmPmD", "data-qa": "list-item-1-label", }, @@ -1212,8 +1210,8 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "rowTitle": "Registration number", - "rowTitleAttributes": {"data-qa": "registration-number-label"}, + "title": "Registration number", + "titleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, ] @@ -1306,32 +1304,32 @@ def test_map_list_collector_config_render_icon_set(): expected = [ { - "rowItems": [ + "itemsList": [ { "actions": [], "iconVisuallyHiddenText": "Completed", "iconType": "check", "id": "one", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "one", "data-qa": "list-item-1-label", }, - "rowTitle": "Mark Bloggs", + "title": "Mark Bloggs", } ] }, { - "rowItems": [ + "itemsList": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "two", - "rowTitleAttributes": { + "titleAttributes": { "data-list-item-id": "two", "data-qa": "list-item-2-label", }, - "rowTitle": "Joe Bloggs", + "title": "Joe Bloggs", } ] }, diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index c92db34d87..2ccff45cf9 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -14,10 +14,10 @@ def router(schema, data_stores): def test_get_not_started_row_for_section(schema, data_stores): expected = { - "rowItems": [ + "itemsList": [ { - "rowTitle": "Breakfast", - "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, + "title": "Breakfast", + "titleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "valueList": [{"text": "Not started"}], "actions": [ @@ -46,10 +46,10 @@ def test_get_not_started_row_for_section(schema, data_stores): def test_get_in_progress_row_for_section(schema, data_stores): expected = { - "rowItems": [ + "itemsList": [ { - "rowTitle": "Breakfast", - "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, + "title": "Breakfast", + "titleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "valueList": [{"text": "Partially completed"}], "actions": [ @@ -78,10 +78,10 @@ def test_get_in_progress_row_for_section(schema, data_stores): def test_get_completed_row_for_section(schema, data_stores): expected = { - "rowItems": [ + "itemsList": [ { - "rowTitle": "Breakfast", - "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, + "title": "Breakfast", + "titleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "iconType": "check", "valueList": [{"text": "Completed"}], diff --git a/tests/functional/base_pages/question.page.js b/tests/functional/base_pages/question.page.js index fcd926369f..9ed4976e60 100644 --- a/tests/functional/base_pages/question.page.js +++ b/tests/functional/base_pages/question.page.js @@ -31,7 +31,7 @@ class QuestionBasePage extends BasePage { } errorNumber(number = 1) { - return `[data-qa="error-link-${number}"]`; + return `[data-qa="error-link-${number}"] > a`; } cancelAndReturn() { diff --git a/tests/functional/base_pages/view_submitted_response_base.page.js b/tests/functional/base_pages/view_submitted_response_base.page.js index 785fc4589c..b91e10651e 100644 --- a/tests/functional/base_pages/view_submitted_response_base.page.js +++ b/tests/functional/base_pages/view_submitted_response_base.page.js @@ -6,11 +6,11 @@ class ViewSubmittedResponseBasePage extends BasePage { } metadataTerm(number = 1) { - return `.ons-description-list > dt:nth-of-type(${number})`; + return `.ons-description-list > .ons-description-list__item:nth-of-type(${number}) > dt`; } metadataValue(number = 1) { - return `.ons-description-list > dd:nth-of-type(${number})`; + return `.ons-description-list > .ons-description-list__item:nth-of-type(${number}) > dd`; } informationPanel() { diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 156b678297..86fcc66112 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -158,7 +158,7 @@ """ -ANSWER_SINGLE_ERROR_LINK_GETTER = r""" singleErrorLink() { return `p[data-qa="error-list"]`; } +ANSWER_SINGLE_ERROR_LINK_GETTER = r""" singleErrorLink() { return `p[data-qa="error-link-1"]`; } """ diff --git a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js index ab018ca044..c591cc89ff 100644 --- a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js @@ -30,15 +30,17 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await expect(await $(SectionSummaryPage.companiesListLabel(1)).getText()).toContain("Name of UK company or branch"); - await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); const listItemId = (await listItemIds())[0]; - await expect(await $(companiesListRowItemAnchor(1)).getHTML()).toContain( + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[0].getHTML()).toContain( `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name`, ); - await expect(await $(companiesListRowItemAnchor(2)).getHTML()).toContain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); - await expect(await $(companiesListRowItemAnchor(3)).getHTML()).toContain( + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[1].getHTML()).toContain( + `return_to_answer_id=registration-number-${listItemId}#registration-number`, + ); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[2].getHTML()).toContain( `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio`, ); }); @@ -51,15 +53,15 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company C", "789", true); await anyMoreCompaniesNo(); await answerUkBasedQuestion(); - await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); - await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); - await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("No"); - await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("789"); - await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[2].getText()).toContain("No"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("789"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); }); it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", async () => { await drivingQuestionYes(); @@ -112,7 +114,7 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); await verifyUrlContains("edit-company/?return_to=section-summary"); await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).toBe("Company A"); @@ -259,15 +261,15 @@ describe("List Collector Section Summary and Summary Items", () => { await click(SectionSummaryTwoPage.submit()); await verifyUrlContains(SubmitPage.url()); - await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); - await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Change"); - await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); - await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("Change"); - await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); - await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Change"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("234"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); await expect(await $(SubmitPage.householderCheckboxAnswer()).getText()).toContain("No"); await expect(await $("body").getHTML()).toContain("Add another UK company or branch"); await expect(await $("body").getHTML()).toContain("Remove"); @@ -295,12 +297,15 @@ describe("List Collector Section Summary and Summary Items", () => { await $(ThankYouPage.savePrintAnswersLink()).click(); await verifyUrlContains(ViewSubmittedResponsePage.pageName); - await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); - await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); - await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); + await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); + await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("234"); + await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); await expect(await $("body").getHTML()).not.toContain("Change"); await expect(await $("body").getHTML()).not.toContain("Remove"); await expect(await $("body").getHTML()).not.toContain("Add another UK company or branch"); @@ -350,10 +355,6 @@ const answerUkBasedQuestion = async () => { await click(UkBasedPage.submit()); }; -const companiesListRowItem = (row, index) => { - return `#group-companies-1 .ons-summary__items .ons-summary__item:nth-of-type(${row}) .ons-summary__row:nth-of-type(${index})`; -}; - -const companiesListRowItemAnchor = (index) => { - return `#group-companies-1 .ons-summary__items .ons-summary__item .ons-summary__row:nth-of-type(${index}) a`; +const companiesListRowItem = (row) => { + return `#group-companies-1 .ons-summary__items .ons-summary__item:nth-of-type(${row})`; }; diff --git a/tests/functional/spec/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/supplementary_data/supplementary_data.spec.js index 171f3ae1a8..e6a25d47a3 100644 --- a/tests/functional/spec/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/supplementary_data/supplementary_data.spec.js @@ -314,12 +314,10 @@ describe("Using supplementary data", () => { }); it("Given I have a section with repeating answers for a supplementary list, When I reach the section summary page, Then I see the supplementary data and my answers rendered correctly", async () => { - await expect(await $$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); + await expect(await $("#dynamic-answer-question .ons-summary__row-title").getText()).toBe("Sales during the previous quarter"); await assertSummaryItems([ - "Articles and equipment for sports or outdoor games", "Volume of sales for Articles and equipment for sports or outdoor games", "Total volume produced for Articles and equipment for sports or outdoor games", - "Kitchen Equipment", "Volume of sales for Kitchen Equipment", "Total volume produced for Kitchen Equipment", "Value of sales for Articles and equipment for sports or outdoor games", @@ -487,29 +485,23 @@ describe("Using supplementary data", () => { // Product details await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).toBe( - "Articles and equipment for sports or outdoor games", - ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).toBe( "Volume of sales for Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).toBe( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).toBe( "Total volume produced for Articles and equipment for sports or outdoor games", ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).toBe("Volume of sales for Kitchen Equipment"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).toBe("100 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).toBe("200 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).toBe("Kitchen Equipment"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).toBe("Volume of sales for Kitchen Equipment"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).toBe( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).toBe( "Total volume produced for Kitchen Equipment", ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).toBe("Volume of sales for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).toBe("Total volume produced for Groceries"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).toBe("50 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).toBe("300 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[6].getText()).toBe("Groceries"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[7].getText()).toBe("Volume of sales for Groceries"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[8].getText()).toBe("Total volume produced for Groceries"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[4].getText()).toBe("40 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[5].getText()).toBe("50 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).toBe( "Value of sales for Articles and equipment for sports or outdoor games", ); From d7d4d5738f80d1fd6fe35b3c8cd77852ac86fc7a Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 6 Mar 2025 15:02:28 +0000 Subject: [PATCH 515/567] Revert "Migrate DS to version 72.2.0 (#1579)" (#1623) This reverts commit ffb91c175fa5daf1b06137e20ad562dd46c71440. --- .design-system-version | 2 +- app/helpers/language_helper.py | 2 +- app/jinja_filters.py | 48 +++++------- app/views/contexts/hub_context.py | 8 +- templates/confirm-email.html | 2 +- templates/feedback.html | 2 +- templates/partials/answers/address.html | 8 +- templates/partials/answers/textarea.html | 2 +- .../partials/feedback-call-to-action.html | 2 +- templates/partials/introduction/preview.html | 2 +- templates/partials/summary/list-summary.html | 2 +- templates/partials/summary/summary.html | 4 +- tests/app/test_jinja_filters.py | 76 ++++++++++--------- tests/app/views/contexts/test_hub_context.py | 18 ++--- tests/functional/base_pages/question.page.js | 2 +- .../view_submitted_response_base.page.js | 4 +- tests/functional/generate_pages.py | 2 +- .../list_collector_section_summary.spec.js | 75 +++++++++--------- .../supplementary_data.spec.js | 20 +++-- 19 files changed, 140 insertions(+), 141 deletions(-) diff --git a/.design-system-version b/.design-system-version index 420c080fbd..10f98a1349 100644 --- a/.design-system-version +++ b/.design-system-version @@ -1 +1 @@ -72.2.0 +70.0.16 diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index f077ec3a5e..87f9afc70f 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -59,7 +59,7 @@ def _get_language_context( language_code: str, current_language: str ) -> dict[str, str | bool]: return { - "isoCode": language_code, + "ISOCode": language_code, "url": _get_query_string_with_language(language_code), "text": LANGUAGE_TEXT[language_code], "current": language_code == current_language, diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 2ba38a206e..9c8754a8ee 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -482,11 +482,11 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche or is_summary_with_calculation(summary_type) or use_answer_label ) and answer.get("label"): - self.title = answer["label"] - self.titleAttributes = {"data-qa": answer["id"] + "-label"} + self.rowTitle = answer["label"] + self.rowTitleAttributes = {"data-qa": answer["id"] + "-label"} else: - self.title = strip_tags(question["title"]) - self.titleAttributes = {"data-qa": question["id"]} + self.rowTitle = strip_tags(question["title"]) + self.rowTitleAttributes = {"data-qa": question["id"]} if edit_link_text: self.id = answer["id"] @@ -546,7 +546,7 @@ def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branche if answers_are_editable: self.actions = [ - SummaryAction(answer, self.title, edit_link_text, item_name) + SummaryAction(answer, self.rowTitle, edit_link_text, item_name) ] @@ -561,16 +561,16 @@ def __init__( use_answer_label: bool = False, item_name: str | None = None, ) -> None: - self.title = strip_tags(question["title"]) + self.rowTitle = strip_tags(question["title"]) self.id = question["id"] - self.itemsList = [] + self.rowItems = [] use_answer_label = use_answer_label or len(question["answers"]) > 1 if is_summary_with_calculation(summary_type) and not answers_are_editable: self.total = True for answer in question["answers"]: - self.itemsList.append( + self.rowItems.append( SummaryRowItem( question, answer, @@ -595,8 +595,8 @@ def map_summary_item_config( calculated_question: dict[str, list] | None, remove_link_text: str | None = None, remove_link_aria_label: str | None = None, -) -> list[dict[str, list | str] | SummaryRow]: - rows: list[dict[str, list | str] | SummaryRow] = [] +) -> list[dict[str, list] | SummaryRow]: + rows: list[dict[str, list] | SummaryRow] = [] for block in group["blocks"]: if block.get("question"): @@ -632,6 +632,7 @@ def map_summary_item_config( item_anchor=block.get("item_anchor"), answers_are_editable=answers_are_editable, ) + rows.extend(list_collector_rows) if is_summary_with_calculation(summary_type): @@ -683,8 +684,8 @@ def map_list_collector_config( item_label: str | None = None, item_anchor: str | None = None, answers_are_editable: bool = True, -) -> list[dict[str, list | str] | SummaryRow]: - rows: list[dict[str, list | str] | SummaryRow] = [] +) -> list[dict[str, list] | SummaryRow]: + rows: list[dict[str, list] | SummaryRow] = [] for index, list_item in enumerate(list_items, 1): item_name = str(list_item.get("item_title")) @@ -741,7 +742,7 @@ def map_list_collector_config( "iconVisuallyHiddenText": "Completed" if icon else None, "actions": actions, "id": list_item.get("list_item_id"), - "titleAttributes": { + "rowTitleAttributes": { "data-qa": f"list-item-{index}-label", "data-list-item-id": list_item.get("list_item_id"), }, @@ -750,12 +751,10 @@ def map_list_collector_config( if item_label: row_item["valueList"] = [{"text": item_name}] - title = item_label or item_name + row_item["rowTitle"] = item_label or item_name + row_items: list = [row_item] if related_answers: - # List summaries driven by List Collector Content pages don't use the row_item created above, as they are not editable - row_items_related: list = [row_item] if editable else [] - for block in related_answers[list_item["list_item_id"]]: summary_row = SummaryRow( block["question"], @@ -766,18 +765,9 @@ def map_list_collector_config( use_answer_label=True, item_name=item_name, ) - row_items_related.extend(summary_row.itemsList) - # Again, List Collector Content list summaries don't use the row_item with "title" and are structured differently - if editable: - row_item["title"] = title - rows.append({"itemsList": row_items_related}) - else: - rows.append({"title": title, "itemsList": row_items_related}) - # Logic for list summaries without dynamic answers (related to List Collector) to display - else: - row_item["title"] = title - row_items: list = [row_item] - rows.append({"itemsList": row_items}) + row_items.extend(summary_row.rowItems) + + rows.append({"rowItems": row_items}) return rows diff --git a/app/views/contexts/hub_context.py b/app/views/contexts/hub_context.py index ff0fa89c38..937f2cfb8e 100644 --- a/app/views/contexts/hub_context.py +++ b/app/views/contexts/hub_context.py @@ -89,10 +89,10 @@ def get_row_context_for_section( ) -> dict[str, str | list]: section_content = self.SECTION_CONTENT_STATES[section_status] context: dict = { - "itemsList": [ + "rowItems": [ { - "title": section_name, - "titleAttributes": {"data-qa": f"hub-row-{row_id}-title"}, + "rowTitle": section_name, + "rowTitleAttributes": {"data-qa": f"hub-row-{row_id}-title"}, "attributes": {"data-qa": f"hub-row-{row_id}-state"}, "valueList": [{"text": section_content["text"]}], "actions": [ @@ -113,7 +113,7 @@ def get_row_context_for_section( CompletionStatus.COMPLETED, CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED, ): - context["itemsList"][0]["iconType"] = "check" + context["rowItems"][0]["iconType"] = "check" return context diff --git a/templates/confirm-email.html b/templates/confirm-email.html index 6b87db58fb..49c1ad15a5 100644 --- a/templates/confirm-email.html +++ b/templates/confirm-email.html @@ -14,7 +14,7 @@ {% set error_title = _("There is a problem with your answer") %} {% include "partials/error-panel.html" %} {% endif %} -

      {{ content.question.title }}

      +

      {{ content.question.title }}

      {{ content.question.description }}

      diff --git a/templates/feedback.html b/templates/feedback.html index 9055c3aabb..8d2b4abfa7 100644 --- a/templates/feedback.html +++ b/templates/feedback.html @@ -31,7 +31,7 @@ {% set error_title = ngettext('There is a problem with your feedback', 'There are %(num)s problems with your feedback', form.mapped_errors | length) %} {% include "partials/error-panel.html" %} {% endif %} -

      {{ content.question.title }}

      +

      {{ content.question.title }}

      {%- for answer in question.answers -%} {% include "partials/answer.html" %} {%- endfor -%} diff --git a/templates/partials/answers/address.html b/templates/partials/answers/address.html index 5999a573f9..382880aff5 100644 --- a/templates/partials/answers/address.html +++ b/templates/partials/answers/address.html @@ -35,8 +35,8 @@ "manualLinkText": _("Manually enter address"), "isEditable": true, "mandatory": answer.mandatory, - "apiDomain": address_lookup_api_url, - "apiDomainBearerToken": content.address_lookup_api_auth_token, + "APIDomain": address_lookup_api_url, + "APIDomainBearerToken": content.address_lookup_api_auth_token, "instructions": _("Use up and down keys to navigate suggestions once you’ve typed more than two characters. Use the enter key to select a suggestion. Touch device users, explore by touch or with swipe gestures."), "ariaYouHaveSelected": _("You have selected"), "ariaMinChars": _("Enter 3 or more characters for suggestions."), @@ -54,8 +54,8 @@ "errorTitle": ngettext('There is a problem with your answer', 'There are %(num)s problems with your answer', 1), "errorMessageEnter": _("Enter an address"), "errorMessageSelect": _("Select or manually enter an address"), - "errorMessageApi": _("Sorry, there was a problem loading addresses"), - "errorMessageApiLinkText": _("Enter address manually"), + "errorMessageAPI": _("Sorry, there was a problem loading addresses"), + "errorMessageAPILinkText": _("Enter address manually"), "options": { "regionCode": answer.lookup_options.region_code | lower, "oneYearAgo": answer.lookup_options.one_year_ago if answer.lookup_options.one_year_ago is defined, diff --git a/templates/partials/answers/textarea.html b/templates/partials/answers/textarea.html index d2782dbdfa..a101f2f474 100644 --- a/templates/partials/answers/textarea.html +++ b/templates/partials/answers/textarea.html @@ -16,7 +16,7 @@ "label": label, "value": input._value() | e, "charCheckLimit": { - "limit": answer.max_length | default(input.maxLength, true), + "limit": answer.max_length | default(input.maxlength, true), "charCountSingular": _("You have {x} character remaining"), "charCountPlural": _("You have {x} characters remaining") }, diff --git a/templates/partials/feedback-call-to-action.html b/templates/partials/feedback-call-to-action.html index 701df56038..4186065279 100644 --- a/templates/partials/feedback-call-to-action.html +++ b/templates/partials/feedback-call-to-action.html @@ -6,7 +6,7 @@ "id": "feedback", "heading": _("What do you think about this service?"), "content": _("Your comments will help us make improvements"), - "linkUrl": url_for('post_submission.send_feedback'), + "url": url_for('post_submission.send_feedback'), "linkText": _("Give feedback") }) }} diff --git a/templates/partials/introduction/preview.html b/templates/partials/introduction/preview.html index ba4f6c833d..5b5050ba8d 100644 --- a/templates/partials/introduction/preview.html +++ b/templates/partials/introduction/preview.html @@ -15,7 +15,7 @@

      {{ intro.title }}

      {% endset %} {% set item = { "title": question.question, - "headingLevel": 3, + "titleTag": "h3", "content": content, "summaryAttributes": { "data-ga": "click", diff --git a/templates/partials/summary/list-summary.html b/templates/partials/summary/list-summary.html index f7bf6498f8..588f6cb923 100644 --- a/templates/partials/summary/list-summary.html +++ b/templates/partials/summary/list-summary.html @@ -18,7 +18,7 @@ ) %} {% endif %} {% set group_config = { - "title": list_title, + "groupTitle": list_title, "rows": rows, "placeholderText": empty_list_text, } %} diff --git a/templates/partials/summary/summary.html b/templates/partials/summary/summary.html index 98031dc831..c0fa459536 100644 --- a/templates/partials/summary/summary.html +++ b/templates/partials/summary/summary.html @@ -15,7 +15,7 @@ {% do summary_groups.append ( { - "title": group.title if group.title else None, + "groupTitle": group.title if group.title else None, "id": group.id if group.id else None, "rows": map_summary_item_config( group=group, @@ -40,7 +40,7 @@ {% do summary_sections.append ( { - "title": section.title if summary_type == "Summary", + "summaryTitle": section.title if summary_type == "Summary", "groups": summary_groups } ) diff --git a/tests/app/test_jinja_filters.py b/tests/app/test_jinja_filters.py index ab65e927f6..c2c2b86d29 100644 --- a/tests/app/test_jinja_filters.py +++ b/tests/app/test_jinja_filters.py @@ -532,32 +532,32 @@ def test_map_list_collector_config_no_actions(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "one", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "one", "data-qa": "list-item-1-label", }, - "title": "Mark Bloggs", + "rowTitle": "Mark Bloggs", } ] }, { - "itemsList": [ + "rowItems": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "two", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "two", "data-qa": "list-item-2-label", }, - "title": "Joe Bloggs", + "rowTitle": "Joe Bloggs", } ] }, @@ -598,7 +598,7 @@ def test_map_list_collector_config(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [ { @@ -611,16 +611,16 @@ def test_map_list_collector_config(): "iconVisuallyHiddenText": None, "iconType": None, "id": "primary", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "primary", "data-qa": "list-item-1-label", }, - "title": "Mark Bloggs (You)", + "rowTitle": "Mark Bloggs (You)", } ] }, { - "itemsList": [ + "rowItems": [ { "actions": [ { @@ -639,11 +639,11 @@ def test_map_list_collector_config(): "iconType": None, "iconVisuallyHiddenText": None, "id": "nonprimary", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "nonprimary", "data-qa": "list-item-2-label", }, - "title": "Joe Bloggs", + "rowTitle": "Joe Bloggs", } ] }, @@ -716,7 +716,7 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [ { @@ -735,8 +735,8 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): "iconVisuallyHiddenText": None, "iconType": None, "id": "VHoiow", - "title": "Name of UK company or branch", - "titleAttributes": { + "rowTitle": "Name of UK company or branch", + "rowTitleAttributes": { "data-list-item-id": "VHoiow", "data-qa": "list-item-1-label", }, @@ -760,8 +760,8 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "title": "Registration number", - "titleAttributes": {"data-qa": "registration-number-label"}, + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, { @@ -784,8 +784,9 @@ def test_map_list_collector_config_with_related_answers_and_answer_title(): ], "attributes": {"data-qa": "authorised-insurer-radio"}, "id": "authorised-insurer-radio", - "title": "Is this UK company or branch an authorised " "insurer?", - "titleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "rowTitle": "Is this UK company or branch an authorised " + "insurer?", + "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, "valueList": [{"text": "Yes"}], }, ] @@ -1008,7 +1009,7 @@ def test_calculated_summary_config(): def test_summary_item_config_with_list_collector(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [ { @@ -1027,8 +1028,8 @@ def test_summary_item_config_with_list_collector(): "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", - "title": "Company A", - "titleAttributes": { + "rowTitle": "Company A", + "rowTitleAttributes": { "data-list-item-id": "vmmPmD", "data-qa": "list-item-1-label", }, @@ -1051,8 +1052,8 @@ def test_summary_item_config_with_list_collector(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "title": "Registration number", - "titleAttributes": {"data-qa": "registration-number-label"}, + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, { @@ -1075,8 +1076,9 @@ def test_summary_item_config_with_list_collector(): ], "attributes": {"data-qa": "authorised-insurer-radio"}, "id": "authorised-insurer-radio", - "title": "Is this UK company or branch an authorised " "insurer?", - "titleAttributes": {"data-qa": "authorised-insurer-radio-label"}, + "rowTitle": "Is this UK company or branch an authorised " + "insurer?", + "rowTitleAttributes": {"data-qa": "authorised-insurer-radio-label"}, "valueList": [{"text": "Yes"}], }, ] @@ -1166,7 +1168,7 @@ def test_summary_item_config_with_list_collector(): def test_summary_item_config_with_list_collector_and_one_related_answer(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [ { @@ -1185,8 +1187,8 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): "iconVisuallyHiddenText": None, "iconType": None, "id": "vmmPmD", - "title": "Company A", - "titleAttributes": { + "rowTitle": "Company A", + "rowTitleAttributes": { "data-list-item-id": "vmmPmD", "data-qa": "list-item-1-label", }, @@ -1210,8 +1212,8 @@ def test_summary_item_config_with_list_collector_and_one_related_answer(): ], "attributes": {"data-qa": "registration-number"}, "id": "registration-number", - "title": "Registration number", - "titleAttributes": {"data-qa": "registration-number-label"}, + "rowTitle": "Registration number", + "rowTitleAttributes": {"data-qa": "registration-number-label"}, "valueList": [{"text": "123"}], }, ] @@ -1304,32 +1306,32 @@ def test_map_list_collector_config_render_icon_set(): expected = [ { - "itemsList": [ + "rowItems": [ { "actions": [], "iconVisuallyHiddenText": "Completed", "iconType": "check", "id": "one", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "one", "data-qa": "list-item-1-label", }, - "title": "Mark Bloggs", + "rowTitle": "Mark Bloggs", } ] }, { - "itemsList": [ + "rowItems": [ { "actions": [], "iconVisuallyHiddenText": None, "iconType": None, "id": "two", - "titleAttributes": { + "rowTitleAttributes": { "data-list-item-id": "two", "data-qa": "list-item-2-label", }, - "title": "Joe Bloggs", + "rowTitle": "Joe Bloggs", } ] }, diff --git a/tests/app/views/contexts/test_hub_context.py b/tests/app/views/contexts/test_hub_context.py index 2ccff45cf9..c92db34d87 100644 --- a/tests/app/views/contexts/test_hub_context.py +++ b/tests/app/views/contexts/test_hub_context.py @@ -14,10 +14,10 @@ def router(schema, data_stores): def test_get_not_started_row_for_section(schema, data_stores): expected = { - "itemsList": [ + "rowItems": [ { - "title": "Breakfast", - "titleAttributes": {"data-qa": "hub-row-section-1-title"}, + "rowTitle": "Breakfast", + "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "valueList": [{"text": "Not started"}], "actions": [ @@ -46,10 +46,10 @@ def test_get_not_started_row_for_section(schema, data_stores): def test_get_in_progress_row_for_section(schema, data_stores): expected = { - "itemsList": [ + "rowItems": [ { - "title": "Breakfast", - "titleAttributes": {"data-qa": "hub-row-section-1-title"}, + "rowTitle": "Breakfast", + "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "valueList": [{"text": "Partially completed"}], "actions": [ @@ -78,10 +78,10 @@ def test_get_in_progress_row_for_section(schema, data_stores): def test_get_completed_row_for_section(schema, data_stores): expected = { - "itemsList": [ + "rowItems": [ { - "title": "Breakfast", - "titleAttributes": {"data-qa": "hub-row-section-1-title"}, + "rowTitle": "Breakfast", + "rowTitleAttributes": {"data-qa": "hub-row-section-1-title"}, "attributes": {"data-qa": "hub-row-section-1-state"}, "iconType": "check", "valueList": [{"text": "Completed"}], diff --git a/tests/functional/base_pages/question.page.js b/tests/functional/base_pages/question.page.js index 9ed4976e60..fcd926369f 100644 --- a/tests/functional/base_pages/question.page.js +++ b/tests/functional/base_pages/question.page.js @@ -31,7 +31,7 @@ class QuestionBasePage extends BasePage { } errorNumber(number = 1) { - return `[data-qa="error-link-${number}"] > a`; + return `[data-qa="error-link-${number}"]`; } cancelAndReturn() { diff --git a/tests/functional/base_pages/view_submitted_response_base.page.js b/tests/functional/base_pages/view_submitted_response_base.page.js index b91e10651e..785fc4589c 100644 --- a/tests/functional/base_pages/view_submitted_response_base.page.js +++ b/tests/functional/base_pages/view_submitted_response_base.page.js @@ -6,11 +6,11 @@ class ViewSubmittedResponseBasePage extends BasePage { } metadataTerm(number = 1) { - return `.ons-description-list > .ons-description-list__item:nth-of-type(${number}) > dt`; + return `.ons-description-list > dt:nth-of-type(${number})`; } metadataValue(number = 1) { - return `.ons-description-list > .ons-description-list__item:nth-of-type(${number}) > dd`; + return `.ons-description-list > dd:nth-of-type(${number})`; } informationPanel() { diff --git a/tests/functional/generate_pages.py b/tests/functional/generate_pages.py index 86fcc66112..156b678297 100755 --- a/tests/functional/generate_pages.py +++ b/tests/functional/generate_pages.py @@ -158,7 +158,7 @@ """ -ANSWER_SINGLE_ERROR_LINK_GETTER = r""" singleErrorLink() { return `p[data-qa="error-link-1"]`; } +ANSWER_SINGLE_ERROR_LINK_GETTER = r""" singleErrorLink() { return `p[data-qa="error-list"]`; } """ diff --git a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js index c591cc89ff..ab018ca044 100644 --- a/tests/functional/spec/list_collector/list_collector_section_summary.spec.js +++ b/tests/functional/spec/list_collector/list_collector_section_summary.spec.js @@ -30,17 +30,15 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); await expect(await $(SectionSummaryPage.companiesListLabel(1)).getText()).toContain("Name of UK company or branch"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); const listItemId = (await listItemIds())[0]; - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[0].getHTML()).toContain( + await expect(await $(companiesListRowItemAnchor(1)).getHTML()).toContain( `return_to=section-summary&return_to_answer_id=${listItemId}#company-or-branch-name`, ); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[1].getHTML()).toContain( - `return_to_answer_id=registration-number-${listItemId}#registration-number`, - ); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[2].getHTML()).toContain( + await expect(await $(companiesListRowItemAnchor(2)).getHTML()).toContain(`return_to_answer_id=registration-number-${listItemId}#registration-number`); + await expect(await $(companiesListRowItemAnchor(3)).getHTML()).toContain( `return_to_answer_id=authorised-insurer-radio-${listItemId}#authorised-insurer-radio`, ); }); @@ -53,15 +51,15 @@ describe("List Collector Section Summary and Summary Items", () => { await addCompany("Company C", "789", true); await anyMoreCompaniesNo(); await answerUkBasedQuestion(); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[2].getText()).toContain("No"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("789"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("No"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("789"); + await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Yes"); }); it("When I remove an item, Then the list of answers should no longer be visible on the section summary.", async () => { await drivingQuestionYes(); @@ -114,7 +112,7 @@ describe("List Collector Section Summary and Summary Items", () => { await drivingQuestionYes(); await addCompany("Company A", "123", true); await anyMoreCompaniesNo(); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); await $(SectionSummaryPage.companiesListEditLink(1)).click(); await verifyUrlContains("edit-company/?return_to=section-summary"); await expect(await $(AnyCompaniesOrBranchesAddPage.companyOrBranchName()).getValue()).toBe("Company A"); @@ -261,15 +259,15 @@ describe("List Collector Section Summary and Summary Items", () => { await click(SectionSummaryTwoPage.submit()); await verifyUrlContains(SubmitPage.url()); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("234"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__actions")[0].getText()).toContain("Change"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(1, 3)).getText()).toContain("Change"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(2, 3)).getText()).toContain("Change"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); + await expect(await $(companiesListRowItem(3, 3)).getText()).toContain("Change"); await expect(await $(SubmitPage.householderCheckboxAnswer()).getText()).toContain("No"); await expect(await $("body").getHTML()).toContain("Add another UK company or branch"); await expect(await $("body").getHTML()).toContain("Remove"); @@ -297,15 +295,12 @@ describe("List Collector Section Summary and Summary Items", () => { await $(ThankYouPage.savePrintAnswersLink()).click(); await verifyUrlContains(ViewSubmittedResponsePage.pageName); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[0].getText()).toContain("Company A"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[1].getText()).toContain("123"); - await expect(await $(companiesListRowItem(1)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[0].getText()).toContain("Company B"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[1].getText()).toContain("456"); - await expect(await $(companiesListRowItem(2)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[0].getText()).toContain("Company C"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[1].getText()).toContain("234"); - await expect(await $(companiesListRowItem(3)).$$(".ons-summary__values")[2].getText()).toContain("Yes"); + await expect(await $(companiesListRowItem(1, 1)).getText()).toContain("Company A"); + await expect(await $(companiesListRowItem(1, 2)).getText()).toContain("123"); + await expect(await $(companiesListRowItem(2, 1)).getText()).toContain("Company B"); + await expect(await $(companiesListRowItem(2, 2)).getText()).toContain("456"); + await expect(await $(companiesListRowItem(3, 1)).getText()).toContain("Company C"); + await expect(await $(companiesListRowItem(3, 2)).getText()).toContain("234"); await expect(await $("body").getHTML()).not.toContain("Change"); await expect(await $("body").getHTML()).not.toContain("Remove"); await expect(await $("body").getHTML()).not.toContain("Add another UK company or branch"); @@ -355,6 +350,10 @@ const answerUkBasedQuestion = async () => { await click(UkBasedPage.submit()); }; -const companiesListRowItem = (row) => { - return `#group-companies-1 .ons-summary__items .ons-summary__item:nth-of-type(${row})`; +const companiesListRowItem = (row, index) => { + return `#group-companies-1 .ons-summary__items .ons-summary__item:nth-of-type(${row}) .ons-summary__row:nth-of-type(${index})`; +}; + +const companiesListRowItemAnchor = (index) => { + return `#group-companies-1 .ons-summary__items .ons-summary__item .ons-summary__row:nth-of-type(${index}) a`; }; diff --git a/tests/functional/spec/supplementary_data/supplementary_data.spec.js b/tests/functional/spec/supplementary_data/supplementary_data.spec.js index e6a25d47a3..171f3ae1a8 100644 --- a/tests/functional/spec/supplementary_data/supplementary_data.spec.js +++ b/tests/functional/spec/supplementary_data/supplementary_data.spec.js @@ -314,10 +314,12 @@ describe("Using supplementary data", () => { }); it("Given I have a section with repeating answers for a supplementary list, When I reach the section summary page, Then I see the supplementary data and my answers rendered correctly", async () => { - await expect(await $("#dynamic-answer-question .ons-summary__row-title").getText()).toBe("Sales during the previous quarter"); + await expect(await $$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); await assertSummaryItems([ + "Articles and equipment for sports or outdoor games", "Volume of sales for Articles and equipment for sports or outdoor games", "Total volume produced for Articles and equipment for sports or outdoor games", + "Kitchen Equipment", "Volume of sales for Kitchen Equipment", "Total volume produced for Kitchen Equipment", "Value of sales for Articles and equipment for sports or outdoor games", @@ -485,23 +487,29 @@ describe("Using supplementary data", () => { // Product details await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[0].getText()).toBe( - "Volume of sales for Articles and equipment for sports or outdoor games", + "Articles and equipment for sports or outdoor games", ); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[1].getText()).toBe( + "Volume of sales for Articles and equipment for sports or outdoor games", + ); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).toBe( "Total volume produced for Articles and equipment for sports or outdoor games", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[2].getText()).toBe("Volume of sales for Kitchen Equipment"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[0].getText()).toBe("100 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[1].getText()).toBe("200 kg"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).toBe( + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[3].getText()).toBe("Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).toBe("Volume of sales for Kitchen Equipment"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).toBe( "Total volume produced for Kitchen Equipment", ); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[4].getText()).toBe("Volume of sales for Groceries"); - await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[5].getText()).toBe("Total volume produced for Groceries"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[2].getText()).toBe("50 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[3].getText()).toBe("300 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[6].getText()).toBe("Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[7].getText()).toBe("Volume of sales for Groceries"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryItems)[8].getText()).toBe("Total volume produced for Groceries"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[4].getText()).toBe("40 kg"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(0)).$$(summaryValues)[5].getText()).toBe("50 kg"); + await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryRowTitles)[0].getText()).toBe("Sales during the previous quarter"); await expect(await $(ViewSubmittedResponsePage.productReportingContent(1)).$$(summaryItems)[0].getText()).toBe( "Value of sales for Articles and equipment for sports or outdoor games", ); From 66bed682baa5b93e9cc6e3e09933464f72ab06e1 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 12 Mar 2025 08:46:45 +0000 Subject: [PATCH 516/567] Schemas v5.28.0 --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0cc951a571..79c74de75f 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.27.0 +v5.28.0 From d6185a2f2a6b290d0342ea93006b38875211eaa5 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:27:00 +0000 Subject: [PATCH 517/567] Fix wtforms issues after version 3.2.1 update (#1626) --- app/forms/validators.py | 6 +- poetry.lock | 1351 +++++++++++++++++++-------------------- pyproject.toml | 2 +- tests/app/conftest.py | 4 - 4 files changed, 665 insertions(+), 698 deletions(-) diff --git a/app/forms/validators.py b/app/forms/validators.py index 2d76c8f4a9..80e3e86cac 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -73,7 +73,7 @@ class ResponseRequired: an option for DataRequired or InputRequired validators in wtforms. """ - field_flags = ("required",) + field_flags = {"required": True} def __init__(self, message: str, strip_whitespace: bool = True): self.message = message @@ -213,7 +213,7 @@ class OptionalForm: Will not stop the validation chain if any one of the fields is populated. """ - field_flags = ("optional",) + field_flags = {"optional": True} def __call__(self, form: Sequence["QuestionnaireForm"], field: Field) -> None: empty_form = True @@ -239,7 +239,7 @@ def __call__(self, form: Sequence["QuestionnaireForm"], field: Field) -> None: class DateRequired: - field_flags = ("required",) + field_flags = {"required": True} def __init__(self, message: str | None = None): self.message = message or error_messages["MANDATORY_DATE"] diff --git a/poetry.lock b/poetry.lock index 8a96fa2666..cb064a1553 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,32 +1,32 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astroid" -version = "3.3.8" +version = "3.3.9" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" files = [ - {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, - {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, + {file = "astroid-3.3.9-py3-none-any.whl", hash = "sha256:d05bfd0acba96a7bd43e222828b7d9bc1e138aaeb0649707908d3702a9831248"}, + {file = "astroid-3.3.9.tar.gz", hash = "sha256:622cc8e3048684aa42c820d9d218978021c3c3d174fb03a9f0d615921744f550"}, ] [[package]] name = "attrs" -version = "24.3.0" +version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, + {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, + {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] @@ -46,17 +46,18 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.3" description = "Screen-scraping library" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.7.0" files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, + {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, ] [package.dependencies] soupsieve = ">1.2" +typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] @@ -122,32 +123,32 @@ files = [ [[package]] name = "boto3" -version = "1.35.98" +version = "1.37.15" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.98-py3-none-any.whl", hash = "sha256:d0224e1499d7189b47aa7f469d96522d98df6f5702fccb20a95a436582ebcd9d"}, - {file = "boto3-1.35.98.tar.gz", hash = "sha256:4b6274b4fe9d7113f978abea66a1f20c8a397c268c9d1b2a6c96b14a256da4a5"}, + {file = "boto3-1.37.15-py3-none-any.whl", hash = "sha256:78cc1b483cc637e1df8e81498d66f89550d4ee92175ccab5be1a2226672fe6b9"}, + {file = "boto3-1.37.15.tar.gz", hash = "sha256:586332456fff19328d57a88214a2ac2eda1bafab743556a836eda46a4ce613c6"}, ] [package.dependencies] -botocore = ">=1.35.98,<1.36.0" +botocore = ">=1.37.15,<1.38.0" jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.10.0,<0.11.0" +s3transfer = ">=0.11.0,<0.12.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.98" +version = "1.37.15" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.98-py3-none-any.whl", hash = "sha256:4f1c0b687488663a774ad3a5e81a5f94fae1bcada2364cfdc48482c4dbf794d5"}, - {file = "botocore-1.35.98.tar.gz", hash = "sha256:d11742b3824bdeac3c89eeeaf5132351af41823bbcef8fc15e95c8250b1de09c"}, + {file = "botocore-1.37.15-py3-none-any.whl", hash = "sha256:996b8d6a342ad7735eb07d8b4a81dad86e60ce0889ccb3edec0cd66eece85393"}, + {file = "botocore-1.37.15.tar.gz", hash = "sha256:72e6f1db6ebc4112d6ba719c97ad71ac7cf4a2f3729ae74fa225641e3ddcba92"}, ] [package.dependencies] @@ -156,7 +157,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.22.0)"] +crt = ["awscrt (==0.23.8)"] [[package]] name = "brotli" @@ -175,10 +176,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -191,14 +188,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -209,24 +200,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -236,10 +211,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -251,10 +222,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -267,10 +234,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -283,10 +246,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -333,24 +292,24 @@ cffi = ">=1.0.0" [[package]] name = "cachetools" -version = "5.5.0" +version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, - {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, + {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, + {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, ] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -577,73 +536,74 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.6.10" +version = "7.7.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, - {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, - {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, - {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, - {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, - {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, - {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, - {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, - {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, - {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, - {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, - {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, - {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, - {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, - {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, - {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, + {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"}, + {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"}, + {file = "coverage-7.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6c96d69928a3a6767fab8dc1ce8a02cf0156836ccb1e820c7f45a423570d98"}, + {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f18d47641282664276977c604b5a261e51fefc2980f5271d547d706b06a837f"}, + {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a1e18a85bd066c7c556d85277a7adf4651f259b2579113844835ba1a74aafd"}, + {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70f0925c4e2bfc965369f417e7cc72538fd1ba91639cf1e4ef4b1a6b50439b3b"}, + {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b0fac2088ec4aaeb5468b814bd3ff5e5978364bfbce5e567c44c9e2854469f6c"}, + {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3e212a894d8ae07fde2ca8b43d666a6d49bbbddb10da0f6a74ca7bd31f20054"}, + {file = "coverage-7.7.0-cp310-cp310-win32.whl", hash = "sha256:f32b165bf6dfea0846a9c9c38b7e1d68f313956d60a15cde5d1709fddcaf3bee"}, + {file = "coverage-7.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:a2454b12a3f12cc4698f3508912e6225ec63682e2ca5a96f80a2b93cef9e63f3"}, + {file = "coverage-7.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a0a207c87a9f743c8072d059b4711f8d13c456eb42dac778a7d2e5d4f3c253a7"}, + {file = "coverage-7.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d673e3add00048215c2cc507f1228a7523fd8bf34f279ac98334c9b07bd2656"}, + {file = "coverage-7.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f81fe93dc1b8e5673f33443c0786c14b77e36f1025973b85e07c70353e46882b"}, + {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8c7524779003d59948c51b4fcbf1ca4e27c26a7d75984f63488f3625c328b9b"}, + {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c124025430249118d018dcedc8b7426f39373527c845093132196f2a483b6dd"}, + {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7f559c36d5cdc448ee13e7e56ed7b6b5d44a40a511d584d388a0f5d940977ba"}, + {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:37cbc7b0d93dfd133e33c7ec01123fbb90401dce174c3b6661d8d36fb1e30608"}, + {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d2a65876274acf544703e943c010b60bd79404e3623a1e5d52b64a6e2728de5"}, + {file = "coverage-7.7.0-cp311-cp311-win32.whl", hash = "sha256:f5a2f71d6a91238e7628f23538c26aa464d390cbdedf12ee2a7a0fb92a24482a"}, + {file = "coverage-7.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae8006772c6b0fa53c33747913473e064985dac4d65f77fd2fdc6474e7cd54e4"}, + {file = "coverage-7.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:056d3017ed67e7ddf266e6f57378ece543755a4c9231e997789ab3bd11392c94"}, + {file = "coverage-7.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33c1394d8407e2771547583b66a85d07ed441ff8fae5a4adb4237ad39ece60db"}, + {file = "coverage-7.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbb7a0c3c21908520149d7751cf5b74eb9b38b54d62997b1e9b3ac19a8ee2fe"}, + {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb356e7ae7c2da13f404bf8f75be90f743c6df8d4607022e759f5d7d89fe83f8"}, + {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce730d484038e97f27ea2dbe5d392ec5c2261f28c319a3bb266f6b213650135"}, + {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa4dff57fc21a575672176d5ab0ef15a927199e775c5e8a3d75162ab2b0c7705"}, + {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b667b91f4f714b17af2a18e220015c941d1cf8b07c17f2160033dbe1e64149f0"}, + {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:693d921621a0c8043bfdc61f7d4df5ea6d22165fe8b807cac21eb80dd94e4bbd"}, + {file = "coverage-7.7.0-cp312-cp312-win32.whl", hash = "sha256:52fc89602cde411a4196c8c6894afb384f2125f34c031774f82a4f2608c59d7d"}, + {file = "coverage-7.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ce8cf59e09d31a4915ff4c3b94c6514af4c84b22c4cc8ad7c3c546a86150a92"}, + {file = "coverage-7.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4545485fef7a8a2d8f30e6f79ce719eb154aab7e44217eb444c1d38239af2072"}, + {file = "coverage-7.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1393e5aa9441dafb0162c36c8506c648b89aea9565b31f6bfa351e66c11bcd82"}, + {file = "coverage-7.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316f29cc3392fa3912493ee4c83afa4a0e2db04ff69600711f8c03997c39baaa"}, + {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ffde1d6bc2a92f9c9207d1ad808550873748ac2d4d923c815b866baa343b3f"}, + {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416e2a8845eaff288f97eaf76ab40367deafb9073ffc47bf2a583f26b05e5265"}, + {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5efdeff5f353ed3352c04e6b318ab05c6ce9249c25ed3c2090c6e9cadda1e3b2"}, + {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:57f3bd0d29bf2bd9325c0ff9cc532a175110c4bf8f412c05b2405fd35745266d"}, + {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ab7090f04b12dc6469882ce81244572779d3a4b67eea1c96fb9ecc8c607ef39"}, + {file = "coverage-7.7.0-cp313-cp313-win32.whl", hash = "sha256:180e3fc68ee4dc5af8b33b6ca4e3bb8aa1abe25eedcb958ba5cff7123071af68"}, + {file = "coverage-7.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:55143aa13c49491f5606f05b49ed88663446dce3a4d3c5d77baa4e36a16d3573"}, + {file = "coverage-7.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc41374d2f27d81d6558f8a24e5c114580ffefc197fd43eabd7058182f743322"}, + {file = "coverage-7.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:89078312f06237417adda7c021c33f80f7a6d2db8572a5f6c330d89b080061ce"}, + {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b2f144444879363ea8834cd7b6869d79ac796cb8f864b0cfdde50296cd95816"}, + {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e6347d1ed882b1159ffea172cb8466ee46c665af4ca397edbf10ff53e9ffaf"}, + {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb203c0afffaf1a8f5b9659a013f8f16a1b2cad3a80a8733ceedc968c0cf4c57"}, + {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ad0edaa97cb983d9f2ff48cadddc3e1fb09f24aa558abeb4dc9a0dbacd12cbb4"}, + {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c5f8a5364fc37b2f172c26a038bc7ec4885f429de4a05fc10fdcb53fb5834c5c"}, + {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4e09534037933bf6eb31d804e72c52ec23219b32c1730f9152feabbd7499463"}, + {file = "coverage-7.7.0-cp313-cp313t-win32.whl", hash = "sha256:1b336d06af14f8da5b1f391e8dec03634daf54dfcb4d1c4fb6d04c09d83cef90"}, + {file = "coverage-7.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b54a1ee4c6f1905a436cbaa04b26626d27925a41cbc3a337e2d3ff7038187f07"}, + {file = "coverage-7.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c8fbce80b2b8bf135d105aa8f5b36eae0c57d702a1cc3ebdea2a6f03f6cdde5"}, + {file = "coverage-7.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9710521f07f526de30ccdead67e6b236fe996d214e1a7fba8b36e2ba2cd8261"}, + {file = "coverage-7.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7789e700f33f2b133adae582c9f437523cd5db8de845774988a58c360fc88253"}, + {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c36093aca722db73633cf2359026ed7782a239eb1c6db2abcff876012dc4cf"}, + {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c075d167a6ec99b798c1fdf6e391a1d5a2d054caffe9593ba0f97e3df2c04f0e"}, + {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d013c07061751ae81861cae6ec3a4fe04e84781b11fd4b6b4201590234b25c7b"}, + {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:104bf640f408f4e115b85110047c7f27377e1a8b7ba86f7db4fa47aa49dc9a8e"}, + {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:39abcacd1ed54e2c33c54bdc488b310e8ef6705833f7148b6eb9a547199d375d"}, + {file = "coverage-7.7.0-cp39-cp39-win32.whl", hash = "sha256:8e336b56301774ace6be0017ff85c3566c556d938359b61b840796a0202f805c"}, + {file = "coverage-7.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c938c6ae59be67ac19a7204e079efc94b38222cd7d0269f96e45e18cddeaa59"}, + {file = "coverage-7.7.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:3b0e6e54591ae0d7427def8a4d40fca99df6b899d10354bab73cd5609807261c"}, + {file = "coverage-7.7.0-py3-none-any.whl", hash = "sha256:708f0a1105ef2b11c79ed54ed31f17e6325ac936501fc373f24be3e6a578146a"}, + {file = "coverage-7.7.0.tar.gz", hash = "sha256:cd879d4646055a573775a1cec863d00c9ff8c55860f8b17f6d8eee9140c06166"}, ] [package.extras] @@ -753,38 +713,46 @@ dev = ["black (==22.3.0)", "hypothesis", "numpy", "pytest (>=5.30)", "pytest-ben [[package]] name = "cryptography" -version = "44.0.0" +version = "44.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" files = [ - {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, - {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, - {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"}, - {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, - {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, - {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"}, - {file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"}, + {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}, + {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}, + {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}, + {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}, + {file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}, + {file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}, + {file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}, + {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}, + {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}, + {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}, + {file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}, + {file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}, + {file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}, + {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}, + {file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}, ] [package.dependencies] @@ -797,17 +765,18 @@ nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] name = "cssbeautifier" -version = "1.15.1" +version = "1.15.4" description = "CSS unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"}, + {file = "cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98"}, + {file = "cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5"}, ] [package.dependencies] @@ -817,20 +786,20 @@ six = ">=1.13.0" [[package]] name = "deprecated" -version = "1.2.15" +version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ - {file = "Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320"}, - {file = "deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d"}, + {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, + {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, ] [package.dependencies] wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "jinja2 (>=3.0.3,<3.1.0)", "setuptools", "sphinx (<2)", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] [[package]] name = "dill" @@ -951,13 +920,13 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.26.2" +version = "2.27.0" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "fakeredis-2.26.2-py3-none-any.whl", hash = "sha256:86d4129df001efc25793cb334008160fccc98425d9f94de47884a92b63988c14"}, - {file = "fakeredis-2.26.2.tar.gz", hash = "sha256:3ee5003a314954032b96b1365290541346c9cc24aab071b52cc983bb99ecafbf"}, + {file = "fakeredis-2.27.0-py3-none-any.whl", hash = "sha256:f4b6e0fa4193acbf00d81dac71ff5cc34fe7d7c12f1560b036f98578a103d5c3"}, + {file = "fakeredis-2.27.0.tar.gz", hash = "sha256:7b7584ec104392592297f46864a82cb7339a23e254ee885bf5ae07cfc64fbce7"}, ] [package.dependencies] @@ -1151,26 +1120,26 @@ test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idn [[package]] name = "google-api-core" -version = "2.24.0" +version = "2.24.2" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9"}, - {file = "google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf"}, + {file = "google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9"}, + {file = "google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696"}, ] [package.dependencies] -google-auth = ">=2.14.1,<3.0.dev0" -googleapis-common-protos = ">=1.56.2,<2.0.dev0" +google-auth = ">=2.14.1,<3.0.0" +googleapis-common-protos = ">=1.56.2,<2.0.0" grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} proto-plus = [ - {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, ] -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" -requests = ">=2.18.0,<3.0.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" +requests = ">=2.18.0,<3.0.0" [package.extras] async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"] @@ -1180,13 +1149,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-auth" -version = "2.37.0" +version = "2.38.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0"}, - {file = "google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00"}, + {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"}, + {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"}, ] [package.dependencies] @@ -1204,13 +1173,13 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "google-cloud-core" -version = "2.4.1" +version = "2.4.3" description = "Google Cloud API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"}, - {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"}, + {file = "google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e"}, + {file = "google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53"}, ] [package.dependencies] @@ -1243,13 +1212,13 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.27.2" +version = "2.28.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_pubsub-2.27.2-py2.py3-none-any.whl", hash = "sha256:a919f84fdea683b0a02464e38dd32332edbcbc8e85da82070079a57791119fd6"}, - {file = "google_cloud_pubsub-2.27.2.tar.gz", hash = "sha256:d92c156c7ddd0e5125008f977898198d7b1ae766026056497271bec4909647fe"}, + {file = "google_cloud_pubsub-2.28.0-py2.py3-none-any.whl", hash = "sha256:76b41a322b43bc845fb06ffe238758726324d957d0161bae3ff4b14339da144b"}, + {file = "google_cloud_pubsub-2.28.0.tar.gz", hash = "sha256:904e894b4e15121521077ac85c9aa8f4e7b8517bc5fb409ddb2aac8df1a02b3c"}, ] [package.dependencies] @@ -1294,59 +1263,65 @@ tracing = ["opentelemetry-api (>=1.1.0)"] [[package]] name = "google-cloud-tasks" -version = "2.18.0" +version = "2.19.2" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google_cloud_tasks-2.18.0-py2.py3-none-any.whl", hash = "sha256:5d994ba60a136c942e322b76bc178e1089fbb9bc08ad58b5178e035cc26d2fe9"}, - {file = "google_cloud_tasks-2.18.0.tar.gz", hash = "sha256:11a26b5b48733405cc4ea066b4f81ac353e5fb8566419b9b4d35d4bd1ceec58c"}, + {file = "google_cloud_tasks-2.19.2-py3-none-any.whl", hash = "sha256:898bf75020ead4dfb836a43d2ad666389ceeec1a4beb3cb65cc25b9accc289bb"}, + {file = "google_cloud_tasks-2.19.2.tar.gz", hash = "sha256:276b47e85f4825923a778d543fc0735e4b24be45f73fa7d964ad1655402d07dc"}, ] [package.dependencies] -google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} -google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" +grpc-google-iam-v1 = ">=0.14.0,<1.0.0" proto-plus = [ - {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0dev", markers = "python_version < \"3.13\""}, + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, ] -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" [[package]] name = "google-crc32c" -version = "1.6.0" +version = "1.7.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false python-versions = ">=3.9" files = [ - {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa"}, - {file = "google_crc32c-1.6.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9"}, - {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7"}, - {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e"}, - {file = "google_crc32c-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc"}, - {file = "google_crc32c-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42"}, - {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4"}, - {file = "google_crc32c-1.6.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8"}, - {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d"}, - {file = "google_crc32c-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f"}, - {file = "google_crc32c-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3"}, - {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d"}, - {file = "google_crc32c-1.6.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b"}, - {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00"}, - {file = "google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3"}, - {file = "google_crc32c-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760"}, - {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205"}, - {file = "google_crc32c-1.6.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0"}, - {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2"}, - {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871"}, - {file = "google_crc32c-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57"}, - {file = "google_crc32c-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c"}, - {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc"}, - {file = "google_crc32c-1.6.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d"}, - {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24"}, - {file = "google_crc32c-1.6.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d"}, - {file = "google_crc32c-1.6.0.tar.gz", hash = "sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc"}, + {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:18f1dfc6baeb3b28b1537d54b3622363352f75fcb2d4b6ffcc37584fe431f122"}, + {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:732378dc4ca08953eac0d13d1c312d99a54d5b483c90b4a5a536132669ed1c24"}, + {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14fdac94aa60d5794652f8ea6c2fcc532032e31f9050698b7ecdc6d4c3a61784"}, + {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bc14187a7fe5c61024c0dd1b578d7f9391df55459bf373c07f66426e09353b6"}, + {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54af59a98a427d0f98b6b0446df52ad286948ab7745da80a1edeb32ad633b3ae"}, + {file = "google_crc32c-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:2515aa89e46c6fa99190ec29bf27f33457ff98e5ca5c6c05602f74e0fb005752"}, + {file = "google_crc32c-1.7.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:96e33b249776f5aa7017a494b78994cf3cc8461291d460b46e75f6bc6cc40dc8"}, + {file = "google_crc32c-1.7.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c2dc799827990dd06b777067e27f57c2a552ddde4c4cd2d883b1b615ee92f9cf"}, + {file = "google_crc32c-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c29f7718f48e32810a41b17126e0ca588a0ae6158b4da2926d8074241a155d"}, + {file = "google_crc32c-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f30548e65291a4658c9e56f6f516159663f2b4a2c991b9af5846f0084ea25d4"}, + {file = "google_crc32c-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:9754f9eaa5ff82166512908f02745d5e883650d7b04d1732b5df3335986ad359"}, + {file = "google_crc32c-1.7.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:11b3b2f16a534c76ce3c9503800c7c2578c13a56e8e409eac273330e25b5c521"}, + {file = "google_crc32c-1.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:fd3afea81a7c7b95f98c065edc5a0fdb58f1fea5960e166962b142ec037fe5e0"}, + {file = "google_crc32c-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d07ad3ff51f26cef5bbad66622004eca3639271230cfc2443845ec4650e1c57"}, + {file = "google_crc32c-1.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af6e80f83d882b247eef2c0366ec72b1ffb89433b9f35dc621d79190840f1ea6"}, + {file = "google_crc32c-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:10721764a9434546b7961194fbb1f80efbcaf45b8498ed379d64f8891d4c155b"}, + {file = "google_crc32c-1.7.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:76bb19d182b999f9c9d580b1d7ab6e9334ab23dd669bf91f501812103408c85b"}, + {file = "google_crc32c-1.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:6a40522958040051c755a173eb98c05ad4d64a6dd898888c3e5ccca2d1cbdcdc"}, + {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f714fe5cdf5007d7064c57cf7471a99e0cbafda24ddfa829117fc3baafa424f7"}, + {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f04e58dbe1bf0c9398e603a9be5aaa09e0ba7eb022a3293195d8749459a01069"}, + {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:364067b063664dd8d1fec75a3fe85edf05c46f688365269beccaf42ef5dfe889"}, + {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1b0d6044799f6ac51d1cc2decb997280a83c448b3bef517a54b57a3b71921c0"}, + {file = "google_crc32c-1.7.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:02bc3295d26cd7666521fd6d5b7b93923ae1eb4417ddd3bc57185a5881ad7b96"}, + {file = "google_crc32c-1.7.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:807503eedd7bf9bfded2776e0bcd0b017998f45b8b1e813cde3b9bf8f7593313"}, + {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ff62f4959fabc8e5e90af920cc2fb0a9ccf1e74fd6da8a56d8ef04a600a68f97"}, + {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2daec556d537b07af25fa3f91841f022dd6869a648ca4aea1add56f87b80647"}, + {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dc17abb62bd7c7a33da307f24ae05cd6e36c24e847a67e3e2165cba23a06dd4"}, + {file = "google_crc32c-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:a79f7e486756385c4309d7815ede53a72f6cbab12ddf140c5f5b335caf08edfb"}, + {file = "google_crc32c-1.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8f48dddd1451026a517d7eb1f8c4ee2491998bfa383abb5fdebf32b0aa333e"}, + {file = "google_crc32c-1.7.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60f89e06ce462a65dd4d14a97bd29d92730713316b8b89720f9b2bb1aef270f7"}, + {file = "google_crc32c-1.7.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1277c27428a6cc89a51f5afbc04b81fae0288fb631117383f0de4f2bf78ffad6"}, + {file = "google_crc32c-1.7.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:731921158ef113bf157b8e65f13303d627fb540f173a410260f2fb7570af644c"}, + {file = "google_crc32c-1.7.0.tar.gz", hash = "sha256:c8c15a04b290c7556f277acc55ad98503a8bc0893ea6860fd5b5d210f3f558ce"}, ] [package.extras] @@ -1372,21 +1347,21 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] name = "googleapis-common-protos" -version = "1.66.0" +version = "1.69.2" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, - {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, + {file = "googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212"}, + {file = "googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f"}, ] [package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" +grpcio = {version = ">=1.44.0,<2.0.0", optional = true, markers = "extra == \"grpc\""} +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" [package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] [[package]] name = "greenlet" @@ -1476,101 +1451,97 @@ test = ["objgraph", "psutil"] [[package]] name = "grpc-google-iam-v1" -version = "0.14.0" +version = "0.14.2" description = "IAM API client library" optional = false python-versions = ">=3.7" files = [ - {file = "grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4"}, - {file = "grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99"}, + {file = "grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351"}, + {file = "grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20"}, ] [package.dependencies] -googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} -grpcio = ">=1.44.0,<2.0.0dev" -protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" +googleapis-common-protos = {version = ">=1.56.0,<2.0.0", extras = ["grpc"]} +grpcio = ">=1.44.0,<2.0.0" +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" [[package]] name = "grpcio" -version = "1.69.0" +version = "1.71.0" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, - {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"}, - {file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"}, - {file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"}, - {file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"}, - {file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"}, - {file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"}, - {file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"}, - {file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"}, - {file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"}, - {file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"}, - {file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"}, - {file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"}, - {file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"}, - {file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"}, - {file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"}, - {file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"}, - {file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"}, - {file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"}, - {file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"}, - {file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"}, - {file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"}, - {file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"}, - {file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"}, - {file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.69.0)"] + {file = "grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd"}, + {file = "grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d"}, + {file = "grpcio-1.71.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:0ab8b2864396663a5b0b0d6d79495657ae85fa37dcb6498a2669d067c65c11ea"}, + {file = "grpcio-1.71.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c30f393f9d5ff00a71bb56de4aa75b8fe91b161aeb61d39528db6b768d7eac69"}, + {file = "grpcio-1.71.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f250ff44843d9a0615e350c77f890082102a0318d66a99540f54769c8766ab73"}, + {file = "grpcio-1.71.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6d8de076528f7c43a2f576bc311799f89d795aa6c9b637377cc2b1616473804"}, + {file = "grpcio-1.71.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9b91879d6da1605811ebc60d21ab6a7e4bae6c35f6b63a061d61eb818c8168f6"}, + {file = "grpcio-1.71.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f71574afdf944e6652203cd1badcda195b2a27d9c83e6d88dc1ce3cfb73b31a5"}, + {file = "grpcio-1.71.0-cp310-cp310-win32.whl", hash = "sha256:8997d6785e93308f277884ee6899ba63baafa0dfb4729748200fcc537858a509"}, + {file = "grpcio-1.71.0-cp310-cp310-win_amd64.whl", hash = "sha256:7d6ac9481d9d0d129224f6d5934d5832c4b1cddb96b59e7eba8416868909786a"}, + {file = "grpcio-1.71.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:d6aa986318c36508dc1d5001a3ff169a15b99b9f96ef5e98e13522c506b37eef"}, + {file = "grpcio-1.71.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:d2c170247315f2d7e5798a22358e982ad6eeb68fa20cf7a820bb74c11f0736e7"}, + {file = "grpcio-1.71.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:e6f83a583ed0a5b08c5bc7a3fe860bb3c2eac1f03f1f63e0bc2091325605d2b7"}, + {file = "grpcio-1.71.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be74ddeeb92cc87190e0e376dbc8fc7736dbb6d3d454f2fa1f5be1dee26b9d7"}, + {file = "grpcio-1.71.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd0dfbe4d5eb1fcfec9490ca13f82b089a309dc3678e2edabc144051270a66e"}, + {file = "grpcio-1.71.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a2242d6950dc892afdf9e951ed7ff89473aaf744b7d5727ad56bdaace363722b"}, + {file = "grpcio-1.71.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0fa05ee31a20456b13ae49ad2e5d585265f71dd19fbd9ef983c28f926d45d0a7"}, + {file = "grpcio-1.71.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3d081e859fb1ebe176de33fc3adb26c7d46b8812f906042705346b314bde32c3"}, + {file = "grpcio-1.71.0-cp311-cp311-win32.whl", hash = "sha256:d6de81c9c00c8a23047136b11794b3584cdc1460ed7cbc10eada50614baa1444"}, + {file = "grpcio-1.71.0-cp311-cp311-win_amd64.whl", hash = "sha256:24e867651fc67717b6f896d5f0cac0ec863a8b5fb7d6441c2ab428f52c651c6b"}, + {file = "grpcio-1.71.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:0ff35c8d807c1c7531d3002be03221ff9ae15712b53ab46e2a0b4bb271f38537"}, + {file = "grpcio-1.71.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:b78a99cd1ece4be92ab7c07765a0b038194ded2e0a26fd654591ee136088d8d7"}, + {file = "grpcio-1.71.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc1a1231ed23caac1de9f943d031f1bc38d0f69d2a3b243ea0d664fc1fbd7fec"}, + {file = "grpcio-1.71.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6beeea5566092c5e3c4896c6d1d307fb46b1d4bdf3e70c8340b190a69198594"}, + {file = "grpcio-1.71.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5170929109450a2c031cfe87d6716f2fae39695ad5335d9106ae88cc32dc84c"}, + {file = "grpcio-1.71.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5b08d03ace7aca7b2fadd4baf291139b4a5f058805a8327bfe9aece7253b6d67"}, + {file = "grpcio-1.71.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f903017db76bf9cc2b2d8bdd37bf04b505bbccad6be8a81e1542206875d0e9db"}, + {file = "grpcio-1.71.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:469f42a0b410883185eab4689060a20488a1a0a00f8bbb3cbc1061197b4c5a79"}, + {file = "grpcio-1.71.0-cp312-cp312-win32.whl", hash = "sha256:ad9f30838550695b5eb302add33f21f7301b882937460dd24f24b3cc5a95067a"}, + {file = "grpcio-1.71.0-cp312-cp312-win_amd64.whl", hash = "sha256:652350609332de6dac4ece254e5d7e1ff834e203d6afb769601f286886f6f3a8"}, + {file = "grpcio-1.71.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:cebc1b34ba40a312ab480ccdb396ff3c529377a2fce72c45a741f7215bfe8379"}, + {file = "grpcio-1.71.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:85da336e3649a3d2171e82f696b5cad2c6231fdd5bad52616476235681bee5b3"}, + {file = "grpcio-1.71.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f9a412f55bb6e8f3bb000e020dbc1e709627dcb3a56f6431fa7076b4c1aab0db"}, + {file = "grpcio-1.71.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47be9584729534660416f6d2a3108aaeac1122f6b5bdbf9fd823e11fe6fbaa29"}, + {file = "grpcio-1.71.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9c80ac6091c916db81131d50926a93ab162a7e97e4428ffc186b6e80d6dda4"}, + {file = "grpcio-1.71.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:789d5e2a3a15419374b7b45cd680b1e83bbc1e52b9086e49308e2c0b5bbae6e3"}, + {file = "grpcio-1.71.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1be857615e26a86d7363e8a163fade914595c81fec962b3d514a4b1e8760467b"}, + {file = "grpcio-1.71.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a76d39b5fafd79ed604c4be0a869ec3581a172a707e2a8d7a4858cb05a5a7637"}, + {file = "grpcio-1.71.0-cp313-cp313-win32.whl", hash = "sha256:74258dce215cb1995083daa17b379a1a5a87d275387b7ffe137f1d5131e2cfbb"}, + {file = "grpcio-1.71.0-cp313-cp313-win_amd64.whl", hash = "sha256:22c3bc8d488c039a199f7a003a38cb7635db6656fa96437a8accde8322ce2366"}, + {file = "grpcio-1.71.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c6a0a28450c16809f94e0b5bfe52cabff63e7e4b97b44123ebf77f448534d07d"}, + {file = "grpcio-1.71.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:a371e6b6a5379d3692cc4ea1cb92754d2a47bdddeee755d3203d1f84ae08e03e"}, + {file = "grpcio-1.71.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:39983a9245d37394fd59de71e88c4b295eb510a3555e0a847d9965088cdbd033"}, + {file = "grpcio-1.71.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9182e0063112e55e74ee7584769ec5a0b4f18252c35787f48738627e23a62b97"}, + {file = "grpcio-1.71.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693bc706c031aeb848849b9d1c6b63ae6bcc64057984bb91a542332b75aa4c3d"}, + {file = "grpcio-1.71.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:20e8f653abd5ec606be69540f57289274c9ca503ed38388481e98fa396ed0b41"}, + {file = "grpcio-1.71.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8700a2a57771cc43ea295296330daaddc0d93c088f0a35cc969292b6db959bf3"}, + {file = "grpcio-1.71.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d35a95f05a8a2cbe8e02be137740138b3b2ea5f80bd004444e4f9a1ffc511e32"}, + {file = "grpcio-1.71.0-cp39-cp39-win32.whl", hash = "sha256:f9c30c464cb2ddfbc2ddf9400287701270fdc0f14be5f08a1e3939f1e749b455"}, + {file = "grpcio-1.71.0-cp39-cp39-win_amd64.whl", hash = "sha256:63e41b91032f298b3e973b3fa4093cbbc620c875e2da7b93e249d4728b54559a"}, + {file = "grpcio-1.71.0.tar.gz", hash = "sha256:2b85f7820475ad3edec209d3d89a7909ada16caab05d3f2e08a7e8ae3200a55c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.71.0)"] [[package]] name = "grpcio-status" -version = "1.69.0" +version = "1.71.0" description = "Status proto mapping for gRPC" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "grpcio_status-1.69.0-py3-none-any.whl", hash = "sha256:d6b2a3c9562c03a817c628d7ba9a925e209c228762d6d7677ae5c9401a542853"}, - {file = "grpcio_status-1.69.0.tar.gz", hash = "sha256:595ef84e5178d6281caa732ccf68ff83259241608d26b0e9c40a5e66eee2a2d2"}, + {file = "grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68"}, + {file = "grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.69.0" +grpcio = ">=1.71.0" protobuf = ">=5.26.1,<6.0dev" [[package]] @@ -1634,13 +1605,13 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve [[package]] name = "humanize" -version = "4.11.0" +version = "4.12.1" description = "Python humanize utilities" optional = false python-versions = ">=3.9" files = [ - {file = "humanize-4.11.0-py3-none-any.whl", hash = "sha256:b53caaec8532bcb2fff70c8826f904c35943f8cecaca29d272d9df38092736c0"}, - {file = "humanize-4.11.0.tar.gz", hash = "sha256:e66f36020a2d5a974c504bd2555cf770621dbdbb6d82f94a6857c0b1ea2608be"}, + {file = "humanize-4.12.1-py3-none-any.whl", hash = "sha256:86014ca5c52675dffa1d404491952f1f5bf03b07c175a51891a343daebf01fea"}, + {file = "humanize-4.12.1.tar.gz", hash = "sha256:1338ba97415c96556758a6e2f65977ed406dddf4620d4c6db9bbdfd07f0f1232"}, ] [package.extras] @@ -1662,13 +1633,13 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.5.0" +version = "8.6.1" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, + {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, + {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, ] [package.dependencies] @@ -1680,7 +1651,7 @@ cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1696,17 +1667,18 @@ files = [ [[package]] name = "isort" -version = "5.13.2" +version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, ] [package.extras] -colors = ["colorama (>=0.4.6)"] +colors = ["colorama"] +plugins = ["setuptools"] [[package]] name = "itsdangerous" @@ -1721,13 +1693,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, - {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -1749,12 +1721,13 @@ files = [ [[package]] name = "jsbeautifier" -version = "1.15.1" +version = "1.15.4" description = "JavaScript unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"}, + {file = "jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528"}, + {file = "jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592"}, ] [package.dependencies] @@ -1908,13 +1881,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.25.1" +version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" files = [ - {file = "marshmallow-3.25.1-py3-none-any.whl", hash = "sha256:ec5d00d873ce473b7f2ffcb7104286a376c354cab0c2fa12f5573dab03e87210"}, - {file = "marshmallow-3.25.1.tar.gz", hash = "sha256:f4debda3bb11153d81ac34b0d582bf23053055ee11e791b54b4b35493468040a"}, + {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, + {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, ] [package.dependencies] @@ -1938,13 +1911,13 @@ files = [ [[package]] name = "mock" -version = "5.1.0" +version = "5.2.0" description = "Rolling backport of unittest.mock for all Pythons" optional = false python-versions = ">=3.6" files = [ - {file = "mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744"}, - {file = "mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d"}, + {file = "mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f"}, + {file = "mock-5.2.0.tar.gz", hash = "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0"}, ] [package.extras] @@ -1954,13 +1927,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.0.27" +version = "5.1.1" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "moto-5.0.27-py3-none-any.whl", hash = "sha256:27042fd94c8def0166d9f2ae8d39d9488d4b3115542b5fca88566c0424549013"}, - {file = "moto-5.0.27.tar.gz", hash = "sha256:6c123de7e0e5e6508a10c399ba3ecf2d5143f263f8e804fd4a7091941c3f5207"}, + {file = "moto-5.1.1-py3-none-any.whl", hash = "sha256:615904d6210431950a59a2bdec365d60e791eacbe3dd07a3a5d742c88ef847dd"}, + {file = "moto-5.1.1.tar.gz", hash = "sha256:5b25dbc62cccd9f36ef062c870db49d976b241129024fab049e2d3d1296e2a57"}, ] [package.dependencies] @@ -1975,7 +1948,7 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "jsonschema", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] +all = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath_ng", "jsonschema", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] @@ -1985,63 +1958,57 @@ cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (> cognitoidp = ["joserfc (>=0.9.0)"] dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.6.1)"] dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.6.1)"] -events = ["jsonpath-ng"] +events = ["jsonpath_ng"] glue = ["pyparsing (>=3.0.7)"] -proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] +proxy = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsonpath_ng", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] quicksight = ["jsonschema"] resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)"] s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.6.1)"] s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.6.1)"] -server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsonpath-ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] +server = ["PyYAML (>=5.1)", "antlr4-python3-runtime", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsonpath_ng", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.6.1)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] -stepfunctions = ["antlr4-python3-runtime", "jsonpath-ng"] +stepfunctions = ["antlr4-python3-runtime", "jsonpath_ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.14.1" +version = "1.15.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, + {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, + {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, + {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, + {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, + {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, + {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, + {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, + {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, + {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, + {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, + {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, + {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, + {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, + {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, + {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, + {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, + {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, + {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, + {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, + {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, + {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, + {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, ] [package.dependencies] @@ -2068,49 +2035,49 @@ files = [ [[package]] name = "opentelemetry-api" -version = "1.29.0" +version = "1.31.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_api-1.29.0-py3-none-any.whl", hash = "sha256:5fcd94c4141cc49c736271f3e1efb777bebe9cc535759c54c936cca4f1b312b8"}, - {file = "opentelemetry_api-1.29.0.tar.gz", hash = "sha256:d04a6cf78aad09614f52964ecb38021e248f5714dc32c2e0d8fd99517b4d69cf"}, + {file = "opentelemetry_api-1.31.0-py3-none-any.whl", hash = "sha256:145b72c6c16977c005c568ec32f4946054ab793d8474a17fd884b0397582c5f2"}, + {file = "opentelemetry_api-1.31.0.tar.gz", hash = "sha256:d8da59e83e8e3993b4726e4c1023cd46f57c4d5a73142e239247e7d814309de1"}, ] [package.dependencies] deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=8.5.0" +importlib-metadata = ">=6.0,<8.7.0" [[package]] name = "opentelemetry-sdk" -version = "1.29.0" +version = "1.31.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_sdk-1.29.0-py3-none-any.whl", hash = "sha256:173be3b5d3f8f7d671f20ea37056710217959e774e2749d984355d1f9391a30a"}, - {file = "opentelemetry_sdk-1.29.0.tar.gz", hash = "sha256:b0787ce6aade6ab84315302e72bd7a7f2f014b0fb1b7c3295b88afe014ed0643"}, + {file = "opentelemetry_sdk-1.31.0-py3-none-any.whl", hash = "sha256:97c9a03865e69723725fb64fe04343a488c3e61e684eb804bd7d6da2215dfc60"}, + {file = "opentelemetry_sdk-1.31.0.tar.gz", hash = "sha256:452d7d5b3c1db2e5e4cb64abede0ddd20690cb244a559c73a59652fdf6726070"}, ] [package.dependencies] -opentelemetry-api = "1.29.0" -opentelemetry-semantic-conventions = "0.50b0" +opentelemetry-api = "1.31.0" +opentelemetry-semantic-conventions = "0.52b0" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.50b0" +version = "0.52b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" files = [ - {file = "opentelemetry_semantic_conventions-0.50b0-py3-none-any.whl", hash = "sha256:e87efba8fdb67fb38113efea6a349531e75ed7ffc01562f65b802fcecb5e115e"}, - {file = "opentelemetry_semantic_conventions-0.50b0.tar.gz", hash = "sha256:02dc6dbcb62f082de9b877ff19a3f1ffaa3c306300fa53bfac761c4567c83d38"}, + {file = "opentelemetry_semantic_conventions-0.52b0-py3-none-any.whl", hash = "sha256:4d843652ae1f9f3c0d4d8df0bfef740627c90495ac043fc33f0a04bad3b606e2"}, + {file = "opentelemetry_semantic_conventions-0.52b0.tar.gz", hash = "sha256:f8bc8873a69d0a2f45746c31980baad2bb10ccee16b1816497ccf99417770386"}, ] [package.dependencies] deprecated = ">=1.2.6" -opentelemetry-api = "1.29.0" +opentelemetry-api = "1.31.0" [[package]] name = "ordered-set" @@ -2205,23 +2172,23 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.49.1" +version = "1.51.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" files = [ - {file = "playwright-1.49.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1041ffb45a0d0bc44d698d3a5aa3ac4b67c9bd03540da43a0b70616ad52592b8"}, - {file = "playwright-1.49.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f38ed3d0c1f4e0a6d1c92e73dd9a61f8855133249d6f0cec28648d38a7137be"}, - {file = "playwright-1.49.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:3be48c6d26dc819ca0a26567c1ae36a980a0303dcd4249feb6f59e115aaddfb8"}, - {file = "playwright-1.49.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:753ca90ee31b4b03d165cfd36e477309ebf2b4381953f2a982ff612d85b147d2"}, - {file = "playwright-1.49.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd9bc8dab37aa25198a01f555f0a2e2c3813fe200fef018ac34dfe86b34994b9"}, - {file = "playwright-1.49.1-py3-none-win32.whl", hash = "sha256:43b304be67f096058e587dac453ece550eff87b8fbed28de30f4f022cc1745bb"}, - {file = "playwright-1.49.1-py3-none-win_amd64.whl", hash = "sha256:47b23cb346283278f5b4d1e1990bcb6d6302f80c0aa0ca93dd0601a1400191df"}, + {file = "playwright-1.51.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bcaaa3d5d73bda659bfb9ff2a288b51e85a91bd89eda86eaf8186550973e416a"}, + {file = "playwright-1.51.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e0ae6eb44297b24738e1a6d9c580ca4243b4e21b7e65cf936a71492c08dd0d4"}, + {file = "playwright-1.51.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:ab4c0ff00bded52c946be60734868febc964c8a08a9b448d7c20cb3811c6521c"}, + {file = "playwright-1.51.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:d5c9f67bc6ef49094618991c78a1466c5bac5ed09157660d78b8510b77f92746"}, + {file = "playwright-1.51.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814e4ec2a1a0d6f6221f075622c06b31ceb2bdc6d622258cfefed900c01569ae"}, + {file = "playwright-1.51.0-py3-none-win32.whl", hash = "sha256:4cef804991867ea27f608b70fa288ee52a57651e22d02ab287f98f8620b9408c"}, + {file = "playwright-1.51.0-py3-none-win_amd64.whl", hash = "sha256:9ece9316c5d383aed1a207f079fc2d552fff92184f0ecf37cc596e912d00a8c3"}, ] [package.dependencies] -greenlet = "3.1.1" -pyee = "12.0.0" +greenlet = ">=3.1.1,<4.0.0" +pyee = ">=12,<13" [[package]] name = "pluggy" @@ -2240,17 +2207,17 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "proto-plus" -version = "1.25.0" -description = "Beautiful, Pythonic protocol buffers." +version = "1.26.1" +description = "Beautiful, Pythonic protocol buffers" optional = false python-versions = ">=3.7" files = [ - {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"}, - {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"}, + {file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"}, + {file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"}, ] [package.dependencies] -protobuf = ">=3.19.0,<6.0.0dev" +protobuf = ">=3.19.0,<7.0.0" [package.extras] testing = ["google-api-core (>=1.31.5)"] @@ -2313,13 +2280,13 @@ files = [ [[package]] name = "pyee" -version = "12.0.0" +version = "12.1.1" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" files = [ - {file = "pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990"}, - {file = "pyee-12.0.0.tar.gz", hash = "sha256:c480603f4aa2927d4766eb41fa82793fe60a82cbfdb8d688e0d08c55a534e145"}, + {file = "pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef"}, + {file = "pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3"}, ] [package.dependencies] @@ -2330,20 +2297,20 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " [[package]] name = "pylint" -version = "3.3.3" +version = "3.3.5" description = "python code static checker" optional = false python-versions = ">=3.9.0" files = [ - {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, - {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, + {file = "pylint-3.3.5-py3-none-any.whl", hash = "sha256:7cb170929a371238530b2eeea09f5f28236d106b70308c3d46a9c0cf11634633"}, + {file = "pylint-3.3.5.tar.gz", hash = "sha256:38d0f784644ed493d91f76b5333a0e370a1c1bc97c22068a77523b4bf1e82c31"}, ] [package.dependencies] astroid = ">=3.3.8,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomlkit = ">=0.10.1" @@ -2397,13 +2364,13 @@ dev = ["build", "flake8", "mypy", "pytest", "twine"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] @@ -2538,13 +2505,13 @@ cramjam = "*" [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] @@ -2626,18 +2593,19 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)" [[package]] name = "referencing" -version = "0.35.1" +version = "0.36.2" description = "JSON Referencing + Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, - {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, + {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, + {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, ] [package.dependencies] attrs = ">=22.2.0" rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "regex" @@ -2765,13 +2733,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "responses" -version = "0.25.6" +version = "0.25.7" description = "A utility library for mocking out the `requests` Python library." optional = false python-versions = ">=3.8" files = [ - {file = "responses-0.25.6-py3-none-any.whl", hash = "sha256:9cac8f21e1193bb150ec557875377e41ed56248aed94e4567ed644db564bacf1"}, - {file = "responses-0.25.6.tar.gz", hash = "sha256:eae7ce61a9603004e76c05691e7c389e59652d91e94b419623c12bbfb8e331d8"}, + {file = "responses-0.25.7-py3-none-any.whl", hash = "sha256:92ca17416c90fe6b35921f52179bff29332076bb32694c0df02dcac2c6bc043c"}, + {file = "responses-0.25.7.tar.gz", hash = "sha256:8ebae11405d7a5df79ab6fd54277f6f2bc29b2d002d0dd2d5c632594d1ddcedb"}, ] [package.dependencies] @@ -2784,114 +2752,114 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.22.3" +version = "0.23.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, - {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, - {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, - {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, - {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, - {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, - {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, - {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, - {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, - {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, - {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, - {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, - {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, - {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, - {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, - {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, - {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, - {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, - {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, - {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, - {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, - {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, - {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, - {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, - {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, - {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, - {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, - {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, - {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, - {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, - {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, - {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, - {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, - {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, - {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, - {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, - {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, - {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, + {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, + {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d"}, + {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f"}, + {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a"}, + {file = "rpds_py-0.23.1-cp310-cp310-win32.whl", hash = "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12"}, + {file = "rpds_py-0.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, + {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, + {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, + {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, + {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, + {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, + {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, + {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, + {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, + {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, + {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, + {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, + {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, + {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, + {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, + {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, + {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, + {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, + {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, + {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19"}, + {file = "rpds_py-0.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a"}, + {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4"}, + {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f"}, + {file = "rpds_py-0.23.1-cp39-cp39-win32.whl", hash = "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495"}, + {file = "rpds_py-0.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4"}, + {file = "rpds_py-0.23.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3"}, + {file = "rpds_py-0.23.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00"}, + {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, ] [[package]] @@ -2910,47 +2878,47 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.9.2" +version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"}, - {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"}, - {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"}, - {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"}, - {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"}, - {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"}, - {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"}, + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, ] [[package]] name = "s3transfer" -version = "0.10.4" +version = "0.11.4" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e"}, - {file = "s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"}, + {file = "s3transfer-0.11.4-py3-none-any.whl", hash = "sha256:ac265fa68318763a03bf2dc4f39d5cbd6a9e178d81cc9483ad27da33637e320d"}, + {file = "s3transfer-0.11.4.tar.gz", hash = "sha256:559f161658e1cf0a911f45940552c696735f5c74e64362e515f333ebed87d679"}, ] [package.dependencies] -botocore = ">=1.33.2,<2.0a.0" +botocore = ">=1.37.4,<2.0a.0" [package.extras] -crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] +crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] [[package]] name = "sdc-cryptography" @@ -2970,13 +2938,13 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "75.8.0" +version = "76.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" files = [ - {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, - {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, + {file = "setuptools-76.1.0-py3-none-any.whl", hash = "sha256:34750dcb17d046929f545dec9b8349fe42bf4ba13ddffee78428aec422dbfb73"}, + {file = "setuptools-76.1.0.tar.gz", hash = "sha256:4959b9ad482ada2ba2320c8f1a8d8481d4d8d668908a7a1b84d987375cd7f5bd"}, ] [package.extras] @@ -2990,121 +2958,121 @@ type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14 [[package]] name = "simplejson" -version = "3.19.3" +version = "3.20.1" description = "Simple, fast, extensible JSON encoder/decoder for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5" files = [ - {file = "simplejson-3.19.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f39caec26007a2d0efab6b8b1d74873ede9351962707afab622cc2285dd26ed0"}, - {file = "simplejson-3.19.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:83c87706265ae3028e8460d08b05f30254c569772e859e5ba61fe8af2c883468"}, - {file = "simplejson-3.19.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0b5ddd2c7d1d3f4d23224bc8a04bbf1430ae9a8149c05b90f8fc610f7f857a23"}, - {file = "simplejson-3.19.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:ad0e0b1ce9bd3edb5cf64b5b5b76eacbfdac8c5367153aeeec8a8b1407f68342"}, - {file = "simplejson-3.19.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:93be280fc69a952c76e261036312c20b910e7fa9e234f1d89bdfe3fa34f8a023"}, - {file = "simplejson-3.19.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6d43e24b88c80f997081503f693be832fc90854f278df277dd54f8a4c847ab61"}, - {file = "simplejson-3.19.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2876027ebdd599d730d36464debe84619b0368e9a642ca6e7c601be55aed439e"}, - {file = "simplejson-3.19.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:0766ca6222b410e08e0053a0dda3606cafb3973d5d00538307f631bb59743396"}, - {file = "simplejson-3.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50d8b742d74c449c4dcac570d08ce0f21f6a149d2d9cf7652dbf2ba9a1bc729a"}, - {file = "simplejson-3.19.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd011fc3c1d88b779645495fdb8189fb318a26981eebcce14109460e062f209b"}, - {file = "simplejson-3.19.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:637c4d4b81825c1f4d651e56210bd35b5604034b192b02d2d8f17f7ce8c18f42"}, - {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f56eb03bc9e432bb81adc8ecff2486d39feb371abb442964ffb44f6db23b332"}, - {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef59a53be400c1fad2c914b8d74c9d42384fed5174f9321dd021b7017fd40270"}, - {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72e8abbc86fcac83629a030888b45fed3a404d54161118be52cb491cd6975d3e"}, - {file = "simplejson-3.19.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8efb03ca77bd7725dfacc9254df00d73e6f43013cf39bd37ef1a8ed0ebb5165"}, - {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:add8850db04b98507a8b62d248a326ecc8561e6d24336d1ca5c605bbfaab4cad"}, - {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fc3dc9fb413fc34c396f52f4c87de18d0bd5023804afa8ab5cc224deeb6a9900"}, - {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dfa420bb9225dd33b6efdabde7c6a671b51150b9b1d9c4e5cd74d3b420b3fe1"}, - {file = "simplejson-3.19.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7b5c472099b39b274dcde27f1113db8d818c9aa3ba8f78cbb8ad04a4c1ac2118"}, - {file = "simplejson-3.19.3-cp310-cp310-win32.whl", hash = "sha256:817abad79241ed4a507b3caf4d3f2be5079f39d35d4c550a061988986bffd2ec"}, - {file = "simplejson-3.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:dd5b9b1783e14803e362a558680d88939e830db2466f3fa22df5c9319f8eea94"}, - {file = "simplejson-3.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e88abff510dcff903a18d11c2a75f9964e768d99c8d147839913886144b2065e"}, - {file = "simplejson-3.19.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934a50a614fb831614db5dbfba35127ee277624dda4d15895c957d2f5d48610c"}, - {file = "simplejson-3.19.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:212fce86a22188b0c7f53533b0f693ea9605c1a0f02c84c475a30616f55a744d"}, - {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d9e8f836688a8fabe6a6b41b334aa550a6823f7b4ac3d3712fc0ad8655be9a8"}, - {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23228037dc5d41c36666384062904d74409a62f52283d9858fa12f4c22cffad1"}, - {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0791f64fed7d4abad639491f8a6b1ba56d3c604eb94b50f8697359b92d983f36"}, - {file = "simplejson-3.19.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f614581b61a26fbbba232a1391f6cee82bc26f2abbb6a0b44a9bba25c56a1c"}, - {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1df0aaf1cb787fdf34484ed4a1f0c545efd8811f6028623290fef1a53694e597"}, - {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:951095be8d4451a7182403354c22ec2de3e513e0cc40408b689af08d02611588"}, - {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a954b30810988feeabde843e3263bf187697e0eb5037396276db3612434049b"}, - {file = "simplejson-3.19.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c40df31a75de98db2cdfead6074d4449cd009e79f54c1ebe5e5f1f153c68ad20"}, - {file = "simplejson-3.19.3-cp311-cp311-win32.whl", hash = "sha256:7e2a098c21ad8924076a12b6c178965d88a0ad75d1de67e1afa0a66878f277a5"}, - {file = "simplejson-3.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:c9bedebdc5fdad48af8783022bae307746d54006b783007d1d3c38e10872a2c6"}, - {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:66a0399e21c2112acacfebf3d832ebe2884f823b1c7e6d1363f2944f1db31a99"}, - {file = "simplejson-3.19.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6ef9383c5e05f445be60f1735c1816163c874c0b1ede8bb4390aff2ced34f333"}, - {file = "simplejson-3.19.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42e5acf80d4d971238d4df97811286a044d720693092b20a56d5e56b7dcc5d09"}, - {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0b0efc7279d768db7c74d3d07f0b5c81280d16ae3fb14e9081dc903e8360771"}, - {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0552eb06e7234da892e1d02365cd2b7b2b1f8233aa5aabdb2981587b7cc92ea0"}, - {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf6a3b9a7d7191471b464fe38f684df10eb491ec9ea454003edb45a011ab187"}, - {file = "simplejson-3.19.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7017329ca8d4dca94ad5e59f496e5fc77630aecfc39df381ffc1d37fb6b25832"}, - {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67a20641afebf4cfbcff50061f07daad1eace6e7b31d7622b6fa2c40d43900ba"}, - {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd6a7dabcc4c32daf601bc45e01b79175dde4b52548becea4f9545b0a4428169"}, - {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:08f9b443a94e72dd02c87098c96886d35790e79e46b24e67accafbf13b73d43b"}, - {file = "simplejson-3.19.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa97278ae6614346b5ca41a45a911f37a3261b57dbe4a00602048652c862c28b"}, - {file = "simplejson-3.19.3-cp312-cp312-win32.whl", hash = "sha256:ef28c3b328d29b5e2756903aed888960bc5df39b4c2eab157ae212f70ed5bf74"}, - {file = "simplejson-3.19.3-cp312-cp312-win_amd64.whl", hash = "sha256:1e662336db50ad665777e6548b5076329a94a0c3d4a0472971c588b3ef27de3a"}, - {file = "simplejson-3.19.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0959e6cb62e3994b5a40e31047ff97ef5c4138875fae31659bead691bed55896"}, - {file = "simplejson-3.19.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a7bfad839c624e139a4863007233a3f194e7c51551081f9789cba52e4da5167"}, - {file = "simplejson-3.19.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afab2f7f2486a866ff04d6d905e9386ca6a231379181a3838abce1f32fbdcc37"}, - {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00313681015ac498e1736b304446ee6d1c72c5b287cd196996dad84369998f7"}, - {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d936ae682d5b878af9d9eb4d8bb1fdd5e41275c8eb59ceddb0aeed857bb264a2"}, - {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c6657485393f2e9b8177c77a7634f13ebe70d5e6de150aae1677d91516ce6b"}, - {file = "simplejson-3.19.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a6a750d3c7461b1c47cfc6bba8d9e57a455e7c5f80057d2a82f738040dd1129"}, - {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ea7a4a998c87c5674a27089e022110a1a08a7753f21af3baf09efe9915c23c3c"}, - {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6300680d83a399be2b8f3b0ef7ef90b35d2a29fe6e9c21438097e0938bbc1564"}, - {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ab69f811a660c362651ae395eba8ce84f84c944cea0df5718ea0ba9d1e4e7252"}, - {file = "simplejson-3.19.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:256e09d0f94d9c3d177d9e95fd27a68c875a4baa2046633df387b86b652f5747"}, - {file = "simplejson-3.19.3-cp313-cp313-win32.whl", hash = "sha256:2c78293470313aefa9cfc5e3f75ca0635721fb016fb1121c1c5b0cb8cc74712a"}, - {file = "simplejson-3.19.3-cp313-cp313-win_amd64.whl", hash = "sha256:3bbcdc438dc1683b35f7a8dc100960c721f922f9ede8127f63bed7dfded4c64c"}, - {file = "simplejson-3.19.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:89b35433186e977fa86ff1fd179c1fadff39cfa3afa1648dab0b6ca53153acd9"}, - {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d43c2d7504eda566c50203cdc9dc043aff6f55f1b7dae0dcd79dfefef9159d1c"}, - {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6890ff9cf0bd2e1d487e2a8869ebd620a44684c0a9667fa5ee751d099d5d84c8"}, - {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1069143a8fb3905e1bc0696c62be7e3adf812e9f1976ac9ae15b05112ff57cc9"}, - {file = "simplejson-3.19.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb324bb903330cbb35d87cce367a12631cd5720afa06e5b9c906483970946da6"}, - {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:0a32859d45d7b85fb803bb68f6bee14526991a1190269116c33399fa0daf9bbf"}, - {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:23833ee7e791ec968b744dfee2a2d39df7152050051096caf4296506d75608d8"}, - {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:d73efb03c5b39249c82488a994f0998f9e4399e3d085209d2120503305ba77a8"}, - {file = "simplejson-3.19.3-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7923878b7a0142d39763ec2dbecff3053c1bedd3653585a8474666e420fe83f5"}, - {file = "simplejson-3.19.3-cp36-cp36m-win32.whl", hash = "sha256:7355c7203353c36d46c4e7b6055293b3d2be097bbc5e2874a2b8a7259f0325dd"}, - {file = "simplejson-3.19.3-cp36-cp36m-win_amd64.whl", hash = "sha256:d1b8b4d6379fe55f471914345fe6171d81a18649dacf3248abfc9c349b4442eb"}, - {file = "simplejson-3.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d36608557b4dcd7a62c29ad4cd7c5a1720bbf7dc942eff9dc42d2c542a5f042d"}, - {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7137e69c6781ecf23afab064be94a277236c9cba31aa48ff1a0ec3995c69171e"}, - {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76f8c28fe2d426182405b18ddf3001fce47835a557dc15c3d8bdea01c03361da"}, - {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff7bc1bbdaa3e487c9469128bf39408e91f5573901cb852e03af378d3582c52d"}, - {file = "simplejson-3.19.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0782cb9bf827f0c488b6aa0f2819f618308a3caf2973cfd792e45d631bec4db"}, - {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:6fea0716c593dabb4392c4996d4e902a83b2428e6da82938cf28a523a11eb277"}, - {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:8f41bb5370b34f63171e65fdb00e12be1d83675cecb23e627df26f4c88dfc021"}, - {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:37105d1d708365b91165e1a6e505bdecc88637091348cf4b6adcdcb4f5a5fb8b"}, - {file = "simplejson-3.19.3-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b9198c1f1f8910a3b86b60f4fe2556d9d28d3fefe35bffe6be509a27402e694d"}, - {file = "simplejson-3.19.3-cp37-cp37m-win32.whl", hash = "sha256:bc164f32dd9691e7082ce5df24b4cf8c6c394bbf9bdeeb5d843127cd07ab8ad2"}, - {file = "simplejson-3.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1bd41f2cb1a2c57656ceff67b12d005cb255c728265e222027ad73193a04005a"}, - {file = "simplejson-3.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0733ecd95ae03ae718ec74aad818f5af5f3155d596f7b242acbc1621e765e5fb"}, - {file = "simplejson-3.19.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a0710d1a5e41c4f829caa1572793dd3130c8d65c2b194c24ff29c4c305c26e0"}, - {file = "simplejson-3.19.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1a53a07320c5ff574d8b1a89c937ce33608832f166f39dff0581ac43dc979abd"}, - {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1773cabfba66a6337b547e45dafbd471b09487370bcab75bd28f626520410d29"}, - {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c0104b4b7d2c75ccedbf1d9d5a3bd2daa75e51053935a44ba012e2fd4c43752"}, - {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c49eeb94b8f09dc8a5843c156a22b8bde6aa1ddc65ca8ddc62dddcc001e6a2d"}, - {file = "simplejson-3.19.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dc5c1a85ff388e98ea877042daec3d157b6db0d85bac6ba5498034689793e7e"}, - {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49549e3d81ab4a58424405aa545602674d8c35c20e986b42bb8668e782a94bac"}, - {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e1a1452ad5723ff129b081e3c8aa4ba56b8734fee4223355ed7b815a7ece69bc"}, - {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d0d5a63f1768fed7e78cf55712dee81f5a345e34d34224f3507ebf71df2b754d"}, - {file = "simplejson-3.19.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7e062767ac165df9a46963f5735aa4eee0089ec1e48b3f2ec46182754b96f55e"}, - {file = "simplejson-3.19.3-cp38-cp38-win32.whl", hash = "sha256:56134bbafe458a7b21f6fddbf889d36bec6d903718f4430768e3af822f8e27c2"}, - {file = "simplejson-3.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:bcde83a553a96dc7533736c547bddaa35414a2566ab0ecf7d3964fc4bdb84c11"}, - {file = "simplejson-3.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b5587feda2b65a79da985ae6d116daf6428bf7489992badc29fc96d16cd27b05"}, - {file = "simplejson-3.19.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0d2b00ecbcd1a3c5ea1abc8bb99a26508f758c1759fd01c3be482a3655a176f"}, - {file = "simplejson-3.19.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:32a3ada8f3ea41db35e6d37b86dade03760f804628ec22e4fe775b703d567426"}, - {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f455672f4738b0f47183c5896e3606cd65c9ddee3805a4d18e8c96aa3f47c84"}, - {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b737a5fefedb8333fa50b8db3dcc9b1d18fd6c598f89fa7debff8b46bf4e511"}, - {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb47ee773ce67476a960e2db4a0a906680c54f662521550828c0cc57d0099426"}, - {file = "simplejson-3.19.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eed8cd98a7b24861da9d3d937f5fbfb6657350c547528a117297fe49e3960667"}, - {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:619756f1dd634b5bdf57d9a3914300526c3b348188a765e45b8b08eabef0c94e"}, - {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dd7230d061e755d60a4d5445bae854afe33444cdb182f3815cff26ac9fb29a15"}, - {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:101a3c8392028cd704a93c7cba8926594e775ca3c91e0bee82144e34190903f1"}, - {file = "simplejson-3.19.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e557712fc79f251673aeb3fad3501d7d4da3a27eff0857af2e1d1afbbcf6685"}, - {file = "simplejson-3.19.3-cp39-cp39-win32.whl", hash = "sha256:0bc5544e3128891bf613b9f71813ee2ec9c11574806f74dd8bb84e5e95bf64a2"}, - {file = "simplejson-3.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:06662392e4913dc8846d6a71a6d5de86db5fba244831abe1dd741d62a4136764"}, - {file = "simplejson-3.19.3-py3-none-any.whl", hash = "sha256:49cc4c7b940d43bd12bf87ec63f28cbc4964fc4e12c031cc8cd01650f43eb94e"}, - {file = "simplejson-3.19.3.tar.gz", hash = "sha256:8e086896c36210ab6050f2f9f095a5f1e03c83fa0e7f296d6cba425411364680"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f5272b5866b259fe6c33c4a8c5073bf8b359c3c97b70c298a2f09a69b52c7c41"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5c0de368f3052a59a1acf21f8b2dd28686a9e4eba2da7efae7ed9554cb31e7bc"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0821871404a537fd0e22eba240c74c0467c28af6cc435903eca394cfc74a0497"}, + {file = "simplejson-3.20.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:c939a1e576bded47d7d03aa2afc2ae90b928b2cf1d9dc2070ceec51fd463f430"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3c4f0a61cdc05550782ca4a2cdb311ea196c2e6be6b24a09bf71360ca8c3ca9b"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6c21f5c026ca633cfffcb6bc1fac2e99f65cb2b24657d3bef21aed9916cc3bbf"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:8d23b7f8d6b72319d6d55a0261089ff621ce87e54731c2d3de6a9bf7be5c028c"}, + {file = "simplejson-3.20.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:cda5c32a98f392909088111ecec23f2b0d39346ceae1a0fea23ab2d1f84ec21d"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e580aa65d5f6c3bf41b9b4afe74be5d5ddba9576701c107c772d936ea2b5043a"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a586ce4f78cec11f22fe55c5bee0f067e803aab9bad3441afe2181693b5ebb5"}, + {file = "simplejson-3.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74a1608f9e6e8c27a4008d70a54270868306d80ed48c9df7872f9f4b8ac87808"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03db8cb64154189a92a7786209f24e391644f3a3fa335658be2df2af1960b8d8"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eea7e2b7d858f6fdfbf0fe3cb846d6bd8a45446865bc09960e51f3d473c2271b"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e66712b17d8425bb7ff8968d4c7c7fd5a2dd7bd63728b28356223c000dd2f91f"}, + {file = "simplejson-3.20.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2cc4f6486f9f515b62f5831ff1888886619b84fc837de68f26d919ba7bbdcbc"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3c2df555ee4016148fa192e2b9cd9e60bc1d40769366134882685e90aee2a1e"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78520f04b7548a5e476b5396c0847e066f1e0a4c0c5e920da1ad65e95f410b11"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f4bd49ecde87b0fe9f55cc971449a32832bca9910821f7072bbfae1155eaa007"}, + {file = "simplejson-3.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7eaae2b88eb5da53caaffdfa50e2e12022553949b88c0df4f9a9663609373f72"}, + {file = "simplejson-3.20.1-cp310-cp310-win32.whl", hash = "sha256:e836fb88902799eac8debc2b642300748f4860a197fa3d9ea502112b6bb8e142"}, + {file = "simplejson-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a19b552b212fc3b5b96fc5ce92333d4a9ac0a800803e1f17ebb16dac4be5"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:325b8c107253d3217e89d7b50c71015b5b31e2433e6c5bf38967b2f80630a8ca"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88a7baa8211089b9e58d78fbc1b0b322103f3f3d459ff16f03a36cece0d0fcf0"}, + {file = "simplejson-3.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:299b1007b8101d50d95bc0db1bf5c38dc372e85b504cf77f596462083ee77e3f"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ec618ed65caab48e81e3ed29586236a8e57daef792f1f3bb59504a7e98cd10"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2cdead1d3197f0ff43373cf4730213420523ba48697743e135e26f3d179f38"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3466d2839fdc83e1af42e07b90bc8ff361c4e8796cd66722a40ba14e458faddd"}, + {file = "simplejson-3.20.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d492ed8e92f3a9f9be829205f44b1d0a89af6582f0cf43e0d129fa477b93fe0c"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f924b485537b640dc69434565463fd6fc0c68c65a8c6e01a823dd26c9983cf79"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e8eacf6a3491bf76ea91a8d46726368a6be0eb94993f60b8583550baae9439e"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d34d04bf90b4cea7c22d8b19091633908f14a096caa301b24c2f3d85b5068fb8"}, + {file = "simplejson-3.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:69dd28d4ce38390ea4aaf212902712c0fd1093dc4c1ff67e09687c3c3e15a749"}, + {file = "simplejson-3.20.1-cp311-cp311-win32.whl", hash = "sha256:dfe7a9da5fd2a3499436cd350f31539e0a6ded5da6b5b3d422df016444d65e43"}, + {file = "simplejson-3.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:896a6c04d7861d507d800da7642479c3547060bf97419d9ef73d98ced8258766"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f31c4a3a7ab18467ee73a27f3e59158255d1520f3aad74315edde7a940f1be23"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884e6183d16b725e113b83a6fc0230152ab6627d4d36cb05c89c2c5bccfa7bc6"}, + {file = "simplejson-3.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03d7a426e416fe0d3337115f04164cd9427eb4256e843a6b8751cacf70abc832"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:000602141d0bddfcff60ea6a6e97d5e10c9db6b17fd2d6c66199fa481b6214bb"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af8377a8af78226e82e3a4349efdde59ffa421ae88be67e18cef915e4023a595"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c7de4c88ab2fbcb8781a3b982ef883696736134e20b1210bca43fb42ff1acf"}, + {file = "simplejson-3.20.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:455a882ff3f97d810709f7b620007d4e0aca8da71d06fc5c18ba11daf1c4df49"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc0f523ce923e7f38eb67804bc80e0a028c76d7868500aa3f59225574b5d0453"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76461ec929282dde4a08061071a47281ad939d0202dc4e63cdd135844e162fbc"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19c2da8c043607bde4d4ef3a6b633e668a7d2e3d56f40a476a74c5ea71949f"}, + {file = "simplejson-3.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2578bedaedf6294415197b267d4ef678fea336dd78ee2a6d2f4b028e9d07be3"}, + {file = "simplejson-3.20.1-cp312-cp312-win32.whl", hash = "sha256:339f407373325a36b7fd744b688ba5bae0666b5d340ec6d98aebc3014bf3d8ea"}, + {file = "simplejson-3.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:627d4486a1ea7edf1f66bb044ace1ce6b4c1698acd1b05353c97ba4864ea2e17"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71e849e7ceb2178344998cbe5ade101f1b329460243c79c27fbfc51c0447a7c3"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b63fdbab29dc3868d6f009a59797cefaba315fd43cd32ddd998ee1da28e50e29"}, + {file = "simplejson-3.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1190f9a3ce644fd50ec277ac4a98c0517f532cfebdcc4bd975c0979a9f05e1fb"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1336ba7bcb722ad487cd265701ff0583c0bb6de638364ca947bb84ecc0015d1"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e975aac6a5acd8b510eba58d5591e10a03e3d16c1cf8a8624ca177491f7230f0"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a6dd11ee282937ad749da6f3b8d87952ad585b26e5edfa10da3ae2536c73078"}, + {file = "simplejson-3.20.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab980fcc446ab87ea0879edad41a5c28f2d86020014eb035cf5161e8de4474c6"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f5aee2a4cb6b146bd17333ac623610f069f34e8f31d2f4f0c1a2186e50c594f0"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:652d8eecbb9a3b6461b21ec7cf11fd0acbab144e45e600c817ecf18e4580b99e"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c09948f1a486a89251ee3a67c9f8c969b379f6ffff1a6064b41fea3bce0a112"}, + {file = "simplejson-3.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cbbd7b215ad4fc6f058b5dd4c26ee5c59f72e031dfda3ac183d7968a99e4ca3a"}, + {file = "simplejson-3.20.1-cp313-cp313-win32.whl", hash = "sha256:ae81e482476eaa088ef9d0120ae5345de924f23962c0c1e20abbdff597631f87"}, + {file = "simplejson-3.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:1b9fd15853b90aec3b1739f4471efbf1ac05066a2c7041bf8db821bb73cd2ddc"}, + {file = "simplejson-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c7edf279c1376f28bf41e916c015a2a08896597869d57d621f55b6a30c7e1e6d"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9202b9de38f12e99a40addd1a8d508a13c77f46d87ab1f9095f154667f4fe81"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:391345b4157cc4e120027e013bd35c45e2c191e2bf48b8913af488cdc3b9243c"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6fdcc9debb711ddd2ad6d69f9386a3d9e8e253234bbb30513e0a7caa9510c51"}, + {file = "simplejson-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9daf8cdc7ee8a9e9f7a3b313ba0a003391857e90d0e82fbcd4d614aa05cb7c3b"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:c02f4868a3a46ffe284a51a88d134dc96feff6079a7115164885331a1ba8ed9f"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:3d7310172d5340febd258cb147f46aae30ad57c445f4d7e1ae8461c10aaf43b0"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:4762e05577955312a4c6802f58dd02e040cc79ae59cda510aa1564d84449c102"}, + {file = "simplejson-3.20.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:8bb98fdf318c05aefd08a92583bd6ee148e93c6756fb1befb7b2d5f27824be78"}, + {file = "simplejson-3.20.1-cp36-cp36m-win32.whl", hash = "sha256:9a74e70818818981294b8e6956ce3496c5e1bd4726ac864fae473197671f7b85"}, + {file = "simplejson-3.20.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e041add470e8f8535cc05509485eb7205729a84441f03b25cde80ad48823792e"}, + {file = "simplejson-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7e9d73f46119240e4f4f07868241749d67d09873f40cb968d639aa9ccc488b86"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae6e637dc24f8fee332ed23dd070e81394138e42cd4fd9d0923e5045ba122e27"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efd3bc6c6b17e3d4620eb6be5196f0d1c08b6ce7c3101fa8e292b79e0908944b"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87fc623d457173a0213bc9ca4e346b83c9d443f63ed5cca847fb0cacea3cfc95"}, + {file = "simplejson-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec6a1e0a7aff76f0e008bebfa950188b9c50b58c1885d898145f48fc8e189a56"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:9c079606f461a6e950099167e21e13985147c8a24be8eea66c9ad68f73fad744"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:9faceb68fba27ef17eda306e4cd97a7b4b14fdadca5fbb15790ba8b26ebeec0c"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:7ceed598e4bacbf5133fe7a418f7991bb2df0683f3ac11fbf9e36a2bc7aa4b85"}, + {file = "simplejson-3.20.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ede69c765e9901861ad7c6139023b7b7d5807c48a2539d817b4ab40018002d5f"}, + {file = "simplejson-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:d8853c269a4c5146ddca4aa7c70e631795e9d11239d5fedb1c6bbc91ffdebcac"}, + {file = "simplejson-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ed6a17fd397f0e2b3ad668fc9e19253ed2e3875ad9086bd7f795c29a3223f4a1"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7551682b60bba3a9e2780742e101cf0a64250e76de7d09b1c4b0c8a7c7cc6834"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd9577ec1c8c3a43040e3787711e4c257c70035b7551a21854b5dec88dad09e1"}, + {file = "simplejson-3.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8e197e4cf6d42c2c57e7c52cd7c1e7b3e37c5911df1314fb393320131e2101"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bd09c8c75666e7f62a33d2f1fb57f81da1fcbb19a9fe7d7910b5756e1dd6048"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bd6bfe5678d73fbd5328eea6a35216503796428fc47f1237432522febaf3a0c"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b75d448fd0ceb2e7c90e72bb82c41f8462550d48529980bc0bab1d2495bfbb"}, + {file = "simplejson-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7e15b716d09f318c8cda3e20f82fae81684ce3d3acd1d7770fa3007df1769de"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3e7963197d958fcf9e98b212b80977d56c022384621ff463d98afc3b6b1ce7e8"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2e671dd62051129185d3a9a92c60101f56cbc174854a1a3dfb69114ebd9e1699"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e25b2a0c396f3b84fb89573d07b0e1846ed563eb364f2ea8230ca92b8a8cb786"}, + {file = "simplejson-3.20.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:489c3a43116082bad56795215786313832ba3991cca1f55838e52a553f451ab6"}, + {file = "simplejson-3.20.1-cp38-cp38-win32.whl", hash = "sha256:4a92e948bad8df7fa900ba2ba0667a98303f3db206cbaac574935c332838208e"}, + {file = "simplejson-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:49d059b8363327eee3c94799dd96782314b2dbd7bcc293b4ad48db69d6f4d362"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a8011f1dd1d676befcd4d675ebdbfdbbefd3bf350052b956ba8c699fca7d8cef"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e91703a4c5fec53e36875ae426ad785f4120bd1d93b65bed4752eeccd1789e0c"}, + {file = "simplejson-3.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e39eaa57c7757daa25bcd21f976c46be443b73dd6c3da47fe5ce7b7048ccefe2"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceab2ce2acdc7fbaa433a93006758db6ba9a659e80c4faa13b80b9d2318e9b17"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d4f320c33277a5b715db5bf5b10dae10c19076bd6d66c2843e04bd12d1f1ea5"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6436c48e64378fa844d8c9e58a5ed0352bbcfd4028369a9b46679b7ab79d2d"}, + {file = "simplejson-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e18345c8dda5d699be8166b61f9d80aaee4545b709f1363f60813dc032dac53"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:90b573693d1526bed576f6817e2a492eaaef68f088b57d7a9e83d122bbb49e51"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:272cc767826e924a6bd369ea3dbf18e166ded29059c7a4d64d21a9a22424b5b5"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:51b41f284d603c4380732d7d619f8b34bd04bc4aa0ed0ed5f4ffd0539b14da44"}, + {file = "simplejson-3.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6e6697a3067d281f01de0fe96fc7cba4ea870d96d7deb7bfcf85186d74456503"}, + {file = "simplejson-3.20.1-cp39-cp39-win32.whl", hash = "sha256:6dd3a1d5aca87bf947f3339b0f8e8e329f1badf548bdbff37fac63c17936da8e"}, + {file = "simplejson-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:463f1fca8fbf23d088e5850fdd0dd4d5faea8900a9f9680270bd98fd649814ca"}, + {file = "simplejson-3.20.1-py3-none-any.whl", hash = "sha256:8a6c1bbac39fa4a79f83cbf1df6ccd8ff7069582a9fd8db1e52cea073bc2c697"}, + {file = "simplejson-3.20.1.tar.gz", hash = "sha256:e64139b4ec4f1f24c142ff7dcafe55a22b811a74d86d66560c8815687143037d"}, ] [[package]] @@ -3216,13 +3184,13 @@ files = [ [[package]] name = "types-cffi" -version = "1.16.0.20241221" +version = "1.17.0.20250319" description = "Typing stubs for cffi" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "types_cffi-1.16.0.20241221-py3-none-any.whl", hash = "sha256:e5b76b4211d7a9185f6ab8d06a106d56c7eb80af7cdb8bfcb4186ade10fb112f"}, - {file = "types_cffi-1.16.0.20241221.tar.gz", hash = "sha256:1c96649618f4b6145f58231acb976e0b448be6b847f7ab733dabe62dfbff6591"}, + {file = "types_cffi-1.17.0.20250319-py3-none-any.whl", hash = "sha256:5e95f0f10d3f2fd0a8a0a10f6b8b1e0e6ff47796ad2fdd4302b5e514b64d6af4"}, + {file = "types_cffi-1.17.0.20250319.tar.gz", hash = "sha256:66b0656818e5363f136a0a361f28e41330b55f83d390b14c6bf56026f57b3603"}, ] [package.dependencies] @@ -3293,13 +3261,13 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20241016" +version = "2.32.0.20250306" description = "Typing stubs for requests" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, - {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, + {file = "types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b"}, + {file = "types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1"}, ] [package.dependencies] @@ -3307,24 +3275,27 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.8.0.20250110" +version = "76.0.0.20250313" description = "Typing stubs for setuptools" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480"}, - {file = "types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271"}, + {file = "types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e"}, + {file = "types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e"}, ] +[package.dependencies] +setuptools = "*" + [[package]] name = "types-simplejson" -version = "3.19.0.20241221" +version = "3.20.0.20250318" description = "Typing stubs for simplejson" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "types_simplejson-3.19.0.20241221-py3-none-any.whl", hash = "sha256:179dfaef8c357156c781fa47cfdfcd953a7953fc375dfe9ab19a20054a828980"}, - {file = "types_simplejson-3.19.0.20241221.tar.gz", hash = "sha256:114af9db0f49ad15755d2b6ad8e6fd04b5a493815e2fc1e011729d4650defc70"}, + {file = "types_simplejson-3.20.0.20250318-py3-none-any.whl", hash = "sha256:e8c9cdb06b566b6ca1c7bf3d4c97fbd69a6f6a5aea760ef127f31e638a094c26"}, + {file = "types_simplejson-3.20.0.20250318.tar.gz", hash = "sha256:5ada2caa2f76826a90b97985f7b0caf55088a23ed4eb9c39fc1f5cb00b985e09"}, ] [[package]] @@ -3340,13 +3311,13 @@ files = [ [[package]] name = "ua-parser" -version = "1.0.0" +version = "1.0.1" description = "Python port of Browserscope's user agent parser" optional = false python-versions = ">=3.9" files = [ - {file = "ua_parser-1.0.0-py3-none-any.whl", hash = "sha256:5b31133606a781f56692caa11a9671a9f330c22604b3c4957a7ba18c152212d0"}, - {file = "ua_parser-1.0.0.tar.gz", hash = "sha256:a9740f53f4fbb72b7a03d304cae32a2785cafc55e8207efb74877bba17c35324"}, + {file = "ua_parser-1.0.1-py3-none-any.whl", hash = "sha256:b059f2cb0935addea7e551251cbbf42e9a8872f86134163bc1a4f79e0945ffea"}, + {file = "ua_parser-1.0.1.tar.gz", hash = "sha256:f9d92bf19d4329019cef91707aecc23c6d65143ad7e29a233f0580fb0d15547d"}, ] [package.dependencies] @@ -3501,13 +3472,13 @@ files = [ [[package]] name = "wtforms" -version = "3.1.2" +version = "3.2.1" description = "Form validation and rendering for Python web development." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "wtforms-3.1.2-py3-none-any.whl", hash = "sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07"}, - {file = "wtforms-3.1.2.tar.gz", hash = "sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9"}, + {file = "wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4"}, + {file = "wtforms-3.2.1.tar.gz", hash = "sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682"}, ] [package.dependencies] @@ -3733,4 +3704,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = "^3.12.6" -content-hash = "4a7eaa375c51b3cd071caae55ef888ac23ab8088e9a55efa1bc932ee651be2f8" +content-hash = "d93aac1b6f81afdafbdc8bd7a206e596c5e3d105475899ad81bbbae14ec30ec5" diff --git a/pyproject.toml b/pyproject.toml index 574a84c484..e2337d2e3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ ordered-set = "^4.1.0" cachetools = "^5.3.0.7" gevent = "^24.2.1" babel = "==2.14.0" # Temporarily pinned - problem for translations found in v2.15.0, see: https://github.com/ONSdigital/eq-questionnaire-runner/pull/1384 -wtforms = "==3.1.2" # Temporarily pinned - problem for breaking changes in v3.2.0, see: https://github.com/pallets-eco/wtforms/releases/tag/3.2.0 +wtforms = "^3.2.1" [build-system] requires = ["poetry-core"] diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 310f397a3e..3a1a8d0622 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -185,10 +185,6 @@ def publisher(mocker): "app.publisher.publisher.google.auth._default._get_explicit_environ_credentials", return_value=(mocker.Mock(universe_domain="test"), "test-project-id"), ) - mocker.patch( - "google.pubsub_v1.services.publisher.client.PublisherClient._compare_universes", - return_value=True, - ) return PubSubPublisher() From e38173fce601e047d34309577244f32808fb7e15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 12:51:00 +0000 Subject: [PATCH 518/567] Bump the npm_and_yarn group across 1 directory with 2 updates (#1632) --- package-lock.json | 250 +++++++++++++++++++++++++++------------------- 1 file changed, 147 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index f57a5498d9..c30567d327 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1515,13 +1515,14 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -1531,13 +1532,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1547,13 +1549,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1563,13 +1566,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1579,13 +1583,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1595,13 +1600,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1611,13 +1617,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1627,13 +1634,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1643,13 +1651,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1659,13 +1668,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1675,13 +1685,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1691,13 +1702,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1707,13 +1719,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1723,13 +1736,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1739,13 +1753,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1755,13 +1770,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1771,13 +1787,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1786,14 +1803,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1803,13 +1838,14 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1819,13 +1855,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1835,13 +1872,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1851,13 +1889,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1867,13 +1906,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1883,13 +1923,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -5606,11 +5647,12 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -5618,30 +5660,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" } }, "node_modules/escalade": { @@ -12087,12 +12130,13 @@ "dev": true }, "node_modules/tsx": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.1.tgz", - "integrity": "sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", + "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "~0.23.0", + "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "bin": { From 94a55204b71e978631b39eac0bae50888f4640c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Mar 2025 13:53:22 +0000 Subject: [PATCH 519/567] Bump the development-dependencies group across 1 directory with 13 updates (#1631) --- package-lock.json | 500 +++++++++++++++++++++++----------------------- 1 file changed, 246 insertions(+), 254 deletions(-) diff --git a/package-lock.json b/package-lock.json index c30567d327..d41008a25b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,9 +65,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -75,22 +75,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", - "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", + "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -106,14 +106,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -190,10 +190,11 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -359,27 +360,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", - "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -541,14 +542,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -782,12 +784,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1178,15 +1181,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", - "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", + "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1244,12 +1248,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1338,13 +1343,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", - "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", + "@babel/compat-data": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", @@ -1358,7 +1363,7 @@ "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", @@ -1373,7 +1378,7 @@ "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", @@ -1401,7 +1406,7 @@ "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", @@ -1409,9 +1414,9 @@ "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -1455,9 +1460,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, "license": "MIT", "dependencies": { @@ -1468,31 +1473,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", - "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1501,9 +1507,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "license": "MIT", "dependencies": { @@ -2808,20 +2814,20 @@ } }, "node_modules/@wdio/cli": { - "version": "9.7.1", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.7.1.tgz", - "integrity": "sha512-oPCmjle4+UEFs2WAql3HQNfRgTu3orwb139jt79cS9XtNtGku4iiAgD/lND+c555TnVJin2BWdu1D4kZdCTVBw==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.12.1.tgz", + "integrity": "sha512-NsY/c27SwXFm80fgkMfMVginrJ032vN9HtOSZpcbM3RXf3dzsvlhPYFTXJhmB8Iu6h4Yy9ejKLgZgZ/VS1fC+g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.1.1", "@vitest/snapshot": "^2.1.1", - "@wdio/config": "9.6.4", - "@wdio/globals": "9.7.1", + "@wdio/config": "9.12.1", + "@wdio/globals": "9.12.1", "@wdio/logger": "9.4.4", "@wdio/protocols": "9.7.0", - "@wdio/types": "9.6.3", - "@wdio/utils": "9.6.4", + "@wdio/types": "9.10.1", + "@wdio/utils": "9.12.1", "async-exit-hook": "^2.0.1", "chalk": "^5.2.0", "chokidar": "^4.0.0", @@ -2836,7 +2842,7 @@ "read-pkg-up": "^10.0.0", "recursive-readdir": "^2.2.3", "tsx": "^4.7.2", - "webdriverio": "9.7.1", + "webdriverio": "9.12.1", "yargs": "^17.7.2" }, "bin": { @@ -2859,15 +2865,15 @@ } }, "node_modules/@wdio/config": { - "version": "9.6.4", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.6.4.tgz", - "integrity": "sha512-oTNXVVzaZ0qaM7oX8tyS3YBr4A3ij2py3Umew3ez0IS2vHpRs1LvLfVWoHRSqrhJIVnfjV3+zqcl9BWALNVD/g==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.12.1.tgz", + "integrity": "sha512-eYyF9HBQg2PyX6ScieZ5akDG4BaJmNBdYFJmwhUAGcJlxLgoI02vSqIuoWaQd5shbvtCdDzsFI0Jt8+S/xqINQ==", "dev": true, "license": "MIT", "dependencies": { "@wdio/logger": "9.4.4", - "@wdio/types": "9.6.3", - "@wdio/utils": "9.6.4", + "@wdio/types": "9.10.1", + "@wdio/utils": "9.12.1", "deepmerge-ts": "^7.0.3", "glob": "^10.2.2", "import-meta-resolve": "^4.0.0" @@ -2877,30 +2883,30 @@ } }, "node_modules/@wdio/globals": { - "version": "9.7.1", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.7.1.tgz", - "integrity": "sha512-o3Zaiyi/amH3VF+D2+lB+UXcaksWx44SIIVZjkgYgHsKAnmrv04JfkneQo+x05J/vNc+BM6jwTgTAtmkfF/t5w==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.12.1.tgz", + "integrity": "sha512-vZ48HnN3MpXcqxmmMtDUExBiHWhqQdRDm7m/j3mDycK/9GUVc+AuSEyFn6P6utsxoaVEqeRO2HZLPbAe54GaTA==", "dev": true, "license": "MIT", "engines": { "node": ">=18.20.0" }, "optionalDependencies": { - "expect-webdriverio": "^5.0.1", - "webdriverio": "9.7.1" + "expect-webdriverio": "^5.1.0", + "webdriverio": "9.12.1" } }, "node_modules/@wdio/local-runner": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.41.0.tgz", - "integrity": "sha512-A5msAjAC8gqiWvtFl+VNm9BBlVb5q3a2o7i+L+Cw7idV3aFY5etigB2wLYMtyBWgB8cXvbZaxXizHhGvZ+iB8Q==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.43.0.tgz", + "integrity": "sha512-FEIl0wvKkn2rfTE+HT0t1O/aOUrIMHjf8zgz4N0m8PFwWEyi/WmM+jkAzruXDM9RJOT57Awv1gTdjQipbVw1Qg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^22.2.0", "@wdio/logger": "8.38.0", "@wdio/repl": "8.40.3", - "@wdio/runner": "8.41.0", + "@wdio/runner": "8.43.0", "@wdio/types": "8.41.0", "async-exit-hook": "^2.0.1", "split2": "^4.1.0", @@ -2996,17 +3002,17 @@ } }, "node_modules/@wdio/mocha-framework": { - "version": "9.6.4", - "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.6.4.tgz", - "integrity": "sha512-8TqCIKGz5CkA2uSJSLXLLjINCeZ/UGvegZiB82Fr9hO3RvirPXbYVV+P6vqB9jqSACZ5kxX22cTUhqXgFxUjyQ==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-9.12.1.tgz", + "integrity": "sha512-gWRpSizXyhFFE5MaHtyf/O51nVL08bEoAqrAtZ8ZDRMhmjnVY6KA64Shxw+ULoK1ta1dpxpIyrsiloyLaBTPuA==", "dev": true, "license": "MIT", "dependencies": { "@types/mocha": "^10.0.6", "@types/node": "^20.11.28", "@wdio/logger": "9.4.4", - "@wdio/types": "9.6.3", - "@wdio/utils": "9.6.4", + "@wdio/types": "9.10.1", + "@wdio/utils": "9.12.1", "mocha": "^10.3.0" }, "engines": { @@ -3034,9 +3040,9 @@ } }, "node_modules/@wdio/repl/node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", + "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3051,15 +3057,15 @@ "license": "MIT" }, "node_modules/@wdio/reporter": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.6.3.tgz", - "integrity": "sha512-OwuwhRtqDZDPU8/6TBSMg+bvw9DKgUVfGu160zamBt8jPg8LZ5VU2VoZlRBNZMAHl3OnSwj+ih2/meEoLf3DUg==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.11.0.tgz", + "integrity": "sha512-YTUlrTsJU2XT5olFOk+tsYm6DcUwmowk0us1gt5Me2zFI3UdUQpuUMfPEqFtayRsfcHwSaPGLKR7Eng+r3uXGQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.1.0", "@wdio/logger": "9.4.4", - "@wdio/types": "9.6.3", + "@wdio/types": "9.10.1", "diff": "^7.0.0", "object-inspect": "^1.12.0" }, @@ -3068,23 +3074,23 @@ } }, "node_modules/@wdio/runner": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.41.0.tgz", - "integrity": "sha512-eQ9vZaHIXBLw7XqiKsasiUGjC8PgJawnHFMPKS0i/4ds+5arHo6ciX0s2uhJ3j/EHw3PYvFPCREp/sXetRuNlQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.43.0.tgz", + "integrity": "sha512-tL6v7hFBQNUy23douH1kvtg+kNdhtgh6qGX2PxTpr2d1ucu4p92umEkkJaFnsRjy2SQmDiV5oAvk/kPphWOHqw==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^22.2.0", - "@wdio/config": "8.41.0", - "@wdio/globals": "8.41.0", + "@wdio/config": "8.43.0", + "@wdio/globals": "8.43.0", "@wdio/logger": "8.38.0", "@wdio/types": "8.41.0", "@wdio/utils": "8.41.0", "deepmerge-ts": "^5.1.0", "expect-webdriverio": "^4.12.0", "gaze": "^1.1.3", - "webdriver": "8.41.0", - "webdriverio": "8.41.0" + "webdriver": "8.43.0", + "webdriverio": "8.43.0" }, "engines": { "node": "^16.13 || >=18" @@ -3113,9 +3119,9 @@ } }, "node_modules/@wdio/runner/node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", + "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3123,9 +3129,9 @@ } }, "node_modules/@wdio/runner/node_modules/@wdio/config": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.41.0.tgz", - "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.43.0.tgz", + "integrity": "sha512-mptlO5Lt8hV/8T9vRkp24G4o/kpI4qa9k4bRlOsI777MgeOlCEu2xLdRvVNGcg7RigDkx4jdYXX1pIHFg7gpSA==", "dev": true, "license": "MIT", "dependencies": { @@ -3142,9 +3148,9 @@ } }, "node_modules/@wdio/runner/node_modules/@wdio/globals": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.41.0.tgz", - "integrity": "sha512-xfUpEppdKzMHy4qoSoQN1cXoBPPh7oMeX+U/jtdvOtla+dd/YZ8pu47zLhQ/GM3gDVrBGnO4w3u4L6Zf/P3KEw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.43.0.tgz", + "integrity": "sha512-RCkldSApdoCAMsQhU7Qy9uXlFoPy8OFWOxbiE8O2WcRpTpSwAwWksN9tOLmevCTn1Ovj+onnxkiZQ0spnAUDbw==", "dev": true, "license": "MIT", "engines": { @@ -3152,7 +3158,7 @@ }, "optionalDependencies": { "expect-webdriverio": "^4.11.2", - "webdriverio": "8.41.0" + "webdriverio": "8.43.0" } }, "node_modules/@wdio/runner/node_modules/@wdio/logger": { @@ -3424,14 +3430,14 @@ "license": "MIT" }, "node_modules/@wdio/runner/node_modules/webdriverio": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.41.0.tgz", - "integrity": "sha512-WlQfw0mUEhTS8DPr+TBSYMhEnqXkFr2dcUwPb5XkffTB+i0wftf+BLXJPSVD9M1PTLyYcFdCIu68pqR54dq5BA==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.43.0.tgz", + "integrity": "sha512-aBwK25CmBQZ98MMlLteMiDiqpuQUsd1RcK6OU2eS921SM1JX4WJGCo27FAgcweb1ehX7rECL1qai1WFCrGfWIA==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^22.2.0", - "@wdio/config": "8.41.0", + "@wdio/config": "8.43.0", "@wdio/logger": "8.38.0", "@wdio/protocols": "8.40.3", "@wdio/repl": "8.40.3", @@ -3441,7 +3447,7 @@ "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1359167", + "devtools-protocol": "^0.0.1400418", "grapheme-splitter": "^1.0.2", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", @@ -3454,7 +3460,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.41.0" + "webdriver": "8.43.0" }, "engines": { "node": "^16.13 || >=18" @@ -3491,14 +3497,14 @@ } }, "node_modules/@wdio/spec-reporter": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.6.3.tgz", - "integrity": "sha512-P9zi8PfdhUWGnwb1tcl7EyLGKHFiCOkS04I7nBnVRAkTTfeQ+jpuDIlefW8DkW91yHHOpoAOOVeMqVBGRc8OoQ==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-9.11.0.tgz", + "integrity": "sha512-S8pd2hm8zWbE2yiAZqpDSgZ0gRnZRwV4xWAKO/dhHgAYWzHamQ1Hk7P0Sp1sQA/Pu+gyRId3uJ+WDiWVos8ftA==", "dev": true, "license": "MIT", "dependencies": { - "@wdio/reporter": "9.6.3", - "@wdio/types": "9.6.3", + "@wdio/reporter": "9.11.0", + "@wdio/types": "9.10.1", "chalk": "^5.1.2", "easy-table": "^1.2.0", "pretty-ms": "^9.0.0" @@ -3520,9 +3526,9 @@ } }, "node_modules/@wdio/types": { - "version": "9.6.3", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.6.3.tgz", - "integrity": "sha512-K3Lu7K5g5bsUcQV6/95XaS3jMwcGUn2pDdryYibKZafklhHjVt3o/xnw6Vgd/JzoSneCKHdwj941n+yDpTJHAw==", + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.10.1.tgz", + "integrity": "sha512-/t1VXPU5Ad1FQjRUP0WlK7IR0dCTX5hSkul8SpCuUpWbeyI4Iol/Wx2b1YU6nS+Ydh78rJCyHxtV0eE5TM1rbw==", "dev": true, "license": "MIT", "dependencies": { @@ -3533,15 +3539,15 @@ } }, "node_modules/@wdio/utils": { - "version": "9.6.4", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.6.4.tgz", - "integrity": "sha512-FMI/F5ju0h0HKC4RRQKW/H9So2cgtK6dd0JCmVdBzQ+/LMluEzlZmQva14HYmNd2t2ZmejYRqAJPV3aAsMAMZA==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.12.1.tgz", + "integrity": "sha512-WrkBdglOwKMpwvCZbOatlLUCghxNWyVfKRDyl92RBX3DuRqqq+uZK8fSHHAJMvXfax5TxcTRzHZUKrQO3ASSXw==", "dev": true, "license": "MIT", "dependencies": { "@puppeteer/browsers": "^2.2.0", "@wdio/logger": "9.4.4", - "@wdio/types": "9.6.3", + "@wdio/types": "9.10.1", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", "edgedriver": "^6.1.1", @@ -3991,13 +3997,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4189,9 +4196,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -4207,11 +4214,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4394,9 +4402,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001707", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", + "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", "dev": true, "funding": [ { @@ -4411,7 +4419,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chainsaw": { "version": "0.1.0", @@ -4804,12 +4813,13 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -5159,10 +5169,11 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1359167", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1359167.tgz", - "integrity": "sha512-f/9PeTaSH3weS/WAwrQb5/s9R3KMOeTGe+Jkhg5952yInub7iDPjdlzRdrDgpLZfxHbTrBuG9aUkAMM+ocVkXQ==", - "dev": true + "version": "0.0.1400418", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1400418.tgz", + "integrity": "sha512-U8j75zDOXF8IP3o0Cgb7K4tFA9uUHEOru2Wx64+EUqL4LNOh9dRe1i8WKR1k3mSpjcCe3aIkTDvEwq0YkI4hfw==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/diff": { "version": "7.0.0", @@ -5373,6 +5384,7 @@ "integrity": "sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@wdio/logger": "^8.38.0", "@zip.js/zip.js": "^2.7.48", @@ -5391,6 +5403,7 @@ "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^5.1.2", "loglevel": "^1.6.0", @@ -5402,10 +5415,11 @@ } }, "node_modules/edgedriver/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -5429,10 +5443,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.41", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", - "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", - "dev": true + "version": "1.5.123", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -6435,9 +6450,9 @@ } }, "node_modules/expect-webdriverio": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.0.5.tgz", - "integrity": "sha512-h04OGd7ZksVj8bgv3bYdjFpmJuKeCnyRrBmpMxYpMDmYSspxg9vsSr0kD5p9oOM16bX0ZXEVXr42RbI2hoLpTw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.1.0.tgz", + "integrity": "sha512-4u3q+Dqx/lXNgvCx1gKia4CfS28z1UxGGfVUkoMNbrsBlTBB2fYqXG+4+YtYoerxvp/XPwIb/+89IGEdyPbDXQ==", "dev": true, "license": "MIT", "optional": true, @@ -7393,9 +7408,9 @@ "dev": true }, "node_modules/htmlfy": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.5.1.tgz", - "integrity": "sha512-nb66M9g0zKrvmR3kk/WOM+5tOT3DzO1yJ4yEJXsz2zfZ3gXiCTrlGvbc4lQzTZyylJj7at+XSVDxFvAVH6J6tQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.6.2.tgz", + "integrity": "sha512-dWRE+TW3QSB5mXsnYCUPLoPmaCu2O7kp6/3xh5fayiGuaNtRL/64SdjhoTBwJ2XvuSkLoMgQDLunrAqwxJj40Q==", "dev": true, "license": "MIT" }, @@ -8739,9 +8754,9 @@ } }, "node_modules/lint-staged": { - "version": "15.4.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz", - "integrity": "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.0.tgz", + "integrity": "sha512-WyCzSbfYGhK7cU+UuDDkzUiytbfbi0ZdPy2orwtM75P3WTtQBzmG40cCxIa8Ii2+XjfxzLH6Be46tUfWS85Xfg==", "dev": true, "license": "MIT", "dependencies": { @@ -9946,10 +9961,11 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "6.0.2", @@ -10575,9 +10591,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -11367,7 +11383,8 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/safe-array-concat": { "version": "1.1.2", @@ -12247,9 +12264,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -12506,9 +12523,9 @@ "dev": true }, "node_modules/uuid": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", - "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", @@ -12682,15 +12699,15 @@ } }, "node_modules/webdriver": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.41.0.tgz", - "integrity": "sha512-n8OrFnVT4hAaGa0Advr3T8ObJdeKNTRklHIEzM2CYVx/5DZt+2KwaKSxWsURNd4zU7FbsfaJUU4rQWCmvozQLg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.43.0.tgz", + "integrity": "sha512-cfyqymgFae4gtxuvp/nz7Yat3qqlEyxopQL7H6MSSOey+mXq3kYZZ0auyuhGaaPvDidOY3Oh597cEB2ft8pW3A==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^22.2.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.41.0", + "@wdio/config": "8.43.0", "@wdio/logger": "8.38.0", "@wdio/protocols": "8.40.3", "@wdio/types": "8.41.0", @@ -12727,9 +12744,9 @@ } }, "node_modules/webdriver/node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.13.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", + "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12737,9 +12754,9 @@ } }, "node_modules/webdriver/node_modules/@wdio/config": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.41.0.tgz", - "integrity": "sha512-/6Z3sfSyhX5oVde0l01fyHimbqRYIVUDBnhDG2EMSCoC2lsaJX3Bm3IYpYHYHHFsgoDCi3B3Gv++t9dn2eSZZw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.43.0.tgz", + "integrity": "sha512-mptlO5Lt8hV/8T9vRkp24G4o/kpI4qa9k4bRlOsI777MgeOlCEu2xLdRvVNGcg7RigDkx4jdYXX1pIHFg7gpSA==", "dev": true, "license": "MIT", "dependencies": { @@ -12938,9 +12955,9 @@ "license": "MIT" }, "node_modules/webdriver/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { @@ -12960,39 +12977,37 @@ } }, "node_modules/webdriverio": { - "version": "9.7.1", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.7.1.tgz", - "integrity": "sha512-P1roVTpXwtzSgNKl9j92LF4+5i2eGd0n9EMvMRdLNnI0v1ws7dNJOHNgsEAepZqMikYPgh2m+5DyXR1Ygoa+nQ==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.12.1.tgz", + "integrity": "sha512-xKasUD3DRey9lEcTAbrI29XCxLX45cxVHOIN5EJK44/thch4zhzfskdCKY3BQ9589TCkSGY1CGf8q9LFx2Ve5Q==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.6.4", + "@wdio/config": "9.12.1", "@wdio/logger": "9.4.4", "@wdio/protocols": "9.7.0", "@wdio/repl": "9.4.4", - "@wdio/types": "9.6.3", - "@wdio/utils": "9.6.4", + "@wdio/types": "9.10.1", + "@wdio/utils": "9.12.1", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", "grapheme-splitter": "^1.0.4", - "htmlfy": "^0.5.0", - "import-meta-resolve": "^4.0.0", + "htmlfy": "^0.6.0", "is-plain-obj": "^4.1.0", "jszip": "^3.10.1", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", - "minimatch": "^9.0.3", "query-selector-shadow-dom": "^1.0.1", "resq": "^1.11.0", "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.7.0" + "webdriver": "9.12.1" }, "engines": { "node": ">=18.20.0" @@ -13019,44 +13034,20 @@ "node": ">=18.20.0" } }, - "node_modules/webdriverio/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/webdriverio/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/webdriverio/node_modules/webdriver": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.7.0.tgz", - "integrity": "sha512-O/Ce4I7HcsqlP3kx9L0F14olOsarKkXUz+hSunOTC9YxsiVoOu5yIcRrHyWUQziYgA4K5gobZSKrTuAr+edA4Q==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.12.1.tgz", + "integrity": "sha512-gtdsfoYAVgPVlN1x3kXhPmOOzQXx6vtw9KB0LCeFQH2zUfNMB7RB1OJN475cFgSgJ7PpyvXk3CKdT1lGCHRTxQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "9.6.4", + "@wdio/config": "9.12.1", "@wdio/logger": "9.4.4", "@wdio/protocols": "9.7.0", - "@wdio/types": "9.6.3", - "@wdio/utils": "9.6.4", + "@wdio/types": "9.10.1", + "@wdio/utils": "9.12.1", "deepmerge-ts": "^7.0.3", "undici": "^6.20.1", "ws": "^8.8.0" @@ -13066,9 +13057,9 @@ } }, "node_modules/webdriverio/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { @@ -13143,6 +13134,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, From 680e41f51cfce0fd89c99795a1eab8f6cf5256de Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Mon, 31 Mar 2025 08:35:29 +0100 Subject: [PATCH 520/567] Bump the production-dependencies group across 1 directory with 5 updates (GCS Submitter fixes) (#1633) --- app/submitter/submitter.py | 9 +- poetry.lock | 315 ++++++++++++++++++++++++++------ pyproject.toml | 4 +- tests/app/submitter/conftest.py | 5 +- 4 files changed, 262 insertions(+), 71 deletions(-) diff --git a/app/submitter/submitter.py b/app/submitter/submitter.py index 21d6bd7698..48e6038511 100644 --- a/app/submitter/submitter.py +++ b/app/submitter/submitter.py @@ -3,7 +3,6 @@ from google.api_core.exceptions import Forbidden from google.cloud import storage # type: ignore -from google.cloud.storage.retry import DEFAULT_RETRY from pika import BasicProperties, BlockingConnection, URLParameters from pika.exceptions import AMQPError, NackError, UnroutableError from structlog import get_logger @@ -53,10 +52,8 @@ def send_message( blob.metadata = metadata - # DEFAULT_RETRY is not idempotent. - # However, this behaviour was deemed acceptable for our use case. try: - blob.upload_from_string(str(message).encode("utf8"), retry=DEFAULT_RETRY) + blob.upload_from_string(str(message).encode("utf8")) except Forbidden as e: # If an object exists then the GCS Client will attempt to delete the existing object before reuploading. # However, in an attempt to reduce duplicate receipts, runner does not have a delete permission. @@ -179,9 +176,7 @@ def upload(self, metadata: MetadataType, payload: str) -> bool: blob = self.bucket.blob(str(uuid4())) blob.metadata = metadata - # DEFAULT_RETRY is not idempotent. - # However, this behaviour was deemed acceptable for our use case. - blob.upload_from_string(payload.encode("utf8"), retry=DEFAULT_RETRY) + blob.upload_from_string(payload.encode("utf8")) return True diff --git a/poetry.lock b/poetry.lock index cb064a1553..3853defa5d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "astroid" @@ -6,6 +6,7 @@ version = "3.3.9" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "astroid-3.3.9-py3-none-any.whl", hash = "sha256:d05bfd0acba96a7bd43e222828b7d9bc1e138aaeb0649707908d3702a9831248"}, {file = "astroid-3.3.9.tar.gz", hash = "sha256:622cc8e3048684aa42c820d9d218978021c3c3d174fb03a9f0d615921744f550"}, @@ -17,18 +18,19 @@ version = "25.3.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "babel" @@ -36,6 +38,7 @@ version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, @@ -50,6 +53,7 @@ version = "4.13.3" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" +groups = ["dev"] files = [ {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, @@ -72,6 +76,7 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, @@ -116,6 +121,7 @@ version = "1.9.0" description = "Fast, simple object-to-object and broadcast signaling" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, @@ -123,17 +129,18 @@ files = [ [[package]] name = "boto3" -version = "1.37.15" +version = "1.37.19" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ - {file = "boto3-1.37.15-py3-none-any.whl", hash = "sha256:78cc1b483cc637e1df8e81498d66f89550d4ee92175ccab5be1a2226672fe6b9"}, - {file = "boto3-1.37.15.tar.gz", hash = "sha256:586332456fff19328d57a88214a2ac2eda1bafab743556a836eda46a4ce613c6"}, + {file = "boto3-1.37.19-py3-none-any.whl", hash = "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130"}, + {file = "boto3-1.37.19.tar.gz", hash = "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8"}, ] [package.dependencies] -botocore = ">=1.37.15,<1.38.0" +botocore = ">=1.37.19,<1.38.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.11.0,<0.12.0" @@ -142,13 +149,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.37.15" +version = "1.37.19" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ - {file = "botocore-1.37.15-py3-none-any.whl", hash = "sha256:996b8d6a342ad7735eb07d8b4a81dad86e60ce0889ccb3edec0cd66eece85393"}, - {file = "botocore-1.37.15.tar.gz", hash = "sha256:72e6f1db6ebc4112d6ba719c97ad71ac7cf4a2f3729ae74fa225641e3ddcba92"}, + {file = "botocore-1.37.19-py3-none-any.whl", hash = "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415"}, + {file = "botocore-1.37.19.tar.gz", hash = "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8"}, ] [package.dependencies] @@ -165,6 +173,8 @@ version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, @@ -176,6 +186,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -188,8 +202,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -200,8 +220,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -211,6 +247,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -222,6 +262,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -234,6 +278,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -246,6 +294,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -257,6 +309,8 @@ version = "1.1.0.0" description = "Python CFFI bindings to the Brotli library" optional = false python-versions = ">=3.7" +groups = ["main"] +markers = "platform_python_implementation == \"PyPy\"" files = [ {file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"}, {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"}, @@ -296,6 +350,7 @@ version = "5.5.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -307,6 +362,7 @@ version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -318,6 +374,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -387,6 +444,7 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] +markers = {dev = "platform_python_implementation != \"PyPy\""} [package.dependencies] pycparser = "*" @@ -397,6 +455,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -498,6 +557,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -512,6 +572,7 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -523,6 +584,7 @@ version = "15.0.1" description = "Colored terminal output for Python's logging module" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, @@ -540,6 +602,7 @@ version = "7.7.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"}, {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"}, @@ -607,7 +670,7 @@ files = [ ] [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cramjam" @@ -615,6 +678,7 @@ version = "2.9.1" description = "Thin Python bindings to de/compression algorithms in Rust" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "cramjam-2.9.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8e82464d1e00fbbb12958999b8471ba5e9f3d9711954505a0a7b378762332e6f"}, {file = "cramjam-2.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d2df8a6511cc08ef1fccd2e0c65e2ebc9f57574ec8376052a76851af5398810"}, @@ -717,6 +781,7 @@ version = "44.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" +groups = ["main", "dev"] files = [ {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, @@ -759,10 +824,10 @@ files = [ cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0) ; python_version >= \"3.8\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] +pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] @@ -774,6 +839,7 @@ version = "1.15.4" description = "CSS unobfuscator and beautifier." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98"}, {file = "cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5"}, @@ -790,6 +856,7 @@ version = "1.2.18" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["main"] files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -799,7 +866,7 @@ files = [ wrapt = ">=1.10,<2" [package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] [[package]] name = "dill" @@ -807,6 +874,7 @@ version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -822,6 +890,7 @@ version = "1.36.4" description = "HTML Template Linter and Formatter" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "djlint-1.36.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2dfb60883ceb92465201bfd392291a7597c6752baede6fbb6f1980cac8d6c5c"}, {file = "djlint-1.36.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bc6a1320c0030244b530ac200642f883d3daa451a115920ef3d56d08b644292"}, @@ -864,6 +933,7 @@ version = "2.7.0" description = "DNS toolkit" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -884,6 +954,7 @@ version = "0.17.0" description = "EditorConfig File Locator and Interpreter for Python" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "EditorConfig-0.17.0-py3-none-any.whl", hash = "sha256:fe491719c5f65959ec00b167d07740e7ffec9a3f362038c72b289330b9991dfc"}, {file = "editorconfig-0.17.0.tar.gz", hash = "sha256:8739052279699840065d3a9f5c125d7d5a98daeefe53b0e5274261d77cb49aa2"}, @@ -895,6 +966,7 @@ version = "2.2.0" description = "A robust email address syntax and deliverability validation library." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, @@ -910,6 +982,7 @@ version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -924,6 +997,7 @@ version = "2.27.0" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" +groups = ["dev"] files = [ {file = "fakeredis-2.27.0-py3-none-any.whl", hash = "sha256:f4b6e0fa4193acbf00d81dac71ff5cc34fe7d7c12f1560b036f98578a103d5c3"}, {file = "fakeredis-2.27.0.tar.gz", hash = "sha256:7b7584ec104392592297f46864a82cb7339a23e254ee885bf5ae07cfc64fbce7"}, @@ -946,6 +1020,7 @@ version = "3.1.0" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "flask-3.1.0-py3-none-any.whl", hash = "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136"}, {file = "flask-3.1.0.tar.gz", hash = "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac"}, @@ -968,6 +1043,7 @@ version = "4.0.0" description = "Adds i18n/l10n support for Flask applications." optional = false python-versions = ">=3.8,<4.0" +groups = ["main"] files = [ {file = "flask_babel-4.0.0-py3-none-any.whl", hash = "sha256:638194cf91f8b301380f36d70e2034c77ee25b98cb5d80a1626820df9a6d4625"}, {file = "flask_babel-4.0.0.tar.gz", hash = "sha256:dbeab4027a3f4a87678a11686496e98e1492eb793cbdd77ab50f4e9a2602a593"}, @@ -985,6 +1061,7 @@ version = "1.17" description = "Compress responses in your Flask app with gzip, deflate, brotli or zstandard." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "Flask_Compress-1.17-py3-none-any.whl", hash = "sha256:415131f197c41109f08e8fdfc3a6628d83d81680fb5ecd0b3a97410e02397b20"}, {file = "flask_compress-1.17.tar.gz", hash = "sha256:1ebb112b129ea7c9e7d6ee6d5cc0d64f226cbc50c4daddf1a58b9bd02253fbd8"}, @@ -1005,6 +1082,7 @@ version = "0.6.3" description = "User authentication and session management for Flask." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333"}, {file = "Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d"}, @@ -1020,6 +1098,7 @@ version = "1.1.0" description = "HTTP security headers for Flask." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "flask-talisman-1.1.0.tar.gz", hash = "sha256:c5f486f5f54420729f84b3c3850cd63f96e8b033a9629bee66c524ea363797ff"}, {file = "flask_talisman-1.1.0-py2.py3-none-any.whl", hash = "sha256:3c42b610ebe49b0e35ca150e179bf51aa1da01e4635b49a674868ea681046208"}, @@ -1031,6 +1110,7 @@ version = "1.2.2" description = "Form rendering, validation, and CSRF protection for Flask with WTForms." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "flask_wtf-1.2.2-py3-none-any.whl", hash = "sha256:e93160c5c5b6b571cf99300b6e01b72f9a101027cab1579901f8b10c5daf0b70"}, {file = "flask_wtf-1.2.2.tar.gz", hash = "sha256:79d2ee1e436cf570bccb7d916533fa18757a2f18c290accffab1b9a0b684666b"}, @@ -1050,6 +1130,7 @@ version = "1.5.1" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, @@ -1064,6 +1145,7 @@ version = "24.11.1" description = "Coroutine-based network library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "gevent-24.11.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:92fe5dfee4e671c74ffaa431fd7ffd0ebb4b339363d24d0d944de532409b935e"}, {file = "gevent-24.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7bfcfe08d038e1fa6de458891bca65c1ada6d145474274285822896a858c870"}, @@ -1112,11 +1194,11 @@ greenlet = {version = ">=3.1.1", markers = "platform_python_implementation == \" "zope.interface" = "*" [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +dnspython = ["dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\""] docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] +monitor = ["psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +recommended = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\""] +test = ["cffi (>=1.17.1) ; platform_python_implementation == \"CPython\"", "coverage (>=5.0) ; sys_platform != \"win32\"", "dnspython (>=1.16.0,<2.0) ; python_version < \"3.10\"", "idna ; python_version < \"3.10\"", "objgraph", "psutil (>=5.7.0) ; sys_platform != \"win32\" or platform_python_implementation == \"CPython\"", "requests"] [[package]] name = "google-api-core" @@ -1124,6 +1206,7 @@ version = "2.24.2" description = "Google API client core library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9"}, {file = "google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696"}, @@ -1136,14 +1219,14 @@ grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_versi grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} proto-plus = [ {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, + {version = ">=1.22.3,<2.0.0"}, ] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" requests = ">=2.18.0,<3.0.0" [package.extras] async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""] grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] @@ -1153,6 +1236,7 @@ version = "2.38.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"}, {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"}, @@ -1177,6 +1261,7 @@ version = "2.4.3" description = "Google Cloud API client core library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_cloud_core-2.4.3-py2.py3-none-any.whl", hash = "sha256:5130f9f4c14b4fafdff75c79448f9495cfade0d8775facf1b09c3bf67e027f6e"}, {file = "google_cloud_core-2.4.3.tar.gz", hash = "sha256:1fab62d7102844b278fe6dead3af32408b1df3eb06f5c7e8634cbd40edc4da53"}, @@ -1195,6 +1280,7 @@ version = "2.20.2" description = "Google Cloud Datastore API client library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_cloud_datastore-2.20.2-py2.py3-none-any.whl", hash = "sha256:d2190180343b807d4aa3b0b3bb837606349b71e5e74e29aa9009c0ae38c0b6a0"}, {file = "google_cloud_datastore-2.20.2.tar.gz", hash = "sha256:9665d009729d9551329d9476f4d5bda9c11d3469243ea8a2c0d9490b65aa899f"}, @@ -1212,47 +1298,49 @@ libcst = ["libcst (>=0.2.5)"] [[package]] name = "google-cloud-pubsub" -version = "2.28.0" +version = "2.29.0" description = "Google Cloud Pub/Sub API client library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "google_cloud_pubsub-2.28.0-py2.py3-none-any.whl", hash = "sha256:76b41a322b43bc845fb06ffe238758726324d957d0161bae3ff4b14339da144b"}, - {file = "google_cloud_pubsub-2.28.0.tar.gz", hash = "sha256:904e894b4e15121521077ac85c9aa8f4e7b8517bc5fb409ddb2aac8df1a02b3c"}, + {file = "google_cloud_pubsub-2.29.0-py2.py3-none-any.whl", hash = "sha256:3ccc76ae623e408c7a80f2f81bfd3ab9dca1d61231cc2a063d569d021449481a"}, + {file = "google_cloud_pubsub-2.29.0.tar.gz", hash = "sha256:b820f8d410c96ad87b8da79c696b979e1a182a170d0c0602626f5b9d8cbf21ee"}, ] [package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} -google-auth = ">=2.14.1,<3.0.0dev" -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -grpcio = ">=1.51.3,<2.0dev" +google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0", extras = ["grpc"]} +google-auth = ">=2.14.1,<3.0.0" +grpc-google-iam-v1 = ">=0.12.4,<1.0.0" +grpcio = ">=1.51.3,<2.0.0" grpcio-status = ">=1.33.2" opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} proto-plus = [ - {version = ">=1.25.0,<2.0.0dev", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\" and python_version < \"3.13\""}, + {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.22.2,<2.0.0", markers = "python_version >= \"3.11\" and python_version < \"3.13\""}, ] -protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0dev" +protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" [package.extras] libcst = ["libcst (>=0.3.10)"] [[package]] name = "google-cloud-storage" -version = "2.19.0" +version = "3.1.0" description = "Google Cloud Storage API client library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba"}, - {file = "google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2"}, + {file = "google_cloud_storage-3.1.0-py2.py3-none-any.whl", hash = "sha256:eaf36966b68660a9633f03b067e4a10ce09f1377cae3ff9f2c699f69a81c66c6"}, + {file = "google_cloud_storage-3.1.0.tar.gz", hash = "sha256:944273179897c7c8a07ee15f2e6466a02da0c7c4b9ecceac2a26017cb2972049"}, ] [package.dependencies] google-api-core = ">=2.15.0,<3.0.0dev" google-auth = ">=2.26.1,<3.0dev" -google-cloud-core = ">=2.3.0,<3.0dev" +google-cloud-core = ">=2.4.2,<3.0dev" google-crc32c = ">=1.0,<2.0dev" google-resumable-media = ">=2.7.2" requests = ">=2.18.0,<3.0.0dev" @@ -1267,6 +1355,7 @@ version = "2.19.2" description = "Google Cloud Tasks API client library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_cloud_tasks-2.19.2-py3-none-any.whl", hash = "sha256:898bf75020ead4dfb836a43d2ad666389ceeec1a4beb3cb65cc25b9accc289bb"}, {file = "google_cloud_tasks-2.19.2.tar.gz", hash = "sha256:276b47e85f4825923a778d543fc0735e4b24be45f73fa7d964ad1655402d07dc"}, @@ -1278,7 +1367,7 @@ google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" grpc-google-iam-v1 = ">=0.14.0,<1.0.0" proto-plus = [ {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, + {version = ">=1.22.3,<2.0.0"}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" @@ -1288,6 +1377,7 @@ version = "1.7.0" description = "A python wrapper of the C library 'Google CRC32C'" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:18f1dfc6baeb3b28b1537d54b3622363352f75fcb2d4b6ffcc37584fe431f122"}, {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:732378dc4ca08953eac0d13d1c312d99a54d5b483c90b4a5a536132669ed1c24"}, @@ -1309,6 +1399,7 @@ files = [ {file = "google_crc32c-1.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:6a40522958040051c755a173eb98c05ad4d64a6dd898888c3e5ccca2d1cbdcdc"}, {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f714fe5cdf5007d7064c57cf7471a99e0cbafda24ddfa829117fc3baafa424f7"}, {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f04e58dbe1bf0c9398e603a9be5aaa09e0ba7eb022a3293195d8749459a01069"}, + {file = "google_crc32c-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:e545b51ddf97f604d30114f7c23eecaf4c06cd6c023ff1ae0b80dcd99af32833"}, {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:364067b063664dd8d1fec75a3fe85edf05c46f688365269beccaf42ef5dfe889"}, {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1b0d6044799f6ac51d1cc2decb997280a83c448b3bef517a54b57a3b71921c0"}, {file = "google_crc32c-1.7.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:02bc3295d26cd7666521fd6d5b7b93923ae1eb4417ddd3bc57185a5881ad7b96"}, @@ -1333,6 +1424,7 @@ version = "2.7.2" description = "Utilities for Google Media Downloads and Resumable Uploads" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa"}, {file = "google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0"}, @@ -1351,6 +1443,7 @@ version = "1.69.2" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212"}, {file = "googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f"}, @@ -1369,6 +1462,7 @@ version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -1444,6 +1538,7 @@ files = [ {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] +markers = {main = "platform_python_implementation == \"CPython\""} [package.extras] docs = ["Sphinx", "furo"] @@ -1455,6 +1550,7 @@ version = "0.14.2" description = "IAM API client library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "grpc_google_iam_v1-0.14.2-py3-none-any.whl", hash = "sha256:a3171468459770907926d56a440b2bb643eec1d7ba215f48f3ecece42b4d8351"}, {file = "grpc_google_iam_v1-0.14.2.tar.gz", hash = "sha256:b3e1fc387a1a329e41672197d0ace9de22c78dd7d215048c4c78712073f7bd20"}, @@ -1471,6 +1567,7 @@ version = "1.71.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "grpcio-1.71.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:c200cb6f2393468142eb50ab19613229dcc7829b5ccee8b658a36005f6669fdd"}, {file = "grpcio-1.71.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b2266862c5ad664a380fbbcdbdb8289d71464c42a8c29053820ee78ba0119e5d"}, @@ -1534,6 +1631,7 @@ version = "1.71.0" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68"}, {file = "grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968"}, @@ -1550,6 +1648,7 @@ version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, @@ -1571,6 +1670,7 @@ version = "0.1.12" description = "An HTML Minifier" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "htmlmin-0.1.12.tar.gz", hash = "sha256:50c1ef4630374a5d723900096a961cff426dff46b48f34d194a81bbe14eca178"}, ] @@ -1581,6 +1681,7 @@ version = "1.4.0" description = "A mocking library for requests." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "httmock-1.4.0-py3-none-any.whl", hash = "sha256:13e6c63f135a928e15d386af789a2890efb03e0e280f29bdc9961f3f0dc34cb9"}, {file = "httmock-1.4.0.tar.gz", hash = "sha256:44eaf4bb59cc64cd6f5d8bf8700b46aa3097cc5651b9bc85c527dfbc71792f41"}, @@ -1595,6 +1696,7 @@ version = "10.0" description = "Human friendly output for text interfaces using Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] files = [ {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, @@ -1605,13 +1707,14 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve [[package]] name = "humanize" -version = "4.12.1" +version = "4.12.2" description = "Python humanize utilities" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "humanize-4.12.1-py3-none-any.whl", hash = "sha256:86014ca5c52675dffa1d404491952f1f5bf03b07c175a51891a343daebf01fea"}, - {file = "humanize-4.12.1.tar.gz", hash = "sha256:1338ba97415c96556758a6e2f65977ed406dddf4620d4c6db9bbdfd07f0f1232"}, + {file = "humanize-4.12.2-py3-none-any.whl", hash = "sha256:e4e44dced598b7e03487f3b1c6fd5b1146c30ea55a110e71d5d4bca3e094259e"}, + {file = "humanize-4.12.2.tar.gz", hash = "sha256:ce0715740e9caacc982bb89098182cf8ded3552693a433311c6a4ce6f4e12a2c"}, ] [package.extras] @@ -1623,6 +1726,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1637,6 +1741,7 @@ version = "8.6.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, @@ -1646,12 +1751,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1660,6 +1765,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -1671,6 +1777,7 @@ version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, @@ -1686,6 +1793,7 @@ version = "2.2.0" description = "Safely pass data to untrusted environments and back." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, @@ -1697,6 +1805,7 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -1714,6 +1823,7 @@ version = "1.0.1" description = "JSON Matching Expressions" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -1725,6 +1835,7 @@ version = "1.15.4" description = "JavaScript unobfuscator and beautifier." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528"}, {file = "jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592"}, @@ -1740,6 +1851,7 @@ version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, @@ -1754,6 +1866,7 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -1765,6 +1878,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -1786,6 +1900,7 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -1800,6 +1915,7 @@ version = "1.5.6" description = "Implementation of JOSE Web standards" optional = false python-versions = ">= 3.8" +groups = ["main"] files = [ {file = "jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789"}, {file = "jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039"}, @@ -1815,6 +1931,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1885,6 +2002,7 @@ version = "3.26.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c"}, {file = "marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6"}, @@ -1904,6 +2022,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1915,6 +2034,7 @@ version = "5.2.0" description = "Rolling backport of unittest.mock for all Pythons" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f"}, {file = "mock-5.2.0.tar.gz", hash = "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0"}, @@ -1931,6 +2051,7 @@ version = "5.1.1" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "moto-5.1.1-py3-none-any.whl", hash = "sha256:615904d6210431950a59a2bdec365d60e791eacbe3dd07a3a5d742c88ef847dd"}, {file = "moto-5.1.1.tar.gz", hash = "sha256:5b25dbc62cccd9f36ef062c870db49d976b241129024fab049e2d3d1296e2a57"}, @@ -1976,6 +2097,7 @@ version = "1.15.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, @@ -2028,6 +2150,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -2039,6 +2162,7 @@ version = "1.31.0" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_api-1.31.0-py3-none-any.whl", hash = "sha256:145b72c6c16977c005c568ec32f4946054ab793d8474a17fd884b0397582c5f2"}, {file = "opentelemetry_api-1.31.0.tar.gz", hash = "sha256:d8da59e83e8e3993b4726e4c1023cd46f57c4d5a73142e239247e7d814309de1"}, @@ -2054,6 +2178,7 @@ version = "1.31.0" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_sdk-1.31.0-py3-none-any.whl", hash = "sha256:97c9a03865e69723725fb64fe04343a488c3e61e684eb804bd7d6da2215dfc60"}, {file = "opentelemetry_sdk-1.31.0.tar.gz", hash = "sha256:452d7d5b3c1db2e5e4cb64abede0ddd20690cb244a559c73a59652fdf6726070"}, @@ -2070,6 +2195,7 @@ version = "0.52b0" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "opentelemetry_semantic_conventions-0.52b0-py3-none-any.whl", hash = "sha256:4d843652ae1f9f3c0d4d8df0bfef740627c90495ac043fc33f0a04bad3b606e2"}, {file = "opentelemetry_semantic_conventions-0.52b0.tar.gz", hash = "sha256:f8bc8873a69d0a2f45746c31980baad2bb10ccee16b1816497ccf99417770386"}, @@ -2085,6 +2211,7 @@ version = "4.1.0" description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, @@ -2099,6 +2226,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -2110,6 +2238,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -2121,6 +2250,7 @@ version = "1.0.0" description = "Wkhtmltopdf python wrapper to convert html to pdf using the webkit rendering engine and qt" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pdfkit-1.0.0-py2-none-any.whl", hash = "sha256:cc122e5aed594198ff7aaa566f2950d2163763576ab891c161bb1f6c630f5a8e"}, {file = "pdfkit-1.0.0-py3-none-any.whl", hash = "sha256:a7a4ca0d978e44fa8310c4909f087052430a6e8e0b1dd7ceef657f139789f96f"}, @@ -2133,6 +2263,7 @@ version = "1.7.1" description = "Python style guide checker" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pep8-1.7.1-py2.py3-none-any.whl", hash = "sha256:b22cfae5db09833bb9bd7c8463b53e1a9c9b39f12e304a8d0bba729c501827ee"}, {file = "pep8-1.7.1.tar.gz", hash = "sha256:fe249b52e20498e59e0b5c5256aa52ee99fc295b26ec9eaa85776ffdb9fe6374"}, @@ -2144,6 +2275,7 @@ version = "1.3.2" description = "Pika Python AMQP Client Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f"}, {file = "pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f"}, @@ -2160,6 +2292,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -2176,6 +2309,7 @@ version = "1.51.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "playwright-1.51.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bcaaa3d5d73bda659bfb9ff2a288b51e85a91bd89eda86eaf8186550973e416a"}, {file = "playwright-1.51.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e0ae6eb44297b24738e1a6d9c580ca4243b4e21b7e65cf936a71492c08dd0d4"}, @@ -2196,6 +2330,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -2211,6 +2346,7 @@ version = "1.26.1" description = "Beautiful, Pythonic protocol buffers" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"}, {file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"}, @@ -2228,6 +2364,7 @@ version = "5.29.3" description = "" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, @@ -2248,6 +2385,7 @@ version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -2259,6 +2397,7 @@ version = "0.4.1" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, @@ -2273,10 +2412,12 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +markers = {dev = "platform_python_implementation != \"PyPy\""} [[package]] name = "pyee" @@ -2284,6 +2425,7 @@ version = "12.1.1" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef"}, {file = "pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3"}, @@ -2293,7 +2435,7 @@ files = [ typing-extensions = "*" [package.extras] -dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "sphinx", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"] +dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio ; python_version >= \"3.4\"", "pytest-trio ; python_version >= \"3.7\"", "sphinx", "toml", "tox", "trio", "trio ; python_version > \"3.6\"", "trio-typing ; python_version > \"3.6\"", "twine", "twisted", "validate-pyproject[all]"] [[package]] name = "pylint" @@ -2301,6 +2443,7 @@ version = "3.3.5" description = "python code static checker" optional = false python-versions = ">=3.9.0" +groups = ["dev"] files = [ {file = "pylint-3.3.5-py3-none-any.whl", hash = "sha256:7cb170929a371238530b2eeea09f5f28236d106b70308c3d46a9c0cf11634633"}, {file = "pylint-3.3.5.tar.gz", hash = "sha256:38d0f784644ed493d91f76b5333a0e370a1c1bc97c22068a77523b4bf1e82c31"}, @@ -2325,6 +2468,7 @@ version = "1.1.0" description = "Pylint plugin which adds linter error for relatives imports.Read more about imports at https://peps.python.org/pep-0008/#imports" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pylint_absolute_imports-1.1.0-py3-none-any.whl", hash = "sha256:3fa7b73481c50cb7e7892d5eeaecf8108ad0ed06d9a545b5c620d20250a1f727"}, {file = "pylint_absolute_imports-1.1.0.tar.gz", hash = "sha256:3ea2bb868626c4662b76cb84d541af4e4cc4a355872fa28def72ad4439b31717"}, @@ -2339,6 +2483,7 @@ version = "0.1.3" description = "McCabe complexity checker as a PyLint plugin" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pylint-mccabe-0.1.3.tar.gz", hash = "sha256:f3628affbc6064c08477243915f6752f3ef59fb82803b00be92f30d0ef7bbf29"}, ] @@ -2354,6 +2499,8 @@ version = "3.5.4" description = "A python implementation of GNU readline." optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, @@ -2368,6 +2515,7 @@ version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, @@ -2388,6 +2536,7 @@ version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, @@ -2406,6 +2555,7 @@ version = "1.3.0" description = "A set of py.test fixtures to test Flask applications." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "pytest-flask-1.3.0.tar.gz", hash = "sha256:58be1c97b21ba3c4d47e0a7691eb41007748506c36bf51004f78df10691fa95e"}, {file = "pytest_flask-1.3.0-py3-none-any.whl", hash = "sha256:c0e36e6b0fddc3b91c4362661db83fa694d1feb91fa505475be6732b5bc8c253"}, @@ -2425,6 +2575,7 @@ version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, @@ -2442,6 +2593,7 @@ version = "1.0.0" description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, @@ -2461,6 +2613,7 @@ version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, @@ -2481,6 +2634,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2495,6 +2649,7 @@ version = "0.7.3" description = "Python library for the snappy compression library from Google" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "python_snappy-0.7.3-py3-none-any.whl", hash = "sha256:074c0636cfcd97e7251330f428064050ac81a52c62ed884fc2ddebbb60ed7f50"}, {file = "python_snappy-0.7.3.tar.gz", hash = "sha256:40216c1badfb2d38ac781ecb162a1d0ec40f8ee9747e610bcfefdfa79486cee3"}, @@ -2509,6 +2664,7 @@ version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, @@ -2520,6 +2676,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2582,6 +2739,7 @@ version = "5.2.1" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, @@ -2597,6 +2755,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -2613,6 +2772,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -2716,6 +2876,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2737,6 +2898,7 @@ version = "0.25.7" description = "A utility library for mocking out the `requests` Python library." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "responses-0.25.7-py3-none-any.whl", hash = "sha256:92ca17416c90fe6b35921f52179bff29332076bb32694c0df02dcac2c6bc043c"}, {file = "responses-0.25.7.tar.gz", hash = "sha256:8ebae11405d7a5df79ab6fd54277f6f2bc29b2d002d0dd2d5c632594d1ddcedb"}, @@ -2748,7 +2910,7 @@ requests = ">=2.30.0,<3.0" urllib3 = ">=1.25.10,<3.0" [package.extras] -tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli", "tomli-w", "types-PyYAML", "types-requests"] +tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "tomli ; python_version < \"3.11\"", "tomli-w", "types-PyYAML", "types-requests"] [[package]] name = "rpds-py" @@ -2756,6 +2918,7 @@ version = "0.23.1" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, @@ -2868,6 +3031,7 @@ version = "4.9" description = "Pure-Python RSA implementation" optional = false python-versions = ">=3.6,<4" +groups = ["main"] files = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, @@ -2882,6 +3046,7 @@ version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, @@ -2909,6 +3074,7 @@ version = "0.11.4" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "s3transfer-0.11.4-py3-none-any.whl", hash = "sha256:ac265fa68318763a03bf2dc4f39d5cbd6a9e178d81cc9483ad27da33637e320d"}, {file = "s3transfer-0.11.4.tar.gz", hash = "sha256:559f161658e1cf0a911f45940552c696735f5c74e64362e515f333ebed87d679"}, @@ -2926,6 +3092,7 @@ version = "1.2.1" description = "A shared library for SDC services that use JWT with JWE" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "sdc_cryptography-1.2.1-py3-none-any.whl", hash = "sha256:2b87da178c0d9c6a8add4e2afbe81139bbb056598af0cda61b52f421fb29451f"}, {file = "sdc_cryptography-1.2.1.tar.gz", hash = "sha256:cea2de62e65940efb72bfd3ed677b1e685678521a52eb4c06769ffce506f5847"}, @@ -2942,19 +3109,20 @@ version = "76.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "setuptools-76.1.0-py3-none-any.whl", hash = "sha256:34750dcb17d046929f545dec9b8349fe42bf4ba13ddffee78428aec422dbfb73"}, {file = "setuptools-76.1.0.tar.gz", hash = "sha256:4959b9ad482ada2ba2320c8f1a8d8481d4d8d668908a7a1b84d987375cd7f5bd"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "simplejson" @@ -2962,6 +3130,7 @@ version = "3.20.1" description = "Simple, fast, extensible JSON encoder/decoder for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5" +groups = ["main"] files = [ {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f5272b5866b259fe6c33c4a8c5073bf8b359c3c97b70c298a2f09a69b52c7c41"}, {file = "simplejson-3.20.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5c0de368f3052a59a1acf21f8b2dd28686a9e4eba2da7efae7ed9554cb31e7bc"}, @@ -3081,6 +3250,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "dev"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -3092,6 +3262,7 @@ version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, @@ -3103,6 +3274,7 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -3110,13 +3282,14 @@ files = [ [[package]] name = "structlog" -version = "24.4.0" +version = "25.2.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "structlog-24.4.0-py3-none-any.whl", hash = "sha256:597f61e80a91cc0749a9fd2a098ed76715a1c8a01f73e336b746504d1aad7610"}, - {file = "structlog-24.4.0.tar.gz", hash = "sha256:b27bfecede327a6d2da5fbc96bd859f114ecc398a6389d664f62085ee7ae6fc4"}, + {file = "structlog-25.2.0-py3-none-any.whl", hash = "sha256:0fecea2e345d5d491b72f3db2e5fcd6393abfc8cd06a4851f21fcd4d1a99f437"}, + {file = "structlog-25.2.0.tar.gz", hash = "sha256:d9f9776944207d1035b8b26072b9b140c63702fd7aa57c2f85d28ab701bd8e92"}, ] [package.extras] @@ -3131,6 +3304,7 @@ version = "2.5.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, @@ -3145,6 +3319,7 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -3156,6 +3331,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -3177,6 +3353,7 @@ version = "5.5.0.20240820" description = "Typing stubs for cachetools" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types-cachetools-5.5.0.20240820.tar.gz", hash = "sha256:b888ab5c1a48116f7799cd5004b18474cd82b5463acb5ffb2db2fc9c7b053bc0"}, {file = "types_cachetools-5.5.0.20240820-py3-none-any.whl", hash = "sha256:efb2ed8bf27a4b9d3ed70d33849f536362603a90b8090a328acf0cd42fda82e2"}, @@ -3188,6 +3365,7 @@ version = "1.17.0.20250319" description = "Typing stubs for cffi" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "types_cffi-1.17.0.20250319-py3-none-any.whl", hash = "sha256:5e95f0f10d3f2fd0a8a0a10f6b8b1e0e6ff47796ad2fdd4302b5e514b64d6af4"}, {file = "types_cffi-1.17.0.20250319.tar.gz", hash = "sha256:66b0656818e5363f136a0a361f28e41330b55f83d390b14c6bf56026f57b3603"}, @@ -3202,6 +3380,7 @@ version = "24.1.0.20240722" description = "Typing stubs for pyOpenSSL" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39"}, {file = "types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54"}, @@ -3217,6 +3396,7 @@ version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, @@ -3228,6 +3408,7 @@ version = "2024.2.0.20241221" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types_pytz-2024.2.0.20241221-py3-none-any.whl", hash = "sha256:8fc03195329c43637ed4f593663df721fef919b60a969066e22606edf0b53ad5"}, {file = "types_pytz-2024.2.0.20241221.tar.gz", hash = "sha256:06d7cde9613e9f7504766a0554a270c369434b50e00975b3a4a0f6eed0f2c1a9"}, @@ -3239,6 +3420,7 @@ version = "6.0.12.20241230" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, @@ -3250,6 +3432,7 @@ version = "4.6.0.20241004" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e"}, {file = "types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed"}, @@ -3265,6 +3448,7 @@ version = "2.32.0.20250306" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b"}, {file = "types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1"}, @@ -3279,6 +3463,7 @@ version = "76.0.0.20250313" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e"}, {file = "types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e"}, @@ -3293,6 +3478,7 @@ version = "3.20.0.20250318" description = "Typing stubs for simplejson" optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "types_simplejson-3.20.0.20250318-py3-none-any.whl", hash = "sha256:e8c9cdb06b566b6ca1c7bf3d4c97fbd69a6f6a5aea760ef127f31e638a094c26"}, {file = "types_simplejson-3.20.0.20250318.tar.gz", hash = "sha256:5ada2caa2f76826a90b97985f7b0caf55088a23ed4eb9c39fc1f5cb00b985e09"}, @@ -3304,6 +3490,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -3315,6 +3502,7 @@ version = "1.0.1" description = "Python port of Browserscope's user agent parser" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "ua_parser-1.0.1-py3-none-any.whl", hash = "sha256:b059f2cb0935addea7e551251cbbf42e9a8872f86134163bc1a4f79e0945ffea"}, {file = "ua_parser-1.0.1.tar.gz", hash = "sha256:f9d92bf19d4329019cef91707aecc23c6d65143ad7e29a233f0580fb0d15547d"}, @@ -3334,6 +3522,7 @@ version = "0.18.0.post1" description = "Precompiled rules for User Agent Parser" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "ua_parser_builtins-0.18.0.post1-py3-none-any.whl", hash = "sha256:eb4f93504040c3a990a6b0742a2afd540d87d7f9f05fd66e94c101db1564674d"}, ] @@ -3344,13 +3533,14 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -3361,6 +3551,7 @@ version = "2.0.28" description = "The uWSGI server" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "uwsgi-2.0.28.tar.gz", hash = "sha256:79ca1891ef2df14508ab0471ee8c0eb94bd2d51d03f32f90c4bbe557ab1e99d0"}, ] @@ -3371,6 +3562,7 @@ version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" +groups = ["main", "dev"] files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -3388,6 +3580,7 @@ version = "1.17.2" description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, @@ -3476,6 +3669,7 @@ version = "3.2.1" description = "Form validation and rendering for Python web development." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4"}, {file = "wtforms-3.2.1.tar.gz", hash = "sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682"}, @@ -3493,6 +3687,7 @@ version = "0.14.2" description = "Makes working with XML feel like you are working with JSON" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"}, {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"}, @@ -3504,17 +3699,18 @@ version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [[package]] @@ -3523,6 +3719,7 @@ version = "5.0" description = "Very basic event publishing system" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, @@ -3541,6 +3738,7 @@ version = "7.2" description = "Interfaces for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -3595,6 +3793,7 @@ version = "0.23.0" description = "Zstandard bindings for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9"}, {file = "zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880"}, @@ -3702,6 +3901,6 @@ cffi = {version = ">=1.11", optional = true, markers = "platform_python_implemen cffi = ["cffi (>=1.11)"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.12.6" -content-hash = "d93aac1b6f81afdafbdc8bd7a206e596c5e3d105475899ad81bbbae14ec30ec5" +content-hash = "e1060bd4b6ef6af5bb8ca80edee4283c21beb06a03ee6b64c78fa453f2c50670" diff --git a/pyproject.toml b/pyproject.toml index e2337d2e3e..780c2161c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ pika = "^1.3.2" pyyaml = "^6.0.1" requests = "^2.32.0" sdc-cryptography = "^1.2.1" -structlog = "^24.1.0" +structlog = "^25.2.0" ua-parser = "^1.0.0" blinker = "^1.7.0" boto3 = "^1.34.151" @@ -70,7 +70,7 @@ humanize = "^4.9.0" flask-talisman = "^1.1.0" marshmallow = "^3.21.3" python-snappy = "^0.7.1" -google-cloud-storage = "^2.17.0" +google-cloud-storage = "^3.1.0" jsonpointer = "^3.0" redis = "^5.0.8" flask-compress = "^1.14" diff --git a/tests/app/submitter/conftest.py b/tests/app/submitter/conftest.py index 96e17103bd..75316125ba 100644 --- a/tests/app/submitter/conftest.py +++ b/tests/app/submitter/conftest.py @@ -2,7 +2,6 @@ import pytest from google.cloud.storage import Blob -from google.resumable_media import InvalidResponse from mock import MagicMock from requests import Response @@ -133,9 +132,7 @@ def gcs_blob_with_retry(mocker): ) response_200.status_code = 200 - mock_transport_request = mocker.Mock( - side_effect=[InvalidResponse(response_503), response_200] - ) + mock_transport_request = mocker.Mock(side_effect=[response_503, response_200]) mock_transport = mocker.Mock() mock_transport.request = mock_transport_request blob._get_transport = mocker.Mock( # pylint: disable=protected-access From 094102f31b305db1b852fab0755a916b5b683bf0 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:28:30 +0100 Subject: [PATCH 521/567] Schemas v5.29.0 (#1640) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 79c74de75f..5cfd5a180a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.28.0 +v5.29.0 From f7e6eb39f0ce365f4bc9bb2cc8d56778f5fcaa70 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 2 Apr 2025 10:23:17 +0100 Subject: [PATCH 522/567] Schemas v5.30.0 (#1641) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 5cfd5a180a..2877a1268a 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.29.0 +v5.30.0 From e43e9e1f82f3a7e87579a8b0963785c0a309cd8f Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:02:17 +0100 Subject: [PATCH 523/567] Use python:3.12-slim-bookworm (#1635) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index bc9d6bfaf7..412079612d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-slim-bullseye +FROM python:3.12-slim-bookworm EXPOSE 5000 From 706d7ec10d8f827e4ec421fa2168e85fa55ba856 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:00:25 +0100 Subject: [PATCH 524/567] Schemas v5.31.0 (#1644) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 2877a1268a..c11f85f3bf 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.30.0 +v5.31.0 From f3cc41919df7b64a1d5cb88730539c55b7c1ccee Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 22 Apr 2025 14:36:56 +0100 Subject: [PATCH 525/567] Removed ruff lint rules EM101 & EM1021 --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 780c2161c2..aa097f1c3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,8 +121,6 @@ extend-ignore = [ "ARG002", # Unused method argument "ARG004", # Unused static method argument: `kwargs "N818", # Exception name should be named with an Error suffix - "EM101", # Exception must not use a string literal, assign to variable first - "EM102", # EM102 Exception must not use an f-string literal, assign to variable first "UP032", # Use f-string instead of `format` call "UP018", # Unnecessary {literal_type} call (rewrite as a literal) "UP015", # Unnecessary open mode parameters From f111f42f66a14d5568a757ba9bfd4876f243ead5 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 22 Apr 2025 14:38:14 +0100 Subject: [PATCH 526/567] Resolved ruff lint error messages EM101 & EM102 --- app/authentication/authenticator.py | 3 +- app/authentication/user.py | 3 +- app/authentication/user_id_generator.py | 6 ++- app/data_models/answer_store.py | 3 +- app/data_models/list_store.py | 3 +- app/data_models/supplementary_data_store.py | 3 +- app/forms/field_handlers/dropdown_handler.py | 3 +- app/forms/field_handlers/select_handlers.py | 3 +- app/forms/questionnaire_form.py | 4 +- app/forms/validators.py | 8 ++-- app/questionnaire/placeholder_renderer.py | 3 +- app/questionnaire/placeholder_transforms.py | 3 +- app/questionnaire/rules/operations.py | 3 +- app/questionnaire/rules/rule_evaluator.py | 3 +- app/questionnaire/rules/utils.py | 3 +- app/questionnaire/value_source_resolver.py | 10 +++-- app/routes/questionnaire.py | 3 +- app/routes/session.py | 9 +++-- app/secrets.py | 3 +- app/services/supplementary_data.py | 7 ++-- app/settings.py | 6 ++- app/setup.py | 42 +++++++++++++------- app/storage/datastore.py | 3 +- app/storage/storage.py | 3 +- app/storage/storage_encryption.py | 9 +++-- app/utilities/metadata_parser_v2.py | 9 +++-- app/utilities/supplementary_data_parser.py | 15 ++++--- app/views/handlers/block.py | 3 +- app/views/handlers/block_factory.py | 9 +++-- app/views/handlers/individual_response.py | 3 +- app/views/handlers/section.py | 3 +- app/views/handlers/submit_questionnaire.py | 3 +- 32 files changed, 128 insertions(+), 66 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 5e1dcc1475..2e7e6ded33 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -179,7 +179,8 @@ def create_session_questionnaire_store( def decrypt_token(encrypted_token: str | None) -> dict[str, Any]: if not encrypted_token: - raise NoTokenException("Please provide a token") + missing_token = "Please provide a token" + raise NoTokenException(missing_token) logger.debug("decrypting token") decrypted_token: dict[str, Any] = decrypt( diff --git a/app/authentication/user.py b/app/authentication/user.py index b32af75709..3fb8464c61 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -7,4 +7,5 @@ def __init__(self, user_id: str | None, user_ik: str | None) -> None: self.user_id = user_id self.user_ik = user_ik else: - raise ValueError("No user_id or user_ik found in session") + missing_user_session_error = "No user_id or user_ik found in session" + raise ValueError(missing_user_session_error) diff --git a/app/authentication/user_id_generator.py b/app/authentication/user_id_generator.py index 01d0fa8b00..601739ec63 100644 --- a/app/authentication/user_id_generator.py +++ b/app/authentication/user_id_generator.py @@ -13,9 +13,11 @@ class UserIDGenerator: def __init__(self, iterations: int, user_id_salt: str, user_ik_salt: str) -> None: if user_id_salt is None: - raise ValueError("user_id_salt is required") + user_id_salt_error = "user_id_salt is required" + raise ValueError(user_id_salt_error) if user_ik_salt is None: - raise ValueError("user_ik_salt is required") + user_ik_salt_error = "user_ik_salt is required" + raise ValueError(user_ik_salt_error) self._iterations = iterations self._user_id_salt = user_id_salt diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index d79796a92e..8bb3875752 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -54,8 +54,9 @@ def _build_map( @staticmethod def _validate(answer: Answer) -> None: if not isinstance(answer, Answer): + type_error_msg = f"Method only supports Answer argument type, found type: {type(answer)}" raise TypeError( - f"Method only supports Answer argument type, found type: {type(answer)}" + type_error_msg ) @property diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index e6c3dc487a..fa03530c9c 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -82,8 +82,9 @@ def first(self) -> str: try: return self.items[0] except IndexError as e: + error_msg_empty_list = f"unable to access first item in list, list '{self.name}' is empty" raise IndexError( - f"unable to access first item in list, list '{self.name}' is empty" + error_msg_empty_list ) from e @property diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index a162e31f33..d15bc810f4 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -144,8 +144,9 @@ def _resolve_value( return None if not isinstance(value, Mapping): # if value is not None, and also not index able, raise an error + invalid_selector_error = f"Cannot use the selector `{selector}` on non-nested data" raise InvalidSupplementaryDataSelector( - f"Cannot use the selector `{selector}` on non-nested data" + invalid_selector_error ) value = value.get(selector) diff --git a/app/forms/field_handlers/dropdown_handler.py b/app/forms/field_handlers/dropdown_handler.py index 6feec04a9f..ea53df8272 100644 --- a/app/forms/field_handlers/dropdown_handler.py +++ b/app/forms/field_handlers/dropdown_handler.py @@ -19,7 +19,8 @@ def choices(self) -> Sequence[Choice]: self._build_dynamic_choices() + self._build_static_choices() ) if not _choices: - raise InvalidSchemaConfigurationException("No dynamic or static choices") + no_choices_msg = "No dynamic or static choices" + raise InvalidSchemaConfigurationException(no_choices_msg) return [ Choice("", self._get_placeholder_text()), diff --git a/app/forms/field_handlers/select_handlers.py b/app/forms/field_handlers/select_handlers.py index fb56a3b172..d194ac2b04 100644 --- a/app/forms/field_handlers/select_handlers.py +++ b/app/forms/field_handlers/select_handlers.py @@ -17,7 +17,8 @@ class SelectHandlerBase(FieldHandler): def choices(self) -> Sequence[ChoiceType]: _choices = self._build_dynamic_choices() + self._build_static_choices() if not _choices: - raise InvalidSchemaConfigurationException("No dynamic or static choices") + no_choices_msg = "No dynamic or static choices" + raise InvalidSchemaConfigurationException(no_choices_msg) return _choices @property diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index e11aca2700..8ab0ef5e85 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -337,7 +337,9 @@ def _get_period_range_for_single_date( ) if not min_period_date or not max_period_date: - raise ValueError("Period range must have a start and end date") + + invalid_period_range = "Period range must have a start and end date" + raise ValueError(invalid_period_range) # Work out the largest possible range, for date range question period_range = max_period_date - min_period_date diff --git a/app/forms/validators.py b/app/forms/validators.py index 80e3e86cac..1a9b2eda38 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -425,9 +425,10 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: + invalid_mutiple_conditions = "There are multiple conditions, but equals is not one of them. " + "We only support <= and >=" raise ValueError( - "There are multiple conditions, but equals is not one of them. " - "We only support <= and >=" + invalid_mutiple_conditions ) from exc condition = f"{conditions[0]} or equals" @@ -470,7 +471,8 @@ def _is_valid( if condition == "less than or equals": return total <= target_total, "TOTAL_SUM_NOT_LESS_THAN_OR_EQUALS" - raise NotImplementedError(f"Condition '{condition}' is not implemented") + invalid_condition = f"Condition '{condition}' is not implemented" + raise NotImplementedError(invalid_condition) class MutuallyExclusiveCheck: diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index afcd146846..4f9abc0040 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -97,7 +97,8 @@ def render_placeholder( placeholder_data["text"] = plural_forms[plural_form_key] if "text" not in placeholder_data and "placeholders" not in placeholder_data: - raise ValueError("No placeholder found to render") + placeholder_not_found = "No placeholder found to render" + raise ValueError(placeholder_not_found) transformed_values = placeholder_parser(placeholder_data["placeholders"]) formatted_placeholder_data: str = placeholder_data["text"].format( diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 2876b42dfd..5fb677ae7c 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -326,7 +326,8 @@ def get_ordinal_indicator(self, number_to_format: int) -> str: 19: "eg", }.get(number_to_format, "fed") - raise NotImplementedError(f"Language code '{self.language}' not implemented.") + invalid_lang_code_error = f"Language code '{self.language}' not implemented." + raise NotImplementedError(invalid_lang_code_error) def first_non_empty_item(self, items: Sequence[str]) -> str: """ diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index a02fcd5019..ae3b0f1d96 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -146,8 +146,9 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: + value_error_msg = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" raise ValueError( - "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" + value_error_msg ) days_difference = ( diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index dd82915afd..5ebb9741d0 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -57,8 +57,9 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operands = rule[operator_name] if not isinstance(operands, Sequence): + invalid_rule_msg = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" raise TypeError( - f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" + invalid_rule_msg ) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/rules/utils.py b/app/questionnaire/rules/utils.py index ea7d268f3f..1ea4ed3c1e 100644 --- a/app/questionnaire/rules/utils.py +++ b/app/questionnaire/rules/utils.py @@ -33,4 +33,5 @@ def parse_datetime(date_string: str | None) -> datetime | None: try: return parse_iso_8601_datetime(date_string) except ValueError as ex: - raise ValueError(f"'{date_string}' is not in a valid date format") from ex + invalid_date_format = f"'{date_string}' is not in a valid date format" + raise ValueError(invalid_date_format) from ex diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index defceff9d2..9b5bdbfb37 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -98,8 +98,9 @@ def _resolve_list_item_id_for_value_source( if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: + missing_location = "list_item_selector source location used without location" raise InvalidLocationException( - "list_item_selector source location used without location" + missing_location ) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -211,7 +212,8 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - raise ValueError("location is required to resolve block progress") + missing_required_location = "location is required to resolve block progress" + raise ValueError(missing_required_location) if not self._is_block_on_path(identifier): return None @@ -316,8 +318,8 @@ def get_calculation_operator( ) -> Callable[[Iterable[IntOrDecimal]], IntOrDecimal]: if calculation_type == "sum": return sum - - raise NotImplementedError(f"Invalid calculation_type: {calculation_type}") + invalid_calculation_type = f"Invalid calculation_type: {calculation_type}" + raise NotImplementedError(invalid_calculation_type) def resolve( self, value_source: Mapping diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index dbee87a6e2..42bc2fa6b7 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -76,8 +76,9 @@ def before_questionnaire_request() -> Response | None: return None if cookie_session.get("submitted"): + error_repeated_submission = "The Questionnaire has been previously submitted" raise PreviouslySubmittedException( - "The Questionnaire has been previously submitted" + error_repeated_submission ) metadata = get_metadata(current_user) diff --git a/app/routes/session.py b/app/routes/session.py index 5aa5c89483..030fac5275 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -223,8 +223,9 @@ def _validate_supplementary_data_lists( """ supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: + error_missing_lists = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" raise ValidationError( - f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" + error_missing_lists ) @@ -305,7 +306,8 @@ def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: return validate_runner_claims_v2(decrypted_token) except ValidationError as e: - raise InvalidTokenException("Invalid runner claims") from e + error_runner_claims = "Invalid runner claims" + raise InvalidTokenException(error_runner_claims) from e def get_questionnaire_claims( @@ -316,4 +318,5 @@ def get_questionnaire_claims( return validate_questionnaire_claims(claims, schema_metadata, unknown=INCLUDE) except ValidationError as e: - raise InvalidTokenException("Invalid questionnaire claims") from e + error_q_claims = "Invalid questionnaire claims" + raise InvalidTokenException(error_q_claims) from e diff --git a/app/secrets.py b/app/secrets.py index 073f0d0b93..012f6ca5f0 100644 --- a/app/secrets.py +++ b/app/secrets.py @@ -22,7 +22,8 @@ def validate_required_secrets( ) for required_secret in all_required_secrets: if required_secret not in secrets["secrets"]: - raise ValueError(f"Missing Secret [{required_secret}]") + missing_secret_error = f"Missing Secret [{required_secret}]" + raise ValueError(missing_secret_error) class SecretStore: diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index 6e2aa28cd3..ff76dfcbf3 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -121,8 +121,8 @@ def decrypt_supplementary_data( return supplementary_data except InvalidTokenException as e: raise InvalidSupplementaryData from e - - raise ValidationError("Supplementary data has no data to decrypt") + missing_data = "Supplementary data has no data to decrypt" + raise ValidationError(missing_data) def validate_supplementary_data( @@ -141,4 +141,5 @@ def validate_supplementary_data( sds_schema_version=sds_schema_version, ) except ValidationError as e: - raise ValidationError("Invalid supplementary data") from e + invalid_data = "Invalid supplementary data" + raise ValidationError(invalid_data) from e diff --git a/app/settings.py b/app/settings.py index 6e6c11abf4..463f262d63 100644 --- a/app/settings.py +++ b/app/settings.py @@ -37,14 +37,16 @@ def read_file(file_name): def get_env_or_fail(key): value = os.getenv(key) if value is None: - raise ValueError(f"Setting '{key}' Missing") + missing_key_error = f"Setting '{key}' Missing" + raise ValueError(missing_key_error) return value def utcoffset_or_fail(date_value, key): if date_value.utcoffset() is None: - raise ValueError(f"'{key}' datetime offset missing") + missing_datetime_offset_error = f"'{key}' datetime offset missing" + raise ValueError(missing_datetime_offset_error) return date_value diff --git a/app/setup.py b/app/setup.py index bfe130b40d..315fb36428 100644 --- a/app/setup.py +++ b/app/setup.py @@ -293,7 +293,8 @@ def setup_storage(application): elif application.config["EQ_STORAGE_BACKEND"] == "dynamodb": setup_dynamodb(application) else: - raise NotImplementedError("Unknown EQ_STORAGE_BACKEND") + unknown_storage_backend = "Unknown EQ_STORAGE_BACKEND" + raise NotImplementedError(unknown_storage_backend) setup_redis(application) @@ -331,8 +332,9 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): + missing_bucket_id_msg = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" raise MissingEnvironmentVariable( - "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" + missing_bucket_id_msg ) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) @@ -342,10 +344,12 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - raise MissingEnvironmentVariable("Setting EQ_RABBITMQ_HOST Missing") + missing_host_msg = "Setting EQ_RABBITMQ_HOST Missing" + raise MissingEnvironmentVariable(missing_host_msg) if not secondary_host: + missing_secondary_host_msg = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" raise MissingEnvironmentVariable( - "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" + missing_secondary_host_msg ) application.eq["submitter"] = RabbitMQSubmitter( @@ -365,7 +369,8 @@ def setup_submitter(application): application.eq["submitter"] = LogSubmitter() else: - raise NotImplementedError("Unknown EQ_SUBMISSION_BACKEND") + unknown_submission_msg = "Unknown EQ_SUBMISSION_BACKEND" + raise NotImplementedError(unknown_submission_msg) def setup_task_client(application): @@ -374,19 +379,23 @@ def setup_task_client(application): elif application.config["EQ_SUBMISSION_CONFIRMATION_BACKEND"] == "log": application.eq["cloud_tasks"] = LogCloudTaskPublisher() else: - raise NotImplementedError("Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND") + unknown_submit_confirmation = "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND" + raise NotImplementedError(unknown_submit_confirmation) def setup_oidc(application): def client_ids_exist(): if not application.config.get("SDS_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable("Setting SDS_OAUTH2_CLIENT_ID Missing") + missing_sds_client_id = "Setting SDS_OAUTH2_CLIENT_ID Missing" + raise MissingEnvironmentVariable(missing_sds_client_id) if not application.config.get("CIR_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable("Setting CIR_OAUTH2_CLIENT_ID Missing") + missing_cir_client_id = "Setting CIR_OAUTH2_CLIENT_ID Missing" + raise MissingEnvironmentVariable(missing_cir_client_id) if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): - raise MissingEnvironmentVariable("Setting OIDC_TOKEN_BACKEND Missing") + missing_token_backend = "Setting OIDC_TOKEN_BACKEND Missing" + raise MissingEnvironmentVariable(missing_token_backend) if oidc_token_backend == "gcp": client_ids_exist() @@ -396,7 +405,8 @@ def client_ids_exist(): application.eq["oidc_credentials_service"] = OIDCCredentialsServiceLocal() else: - raise NotImplementedError("Unknown OIDC_TOKEN_BACKEND") + unknown_token_backend = "Unknown OIDC_TOKEN_BACKEND" + raise NotImplementedError(unknown_token_backend) def setup_publisher(application): @@ -407,14 +417,16 @@ def setup_publisher(application): application.eq["publisher"] = LogPublisher() else: - raise NotImplementedError("Unknown EQ_PUBLISHER_BACKEND") + unknown_publisher_backend_msg = "Unknown EQ_PUBLISHER_BACKEND" + raise NotImplementedError(unknown_publisher_backend_msg) def setup_feedback(application): if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): + missing_feedback_bucket_id_msg = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" raise MissingEnvironmentVariable( - "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" + missing_feedback_bucket_id_msg ) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( @@ -424,7 +436,8 @@ def setup_feedback(application): elif application.config["EQ_FEEDBACK_BACKEND"] == "log": application.eq["feedback_submitter"] = LogFeedbackSubmitter() else: - raise NotImplementedError("Unknown EQ_FEEDBACK_BACKEND") + unknown_feedback_backend_msg = "Unknown EQ_FEEDBACK_BACKEND" + raise NotImplementedError(unknown_feedback_backend_msg) def add_blueprints(application): @@ -462,7 +475,8 @@ def add_blueprints(application): def setup_secure_cookies(application): secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") if not secret_key: - raise ValueError("Application secret key does not exist") + no_secret_key_msg = "Application secret key does not exist" + raise ValueError(no_secret_key_msg) application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/storage/datastore.py b/app/storage/datastore.py index 4eac362543..ab6ca445d0 100644 --- a/app/storage/datastore.py +++ b/app/storage/datastore.py @@ -15,7 +15,8 @@ def __init__(self, client: datastore.Client) -> None: @Retry() def put(self, model: ModelTypes, overwrite: bool = True) -> bool: if not overwrite: - raise NotImplementedError("Unique key checking not supported") + unsupported_unique_key = "Unique key checking not supported" + raise NotImplementedError(unsupported_unique_key) storage_model = StorageModel(model_type=type(model)) serialized_item = storage_model.serialize(model) diff --git a/app/storage/storage.py b/app/storage/storage.py index da0704831c..05c57435d6 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -55,7 +55,8 @@ def __init__(self, model_type: type[ModelTypes]) -> None: self._model_type = model_type if self._model_type not in self.TABLE_CONFIG_BY_TYPE: - raise KeyError("Invalid model_type provided") + invalid_model_type_msg = "Invalid model_type provided" + raise KeyError(invalid_model_type_msg) self._config = self.TABLE_CONFIG_BY_TYPE[self._model_type] self._schema = self._config["schema"]() diff --git a/app/storage/storage_encryption.py b/app/storage/storage_encryption.py index cd86b74c12..c39271e3bb 100644 --- a/app/storage/storage_encryption.py +++ b/app/storage/storage_encryption.py @@ -15,11 +15,14 @@ def __init__( self, user_id: str | None, user_ik: str | None, pepper: str | None ) -> None: if not user_id: - raise ValueError("user_id not provided") + missing_user_id_msg = "user_id not provided" + raise ValueError(missing_user_id_msg) if not user_ik: - raise ValueError("user_ik not provided") + missing_user_ik_msg = "user_ik not provided" + raise ValueError(missing_user_ik_msg) if not pepper: - raise ValueError("pepper not provided") + missing_pepper_msg = "pepper not provided" + raise ValueError(missing_pepper_msg) self.key = self._generate_key(user_id, user_ik, pepper) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 081e3c9db5..4342cde639 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -63,8 +63,9 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument ] if missing_receipting_keys: + missing_keys_msg = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" raise ValidationError( - f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" + missing_keys_msg ) @@ -106,12 +107,14 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument if data.get(option) ] if len(options) == 0: + missing_metadata_option = "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" raise ValidationError( - "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + missing_metadata_option ) if len(options) > 1: + error_msg = f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata, but {', '.join(options)} were provided" raise ValidationError( - f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata, but {', '.join(options)} were provided" + error_msg ) diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 870acc92d3..91610738d2 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -23,8 +23,9 @@ def validate_identifier( # pylint: disable=no-self-use if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): + item_identifier_error_msg = "Item identifier must be a non-empty string or non-negative integer" raise ValidationError( - "Item identifier must be a non-empty string or non-negative integer" + item_identifier_error_msg ) @@ -41,8 +42,9 @@ def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data and data["identifier"] != self.context["identifier"]: + identifier_error_msg = "Supplementary data did not return the specified Identifier" raise ValidationError( - "Supplementary data did not return the specified Identifier" + identifier_error_msg ) @@ -62,20 +64,23 @@ def validate_payload( # pylint: disable=unused-argument ) -> None: if payload: if payload["dataset_id"] != self.context["dataset_id"]: + dataset_id_error_msg = "Supplementary data did not return the specified Dataset ID" raise ValidationError( - "Supplementary data did not return the specified Dataset ID" + dataset_id_error_msg ) if payload["survey_id"] != self.context["survey_id"]: + survey_id_error_msg = "Supplementary data did not return the specified Survey ID" raise ValidationError( - "Supplementary data did not return the specified Survey ID" + survey_id_error_msg ) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): + mismatch_version_error_msg = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" raise ValidationError( - "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" + mismatch_version_error_msg ) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 16bab685d4..7b6bf067a4 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -53,8 +53,9 @@ def __init__( self.resume = "resume" in request_args if not self.is_location_valid(): + invalid_location_msg = f"location {self._current_location} is not valid" raise InvalidLocationException( - f"location {self._current_location} is not valid" + invalid_location_msg ) @property diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 2fb7816a7c..1460ced45c 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -57,21 +57,24 @@ def get_block_handler( block = schema.get_block(block_id) if not block: + invalid_block_id_msg = f"block id {block_id} is not valid for this schema" raise InvalidLocationException( - f"block id {block_id} is not valid for this schema" + invalid_block_id_msg ) if schema.is_block_in_repeating_section(block_id=block["id"]) and not all( (list_name, list_item_id) ): + invalid_list_msg = f"block id {block_id} is in a repeating section without valid list_name/list_item_id" raise InvalidLocationException( - f"block id {block_id} is in a repeating section without valid list_name/list_item_id" + invalid_list_msg ) block_type = block["type"] block_class = BLOCK_MAPPINGS.get(block_type) if not block_class: - raise ValueError(f"block type {block_type} is not valid") + invalid_block_type_msg = f"block type {block_type} is not valid" + raise ValueError(invalid_block_type_msg) section_id = schema.get_section_id_for_block_id(block_id) diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index f16741a436..02cb90087d 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -230,8 +230,9 @@ def _check_individual_response_count(self) -> None: ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): + response_limit_msg = "Individual response limit has been reached" raise IndividualResponseLimitReached( - "Individual response limit has been reached" + response_limit_msg ) def _update_individual_response_count(self) -> None: diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index d5cb189417..e8831a2c45 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -26,7 +26,8 @@ def __init__( data_stores=self._questionnaire_store.data_stores, ) if not self._is_valid_location(): - raise InvalidLocationException(f"location {self._section_id} is not valid") + invalid_location_msg = f"location {self._section_id} is not valid" + raise InvalidLocationException(invalid_location_msg) self.current_location = Location( section_id=self._section_id, diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index 36f21c34d1..c3aec11dc5 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -18,7 +18,8 @@ def __init__( language: str, ): if not schema.is_flow_linear: - raise InvalidLocationException("Submit page not enabled") + submit_page_msg = "Submit page not enabled" + raise InvalidLocationException(submit_page_msg) self._schema = schema self._questionnaire_store = questionnaire_store From 4dfba4cc59a9c72b6ada44f0396b905ca6207955 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 23 Apr 2025 13:18:02 +0100 Subject: [PATCH 527/567] Updated the code to ensure the error message string is passed in exception class --- app/authentication/authenticator.py | 3 +-- app/authentication/no_token_exception.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/authentication/authenticator.py b/app/authentication/authenticator.py index 2e7e6ded33..ffcc5f3ec6 100644 --- a/app/authentication/authenticator.py +++ b/app/authentication/authenticator.py @@ -179,8 +179,7 @@ def create_session_questionnaire_store( def decrypt_token(encrypted_token: str | None) -> dict[str, Any]: if not encrypted_token: - missing_token = "Please provide a token" - raise NoTokenException(missing_token) + raise NoTokenException() logger.debug("decrypting token") decrypted_token: dict[str, Any] = decrypt( diff --git a/app/authentication/no_token_exception.py b/app/authentication/no_token_exception.py index d79e2e82ef..8fe20e7148 100644 --- a/app/authentication/no_token_exception.py +++ b/app/authentication/no_token_exception.py @@ -1,5 +1,5 @@ class NoTokenException(Exception): - def __init__(self, value: str | int) -> None: + def __init__(self, value: str | int = "Please provide a token") -> None: super().__init__() self.value = value From f7386e4627ef3403332d03b360e37a6e09d6b065 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 23 Apr 2025 14:24:39 +0100 Subject: [PATCH 528/567] Corrected string formatting into a single string --- app/forms/validators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/forms/validators.py b/app/forms/validators.py index 1a9b2eda38..3970a23030 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -425,8 +425,7 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: - invalid_mutiple_conditions = "There are multiple conditions, but equals is not one of them. " - "We only support <= and >=" + invalid_mutiple_conditions = "There are multiple conditions, but equals is not one of them. We only support <= and >=" raise ValueError( invalid_mutiple_conditions ) from exc From c5ce959d994721de0ab5eb70992678eb666c2264 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Thu, 24 Apr 2025 12:48:29 +0100 Subject: [PATCH 529/567] resolved too many local variables issues --- app/views/handlers/block_factory.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 1460ced45c..9195571788 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -56,25 +56,25 @@ def get_block_handler( ) -> Any: block = schema.get_block(block_id) + block_error_msg = (f"block id {block_id} is not valid for this schema", + f"block id {block_id} is in a repeating section without valid list_name/list_item_id", + f"block id {block_id} is not valid for this schema") if not block: - invalid_block_id_msg = f"block id {block_id} is not valid for this schema" raise InvalidLocationException( - invalid_block_id_msg + block_error_msg[0] ) if schema.is_block_in_repeating_section(block_id=block["id"]) and not all( (list_name, list_item_id) ): - invalid_list_msg = f"block id {block_id} is in a repeating section without valid list_name/list_item_id" raise InvalidLocationException( - invalid_list_msg + block_error_msg[1] ) block_type = block["type"] block_class = BLOCK_MAPPINGS.get(block_type) if not block_class: - invalid_block_type_msg = f"block type {block_type} is not valid" - raise ValueError(invalid_block_type_msg) + raise ValueError(block_error_msg[2]) section_id = schema.get_section_id_for_block_id(block_id) From dd0814ef7f388ba06582d0003df40ef1a3db0050 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Thu, 24 Apr 2025 12:52:08 +0100 Subject: [PATCH 530/567] msg in exception class due to same messages --- app/forms/field_handlers/dropdown_handler.py | 3 +-- app/forms/field_handlers/select_handlers.py | 3 +-- app/questionnaire/questionnaire_schema.py | 6 +++++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/forms/field_handlers/dropdown_handler.py b/app/forms/field_handlers/dropdown_handler.py index ea53df8272..eae1f794b3 100644 --- a/app/forms/field_handlers/dropdown_handler.py +++ b/app/forms/field_handlers/dropdown_handler.py @@ -19,8 +19,7 @@ def choices(self) -> Sequence[Choice]: self._build_dynamic_choices() + self._build_static_choices() ) if not _choices: - no_choices_msg = "No dynamic or static choices" - raise InvalidSchemaConfigurationException(no_choices_msg) + raise InvalidSchemaConfigurationException() return [ Choice("", self._get_placeholder_text()), diff --git a/app/forms/field_handlers/select_handlers.py b/app/forms/field_handlers/select_handlers.py index d194ac2b04..bd3533df7b 100644 --- a/app/forms/field_handlers/select_handlers.py +++ b/app/forms/field_handlers/select_handlers.py @@ -17,8 +17,7 @@ class SelectHandlerBase(FieldHandler): def choices(self) -> Sequence[ChoiceType]: _choices = self._build_dynamic_choices() + self._build_static_choices() if not _choices: - no_choices_msg = "No dynamic or static choices" - raise InvalidSchemaConfigurationException(no_choices_msg) + raise InvalidSchemaConfigurationException() return _choices @property diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 281bb3f33d..65150562bc 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -59,7 +59,11 @@ class InvalidSchemaConfigurationException(Exception): - pass + def __init__(self, value: str = "No dynamic or static choices") -> None: + super().__init__() + self.value = value + def __str__(self) -> str: + return str(self.value) @dataclass(frozen=True) From ecd06951c2ba4a17ec5bb4c812145ab661636a74 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 25 Apr 2025 15:11:25 +0100 Subject: [PATCH 531/567] updated type hinting to remove int --- app/authentication/no_token_exception.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/authentication/no_token_exception.py b/app/authentication/no_token_exception.py index 8fe20e7148..8671397ee9 100644 --- a/app/authentication/no_token_exception.py +++ b/app/authentication/no_token_exception.py @@ -1,5 +1,5 @@ class NoTokenException(Exception): - def __init__(self, value: str | int = "Please provide a token") -> None: + def __init__(self, value: str = "Please provide a token") -> None: super().__init__() self.value = value From bb2b6af5617b5525338e2ed8261713eed02a365f Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 25 Apr 2025 15:30:50 +0100 Subject: [PATCH 532/567] Switched to dict for better readability and to reduce local variables. --- app/views/handlers/block_factory.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index 9195571788..ea1bd016ff 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -56,25 +56,28 @@ def get_block_handler( ) -> Any: block = schema.get_block(block_id) - block_error_msg = (f"block id {block_id} is not valid for this schema", - f"block id {block_id} is in a repeating section without valid list_name/list_item_id", - f"block id {block_id} is not valid for this schema") + block_error_message = { + "invalid_block_id": f"block id {block_id} is not valid for this schema", + "invalid_list": f"block id {block_id} is in a repeating section without valid list_name/list_item_id", + "invalid_block_type": f"block id {block_id} is not valid for this schema", + } + if not block: raise InvalidLocationException( - block_error_msg[0] + block_error_message["invalid_block_id"] ) if schema.is_block_in_repeating_section(block_id=block["id"]) and not all( (list_name, list_item_id) ): raise InvalidLocationException( - block_error_msg[1] + block_error_message["invalid_list"] ) block_type = block["type"] block_class = BLOCK_MAPPINGS.get(block_type) if not block_class: - raise ValueError(block_error_msg[2]) + raise ValueError(block_error_message["invalid_block_type"]) section_id = schema.get_section_id_for_block_id(block_id) From 7db155f6ec3afcab84415a2773c49523a8d32536 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 25 Apr 2025 16:52:03 +0100 Subject: [PATCH 533/567] updated the variable names for consitency --- app/data_models/answer_store.py | 4 +-- app/data_models/list_store.py | 4 +-- app/questionnaire/rules/operations.py | 4 +-- app/questionnaire/rules/rule_evaluator.py | 4 +-- app/questionnaire/value_source_resolver.py | 8 +++--- app/routes/questionnaire.py | 4 +-- app/routes/session.py | 12 ++++---- app/services/supplementary_data.py | 8 +++--- app/setup.py | 32 +++++++++++----------- app/storage/storage.py | 4 +-- app/storage/storage_encryption.py | 12 ++++---- app/utilities/metadata_parser_v2.py | 9 +++--- app/utilities/supplementary_data_parser.py | 20 +++++++------- app/views/handlers/block.py | 4 +-- app/views/handlers/individual_response.py | 4 +-- app/views/handlers/section.py | 4 +-- app/views/handlers/submit_questionnaire.py | 4 +-- 17 files changed, 71 insertions(+), 70 deletions(-) diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index 8bb3875752..158f69573d 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -54,9 +54,9 @@ def _build_map( @staticmethod def _validate(answer: Answer) -> None: if not isinstance(answer, Answer): - type_error_msg = f"Method only supports Answer argument type, found type: {type(answer)}" + answer_type_error_message = f"Method only supports Answer argument type, found type: {type(answer)}" raise TypeError( - type_error_msg + answer_type_error_message ) @property diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index fa03530c9c..533e9e7caa 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -82,9 +82,9 @@ def first(self) -> str: try: return self.items[0] except IndexError as e: - error_msg_empty_list = f"unable to access first item in list, list '{self.name}' is empty" + empty_list_error_message = f"unable to access first item in list, list '{self.name}' is empty" raise IndexError( - error_msg_empty_list + empty_list_error_message ) from e @property diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index ae3b0f1d96..48f97c3bd7 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -146,9 +146,9 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: - value_error_msg = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" + invalid_negative_days_offset = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" raise ValueError( - value_error_msg + invalid_negative_days_offset ) days_difference = ( diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 5ebb9741d0..e5bcc71e7a 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -57,9 +57,9 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operands = rule[operator_name] if not isinstance(operands, Sequence): - invalid_rule_msg = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" + invalid_rule_message = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" raise TypeError( - invalid_rule_msg + invalid_rule_message ) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 9b5bdbfb37..8ef44bd6a8 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -98,9 +98,9 @@ def _resolve_list_item_id_for_value_source( if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: - missing_location = "list_item_selector source location used without location" + invalid_selector_location = "list_item_selector source location used without location" raise InvalidLocationException( - missing_location + invalid_selector_location ) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -212,8 +212,8 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - missing_required_location = "location is required to resolve block progress" - raise ValueError(missing_required_location) + location_required_error= "location is required to resolve block progress" + raise ValueError(location_required_error) if not self._is_block_on_path(identifier): return None diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 42bc2fa6b7..4ec5fd0785 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -76,9 +76,9 @@ def before_questionnaire_request() -> Response | None: return None if cookie_session.get("submitted"): - error_repeated_submission = "The Questionnaire has been previously submitted" + repeated_submission_error = "The Questionnaire has been previously submitted" raise PreviouslySubmittedException( - error_repeated_submission + repeated_submission_error ) metadata = get_metadata(current_user) diff --git a/app/routes/session.py b/app/routes/session.py index 030fac5275..9758072280 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -223,9 +223,9 @@ def _validate_supplementary_data_lists( """ supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: - error_missing_lists = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" + missing_schema_lists_error = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" raise ValidationError( - error_missing_lists + missing_schema_lists_error ) @@ -306,8 +306,8 @@ def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: return validate_runner_claims_v2(decrypted_token) except ValidationError as e: - error_runner_claims = "Invalid runner claims" - raise InvalidTokenException(error_runner_claims) from e + runner_claims_error = "Invalid runner claims" + raise InvalidTokenException(runner_claims_error) from e def get_questionnaire_claims( @@ -318,5 +318,5 @@ def get_questionnaire_claims( return validate_questionnaire_claims(claims, schema_metadata, unknown=INCLUDE) except ValidationError as e: - error_q_claims = "Invalid questionnaire claims" - raise InvalidTokenException(error_q_claims) from e + questionnaire_claims_error = "Invalid questionnaire claims" + raise InvalidTokenException(questionnaire_claims_error) from e diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index ff76dfcbf3..f62b00434c 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -121,8 +121,8 @@ def decrypt_supplementary_data( return supplementary_data except InvalidTokenException as e: raise InvalidSupplementaryData from e - missing_data = "Supplementary data has no data to decrypt" - raise ValidationError(missing_data) + supplementary_data_empty_error = "Supplementary data has no data to decrypt" + raise ValidationError(supplementary_data_empty_error) def validate_supplementary_data( @@ -141,5 +141,5 @@ def validate_supplementary_data( sds_schema_version=sds_schema_version, ) except ValidationError as e: - invalid_data = "Invalid supplementary data" - raise ValidationError(invalid_data) from e + invalid_supplementary_data = "Invalid supplementary data" + raise ValidationError(invalid_supplementary_data) from e diff --git a/app/setup.py b/app/setup.py index 315fb36428..5b4a6a2583 100644 --- a/app/setup.py +++ b/app/setup.py @@ -332,9 +332,9 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): - missing_bucket_id_msg = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" + missing_bucket_id_message = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" raise MissingEnvironmentVariable( - missing_bucket_id_msg + missing_bucket_id_message ) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) @@ -344,12 +344,12 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - missing_host_msg = "Setting EQ_RABBITMQ_HOST Missing" - raise MissingEnvironmentVariable(missing_host_msg) + missing_host_message = "Setting EQ_RABBITMQ_HOST Missing" + raise MissingEnvironmentVariable(missing_host_message) if not secondary_host: - missing_secondary_host_msg = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" + missing_secondary_host_message = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" raise MissingEnvironmentVariable( - missing_secondary_host_msg + missing_secondary_host_message ) application.eq["submitter"] = RabbitMQSubmitter( @@ -369,8 +369,8 @@ def setup_submitter(application): application.eq["submitter"] = LogSubmitter() else: - unknown_submission_msg = "Unknown EQ_SUBMISSION_BACKEND" - raise NotImplementedError(unknown_submission_msg) + unknown_submission_message = "Unknown EQ_SUBMISSION_BACKEND" + raise NotImplementedError(unknown_submission_message) def setup_task_client(application): @@ -417,16 +417,16 @@ def setup_publisher(application): application.eq["publisher"] = LogPublisher() else: - unknown_publisher_backend_msg = "Unknown EQ_PUBLISHER_BACKEND" - raise NotImplementedError(unknown_publisher_backend_msg) + unknown_publisher_backend_message = "Unknown EQ_PUBLISHER_BACKEND" + raise NotImplementedError(unknown_publisher_backend_message) def setup_feedback(application): if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): - missing_feedback_bucket_id_msg = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" + missing_feedback_bucket_id_message = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" raise MissingEnvironmentVariable( - missing_feedback_bucket_id_msg + missing_feedback_bucket_id_message ) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( @@ -436,8 +436,8 @@ def setup_feedback(application): elif application.config["EQ_FEEDBACK_BACKEND"] == "log": application.eq["feedback_submitter"] = LogFeedbackSubmitter() else: - unknown_feedback_backend_msg = "Unknown EQ_FEEDBACK_BACKEND" - raise NotImplementedError(unknown_feedback_backend_msg) + unknown_feedback_backend_message = "Unknown EQ_FEEDBACK_BACKEND" + raise NotImplementedError(unknown_feedback_backend_message) def add_blueprints(application): @@ -475,8 +475,8 @@ def add_blueprints(application): def setup_secure_cookies(application): secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") if not secret_key: - no_secret_key_msg = "Application secret key does not exist" - raise ValueError(no_secret_key_msg) + no_secret_key_message = "Application secret key does not exist" + raise ValueError(no_secret_key_message) application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/storage/storage.py b/app/storage/storage.py index 05c57435d6..a860e7d44d 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -55,8 +55,8 @@ def __init__(self, model_type: type[ModelTypes]) -> None: self._model_type = model_type if self._model_type not in self.TABLE_CONFIG_BY_TYPE: - invalid_model_type_msg = "Invalid model_type provided" - raise KeyError(invalid_model_type_msg) + invalid_model_type_message = "Invalid model_type provided" + raise KeyError(invalid_model_type_message) self._config = self.TABLE_CONFIG_BY_TYPE[self._model_type] self._schema = self._config["schema"]() diff --git a/app/storage/storage_encryption.py b/app/storage/storage_encryption.py index c39271e3bb..f09a9421ce 100644 --- a/app/storage/storage_encryption.py +++ b/app/storage/storage_encryption.py @@ -15,14 +15,14 @@ def __init__( self, user_id: str | None, user_ik: str | None, pepper: str | None ) -> None: if not user_id: - missing_user_id_msg = "user_id not provided" - raise ValueError(missing_user_id_msg) + missing_user_id_message = "user_id not provided" + raise ValueError(missing_user_id_message) if not user_ik: - missing_user_ik_msg = "user_ik not provided" - raise ValueError(missing_user_ik_msg) + missing_user_ik_message = "user_ik not provided" + raise ValueError(missing_user_ik_message) if not pepper: - missing_pepper_msg = "pepper not provided" - raise ValueError(missing_pepper_msg) + missing_pepper_message = "pepper not provided" + raise ValueError(missing_pepper_message) self.key = self._generate_key(user_id, user_ik, pepper) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 4342cde639..9308f0d4b5 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -63,9 +63,9 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument ] if missing_receipting_keys: - missing_keys_msg = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" + missing_keys_message = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" raise ValidationError( - missing_keys_msg + missing_keys_message ) @@ -112,9 +112,10 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument missing_metadata_option ) if len(options) > 1: - error_msg = f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata, but {', '.join(options)} were provided" + invalid_metadata_combination = f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata,\ + but {', '.join(options)} were provided" raise ValidationError( - error_msg + invalid_metadata_combination ) diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 91610738d2..5b89dbe80c 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -23,9 +23,9 @@ def validate_identifier( # pylint: disable=no-self-use if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): - item_identifier_error_msg = "Item identifier must be a non-empty string or non-negative integer" + item_identifier_error_message = "Item identifier must be a non-empty string or non-negative integer" raise ValidationError( - item_identifier_error_msg + item_identifier_error_message ) @@ -42,9 +42,9 @@ def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data and data["identifier"] != self.context["identifier"]: - identifier_error_msg = "Supplementary data did not return the specified Identifier" + identifier_error_message = "Supplementary data did not return the specified Identifier" raise ValidationError( - identifier_error_msg + identifier_error_message ) @@ -64,23 +64,23 @@ def validate_payload( # pylint: disable=unused-argument ) -> None: if payload: if payload["dataset_id"] != self.context["dataset_id"]: - dataset_id_error_msg = "Supplementary data did not return the specified Dataset ID" + dataset_id_error_message = "Supplementary data did not return the specified Dataset ID" raise ValidationError( - dataset_id_error_msg + dataset_id_error_message ) if payload["survey_id"] != self.context["survey_id"]: - survey_id_error_msg = "Supplementary data did not return the specified Survey ID" + survey_id_error_message = "Supplementary data did not return the specified Survey ID" raise ValidationError( - survey_id_error_msg + survey_id_error_message ) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): - mismatch_version_error_msg = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" + mismatch_version_error_message = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" raise ValidationError( - mismatch_version_error_msg + mismatch_version_error_message ) diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 7b6bf067a4..c0ae796cb5 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -53,9 +53,9 @@ def __init__( self.resume = "resume" in request_args if not self.is_location_valid(): - invalid_location_msg = f"location {self._current_location} is not valid" + invalid_location_message = f"location {self._current_location} is not valid" raise InvalidLocationException( - invalid_location_msg + invalid_location_message ) @property diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 02cb90087d..e6e45d0c8b 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -230,9 +230,9 @@ def _check_individual_response_count(self) -> None: ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): - response_limit_msg = "Individual response limit has been reached" + response_limit_message = "Individual response limit has been reached" raise IndividualResponseLimitReached( - response_limit_msg + response_limit_message ) def _update_individual_response_count(self) -> None: diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index e8831a2c45..a7e98372ad 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -26,8 +26,8 @@ def __init__( data_stores=self._questionnaire_store.data_stores, ) if not self._is_valid_location(): - invalid_location_msg = f"location {self._section_id} is not valid" - raise InvalidLocationException(invalid_location_msg) + invalid_location_message = f"location {self._section_id} is not valid" + raise InvalidLocationException(invalid_location_message) self.current_location = Location( section_id=self._section_id, diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index c3aec11dc5..a73d2f84e7 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -18,8 +18,8 @@ def __init__( language: str, ): if not schema.is_flow_linear: - submit_page_msg = "Submit page not enabled" - raise InvalidLocationException(submit_page_msg) + submit_page_message = "Submit page not enabled" + raise InvalidLocationException(submit_page_message) self._schema = schema self._questionnaire_store = questionnaire_store From 43dbc3dabe7611a13317c977784d8233dda900a9 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 25 Apr 2025 16:58:38 +0100 Subject: [PATCH 534/567] updated the changes by passing variable as class attributes --- app/authentication/user.py | 4 ++-- app/authentication/user_id_generator.py | 8 ++++---- app/questionnaire/placeholder_renderer.py | 4 ++-- app/views/handlers/block_factory.py | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/authentication/user.py b/app/authentication/user.py index 3fb8464c61..d870345b58 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -2,10 +2,10 @@ class User(UserMixin): + missing_user_session_error = "No user_id or user_ik found in session" def __init__(self, user_id: str | None, user_ik: str | None) -> None: if user_id and user_ik: self.user_id = user_id self.user_ik = user_ik else: - missing_user_session_error = "No user_id or user_ik found in session" - raise ValueError(missing_user_session_error) + raise ValueError(self.missing_user_session_error) diff --git a/app/authentication/user_id_generator.py b/app/authentication/user_id_generator.py index 601739ec63..ee3d46594a 100644 --- a/app/authentication/user_id_generator.py +++ b/app/authentication/user_id_generator.py @@ -11,13 +11,13 @@ class UserIDGenerator: + user_id_salt_error = "user_id_salt is required" + user_ik_salt_error = "user_ik_salt is required" def __init__(self, iterations: int, user_id_salt: str, user_ik_salt: str) -> None: if user_id_salt is None: - user_id_salt_error = "user_id_salt is required" - raise ValueError(user_id_salt_error) + raise ValueError(self.user_id_salt_error) if user_ik_salt is None: - user_ik_salt_error = "user_ik_salt is required" - raise ValueError(user_ik_salt_error) + raise ValueError(self.user_ik_salt_error) self._iterations = iterations self._user_id_salt = user_id_salt diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 4f9abc0040..07213cf90c 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -19,6 +19,7 @@ class PlaceholderRenderer: Renders placeholders specified by a list of pointers in a schema block to their final strings """ + placeholder_not_found = "No placeholder found to render" def __init__( self, @@ -97,8 +98,7 @@ def render_placeholder( placeholder_data["text"] = plural_forms[plural_form_key] if "text" not in placeholder_data and "placeholders" not in placeholder_data: - placeholder_not_found = "No placeholder found to render" - raise ValueError(placeholder_not_found) + raise ValueError(self.placeholder_not_found) transformed_values = placeholder_parser(placeholder_data["placeholders"]) formatted_placeholder_data: str = placeholder_data["text"].format( diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index ea1bd016ff..f5c9ba3ebd 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -61,7 +61,6 @@ def get_block_handler( "invalid_list": f"block id {block_id} is in a repeating section without valid list_name/list_item_id", "invalid_block_type": f"block id {block_id} is not valid for this schema", } - if not block: raise InvalidLocationException( block_error_message["invalid_block_id"] From e4f5cd467c4312d88d5c8c799dc6f435751fae57 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Mon, 28 Apr 2025 10:13:04 +0100 Subject: [PATCH 535/567] format error message string to resolve linting line length issue --- app/utilities/metadata_parser_v2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 9308f0d4b5..16d9f2f834 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -112,8 +112,10 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument missing_metadata_option ) if len(options) > 1: - invalid_metadata_combination = f"Only one of schema_name, schema_url or cir_instrument_id should be specified in metadata,\ - but {', '.join(options)} were provided" + invalid_metadata_combination = ( + "Only one of schema_name, schema_url or cir_instrument_id should be specified " + f"in metadata, but {', '.join(options)} were provided" + ) raise ValidationError( invalid_metadata_combination ) From 243b76ce54a81b38d97cebd757e45362daaa904c Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:57:44 +0100 Subject: [PATCH 536/567] Upgrade Node Version to v22.15.0 (#1649) * upgrade node version to v23.11.0 * change package.json type to module Signed-off-by: Yuyutsu Rai * update node to latest stable version - 22.15.0 --------- Signed-off-by: Yuyutsu Rai --- .nvmrc | 2 +- package.json | 1 + tests/functional/{wdio.conf.js => wdio.conf.cjs} | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename tests/functional/{wdio.conf.js => wdio.conf.cjs} (100%) diff --git a/.nvmrc b/.nvmrc index 790e1105f2..d2c5c8a013 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.10.0 +v22.15.0 \ No newline at end of file diff --git a/package.json b/package.json index 4d96f56862..fade6d4dfa 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "eq-questionnaire-runner", "version": "1.0.0", "description": "ONS Digital eQ Questionnaire Runner App", + "type": "module", "author": { "name": "ONS Digital", "url": "http://onsdigital.github.io/" diff --git a/tests/functional/wdio.conf.js b/tests/functional/wdio.conf.cjs similarity index 100% rename from tests/functional/wdio.conf.js rename to tests/functional/wdio.conf.cjs From 88b3b8a9482b73c3eba78c2b460b708247fd90cf Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:39:37 +0100 Subject: [PATCH 537/567] update codeql matrix to include the actions language (#1648) --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f8dda6a8fd..0cd2db8713 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,7 +24,7 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['python', 'javascript'] + language: ['python', 'javascript', 'actions'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection From f65161140a614ebe15ec549452c4bf91202c8abd Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Mon, 28 Apr 2025 14:37:03 +0100 Subject: [PATCH 538/567] change variable name to upper case --- app/questionnaire/placeholder_renderer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 07213cf90c..64c818f561 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -19,7 +19,8 @@ class PlaceholderRenderer: Renders placeholders specified by a list of pointers in a schema block to their final strings """ - placeholder_not_found = "No placeholder found to render" + + PLACEHOLDER_NOT_FOUND = "No placeholder found to render" def __init__( self, @@ -98,7 +99,7 @@ def render_placeholder( placeholder_data["text"] = plural_forms[plural_form_key] if "text" not in placeholder_data and "placeholders" not in placeholder_data: - raise ValueError(self.placeholder_not_found) + raise ValueError(self.PLACEHOLDER_NOT_FOUND) transformed_values = placeholder_parser(placeholder_data["placeholders"]) formatted_placeholder_data: str = placeholder_data["text"].format( From bc369fffc74139426b1c40a34de8b578170af527 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Mon, 28 Apr 2025 14:38:26 +0100 Subject: [PATCH 539/567] ran lint formatter --- app/authentication/user.py | 1 + app/authentication/user_id_generator.py | 1 + app/data_models/answer_store.py | 6 ++--- app/data_models/list_store.py | 8 +++--- app/data_models/supplementary_data_store.py | 6 ++--- app/forms/field_handlers/number_handler.py | 1 - app/forms/validators.py | 5 +--- app/questionnaire/questionnaire_schema.py | 1 + app/questionnaire/rules/operations.py | 4 +-- app/questionnaire/rules/rule_evaluator.py | 4 +-- app/questionnaire/value_source_resolver.py | 10 +++++--- app/routes/questionnaire.py | 4 +-- app/routes/session.py | 4 +-- app/setup.py | 16 ++++++------ app/utilities/metadata_parser_v2.py | 12 +++------ app/utilities/supplementary_data_parser.py | 28 ++++++++++----------- app/views/handlers/block.py | 4 +-- app/views/handlers/block_factory.py | 8 ++---- app/views/handlers/individual_response.py | 4 +-- 19 files changed, 51 insertions(+), 76 deletions(-) diff --git a/app/authentication/user.py b/app/authentication/user.py index d870345b58..b2211d9ffc 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -3,6 +3,7 @@ class User(UserMixin): missing_user_session_error = "No user_id or user_ik found in session" + def __init__(self, user_id: str | None, user_ik: str | None) -> None: if user_id and user_ik: self.user_id = user_id diff --git a/app/authentication/user_id_generator.py b/app/authentication/user_id_generator.py index ee3d46594a..bf946c49a7 100644 --- a/app/authentication/user_id_generator.py +++ b/app/authentication/user_id_generator.py @@ -13,6 +13,7 @@ class UserIDGenerator: user_id_salt_error = "user_id_salt is required" user_ik_salt_error = "user_ik_salt is required" + def __init__(self, iterations: int, user_id_salt: str, user_ik_salt: str) -> None: if user_id_salt is None: raise ValueError(self.user_id_salt_error) diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index 158f69573d..c12b7d168d 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -54,10 +54,10 @@ def _build_map( @staticmethod def _validate(answer: Answer) -> None: if not isinstance(answer, Answer): - answer_type_error_message = f"Method only supports Answer argument type, found type: {type(answer)}" - raise TypeError( - answer_type_error_message + answer_type_error_message = ( + f"Method only supports Answer argument type, found type: {type(answer)}" ) + raise TypeError(answer_type_error_message) @property def is_dirty(self) -> bool: diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 533e9e7caa..54db73ff8c 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -82,10 +82,10 @@ def first(self) -> str: try: return self.items[0] except IndexError as e: - empty_list_error_message = f"unable to access first item in list, list '{self.name}' is empty" - raise IndexError( - empty_list_error_message - ) from e + empty_list_error_message = ( + f"unable to access first item in list, list '{self.name}' is empty" + ) + raise IndexError(empty_list_error_message) from e @property def count(self) -> int: diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index d15bc810f4..14d563f47b 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -144,10 +144,10 @@ def _resolve_value( return None if not isinstance(value, Mapping): # if value is not None, and also not index able, raise an error - invalid_selector_error = f"Cannot use the selector `{selector}` on non-nested data" - raise InvalidSupplementaryDataSelector( - invalid_selector_error + invalid_selector_error = ( + f"Cannot use the selector `{selector}` on non-nested data" ) + raise InvalidSupplementaryDataSelector(invalid_selector_error) value = value.get(selector) return value diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index bc721d389a..78ff7a3e83 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -1,5 +1,4 @@ from functools import cached_property - from typing import Any from wtforms import DecimalField, IntegerField diff --git a/app/forms/validators.py b/app/forms/validators.py index 3970a23030..1c775c8344 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -6,7 +6,6 @@ from decimal import Decimal, InvalidOperation from typing import TYPE_CHECKING, Iterable, Mapping, Sequence - import flask_babel from babel import numbers from dateutil.relativedelta import relativedelta @@ -426,9 +425,7 @@ def __call__( conditions.remove("equals") except ValueError as exc: invalid_mutiple_conditions = "There are multiple conditions, but equals is not one of them. We only support <= and >=" - raise ValueError( - invalid_mutiple_conditions - ) from exc + raise ValueError(invalid_mutiple_conditions) from exc condition = f"{conditions[0]} or equals" else: diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 65150562bc..d8a0485eb8 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -62,6 +62,7 @@ class InvalidSchemaConfigurationException(Exception): def __init__(self, value: str = "No dynamic or static choices") -> None: super().__init__() self.value = value + def __str__(self) -> str: return str(self.value) diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 48f97c3bd7..7079ebd576 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -147,9 +147,7 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: invalid_negative_days_offset = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" - raise ValueError( - invalid_negative_days_offset - ) + raise ValueError(invalid_negative_days_offset) days_difference = ( value_as_date.weekday() - DAYS_OF_WEEK[day_of_week_offset] diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index e5bcc71e7a..a53308945e 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -58,9 +58,7 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: if not isinstance(operands, Sequence): invalid_rule_message = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" - raise TypeError( - invalid_rule_message - ) + raise TypeError(invalid_rule_message) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 8ef44bd6a8..0a331e3464 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -98,10 +98,10 @@ def _resolve_list_item_id_for_value_source( if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: - invalid_selector_location = "list_item_selector source location used without location" - raise InvalidLocationException( - invalid_selector_location + invalid_selector_location = ( + "list_item_selector source location used without location" ) + raise InvalidLocationException(invalid_selector_location) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -212,7 +212,9 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - location_required_error= "location is required to resolve block progress" + location_required_error = ( + "location is required to resolve block progress" + ) raise ValueError(location_required_error) if not self._is_block_on_path(identifier): diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 4ec5fd0785..e686840b08 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -77,9 +77,7 @@ def before_questionnaire_request() -> Response | None: if cookie_session.get("submitted"): repeated_submission_error = "The Questionnaire has been previously submitted" - raise PreviouslySubmittedException( - repeated_submission_error - ) + raise PreviouslySubmittedException(repeated_submission_error) metadata = get_metadata(current_user) if not metadata: diff --git a/app/routes/session.py b/app/routes/session.py index 9758072280..717962cb71 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -224,9 +224,7 @@ def _validate_supplementary_data_lists( supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: missing_schema_lists_error = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" - raise ValidationError( - missing_schema_lists_error - ) + raise ValidationError(missing_schema_lists_error) def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: diff --git a/app/setup.py b/app/setup.py index 5b4a6a2583..5df3889803 100644 --- a/app/setup.py +++ b/app/setup.py @@ -333,9 +333,7 @@ def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): missing_bucket_id_message = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" - raise MissingEnvironmentVariable( - missing_bucket_id_message - ) + raise MissingEnvironmentVariable(missing_bucket_id_message) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) @@ -347,10 +345,10 @@ def setup_submitter(application): missing_host_message = "Setting EQ_RABBITMQ_HOST Missing" raise MissingEnvironmentVariable(missing_host_message) if not secondary_host: - missing_secondary_host_message = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" - raise MissingEnvironmentVariable( - missing_secondary_host_message + missing_secondary_host_message = ( + "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" ) + raise MissingEnvironmentVariable(missing_secondary_host_message) application.eq["submitter"] = RabbitMQSubmitter( host=host, @@ -424,10 +422,10 @@ def setup_publisher(application): def setup_feedback(application): if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): - missing_feedback_bucket_id_message = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" - raise MissingEnvironmentVariable( - missing_feedback_bucket_id_message + missing_feedback_bucket_id_message = ( + "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" ) + raise MissingEnvironmentVariable(missing_feedback_bucket_id_message) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( bucket_name=bucket_name diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 16d9f2f834..156e1ba87a 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -64,9 +64,7 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument if missing_receipting_keys: missing_keys_message = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" - raise ValidationError( - missing_keys_message - ) + raise ValidationError(missing_keys_message) class RunnerMetadataSchema(Schema, StripWhitespaceMixin): @@ -108,17 +106,13 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument ] if len(options) == 0: missing_metadata_option = "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" - raise ValidationError( - missing_metadata_option - ) + raise ValidationError(missing_metadata_option) if len(options) > 1: invalid_metadata_combination = ( "Only one of schema_name, schema_url or cir_instrument_id should be specified " f"in metadata, but {', '.join(options)} were provided" ) - raise ValidationError( - invalid_metadata_combination - ) + raise ValidationError(invalid_metadata_combination) def validate_questionnaire_claims( diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 5b89dbe80c..c7d257f415 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -23,10 +23,10 @@ def validate_identifier( # pylint: disable=no-self-use if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): - item_identifier_error_message = "Item identifier must be a non-empty string or non-negative integer" - raise ValidationError( - item_identifier_error_message + item_identifier_error_message = ( + "Item identifier must be a non-empty string or non-negative integer" ) + raise ValidationError(item_identifier_error_message) class ItemsData(Schema, StripWhitespaceMixin): @@ -42,10 +42,10 @@ def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data and data["identifier"] != self.context["identifier"]: - identifier_error_message = "Supplementary data did not return the specified Identifier" - raise ValidationError( - identifier_error_message + identifier_error_message = ( + "Supplementary data did not return the specified Identifier" ) + raise ValidationError(identifier_error_message) class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): @@ -64,24 +64,22 @@ def validate_payload( # pylint: disable=unused-argument ) -> None: if payload: if payload["dataset_id"] != self.context["dataset_id"]: - dataset_id_error_message = "Supplementary data did not return the specified Dataset ID" - raise ValidationError( - dataset_id_error_message + dataset_id_error_message = ( + "Supplementary data did not return the specified Dataset ID" ) + raise ValidationError(dataset_id_error_message) if payload["survey_id"] != self.context["survey_id"]: - survey_id_error_message = "Supplementary data did not return the specified Survey ID" - raise ValidationError( - survey_id_error_message + survey_id_error_message = ( + "Supplementary data did not return the specified Survey ID" ) + raise ValidationError(survey_id_error_message) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): mismatch_version_error_message = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" - raise ValidationError( - mismatch_version_error_message - ) + raise ValidationError(mismatch_version_error_message) def validate_supplementary_data_v1( diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index c0ae796cb5..5d2304b217 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -54,9 +54,7 @@ def __init__( if not self.is_location_valid(): invalid_location_message = f"location {self._current_location} is not valid" - raise InvalidLocationException( - invalid_location_message - ) + raise InvalidLocationException(invalid_location_message) @property def current_location(self) -> LocationType: diff --git a/app/views/handlers/block_factory.py b/app/views/handlers/block_factory.py index f5c9ba3ebd..aa1300aa44 100644 --- a/app/views/handlers/block_factory.py +++ b/app/views/handlers/block_factory.py @@ -62,16 +62,12 @@ def get_block_handler( "invalid_block_type": f"block id {block_id} is not valid for this schema", } if not block: - raise InvalidLocationException( - block_error_message["invalid_block_id"] - ) + raise InvalidLocationException(block_error_message["invalid_block_id"]) if schema.is_block_in_repeating_section(block_id=block["id"]) and not all( (list_name, list_item_id) ): - raise InvalidLocationException( - block_error_message["invalid_list"] - ) + raise InvalidLocationException(block_error_message["invalid_list"]) block_type = block["type"] block_class = BLOCK_MAPPINGS.get(block_type) diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index e6e45d0c8b..2a92af20ba 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -231,9 +231,7 @@ def _check_individual_response_count(self) -> None: >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): response_limit_message = "Individual response limit has been reached" - raise IndividualResponseLimitReached( - response_limit_message - ) + raise IndividualResponseLimitReached(response_limit_message) def _update_individual_response_count(self) -> None: response_metadata = self._questionnaire_store.data_stores.response_metadata From 1bb9e023ffacf23d6170e43ce4d18c15729a30df Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Mon, 28 Apr 2025 15:13:26 +0100 Subject: [PATCH 540/567] changed the attribute of the class to uppercase --- app/authentication/user.py | 4 ++-- app/authentication/user_id_generator.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/authentication/user.py b/app/authentication/user.py index b2211d9ffc..3d9708fb04 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -2,11 +2,11 @@ class User(UserMixin): - missing_user_session_error = "No user_id or user_ik found in session" + MISSING_USER_SESSION_ERROR = "No user_id or user_ik found in session" def __init__(self, user_id: str | None, user_ik: str | None) -> None: if user_id and user_ik: self.user_id = user_id self.user_ik = user_ik else: - raise ValueError(self.missing_user_session_error) + raise ValueError(self.MISSING_USER_SESSION_ERROR) diff --git a/app/authentication/user_id_generator.py b/app/authentication/user_id_generator.py index bf946c49a7..73021138df 100644 --- a/app/authentication/user_id_generator.py +++ b/app/authentication/user_id_generator.py @@ -11,14 +11,14 @@ class UserIDGenerator: - user_id_salt_error = "user_id_salt is required" - user_ik_salt_error = "user_ik_salt is required" + USER_ID_SALT_ERROR = "user_id_salt is required" + USER_IK_SALT_ERROR = "user_ik_salt is required" def __init__(self, iterations: int, user_id_salt: str, user_ik_salt: str) -> None: if user_id_salt is None: - raise ValueError(self.user_id_salt_error) + raise ValueError(self.USER_ID_SALT_ERROR) if user_ik_salt is None: - raise ValueError(self.user_ik_salt_error) + raise ValueError(self.USER_IK_SALT_ERROR) self._iterations = iterations self._user_id_salt = user_id_salt From 6d1d65282ae130c946cbddc8d908c014cb296400 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Tue, 29 Apr 2025 13:48:37 +0100 Subject: [PATCH 541/567] Schemas v5.32.0 (#1653) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c11f85f3bf..77e9c30c8b 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.31.0 +v5.32.0 From 03f1bcf3cdb4623a743bc620758335efc0db91b3 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 29 Apr 2025 14:11:28 +0100 Subject: [PATCH 542/567] Added a dict and updated missingEnvironmentVariable class --- app/setup.py | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/app/setup.py b/app/setup.py index 5df3889803..edbda24ea6 100644 --- a/app/setup.py +++ b/app/setup.py @@ -84,9 +84,20 @@ logger = get_logger() +UNKNOWN_IMPLEMENTED_ERROR = { + "unknown_storage_backend": "Unknown EQ_STORAGE_BACKEND", + "unknown_submission": "Unknown EQ_SUBMISSION_BACKEND", + "unknown_submit_confirmation": "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND", + "unknown_token_backend": "Unknown OIDC_TOKEN_BACKEND", + "unknown_publisher_backend": "Unknown EQ_PUBLISHER_BACKEND", + "unknown_feedback_backend": "Unknown EQ_FEEDBACK_BACKEND", +} + class MissingEnvironmentVariable(Exception): - pass + MISSING_BUCKET_ID_MESSAGE = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" + MISSING_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST Missing" + MISSING_SECONDARY_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" class AWSReverseProxied: @@ -293,8 +304,7 @@ def setup_storage(application): elif application.config["EQ_STORAGE_BACKEND"] == "dynamodb": setup_dynamodb(application) else: - unknown_storage_backend = "Unknown EQ_STORAGE_BACKEND" - raise NotImplementedError(unknown_storage_backend) + raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_storage_backend"]) setup_redis(application) @@ -332,9 +342,9 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): - missing_bucket_id_message = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" - raise MissingEnvironmentVariable(missing_bucket_id_message) - + raise MissingEnvironmentVariable( + MissingEnvironmentVariable.MISSING_BUCKET_ID_MESSAGE + ) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) elif application.config["EQ_SUBMISSION_BACKEND"] == "rabbitmq": @@ -342,13 +352,13 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - missing_host_message = "Setting EQ_RABBITMQ_HOST Missing" - raise MissingEnvironmentVariable(missing_host_message) + raise MissingEnvironmentVariable( + MissingEnvironmentVariable.MISSING_HOST_MESSAGE + ) if not secondary_host: - missing_secondary_host_message = ( - "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" + raise MissingEnvironmentVariable( + MissingEnvironmentVariable.MISSING_SECONDARY_HOST_MESSAGE ) - raise MissingEnvironmentVariable(missing_secondary_host_message) application.eq["submitter"] = RabbitMQSubmitter( host=host, @@ -367,8 +377,7 @@ def setup_submitter(application): application.eq["submitter"] = LogSubmitter() else: - unknown_submission_message = "Unknown EQ_SUBMISSION_BACKEND" - raise NotImplementedError(unknown_submission_message) + raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_submission"]) def setup_task_client(application): @@ -377,8 +386,9 @@ def setup_task_client(application): elif application.config["EQ_SUBMISSION_CONFIRMATION_BACKEND"] == "log": application.eq["cloud_tasks"] = LogCloudTaskPublisher() else: - unknown_submit_confirmation = "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND" - raise NotImplementedError(unknown_submit_confirmation) + raise NotImplementedError( + UNKNOWN_IMPLEMENTED_ERROR["unknown_submit_confirmation"] + ) def setup_oidc(application): @@ -403,8 +413,7 @@ def client_ids_exist(): application.eq["oidc_credentials_service"] = OIDCCredentialsServiceLocal() else: - unknown_token_backend = "Unknown OIDC_TOKEN_BACKEND" - raise NotImplementedError(unknown_token_backend) + raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_token_backend"]) def setup_publisher(application): @@ -415,8 +424,9 @@ def setup_publisher(application): application.eq["publisher"] = LogPublisher() else: - unknown_publisher_backend_message = "Unknown EQ_PUBLISHER_BACKEND" - raise NotImplementedError(unknown_publisher_backend_message) + raise NotImplementedError( + UNKNOWN_IMPLEMENTED_ERROR["unknown_publisher_backend"] + ) def setup_feedback(application): @@ -434,8 +444,7 @@ def setup_feedback(application): elif application.config["EQ_FEEDBACK_BACKEND"] == "log": application.eq["feedback_submitter"] = LogFeedbackSubmitter() else: - unknown_feedback_backend_message = "Unknown EQ_FEEDBACK_BACKEND" - raise NotImplementedError(unknown_feedback_backend_message) + raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_feedback_backend"]) def add_blueprints(application): From 945ee2ed4bbe10a9587c5766a590e4c540f2f2af Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 2 May 2025 17:26:52 +0100 Subject: [PATCH 543/567] updated the code to improve readability --- app/data_models/answer_store.py | 8 +++--- app/data_models/list_store.py | 4 +-- app/forms/questionnaire_form.py | 3 +- app/forms/validators.py | 4 +-- app/questionnaire/placeholder_transforms.py | 4 +-- app/questionnaire/rules/operations.py | 2 +- app/questionnaire/rules/rule_evaluator.py | 4 +-- app/questionnaire/rules/utils.py | 2 +- app/questionnaire/value_source_resolver.py | 12 ++++---- app/routes/questionnaire.py | 2 +- app/routes/session.py | 9 +++--- app/services/supplementary_data.py | 2 +- app/settings.py | 6 ++-- app/setup.py | 19 ++++++------ app/storage/datastore.py | 5 ++-- app/storage/storage.py | 4 +-- app/storage/storage_encryption.py | 13 +++++---- app/utilities/metadata_parser_v2.py | 4 +-- app/utilities/supplementary_data_parser.py | 32 +++++++++------------ app/views/handlers/block.py | 5 ++-- app/views/handlers/individual_response.py | 2 +- app/views/handlers/section.py | 4 +-- app/views/handlers/submit_questionnaire.py | 5 ++-- 23 files changed, 75 insertions(+), 80 deletions(-) diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index c12b7d168d..46546cced5 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -53,11 +53,11 @@ def _build_map( @staticmethod def _validate(answer: Answer) -> None: + answer_type_error = ( + f"Method only supports Answer argument type, found type: {type(answer)}" + ) if not isinstance(answer, Answer): - answer_type_error_message = ( - f"Method only supports Answer argument type, found type: {type(answer)}" - ) - raise TypeError(answer_type_error_message) + raise TypeError(answer_type_error) @property def is_dirty(self) -> bool: diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 54db73ff8c..9762f59f7a 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -82,10 +82,10 @@ def first(self) -> str: try: return self.items[0] except IndexError as e: - empty_list_error_message = ( + empty_list_error = ( f"unable to access first item in list, list '{self.name}' is empty" ) - raise IndexError(empty_list_error_message) from e + raise IndexError(empty_list_error) from e @property def count(self) -> int: diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 8ab0ef5e85..3408b56836 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -309,6 +309,7 @@ def _get_period_range_for_single_date( date_from: Mapping[str, dict], date_to: Mapping[str, dict], ) -> timedelta: + invalid_period_range = "Period range must have a start and end date" list_item_id = self.location.list_item_id if self.location else None value_source_resolver = ValueSourceResolver( data_stores=self.data_stores, @@ -337,8 +338,6 @@ def _get_period_range_for_single_date( ) if not min_period_date or not max_period_date: - - invalid_period_range = "Period range must have a start and end date" raise ValueError(invalid_period_range) # Work out the largest possible range, for date range question diff --git a/app/forms/validators.py b/app/forms/validators.py index 1c775c8344..3d12af38f1 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -424,8 +424,8 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: - invalid_mutiple_conditions = "There are multiple conditions, but equals is not one of them. We only support <= and >=" - raise ValueError(invalid_mutiple_conditions) from exc + invalid_multiple_condition = "There are multiple conditions, but equals is not one of them. We only support <= and >=" + raise ValueError(invalid_multiple_condition) from exc condition = f"{conditions[0]} or equals" else: diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 5fb677ae7c..dd5c0598d7 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -300,6 +300,7 @@ def format_ordinal( return f"{number_to_format}{indicator}" def get_ordinal_indicator(self, number_to_format: int) -> str: + invalid_language_code = f"Language code '{self.language}' not implemented." if self.language in ["en", "eo"]: if 11 <= number_to_format % 100 <= 13: return "th" @@ -326,8 +327,7 @@ def get_ordinal_indicator(self, number_to_format: int) -> str: 19: "eg", }.get(number_to_format, "fed") - invalid_lang_code_error = f"Language code '{self.language}' not implemented." - raise NotImplementedError(invalid_lang_code_error) + raise NotImplementedError(invalid_language_code) def first_non_empty_item(self, items: Sequence[str]) -> str: """ diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 7079ebd576..3992c50c5e 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -135,6 +135,7 @@ def resolve_date_from_string( offset: DateOffset | None = None, offset_by_full_weeks: bool = False, ) -> date | None: + invalid_negative_days_offset = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" datetime_value = parse_datetime(date_string) if not datetime_value: return None @@ -146,7 +147,6 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: - invalid_negative_days_offset = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" raise ValueError(invalid_negative_days_offset) days_difference = ( diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index a53308945e..f4456430c6 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -55,10 +55,10 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operator_name = next(iter(rule)) operator = Operator(operator_name, self.operations) operands = rule[operator_name] + invalid_rule = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" if not isinstance(operands, Sequence): - invalid_rule_message = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" - raise TypeError(invalid_rule_message) + raise TypeError(invalid_rule) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/rules/utils.py b/app/questionnaire/rules/utils.py index 1ea4ed3c1e..ab2031f0e7 100644 --- a/app/questionnaire/rules/utils.py +++ b/app/questionnaire/rules/utils.py @@ -24,6 +24,7 @@ def parse_datetime(date_string: str | None) -> datetime | None: Convert `date` from string into `datetime` object. `date` can be 'YYYY-MM-DD', 'YYYY-MM','now' or ISO 8601 format. Note that in the shorthand YYYY-MM format, day_of_month is assumed to be 1. """ + invalid_date_format = f"'{date_string}' is not in a valid date format" if not date_string: return None @@ -33,5 +34,4 @@ def parse_datetime(date_string: str | None) -> datetime | None: try: return parse_iso_8601_datetime(date_string) except ValueError as ex: - invalid_date_format = f"'{date_string}' is not in a valid date format" raise ValueError(invalid_date_format) from ex diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 0a331e3464..7d58acfd48 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -95,12 +95,12 @@ def _resolve_list_item_id_for_answer_id(self, answer_id: str) -> str | None: def _resolve_list_item_id_for_value_source( self, value_source: Mapping ) -> str | None: + invalid_selector_location = ( + "list_item_selector source location used without location" + ) if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: - invalid_selector_location = ( - "list_item_selector source location used without location" - ) raise InvalidLocationException(invalid_selector_location) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -203,6 +203,7 @@ def _resolve_progress_value_source( ) -> ValueSourceEscapedTypes | ValueSourceTypes | None: identifier = value_source["identifier"] selector = value_source["selector"] + location_required_error = "location is required to resolve block progress" if selector == "section": # List item id is set to None here as we do not support checking progress value sources for # repeating sections @@ -212,9 +213,6 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - location_required_error = ( - "location is required to resolve block progress" - ) raise ValueError(location_required_error) if not self._is_block_on_path(identifier): @@ -318,9 +316,9 @@ def _resolve_supplementary_data_source( def get_calculation_operator( calculation_type: str, ) -> Callable[[Iterable[IntOrDecimal]], IntOrDecimal]: + invalid_calculation_type = f"Invalid calculation_type: {calculation_type}" if calculation_type == "sum": return sum - invalid_calculation_type = f"Invalid calculation_type: {calculation_type}" raise NotImplementedError(invalid_calculation_type) def resolve( diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index e686840b08..3d702e2665 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -72,11 +72,11 @@ @questionnaire_blueprint.before_request @login_required def before_questionnaire_request() -> Response | None: + repeated_submission_error = "The Questionnaire has been previously submitted" if request.method == "OPTIONS": return None if cookie_session.get("submitted"): - repeated_submission_error = "The Questionnaire has been previously submitted" raise PreviouslySubmittedException(repeated_submission_error) metadata = get_metadata(current_user) diff --git a/app/routes/session.py b/app/routes/session.py index 717962cb71..1f326528a7 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -223,8 +223,8 @@ def _validate_supplementary_data_lists( """ supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: - missing_schema_lists_error = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" - raise ValidationError(missing_schema_lists_error) + missing_schema_lists = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" + raise ValidationError(missing_schema_lists) def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: @@ -300,21 +300,22 @@ def get_signed_out() -> Response | str: def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: + runner_claims_error = "Invalid runner claims" try: return validate_runner_claims_v2(decrypted_token) except ValidationError as e: - runner_claims_error = "Invalid runner claims" raise InvalidTokenException(runner_claims_error) from e def get_questionnaire_claims( decrypted_token: Mapping, schema_metadata: Iterable[Mapping[str, str]] ) -> dict: + questionnaire_claims_error = "Invalid questionnaire claims" + try: claims = decrypted_token.get("survey_metadata", {}).get("data", {}) return validate_questionnaire_claims(claims, schema_metadata, unknown=INCLUDE) except ValidationError as e: - questionnaire_claims_error = "Invalid questionnaire claims" raise InvalidTokenException(questionnaire_claims_error) from e diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index f62b00434c..06ae1452a7 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -132,6 +132,7 @@ def validate_supplementary_data( survey_id: str, sds_schema_version: str | None = None, ) -> dict[str, str | dict | int | list]: + invalid_supplementary_data = "Invalid supplementary data" try: return validate_supplementary_data_v1( supplementary_data=supplementary_data, @@ -141,5 +142,4 @@ def validate_supplementary_data( sds_schema_version=sds_schema_version, ) except ValidationError as e: - invalid_supplementary_data = "Invalid supplementary data" raise ValidationError(invalid_supplementary_data) from e diff --git a/app/settings.py b/app/settings.py index 463f262d63..426e4d9003 100644 --- a/app/settings.py +++ b/app/settings.py @@ -35,18 +35,18 @@ def read_file(file_name): def get_env_or_fail(key): + missing_key_error = f"Setting '{key}' Missing" value = os.getenv(key) if value is None: - missing_key_error = f"Setting '{key}' Missing" raise ValueError(missing_key_error) return value def utcoffset_or_fail(date_value, key): + missing_datetime_offset = f"'{key}' datetime offset missing" if date_value.utcoffset() is None: - missing_datetime_offset_error = f"'{key}' datetime offset missing" - raise ValueError(missing_datetime_offset_error) + raise ValueError(missing_datetime_offset) return date_value diff --git a/app/setup.py b/app/setup.py index edbda24ea6..718f8c9a07 100644 --- a/app/setup.py +++ b/app/setup.py @@ -392,18 +392,19 @@ def setup_task_client(application): def setup_oidc(application): + missing_sds_client_id = "Setting SDS_OAUTH2_CLIENT_ID Missing" + missing_cir_client_id = "Setting CIR_OAUTH2_CLIENT_ID Missing" + missing_token_backen = "Setting OIDC_TOKEN_BACKEND Missing" + def client_ids_exist(): if not application.config.get("SDS_OAUTH2_CLIENT_ID"): - missing_sds_client_id = "Setting SDS_OAUTH2_CLIENT_ID Missing" raise MissingEnvironmentVariable(missing_sds_client_id) if not application.config.get("CIR_OAUTH2_CLIENT_ID"): - missing_cir_client_id = "Setting CIR_OAUTH2_CLIENT_ID Missing" raise MissingEnvironmentVariable(missing_cir_client_id) if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): - missing_token_backend = "Setting OIDC_TOKEN_BACKEND Missing" - raise MissingEnvironmentVariable(missing_token_backend) + raise MissingEnvironmentVariable(missing_token_backen) if oidc_token_backend == "gcp": client_ids_exist() @@ -430,12 +431,10 @@ def setup_publisher(application): def setup_feedback(application): + missing_feedback__bucket_id = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): - missing_feedback_bucket_id_message = ( - "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" - ) - raise MissingEnvironmentVariable(missing_feedback_bucket_id_message) + raise MissingEnvironmentVariable(missing_feedback__bucket_id) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( bucket_name=bucket_name @@ -480,10 +479,10 @@ def add_blueprints(application): def setup_secure_cookies(application): + invalid_secret_key = "Application secret key does not exist" secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") if not secret_key: - no_secret_key_message = "Application secret key does not exist" - raise ValueError(no_secret_key_message) + raise ValueError(invalid_secret_key) application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/storage/datastore.py b/app/storage/datastore.py index ab6ca445d0..3fcf1ae982 100644 --- a/app/storage/datastore.py +++ b/app/storage/datastore.py @@ -9,14 +9,15 @@ class Datastore(StorageHandler): + UNSUPPORTED_UNIQUE_KEY = "Unique key checking not supported" + def __init__(self, client: datastore.Client) -> None: super().__init__(client) @Retry() def put(self, model: ModelTypes, overwrite: bool = True) -> bool: if not overwrite: - unsupported_unique_key = "Unique key checking not supported" - raise NotImplementedError(unsupported_unique_key) + raise NotImplementedError(self.UNSUPPORTED_UNIQUE_KEY) storage_model = StorageModel(model_type=type(model)) serialized_item = storage_model.serialize(model) diff --git a/app/storage/storage.py b/app/storage/storage.py index a860e7d44d..3290d48031 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -29,6 +29,7 @@ class TableConfig(TypedDict, total=False): class StorageModel: + INVALID_MODEL_TYPE = "Invalid model_type provided" TABLE_CONFIG_BY_TYPE: dict[type[ModelTypes], TableConfig] = { app_models.QuestionnaireState: { "key_field": "user_id", @@ -55,8 +56,7 @@ def __init__(self, model_type: type[ModelTypes]) -> None: self._model_type = model_type if self._model_type not in self.TABLE_CONFIG_BY_TYPE: - invalid_model_type_message = "Invalid model_type provided" - raise KeyError(invalid_model_type_message) + raise KeyError(self.INVALID_MODEL_TYPE) self._config = self.TABLE_CONFIG_BY_TYPE[self._model_type] self._schema = self._config["schema"]() diff --git a/app/storage/storage_encryption.py b/app/storage/storage_encryption.py index f09a9421ce..335ca38e03 100644 --- a/app/storage/storage_encryption.py +++ b/app/storage/storage_encryption.py @@ -11,18 +11,19 @@ class StorageEncryption: + MISSING_USER_ID = "user_id not provided" + MISSING_USER_IK = "user_ik not provided" + MISSING_PEPPER = "pepper not provided" + def __init__( self, user_id: str | None, user_ik: str | None, pepper: str | None ) -> None: if not user_id: - missing_user_id_message = "user_id not provided" - raise ValueError(missing_user_id_message) + raise ValueError(self.MISSING_USER_ID) if not user_ik: - missing_user_ik_message = "user_ik not provided" - raise ValueError(missing_user_ik_message) + raise ValueError(self.MISSING_USER_IK) if not pepper: - missing_pepper_message = "pepper not provided" - raise ValueError(missing_pepper_message) + raise ValueError(self.MISSING_PEPPER) self.key = self._generate_key(user_id, user_ik, pepper) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 156e1ba87a..18065d29c7 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -63,8 +63,8 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument ] if missing_receipting_keys: - missing_keys_message = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" - raise ValidationError(missing_keys_message) + invalid_receipting_keys = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" + raise ValidationError(invalid_receipting_keys) class RunnerMetadataSchema(Schema, StripWhitespaceMixin): diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index c7d257f415..be1cafaa3f 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -15,6 +15,9 @@ class ItemsSchema(Schema): identifier = fields.Field(required=True) + ITEM_IDENTIFIER_ERROR = ( + "Item identifier must be a non-empty string or non-negative integer" + ) @validates("identifier") def validate_identifier( # pylint: disable=no-self-use @@ -23,10 +26,7 @@ def validate_identifier( # pylint: disable=no-self-use if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): - item_identifier_error_message = ( - "Item identifier must be a non-empty string or non-negative integer" - ) - raise ValidationError(item_identifier_error_message) + raise ValidationError(self.ITEM_IDENTIFIER_ERROR) class ItemsData(Schema, StripWhitespaceMixin): @@ -41,11 +41,11 @@ class SupplementaryData(Schema, StripWhitespaceMixin): def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: + sds_identifier_error = ( + "Supplementary data did not return the specified Identifier" + ) if data and data["identifier"] != self.context["identifier"]: - identifier_error_message = ( - "Supplementary data did not return the specified Identifier" - ) - raise ValidationError(identifier_error_message) + raise ValidationError(sds_identifier_error) class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): @@ -62,24 +62,20 @@ class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): def validate_payload( # pylint: disable=unused-argument self, payload: Mapping, **kwargs: Any ) -> None: + dataset_id_error = "Supplementary data did not return the specified Dataset ID" + survey_id_error = "Supplementary data did not return the specified Survey ID" + mismatch_sds_version = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" if payload: if payload["dataset_id"] != self.context["dataset_id"]: - dataset_id_error_message = ( - "Supplementary data did not return the specified Dataset ID" - ) - raise ValidationError(dataset_id_error_message) + raise ValidationError(dataset_id_error) if payload["survey_id"] != self.context["survey_id"]: - survey_id_error_message = ( - "Supplementary data did not return the specified Survey ID" - ) - raise ValidationError(survey_id_error_message) + raise ValidationError(survey_id_error) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): - mismatch_version_error_message = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" - raise ValidationError(mismatch_version_error_message) + raise ValidationError(mismatch_sds_version) def validate_supplementary_data_v1( diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 5d2304b217..7a4b79b8f7 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -51,10 +51,9 @@ def __init__( ) self.resume = "resume" in request_args - + invalid_location = f"location {self._current_location} is not valid" if not self.is_location_valid(): - invalid_location_message = f"location {self._current_location} is not valid" - raise InvalidLocationException(invalid_location_message) + raise InvalidLocationException(invalid_location) @property def current_location(self) -> LocationType: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 2a92af20ba..2d6e6c4f5c 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -224,13 +224,13 @@ def _publish_fulfilment_request(self, mobile_number: str | None = None) -> None: raise IndividualResponseFulfilmentRequestPublicationFailed from exc def _check_individual_response_count(self) -> None: + response_limit_message = "Individual response limit has been reached" if ( self._questionnaire_store.data_stores.response_metadata.get( "individual_response_count", 0 ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): - response_limit_message = "Individual response limit has been reached" raise IndividualResponseLimitReached(response_limit_message) def _update_individual_response_count(self) -> None: diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index a7e98372ad..dad60fef5a 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -26,8 +26,8 @@ def __init__( data_stores=self._questionnaire_store.data_stores, ) if not self._is_valid_location(): - invalid_location_message = f"location {self._section_id} is not valid" - raise InvalidLocationException(invalid_location_message) + invalid_loaction = f"location {self._section_id} is not valid" + raise InvalidLocationException(invalid_loaction) self.current_location = Location( section_id=self._section_id, diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index a73d2f84e7..6e13a43bfe 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -11,6 +11,8 @@ class SubmitQuestionnaireHandler: + SUBMIT_PAGE_MESSAGE = "Submit page not enabled" + def __init__( self, schema: QuestionnaireSchema, @@ -18,8 +20,7 @@ def __init__( language: str, ): if not schema.is_flow_linear: - submit_page_message = "Submit page not enabled" - raise InvalidLocationException(submit_page_message) + raise InvalidLocationException(self.SUBMIT_PAGE_MESSAGE) self._schema = schema self._questionnaire_store = questionnaire_store From 363fb4c17588bda699f742e4ab30e1d9a088371d Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 6 May 2025 13:35:36 +0100 Subject: [PATCH 544/567] Refactor code to use list comprehensions and list.extend for adherence to Python best practices --- app/data_models/answer_store.py | 9 +++++---- app/data_models/relationship_store.py | 15 +++++++-------- app/forms/field_handlers/number_handler.py | 1 - app/forms/validators.py | 1 - app/helpers/language_helper.py | 7 ++++--- app/questionnaire/questionnaire_schema.py | 6 +++--- app/questionnaire/router.py | 10 ++++------ app/questionnaire/schema_utils.py | 5 +---- app/views/contexts/question.py | 8 +++++--- app/views/handlers/question.py | 10 +++++----- pyproject.toml | 1 - 11 files changed, 34 insertions(+), 39 deletions(-) diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index d79796a92e..cb03586b09 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -141,10 +141,11 @@ def remove_all_answers_for_list_item_ids(self, *list_item_ids: str) -> None: *Not efficient.* """ - keys_to_delete = [] - for answer in self: - if answer.list_item_id and answer.list_item_id in list_item_ids: - keys_to_delete.append((answer.answer_id, answer.list_item_id)) + keys_to_delete = [ + (answer.answer_id, answer.list_item_id) + for answer in self + if answer.list_item_id and answer.list_item_id in list_item_ids + ] for key in keys_to_delete: del self.answer_map[key] diff --git a/app/data_models/relationship_store.py b/app/data_models/relationship_store.py index 1fd0d8ed59..26ef35b927 100644 --- a/app/data_models/relationship_store.py +++ b/app/data_models/relationship_store.py @@ -81,16 +81,15 @@ def remove_all_relationships_for_list_item_id(self, list_item_id: str) -> None: This method iterates through the entire list of relationships. """ - keys_to_delete = [] - - for relationship in self: - if list_item_id in ( + keys_to_delete = [ + (relationship.list_item_id, relationship.to_list_item_id) + for relationship in self + if list_item_id + in ( relationship.to_list_item_id, relationship.list_item_id, - ): - keys_to_delete.append( - (relationship.list_item_id, relationship.to_list_item_id) - ) + ) + ] for key in keys_to_delete: del self._relationships[key] diff --git a/app/forms/field_handlers/number_handler.py b/app/forms/field_handlers/number_handler.py index bc721d389a..78ff7a3e83 100644 --- a/app/forms/field_handlers/number_handler.py +++ b/app/forms/field_handlers/number_handler.py @@ -1,5 +1,4 @@ from functools import cached_property - from typing import Any from wtforms import DecimalField, IntegerField diff --git a/app/forms/validators.py b/app/forms/validators.py index 80e3e86cac..dacd8dbf53 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -6,7 +6,6 @@ from decimal import Decimal, InvalidOperation from typing import TYPE_CHECKING, Iterable, Mapping, Sequence - import flask_babel from babel import numbers from dateutil.relativedelta import relativedelta diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index 87f9afc70f..ea4d298bc4 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -45,10 +45,11 @@ def get_languages_context(current_language: str) -> dict[str, list[dict]] | None context = [] allowed_languages = g.get("allowed_languages") if allowed_languages and len(allowed_languages) > 1: - for language in allowed_languages: - context.append(_get_language_context(language, current_language)) + context.extend( + _get_language_context(language, current_language) + for language in allowed_languages + ) return {"languages": context} - if (language := cookie_session.get("language_code")) and language in LANGUAGE_TEXT: return {"languages": [_get_language_context(language, language)]} diff --git a/app/questionnaire/questionnaire_schema.py b/app/questionnaire/questionnaire_schema.py index 281bb3f33d..9248105e7f 100644 --- a/app/questionnaire/questionnaire_schema.py +++ b/app/questionnaire/questionnaire_schema.py @@ -1105,9 +1105,9 @@ def get_all_questions_for_block(block: Mapping) -> list[ImmutableDict]: if block.get("question"): all_questions.append(block["question"]) elif block.get("question_variants"): - for variant in block["question_variants"]: - all_questions.append(variant["question"]) - + all_questions.extend( + variant["question"] for variant in block["question_variants"] + ) return all_questions return [] diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index b8ceb78e80..b6dac49130 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -462,12 +462,10 @@ def full_routing_path(self) -> list[RoutingPath]: repeating_list = self._schema.get_repeating_list_for_section(section_id) if repeating_list: - for list_item_id in self._data_stores.list_store[repeating_list]: - full_routing_path.append( - self._path_finder.routing_path( - SectionKey(section_id, list_item_id) - ) - ) + full_routing_path.extend( + self._path_finder.routing_path(SectionKey(section_id, list_item_id)) + for list_item_id in self._data_stores.list_store[repeating_list] + ) else: full_routing_path.append( self._path_finder.routing_path(SectionKey(section_id)) diff --git a/app/questionnaire/schema_utils.py b/app/questionnaire/schema_utils.py index f5da936495..967c5fa1c8 100644 --- a/app/questionnaire/schema_utils.py +++ b/app/questionnaire/schema_utils.py @@ -35,8 +35,5 @@ def get_answers_from_question(question: Mapping) -> list: def get_answer_ids_in_block(block: Mapping) -> list[str]: question = block["question"] - answer_ids = [] - for answer in get_answers_from_question(question): - answer_ids.append(answer["id"]) - + answer_ids = [answer["id"] for answer in get_answers_from_question(question)] return answer_ids diff --git a/app/views/contexts/question.py b/app/views/contexts/question.py index 0be2ad4c4f..d09a612408 100644 --- a/app/views/contexts/question.py +++ b/app/views/contexts/question.py @@ -24,9 +24,11 @@ def build_question_context( answer_ids.append(answer["id"]) if answer["type"] in ("Checkbox", "Radio"): - for option in answer.get("options", []): - if "detail_answer" in option: - answer_ids.append(option["detail_answer"]["id"]) + answer_ids.extend( + option["detail_answer"]["id"] + for option in answer.get("options", []) + if "detail_answer" in option + ) for answer_id in answer_ids: context["form"]["answer_errors"][answer_id] = form.answer_errors(answer_id) diff --git a/app/views/handlers/question.py b/app/views/handlers/question.py index ad18cbf844..0bbf284709 100644 --- a/app/views/handlers/question.py +++ b/app/views/handlers/question.py @@ -217,11 +217,11 @@ def get_return_to_hub_url(self) -> str | None: return url_for(".get_questionnaire") def clear_radio_answers(self) -> None: - answer_ids_to_remove = [] - for answer in self.rendered_block["question"]["answers"]: - if answer["type"] == "Radio": - answer_ids_to_remove.append(answer["id"]) - + answer_ids_to_remove = [ + answer["id"] + for answer in self.rendered_block["question"]["answers"] + if answer["type"] == "Radio" + ] if answer_ids_to_remove: self.questionnaire_store_updater.remove_answers( answer_ids_to_remove, self.current_location.list_item_id diff --git a/pyproject.toml b/pyproject.toml index 780c2161c2..6a504e5e1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,7 +151,6 @@ extend-ignore = [ "TRY400", # Use logging.exception instead of logging.error "TRY201", # Avoid using `raise Exception` without specifying an exception class "TRY300", # Consider moving this statement to an `else` block - "PERF401", # Use a list comprehension to create a transformed list "RET501", # Do not explicitly `return None` in function if it is the only possible return value "RET503", # Missing explicit `return` at the end of function able to return non-`None` value "RET504", # Unnecessary assignment to `context` before `return` statement From a5f35b82a9847cc16d3d19f40ed8965962bf7752 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 6 May 2025 15:07:20 +0100 Subject: [PATCH 545/567] added typehints to variables --- app/helpers/language_helper.py | 2 +- app/questionnaire/router.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index ea4d298bc4..cfa0649e0b 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -42,7 +42,7 @@ def handle_language(metadata: MetadataProxy | None = None) -> None: def get_languages_context(current_language: str) -> dict[str, list[dict]] | None: - context = [] + context: list[dict[str, str | bool]] = [] allowed_languages = g.get("allowed_languages") if allowed_languages and len(allowed_languages) > 1: context.extend( diff --git a/app/questionnaire/router.py b/app/questionnaire/router.py index b6dac49130..8424e636b5 100644 --- a/app/questionnaire/router.py +++ b/app/questionnaire/router.py @@ -457,7 +457,7 @@ def get_last_location_in_section(routing_path: RoutingPath) -> Location: ) def full_routing_path(self) -> list[RoutingPath]: - full_routing_path = [] + full_routing_path: list[RoutingPath] = [] for section_id in self.enabled_section_ids: repeating_list = self._schema.get_repeating_list_for_section(section_id) From dde2c8bfe621f8c26b8ecc9f7ceb854a6f539f85 Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 7 May 2025 08:11:29 +0100 Subject: [PATCH 546/567] Fix validate schemas script (#1650) --- scripts/validate_test_schemas.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/validate_test_schemas.py b/scripts/validate_test_schemas.py index 12b7ff2fbb..445280a41a 100644 --- a/scripts/validate_test_schemas.py +++ b/scripts/validate_test_schemas.py @@ -86,7 +86,6 @@ def validate_schema(schema_path): def main(): # pylint: disable=broad-exception-caught - error = False passed = 0 failed = 0 @@ -122,13 +121,13 @@ def main(): "\033[31mHTTP Status @ /validate: %s\033[0m", result_response ) logging.error("\033[31mHTTP Status: %s\033[0m", formatted_json) - error = True failed += 1 except Exception as e: logging.error("\033[31mError processing %s: %s\033[0m", schema, e) + failed += 1 logging.info("\033[32m%s passed\033[0m - \033[31m%s failed\033[0m", passed, failed) - if error: + if passed != len(schemas): sys.exit(1) From 56f88a2fe85ab386d3140dd00211e2269a39f917 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 7 May 2025 17:14:13 +0100 Subject: [PATCH 547/567] refactored the code to avoid needing to extend the list. --- app/helpers/language_helper.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index cfa0649e0b..f274e3bbc3 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -42,14 +42,13 @@ def handle_language(metadata: MetadataProxy | None = None) -> None: def get_languages_context(current_language: str) -> dict[str, list[dict]] | None: - context: list[dict[str, str | bool]] = [] allowed_languages = g.get("allowed_languages") if allowed_languages and len(allowed_languages) > 1: - context.extend( - _get_language_context(language, current_language) - for language in allowed_languages - ) - return {"languages": context} + context: list[dict[str, str | bool]] = [ + _get_language_context(language, current_language) + for language in allowed_languages + ] + return {"languages": context} if (language := cookie_session.get("language_code")) and language in LANGUAGE_TEXT: return {"languages": [_get_language_context(language, language)]} From dab7c919729ad68422c3fa1dd8fd89c13d2b60d1 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Wed, 7 May 2025 17:32:57 +0100 Subject: [PATCH 548/567] fixed indentation issue --- app/helpers/language_helper.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/helpers/language_helper.py b/app/helpers/language_helper.py index f274e3bbc3..0d08b9d460 100644 --- a/app/helpers/language_helper.py +++ b/app/helpers/language_helper.py @@ -44,11 +44,12 @@ def handle_language(metadata: MetadataProxy | None = None) -> None: def get_languages_context(current_language: str) -> dict[str, list[dict]] | None: allowed_languages = g.get("allowed_languages") if allowed_languages and len(allowed_languages) > 1: - context: list[dict[str, str | bool]] = [ - _get_language_context(language, current_language) - for language in allowed_languages - ] - return {"languages": context} + context: list[dict[str, str | bool]] = [ + _get_language_context(language, current_language) + for language in allowed_languages + ] + return {"languages": context} + if (language := cookie_session.get("language_code")) and language in LANGUAGE_TEXT: return {"languages": [_get_language_context(language, language)]} From c1451620b4d6362074e9305ba9792f73dfba638c Mon Sep 17 00:00:00 2001 From: Viraj Patel <144226112+VirajP1002@users.noreply.github.com> Date: Wed, 14 May 2025 14:08:41 +0100 Subject: [PATCH 549/567] Unpin Poetry Dotenv Plugin and Upgrade Poetry to v2.1.2 (#1628) --- .github/workflows/pull_request.yml | 12 +- Dockerfile | 2 +- README.md | 2 +- poetry.lock | 631 ++++++++++++++--------------- 4 files changed, 308 insertions(+), 339 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 7e4f42e8ca..17d0da37de 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -22,7 +22,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.4 + version: 2.1.2 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -56,7 +56,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.4 + version: 2.1.2 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -95,14 +95,14 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.4 + version: 2.1.2 virtualenvs-create: true - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" - name: Install dotenv plugin - run: poetry self add poetry-plugin-dotenv@2.6.1 + run: poetry self add poetry-plugin-dotenv@2.9.0 - name: Load templates run: make load-design-system-templates - name: Compile translations @@ -122,7 +122,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.4 + version: 2.1.2 virtualenvs-create: true - uses: actions/setup-python@v5 with: @@ -151,7 +151,7 @@ jobs: - name: Install Poetry uses: snok/install-poetry@v1 with: - version: 1.8.4 + version: 2.1.2 virtualenvs-create: true - uses: actions/setup-python@v5 with: diff --git a/Dockerfile b/Dockerfile index 412079612d..38263fc195 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ COPY pyproject.toml pyproject.toml COPY poetry.lock poetry.lock RUN groupadd -r appuser && useradd -r -g appuser -u 9000 appuser && chown -R appuser:appuser . -RUN pip install "poetry==1.8.4" && \ +RUN pip install "poetry==2.1.2" && \ poetry config virtualenvs.create false && \ poetry install --only main && \ make build diff --git a/README.md b/README.md index fcf50cc66c..7e899af335 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ pip install --upgrade pip setuptools Install poetry, poetry dotenv plugin and install dependencies: ``` shell -curl -sSL https://install.python-poetry.org | python3 - --version 1.8.4 +curl -sSL https://install.python-poetry.org | python3 - --version 2.1.2 poetry self add poetry-plugin-dotenv poetry install ``` diff --git a/poetry.lock b/poetry.lock index 3853defa5d..205b5c2947 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "astroid" @@ -129,18 +129,18 @@ files = [ [[package]] name = "boto3" -version = "1.37.19" +version = "1.37.23" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "boto3-1.37.19-py3-none-any.whl", hash = "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130"}, - {file = "boto3-1.37.19.tar.gz", hash = "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8"}, + {file = "boto3-1.37.23-py3-none-any.whl", hash = "sha256:fc462b9fd738bd8a1c121d94d237c6b6a05a2c1cc709d16f5223acb752f7310b"}, + {file = "boto3-1.37.23.tar.gz", hash = "sha256:82f4599a34f5eb66e916b9ac8547394f6e5899c19580e74b60237db04cf66d1e"}, ] [package.dependencies] -botocore = ">=1.37.19,<1.38.0" +botocore = ">=1.37.23,<1.38.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.11.0,<0.12.0" @@ -149,14 +149,14 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.37.19" +version = "1.37.23" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "botocore-1.37.19-py3-none-any.whl", hash = "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415"}, - {file = "botocore-1.37.19.tar.gz", hash = "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8"}, + {file = "botocore-1.37.23-py3-none-any.whl", hash = "sha256:ffbe1f5958adb1c50d72d3ad1018cb265fe349248c08782d334601c0814f0e38"}, + {file = "botocore-1.37.23.tar.gz", hash = "sha256:3a249c950cef9ee9ed7b2278500ad83a4ad6456bc433a43abd1864d1b61b2acb"}, ] [package.dependencies] @@ -186,10 +186,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -202,14 +198,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -220,24 +210,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -247,10 +221,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -262,10 +232,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -278,10 +244,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -294,10 +256,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -598,75 +556,75 @@ cron = ["capturer (>=2.4)"] [[package]] name = "coverage" -version = "7.7.0" +version = "7.8.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "coverage-7.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a538a23119d1e2e2ce077e902d02ea3d8e0641786ef6e0faf11ce82324743944"}, - {file = "coverage-7.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1586ad158523f4133499a4f322b230e2cfef9cc724820dbd58595a5a236186f4"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6c96d69928a3a6767fab8dc1ce8a02cf0156836ccb1e820c7f45a423570d98"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f18d47641282664276977c604b5a261e51fefc2980f5271d547d706b06a837f"}, - {file = "coverage-7.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a1e18a85bd066c7c556d85277a7adf4651f259b2579113844835ba1a74aafd"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70f0925c4e2bfc965369f417e7cc72538fd1ba91639cf1e4ef4b1a6b50439b3b"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b0fac2088ec4aaeb5468b814bd3ff5e5978364bfbce5e567c44c9e2854469f6c"}, - {file = "coverage-7.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3e212a894d8ae07fde2ca8b43d666a6d49bbbddb10da0f6a74ca7bd31f20054"}, - {file = "coverage-7.7.0-cp310-cp310-win32.whl", hash = "sha256:f32b165bf6dfea0846a9c9c38b7e1d68f313956d60a15cde5d1709fddcaf3bee"}, - {file = "coverage-7.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:a2454b12a3f12cc4698f3508912e6225ec63682e2ca5a96f80a2b93cef9e63f3"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a0a207c87a9f743c8072d059b4711f8d13c456eb42dac778a7d2e5d4f3c253a7"}, - {file = "coverage-7.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2d673e3add00048215c2cc507f1228a7523fd8bf34f279ac98334c9b07bd2656"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f81fe93dc1b8e5673f33443c0786c14b77e36f1025973b85e07c70353e46882b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8c7524779003d59948c51b4fcbf1ca4e27c26a7d75984f63488f3625c328b9b"}, - {file = "coverage-7.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c124025430249118d018dcedc8b7426f39373527c845093132196f2a483b6dd"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e7f559c36d5cdc448ee13e7e56ed7b6b5d44a40a511d584d388a0f5d940977ba"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:37cbc7b0d93dfd133e33c7ec01123fbb90401dce174c3b6661d8d36fb1e30608"}, - {file = "coverage-7.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d2a65876274acf544703e943c010b60bd79404e3623a1e5d52b64a6e2728de5"}, - {file = "coverage-7.7.0-cp311-cp311-win32.whl", hash = "sha256:f5a2f71d6a91238e7628f23538c26aa464d390cbdedf12ee2a7a0fb92a24482a"}, - {file = "coverage-7.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ae8006772c6b0fa53c33747913473e064985dac4d65f77fd2fdc6474e7cd54e4"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:056d3017ed67e7ddf266e6f57378ece543755a4c9231e997789ab3bd11392c94"}, - {file = "coverage-7.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:33c1394d8407e2771547583b66a85d07ed441ff8fae5a4adb4237ad39ece60db"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fbb7a0c3c21908520149d7751cf5b74eb9b38b54d62997b1e9b3ac19a8ee2fe"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb356e7ae7c2da13f404bf8f75be90f743c6df8d4607022e759f5d7d89fe83f8"}, - {file = "coverage-7.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce730d484038e97f27ea2dbe5d392ec5c2261f28c319a3bb266f6b213650135"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aa4dff57fc21a575672176d5ab0ef15a927199e775c5e8a3d75162ab2b0c7705"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b667b91f4f714b17af2a18e220015c941d1cf8b07c17f2160033dbe1e64149f0"}, - {file = "coverage-7.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:693d921621a0c8043bfdc61f7d4df5ea6d22165fe8b807cac21eb80dd94e4bbd"}, - {file = "coverage-7.7.0-cp312-cp312-win32.whl", hash = "sha256:52fc89602cde411a4196c8c6894afb384f2125f34c031774f82a4f2608c59d7d"}, - {file = "coverage-7.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ce8cf59e09d31a4915ff4c3b94c6514af4c84b22c4cc8ad7c3c546a86150a92"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4545485fef7a8a2d8f30e6f79ce719eb154aab7e44217eb444c1d38239af2072"}, - {file = "coverage-7.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1393e5aa9441dafb0162c36c8506c648b89aea9565b31f6bfa351e66c11bcd82"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316f29cc3392fa3912493ee4c83afa4a0e2db04ff69600711f8c03997c39baaa"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1ffde1d6bc2a92f9c9207d1ad808550873748ac2d4d923c815b866baa343b3f"}, - {file = "coverage-7.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:416e2a8845eaff288f97eaf76ab40367deafb9073ffc47bf2a583f26b05e5265"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5efdeff5f353ed3352c04e6b318ab05c6ce9249c25ed3c2090c6e9cadda1e3b2"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:57f3bd0d29bf2bd9325c0ff9cc532a175110c4bf8f412c05b2405fd35745266d"}, - {file = "coverage-7.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ab7090f04b12dc6469882ce81244572779d3a4b67eea1c96fb9ecc8c607ef39"}, - {file = "coverage-7.7.0-cp313-cp313-win32.whl", hash = "sha256:180e3fc68ee4dc5af8b33b6ca4e3bb8aa1abe25eedcb958ba5cff7123071af68"}, - {file = "coverage-7.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:55143aa13c49491f5606f05b49ed88663446dce3a4d3c5d77baa4e36a16d3573"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc41374d2f27d81d6558f8a24e5c114580ffefc197fd43eabd7058182f743322"}, - {file = "coverage-7.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:89078312f06237417adda7c021c33f80f7a6d2db8572a5f6c330d89b080061ce"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b2f144444879363ea8834cd7b6869d79ac796cb8f864b0cfdde50296cd95816"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60e6347d1ed882b1159ffea172cb8466ee46c665af4ca397edbf10ff53e9ffaf"}, - {file = "coverage-7.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb203c0afffaf1a8f5b9659a013f8f16a1b2cad3a80a8733ceedc968c0cf4c57"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ad0edaa97cb983d9f2ff48cadddc3e1fb09f24aa558abeb4dc9a0dbacd12cbb4"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c5f8a5364fc37b2f172c26a038bc7ec4885f429de4a05fc10fdcb53fb5834c5c"}, - {file = "coverage-7.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4e09534037933bf6eb31d804e72c52ec23219b32c1730f9152feabbd7499463"}, - {file = "coverage-7.7.0-cp313-cp313t-win32.whl", hash = "sha256:1b336d06af14f8da5b1f391e8dec03634daf54dfcb4d1c4fb6d04c09d83cef90"}, - {file = "coverage-7.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b54a1ee4c6f1905a436cbaa04b26626d27925a41cbc3a337e2d3ff7038187f07"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c8fbce80b2b8bf135d105aa8f5b36eae0c57d702a1cc3ebdea2a6f03f6cdde5"}, - {file = "coverage-7.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9710521f07f526de30ccdead67e6b236fe996d214e1a7fba8b36e2ba2cd8261"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7789e700f33f2b133adae582c9f437523cd5db8de845774988a58c360fc88253"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c36093aca722db73633cf2359026ed7782a239eb1c6db2abcff876012dc4cf"}, - {file = "coverage-7.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c075d167a6ec99b798c1fdf6e391a1d5a2d054caffe9593ba0f97e3df2c04f0e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d013c07061751ae81861cae6ec3a4fe04e84781b11fd4b6b4201590234b25c7b"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:104bf640f408f4e115b85110047c7f27377e1a8b7ba86f7db4fa47aa49dc9a8e"}, - {file = "coverage-7.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:39abcacd1ed54e2c33c54bdc488b310e8ef6705833f7148b6eb9a547199d375d"}, - {file = "coverage-7.7.0-cp39-cp39-win32.whl", hash = "sha256:8e336b56301774ace6be0017ff85c3566c556d938359b61b840796a0202f805c"}, - {file = "coverage-7.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c938c6ae59be67ac19a7204e079efc94b38222cd7d0269f96e45e18cddeaa59"}, - {file = "coverage-7.7.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:3b0e6e54591ae0d7427def8a4d40fca99df6b899d10354bab73cd5609807261c"}, - {file = "coverage-7.7.0-py3-none-any.whl", hash = "sha256:708f0a1105ef2b11c79ed54ed31f17e6325ac936501fc373f24be3e6a578146a"}, - {file = "coverage-7.7.0.tar.gz", hash = "sha256:cd879d4646055a573775a1cec863d00c9ff8c55860f8b17f6d8eee9140c06166"}, + {file = "coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"}, + {file = "coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"}, + {file = "coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3"}, + {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676"}, + {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d"}, + {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a"}, + {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c"}, + {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f"}, + {file = "coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f"}, + {file = "coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23"}, + {file = "coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27"}, + {file = "coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea"}, + {file = "coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7"}, + {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040"}, + {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543"}, + {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2"}, + {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318"}, + {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9"}, + {file = "coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c"}, + {file = "coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78"}, + {file = "coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc"}, + {file = "coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6"}, + {file = "coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d"}, + {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05"}, + {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a"}, + {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6"}, + {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47"}, + {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe"}, + {file = "coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545"}, + {file = "coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b"}, + {file = "coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd"}, + {file = "coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00"}, + {file = "coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64"}, + {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067"}, + {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008"}, + {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733"}, + {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323"}, + {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3"}, + {file = "coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d"}, + {file = "coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487"}, + {file = "coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25"}, + {file = "coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42"}, + {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502"}, + {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1"}, + {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4"}, + {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73"}, + {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a"}, + {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883"}, + {file = "coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada"}, + {file = "coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257"}, + {file = "coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f"}, + {file = "coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a"}, + {file = "coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82"}, + {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814"}, + {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c"}, + {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd"}, + {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4"}, + {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899"}, + {file = "coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f"}, + {file = "coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3"}, + {file = "coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd"}, + {file = "coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7"}, + {file = "coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501"}, ] [package.extras] @@ -993,14 +951,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.27.0" +version = "2.28.0" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" groups = ["dev"] files = [ - {file = "fakeredis-2.27.0-py3-none-any.whl", hash = "sha256:f4b6e0fa4193acbf00d81dac71ff5cc34fe7d7c12f1560b036f98578a103d5c3"}, - {file = "fakeredis-2.27.0.tar.gz", hash = "sha256:7b7584ec104392592297f46864a82cb7339a23e254ee885bf5ae07cfc64fbce7"}, + {file = "fakeredis-2.28.0-py3-none-any.whl", hash = "sha256:e19b2a72cb0d13edaae95637748cef569840866d0807d93f4d381571034d1388"}, + {file = "fakeredis-2.28.0.tar.gz", hash = "sha256:4768a78240ca03af79f8922f0b6774efb52c6bf2d27d38a622da9a2a7e8abc8d"}, ] [package.dependencies] @@ -1373,46 +1331,46 @@ protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4 [[package]] name = "google-crc32c" -version = "1.7.0" +version = "1.7.1" description = "A python wrapper of the C library 'Google CRC32C'" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:18f1dfc6baeb3b28b1537d54b3622363352f75fcb2d4b6ffcc37584fe431f122"}, - {file = "google_crc32c-1.7.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:732378dc4ca08953eac0d13d1c312d99a54d5b483c90b4a5a536132669ed1c24"}, - {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:14fdac94aa60d5794652f8ea6c2fcc532032e31f9050698b7ecdc6d4c3a61784"}, - {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bc14187a7fe5c61024c0dd1b578d7f9391df55459bf373c07f66426e09353b6"}, - {file = "google_crc32c-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54af59a98a427d0f98b6b0446df52ad286948ab7745da80a1edeb32ad633b3ae"}, - {file = "google_crc32c-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:2515aa89e46c6fa99190ec29bf27f33457ff98e5ca5c6c05602f74e0fb005752"}, - {file = "google_crc32c-1.7.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:96e33b249776f5aa7017a494b78994cf3cc8461291d460b46e75f6bc6cc40dc8"}, - {file = "google_crc32c-1.7.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:c2dc799827990dd06b777067e27f57c2a552ddde4c4cd2d883b1b615ee92f9cf"}, - {file = "google_crc32c-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c29f7718f48e32810a41b17126e0ca588a0ae6158b4da2926d8074241a155d"}, - {file = "google_crc32c-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f30548e65291a4658c9e56f6f516159663f2b4a2c991b9af5846f0084ea25d4"}, - {file = "google_crc32c-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:9754f9eaa5ff82166512908f02745d5e883650d7b04d1732b5df3335986ad359"}, - {file = "google_crc32c-1.7.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:11b3b2f16a534c76ce3c9503800c7c2578c13a56e8e409eac273330e25b5c521"}, - {file = "google_crc32c-1.7.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:fd3afea81a7c7b95f98c065edc5a0fdb58f1fea5960e166962b142ec037fe5e0"}, - {file = "google_crc32c-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d07ad3ff51f26cef5bbad66622004eca3639271230cfc2443845ec4650e1c57"}, - {file = "google_crc32c-1.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af6e80f83d882b247eef2c0366ec72b1ffb89433b9f35dc621d79190840f1ea6"}, - {file = "google_crc32c-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:10721764a9434546b7961194fbb1f80efbcaf45b8498ed379d64f8891d4c155b"}, - {file = "google_crc32c-1.7.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:76bb19d182b999f9c9d580b1d7ab6e9334ab23dd669bf91f501812103408c85b"}, - {file = "google_crc32c-1.7.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:6a40522958040051c755a173eb98c05ad4d64a6dd898888c3e5ccca2d1cbdcdc"}, - {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f714fe5cdf5007d7064c57cf7471a99e0cbafda24ddfa829117fc3baafa424f7"}, - {file = "google_crc32c-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f04e58dbe1bf0c9398e603a9be5aaa09e0ba7eb022a3293195d8749459a01069"}, - {file = "google_crc32c-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:e545b51ddf97f604d30114f7c23eecaf4c06cd6c023ff1ae0b80dcd99af32833"}, - {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:364067b063664dd8d1fec75a3fe85edf05c46f688365269beccaf42ef5dfe889"}, - {file = "google_crc32c-1.7.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1b0d6044799f6ac51d1cc2decb997280a83c448b3bef517a54b57a3b71921c0"}, - {file = "google_crc32c-1.7.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:02bc3295d26cd7666521fd6d5b7b93923ae1eb4417ddd3bc57185a5881ad7b96"}, - {file = "google_crc32c-1.7.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:807503eedd7bf9bfded2776e0bcd0b017998f45b8b1e813cde3b9bf8f7593313"}, - {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ff62f4959fabc8e5e90af920cc2fb0a9ccf1e74fd6da8a56d8ef04a600a68f97"}, - {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2daec556d537b07af25fa3f91841f022dd6869a648ca4aea1add56f87b80647"}, - {file = "google_crc32c-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dc17abb62bd7c7a33da307f24ae05cd6e36c24e847a67e3e2165cba23a06dd4"}, - {file = "google_crc32c-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:a79f7e486756385c4309d7815ede53a72f6cbab12ddf140c5f5b335caf08edfb"}, - {file = "google_crc32c-1.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8f48dddd1451026a517d7eb1f8c4ee2491998bfa383abb5fdebf32b0aa333e"}, - {file = "google_crc32c-1.7.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60f89e06ce462a65dd4d14a97bd29d92730713316b8b89720f9b2bb1aef270f7"}, - {file = "google_crc32c-1.7.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1277c27428a6cc89a51f5afbc04b81fae0288fb631117383f0de4f2bf78ffad6"}, - {file = "google_crc32c-1.7.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:731921158ef113bf157b8e65f13303d627fb540f173a410260f2fb7570af644c"}, - {file = "google_crc32c-1.7.0.tar.gz", hash = "sha256:c8c15a04b290c7556f277acc55ad98503a8bc0893ea6860fd5b5d210f3f558ce"}, + {file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76"}, + {file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d"}, + {file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c"}, + {file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb"}, + {file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603"}, + {file = "google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a"}, + {file = "google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06"}, + {file = "google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9"}, + {file = "google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77"}, + {file = "google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53"}, + {file = "google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d"}, + {file = "google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194"}, + {file = "google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e"}, + {file = "google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337"}, + {file = "google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65"}, + {file = "google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6"}, + {file = "google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35"}, + {file = "google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638"}, + {file = "google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb"}, + {file = "google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6"}, + {file = "google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db"}, + {file = "google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3"}, + {file = "google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9"}, + {file = "google_crc32c-1.7.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:9fc196f0b8d8bd2789352c6a522db03f89e83a0ed6b64315923c396d7a932315"}, + {file = "google_crc32c-1.7.1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb5e35dcd8552f76eed9461a23de1030920a3c953c1982f324be8f97946e7127"}, + {file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2226b6a8da04f1d9e61d3e357f2460b9551c5e6950071437e122c958a18ae14"}, + {file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f2b3522222746fff0e04a9bd0a23ea003ba3cccc8cf21385c564deb1f223242"}, + {file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bda0fcb632d390e3ea8b6b07bf6b4f4a66c9d02dcd6fbf7ba00a197c143f582"}, + {file = "google_crc32c-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:713121af19f1a617054c41f952294764e0c5443d5a5d9034b2cd60f5dd7e0349"}, + {file = "google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589"}, + {file = "google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b"}, + {file = "google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48"}, + {file = "google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82"}, + {file = "google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472"}, ] [package.extras] @@ -1761,14 +1719,14 @@ type = ["pytest-mypy"] [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] @@ -2047,19 +2005,19 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.1.1" +version = "5.1.2" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "moto-5.1.1-py3-none-any.whl", hash = "sha256:615904d6210431950a59a2bdec365d60e791eacbe3dd07a3a5d742c88ef847dd"}, - {file = "moto-5.1.1.tar.gz", hash = "sha256:5b25dbc62cccd9f36ef062c870db49d976b241129024fab049e2d3d1296e2a57"}, + {file = "moto-5.1.2-py3-none-any.whl", hash = "sha256:3789084bb20052b6eb846fe6f4831ce6dfe8a3b197c8f63789b40281b5e1731d"}, + {file = "moto-5.1.2.tar.gz", hash = "sha256:0e4c650d31eacfbe726c37e956efa04d36948e23f7d3228a7c3746aa839e66c2"}, ] [package.dependencies] boto3 = ">=1.9.201" -botocore = ">=1.14.0,<1.35.45 || >1.35.45,<1.35.46 || >1.35.46" +botocore = ">=1.20.88,<1.35.45 || >1.35.45,<1.35.46 || >1.35.46" cryptography = ">=35.0.0" Jinja2 = ">=2.10.1" python-dateutil = ">=2.1,<3.0.0" @@ -2158,14 +2116,14 @@ files = [ [[package]] name = "opentelemetry-api" -version = "1.31.0" +version = "1.31.1" description = "OpenTelemetry Python API" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "opentelemetry_api-1.31.0-py3-none-any.whl", hash = "sha256:145b72c6c16977c005c568ec32f4946054ab793d8474a17fd884b0397582c5f2"}, - {file = "opentelemetry_api-1.31.0.tar.gz", hash = "sha256:d8da59e83e8e3993b4726e4c1023cd46f57c4d5a73142e239247e7d814309de1"}, + {file = "opentelemetry_api-1.31.1-py3-none-any.whl", hash = "sha256:1511a3f470c9c8a32eeea68d4ea37835880c0eed09dd1a0187acc8b1301da0a1"}, + {file = "opentelemetry_api-1.31.1.tar.gz", hash = "sha256:137ad4b64215f02b3000a0292e077641c8611aab636414632a9b9068593b7e91"}, ] [package.dependencies] @@ -2174,36 +2132,36 @@ importlib-metadata = ">=6.0,<8.7.0" [[package]] name = "opentelemetry-sdk" -version = "1.31.0" +version = "1.31.1" description = "OpenTelemetry Python SDK" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "opentelemetry_sdk-1.31.0-py3-none-any.whl", hash = "sha256:97c9a03865e69723725fb64fe04343a488c3e61e684eb804bd7d6da2215dfc60"}, - {file = "opentelemetry_sdk-1.31.0.tar.gz", hash = "sha256:452d7d5b3c1db2e5e4cb64abede0ddd20690cb244a559c73a59652fdf6726070"}, + {file = "opentelemetry_sdk-1.31.1-py3-none-any.whl", hash = "sha256:882d021321f223e37afaca7b4e06c1d8bbc013f9e17ff48a7aa017460a8e7dae"}, + {file = "opentelemetry_sdk-1.31.1.tar.gz", hash = "sha256:c95f61e74b60769f8ff01ec6ffd3d29684743404603df34b20aa16a49dc8d903"}, ] [package.dependencies] -opentelemetry-api = "1.31.0" -opentelemetry-semantic-conventions = "0.52b0" +opentelemetry-api = "1.31.1" +opentelemetry-semantic-conventions = "0.52b1" typing-extensions = ">=3.7.4" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.52b0" +version = "0.52b1" description = "OpenTelemetry Semantic Conventions" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "opentelemetry_semantic_conventions-0.52b0-py3-none-any.whl", hash = "sha256:4d843652ae1f9f3c0d4d8df0bfef740627c90495ac043fc33f0a04bad3b606e2"}, - {file = "opentelemetry_semantic_conventions-0.52b0.tar.gz", hash = "sha256:f8bc8873a69d0a2f45746c31980baad2bb10ccee16b1816497ccf99417770386"}, + {file = "opentelemetry_semantic_conventions-0.52b1-py3-none-any.whl", hash = "sha256:72b42db327e29ca8bb1b91e8082514ddf3bbf33f32ec088feb09526ade4bc77e"}, + {file = "opentelemetry_semantic_conventions-0.52b1.tar.gz", hash = "sha256:7b3d226ecf7523c27499758a58b542b48a0ac8d12be03c0488ff8ec60c5bae5d"}, ] [package.dependencies] deprecated = ">=1.2.6" -opentelemetry-api = "1.31.0" +opentelemetry-api = "1.31.1" [[package]] name = "ordered-set" @@ -2288,20 +2246,20 @@ twisted = ["twisted"] [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.3.7" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, + {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] name = "playwright" @@ -2360,23 +2318,23 @@ testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "5.29.3" +version = "5.29.4" description = "" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888"}, - {file = "protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a"}, - {file = "protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e"}, - {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84"}, - {file = "protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f"}, - {file = "protobuf-5.29.3-cp38-cp38-win32.whl", hash = "sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252"}, - {file = "protobuf-5.29.3-cp38-cp38-win_amd64.whl", hash = "sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107"}, - {file = "protobuf-5.29.3-cp39-cp39-win32.whl", hash = "sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7"}, - {file = "protobuf-5.29.3-cp39-cp39-win_amd64.whl", hash = "sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da"}, - {file = "protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f"}, - {file = "protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620"}, + {file = "protobuf-5.29.4-cp310-abi3-win32.whl", hash = "sha256:13eb236f8eb9ec34e63fc8b1d6efd2777d062fa6aaa68268fb67cf77f6839ad7"}, + {file = "protobuf-5.29.4-cp310-abi3-win_amd64.whl", hash = "sha256:bcefcdf3976233f8a502d265eb65ea740c989bacc6c30a58290ed0e519eb4b8d"}, + {file = "protobuf-5.29.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:307ecba1d852ec237e9ba668e087326a67564ef83e45a0189a772ede9e854dd0"}, + {file = "protobuf-5.29.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:aec4962f9ea93c431d5714ed1be1c93f13e1a8618e70035ba2b0564d9e633f2e"}, + {file = "protobuf-5.29.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:d7d3f7d1d5a66ed4942d4fefb12ac4b14a29028b209d4bfb25c68ae172059922"}, + {file = "protobuf-5.29.4-cp38-cp38-win32.whl", hash = "sha256:1832f0515b62d12d8e6ffc078d7e9eb06969aa6dc13c13e1036e39d73bebc2de"}, + {file = "protobuf-5.29.4-cp38-cp38-win_amd64.whl", hash = "sha256:476cb7b14914c780605a8cf62e38c2a85f8caff2e28a6a0bad827ec7d6c85d68"}, + {file = "protobuf-5.29.4-cp39-cp39-win32.whl", hash = "sha256:fd32223020cb25a2cc100366f1dedc904e2d71d9322403224cdde5fdced0dabe"}, + {file = "protobuf-5.29.4-cp39-cp39-win_amd64.whl", hash = "sha256:678974e1e3a9b975b8bc2447fca458db5f93a2fb6b0c8db46b6675b5b5346812"}, + {file = "protobuf-5.29.4-py3-none-any.whl", hash = "sha256:3fde11b505e1597f71b875ef2fc52062b6a9740e5f7c8997ce878b6009145862"}, + {file = "protobuf-5.29.4.tar.gz", hash = "sha256:4f1dfcd7997b31ef8f53ec82781ff434a28bf71d9102ddde14d076adcfc78c99"}, ] [[package]] @@ -2393,18 +2351,18 @@ files = [ [[package]] name = "pyasn1-modules" -version = "0.4.1" +version = "0.4.2" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, - {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, ] [package.dependencies] -pyasn1 = ">=0.4.6,<0.7.0" +pyasn1 = ">=0.6.1,<0.7.0" [[package]] name = "pycparser" @@ -2439,23 +2397,23 @@ dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", " [[package]] name = "pylint" -version = "3.3.5" +version = "3.3.6" description = "python code static checker" optional = false python-versions = ">=3.9.0" groups = ["dev"] files = [ - {file = "pylint-3.3.5-py3-none-any.whl", hash = "sha256:7cb170929a371238530b2eeea09f5f28236d106b70308c3d46a9c0cf11634633"}, - {file = "pylint-3.3.5.tar.gz", hash = "sha256:38d0f784644ed493d91f76b5333a0e370a1c1bc97c22068a77523b4bf1e82c31"}, + {file = "pylint-3.3.6-py3-none-any.whl", hash = "sha256:8b7c2d3e86ae3f94fb27703d521dd0b9b6b378775991f504d7c3a6275aa0a6a6"}, + {file = "pylint-3.3.6.tar.gz", hash = "sha256:b634a041aac33706d56a0d217e6587228c66427e20ec21a019bc4cdee48c040a"}, ] [package.dependencies] -astroid = ">=3.3.8,<=3.4.0-dev0" +astroid = ">=3.3.8,<=3.4.0.dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} -isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" +isort = ">=4.2.5,<5.13 || >5.13,<7" mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" +platformdirs = ">=2.2" tomlkit = ">=0.10.1" [package.extras] @@ -2660,14 +2618,14 @@ cramjam = "*" [[package]] name = "pytz" -version = "2025.1" +version = "2025.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, - {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, + {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"}, + {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"}, ] [[package]] @@ -2914,115 +2872,126 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rpds-py" -version = "0.23.1" +version = "0.24.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "rpds_py-0.23.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed"}, - {file = "rpds_py-0.23.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d"}, - {file = "rpds_py-0.23.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f"}, - {file = "rpds_py-0.23.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a"}, - {file = "rpds_py-0.23.1-cp310-cp310-win32.whl", hash = "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12"}, - {file = "rpds_py-0.23.1-cp310-cp310-win_amd64.whl", hash = "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda"}, - {file = "rpds_py-0.23.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590"}, - {file = "rpds_py-0.23.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580"}, - {file = "rpds_py-0.23.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35"}, - {file = "rpds_py-0.23.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522"}, - {file = "rpds_py-0.23.1-cp311-cp311-win32.whl", hash = "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6"}, - {file = "rpds_py-0.23.1-cp311-cp311-win_amd64.whl", hash = "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf"}, - {file = "rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c"}, - {file = "rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc"}, - {file = "rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef"}, - {file = "rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad"}, - {file = "rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057"}, - {file = "rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165"}, - {file = "rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935"}, - {file = "rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013"}, - {file = "rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957"}, - {file = "rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93"}, - {file = "rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd"}, - {file = "rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70"}, - {file = "rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731"}, - {file = "rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722"}, - {file = "rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b"}, - {file = "rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5"}, - {file = "rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7"}, - {file = "rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d"}, - {file = "rpds_py-0.23.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19"}, - {file = "rpds_py-0.23.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a"}, - {file = "rpds_py-0.23.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4"}, - {file = "rpds_py-0.23.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f"}, - {file = "rpds_py-0.23.1-cp39-cp39-win32.whl", hash = "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495"}, - {file = "rpds_py-0.23.1-cp39-cp39-win_amd64.whl", hash = "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4"}, - {file = "rpds_py-0.23.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3"}, - {file = "rpds_py-0.23.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00"}, - {file = "rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707"}, + {file = "rpds_py-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724"}, + {file = "rpds_py-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8acd55bd5b071156bae57b555f5d33697998752673b9de554dd82f5b5352727"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7e80d375134ddb04231a53800503752093dbb65dad8dabacce2c84cccc78e964"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60748789e028d2a46fc1c70750454f83c6bdd0d05db50f5ae83e2db500b34da5"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e1daf5bf6c2be39654beae83ee6b9a12347cb5aced9a29eecf12a2d25fff664"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b221c2457d92a1fb3c97bee9095c874144d196f47c038462ae6e4a14436f7bc"}, + {file = "rpds_py-0.24.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66420986c9afff67ef0c5d1e4cdc2d0e5262f53ad11e4f90e5e22448df485bf0"}, + {file = "rpds_py-0.24.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:43dba99f00f1d37b2a0265a259592d05fcc8e7c19d140fe51c6e6f16faabeb1f"}, + {file = "rpds_py-0.24.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a88c0d17d039333a41d9bf4616bd062f0bd7aa0edeb6cafe00a2fc2a804e944f"}, + {file = "rpds_py-0.24.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc31e13ce212e14a539d430428cd365e74f8b2d534f8bc22dd4c9c55b277b875"}, + {file = "rpds_py-0.24.0-cp310-cp310-win32.whl", hash = "sha256:fc2c1e1b00f88317d9de6b2c2b39b012ebbfe35fe5e7bef980fd2a91f6100a07"}, + {file = "rpds_py-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0145295ca415668420ad142ee42189f78d27af806fcf1f32a18e51d47dd2052"}, + {file = "rpds_py-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2d3ee4615df36ab8eb16c2507b11e764dcc11fd350bbf4da16d09cda11fcedef"}, + {file = "rpds_py-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e13ae74a8a3a0c2f22f450f773e35f893484fcfacb00bb4344a7e0f4f48e1f97"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf86f72d705fc2ef776bb7dd9e5fbba79d7e1f3e258bf9377f8204ad0fc1c51e"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c43583ea8517ed2e780a345dd9960896afc1327e8cf3ac8239c167530397440d"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cd031e63bc5f05bdcda120646a0d32f6d729486d0067f09d79c8db5368f4586"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34d90ad8c045df9a4259c47d2e16a3f21fdb396665c94520dbfe8766e62187a4"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e838bf2bb0b91ee67bf2b889a1a841e5ecac06dd7a2b1ef4e6151e2ce155c7ae"}, + {file = "rpds_py-0.24.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04ecf5c1ff4d589987b4d9882872f80ba13da7d42427234fce8f22efb43133bc"}, + {file = "rpds_py-0.24.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:630d3d8ea77eabd6cbcd2ea712e1c5cecb5b558d39547ac988351195db433f6c"}, + {file = "rpds_py-0.24.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ebcb786b9ff30b994d5969213a8430cbb984cdd7ea9fd6df06663194bd3c450c"}, + {file = "rpds_py-0.24.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:174e46569968ddbbeb8a806d9922f17cd2b524aa753b468f35b97ff9c19cb718"}, + {file = "rpds_py-0.24.0-cp311-cp311-win32.whl", hash = "sha256:5ef877fa3bbfb40b388a5ae1cb00636a624690dcb9a29a65267054c9ea86d88a"}, + {file = "rpds_py-0.24.0-cp311-cp311-win_amd64.whl", hash = "sha256:e274f62cbd274359eff63e5c7e7274c913e8e09620f6a57aae66744b3df046d6"}, + {file = "rpds_py-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d8551e733626afec514b5d15befabea0dd70a343a9f23322860c4f16a9430205"}, + {file = "rpds_py-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e374c0ce0ca82e5b67cd61fb964077d40ec177dd2c4eda67dba130de09085c7"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d69d003296df4840bd445a5d15fa5b6ff6ac40496f956a221c4d1f6f7b4bc4d9"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8212ff58ac6dfde49946bea57474a386cca3f7706fc72c25b772b9ca4af6b79e"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:528927e63a70b4d5f3f5ccc1fa988a35456eb5d15f804d276709c33fc2f19bda"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a824d2c7a703ba6daaca848f9c3d5cb93af0505be505de70e7e66829affd676e"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d51febb7a114293ffd56c6cf4736cb31cd68c0fddd6aa303ed09ea5a48e029"}, + {file = "rpds_py-0.24.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3fab5f4a2c64a8fb64fc13b3d139848817a64d467dd6ed60dcdd6b479e7febc9"}, + {file = "rpds_py-0.24.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9be4f99bee42ac107870c61dfdb294d912bf81c3c6d45538aad7aecab468b6b7"}, + {file = "rpds_py-0.24.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:564c96b6076a98215af52f55efa90d8419cc2ef45d99e314fddefe816bc24f91"}, + {file = "rpds_py-0.24.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:75a810b7664c17f24bf2ffd7f92416c00ec84b49bb68e6a0d93e542406336b56"}, + {file = "rpds_py-0.24.0-cp312-cp312-win32.whl", hash = "sha256:f6016bd950be4dcd047b7475fdf55fb1e1f59fc7403f387be0e8123e4a576d30"}, + {file = "rpds_py-0.24.0-cp312-cp312-win_amd64.whl", hash = "sha256:998c01b8e71cf051c28f5d6f1187abbdf5cf45fc0efce5da6c06447cba997034"}, + {file = "rpds_py-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2d8e4508e15fc05b31285c4b00ddf2e0eb94259c2dc896771966a163122a0c"}, + {file = "rpds_py-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f00c16e089282ad68a3820fd0c831c35d3194b7cdc31d6e469511d9bffc535c"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951cc481c0c395c4a08639a469d53b7d4afa252529a085418b82a6b43c45c240"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9ca89938dff18828a328af41ffdf3902405a19f4131c88e22e776a8e228c5a8"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0ef550042a8dbcd657dfb284a8ee00f0ba269d3f2286b0493b15a5694f9fe8"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b2356688e5d958c4d5cb964af865bea84db29971d3e563fb78e46e20fe1848b"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78884d155fd15d9f64f5d6124b486f3d3f7fd7cd71a78e9670a0f6f6ca06fb2d"}, + {file = "rpds_py-0.24.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6a4a535013aeeef13c5532f802708cecae8d66c282babb5cd916379b72110cf7"}, + {file = "rpds_py-0.24.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:84e0566f15cf4d769dade9b366b7b87c959be472c92dffb70462dd0844d7cbad"}, + {file = "rpds_py-0.24.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:823e74ab6fbaa028ec89615ff6acb409e90ff45580c45920d4dfdddb069f2120"}, + {file = "rpds_py-0.24.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c61a2cb0085c8783906b2f8b1f16a7e65777823c7f4d0a6aaffe26dc0d358dd9"}, + {file = "rpds_py-0.24.0-cp313-cp313-win32.whl", hash = "sha256:60d9b630c8025b9458a9d114e3af579a2c54bd32df601c4581bd054e85258143"}, + {file = "rpds_py-0.24.0-cp313-cp313-win_amd64.whl", hash = "sha256:6eea559077d29486c68218178ea946263b87f1c41ae7f996b1f30a983c476a5a"}, + {file = "rpds_py-0.24.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:d09dc82af2d3c17e7dd17120b202a79b578d79f2b5424bda209d9966efeed114"}, + {file = "rpds_py-0.24.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5fc13b44de6419d1e7a7e592a4885b323fbc2f46e1f22151e3a8ed3b8b920405"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c347a20d79cedc0a7bd51c4d4b7dbc613ca4e65a756b5c3e57ec84bd43505b47"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20f2712bd1cc26a3cc16c5a1bfee9ed1abc33d4cdf1aabd297fe0eb724df4272"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aad911555286884be1e427ef0dc0ba3929e6821cbeca2194b13dc415a462c7fd"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0aeb3329c1721c43c58cae274d7d2ca85c1690d89485d9c63a006cb79a85771a"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a0f156e9509cee987283abd2296ec816225145a13ed0391df8f71bf1d789e2d"}, + {file = "rpds_py-0.24.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa6800adc8204ce898c8a424303969b7aa6a5e4ad2789c13f8648739830323b7"}, + {file = "rpds_py-0.24.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a18fc371e900a21d7392517c6f60fe859e802547309e94313cd8181ad9db004d"}, + {file = "rpds_py-0.24.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9168764133fd919f8dcca2ead66de0105f4ef5659cbb4fa044f7014bed9a1797"}, + {file = "rpds_py-0.24.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5f6e3cec44ba05ee5cbdebe92d052f69b63ae792e7d05f1020ac5e964394080c"}, + {file = "rpds_py-0.24.0-cp313-cp313t-win32.whl", hash = "sha256:8ebc7e65ca4b111d928b669713865f021b7773350eeac4a31d3e70144297baba"}, + {file = "rpds_py-0.24.0-cp313-cp313t-win_amd64.whl", hash = "sha256:675269d407a257b8c00a6b58205b72eec8231656506c56fd429d924ca00bb350"}, + {file = "rpds_py-0.24.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a36b452abbf29f68527cf52e181fced56685731c86b52e852053e38d8b60bc8d"}, + {file = "rpds_py-0.24.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b3b397eefecec8e8e39fa65c630ef70a24b09141a6f9fc17b3c3a50bed6b50e"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdabcd3beb2a6dca7027007473d8ef1c3b053347c76f685f5f060a00327b8b65"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5db385bacd0c43f24be92b60c857cf760b7f10d8234f4bd4be67b5b20a7c0b6b"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8097b3422d020ff1c44effc40ae58e67d93e60d540a65649d2cdaf9466030791"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:493fe54318bed7d124ce272fc36adbf59d46729659b2c792e87c3b95649cdee9"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8aa362811ccdc1f8dadcc916c6d47e554169ab79559319ae9fae7d7752d0d60c"}, + {file = "rpds_py-0.24.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8f9a6e7fd5434817526815f09ea27f2746c4a51ee11bb3439065f5fc754db58"}, + {file = "rpds_py-0.24.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8205ee14463248d3349131bb8099efe15cd3ce83b8ef3ace63c7e976998e7124"}, + {file = "rpds_py-0.24.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:921ae54f9ecba3b6325df425cf72c074cd469dea843fb5743a26ca7fb2ccb149"}, + {file = "rpds_py-0.24.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:32bab0a56eac685828e00cc2f5d1200c548f8bc11f2e44abf311d6b548ce2e45"}, + {file = "rpds_py-0.24.0-cp39-cp39-win32.whl", hash = "sha256:f5c0ed12926dec1dfe7d645333ea59cf93f4d07750986a586f511c0bc61fe103"}, + {file = "rpds_py-0.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:afc6e35f344490faa8276b5f2f7cbf71f88bc2cda4328e00553bd451728c571f"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:619ca56a5468f933d940e1bf431c6f4e13bef8e688698b067ae68eb4f9b30e3a"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b28e5122829181de1898c2c97f81c0b3246d49f585f22743a1246420bb8d399"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e5ab32cf9eb3647450bc74eb201b27c185d3857276162c101c0f8c6374e098"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:208b3a70a98cf3710e97cabdc308a51cd4f28aa6e7bb11de3d56cd8b74bab98d"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbc4362e06f950c62cad3d4abf1191021b2ffaf0b31ac230fbf0526453eee75e"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebea2821cdb5f9fef44933617be76185b80150632736f3d76e54829ab4a3b4d1"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4df06c35465ef4d81799999bba810c68d29972bf1c31db61bfdb81dd9d5bb"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3aa13bdf38630da298f2e0d77aca967b200b8cc1473ea05248f6c5e9c9bdb44"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:041f00419e1da7a03c46042453598479f45be3d787eb837af382bfc169c0db33"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8754d872a5dfc3c5bf9c0e059e8107451364a30d9fd50f1f1a85c4fb9481164"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:896c41007931217a343eff197c34513c154267636c8056fb409eafd494c3dcdc"}, + {file = "rpds_py-0.24.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:92558d37d872e808944c3c96d0423b8604879a3d1c86fdad508d7ed91ea547d5"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f9e0057a509e096e47c87f753136c9b10d7a91842d8042c2ee6866899a717c0d"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6e109a454412ab82979c5b1b3aee0604eca4bbf9a02693bb9df027af2bfa91a"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc1c892b1ec1f8cbd5da8de287577b455e388d9c328ad592eabbdcb6fc93bee5"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c39438c55983d48f4bb3487734d040e22dad200dab22c41e331cee145e7a50d"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d7e8ce990ae17dda686f7e82fd41a055c668e13ddcf058e7fb5e9da20b57793"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ea7f4174d2e4194289cb0c4e172d83e79a6404297ff95f2875cf9ac9bced8ba"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb2954155bb8f63bb19d56d80e5e5320b61d71084617ed89efedb861a684baea"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04f2b712a2206e13800a8136b07aaedc23af3facab84918e7aa89e4be0260032"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:eda5c1e2a715a4cbbca2d6d304988460942551e4e5e3b7457b50943cd741626d"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:9abc80fe8c1f87218db116016de575a7998ab1629078c90840e8d11ab423ee25"}, + {file = "rpds_py-0.24.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6a727fd083009bc83eb83d6950f0c32b3c94c8b80a9b667c87f4bd1274ca30ba"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e0f3ef95795efcd3b2ec3fe0a5bcfb5dadf5e3996ea2117427e524d4fbf309c6"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:2c13777ecdbbba2077670285dd1fe50828c8742f6a4119dbef6f83ea13ad10fb"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79e8d804c2ccd618417e96720ad5cd076a86fa3f8cb310ea386a3e6229bae7d1"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd822f019ccccd75c832deb7aa040bb02d70a92eb15a2f16c7987b7ad4ee8d83"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0047638c3aa0dbcd0ab99ed1e549bbf0e142c9ecc173b6492868432d8989a046"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5b66d1b201cc71bc3081bc2f1fc36b0c1f268b773e03bbc39066651b9e18391"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbcbb6db5582ea33ce46a5d20a5793134b5365110d84df4e30b9d37c6fd40ad3"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63981feca3f110ed132fd217bf7768ee8ed738a55549883628ee3da75bb9cb78"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3a55fc10fdcbf1a4bd3c018eea422c52cf08700cf99c28b5cb10fe97ab77a0d3"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:c30ff468163a48535ee7e9bf21bd14c7a81147c0e58a36c1078289a8ca7af0bd"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:369d9c6d4c714e36d4a03957b4783217a3ccd1e222cdd67d464a3a479fc17796"}, + {file = "rpds_py-0.24.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:24795c099453e3721fda5d8ddd45f5dfcc8e5a547ce7b8e9da06fecc3832e26f"}, + {file = "rpds_py-0.24.0.tar.gz", hash = "sha256:772cc1b2cd963e7e17e6cc55fe0371fb9c704d63e44cacec7b9b7f523b78919e"}, ] [[package]] @@ -3105,19 +3074,19 @@ PyYAML = "*" [[package]] name = "setuptools" -version = "76.1.0" +version = "78.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "setuptools-76.1.0-py3-none-any.whl", hash = "sha256:34750dcb17d046929f545dec9b8349fe42bf4ba13ddffee78428aec422dbfb73"}, - {file = "setuptools-76.1.0.tar.gz", hash = "sha256:4959b9ad482ada2ba2320c8f1a8d8481d4d8d668908a7a1b84d987375cd7f5bd"}, + {file = "setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8"}, + {file = "setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] -core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] @@ -3361,14 +3330,14 @@ files = [ [[package]] name = "types-cffi" -version = "1.17.0.20250319" +version = "1.17.0.20250326" description = "Typing stubs for cffi" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_cffi-1.17.0.20250319-py3-none-any.whl", hash = "sha256:5e95f0f10d3f2fd0a8a0a10f6b8b1e0e6ff47796ad2fdd4302b5e514b64d6af4"}, - {file = "types_cffi-1.17.0.20250319.tar.gz", hash = "sha256:66b0656818e5363f136a0a361f28e41330b55f83d390b14c6bf56026f57b3603"}, + {file = "types_cffi-1.17.0.20250326-py3-none-any.whl", hash = "sha256:5af4ecd7374ae0d5fa9e80864e8d4b31088cc32c51c544e3af7ed5b5ed681447"}, + {file = "types_cffi-1.17.0.20250326.tar.gz", hash = "sha256:6c8fea2c2f34b55e5fb77b1184c8ad849d57cf0ddccbc67a62121ac4b8b32254"}, ] [package.dependencies] @@ -3416,14 +3385,14 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20241230" +version = "6.0.12.20250326" description = "Typing stubs for PyYAML" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, - {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, + {file = "types_pyyaml-6.0.12.20250326-py3-none-any.whl", hash = "sha256:961871cfbdc1ad8ae3cb6ae3f13007262bcfc168adc513119755a6e4d5d7ed65"}, + {file = "types_pyyaml-6.0.12.20250326.tar.gz", hash = "sha256:5e2d86d8706697803f361ba0b8188eef2999e1c372cd4faee4ebb0844b8a4190"}, ] [[package]] @@ -3444,14 +3413,14 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20250306" +version = "2.32.0.20250328" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b"}, - {file = "types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1"}, + {file = "types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2"}, + {file = "types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32"}, ] [package.dependencies] @@ -3459,14 +3428,14 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "76.0.0.20250313" +version = "78.1.0.20250329" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_setuptools-76.0.0.20250313-py3-none-any.whl", hash = "sha256:bf454b2a49b8cfd7ebcf5844d4dd5fe4c8666782df1e3663c5866fd51a47460e"}, - {file = "types_setuptools-76.0.0.20250313.tar.gz", hash = "sha256:b2be66f550f95f3cad2a7d46177b273c7e9c80df7d257fa57addbbcfc8126a9e"}, + {file = "types_setuptools-78.1.0.20250329-py3-none-any.whl", hash = "sha256:ea47eab891afb506f470eee581dcde44d64dc99796665da794da6f83f50f6776"}, + {file = "types_setuptools-78.1.0.20250329.tar.gz", hash = "sha256:31e62950c38b8cc1c5114b077504e36426860a064287cac11b9666ab3a483234"}, ] [package.dependencies] @@ -3474,26 +3443,26 @@ setuptools = "*" [[package]] name = "types-simplejson" -version = "3.20.0.20250318" +version = "3.20.0.20250326" description = "Typing stubs for simplejson" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_simplejson-3.20.0.20250318-py3-none-any.whl", hash = "sha256:e8c9cdb06b566b6ca1c7bf3d4c97fbd69a6f6a5aea760ef127f31e638a094c26"}, - {file = "types_simplejson-3.20.0.20250318.tar.gz", hash = "sha256:5ada2caa2f76826a90b97985f7b0caf55088a23ed4eb9c39fc1f5cb00b985e09"}, + {file = "types_simplejson-3.20.0.20250326-py3-none-any.whl", hash = "sha256:db1ddea7b8f7623b27a137578f22fc6c618db8c83ccfb1828ca0d2f0ec11efa7"}, + {file = "types_simplejson-3.20.0.20250326.tar.gz", hash = "sha256:b2689bc91e0e672d7a5a947b4cb546b76ae7ddc2899c6678e72a10bf96cd97d2"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" +version = "4.13.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5"}, + {file = "typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b"}, ] [[package]] From 60499308e6670455bc8bfd1be5d5bb36849935cd Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Thu, 15 May 2025 09:26:13 +0100 Subject: [PATCH 550/567] Schemas v5.33.0 (#1665) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 77e9c30c8b..e1c0d594b1 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.32.0 +v5.33.0 From 834a246bf9cd689b43fef7dad37ae41417db0983 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Mon, 19 May 2025 12:59:28 +0100 Subject: [PATCH 551/567] Make AJV Confirgurable (#1642) * point to validator branch * update AJV_HOST enviroment variable to AJV_VALIDATOR_HOST * revert validator tag --- docker-compose-schema-validator.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-schema-validator.yml b/docker-compose-schema-validator.yml index 64f9ed7d22..9bd00b8f06 100644 --- a/docker-compose-schema-validator.yml +++ b/docker-compose-schema-validator.yml @@ -11,7 +11,7 @@ services: networks: - eq-schema environment: - AJV_HOST: ajv-validator + AJV_VALIDATOR_HOST: ajv-validator ports: - 5001:5000 depends_on: From 61c8e2c72a16226b450ef14c39e874f698a6674a Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Mon, 19 May 2025 16:31:26 +0100 Subject: [PATCH 552/567] Schemas v5.34.0 (#1666) Signed-off-by: Yuyutsu Rai --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index e1c0d594b1..c92d06d632 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.33.0 +v5.34.0 From afa9fef66b189712d2505445ff34b404f66b2bd8 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 20 May 2025 09:50:26 +0100 Subject: [PATCH 553/567] Move error messages to class and module-level constants --- app/forms/questionnaire_form.py | 5 +-- app/forms/validators.py | 5 +-- app/questionnaire/rules/operations.py | 5 ++- app/questionnaire/rules/rule_evaluator.py | 4 +-- app/questionnaire/value_source_resolver.py | 11 +++---- app/routes/questionnaire.py | 4 +-- app/routes/session.py | 9 +++--- app/services/supplementary_data.py | 10 +++--- app/setup.py | 36 ++++++++++++---------- app/utilities/metadata_parser_v2.py | 5 +-- app/utilities/supplementary_data_parser.py | 20 ++++++------ app/views/handlers/individual_response.py | 4 +-- 12 files changed, 62 insertions(+), 56 deletions(-) diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 3408b56836..9af7131586 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -40,6 +40,8 @@ # pylint: disable=too-many-locals class QuestionnaireForm(FlaskForm): + INVALID_PERIOD_RANGE = "Period range must have a start and end date" + def __init__( self, schema: QuestionnaireSchema, @@ -309,7 +311,6 @@ def _get_period_range_for_single_date( date_from: Mapping[str, dict], date_to: Mapping[str, dict], ) -> timedelta: - invalid_period_range = "Period range must have a start and end date" list_item_id = self.location.list_item_id if self.location else None value_source_resolver = ValueSourceResolver( data_stores=self.data_stores, @@ -338,7 +339,7 @@ def _get_period_range_for_single_date( ) if not min_period_date or not max_period_date: - raise ValueError(invalid_period_range) + raise ValueError(QuestionnaireForm.INVALID_PERIOD_RANGE) # Work out the largest possible range, for date range question period_range = max_period_date - min_period_date diff --git a/app/forms/validators.py b/app/forms/validators.py index 3d12af38f1..73ba31b07e 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -408,6 +408,8 @@ def _build_range_length_error(period_object: PeriodType) -> str: class SumCheck: + INVALID_MULTIPLE_CONDITION = "There are multiple conditions, but equals is not one of them. We only support <= and >=" + def __init__(self, messages: OptionalMessage = None, currency: str | None = None): self.messages = {**error_messages, **(messages or {})} self.currency = currency @@ -424,8 +426,7 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: - invalid_multiple_condition = "There are multiple conditions, but equals is not one of them. We only support <= and >=" - raise ValueError(invalid_multiple_condition) from exc + raise ValueError(SumCheck.INVALID_MULTIPLE_CONDITION) from exc condition = f"{conditions[0]} or equals" else: diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 3992c50c5e..c8a4f94ced 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -54,7 +54,7 @@ class Operations: """ A class to group the operations """ - + INVALID_NEGATIVE_DAYS_OFFSET = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" def __init__( self, language: str, @@ -135,7 +135,6 @@ def resolve_date_from_string( offset: DateOffset | None = None, offset_by_full_weeks: bool = False, ) -> date | None: - invalid_negative_days_offset = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" datetime_value = parse_datetime(date_string) if not datetime_value: return None @@ -147,7 +146,7 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: - raise ValueError(invalid_negative_days_offset) + raise ValueError(Operations.INVALID_NEGATIVE_DAYS_OFFSET) days_difference = ( value_as_date.weekday() - DAYS_OF_WEEK[day_of_week_offset] diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index f4456430c6..60b815ff87 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -55,10 +55,10 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operator_name = next(iter(rule)) operator = Operator(operator_name, self.operations) operands = rule[operator_name] - invalid_rule = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" + invalid_operands_rule = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" if not isinstance(operands, Sequence): - raise TypeError(invalid_rule) + raise TypeError(invalid_operands_rule) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 7d58acfd48..391dfc113a 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -26,6 +26,9 @@ @dataclass class ValueSourceResolver: + INVALID_SELECTOR_LOCATION = "list_item_selector source location used without location" + LOCATION_REQUIRED_ERROR = "location is required to resolve block progress" + data_stores: DataStores schema: QuestionnaireSchema location: LocationType | None @@ -95,13 +98,10 @@ def _resolve_list_item_id_for_answer_id(self, answer_id: str) -> str | None: def _resolve_list_item_id_for_value_source( self, value_source: Mapping ) -> str | None: - invalid_selector_location = ( - "list_item_selector source location used without location" - ) if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: - raise InvalidLocationException(invalid_selector_location) + raise InvalidLocationException(self.INVALID_SELECTOR_LOCATION) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -203,7 +203,6 @@ def _resolve_progress_value_source( ) -> ValueSourceEscapedTypes | ValueSourceTypes | None: identifier = value_source["identifier"] selector = value_source["selector"] - location_required_error = "location is required to resolve block progress" if selector == "section": # List item id is set to None here as we do not support checking progress value sources for # repeating sections @@ -213,7 +212,7 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - raise ValueError(location_required_error) + raise ValueError(self.LOCATION_REQUIRED_ERROR) if not self._is_block_on_path(identifier): return None diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 3d702e2665..0f2ec59fc5 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -68,16 +68,16 @@ name="post_submission", import_name=__name__, url_prefix="/submitted/" ) +REPEATED_SUBMISSION_ERROR = "The Questionnaire has been previously submitted" @questionnaire_blueprint.before_request @login_required def before_questionnaire_request() -> Response | None: - repeated_submission_error = "The Questionnaire has been previously submitted" if request.method == "OPTIONS": return None if cookie_session.get("submitted"): - raise PreviouslySubmittedException(repeated_submission_error) + raise PreviouslySubmittedException(REPEATED_SUBMISSION_ERROR) metadata = get_metadata(current_user) if not metadata: diff --git a/app/routes/session.py b/app/routes/session.py index 1f326528a7..3a25166a16 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -41,6 +41,9 @@ session_blueprint = Blueprint("session", __name__) +INVALID_RUNNER_CLAIMS_ERROR = "Invalid runner claims" +INVALID_QUESTIONNAIRE_CLAIMS_ERROR = "Invalid questionnaire claims" + @session_blueprint.after_request def add_cache_control(response: Response) -> Response: @@ -300,22 +303,20 @@ def get_signed_out() -> Response | str: def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: - runner_claims_error = "Invalid runner claims" try: return validate_runner_claims_v2(decrypted_token) except ValidationError as e: - raise InvalidTokenException(runner_claims_error) from e + raise InvalidTokenException(INVALID_RUNNER_CLAIMS_ERROR) from e def get_questionnaire_claims( decrypted_token: Mapping, schema_metadata: Iterable[Mapping[str, str]] ) -> dict: - questionnaire_claims_error = "Invalid questionnaire claims" try: claims = decrypted_token.get("survey_metadata", {}).get("data", {}) return validate_questionnaire_claims(claims, schema_metadata, unknown=INCLUDE) except ValidationError as e: - raise InvalidTokenException(questionnaire_claims_error) from e + raise InvalidTokenException(INVALID_QUESTIONNAIRE_CLAIMS_ERROR) from e diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index 06ae1452a7..f54671141f 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -30,7 +30,11 @@ logger = get_logger() + + class SupplementaryDataRequestFailed(Exception): + SUPPLEMENTARY_DATA_EMPTY_ERROR = "Supplementary data has no data to decrypt" + INVALID_SUPPLEMENTARY_DATA = "Invalid supplementary data" def __str__(self) -> str: return "Supplementary Data request failed" @@ -121,8 +125,7 @@ def decrypt_supplementary_data( return supplementary_data except InvalidTokenException as e: raise InvalidSupplementaryData from e - supplementary_data_empty_error = "Supplementary data has no data to decrypt" - raise ValidationError(supplementary_data_empty_error) + raise ValidationError(SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_EMPTY_ERROR) def validate_supplementary_data( @@ -132,7 +135,6 @@ def validate_supplementary_data( survey_id: str, sds_schema_version: str | None = None, ) -> dict[str, str | dict | int | list]: - invalid_supplementary_data = "Invalid supplementary data" try: return validate_supplementary_data_v1( supplementary_data=supplementary_data, @@ -142,4 +144,4 @@ def validate_supplementary_data( sds_schema_version=sds_schema_version, ) except ValidationError as e: - raise ValidationError(invalid_supplementary_data) from e + raise ValidationError(SupplementaryDataRequestFailed.INVALID_SUPPLEMENTARY_DATA) from e diff --git a/app/setup.py b/app/setup.py index 718f8c9a07..8a651bf213 100644 --- a/app/setup.py +++ b/app/setup.py @@ -84,6 +84,16 @@ logger = get_logger() +MISSING_BUCKET_ID_MESSAGE = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" +MISSING_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST Missing" +MISSING_SECONDARY_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" +MISSING_SDS_CLIENT_ID = "Setting SDS_OAUTH2_CLIENT_ID Missing" +MISSING_CIR_CLIENT_ID = "Setting CIR_OAUTH2_CLIENT_ID Missing" +MISSING_TOKEN_BACKEND = "Setting OIDC_TOKEN_BACKEND Missing" +MISSING_FEEDBACK_BUCKET_ID = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" +INVALID_SECRET_KEY = "Application secret key does not exist" + + UNKNOWN_IMPLEMENTED_ERROR = { "unknown_storage_backend": "Unknown EQ_STORAGE_BACKEND", "unknown_submission": "Unknown EQ_SUBMISSION_BACKEND", @@ -95,9 +105,7 @@ class MissingEnvironmentVariable(Exception): - MISSING_BUCKET_ID_MESSAGE = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" - MISSING_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST Missing" - MISSING_SECONDARY_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" + pass class AWSReverseProxied: @@ -343,7 +351,7 @@ def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): raise MissingEnvironmentVariable( - MissingEnvironmentVariable.MISSING_BUCKET_ID_MESSAGE + MISSING_BUCKET_ID_MESSAGE ) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) @@ -353,11 +361,11 @@ def setup_submitter(application): if not host: raise MissingEnvironmentVariable( - MissingEnvironmentVariable.MISSING_HOST_MESSAGE + MISSING_HOST_MESSAGE ) if not secondary_host: raise MissingEnvironmentVariable( - MissingEnvironmentVariable.MISSING_SECONDARY_HOST_MESSAGE + MISSING_SECONDARY_HOST_MESSAGE ) application.eq["submitter"] = RabbitMQSubmitter( @@ -392,19 +400,15 @@ def setup_task_client(application): def setup_oidc(application): - missing_sds_client_id = "Setting SDS_OAUTH2_CLIENT_ID Missing" - missing_cir_client_id = "Setting CIR_OAUTH2_CLIENT_ID Missing" - missing_token_backen = "Setting OIDC_TOKEN_BACKEND Missing" - def client_ids_exist(): if not application.config.get("SDS_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable(missing_sds_client_id) + raise MissingEnvironmentVariable(MISSING_SDS_CLIENT_ID) if not application.config.get("CIR_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable(missing_cir_client_id) + raise MissingEnvironmentVariable(MISSING_CIR_CLIENT_ID) if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): - raise MissingEnvironmentVariable(missing_token_backen) + raise MissingEnvironmentVariable(MISSING_TOKEN_BACKEND) if oidc_token_backend == "gcp": client_ids_exist() @@ -431,10 +435,9 @@ def setup_publisher(application): def setup_feedback(application): - missing_feedback__bucket_id = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): - raise MissingEnvironmentVariable(missing_feedback__bucket_id) + raise MissingEnvironmentVariable(MISSING_FEEDBACK_BUCKET_ID) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( bucket_name=bucket_name @@ -479,10 +482,9 @@ def add_blueprints(application): def setup_secure_cookies(application): - invalid_secret_key = "Application secret key does not exist" secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") if not secret_key: - raise ValueError(invalid_secret_key) + raise ValueError(INVALID_SECRET_KEY) application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 18065d29c7..1dd6577529 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -70,6 +70,8 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument class RunnerMetadataSchema(Schema, StripWhitespaceMixin): """Metadata which is required for the operation of runner itself""" + MISSING_METADATA_OPTION = "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + jti = VALIDATORS["uuid"]() tx_id = VALIDATORS["uuid"]() case_id = VALIDATORS["uuid"]() @@ -105,8 +107,7 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument if data.get(option) ] if len(options) == 0: - missing_metadata_option = "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" - raise ValidationError(missing_metadata_option) + raise ValidationError(self.MISSING_METADATA_OPTION) if len(options) > 1: invalid_metadata_combination = ( "Only one of schema_name, schema_url or cir_instrument_id should be specified " diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index be1cafaa3f..6abb5e6f60 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -34,6 +34,9 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): + SDS_IDENTIFIER_ERROR = "Supplementary data did not return the specified Identifier" + + identifier = VALIDATORS["string"](validate=validate.Length(min=1)) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @@ -41,14 +44,14 @@ class SupplementaryData(Schema, StripWhitespaceMixin): def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: - sds_identifier_error = ( - "Supplementary data did not return the specified Identifier" - ) if data and data["identifier"] != self.context["identifier"]: - raise ValidationError(sds_identifier_error) + raise ValidationError(self.SDS_IDENTIFIER_ERROR) class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): + DATASET_ID_ERROR = "Supplementary data did not return the specified Dataset ID" + SURVEY_ID_ERROR = "Supplementary data did not return the specified Survey ID" + MISMATCH_SDS_VERSION = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" dataset_id = VALIDATORS["uuid"]() survey_id = VALIDATORS["string"](validate=validate.Length(min=1)) data = fields.Nested( @@ -62,20 +65,17 @@ class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): def validate_payload( # pylint: disable=unused-argument self, payload: Mapping, **kwargs: Any ) -> None: - dataset_id_error = "Supplementary data did not return the specified Dataset ID" - survey_id_error = "Supplementary data did not return the specified Survey ID" - mismatch_sds_version = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" if payload: if payload["dataset_id"] != self.context["dataset_id"]: - raise ValidationError(dataset_id_error) + raise ValidationError(self.DATASET_ID_ERROR) if payload["survey_id"] != self.context["survey_id"]: - raise ValidationError(survey_id_error) + raise ValidationError(self.SURVEY_ID_ERROR) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): - raise ValidationError(mismatch_sds_version) + raise ValidationError(self.MISMATCH_SDS_VERSION) def validate_supplementary_data_v1( diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 2d6e6c4f5c..f13ab95df1 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -43,6 +43,7 @@ class IndividualResponsePostalDeadlinePast(Exception): class IndividualResponseHandler: + RESPONSE_LIMIT_MESSAGE = "Individual response limit has been reached" @staticmethod def _person_name_transforms(list_name: str) -> list[Mapping]: return [ @@ -224,14 +225,13 @@ def _publish_fulfilment_request(self, mobile_number: str | None = None) -> None: raise IndividualResponseFulfilmentRequestPublicationFailed from exc def _check_individual_response_count(self) -> None: - response_limit_message = "Individual response limit has been reached" if ( self._questionnaire_store.data_stores.response_metadata.get( "individual_response_count", 0 ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): - raise IndividualResponseLimitReached(response_limit_message) + raise IndividualResponseLimitReached(self.RESPONSE_LIMIT_MESSAGE) def _update_individual_response_count(self) -> None: response_metadata = self._questionnaire_store.data_stores.response_metadata From 75d47399b454a45d8b003284cd66b8f9978a3fe9 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Tue, 20 May 2025 11:45:18 +0100 Subject: [PATCH 554/567] Reformatted the files --- app/questionnaire/rules/operations.py | 2 ++ app/questionnaire/value_source_resolver.py | 4 +++- app/routes/questionnaire.py | 1 + app/services/supplementary_data.py | 7 ++++--- app/setup.py | 12 +++--------- app/utilities/metadata_parser_v2.py | 4 +++- app/utilities/supplementary_data_parser.py | 1 - app/views/handlers/individual_response.py | 1 + 8 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index c8a4f94ced..980ad3daa2 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -54,7 +54,9 @@ class Operations: """ A class to group the operations """ + INVALID_NEGATIVE_DAYS_OFFSET = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" + def __init__( self, language: str, diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index 391dfc113a..fafd13786b 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -26,7 +26,9 @@ @dataclass class ValueSourceResolver: - INVALID_SELECTOR_LOCATION = "list_item_selector source location used without location" + INVALID_SELECTOR_LOCATION = ( + "list_item_selector source location used without location" + ) LOCATION_REQUIRED_ERROR = "location is required to resolve block progress" data_stores: DataStores diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 0f2ec59fc5..50a6dbc9d7 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -70,6 +70,7 @@ REPEATED_SUBMISSION_ERROR = "The Questionnaire has been previously submitted" + @questionnaire_blueprint.before_request @login_required def before_questionnaire_request() -> Response | None: diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index f54671141f..0a53ec37a6 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -30,11 +30,10 @@ logger = get_logger() - - class SupplementaryDataRequestFailed(Exception): SUPPLEMENTARY_DATA_EMPTY_ERROR = "Supplementary data has no data to decrypt" INVALID_SUPPLEMENTARY_DATA = "Invalid supplementary data" + def __str__(self) -> str: return "Supplementary Data request failed" @@ -144,4 +143,6 @@ def validate_supplementary_data( sds_schema_version=sds_schema_version, ) except ValidationError as e: - raise ValidationError(SupplementaryDataRequestFailed.INVALID_SUPPLEMENTARY_DATA) from e + raise ValidationError( + SupplementaryDataRequestFailed.INVALID_SUPPLEMENTARY_DATA + ) from e diff --git a/app/setup.py b/app/setup.py index 8a651bf213..52b4f3c1b9 100644 --- a/app/setup.py +++ b/app/setup.py @@ -350,9 +350,7 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): - raise MissingEnvironmentVariable( - MISSING_BUCKET_ID_MESSAGE - ) + raise MissingEnvironmentVariable(MISSING_BUCKET_ID_MESSAGE) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) elif application.config["EQ_SUBMISSION_BACKEND"] == "rabbitmq": @@ -360,13 +358,9 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - raise MissingEnvironmentVariable( - MISSING_HOST_MESSAGE - ) + raise MissingEnvironmentVariable(MISSING_HOST_MESSAGE) if not secondary_host: - raise MissingEnvironmentVariable( - MISSING_SECONDARY_HOST_MESSAGE - ) + raise MissingEnvironmentVariable(MISSING_SECONDARY_HOST_MESSAGE) application.eq["submitter"] = RabbitMQSubmitter( host=host, diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 1dd6577529..fb1c2d6ea0 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -70,7 +70,9 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument class RunnerMetadataSchema(Schema, StripWhitespaceMixin): """Metadata which is required for the operation of runner itself""" - MISSING_METADATA_OPTION = "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + MISSING_METADATA_OPTION = ( + "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" + ) jti = VALIDATORS["uuid"]() tx_id = VALIDATORS["uuid"]() diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 6abb5e6f60..da6975e8df 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -36,7 +36,6 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): SDS_IDENTIFIER_ERROR = "Supplementary data did not return the specified Identifier" - identifier = VALIDATORS["string"](validate=validate.Length(min=1)) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index f13ab95df1..81de0a5950 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -44,6 +44,7 @@ class IndividualResponsePostalDeadlinePast(Exception): class IndividualResponseHandler: RESPONSE_LIMIT_MESSAGE = "Individual response limit has been reached" + @staticmethod def _person_name_transforms(list_name: str) -> list[Mapping]: return [ From bd63f163ed82d92cdee476951c6bbb196c11baf0 Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 23 May 2025 10:07:54 +0100 Subject: [PATCH 555/567] Apply code changes based on review feedback --- app/authentication/user.py | 4 +- app/authentication/user_id_generator.py | 8 +-- app/data_models/answer_store.py | 4 +- app/data_models/list_store.py | 4 +- app/data_models/supplementary_data_store.py | 4 +- app/forms/questionnaire_form.py | 4 +- app/forms/validators.py | 10 ++-- app/questionnaire/placeholder_renderer.py | 4 +- app/questionnaire/placeholder_transforms.py | 4 +- app/questionnaire/rules/operations.py | 4 +- app/questionnaire/rules/rule_evaluator.py | 4 +- app/questionnaire/rules/utils.py | 4 +- app/questionnaire/value_source_resolver.py | 12 ++-- app/routes/questionnaire.py | 4 +- app/routes/session.py | 14 +++-- app/secrets.py | 4 +- app/services/supplementary_data.py | 8 +-- app/settings.py | 8 +-- app/setup.py | 61 ++++++++++----------- app/storage/datastore.py | 4 +- app/storage/storage.py | 4 +- app/storage/storage_encryption.py | 12 ++-- app/utilities/metadata_parser_v2.py | 14 +++-- app/utilities/supplementary_data_parser.py | 22 ++++---- app/views/handlers/block.py | 4 +- app/views/handlers/individual_response.py | 4 +- app/views/handlers/section.py | 4 +- app/views/handlers/submit_questionnaire.py | 4 +- 28 files changed, 123 insertions(+), 118 deletions(-) diff --git a/app/authentication/user.py b/app/authentication/user.py index 3d9708fb04..9157fb4a73 100644 --- a/app/authentication/user.py +++ b/app/authentication/user.py @@ -2,11 +2,11 @@ class User(UserMixin): - MISSING_USER_SESSION_ERROR = "No user_id or user_ik found in session" + USER_SESSION_ERROR_MESSAGE = "No user_id or user_ik found in session" def __init__(self, user_id: str | None, user_ik: str | None) -> None: if user_id and user_ik: self.user_id = user_id self.user_ik = user_ik else: - raise ValueError(self.MISSING_USER_SESSION_ERROR) + raise ValueError(self.USER_SESSION_ERROR_MESSAGE) diff --git a/app/authentication/user_id_generator.py b/app/authentication/user_id_generator.py index 73021138df..a5d454a9b2 100644 --- a/app/authentication/user_id_generator.py +++ b/app/authentication/user_id_generator.py @@ -11,14 +11,14 @@ class UserIDGenerator: - USER_ID_SALT_ERROR = "user_id_salt is required" - USER_IK_SALT_ERROR = "user_ik_salt is required" + USER_ID_SALT_ERROR_MESSAGE = "user_id_salt is required" + USER_IK_SALT_ERROR_MESSAGE = "user_ik_salt is required" def __init__(self, iterations: int, user_id_salt: str, user_ik_salt: str) -> None: if user_id_salt is None: - raise ValueError(self.USER_ID_SALT_ERROR) + raise ValueError(self.USER_ID_SALT_ERROR_MESSAGE) if user_ik_salt is None: - raise ValueError(self.USER_IK_SALT_ERROR) + raise ValueError(self.USER_IK_SALT_ERROR_MESSAGE) self._iterations = iterations self._user_id_salt = user_id_salt diff --git a/app/data_models/answer_store.py b/app/data_models/answer_store.py index 52b5716bd3..10b83c41c1 100644 --- a/app/data_models/answer_store.py +++ b/app/data_models/answer_store.py @@ -53,11 +53,11 @@ def _build_map( @staticmethod def _validate(answer: Answer) -> None: - answer_type_error = ( + answer_type_error_message = ( f"Method only supports Answer argument type, found type: {type(answer)}" ) if not isinstance(answer, Answer): - raise TypeError(answer_type_error) + raise TypeError(answer_type_error_message) @property def is_dirty(self) -> bool: diff --git a/app/data_models/list_store.py b/app/data_models/list_store.py index 9762f59f7a..54db73ff8c 100644 --- a/app/data_models/list_store.py +++ b/app/data_models/list_store.py @@ -82,10 +82,10 @@ def first(self) -> str: try: return self.items[0] except IndexError as e: - empty_list_error = ( + empty_list_error_message = ( f"unable to access first item in list, list '{self.name}' is empty" ) - raise IndexError(empty_list_error) from e + raise IndexError(empty_list_error_message) from e @property def count(self) -> int: diff --git a/app/data_models/supplementary_data_store.py b/app/data_models/supplementary_data_store.py index 14d563f47b..79ccb04392 100644 --- a/app/data_models/supplementary_data_store.py +++ b/app/data_models/supplementary_data_store.py @@ -144,10 +144,10 @@ def _resolve_value( return None if not isinstance(value, Mapping): # if value is not None, and also not index able, raise an error - invalid_selector_error = ( + invalid_selector_error_message = ( f"Cannot use the selector `{selector}` on non-nested data" ) - raise InvalidSupplementaryDataSelector(invalid_selector_error) + raise InvalidSupplementaryDataSelector(invalid_selector_error_message) value = value.get(selector) return value diff --git a/app/forms/questionnaire_form.py b/app/forms/questionnaire_form.py index 9af7131586..58a20e33d5 100644 --- a/app/forms/questionnaire_form.py +++ b/app/forms/questionnaire_form.py @@ -40,7 +40,7 @@ # pylint: disable=too-many-locals class QuestionnaireForm(FlaskForm): - INVALID_PERIOD_RANGE = "Period range must have a start and end date" + PERIOD_RANGE_ERROR_MESSAGE = "Period range must have a start and end date" def __init__( self, @@ -339,7 +339,7 @@ def _get_period_range_for_single_date( ) if not min_period_date or not max_period_date: - raise ValueError(QuestionnaireForm.INVALID_PERIOD_RANGE) + raise ValueError(QuestionnaireForm.PERIOD_RANGE_ERROR_MESSAGE) # Work out the largest possible range, for date range question period_range = max_period_date - min_period_date diff --git a/app/forms/validators.py b/app/forms/validators.py index 73ba31b07e..4a5743d173 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -408,7 +408,9 @@ def _build_range_length_error(period_object: PeriodType) -> str: class SumCheck: - INVALID_MULTIPLE_CONDITION = "There are multiple conditions, but equals is not one of them. We only support <= and >=" + MULTIPLE_CONDITION_ERROR_MESSAGE = ( + "There are multiple conditions, but equals is not one of them. We only support <= and >=" + ) def __init__(self, messages: OptionalMessage = None, currency: str | None = None): self.messages = {**error_messages, **(messages or {})} @@ -426,7 +428,7 @@ def __call__( try: conditions.remove("equals") except ValueError as exc: - raise ValueError(SumCheck.INVALID_MULTIPLE_CONDITION) from exc + raise ValueError(SumCheck.MULTIPLE_CONDITION_ERROR_MESSAGE) from exc condition = f"{conditions[0]} or equals" else: @@ -468,8 +470,8 @@ def _is_valid( if condition == "less than or equals": return total <= target_total, "TOTAL_SUM_NOT_LESS_THAN_OR_EQUALS" - invalid_condition = f"Condition '{condition}' is not implemented" - raise NotImplementedError(invalid_condition) + unimplemented_condition_error_message = f"Condition '{condition}' is not implemented" + raise NotImplementedError(unimplemented_condition_error_message) class MutuallyExclusiveCheck: diff --git a/app/questionnaire/placeholder_renderer.py b/app/questionnaire/placeholder_renderer.py index 64c818f561..33f437d4c0 100644 --- a/app/questionnaire/placeholder_renderer.py +++ b/app/questionnaire/placeholder_renderer.py @@ -20,7 +20,7 @@ class PlaceholderRenderer: strings """ - PLACEHOLDER_NOT_FOUND = "No placeholder found to render" + PLACEHOLDER_ERROR_MESSAGE = "No placeholder found to render" def __init__( self, @@ -99,7 +99,7 @@ def render_placeholder( placeholder_data["text"] = plural_forms[plural_form_key] if "text" not in placeholder_data and "placeholders" not in placeholder_data: - raise ValueError(self.PLACEHOLDER_NOT_FOUND) + raise ValueError(self.PLACEHOLDER_ERROR_MESSAGE) transformed_values = placeholder_parser(placeholder_data["placeholders"]) formatted_placeholder_data: str = placeholder_data["text"].format( diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index dd5c0598d7..408ee8ac35 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -300,7 +300,6 @@ def format_ordinal( return f"{number_to_format}{indicator}" def get_ordinal_indicator(self, number_to_format: int) -> str: - invalid_language_code = f"Language code '{self.language}' not implemented." if self.language in ["en", "eo"]: if 11 <= number_to_format % 100 <= 13: return "th" @@ -327,7 +326,8 @@ def get_ordinal_indicator(self, number_to_format: int) -> str: 19: "eg", }.get(number_to_format, "fed") - raise NotImplementedError(invalid_language_code) + language_code_error_message = f"Language code '{self.language}' not implemented." + raise NotImplementedError(language_code_error_message) def first_non_empty_item(self, items: Sequence[str]) -> str: """ diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index 980ad3daa2..f0974bccb1 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -55,7 +55,7 @@ class Operations: A class to group the operations """ - INVALID_NEGATIVE_DAYS_OFFSET = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" + NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" def __init__( self, @@ -148,7 +148,7 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: - raise ValueError(Operations.INVALID_NEGATIVE_DAYS_OFFSET) + raise ValueError(Operations.NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE ) days_difference = ( value_as_date.weekday() - DAYS_OF_WEEK[day_of_week_offset] diff --git a/app/questionnaire/rules/rule_evaluator.py b/app/questionnaire/rules/rule_evaluator.py index 60b815ff87..6abff8f571 100644 --- a/app/questionnaire/rules/rule_evaluator.py +++ b/app/questionnaire/rules/rule_evaluator.py @@ -55,10 +55,10 @@ def _evaluate(self, rule: dict[str, Sequence]) -> bool | date | None: operator_name = next(iter(rule)) operator = Operator(operator_name, self.operations) operands = rule[operator_name] - invalid_operands_rule = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" + operands_rule_error_message = f"The rule is invalid, operands should be of type Sequence and not {type(operands)}" if not isinstance(operands, Sequence): - raise TypeError(invalid_operands_rule) + raise TypeError(operands_rule_error_message) resolved_operands: Iterable[ResolvedOperand] diff --git a/app/questionnaire/rules/utils.py b/app/questionnaire/rules/utils.py index ab2031f0e7..cac041a93e 100644 --- a/app/questionnaire/rules/utils.py +++ b/app/questionnaire/rules/utils.py @@ -24,7 +24,7 @@ def parse_datetime(date_string: str | None) -> datetime | None: Convert `date` from string into `datetime` object. `date` can be 'YYYY-MM-DD', 'YYYY-MM','now' or ISO 8601 format. Note that in the shorthand YYYY-MM format, day_of_month is assumed to be 1. """ - invalid_date_format = f"'{date_string}' is not in a valid date format" + date_format_error_message = f"'{date_string}' is not in a valid date format" if not date_string: return None @@ -34,4 +34,4 @@ def parse_datetime(date_string: str | None) -> datetime | None: try: return parse_iso_8601_datetime(date_string) except ValueError as ex: - raise ValueError(invalid_date_format) from ex + raise ValueError(date_format_error_message) from ex diff --git a/app/questionnaire/value_source_resolver.py b/app/questionnaire/value_source_resolver.py index fafd13786b..d00363090a 100644 --- a/app/questionnaire/value_source_resolver.py +++ b/app/questionnaire/value_source_resolver.py @@ -26,10 +26,10 @@ @dataclass class ValueSourceResolver: - INVALID_SELECTOR_LOCATION = ( + SELECTOR_LOCATION_ERROR_MESSAGE = ( "list_item_selector source location used without location" ) - LOCATION_REQUIRED_ERROR = "location is required to resolve block progress" + LOCATION_REQUIRED_ERROR_MESSAGE = "location is required to resolve block progress" data_stores: DataStores schema: QuestionnaireSchema @@ -103,7 +103,7 @@ def _resolve_list_item_id_for_value_source( if list_item_selector := value_source.get("list_item_selector"): if list_item_selector["source"] == "location": if not self.location: - raise InvalidLocationException(self.INVALID_SELECTOR_LOCATION) + raise InvalidLocationException(self.SELECTOR_LOCATION_ERROR_MESSAGE) # Type ignore: the identifier is a string, same below return getattr(self.location, list_item_selector["identifier"]) # type: ignore @@ -214,7 +214,7 @@ def _resolve_progress_value_source( if selector == "block": if not self.location: - raise ValueError(self.LOCATION_REQUIRED_ERROR) + raise ValueError(self.LOCATION_REQUIRED_ERROR_MESSAGE) if not self._is_block_on_path(identifier): return None @@ -317,10 +317,10 @@ def _resolve_supplementary_data_source( def get_calculation_operator( calculation_type: str, ) -> Callable[[Iterable[IntOrDecimal]], IntOrDecimal]: - invalid_calculation_type = f"Invalid calculation_type: {calculation_type}" + calculation_type_error_message = f"Invalid calculation_type: {calculation_type}" if calculation_type == "sum": return sum - raise NotImplementedError(invalid_calculation_type) + raise NotImplementedError(calculation_type_error_message) def resolve( self, value_source: Mapping diff --git a/app/routes/questionnaire.py b/app/routes/questionnaire.py index 50a6dbc9d7..814bcb30fd 100644 --- a/app/routes/questionnaire.py +++ b/app/routes/questionnaire.py @@ -68,7 +68,7 @@ name="post_submission", import_name=__name__, url_prefix="/submitted/" ) -REPEATED_SUBMISSION_ERROR = "The Questionnaire has been previously submitted" +REPEATED_SUBMISSION_ERROR_MESSAGE = "The Questionnaire has been previously submitted" @questionnaire_blueprint.before_request @@ -78,7 +78,7 @@ def before_questionnaire_request() -> Response | None: return None if cookie_session.get("submitted"): - raise PreviouslySubmittedException(REPEATED_SUBMISSION_ERROR) + raise PreviouslySubmittedException(REPEATED_SUBMISSION_ERROR_MESSAGE) metadata = get_metadata(current_user) if not metadata: diff --git a/app/routes/session.py b/app/routes/session.py index 3a25166a16..f717d90315 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -41,8 +41,8 @@ session_blueprint = Blueprint("session", __name__) -INVALID_RUNNER_CLAIMS_ERROR = "Invalid runner claims" -INVALID_QUESTIONNAIRE_CLAIMS_ERROR = "Invalid questionnaire claims" +RUNNER_CLAIMS_ERROR_MESSAGE = "Invalid runner claims" +QUESTIONNAIRE_CLAIMS_ERROR_MESSAGE = "Invalid questionnaire claims" @session_blueprint.after_request @@ -226,8 +226,10 @@ def _validate_supplementary_data_lists( """ supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: - missing_schema_lists = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" - raise ValidationError(missing_schema_lists) + missing_schema_lists_error_message = ( + f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" + ) + raise ValidationError(missing_schema_lists_error_message) def validate_jti(decrypted_token: dict[str, str | list | int]) -> None: @@ -307,7 +309,7 @@ def get_runner_claims(decrypted_token: Mapping[str, Any]) -> dict: return validate_runner_claims_v2(decrypted_token) except ValidationError as e: - raise InvalidTokenException(INVALID_RUNNER_CLAIMS_ERROR) from e + raise InvalidTokenException(RUNNER_CLAIMS_ERROR_MESSAGE) from e def get_questionnaire_claims( @@ -319,4 +321,4 @@ def get_questionnaire_claims( return validate_questionnaire_claims(claims, schema_metadata, unknown=INCLUDE) except ValidationError as e: - raise InvalidTokenException(INVALID_QUESTIONNAIRE_CLAIMS_ERROR) from e + raise InvalidTokenException(QUESTIONNAIRE_CLAIMS_ERROR_MESSAGE) from e diff --git a/app/secrets.py b/app/secrets.py index 012f6ca5f0..e50228d467 100644 --- a/app/secrets.py +++ b/app/secrets.py @@ -22,8 +22,8 @@ def validate_required_secrets( ) for required_secret in all_required_secrets: if required_secret not in secrets["secrets"]: - missing_secret_error = f"Missing Secret [{required_secret}]" - raise ValueError(missing_secret_error) + missing_secret_error_message = f"Missing Secret [{required_secret}]" + raise ValueError(missing_secret_error_message) class SecretStore: diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index 0a53ec37a6..8cffe1969a 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -31,8 +31,8 @@ class SupplementaryDataRequestFailed(Exception): - SUPPLEMENTARY_DATA_EMPTY_ERROR = "Supplementary data has no data to decrypt" - INVALID_SUPPLEMENTARY_DATA = "Invalid supplementary data" + SUPPLEMENTARY_DATA_EMPTY_ERROR_MESSAGE = "Supplementary data has no data to decrypt" + SUPPLEMENTARY_DATA_ERROR_MESSAGE = "Invalid supplementary data" def __str__(self) -> str: return "Supplementary Data request failed" @@ -124,7 +124,7 @@ def decrypt_supplementary_data( return supplementary_data except InvalidTokenException as e: raise InvalidSupplementaryData from e - raise ValidationError(SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_EMPTY_ERROR) + raise ValidationError(SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_EMPTY_ERROR_MESSAGE) def validate_supplementary_data( @@ -144,5 +144,5 @@ def validate_supplementary_data( ) except ValidationError as e: raise ValidationError( - SupplementaryDataRequestFailed.INVALID_SUPPLEMENTARY_DATA + SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_ERROR_MESSAGE ) from e diff --git a/app/settings.py b/app/settings.py index 426e4d9003..1e559a83ed 100644 --- a/app/settings.py +++ b/app/settings.py @@ -35,18 +35,18 @@ def read_file(file_name): def get_env_or_fail(key): - missing_key_error = f"Setting '{key}' Missing" + missing_key_error_message = f"Setting '{key}' Missing" value = os.getenv(key) if value is None: - raise ValueError(missing_key_error) + raise ValueError(missing_key_error_message) return value def utcoffset_or_fail(date_value, key): - missing_datetime_offset = f"'{key}' datetime offset missing" + datetime_offset_error_message = f"'{key}' datetime offset missing" if date_value.utcoffset() is None: - raise ValueError(missing_datetime_offset) + raise ValueError(datetime_offset_error_message) return date_value diff --git a/app/setup.py b/app/setup.py index 52b4f3c1b9..a9aecaba03 100644 --- a/app/setup.py +++ b/app/setup.py @@ -84,24 +84,21 @@ logger = get_logger() -MISSING_BUCKET_ID_MESSAGE = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" -MISSING_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST Missing" -MISSING_SECONDARY_HOST_MESSAGE = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" -MISSING_SDS_CLIENT_ID = "Setting SDS_OAUTH2_CLIENT_ID Missing" -MISSING_CIR_CLIENT_ID = "Setting CIR_OAUTH2_CLIENT_ID Missing" -MISSING_TOKEN_BACKEND = "Setting OIDC_TOKEN_BACKEND Missing" -MISSING_FEEDBACK_BUCKET_ID = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" -INVALID_SECRET_KEY = "Application secret key does not exist" - - -UNKNOWN_IMPLEMENTED_ERROR = { - "unknown_storage_backend": "Unknown EQ_STORAGE_BACKEND", - "unknown_submission": "Unknown EQ_SUBMISSION_BACKEND", - "unknown_submit_confirmation": "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND", - "unknown_token_backend": "Unknown OIDC_TOKEN_BACKEND", - "unknown_publisher_backend": "Unknown EQ_PUBLISHER_BACKEND", - "unknown_feedback_backend": "Unknown EQ_FEEDBACK_BACKEND", -} +BUCKET_ID_ERROR_MESSAGE = "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" +HOST_ERROR_MESSAGE = "Setting EQ_RABBITMQ_HOST Missing" +SECONDARY_HOST_ERROR_MESSAGE = "Setting EQ_RABBITMQ_HOST_SECONDARY Missing" +SDS_CLIENT_ID_ERROR_MESSAGE = "Setting SDS_OAUTH2_CLIENT_ID Missing" +CIR_CLIENT_ID_ERROR_MESSAGE = "Setting CIR_OAUTH2_CLIENT_ID Missing" +TOKEN_BACKEND_ERROR_MESSAGE = "Setting OIDC_TOKEN_BACKEND Missing" +FEEDBACK_BUCKET_ID_ERROR_MESSAGE = "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" +SECRET_KEY_ERROR_MESSAGE = "Application secret key does not exist" + +STORAGE_BACKEND_ERROR_MESSAGE = "Unknown EQ_STORAGE_BACKEND" +SUBMISSION_ERROR_MESSAGE = "Unknown EQ_SUBMISSION_BACKEND" +SUBMIT_CONFIRMATION_ERROR_MESSAGE = "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND" +TOKEN_BACKEND_UNKNOWN_ERROR_MESSAGE = "Unknown OIDC_TOKEN_BACKEND" +PUBLISHER_BACKEND_ERROR_MESSAGE = "Unknown EQ_PUBLISHER_BACKEND" +FEEDBACK_BACKEND_ERROR_MESSAGE = "Unknown EQ_FEEDBACK_BACKEND" class MissingEnvironmentVariable(Exception): @@ -312,7 +309,7 @@ def setup_storage(application): elif application.config["EQ_STORAGE_BACKEND"] == "dynamodb": setup_dynamodb(application) else: - raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_storage_backend"]) + raise NotImplementedError(STORAGE_BACKEND_ERROR_MESSAGE) setup_redis(application) @@ -350,7 +347,7 @@ def setup_redis(application): def setup_submitter(application): if application.config["EQ_SUBMISSION_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_SUBMISSION_BUCKET_ID")): - raise MissingEnvironmentVariable(MISSING_BUCKET_ID_MESSAGE) + raise MissingEnvironmentVariable(BUCKET_ID_ERROR_MESSAGE) application.eq["submitter"] = GCSSubmitter(bucket_name=bucket_name) elif application.config["EQ_SUBMISSION_BACKEND"] == "rabbitmq": @@ -358,9 +355,9 @@ def setup_submitter(application): secondary_host = application.config.get("EQ_RABBITMQ_HOST_SECONDARY") if not host: - raise MissingEnvironmentVariable(MISSING_HOST_MESSAGE) + raise MissingEnvironmentVariable(HOST_ERROR_MESSAGE) if not secondary_host: - raise MissingEnvironmentVariable(MISSING_SECONDARY_HOST_MESSAGE) + raise MissingEnvironmentVariable(SECONDARY_HOST_ERROR_MESSAGE) application.eq["submitter"] = RabbitMQSubmitter( host=host, @@ -379,7 +376,7 @@ def setup_submitter(application): application.eq["submitter"] = LogSubmitter() else: - raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_submission"]) + raise NotImplementedError(SUBMISSION_ERROR_MESSAGE) def setup_task_client(application): @@ -389,20 +386,20 @@ def setup_task_client(application): application.eq["cloud_tasks"] = LogCloudTaskPublisher() else: raise NotImplementedError( - UNKNOWN_IMPLEMENTED_ERROR["unknown_submit_confirmation"] + SUBMIT_CONFIRMATION_ERROR_MESSAGE ) def setup_oidc(application): def client_ids_exist(): if not application.config.get("SDS_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable(MISSING_SDS_CLIENT_ID) + raise MissingEnvironmentVariable(SDS_CLIENT_ID_ERROR_MESSAGE) if not application.config.get("CIR_OAUTH2_CLIENT_ID"): - raise MissingEnvironmentVariable(MISSING_CIR_CLIENT_ID) + raise MissingEnvironmentVariable(CIR_CLIENT_ID_ERROR_MESSAGE) if not (oidc_token_backend := application.config.get("OIDC_TOKEN_BACKEND")): - raise MissingEnvironmentVariable(MISSING_TOKEN_BACKEND) + raise MissingEnvironmentVariable(TOKEN_BACKEND_ERROR_MESSAGE) if oidc_token_backend == "gcp": client_ids_exist() @@ -412,7 +409,7 @@ def client_ids_exist(): application.eq["oidc_credentials_service"] = OIDCCredentialsServiceLocal() else: - raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_token_backend"]) + raise NotImplementedError(TOKEN_BACKEND_UNKNOWN_ERROR_MESSAGE) def setup_publisher(application): @@ -424,14 +421,14 @@ def setup_publisher(application): else: raise NotImplementedError( - UNKNOWN_IMPLEMENTED_ERROR["unknown_publisher_backend"] + PUBLISHER_BACKEND_ERROR_MESSAGE ) def setup_feedback(application): if application.config["EQ_FEEDBACK_BACKEND"] == "gcs": if not (bucket_name := application.config.get("EQ_GCS_FEEDBACK_BUCKET_ID")): - raise MissingEnvironmentVariable(MISSING_FEEDBACK_BUCKET_ID) + raise MissingEnvironmentVariable(FEEDBACK_BUCKET_ID_ERROR_MESSAGE) application.eq["feedback_submitter"] = GCSFeedbackSubmitter( bucket_name=bucket_name @@ -440,7 +437,7 @@ def setup_feedback(application): elif application.config["EQ_FEEDBACK_BACKEND"] == "log": application.eq["feedback_submitter"] = LogFeedbackSubmitter() else: - raise NotImplementedError(UNKNOWN_IMPLEMENTED_ERROR["unknown_feedback_backend"]) + raise NotImplementedError(FEEDBACK_BACKEND_ERROR_MESSAGE) def add_blueprints(application): @@ -478,7 +475,7 @@ def add_blueprints(application): def setup_secure_cookies(application): secret_key = application.eq["secret_store"].get_secret_by_name("EQ_SECRET_KEY") if not secret_key: - raise ValueError(INVALID_SECRET_KEY) + raise ValueError(SECRET_KEY_ERROR_MESSAGE) application.secret_key = secret_key application.session_interface = SHA256SecureCookieSessionInterface() diff --git a/app/storage/datastore.py b/app/storage/datastore.py index 3fcf1ae982..d32d9798ef 100644 --- a/app/storage/datastore.py +++ b/app/storage/datastore.py @@ -9,7 +9,7 @@ class Datastore(StorageHandler): - UNSUPPORTED_UNIQUE_KEY = "Unique key checking not supported" + UNIQUE_KEY_ERROR_MESSAGE = "Unique key checking not supported" def __init__(self, client: datastore.Client) -> None: super().__init__(client) @@ -17,7 +17,7 @@ def __init__(self, client: datastore.Client) -> None: @Retry() def put(self, model: ModelTypes, overwrite: bool = True) -> bool: if not overwrite: - raise NotImplementedError(self.UNSUPPORTED_UNIQUE_KEY) + raise NotImplementedError(self.UNIQUE_KEY_ERROR_MESSAGE) storage_model = StorageModel(model_type=type(model)) serialized_item = storage_model.serialize(model) diff --git a/app/storage/storage.py b/app/storage/storage.py index 3290d48031..9b88715d19 100644 --- a/app/storage/storage.py +++ b/app/storage/storage.py @@ -29,7 +29,7 @@ class TableConfig(TypedDict, total=False): class StorageModel: - INVALID_MODEL_TYPE = "Invalid model_type provided" + MODEL_TYPE_ERROR_MESSAGE = "Invalid model_type provided" TABLE_CONFIG_BY_TYPE: dict[type[ModelTypes], TableConfig] = { app_models.QuestionnaireState: { "key_field": "user_id", @@ -56,7 +56,7 @@ def __init__(self, model_type: type[ModelTypes]) -> None: self._model_type = model_type if self._model_type not in self.TABLE_CONFIG_BY_TYPE: - raise KeyError(self.INVALID_MODEL_TYPE) + raise KeyError(self.MODEL_TYPE_ERROR_MESSAGE) self._config = self.TABLE_CONFIG_BY_TYPE[self._model_type] self._schema = self._config["schema"]() diff --git a/app/storage/storage_encryption.py b/app/storage/storage_encryption.py index 335ca38e03..73f23c533b 100644 --- a/app/storage/storage_encryption.py +++ b/app/storage/storage_encryption.py @@ -11,19 +11,19 @@ class StorageEncryption: - MISSING_USER_ID = "user_id not provided" - MISSING_USER_IK = "user_ik not provided" - MISSING_PEPPER = "pepper not provided" + USER_ID_ERROR_MESSAGE = "user_id not provided" + USER_IK_ERROR_MESSAGE = "user_ik not provided" + PEPPER_ERROR_MESSAGE = "pepper not provided" def __init__( self, user_id: str | None, user_ik: str | None, pepper: str | None ) -> None: if not user_id: - raise ValueError(self.MISSING_USER_ID) + raise ValueError(self.USER_ID_ERROR_MESSAGE) if not user_ik: - raise ValueError(self.MISSING_USER_IK) + raise ValueError(self.USER_IK_ERROR_MESSAGE) if not pepper: - raise ValueError(self.MISSING_PEPPER) + raise ValueError(self.PEPPER_ERROR_MESSAGE) self.key = self._generate_key(user_id, user_ik, pepper) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index fb1c2d6ea0..bf34684118 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -63,14 +63,16 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument ] if missing_receipting_keys: - invalid_receipting_keys = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" - raise ValidationError(invalid_receipting_keys) + receipting_keys_error_message = ( + f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" + ) + raise ValidationError(receipting_keys_error_message) class RunnerMetadataSchema(Schema, StripWhitespaceMixin): """Metadata which is required for the operation of runner itself""" - MISSING_METADATA_OPTION = ( + METADATA_OPTION_ERROR_MESSAGE = ( "Neither schema_name, schema_url or cir_instrument_id has been set in metadata" ) @@ -109,13 +111,13 @@ def validate_schema_options( # pylint: disable=no-self-use, unused-argument if data.get(option) ] if len(options) == 0: - raise ValidationError(self.MISSING_METADATA_OPTION) + raise ValidationError(self.METADATA_OPTION_ERROR_MESSAGE) if len(options) > 1: - invalid_metadata_combination = ( + metadata_combination_error_message = ( "Only one of schema_name, schema_url or cir_instrument_id should be specified " f"in metadata, but {', '.join(options)} were provided" ) - raise ValidationError(invalid_metadata_combination) + raise ValidationError(metadata_combination_error_message) def validate_questionnaire_claims( diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index da6975e8df..e4dd99fdf3 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -15,7 +15,7 @@ class ItemsSchema(Schema): identifier = fields.Field(required=True) - ITEM_IDENTIFIER_ERROR = ( + ITEM_IDENTIFIER_ERROR_MESSAGE = ( "Item identifier must be a non-empty string or non-negative integer" ) @@ -26,7 +26,7 @@ def validate_identifier( # pylint: disable=no-self-use if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): - raise ValidationError(self.ITEM_IDENTIFIER_ERROR) + raise ValidationError(self.ITEM_IDENTIFIER_ERROR_MESSAGE) class ItemsData(Schema, StripWhitespaceMixin): @@ -34,7 +34,7 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): - SDS_IDENTIFIER_ERROR = "Supplementary data did not return the specified Identifier" + SDS_IDENTIFIER_ERROR_MESSAGE = "Supplementary data did not return the specified Identifier" identifier = VALIDATORS["string"](validate=validate.Length(min=1)) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @@ -44,13 +44,15 @@ def validate_identifier( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data and data["identifier"] != self.context["identifier"]: - raise ValidationError(self.SDS_IDENTIFIER_ERROR) + raise ValidationError(self.SDS_IDENTIFIER_ERROR_MESSAGE) class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): - DATASET_ID_ERROR = "Supplementary data did not return the specified Dataset ID" - SURVEY_ID_ERROR = "Supplementary data did not return the specified Survey ID" - MISMATCH_SDS_VERSION = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" + + DATASET_ID_ERROR_MESSAGE = "Supplementary data did not return the specified Dataset ID" + SURVEY_ID_ERROR_MESSAGE = "Supplementary data did not return the specified Survey ID" + SDS_VERSION_ERROR_MESSAGE = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" + dataset_id = VALIDATORS["uuid"]() survey_id = VALIDATORS["string"](validate=validate.Length(min=1)) data = fields.Nested( @@ -66,15 +68,15 @@ def validate_payload( # pylint: disable=unused-argument ) -> None: if payload: if payload["dataset_id"] != self.context["dataset_id"]: - raise ValidationError(self.DATASET_ID_ERROR) + raise ValidationError(self.DATASET_ID_ERROR_MESSAGE) if payload["survey_id"] != self.context["survey_id"]: - raise ValidationError(self.SURVEY_ID_ERROR) + raise ValidationError(self.SURVEY_ID_ERROR_MESSAGE) if self.context["sds_schema_version"] and ( payload["data"]["schema_version"] != self.context["sds_schema_version"] ): - raise ValidationError(self.MISMATCH_SDS_VERSION) + raise ValidationError(self.SDS_VERSION_ERROR_MESSAGE) def validate_supplementary_data_v1( diff --git a/app/views/handlers/block.py b/app/views/handlers/block.py index 7a4b79b8f7..c2104ec0b9 100644 --- a/app/views/handlers/block.py +++ b/app/views/handlers/block.py @@ -51,9 +51,9 @@ def __init__( ) self.resume = "resume" in request_args - invalid_location = f"location {self._current_location} is not valid" + location_error_message = f"location {self._current_location} is not valid" if not self.is_location_valid(): - raise InvalidLocationException(invalid_location) + raise InvalidLocationException(location_error_message) @property def current_location(self) -> LocationType: diff --git a/app/views/handlers/individual_response.py b/app/views/handlers/individual_response.py index 81de0a5950..e34bc0e7f2 100644 --- a/app/views/handlers/individual_response.py +++ b/app/views/handlers/individual_response.py @@ -43,7 +43,7 @@ class IndividualResponsePostalDeadlinePast(Exception): class IndividualResponseHandler: - RESPONSE_LIMIT_MESSAGE = "Individual response limit has been reached" + RESPONSE_LIMIT_ERROR_MESSAGE = "Individual response limit has been reached" @staticmethod def _person_name_transforms(list_name: str) -> list[Mapping]: @@ -232,7 +232,7 @@ def _check_individual_response_count(self) -> None: ) >= current_app.config["EQ_INDIVIDUAL_RESPONSE_LIMIT"] ): - raise IndividualResponseLimitReached(self.RESPONSE_LIMIT_MESSAGE) + raise IndividualResponseLimitReached(self.RESPONSE_LIMIT_ERROR_MESSAGE) def _update_individual_response_count(self) -> None: response_metadata = self._questionnaire_store.data_stores.response_metadata diff --git a/app/views/handlers/section.py b/app/views/handlers/section.py index dad60fef5a..779626fe9f 100644 --- a/app/views/handlers/section.py +++ b/app/views/handlers/section.py @@ -26,8 +26,8 @@ def __init__( data_stores=self._questionnaire_store.data_stores, ) if not self._is_valid_location(): - invalid_loaction = f"location {self._section_id} is not valid" - raise InvalidLocationException(invalid_loaction) + location_error_message = f"location {self._section_id} is not valid" + raise InvalidLocationException(location_error_message) self.current_location = Location( section_id=self._section_id, diff --git a/app/views/handlers/submit_questionnaire.py b/app/views/handlers/submit_questionnaire.py index 6e13a43bfe..c7227ad3cd 100644 --- a/app/views/handlers/submit_questionnaire.py +++ b/app/views/handlers/submit_questionnaire.py @@ -11,7 +11,7 @@ class SubmitQuestionnaireHandler: - SUBMIT_PAGE_MESSAGE = "Submit page not enabled" + SUBMIT_PAGE_DISABLED_ERROR_MESSAGE = "Submit page not enabled" def __init__( self, @@ -20,7 +20,7 @@ def __init__( language: str, ): if not schema.is_flow_linear: - raise InvalidLocationException(self.SUBMIT_PAGE_MESSAGE) + raise InvalidLocationException(self.SUBMIT_PAGE_DISABLED_ERROR_MESSAGE) self._schema = schema self._questionnaire_store = questionnaire_store From dc1e88eb17de1e65ba30d48f286955aaf65026fb Mon Sep 17 00:00:00 2001 From: Farhanam76 Date: Fri, 23 May 2025 10:09:34 +0100 Subject: [PATCH 556/567] formatted the files --- app/forms/validators.py | 8 ++++---- app/questionnaire/placeholder_transforms.py | 4 +++- app/questionnaire/rules/operations.py | 4 ++-- app/routes/session.py | 4 +--- app/services/supplementary_data.py | 4 +++- app/setup.py | 8 ++------ app/utilities/metadata_parser_v2.py | 4 +--- app/utilities/supplementary_data_parser.py | 12 +++++++++--- 8 files changed, 25 insertions(+), 23 deletions(-) diff --git a/app/forms/validators.py b/app/forms/validators.py index 4a5743d173..041d221966 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -408,9 +408,7 @@ def _build_range_length_error(period_object: PeriodType) -> str: class SumCheck: - MULTIPLE_CONDITION_ERROR_MESSAGE = ( - "There are multiple conditions, but equals is not one of them. We only support <= and >=" - ) + MULTIPLE_CONDITION_ERROR_MESSAGE = "There are multiple conditions, but equals is not one of them. We only support <= and >=" def __init__(self, messages: OptionalMessage = None, currency: str | None = None): self.messages = {**error_messages, **(messages or {})} @@ -470,7 +468,9 @@ def _is_valid( if condition == "less than or equals": return total <= target_total, "TOTAL_SUM_NOT_LESS_THAN_OR_EQUALS" - unimplemented_condition_error_message = f"Condition '{condition}' is not implemented" + unimplemented_condition_error_message = ( + f"Condition '{condition}' is not implemented" + ) raise NotImplementedError(unimplemented_condition_error_message) diff --git a/app/questionnaire/placeholder_transforms.py b/app/questionnaire/placeholder_transforms.py index 408ee8ac35..500626aba6 100644 --- a/app/questionnaire/placeholder_transforms.py +++ b/app/questionnaire/placeholder_transforms.py @@ -326,7 +326,9 @@ def get_ordinal_indicator(self, number_to_format: int) -> str: 19: "eg", }.get(number_to_format, "fed") - language_code_error_message = f"Language code '{self.language}' not implemented." + language_code_error_message = ( + f"Language code '{self.language}' not implemented." + ) raise NotImplementedError(language_code_error_message) def first_non_empty_item(self, items: Sequence[str]) -> str: diff --git a/app/questionnaire/rules/operations.py b/app/questionnaire/rules/operations.py index f0974bccb1..a821001dd0 100644 --- a/app/questionnaire/rules/operations.py +++ b/app/questionnaire/rules/operations.py @@ -55,7 +55,7 @@ class Operations: A class to group the operations """ - NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" + NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE = "Negative days offset must be less than or equal to -7 when used with `day_of_week` offset" def __init__( self, @@ -148,7 +148,7 @@ def resolve_date_from_string( if day_of_week_offset := offset.get("day_of_week"): if 0 > days_offset > -7: - raise ValueError(Operations.NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE ) + raise ValueError(Operations.NEGATIVE_DAYS_OFFSET_ERROR_MESSAGE) days_difference = ( value_as_date.weekday() - DAYS_OF_WEEK[day_of_week_offset] diff --git a/app/routes/session.py b/app/routes/session.py index f717d90315..a5bc710c15 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -226,9 +226,7 @@ def _validate_supplementary_data_lists( """ supplementary_lists = supplementary_data.get("items", {}).keys() if missing := schema.supplementary_lists - supplementary_lists: - missing_schema_lists_error_message = ( - f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" - ) + missing_schema_lists_error_message = f"Supplementary data does not include the following lists required for the schema: {', '.join(missing)}" raise ValidationError(missing_schema_lists_error_message) diff --git a/app/services/supplementary_data.py b/app/services/supplementary_data.py index 8cffe1969a..59c03d9a45 100644 --- a/app/services/supplementary_data.py +++ b/app/services/supplementary_data.py @@ -124,7 +124,9 @@ def decrypt_supplementary_data( return supplementary_data except InvalidTokenException as e: raise InvalidSupplementaryData from e - raise ValidationError(SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_EMPTY_ERROR_MESSAGE) + raise ValidationError( + SupplementaryDataRequestFailed.SUPPLEMENTARY_DATA_EMPTY_ERROR_MESSAGE + ) def validate_supplementary_data( diff --git a/app/setup.py b/app/setup.py index a9aecaba03..73cfe54c67 100644 --- a/app/setup.py +++ b/app/setup.py @@ -385,9 +385,7 @@ def setup_task_client(application): elif application.config["EQ_SUBMISSION_CONFIRMATION_BACKEND"] == "log": application.eq["cloud_tasks"] = LogCloudTaskPublisher() else: - raise NotImplementedError( - SUBMIT_CONFIRMATION_ERROR_MESSAGE - ) + raise NotImplementedError(SUBMIT_CONFIRMATION_ERROR_MESSAGE) def setup_oidc(application): @@ -420,9 +418,7 @@ def setup_publisher(application): application.eq["publisher"] = LogPublisher() else: - raise NotImplementedError( - PUBLISHER_BACKEND_ERROR_MESSAGE - ) + raise NotImplementedError(PUBLISHER_BACKEND_ERROR_MESSAGE) def setup_feedback(application): diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index bf34684118..4598933cb8 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -63,9 +63,7 @@ def validate_receipting_keys( # pylint: disable=no-self-use, unused-argument ] if missing_receipting_keys: - receipting_keys_error_message = ( - f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" - ) + receipting_keys_error_message = f"Receipting keys: {missing_receipting_keys} not set in Survey Metadata" raise ValidationError(receipting_keys_error_message) diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index e4dd99fdf3..72a316eb1d 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -34,7 +34,9 @@ class ItemsData(Schema, StripWhitespaceMixin): class SupplementaryData(Schema, StripWhitespaceMixin): - SDS_IDENTIFIER_ERROR_MESSAGE = "Supplementary data did not return the specified Identifier" + SDS_IDENTIFIER_ERROR_MESSAGE = ( + "Supplementary data did not return the specified Identifier" + ) identifier = VALIDATORS["string"](validate=validate.Length(min=1)) items = fields.Nested(ItemsData, required=False, unknown=INCLUDE) @@ -49,8 +51,12 @@ def validate_identifier( # pylint: disable=unused-argument class SupplementaryDataMetadataSchema(Schema, StripWhitespaceMixin): - DATASET_ID_ERROR_MESSAGE = "Supplementary data did not return the specified Dataset ID" - SURVEY_ID_ERROR_MESSAGE = "Supplementary data did not return the specified Survey ID" + DATASET_ID_ERROR_MESSAGE = ( + "Supplementary data did not return the specified Dataset ID" + ) + SURVEY_ID_ERROR_MESSAGE = ( + "Supplementary data did not return the specified Survey ID" + ) SDS_VERSION_ERROR_MESSAGE = "The Supplementary Dataset Schema Version does not match the version set in the Questionnaire Schema" dataset_id = VALIDATORS["uuid"]() From 3fb8cd1a2b7ac375abda80615428587bb0d21d3b Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Wed, 28 May 2025 14:09:12 +0100 Subject: [PATCH 557/567] Schemas v5.35.0 (#1668) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index c92d06d632..ff506ef80c 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.34.0 +v5.35.0 From f819a2094b6b2a4a7745447af0eeb53eddf30952 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:25:53 +0100 Subject: [PATCH 558/567] Schemas 5.36.0 (#1672) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index ff506ef80c..0eb8e7502e 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.35.0 +v5.36.0 From bd7893354befec55333949346974b7f6c2596b53 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 12 Jun 2025 15:28:24 +0100 Subject: [PATCH 559/567] Schema Release v5.37.0 (#1673) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 0eb8e7502e..a2d0712a90 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.36.0 +v5.37.0 From 2781ef4f0eaf6081b5602b3d472591f5ac23efe4 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:28:49 +0100 Subject: [PATCH 560/567] Remove Push to Docker Hub Step in Github Actions (#1680) * Remove Push to Docker Hub Step in Github Actions * remove docker hub tag path * Remove Folded Block Scalar in Build step: * exlcude hub and spoke and supplementary datag * remove hub and spoke and supplementary data suites from GA matrix * remove specs from exclude config in WDIO * Remove DockerHub Build Step --- .github/workflows/main.yml | 22 ---------------------- .github/workflows/pull_request.yml | 12 ++---------- 2 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 7158f7bc44..0000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Main - -on: - push: - branches: - - main - -jobs: - docker-push: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Write app version - run: printf "$GITHUB_SHA" > .application-version - - name: Build - run: > - docker build -t onsdigital/eq-questionnaire-runner:latest . - - name: Push to Docker Hub - run: | - echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin - echo "Pushing with tag [latest]" - docker push onsdigital/eq-questionnaire-runner:latest diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 17d0da37de..8f0ebad55d 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -134,7 +134,7 @@ jobs: needs: [python-dependencies, node-dependencies] strategy: matrix: - suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, summaries, general, journeys, components, list_collector, hub_and_spoke, supplementary_data] + suite: [ timeout_modal_expired, timeout_modal_extended, timeout_modal_extended_new_window, features, summaries, general, journeys, components, list_collector] runs-on: ubuntu-22.04 timeout-minutes: 30 env: @@ -190,15 +190,7 @@ jobs: echo "Writing SHA $SHA to .application_version" printf $SHA > .application-version - name: Build - run: > - docker build -t onsdigital/eq-questionnaire-runner:$TAG - -t ${{ secrets.GAR_LOCATION }}/${{ secrets.GAR_PROJECT_ID }}/docker-images/eq-questionnaire-runner:$TAG . - - name: Push to Docker Hub - run: | - echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin - echo "Pushing to DockerHub with tag $TAG" - docker push onsdigital/eq-questionnaire-runner:$TAG - + run: docker build -t ${{ secrets.GAR_LOCATION }}/${{ secrets.GAR_PROJECT_ID }}/docker-images/eq-questionnaire-runner:$TAG . - name: Push to GAR run: | gcloud auth configure-docker ${{ secrets.GAR_LOCATION }} From 39f232a9c3af5f92cf683b20a16545e26375c6be Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:24:55 +0100 Subject: [PATCH 561/567] Schemas Release v5.38.0 (#1679) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index a2d0712a90..997c9c0b2d 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.37.0 +v5.38.0 From 83ed25bdbba3b3fec4d0681ce651242ca26dbe89 Mon Sep 17 00:00:00 2001 From: liamtoozer Date: Fri, 27 Jun 2025 15:09:17 +0100 Subject: [PATCH 562/567] Bump dev dependencies and fix linting (#1678) --- app/forms/validators.py | 4 +- app/helpers/schema_helpers.py | 2 +- app/jinja_filters.py | 6 +- app/utilities/mappings.py | 2 +- app/utilities/metadata_parser_v2.py | 2 +- app/utilities/supplementary_data_parser.py | 4 +- poetry.lock | 359 ++++++++++++--------- pyproject.toml | 8 +- templates/sectionsummary.html | 3 +- 9 files changed, 226 insertions(+), 164 deletions(-) diff --git a/app/forms/validators.py b/app/forms/validators.py index 041d221966..51ca7c4d80 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -377,7 +377,9 @@ def _return_relative_delta(period_object: PeriodType) -> relativedelta: def _is_first_relative_delta_largest( relativedelta1: relativedelta, relativedelta2: relativedelta ) -> bool: - epoch = datetime.min # generic epoch for comparison purposes only + epoch = datetime.min.replace( + tzinfo=timezone.utc + ) # generic epoch for comparison purposes only date1 = epoch + relativedelta1 date2 = epoch + relativedelta2 return date1 > date2 diff --git a/app/helpers/schema_helpers.py b/app/helpers/schema_helpers.py index 0f995b808f..2da547e55e 100644 --- a/app/helpers/schema_helpers.py +++ b/app/helpers/schema_helpers.py @@ -13,7 +13,7 @@ def with_schema( - function: Callable[Concatenate[QuestionnaireSchema, P], T] + function: Callable[Concatenate[QuestionnaireSchema, P], T], ) -> Callable[P, T]: """Adds the survey schema as the first argument to the function being wrapped. Use on flask request handlers or methods called by flask request handlers. diff --git a/app/jinja_filters.py b/app/jinja_filters.py index 9c8754a8ee..7ccc6e4636 100644 --- a/app/jinja_filters.py +++ b/app/jinja_filters.py @@ -32,11 +32,11 @@ def mark_safe(context: nodes.EvalContext, value: str) -> Markup | str: - return Markup(value) if context.autoescape else value + return Markup(value) if context.autoescape else value # noqa: S704 def strip_tags(value: str) -> Markup: - return escape(Markup(value).striptags()) + return escape(Markup(value).striptags()) # noqa: S704 @blueprint.app_template_filter() @@ -465,7 +465,7 @@ def __init__(self, text: str, other: str | None = None) -> None: class SummaryRowItem: - def __init__( # noqa: C901, R0912 pylint: disable=too-complex, too-many-branches + def __init__( # noqa: C901 pylint: disable=too-complex, too-many-branches self, question: SelectFieldBase._Option, answer: SelectFieldBase._Option, diff --git a/app/utilities/mappings.py b/app/utilities/mappings.py index efbe0b1aee..4a7caa58ad 100644 --- a/app/utilities/mappings.py +++ b/app/utilities/mappings.py @@ -6,7 +6,7 @@ def get_flattened_mapping_values( - map_to_flatten: Mapping[SectionKey, Iterable[str]] | Mapping[str, Iterable[str]] + map_to_flatten: Mapping[SectionKey, Iterable[str]] | Mapping[str, Iterable[str]], ) -> OrderedSet[str]: return OrderedSet([x for v in map_to_flatten.values() for x in v]) diff --git a/app/utilities/metadata_parser_v2.py b/app/utilities/metadata_parser_v2.py index 4598933cb8..431c72bc19 100644 --- a/app/utilities/metadata_parser_v2.py +++ b/app/utilities/metadata_parser_v2.py @@ -99,7 +99,7 @@ class RunnerMetadataSchema(Schema, StripWhitespaceMixin): survey_metadata = fields.Nested(SurveyMetadata, required=False) @validates_schema - def validate_schema_options( # pylint: disable=no-self-use, unused-argument + def validate_schema_options( # pylint: disable=unused-argument self, data: Mapping, **kwargs: Any ) -> None: if data: diff --git a/app/utilities/supplementary_data_parser.py b/app/utilities/supplementary_data_parser.py index 72a316eb1d..96c8b140df 100644 --- a/app/utilities/supplementary_data_parser.py +++ b/app/utilities/supplementary_data_parser.py @@ -20,9 +20,7 @@ class ItemsSchema(Schema): ) @validates("identifier") - def validate_identifier( # pylint: disable=no-self-use - self, identifier: fields.Field - ) -> None: + def validate_identifier(self, identifier: fields.Field) -> None: if not (isinstance(identifier, str) and identifier.strip()) and not ( isinstance(identifier, int) and identifier >= 0 ): diff --git a/poetry.lock b/poetry.lock index 205b5c2947..e138b3cdb5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "astroid" @@ -49,14 +49,14 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" -version = "4.13.3" +version = "4.13.4" description = "Screen-scraping library" optional = false python-versions = ">=3.7.0" groups = ["dev"] files = [ - {file = "beautifulsoup4-4.13.3-py3-none-any.whl", hash = "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16"}, - {file = "beautifulsoup4-4.13.3.tar.gz", hash = "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b"}, + {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, ] [package.dependencies] @@ -72,34 +72,34 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.10.0" +version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, - {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, - {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, - {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, - {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, - {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, - {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, - {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, - {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, - {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, - {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, - {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, - {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, - {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, - {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, - {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, - {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, - {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, - {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, - {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, - {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, - {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, + {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, + {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, + {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, + {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, + {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, + {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, + {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, + {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, + {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, + {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, + {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, + {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, + {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, + {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, + {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, + {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, + {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, + {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, + {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, + {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, + {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, + {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, ] [package.dependencies] @@ -186,6 +186,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -198,8 +202,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -210,8 +220,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -221,6 +247,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -232,6 +262,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -244,6 +278,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -256,6 +294,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -951,14 +993,14 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "fakeredis" -version = "2.28.0" +version = "2.29.0" description = "Python implementation of redis API, can be used for testing purposes." optional = false python-versions = "<4.0,>=3.7" groups = ["dev"] files = [ - {file = "fakeredis-2.28.0-py3-none-any.whl", hash = "sha256:e19b2a72cb0d13edaae95637748cef569840866d0807d93f4d381571034d1388"}, - {file = "fakeredis-2.28.0.tar.gz", hash = "sha256:4768a78240ca03af79f8922f0b6774efb52c6bf2d27d38a622da9a2a7e8abc8d"}, + {file = "fakeredis-2.29.0-py3-none-any.whl", hash = "sha256:f644c0a69dc088455d75a9b259d101e28a1c5659381aa6d9ee6c2b31eb5a909f"}, + {file = "fakeredis-2.29.0.tar.gz", hash = "sha256:159cebf2c53e2c2bd7d18220fa93aa5f1d7152f6b6dd7896c46234d674342398"}, ] [package.dependencies] @@ -1084,14 +1126,14 @@ email = ["email-validator"] [[package]] name = "freezegun" -version = "1.5.1" +version = "1.5.2" description = "Let your Python tests travel through time" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"}, - {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"}, + {file = "freezegun-1.5.2-py3-none-any.whl", hash = "sha256:5aaf3ba229cda57afab5bd311f0108d86b6fb119ae89d2cd9c43ec8c1733c85b"}, + {file = "freezegun-1.5.2.tar.gz", hash = "sha256:a54ae1d2f9c02dbf42e02c18a3ab95ab4295818b549a34dac55592d72a905181"}, ] [package.dependencies] @@ -1276,7 +1318,7 @@ opentelemetry-api = {version = ">=1.27.0", markers = "python_version >= \"3.8\"" opentelemetry-sdk = {version = ">=1.27.0", markers = "python_version >= \"3.8\""} proto-plus = [ {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.2,<2.0.0", markers = "python_version >= \"3.11\" and python_version < \"3.13\""}, + {version = ">=1.22.2,<2.0.0", markers = "python_version >= \"3.11\""}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" @@ -1325,7 +1367,7 @@ google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0" grpc-google-iam-v1 = ">=0.14.0,<1.0.0" proto-plus = [ {version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""}, - {version = ">=1.22.3,<2.0.0"}, + {version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""}, ] protobuf = ">=3.20.2,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" @@ -1832,14 +1874,14 @@ files = [ [[package]] name = "jsonschema" -version = "4.23.0" +version = "4.24.0" description = "An implementation of JSON Schema validation for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, - {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, + {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, + {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, ] [package.dependencies] @@ -2005,14 +2047,14 @@ test = ["pytest", "pytest-cov"] [[package]] name = "moto" -version = "5.1.2" +version = "5.1.6" description = "A library that allows you to easily mock out tests based on AWS infrastructure" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "moto-5.1.2-py3-none-any.whl", hash = "sha256:3789084bb20052b6eb846fe6f4831ce6dfe8a3b197c8f63789b40281b5e1731d"}, - {file = "moto-5.1.2.tar.gz", hash = "sha256:0e4c650d31eacfbe726c37e956efa04d36948e23f7d3228a7c3746aa839e66c2"}, + {file = "moto-5.1.6-py3-none-any.whl", hash = "sha256:e4a3092bc8fe9139caa77cd34cdcbad804de4d9671e2270ea3b4d53f5c645047"}, + {file = "moto-5.1.6.tar.gz", hash = "sha256:baf7afa9d4a92f07277b29cf466d0738f25db2ed2ee12afcb1dc3f2c540beebd"}, ] [package.dependencies] @@ -2051,48 +2093,49 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "mypy" -version = "1.15.0" +version = "1.16.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, + {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"}, + {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"}, + {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"}, + {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"}, + {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"}, + {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"}, + {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"}, + {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"}, + {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"}, + {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"}, + {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"}, + {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"}, + {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"}, + {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"}, + {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"}, + {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"}, + {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"}, + {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"}, + {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"}, + {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"}, + {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"}, + {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"}, + {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"}, + {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"}, + {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"}, + {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"}, + {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"}, + {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"}, + {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"}, + {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"}, + {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"}, + {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"}, ] [package.dependencies] mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" typing_extensions = ">=4.6.0" [package.extras] @@ -2263,24 +2306,25 @@ type = ["mypy (>=1.14.1)"] [[package]] name = "playwright" -version = "1.51.0" +version = "1.52.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "playwright-1.51.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bcaaa3d5d73bda659bfb9ff2a288b51e85a91bd89eda86eaf8186550973e416a"}, - {file = "playwright-1.51.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e0ae6eb44297b24738e1a6d9c580ca4243b4e21b7e65cf936a71492c08dd0d4"}, - {file = "playwright-1.51.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:ab4c0ff00bded52c946be60734868febc964c8a08a9b448d7c20cb3811c6521c"}, - {file = "playwright-1.51.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:d5c9f67bc6ef49094618991c78a1466c5bac5ed09157660d78b8510b77f92746"}, - {file = "playwright-1.51.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:814e4ec2a1a0d6f6221f075622c06b31ceb2bdc6d622258cfefed900c01569ae"}, - {file = "playwright-1.51.0-py3-none-win32.whl", hash = "sha256:4cef804991867ea27f608b70fa288ee52a57651e22d02ab287f98f8620b9408c"}, - {file = "playwright-1.51.0-py3-none-win_amd64.whl", hash = "sha256:9ece9316c5d383aed1a207f079fc2d552fff92184f0ecf37cc596e912d00a8c3"}, + {file = "playwright-1.52.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:19b2cb9d4794062008a635a99bd135b03ebb782d460f96534a91cb583f549512"}, + {file = "playwright-1.52.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0797c0479cbdc99607412a3c486a3a2ec9ddc77ac461259fd2878c975bcbb94a"}, + {file = "playwright-1.52.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:7223960b7dd7ddeec1ba378c302d1d09733b8dac438f492e9854c85d3ca7144f"}, + {file = "playwright-1.52.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:d010124d24a321e0489a8c0d38a3971a7ca7656becea7656c9376bfea7f916d4"}, + {file = "playwright-1.52.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4173e453c43180acc60fd77ffe1ebee8d0efbfd9986c03267007b9c3845415af"}, + {file = "playwright-1.52.0-py3-none-win32.whl", hash = "sha256:cd0bdf92df99db6237a99f828e80a6a50db6180ef8d5352fc9495df2c92f9971"}, + {file = "playwright-1.52.0-py3-none-win_amd64.whl", hash = "sha256:dcbf75101eba3066b7521c6519de58721ea44379eb17a0dafa94f9f1b17f59e4"}, + {file = "playwright-1.52.0-py3-none-win_arm64.whl", hash = "sha256:9d0085b8de513de5fb50669f8e6677f0252ef95a9a1d2d23ccee9638e71e65cb"}, ] [package.dependencies] greenlet = ">=3.1.1,<4.0.0" -pyee = ">=12,<13" +pyee = ">=13,<14" [[package]] name = "pluggy" @@ -2379,32 +2423,47 @@ markers = {dev = "platform_python_implementation != \"PyPy\""} [[package]] name = "pyee" -version = "12.1.1" +version = "13.0.0" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef"}, - {file = "pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3"}, + {file = "pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498"}, + {file = "pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37"}, ] [package.dependencies] typing-extensions = "*" [package.extras] -dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio ; python_version >= \"3.4\"", "pytest-trio ; python_version >= \"3.7\"", "sphinx", "toml", "tox", "trio", "trio ; python_version > \"3.6\"", "trio-typing ; python_version > \"3.6\"", "twine", "twisted", "validate-pyproject[all]"] +dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "mypy", "pytest", "pytest-asyncio ; python_version >= \"3.4\"", "pytest-trio ; python_version >= \"3.7\"", "sphinx", "toml", "tox", "trio", "trio ; python_version > \"3.6\"", "trio-typing ; python_version > \"3.6\"", "twine", "twisted", "validate-pyproject[all]"] + +[[package]] +name = "pygments" +version = "2.19.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, + {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.3.6" +version = "3.3.7" description = "python code static checker" optional = false python-versions = ">=3.9.0" groups = ["dev"] files = [ - {file = "pylint-3.3.6-py3-none-any.whl", hash = "sha256:8b7c2d3e86ae3f94fb27703d521dd0b9b6b378775991f504d7c3a6275aa0a6a6"}, - {file = "pylint-3.3.6.tar.gz", hash = "sha256:b634a041aac33706d56a0d217e6587228c66427e20ec21a019bc4cdee48c040a"}, + {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, + {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, ] [package.dependencies] @@ -2469,40 +2528,42 @@ dev = ["build", "flake8", "mypy", "pytest", "twine"] [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"}, + {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" -version = "6.0.0" +version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, - {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, + {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, + {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, ] [package.dependencies] coverage = {version = ">=7.5", extras = ["toml"]} -pytest = ">=4.6" +pluggy = ">=1.2" +pytest = ">=6.2.5" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] @@ -2529,14 +2590,14 @@ docs = ["Sphinx", "sphinx-rtd-theme"] [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.14.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, + {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, ] [package.dependencies] @@ -2567,14 +2628,14 @@ dev = ["black", "flake8", "pre-commit"] [[package]] name = "pytest-xdist" -version = "3.6.1" +version = "3.7.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0"}, + {file = "pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126"}, ] [package.dependencies] @@ -3011,30 +3072,30 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruff" -version = "0.9.10" +version = "0.11.13" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, - {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, - {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, - {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, - {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, - {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, - {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, - {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, - {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, + {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"}, + {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"}, + {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"}, + {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"}, + {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"}, + {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"}, + {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"}, ] [[package]] @@ -3318,14 +3379,14 @@ telegram = ["requests"] [[package]] name = "types-cachetools" -version = "5.5.0.20240820" +version = "6.0.0.20250525" description = "Typing stubs for cachetools" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types-cachetools-5.5.0.20240820.tar.gz", hash = "sha256:b888ab5c1a48116f7799cd5004b18474cd82b5463acb5ffb2db2fc9c7b053bc0"}, - {file = "types_cachetools-5.5.0.20240820-py3-none-any.whl", hash = "sha256:efb2ed8bf27a4b9d3ed70d33849f536362603a90b8090a328acf0cd42fda82e2"}, + {file = "types_cachetools-6.0.0.20250525-py3-none-any.whl", hash = "sha256:1de8f0fe4bdcb187a48d2026c1e3672830f67943ad2bf3486abe031b632f1252"}, + {file = "types_cachetools-6.0.0.20250525.tar.gz", hash = "sha256:baf06f234cac3aeb44c07893447ba03ecdb6c0742ba2607e28a35d38e6821b02"}, ] [[package]] @@ -3361,38 +3422,38 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20241206" +version = "2.9.0.20250516" description = "Typing stubs for python-dateutil" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, - {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, + {file = "types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93"}, + {file = "types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5"}, ] [[package]] name = "types-pytz" -version = "2024.2.0.20241221" +version = "2025.2.0.20250516" description = "Typing stubs for pytz" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_pytz-2024.2.0.20241221-py3-none-any.whl", hash = "sha256:8fc03195329c43637ed4f593663df721fef919b60a969066e22606edf0b53ad5"}, - {file = "types_pytz-2024.2.0.20241221.tar.gz", hash = "sha256:06d7cde9613e9f7504766a0554a270c369434b50e00975b3a4a0f6eed0f2c1a9"}, + {file = "types_pytz-2025.2.0.20250516-py3-none-any.whl", hash = "sha256:e0e0c8a57e2791c19f718ed99ab2ba623856b11620cb6b637e5f62ce285a7451"}, + {file = "types_pytz-2025.2.0.20250516.tar.gz", hash = "sha256:e1216306f8c0d5da6dafd6492e72eb080c9a166171fa80dd7a1990fd8be7a7b3"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.20250326" +version = "6.0.12.20250516" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_pyyaml-6.0.12.20250326-py3-none-any.whl", hash = "sha256:961871cfbdc1ad8ae3cb6ae3f13007262bcfc168adc513119755a6e4d5d7ed65"}, - {file = "types_pyyaml-6.0.12.20250326.tar.gz", hash = "sha256:5e2d86d8706697803f361ba0b8188eef2999e1c372cd4faee4ebb0844b8a4190"}, + {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"}, + {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"}, ] [[package]] @@ -3413,14 +3474,14 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20250328" +version = "2.32.4.20250611" description = "Typing stubs for requests" optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2"}, - {file = "types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32"}, + {file = "types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072"}, + {file = "types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826"}, ] [package.dependencies] @@ -3872,4 +3933,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = "^3.12.6" -content-hash = "e1060bd4b6ef6af5bb8ca80edee4283c21beb06a03ee6b64c78fa453f2c50670" +content-hash = "a286592c9f1bfc434bb5b8557bb5aed60a411f9288d2535ec6cdc1561aae4994" diff --git a/pyproject.toml b/pyproject.toml index ab37e32617..6d62b3ac9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,12 +39,12 @@ types-redis = "^4.6.0.20240819" types-PyYAML = "^6.0.12.20240808" types-python-dateutil = "^2.9.0.20240821" pytest-mock = "^3.12.0" -types-cachetools = "^5.3.0.7" -types-pytz = "^2024.1.0.20240417" +types-cachetools = "^6.0.0.20250525" +types-pytz = "^2025.2.0.20250516" playwright = "^1.42.0" -black = "^24.2.0" +black = "^25.1.0" djlint = "^1.34.2" -ruff = "^0.9.1" +ruff = "^0.11.13" [tool.poetry.dependencies] diff --git a/templates/sectionsummary.html b/templates/sectionsummary.html index 5def65d85d..2e4a0dec02 100644 --- a/templates/sectionsummary.html +++ b/templates/sectionsummary.html @@ -30,7 +30,8 @@

      {{ content.summary.title }}

      {% set add_link_text = summary.add_link_text %} {% set empty_list_text = summary.empty_list_text %} {% set list_title = summary.title %} -
      +
      {% if summary.list %} {% set list = summary.list %} {% include "partials/summary/list-summary.html" %} From 226e25289089d895ea7b55fea96b3d9fadb53ef8 Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Mon, 30 Jun 2025 06:11:27 +0100 Subject: [PATCH 563/567] Assign Worfklows Permissions, Pin Dependencies to a Commit Hash and Update CodeQL Configuration (#1659) * Assign worfklows permissions and pin dependices to a commit hash * remove with matrixes in aalysis perform Signed-off-by: Yuyutsu Rai * update codeql with standard advanced configuration * remove manual mode| * revert removal of maunal matrix mode * remove typescript from the language matrix * remov e commenst and change build mode toauto build * revert back to none build mode * remove manual mode if statement * give blank space * update Anlayse in codeyaml * remove with paramter * update reomve buildmode paramter * revert title and lanauge formatting * pin commmits in codeql analysis yml * specifiy ubuntu version * pin actions to commit hash Signed-off-by: Yuyutsu Rai * Add Build mode option to matrix * update pathing to language in codeql analysis perform job Signed-off-by: Yuyutsu Rai * update name and formatting to be consistent * add permission to job in pull request yml --------- Signed-off-by: Yuyutsu Rai --- .github/workflows/codeql-analysis.yml | 63 ++++++++++----------------- .github/workflows/pull_request.yml | 54 ++++++++++++++--------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0cd2db8713..7af23d64cf 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,36 +1,37 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -name: "CodeQL" +name: "CodeQL Advanced" on: push: - branches: [main] + branches: [ "main" ] pull_request: - # The branches below must be a subset of the branches above - branches: [main] + branches: [ "main" ] schedule: - cron: '0 15 * * 3' jobs: analyze: - name: Analyze - runs-on: ubuntu-22.04 + name: Analyze (${{ matrix.language }}) + runs-on: [ubuntu-24.04] + permissions: + security-events: write + packages: read + actions: read + contents: read strategy: fail-fast: false matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['python', 'javascript', 'actions'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -38,29 +39,11 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - + build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + with: + category: "/language:${{matrix.language}}" \ No newline at end of file diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8f0ebad55d..81f29dde20 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -14,17 +14,19 @@ concurrency: jobs: python-dependencies: + permissions: + contents: 'read' runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 with: version: 2.1.2 virtualenvs-create: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'poetry' @@ -33,32 +35,36 @@ jobs: sudo apt-get install libsnappy-dev poetry install node-dependencies: + permissions: + contents: 'read' runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version-file : ".nvmrc" - name: Install npm deps run: npm install lint: + permissions: + contents: 'read' needs: [python-dependencies, node-dependencies] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - - uses: actions/setup-node@v4 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version-file: ".nvmrc" - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 with: version: 2.1.2 virtualenvs-create: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" @@ -77,10 +83,12 @@ jobs: - name: HTML linting run: make lint-html test-unit: + permissions: + contents: 'read' needs: python-dependencies runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install apt dependencies @@ -93,11 +101,11 @@ jobs: - name: Write app version run: printf "${{ github.event.pull_request.head.sha }}" > .application-version - name: Install Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 with: version: 2.1.2 virtualenvs-create: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" @@ -112,25 +120,29 @@ jobs: - name: Running unit tests run: make test-unit validate-schemas: + permissions: + contents: 'read' runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Run validator run: ./scripts/run_validator.sh - name: Install Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 with: version: 2.1.2 virtualenvs-create: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" - name: Running schema validation run: make validate-test-schemas test-functional: + permissions: + contents: 'read' needs: [python-dependencies, node-dependencies] strategy: matrix: @@ -142,18 +154,18 @@ jobs: # :TODO: Revisit & update when 2 instances can be used without adverse effects EQ_FUNCTIONAL_TEST_MAX_INSTANCES: 2 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version-file: ".nvmrc" - run: | echo "PYTHON_VERSION=$(cat .python-version)" >> $GITHUB_ENV - name: Install Poetry - uses: snok/install-poetry@v1 + uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1 with: version: 2.1.2 virtualenvs-create: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: "poetry" @@ -171,10 +183,10 @@ jobs: id-token: 'write' runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - id: auth name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 + uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.17 with: token_format: 'access_token' workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }} From 158446f9d81c2bd3efe68057c686cb3aa692c13d Mon Sep 17 00:00:00 2001 From: petechd <53475968+petechd@users.noreply.github.com> Date: Tue, 1 Jul 2025 13:37:08 +0100 Subject: [PATCH 564/567] Add conditional workflow for tests in actions (#1684) --- .github/workflows/pull_request.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 81f29dde20..729a79556e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -83,6 +83,7 @@ jobs: - name: HTML linting run: make lint-html test-unit: + if: "!contains(github.event.pull_request.labels.*.name, 'tests not required')" permissions: contents: 'read' needs: python-dependencies @@ -141,6 +142,7 @@ jobs: - name: Running schema validation run: make validate-test-schemas test-functional: + if: "!contains(github.event.pull_request.labels.*.name, 'tests not required')" permissions: contents: 'read' needs: [python-dependencies, node-dependencies] From f2a6536a0aca28dbb57eea5f5be4a959c1c37792 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Tue, 1 Jul 2025 14:42:40 +0100 Subject: [PATCH 565/567] Schemas Release v5.39.0 (#1699) --- .schemas-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.schemas-version b/.schemas-version index 997c9c0b2d..2ccc90f1e6 100644 --- a/.schemas-version +++ b/.schemas-version @@ -1 +1 @@ -v5.38.0 +v5.39.0 From edb570f4cf9542ca48bfa3ef8c6c4f6468433ea3 Mon Sep 17 00:00:00 2001 From: rmccar <42928680+rmccar@users.noreply.github.com> Date: Thu, 3 Jul 2025 09:21:21 +0100 Subject: [PATCH 566/567] remove lint staged (#1690) --- .github/dependabot.yaml | 1 - package-lock.json | 592 ---------------------------------------- package.json | 1 - 3 files changed, 594 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 884f15a9ce..da4a38cc95 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -31,7 +31,6 @@ updates: - "eslint*" - "json*" - "jsrsasign*" - - "lint-staged*" - "livereload*" - "node-forge*" - "prettier*" diff --git a/package-lock.json b/package-lock.json index d41008a25b..80b026618f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "eslint-plugin-promise": "^6.6.0", "json-web-key": "^0.4.0", "jsrsasign": "^11.0.0", - "lint-staged": "^15.2.10", "livereload": "^0.9.3", "node-forge": "^1.3.1", "node-jose": "^2.2.0", @@ -4543,60 +4542,6 @@ "node": ">=8" } }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -4750,22 +4695,6 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -5501,18 +5430,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -6392,12 +6309,6 @@ "node": ">=6" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -6987,18 +6898,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -7777,18 +7676,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -8731,19 +8618,6 @@ "immediate": "~3.0.5" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/lines-and-columns": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", @@ -8753,157 +8627,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/lint-staged": { - "version": "15.5.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.0.tgz", - "integrity": "sha512-WyCzSbfYGhK7cU+UuDDkzUiytbfbi0ZdPy2orwtM75P3WTtQBzmG40cCxIa8Ii2+XjfxzLH6Be46tUfWS85Xfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.4.1", - "commander": "^13.1.0", - "debug": "^4.4.0", - "execa": "^8.0.1", - "lilconfig": "^3.1.3", - "listr2": "^8.2.5", - "micromatch": "^4.0.8", - "pidtree": "^0.6.0", - "string-argv": "^0.3.2", - "yaml": "^2.7.0" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -8911,75 +8634,6 @@ "dev": true, "license": "ISC" }, - "node_modules/listr2": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", - "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", - "dev": true, - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true - }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/livereload": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", @@ -9252,123 +8906,6 @@ "node": ">=8" } }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dev": true, - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dev": true, - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/loglevel": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", @@ -9447,12 +8984,6 @@ "semver": "bin/semver" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -9466,30 +8997,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mimic-response": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", @@ -10152,21 +9659,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -10469,18 +9961,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -11263,22 +10743,6 @@ "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", "dev": true }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -11289,12 +10753,6 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true - }, "node_modules/rgb2hex": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", @@ -11602,34 +11060,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -11803,15 +11233,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -13417,19 +12838,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index fade6d4dfa..9f22f533a9 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "eslint-plugin-promise": "^6.6.0", "json-web-key": "^0.4.0", "jsrsasign": "^11.0.0", - "lint-staged": "^15.2.10", "livereload": "^0.9.3", "node-forge": "^1.3.1", "node-jose": "^2.2.0", From a885ed938f9d67b1b57faeb11328b8136a2bdfad Mon Sep 17 00:00:00 2001 From: Yuyutsu Rai <113098086+Yuyuutsu@users.noreply.github.com> Date: Thu, 3 Jul 2025 12:32:32 +0100 Subject: [PATCH 567/567] Add logging when retrieving existing SDS dataset ID from Questionnaire Store (#1700) --- app/routes/session.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/routes/session.py b/app/routes/session.py index a5bc710c15..0213a0031f 100644 --- a/app/routes/session.py +++ b/app/routes/session.py @@ -162,11 +162,17 @@ def _set_questionnaire_supplementary_data( not (new_sds_dataset_id := metadata["sds_dataset_id"]) or existing_sds_dataset_id == new_sds_dataset_id ): - # no need to fetch: either no supplementary data or it hasn't changed, just validate lists - _validate_supplementary_data_lists( - supplementary_data=questionnaire_store.data_stores.supplementary_data_store.raw_data, - schema=schema, - ) + sds_dataset_id = existing_sds_dataset_id or new_sds_dataset_id + if sds_dataset_id: + logger.info( + "validating stored supplementary data", + sds_dataset_id=sds_dataset_id, + ) + # no need to fetch: either no supplementary data or it hasn't changed, just validate lists + _validate_supplementary_data_lists( + supplementary_data=questionnaire_store.data_stores.supplementary_data_store.raw_data, + schema=schema, + ) return identifier = (